mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-10-12 08:12:33 +00:00
Merge pull request #7755 from VersatusHPC/fix/ubuntu-subiquity-installnic
fix(xcat-core): capture Subiquity install failing
This commit is contained in:
@@ -372,6 +372,8 @@ sub subvars {
|
||||
$inc =~ s/#XCATVAR:([^#]+)#/envvar($1)/eg;
|
||||
$inc =~ s/#ENV:([^#]+)#/envvar($1)/eg;
|
||||
$inc =~ s/#UBUNTU_SUBIQUITY_APT_CONFIG#/ubuntu_subiquity_apt_config($media_dir)/eg;
|
||||
$inc =~ s/#SUBIQUITYINSTALLNIC#/subiquity_install_nic()/eg;
|
||||
$inc =~ s/#SUBIQUITYINSTALLMAC#/subiquity_install_mac()/eg;
|
||||
$inc =~ s/#MACHINEPASSWORD#/machinepassword()/eg;
|
||||
$inc =~ s/#CRYPT:([^:]+):([^:]+):([^#]+)#/crydb($1,$2,$3)/eg;
|
||||
$inc =~ s/#CRYPTORLOCKED:([^:]+):([^:]+):([^#]+)#/crydb_or_locked($1,$2,$3)/eg;
|
||||
@@ -1654,6 +1656,71 @@ sub crydb_or_locked
|
||||
return '*';
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------------------
|
||||
|
||||
=head3 subiquity_install_netcfg
|
||||
|
||||
Resolve the interface the INSTALLED system must bring up, in xCAT's own order:
|
||||
noderes.installnic, else noderes.primarynic, else match on mac.mac. Either attribute may name
|
||||
an interface OR carry a MAC address. xCAT::NetworkUtils::gen_net_boot_params already owns that
|
||||
order for the netboot kernel parameters, so it is reused here rather than re-derived -- and in
|
||||
particular the install template never re-derives any part of it in shell.
|
||||
|
||||
Arguments:
|
||||
$installnic - noderes.installnic (may be undef or empty)
|
||||
$primarynic - noderes.primarynic (may be undef or empty)
|
||||
$macentry - the raw mac.mac entry (may hold |-separated, !hostname-suffixed entries)
|
||||
$nodename - the node the entry is resolved for
|
||||
Returns:
|
||||
($setname, $macaddress)
|
||||
$setname - the name netplan must rename the matched device to, empty when the device is
|
||||
matched by MAC alone (installnic unset/"mac", or a MAC address given)
|
||||
$macaddress - the address netplan matches on, lower-cased
|
||||
|
||||
=cut
|
||||
|
||||
#--------------------------------------------------------------------------------
|
||||
sub subiquity_install_netcfg {
|
||||
my ($installnic, $primarynic, $macentry, $nodename) = @_;
|
||||
|
||||
my $macmac = xCAT::Utils->parseMacTabEntry(defined($macentry) ? $macentry : '', $nodename);
|
||||
my $params = xCAT::NetworkUtils->gen_net_boot_params($installnic, $primarynic, $macmac);
|
||||
|
||||
my $setname = defined($params->{nicname}) ? $params->{nicname} : '';
|
||||
my $macaddress = defined($params->{mac}) ? lc($params->{mac}) : '';
|
||||
return ($setname, $macaddress);
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------------------
|
||||
|
||||
=head3 subiquity_install_nic / subiquity_install_mac
|
||||
|
||||
Render #SUBIQUITYINSTALLNIC# / #SUBIQUITYINSTALLMAC# for the node being templated. installnic
|
||||
and primarynic are read blank-okay (a node that sets neither is normal, and is what
|
||||
"match on mac.mac" means); mac.mac stays a required lookup, as it was when the template read it
|
||||
directly -- a node with no MAC cannot be matched by netplan at all.
|
||||
|
||||
=cut
|
||||
|
||||
#--------------------------------------------------------------------------------
|
||||
sub subiquity_install_netcfg_for_node {
|
||||
return subiquity_install_netcfg(
|
||||
tabdb('noderes', '$NODE', 'installnic', 1),
|
||||
tabdb('noderes', '$NODE', 'primarynic', 1),
|
||||
tabdb('mac', '$NODE', 'mac'),
|
||||
$node);
|
||||
}
|
||||
|
||||
sub subiquity_install_nic {
|
||||
my ($setname) = subiquity_install_netcfg_for_node();
|
||||
return $setname;
|
||||
}
|
||||
|
||||
sub subiquity_install_mac {
|
||||
my (undef, $macaddress) = subiquity_install_netcfg_for_node();
|
||||
return $macaddress;
|
||||
}
|
||||
|
||||
sub ubuntu_subiquity_apt_mirror
|
||||
{
|
||||
# Apt mirror for Subiquity installs. site.ubuntu_apt_mirror overrides; otherwise default to the
|
||||
|
||||
@@ -74,11 +74,10 @@ autoinstall:
|
||||
- mkdir -p /target/var/log/xcat/
|
||||
- '{
|
||||
cat /tmp/pre-install.log >> /target/var/log/xcat/xcat.log;
|
||||
installnic="#TABLE:noderes:$NODE:installnic#";
|
||||
installmac="#TABLE:mac:$NODE:mac#";
|
||||
installmac="$(printf ''%s'' "${installmac}" | cut -d''|'' -f1 | cut -d''!'' -f1 | tr ''A-F'' ''a-f'')";
|
||||
installnic="#SUBIQUITYINSTALLNIC#";
|
||||
installmac="#SUBIQUITYINSTALLMAC#";
|
||||
mkdir -p /target/etc/netplan;
|
||||
if [ "${installnic}" = "mac" ]; then
|
||||
if [ -z "${installnic}" ]; then
|
||||
printf ''%s\n'' "network:" " version: 2" " ethernets:" " xcat-install:" " match:" " macaddress: \"${installmac}\"" " dhcp4: 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" >/target/etc/netplan/00-xcat-install.yaml;
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
#!/usr/bin/env perl
|
||||
# The interface the INSTALLED system brings up must be resolved in xCAT's own order --
|
||||
# noderes.installnic, else noderes.primarynic, else match on mac.mac -- and either attribute may
|
||||
# hold an interface NAME or a MAC address. Treating an empty installnic as "mac" straight away
|
||||
# skips primarynic entirely, so a node that configures only primarynic is provisioned with a
|
||||
# netplan that matches on mac.mac and never renames the interface.
|
||||
#
|
||||
# Two halves are covered: the resolution itself (xCAT::Template, which delegates the order to
|
||||
# xCAT::NetworkUtils::gen_net_boot_params rather than re-deriving it), and the netplan the
|
||||
# template's late-command actually writes once rendered with the resolved values.
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use FindBin;
|
||||
use lib "$FindBin::Bin/../../perl-xCAT";
|
||||
use lib "$FindBin::Bin/../../xCAT-server/lib/perl";
|
||||
use File::Temp qw(tempdir);
|
||||
use Test::More;
|
||||
use xCAT::Template;
|
||||
|
||||
my $NODE = 'node01';
|
||||
my $NODE_MAC = 'AA:BB:CC:DD:EE:01';
|
||||
|
||||
# ---- resolution: installnic -> primarynic -> mac.mac, each of which may be a name or a MAC ------
|
||||
my @cases = (
|
||||
{
|
||||
name => 'installnic names an interface',
|
||||
installnic => 'ens4',
|
||||
primarynic => 'eno1',
|
||||
setname => 'ens4',
|
||||
macaddress => 'aa:bb:cc:dd:ee:01',
|
||||
why => 'renames the device matched by mac.mac',
|
||||
},
|
||||
{
|
||||
name => 'installnic holds a MAC address',
|
||||
installnic => 'aa:bb:cc:dd:ee:02',
|
||||
primarynic => 'eno1',
|
||||
setname => '',
|
||||
macaddress => 'aa:bb:cc:dd:ee:02',
|
||||
why => 'matches that MAC and renames nothing',
|
||||
},
|
||||
{
|
||||
name => 'installnic empty, primarynic names an interface',
|
||||
installnic => '',
|
||||
primarynic => 'eno1',
|
||||
setname => 'eno1',
|
||||
macaddress => 'aa:bb:cc:dd:ee:01',
|
||||
why => 'falls back to primarynic instead of skipping to mac',
|
||||
},
|
||||
{
|
||||
name => 'installnic empty, primarynic holds a MAC address',
|
||||
installnic => '',
|
||||
primarynic => 'aa:bb:cc:dd:ee:03',
|
||||
setname => '',
|
||||
macaddress => 'aa:bb:cc:dd:ee:03',
|
||||
why => 'falls back to the primarynic MAC',
|
||||
},
|
||||
{
|
||||
name => 'installnic and primarynic both unset',
|
||||
installnic => '',
|
||||
primarynic => '',
|
||||
setname => '',
|
||||
macaddress => 'aa:bb:cc:dd:ee:01',
|
||||
why => 'finally falls back to mac.mac',
|
||||
},
|
||||
{
|
||||
name => 'installnic set to the literal "mac"',
|
||||
installnic => 'mac',
|
||||
primarynic => 'eno1',
|
||||
setname => '',
|
||||
macaddress => 'aa:bb:cc:dd:ee:01',
|
||||
why => 'is an explicit request to match by MAC, so primarynic is not consulted',
|
||||
},
|
||||
);
|
||||
|
||||
for my $case (@cases) {
|
||||
my ($setname, $macaddress) = xCAT::Template::subiquity_install_netcfg(
|
||||
$case->{installnic}, $case->{primarynic}, $NODE_MAC, $NODE);
|
||||
is($setname, $case->{setname}, "$case->{name}: $case->{why} (set-name)");
|
||||
is($macaddress, $case->{macaddress}, "$case->{name}: $case->{why} (macaddress)");
|
||||
}
|
||||
|
||||
# mac.mac carries |-separated, !hostname-suffixed entries; the netplan must match this node's.
|
||||
{
|
||||
my $entry = "aa:bb:cc:dd:ee:09!other|aa:bb:cc:dd:ee:0a!$NODE";
|
||||
my (undef, $macaddress) =
|
||||
xCAT::Template::subiquity_install_netcfg('', '', $entry, $NODE);
|
||||
is($macaddress, 'aa:bb:cc:dd:ee:0a',
|
||||
'a multi-entry mac.mac resolves to this node\'s address, lower-cased');
|
||||
}
|
||||
|
||||
# ---- rendering: run the template's own late-command and read the netplan it writes --------------
|
||||
my $tmpl_path = defined $ENV{XCATROOT}
|
||||
? "$ENV{XCATROOT}/share/xcat/install/ubuntu/compute.subiquity.tmpl" : '';
|
||||
$tmpl_path = 'xCAT-server/share/xcat/install/ubuntu/compute.subiquity.tmpl'
|
||||
unless -f $tmpl_path;
|
||||
$tmpl_path = "$FindBin::Bin/../../xCAT-server/share/xcat/install/ubuntu/compute.subiquity.tmpl"
|
||||
unless -f $tmpl_path;
|
||||
|
||||
SKIP: {
|
||||
skip 'compute.subiquity.tmpl not found', 4 unless -f $tmpl_path;
|
||||
my $tmpl = do { local $/; open my $fh, '<', $tmpl_path or die $!; <$fh> };
|
||||
|
||||
# The netplan-writing part of late-commands, verbatim: from the resolved values down to the
|
||||
# chmod. Rendering it here is what proves the shell branches on what Perl resolved.
|
||||
my ($snippet) = $tmpl =~ /(installnic="#SUBIQUITYINSTALLNIC#".*?fi;)/s;
|
||||
ok($snippet, 'the netplan late-command is rendered from the resolved values');
|
||||
|
||||
skip 'netplan late-command not found in the template', 3 unless $snippet;
|
||||
$snippet =~ s/''/'/g; # undo the YAML single-quote escaping
|
||||
|
||||
for my $case (
|
||||
{ name => 'a resolved interface name', setname => 'eno1', mac => 'aa:bb:cc:dd:ee:01' },
|
||||
{ name => 'no interface name', setname => '', mac => 'aa:bb:cc:dd:ee:03' },
|
||||
)
|
||||
{
|
||||
my $dir = tempdir(CLEANUP => 1);
|
||||
my $script = $snippet;
|
||||
$script =~ s/#SUBIQUITYINSTALLNIC#/$case->{setname}/g;
|
||||
$script =~ s/#SUBIQUITYINSTALLMAC#/$case->{mac}/g;
|
||||
$script =~ s{/target/}{$dir/target/}g;
|
||||
system('sh', '-c', "set -e\n$script") == 0
|
||||
or die "netplan late-command failed for $case->{name}\n";
|
||||
|
||||
my $netplan = do {
|
||||
local $/;
|
||||
open my $fh, '<', "$dir/target/etc/netplan/00-xcat-install.yaml" or die $!;
|
||||
<$fh>;
|
||||
};
|
||||
my @expected = (
|
||||
'network:', ' version: 2', ' ethernets:', ' xcat-install:',
|
||||
' match:', qq( macaddress: "$case->{mac}"),
|
||||
($case->{setname} ne '' ? " set-name: $case->{setname}" : ()),
|
||||
' dhcp4: true',
|
||||
);
|
||||
is($netplan, join("\n", @expected) . "\n",
|
||||
"the netplan written for $case->{name} matches the resolved values");
|
||||
}
|
||||
}
|
||||
|
||||
done_testing();
|
||||
@@ -36,10 +36,26 @@ unlike($tmpl, qr/echo.*GRUB_CMDLINE.*\\"/, 'no escaped double quotes in echo GRU
|
||||
unlike($tmpl, qr/\\\\x22/, 'template does not rely on non-portable printf hex escapes');
|
||||
like($tmpl, qr/printf ''%s\\n'' ''GRUB_CMDLINE_LINUX="#TABLEBLANKOKAY:bootparams:\$NODE:kcmdline#"''/, 'GRUB line uses portable printf quoting');
|
||||
like($tmpl, qr/\/target\/etc\/netplan\/00-xcat-install\.yaml/, 'template writes an xCAT-owned target netplan file');
|
||||
like($tmpl, qr/installnic="#TABLE:noderes:\$NODE:installnic#"/, 'target netplan uses node installnic');
|
||||
like($tmpl, qr/installmac="#TABLE:mac:\$NODE:mac#"/, 'target netplan uses node MAC');
|
||||
like($tmpl, qr/installmac="\$\(printf ''%s'' "\$\{installmac\}".*\| tr ''A-F'' ''a-f''\)"/, 'target netplan normalizes MAC case');
|
||||
like($tmpl, qr/installmac="\$\(printf ''%s'' "\$\{installmac\}" \| cut -d''\|'' -f1 \| cut -d''!'' -f1/, 'target netplan strips mac table suffixes before matching');
|
||||
# Regression: an UNSET noderes.installnic must not fatally break Subiquity xnba generation, and
|
||||
# the interface must still be resolved in xCAT's order (installnic -> primarynic -> mac.mac). The
|
||||
# template read #TABLE:noderes:$NODE:installnic#, and Template.pm's tabdb raises "Unable to find
|
||||
# requested field <installnic> from table <noderes>" -> "Failed to generate xnba configurations"
|
||||
# when the node carries no installnic, so the Ubuntu diskful install never started. The resolution
|
||||
# now happens in Perl (xCAT::Template, via xCAT::NetworkUtils::gen_net_boot_params) and reaches the
|
||||
# template already resolved, so no part of that order is re-derived in shell. The resolution itself
|
||||
# and the netplan it produces are covered by ubuntu_subiquity_installnic.t.
|
||||
unlike($tmpl, qr/noderes:\$NODE:(?:installnic|primarynic)/,
|
||||
'the template does not read installnic/primarynic itself (fatal when unset, and it would have to redo the fallback)');
|
||||
like($tmpl, qr/installnic="#SUBIQUITYINSTALLNIC#"/,
|
||||
'the target netplan uses the interface name xCAT resolved for this node');
|
||||
like($tmpl, qr/installmac="#SUBIQUITYINSTALLMAC#"/,
|
||||
'the target netplan uses the MAC address xCAT resolved for this node');
|
||||
like($tmpl, qr/if \[ -z "\$\{installnic\}" \]; then/,
|
||||
'no resolved interface name means match by MAC alone, with no set-name rename');
|
||||
unlike($tmpl, qr/tr ''A-F'' ''a-f''/,
|
||||
'the shell no longer normalizes the MAC (Template.pm resolves mac.mac entries)');
|
||||
unlike($tmpl, qr/cut -d''\|'' -f1/,
|
||||
'the shell no longer splits mac.mac entries (Template.pm resolves them for this node)');
|
||||
like($tmpl, qr/printf ''%s\\n'' "network:" " version: 2" " ethernets:" " xcat-install:" " match:" " macaddress: \\"\$\{installmac\}\\"" " set-name: \$\{installnic\}" " dhcp4: true" >\/target\/etc\/netplan\/00-xcat-install\.yaml;/, 'target netplan printf stays on one shell line');
|
||||
like($tmpl, qr/" macaddress: \\"\$\{installmac\}\\""/, 'target netplan matches by MAC address');
|
||||
like($tmpl, qr/" set-name: \$\{installnic\}"/, 'target netplan sets the expected installnic name');
|
||||
|
||||
Reference in New Issue
Block a user