2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-09-21 16:39:30 +00:00
Files
xcat-core/xCAT-test/unit/genesis_payload_verification.t
T
Daniel Hilst 4dd16eb5f1 test(xcat-core): capture the genesis harness passing on a failed nodeset
The nodeset_shell_incorrectmasterip case ran "nodeset testnode shell", the command failed
with "/tftpboot/boot/grub2/grub2.x86_64 does not exits", and the case still passed.
check_destiny in xCAT-test/autotest/testcase/genesis/test.sh discards the return value of
runcmd and greps the boot configuration file, which grub2.pm writes before it stops on the
missing boot loader. The sub-case asserts nothing.

wait_for_boot in xCAT-test/autotest/testcase/genesis/genesistest.pl waits for
nodelist.status "booted". A Genesis node reports its destiny with getdestiny and xcatd
writes "shell", "configuring" or "booting" from it, never "booted". Every caller discards
the return value, so each case rests on its xdsh probes alone.

genesis_incorrectmasterip_check.t now runs the check with a nodeset that fails, and reads
whether the grub2 boot loader for the node arch is present when nodeset runs.
genesis_testcase_helpers.t drives the status wait with lsdef shadowed, and drives the shell
case with every command it runs shadowed. genesis_payload_verification.t reads a payload
without mktemp, which getdestiny needs to make its request file.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
(cherry picked from commit 3597760645ddf38f110457934982e2cc9a113f84)
2026-09-04 19:28:33 -03:00

96 lines
4.2 KiB
Perl

#!/usr/bin/env perl
# Drive verify-genesis-payload against payload trees that reproduce the three holes the
# released legacy Genesis image shipped with.
use strict;
use warnings;
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 Test::More;
use XCAT::Test::File qw(repo_path);
my $verifier = repo_path('xCAT-genesis-builder/verify-genesis-payload');
plan skip_all => 'verify-genesis-payload not found' unless -f $verifier;
plan tests => 11;
my $tmpdir = tempdir(CLEANUP => 1);
# A complete payload: OpenSSH 9.9 sshd plus its session helper, tmux plus a UTF-8 locale.
my $good = build_payload(sshd_execs_session => 1, session_helper => 1, tmux => 1, locale => 1, dhclient => 1, mktemp => 1);
my ($rc, $err) = run($good, 'usr/sbin/dhclient');
is($rc, 0, 'a complete payload passes') or diag($err);
# doxcat calls dhclient with ISC flags. The released el9 image carried dhclient.conf and
# dhclient-script but no dhclient, so Genesis never acquired an address.
my $nodhcp = build_payload(sshd_execs_session => 1, session_helper => 1, tmux => 1, locale => 1, dhclient => 0, mktemp => 1);
($rc, $err) = run($nodhcp, 'usr/sbin/dhclient');
isnt($rc, 0, 'a payload without dhclient fails');
like($err, qr{usr/sbin/dhclient}, 'the missing dhclient is named');
# sshd 9.9 execs /usr/libexec/openssh/sshd-session for every connection.
my $nohelper = build_payload(sshd_execs_session => 1, session_helper => 0, tmux => 1, locale => 1, dhclient => 1, mktemp => 1);
($rc, $err) = run($nohelper, 'usr/sbin/dhclient');
isnt($rc, 0, 'a payload whose sshd execs sshd-session but does not ship it fails');
like($err, qr{sshd-session}, 'the missing sshd-session is named');
# OpenSSH 8 does not use the helper, so el8 must still pass without it.
my $openssh8 = build_payload(sshd_execs_session => 0, session_helper => 0, tmux => 1, locale => 1, dhclient => 1, mktemp => 1);
($rc, $err) = run($openssh8, 'usr/sbin/dhclient');
is($rc, 0, 'an OpenSSH 8 payload passes without sshd-session') or diag($err);
# tmux without a UTF-8 locale is what stopped doxcat from ever running.
my $nolocale = build_payload(sshd_execs_session => 1, session_helper => 1, tmux => 1, locale => 0, dhclient => 1, mktemp => 1);
($rc, $err) = run($nolocale, 'usr/sbin/dhclient');
isnt($rc, 0, 'a payload with tmux and no UTF-8 locale fails');
like($err, qr{C\.utf8}, 'the missing locale is named');
# getdestiny makes its request file with mktemp. Without it the node never reports its destiny,
# so xcatd never sets nodelist.status and the node stays at powering-on.
my $nomktemp = build_payload(sshd_execs_session => 1, session_helper => 1, tmux => 1, locale => 1, dhclient => 1, mktemp => 0);
($rc, $err) = run($nomktemp, 'usr/sbin/dhclient');
isnt($rc, 0, 'a payload without mktemp fails');
like($err, qr{usr/bin/mktemp}, 'the missing mktemp is named');
($rc, $err) = run("$tmpdir/does-not-exist");
is($rc >> 0, 2, 'a missing payload directory is a usage error');
#---
# build_payload: make a payload tree with the pieces the verifier reasons about.
#---
sub build_payload {
my (%opt) = @_;
my $root = tempdir(DIR => $tmpdir, CLEANUP => 1);
make_path("$root/usr/sbin", "$root/usr/bin", "$root/usr/libexec/openssh");
write_text("$root/usr/sbin/sshd",
$opt{sshd_execs_session}
? "OpenSSH_9.9p1\n/usr/libexec/openssh/sshd-session\n"
: "OpenSSH_8.0p1\n");
write_text("$root/usr/libexec/openssh/sshd-session", "helper\n") if $opt{session_helper};
write_text("$root/usr/bin/tmux", "tmux\n") if $opt{tmux};
if ($opt{locale}) {
make_path("$root/usr/lib/locale/C.utf8");
write_text("$root/usr/lib/locale/C.utf8/LC_CTYPE", "ctype\n");
}
write_text("$root/usr/sbin/dhclient", "dhclient\n") if $opt{dhclient};
write_text("$root/usr/bin/mktemp", "mktemp\n") if $opt{mktemp};
return $root;
}
#---
# run: run the verifier and return its exit status and stderr.
#---
sub run {
my ($root, @required) = @_;
my $errfile = "$tmpdir/err.$$";
my $cmd = join ' ', map { "'$_'" } ($verifier, $root, @required);
system("/bin/bash $cmd >/dev/null 2>$errfile");
my $status = $? >> 8;
my $err = -f $errfile ? read_text($errfile) : '';
unlink $errfile;
return ($status, $err);
}