2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-10-02 00:41:42 +00:00

fix(xcat-server): an Ubuntu service node installs from a debian-installer preseed

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
This commit is contained in:
Daniel Hilst
2026-09-29 17:18:04 -03:00
parent 95005ad836
commit 9efb3fb3c0
2 changed files with 198 additions and 0 deletions
@@ -0,0 +1,137 @@
#cloud-config
autoinstall:
version: 1
keyboard: {layout: us, toggle: null, variant: ''}
locale: en_US
network:
version: 2
ethernets:
xcat-install:
match:
name: "e*"
dhcp4: true
ssh:
allow-pw: true
authorized-keys: []
install-server: true
identity:
realname: 'xCAT Admin'
username: xcatadm
hostname: #HOSTNAME#
password: "#CRYPTORLOCKED:passwd:key=system,username=root:password#"
#UBUNTU_SUBIQUITY_APT_CONFIG#
kernel:
package: linux-generic
user-data:
hostname: #HOSTNAME#
disable_root: false
package_update: false
package_upgrade: false
timezone: #TABLE:site:key=timezone:value#
chpasswd:
list:
- "root:#CRYPTORLOCKED:passwd:key=system,username=root:password#"
expire: false
packages:
- openssh-server
- openssh-client
- wget
- vim
- rsync
- busybox-static
- gawk
- bind9-dnsutils
- chrony
- gpg
- #INCLUDE_DEFAULT_PKGLIST_AUTOINSTALL#
early-commands:
- |
exec >/tmp/pre-install.log 2>&1
set -eux
echo "=== xCAT early-commands ==="
echo "=== network ==="
ip addr show
ip route show
echo "=== disk detection ==="
wget -T 30 -O /tmp/getinstdisk http://#XCATVAR:XCATMASTER##COLONHTTPPORT#/install/autoinst/getinstdisk
test -s /tmp/getinstdisk
chmod u+x /tmp/getinstdisk
/tmp/getinstdisk
echo "=== pre-install script ==="
wget -T 30 -O /tmp/pre.sh http://#XCATVAR:XCATMASTER##COLONHTTPPORT#/install/autoinst/#HOSTNAME#.pre
test -s /tmp/pre.sh
chmod u+x /tmp/pre.sh
/tmp/pre.sh
echo "=== storage injection ==="
test -s /tmp/partitionfile
sed -i '/^\.\.\.$/d' /autoinstall.yaml
cat /tmp/partitionfile >> /autoinstall.yaml
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, 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}')"
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:
- mkdir -p /target/var/log/xcat/
- '{
cat /tmp/pre-install.log >> /target/var/log/xcat/xcat.log;
installnic="#SUBIQUITYINSTALLNIC#";
installmac="#SUBIQUITYINSTALLMAC#";
mkdir -p /target/etc/netplan;
if [ -z "${installnic}" ]; then
printf ''%s\n'' "network:" " version: 2" " ethernets:" " xcat-install:" " match:" " macaddress: \"${installmac}\"" " dhcp4: true" " dhcp4-overrides:" " use-domains: true" >/target/etc/netplan/00-xcat-install.yaml;
else
printf ''%s\n'' "network:" " version: 2" " ethernets:" " xcat-install:" " match:" " macaddress: \"${installmac}\"" " set-name: ${installnic}" " dhcp4: true" " dhcp4-overrides:" " use-domains: true" >/target/etc/netplan/00-xcat-install.yaml;
fi;
chmod 600 /target/etc/netplan/00-xcat-install.yaml;
printf ''%s\n'' ''#HOSTNAME#'' >/target/etc/hostname;
if grep -q ''^127\.0\.1\.1'' /target/etc/hosts; then
sed -i ''s/^127\.0\.1\.1.*/127.0.1.1 #HOSTNAME#/'' /target/etc/hosts;
else
printf ''%s\n'' ''127.0.1.1 #HOSTNAME#'' >>/target/etc/hosts;
fi;
mkdir -p /target/etc/cloud;
touch /target/etc/cloud/cloud-init.disabled;
echo "Updating kernel command line...";
printf ''%s\n'' ''GRUB_CMDLINE_LINUX="#TABLEBLANKOKAY:bootparams:$NODE:kcmdline#"'' >>/target/etc/default/grub;
curtin in-target --target /target update-grub2;
echo "Running late_command Installation script...";
wget -T 30 http://#XCATVAR:XCATMASTER##COLONHTTPPORT#/install/autoinst/#HOSTNAME#.post;
chmod u+x #HOSTNAME#.post;
cp ./#HOSTNAME#.post /target/root/post.script;
curtin in-target --target /target /root/post.script;
} >>/target/var/log/xcat/xcat.log 2>&1'
# The installer's sources for the otherpkgs repository and the pkgdir mirrors, and the apt
# configuration that kept recommended packages out, served the install; ospkgs and otherpkgs
# write their own after the first boot. A separate item, so the status of the post script above
# still decides whether the install goes on.
- rm -f /target/etc/apt/sources.list.d/xcat-otherpkgs-*.list /target/etc/apt/sources.list.d/xcat-otherpkgs-*.sources /target/etc/apt/sources.list.d/xcat-pkgdir-*.list /target/etc/apt/sources.list.d/xcat-pkgdir-*.sources /target/etc/apt/apt.conf.d/94curtin-config
# Flip the node to local-disk boot, or it PXE-loops back into the installer on reboot.
# xcatd's install monitor greets with "ready", then answers "next" with "done" and runs
# "nodeset <node> 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:
# Subiquity waits for every error command to return. "nc -l 8080" waits for a collector, which
# an unattended install does not have. Keep the archive on the installer, and print the end of
# the curtin log to the console, which the management node records.
#
# Both destinations are read from the environment so a test can point them at its own
# directory. The installer sets neither, so an install writes where it always did.
- ['sh', '-c', 'tar -c --ignore-failed-read --transform="s/^/#HOSTNAME#-logs\//" /var/crash /var/log/installer /tmp/pre-install.log /autoinstall.yaml >"${XCAT_ERROR_ARCHIVE:-/run/#HOSTNAME#-logs.tar}" 2>/dev/null; exit 0']
- ['sh', '-c', 'tail -n 80 /var/log/installer/curtin-install.log >"${XCAT_ERROR_CONSOLE:-/dev/console}" 2>/dev/null; exit 0']
@@ -0,0 +1,61 @@
#!/usr/bin/env perl
use strict;
use warnings;
use FindBin;
use Test::More;
# xCAT decides that an Ubuntu osimage uses the Subiquity installer from the NAME of the template
# it resolved: xCAT_plugin::debian::using_subiquity matches /subiquity/ in the path. That decision
# controls the netboot kernel command line. A template whose name does not match it makes xCAT
# boot the live-server initrd with the debian-installer preseed arguments, without boot=casper and
# without nfsroot, and the node never finds a live filesystem.
#
# The compute profile has compute.subiquity.tmpl. The service profile had only service.tmpl, so an
# Ubuntu 24.04 service node took the preseed path.
use lib "$FindBin::Bin/../../perl-xCAT";
use lib "$FindBin::Bin/../../xCAT-server/lib/perl";
use xCAT::SvrUtils;
my $plugin = "$FindBin::Bin/../../xCAT-server/lib/xcat/plugins/debian.pm";
plan skip_all => 'debian.pm not found' unless -r $plugin;
eval { require $plugin; 1 } or plan skip_all => "could not load debian.pm: $@";
my $share = "$FindBin::Bin/../../xCAT-server/share/xcat/install/ubuntu";
plan skip_all => "$share not found" unless -d $share;
# get_file_name takes genos as its last argument. update_tables_with_templates passes 'subiquity'
# for every Ubuntu 20.04 and later osimage.
sub resolved {
my ($profile, $osver, $genos) = @_;
return xCAT::SvrUtils::get_tmpl_file_name($share, $profile, $osver, 'x86_64', $genos);
}
for my $profile (qw(compute service)) {
my $tmpl = resolved($profile, 'ubuntu24.04', 'subiquity');
ok(defined $tmpl && -r $tmpl, "$profile: Ubuntu 24.04 resolves an install template");
ok(xCAT_plugin::debian::using_subiquity('ubuntu24.04', $tmpl),
"$profile: the Ubuntu 24.04 template xCAT resolved is a Subiquity one");
}
# Ubuntu 18.04 predates Subiquity and keeps the preseed path. genos stays the os version there.
for my $profile (qw(compute service)) {
my $tmpl = resolved($profile, 'ubuntu18.04', 'ubuntu18.04');
ok(defined $tmpl && -r $tmpl, "$profile: Ubuntu 18.04 resolves an install template");
ok(!xCAT_plugin::debian::using_subiquity('ubuntu18.04', $tmpl),
"$profile: Ubuntu 18.04 keeps the debian-installer template");
}
# The service template must carry the autoinstall document Subiquity reads. A file named for
# Subiquity that holds a preseed would satisfy using_subiquity and still not install.
my $service = resolved('service', 'ubuntu24.04', 'subiquity');
SKIP: {
skip 'no service template resolved', 3 unless defined $service && -r $service;
my $text = do { local $/; open my $fh, '<', $service or die "$service: $!"; <$fh> };
like($text, qr/^#cloud-config/, 'the service template is a cloud-config document');
like($text, qr/^autoinstall:/m, '... carrying an autoinstall section');
unlike($text, qr/^d-i /m, '... and no debian-installer preseed directives');
}
done_testing();