mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-10-21 15:18:19 +00:00
test(provisioning): isolate install disk selection checks
This commit is contained in:
@@ -29,6 +29,13 @@ jobs:
|
||||
- name: Check Debian probe payload permissions
|
||||
if: ${{ !cancelled() }}
|
||||
run: bats xCAT-test/native/probe_debian_permissions.bats
|
||||
- name: Test isolated system scripts
|
||||
if: ${{ !cancelled() }}
|
||||
run: |
|
||||
workdir=$(sudo mktemp -d /tmp/xcat-script-tests.XXXXXX)
|
||||
trap 'sudo rm -rf -- "$workdir"' EXIT
|
||||
sudo cp -R xCAT-test xCAT-server perl-xCAT xCAT "$workdir/"
|
||||
sudo sh -c 'cd "$1" && prove -v xCAT-test/native/getinstdisk_selection.t' sh "$workdir"
|
||||
- name: Prepare xCAT syslog directory
|
||||
if: ${{ !cancelled() }}
|
||||
run: sudo install -d -o syslog -g adm -m 0750 /var/log/xcat
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package XCAT::Test::Sandbox;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Exporter qw(import);
|
||||
use File::Path qw(make_path);
|
||||
use File::Temp qw(tempdir);
|
||||
use XCAT::Test::Process qw(run_command);
|
||||
|
||||
our @EXPORT_OK = qw(sandbox_root sandbox_run);
|
||||
|
||||
sub sandbox_root {
|
||||
my $root = tempdir(CLEANUP => 1);
|
||||
make_path(map { "$root/$_" } qw(bin boot etc log target tmp));
|
||||
return $root;
|
||||
}
|
||||
|
||||
sub sandbox_run {
|
||||
my ($root, @command) = @_;
|
||||
my $options = ref($command[0]) eq 'HASH' ? shift @command : {};
|
||||
my $bwrap = -x '/usr/bin/bwrap' ? '/usr/bin/bwrap' : '/bin/bwrap';
|
||||
die 'Install bubblewrap to run this test' unless -x $bwrap;
|
||||
my @sandbox = ($bwrap, '--unshare-all', '--die-with-parent', '--new-session',
|
||||
'--tmpfs', '/', '--proc', '/proc', '--dev', '/dev', '--tmpfs', '/run',
|
||||
'--setenv', 'PATH', '/fixture/bin:/usr/bin:/bin',
|
||||
'--setenv', 'LC_ALL', 'C', '--bind', $root, '/fixture', '--chdir', '/fixture');
|
||||
for my $directory (qw(/usr /bin /sbin /lib /lib64)) {
|
||||
push @sandbox, '--ro-bind', $directory, $directory if -d $directory;
|
||||
}
|
||||
for my $mount (qw(boot etc target tmp)) {
|
||||
push @sandbox, '--bind', "$root/$mount", "/$mount";
|
||||
}
|
||||
push @sandbox, '--bind', "$root/log", '/var/log';
|
||||
push @sandbox, '--ro-bind', '/etc/alternatives', '/etc/alternatives' if -d '/etc/alternatives';
|
||||
my @mounts;
|
||||
for my $kind (qw(read_only writable)) {
|
||||
my $flag = $kind eq 'read_only' ? '--ro-bind' : '--bind';
|
||||
push @mounts, map { [$flag, $_, $options->{$kind}{$_}] }
|
||||
keys %{$options->{$kind} // {}};
|
||||
}
|
||||
for my $mount (sort { length($a->[2]) <=> length($b->[2]) || $a->[2] cmp $b->[2] } @mounts) {
|
||||
push @sandbox, @$mount;
|
||||
}
|
||||
for my $name (keys %{$options->{env} // {}}) {
|
||||
push @sandbox, '--setenv', $name, $options->{env}{$name};
|
||||
}
|
||||
local %ENV;
|
||||
return run_command(@sandbox, '/usr/bin/timeout', '--kill-after=5', '45', @command);
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -0,0 +1,79 @@
|
||||
package XCAT::Test::Template;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Exporter qw(import);
|
||||
use File::Path qw(make_path);
|
||||
use File::Temp qw(tempdir);
|
||||
use File::Slurper qw(write_text);
|
||||
use JSON::PP qw(encode_json);
|
||||
use XCAT::Test::File qw(repo_path);
|
||||
use XCAT::Test::Sandbox qw(sandbox_root sandbox_run);
|
||||
|
||||
our @EXPORT_OK = qw(template_database set_row render_template);
|
||||
|
||||
sub template_database {
|
||||
my $dir = tempdir(CLEANUP => 1);
|
||||
make_path("$dir/db", "$dir/install");
|
||||
symlink(repo_path('xCAT/postscripts'), "$dir/install/postscripts") or die $!;
|
||||
$ENV{XCATROOT} = repo_path('xCAT-server');
|
||||
$ENV{XCATCFG} = "SQLite:$dir/db";
|
||||
require xCAT::Table;
|
||||
require xCAT::Template;
|
||||
require xCAT::Postage;
|
||||
my %site = (
|
||||
installdir => "$dir/install", tftpdir => "$dir/tftpboot",
|
||||
master => '192.0.2.1', timezone => 'UTC', domain => 'example.invalid',
|
||||
xcatiport => 3002, xcatdport => 3001, httpport => 8080,
|
||||
xcatdebugmode => 0, nodestatus => 1, secureroot => 1,
|
||||
managedaddressmode => 'dhcp',
|
||||
);
|
||||
set_row('site', {key => $_}, {value => $site{$_}}) for keys %site;
|
||||
%::XCATSITEVALS = %site;
|
||||
set_row('nodelist', {node => 'node'}, {groups => 'all'});
|
||||
set_row('noderes', {node => 'node'}, {
|
||||
xcatmaster => '192.0.2.1', nfsserver => '192.0.2.1', installnic => 'mac',
|
||||
});
|
||||
set_row('mac', {node => 'node'}, {mac => '52:54:00:12:34:56'});
|
||||
set_row('bootparams', {node => 'node'}, {kcmdline => 'console=ttyS0 quiet'});
|
||||
return $dir;
|
||||
}
|
||||
|
||||
sub set_row {
|
||||
my ($name, $key, $values) = @_;
|
||||
my $table = xCAT::Table->new($name, -create => 1) or die "Cannot create $name";
|
||||
$table->setAttribs($key, $values);
|
||||
$table->close();
|
||||
return;
|
||||
}
|
||||
|
||||
sub render_template {
|
||||
my ($database, @arguments) = @_;
|
||||
my $root = sandbox_root();
|
||||
write_text("$root/arguments.json", encode_json(\@arguments));
|
||||
write_text("$root/render.pl", <<'PERL');
|
||||
use strict;
|
||||
use warnings;
|
||||
use File::Slurper qw(read_text);
|
||||
use JSON::PP qw(decode_json);
|
||||
use xCAT::Template;
|
||||
use xCAT::Postage;
|
||||
my $site = xCAT::Table->new('site');
|
||||
my @attributes = $site->getAllAttribs('key', 'value');
|
||||
%::XCATSITEVALS = map { $_->{key} => $_->{value} } @attributes;
|
||||
$site->close();
|
||||
my $arguments = decode_json(read_text('/fixture/arguments.json'));
|
||||
my $error = xCAT::Template->subvars(@$arguments);
|
||||
die "$error\n" if $error;
|
||||
PERL
|
||||
my $source = repo_path('.');
|
||||
my ($status, $output) = sandbox_run($root, {
|
||||
read_only => {$source => $source, repo_path('xCAT/postscripts') => '/install/postscripts'},
|
||||
writable => {$database => $database},
|
||||
env => {XCATROOT => $ENV{XCATROOT}, XCATCFG => $ENV{XCATCFG}},
|
||||
}, $^X, '-I' . repo_path('perl-xCAT'), '-I' . repo_path('xCAT-server/lib/perl'), '/fixture/render.pl');
|
||||
die "Template rendering failed ($status): $output" if $status;
|
||||
return;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -0,0 +1,158 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
use FindBin;
|
||||
use lib "$FindBin::Bin/../lib";
|
||||
use lib "$FindBin::Bin/../../perl-xCAT", "$FindBin::Bin/../../xCAT-server/lib/perl";
|
||||
use File::Path qw(make_path);
|
||||
use File::Slurper qw(read_binary write_binary);
|
||||
use Test::More;
|
||||
use XCAT::Test::File qw(repo_path);
|
||||
use XCAT::Test::Sandbox qw(sandbox_root sandbox_run);
|
||||
use XCAT::Test::Template qw(template_database set_row render_template);
|
||||
|
||||
plan skip_all => 'disk selection requires Linux namespaces' unless $^O eq 'linux';
|
||||
BAIL_OUT('run as root to create isolated dummy block devices') if $>;
|
||||
my $script = repo_path('xCAT-server/share/xcat/install/scripts/getinstdisk');
|
||||
BAIL_OUT("missing disk selector: $script") unless -r $script;
|
||||
my $database;
|
||||
|
||||
sub selects {
|
||||
my ($expected, $name, $disks, %options) = @_;
|
||||
subtest $name => sub {
|
||||
my $root = sandbox_root();
|
||||
make_path("$root/props", "$root/dev/md");
|
||||
my $partitions = "major minor #blocks name\n\n";
|
||||
my $minor = 0;
|
||||
for my $disk (sort keys %$disks) {
|
||||
my $attr = $disks->{$disk};
|
||||
$partitions .= sprintf " 8 %5d 524288000 %s\n", $minor++, $disk;
|
||||
my $properties = "DEVTYPE=disk\n";
|
||||
$properties .= "ID_WWN=$attr->{wwn}\n" if $attr->{wwn};
|
||||
$properties .= "DEVPATH=$attr->{path}\n" if $attr->{path};
|
||||
write_binary("$root/props/$disk.props", $properties);
|
||||
my $size = $attr->{size} // 1024000000;
|
||||
my $attributes = qq{ ATTRS{size}=="$size"\n};
|
||||
$attributes .= qq{ DRIVERS=="$attr->{driver}"\n} if $attr->{driver};
|
||||
write_binary("$root/props/$disk.attrs", $attributes);
|
||||
}
|
||||
write_binary("$root/partitions", $partitions);
|
||||
write_binary("$root/bin/udevadm", <<'SH');
|
||||
#!/bin/sh
|
||||
name=
|
||||
query=
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case "$1" in
|
||||
info) shift ;;
|
||||
--name=*) name=${1#--name=}; shift ;;
|
||||
--name|-n) name=$2; shift 2 ;;
|
||||
--query=*) query=${1#--query=}; shift ;;
|
||||
--query|-q) query=$2; shift 2 ;;
|
||||
--attribute-walk|-a) query=attributes; shift ;;
|
||||
*) exit 97 ;;
|
||||
esac
|
||||
done
|
||||
name=${name#/dev/}
|
||||
case "$query" in
|
||||
property) cat "/fixture/props/$name.props" ;;
|
||||
attributes) cat "/fixture/props/$name.attrs" ;;
|
||||
*) exit 97 ;;
|
||||
esac
|
||||
SH
|
||||
chmod 0755, "$root/bin/udevadm" or die $!;
|
||||
for my $device (@{$options{blocks} // []}) {
|
||||
system('mknod', "$root/dev/$device", 'b', '240', '0') == 0 or die "mknod: $?";
|
||||
}
|
||||
write_binary("$root/dev/$_", '') for @{$options{regular} // []};
|
||||
my %mounts = ($script => '/getinstdisk', "$root/partitions" => '/proc/partitions');
|
||||
$mounts{"$root/dev/$_"} = "/dev/$_" for @{$options{blocks} // []}, @{$options{regular} // []};
|
||||
if ($options{no_awk}) {
|
||||
write_binary("$root/bin/find", "#!/bin/sh\nexit 0\n");
|
||||
write_binary("$root/bin/awk", "#!/bin/sh\nprintf called >>/fixture/awk-calls\nexit 97\n");
|
||||
chmod 0755, "$root/bin/find", "$root/bin/awk" or die $!;
|
||||
}
|
||||
my $command = $options{logger} ?
|
||||
'msgutil_r() { printf "%s\n" "$*" >> /fixture/warnings; }; . /getinstdisk' :
|
||||
'. /getinstdisk';
|
||||
if ($options{pre}) {
|
||||
render_template($database, repo_path('xCAT-server/share/xcat/install/scripts/pre.rhels10'),
|
||||
"$database/pre.sh", 'node', "$database/image.pkglist", '/install/rhels10/x86_64',
|
||||
'rh', undef, {xcatmaster => '192.0.2.1'}, osarch => 'x86_64');
|
||||
make_path("$root/libexec");
|
||||
write_binary("$root/libexec/platform-python", "#!/bin/sh\nexit 0\n");
|
||||
chmod 0755, "$root/libexec/platform-python" or die $!;
|
||||
$mounts{"$root/libexec"} = '/usr/libexec';
|
||||
$mounts{"$database/pre.sh"} = '/pre.sh';
|
||||
for my $name (qw(logger vgs vgchange vgremove chown)) {
|
||||
write_binary("$root/bin/$name", "#!/bin/sh\nexit 0\n");
|
||||
chmod 0755, "$root/bin/$name" or die $!;
|
||||
}
|
||||
write_binary("$root/bin/mknod", "#!/bin/sh\n: >\"\$1\"\n");
|
||||
chmod 0755, "$root/bin/mknod" or die $!;
|
||||
$command = '. /pre.sh';
|
||||
}
|
||||
for my $pass (1 .. 2) {
|
||||
write_binary("$root/tmp/xcat.install_disk", "/dev/not-selected\n");
|
||||
my ($rc, $output) = sandbox_run($root, {read_only => \%mounts,
|
||||
env => {MASTER_IP => '192.0.2.1', log_label => 'install'}},
|
||||
$options{pre} ? '/bin/bash' : '/bin/sh', '-c', $command);
|
||||
is($rc, 0, "pass $pass completes") or diag($output);
|
||||
unlike($output, qr/msgutil_r/, "pass $pass needs no absent installer logger") unless $options{logger};
|
||||
is(-f "$root/tmp/xcat.install_disk" ? scalar read_binary("$root/tmp/xcat.install_disk") : '',
|
||||
"$expected\n", "pass $pass selects the expected disk");
|
||||
ok(!-e "$root/tmp/xcat.getinstalldisk", "pass $pass removes scan state");
|
||||
ok(!-e "$root/awk-calls", "pass $pass works without calling awk") if $options{no_awk};
|
||||
if ($options{pre}) {
|
||||
my $partition = "$root/tmp/partitionfile";
|
||||
my @disk_rules = grep { /^ignoredisk\b/ } split /\n/,
|
||||
(-f $partition ? scalar read_binary($partition) : '');
|
||||
is($disk_rules[-1], "ignoredisk --only-use=$expected",
|
||||
"pass $pass uses the selected disk in the rendered installer partition plan");
|
||||
}
|
||||
}
|
||||
if ($options{logger}) {
|
||||
is(scalar read_binary("$root/warnings"),
|
||||
"192.0.2.1 warn Disk detection failed, defaulting to /dev/sda /var/log/xcat/xcat.log install\n" x 2,
|
||||
'default selection reaches the installer logger');
|
||||
}
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
for my $no_awk (0, 1) {
|
||||
subtest $no_awk ? 'without awk discovery' : 'with awk discovery' => sub {
|
||||
my @cases = (
|
||||
['/dev/sdb', 'direct disk before RAID', {sda => {driver => 'megaraid_sas'}, sdb => {driver => 'ahci'}}],
|
||||
['/dev/sda', 'RAID alone', {sda => {driver => 'megaraid_sas'}}],
|
||||
['/dev/sdb', 'direct disk before SAS', {sda => {driver => 'mpt3sas'}, sdb => {driver => 'ahci'}}],
|
||||
['/dev/sda', 'SAS before unclassified driver', {sda => {driver => 'mpt3sas'}, sdb => {driver => 'virtio_blk'}}],
|
||||
['/dev/nvme0n1', 'driverless NVMe', {nvme0n1 => {}}],
|
||||
['/dev/sda', 'empty scan', {}],
|
||||
['/dev/sdb', 'group wins over RAID WWN', {sda => {driver => 'megaraid_sas', wwn => '0x5001'}, sdb => {driver => 'ahci'}}],
|
||||
['/dev/sda', 'group wins when scanned first', {sda => {driver => 'ahci'}, sdb => {driver => 'megaraid_sas', wwn => '0x5002'}}],
|
||||
['/dev/sdb', 'WWN before no identifier', {sda => {driver => 'ahci'}, sdb => {driver => 'ahci', wwn => '0x5001'}}],
|
||||
['/dev/sdb', 'lower WWN', {sda => {driver => 'ahci', wwn => '0x5002'}, sdb => {driver => 'ahci', wwn => '0x5001'}}],
|
||||
['/dev/sda', 'path before no identifier', {sda => {driver => 'ahci', path => '/devices/pci/block/sda'}, sdb => {driver => 'ahci'}}],
|
||||
['/dev/xvda', 'scanned Xen disk', {xvda => {driver => 'vbd'}}],
|
||||
['/dev/xvdb', 'Xen driver ordering', {xvda => {driver => 'vbd'}, xvdb => {driver => 'ahci'}}],
|
||||
['/dev/sdb', 'ignore small media', {sda => {driver => 'ahci', size => 100}, sdb => {driver => 'virtio_blk'}}],
|
||||
);
|
||||
selects(@$_, no_awk => $no_awk) for @cases;
|
||||
};
|
||||
}
|
||||
selects('/dev/md/Volume0_0', 'VROC numbered fallback', {}, blocks => ['md/Volume0_0']);
|
||||
selects('/dev/md/Volume0', 'VROC unnumbered fallback', {}, blocks => ['md/Volume0']);
|
||||
selects('/dev/md/Volume0_0', 'VROC preference over other fallbacks', {}, blocks => ['md/Volume0_0', 'md/Volume0', 'xvda']);
|
||||
selects('/dev/xvda', 'Xen fallback', {}, blocks => ['xvda']);
|
||||
selects('/dev/sdb', 'scanned disk before fallback devices', {sdb => {driver => 'ahci'}}, blocks => ['md/Volume0_0', 'xvda']);
|
||||
selects('/dev/sda', 'regular files are not block devices', {}, regular => ['md/Volume0_0', 'md/Volume0', 'xvda']);
|
||||
selects('/dev/sda', 'default selection with installer logger', {}, logger => 1);
|
||||
|
||||
$database = template_database();
|
||||
set_row('site', {key => 'nodestatus'}, {value => 0});
|
||||
set_row('nodetype', {node => 'node'}, {os => 'rhels10', arch => 'x86_64', provmethod => 'install'});
|
||||
write_binary("$database/image.pkglist", '');
|
||||
selects('/dev/sdb', 'rendered RHEL 10 installer disk selection',
|
||||
{sda => {driver => 'megaraid_sas'}, sdb => {driver => 'ahci'}}, pre => 1);
|
||||
|
||||
done_testing();
|
||||
@@ -1,181 +0,0 @@
|
||||
#!/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();
|
||||
Reference in New Issue
Block a user