From 6d32aa944c9f103925e1f0fb2c876ab1ac5cca68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Wed, 26 Aug 2026 17:25:00 -0300 Subject: [PATCH] test(dhcp): exercise ISC client boot policy --- xCAT-test/unit/dhcp_isc_client_arch.t | 91 +++++++++++++-------------- 1 file changed, 43 insertions(+), 48 deletions(-) diff --git a/xCAT-test/unit/dhcp_isc_client_arch.t b/xCAT-test/unit/dhcp_isc_client_arch.t index 8642528aa..b2bd38d00 100644 --- a/xCAT-test/unit/dhcp_isc_client_arch.t +++ b/xCAT-test/unit/dhcp_isc_client_arch.t @@ -3,62 +3,57 @@ use strict; use warnings; use FindBin; -use lib "$FindBin::Bin/../lib"; +use lib "$FindBin::Bin/../../perl-xCAT"; use Test::More; -use XCAT::Test::File qw(repo_path slurp_repo_file); +use xCAT::DHCP::BootPolicy; -# 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 $rendered = join '', @{ xCAT::DHCP::BootPolicy->isc_client_architecture_lines( + next_server => '192.0.2.10', + portsuffix => ':8080', + tftpdir => '/srv/tftp', + net => '192.0.2.0', + prefix => 24, + ) }; -my $plugin = repo_path('xCAT-server/lib/xcat/plugins/dhcp.pm'); - -plan skip_all => "$plugin not found" unless -r $plugin; - -my $source = slurp_repo_file('xCAT-server/lib/xcat/plugins/dhcp.pm'); - -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\\";/, +like( + $rendered, + qr/client-architecture = 00:0b \{ #aaarch64\n\s+filename "boot\/grub2\/grub2\.aarch64";/, + 'the ISC policy renders the aarch64 boot branch', +); +like( + $rendered, + qr/client-architecture = 00:1b \{ #riscv64 uefi\n\s+filename "boot\/grub2\/grub2\.riscv64";/, + 'the ISC policy renders the riscv64 TFTP boot branch', +); +like( + $rendered, + qr/client-architecture = 00:1c \{ #riscv64 uefi http boot\n\s+option vendor-class-identifier "HTTPClient";\n\s+filename "http:\/\/192\.0\.2\.10:8080\/srv\/tftp\/boot\/grub2\/grub2\.riscv64";/, + 'the ISC policy renders the riscv64 HTTP boot branch with the subnet URL', +); +like( + $rendered, + qr/option conf-file = "http:\/\/192\.0\.2\.10:8080\/tftpboot\/pxelinux\.cfg\/p\/192\.0\.2\.0_24";/, + 'the existing OPAL branch keeps its subnet URL', ); -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' ); -} - -# HTTP boot firmware (architecture id 28) needs the boot file as a URL and only -# accepts an offer that is tagged HTTPClient -ok( - $source =~ /client-architecture = 00:1c \{[^\n]*\n\s*push \@netent, "\s*option vendor-class-identifier \\"HTTPClient\\";[^\n]*\n\s*push \@netent, "\s*filename \\"http:\/\/\$tftp\$portsuffix\$tftpdir\/boot\/grub2\/grub2\.riscv64\\";/, - 'the ISC subnet block hands RISC-V HTTP boot clients the boot loader as a URL', -); -my $httpboot_pos = $-[0]; -SKIP: { - skip 'the riscv64 branches were not both found', 2 unless defined $pos{riscv64} && defined $httpboot_pos; - cmp_ok( $pos{riscv64}, '<', $httpboot_pos, 'the PXE branch keeps its place before the HTTP boot branch' ); - cmp_ok( $httpboot_pos, '<', $pos{yaboot}, 'the HTTP boot branch is rendered before the /yaboot fallback' ); -} - -my @riscv_ids = $source =~ /client-architecture = (00:1[9a-e])/g; +my @riscv_ids = $rendered =~ /client-architecture = (00:1[9a-e])/g; is_deeply( - [ sort @riscv_ids ], + \@riscv_ids, [ '00:1b', '00:1c' ], - 'only the RISC-V 64-bit UEFI architecture ids (27 and 28) are mapped', + 'only the RISC-V 64-bit UEFI architecture ids are mapped', ); +my $aarch64_pos = index($rendered, 'client-architecture = 00:0b'); +my $tftp_pos = index($rendered, 'client-architecture = 00:1b'); +my $http_pos = index($rendered, 'client-architecture = 00:1c'); +my $opal_pos = index($rendered, 'client-architecture = 00:0e'); +my $fallback_pos = index($rendered, 'substring(filename,0,1) = null'); + +cmp_ok($aarch64_pos, '<', $tftp_pos, 'riscv64 follows the aarch64 branch'); +cmp_ok($tftp_pos, '<', $http_pos, 'the TFTP branch precedes the HTTP branch'); +cmp_ok($http_pos, '<', $opal_pos, 'the HTTP branch precedes the OPAL branch'); +cmp_ok($http_pos, '<', $fallback_pos, 'the HTTP branch is reachable before the fallback'); +like($rendered, qr/filename "\/yaboot";\n\s*\}\n\z/, 'the policy ends with the existing yaboot fallback'); + done_testing();