From d64366a30a2d301a592745106a8dab5ab991aa9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Thu, 20 Aug 2026 16:32:27 -0300 Subject: [PATCH] test(xCAT-test): cover the riscv64 DHCP boot policy Assert the Kea xcat-riscv64 class (architecture 0x001b only, grub2 boot file, present with and without xNBA loaders) and pin the ISC subnet block so the riscv64 branch stays ahead of the /yaboot fallback that would otherwise shadow it. --- xCAT-test/unit/dhcp_boot_policy.t | 8 +++-- xCAT-test/unit/dhcp_isc_client_arch.t | 48 +++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 xCAT-test/unit/dhcp_isc_client_arch.t diff --git a/xCAT-test/unit/dhcp_boot_policy.t b/xCAT-test/unit/dhcp_boot_policy.t index b5369b066..d7021bc21 100644 --- a/xCAT-test/unit/dhcp_boot_policy.t +++ b/xCAT-test/unit/dhcp_boot_policy.t @@ -9,13 +9,13 @@ use Test::More; use xCAT::DHCP::BootPolicy; my $fallback_classes = xCAT::DHCP::BootPolicy->kea_client_classes(); -is( scalar @$fallback_classes, 4, 'Kea boot policy omits xNBA classes when xNBA loaders are unavailable' ); +is( scalar @$fallback_classes, 5, 'Kea boot policy omits xNBA classes when xNBA loaders are unavailable' ); my %fallback_by_name = map { $_->{name} => $_ } @$fallback_classes; is( $fallback_by_name{'xcat-bios'}{'boot-file-name'}, 'pxelinux.0', 'BIOS clients fall back to pxelinux.0 without xNBA loaders' ); 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, 5, 'Kea boot policy renders expected xNBA client classes' ); +is( scalar @$classes, 6, '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' ); @@ -27,6 +27,10 @@ like( $by_name{'xcat-uefi-x64'}{test}, qr/not \(\(option\[77\]\.exists/, 'generi 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' ); +is( $by_name{'xcat-riscv64'}{'boot-file-name'}, 'boot/grub2/grub2.riscv64', 'RISC-V 64-bit UEFI clients receive the riscv64 grub2 boot file' ); +is( $by_name{'xcat-riscv64'}{test}, 'option[93].hex == 0x001b', 'RISC-V 64-bit UEFI class matches IANA client architecture 27 only' ); +is( $fallback_by_name{'xcat-riscv64'}{'boot-file-name'}, 'boot/grub2/grub2.riscv64', 'riscv64 clients get grub2 even without xNBA loaders' ); +unlike( join( ' ', map { $_->{test} } @$classes ), qr/0x001[9ade]/, 'no class claims the RISC-V 32-bit or 128-bit architecture ids' ); my $xnba_classes = xCAT::DHCP::BootPolicy->kea_xnba_node_classes( xnba_efi => 1, diff --git a/xCAT-test/unit/dhcp_isc_client_arch.t b/xCAT-test/unit/dhcp_isc_client_arch.t new file mode 100644 index 000000000..d65e33a94 --- /dev/null +++ b/xCAT-test/unit/dhcp_isc_client_arch.t @@ -0,0 +1,48 @@ +#!/usr/bin/env perl +use strict; +use warnings; + +use FindBin; +use File::Spec; +use Test::More; + +# The ISC dhcpd subnet block in dhcp.pm maps DHCP option 93 (client system +# architecture) to a boot file. The block is rendered inside a large +# database-backed subroutine, so this pins the shipped source text: every +# architecture branch must appear before the catch-all that hands unknown +# clients /yaboot, otherwise the branch is unreachable. + +my $repo_root = File::Spec->catdir( $FindBin::Bin, '..', '..' ); +my $plugin = File::Spec->catfile( $repo_root, 'xCAT-server', 'lib', 'xcat', 'plugins', 'dhcp.pm' ); + +plan skip_all => "$plugin not found" unless -r $plugin; + +open( my $fh, '<', $plugin ) or die "Unable to read $plugin: $!"; +my $source = do { local $/; <$fh> }; +close($fh); + +my %branch = ( + aarch64 => qr/client-architecture = 00:0b \{[^\n]*\n\s*push \@netent, "\s*filename \\"boot\/grub2\/grub2\.aarch64\\";/, + riscv64 => qr/client-architecture = 00:1b \{[^\n]*\n\s*push \@netent, "\s*filename \\"boot\/grub2\/grub2\.riscv64\\";/, + opal => qr/client-architecture = 00:0e \{/, + yaboot => qr/substring\(filename,0,1\) = null \{[^\n]*\n\s*push \@netent, "\s*filename \\"\/yaboot\\";/, +); + +my %pos; +for my $name ( sort keys %branch ) { + ok( $source =~ $branch{$name}, "the ISC subnet block renders the $name branch" ) + or next; + $pos{$name} = $-[0]; +} + +SKIP: { + skip 'not every branch was found', 3 unless 4 == scalar keys %pos; + cmp_ok( $pos{aarch64}, '<', $pos{riscv64}, 'riscv64 follows the aarch64 branch' ); + cmp_ok( $pos{riscv64}, '<', $pos{opal}, 'riscv64 is rendered before the POWER OPAL branch' ); + cmp_ok( $pos{riscv64}, '<', $pos{yaboot}, 'riscv64 is rendered before the /yaboot fallback, so it is reachable' ); +} + +my @riscv_ids = $source =~ /client-architecture = (00:1[9a-e])/g; +is_deeply( \@riscv_ids, ['00:1b'], 'only the RISC-V 64-bit UEFI architecture id (27) is mapped' ); + +done_testing();