From 119b19ce14a3ccf10171a710958eefee416cd16e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Tue, 5 May 2026 02:27:35 -0300 Subject: [PATCH] fix: handle dotted OS versions in profile asset lookup --- xCAT-server/lib/perl/xCAT/SvrUtils.pm | 77 +++++-- xCAT-server/lib/xcat/plugins/anaconda.pm | 39 ++-- .../share/xcat/netboot/imgutils/imgutils.pm | 46 ++-- xCAT-test/unit/anaconda_oracle_distname.t | 50 ++++ xCAT-test/unit/profile_asset_dotted_osvers.t | 216 ++++++++++++++++++ 5 files changed, 369 insertions(+), 59 deletions(-) create mode 100644 xCAT-test/unit/anaconda_oracle_distname.t create mode 100644 xCAT-test/unit/profile_asset_dotted_osvers.t diff --git a/xCAT-server/lib/perl/xCAT/SvrUtils.pm b/xCAT-server/lib/perl/xCAT/SvrUtils.pm index 3bb5aa32b..5bebb4ccc 100644 --- a/xCAT-server/lib/perl/xCAT/SvrUtils.pm +++ b/xCAT-server/lib/perl/xCAT/SvrUtils.pm @@ -463,22 +463,22 @@ sub getsynclistfile() none Error: Example: - xCAT::SvrUtils->get_os_search_list("ubuntu18.04.2"); - Will returns - # ubuntu18.04.2 - # ubuntu18.04.1 - # ubuntu18.04.0 - # ubuntu18.04 - # ubuntu18.4 - # ubuntu18.03 - # ubuntu18.3 - # ubuntu18.02 - # ubuntu18.2 - # ubuntu18.01 - # ubuntu18.1 - # ubuntu18.00 - # ubuntu18.0 - # ubuntu18 + xCAT::SvrUtils->get_os_search_list("ubuntu24.04.1"); + Will return + # ubuntu24.04.1 + # ubuntu24.04.0 + # ubuntu24.04 + + xCAT::SvrUtils->get_os_search_list("rocky9.6"); + Will return + # rocky9.6 + # rocky9.5 + # rocky9.4 + # rocky9.3 + # rocky9.2 + # rocky9.1 + # rocky9.0 + # rocky9 Comments: none @@ -491,8 +491,12 @@ sub get_os_search_list { my @word = split(/\./, $baseos); my @list = (); - while ($word[-1] =~ /^[0-9]+$/) { + while (@word && $word[-1] =~ /^[0-9]+$/) { my $last = pop(@word); + if ($last =~ /^0[0-9]+$/) { + push(@list, join('.', @word, $last)); + return @list; + } while ($last >= 0) { push(@list, join('.', @word, $last)); if ($last =~ /^0[0-9]/) { @@ -515,17 +519,42 @@ sub get_os_search_list { sub _profile_file_matches { my ($filename, $osver, $shortosver, $genos, $arch) = @_; - # osver and arch specific, for example compute.sle15.x86_64.pkglist - if ($filename =~ /[^\.]*\.([^\.]*)\.([^\.]*)\./) { - return (($1 eq $osver || $1 eq $shortosver || $1 eq $genos) && $2 eq $arch); + my $stem = $filename; + foreach my $extension (qw(otherpkgs.pkglist imgcapture.exlist pkglist tmpl exlist postinstall synclist repolist)) { + if ($stem =~ s/\.\Q$extension\E$//) { + last; + } + } + $stem =~ s/\.[^.]+$// if $stem eq $filename; + + my @parts = split(/\./, $stem); + + # Not OS or architecture specific, for example compute.pkglist. + if (scalar(@parts) <= 1) { + return 1; } - # osver or arch specific, for example compute.sle15.pkglist - if ($filename =~ /[^\.]*\.([^\.]*)\./) { - return ($1 eq $osver || $1 eq $shortosver || $1 eq $genos || $1 eq $arch); + shift(@parts); # profile + + my $suffix = join('.', @parts); + if ($suffix eq $arch) { + return 1; } - return 1; + my %valid_os_suffix = map { $_ => 1 } xCAT::SvrUtils::get_os_search_list($osver); + $valid_os_suffix{$genos} = 1 if $genos; + + if ($valid_os_suffix{$suffix}) { + return 1; + } + + # OS and architecture specific, for example compute.ubuntu24.04.x86_64.pkglist. + if (scalar(@parts) > 1 && $parts[-1] eq $arch) { + pop(@parts); + return $valid_os_suffix{join('.', @parts)} ? 1 : 0; + } + + return 0; } sub get_file_name { diff --git a/xCAT-server/lib/xcat/plugins/anaconda.pm b/xCAT-server/lib/xcat/plugins/anaconda.pm index e6ba82ebe..79988bdf2 100644 --- a/xCAT-server/lib/xcat/plugins/anaconda.pm +++ b/xCAT-server/lib/xcat/plugins/anaconda.pm @@ -36,6 +36,25 @@ my $httpport = "80"; my $useflowcontrol = "0"; +sub _oracle_linux_distname +{ + my $desc = shift; + + return unless defined($desc); + + my $version; + if ($desc =~ /Oracle Linux\s+((?:\d+\.)*\d+)/) { + $version = $1; + } elsif ($desc =~ /OL-((?:\d+\.)*\d+)/) { + $version = $1; + } else { + return; + } + + $version =~ s/^(\d+\.\d+)\.0$/$1/; + return "ol$version"; +} + sub handled_commands { @@ -2123,25 +2142,9 @@ sub copycd } close($dinfo); } - elsif ($desc =~ /Oracle Linux/) + elsif (my $ol_distname = _oracle_linux_distname($desc)) { - # - # Attempt to auto-detect for OL8 OS, the last element - # (accessed with [-1] array index) has typically been the version - # ex: "Oracle Linux 8.3.0" - # - my @ol_version = split / /, $desc; - $distname = "ol" . $ol_version[-1]; - } - elsif ($desc =~ /OL-/) - { - # - # Attempt to auto-detect for OL7 OS, the first element - # (after "-") has typically been the version - # ex: OL-7.9 Server.x86_64 - # - my @ol_version = split /[- ]/, $desc; - $distname = "ol" . $ol_version[1]; + $distname = $ol_distname; } elsif ($desc =~ /Fedora/) { diff --git a/xCAT-server/share/xcat/netboot/imgutils/imgutils.pm b/xCAT-server/share/xcat/netboot/imgutils/imgutils.pm index e28626817..903baf453 100644 --- a/xCAT-server/share/xcat/netboot/imgutils/imgutils.pm +++ b/xCAT-server/share/xcat/netboot/imgutils/imgutils.pm @@ -9,6 +9,7 @@ use warnings "all"; use File::Basename; use File::Path; use Cwd qw(realpath); +use xCAT::SvrUtils; sub varsubinline{ my $line=shift; @@ -28,6 +29,19 @@ sub varsubinline{ return $line; } +sub _profile_lookup_osbase_list { + my $osver = shift; + + # OS version on s390x can contain 'sp', e.g. sles11sp1 + # If OS version contains 'sp', get the index of 'sp' instead of '.' + if ($osver =~ /sles/ && $osver =~ /sp/) { + my $dotpos = rindex($osver, "sp"); + return (substr($osver, 0, $dotpos)); + } + + return grep { $_ ne $osver } xCAT::SvrUtils::get_os_search_list($osver); +} + sub get_profile_def_filename { my $osver = shift; my $profile = shift; @@ -39,34 +53,32 @@ sub get_profile_def_filename { my $ext = shift; - my $dotpos; - - # OS version on s390x can contain 'sp', e.g. sles11sp1 - # If OS version contains 'sp', get the index of 'sp' instead of '.' - if ($osver =~ /sles/ && $osver =~ /sp/) { - $dotpos = rindex($osver, "sp"); - } else { - $dotpos = rindex($osver, "."); - } - - my $osbase = substr($osver, 0, $dotpos); + my @osbase = _profile_lookup_osbase_list($osver); my $fallbackos; if ($osver =~ /^leap15/) { $fallbackos = "sle15"; } if (-r "$base/$profile.$osver.$arch.$ext") { return "$base/$profile.$osver.$arch.$ext"; - } elsif (-r "$base/$profile.$osbase.$arch.$ext") { - return "$base/$profile.$osbase.$arch.$ext"; - } elsif ($fallbackos && -r "$base/$profile.$fallbackos.$arch.$ext") { + } + foreach my $osbase (@osbase) { + if (-r "$base/$profile.$osbase.$arch.$ext") { + return "$base/$profile.$osbase.$arch.$ext"; + } + } + if ($fallbackos && -r "$base/$profile.$fallbackos.$arch.$ext") { return "$base/$profile.$fallbackos.$arch.$ext"; } elsif (-r "$base/$profile.$arch.$ext") { return "$base/$profile.$arch.$ext"; } elsif (-r "$base/$profile.$osver.$ext") { return "$base/$profile.$osver.$ext"; - } elsif (-r "$base/$profile.$osbase.$ext") { - return "$base/$profile.$osbase.$ext"; - } elsif ($fallbackos && -r "$base/$profile.$fallbackos.$ext") { + } + foreach my $osbase (@osbase) { + if (-r "$base/$profile.$osbase.$ext") { + return "$base/$profile.$osbase.$ext"; + } + } + if ($fallbackos && -r "$base/$profile.$fallbackos.$ext") { return "$base/$profile.$fallbackos.$ext"; } elsif (-r "$base/$profile.$ext") { return "$base/$profile.$ext"; diff --git a/xCAT-test/unit/anaconda_oracle_distname.t b/xCAT-test/unit/anaconda_oracle_distname.t new file mode 100644 index 000000000..6a4ecde1c --- /dev/null +++ b/xCAT-test/unit/anaconda_oracle_distname.t @@ -0,0 +1,50 @@ +use strict; +use warnings; + +use File::Spec; +use FindBin; +use Test::More; + +my $repo_root = File::Spec->catdir($FindBin::Bin, '..', '..'); +$ENV{XCATROOT} = File::Spec->catdir($repo_root, 'xCAT-server'); + +use lib "$FindBin::Bin/../../perl-xCAT"; +use lib "$FindBin::Bin/../../xCAT-server/lib/perl"; + +my $anaconda = File::Spec->catfile( + $repo_root, + 'xCAT-server/lib/xcat/plugins/anaconda.pm' +); +do $anaconda or die $@ || "Unable to load $anaconda: $!"; + +is( + xCAT_plugin::anaconda::_oracle_linux_distname('Oracle Linux 8.4.0'), + 'ol8.4', + 'Oracle Linux media version with trailing update zero maps to major.minor distname' +); + +is( + xCAT_plugin::anaconda::_oracle_linux_distname('Oracle Linux 9.3'), + 'ol9.3', + 'Oracle Linux media version without trailing update zero keeps major.minor distname' +); + +is( + xCAT_plugin::anaconda::_oracle_linux_distname('Oracle Linux 9.0'), + 'ol9.0', + 'Oracle Linux zero minor release keeps major.minor distname' +); + +is( + xCAT_plugin::anaconda::_oracle_linux_distname('OL-7.9 Server.x86_64'), + 'ol7.9', + 'legacy OL media description maps to major.minor distname' +); + +is( + xCAT_plugin::anaconda::_oracle_linux_distname('Rocky Linux 9.6'), + undef, + 'Oracle Linux distname helper ignores other distributions' +); + +done_testing(); diff --git a/xCAT-test/unit/profile_asset_dotted_osvers.t b/xCAT-test/unit/profile_asset_dotted_osvers.t new file mode 100644 index 000000000..b79a9c320 --- /dev/null +++ b/xCAT-test/unit/profile_asset_dotted_osvers.t @@ -0,0 +1,216 @@ +use strict; +use warnings; + +use Cwd qw(realpath); +use File::Spec; +use File::Temp qw(tempdir); +use FindBin; +use Test::More; + +use lib "$FindBin::Bin/../../perl-xCAT"; +use lib "$FindBin::Bin/../../xCAT-server/lib/perl"; +use lib "$FindBin::Bin/../../xCAT-server/share/xcat/netboot/imgutils"; + +use xCAT::SvrUtils; +use imgutils; + +my @ubuntu_2404_search = xCAT::SvrUtils::get_os_search_list('ubuntu24.04'); +is_deeply( + \@ubuntu_2404_search, + ['ubuntu24.04'], + 'leading-zero minor release lookup does not fall back to the major-only suffix' +); + +my @ubuntu_point_search = xCAT::SvrUtils::get_os_search_list('ubuntu24.04.1'); +is_deeply( + \@ubuntu_point_search, + [qw(ubuntu24.04.1 ubuntu24.04.0 ubuntu24.04)], + 'point release lookup can fall back within the same leading-zero minor release' +); + +my @rocky_search = xCAT::SvrUtils::get_os_search_list('rocky9.6'); +is_deeply( + \@rocky_search, + [qw(rocky9.6 rocky9.5 rocky9.4 rocky9.3 rocky9.2 rocky9.1 rocky9.0 rocky9)], + 'non-leading-zero minor release lookup keeps existing major-version fallback behavior' +); + +my @ol_search = xCAT::SvrUtils::get_os_search_list('ol8.4.0'); +is_deeply( + \@ol_search, + [qw(ol8.4.0 ol8.4 ol8.3 ol8.2 ol8.1 ol8.0 ol8)], + 'trailing zero update release lookup can fall back to the major.minor release' +); + +ok( + xCAT::SvrUtils::_profile_file_matches( + 'compute.ubuntu24.04.x86_64.pkglist', + 'ubuntu24.04', + 'ubuntu24', + 'subiquity', + 'x86_64' + ), + 'profile discovery treats ubuntu24.04 as the OS suffix and x86_64 as the arch suffix' +); + +ok( + xCAT::SvrUtils::_profile_file_matches( + 'compute.rocky9.x86_64.pkglist', + 'rocky9.6', + 'rocky9', + 'rocky9', + 'x86_64' + ), + 'profile discovery keeps major-version fallback assets eligible for non-leading-zero minor releases' +); + +ok( + xCAT::SvrUtils::_profile_file_matches( + 'compute.ol8.4.x86_64.pkglist', + 'ol8.4.0', + 'ol8', + 'ol8', + 'x86_64' + ), + 'profile discovery lets update releases fall back to major.minor assets' +); + +ok( + !xCAT::SvrUtils::_profile_file_matches( + 'compute.ubuntu24.x86_64.pkglist', + 'ubuntu24.04', + 'ubuntu24', + 'subiquity', + 'x86_64' + ), + 'profile discovery does not treat ubuntu24 as equivalent to ubuntu24.04' +); + +ok( + xCAT::SvrUtils::_profile_file_matches( + 'compute.ubuntu24.04.pkglist', + 'ubuntu24.04', + 'ubuntu24', + 'subiquity', + 'x86_64' + ), + 'profile discovery accepts dotted Ubuntu OS-only suffixes' +); + +ok( + xCAT::SvrUtils::_profile_file_matches( + 'compute.ubuntu24.04.x86_64.otherpkgs.pkglist', + 'ubuntu24.04', + 'ubuntu24', + 'subiquity', + 'x86_64' + ), + 'profile discovery handles compound package-list extensions after dotted Ubuntu suffixes' +); + +ok( + xCAT::SvrUtils::_profile_file_matches( + 'compute.otherpkgs.pkglist', + 'ubuntu24.04', + 'ubuntu24', + 'subiquity', + 'x86_64' + ), + 'profile discovery keeps profile-only compound package-list assets eligible' +); + +my $svrutils_dir = tempdir(CLEANUP => 1); +_write_file(File::Spec->catfile($svrutils_dir, 'compute.pkglist'), "default\n"); +_write_file(File::Spec->catfile($svrutils_dir, 'compute.ubuntu24.x86_64.pkglist'), "wrong\n"); + +is( + xCAT::SvrUtils::get_pkglist_file_name($svrutils_dir, 'compute', 'ubuntu24.04', 'x86_64', 'subiquity'), + File::Spec->catfile($svrutils_dir, 'compute.pkglist'), + 'SvrUtils lookup does not fall back from ubuntu24.04 to ubuntu24' +); + +_write_file(File::Spec->catfile($svrutils_dir, 'compute.ubuntu24.04.x86_64.pkglist'), "exact\n"); +is( + xCAT::SvrUtils::get_pkglist_file_name($svrutils_dir, 'compute', 'ubuntu24.04', 'x86_64', 'subiquity'), + File::Spec->catfile($svrutils_dir, 'compute.ubuntu24.04.x86_64.pkglist'), + 'SvrUtils lookup selects exact dotted Ubuntu arch-specific pkglist' +); + +unlink File::Spec->catfile($svrutils_dir, 'compute.ubuntu24.04.x86_64.pkglist'); +_write_file(File::Spec->catfile($svrutils_dir, 'compute.ubuntu24.04.pkglist'), "release\n"); +is( + xCAT::SvrUtils::get_pkglist_file_name($svrutils_dir, 'compute', 'ubuntu24.04.1', 'x86_64', 'subiquity'), + File::Spec->catfile($svrutils_dir, 'compute.ubuntu24.04.pkglist'), + 'SvrUtils lookup allows Ubuntu point releases to fall back to the same major.minor release' +); + +my $svrutils_rpm_dir = tempdir(CLEANUP => 1); +_write_file(File::Spec->catfile($svrutils_rpm_dir, 'compute.pkglist'), "default\n"); +_write_file(File::Spec->catfile($svrutils_rpm_dir, 'compute.rocky9.x86_64.pkglist'), "major\n"); + +is( + xCAT::SvrUtils::get_pkglist_file_name($svrutils_rpm_dir, 'compute', 'rocky9.6', 'x86_64', 'rocky9'), + File::Spec->catfile($svrutils_rpm_dir, 'compute.rocky9.x86_64.pkglist'), + 'SvrUtils lookup keeps major-version fallback for non-leading-zero minor releases' +); + +_write_file(File::Spec->catfile($svrutils_rpm_dir, 'compute.ol8.4.x86_64.pkglist'), "minor\n"); +is( + xCAT::SvrUtils::get_pkglist_file_name($svrutils_rpm_dir, 'compute', 'ol8.4.0', 'x86_64', 'ol8'), + File::Spec->catfile($svrutils_rpm_dir, 'compute.ol8.4.x86_64.pkglist'), + 'SvrUtils lookup lets update releases fall back to major.minor assets before major-only assets' +); + +my $imgutils_dir = tempdir(CLEANUP => 1); +my $real_imgutils_dir = realpath($imgutils_dir) || $imgutils_dir; +_write_file(File::Spec->catfile($imgutils_dir, 'compute.pkglist'), "default\n"); +_write_file(File::Spec->catfile($imgutils_dir, 'compute.ubuntu24.x86_64.pkglist'), "wrong\n"); + +is( + imgutils::get_profile_def_filename('ubuntu24.04', 'compute', 'x86_64', $imgutils_dir, 'pkglist'), + File::Spec->catfile($real_imgutils_dir, 'compute.pkglist'), + 'imgutils lookup does not fall back from ubuntu24.04 to ubuntu24' +); + +_write_file(File::Spec->catfile($imgutils_dir, 'compute.ubuntu24.04.x86_64.pkglist'), "exact\n"); +is( + imgutils::get_profile_def_filename('ubuntu24.04', 'compute', 'x86_64', $imgutils_dir, 'pkglist'), + File::Spec->catfile($real_imgutils_dir, 'compute.ubuntu24.04.x86_64.pkglist'), + 'imgutils lookup selects exact dotted Ubuntu arch-specific pkglist' +); + +is( + imgutils::get_profile_def_filename('ubuntu24.04.1', 'compute', 'x86_64', $imgutils_dir, 'pkglist'), + File::Spec->catfile($real_imgutils_dir, 'compute.ubuntu24.04.x86_64.pkglist'), + 'imgutils lookup allows Ubuntu point releases to fall back to the same major.minor release' +); + +my $imgutils_rpm_dir = tempdir(CLEANUP => 1); +my $real_imgutils_rpm_dir = realpath($imgutils_rpm_dir) || $imgutils_rpm_dir; +_write_file(File::Spec->catfile($imgutils_rpm_dir, 'compute.pkglist'), "default\n"); +_write_file(File::Spec->catfile($imgutils_rpm_dir, 'compute.rocky9.x86_64.pkglist'), "major\n"); + +is( + imgutils::get_profile_def_filename('rocky9.6', 'compute', 'x86_64', $imgutils_rpm_dir, 'pkglist'), + File::Spec->catfile($real_imgutils_rpm_dir, 'compute.rocky9.x86_64.pkglist'), + 'imgutils lookup keeps major-version fallback for non-leading-zero minor releases' +); + +_write_file(File::Spec->catfile($imgutils_rpm_dir, 'compute.ol8.4.x86_64.pkglist'), "minor\n"); +is( + imgutils::get_profile_def_filename('ol8.4.0', 'compute', 'x86_64', $imgutils_rpm_dir, 'pkglist'), + File::Spec->catfile($real_imgutils_rpm_dir, 'compute.ol8.4.x86_64.pkglist'), + 'imgutils lookup lets update releases fall back to major.minor assets before major-only assets' +); + +done_testing(); + +sub _write_file { + my ($path, $content) = @_; + + open(my $fh, '>', $path) or die "Cannot write $path: $!"; + print {$fh} $content; + close($fh); + + return; +}