From 05fc81f7b5e45eeb43283e12d99f06cae7f61688 Mon Sep 17 00:00:00 2001 From: Daniel Hilst <392820+dhilst@users.noreply.github.com> Date: Thu, 3 Sep 2026 14:52:49 -0300 Subject: [PATCH] fix(subiquity): three values the Ubuntu install path accepts and cannot use The boot flip in compute.subiquity.tmpl addressed port 3002. xcatd's install monitor listens on site.xcatiport, so a cluster that moves the port loses the flip and every node PXE-loops back into the installer. The flip now reads site.xcatiport and keeps 3002 as the default. TABLEBLANKOKAY, because the key is optional and a plain TABLE lookup of an absent key fails the whole template. The flip also counted any reply as an accepted request. It now requires the monitor's "ready" greeting before it sends "next", and "done" afterwards, so a different service on that port is not read as a flipped node. subiquity_nfsroot_server in debian.pm called getipaddr without a family. A dual-stack management node answers with its IPv6 address, and casper takes everything after the first colon in nfsroot= as the path, so the live filesystem never mounts. It now asks for IPv4, as dhcp.pm and mknb.pm do. The DNS setup wrote the xcatmaster name as a nameserver when getent found no address, which is the case the step exists to prevent. It now keeps the resolv.conf DHCP gave the live installer. ubuntu_subiquity_boot_flip.t, debian_subiquity_boot_params.t and ubuntu_resolvconf_ip.t fail on the parent commit and pass here. Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com> --- xCAT-server/lib/xcat/plugins/debian.pm | 6 +++-- .../install/ubuntu/compute.subiquity.tmpl | 27 ++++++++++++------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/xCAT-server/lib/xcat/plugins/debian.pm b/xCAT-server/lib/xcat/plugins/debian.pm index b923c37c8..07fcbfb20 100644 --- a/xCAT-server/lib/xcat/plugins/debian.pm +++ b/xCAT-server/lib/xcat/plugins/debian.pm @@ -521,7 +521,7 @@ sub copycd Arguments: $instserver the install server name, address, or the '!myipfn!' placeholder - $resolver optional coderef, for tests; defaults to NetworkUtils::getipaddr + $resolver optional coderef, for tests; defaults to NetworkUtils::getipaddr, IPv4 only Returns: the value to put in nfsroot, or undef when a real name does not resolve @@ -534,7 +534,9 @@ sub subiquity_nfsroot_server { return undef unless defined($instserver) && length($instserver); return $instserver if $instserver eq '!myipfn!'; - $resolver ||= sub { xCAT::NetworkUtils->getipaddr($_[0]) }; + # A dual-stack management node also has an AAAA record. casper takes everything after the + # first colon in nfsroot= as the path, so an IPv6 address there cannot be parsed. + $resolver ||= sub { xCAT::NetworkUtils->getipaddr($_[0], OnlyV4 => 1) }; return $resolver->($instserver); } diff --git a/xCAT-server/share/xcat/install/ubuntu/compute.subiquity.tmpl b/xCAT-server/share/xcat/install/ubuntu/compute.subiquity.tmpl index 4ec7faf20..9da43e4c0 100644 --- a/xCAT-server/share/xcat/install/ubuntu/compute.subiquity.tmpl +++ b/xCAT-server/share/xcat/install/ubuntu/compute.subiquity.tmpl @@ -68,12 +68,17 @@ autoinstall: echo "=== DNS setup ===" # glibc's resolver discards a nameserver line naming a host, so writing the xcatmaster # *name* leaves the installer -- and the in-target apt that inherits this file -- with no - # DNS. Resolve it here, while the live installer's DHCP resolv.conf still works. + # DNS. Resolve it here, while the live installer's DHCP resolv.conf still works, and keep + # that file when the name does not resolve. A name written back is the case this step + # exists to prevent. xcatmaster_host="#TABLE:noderes:$NODE:xcatmaster#" xcatmaster_ip="$(getent ahostsv4 "$xcatmaster_host" | awk '{print $1; exit}')" - [ -n "$xcatmaster_ip" ] || xcatmaster_ip="$xcatmaster_host" - rm -f /etc/resolv.conf - echo "nameserver $xcatmaster_ip" >/etc/resolv.conf + if [ -n "$xcatmaster_ip" ]; then + rm -f /etc/resolv.conf + echo "nameserver $xcatmaster_ip" >/etc/resolv.conf + else + echo "xcat: $xcatmaster_host has no IPv4 address; keeping the resolver DHCP supplied" + fi echo "domain #TABLE:site:key=domain:value#" >>/etc/resolv.conf echo "=== early-commands complete ===" late-commands: @@ -108,11 +113,13 @@ autoinstall: curtin in-target --target /target /root/post.script; } >>/target/var/log/xcat/xcat.log 2>&1' # Flip the node to local-disk boot, or it PXE-loops back into the installer on reboot. - # "next" is the request xcatd's install monitor answers with "nodeset next". Send it - # from the live installer, retried, and log a failure rather than silently reinstalling. A - # monitor that died and never came back is a separate problem; see #7759 -- but one that - # accepts the connection and then never answers would block a bare read forever and hang the - # install here, so both reads are bounded. - - ['bash', '-c', 'xm=#XCATVAR:XCATMASTER#; ok=0; for i in 1 2 3 4 5; do if exec 3<>/dev/tcp/$xm/3002; then read -r -t 10 r <&3 || true; printf "next\n" >&3; if read -r -t 10 r <&3; then ok=1; fi; exec 3>&- 3<&-; [ "$ok" = 1 ] && break; fi; sleep 5; done; if [ "$ok" != 1 ]; then echo "xcat: FAILED to flip $(hostname) to local-disk boot via $xm:3002; the node will PXE back into the installer" >>/target/var/log/xcat/xcat.log; fi; exit 0'] + # xcatd's install monitor greets with "ready", then answers "next" with "done" and runs + # "nodeset next". Require both tokens: another service on that port is not a flipped + # node. The monitor listens on site.xcatiport; TABLEBLANKOKAY because an absent key fails a + # plain TABLE lookup, and with it the whole template. + # Retry, and log a failure rather than reinstall silently. Both reads are bounded: a monitor + # that accepts and never answers would block a bare read and hang the install here. A + # monitor that died and never came back is #7759. + - ['bash', '-c', 'xm=#XCATVAR:XCATMASTER#; port="#TABLEBLANKOKAY:site:key=xcatiport:value#"; [ -n "$port" ] || port=3002; ok=0; for i in 1 2 3 4 5; do if exec 3<>/dev/tcp/$xm/$port; then if read -r -t 10 hello <&3 && [ "$hello" = "ready" ]; then printf "next\n" >&3; if read -r -t 10 ack <&3 && [ "$ack" = "done" ]; then ok=1; fi; fi; exec 3>&- 3<&-; [ "$ok" = 1 ] && break; fi; sleep 5; done; if [ "$ok" != 1 ]; then echo "xcat: FAILED to flip $(hostname) to local-disk boot via $xm:$port; the node will PXE back into the installer" >>/target/var/log/xcat/xcat.log; fi; exit 0'] error-commands: - tar -c --ignore-failed-read --transform='s/^/#HOSTNAME#-logs\//' /var/crash /var/log/installer /tmp/pre-install.log /autoinstall.yaml 2>/dev/null |nc -l 8080