From 4312fe8337ba4702bcf04e18cf91021799132e2d 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, 3 Sep 2026 18:01:39 -0300 Subject: [PATCH 1/2] refactor(debian): resolve the install kernel and initrd from a table The probe spelled out every candidate path twice inside one nested condition, once to test it and once to assign it, so adding an architecture meant adding another branch of the same shape. Move the candidates into a table keyed by architecture family and walk it in order. Same paths, same precedence, same failure behaviour: a media tree that matches nothing leaves the caller on the "install image not found" path as before. --- xCAT-server/lib/xcat/plugins/debian.pm | 85 ++++++++++++-------------- 1 file changed, 39 insertions(+), 46 deletions(-) diff --git a/xCAT-server/lib/xcat/plugins/debian.pm b/xCAT-server/lib/xcat/plugins/debian.pm index a54a8a3b5..fbae0f344 100644 --- a/xCAT-server/lib/xcat/plugins/debian.pm +++ b/xCAT-server/lib/xcat/plugins/debian.pm @@ -178,6 +178,43 @@ sub using_subiquity return 0; } +# The first readable pair wins, so netboot trees precede live images and a +# hardware-enablement kernel precedes the release one. +my %INSTALL_BOOT_FILES = ( + 'x86' => [ + [ 'install/hwe-netboot/ubuntu-installer/{darch}/linux', 'install/hwe-netboot/ubuntu-installer/{darch}/initrd.gz' ], + [ 'install/netboot/ubuntu-installer/{darch}/linux', 'install/netboot/ubuntu-installer/{darch}/initrd.gz' ], + [ 'install/netboot/vmlinuz', 'install/netboot/initrd.gz' ], + [ 'casper/hwe-vmlinuz', 'casper/hwe-initrd' ], + [ 'casper/vmlinuz', 'casper/initrd' ], + ], + 'ppc64' => [ + [ 'install/netboot/ubuntu-installer/{darch}/vmlinux', 'install/netboot/ubuntu-installer/{darch}/initrd.gz' ], + [ 'install/vmlinux', 'install/netboot/initrd.gz' ], + ], +); + +sub install_boot_files +{ + my ($arch, $darch, $pkgdir) = @_; + return unless defined($arch) && defined($pkgdir); + $darch = '' unless defined $darch; + + my $family = + $arch =~ /x86/i ? 'x86' + : $arch =~ /ppc64/i ? 'ppc64' + : undef; + return unless $family; + + foreach my $candidate (@{ $INSTALL_BOOT_FILES{$family} }) { + my ($kernel, $initrd) = @{$candidate}; + s/\{darch\}/$darch/g for ($kernel, $initrd); + ($kernel, $initrd) = ("$pkgdir/$kernel", "$pkgdir/$initrd"); + return ($kernel, $initrd) if -r $kernel and -r $initrd; + } + return; +} + sub is_ubuntu_live_media { my $media_path = shift; @@ -997,52 +1034,8 @@ sub mkinstall { my $initrdpath; my $maxmem; - if ( - ( - ($arch =~ /x86/ and - ( - (-r "$pkgdir/install/hwe-netboot/ubuntu-installer/$darch/linux" - and $kernpath = "$pkgdir/install/hwe-netboot/ubuntu-installer/$darch/linux" - and -r "$pkgdir/install/hwe-netboot/ubuntu-installer/$darch/initrd.gz" - and $initrdpath = "$pkgdir/install/hwe-netboot/ubuntu-installer/$darch/initrd.gz" - ) or - (-r "$pkgdir/install/netboot/ubuntu-installer/$darch/linux" - and $kernpath = "$pkgdir/install/netboot/ubuntu-installer/$darch/linux" - and -r "$pkgdir/install/netboot/ubuntu-installer/$darch/initrd.gz" - and $initrdpath = "$pkgdir/install/netboot/ubuntu-installer/$darch/initrd.gz" - ) or - (-r "$pkgdir/install/netboot/vmlinuz" - and $kernpath = "$pkgdir/install/netboot/vmlinuz" - and -r "$pkgdir/install/netboot/initrd.gz" - and $initrdpath = "$pkgdir/install/netboot/initrd.gz" - ) or - (-r "$pkgdir/casper/hwe-vmlinuz" - and $kernpath = "$pkgdir/casper/hwe-vmlinuz" - and -r "$pkgdir/casper/hwe-initrd" - and $initrdpath = "$pkgdir/casper/hwe-initrd" - ) or - (-r "$pkgdir/casper/vmlinuz" - and $kernpath = "$pkgdir/casper/vmlinuz" - and -r "$pkgdir/casper/initrd" - and $initrdpath = "$pkgdir/casper/initrd" - ) - ) - ) or ( - $arch =~ /ppc64/i and ( - (-r "$pkgdir/install/netboot/ubuntu-installer/$darch/vmlinux" - and $kernpath = "$pkgdir/install/netboot/ubuntu-installer/$darch/vmlinux" - and -r "$pkgdir/install/netboot/ubuntu-installer/$darch/initrd.gz" - and $initrdpath = "$pkgdir/install/netboot/ubuntu-installer/$darch/initrd.gz" - ) or - (-r "$pkgdir/install/vmlinux" - and $kernpath = "$pkgdir/install/vmlinux" - and -r "$pkgdir/install/netboot/initrd.gz" - and $initrdpath = "$pkgdir/install/netboot/initrd.gz" - ) - ) - ) - ) - ) { + ($kernpath, $initrdpath) = install_boot_files($arch, $darch, $pkgdir); + if ($kernpath) { #TODO: driver slipstream, targetted for network. # Copy the install resource to /tftpboot and check to only copy once From b97c6feb2d918463963e9097d3e891810783625f 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, 3 Sep 2026 18:11:28 -0300 Subject: [PATCH 2/2] test(xCAT-test): cover the install kernel and initrd resolution Build each Ubuntu media layout on disk and ask the resolver for its kernel and initrd: the netboot trees, the flat netboot layout, both casper images, and the POWER layouts where the kernel and the initrd sit in different directories. Pin the precedence the installer depends on, a netboot tree over a live image and a hardware-enablement kernel over the release one, and pin the three ways media resolve to nothing. --- xCAT-test/unit/debian_install_boot_files.t | 102 +++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 xCAT-test/unit/debian_install_boot_files.t diff --git a/xCAT-test/unit/debian_install_boot_files.t b/xCAT-test/unit/debian_install_boot_files.t new file mode 100644 index 000000000..b5783bc94 --- /dev/null +++ b/xCAT-test/unit/debian_install_boot_files.t @@ -0,0 +1,102 @@ +#!/usr/bin/env perl +use strict; +use warnings; + +use File::Path qw(make_path); +use File::Temp qw(tempdir); +use FindBin; +use Test::More; + +# The installer kernel and initrd sit in a different place on every Ubuntu media layout: +# netboot trees name them after the Debian architecture, live images keep them under +# casper, and a hardware-enablement kernel ships beside the release one. Build each layout +# on disk and ask the resolver, rather than reading the table that describes them. + +use lib "$FindBin::Bin/../../perl-xCAT"; +use lib "$FindBin::Bin/../../xCAT-server/lib/perl"; +my $plugin = "$FindBin::Bin/../../xCAT-server/lib/xcat/plugins/debian.pm"; +plan skip_all => 'debian.pm not found' unless -r $plugin; +eval { require $plugin; 1 } or plan skip_all => "could not load debian.pm: $@"; + +sub media { + my (@relative) = @_; + my $root = tempdir(CLEANUP => 1); + foreach my $path (@relative) { + my $full = "$root/$path"; + ($full =~ m{^(.*)/[^/]+$}) and make_path($1); + open(my $fh, '>', $full) or die "cannot create $full: $!"; + close($fh); + } + return $root; +} + +sub resolved { + my ($arch, $darch, $root) = @_; + my ($kernel, $initrd) = + xCAT_plugin::debian::install_boot_files($arch, $darch, $root); + return unless defined $kernel; + s/^\Q$root\E\/// for ($kernel, $initrd); + return "$kernel|$initrd"; +} + +# --- x86_64 layouts, in the order the media are probed --------------------- +is( + resolved('x86_64', 'amd64', + media('install/netboot/ubuntu-installer/amd64/linux', + 'install/netboot/ubuntu-installer/amd64/initrd.gz')), + 'install/netboot/ubuntu-installer/amd64/linux|install/netboot/ubuntu-installer/amd64/initrd.gz', + 'a netboot tree is named after the Debian architecture', +); +is( + resolved('x86_64', 'amd64', media('casper/vmlinuz', 'casper/initrd')), + 'casper/vmlinuz|casper/initrd', + 'a live image keeps its kernel under casper', +); +is( + resolved('x86_64', 'amd64', + media('casper/hwe-vmlinuz', 'casper/hwe-initrd', 'casper/vmlinuz', 'casper/initrd')), + 'casper/hwe-vmlinuz|casper/hwe-initrd', + 'the hardware-enablement kernel wins over the release kernel', +); +is( + resolved('x86_64', 'amd64', + media('install/hwe-netboot/ubuntu-installer/amd64/linux', + 'install/hwe-netboot/ubuntu-installer/amd64/initrd.gz', + 'casper/vmlinuz', 'casper/initrd')), + 'install/hwe-netboot/ubuntu-installer/amd64/linux|install/hwe-netboot/ubuntu-installer/amd64/initrd.gz', + 'a netboot tree wins over a live image on the same media', +); +is( + resolved('x86_64', 'amd64', media('install/netboot/vmlinuz', 'install/netboot/initrd.gz')), + 'install/netboot/vmlinuz|install/netboot/initrd.gz', + 'the flat netboot layout resolves', +); + +# --- ppc64 layouts --------------------------------------------------------- +is( + resolved('ppc64', 'ppc64el', + media('install/netboot/ubuntu-installer/ppc64el/vmlinux', + 'install/netboot/ubuntu-installer/ppc64el/initrd.gz')), + 'install/netboot/ubuntu-installer/ppc64el/vmlinux|install/netboot/ubuntu-installer/ppc64el/initrd.gz', + 'POWER keeps a vmlinux in its netboot tree', +); +is( + resolved('ppc64le', 'ppc64el', media('install/vmlinux', 'install/netboot/initrd.gz')), + 'install/vmlinux|install/netboot/initrd.gz', + 'the kernel and the initrd may sit in different directories', +); +is( + resolved('ppc64', 'ppc64el', media('casper/vmlinuz', 'casper/initrd')), + undef, + 'POWER does not accept the x86 live layout', +); + +# --- nothing to boot ------------------------------------------------------- +is(resolved('x86_64', 'amd64', media('casper/vmlinuz')), undef, + 'a kernel without its initrd is not a match'); +is(resolved('x86_64', 'amd64', media('README')), undef, + 'media with no installer resolves to nothing'); +is(resolved('s390x', 's390x', media('casper/vmlinuz', 'casper/initrd')), undef, + 'an architecture the table does not describe resolves to nothing'); + +done_testing();