diff --git a/xCAT-server/share/xcat/netboot/imgutils/imgutils.pm b/xCAT-server/share/xcat/netboot/imgutils/imgutils.pm index 0907db83b..ea01a75ff 100644 --- a/xCAT-server/share/xcat/netboot/imgutils/imgutils.pm +++ b/xCAT-server/share/xcat/netboot/imgutils/imgutils.pm @@ -12,6 +12,42 @@ use File::Path; use Cwd qw(realpath); use xCAT::SvrUtils; +sub el_major_version { + my $version = shift; + + return if !defined($version); + + my ($os_family, $os_major) = xCAT::SvrUtils::parseosver($version); + return + if !defined($os_family) + || $os_family !~ /^(?:rhels?|rhelc|rhelhpc|centos(?:-stream)?|rocky|alma(?:linux)?|ol)$/; + + return $os_major if length($os_major); + return; +} + +sub rpm_installroot_command { + my ( $osver, $rootimg_dir, $non_interactive, $dnf_available ) = @_; + $non_interactive ||= ""; + $dnf_available = -x "/usr/bin/dnf" unless defined($dnf_available); + my $majorrel = el_major_version($osver); + my $pkgmgr = "yum"; + + # EL8 and newer are dnf-native. Keep yum as the fallback for legacy + # systems and minimal environments that still provide only yum. + if (defined($majorrel) && $majorrel > 7 && $dnf_available) { + $pkgmgr = "dnf"; + } + + my $cmd = "$pkgmgr $non_interactive -c /tmp/genimage.$$.yum.conf --installroot=$rootimg_dir/ --disablerepo=* "; + if (defined($majorrel) && $majorrel > 7) { + $cmd .= "--releasever=" . $majorrel . " "; + $cmd .= "--setopt=module_platform_id=platform:el" . $majorrel . " "; + } + + return $cmd; +} + sub varsubinline{ my $line=shift; my $refvardict=shift; diff --git a/xCAT-server/share/xcat/netboot/rh/genimage b/xCAT-server/share/xcat/netboot/rh/genimage index 4a41286ce..20424cd82 100755 --- a/xCAT-server/share/xcat/netboot/rh/genimage +++ b/xCAT-server/share/xcat/netboot/rh/genimage @@ -92,38 +92,6 @@ sub majversion { return $majorrel; } -sub el_major_version { - my $version = shift; - - if (defined($version) - && $version =~ /^(?:rhels?|centos|rocky|alma(?:linux)?|ol)\D*(\d+)/) - { - return $1; - } - - return; -} - -sub rpm_installroot_command { - my $non_interactive = shift || ""; - my $majorrel = el_major_version($osver); - my $pkgmgr = "yum"; - - # EL8 and newer are dnf-native. Keep yum as the fallback for legacy - # systems and minimal environments that still provide only yum. - if (defined($majorrel) && $majorrel > 7 && -x "/usr/bin/dnf") { - $pkgmgr = "dnf"; - } - - my $cmd = "$pkgmgr $non_interactive -c /tmp/genimage.$$.yum.conf --installroot=$rootimg_dir/ --disablerepo=* "; - if (defined($majorrel) && $majorrel > 7) { - $cmd .= "--releasever=" . $majorrel . " "; - $cmd .= "--setopt=module_platform_id=platform:el" . $majorrel . " "; - } - - return $cmd; -} - sub mount_chroot { my $rootimage_dir = shift; @@ -396,7 +364,9 @@ if($onlyinitrd){ my $non_interactive; if (!$prompt) { $non_interactive = "-y"; } - my $yumcmd = rpm_installroot_command($non_interactive); + my $yumcmd = imgutils::rpm_installroot_command( + $osver, $rootimg_dir, $non_interactive + ); foreach (0 .. $repnum) { $yumcmd .= "--enablerepo=$osver-$arch-$_ "; @@ -562,7 +532,9 @@ if($onlyinitrd){ } close($yumconfig); $index--; - my $yumcmd_base = rpm_installroot_command($non_interactive); + my $yumcmd_base = imgutils::rpm_installroot_command( + $osver, $rootimg_dir, $non_interactive + ); #yum/rpm/zypper has defect on calculating diskspace usage when installing rpm on a NFS mounted installroot if (isNFSdir("$rootimg_dir")) { diff --git a/xCAT-test/unit/rh_genimage_package_manager.t b/xCAT-test/unit/rh_genimage_package_manager.t index b419a3351..177668cf0 100644 --- a/xCAT-test/unit/rh_genimage_package_manager.t +++ b/xCAT-test/unit/rh_genimage_package_manager.t @@ -3,19 +3,82 @@ use strict; use warnings; use FindBin; -use lib "$FindBin::Bin/../lib"; +use lib "$FindBin::Bin/../../perl-xCAT"; +use lib "$FindBin::Bin/../../xCAT-server/share/xcat/netboot/imgutils"; +use lib "$FindBin::Bin/../../xCAT-server/lib/perl"; use Test::More; -use XCAT::Test::File qw(slurp_repo_file); +use imgutils; -my $genimage = slurp_repo_file('xCAT-server/share/xcat/netboot/rh/genimage'); +my @version_cases = ( + [ 'rhel8', 8, 'RHEL' ], + [ 'rhels8.10', 8, 'RHEL Server' ], + [ 'rhelc5', 5, 'RHEL Client' ], + [ 'rhelhpc7.2', 7, 'RHEL ComputeNode' ], + [ 'rhels7.5-alternate', 7, 'RHEL alternate-media suffix' ], + [ 'centos7.9', 7, 'CentOS Linux' ], + [ 'centos-stream9', 9, 'CentOS Stream' ], + [ 'rocky9.6', 9, 'Rocky Linux' ], + [ 'alma8.10', 8, 'AlmaLinux short name' ], + [ 'almalinux9.4', 9, 'AlmaLinux long name' ], + [ 'ol8.4.0', 8, 'Oracle Linux' ], + [ 'rhels10.0', 10, 'two-digit RHEL Server' ], + [ 'fedora42', undef, 'reachable unsupported Fedora' ], + [ 'SL7.9', undef, 'reachable unsupported Scientific Linux' ], + [ 'RHEL9', undef, 'case-mismatched distribution' ], + [ 'rhels', undef, 'missing major version' ], + [ '', undef, 'empty version' ], + [ undef, undef, 'undefined version' ], +); -like( $genimage, qr/sub el_major_version/, 'RH genimage has an EL major-version helper' ); -like( $genimage, qr/sub rpm_installroot_command/, 'RH genimage builds RPM installroot commands through one helper' ); -like( $genimage, qr/-x "\/usr\/bin\/dnf".*?\$pkgmgr = "dnf"/s, 'EL8+ genimage prefers dnf when it is available' ); -like( $genimage, qr/--releasever=.*?--setopt=module_platform_id=platform:el/s, 'EL8+ installroot commands keep releasever and module platform options' ); -like( $genimage, qr/my \$yumcmd = rpm_installroot_command\(\$non_interactive\);/, 'base package pass uses shared installroot command builder' ); -like( $genimage, qr/my \$yumcmd_base = rpm_installroot_command\(\$non_interactive\);/, 'otherpkgs pass uses shared installroot command builder' ); -unlike( $genimage, qr/my \$yumcmd(?:_base)? = "yum /, 'RH genimage no longer hardcodes yum in installroot command builders' ); +for my $case (@version_cases) { + my ( $version, $expected, $description ) = @{$case}; + is( + imgutils::el_major_version($version), + $expected, + "$description resolves to the expected EL major version" + ); +} + +sub installroot_command_for { + my ( $version, $dnf_available, $non_interactive ) = @_; + + return imgutils::rpm_installroot_command( + $version, '/var/tmp/root-image', $non_interactive, $dnf_available + ); +} + +my $el7_command = installroot_command_for( 'rhels7.9', 1, '-y' ); +like( $el7_command, qr/^yum -y /, 'EL7 keeps yum as its package manager' ); +unlike( $el7_command, qr/--releasever=/, 'EL7 omits releasever' ); +unlike( $el7_command, qr/--setopt=module_platform_id=/, 'EL7 omits module platform configuration' ); + +my $el8_command = installroot_command_for( 'rhels8.10', 1, '-y' ); +like( $el8_command, qr/^dnf -y /, 'EL8 uses dnf when it is available' ); +like( $el8_command, qr/--releasever=8 /, 'EL8 sets releasever at the package-manager boundary' ); +like( $el8_command, qr/--setopt=module_platform_id=platform:el8 /, 'EL8 sets its module platform' ); + +my $el9_dnf_command = installroot_command_for( 'centos-stream9', 1, '-y' ); +like( $el9_dnf_command, qr/^dnf -y /, 'EL9 uses dnf when it is available' ); +like( $el9_dnf_command, qr/--installroot=\/var\/tmp\/root-image\//, 'EL9 preserves the requested installroot' ); +like( $el9_dnf_command, qr/--releasever=9 /, 'EL9 sets releasever from the parsed major version' ); +like( $el9_dnf_command, qr/--setopt=module_platform_id=platform:el9 /, 'EL9 sets its module platform' ); + +my $el9_yum_command = installroot_command_for( 'centos-stream9', 0, '-y' ); +like( $el9_yum_command, qr/^yum -y /, 'EL9 falls back to yum when dnf is unavailable' ); +like( $el9_yum_command, qr/--releasever=9 /, 'the EL9 yum fallback keeps releasever' ); +like( $el9_yum_command, qr/--setopt=module_platform_id=platform:el9 /, 'the EL9 yum fallback keeps its module platform' ); + +my $el10_command = installroot_command_for( 'rhels10.0', 1, '-y' ); +like( $el10_command, qr/--releasever=10 /, 'EL10 preserves its two-digit releasever' ); +like( $el10_command, qr/--setopt=module_platform_id=platform:el10 /, 'EL10 preserves its two-digit module platform' ); + +my $unsupported_command = installroot_command_for( 'fedora42', 1, '-y' ); +like( $unsupported_command, qr/^yum -y /, 'unsupported distributions keep the yum fallback' ); +unlike( $unsupported_command, qr/--releasever=/, 'unsupported distributions omit releasever' ); +unlike( $unsupported_command, qr/--setopt=module_platform_id=/, 'unsupported distributions omit module platform configuration' ); + +my $interactive_command = installroot_command_for( 'rhels8.10', 1, undef ); +like( $interactive_command, qr/^dnf -c /, 'interactive mode omits the non-interactive package-manager option' ); done_testing();