2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-09-26 09:44:03 +00:00

fix(xcat-core): the genesis harness passes on a nodeset that failed and a node that never booted

The nodeset_shell_incorrectmasterip case passed while "nodeset testnode shell" failed with
"/tftpboot/boot/grub2/grub2.x86_64 does not exits". The grub2 sub-case asserted nothing. Every
genesis case reported "After 30 iterations node status: powering-on" and passed anyway.

check_destiny in xCAT-test/autotest/testcase/genesis/test.sh discarded the return value of
runcmd and read the boot configuration file, which grub2.pm writes before it stops on the
missing boot loader. xCAT builds no grub2 boot loader for x86_64, so the file is absent on a
correctly built management node. wait_for_boot in genesistest.pl waited for nodelist.status
"booted"; a Genesis node reports its destiny with getdestiny and xcatd writes "shell",
"configuring" or "booting" from it. Every caller discarded the return value. The node did not
reach even those statuses, because getdestiny makes its request file with mktemp and the
dracut module never installed it.

check_destiny now returns the status of nodeset, and the grub2 check stages an empty
grub2.<arch> when the management node has none and removes it after. wait_for_node_status
takes the status the destiny implies and each caller fails when the node does not reach it;
the shell case moved into run_nodeset_shell_test so its result can be read. clearenv no longer
waits, because "rinstall <node> boot" boots a disk with no operating system and reports
nothing. The dracut modules install mktemp and verify-genesis-payload requires it.

Tests: genesis_incorrectmasterip_check.t runs the check with a failing nodeset and reads
whether the boot loader is present when nodeset runs; genesis_testcase_helpers.t drives the
status wait and the shell case; genesis_payload_verification.t reads a payload without mktemp.
Each fails on the parent commit.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>

(cherry picked from commit 8ecf0a806785023aca0ea6d1b0e41810630816b2)

