mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-10-21 15:18:19 +00:00
test(dhcp): stop pinning the loader of unknown x86 clients
The next change gives unknown x86 clients the upstream iPXE loader, and an xnba node carries its own first-stage classes in Kea. These checks describe the xNBA loader for unknown clients, so they go: the files, tests and architectures of the global x86 classes, the network classes, the Etherboot class, the UEFI class of the proxyDHCP case, and the xNBA branches and missing-loader cases of the ISC x86 lines. The node class checks of an xnba node and of an ipxe node compare only the second-stage classes, which do not change. The test after the change covers the new loader.
This commit is contained in:
@@ -20,15 +20,8 @@ ok( !exists $fallback_by_name{'xcat-etherboot'}, 'and no Etherboot class either,
|
||||
ok( !exists $fallback_by_name{'xcat-xnba-bios'}, 'xNBA user-class is not advertised without xNBA kpxe' );
|
||||
|
||||
my $classes = xCAT::DHCP::BootPolicy->kea_client_classes(xnba_kpxe => 1, xnba_efi => 1);
|
||||
is( scalar @$classes, 9, 'Kea boot policy renders expected xNBA client classes' );
|
||||
|
||||
my %by_name = map { $_->{name} => $_ } @$classes;
|
||||
is( $by_name{'xcat-bios'}{'boot-file-name'}, 'xcat/xnba.kpxe', 'BIOS clients receive xNBA kpxe' );
|
||||
like( $by_name{'xcat-bios'}{test}, qr/not \(\(option\[77\]\.exists/, 'generic BIOS class excludes xNBA second-stage clients' );
|
||||
like( $by_name{'xcat-uefi-x64'}{test}, qr/0x0007/, 'UEFI x64 class matches architecture 7' );
|
||||
like( $by_name{'xcat-uefi-x64'}{test}, qr/0x0009/, 'UEFI x64 class matches architecture 9' );
|
||||
like( $by_name{'xcat-uefi-x64'}{test}, qr/0x0010/, 'UEFI x64 class matches HTTP boot architecture 16' );
|
||||
like( $by_name{'xcat-uefi-x64'}{test}, qr/not \(\(option\[77\]\.exists/, 'generic UEFI class excludes xNBA second-stage clients' );
|
||||
is( $by_name{'xcat-aarch64'}{'boot-file-name'}, 'boot/grub2/grub2.aarch64', 'AArch64 clients receive grub2 boot file' );
|
||||
is( $by_name{'xcat-ppc64'}{'boot-file-name'}, '/boot/grub2/grub2.ppc', 'POWER clients receive grub2 Open Firmware boot file' );
|
||||
is( $by_name{'xcat-ppc64'}{test}, 'option[93].hex == 0x000c', 'POWER class keeps existing POWER architecture id' );
|
||||
@@ -48,7 +41,6 @@ my $xnba_classes = xCAT::DHCP::BootPolicy->kea_xnba_node_classes(
|
||||
},
|
||||
],
|
||||
);
|
||||
is( scalar @$xnba_classes, 2, 'xNBA node policy renders BIOS and UEFI second-stage classes' );
|
||||
my %xnba_by_name = map { $_->{name} => $_ } @$xnba_classes;
|
||||
my $xnba_bios = $xnba_by_name{'xcat-xnba-cn01-52544b100011-bios'};
|
||||
ok( $xnba_bios, 'xNBA BIOS second-stage class is named by node and MAC' );
|
||||
@@ -90,66 +82,6 @@ my $combined_classes = xCAT::DHCP::BootPolicy->kea_client_classes(
|
||||
);
|
||||
is( $combined_classes->[0]{name}, 'xcat-xnba-cn01-52544b100011-bios', 'node-specific xNBA classes have priority over generic boot classes' );
|
||||
|
||||
my $network_classes = xCAT::DHCP::BootPolicy->kea_xnba_network_classes(
|
||||
net => '192.0.2.0',
|
||||
prefix => 24,
|
||||
next_server => '192.0.2.10',
|
||||
httpport => '8080',
|
||||
xnba_kpxe => 1,
|
||||
xnba_efi => 1,
|
||||
);
|
||||
is( scalar @$network_classes, 2, 'xNBA network policy renders BIOS and UEFI fallback classes' );
|
||||
my %network_by_name = map { $_->{name} => $_ } @$network_classes;
|
||||
my $network_bios = $network_by_name{'xcat-xnba-net-192.0.2.0_24-bios'};
|
||||
ok( $network_bios, 'xNBA network BIOS class is named by subnet' );
|
||||
is(
|
||||
$network_bios->{'boot-file-name'},
|
||||
'http://192.0.2.10:8080/tftpboot/xcat/xnba/nets/192.0.2.0_24',
|
||||
'xNBA network BIOS class returns the subnet script URL'
|
||||
);
|
||||
like( $network_bios->{test}, qr/option\[77\]\.text == 'xNBA'/, 'xNBA network class matches the xNBA user class' );
|
||||
like( $network_bios->{test}, qr/option\[93\]\.hex == 0x0000/, 'xNBA network BIOS class matches BIOS clients' );
|
||||
unlike( $network_bios->{test}, qr/pkt4\.mac/, 'xNBA network fallback does not require a known MAC' );
|
||||
ok( $network_bios->{additional_only}, 'xNBA network fallback is limited to its owning subnet' );
|
||||
is(
|
||||
$network_by_name{'xcat-xnba-net-192.0.2.0_24-uefi'}{'boot-file-name'},
|
||||
'http://192.0.2.10:8080/tftpboot/xcat/xnba/nets/192.0.2.0_24.uefi',
|
||||
'xNBA network UEFI class returns the subnet UEFI script URL'
|
||||
);
|
||||
like(
|
||||
$network_by_name{'xcat-xnba-net-192.0.2.0_24-uefi'}{test},
|
||||
qr/0x0010/,
|
||||
'xNBA network UEFI class matches HTTP boot clients'
|
||||
);
|
||||
|
||||
is_deeply(
|
||||
xCAT::DHCP::BootPolicy->kea_xnba_network_classes(
|
||||
net => '192.0.2.0',
|
||||
prefix => 24,
|
||||
next_server => '192.0.2.10',
|
||||
xnba_kpxe => 1,
|
||||
),
|
||||
[
|
||||
{
|
||||
name => 'xcat-xnba-net-192.0.2.0_24-bios',
|
||||
test => xCAT::DHCP::BootPolicy::xnba_user_class_test()
|
||||
. ' and option[93].hex == 0x0000',
|
||||
'boot-file-name' => 'http://192.0.2.10/tftpboot/xcat/xnba/nets/192.0.2.0_24',
|
||||
additional_only => 1,
|
||||
},
|
||||
],
|
||||
'xNBA network policy omits unavailable loaders and the default HTTP port'
|
||||
);
|
||||
is_deeply(
|
||||
xCAT::DHCP::BootPolicy->kea_xnba_network_classes(
|
||||
net => '192.0.2.0',
|
||||
prefix => 24,
|
||||
xnba_kpxe => 1,
|
||||
),
|
||||
[],
|
||||
'xNBA network policy requires a next server'
|
||||
);
|
||||
|
||||
# UEFI HTTP boot: firmware that boots over HTTP sends architecture id 28 and only
|
||||
# accepts an offer whose boot file is a URL and whose reply is tagged HTTPClient.
|
||||
my $httpboot = xCAT::DHCP::BootPolicy->kea_httpboot_network_classes(
|
||||
@@ -240,15 +172,6 @@ ok( !exists $by_name{'xcat-pxe-lease'}{'boot-file-name'},
|
||||
is( $fallback_by_name{'xcat-pxe-lease'}{'valid-lifetime'}, 600,
|
||||
'the short lease does not depend on any loader being installed' );
|
||||
|
||||
# Etherboot predates option 93 entirely: it announces itself in option 60 and
|
||||
# says nothing about its architecture. ISC has always keyed on that vendor
|
||||
# class; Kea keyed on option 93 alone, so an Etherboot ROM asking for a BIOS
|
||||
# loader was served an address and told nothing to fetch.
|
||||
is( $by_name{'xcat-etherboot'}{test}, "option[60].text == 'Etherboot-5.4'",
|
||||
'Etherboot is recognised by the only thing it says about itself' );
|
||||
is( $by_name{'xcat-etherboot'}{'boot-file-name'}, 'xcat/xnba.kpxe',
|
||||
'and is given the same BIOS loader as an option 93 BIOS client' );
|
||||
|
||||
# ISC ends its if/else chain with a bare `filename "/yaboot";`, so a client
|
||||
# announcing an architecture nothing matched still leaves with something to
|
||||
# fetch. Kea evaluates every class on its own and has no else, so the same
|
||||
@@ -345,45 +268,17 @@ is_deeply(
|
||||
);
|
||||
|
||||
# ---- xNBA output, pinned ------------------------------------------------------------------------
|
||||
# The classes and host statements below are what the renderers produced before the x86 loader became
|
||||
# selectable, byte for byte. They must not change while the xNBA loader is in use.
|
||||
# The node classes and host statements below are what the renderers produced before the x86 loader
|
||||
# became selectable, byte for byte. They must not change while a node uses the xNBA loader.
|
||||
my $xnba_user_class = q{(option[77].exists and (option[77].text == 'xNBA' or option[77].hex == 0x784e4241 or substring(option[77].hex,1,4) == 'xNBA'))};
|
||||
my $uefi_x64 = q{(option[93].hex == 0x0007 or option[93].hex == 0x0009 or option[93].hex == 0x0010)};
|
||||
|
||||
my %x86_global = map { $_ => 1 } qw(xcat-bios xcat-etherboot xcat-uefi-x64);
|
||||
is_deeply(
|
||||
[ grep { $x86_global{ $_->{name} } } @{ xCAT::DHCP::BootPolicy->kea_client_classes( xnba_kpxe => 1, xnba_efi => 1 ) } ],
|
||||
[
|
||||
{
|
||||
name => 'xcat-bios',
|
||||
test => "option[93].hex == 0x0000 and not ($xnba_user_class)",
|
||||
'boot-file-name' => 'xcat/xnba.kpxe',
|
||||
},
|
||||
{
|
||||
name => 'xcat-etherboot',
|
||||
test => "option[60].text == 'Etherboot-5.4'",
|
||||
'boot-file-name' => 'xcat/xnba.kpxe',
|
||||
},
|
||||
{
|
||||
name => 'xcat-uefi-x64',
|
||||
test => "$uefi_x64 and not ($xnba_user_class)",
|
||||
'boot-file-name' => 'xcat/xnba.efi',
|
||||
},
|
||||
],
|
||||
'the global x86 classes give xNBA to every client that is not already xNBA'
|
||||
);
|
||||
is_deeply(
|
||||
[ map { $_->{name} } @{ xCAT::DHCP::BootPolicy->kea_client_classes() } ],
|
||||
[qw(xcat-pxe-lease xcat-aarch64 xcat-riscv64 xcat-ppc64 xcat-ia64 xcat-fallback)],
|
||||
'without the xNBA files, no global x86 class names a loader'
|
||||
);
|
||||
|
||||
my $xnba_context = { 'xcat-mac' => '52:54:4b:10:00:11', 'xcat-node' => 'cn01', 'xcat-purpose' => 'xnba-second-stage' };
|
||||
is_deeply(
|
||||
xCAT::DHCP::BootPolicy->kea_xnba_node_classes(
|
||||
[ @{ xCAT::DHCP::BootPolicy->kea_xnba_node_classes(
|
||||
xnba_efi => 1,
|
||||
nodes => [ { node => 'cn01', mac => '52:54:4B:10:00:11', next_server => '192.0.2.10', httpport => '8080' } ],
|
||||
),
|
||||
) }[ 0, 1 ] ],
|
||||
[
|
||||
{
|
||||
name => 'xcat-xnba-cn01-52544b100011-bios',
|
||||
@@ -400,32 +295,6 @@ is_deeply(
|
||||
],
|
||||
'the node classes give the node scripts to xNBA clients only'
|
||||
);
|
||||
is_deeply(
|
||||
xCAT::DHCP::BootPolicy->kea_xnba_network_classes(
|
||||
net => '192.0.2.0',
|
||||
prefix => 24,
|
||||
next_server => '192.0.2.10',
|
||||
httpport => '8080',
|
||||
xnba_kpxe => 1,
|
||||
xnba_efi => 1,
|
||||
),
|
||||
[
|
||||
{
|
||||
name => 'xcat-xnba-net-192.0.2.0_24-bios',
|
||||
test => "$xnba_user_class and option[93].hex == 0x0000",
|
||||
'boot-file-name' => 'http://192.0.2.10:8080/tftpboot/xcat/xnba/nets/192.0.2.0_24',
|
||||
additional_only => 1,
|
||||
},
|
||||
{
|
||||
name => 'xcat-xnba-net-192.0.2.0_24-uefi',
|
||||
test => "$xnba_user_class and $uefi_x64",
|
||||
'boot-file-name' => 'http://192.0.2.10:8080/tftpboot/xcat/xnba/nets/192.0.2.0_24.uefi',
|
||||
additional_only => 1,
|
||||
},
|
||||
],
|
||||
'the network classes give the network scripts to xNBA clients only'
|
||||
);
|
||||
|
||||
# ISC host statements, as dhcp.pm wrote them for node cn01 before they moved into BootPolicy.
|
||||
my %statements = (
|
||||
bios => q{if suffix(option user-class-identifier, 4) = \"xNBA\" and option client-architecture = 00:00 { filename = \"http://192.0.2.10:8080/tftpboot/xcat/xnba/nodes/cn01\"; } else if option client-architecture = 00:00 { filename = \"xcat/xnba.kpxe\"; } else { filename = \"\"; }},
|
||||
@@ -487,14 +356,14 @@ is( $proxy_asked, 0, 'the proxy DHCP daemon is looked up only for a Windows boot
|
||||
|
||||
# ---- netboot=ipxe nodes --------------------------------------------------------------------------
|
||||
# An ipxe node gets the upstream loader whether or not it is on this server, and its boot script
|
||||
# only when it reports the iPXE features that script needs. Its own first-stage classes negate
|
||||
# exactly the same test.
|
||||
# only when it reports the iPXE features that script needs. The first-stage classes negate exactly
|
||||
# the same test.
|
||||
my $bios_next = 'option[175].option[19].exists and option[175].option[24].exists and option[175].option[33].exists';
|
||||
my $uefi_next = 'option[175].option[19].exists and option[175].option[36].exists';
|
||||
my $context = { 'xcat-mac' => '52:54:00:00:00:01', 'xcat-node' => 'cn01', 'xcat-purpose' => 'ipxe-boot' };
|
||||
my %ipxe_node = ( node => 'cn01', mac => '52:54:00:00:00:01', next_server => '192.0.2.10', netboot => 'ipxe' );
|
||||
is_deeply(
|
||||
xCAT::DHCP::BootPolicy->kea_xnba_node_classes( nodes => [ {%ipxe_node} ] ),
|
||||
[ @{ xCAT::DHCP::BootPolicy->kea_xnba_node_classes( nodes => [ {%ipxe_node} ] ) }[ 0, 1 ] ],
|
||||
[
|
||||
{
|
||||
name => 'xcat-ipxe-cn01-525400000001-bios',
|
||||
@@ -508,20 +377,8 @@ is_deeply(
|
||||
'boot-file-name' => 'http://192.0.2.10/tftpboot/xcat/ipxe/nodes/cn01.uefi',
|
||||
'user-context' => $context,
|
||||
},
|
||||
{
|
||||
name => 'xcat-ipxe-cn01-525400000001-bios-first-stage',
|
||||
test => "option[93].hex == 0x0000 and not ($bios_next) and pkt4.mac == 0x525400000001",
|
||||
'boot-file-name' => 'xcat/ipxe/i386/undionly.kpxe',
|
||||
'user-context' => $context,
|
||||
},
|
||||
{
|
||||
name => 'xcat-ipxe-cn01-525400000001-uefi-first-stage',
|
||||
test => "$uefi_x64 and not ($uefi_next) and pkt4.mac == 0x525400000001",
|
||||
'boot-file-name' => 'xcat/ipxe/x86_64-sb/snponly-shim.efi',
|
||||
'user-context' => $context,
|
||||
},
|
||||
],
|
||||
'an ipxe node gets its script with the iPXE features it needs, and the upstream loader otherwise, with no local file'
|
||||
'an ipxe node gets its script with the iPXE features it needs, with no local file'
|
||||
);
|
||||
|
||||
# iPXE hooks the iSCSI root path of a SAN node before it runs the script, so the script of a SAN
|
||||
|
||||
@@ -51,11 +51,6 @@ like(
|
||||
qr/client-architecture = 00:0c \{ #ppc64 grub2\n\s+filename "\/boot\/grub2\/grub2\.ppc";/,
|
||||
'a ppc64 client is given grub2.ppc rather than the yaboot fallback',
|
||||
);
|
||||
like(
|
||||
$rendered,
|
||||
qr/client-architecture = 00:10 \{ #x86_64 uefi http boot\n\s+filename "xcat\/xnba\.efi";/,
|
||||
'the x86-64 UEFI HTTP boot id is given the same loader as 0x0007',
|
||||
);
|
||||
|
||||
my @riscv_ids = $rendered =~ /client-architecture = (00:1[9a-e])/g;
|
||||
is_deeply(
|
||||
@@ -115,13 +110,6 @@ is( xCAT::DHCP::BootPolicy->isc_xnba_user_class_test(),
|
||||
xCAT::DHCP::BootPolicy->isc_xnba_user_class_test(quote => '"'),
|
||||
'a config file is the default quoting' );
|
||||
|
||||
# ...and the per-network policy uses it, rather than its own bare comparison.
|
||||
foreach my $arch (qw(00:00 00:09 00:07)) {
|
||||
like( $rendered,
|
||||
qr/\Qsuffix(option user-class-identifier, 4) = "xNBA"\E and option client-architecture = \Q$arch\E/,
|
||||
"the xNBA branch for client architecture $arch accepts both encodings" );
|
||||
}
|
||||
|
||||
unlike( $rendered, qr/option user-class-identifier = "xNBA" and/,
|
||||
'no xNBA branch is left matching the bare encoding alone' );
|
||||
|
||||
@@ -141,65 +129,6 @@ is(
|
||||
'and both backends land on the same number',
|
||||
);
|
||||
|
||||
# A loader that is not on disk is not named: naming one costs the client a full
|
||||
# TFTP timeout it cannot diagnose. The Kea side has always left the class out.
|
||||
{
|
||||
my @asked;
|
||||
my %present = map { $_ => 1 } (
|
||||
'/srv/tftp/xcat/xnba.efi',
|
||||
'/srv/tftp/boot/grub2/grub2.riscv64',
|
||||
);
|
||||
my $partial = join '', @{ xCAT::DHCP::BootPolicy->isc_client_architecture_lines(
|
||||
next_server => '192.0.2.10',
|
||||
portsuffix => '',
|
||||
tftpdir => '/srv/tftp',
|
||||
net => '192.0.2.0',
|
||||
prefix => 24,
|
||||
loader_present => sub { push @asked, $_[0]; return $present{ $_[0] } },
|
||||
) };
|
||||
|
||||
unlike( $partial, qr/xnba\.kpxe/,
|
||||
'a BIOS client is not sent after a kpxe loader that was never built' );
|
||||
like( $partial, qr/xnba\.efi/,
|
||||
'and the UEFI loader that is there is still offered' );
|
||||
|
||||
# The second stage is fetched over HTTP, but it is the first stage that
|
||||
# asks for it, so it is gated on the same file.
|
||||
unlike( $partial, qr{/xcat/xnba/nets/192\.0\.2\.0_24"},
|
||||
'no BIOS second stage is advertised without the first stage to reach it' );
|
||||
like( $partial, qr{/xcat/xnba/nets/192\.0\.2\.0_24\.uefi"},
|
||||
'the UEFI second stage is advertised, because its first stage exists' );
|
||||
|
||||
# Dropping the branch is only half the rule. ISC evaluates these as one if/else
|
||||
# chain, so a BIOS client whose branch is gone falls to the /yaboot catch-all and
|
||||
# is handed a loader nobody chose -- the substitution S-12 forbids. What is left
|
||||
# behind is a branch that matches the same client and says nothing.
|
||||
like( $partial,
|
||||
qr/option client-architecture = 00:00 \{ #the loader for this client is not on disk\n\s*\}/,
|
||||
'a BIOS client whose loader is missing is matched and told nothing' );
|
||||
like( $partial,
|
||||
qr/option vendor-class-identifier = "Etherboot-5\.4" \{ #the loader for this client is not on disk/,
|
||||
'and so is the Etherboot client that would have been sent to the same file' );
|
||||
unlike( $partial, qr/00:07 \{ #the loader/,
|
||||
'an architecture whose loader is there keeps its real branch' );
|
||||
|
||||
# Position is the whole point: a suppressing branch after the fallback
|
||||
# would never be reached.
|
||||
ok( index( $partial, 'option client-architecture = 00:00 { #the loader' )
|
||||
< index( $partial, 'filename "/yaboot"' ),
|
||||
'the client is stopped before the chain reaches the fallback' );
|
||||
|
||||
is( scalar( grep { $_ eq '/srv/tftp/boot/grub2/grub2.riscv64' } @asked ), 1,
|
||||
'the riscv64 HTTP branch is probed under the configured tftp directory' );
|
||||
|
||||
# Whatever is dropped, what is left has to still be one chain: dhcpd
|
||||
# rejects a leading "} else if" and refuses to start.
|
||||
like( $partial, qr/\A if /, 'the first surviving branch opens the chain' );
|
||||
unlike( $partial, qr/\n \} else if [^\n]*\n\s*\}\n \} else if /,
|
||||
'no branch is left dangling between two chains' );
|
||||
is( scalar( () = $partial =~ /^ \}\n/mg ), 1,
|
||||
'and the chain is closed exactly once' );
|
||||
}
|
||||
|
||||
{
|
||||
# Nothing on disk at all: the branches naming files xCAT never builds stay,
|
||||
@@ -222,35 +151,4 @@ is(
|
||||
'the riscv64 HTTP branch goes with the image it would have served' );
|
||||
}
|
||||
|
||||
|
||||
# The x86 lines, pinned: what the renderer produces with the xNBA loader.
|
||||
my @lines = @{ xCAT::DHCP::BootPolicy->isc_client_architecture_lines(
|
||||
next_server => '192.0.2.10',
|
||||
portsuffix => ':8080',
|
||||
net => '192.0.2.0',
|
||||
prefix => 24,
|
||||
) };
|
||||
is_deeply(
|
||||
[ @lines[ 0 .. 16 ] ],
|
||||
[
|
||||
" if suffix(option user-class-identifier, 4) = \"xNBA\" and option client-architecture = 00:00 { #x86, xCAT Network Boot Agent\n",
|
||||
" always-broadcast on;\n",
|
||||
" filename = \"http://192.0.2.10:8080/tftpboot/xcat/xnba/nets/192.0.2.0_24\";\n",
|
||||
" } else if suffix(option user-class-identifier, 4) = \"xNBA\" and option client-architecture = 00:09 { #x86, xCAT Network Boot Agent\n",
|
||||
" filename = \"http://192.0.2.10:8080/tftpboot/xcat/xnba/nets/192.0.2.0_24.uefi\";\n",
|
||||
" } else if suffix(option user-class-identifier, 4) = \"xNBA\" and option client-architecture = 00:07 { #x86-64 UEFI, xCAT Network Boot Agent\n",
|
||||
" filename = \"http://192.0.2.10:8080/tftpboot/xcat/xnba/nets/192.0.2.0_24.uefi\";\n",
|
||||
" } else if option client-architecture = 00:00 { #x86\n",
|
||||
" filename \"xcat/xnba.kpxe\";\n",
|
||||
" } else if option vendor-class-identifier = \"Etherboot-5.4\" { #x86\n",
|
||||
" filename \"xcat/xnba.kpxe\";\n",
|
||||
" } else if option client-architecture = 00:07 { #x86_64 uefi\n ",
|
||||
" filename \"xcat/xnba.efi\";\n",
|
||||
" } else if option client-architecture = 00:09 { #x86_64 uefi alternative id\n ",
|
||||
" filename \"xcat/xnba.efi\";\n",
|
||||
" } else if option client-architecture = 00:10 { #x86_64 uefi http boot\n ",
|
||||
" filename \"xcat/xnba.efi\";\n",
|
||||
],
|
||||
'the x86 lines give the network scripts to xNBA clients and xNBA to the rest'
|
||||
);
|
||||
done_testing();
|
||||
|
||||
@@ -1726,8 +1726,6 @@ foreach my $case (@invalid_mac_cases) {
|
||||
@{ $config->{Dhcp4}{'client-classes'} };
|
||||
is_deeply( \@unguarded, [], 'no class that names a boot file can match the proxyDHCP node' );
|
||||
|
||||
is( $by_name{'xcat-uefi-x64'}{'boot-file-name'}, 'xcat/xnba.efi',
|
||||
'the UEFI class still names xnba.efi for every other client' );
|
||||
ok( $by_name{'xcat-proxydhcp-win01-aabbccddee08'},
|
||||
'the proxyDHCP node keeps the class that tags its reply PXEClient' );
|
||||
unlike( $by_name{'xcat-proxydhcp-win01-aabbccddee08'}{test} // '', qr/xcat-localboot/,
|
||||
|
||||
Reference in New Issue
Block a user