2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-09-21 16:39:30 +00:00

test(xcat-core): capture primarynic being skipped when installnic is unset

xCAT resolves the provisioning interface as installnic -> primarynic -> mac.mac,
and either attribute may hold an interface NAME or a MAC address;
xCAT::NetworkUtils::gen_net_boot_params owns that order for the netboot kernel
parameters. The Subiquity template short-circuits it: an empty installnic is
treated as "mac" straight away, so a node that configures only primarynic is
installed with a netplan that matches on mac.mac and never renames the
interface, and a primarynic holding a MAC address is ignored entirely.

Assert the full order against a resolver in xCAT::Template -- installnic as a
name, installnic as a MAC, installnic empty with primarynic as a name, empty
with primarynic as a MAC, both empty, and the literal "mac" -- plus multi-entry
mac.mac resolution, and then run the template's own netplan late-command with
the resolved values and compare the file it writes. These fail on the current
template, which carries the resolution in shell and has no resolver to call.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
This commit is contained in:
Daniel Hilst
2026-08-24 18:14:40 -03:00
parent 62ab9caead
commit f021fe74fe
@@ -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();