2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-09-11 03:56:25 +00:00
Files
xcat-core/xCAT-server/share/xcat/netboot/imgutils/imgutils.pm
T
Vinícius Ferrão 53709af9b3 feat(imgutils): give riscv64 Ubuntu netboot images their network drivers
The Ubuntu driver table had no riscv64 entry, so genimage was handed an
empty list and built an image carrying no network module. A node whose NIC
is not built into the kernel then has no interface to fetch its root
filesystem with. The architecture now gets the same drivers the enterprise
Linux table lists for it, plus the overlay module every Ubuntu image needs.

Signed-off-by: Vinícius Ferrão <2031761+viniciusferrao@users.noreply.github.com>
2026-09-08 17:02:04 -03:00

402 lines
13 KiB
Perl

#!/usr/bin/perl -w
# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
# #(C)IBM Corp
package imgutils;
use strict;
use warnings "all";
use File::Basename;
use File::Find;
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;
my @varsinline= $line =~ /\$\{?(\w+)\}?/g;
my @unresolvedvars;
foreach my $var(@varsinline){
if(exists $refvardict->{$var}){
$line=~ s/\$\{$var\}/$refvardict->{$var}/g;
$line=~ s/\$$var/$refvardict->{$var}/g;
}else{
push @unresolvedvars,$var;
}
}
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 resolver_library_paths {
my $arch = shift;
my $libdir = $arch =~ /x86_64|aarch64|riscv64/ ? 'lib64' : 'lib';
return ( "$libdir/libnss_dns.so.2", "$libdir/libresolv.so.2" );
}
sub get_profile_def_filename {
my $osver = shift;
my $profile = shift;
my $arch = shift;
my $tmp_base = shift;
my $base = realpath($tmp_base); #get the full path
if (!$base) { $base = $tmp_base; }
my $ext = shift;
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";
}
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";
}
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";
}
return "";
}
sub include_file
{
my $file = shift;
my $idir = shift;
my @text = ();
$file=varsubinline($file,\%ENV);
unless ($file =~ /^\//) {
$file = $idir . "/" . $file;
}
open(INCLUDE, $file) ||
return "#INCLUDEBAD:cannot open $file#";
while (<INCLUDE>) {
chomp($_);
s/\s+$//; #remove trailing spaces
next if /^\s*$/; #-- skip empty lines
next
if (/^\s*#/
&& !/^\s*#INCLUDE:[^#^\n]+#/
&& !/^\s*#NEW_INSTALL_LIST#/
&& !/^\s*#ENV:[^#^\n]+#/); #-- skip comments
push(@text, $_);
}
close(INCLUDE);
return join(',', @text);
}
sub get_package_names {
my $plist_file_list = shift;
my %pkgnames = ();
my @plist_file_names = split ',', $plist_file_list;
foreach my $plist_file_name (@plist_file_names) {
# this variable needs to be cleaned when loop the pkglist files
my @tmp_array = ();
if ($plist_file_name && -r $plist_file_name) {
my $pkgfile;
open($pkgfile, "<", "$plist_file_name");
while (<$pkgfile>) {
chomp;
s/\s+$//; #remove trailing white spaces
next if /^\s*$/; #-- skip empty lines
next
if (/^\s*#/
&& !/^\s*#INCLUDE:[^#^\n]+#/
&& !/^\s*#NEW_INSTALL_LIST#/
&& !/^\s*#ENV:[^#^\n]+#/); #-- skip comments
push(@tmp_array, $_);
}
close($pkgfile);
if (@tmp_array > 0) {
my $pkgtext = join(',', @tmp_array);
#handle the #INLCUDE# tag recursively
my $idir = dirname($plist_file_name);
my $doneincludes = 0;
while (not $doneincludes) {
$doneincludes = 1;
if ($pkgtext =~ /#INCLUDE:[^#^\n]+#/) {
$doneincludes = 0;
$pkgtext =~ s/#INCLUDE:([^#^\n]+)#/include_file($1,$idir)/eg;
}
}
#print "\n\npkgtext=$pkgtext\n\n";
my @tmp = split(',', $pkgtext);
my $pass = 1;
foreach (@tmp) {
my $idir;
if (/^--/) {
$idir = "POST_REMOVE"; #line starts with -- means the package should be removed after otherpkgs are installed
s/^--//;
} elsif (/^-/) {
$idir = "PRE_REMOVE"; #line starts with single - means the package should be removed before otherpkgs are installed
s/^-//;
} elsif (/^#NEW_INSTALL_LIST#/) {
$pass++;
next;
} elsif (/^#ENV:([^#^\n]+)#/) {
my $pa = $pkgnames{$pass}{ENVLIST};
my $env = $1;
if (exists($pkgnames{$pass}{ENVLIST})) {
push(@$pa, $env);
} else {
$pkgnames{$pass}{ENVLIST} = [$env];
}
next;
} elsif (/^#INCLUDEBAD:([^#^\n]+)#/) {
my $pa = $pkgnames{$pass}{INCLUDEBAD};
my $file = $1;
if (exists($pkgnames{$pass}{INCLUDEBAD})) {
push(@$pa, $file);
} else {
$pkgnames{$pass}{INCLUDEBAD} = [$file];
}
next;
} elsif (/^#/) {
# ignore all other comment lines
next;
} else {
$idir = dirname($_);
}
my $fn = basename($_);
if (exists($pkgnames{$pass}{$idir})) {
my $pa = $pkgnames{$pass}{$idir};
push(@$pa, $fn);
} else {
$pkgnames{$pass}{$idir} = [$fn];
}
}
}
}
}
return %pkgnames;
}
sub default_net_drivers {
my ( $family, $arch ) = @_;
my %drivers = (
rh => {
x86 => [qw(tg3 bnx2 bnx2x e1000 e1000e igb mlx_en mlx5_core virtio_net be2net)],
x86_64 => [qw(tg3 bnx2 bnx2x e1000 e1000e igb mlx_en mlx5_core virtio_net be2net)],
aarch64 => [qw(tg3 bnx2 bnx2x e1000e igb mlx_en mlx5_core virtio_net)],
riscv64 => [qw(e1000 e1000e igb ixgbe r8169 tg3 bnx2x mlx5_core virtio_net)],
ppc64 => [qw(e1000 e1000e igb ibmveth ehea)],
s390x => [qw(qdio ccwgroup)],
},
sles => {
x86 => [qw(tg3 bnx2 bnx2x e1000 e1000e virtio_net virtio_balloon igb mlx4_en mlx5_core be2net)],
x86_64 => [qw(tg3 bnx2 bnx2x e1000 e1000e virtio_net virtio_balloon igb mlx4_en mlx5_core be2net)],
ppc64 => [qw(tg3 e1000 e1000e igb ibmveth ehea be2net)],
s390x => [qw(qdio ccwgroup qeth qeth_l2 qeth_l3)],
},
ubuntu => {
x86 => [qw(tg3 bnx2 bnx2x e1000 e1000e igb mlx_en mlx5_core virtio_net overlay)],
x86_64 => [qw(tg3 bnx2 bnx2x e1000 e1000e igb mlx_en mlx5_core virtio_net overlay)],
ppc64el => [qw(tg3 bnx2 bnx2x e1000 e1000e igb ibmveth ehea mlx_en mlx4_en mlx5_core virtio_net overlay)],
ppc64 => [qw(e1000 e1000e igb ibmveth ehea)],
riscv64 => [qw(e1000 e1000e igb ixgbe r8169 tg3 bnx2x mlx5_core virtio_net overlay)],
s390x => [qw(qdio ccwgroup)],
},
);
return () if !exists($drivers{$family}) || !exists($drivers{$family}{$arch});
return @{ $drivers{$family}{$arch} };
}
sub _kernel_module_name {
my $path = shift;
my $name = basename($path);
$name =~ s/\.ko(?:\.(?:gz|xz|zst))?$//x;
$name =~ tr/-/_/;
return $name;
}
sub target_kernel_module_availability {
my ( $rootimg_dir, $kernelver, @modules ) = @_;
my %requested = map { _kernel_module_name($_) => 1 } @modules;
my %available = map { $_ => 0 } keys %requested;
my $module_root = "$rootimg_dir/lib/modules/$kernelver";
my $modules_dep = "$module_root/modules.dep";
if (open(my $dep_fh, '<', $modules_dep)) {
while (my $line = <$dep_fh>) {
my ($path) = split /:/x, $line, 2;
my $name = _kernel_module_name($path);
if ($requested{$name} && -f "$module_root/$path") {
$available{$name} = 1;
}
}
close($dep_fh);
}
my $modules_builtin = "$module_root/modules.builtin";
if (open(my $builtin_fh, '<', $modules_builtin)) {
while (my $path = <$builtin_fh>) {
chomp($path);
my $name = _kernel_module_name($path);
$available{$name} = 1 if $requested{$name};
}
close($builtin_fh);
}
if (-d $module_root && grep { !$available{$_} } keys %requested) {
find(
{
no_chdir => 1,
wanted => sub {
return if !-f $File::Find::name;
return if $File::Find::name !~ /\.ko(?:\.(?:gz|xz|zst))?$/x;
my $name = _kernel_module_name($File::Find::name);
$available{$name} = 1 if $requested{$name};
},
},
$module_root,
);
}
return %available;
}
sub resolve_mellanox_default_net_drivers {
my ( $rootimg_dir, $kernelver, $requested_drivers, @default_drivers ) = @_;
my @drivers;
my %seen;
my $has_mellanox_defaults = grep {
_kernel_module_name($_) =~ /^mlx(?:_en|4_en|5_core)$/x
} @default_drivers;
my $has_mellanox_drivers = $has_mellanox_defaults || grep {
_kernel_module_name($_) =~ /^mlx(?:_en|4_en|5_core)$/x
} @{$requested_drivers};
if (!$has_mellanox_drivers) {
return grep { !$seen{$_}++ } (@{$requested_drivers}, @default_drivers);
}
my %available = target_kernel_module_availability(
$rootimg_dir,
$kernelver,
qw(mlx_en mlx4_en mlx5_core mlx4_ib mlx5_ib ib_ipoib),
);
# Keep unavailable explicit requests, except for the historical mlx4 alias.
foreach my $driver (@{$requested_drivers}) {
my $name = _kernel_module_name($driver);
if ($name eq 'mlx_en'
&& !$available{'mlx_en'}
&& $available{'mlx4_en'})
{
$driver =~ s/mlx_en/mlx4_en/;
}
push @drivers, $driver;
}
foreach my $driver (@default_drivers) {
my $name = _kernel_module_name($driver);
if ($name eq 'mlx_en') {
if (!$available{'mlx_en'}) {
next if !$available{'mlx4_en'};
$driver =~ s/mlx_en/mlx4_en/;
}
} elsif ($name eq 'mlx4_en' || $name eq 'mlx5_core') {
next if !$available{$name};
}
push @drivers, $driver;
}
if ($has_mellanox_defaults) {
my @mellanox_ib_drivers = grep { $available{$_} } qw(mlx4_ib mlx5_ib);
push @drivers, map { "$_.ko" } @mellanox_ib_drivers;
if (@mellanox_ib_drivers && $available{'ib_ipoib'}) {
push @drivers, 'ib_ipoib.ko';
}
}
return grep { !$seen{$_}++ } @drivers;
}
1;