diff --git a/xCAT-test/bats/ipxe_secureboot_shim_restore.bats b/xCAT-test/bats/ipxe_secureboot_shim_restore.bats index f48218aa9..a5b7d617e 100644 --- a/xCAT-test/bats/ipxe_secureboot_shim_restore.bats +++ b/xCAT-test/bats/ipxe_secureboot_shim_restore.bats @@ -6,7 +6,10 @@ # the next case on that node measures the state this one left. So the helper has to report a # failed restoration instead of its own result. # -# lsdef, nodeset and chdef are stubbed, and every call is recorded in a log the tests read. +# lsdef, nodeset and chdef are stubbed, and every call is recorded in a log the tests read. A +# stub can also be told to fail: STUB_STAT_RC fails "nodeset stat", and STUB_LSDEF_FAIL_ATTR +# fails the lsdef of one attribute. Both failures print nothing on stdout, which is what an +# attribute that is unset prints too. load 'helpers/shell_source' @@ -29,6 +32,9 @@ setup() # provmethod is what sends the helper through chdef on the way out. export STUB_BEFORE=boot export STUB_PROVMETHOD= + export STUB_PROFILE=compute + export STUB_STAT_RC=0 + export STUB_LSDEF_FAIL_ATTR= export STUB_NODESET_RESTORE_RC=0 export STUB_CHDEF_RC=0 export STUB_CHDEF_NETBOOT_RC=0 @@ -56,6 +62,12 @@ while [ $# -gt 0 ]; do *) shift ;; esac done +# A failed lookup prints nothing and exits non-zero. An attribute that is unset prints nothing +# and exits zero, so only the exit status tells the two apart. +if [ -n "$STUB_LSDEF_FAIL_ATTR" ] && [ "$attr" = "$STUB_LSDEF_FAIL_ATTR" ]; then + echo "Error: could not read $attr" >&2 + exit 1 +fi case "$type/$attr" in site/tftpdir) echo " tftpdir=$STUB_TFTPDIR" ;; site/installdir) echo " installdir=$STUB_INSTALLDIR" ;; @@ -63,6 +75,7 @@ case "$type/$attr" in node/arch) echo " arch=$STUB_ARCH" ;; node/os) echo " os=$STUB_OS" ;; node/provmethod) [ -n "$STUB_PROVMETHOD" ] && echo " provmethod=$STUB_PROVMETHOD" ;; + node/profile) [ -n "$STUB_PROFILE" ] && echo " profile=$STUB_PROFILE" ;; osimage/) [ -n "$STUB_NO_OSIMAGE" ] && exit 1; exit 0 ;; *) echo "unexpected lsdef $type $obj $attr" >&2; exit 1 ;; esac @@ -76,6 +89,7 @@ STUB echo "nodeset $*" >>"$STUB_CALLS" case "$2" in stat) + [ "$STUB_STAT_RC" = 0 ] || { echo "Error: cannot reach the server" >&2; exit "$STUB_STAT_RC"; } echo "$1: $STUB_BEFORE" ;; osimage=*) @@ -113,7 +127,7 @@ STUB [ "$status" -eq 0 ] [[ "$output" == *IPXE_SHIM_OK* ]] grep -q "^nodeset $NODE boot$" "$CALLS" - grep -q "^chdef -t node -o $NODE provmethod=$" "$CALLS" + grep -q "^chdef -t node -o $NODE provmethod= profile=compute$" "$CALLS" } @test "a nodeset that cannot put the state back fails the case" { @@ -131,7 +145,7 @@ STUB [ "$status" -ne 0 ] [[ "$output" != *IPXE_SHIM_OK* ]] [[ "$output" == *IPXE_SHIM_FAIL* ]] - grep -q "^chdef -t node -o $NODE provmethod=$" "$CALLS" + grep -q "^chdef -t node -o $NODE provmethod= profile=compute$" "$CALLS" } # A control: the helper already restores the state when its own assertion fails. It holds the @@ -209,3 +223,113 @@ STUB done done } + + +# nodeset stat reports chain.currstate verbatim, and a state can carry an argument: +# "runcmd=bmcsetup" is one state, not the state "runcmd". The helper matched ${before%% *} +# against four names, so a node in any other state kept the installation this case configured +# and the case still printed IPXE_SHIM_OK. +@test "a runcmd state goes back with its command argument" { + STUB_BEFORE='runcmd=bmcsetup' + run "$SCRIPT" "$NODE" + [ "$status" -eq 0 ] + [[ "$output" == *IPXE_SHIM_OK* ]] + grep -q "^nodeset $NODE runcmd=bmcsetup$" "$CALLS" +} + +# The saved image is not the image the helper sets, or the assertion matches the helper's own +# forward call and cannot fail. +@test "an osimage state goes back with its image name" { + STUB_BEFORE="osimage=$OS-x86_64-netboot-compute" + run "$SCRIPT" "$NODE" + [ "$status" -eq 0 ] + [[ "$output" == *IPXE_SHIM_OK* ]] + grep -q "^nodeset $NODE osimage=$OS-x86_64-netboot-compute$" "$CALLS" +} + +# install, netboot and statelite are deprecated: destiny.pm answers "The options install, +# netboot and statelite have been deprecated" and sets errorabort, so replaying one fails and +# leaves the node configured for installation. The helper cannot put such a state back, so it +# must refuse while the node is still untouched. +@test "a saved state the helper cannot replay stops it before the first mutation" { + STUB_BEFORE=install + STUB_NETBOOT=xnba + run "$SCRIPT" "$NODE" + [ "$status" -ne 0 ] + [[ "$output" != *IPXE_SHIM_OK* ]] + [[ "$output" == *"state [install]"* ]] + refute_grep -q "^chdef " "$CALLS" + refute_grep -q "^nodeset $NODE osimage" "$CALLS" +} + +# A pipeline hides the exit status of its first command, so "nodeset stat | sed | head" reported +# success with an empty state. The helper then read the node as stateless and restored nothing. +@test "a nodeset stat that fails stops the helper before the first mutation" { + STUB_STAT_RC=1 + STUB_NETBOOT=xnba + run "$SCRIPT" "$NODE" + [ "$status" -ne 0 ] + [[ "$output" != *IPXE_SHIM_OK* ]] + [[ "$output" == *"nodeset $NODE stat returned non-zero"* ]] + refute_grep -q "^chdef " "$CALLS" + refute_grep -q "^nodeset $NODE osimage" "$CALLS" +} + +# lsdef prints nothing for an attribute that is unset, so a failed lookup and an unset attribute +# were the same empty string. The helper clears the provmethod it believes was unset, so a +# failed lookup made it delete the provmethod the node really had. +@test "a provmethod lookup that fails does not clear the provmethod" { + STUB_LSDEF_FAIL_ATTR=provmethod + STUB_PROVMETHOD=alma9.8-x86_64-install-compute + run "$SCRIPT" "$NODE" + [ "$status" -ne 0 ] + [[ "$output" != *IPXE_SHIM_OK* ]] + [[ "$output" == *"-i provmethod"* ]] + refute_grep -q "^chdef " "$CALLS" + refute_grep -q "^nodeset $NODE osimage" "$CALLS" +} + +# The same read, the same damage: a netboot method read as empty is put back as empty, which +# clears the method the cluster boots the node with. +@test "a netboot lookup that fails does not change the netboot method" { + STUB_LSDEF_FAIL_ATTR=netboot + run "$SCRIPT" "$NODE" + [ "$status" -ne 0 ] + [[ "$output" != *IPXE_SHIM_OK* ]] + [[ "$output" == *"-i netboot"* ]] + refute_grep -q "^chdef " "$CALLS" + refute_grep -q "^nodeset $NODE osimage" "$CALLS" +} + +# A control for the two above: an attribute that is readable and unset is not a failed lookup, +# and the helper still runs. Without this the tests above are satisfied by a helper that refuses +# every empty attribute. +@test "a provmethod that is unset but readable is not a failed lookup" { + STUB_PROVMETHOD= + run "$SCRIPT" "$NODE" + [ "$status" -eq 0 ] + [[ "$output" == *IPXE_SHIM_OK* ]] + grep -q "^nodeset $NODE osimage=$OS-x86_64-install-compute$" "$CALLS" +} + +# setdestiny writes nodetype.provmethod, profile, os and arch from the osimage row it resolved +# (destiny.pm, "my $updateattribs"). On the path where the helper names its own +# --install-compute image, os and arch come back as the node's own values by +# construction of that name, and profile does not: it becomes the row's profile. +@test "the profile that nodeset osimage overwrites goes back too" { + STUB_PROVMETHOD= + STUB_PROFILE=service + run "$SCRIPT" "$NODE" + [ "$status" -eq 0 ] + [[ "$output" == *IPXE_SHIM_OK* ]] + grep -q "^chdef -t node -o $NODE provmethod= profile=service$" "$CALLS" +} + +# An arch that cannot be read is not an arch that is wrong, and the message has to say which. +@test "an arch lookup that fails is reported as a failed lookup" { + STUB_LSDEF_FAIL_ATTR=arch + run "$SCRIPT" "$NODE" + [ "$status" -ne 0 ] + [[ "$output" == *"-i arch"* ]] + [[ "$output" != *"only x86_64 has a UEFI shim"* ]] +}