2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-09-09 14:36:43 +00:00
Files
xcat-core/xCAT-test/unit/mellanox_netdrivers.t
T
2026-08-28 14:32:48 -03:00

435 lines
13 KiB
Perl

#!/usr/bin/env perl
use strict;
use warnings;
use Carp qw(croak);
use File::Basename qw(dirname);
use File::Path qw(make_path);
use File::Slurper qw(read_text write_text);
use File::Temp qw(tempdir);
use FindBin;
use lib "$FindBin::Bin/../lib";
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 Test::More;
use XCAT::Test::File qw(repo_path);
use imgutils;
my @cases = (
[ 'rh', 'x86', [qw(tg3 bnx2 bnx2x e1000 e1000e igb mlx_en mlx5_core virtio_net be2net)] ],
[ 'rh', 'x86_64', [qw(tg3 bnx2 bnx2x e1000 e1000e igb mlx_en mlx5_core virtio_net be2net)] ],
[ 'rh', 'aarch64', [qw(tg3 bnx2 bnx2x e1000e igb mlx_en mlx5_core virtio_net)] ],
[ 'rh', 'ppc64', [qw(e1000 e1000e igb ibmveth ehea)] ],
[ 'rh', 's390x', [qw(qdio ccwgroup)] ],
[ 'sles', 'x86', [qw(tg3 bnx2 bnx2x e1000 e1000e virtio_net virtio_balloon igb mlx4_en mlx5_core be2net)] ],
[ 'sles', 'x86_64', [qw(tg3 bnx2 bnx2x e1000 e1000e virtio_net virtio_balloon igb mlx4_en mlx5_core be2net)] ],
[ 'sles', 'ppc64', [qw(tg3 e1000 e1000e igb ibmveth ehea be2net)] ],
[ 'sles', '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)] ],
[ 'ubuntu', 'x86_64', [qw(tg3 bnx2 bnx2x e1000 e1000e igb mlx_en mlx5_core virtio_net overlay)] ],
[ 'ubuntu', 'ppc64el', [qw(tg3 bnx2 bnx2x e1000 e1000e igb ibmveth ehea mlx_en mlx4_en mlx5_core virtio_net overlay)] ],
[ 'ubuntu', 'ppc64', [qw(e1000 e1000e igb ibmveth ehea)] ],
[ 'ubuntu', 's390x', [qw(qdio ccwgroup)] ],
);
foreach my $family (qw(rh sles ubuntu)) {
my $source = read_text(repo_path(
"xCAT-server/share/xcat/netboot/$family/genimage",
));
my $load_dd = index($source, 'my @dd_drivers = &load_dd();');
my $resolve = index(
$source,
'@ndrivers = imgutils::resolve_mellanox_default_net_drivers(',
);
ok(
$load_dd >= 0 && $resolve > $load_dd,
"$family resolves Mellanox defaults after installing driver updates",
);
}
foreach my $case (@cases) {
my ( $family, $arch, $expected ) = @{$case};
is_deeply(
[ imgutils::default_net_drivers( $family, $arch ) ],
$expected,
"$family $arch uses the expected default network drivers",
);
}
is_deeply(
[ imgutils::default_net_drivers( 'unknown', 'x86_64' ) ],
[],
'an unknown image family has no default drivers',
);
sub create_target_kernel {
my (%args) = @_;
my $root = tempdir( CLEANUP => 1 );
my $kernelver = 'test-kernel';
my $module_root = "$root/lib/modules/$kernelver";
make_path($module_root);
foreach my $path (@{ $args{'files'} || [] }) {
my $full_path = "$module_root/$path";
make_path(dirname($full_path));
write_text($full_path, 'module');
}
write_text(
"$module_root/modules.dep",
join('', map { "$_:\n" } @{ $args{'dependencies'} || [] }),
);
write_text(
"$module_root/modules.builtin",
join('', map { "$_\n" } @{ $args{'builtins'} || [] }),
);
return ( $root, $kernelver );
}
{
my $mlx5 = 'kernel/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.ko.xz';
my ( $root, $kernelver ) = create_target_kernel(
files => [$mlx5],
dependencies => [$mlx5],
);
is_deeply(
[ imgutils::resolve_mellanox_default_net_drivers(
$root,
$kernelver,
[qw(e1000.ko)],
qw(tg3.ko mlx_en.ko mlx5_core.ko),
) ],
[qw(e1000.ko tg3.ko mlx5_core.ko)],
'an EL10-like target uses mlx5_core without stale mlx_en or mlx4_en',
);
}
{
my @doca_modules = (
'updates/dkms/mlx5_core.ko.zst',
'updates/dkms/mlx5_ib.ko.zst',
'updates/dkms/ib_ipoib.ko.zst',
);
my ( $root, $kernelver ) = create_target_kernel(
files => \@doca_modules,
dependencies => \@doca_modules,
);
is_deeply(
[ imgutils::resolve_mellanox_default_net_drivers(
$root,
$kernelver,
[],
qw(mlx_en.ko mlx5_core.ko),
) ],
[qw(mlx5_core.ko mlx5_ib.ko ib_ipoib.ko)],
'a DOCA-OFED target includes the available mlx5 InfiniBand and IPoIB modules',
);
}
{
my @mlx4_modules = (
'kernel/drivers/net/ethernet/mellanox/mlx4/mlx4_en.ko.xz',
'kernel/drivers/infiniband/hw/mlx4/mlx4_ib.ko.xz',
'kernel/drivers/infiniband/ulp/ipoib/ib_ipoib.ko.xz',
);
my ( $root, $kernelver ) = create_target_kernel(
files => \@mlx4_modules,
dependencies => \@mlx4_modules,
);
is_deeply(
[ imgutils::resolve_mellanox_default_net_drivers(
$root, $kernelver, [], qw(mlx_en.ko),
) ],
[qw(mlx4_en.ko mlx4_ib.ko ib_ipoib.ko)],
'an older mlx4 target includes its available InfiniBand and IPoIB modules',
);
}
{
my @modules = (
'kernel/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.ko.xz',
'kernel/drivers/infiniband/ulp/ipoib/ib_ipoib.ko.xz',
);
my ( $root, $kernelver ) = create_target_kernel(
files => \@modules,
dependencies => \@modules,
);
is_deeply(
[ imgutils::resolve_mellanox_default_net_drivers(
$root, $kernelver, [], qw(mlx5_core.ko),
) ],
[qw(mlx5_core.ko)],
'IPoIB is not added without an available Mellanox InfiniBand driver',
);
}
{
my @modules = (
'updates/dkms/mlx5_core.ko.zst',
'updates/dkms/mlx5_ib.ko.zst',
'updates/dkms/ib_ipoib.ko.zst',
);
my ( $root, $kernelver ) = create_target_kernel(
files => \@modules,
dependencies => \@modules,
);
is_deeply(
[ imgutils::resolve_mellanox_default_net_drivers(
$root,
$kernelver,
[qw(mlx5_ib.ko ib_ipoib.ko)],
qw(mlx5_core.ko),
) ],
[qw(mlx5_ib.ko ib_ipoib.ko mlx5_core.ko)],
'explicit and optional InfiniBand drivers are deduplicated',
);
}
{
my @modules = (
'updates/dkms/mlx5_core.ko.zst',
'updates/dkms/mlx5_ib.ko.zst',
'updates/dkms/ib_ipoib.ko.zst',
);
my ( $root, $kernelver ) = create_target_kernel(
files => \@modules,
dependencies => \@modules,
);
is_deeply(
[ imgutils::resolve_mellanox_default_net_drivers(
$root, $kernelver, [qw(mlx5_core.ko)],
) ],
[qw(mlx5_core.ko)],
'an explicit Mellanox request does not enable optional InfiniBand defaults',
);
}
{
my @modules = (
'updates/mlnx-ofed/mlx4_ib.ko.zst',
'updates/mlnx-ofed/ib_ipoib.ko.zst',
);
my ( $root, $kernelver ) = create_target_kernel(
files => \@modules,
dependencies => \@modules,
);
is_deeply(
[ imgutils::resolve_mellanox_default_net_drivers(
$root, $kernelver, [], qw(mlx_en.ko),
) ],
[qw(mlx4_ib.ko ib_ipoib.ko)],
'an InfiniBand-only OFED stack does not require an Ethernet frontend',
);
}
{
my $mlx4 = 'kernel/drivers/net/ethernet/mellanox/mlx4/mlx4_en.ko.xz';
my ( $root, $kernelver ) = create_target_kernel(
files => [$mlx4],
dependencies => [$mlx4],
);
is_deeply(
[ imgutils::resolve_mellanox_default_net_drivers(
$root, $kernelver, [], qw(mlx_en.ko),
) ],
[qw(mlx4_en.ko)],
'an older in-box kernel resolves the legacy placeholder to mlx4_en',
);
}
{
my $mlx_en = 'updates/mlnx-ofed/drivers/net/mlx_en.ko.zst';
my ( $root, $kernelver ) = create_target_kernel(
files => [$mlx_en],
dependencies => [$mlx_en],
);
is_deeply(
[ imgutils::resolve_mellanox_default_net_drivers(
$root, $kernelver, [], qw(mlx_en.ko),
) ],
[qw(mlx_en.ko)],
'a real legacy MLNX_OFED mlx_en module is retained',
);
}
{
my ( $root, $kernelver ) = create_target_kernel();
is_deeply(
[ imgutils::resolve_mellanox_default_net_drivers(
$root,
$kernelver,
[],
qw(tg3.ko mlx_en.ko mlx4_en.ko mlx5_core.ko),
) ],
[qw(tg3.ko)],
'unavailable optional Mellanox defaults are omitted',
);
}
{
my @paths = (
'weak-updates/vendor/mlx4_en.ko.xz',
'extra/vendor/mlx5_core.ko.zst',
);
my ( $root, $kernelver ) = create_target_kernel(files => \@paths);
my %available = imgutils::target_kernel_module_availability(
$root, $kernelver, qw(mlx4_en mlx5_core),
);
ok($available{'mlx4_en'}, 'a recursively installed .ko.xz module is available');
ok($available{'mlx5_core'}, 'a recursively installed .ko.zst module is available');
}
{
my @paths = (
'weak-updates/vendor/mlx4_en.ko.xz',
'extra/vendor/mlx5_core.ko.zst',
);
my ( $root, $kernelver ) = create_target_kernel(
files => \@paths,
dependencies => \@paths,
);
is_deeply(
[ imgutils::resolve_mellanox_default_net_drivers(
$root,
$kernelver,
[],
qw(mlx_en.ko mlx4_en.ko mlx5_core.ko mlx5_core.ko),
) ],
[qw(mlx4_en.ko mlx5_core.ko)],
'resolved Mellanox defaults are deduplicated',
);
}
{
my $mlx5 = 'kernel/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.ko.xz';
my ( $root, $kernelver ) = create_target_kernel(
files => [$mlx5],
dependencies => [$mlx5],
);
is_deeply(
[ imgutils::resolve_mellanox_default_net_drivers(
$root,
$kernelver,
[qw(mlx_en.ko e1000.ko)],
qw(mlx_en.ko mlx5_core.ko),
) ],
[qw(mlx_en.ko e1000.ko mlx5_core.ko)],
'a missing explicitly requested driver is not discarded as an optional default',
);
}
{
my $mlx4 = 'kernel/drivers/net/ethernet/mellanox/mlx4/mlx4_en.ko.xz';
my ( $root, $kernelver ) = create_target_kernel(
files => [$mlx4],
dependencies => [$mlx4],
);
is_deeply(
[ imgutils::resolve_mellanox_default_net_drivers(
$root,
$kernelver,
[qw(mlx_en.ko mlx4_en.ko e1000.ko)],
qw(tg3.ko),
) ],
[qw(mlx4_en.ko e1000.ko tg3.ko)],
'an explicitly requested legacy alias resolves to the in-box mlx4 driver',
);
}
{
my $builtin = 'kernel/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.ko';
my ( $root, $kernelver ) = create_target_kernel(builtins => [$builtin]);
my %available = imgutils::target_kernel_module_availability(
$root, $kernelver, qw(mlx5_core),
);
ok($available{'mlx5_core'}, 'modules.builtin identifies an available target module');
}
my $tmpdir = tempdir( CLEANUP => 1 );
my $modprobe_log = "$tmpdir/modprobe.log";
my $fake_modprobe = "$tmpdir/modprobe";
write_text(
$fake_modprobe,
"#!/bin/sh\nprintf '%s\\n' \"\$1\" >>\"\$MODPROBE_LOG\"\n" .
"case \" \${FAIL_MODPROBE:-} \" in\n" .
" *\" \$1 \"*) exit 1 ;;\n" .
"esac\n" .
"exit 0\n",
);
chmod( 0755, $fake_modprobe ) or croak "chmod $fake_modprobe: $!";
sub run_mellanox_loader {
return system(
repo_path('xCAT-genesis-scripts/usr/sbin/loadmlxeth'),
$fake_modprobe,
) >> 8;
}
{
local %ENV = (
%ENV,
MODPROBE_LOG => $modprobe_log,
);
is(
run_mellanox_loader(),
0,
'the Genesis Mellanox loader succeeds when both modules load',
);
}
is(
read_text($modprobe_log),
"mlx4_en\nmlx5_core\n",
'the Genesis loader requests both Mellanox Ethernet driver generations',
);
unlink($modprobe_log) or croak "unlink $modprobe_log: $!";
{
local %ENV = (
%ENV,
FAIL_MODPROBE => 'mlx4_en',
MODPROBE_LOG => $modprobe_log,
);
is(run_mellanox_loader(), 0,
'the Genesis Mellanox loader succeeds when mlx5 loads');
}
is(
read_text($modprobe_log),
"mlx4_en\nmlx5_core\n",
'a failed mlx4 load does not suppress the independent mlx5 attempt',
);
unlink($modprobe_log) or croak "unlink $modprobe_log: $!";
{
local %ENV = (
%ENV,
FAIL_MODPROBE => 'mlx5_core',
MODPROBE_LOG => $modprobe_log,
);
is(run_mellanox_loader(), 0,
'the Genesis Mellanox loader succeeds when mlx4 loads');
}
is(
read_text($modprobe_log),
"mlx4_en\nmlx5_core\n",
'the Genesis loader tries both generations before reporting failure',
);
unlink($modprobe_log) or croak "unlink $modprobe_log: $!";
{
local %ENV = (
%ENV,
FAIL_MODPROBE => 'mlx4_en mlx5_core',
MODPROBE_LOG => $modprobe_log,
);
isnt(run_mellanox_loader(), 0,
'the Genesis Mellanox loader fails when neither module loads');
}
is(
read_text($modprobe_log),
"mlx4_en\nmlx5_core\n",
'both generations are attempted when neither module loads',
);
done_testing();