mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-09-25 01:04:05 +00:00
Merge pull request #7851 from VersatusHPC/fix/updatenode-xcatpost-cleanup
fix(xcatdsklspost): updatenode leaves /xcatpost populated when site.cleanupdiskfullxcatpost is set
This commit is contained in:
@@ -288,7 +288,9 @@ site Attributes:
|
||||
|
||||
cleanupdiskfullxcatpost: (yes/1 or no/0). Set to 'yes' or '1' to clean up the /xcatpost
|
||||
directory on the diskfull nodes after the
|
||||
postscripts are run with no errors. Default is no.
|
||||
postscripts are run with no errors: at deployment, after each
|
||||
updatenode run, and after a reboot. updateflag.awk stays, so
|
||||
the node can still report its status. Default is no.
|
||||
|
||||
db2installloc: The location which the service nodes should mount for
|
||||
the db2 code to install. Format is hostname:/path. If hostname is
|
||||
|
||||
@@ -1198,7 +1198,9 @@ passed as argument rather than by table value',
|
||||
" postscripts are run. Default is no.\n\n" .
|
||||
" cleanupdiskfullxcatpost: (yes/1 or no/0). Set to 'yes' or '1' to clean up the /xcatpost\n" .
|
||||
" directory on the diskfull nodes after the\n" .
|
||||
" postscripts are run with no errors. Default is no.\n\n" .
|
||||
" postscripts are run with no errors: at deployment, after each\n" .
|
||||
" updatenode run, and after a reboot. updateflag.awk stays, so\n" .
|
||||
" the node can still report its status. Default is no.\n\n" .
|
||||
" db2installloc: The location which the service nodes should mount for\n" .
|
||||
" the db2 code to install. Format is hostname:/path. If hostname is\n" .
|
||||
" omitted, it defaults to the management node. Default is /mntdb2.\n\n" .
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
#!/usr/bin/env bats
|
||||
|
||||
load 'helpers/shell_source'
|
||||
|
||||
setup()
|
||||
{
|
||||
DSKLSPOST="$(repo_path 'xCAT/postscripts/xcatdsklspost')"
|
||||
[ -r "$DSKLSPOST" ] || skip "$DSKLSPOST is required"
|
||||
XCATPOST="${BATS_TEST_TMPDIR}/xcatpost"
|
||||
MYPS="${BATS_TEST_TMPDIR}/mypostscript"
|
||||
MSGLOG="${BATS_TEST_TMPDIR}/msgutil.log"
|
||||
export DSKLSPOST XCATPOST MYPS MSGLOG
|
||||
}
|
||||
|
||||
# A scratch /xcatpost with what an updatenode run downloads into it.
|
||||
make_xcatpost()
|
||||
{
|
||||
mkdir -p "$XCATPOST/_xcat"
|
||||
printf 'updateflag\n' >"$XCATPOST/updateflag.awk"
|
||||
printf 'setroute\n' >"$XCATPOST/setroute"
|
||||
printf 'xcatlib\n' >"$XCATPOST/xcatlib.sh"
|
||||
printf 'credentials\n' >"$XCATPOST/_xcat/postscript.cfg"
|
||||
}
|
||||
|
||||
# A scratch mypostscript that carries the site values and the run result.
|
||||
# $1 is the value of return_value, the rest are the site table lines.
|
||||
make_mypostscript()
|
||||
{
|
||||
local result=$1
|
||||
shift
|
||||
{
|
||||
printf '%s\n' '#!/bin/bash'
|
||||
printf 'msgutil_r() { printf "%%s\\n" "$3" >>"%s"; }\n' "$MSGLOG"
|
||||
printf 'MASTER_IP=192.0.2.1\n'
|
||||
printf 'NODE=node1\n'
|
||||
printf 'log_label=xcat.updatenode\n'
|
||||
printf 'return_value=%s\n' "$result"
|
||||
printf '%s\n' "$@"
|
||||
} >"$MYPS"
|
||||
}
|
||||
|
||||
append_cleanup()
|
||||
{
|
||||
(
|
||||
XCATDSKLSPOST_SOURCE_ONLY=1
|
||||
export XCATDSKLSPOST_SOURCE_ONLY
|
||||
# shellcheck disable=SC1090
|
||||
. "$DSKLSPOST"
|
||||
type -t append_xcatpost_cleanup >/dev/null \
|
||||
|| { echo "xcatdsklspost defines no append_xcatpost_cleanup" >&2; exit 99; }
|
||||
append_xcatpost_cleanup "$MYPS" "$XCATPOST"
|
||||
)
|
||||
}
|
||||
|
||||
@test "an updatenode run that succeeds removes the postscripts when site.cleanupdiskfullxcatpost is set" {
|
||||
make_xcatpost
|
||||
make_mypostscript 0 "CLEANUPXCATPOST='no'" "CLEANUPDISKFULLXCATPOST='yes'"
|
||||
append_cleanup
|
||||
|
||||
run bash "$MYPS"
|
||||
[ "$status" -eq 0 ]
|
||||
[ ! -e "$XCATPOST/setroute" ]
|
||||
[ ! -e "$XCATPOST/xcatlib.sh" ]
|
||||
[ ! -e "$XCATPOST/_xcat" ]
|
||||
}
|
||||
|
||||
@test "the cleanup keeps updateflag.awk so the node can still report its status" {
|
||||
make_xcatpost
|
||||
make_mypostscript 0 "CLEANUPXCATPOST='no'" "CLEANUPDISKFULLXCATPOST='yes'"
|
||||
append_cleanup
|
||||
|
||||
run bash "$MYPS"
|
||||
[ "$status" -eq 0 ]
|
||||
[ -f "$XCATPOST/updateflag.awk" ]
|
||||
[ -d "$XCATPOST" ]
|
||||
grep -q 'cleanup of .* completed' "$MSGLOG"
|
||||
}
|
||||
|
||||
@test "an updatenode run that fails keeps the postscripts for diagnosis" {
|
||||
make_xcatpost
|
||||
make_mypostscript 1 "CLEANUPXCATPOST='no'" "CLEANUPDISKFULLXCATPOST='yes'"
|
||||
append_cleanup
|
||||
|
||||
run bash "$MYPS"
|
||||
[ "$status" -eq 0 ]
|
||||
[ -f "$XCATPOST/setroute" ]
|
||||
[ -f "$XCATPOST/_xcat/postscript.cfg" ]
|
||||
refute_grep -q 'cleanup of .* completed' "$MSGLOG"
|
||||
}
|
||||
|
||||
@test "site.cleanupdiskfullxcatpost accepts the other true values of the site table" {
|
||||
make_xcatpost
|
||||
make_mypostscript 0 "CLEANUPDISKFULLXCATPOST='1'"
|
||||
append_cleanup
|
||||
|
||||
run bash "$MYPS"
|
||||
[ "$status" -eq 0 ]
|
||||
[ ! -e "$XCATPOST/setroute" ]
|
||||
}
|
||||
|
||||
# cleanupdiskfullxcatpost names the node type it applies to. updatenode calls this script for a
|
||||
# diskless or statelite node as well, and the site value is one row that reaches all of them, so
|
||||
# without a guard a diskless node loses its postscripts to a setting that does not name it.
|
||||
@test "a netboot node keeps its postscripts when site.cleanupdiskfullxcatpost is set" {
|
||||
make_xcatpost
|
||||
make_mypostscript 0 "NODESETSTATE='netboot'" "CLEANUPDISKFULLXCATPOST='yes'"
|
||||
append_cleanup
|
||||
|
||||
run bash "$MYPS"
|
||||
[ "$status" -eq 0 ]
|
||||
[ -f "$XCATPOST/setroute" ]
|
||||
[ -f "$XCATPOST/_xcat/postscript.cfg" ]
|
||||
# Nothing was appended, so the run cannot report a cleanup it did not do.
|
||||
refute_grep -q 'cleanup of .* completed' "$MSGLOG"
|
||||
}
|
||||
|
||||
@test "a statelite node keeps its postscripts when site.cleanupdiskfullxcatpost is set" {
|
||||
make_xcatpost
|
||||
make_mypostscript 0 "NODESETSTATE='statelite'" "CLEANUPDISKFULLXCATPOST='yes'"
|
||||
append_cleanup
|
||||
|
||||
run bash "$MYPS"
|
||||
[ "$status" -eq 0 ]
|
||||
[ -f "$XCATPOST/setroute" ]
|
||||
}
|
||||
|
||||
@test "a diskful node is still cleaned when site.cleanupdiskfullxcatpost is set" {
|
||||
make_xcatpost
|
||||
make_mypostscript 0 "NODESETSTATE='install'" "CLEANUPDISKFULLXCATPOST='yes'"
|
||||
append_cleanup
|
||||
|
||||
run bash "$MYPS"
|
||||
[ "$status" -eq 0 ]
|
||||
[ ! -e "$XCATPOST/setroute" ]
|
||||
[ -f "$XCATPOST/updateflag.awk" ]
|
||||
}
|
||||
|
||||
# site.cleanupxcatpost names no node type, so it keeps applying to every one. A guard added to the
|
||||
# wrong branch would show up here.
|
||||
@test "site.cleanupxcatpost still applies to a netboot node" {
|
||||
make_xcatpost
|
||||
make_mypostscript 0 "NODESETSTATE='netboot'" "CLEANUPXCATPOST='yes'"
|
||||
append_cleanup
|
||||
|
||||
run bash "$MYPS"
|
||||
[ "$status" -eq 0 ]
|
||||
[ -z "$(ls -A "$XCATPOST")" ]
|
||||
}
|
||||
|
||||
@test "site.cleanupxcatpost removes every file including updateflag.awk" {
|
||||
make_xcatpost
|
||||
make_mypostscript 0 "CLEANUPXCATPOST='yes'" "CLEANUPDISKFULLXCATPOST='no'"
|
||||
append_cleanup
|
||||
|
||||
run bash "$MYPS"
|
||||
[ "$status" -eq 0 ]
|
||||
[ -z "$(ls -A "$XCATPOST")" ]
|
||||
}
|
||||
|
||||
@test "neither setting leaves the downloaded postscripts in place" {
|
||||
make_xcatpost
|
||||
make_mypostscript 0 "CLEANUPXCATPOST='no'" "CLEANUPDISKFULLXCATPOST='no'"
|
||||
append_cleanup
|
||||
|
||||
run bash "$MYPS"
|
||||
[ "$status" -eq 0 ]
|
||||
[ -f "$XCATPOST/setroute" ]
|
||||
[ -f "$XCATPOST/updateflag.awk" ]
|
||||
refute_grep -q -E 'rm -rf|-delete' "$MYPS"
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use FindBin;
|
||||
use File::Temp qw(tempdir);
|
||||
use Test::More;
|
||||
|
||||
# The install-time post script embeds whole postscripts with "#INCLUDE:<path>#" inside a
|
||||
# here-document. The template copies the file verbatim, so a line in the postscript that
|
||||
# equals the here-document delimiter ends that here-document early. The generated install
|
||||
# script then holds the rest of the postscript as shell code.
|
||||
#
|
||||
# That happened: a "cat >> ... <<EOF" added to xcatdsklspost put a line "EOF" in the file,
|
||||
# post.xcat embeds it with "cat >/opt/xcat/xcatdsklspost << 'EOF'", and every diskfull
|
||||
# install ran a post script that bash refused to parse. No postscript ran, so remoteshell
|
||||
# never installed the root key and the node answered "Permission denied (publickey)".
|
||||
|
||||
my $root = "$FindBin::Bin/../..";
|
||||
my $scriptdir = "$root/xCAT-server/share/xcat/install/scripts";
|
||||
die "$scriptdir not found\n" unless -d $scriptdir;
|
||||
|
||||
# Return every (post script, delimiter, embedded file) the install scripts declare.
|
||||
sub embeddings {
|
||||
my @found;
|
||||
opendir(my $dh, $scriptdir) or die "cannot read $scriptdir: $!";
|
||||
my @posts = sort grep { /^post\./ and -f "$scriptdir/$_" } readdir($dh);
|
||||
closedir $dh;
|
||||
|
||||
for my $post (@posts) {
|
||||
open(my $fh, '<', "$scriptdir/$post") or die "cannot read $post: $!";
|
||||
my @lines = <$fh>;
|
||||
close $fh;
|
||||
for my $i (0 .. $#lines - 1) {
|
||||
# cat >file << 'EOF' / (cat << 'EOF'
|
||||
my ($delim) = $lines[$i] =~ /<<-?\s*'([A-Za-z_][A-Za-z0-9_]*)'\s*$/;
|
||||
next unless defined $delim;
|
||||
my ($include) = $lines[$i + 1] =~ /^#INCLUDE:(.+)#\s*$/;
|
||||
next unless defined $include;
|
||||
# The include path is a template expression; only its last element names the file.
|
||||
next unless $include =~ m{/postscripts/([^/#]+)$};
|
||||
my $file = "$root/xCAT/postscripts/$1";
|
||||
next unless -f $file;
|
||||
push @found, { post => $post, delim => $delim, file => $file, name => $1 };
|
||||
}
|
||||
}
|
||||
return @found;
|
||||
}
|
||||
|
||||
my @embeddings = embeddings();
|
||||
die 'no "#INCLUDE:" inside a here-document was found; the scan no longer matches the install scripts'
|
||||
unless @embeddings;
|
||||
|
||||
my $scratch = tempdir(CLEANUP => 1);
|
||||
|
||||
for my $e (@embeddings) {
|
||||
my $label = "$e->{post} embeds $e->{name} in a <<'$e->{delim}' here-document";
|
||||
|
||||
open(my $fh, '<', $e->{file}) or die "cannot read $e->{file}: $!";
|
||||
my @body = <$fh>;
|
||||
close $fh;
|
||||
|
||||
my @collisions = grep { $body[$_] =~ /^\Q$e->{delim}\E\s*$/ } 0 .. $#body;
|
||||
is(scalar @collisions, 0,
|
||||
"$label, and no line of $e->{name} is \"$e->{delim}\"")
|
||||
or diag("$e->{name} line(s) " . join(', ', map { $_ + 1 } @collisions)
|
||||
. " end the here-document early");
|
||||
|
||||
# Assemble the embedding the way the template does and let bash parse it. bash reads the
|
||||
# here-document up to the first delimiter line, so an early end leaves the rest of the
|
||||
# postscript as commands. -n parses and runs nothing.
|
||||
my $script = "$scratch/$e->{post}.$e->{name}.sh";
|
||||
open(my $out, '>', $script) or die "cannot write $script: $!";
|
||||
print $out "cat >\"\$1\" <<'$e->{delim}'\n", @body, "$e->{delim}\n";
|
||||
close $out;
|
||||
|
||||
my $errors = qx{bash -n \Q$script\E 2>&1};
|
||||
is($?, 0, "$label, and bash parses the result")
|
||||
or diag($errors);
|
||||
}
|
||||
|
||||
done_testing();
|
||||
@@ -224,6 +224,60 @@ parsehttpserver ()
|
||||
fi
|
||||
}
|
||||
|
||||
#####################################################
|
||||
# Append to the generated mypostscript the /xcatpost cleanup the site table asks for.
|
||||
#
|
||||
# The append happens on every path this script serves: node deployment, and each
|
||||
# updatenode, moncfg or reboot run.
|
||||
#
|
||||
# The two site attributes do not ask for the same thing. cleanupxcatpost removes
|
||||
# every file and ignores the result of the run. cleanupdiskfullxcatpost keeps
|
||||
# updateflag.awk, which the node needs to report its status, and removes nothing
|
||||
# after a failed run, so the postscripts stay on the node for diagnosis.
|
||||
# xcatinstallpost applies the same rule at install time.
|
||||
#
|
||||
# cleanupdiskfullxcatpost names the node type it applies to, and this script serves
|
||||
# every type: updatenode calls it for a diskless or statelite node as well. The site
|
||||
# value is one row and reaches all of them, so the diskful branch reads NODESETSTATE
|
||||
# and does nothing for netboot or statelite. cleanupxcatpost carries no such word and
|
||||
# keeps applying everywhere.
|
||||
#
|
||||
# $1 - path of the generated mypostscript
|
||||
# $2 - the xcatpost directory to remove the postscripts from
|
||||
#####################################################
|
||||
append_xcatpost_cleanup()
|
||||
{
|
||||
local mypostscript=$1
|
||||
local postdir=$2
|
||||
local cleanupxcatpost
|
||||
local cleanupdiskfullxcatpost
|
||||
|
||||
cleanupxcatpost=`grep CLEANUPXCATPOST= "$mypostscript" |awk -F = '{print $2}' | tr -d \'\" | tr A-Z a-z`
|
||||
if [[ "$cleanupxcatpost" =~ ^(1|yes|y)$ ]]; then
|
||||
echo "cd /" >> "$mypostscript"
|
||||
# /xcatpost might be read-only for statelite nodes
|
||||
echo "rm -rf $postdir/*" >> "$mypostscript"
|
||||
fi
|
||||
|
||||
local nodesetstate
|
||||
nodesetstate=`grep '^NODESETSTATE=' "$mypostscript" |awk -F = '{print $2}' | tr -d \'\" | tr A-Z a-z`
|
||||
cleanupdiskfullxcatpost=`grep CLEANUPDISKFULLXCATPOST= "$mypostscript" |awk -F = '{print $2}' | tr -d \'\" | tr A-Z a-z`
|
||||
if [[ "$cleanupdiskfullxcatpost" =~ ^(1|yes|y)$ ]] \
|
||||
&& [ "$nodesetstate" != "netboot" ] && [ "$nodesetstate" != "statelite" ]; then
|
||||
# post.xcat and the other install scripts embed this whole file in a here-document
|
||||
# that ends at a line "EOF". A line "EOF" here ends that here-document early and
|
||||
# leaves the rest of this file as shell code in the generated install script.
|
||||
cat >> "$mypostscript" <<XCATPOST_CLEANUP
|
||||
if [ "\$return_value" -eq "0" ]; then
|
||||
cd /
|
||||
find $postdir/ -type f -not -name 'updateflag.awk' -delete
|
||||
find $postdir/ -type d -empty -delete
|
||||
msgutil_r \$MASTER_IP "info" "cleanup of $postdir completed.(\$NODE)" "/var/log/xcat/xcat.log" "\$log_label"
|
||||
fi
|
||||
XCATPOST_CLEANUP
|
||||
fi
|
||||
}
|
||||
|
||||
if [ "$XCATDSKLSPOST_SOURCE_ONLY" = "1" ]; then
|
||||
return 0 2>/dev/null || exit 0
|
||||
fi
|
||||
@@ -1086,12 +1140,7 @@ fi
|
||||
DHCP_TMP=`sed 's/\(DHCPINTERFACES=\)\(.*\)$/\1"\2"/' /$xcatpost/mypostscript`
|
||||
echo "$DHCP_TMP" > /$xcatpost/mypostscript
|
||||
|
||||
CLEANUPXCATPOST=`grep CLEANUPXCATPOST= /$xcatpost/mypostscript |awk -F = '{print $2}' | tr -d \'\" | tr A-Z a-z`
|
||||
if [[ "$CLEANUPXCATPOST" =~ ^(1|yes|y)$ ]]; then
|
||||
echo "cd /" >> /$xcatpost/mypostscript
|
||||
# /xcatpost might be read-only for statelite nodes
|
||||
echo "rm -rf /$xcatpost/*" >> /$xcatpost/mypostscript
|
||||
fi
|
||||
append_xcatpost_cleanup /$xcatpost/mypostscript /$xcatpost
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user