mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-10-01 16:31:41 +00:00
Merge pull request #7889 from VersatusHPC/fix/service-node-ub
fix(xcat-server): an Ubuntu service node never installs, and never serves its compute node
This commit is contained in:
@@ -1708,6 +1708,43 @@ sub nfs_export_exists {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
=head3 nfs_export_line
|
||||
|
||||
The /etc/exports line for a directory a service node serves to its nodes.
|
||||
|
||||
Arguments:
|
||||
$dir the directory to export
|
||||
reexport true when $dir is itself an NFS mount
|
||||
|
||||
Returns:
|
||||
one export line, with no trailing newline.
|
||||
|
||||
exportfs refuses an NFS mount without an explicit fsid, and crossmnt lets a client cross into
|
||||
the mount below. The fsid is derived from the path, so a restart does not make client mounts
|
||||
stale.
|
||||
|
||||
=cut
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
sub nfs_export_line {
|
||||
my ( $dir, %opts ) = _nfs_method_args(@_);
|
||||
my $options = 'rw,no_root_squash,sync,no_subtree_check,insecure';
|
||||
if ( $opts{reexport} ) {
|
||||
$options .= ',crossmnt,fsid=' . _nfs_export_fsid($dir);
|
||||
}
|
||||
return "$dir *($options)";
|
||||
}
|
||||
|
||||
# A stable, non-zero fsid for a path. 0 is reserved for the export root.
|
||||
sub _nfs_export_fsid {
|
||||
my ($dir) = @_;
|
||||
my $sum = 0;
|
||||
$sum = ( $sum * 31 + ord($_) ) % 2147483647 for split //, $dir;
|
||||
return $sum || 1;
|
||||
}
|
||||
|
||||
sub _ensure_nfs_exported {
|
||||
my ($nfsserver, $nfsdirectory, $callback, %opts) = @_;
|
||||
my $export_options = 'rw,no_root_squash,sync,no_subtree_check,insecure';
|
||||
@@ -2512,4 +2549,29 @@ sub searchcompressedrootimg{
|
||||
}
|
||||
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
=head3 named_service_action
|
||||
|
||||
Which service action brings a freshly written named configuration into effect.
|
||||
|
||||
Linux gets a restart. On Debian the package already runs named, so a start is a no-op and
|
||||
the daemon keeps serving the configuration it read at install time.
|
||||
|
||||
Arguments: the platform, 'aix' or 'linux'
|
||||
Returns: 'start', 'restart', or '' for a platform with no action
|
||||
|
||||
=cut
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
sub named_service_action {
|
||||
my ($platform) = @_;
|
||||
$platform = '' unless defined $platform;
|
||||
return 'start' if $platform eq 'aix';
|
||||
return 'restart' if $platform eq 'linux';
|
||||
return '';
|
||||
}
|
||||
|
||||
|
||||
1;
|
||||
|
||||
@@ -788,6 +788,8 @@ sub setup_FTP
|
||||
=cut
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
sub setup_DNS
|
||||
{
|
||||
my $srvclist = shift;
|
||||
@@ -827,11 +829,15 @@ sub setup_DNS
|
||||
#}
|
||||
|
||||
#my $rc = xCAT::Utils->startService($serv);
|
||||
# Restart, not start. makenamed.conf has just rewritten named.conf, and a start is a no-op
|
||||
# where the package already runs the daemon -- Debian does -- so the service keeps serving the
|
||||
# configuration it read at install time.
|
||||
my $rc = 0;
|
||||
if (xCAT::Utils->isAIX()) {
|
||||
my $action = xCAT::SvrUtils::named_service_action(xCAT::Utils->isAIX() ? 'aix' : 'linux');
|
||||
if ($action eq 'start') {
|
||||
$rc = xCAT::Utils->startService("named");
|
||||
} elsif (xCAT::Utils->isLinux()) {
|
||||
$rc = xCAT::Utils->startservice("named");
|
||||
} elsif ($action eq 'restart') {
|
||||
$rc = xCAT::Utils->restartservice("named");
|
||||
}
|
||||
|
||||
if ($rc != 0)
|
||||
@@ -919,6 +925,20 @@ sub setup_NFS
|
||||
my $rc = 0;
|
||||
if (xCAT::Utils->isLinux())
|
||||
{
|
||||
# nfsserver=1 means this node serves files. With site.installloc set the install directory
|
||||
# is itself a mount here, and re-exporting one needs an fsid.
|
||||
my $installdir = xCAT::TableUtils->getInstallDir() || "/install";
|
||||
unless (xCAT::SvrUtils->nfs_export_exists($installdir))
|
||||
{
|
||||
my $line = xCAT::SvrUtils->nfs_export_line($installdir,
|
||||
reexport => (xCAT::Utils->isMounted($installdir) ? 1 : 0));
|
||||
xCAT::Utils->runcmd("/bin/echo '$line' >> /etc/exports", 0);
|
||||
if ($::RUNCMD_RC != 0)
|
||||
{
|
||||
xCAT::MsgUtils->message('S', "Could not add $installdir to /etc/exports.");
|
||||
}
|
||||
}
|
||||
|
||||
#my $os = xCAT::Utils->osver();
|
||||
#if ($os =~ /sles.*/)
|
||||
#{
|
||||
@@ -930,6 +950,13 @@ sub setup_NFS
|
||||
# $rc = xCAT::Utils->startService("nfs");
|
||||
#}
|
||||
$rc = xCAT::Utils->startservice("nfs");
|
||||
|
||||
# After the daemon, so a fresh export is picked up.
|
||||
xCAT::Utils->runcmd("/usr/sbin/exportfs -a", 0);
|
||||
if ($::RUNCMD_RC != 0)
|
||||
{
|
||||
xCAT::MsgUtils->message('S', "Error with /usr/sbin/exportfs -a.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{ #AIX
|
||||
|
||||
@@ -19,7 +19,7 @@ is_lsb_ubuntu ()
|
||||
exit 1 # Not Ubuntu
|
||||
}
|
||||
|
||||
' /etc/lsb-release >/dev/null 2>&1
|
||||
' "${1:-/etc/lsb-release}" >/dev/null 2>&1
|
||||
|
||||
# Routine exit status is exit status of the last command -- the awk script.
|
||||
#
|
||||
@@ -28,14 +28,56 @@ is_lsb_ubuntu ()
|
||||
}
|
||||
|
||||
|
||||
DIRECTORY=/var/named
|
||||
# forwarder_addresses [<resolv.conf> [<systemd-resolved uplink>]]
|
||||
#
|
||||
# The addresses named must forward to, one per line.
|
||||
#
|
||||
# A loopback address is not a forwarder: under systemd-resolved /etc/resolv.conf holds the
|
||||
# 127.0.0.53 stub, and that stub points back at this named. The real servers are in the
|
||||
# systemd-resolved uplink file.
|
||||
forwarder_addresses()
|
||||
{
|
||||
_fa_resolv=${1:-/etc/resolv.conf}
|
||||
_fa_uplink=${2:-/run/systemd/resolve/resolv.conf}
|
||||
_fa_addrs=$(_fa_usable "$_fa_resolv")
|
||||
if [ -z "$_fa_addrs" ]; then
|
||||
_fa_addrs=$(_fa_usable "$_fa_uplink")
|
||||
fi
|
||||
[ -n "$_fa_addrs" ] && printf '%s\n' "$_fa_addrs"
|
||||
return 0
|
||||
}
|
||||
|
||||
# check for SLES
|
||||
grep -s -q sles /etc/os-release
|
||||
IS_SLES=$?
|
||||
if [ -f /etc/SuSE-release ] || [ $IS_SLES -eq 0 ]; then
|
||||
DIRECTORY=/var/lib/named
|
||||
fi
|
||||
_fa_usable()
|
||||
{
|
||||
[ -r "$1" ] || return 0
|
||||
awk '$1 == "nameserver" && $2 !~ /^127\./ && $2 != "::1" { print $2 }' "$1" 2>/dev/null
|
||||
}
|
||||
|
||||
# named_directory [<lsb-release> [<os-release> [<SuSE-release>]]]
|
||||
#
|
||||
# The working directory for named. It must be writable by the user named drops to: Debian runs
|
||||
# it as "bind" and ships /var/cache/bind, while /var/named is created root-owned.
|
||||
named_directory()
|
||||
{
|
||||
_nd_lsb=${1:-/etc/lsb-release}
|
||||
_nd_os=${2:-/etc/os-release}
|
||||
_nd_suse=${3:-/etc/SuSE-release}
|
||||
|
||||
if is_lsb_ubuntu "$_nd_lsb"; then
|
||||
echo /var/cache/bind
|
||||
return 0
|
||||
fi
|
||||
if [ -f "$_nd_suse" ] || grep -s -q sles "$_nd_os"; then
|
||||
echo /var/lib/named
|
||||
return 0
|
||||
fi
|
||||
echo /var/named
|
||||
}
|
||||
|
||||
# A test loads this file for the routines above, so the rest must not run on a require.
|
||||
[ "${MAKENAMED_LIB:-}" = 1 ] && return 0
|
||||
|
||||
DIRECTORY=$(named_directory)
|
||||
FILE=/etc/named.conf
|
||||
|
||||
if ( is_lsb_ubuntu ); then
|
||||
@@ -58,7 +100,7 @@ echo "options {
|
||||
forward only;
|
||||
forwarders {" >$FILE
|
||||
|
||||
for i in $(grep "^nameserver" /etc/resolv.conf | awk '{print $2}')
|
||||
forwarder_addresses | while read -r i
|
||||
do
|
||||
echo " $i;"
|
||||
done >>$FILE
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
#cloud-config
|
||||
autoinstall:
|
||||
version: 1
|
||||
keyboard: {layout: us, toggle: null, variant: ''}
|
||||
locale: en_US
|
||||
network:
|
||||
version: 2
|
||||
ethernets:
|
||||
xcat-install:
|
||||
match:
|
||||
name: "e*"
|
||||
dhcp4: true
|
||||
ssh:
|
||||
allow-pw: true
|
||||
authorized-keys: []
|
||||
install-server: true
|
||||
identity:
|
||||
realname: 'xCAT Admin'
|
||||
username: xcatadm
|
||||
hostname: #HOSTNAME#
|
||||
password: "#CRYPTORLOCKED:passwd:key=system,username=root:password#"
|
||||
#UBUNTU_SUBIQUITY_APT_CONFIG#
|
||||
kernel:
|
||||
package: linux-generic
|
||||
user-data:
|
||||
hostname: #HOSTNAME#
|
||||
disable_root: false
|
||||
package_update: false
|
||||
package_upgrade: false
|
||||
timezone: #TABLE:site:key=timezone:value#
|
||||
chpasswd:
|
||||
list:
|
||||
- "root:#CRYPTORLOCKED:passwd:key=system,username=root:password#"
|
||||
expire: false
|
||||
packages:
|
||||
- openssh-server
|
||||
- openssh-client
|
||||
- wget
|
||||
- vim
|
||||
- rsync
|
||||
- busybox-static
|
||||
- gawk
|
||||
- bind9-dnsutils
|
||||
- chrony
|
||||
- gpg
|
||||
- #INCLUDE_DEFAULT_PKGLIST_AUTOINSTALL#
|
||||
early-commands:
|
||||
- |
|
||||
exec >/tmp/pre-install.log 2>&1
|
||||
set -eux
|
||||
echo "=== xCAT early-commands ==="
|
||||
echo "=== network ==="
|
||||
ip addr show
|
||||
ip route show
|
||||
echo "=== disk detection ==="
|
||||
wget -T 30 -O /tmp/getinstdisk http://#XCATVAR:XCATMASTER##COLONHTTPPORT#/install/autoinst/getinstdisk
|
||||
test -s /tmp/getinstdisk
|
||||
chmod u+x /tmp/getinstdisk
|
||||
/tmp/getinstdisk
|
||||
echo "=== pre-install script ==="
|
||||
wget -T 30 -O /tmp/pre.sh http://#XCATVAR:XCATMASTER##COLONHTTPPORT#/install/autoinst/#HOSTNAME#.pre
|
||||
test -s /tmp/pre.sh
|
||||
chmod u+x /tmp/pre.sh
|
||||
/tmp/pre.sh
|
||||
echo "=== storage injection ==="
|
||||
test -s /tmp/partitionfile
|
||||
sed -i '/^\.\.\.$/d' /autoinstall.yaml
|
||||
cat /tmp/partitionfile >> /autoinstall.yaml
|
||||
echo "=== DNS setup ==="
|
||||
# glibc's resolver discards a nameserver line naming a host, so writing the xcatmaster
|
||||
# *name* leaves the installer -- and the in-target apt that inherits this file -- with no
|
||||
# DNS. Resolve it here, while the live installer's DHCP resolv.conf still works, and keep
|
||||
# that file when the name does not resolve. A name written back is the case this step
|
||||
# exists to prevent.
|
||||
xcatmaster_host="#TABLE:noderes:$NODE:xcatmaster#"
|
||||
xcatmaster_ip="$(getent ahostsv4 "$xcatmaster_host" | awk '{print $1; exit}')"
|
||||
if [ -n "$xcatmaster_ip" ]; then
|
||||
rm -f /etc/resolv.conf
|
||||
echo "nameserver $xcatmaster_ip" >/etc/resolv.conf
|
||||
else
|
||||
echo "xcat: $xcatmaster_host has no IPv4 address; keeping the resolver DHCP supplied"
|
||||
fi
|
||||
echo "domain #TABLE:site:key=domain:value#" >>/etc/resolv.conf
|
||||
echo "=== early-commands complete ==="
|
||||
late-commands:
|
||||
- mkdir -p /target/var/log/xcat/
|
||||
- '{
|
||||
cat /tmp/pre-install.log >> /target/var/log/xcat/xcat.log;
|
||||
installnic="#SUBIQUITYINSTALLNIC#";
|
||||
installmac="#SUBIQUITYINSTALLMAC#";
|
||||
mkdir -p /target/etc/netplan;
|
||||
if [ -z "${installnic}" ]; then
|
||||
printf ''%s\n'' "network:" " version: 2" " ethernets:" " xcat-install:" " match:" " macaddress: \"${installmac}\"" " dhcp4: true" " dhcp4-overrides:" " use-domains: true" >/target/etc/netplan/00-xcat-install.yaml;
|
||||
else
|
||||
printf ''%s\n'' "network:" " version: 2" " ethernets:" " xcat-install:" " match:" " macaddress: \"${installmac}\"" " set-name: ${installnic}" " dhcp4: true" " dhcp4-overrides:" " use-domains: true" >/target/etc/netplan/00-xcat-install.yaml;
|
||||
fi;
|
||||
chmod 600 /target/etc/netplan/00-xcat-install.yaml;
|
||||
printf ''%s\n'' ''#HOSTNAME#'' >/target/etc/hostname;
|
||||
if grep -q ''^127\.0\.1\.1'' /target/etc/hosts; then
|
||||
sed -i ''s/^127\.0\.1\.1.*/127.0.1.1 #HOSTNAME#/'' /target/etc/hosts;
|
||||
else
|
||||
printf ''%s\n'' ''127.0.1.1 #HOSTNAME#'' >>/target/etc/hosts;
|
||||
fi;
|
||||
mkdir -p /target/etc/cloud;
|
||||
touch /target/etc/cloud/cloud-init.disabled;
|
||||
echo "Updating kernel command line...";
|
||||
printf ''%s\n'' ''GRUB_CMDLINE_LINUX="#TABLEBLANKOKAY:bootparams:$NODE:kcmdline#"'' >>/target/etc/default/grub;
|
||||
curtin in-target --target /target update-grub2;
|
||||
echo "Running late_command Installation script...";
|
||||
wget -T 30 http://#XCATVAR:XCATMASTER##COLONHTTPPORT#/install/autoinst/#HOSTNAME#.post;
|
||||
chmod u+x #HOSTNAME#.post;
|
||||
cp ./#HOSTNAME#.post /target/root/post.script;
|
||||
curtin in-target --target /target /root/post.script;
|
||||
} >>/target/var/log/xcat/xcat.log 2>&1'
|
||||
# The installer's sources for the otherpkgs repository and the pkgdir mirrors, and the apt
|
||||
# configuration that kept recommended packages out, served the install; ospkgs and otherpkgs
|
||||
# write their own after the first boot. A separate item, so the status of the post script above
|
||||
# still decides whether the install goes on.
|
||||
- rm -f /target/etc/apt/sources.list.d/xcat-otherpkgs-*.list /target/etc/apt/sources.list.d/xcat-otherpkgs-*.sources /target/etc/apt/sources.list.d/xcat-pkgdir-*.list /target/etc/apt/sources.list.d/xcat-pkgdir-*.sources /target/etc/apt/apt.conf.d/94curtin-config
|
||||
# Flip the node to local-disk boot, or it PXE-loops back into the installer on reboot.
|
||||
# xcatd's install monitor greets with "ready", then answers "next" with "done" and runs
|
||||
# "nodeset <node> next". Require both tokens: another service on that port is not a flipped
|
||||
# node. The monitor listens on site.xcatiport; TABLEBLANKOKAY because an absent key fails a
|
||||
# plain TABLE lookup, and with it the whole template.
|
||||
# Retry, and log a failure rather than reinstall silently. Both reads are bounded: a monitor
|
||||
# that accepts and never answers would block a bare read and hang the install here. A
|
||||
# monitor that died and never came back is #7759.
|
||||
- ['bash', '-c', 'xm=#XCATVAR:XCATMASTER#; port="#TABLEBLANKOKAY:site:key=xcatiport:value#"; [ -n "$port" ] || port=3002; ok=0; for i in 1 2 3 4 5; do if exec 3<>/dev/tcp/$xm/$port; then if read -r -t 10 hello <&3 && [ "$hello" = "ready" ]; then printf "next\n" >&3; if read -r -t 10 ack <&3 && [ "$ack" = "done" ]; then ok=1; fi; fi; exec 3>&- 3<&-; [ "$ok" = 1 ] && break; fi; sleep 5; done; if [ "$ok" != 1 ]; then echo "xcat: FAILED to flip $(hostname) to local-disk boot via $xm:$port; the node will PXE back into the installer" >>/target/var/log/xcat/xcat.log; fi; exit 0']
|
||||
error-commands:
|
||||
# Subiquity waits for every error command to return. "nc -l 8080" waits for a collector, which
|
||||
# an unattended install does not have. Keep the archive on the installer, and print the end of
|
||||
# the curtin log to the console, which the management node records.
|
||||
#
|
||||
# Both destinations are read from the environment so a test can point them at its own
|
||||
# directory. The installer sets neither, so an install writes where it always did.
|
||||
- ['sh', '-c', 'tar -c --ignore-failed-read --transform="s/^/#HOSTNAME#-logs\//" /var/crash /var/log/installer /tmp/pre-install.log /autoinstall.yaml >"${XCAT_ERROR_ARCHIVE:-/run/#HOSTNAME#-logs.tar}" 2>/dev/null; exit 0']
|
||||
- ['sh', '-c', 'tail -n 80 /var/log/installer/curtin-install.log >"${XCAT_ERROR_CONSOLE:-/dev/console}" 2>/dev/null; exit 0']
|
||||
@@ -0,0 +1,240 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# check_provisioning_source.sh --baseline <compute node> <service node>
|
||||
# check_provisioning_source.sh <compute node> <service node>
|
||||
# check_provisioning_source.sh --count <ip> [<baseline spec>]
|
||||
#
|
||||
# Answer which server sent the compute node its boot payload during THIS run.
|
||||
#
|
||||
# The hierarchy cases set noderes.servicenode and read SERVICEGROUP back out of the compute
|
||||
# node's xcatinfo. That records what xCAT wrote, not where the node fetched from. The management
|
||||
# node, the service node and the compute node share one subnet, both dhcpd instances hold a
|
||||
# reservation for the compute node, and xCAT does not arbitrate between them. The compute node
|
||||
# takes whichever server answers first, so a flat provision satisfies every other assertion the
|
||||
# cases make.
|
||||
#
|
||||
# The httpd access logs settle it. The xNBA exchange hands out an http:// filename, so the
|
||||
# kernel, the initrd, the root image and the install tree are HTTP requests logged against the
|
||||
# compute node's address on the server that answered them.
|
||||
#
|
||||
# Two things bound what counts, and without either one the answer is wrong in both directions.
|
||||
# Run --baseline before provisioning: an earlier flat run leaves management-node requests for the
|
||||
# same address, and counting the whole log fails a later hierarchical run for them. And only a
|
||||
# request under /install or /tftpboot is a boot payload: a 404 for /favicon.ico is a request from
|
||||
# the compute node that carries no payload, and it must not stand for one. wget asks for the root
|
||||
# image as //install/..., so the leading slash repeats.
|
||||
#
|
||||
# Scope: the PXE ROM exchange hands out xcat/xnba.kpxe over TFTP and httpd never sees it.
|
||||
# xnba.kpxe is the same binary on both servers, so it decides nothing about the fetch source.
|
||||
#
|
||||
# Run this on the management node. It reads the service node's log with "xdsh -e", which copies
|
||||
# this script to the service node and runs it there with --count.
|
||||
|
||||
set -u
|
||||
|
||||
TOKEN=XCAT_HTTPD_REQUESTS
|
||||
STATE="${XCAT_PROV_SOURCE_STATE:-/var/tmp/xcat-provisioning-source.base}"
|
||||
|
||||
# Every readable candidate log. The combined format puts the client address in field 1, and the
|
||||
# Debian per-vhost format puts the vhost there and the client in field 2.
|
||||
local_logs()
|
||||
{
|
||||
# An explicit log REPLACES the search. Without this a test that points here still reads the
|
||||
# host's own apache log, so it cannot control what the check counts.
|
||||
if [ -n "${XCAT_HTTPD_ACCESS_LOG:-}" ]; then
|
||||
[ -r "$XCAT_HTTPD_ACCESS_LOG" ] && echo "$XCAT_HTTPD_ACCESS_LOG"
|
||||
return 0
|
||||
fi
|
||||
|
||||
for f in /var/log/httpd/access_log \
|
||||
/var/log/apache2/access.log \
|
||||
/var/log/apache2/access_log \
|
||||
/var/log/apache2/other_vhosts_access.log
|
||||
do
|
||||
[ -r "$f" ] || continue
|
||||
echo "$f"
|
||||
done
|
||||
}
|
||||
|
||||
# Record how many lines each log holds now, as file:lines joined by commas.
|
||||
baseline_local()
|
||||
{
|
||||
spec=""
|
||||
for f in $(local_logs); do
|
||||
n=$(wc -l <"$f" 2>/dev/null | tr -d ' ')
|
||||
[ -n "$n" ] || n=0
|
||||
spec="$spec,$f:$n"
|
||||
done
|
||||
echo "$TOKEN base $(echo "$spec" | sed 's/^,//')"
|
||||
}
|
||||
|
||||
# Count the boot-payload requests this address made AFTER the baseline. A log shorter than its
|
||||
# baseline was rotated, so its baseline no longer locates anything and the whole file is new.
|
||||
count_local_requests()
|
||||
{
|
||||
ip="$1"
|
||||
spec="${2:-}"
|
||||
logs=$(local_logs | tr '\n' ' ')
|
||||
|
||||
if [ -z "$logs" ]; then
|
||||
echo "$TOKEN nolog 0 0 0"
|
||||
return 0
|
||||
fi
|
||||
|
||||
checked=""
|
||||
for f in $logs; do
|
||||
base=$(echo "$spec" | tr ',' '\n' | sed -n "s|^$f:||p" | head -1)
|
||||
[ -n "$base" ] || base=0
|
||||
now=$(wc -l <"$f" 2>/dev/null | tr -d ' ')
|
||||
[ -n "$now" ] || now=0
|
||||
[ "$now" -lt "$base" ] && base=0
|
||||
checked="$checked,$f:$base"
|
||||
done
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
awk -v ip="$ip" -v token="$TOKEN" -v bases="$(echo "$checked" | sed 's/^,//')" '
|
||||
BEGIN {
|
||||
n = split(bases, a, ",")
|
||||
for (i = 1; i <= n; i++) {
|
||||
if (a[i] == "") continue
|
||||
p = index(a[i], ":")
|
||||
if (p) base[substr(a[i], 1, p - 1)] = substr(a[i], p + 1) + 0
|
||||
}
|
||||
}
|
||||
{
|
||||
total++
|
||||
b = (FILENAME in base) ? base[FILENAME] : 0
|
||||
if (FNR <= b) next
|
||||
fresh++
|
||||
if ($1 != ip && $2 != ip) next
|
||||
path = ""
|
||||
for (i = 1; i <= NF; i++) if ($i ~ /^"(GET|HEAD|POST)$/) { path = $(i + 1); break }
|
||||
if (path ~ /^\/+(install|tftpboot)\//) payload++
|
||||
}
|
||||
END { print token, "ok", payload + 0, fresh + 0, total + 0 }
|
||||
' $logs
|
||||
}
|
||||
|
||||
# xdsh prefixes each line with the node name, so read the fields after the token.
|
||||
read_counts()
|
||||
{
|
||||
awk -v token="$TOKEN" '
|
||||
{ for (i = 1; i <= NF; i++) if ($i == token) { print $(i+1), $(i+2), $(i+3), $(i+4); exit } }
|
||||
'
|
||||
}
|
||||
|
||||
read_baseline()
|
||||
{
|
||||
awk -v token="$TOKEN" '
|
||||
{ for (i = 1; i <= NF; i++) if ($i == token && $(i+1) == "base") { print $(i+2); exit } }
|
||||
'
|
||||
}
|
||||
|
||||
node_address()
|
||||
{
|
||||
node="$1"
|
||||
addr=$(lsdef -t node -o "$node" -i ip 2>/dev/null | sed -n 's/^[[:space:]]*ip=//p' | head -1)
|
||||
[ -n "$addr" ] || addr=$(getent ahostsv4 "$node" 2>/dev/null | awk '{ print $1; exit }')
|
||||
echo "$addr"
|
||||
}
|
||||
|
||||
if [ "${1:-}" = "--count" ]; then
|
||||
count_local_requests "${2:-}" "${3:-}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "${1:-}" = "--baseline-local" ]; then
|
||||
baseline_local
|
||||
exit 0
|
||||
fi
|
||||
|
||||
MODE=check
|
||||
if [ "${1:-}" = "--baseline" ]; then
|
||||
MODE=baseline
|
||||
shift
|
||||
fi
|
||||
|
||||
CN="${1:-}"
|
||||
SN="${2:-}"
|
||||
if [ -z "$CN" ] || [ -z "$SN" ]; then
|
||||
echo "provisioning source error: usage: $0 [--baseline] <compute node> <service node>" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
SELF=$(readlink -f "$0")
|
||||
MN=$(hostname)
|
||||
|
||||
if [ "$MODE" = baseline ]; then
|
||||
MN_BASE=$(baseline_local | read_baseline)
|
||||
SN_BASE=$(xdsh "$SN" -e "$SELF" --baseline-local 2>&1 | read_baseline)
|
||||
if [ -z "$MN_BASE" ]; then
|
||||
echo "provisioning source error: no access log to baseline on $MN" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "$SN_BASE" ]; then
|
||||
echo "provisioning source error: no access log to baseline on $SN" >&2
|
||||
exit 1
|
||||
fi
|
||||
printf 'MN %s\nSN %s\n' "$MN_BASE" "$SN_BASE" >"$STATE" || exit 1
|
||||
echo "provisioning source baseline recorded in $STATE"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Without a baseline this cannot tell a request from this run from one an earlier run left
|
||||
# behind, so it refuses rather than answering from the whole log.
|
||||
if [ ! -r "$STATE" ]; then
|
||||
echo "provisioning source error: no baseline in $STATE. Run --baseline before provisioning" >&2
|
||||
exit 1
|
||||
fi
|
||||
MN_BASE=$(sed -n 's/^MN //p' "$STATE" | head -1)
|
||||
SN_BASE=$(sed -n 's/^SN //p' "$STATE" | head -1)
|
||||
|
||||
CN_IP=$(node_address "$CN")
|
||||
if [ -z "$CN_IP" ]; then
|
||||
echo "provisioning source error: $CN has no address, so no log can be read for it" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
MN_COUNTS=$(count_local_requests "$CN_IP" "$MN_BASE" | read_counts)
|
||||
SN_COUNTS=$(xdsh "$SN" -e "$SELF" --count "$CN_IP" "$SN_BASE" 2>&1 | read_counts)
|
||||
|
||||
set -- $MN_COUNTS
|
||||
MN_STATE="${1:-none}" MN_REQ="${2:-0}" MN_NEW="${3:-0}" MN_LINES="${4:-0}"
|
||||
set -- $SN_COUNTS
|
||||
SN_STATE="${1:-none}" SN_REQ="${2:-0}" SN_NEW="${3:-0}" SN_LINES="${4:-0}"
|
||||
|
||||
echo "$SN served $CN $SN_REQ boot-payload request(s) since the baseline (log $SN_STATE, $SN_NEW new of $SN_LINES lines)"
|
||||
echo "$MN served $CN $MN_REQ boot-payload request(s) since the baseline (log $MN_STATE, $MN_NEW new of $MN_LINES lines)"
|
||||
|
||||
RC=0
|
||||
|
||||
if [ "$SN_STATE" != ok ]; then
|
||||
echo "provisioning source error: no httpd access log could be read on $SN" >&2
|
||||
RC=1
|
||||
fi
|
||||
|
||||
# An empty log makes "the management node served nothing" a property of the file, not a
|
||||
# measurement. New lines are NOT required: the service node is provisioned before the baseline, so
|
||||
# after it a correct hierarchical run leaves the management node's log unchanged.
|
||||
if [ "$MN_STATE" != ok ] || [ "$MN_LINES" -eq 0 ]; then
|
||||
echo "provisioning source error: no httpd access log with entries could be read on $MN" >&2
|
||||
RC=1
|
||||
fi
|
||||
|
||||
if [ "$RC" -eq 0 ] && [ "$SN_REQ" -eq 0 ]; then
|
||||
echo "provisioning source error: $SN served $CN no boot payload, so it did not provision it" >&2
|
||||
RC=1
|
||||
fi
|
||||
|
||||
# Count requests, not bytes. A 304 or a HEAD carries no body, so the management node can answer
|
||||
# for the compute node and still log 0 bytes.
|
||||
if [ "$RC" -eq 0 ] && [ "$MN_REQ" -gt 0 ]; then
|
||||
echo "provisioning source error: $MN answered $MN_REQ boot-payload request(s) for $CN, so this provision was flat" >&2
|
||||
RC=1
|
||||
fi
|
||||
|
||||
if [ "$RC" -eq 0 ]; then
|
||||
echo "provisioning source ok: $SN served $CN and $MN served it nothing"
|
||||
fi
|
||||
|
||||
exit "$RC"
|
||||
@@ -1,7 +1,10 @@
|
||||
start:SN_setup_case
|
||||
os:Linux
|
||||
os:rhels,sles
|
||||
label:sn_diskful,provision
|
||||
#stop:yes
|
||||
# createrepo, the xcat/ prefix in the otherpkgs pkglist paths, and yum or zypper for
|
||||
# perl-Sys-Virt are all rpm. A Debian management node has none of them, so os: above
|
||||
# names the rpm families only and SN_setup_case_ub covers Ubuntu.
|
||||
cmd:fdisk -l
|
||||
cmd:df -T
|
||||
cmd:XCAT_DATABASE=$$XCAT_DATABASE /opt/xcat/share/xcat/tools/autotest/testcase/installation/pre_deploy_sn __GETNODEATTR($$SN,os)__ __GETNODEATTR($$SN,arch)__
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
start:SN_setup_case_ub
|
||||
os:ubuntu
|
||||
label:sn_diskful,provision
|
||||
#stop:yes
|
||||
# The Debian counterpart of SN_setup_case. Same order, same database setup and the same node
|
||||
# state at the end, so reg_linux_diskless_installation_hierarchy and
|
||||
# reg_linux_diskfull_installation_hierarchy run after either one without a change.
|
||||
#
|
||||
# Four things differ, all of them package format. A Debian management node has no createrepo,
|
||||
# so the staged repositories are indexed with dpkg-scanpackages. service.ubuntu.otherpkgs.pkglist
|
||||
# names its directories as xcat-core and xcat-dep, without the xcat/ prefix the EL pkglists use,
|
||||
# so otherpkgdir goes one level deeper. perl-Sys-Virt is libsys-virt-perl. SN_install_openbmc_py.sh
|
||||
# is not called: it acts on rhels7 ppc64 only.
|
||||
cmd:fdisk -l
|
||||
cmd:df -T
|
||||
cmd:XCAT_DATABASE=$$XCAT_DATABASE /opt/xcat/share/xcat/tools/autotest/testcase/installation/pre_deploy_sn __GETNODEATTR($$SN,os)__ __GETNODEATTR($$SN,arch)__
|
||||
check:rc==0
|
||||
|
||||
cmd:chtab key=nameservers site.value="<xcatmaster>"
|
||||
check:rc==0
|
||||
|
||||
cmd:makedns -n
|
||||
check:rc==0
|
||||
cmd:if [ -x /usr/bin/goconserver ]; then makegocons $$SN; else makeconservercf $$SN;fi
|
||||
check:rc==0
|
||||
cmd:sleep 20
|
||||
cmd:if [[ "__GETNODEATTR($$SN,arch)__" =~ "ppc64" ]]; then getmacs -D $$SN -V; fi
|
||||
check:rc==0
|
||||
cmd:makedhcp -n
|
||||
check:rc==0
|
||||
cmd:makedhcp -a
|
||||
check:rc==0
|
||||
cmd:sleep 2
|
||||
cmd:if [ -f /etc/kea/kea-dhcp4.conf ]; then grep $$SN /etc/kea/kea-dhcp4.conf; elif [ -f /var/lib/dhcp/dhcpd.leases ]; then grep $$SN /var/lib/dhcp/dhcpd.leases; fi
|
||||
check:output=~$$SN
|
||||
cmd:chdef -t node $$SN groups=service,all
|
||||
check:rc==0
|
||||
cmd:chdef -t group -o service profile=service primarynic=mac installnic=mac
|
||||
check:rc==0
|
||||
cmd:chdef -t group -o service setupnfs=1 setupdhcp=1 setuptftp=1 setupnameserver=1 setupconserver=2 setupntp=1
|
||||
check:rc==0
|
||||
cmd:chdef -t group -o service nfsserver=$$MN tftpserver=$$MN xcatmaster=$$MN monserver=$$MN
|
||||
check:rc==0
|
||||
cmd:chtab node=service postscripts.postscripts="servicenode"
|
||||
check:rc==0
|
||||
|
||||
cmd:copycds $$ISO
|
||||
check:rc==0
|
||||
|
||||
cmd:chdef -t site clustersite installloc="/install"
|
||||
check:rc==0
|
||||
|
||||
cmd:/opt/xcat/share/xcat/tools/autotest/testcase/installation/stage_sn_apt_repo.sh /install/post/otherpkgs/__GETNODEATTR($$SN,os)__/__GETNODEATTR($$SN,arch)__/xcat/xcat-core
|
||||
check:rc==0
|
||||
check:output=~apt index ok
|
||||
|
||||
cmd:/opt/xcat/share/xcat/tools/autotest/testcase/installation/stage_sn_apt_repo.sh /install/post/otherpkgs/__GETNODEATTR($$SN,os)__/__GETNODEATTR($$SN,arch)__/xcat/xcat-dep __GETNODEATTR($$SN,os)__
|
||||
check:rc==0
|
||||
check:output=~apt index ok
|
||||
|
||||
# service.ubuntu.otherpkgs.pkglist names mariadb-client with no directory, so the otherpkgs
|
||||
# postscript writes a source line for otherpkgdir itself. apt-get update then fails on it, and
|
||||
# the postscript deletes the source line it had just written for xcat-core, because the
|
||||
# apt-cache show that follows returns non-zero. Index this level too.
|
||||
cmd:/opt/xcat/share/xcat/tools/autotest/testcase/installation/stage_sn_apt_repo.sh /install/post/otherpkgs/__GETNODEATTR($$SN,os)__/__GETNODEATTR($$SN,arch)__/xcat
|
||||
check:rc==0
|
||||
check:output=~apt index ok
|
||||
|
||||
cmd:chdef -t osimage __GETNODEATTR($$SN,os)__-__GETNODEATTR($$SN,arch)__-install-service otherpkgdir=/install/post/otherpkgs/__GETNODEATTR($$SN,os)__/__GETNODEATTR($$SN,arch)__/xcat
|
||||
check:rc==0
|
||||
|
||||
cmd:chdef -t osimage __GETNODEATTR($$SN,os)__-__GETNODEATTR($$SN,arch)__-install-service otherpkglist=/opt/xcat/share/xcat/install/ubuntu/service.ubuntu.otherpkgs.pkglist
|
||||
check:rc==0
|
||||
|
||||
cmd:/opt/xcat/share/xcat/tools/autotest/testcase/commoncmd/retry_install.sh $$SN __GETNODEATTR($$SN,os)__-__GETNODEATTR($$SN,arch)__-install-service
|
||||
check:rc==0
|
||||
check:output=~Provision node\(s\)\: $$SN
|
||||
|
||||
cmd:if [[ -f /var/lib/kea/kea-leases4.csv ]]; then cat /var/lib/kea/kea-leases4.csv; elif [[ -f /var/lib/dhcp/dhcpd.leases ]]; then cat /var/lib/dhcp/dhcpd.leases; fi
|
||||
|
||||
cmd:ping $$SN -c 3
|
||||
check:rc==0
|
||||
check:output=~64 bytes from $$SN
|
||||
cmd:lsdef -l $$SN | grep status
|
||||
check:rc==0
|
||||
check:output=~booted
|
||||
cmd:xdsh $$SN date
|
||||
check:rc==0
|
||||
check:output=~\d\d:\d\d:\d\d
|
||||
cmd:xdsh $$SN "ps -ef |grep xcatd"
|
||||
check:rc==0
|
||||
check:output=~xcatd:
|
||||
# Ubuntu's /etc/bash.bashrc returns before /etc/profile.d on a non-interactive shell, so the
|
||||
# xCAT commands are not on the PATH xdsh gets. The EL shell reads profile.d there and the EL
|
||||
# case does not need this.
|
||||
cmd:xdsh $$SN ". /etc/profile.d/xcat.sh; lsdef"
|
||||
check:rc==0
|
||||
check:output=~$$SN: $$SN
|
||||
cmd:xdsh $$SN ". /etc/profile.d/xcat.sh; tabdump site"
|
||||
check:rc==0
|
||||
check:output=~tftpdir
|
||||
cmd:xdsh $$SN "mount"
|
||||
check:rc==0
|
||||
check:output=~/install on /install
|
||||
cmd:xdsh $$SN "mount"
|
||||
check:rc==0
|
||||
check:output=~/tftpboot on /tftpboot
|
||||
cmd:xdsh $$SN cat /opt/xcat/xcatinfo
|
||||
check:rc==0
|
||||
check:output=~NODE=$$SN
|
||||
check:output=~IMAGENAME=__GETNODEATTR($$SN,os)__-__GETNODEATTR($$SN,arch)__-install-service
|
||||
cmd:xdsh $$SN "cat /var/log/xcat/xcat.log"
|
||||
cmd:xdsh $$SN "DEBIAN_FRONTEND=noninteractive apt-get install -y libsys-virt-perl"
|
||||
check:rc==0
|
||||
cmd:makentp -a
|
||||
check:rc==0
|
||||
cmd:if [ -x /usr/bin/goconserver ]; then makegocons -d $$SN; else makeconservercf -d $$SN;fi
|
||||
end
|
||||
@@ -47,6 +47,10 @@ check:rc==0
|
||||
cmd:if [[ -f /test.synclist ]] ;then mv -f /test.synclist /test.synclist.bak;fi;
|
||||
cmd:echo "/test.synclist -> /test.synclist" > /test.synclist;chdef -t osimage -o __GETNODEATTR($$CN,os)__-__GETNODEATTR($$CN,arch)__-install-compute synclists=/test.synclist
|
||||
check:rc==0
|
||||
# The logs carry requests from earlier runs, so record where both of them end
|
||||
# before this provision. The check below reads only what comes after.
|
||||
cmd:/opt/xcat/share/xcat/tools/autotest/testcase/commoncmd/check_provisioning_source.sh --baseline $$CN $$SN
|
||||
check:rc==0
|
||||
cmd:nodeset $$CN osimage=__GETNODEATTR($$CN,os)__-__GETNODEATTR($$CN,arch)__-install-compute
|
||||
check:rc==0
|
||||
cmd:updatenode $$CN -f
|
||||
@@ -74,6 +78,13 @@ check:rc==0
|
||||
check:output=~NODE=$$CN
|
||||
check:output=~IMAGENAME=__GETNODEATTR($$CN,os)__-__GETNODEATTR($$CN,arch)__-install-compute
|
||||
check:output=~SERVICEGROUP=$$SN
|
||||
# SERVICEGROUP above is a copy of the noderes.servicenode value this case wrote, so it says
|
||||
# what xCAT set and not which server sent the boot payload. Both dhcpd instances answer for
|
||||
# $$CN, so the management node can win the xNBA exchange and serve it. The httpd access logs
|
||||
# are what separates the two topologies.
|
||||
cmd:/opt/xcat/share/xcat/tools/autotest/testcase/commoncmd/check_provisioning_source.sh $$CN $$SN
|
||||
check:rc==0
|
||||
check:output=~provisioning source ok
|
||||
cmd:xdsh $$CN "cat /var/log/xcat/xcat.log"
|
||||
cmd:xdsh $$CN "cat /test.synclist"
|
||||
check:rc==0
|
||||
|
||||
@@ -53,6 +53,10 @@ check:rc==0
|
||||
cmd:packimage __GETNODEATTR($$CN,os)__-__GETNODEATTR($$CN,arch)__-netboot-compute
|
||||
check:rc==0
|
||||
|
||||
# The logs carry requests from earlier runs, so record where both of them end
|
||||
# before this provision. The check below reads only what comes after.
|
||||
cmd:/opt/xcat/share/xcat/tools/autotest/testcase/commoncmd/check_provisioning_source.sh --baseline $$CN $$SN
|
||||
check:rc==0
|
||||
cmd:nodeset $$CN osimage=__GETNODEATTR($$CN,os)__-__GETNODEATTR($$CN,arch)__-netboot-compute
|
||||
check:rc==0
|
||||
|
||||
@@ -82,6 +86,13 @@ check:output=~NODE=$$CN
|
||||
check:output=~IMAGENAME='__GETNODEATTR($$CN,os)__-__GETNODEATTR($$CN,arch)__-netboot-compute'
|
||||
check:output=~IMAGEUUID='\w+-\w+-\w+-\w+-\w+'
|
||||
check:output=~SERVICEGROUP=$$SN
|
||||
# SERVICEGROUP above is a copy of the noderes.servicenode value this case wrote, so it says
|
||||
# what xCAT set and not which server sent the boot payload. Both dhcpd instances answer for
|
||||
# $$CN, so the management node can win the xNBA exchange and serve it. The httpd access logs
|
||||
# are what separates the two topologies.
|
||||
cmd:/opt/xcat/share/xcat/tools/autotest/testcase/commoncmd/check_provisioning_source.sh $$CN $$SN
|
||||
check:rc==0
|
||||
check:output=~provisioning source ok
|
||||
cmd:xdsh $$CN "cat /var/log/xcat/xcat.log"
|
||||
cmd:rootimgdir=`lsdef -t osimage __GETNODEATTR($$CN,os)__-__GETNODEATTR($$CN,arch)__-netboot-compute|grep rootimgdir|awk -F'=' '{print $2}'`; if [ -d $rootimgdir.regbak ]; then rm -rf $rootimgdir; mv $rootimgdir.regbak $rootimgdir; fi
|
||||
check:rc==0
|
||||
@@ -171,6 +182,10 @@ cmd:packimage -m squashfs __GETNODEATTR($$CN,os)__-__GETNODEATTR($$CN,arch)__-ne
|
||||
check:rc==0
|
||||
check:output=~archive method:squashfs
|
||||
|
||||
# The logs carry requests from earlier runs, so record where both of them end
|
||||
# before this provision. The check below reads only what comes after.
|
||||
cmd:/opt/xcat/share/xcat/tools/autotest/testcase/commoncmd/check_provisioning_source.sh --baseline $$CN $$SN
|
||||
check:rc==0
|
||||
cmd:nodeset $$CN osimage=__GETNODEATTR($$CN,os)__-__GETNODEATTR($$CN,arch)__-netboot-compute
|
||||
check:rc==0
|
||||
|
||||
@@ -200,6 +215,13 @@ check:output=~NODE=$$CN
|
||||
check:output=~IMAGENAME='__GETNODEATTR($$CN,os)__-__GETNODEATTR($$CN,arch)__-netboot-compute'
|
||||
check:output=~IMAGEUUID='\w+-\w+-\w+-\w+-\w+'
|
||||
check:output=~SERVICEGROUP=$$SN
|
||||
# SERVICEGROUP above is a copy of the noderes.servicenode value this case wrote, so it says
|
||||
# what xCAT set and not which server sent the boot payload. Both dhcpd instances answer for
|
||||
# $$CN, so the management node can win the xNBA exchange and serve it. The httpd access logs
|
||||
# are what separates the two topologies.
|
||||
cmd:/opt/xcat/share/xcat/tools/autotest/testcase/commoncmd/check_provisioning_source.sh $$CN $$SN
|
||||
check:rc==0
|
||||
check:output=~provisioning source ok
|
||||
cmd:xdsh $$CN "cat /var/log/xcat/xcat.log"
|
||||
cmd:rootimgdir=`lsdef -t osimage __GETNODEATTR($$CN,os)__-__GETNODEATTR($$CN,arch)__-netboot-compute|grep rootimgdir|awk -F'=' '{print $2}'`; if [ -d $rootimgdir.regbak ]; then rm -rf $rootimgdir; mv $rootimgdir.regbak $rootimgdir; fi
|
||||
check:rc==0
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# stage_sn_apt_repo.sh <repository directory> [<os version>]
|
||||
#
|
||||
# Write a flat apt index at <repository directory>, so the otherpkgs postscript on a Debian
|
||||
# service node can install from it.
|
||||
#
|
||||
# The postscript builds one source line per directory named in the osimage otherpkglist, in the
|
||||
# form "deb <type>://<otherpkgdir>/<directory> ./". That trailing "./" is apt's flat layout: it
|
||||
# reads <directory>/Packages and takes each Filename relative to it. A reprepro tree with
|
||||
# dists/ and pool/ has no such file, and neither has a directory of loose debs.
|
||||
#
|
||||
# <os version> selects one subdirectory when the tree holds one per release, which is how
|
||||
# xcat-dep stages its debs. Without it, or with no subdirectory whose name starts the os
|
||||
# version, the whole tree is indexed.
|
||||
#
|
||||
# Run this on the management node. The EL cases call createrepo here, which no Debian host has.
|
||||
|
||||
set -u
|
||||
|
||||
DIR="${1:-}"
|
||||
OSVER="${2:-}"
|
||||
|
||||
if [ -z "$DIR" ]; then
|
||||
echo "usage: $0 <repository directory> [<os version>]" >&2
|
||||
exit 2
|
||||
fi
|
||||
if [ ! -d "$DIR" ]; then
|
||||
echo "apt index error: $DIR is not a directory" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
scan=.
|
||||
for sub in "$DIR"/*/; do
|
||||
[ -d "$sub" ] || continue
|
||||
name=$(basename "$sub")
|
||||
case "$OSVER" in
|
||||
"$name"*) scan=$name ;;
|
||||
esac
|
||||
done
|
||||
|
||||
cd "$DIR" || exit 1
|
||||
err=$(mktemp) || exit 1
|
||||
if ! dpkg-scanpackages -m "$scan" > Packages.new 2>"$err"; then
|
||||
echo "apt index error: dpkg-scanpackages failed under $DIR/$scan" >&2
|
||||
cat "$err" >&2
|
||||
rm -f Packages.new "$err"
|
||||
exit 1
|
||||
fi
|
||||
rm -f "$err"
|
||||
|
||||
# An empty index is the failure this guard exists for: apt reports no candidate, and the
|
||||
# service node install then fails somewhere else entirely.
|
||||
count=$(grep -c '^Package: ' Packages.new || true)
|
||||
if [ "$count" -eq 0 ]; then
|
||||
echo "apt index error: no deb package under $DIR/$scan" >&2
|
||||
rm -f Packages.new
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mv -f Packages.new Packages
|
||||
gzip -9cf Packages > Packages.gz
|
||||
echo "apt index ok: $count package(s) indexed at $DIR from $scan"
|
||||
@@ -0,0 +1,291 @@
|
||||
#!/usr/bin/env bats
|
||||
#
|
||||
# Drive check_provisioning_source.sh, which is what tells a hierarchical provision from a flat
|
||||
# one. The management node, the service node and the compute node share one subnet and both
|
||||
# dhcpd instances answer for the compute node, so the management node can win the xNBA exchange
|
||||
# and serve the boot payload itself. The httpd access logs are the only record of that.
|
||||
#
|
||||
# The check takes a baseline of both logs before provisioning and reads only what came after, so
|
||||
# these tests write the old lines, take the baseline, then append the lines of the run.
|
||||
#
|
||||
# lsdef, xdsh and hostname are stubbed. XCAT_HTTPD_ACCESS_LOG is the management node's log.
|
||||
|
||||
load 'helpers/shell_source'
|
||||
|
||||
CN=cn01
|
||||
SN=sn01
|
||||
CN_IP=192.0.2.10
|
||||
|
||||
setup()
|
||||
{
|
||||
SCRIPT="$(require_repo_file 'xCAT-test/autotest/testcase/commoncmd/check_provisioning_source.sh')"
|
||||
BIN="${BATS_TEST_TMPDIR}/bin"
|
||||
MN_LOG="${BATS_TEST_TMPDIR}/mn-access_log"
|
||||
SN_LOG="${BATS_TEST_TMPDIR}/sn-access_log"
|
||||
STATE="${BATS_TEST_TMPDIR}/baseline"
|
||||
mkdir -p "$BIN"
|
||||
: >"$MN_LOG"
|
||||
: >"$SN_LOG"
|
||||
|
||||
printf '#!/bin/sh\nprintf "Object name: %s\\n ip=%s\\n" "$3" "%s"\n' "%s" "%s" "$CN_IP" >"$BIN/lsdef"
|
||||
printf '#!/bin/sh\necho mn01\n' >"$BIN/hostname"
|
||||
# A test that reaches the network measures the lab, not this script.
|
||||
printf '#!/bin/sh\necho "unexpected getent $*" >&2\nexit 1\n' >"$BIN/getent"
|
||||
# xdsh -e copies the script to the service node and runs it there. Run it here instead, with
|
||||
# the service node log in place of the management node one, and prefix the node name as xdsh
|
||||
# does.
|
||||
printf '#!/bin/sh\nnode=$1\nshift\n[ "$1" = "-e" ] && shift\nscript=$1\nshift\nXCAT_HTTPD_ACCESS_LOG=%s "$script" "$@" | sed "s/^/$node: /"\n' \
|
||||
"$SN_LOG" >"$BIN/xdsh"
|
||||
chmod 0755 "$BIN"/*
|
||||
|
||||
export PATH="$BIN:$PATH"
|
||||
}
|
||||
|
||||
# One access-log line in the combined format: $1 client, $2 bytes, $3 path.
|
||||
access_line()
|
||||
{
|
||||
printf '%s - - [01/Jan/2026:00:00:00 +0000] "GET %s HTTP/1.1" 200 %s "-" "iPXE"\n' "$1" "$3" "$2"
|
||||
}
|
||||
|
||||
take_baseline()
|
||||
{
|
||||
env XCAT_HTTPD_ACCESS_LOG="$MN_LOG" XCAT_PROV_SOURCE_STATE="$STATE" \
|
||||
"$SCRIPT" --baseline "$CN" "$SN" >/dev/null
|
||||
}
|
||||
|
||||
run_check()
|
||||
{
|
||||
run env XCAT_HTTPD_ACCESS_LOG="$MN_LOG" XCAT_PROV_SOURCE_STATE="$STATE" "$SCRIPT" "$CN" "$SN"
|
||||
}
|
||||
|
||||
# A log that has to hold something at baseline time, so the baseline is not trivially zero.
|
||||
seed_logs()
|
||||
{
|
||||
access_line 192.0.2.30 512 /install/rh/x86_64/ >"$SN_LOG"
|
||||
access_line 192.0.2.31 512 /install/rh/x86_64/ >"$MN_LOG"
|
||||
}
|
||||
|
||||
@test "a service node that served the compute node and a silent management node pass" {
|
||||
seed_logs
|
||||
take_baseline
|
||||
access_line "$CN_IP" 12345678 /tftpboot/xcat/genesis.kernel >>"$SN_LOG"
|
||||
access_line 192.0.2.21 4096 /install/rh/x86_64/ >>"$MN_LOG"
|
||||
|
||||
run_check
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"provisioning source ok"* ]]
|
||||
}
|
||||
|
||||
@test "a management node request from BEFORE the baseline does not fail this run" {
|
||||
access_line "$CN_IP" 12345678 /tftpboot/xcat/genesis.kernel >"$MN_LOG"
|
||||
access_line 192.0.2.30 512 /install/rh/x86_64/ >"$SN_LOG"
|
||||
take_baseline
|
||||
access_line "$CN_IP" 12345678 /tftpboot/xcat/genesis.kernel >>"$SN_LOG"
|
||||
access_line 192.0.2.21 4096 /install/rh/x86_64/ >>"$MN_LOG"
|
||||
|
||||
run_check
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"provisioning source ok"* ]]
|
||||
}
|
||||
|
||||
@test "a service node request from BEFORE the baseline does not satisfy the check" {
|
||||
access_line "$CN_IP" 12345678 /tftpboot/xcat/genesis.kernel >"$SN_LOG"
|
||||
access_line 192.0.2.31 512 /install/rh/x86_64/ >"$MN_LOG"
|
||||
take_baseline
|
||||
access_line 192.0.2.21 4096 /install/rh/x86_64/ >>"$MN_LOG"
|
||||
|
||||
run_check
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"no boot payload"* ]]
|
||||
}
|
||||
|
||||
@test "a request that carries no boot payload does not satisfy the check" {
|
||||
seed_logs
|
||||
take_baseline
|
||||
printf '%s - - [01/Jan/2026:00:00:00 +0000] "GET /favicon.ico HTTP/1.1" 404 209 "-" "iPXE"\n' \
|
||||
"$CN_IP" >>"$SN_LOG"
|
||||
access_line 192.0.2.21 4096 /install/rh/x86_64/ >>"$MN_LOG"
|
||||
|
||||
run_check
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"no boot payload"* ]]
|
||||
}
|
||||
|
||||
@test "a management node request that carries no boot payload does not read as a flat provision" {
|
||||
seed_logs
|
||||
take_baseline
|
||||
access_line "$CN_IP" 12345678 /tftpboot/xcat/genesis.kernel >>"$SN_LOG"
|
||||
printf '%s - - [01/Jan/2026:00:00:00 +0000] "GET /favicon.ico HTTP/1.1" 404 209 "-" "iPXE"\n' \
|
||||
"$CN_IP" >>"$MN_LOG"
|
||||
|
||||
run_check
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"provisioning source ok"* ]]
|
||||
}
|
||||
|
||||
@test "the check refuses to answer with no baseline" {
|
||||
seed_logs
|
||||
access_line "$CN_IP" 12345678 /tftpboot/xcat/genesis.kernel >>"$SN_LOG"
|
||||
|
||||
run_check
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"no baseline"* ]]
|
||||
[[ "$output" != *"provisioning source ok"* ]]
|
||||
}
|
||||
|
||||
@test "a log rotated below its baseline is read from its first line" {
|
||||
for i in 1 2 3 4 5; do access_line 192.0.2.30 512 /install/rh/x86_64/; done >"$SN_LOG"
|
||||
access_line 192.0.2.31 512 /install/rh/x86_64/ >"$MN_LOG"
|
||||
take_baseline
|
||||
access_line "$CN_IP" 12345678 /tftpboot/xcat/genesis.kernel >"$SN_LOG"
|
||||
access_line 192.0.2.21 4096 /install/rh/x86_64/ >>"$MN_LOG"
|
||||
|
||||
run_check
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"provisioning source ok"* ]]
|
||||
}
|
||||
|
||||
@test "the management node answering for the compute node fails the check" {
|
||||
seed_logs
|
||||
take_baseline
|
||||
access_line "$CN_IP" 12345678 /tftpboot/xcat/genesis.kernel >>"$SN_LOG"
|
||||
{
|
||||
access_line 192.0.2.21 4096 /install/rh/x86_64/
|
||||
access_line "$CN_IP" 12345678 /tftpboot/xcat/genesis.kernel
|
||||
} >>"$MN_LOG"
|
||||
|
||||
run_check
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"this provision was flat"* ]]
|
||||
[[ "$output" != *"provisioning source ok"* ]]
|
||||
}
|
||||
|
||||
@test "a management node answering only a bodyless request still fails the check" {
|
||||
seed_logs
|
||||
take_baseline
|
||||
access_line "$CN_IP" 12345678 /tftpboot/xcat/genesis.kernel >>"$SN_LOG"
|
||||
{
|
||||
access_line 192.0.2.21 4096 /install/rh/x86_64/
|
||||
printf '%s - - [01/Jan/2026:00:00:00 +0000] "HEAD %s HTTP/1.1" 304 - "-" "iPXE"\n' \
|
||||
"$CN_IP" /tftpboot/xcat/genesis.kernel
|
||||
} >>"$MN_LOG"
|
||||
|
||||
run_check
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"this provision was flat"* ]]
|
||||
}
|
||||
|
||||
@test "a service node that served the compute node nothing fails the check" {
|
||||
seed_logs
|
||||
take_baseline
|
||||
access_line 192.0.2.22 4096 /install/rh/x86_64/ >>"$SN_LOG"
|
||||
access_line 192.0.2.21 4096 /install/rh/x86_64/ >>"$MN_LOG"
|
||||
|
||||
run_check
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"no boot payload"* ]]
|
||||
}
|
||||
|
||||
@test "an unreadable service node log fails the check instead of passing it" {
|
||||
seed_logs
|
||||
take_baseline
|
||||
access_line 192.0.2.21 4096 /install/rh/x86_64/ >>"$MN_LOG"
|
||||
printf '#!/bin/sh\nexit 1\n' >"$BIN/xdsh"
|
||||
chmod 0755 "$BIN/xdsh"
|
||||
|
||||
run_check
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"no httpd access log could be read on $SN"* ]]
|
||||
}
|
||||
|
||||
# The management node provisions the service node BEFORE this baseline, so it can serve nothing
|
||||
# after it. That is the hierarchical result, and it was read as a broken log.
|
||||
@test "a management node that serves nothing after the baseline passes" {
|
||||
seed_logs
|
||||
take_baseline
|
||||
access_line "$CN_IP" 12345678 /tftpboot/xcat/genesis.kernel >>"$SN_LOG"
|
||||
|
||||
run_check
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"provisioning source ok"* ]]
|
||||
}
|
||||
|
||||
@test "a management node log with no entry at all fails the check instead of reading as silence" {
|
||||
: >"$MN_LOG"
|
||||
access_line 192.0.2.30 512 /install/rh/x86_64/ >"$SN_LOG"
|
||||
take_baseline
|
||||
access_line "$CN_IP" 12345678 /tftpboot/xcat/genesis.kernel >>"$SN_LOG"
|
||||
|
||||
run_check
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"no httpd access log with entries could be read on mn01"* ]]
|
||||
}
|
||||
|
||||
# The compute node fetches the root image with wget from a URL that carries a double slash, so
|
||||
# the path in the log reads //install/... and the payload filter did not match it.
|
||||
@test "a boot payload requested under a doubled slash counts" {
|
||||
seed_logs
|
||||
take_baseline
|
||||
access_line "$CN_IP" 978729607 //install/netboot/ubuntu/x86_64/compute/rootimg.cpio.gz >>"$SN_LOG"
|
||||
access_line 192.0.2.21 4096 /install/rh/x86_64/ >>"$MN_LOG"
|
||||
|
||||
run_check
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"provisioning source ok"* ]]
|
||||
}
|
||||
|
||||
@test "a doubled-slash boot payload from the management node reads as a flat provision" {
|
||||
seed_logs
|
||||
take_baseline
|
||||
access_line "$CN_IP" 12345678 /tftpboot/xcat/genesis.kernel >>"$SN_LOG"
|
||||
{
|
||||
access_line 192.0.2.21 4096 /install/rh/x86_64/
|
||||
access_line "$CN_IP" 978729607 //install/netboot/ubuntu/x86_64/compute/rootimg.cpio.gz
|
||||
} >>"$MN_LOG"
|
||||
|
||||
run_check
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"this provision was flat"* ]]
|
||||
}
|
||||
|
||||
@test "the Debian per-vhost log format is read as the client address" {
|
||||
seed_logs
|
||||
take_baseline
|
||||
printf 'xcat:80 %s - - [01/Jan/2026:00:00:00 +0000] "GET %s HTTP/1.1" 200 12345678\n' \
|
||||
"$CN_IP" /tftpboot/xcat/genesis.kernel >>"$SN_LOG"
|
||||
printf 'xcat:80 %s - - [01/Jan/2026:00:00:00 +0000] "GET %s HTTP/1.1" 200 4096\n' \
|
||||
192.0.2.21 /install/ubuntu/x86_64/ >>"$MN_LOG"
|
||||
|
||||
run_check
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"provisioning source ok"* ]]
|
||||
}
|
||||
|
||||
@test "a compute node with no address fails the check" {
|
||||
seed_logs
|
||||
take_baseline
|
||||
printf '#!/bin/sh\nexit 1\n' >"$BIN/lsdef"
|
||||
printf '#!/bin/sh\nexit 2\n' >"$BIN/getent"
|
||||
chmod 0755 "$BIN/lsdef" "$BIN/getent"
|
||||
access_line "$CN_IP" 12345678 /tftpboot/xcat/genesis.kernel >>"$SN_LOG"
|
||||
access_line 192.0.2.21 4096 /install/rh/x86_64/ >>"$MN_LOG"
|
||||
|
||||
run_check
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"has no address"* ]]
|
||||
}
|
||||
|
||||
@test "a baseline that cannot reach the service node names the service node" {
|
||||
seed_logs
|
||||
printf '#!/bin/sh\nexit 1\n' >"$BIN/xdsh"
|
||||
chmod 0755 "$BIN/xdsh"
|
||||
|
||||
run env XCAT_HTTPD_ACCESS_LOG="$MN_LOG" XCAT_PROV_SOURCE_STATE="$STATE" \
|
||||
"$SCRIPT" --baseline "$CN" "$SN"
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"no access log to baseline on $SN"* ]]
|
||||
# The message used to append the management node's baseline spec after the node name, so a
|
||||
# substring match on the name alone passes either way.
|
||||
[[ "$output" != *"$MN_LOG"* ]]
|
||||
[ ! -e "$STATE" ]
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
#!/usr/bin/env bats
|
||||
#
|
||||
# makenamed.conf builds the forwarder list of a service node's named from /etc/resolv.conf.
|
||||
#
|
||||
# On a host managed by systemd-resolved that file holds the 127.0.0.53 stub, and the stub points
|
||||
# back at this named for the link. forward-only to it is a loop: the service node answers
|
||||
# nothing, and every compute node behind it fails to resolve. Measured on xcat22-sn, where
|
||||
# "dig @<sn> xcat22-sn.xcat22.lab" returned nothing while the same query to the management node
|
||||
# answered. The EL service node has the real upstream in /etc/resolv.conf, which is why it works.
|
||||
#
|
||||
# systemd-resolved writes the real servers to /run/systemd/resolve/resolv.conf.
|
||||
|
||||
load 'helpers/shell_source'
|
||||
|
||||
setup()
|
||||
{
|
||||
SCRIPT="$(require_repo_file 'xCAT-server/sbin/makenamed.conf')"
|
||||
RESOLV="${BATS_TEST_TMPDIR}/resolv.conf"
|
||||
UPLINK="${BATS_TEST_TMPDIR}/uplink.conf"
|
||||
# MAKENAMED_LIB stops the script before it writes anything, so only the routine is loaded.
|
||||
MAKENAMED_LIB=1 . "$SCRIPT"
|
||||
export -f forwarder_addresses _fa_usable
|
||||
}
|
||||
|
||||
@test "a real nameserver is a forwarder" {
|
||||
printf 'nameserver 192.168.222.1\nsearch xcat22.lab\n' >"$RESOLV"
|
||||
run forwarder_addresses "$RESOLV" "$UPLINK"
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$output" = "192.168.222.1" ]
|
||||
}
|
||||
|
||||
@test "the systemd-resolved stub is not a forwarder, and the uplink answers instead" {
|
||||
printf 'nameserver 127.0.0.53\noptions edns0 trust-ad\n' >"$RESOLV"
|
||||
printf 'nameserver 192.168.222.1\nsearch xcat22.lab\n' >"$UPLINK"
|
||||
run forwarder_addresses "$RESOLV" "$UPLINK"
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$output" = "192.168.222.1" ]
|
||||
}
|
||||
|
||||
@test "any loopback address is rejected, not only the stub" {
|
||||
printf 'nameserver 127.0.0.1\nnameserver ::1\n' >"$RESOLV"
|
||||
printf 'nameserver 10.0.0.1\n' >"$UPLINK"
|
||||
run forwarder_addresses "$RESOLV" "$UPLINK"
|
||||
[ "$output" = "10.0.0.1" ]
|
||||
}
|
||||
|
||||
@test "several real nameservers are all forwarders, in order" {
|
||||
printf 'nameserver 192.168.222.1\nnameserver 10.0.0.2\n' >"$RESOLV"
|
||||
run forwarder_addresses "$RESOLV" "$UPLINK"
|
||||
[ "$output" = "192.168.222.1
|
||||
10.0.0.2" ]
|
||||
}
|
||||
|
||||
@test "a real nameserver wins over the uplink, which is only the fallback" {
|
||||
printf 'nameserver 192.168.222.1\n' >"$RESOLV"
|
||||
printf 'nameserver 10.9.9.9\n' >"$UPLINK"
|
||||
run forwarder_addresses "$RESOLV" "$UPLINK"
|
||||
[ "$output" = "192.168.222.1" ]
|
||||
}
|
||||
|
||||
@test "no usable address anywhere prints nothing rather than a blank forwarder" {
|
||||
printf 'nameserver 127.0.0.53\n' >"$RESOLV"
|
||||
: >"$UPLINK"
|
||||
# Count the lines. A blank line reaches named.conf as an empty forwarder entry, which bind
|
||||
# rejects as a syntax error, and $output cannot tell one from no output at all.
|
||||
run bash -c "forwarder_addresses '$RESOLV' '$UPLINK' | wc -l"
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$output" = "0" ]
|
||||
}
|
||||
|
||||
@test "an absent uplink file is not an error" {
|
||||
printf 'nameserver 127.0.0.53\n' >"$RESOLV"
|
||||
run forwarder_addresses "$RESOLV" "${BATS_TEST_TMPDIR}/absent"
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$output" = "" ]
|
||||
}
|
||||
|
||||
# named runs as "bind" on Debian and writes its managed-keys database into the configured
|
||||
# directory. /var/named is created root-owned, so the write fails, DNSSEC initialisation fails
|
||||
# with it, and the server answers NXDOMAIN to every query -- including forwarded ones, which is
|
||||
# how a correct forwarder list still resolved nothing on xcat22-sn.
|
||||
|
||||
@test "Ubuntu names the directory its named can write" {
|
||||
printf 'DISTRIB_ID=Ubuntu\nDISTRIB_RELEASE=24.04\n' >"${BATS_TEST_TMPDIR}/lsb"
|
||||
run named_directory "${BATS_TEST_TMPDIR}/lsb" "${BATS_TEST_TMPDIR}/absent-os" "${BATS_TEST_TMPDIR}/absent-suse"
|
||||
[ "$output" = "/var/cache/bind" ]
|
||||
}
|
||||
|
||||
@test "SLES keeps its own directory" {
|
||||
: >"${BATS_TEST_TMPDIR}/lsb"
|
||||
printf 'ID="sles"\n' >"${BATS_TEST_TMPDIR}/os"
|
||||
run named_directory "${BATS_TEST_TMPDIR}/lsb" "${BATS_TEST_TMPDIR}/os" "${BATS_TEST_TMPDIR}/absent-suse"
|
||||
[ "$output" = "/var/lib/named" ]
|
||||
}
|
||||
|
||||
@test "EL keeps /var/named" {
|
||||
: >"${BATS_TEST_TMPDIR}/lsb"
|
||||
printf 'ID="almalinux"\n' >"${BATS_TEST_TMPDIR}/os"
|
||||
run named_directory "${BATS_TEST_TMPDIR}/lsb" "${BATS_TEST_TMPDIR}/os" "${BATS_TEST_TMPDIR}/absent-suse"
|
||||
[ "$output" = "/var/named" ]
|
||||
}
|
||||
|
||||
@test "an absent lsb-release is not Ubuntu" {
|
||||
printf 'ID="almalinux"\n' >"${BATS_TEST_TMPDIR}/os"
|
||||
run named_directory "${BATS_TEST_TMPDIR}/absent-lsb" "${BATS_TEST_TMPDIR}/os" "${BATS_TEST_TMPDIR}/absent-suse"
|
||||
[ "$output" = "/var/named" ]
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env bats
|
||||
#
|
||||
# The otherpkgs postscript stages an apt repository that carries a Packages file and no Release.
|
||||
# apt refuses an unsigned repository outright -- "does not have a Release file" -- so the
|
||||
# postscript's apt-cache show finds nothing, it deletes the source line it has just written, and
|
||||
# the package is never installed. On a service node that package is xcatsn.
|
||||
#
|
||||
# This drives apt against a real flat repository in a scratch tree, with both spellings of the
|
||||
# source line, and then holds the postscript to the one apt accepts.
|
||||
|
||||
load 'helpers/shell_source'
|
||||
|
||||
setup()
|
||||
{
|
||||
command -v apt-get >/dev/null 2>&1 || skip 'apt-get is required'
|
||||
command -v dpkg-scanpackages >/dev/null 2>&1 || skip 'dpkg-scanpackages is required'
|
||||
|
||||
REPO="${BATS_TEST_TMPDIR}/repo"
|
||||
ROOT="${BATS_TEST_TMPDIR}/aptroot"
|
||||
mkdir -p "$REPO" "$ROOT/etc/apt/sources.list.d" "$ROOT/var/lib/apt/lists/partial" \
|
||||
"$ROOT/var/lib/dpkg" "$ROOT/var/cache/apt/archives/partial"
|
||||
: >"$ROOT/var/lib/dpkg/status"
|
||||
|
||||
# A minimal binary package, so the index describes something real.
|
||||
local build="${BATS_TEST_TMPDIR}/pkg/xcatsn"
|
||||
mkdir -p "$build/DEBIAN"
|
||||
printf 'Package: xcatsn\nVersion: 2.20.0\nArchitecture: all\nMaintainer: t <t@example.invalid>\nDescription: probe\n' \
|
||||
>"$build/DEBIAN/control"
|
||||
dpkg-deb --build -Znone "$build" "$REPO/xcatsn_2.20.0_all.deb" >/dev/null
|
||||
( cd "$REPO" && dpkg-scanpackages -m . >Packages 2>/dev/null )
|
||||
}
|
||||
|
||||
# Resolve xcatsn through apt with the given source line. Sets OUT and STATUS.
|
||||
resolve()
|
||||
{
|
||||
printf '%s\n' "$1" >"$ROOT/etc/apt/sources.list.d/probe.list"
|
||||
apt-get -o "Dir=$ROOT" -o "Dir::State::status=$ROOT/var/lib/dpkg/status" \
|
||||
-o "Dir::Etc::sourcelist=$ROOT/etc/apt/sources.list.d/probe.list" \
|
||||
-o Dir::Etc::sourceparts=/dev/null -o APT::Get::List-Cleanup=0 \
|
||||
update >/dev/null 2>&1 || true # an untrusted source makes update itself exit 100,
|
||||
# and that refusal is what this test measures. Under
|
||||
# bats' set -e an unguarded failure here aborts the
|
||||
# function before apt-cache runs, so the test could
|
||||
# only ever pass where apt-get is absent and it skips.
|
||||
OUT="$(apt-cache -o "Dir=$ROOT" -o "Dir::State::status=$ROOT/var/lib/dpkg/status" \
|
||||
show xcatsn 2>&1)" && STATUS=0 || STATUS=$?
|
||||
}
|
||||
|
||||
@test "apt refuses the source line without trusted=yes, so the package cannot be found" {
|
||||
resolve "deb file://$REPO ./"
|
||||
[ "$STATUS" -ne 0 ]
|
||||
[[ "$OUT" != *"Package: xcatsn"* ]]
|
||||
}
|
||||
|
||||
@test "apt resolves the package when the source line is trusted" {
|
||||
resolve "deb [trusted=yes] file://$REPO ./"
|
||||
[ "$STATUS" -eq 0 ]
|
||||
[[ "$OUT" == *"Package: xcatsn"* ]]
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
#!/usr/bin/env bats
|
||||
#
|
||||
# Drive stage_sn_apt_repo.sh, which writes the flat apt index a Debian service node installs
|
||||
# from. The otherpkgs postscript writes "deb <type>://<otherpkgdir>/<directory> ./", and that
|
||||
# trailing "./" makes apt read <directory>/Packages and resolve each Filename against it.
|
||||
#
|
||||
# dpkg-scanpackages is stubbed, so the test measures the directory selection and the guards and
|
||||
# not dpkg-dev.
|
||||
|
||||
load 'helpers/shell_source'
|
||||
|
||||
setup()
|
||||
{
|
||||
SCRIPT="$(require_repo_file 'xCAT-test/autotest/testcase/installation/stage_sn_apt_repo.sh')"
|
||||
REPO="${BATS_TEST_TMPDIR}/repo"
|
||||
BIN="${BATS_TEST_TMPDIR}/bin"
|
||||
RECORD="${BATS_TEST_TMPDIR}/scanned"
|
||||
mkdir -p "$REPO" "$BIN"
|
||||
|
||||
# Record the directory it was asked to scan, and emit one stanza per deb found under it.
|
||||
cat >"$BIN/dpkg-scanpackages" <<STUB
|
||||
#!/bin/sh
|
||||
[ "\$1" = "-m" ] && shift
|
||||
printf '%s\n' "\$1" >>"$RECORD"
|
||||
find "\$1" -name '*.deb' | while read -r d; do
|
||||
printf 'Package: %s\nFilename: %s\n\n' "\$(basename "\$d" .deb)" "\$d"
|
||||
done
|
||||
exit 0
|
||||
STUB
|
||||
chmod 0755 "$BIN/dpkg-scanpackages"
|
||||
export PATH="$BIN:$PATH"
|
||||
}
|
||||
|
||||
deb()
|
||||
{
|
||||
mkdir -p "$(dirname "$REPO/$1")"
|
||||
: >"$REPO/$1"
|
||||
}
|
||||
|
||||
scanned()
|
||||
{
|
||||
read_file_or_empty "$RECORD"
|
||||
}
|
||||
|
||||
@test "a reprepro tree is indexed whole, so the pool is reachable from the flat index" {
|
||||
deb pool/main/x/xcat/xcatsn_2.20.0_amd64.deb
|
||||
deb pool/main/x/xcat/xcat-client_2.20.0_all.deb
|
||||
|
||||
run "$SCRIPT" "$REPO"
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$(scanned)" = "." ]
|
||||
[[ "$output" == *"2 package(s) indexed"* ]]
|
||||
grep -q '^Package: xcatsn_2.20.0_amd64$' "$REPO/Packages"
|
||||
[ -s "$REPO/Packages.gz" ]
|
||||
}
|
||||
|
||||
@test "a tree with one directory per release is indexed from the directory the os version names" {
|
||||
deb ubuntu22.04/conserver-xcat_8.2.1-1_amd64.deb
|
||||
deb ubuntu24.04/conserver-xcat_8.2.1-1_amd64.deb
|
||||
deb ubuntu26.04/conserver-xcat_8.2.1-1_amd64.deb
|
||||
|
||||
run "$SCRIPT" "$REPO" ubuntu24.04.4
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$(scanned)" = "ubuntu24.04" ]
|
||||
[[ "$output" == *"1 package(s) indexed"* ]]
|
||||
}
|
||||
|
||||
@test "an os version no directory matches falls back to the whole tree" {
|
||||
deb ubuntu22.04/conserver-xcat_8.2.1-1_amd64.deb
|
||||
deb ubuntu26.04/conserver-xcat_8.2.1-1_amd64.deb
|
||||
|
||||
run "$SCRIPT" "$REPO" ubuntu24.04.4
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$(scanned)" = "." ]
|
||||
}
|
||||
|
||||
@test "a tree with no deb fails instead of writing an empty index" {
|
||||
mkdir -p "$REPO/pool"
|
||||
|
||||
run "$SCRIPT" "$REPO"
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"no deb package under"* ]]
|
||||
[ ! -e "$REPO/Packages" ]
|
||||
}
|
||||
|
||||
@test "a failing dpkg-scanpackages fails the step and leaves no index" {
|
||||
deb pool/main/x/xcat/xcatsn_2.20.0_amd64.deb
|
||||
printf '#!/bin/sh\necho "cannot read" >&2\nexit 2\n' >"$BIN/dpkg-scanpackages"
|
||||
chmod 0755 "$BIN/dpkg-scanpackages"
|
||||
|
||||
run "$SCRIPT" "$REPO"
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"dpkg-scanpackages failed"* ]]
|
||||
[ ! -e "$REPO/Packages" ]
|
||||
}
|
||||
|
||||
@test "a directory that does not exist fails" {
|
||||
run "$SCRIPT" "${BATS_TEST_TMPDIR}/absent"
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"is not a directory"* ]]
|
||||
}
|
||||
|
||||
@test "no argument fails" {
|
||||
run "$SCRIPT"
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"usage:"* ]]
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
# The fast oracle of the Ubuntu 24.04 service node cell.
|
||||
#
|
||||
# xCAT-test/quick.sh -f xCAT-test/quick-servicenode-ub.txt
|
||||
#
|
||||
# RUN IT ON xcat-master-ub. otherpkgs_apt_source_trusted skips its two resolving assertions where
|
||||
# apt-get is absent, so on an EL host this list silently proves less and the trusted=yes fix
|
||||
# reads as unnecessary.
|
||||
#
|
||||
# KNOWN HOLES, where a wrong drop comes from. A first run dropped four real fixes:
|
||||
# - the apt source line the postscript writes. otherpkgs_apt_source_trusted measures APT, not
|
||||
# what xCAT wrote. STILL OPEN.
|
||||
# - the NFS export casper needs. CLOSED by service_node_artifacts_ubuntu.t.
|
||||
# - the xdsh PATH fix. STILL OPEN.
|
||||
# - the named reload. CLOSED by aasn_named_service_action.t.
|
||||
|
||||
xCAT-test/unit/service_node_artifacts_ubuntu.t
|
||||
xCAT-test/unit/aasn_named_service_action.t
|
||||
xCAT-test/unit/ubuntu_service_subiquity_template.t
|
||||
xCAT-test/bats/makenamed_forwarders.bats
|
||||
xCAT-test/bats/otherpkgs_apt_source_trusted.bats
|
||||
xCAT-test/bats/stage_sn_apt_repo.bats
|
||||
@@ -0,0 +1,32 @@
|
||||
# The fast oracle of the service node change set: one test per fix, each written with its fix.
|
||||
#
|
||||
# Run it with: xCAT-test/quick.sh -f xCAT-test/quick-servicenode.txt
|
||||
#
|
||||
# These are the tests that failed before their fix landed. Together they stand in for the
|
||||
# end-to-end service node suite, which provisions a management node, a service node and a
|
||||
# compute node on two families and takes 45 to 90 minutes a run.
|
||||
#
|
||||
# What each one covers:
|
||||
# check_provisioning_source the compute node was served by the service node, not the MN
|
||||
# makenamed_forwarders a Debian service node does not forward DNS to its own stub
|
||||
# otherpkgs_apt_source_trusted the staged apt source apt will accept
|
||||
# stage_sn_apt_repo the Debian repository index, where createrepo does not exist
|
||||
# servicenode_builder_repo the builder repository per EL release: crb, powertools, codeready
|
||||
# svrutils_nfs_export_line a service node that mounts /install still exports it
|
||||
# ubuntu_service_subiquity_template the service profile resolves a Subiquity template
|
||||
#
|
||||
# A SKIP is not a PASS. otherpkgs_apt_source_trusted skips its two resolving assertions where
|
||||
# apt-get is absent, so on an EL builder that file proves less than it does on a Debian one. Run
|
||||
# this list on the builder of the family the candidate touches, or read the skips.
|
||||
#
|
||||
# NOT covered, and known: the xdsh PATH fix, and the named working directory and reload fixes.
|
||||
# A candidate that removes those passes this list and fails the end-to-end suite. Fill these in
|
||||
# before trusting the list for a minimization.
|
||||
|
||||
xCAT-test/bats/check_provisioning_source.bats
|
||||
xCAT-test/bats/makenamed_forwarders.bats
|
||||
xCAT-test/bats/otherpkgs_apt_source_trusted.bats
|
||||
xCAT-test/bats/stage_sn_apt_repo.bats
|
||||
xCAT-test/unit/servicenode_builder_repo.t
|
||||
xCAT-test/unit/svrutils_nfs_export_line.t
|
||||
xCAT-test/unit/ubuntu_service_subiquity_template.t
|
||||
Executable
+79
@@ -0,0 +1,79 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Run a fast subset of the unit tests: the Perl .t files with prove and the shell .bats files
|
||||
# with bats, in one command, from the source tree. No build, no installed xCAT, no cluster.
|
||||
#
|
||||
# WHY THIS EXISTS. Delta debugging a change set runs the suite once per candidate. The
|
||||
# end-to-end suite provisions machines and costs 45 to 90 minutes a run, so the rounds run on a
|
||||
# fast oracle instead, and the slow suite runs once on the set that settles. This is that fast
|
||||
# oracle, and it is useful on its own: it is the same two commands CI runs, without the wait.
|
||||
#
|
||||
# xCAT-test/quick.sh every unit test and every bats file
|
||||
# xCAT-test/quick.sh path/a.t path/b.bats only those, in the order given
|
||||
# xCAT-test/quick.sh -f list.txt the files named in list.txt, one per line
|
||||
#
|
||||
# Exit status is 0 only when every file passed. A missing file is an error, not a skip: a fast
|
||||
# oracle that silently runs nothing reports PASS for a tree that contains none of the tests.
|
||||
|
||||
set -u -o pipefail
|
||||
|
||||
usage() { sed -n '3,20p' "$0" | sed 's/^# \{0,1\}//'; exit "${1:-0}"; }
|
||||
|
||||
root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
||||
files=()
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
-h|--help) usage 0 ;;
|
||||
-f|--from) shift; [ -r "${1:-}" ] || { echo "quick: cannot read list '${1:-}'" >&2; exit 2; }
|
||||
while read -r line; do
|
||||
case "$line" in ''|\#*) continue ;; esac
|
||||
files+=("$line")
|
||||
done < "$1" ;;
|
||||
-*) echo "quick: unknown option '$1'" >&2; usage 2 ;;
|
||||
*) files+=("$1") ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
cd "$root" || exit 2
|
||||
|
||||
if [ "${#files[@]}" -eq 0 ]; then
|
||||
echo "[quick] whole fast suite: prove -r xCAT-test/unit, then bats -r xCAT-test/bats"
|
||||
rc=0
|
||||
prove -r xCAT-test/unit || rc=1
|
||||
bats -r xCAT-test/bats || rc=1
|
||||
exit $rc
|
||||
fi
|
||||
|
||||
# A named file that is absent means the caller asked for a test this tree does not have. Say so
|
||||
# and fail: reporting PASS here is how a minimization drops the commit that adds a test.
|
||||
missing=()
|
||||
for f in "${files[@]}"; do [ -e "$f" ] || missing+=("$f"); done
|
||||
if [ "${#missing[@]}" -gt 0 ]; then
|
||||
printf '[quick] MISSING from this tree, refusing to report a result:\n' >&2
|
||||
printf ' %s\n' "${missing[@]}" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
perl_tests=(); bats_tests=()
|
||||
for f in "${files[@]}"; do
|
||||
case "$f" in
|
||||
*.t) perl_tests+=("$f") ;;
|
||||
*.bats) bats_tests+=("$f") ;;
|
||||
*) echo "quick: '$f' is neither a .t nor a .bats" >&2; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
rc=0
|
||||
if [ "${#perl_tests[@]}" -gt 0 ]; then
|
||||
echo "[quick] prove ${#perl_tests[@]} Perl test(s)"
|
||||
prove "${perl_tests[@]}" || rc=1
|
||||
fi
|
||||
if [ "${#bats_tests[@]}" -gt 0 ]; then
|
||||
echo "[quick] bats ${#bats_tests[@]} shell test(s)"
|
||||
bats "${bats_tests[@]}" || rc=1
|
||||
fi
|
||||
|
||||
[ $rc -eq 0 ] && echo "[quick] PASS (${#files[@]} file(s))" || echo "[quick] FAIL"
|
||||
exit $rc
|
||||
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env perl
|
||||
|
||||
# setup_DNS writes named.conf through makenamed.conf and then brings the daemon up. It used to
|
||||
# START the service. On Debian the package already runs named, so a start is a no-op: the daemon
|
||||
# keeps serving the configuration it read at install time, the zone xCAT has just written is
|
||||
# never loaded, and a compute node cannot resolve its service node.
|
||||
#
|
||||
# A minimization of this change set dropped that fix, because no fast test could see it. The
|
||||
# decision lives in xCAT::SvrUtils, which a test can load; AAsn.pm cannot be loaded from a source
|
||||
# tree at all, since it pulls in xCAT::Table and the rest of the server.
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use FindBin;
|
||||
use Test::More;
|
||||
|
||||
use lib "$FindBin::Bin/../../perl-xCAT";
|
||||
use lib "$FindBin::Bin/../../xCAT-server/lib/perl";
|
||||
use xCAT::SvrUtils;
|
||||
|
||||
can_ok('xCAT::SvrUtils', 'named_service_action') or do { done_testing(); exit 1 };
|
||||
|
||||
is(xCAT::SvrUtils::named_service_action('linux'), 'restart',
|
||||
'Linux restarts named, so the configuration just written is the one it serves');
|
||||
is(xCAT::SvrUtils::named_service_action('aix'), 'start',
|
||||
'AIX keeps the start it has always used');
|
||||
is(xCAT::SvrUtils::named_service_action(''), '',
|
||||
'an unknown platform does nothing rather than guessing an action');
|
||||
is(xCAT::SvrUtils::named_service_action(undef), '',
|
||||
'an undefined platform does nothing');
|
||||
|
||||
done_testing();
|
||||
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env perl
|
||||
|
||||
# The Ubuntu counterpart of service_node_artifacts_el.t: render the artifacts an Ubuntu service
|
||||
# node needs and assert each, so the fast oracle fails for any wrong artifact rather than only
|
||||
# for the defects someone has already hit.
|
||||
#
|
||||
# It exists because the first Ubuntu oracle dropped two real fixes. The apt test measured apt's
|
||||
# behaviour rather than the line the postscript writes, and nothing asserted the export line at
|
||||
# all, although casper mounts the install tree over NFS from the service node.
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use FindBin;
|
||||
use Test::More;
|
||||
|
||||
use lib "$FindBin::Bin/../../perl-xCAT";
|
||||
use lib "$FindBin::Bin/../../xCAT-server/lib/perl";
|
||||
|
||||
# get_file_name takes genos LAST, and update_tables_with_templates passes 'subiquity' for every
|
||||
# Ubuntu 20.04 and later osimage. Passing the os version there resolves the preseed instead, which
|
||||
# is the mistake this test would otherwise make about its own subject.
|
||||
my %SN = (osver => 'ubuntu24.04', arch => 'x86_64', profile => 'service', genos => 'subiquity');
|
||||
my $SHARE = "$FindBin::Bin/../../xCAT-server/share/xcat/install";
|
||||
|
||||
my $have = eval { require xCAT::SvrUtils; 1 } ? 1 : 0;
|
||||
ok($have, 'xCAT::SvrUtils loads') or do { done_testing(); exit 1 };
|
||||
|
||||
# 1. The installer. Ubuntu 20.04 and later install with Subiquity, and xCAT decides that from the
|
||||
# NAME of the template it resolved. Without a service.subiquity.tmpl the service profile falls
|
||||
# back to the debian-installer preseed and the node boots the live image without casper.
|
||||
my $tmpl = xCAT::SvrUtils::get_tmpl_file_name("$SHARE/ubuntu", $SN{profile}, $SN{osver},
|
||||
$SN{arch}, $SN{genos});
|
||||
ok(defined $tmpl && length $tmpl, 'the Ubuntu service profile resolves a template');
|
||||
like($tmpl, qr{subiquity}, '... and it is a Subiquity template, or the node never installs');
|
||||
like($tmpl, qr{/service[^/]*\.tmpl$}, '... of the service profile, not compute');
|
||||
|
||||
# The compute profile must keep resolving too: an assertion that only ever looks at one profile
|
||||
# cannot tell a profile fix from a lookup fix.
|
||||
my $compute = xCAT::SvrUtils::get_tmpl_file_name("$SHARE/ubuntu", 'compute', $SN{osver},
|
||||
$SN{arch}, $SN{genos});
|
||||
like($compute, qr{subiquity}, 'the compute profile still resolves its own Subiquity template');
|
||||
|
||||
# 2. The NFS export. casper mounts the install tree from the service node, so a service node that
|
||||
# exports nothing stops the compute node at "Unable to find a live file system on the network".
|
||||
my $can_export = xCAT::SvrUtils->can('nfs_export_line') ? 1 : 0;
|
||||
ok($can_export, 'SvrUtils can render an export line, without which casper finds no filesystem');
|
||||
|
||||
SKIP: {
|
||||
skip 'nfs_export_line absent', 3 unless $can_export;
|
||||
my $re = xCAT::SvrUtils->nfs_export_line('/install', reexport => 1);
|
||||
like($re, qr{\bfsid=\d+}, 're-exporting the mounted /install carries an fsid');
|
||||
like($re, qr{\bcrossmnt\b}, '... and crossmnt');
|
||||
like($re, qr{\bno_root_squash\b}, '... and no_root_squash, which the installer needs');
|
||||
}
|
||||
|
||||
done_testing();
|
||||
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use FindBin;
|
||||
use Test::More;
|
||||
|
||||
# A service node with servicenode.nfsserver=1 is asked to "set up file services on this service
|
||||
# node". When site.installloc is set it MOUNTS /install from the management node, and AAsn.pm
|
||||
# then exported nothing at all, so the node served no NFS. An Ubuntu compute node boots the live
|
||||
# installer with casper, which mounts the install tree over NFS from its own service node, and it
|
||||
# stopped at "Unable to find a live file system on the network". Measured on xcat22-sn:
|
||||
# showmount -e returned an empty list and /etc/exports was empty while nfs-kernel-server was
|
||||
# active.
|
||||
#
|
||||
# Re-exporting an NFS mount is not the same as exporting a local directory. Linux requires an
|
||||
# explicit fsid, because it cannot derive one from the underlying filesystem, and refuses the
|
||||
# export without it.
|
||||
|
||||
use lib "$FindBin::Bin/../../perl-xCAT";
|
||||
use lib "$FindBin::Bin/../../xCAT-server/lib/perl";
|
||||
use xCAT::SvrUtils;
|
||||
|
||||
can_ok('xCAT::SvrUtils', 'nfs_export_line') or done_testing() && exit;
|
||||
|
||||
my $local = xCAT::SvrUtils->nfs_export_line('/install');
|
||||
is($local, '/install *(rw,no_root_squash,sync,no_subtree_check,insecure)',
|
||||
'a local directory keeps the options xCAT has always written');
|
||||
|
||||
my $reexport = xCAT::SvrUtils->nfs_export_line('/install', reexport => 1);
|
||||
like($reexport, qr{^/install \*\(}, 're-export names the same directory and clients');
|
||||
like($reexport, qr{\brw\b}, '... stays read-write');
|
||||
like($reexport, qr{\bno_root_squash\b}, '... keeps no_root_squash, which the installer needs');
|
||||
like($reexport, qr{\binsecure\b}, '... keeps insecure, for clients on high ports');
|
||||
like($reexport, qr{\bfsid=\d+}, '... carries an fsid, without which exportfs refuses an NFS mount');
|
||||
like($reexport, qr{\bcrossmnt\b}, '... carries crossmnt, so the mount below it is followed');
|
||||
|
||||
# The fsid must be stable across runs, or every restart hands clients a new filesystem identity
|
||||
# and their mounts go stale.
|
||||
is(xCAT::SvrUtils->nfs_export_line('/install', reexport => 1), $reexport,
|
||||
'the same directory yields the same fsid every time');
|
||||
|
||||
my ($install_fsid) = $reexport =~ /fsid=(\d+)/;
|
||||
my ($tftp_fsid) = xCAT::SvrUtils->nfs_export_line('/tftpboot', reexport => 1) =~ /fsid=(\d+)/;
|
||||
isnt($tftp_fsid, $install_fsid, 'two directories do not share one fsid');
|
||||
cmp_ok($install_fsid, '>', 0, 'the fsid is not 0, which Linux reserves for the export root');
|
||||
# The guard that keeps it non-zero is only reachable when the checksum itself is 0, which an
|
||||
# empty path produces. Drive it directly rather than claim a path that cannot reach it.
|
||||
cmp_ok(xCAT::SvrUtils::_nfs_export_fsid(''), '>', 0,
|
||||
'a path whose checksum is 0 still yields a usable fsid');
|
||||
|
||||
done_testing();
|
||||
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use FindBin;
|
||||
use Test::More;
|
||||
|
||||
# xCAT decides that an Ubuntu osimage uses the Subiquity installer from the NAME of the template
|
||||
# it resolved: xCAT_plugin::debian::using_subiquity matches /subiquity/ in the path. That decision
|
||||
# controls the netboot kernel command line. A template whose name does not match it makes xCAT
|
||||
# boot the live-server initrd with the debian-installer preseed arguments, without boot=casper and
|
||||
# without nfsroot, and the node never finds a live filesystem.
|
||||
#
|
||||
# The compute profile has compute.subiquity.tmpl. The service profile had only service.tmpl, so an
|
||||
# Ubuntu 24.04 service node took the preseed path.
|
||||
|
||||
use lib "$FindBin::Bin/../../perl-xCAT";
|
||||
use lib "$FindBin::Bin/../../xCAT-server/lib/perl";
|
||||
use xCAT::SvrUtils;
|
||||
|
||||
my $plugin = "$FindBin::Bin/../../xCAT-server/lib/xcat/plugins/debian.pm";
|
||||
plan skip_all => 'debian.pm not found' unless -r $plugin;
|
||||
eval { require $plugin; 1 } or plan skip_all => "could not load debian.pm: $@";
|
||||
|
||||
my $share = "$FindBin::Bin/../../xCAT-server/share/xcat/install/ubuntu";
|
||||
plan skip_all => "$share not found" unless -d $share;
|
||||
|
||||
# get_file_name takes genos as its last argument. update_tables_with_templates passes 'subiquity'
|
||||
# for every Ubuntu 20.04 and later osimage.
|
||||
sub resolved {
|
||||
my ($profile, $osver, $genos) = @_;
|
||||
return xCAT::SvrUtils::get_tmpl_file_name($share, $profile, $osver, 'x86_64', $genos);
|
||||
}
|
||||
|
||||
for my $profile (qw(compute service)) {
|
||||
my $tmpl = resolved($profile, 'ubuntu24.04', 'subiquity');
|
||||
ok(defined $tmpl && -r $tmpl, "$profile: Ubuntu 24.04 resolves an install template");
|
||||
ok(xCAT_plugin::debian::using_subiquity('ubuntu24.04', $tmpl),
|
||||
"$profile: the Ubuntu 24.04 template xCAT resolved is a Subiquity one");
|
||||
}
|
||||
|
||||
# Ubuntu 18.04 predates Subiquity and keeps the preseed path. genos stays the os version there.
|
||||
for my $profile (qw(compute service)) {
|
||||
my $tmpl = resolved($profile, 'ubuntu18.04', 'ubuntu18.04');
|
||||
ok(defined $tmpl && -r $tmpl, "$profile: Ubuntu 18.04 resolves an install template");
|
||||
ok(!xCAT_plugin::debian::using_subiquity('ubuntu18.04', $tmpl),
|
||||
"$profile: Ubuntu 18.04 keeps the debian-installer template");
|
||||
}
|
||||
|
||||
# The service template must carry the autoinstall document Subiquity reads. A file named for
|
||||
# Subiquity that holds a preseed would satisfy using_subiquity and still not install.
|
||||
my $service = resolved('service', 'ubuntu24.04', 'subiquity');
|
||||
SKIP: {
|
||||
skip 'no service template resolved', 3 unless defined $service && -r $service;
|
||||
my $text = do { local $/; open my $fh, '<', $service or die "$service: $!"; <$fh> };
|
||||
like($text, qr/^#cloud-config/, 'the service template is a cloud-config document');
|
||||
like($text, qr/^autoinstall:/m, '... carrying an autoinstall section');
|
||||
unlike($text, qr/^d-i /m, '... and no debian-installer preseed directives');
|
||||
}
|
||||
|
||||
done_testing();
|
||||
@@ -828,7 +828,9 @@ while [ $op_index -le $OTHERPKGS_INDEX ]; do
|
||||
else
|
||||
type=file
|
||||
fi
|
||||
echo "deb $type://$whole_path ./" > $REPOFILE
|
||||
# apt refuses an unsigned repository outright, and this one carries a
|
||||
# Packages file and no Release. The rpm arm writes gpgcheck=0 for this.
|
||||
echo "deb [trusted=yes] $type://$whole_path ./" > $REPOFILE
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user