The Release hunk of the original commit is dropped. buildrpms.pl writes Release from
SOURCE_DATE_EPOCH at build time, so the committed snap stamp is build debris.
This commit is contained in:
Daniel Hilst
2026-09-04 15:12:25 -03:00
parent 4dd16eb5f1
commit 1db2c7cde0
5 changed files with 88 additions and 31 deletions
@@ -47,6 +47,9 @@ install() {
dracut_install mount.nfs sshd vi reboot lspci parted tmux mkfs mkfs.ext4 mkfs.xfs xfs_db
#dracut_install libvirtd /usr/share/libvirt/cpu_map.xml /usr/bin/qemu-img /usr/libexec/qemu-kvm
dracut_install mkswap df ifenslave ssh-keygen scp clear
# getdestiny makes its request file with mktemp. Without it the node reports no
# destiny, so xcatd never moves nodelist.status past powering-on.
dracut_install mktemp
dracut_install lldpad
# RHEL 10 packages no ISC dhcp-client. Install whichever client the build root carries;
@@ -52,6 +52,9 @@ install() {
dracut_install mount.nfs sshd vi reboot lspci parted screen mkfs mkfs.ext4 mkfs.btrfs
#dracut_install libvirtd /usr/share/libvirt/cpu_map.xml /usr/bin/qemu-img /usr/libexec/qemu-kvm
dracut_install mkswap df ifenslave ssh-keygen scp clear
# getdestiny makes its request file with mktemp. Without it the node reports no
# destiny, so xcatd never moves nodelist.status past powering-on.
dracut_install mktemp
dracut_install dhclient lldpad
# OpenSSH 9.8 moved the per-connection work into sshd-session, which sshd execs by
@@ -37,6 +37,7 @@ for path in "$@"; do
done
require usr/sbin/sshd "Genesis is reached over ssh"
require usr/bin/mktemp "getdestiny makes its request file with it"
# OpenSSH 9.8 split the per-connection work into sshd-session, which sshd execs by absolute
# path. EL9 carries OpenSSH 9.9, so an image with sshd alone refuses every connection.
@@ -83,29 +83,7 @@ if (!(-e $nodestanza)) {
####nodesetshell test for genesis
####################################
if ($genesis_nodesetshell_test) {
send_msg(2, "[$$]:Running nodeset NODE shell test...............");
`nodeset $noderange shell`;
if ($?) {
send_msg(0, "[$$]:nodeset $noderange shell failed...............");
exit 1;
}
`rpower $noderange boot`;
if ($?) {
send_msg(0, "[$$]:rpower $noderange failed...............");
exit 1;
}
else {
send_msg(2, "Installing with \"nodeset $noderange shell\" for shell test");
sleep 120; # wait 2 min for install to finish
wait_for_boot();
}
#run nodeshell test
send_msg(2, "prepare for nodeshell script.");
if ( &testxdsh(3)) {
send_msg(0, "[$$]:Could not verify test results using xdsh...............");
exit 1;
}
send_msg(2, "[$$]:Running nodesetshell test success...............");
exit 1 if &run_nodeset_shell_test();
}
####################################
####runcmd test for genesis
@@ -142,6 +120,36 @@ if ($clear_env) {
send_msg(2, "[$$]:Clear genesis test enviroment success...............");
}
##################################
#run_nodeset_shell_test
#################################
sub run_nodeset_shell_test {
send_msg(2, "[$$]:Running nodeset NODE shell test...............");
`nodeset $noderange shell`;
if ($?) {
send_msg(0, "[$$]:nodeset $noderange shell failed...............");
return 1;
}
`rpower $noderange boot`;
if ($?) {
send_msg(0, "[$$]:rpower $noderange failed...............");
return 1;
}
send_msg(2, "Installing with \"nodeset $noderange shell\" for shell test");
sleep 120; # wait 2 min for install to finish
if (&wait_for_node_status("shell")) {
send_msg(0, "[$$]:$noderange did not report the shell destiny...............");
return 1;
}
#run nodeshell test
send_msg(2, "prepare for nodeshell script.");
if (&testxdsh(3)) {
send_msg(0, "[$$]:Could not verify test results using xdsh...............");
return 1;
}
send_msg(2, "[$$]:Running nodesetshell test success...............");
return 0;
}
##################################
#report_genesis_files
#################################
sub report_genesis_files {
@@ -223,7 +231,7 @@ sub rungenesiscmd {
else {
send_msg(2, "Installing with \"$rinstall_cmd\" for runcmd test");
sleep 120; # wait 2 min for install to finish
wait_for_boot();
$value = -1 if &wait_for_node_status("configuring");
}
return $value;
}
@@ -266,7 +274,7 @@ sub rungenesisimg {
} else {
send_msg(2, "Installing with \"$rinstall_cmd\" for runimage test\n");
sleep 120; # wait 2 min for install to finish
wait_for_boot();
$value = -1 if &wait_for_node_status("booting");
}
return $value;
}
@@ -375,8 +383,9 @@ sub clearenv {
`cat $nodestanza | chdef -z`;
unlink("$nodestanza");
}
# "rinstall <node> boot" boots the node from its disk, which carries no operating system,
# so the node reports no destiny and nodelist.status stays at powering-on. Only wait.
sleep 120; # wait 2 min for reboot to finish
wait_for_boot();
return 0;
}
####################################
@@ -448,9 +457,10 @@ sub send_msg {
}
#########################################
### Wait for node to be in "booted" state
### Wait for the node to report the status its destiny implies
##########################################
sub wait_for_boot {
sub wait_for_node_status {
my ($expected) = @_;
my $iterations = 30; # Max wait 30x10 = 5 min
my $sleep_interval = 10;
my $boot_status;
@@ -458,11 +468,11 @@ sub wait_for_boot {
foreach my $i (1..$iterations) {
$boot_status = `lsdef $noderange -i status -c | cut -d'=' -f2`;
chop($boot_status);
if ($boot_status eq "booted") {
if ($boot_status eq $expected) {
return 0;
}
sleep $sleep_interval;
}
print "After $iterations iterations node status: $boot_status \n";
print "After $iterations iterations node status: $boot_status, expected $expected \n";
return 1;
}
+41 -1
View File
@@ -23,11 +23,41 @@ TESTNODE_ARCH="$(uname -m)"
# against a scratch tree.
TFTPDIR="${TFTPDIR:-/tftpboot}"
# grub2.pm names the boot loader grub2.<arch>, with every ppc64 flavour written as "ppc".
TESTNODE_LOADER_ARCH="$TESTNODE_ARCH"
[[ $TESTNODE_LOADER_ARCH =~ ^ppc64 ]] && TESTNODE_LOADER_ARCH="ppc"
STAGED_BOOT_LOADER=""
MASTER_PRIVATE_IP="192.168.1.1"
MASTER_PRIVATE_NETMASK="255.255.0.0"
MASTER_PRIVATE_NETWORK="192_168_0_0-255_255_0_0"
# xCAT builds no grub2 network boot loader for x86_64 or aarch64. The administrator installs
# grub2.<arch> by hand -- docs/source/guides/install-guides/yum/grub2.rst. grub2.pm stops the
# configuration when the file is absent, and this case reads the configuration only.
function stage_boot_loader() {
local loader="$TFTPDIR/boot/grub2/grub2.$TESTNODE_LOADER_ARCH";
if [[ -e $loader ]];then
return 0;
fi
mkdir -p "$TFTPDIR/boot/grub2" || return 1;
: > "$loader" || return 1;
STAGED_BOOT_LOADER="$loader";
echo "Staged an empty boot loader at $loader for the check";
return 0;
}
function unstage_boot_loader() {
if [[ -z $STAGED_BOOT_LOADER ]];then
return 0;
fi
# grub2.pm links grub2-<node> to the loader. Remove the link with the file it points at.
rm -f "$STAGED_BOOT_LOADER" "$TFTPDIR/boot/grub2/grub2-${TESTNODE}";
STAGED_BOOT_LOADER="";
return 0;
}
function check_destiny() {
cmd="chdef ${TESTNODE} arch=${TESTNODE_ARCH} cons=ipmi groups=all ip=${TESTNODE_IP} mac=4e:ee:ee:ee:ee:0e netboot=$NETBOOT tftpserver=$MASTER_PRIVATE_IP xcatmaster=$MASTER_PRIVATE_IP";
runcmd $cmd;
@@ -58,8 +88,15 @@ function check_destiny() {
grep ${TESTNODE} /etc/hosts
cmd="nodeset ${TESTNODE} shell";
runcmd $cmd;
# grub2.pm writes the boot configuration and only then stops on a missing boot loader,
# so the file the check reads below exists even when nodeset failed.
nodeset_rc=$?;
cmd="ip addr del $MASTER_PRIVATE_IP/$MASTER_PRIVATE_NETMASK dev $NET2";
runcmd $cmd;
if [[ $nodeset_rc -ne 0 ]];then
echo "'nodeset ${TESTNODE} shell' FAILED";
return 1;
fi
echo "Check if 'nodeset ${TESTNODE} shell' is added to ${SHELLFOLDER}/${TESTNODE}"
echo "==============================================="
cat "${SHELLFOLDER}/${TESTNODE}"
@@ -97,9 +134,12 @@ while [ "$#" -ge "0" ]; do
SHELLFOLDER="$TFTPDIR/xcat/xnba/nodes"
else
SHELLFOLDER="$TFTPDIR/boot/grub2";
stage_boot_loader || exit 1;
fi
check_destiny ;
if [[ $? -eq 1 ]];then
rc=$?;
unstage_boot_loader;
if [[ $rc -eq 1 ]];then
exit 1
else
exit 0