2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-08-26 08:36:42 +00:00
Files
xcat-core/xCAT-test/unit/getinstdisk_selection.t
T
Vinícius Ferrão 492f9171a2 test(getinstdisk): cover the driver group against the identifier
Cover a RAID volume that reports a WWN against a direct attached disk
that reports none, in both scan orders, which the previous readback
decided by identifier. Keep the identifier rules of one group under
test as well: the disk that reports a WWN wins, the lower WWN wins
between two, and a path wins over no identifier at all.
2026-08-21 01:13:16 -03:00

182 lines
7.0 KiB
Perl

#!/usr/bin/env perl
use strict;
use warnings;
use FindBin;
use File::Spec;
use File::Temp qw(tempdir);
use Test::More;
my $repo_root = File::Spec->catdir( $FindBin::Bin, '..', '..' );
my $script_dir = File::Spec->catdir( $repo_root, 'xCAT-server', 'share', 'xcat', 'install', 'scripts' );
# One script serves every installer, so one file is exercised here.
my @scripts = map { File::Spec->catfile( $script_dir, $_ ) } qw(getinstdisk);
plan skip_all => 'getinstdisk not found' if grep { !-r $_ } @scripts;
our $script;
sub slurp {
open( my $fh, '<', $_[0] ) or die "Unable to read $_[0]: $!";
my $c = do { local $/; <$fh> };
close($fh);
return $c;
}
# The script reads /proc/partitions and writes under /tmp, so each scenario
# runs a copy with those paths moved into its own sandbox, and a stub udevadm
# serves the device properties from fixture files.
sub run_scenario {
my (%disk) = @_;
my $sandbox = tempdir( CLEANUP => 1 );
my $fixdir = "$sandbox/fix";
my $bindir = "$sandbox/bin";
mkdir $fixdir;
mkdir $bindir;
my $body = slurp($script);
$body =~ s{/proc/partitions}{$sandbox/partitions}g;
$body =~ s{/tmp/xcat\.install_disk}{$sandbox/xcat.install_disk}g;
$body =~ s{/tmp/xcat\.getinstalldisk}{$sandbox/xcat.getinstalldisk}g;
$body =~ s{/dev/md/Volume0}{$sandbox/md/Volume0}g;
$body =~ s{"/dev/xvda"}{"$sandbox/xvda"}g;
open( my $sh, '>', "$sandbox/getinstdisk" ) or die $!;
print $sh $body;
close($sh);
open( my $parts, '>', "$sandbox/partitions" ) or die $!;
print $parts "major minor #blocks name\n\n";
my $minor = 0;
for my $name ( sort keys %disk ) {
printf $parts " 8 %5d 524288000 %s\n", $minor++, $name;
}
close($parts);
for my $name ( sort keys %disk ) {
my %attr = %{ $disk{$name} };
open( my $props, '>', "$fixdir/$name.props" ) or die $!;
print $props "ID_WWN=$attr{wwn}\n" if $attr{wwn};
print $props "DEVPATH=$attr{path}\n" if $attr{path};
print $props "DEVTYPE=disk\n";
close($props);
open( my $attrs, '>', "$fixdir/$name.attrs" ) or die $!;
print $attrs qq{ ATTRS{size}=="1024000000"\n};
print $attrs qq{ DRIVERS=="$attr{driver}"\n} if $attr{driver};
my @models = $attr{models} ? @{ $attr{models} } : ( $attr{model} ? $attr{model} : () );
print $attrs qq{ ATTRS{model}=="$_"\n} for @models;
close($attrs);
}
open( my $udev, '>', "$bindir/udevadm" ) or die $!;
print $udev <<'UDEV';
#!/bin/sh
for a in "$@"; do
case "$a" in
--name=*) name=${a#--name=} ;;
esac
done
name=${name#/dev/}
case "$*" in
*--query=property*) cat "$FIXDIR/$name.props" 2>/dev/null ;;
*--attribute-walk*) cat "$FIXDIR/$name.attrs" 2>/dev/null ;;
esac
exit 0
UDEV
close($udev);
chmod 0755, "$bindir/udevadm";
local $ENV{FIXDIR} = $fixdir;
local $ENV{PATH} = "$bindir:$ENV{PATH}";
local $ENV{MASTER_IP} = '';
system("sh $sandbox/getinstdisk >$sandbox/log 2>&1");
my $chosen = -r "$sandbox/xcat.install_disk" ? slurp("$sandbox/xcat.install_disk") : '';
chomp $chosen;
return $chosen;
}
# Every scenario asserts against both copies of the script.
sub selects {
my ( $expected, $name, %disk ) = @_;
for my $candidate (@scripts) {
local $script = $candidate;
my $variant = ( File::Spec->splitpath($candidate) )[2];
is( run_scenario(%disk), $expected, "$name ($variant)" );
}
return;
}
# A direct attached disk wins over a RAID volume when both are present. The
# RAID volume sorts first by name, so the choice comes from the driver group.
selects( '/dev/sdb', 'the direct attached disk wins over the RAID volume',
sda => { driver => 'megaraid_sas' },
sdb => { driver => 'ahci' } );
# A server with only RAID volumes still selects one.
selects( '/dev/sda', 'a RAID volume is selected when nothing better exists',
sda => { driver => 'megaraid_sas' } );
# A SAS host adapter loses to a direct attached disk, and still wins over a
# driver with no group of its own.
selects( '/dev/sdb', 'the direct attached disk wins over the host adapter',
sda => { driver => 'mpt3sas' },
sdb => { driver => 'ahci' } );
selects( '/dev/sda', 'the host adapter wins over an unknown driver',
sda => { driver => 'mpt3sas' },
sdb => { driver => 'virtio_blk' } );
# A driverless NVMe device still gets selected from the last group.
selects( '/dev/nvme0n1', 'an NVMe device is selected from the last group',
nvme0n1 => {} );
# No usable disk falls back to the documented default.
selects( '/dev/sda', 'no disks fall back to the default' );
# The driver group decides before the identifier. A disk that reports no WWN
# used to be dropped when another disk reported one, or to be ignored because
# the readback only opened the files of the last identifier class seen.
selects( '/dev/sdb', 'the direct attached disk wins when only the RAID volume reports a WWN',
sda => { driver => 'megaraid_sas', wwn => '0x5000cca0aaaa0001' },
sdb => { driver => 'ahci' } );
selects( '/dev/sda', 'the direct attached disk wins when it is scanned first without a WWN',
sda => { driver => 'ahci' },
sdb => { driver => 'megaraid_sas', wwn => '0x5000cca0aaaa0002' } );
# Within one driver group the identifier decides, and a disk that reports one
# is preferred, because that name is stable across reboots.
selects( '/dev/sdb', 'the disk with a WWN wins inside the group',
sda => { driver => 'ahci' },
sdb => { driver => 'ahci', wwn => '0x5000cca0bbbb0001' } );
selects( '/dev/sdb', 'the lower WWN wins inside the group',
sda => { driver => 'ahci', wwn => '0x5000cca0bbbb0002' },
sdb => { driver => 'ahci', wwn => '0x5000cca0bbbb0001' } );
selects( '/dev/sda', 'a path is preferred over no identifier at all',
sda => { driver => 'ahci', path => '/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda' },
sdb => { driver => 'ahci' } );
# A Xen guest presents xvd names. The scan has to see them, because the
# fallback below it would take the first one without looking.
selects( '/dev/xvda', 'a Xen disk is scanned rather than assumed',
xvda => { driver => 'vbd' } );
selects( '/dev/xvdb', 'the better driver group wins among Xen disks',
xvda => { driver => 'vbd' },
xvdb => { driver => 'ahci' } );
# Every installer includes the one script, which carries the fallbacks and the
# logging that the separate RHEL 10 copy used to hold on its own.
my $common = slurp( $scripts[0] );
ok( !-e File::Spec->catfile( $script_dir, 'getinstdisk.rhels10' ),
'the RHEL 10 copy of the script is gone' );
like( slurp( File::Spec->catfile( $script_dir, 'pre.rhels10' ) ),
qr{/share/xcat/install/scripts/getinstdisk#},
'the RHEL 10 installer includes the common script' );
like( $common, qr{-b /dev/md/Volume0_0}, 'the common script keeps the VROC fallback' );
like( $common, qr{-b "/dev/xvda"}, 'the common script carries the Xen fallback' );
like( $common, qr{command -v msgutil_r [^\n]*\n\s*msgutil_r },
'the failure log is guarded, because one installer does not define it' );
done_testing();