mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-10-20 22:55:40 +00:00
Merge pull request #7936 from VersatusHPC/refactor/r26-genesis-reuse
refactor(genesis): share runtime helpers and build policy
This commit is contained in:
@@ -6,9 +6,24 @@ on:
|
||||
- 'xCAT-genesis-scripts/**'
|
||||
- 'xCAT-test/native/genesis_metadata.py'
|
||||
- 'xCAT-test/native/genesis_console.py'
|
||||
- 'xCAT-test/native/genesis_console_dialogs.py'
|
||||
- '.github/workflows/genesis_openembedded.yml'
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
console_dialogs:
|
||||
name: Genesis console dialogs
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Install console test dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y \
|
||||
build-essential meson ninja-build pkg-config libnewt-dev \
|
||||
libsystemd-dev python3-pyte
|
||||
- name: Exercise the newt dialogs
|
||||
run: /usr/bin/python3 xCAT-test/native/genesis_console_dialogs.py
|
||||
genesis_openembedded:
|
||||
name: OpenEmbedded metadata
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
@@ -37,13 +37,8 @@ machine_dir=$deploy_dir/images/$machine
|
||||
image_name=xcat-genesis-image-$machine.rootfs
|
||||
license_machine=${machine//-/_}
|
||||
|
||||
resolve_artifact() {
|
||||
local path=$1 resolved
|
||||
|
||||
resolved=$(readlink -f -- "$path")
|
||||
[[ -f $resolved && ! -L $resolved ]] || fail "missing artifact: $path"
|
||||
printf '%s\n' "$resolved"
|
||||
}
|
||||
script_dir=$(CDPATH='' cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
|
||||
source "$script_dir/scripts/export-functions"
|
||||
|
||||
kernel=$(resolve_artifact "$machine_dir/$kernel_name")
|
||||
initramfs=$(resolve_artifact "$machine_dir/$image_name.cpio.gz")
|
||||
|
||||
@@ -40,13 +40,7 @@ machine_dir=$deploy_dir/images/$machine
|
||||
stem=$extension-$machine
|
||||
artifact_stem=$stem.sysext
|
||||
|
||||
resolve_artifact() {
|
||||
local path=$1 resolved
|
||||
|
||||
resolved=$(readlink -f -- "$path")
|
||||
[[ -f $resolved && ! -L $resolved ]] || fail "missing artifact: $path"
|
||||
printf '%s\n' "$resolved"
|
||||
}
|
||||
source "$script_dir/scripts/export-functions"
|
||||
|
||||
image=$(resolve_artifact "$machine_dir/$artifact_stem.squashfs-zst")
|
||||
manifest=$(resolve_artifact "$machine_dir/$artifact_stem.manifest.json")
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# Shared image policy; tuning and hardware settings belong to each machine.
|
||||
MACHINE_FEATURES_DEFAULTS = ""
|
||||
MACHINE_EXTRA_RRECOMMENDS = ""
|
||||
IMAGE_FSTYPES = "cpio.gz ext4"
|
||||
QB_DEFAULT_FSTYPE = "ext4"
|
||||
@@ -11,17 +11,13 @@ KERNEL_IMAGETYPE = "Image"
|
||||
SERIAL_CONSOLES = "115200;ttyAMA0"
|
||||
XCAT_GENESIS_CONSOLE_TTY = "ttyAMA0"
|
||||
|
||||
MACHINE_FEATURES_DEFAULTS = ""
|
||||
require conf/machine/include/xcat-genesis-base.inc
|
||||
MACHINE_FEATURES = "pci"
|
||||
MACHINE_ESSENTIAL_EXTRA_RDEPENDS = ""
|
||||
MACHINE_EXTRA_RRECOMMENDS = ""
|
||||
|
||||
IMAGE_FSTYPES = "cpio.gz ext4"
|
||||
|
||||
QB_SYSTEM_NAME = "qemu-system-aarch64"
|
||||
QB_MACHINE = "-machine virt"
|
||||
QB_CPU = "-cpu cortex-a57"
|
||||
QB_SMP = "-smp 2"
|
||||
QB_DEFAULT_FSTYPE = "ext4"
|
||||
QB_KERNEL_CMDLINE_APPEND = "console=ttyAMA0"
|
||||
QB_OPT_APPEND = ""
|
||||
|
||||
@@ -12,17 +12,13 @@ KERNEL_IMAGETYPE = "zImage"
|
||||
SERIAL_CONSOLES = "115200;ttyAMA0"
|
||||
XCAT_GENESIS_CONSOLE_TTY = "ttyAMA0"
|
||||
|
||||
MACHINE_FEATURES_DEFAULTS = ""
|
||||
require conf/machine/include/xcat-genesis-base.inc
|
||||
MACHINE_FEATURES = "pci"
|
||||
MACHINE_ESSENTIAL_EXTRA_RDEPENDS = ""
|
||||
MACHINE_EXTRA_RRECOMMENDS = ""
|
||||
|
||||
IMAGE_FSTYPES = "cpio.gz ext4"
|
||||
|
||||
QB_SYSTEM_NAME = "qemu-system-arm"
|
||||
QB_MACHINE = "-machine virt,highmem=off"
|
||||
QB_CPU = "-cpu cortex-a15"
|
||||
QB_SMP = "-smp 2"
|
||||
QB_DEFAULT_FSTYPE = "ext4"
|
||||
QB_KERNEL_CMDLINE_APPEND = "console=ttyAMA0"
|
||||
QB_OPT_APPEND = ""
|
||||
|
||||
@@ -13,17 +13,13 @@ KERNEL_IMAGETYPE = "vmlinux"
|
||||
SERIAL_CONSOLES = "115200;hvc0"
|
||||
XCAT_GENESIS_CONSOLE_TTY = "hvc0"
|
||||
|
||||
MACHINE_FEATURES_DEFAULTS = ""
|
||||
require conf/machine/include/xcat-genesis-base.inc
|
||||
MACHINE_FEATURES = "pci"
|
||||
MACHINE_ESSENTIAL_EXTRA_RDEPENDS = ""
|
||||
MACHINE_EXTRA_RRECOMMENDS = ""
|
||||
|
||||
IMAGE_FSTYPES = "cpio.gz ext4"
|
||||
|
||||
QB_SYSTEM_NAME = "qemu-system-ppc64"
|
||||
QB_MACHINE = "-machine pseries"
|
||||
QB_CPU = "-cpu POWER8"
|
||||
QB_SMP = "-smp 2"
|
||||
QB_DEFAULT_FSTYPE = "ext4"
|
||||
QB_KERNEL_CMDLINE_APPEND = "console=hvc0 nohugevmalloc"
|
||||
QB_OPT_APPEND = ""
|
||||
|
||||
@@ -13,17 +13,13 @@ KERNEL_IMAGETYPE = "vmlinux"
|
||||
SERIAL_CONSOLES = "115200;hvc0"
|
||||
XCAT_GENESIS_CONSOLE_TTY = "hvc0"
|
||||
|
||||
MACHINE_FEATURES_DEFAULTS = ""
|
||||
require conf/machine/include/xcat-genesis-base.inc
|
||||
MACHINE_FEATURES = "pci"
|
||||
MACHINE_ESSENTIAL_EXTRA_RDEPENDS = ""
|
||||
MACHINE_EXTRA_RRECOMMENDS = ""
|
||||
|
||||
IMAGE_FSTYPES = "cpio.gz ext4"
|
||||
|
||||
QB_SYSTEM_NAME = "qemu-system-ppc64"
|
||||
QB_MACHINE = "-machine pseries"
|
||||
QB_CPU = "-cpu POWER8"
|
||||
QB_SMP = "-smp 2"
|
||||
QB_DEFAULT_FSTYPE = "ext4"
|
||||
QB_KERNEL_CMDLINE_APPEND = "console=hvc0 nohugevmalloc"
|
||||
QB_OPT_APPEND = ""
|
||||
|
||||
@@ -11,12 +11,9 @@ KERNEL_IMAGETYPES = ""
|
||||
SERIAL_CONSOLES = "115200;ttyS0"
|
||||
XCAT_GENESIS_CONSOLE_TTY = "ttyS0"
|
||||
|
||||
MACHINE_FEATURES_DEFAULTS = ""
|
||||
require conf/machine/include/xcat-genesis-base.inc
|
||||
MACHINE_FEATURES = "pci"
|
||||
MACHINE_ESSENTIAL_EXTRA_RDEPENDS = ""
|
||||
MACHINE_EXTRA_RRECOMMENDS = ""
|
||||
|
||||
IMAGE_FSTYPES = "cpio.gz ext4"
|
||||
EXTRA_IMAGEDEPENDS = "opensbi"
|
||||
RISCV_SBI_PLAT = "generic"
|
||||
|
||||
@@ -25,6 +22,5 @@ QB_MACHINE = "-machine virt"
|
||||
QB_CPU = "-cpu rv64"
|
||||
QB_SMP = "-smp 2"
|
||||
QB_DEFAULT_BIOS = "fw_jump.elf"
|
||||
QB_DEFAULT_FSTYPE = "ext4"
|
||||
QB_KERNEL_CMDLINE_APPEND = "console=ttyS0 earlycon=sbi"
|
||||
QB_OPT_APPEND = ""
|
||||
|
||||
@@ -14,12 +14,9 @@ KERNEL_IMAGETYPE = "bzImage"
|
||||
SERIAL_CONSOLES = "115200;ttysclp0"
|
||||
XCAT_GENESIS_CONSOLE_TTY = "ttysclp0"
|
||||
|
||||
MACHINE_FEATURES_DEFAULTS = ""
|
||||
require conf/machine/include/xcat-genesis-base.inc
|
||||
MACHINE_FEATURES = ""
|
||||
MACHINE_ESSENTIAL_EXTRA_RDEPENDS = ""
|
||||
MACHINE_EXTRA_RRECOMMENDS = ""
|
||||
|
||||
IMAGE_FSTYPES = "cpio.gz ext4"
|
||||
|
||||
QB_SYSTEM_NAME = "qemu-system-s390x"
|
||||
QB_MACHINE = "-machine s390-ccw-virtio"
|
||||
@@ -27,7 +24,6 @@ QB_MACHINE = "-machine s390-ccw-virtio"
|
||||
QB_CPU = "-cpu z10EC-base,msa=off,msa1=off,dfp=off,dfphp=off,parseh=off,hfpue=off,hfpm=off,pfpo=off,dateh2=off,csske=off,asnlxr=off,tods=off"
|
||||
QB_SMP = "-smp 2"
|
||||
QB_MEM = "-m 4096"
|
||||
QB_DEFAULT_FSTYPE = "ext4"
|
||||
QB_KERNEL_CMDLINE_APPEND = "console=ttysclp0"
|
||||
QB_NETWORK_DEVICE = "-device virtio-net-ccw,netdev=net0,mac=@MAC@"
|
||||
QB_ROOTFS_OPT = "-drive id=disk0,file=@ROOTFS@,if=none,format=raw -device virtio-blk-ccw,drive=disk0"
|
||||
|
||||
@@ -14,14 +14,10 @@ KERNEL_IMAGETYPE = "bzImage"
|
||||
SERIAL_CONSOLES = "115200;ttyS0"
|
||||
XCAT_GENESIS_CONSOLE_TTY = "ttyS0"
|
||||
|
||||
MACHINE_FEATURES_DEFAULTS = ""
|
||||
require conf/machine/include/xcat-genesis-base.inc
|
||||
MACHINE_FEATURES = "pci"
|
||||
MACHINE_EXTRA_RRECOMMENDS = ""
|
||||
|
||||
IMAGE_FSTYPES = "cpio.gz ext4"
|
||||
|
||||
QB_SYSTEM_NAME = "qemu-system-x86_64"
|
||||
QB_DEFAULT_FSTYPE = "ext4"
|
||||
QB_CPU:x86-64 = "-cpu qemu64 -machine q35,i8042=off"
|
||||
QB_CPU_KVM:x86-64 = "-cpu qemu64 -machine q35,i8042=off"
|
||||
QB_KERNEL_CMDLINE_APPEND = "console=ttyS0"
|
||||
|
||||
@@ -12,13 +12,9 @@ KERNEL_IMAGETYPE = "bzImage"
|
||||
SERIAL_CONSOLES = "115200;ttyS0"
|
||||
XCAT_GENESIS_CONSOLE_TTY = "ttyS0"
|
||||
|
||||
MACHINE_FEATURES_DEFAULTS = ""
|
||||
require conf/machine/include/xcat-genesis-base.inc
|
||||
MACHINE_FEATURES = "pci"
|
||||
MACHINE_ESSENTIAL_EXTRA_RDEPENDS = ""
|
||||
MACHINE_EXTRA_RRECOMMENDS = ""
|
||||
|
||||
IMAGE_FSTYPES = "cpio.gz ext4"
|
||||
|
||||
QB_SYSTEM_NAME = "qemu-system-i386"
|
||||
QB_DEFAULT_FSTYPE = "ext4"
|
||||
QB_KERNEL_CMDLINE_APPEND = "console=ttyS0"
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
SUMMARY = "xCAT Genesis runtime functions"
|
||||
LICENSE = "EPL-1.0"
|
||||
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/EPL-1.0;md5=57f8d5e2b3e98ac6e088986c12bf94e6"
|
||||
|
||||
FILESEXTRAPATHS:prepend := "${THISDIR}/../xcat-genesis-init/files:"
|
||||
SRC_URI = "file://genesis-functions"
|
||||
S = "${UNPACKDIR}"
|
||||
|
||||
RDEPENDS:${PN} = "bash util-linux-logger"
|
||||
|
||||
do_install() {
|
||||
install -d ${D}${libexecdir}/xcat
|
||||
install -m 0644 ${UNPACKDIR}/genesis-functions \
|
||||
${D}${libexecdir}/xcat/genesis-functions
|
||||
}
|
||||
|
||||
FILES:${PN} = "${libexecdir}/xcat/genesis-functions"
|
||||
+15
-30
@@ -187,25 +187,27 @@ static const char help_text[] =
|
||||
" F12 asks before opening a root maintenance shell. Exit returns here.\n"
|
||||
" Up and Down scroll this page.";
|
||||
|
||||
static void show_help(void) {
|
||||
static void show_text_dialog(const char *title, const char *text, int back_key,
|
||||
const char *help_line) {
|
||||
struct newtExitStruct event;
|
||||
newtComponent form;
|
||||
newtComponent text_box;
|
||||
|
||||
newtCenteredWindow(72, 19, "Genesis help");
|
||||
newtCenteredWindow(72, 19, title);
|
||||
form = newtForm(NULL, NULL, NEWT_FLAG_NOF12);
|
||||
text_box = newtTextbox(1, 1, 68, 17, NEWT_FLAG_WRAP | NEWT_FLAG_SCROLL);
|
||||
newtTextboxSetText(text_box, help_text);
|
||||
newtTextboxSetText(text_box, text);
|
||||
newtFormAddComponent(form, text_box);
|
||||
newtFormAddHotKey(form, NEWT_KEY_F1);
|
||||
newtFormAddHotKey(form, back_key);
|
||||
newtFormAddHotKey(form, NEWT_KEY_ESCAPE);
|
||||
newtPushHelpLine(" F1 Back Esc Back Up/Down Scroll");
|
||||
newtPushHelpLine(help_line);
|
||||
newtRefresh();
|
||||
|
||||
while (!xcat_console_stop_requested) {
|
||||
newtFormRun(form, &event);
|
||||
if (event.reason == NEWT_EXIT_ERROR ||
|
||||
(event.reason == NEWT_EXIT_HOTKEY &&
|
||||
(event.u.key == NEWT_KEY_F1 || event.u.key == NEWT_KEY_ESCAPE)))
|
||||
(event.u.key == back_key || event.u.key == NEWT_KEY_ESCAPE)))
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -214,36 +216,19 @@ static void show_help(void) {
|
||||
newtPopWindow();
|
||||
}
|
||||
|
||||
static void show_help(void) {
|
||||
show_text_dialog("Genesis help", help_text, NEWT_KEY_F1,
|
||||
" F1 Back Esc Back Up/Down Scroll");
|
||||
}
|
||||
|
||||
static void show_diagnostics(void) {
|
||||
struct console_state state;
|
||||
struct newtExitStruct event;
|
||||
newtComponent form;
|
||||
newtComponent text_box;
|
||||
char text[4096];
|
||||
|
||||
newtCenteredWindow(72, 19, "Genesis diagnostics");
|
||||
form = newtForm(NULL, NULL, NEWT_FLAG_NOF12);
|
||||
text_box = newtTextbox(1, 1, 68, 17, NEWT_FLAG_WRAP | NEWT_FLAG_SCROLL);
|
||||
newtFormAddComponent(form, text_box);
|
||||
newtFormAddHotKey(form, NEWT_KEY_F2);
|
||||
newtFormAddHotKey(form, NEWT_KEY_ESCAPE);
|
||||
newtPushHelpLine(" F2 Back Esc Back Up/Down Scroll");
|
||||
|
||||
xcat_load_console_state(&state);
|
||||
xcat_format_diagnostics(&state, text, sizeof(text));
|
||||
newtTextboxSetText(text_box, text);
|
||||
newtRefresh();
|
||||
while (!xcat_console_stop_requested) {
|
||||
newtFormRun(form, &event);
|
||||
if (event.reason == NEWT_EXIT_ERROR ||
|
||||
(event.reason == NEWT_EXIT_HOTKEY &&
|
||||
(event.u.key == NEWT_KEY_F2 || event.u.key == NEWT_KEY_ESCAPE)))
|
||||
break;
|
||||
}
|
||||
|
||||
newtFormDestroy(form);
|
||||
newtPopHelpLine();
|
||||
newtPopWindow();
|
||||
show_text_dialog("Genesis diagnostics", text, NEWT_KEY_F2,
|
||||
" F2 Back Esc Back Up/Down Scroll");
|
||||
}
|
||||
|
||||
static bool journal_value(sd_journal *journal, const char *field, char *value, size_t size) {
|
||||
|
||||
+9
-19
@@ -43,6 +43,7 @@ publish_status() {
|
||||
[[ -z $last_verified_seconds ]] \
|
||||
|| fields+=("VERIFIED_SECONDS=$last_verified_seconds")
|
||||
genesis_publish_status "$status_command" action xcat-genesis-action \
|
||||
'unable to publish action status' \
|
||||
"$state" "$detail" "${fields[@]}" "$@"
|
||||
}
|
||||
|
||||
@@ -102,21 +103,10 @@ store_destiny() {
|
||||
parse_destiny "$raw"
|
||||
}
|
||||
|
||||
request_current_action() {
|
||||
local action
|
||||
request_action() {
|
||||
local command=$1 action
|
||||
|
||||
if ! action=$(timeout "$request_timeout" "$getdestiny_command" \
|
||||
"$XCATDEST" --metadata "$metadata_file" --once); then
|
||||
return 1
|
||||
fi
|
||||
store_destiny "$action" || return 1
|
||||
last_verified_seconds=$(genesis_uptime_seconds)
|
||||
}
|
||||
|
||||
request_next_action() {
|
||||
local action
|
||||
|
||||
if ! action=$(timeout "$request_timeout" "$nextdestiny_command" \
|
||||
if ! action=$(timeout "$request_timeout" "$command" \
|
||||
"$XCATDEST" --metadata "$metadata_file" --once); then
|
||||
return 1
|
||||
fi
|
||||
@@ -195,7 +185,7 @@ for ((step = 1; ; step++)); do
|
||||
'xCAT did not accept the node discovery' \
|
||||
'Review active discovery methods and node matching'
|
||||
publish_status RUNNING 'Loading the discovered node assignment'
|
||||
request_current_action \
|
||||
request_action "$getdestiny_command" \
|
||||
|| fail_action DISCOVERY_ASSIGNMENT_FAILED \
|
||||
'The discovered node has no xCAT assignment' \
|
||||
'Review the discovered node definition and chain'
|
||||
@@ -208,7 +198,7 @@ for ((step = 1; ; step++)); do
|
||||
publish_status IDLE 'Waiting for an action from xCAT' \
|
||||
"NEXT_RETRY_SECONDS=$poll_seconds"
|
||||
sleep "$poll_seconds"
|
||||
if ! request_current_action; then
|
||||
if ! request_action "$getdestiny_command"; then
|
||||
publish_status DEGRADED 'xCAT did not answer the action poll' \
|
||||
'CODE=XCAT_ACTION_POLL_FAILED' \
|
||||
'RECOVERY=Check xcatd and the management network' \
|
||||
@@ -217,14 +207,14 @@ for ((step = 1; ; step++)); do
|
||||
;;
|
||||
osimage|ondiscover)
|
||||
publish_status RUNNING 'Advancing the xCAT action chain'
|
||||
request_next_action \
|
||||
request_action "$nextdestiny_command" \
|
||||
|| fail_action XCAT_CHAIN_ADVANCE_FAILED \
|
||||
'xCAT did not advance the node action chain' \
|
||||
'Review the node chain and xcatd logs'
|
||||
;;
|
||||
runcmd)
|
||||
run_approved_command
|
||||
request_next_action \
|
||||
request_action "$nextdestiny_command" \
|
||||
|| fail_action XCAT_CHAIN_ADVANCE_FAILED \
|
||||
'xCAT did not advance after the approved action' \
|
||||
'Review the node chain and xcatd logs'
|
||||
@@ -246,7 +236,7 @@ for ((step = 1; ; step++)); do
|
||||
;;
|
||||
boot|reboot)
|
||||
publish_status RUNNING 'Advancing the chain before reboot'
|
||||
request_next_action \
|
||||
request_action "$nextdestiny_command" \
|
||||
|| fail_action XCAT_CHAIN_ADVANCE_FAILED \
|
||||
'xCAT did not advance the node action chain' \
|
||||
'Review the node chain and xcatd logs'
|
||||
|
||||
+3
-3
@@ -1,9 +1,9 @@
|
||||
genesis_publish_status() {
|
||||
local status_command=$1 component=$2 log_tag=$3
|
||||
shift 3
|
||||
local status_command=$1 component=$2 log_tag=$3 log_message=$4
|
||||
shift 4
|
||||
|
||||
"$status_command" "$component" "$@" \
|
||||
|| logger -t "$log_tag" -- "unable to publish $component status"
|
||||
|| logger -t "$log_tag" -- "$log_message"
|
||||
}
|
||||
|
||||
genesis_uptime_seconds() {
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ failure_recovery='Check the management network and boot parameters'
|
||||
|
||||
publish_status() {
|
||||
genesis_publish_status "$status_command" network \
|
||||
xcat-genesis-network-state "$@"
|
||||
xcat-genesis-network-state 'unable to publish network status' "$@"
|
||||
}
|
||||
|
||||
finish_status() {
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ declare -a cmdline_arguments
|
||||
|
||||
publish_status() {
|
||||
genesis_publish_status "$status_command" registration \
|
||||
xcat-genesis-register "$@"
|
||||
xcat-genesis-register 'unable to publish registration status' "$@"
|
||||
}
|
||||
|
||||
finish_status() {
|
||||
|
||||
+1
-5
@@ -3,7 +3,6 @@ LICENSE = "EPL-1.0"
|
||||
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/EPL-1.0;md5=57f8d5e2b3e98ac6e088986c12bf94e6"
|
||||
|
||||
SRC_URI = "file://00-xcat-genesis.preset \
|
||||
file://genesis-functions \
|
||||
file://genesis-maintenance-shell \
|
||||
file://genesis-action \
|
||||
file://genesis-metrics \
|
||||
@@ -21,7 +20,7 @@ S = "${UNPACKDIR}"
|
||||
|
||||
inherit systemd
|
||||
|
||||
RDEPENDS:${PN} = "bash coreutils ipmitool iproute2 networkmanager-nmcli util-linux-logger xcat-genesis-discovery xcat-genesis-protocol"
|
||||
RDEPENDS:${PN} = "bash coreutils ipmitool iproute2 networkmanager-nmcli util-linux-logger xcat-genesis-common xcat-genesis-discovery xcat-genesis-protocol"
|
||||
|
||||
SYSTEMD_SERVICE:${PN} = "xcat-genesis-action.service \
|
||||
xcat-genesis-metrics.service \
|
||||
@@ -32,8 +31,6 @@ SYSTEMD_AUTO_ENABLE = "enable"
|
||||
|
||||
do_install() {
|
||||
install -d ${D}${libexecdir}/xcat
|
||||
install -m 0644 ${UNPACKDIR}/genesis-functions \
|
||||
${D}${libexecdir}/xcat/genesis-functions
|
||||
install -m 0755 ${UNPACKDIR}/genesis-maintenance-shell \
|
||||
${D}${libexecdir}/xcat/genesis-maintenance-shell
|
||||
install -m 0755 ${UNPACKDIR}/genesis-action \
|
||||
@@ -67,7 +64,6 @@ do_install() {
|
||||
}
|
||||
|
||||
FILES:${PN} = "${libexecdir}/xcat/genesis-maintenance-shell \
|
||||
${libexecdir}/xcat/genesis-functions \
|
||||
${libexecdir}/xcat/genesis-action \
|
||||
${libexecdir}/xcat/genesis-metrics \
|
||||
${libexecdir}/xcat/genesis-network-refresh \
|
||||
|
||||
@@ -57,7 +57,7 @@ SRC_URI:append:xcat-genesis-ppc64le = " \
|
||||
file://xcat-genesis-powerpc64.cfg \
|
||||
"
|
||||
|
||||
do_kernel_metadata:prepend:xcat-genesis-ppc64le() {
|
||||
xcat_genesis_patch_power_metadata() {
|
||||
metadata=${UNPACKDIR}/${KMETA}/bsp/qemu-ppc64/qemu-ppc64.cfg
|
||||
if grep -qx 'CONFIG_CRC32_SLICEBY8=y' "$metadata"; then
|
||||
patch -p1 -d ${UNPACKDIR}/${KMETA} \
|
||||
@@ -65,6 +65,10 @@ do_kernel_metadata:prepend:xcat-genesis-ppc64le() {
|
||||
fi
|
||||
}
|
||||
|
||||
do_kernel_metadata:prepend:xcat-genesis-ppc64le() {
|
||||
xcat_genesis_patch_power_metadata
|
||||
}
|
||||
|
||||
COMPATIBLE_MACHINE:xcat-genesis-ppc64 = "^xcat-genesis-ppc64$"
|
||||
KMACHINE:xcat-genesis-ppc64 = "qemuppc64"
|
||||
KBRANCH:xcat-genesis-ppc64 = "v6.18/standard/base"
|
||||
@@ -77,11 +81,7 @@ SRC_URI:append:xcat-genesis-ppc64 = " \
|
||||
"
|
||||
|
||||
do_kernel_metadata:prepend:xcat-genesis-ppc64() {
|
||||
metadata=${UNPACKDIR}/${KMETA}/bsp/qemu-ppc64/qemu-ppc64.cfg
|
||||
if grep -qx 'CONFIG_CRC32_SLICEBY8=y' "$metadata"; then
|
||||
patch -p1 -d ${UNPACKDIR}/${KMETA} \
|
||||
< ${UNPACKDIR}/0001-qemu-ppc64-drop-removed-crc32-option.patch
|
||||
fi
|
||||
xcat_genesis_patch_power_metadata
|
||||
}
|
||||
do_kernel_metadata[depends] += "patch-native:do_populate_sysroot"
|
||||
|
||||
|
||||
+5
-2
@@ -1,6 +1,9 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
functions_file=${XCAT_GENESIS_FUNCTIONS:-/usr/libexec/xcat/genesis-functions}
|
||||
source "$functions_file"
|
||||
|
||||
state_dir=${XCAT_STATE_DIR:-/run/xcat}
|
||||
status_command=${XCAT_STATUS_COMMAND:-/usr/libexec/xcat/genesis-status}
|
||||
network_file=${XCAT_NETWORK_FILE:-$state_dir/genesis.env}
|
||||
@@ -22,8 +25,8 @@ temporary_dir=
|
||||
[[ "$retry_seconds" =~ ^[0-9]+$ ]] || retry_seconds=5
|
||||
|
||||
publish_status() {
|
||||
"$status_command" discovery "$@" \
|
||||
|| logger -t xcat-genesis-discover -- 'unable to publish discovery status'
|
||||
genesis_publish_status "$status_command" discovery \
|
||||
xcat-genesis-discover 'unable to publish discovery status' "$@"
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2317,SC2329
|
||||
|
||||
+5
-2
@@ -1,6 +1,9 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
functions_file=${XCAT_GENESIS_FUNCTIONS:-/usr/libexec/xcat/genesis-functions}
|
||||
source "$functions_file"
|
||||
|
||||
state_dir=${XCAT_STATE_DIR:-/run/xcat}
|
||||
status_command=${XCAT_STATUS_COMMAND:-/usr/libexec/xcat/genesis-status}
|
||||
network_file=${XCAT_NETWORK_FILE:-$state_dir/genesis.env}
|
||||
@@ -15,8 +18,8 @@ temporary_dir=
|
||||
[[ "$request_timeout" =~ ^[1-9][0-9]*$ ]] || request_timeout=20
|
||||
|
||||
publish_status() {
|
||||
"$status_command" credentials "$@" \
|
||||
|| logger -t xcat-genesis-getcert -- 'unable to publish credential status'
|
||||
genesis_publish_status "$status_command" credentials \
|
||||
xcat-genesis-getcert 'unable to publish credential status' "$@"
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2329
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ do_compile() {
|
||||
-o genesis-udp-send
|
||||
}
|
||||
|
||||
RDEPENDS:${PN} = "bash coreutils gzip iproute2 openssl-bin util-linux-logger util-linux-lsblk"
|
||||
RDEPENDS:${PN} = "bash coreutils gzip iproute2 openssl-bin util-linux-logger util-linux-lsblk xcat-genesis-common"
|
||||
|
||||
SYSTEMD_SERVICE:${PN} = "xcat-genesis-discovery.socket \
|
||||
xcat-genesis-credential.socket \
|
||||
|
||||
+6
@@ -3,9 +3,11 @@ LICENSE = "EPL-1.0"
|
||||
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/EPL-1.0;md5=57f8d5e2b3e98ac6e088986c12bf94e6"
|
||||
|
||||
FILESEXTRAPATHS:prepend := "${THISDIR}/../../../../../xCAT-genesis-scripts/usr/bin:"
|
||||
FILESEXTRAPATHS:prepend := "${THISDIR}/../../../../../xCAT-genesis-scripts/usr/lib/xcat:"
|
||||
|
||||
SRC_URI = "file://getdestiny \
|
||||
file://nextdestiny \
|
||||
file://destiny.sh \
|
||||
"
|
||||
S = "${UNPACKDIR}"
|
||||
|
||||
@@ -15,4 +17,8 @@ do_install() {
|
||||
install -d ${D}${bindir}
|
||||
install -m 0755 ${UNPACKDIR}/getdestiny ${D}${bindir}/getdestiny
|
||||
install -m 0755 ${UNPACKDIR}/nextdestiny ${D}${bindir}/nextdestiny
|
||||
install -d ${D}${prefix}/lib/xcat
|
||||
install -m 0644 ${UNPACKDIR}/destiny.sh ${D}${prefix}/lib/xcat/destiny.sh
|
||||
}
|
||||
|
||||
FILES:${PN} += "${prefix}/lib/xcat/destiny.sh"
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
resolve_artifact() {
|
||||
local path=$1 resolved
|
||||
|
||||
resolved=$(readlink -f -- "$path")
|
||||
[[ -f $resolved && ! -L $resolved ]] || fail "missing artifact: $path"
|
||||
printf '%s\n' "$resolved"
|
||||
}
|
||||
@@ -43,26 +43,12 @@ done
|
||||
|
||||
[[ -n $destination ]] || usage
|
||||
|
||||
source "$(dirname -- "${BASH_SOURCE[0]}")/../lib/xcat/destiny.sh" || exit 1
|
||||
|
||||
temporary_dir=$(mktemp -d "${TMPDIR:-/tmp}/getdestiny.XXXXXX") || exit 1
|
||||
request_file=$temporary_dir/request.xml
|
||||
response_file=$temporary_dir/response.xml
|
||||
|
||||
cleanup() {
|
||||
rm -rf -- "$temporary_dir"
|
||||
[[ -z $metadata_temporary ]] || rm -f -- "$metadata_temporary"
|
||||
}
|
||||
|
||||
terminate() {
|
||||
local status=$1
|
||||
|
||||
trap - HUP INT TERM
|
||||
if [[ -n $request_pid ]]; then
|
||||
kill -TERM "$request_pid" 2>/dev/null || true
|
||||
wait "$request_pid" 2>/dev/null || true
|
||||
fi
|
||||
exit "$status"
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
trap 'terminate 129' HUP
|
||||
trap 'terminate 130' INT
|
||||
@@ -77,17 +63,7 @@ printf '%s\n' \
|
||||
request_destiny() {
|
||||
local request_status=0
|
||||
|
||||
if [[ -f /etc/xcat/cert.pem && -f /etc/xcat/certkey.pem ]]; then
|
||||
openssl s_client -key /etc/xcat/certkey.pem \
|
||||
-cert /etc/xcat/cert.pem -connect "$destination" -quiet \
|
||||
2>/dev/null <"$request_file" >"$response_file" &
|
||||
else
|
||||
openssl s_client -connect "$destination" -quiet \
|
||||
2>/dev/null <"$request_file" >"$response_file" &
|
||||
fi
|
||||
request_pid=$!
|
||||
wait "$request_pid" || request_status=$?
|
||||
request_pid=
|
||||
send_destiny_request || request_status=$?
|
||||
if ((request_status != 0)) && [[ ! -s $response_file ]]; then
|
||||
return 1
|
||||
fi
|
||||
@@ -105,13 +81,6 @@ while ! request_destiny; do
|
||||
printf '%*s\r' 48 '' >&2
|
||||
done
|
||||
|
||||
extract_element() {
|
||||
local element=$1
|
||||
|
||||
sed -n "s:.*<$element>\([^<]*\)</$element>.*:\1:p" "$response_file" \
|
||||
| head -n 1 | tr -cd '\40-\176'
|
||||
}
|
||||
|
||||
destiny=$(extract_element destiny)
|
||||
if [[ -z $destiny ]]; then
|
||||
((once)) && exit 1
|
||||
@@ -120,24 +89,7 @@ if [[ -z $destiny ]]; then
|
||||
fi
|
||||
|
||||
if [[ -n $metadata_file ]]; then
|
||||
metadata_dir=${metadata_file%/*}
|
||||
if [[ $metadata_dir == "$metadata_file" ]]; then
|
||||
metadata_dir=.
|
||||
elif [[ -z $metadata_dir ]]; then
|
||||
metadata_dir=/
|
||||
fi
|
||||
[[ -d $metadata_dir ]] || exit 1
|
||||
metadata_temporary=$(mktemp "$metadata_dir/.getdestiny.XXXXXX") || exit 1
|
||||
{
|
||||
printf 'XCAT_NODE_NAME=%s\n' "$(extract_element name)"
|
||||
printf 'XCAT_DESTINY=%s\n' "$destiny"
|
||||
printf 'XCAT_IMAGE_SERVER=%s\n' "$(extract_element imgserver)"
|
||||
printf 'XCAT_KERNEL=%s\n' "$(extract_element kernel)"
|
||||
printf 'XCAT_INITRD=%s\n' "$(extract_element initrd)"
|
||||
printf 'XCAT_KERNEL_COMMAND_LINE=%s\n' "$(extract_element kcmdline)"
|
||||
} >"$metadata_temporary"
|
||||
chmod 0600 "$metadata_temporary"
|
||||
mv -f -- "$metadata_temporary" "$metadata_file"
|
||||
write_metadata getdestiny
|
||||
fi
|
||||
|
||||
printf '%s\n' "$destiny"
|
||||
|
||||
@@ -43,26 +43,12 @@ done
|
||||
|
||||
[[ -n $destination ]] || usage
|
||||
|
||||
source "$(dirname -- "${BASH_SOURCE[0]}")/../lib/xcat/destiny.sh" || exit 1
|
||||
|
||||
temporary_dir=$(mktemp -d "${TMPDIR:-/tmp}/nextdestiny.XXXXXX") || exit 1
|
||||
request_file=$temporary_dir/request.xml
|
||||
response_file=$temporary_dir/response.xml
|
||||
|
||||
cleanup() {
|
||||
rm -rf -- "$temporary_dir"
|
||||
[[ -z $metadata_temporary ]] || rm -f -- "$metadata_temporary"
|
||||
}
|
||||
|
||||
terminate() {
|
||||
local status=$1
|
||||
|
||||
trap - HUP INT TERM
|
||||
if [[ -n $request_pid ]]; then
|
||||
kill -TERM "$request_pid" 2>/dev/null || true
|
||||
wait "$request_pid" 2>/dev/null || true
|
||||
fi
|
||||
exit "$status"
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
trap 'terminate 129' HUP
|
||||
trap 'terminate 130' INT
|
||||
@@ -75,17 +61,7 @@ printf '%s\n' \
|
||||
'</xcatrequest>' >"$request_file"
|
||||
|
||||
request_destiny() {
|
||||
if [[ -f /etc/xcat/cert.pem && -f /etc/xcat/certkey.pem ]]; then
|
||||
openssl s_client -key /etc/xcat/certkey.pem \
|
||||
-cert /etc/xcat/cert.pem -connect "$destination" -quiet \
|
||||
2>/dev/null <"$request_file" >"$response_file" &
|
||||
else
|
||||
openssl s_client -connect "$destination" -quiet \
|
||||
2>/dev/null <"$request_file" >"$response_file" &
|
||||
fi
|
||||
request_pid=$!
|
||||
wait "$request_pid" || true
|
||||
request_pid=
|
||||
send_destiny_request || true
|
||||
[[ -s $response_file ]]
|
||||
}
|
||||
|
||||
@@ -98,13 +74,6 @@ else
|
||||
request_destiny || true
|
||||
fi
|
||||
|
||||
extract_element() {
|
||||
local element=$1
|
||||
|
||||
sed -n "s:.*<$element>\([^<]*\)</$element>.*:\1:p" "$response_file" \
|
||||
| head -n 1 | tr -cd '\40-\176'
|
||||
}
|
||||
|
||||
destiny=$(extract_element destiny)
|
||||
if [[ -z $destiny ]]; then
|
||||
error=$(extract_element error)
|
||||
@@ -113,24 +82,7 @@ if [[ -z $destiny ]]; then
|
||||
fi
|
||||
|
||||
if [[ -n $metadata_file ]]; then
|
||||
metadata_dir=${metadata_file%/*}
|
||||
if [[ $metadata_dir == "$metadata_file" ]]; then
|
||||
metadata_dir=.
|
||||
elif [[ -z $metadata_dir ]]; then
|
||||
metadata_dir=/
|
||||
fi
|
||||
[[ -d $metadata_dir ]] || exit 1
|
||||
metadata_temporary=$(mktemp "$metadata_dir/.nextdestiny.XXXXXX") || exit 1
|
||||
{
|
||||
printf 'XCAT_NODE_NAME=%s\n' "$(extract_element name)"
|
||||
printf 'XCAT_DESTINY=%s\n' "$destiny"
|
||||
printf 'XCAT_IMAGE_SERVER=%s\n' "$(extract_element imgserver)"
|
||||
printf 'XCAT_KERNEL=%s\n' "$(extract_element kernel)"
|
||||
printf 'XCAT_INITRD=%s\n' "$(extract_element initrd)"
|
||||
printf 'XCAT_KERNEL_COMMAND_LINE=%s\n' "$(extract_element kcmdline)"
|
||||
} >"$metadata_temporary"
|
||||
chmod 0600 "$metadata_temporary"
|
||||
mv -f -- "$metadata_temporary" "$metadata_file"
|
||||
write_metadata nextdestiny
|
||||
fi
|
||||
|
||||
printf '%s\n' "$destiny"
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
cleanup() {
|
||||
rm -rf -- "$temporary_dir"
|
||||
[[ -z $metadata_temporary ]] || rm -f -- "$metadata_temporary"
|
||||
}
|
||||
|
||||
terminate() {
|
||||
local status=$1
|
||||
|
||||
trap - HUP INT TERM
|
||||
if [[ -n $request_pid ]]; then
|
||||
kill -TERM "$request_pid" 2>/dev/null || true
|
||||
wait "$request_pid" 2>/dev/null || true
|
||||
fi
|
||||
exit "$status"
|
||||
}
|
||||
|
||||
send_destiny_request() {
|
||||
local request_status=0
|
||||
|
||||
if [[ -f /etc/xcat/cert.pem && -f /etc/xcat/certkey.pem ]]; then
|
||||
openssl s_client -key /etc/xcat/certkey.pem \
|
||||
-cert /etc/xcat/cert.pem -connect "$destination" -quiet \
|
||||
2>/dev/null <"$request_file" >"$response_file" &
|
||||
else
|
||||
openssl s_client -connect "$destination" -quiet \
|
||||
2>/dev/null <"$request_file" >"$response_file" &
|
||||
fi
|
||||
request_pid=$!
|
||||
wait "$request_pid" || request_status=$?
|
||||
request_pid=
|
||||
return "$request_status"
|
||||
}
|
||||
|
||||
extract_element() {
|
||||
local element=$1
|
||||
|
||||
sed -n "s:.*<$element>\([^<]*\)</$element>.*:\1:p" "$response_file" \
|
||||
| head -n 1 | tr -cd '\40-\176'
|
||||
}
|
||||
|
||||
write_metadata() {
|
||||
local command=$1 metadata_dir
|
||||
|
||||
metadata_dir=${metadata_file%/*}
|
||||
if [[ $metadata_dir == "$metadata_file" ]]; then
|
||||
metadata_dir=.
|
||||
elif [[ -z $metadata_dir ]]; then
|
||||
metadata_dir=/
|
||||
fi
|
||||
[[ -d $metadata_dir ]] || exit 1
|
||||
metadata_temporary=$(mktemp "$metadata_dir/.$command.XXXXXX") || exit 1
|
||||
{
|
||||
printf 'XCAT_NODE_NAME=%s\n' "$(extract_element name)"
|
||||
printf 'XCAT_DESTINY=%s\n' "$destiny"
|
||||
printf 'XCAT_IMAGE_SERVER=%s\n' "$(extract_element imgserver)"
|
||||
printf 'XCAT_KERNEL=%s\n' "$(extract_element kernel)"
|
||||
printf 'XCAT_INITRD=%s\n' "$(extract_element initrd)"
|
||||
printf 'XCAT_KERNEL_COMMAND_LINE=%s\n' "$(extract_element kcmdline)"
|
||||
} >"$metadata_temporary"
|
||||
chmod 0600 "$metadata_temporary"
|
||||
mv -f -- "$metadata_temporary" "$metadata_file"
|
||||
}
|
||||
@@ -113,6 +113,7 @@ touch /etc/xcat/genesis-scripts-updated
|
||||
%{rpminstallroot}/usr/bin/update_flash_nv
|
||||
%{rpminstallroot}/usr/bin/restart
|
||||
%{rpminstallroot}/usr/lib/xcat/fips.sh
|
||||
%{rpminstallroot}/usr/lib/xcat/destiny.sh
|
||||
%{rpminstallroot}/etc/init.d/functions
|
||||
%{rpminstallroot}/etc/udev/rules.d/99-imm.rules
|
||||
%{rpminstallroot}/etc/udev/rules.d/99-tsm.rules
|
||||
|
||||
@@ -27,6 +27,7 @@ setup()
|
||||
cat >"$bin/getdestiny" <<'SH'
|
||||
#!/bin/sh
|
||||
printf 'getdestiny %s\n' "$*" >>"$XCAT_TEST_LOG"
|
||||
[ -z "${XCAT_TEST_DESTINY_DELAY-}" ] || sleep "$XCAT_TEST_DESTINY_DELAY"
|
||||
metadata=
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case "$1" in
|
||||
@@ -46,13 +47,14 @@ SH
|
||||
cat >"$bin/nextdestiny" <<'SH'
|
||||
#!/bin/sh
|
||||
printf 'nextdestiny %s\n' "$*" >>"$XCAT_TEST_LOG"
|
||||
[ -z "${XCAT_TEST_DESTINY_DELAY-}" ] || sleep "$XCAT_TEST_DESTINY_DELAY"
|
||||
[ -s "$XCAT_TEST_NEXTDESTINY_QUEUE" ] || exit 1
|
||||
response=$(head -n 1 "$XCAT_TEST_NEXTDESTINY_QUEUE")
|
||||
tail -n +2 "$XCAT_TEST_NEXTDESTINY_QUEUE" >"$XCAT_TEST_NEXTDESTINY_QUEUE.new"
|
||||
mv "$XCAT_TEST_NEXTDESTINY_QUEUE.new" "$XCAT_TEST_NEXTDESTINY_QUEUE"
|
||||
printf '%s\n' "$response"
|
||||
SH
|
||||
printf '#!/bin/sh\nprintf "logger %%s\\n" "$*" >>"$XCAT_TEST_LOG"\n' >"$bin/logger"
|
||||
printf '#!/bin/sh\nprintf "logger %%s\\n" "$*" >>"$XCAT_TEST_LOG"\n[ -z "${XCAT_TEST_LOGGER_FAIL-}" ]\n' >"$bin/logger"
|
||||
printf '#!/bin/sh\nprintf "%%s\\n" discover >>"$XCAT_TEST_LOG"\n' >"$bin/discover"
|
||||
printf '#!/bin/sh\nprintf "%%s\\n" getcert >>"$XCAT_TEST_LOG"\n: >"$XCAT_TEST_CERTIFICATE_FILE"\n' >"$bin/getcert"
|
||||
printf '#!/bin/sh\nprintf "reboot-control %%s\\n" "$*" >>"$XCAT_TEST_LOG"\n' >"$bin/reboot-control"
|
||||
@@ -221,3 +223,59 @@ has_no_line()
|
||||
[ "$status" -eq 0 ]
|
||||
has_line "$record" '^STATE=DEGRADED$'
|
||||
}
|
||||
|
||||
@test "current and next requests pass the endpoint, metadata path and one-shot flag" {
|
||||
export XCAT_ACTION_REQUEST_TIMEOUT=10
|
||||
GETDESTINY=$'standby\n' MAX_STEPS=2 run_action standby
|
||||
[ "$status" -eq 0 ] || { printf '%s\n' "$log" "$record"; return 1; }
|
||||
grep -Fx "getdestiny 192.0.2.10:3001 --metadata $XCAT_METADATA_FILE --once" "$command_log"
|
||||
has_line "$record" '^VERIFIED_SECONDS=42$'
|
||||
[ "$(cat "$destiny_file")" = standby ]
|
||||
[ "$(stat -c %a "$destiny_file")" = 600 ]
|
||||
|
||||
GETDESTINY=$'standby\n' NEXTDESTINY=$'standby\n' MAX_STEPS=2 run_action osimage
|
||||
[ "$status" -eq 0 ] || { printf '%s\n' "$log" "$record"; return 1; }
|
||||
grep -Fx "nextdestiny 192.0.2.10:3001 --metadata $XCAT_METADATA_FILE --once" "$command_log"
|
||||
has_line "$record" '^VERIFIED_SECONDS=42$'
|
||||
has_line "$record" '^ACTION=standby$'
|
||||
[ "$(cat "$destiny_file")" = standby ]
|
||||
}
|
||||
|
||||
@test "empty current and next responses preserve the last action with distinct failure policies" {
|
||||
GETDESTINY=$'\n' run_action standby
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$(cat "$destiny_file")" = standby ]
|
||||
has_line "$record" '^STATE=DEGRADED$'
|
||||
has_line "$record" '^CODE=XCAT_ACTION_POLL_FAILED$'
|
||||
has_no_line "$record" '^VERIFIED_SECONDS='
|
||||
|
||||
NEXTDESTINY=$'\n' run_action osimage
|
||||
[ "$status" -eq 1 ]
|
||||
[ "$(cat "$destiny_file")" = osimage ]
|
||||
has_line "$record" '^STATE=FAILED$'
|
||||
has_line "$record" '^CODE=XCAT_CHAIN_ADVANCE_FAILED$'
|
||||
has_no_line "$record" '^VERIFIED_SECONDS='
|
||||
}
|
||||
|
||||
@test "current and next action requests enforce the request timeout" {
|
||||
export XCAT_ACTION_REQUEST_TIMEOUT=1 XCAT_TEST_DESTINY_DELAY=10
|
||||
run_action standby
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$(cat "$destiny_file")" = standby ]
|
||||
has_line "$record" '^CODE=XCAT_ACTION_POLL_FAILED$'
|
||||
|
||||
run_action osimage
|
||||
[ "$status" -eq 1 ]
|
||||
[ "$(cat "$destiny_file")" = osimage ]
|
||||
has_line "$record" '^CODE=XCAT_CHAIN_ADVANCE_FAILED$'
|
||||
}
|
||||
|
||||
@test "action status failures use the caller diagnostic and propagate a logging failure" {
|
||||
export XCAT_STATUS_COMMAND=/bin/false
|
||||
run_action standby
|
||||
[ "$status" -eq 0 ]
|
||||
grep -Fx 'logger -t xcat-genesis-action -- unable to publish action status' "$command_log"
|
||||
[ "$(cat "$destiny_file")" = standby ]
|
||||
XCAT_TEST_LOGGER_FAIL=1 run_action standby
|
||||
[ "$status" -eq 1 ]
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ setup()
|
||||
printf '%s\n' XCATDEST=192.0.2.10:3001 XCATMASTER=192.0.2.10 XCATPORT=3001 \
|
||||
XCAT_INTERFACE=eth0 XCAT_SOURCE_ADDRESS=192.0.2.98 >"$state_dir/genesis.env"
|
||||
|
||||
printf '#!/bin/sh\n[ -z "${XCAT_TEST_LOGGER_FAIL-}" ]\n' >"$bin/logger"
|
||||
printf '#!/bin/sh\nprintf "logger %%s\\n" "$*" >>"$XCAT_TEST_LOG"\n[ -z "${XCAT_TEST_LOGGER_FAIL-}" ]\n' >"$bin/logger"
|
||||
printf '#!/bin/sh\n[ "$1" = "-m" ] && printf "%%s\\n" "${XCAT_TEST_ARCH-x86_64}"\n' >"$bin/uname"
|
||||
cat >"$bin/ipmitool" <<'SH'
|
||||
#!/bin/sh
|
||||
@@ -135,6 +135,7 @@ SH
|
||||
export XCAT_PROC_ROOT="$proc"
|
||||
export XCAT_STATE_DIR="$state_dir"
|
||||
export XCAT_STATUS_COMMAND="$(require_repo_file 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-status')"
|
||||
export XCAT_GENESIS_FUNCTIONS="$(require_repo_file 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-functions')"
|
||||
export XCAT_STATUS_DIR="$state_dir/status"
|
||||
export XCAT_SYS_ROOT="$root"
|
||||
export XCAT_TEST_LOG="$root/commands.log"
|
||||
@@ -291,12 +292,17 @@ not_in_packet()
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
|
||||
@test "the certificate client installs only the PEM certificate from a signed x509 request" {
|
||||
printf '%s\n' 'XCATDEST=[2001:db8::10]:3001' XCATMASTER=2001:db8::10 XCATPORT=3001 \
|
||||
XCAT_INTERFACE=eth0 XCAT_SOURCE_ADDRESS=2001:db8::98 >"$state_dir/genesis.env"
|
||||
credential_response()
|
||||
{
|
||||
printf '%s\n' XCAT_NODE_NAME=node042 XCAT_DESTINY=standby >"$XCAT_METADATA_FILE"
|
||||
printf '%s\n' '<xcatresponse>' '<data><content>' '-----BEGIN CERTIFICATE-----' 'Q0VSVA==' \
|
||||
'-----END CERTIFICATE-----' '</content></data>' '</xcatresponse>' >"$XCAT_TEST_CREDENTIAL_RESPONSE"
|
||||
}
|
||||
|
||||
@test "the certificate client installs only the PEM certificate from a signed x509 request" {
|
||||
printf '%s\n' 'XCATDEST=[2001:db8::10]:3001' XCATMASTER=2001:db8::10 XCATPORT=3001 \
|
||||
XCAT_INTERFACE=eth0 XCAT_SOURCE_ADDRESS=2001:db8::98 >"$state_dir/genesis.env"
|
||||
credential_response
|
||||
run /bin/bash "$GETCERT"
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$(cat "$XCAT_KEY_DIR/cert.pem")" = '-----BEGIN CERTIFICATE-----
|
||||
@@ -311,6 +317,27 @@ Q0VSVA==
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "discovery status failures use the caller diagnostic and propagate a logging failure" {
|
||||
export XCAT_STATUS_COMMAND=/bin/false
|
||||
discover
|
||||
[ "$status" -eq 0 ]
|
||||
[ -s "$packet" ]
|
||||
grep -Fx 'logger -t xcat-genesis-discover -- unable to publish discovery status' "$XCAT_TEST_LOG"
|
||||
XCAT_TEST_LOGGER_FAIL=1 discover
|
||||
[ "$status" -eq 1 ]
|
||||
}
|
||||
|
||||
@test "credential status failures keep the singular diagnostic and propagate a logging failure" {
|
||||
export XCAT_STATUS_COMMAND=/bin/false
|
||||
credential_response
|
||||
run /bin/bash "$GETCERT"
|
||||
[ "$status" -eq 0 ]
|
||||
[ -s "$XCAT_KEY_DIR/cert.pem" ]
|
||||
grep -Fx 'logger -t xcat-genesis-getcert -- unable to publish credential status' "$XCAT_TEST_LOG"
|
||||
XCAT_TEST_LOGGER_FAIL=1 run /bin/bash "$GETCERT"
|
||||
[ "$status" -eq 1 ]
|
||||
}
|
||||
|
||||
@test "the certificate client rejects an unsafe node name with a specific code" {
|
||||
printf '%s\n' XCAT_NODE_NAME=invalid/name >"$XCAT_METADATA_FILE"
|
||||
run /bin/bash "$GETCERT"
|
||||
|
||||
@@ -38,8 +38,54 @@ version=1
|
||||
architecture=x86_64" ]
|
||||
}
|
||||
|
||||
@test "relative image export ignores an inherited CDPATH" {
|
||||
cd "$(repo_root)"
|
||||
run env CDPATH=. xCAT-genesis-base/oe/export x86_64 "$deploy" "$out_dir"
|
||||
[ "$status" -eq 0 ]
|
||||
cmp "$machine_dir/bzImage" "$out_dir/kernel"
|
||||
cmp "$machine_dir/$image.cpio.gz" "$out_dir/initramfs.cpio.gz"
|
||||
(cd "$out_dir" && sha256sum -c SHA256SUMS >/dev/null)
|
||||
}
|
||||
|
||||
@test "the export manifest has a checksum, and all exported checksums verify" {
|
||||
"$EXPORT" x86_64 "$deploy" "$out_dir"
|
||||
grep -Eq '^[0-9a-f]{64} xcat-genesis\.manifest$' "$out_dir/SHA256SUMS"
|
||||
(cd "$out_dir" && sha256sum -c SHA256SUMS >/dev/null)
|
||||
}
|
||||
|
||||
@test "image export follows deploy artifact links and preserves bytes and modes" {
|
||||
mv "$machine_dir/bzImage" "$machine_dir/bzImage-versioned"
|
||||
ln -s bzImage-versioned "$machine_dir/bzImage"
|
||||
"$EXPORT" x86_64 "$deploy" "$out_dir"
|
||||
cmp "$machine_dir/bzImage-versioned" "$out_dir/kernel"
|
||||
cmp "$machine_dir/$image.cpio.gz" "$out_dir/initramfs.cpio.gz"
|
||||
cmp "$machine_dir/$image.manifest" "$out_dir/image.manifest"
|
||||
cmp "$license_dir/license.manifest" "$out_dir/license.manifest"
|
||||
[ "$(stat -c %a "$out_dir/kernel")" = 644 ]
|
||||
[ "$(stat -c %a "$out_dir/initramfs.cpio.gz")" = 644 ]
|
||||
(cd "$out_dir" && sha256sum -c SHA256SUMS >/dev/null)
|
||||
}
|
||||
|
||||
@test "image export rejects missing, dangling and directory artifacts" {
|
||||
local input="$machine_dir/bzImage" kind
|
||||
rm "$input"
|
||||
for kind in missing dangling directory; do
|
||||
case "$kind" in
|
||||
dangling) ln -s absent "$input" ;;
|
||||
directory) rm "$input"; mkdir "$input" ;;
|
||||
esac
|
||||
run "$EXPORT" x86_64 "$deploy" "$out_dir"
|
||||
[ "$status" -eq 1 ]
|
||||
[ "$output" = "Genesis export: missing artifact: $input" ]
|
||||
[ ! -e "$out_dir" ]
|
||||
done
|
||||
}
|
||||
|
||||
@test "an unreadable image artifact leaves no partial export" {
|
||||
[ "$(id -u)" -ne 0 ] || skip 'requires an unprivileged user'
|
||||
chmod 000 "$machine_dir/bzImage"
|
||||
run "$EXPORT" x86_64 "$deploy" "$out_dir"
|
||||
[ "$status" -ne 0 ]
|
||||
[ ! -e "$out_dir" ]
|
||||
[ -z "$(find "$BATS_TEST_TMPDIR" -maxdepth 1 -name '.export.*' -print)" ]
|
||||
}
|
||||
|
||||
@@ -74,14 +74,24 @@ verify_changed()
|
||||
[ -s "$signature" ]
|
||||
}
|
||||
|
||||
@test "the built extension exports as a signed bundle with its key and checksums" {
|
||||
prepare_extension_deploy()
|
||||
{
|
||||
stem=xcat-genesis-extension-smoke-xcat-genesis-x86-64
|
||||
machine_dir="$root/deploy/images/xcat-genesis-x86-64"
|
||||
mkdir -p "$machine_dir"
|
||||
cp "$image" "$machine_dir/$stem.sysext.squashfs-zst"
|
||||
cp "$manifest" "$machine_dir/$stem.sysext.manifest.json"
|
||||
run "$EXPORTER" x86_64 xcat-genesis-extension-smoke "$root/deploy" \
|
||||
}
|
||||
|
||||
export_extension()
|
||||
{
|
||||
"$EXPORTER" x86_64 xcat-genesis-extension-smoke "$root/deploy" \
|
||||
"$private_key" "$public_key" "$root/bundle"
|
||||
}
|
||||
|
||||
@test "the built extension exports as a signed bundle with its key and checksums" {
|
||||
prepare_extension_deploy
|
||||
run export_extension
|
||||
[ "$status" -eq 0 ]
|
||||
[ -f "$root/bundle/extensions/$stem.squashfs-zst" ]
|
||||
[ -f "$root/bundle/extensions/$stem.manifest.json" ]
|
||||
@@ -90,6 +100,45 @@ verify_changed()
|
||||
(cd "$root/bundle" && sha256sum -c SHA256SUMS >/dev/null)
|
||||
}
|
||||
|
||||
@test "extension export follows deploy links and preserves payload bytes and modes" {
|
||||
prepare_extension_deploy
|
||||
local input="$machine_dir/$stem.sysext.squashfs-zst"
|
||||
mv "$input" "$input.versioned"
|
||||
ln -s "$(basename "$input").versioned" "$input"
|
||||
export_extension
|
||||
cmp "$image" "$root/bundle/extensions/$stem.squashfs-zst"
|
||||
cmp "$manifest" "$root/bundle/extensions/$stem.manifest.json"
|
||||
[ "$(stat -c %a "$root/bundle/extensions/$stem.squashfs-zst")" = 644 ]
|
||||
[ "$(stat -c %a "$root/bundle/extensions/$stem.manifest.json")" = 644 ]
|
||||
(cd "$root/bundle" && sha256sum -c SHA256SUMS >/dev/null)
|
||||
}
|
||||
|
||||
@test "extension export rejects missing, dangling and directory artifacts" {
|
||||
prepare_extension_deploy
|
||||
local input="$machine_dir/$stem.sysext.squashfs-zst" kind
|
||||
rm "$input"
|
||||
for kind in missing dangling directory; do
|
||||
case "$kind" in
|
||||
dangling) ln -s absent "$input" ;;
|
||||
directory) rm "$input"; mkdir "$input" ;;
|
||||
esac
|
||||
run export_extension
|
||||
[ "$status" -eq 1 ]
|
||||
[ "$output" = "Genesis extension export: missing artifact: $input" ]
|
||||
[ ! -e "$root/bundle" ]
|
||||
done
|
||||
}
|
||||
|
||||
@test "an unreadable extension artifact leaves no partial bundle" {
|
||||
[ "$(id -u)" -ne 0 ] || skip 'requires an unprivileged user'
|
||||
prepare_extension_deploy
|
||||
chmod 000 "$machine_dir/$stem.sysext.squashfs-zst"
|
||||
run export_extension
|
||||
[ "$status" -ne 0 ]
|
||||
[ ! -e "$root/bundle" ]
|
||||
[ -z "$(find "$root" -maxdepth 1 -name '.bundle.*' -print)" ]
|
||||
}
|
||||
|
||||
@test "a valid extension is accepted" {
|
||||
run /bin/bash "$LOADER" verify "$manifest" "$image" "$signature"
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
@@ -103,9 +103,17 @@ SH
|
||||
cat >"$bin/openssl" <<'SH'
|
||||
#!/bin/sh
|
||||
printf 'openssl %s\n' "$*" >>"$XCAT_TEST_LOG"
|
||||
cat >"$XCAT_TEST_REQUEST_FILE"
|
||||
printf '%s\n' "$$" >"$XCAT_TEST_REQUEST_PID"
|
||||
[ -z "${XCAT_TEST_OPENSSL_DELAY-}" ] || exec sleep "$XCAT_TEST_OPENSSL_DELAY"
|
||||
[ -z "${XCAT_TEST_OPENSSL_STATUS-}" ] || exit "$XCAT_TEST_OPENSSL_STATUS"
|
||||
if [ -n "${XCAT_TEST_FIRST_ERROR-}" ] && [ ! -f "$XCAT_TEST_FIRST_ERROR" ]; then
|
||||
touch "$XCAT_TEST_FIRST_ERROR"
|
||||
printf '<xcatresponse><error>busy</error></xcatresponse>\n'
|
||||
exit 1
|
||||
fi
|
||||
cat "$XCAT_TEST_RESPONSE_FILE"
|
||||
exit "${XCAT_TEST_OPENSSL_AFTER_STATUS:-0}"
|
||||
SH
|
||||
chmod 0755 "$bin"/*
|
||||
|
||||
@@ -117,6 +125,8 @@ SH
|
||||
export XCAT_SYS_CLASS_NET="$root/sys/class/net"
|
||||
export XCAT_TEST_LOG="$root/commands.log"
|
||||
export XCAT_TEST_RESPONSE_FILE="$root/destiny-response.xml"
|
||||
export XCAT_TEST_REQUEST_FILE="$root/request.xml"
|
||||
export XCAT_TEST_REQUEST_PID="$root/request.pid"
|
||||
export XCAT_TEST_ROUTE='127.0.0.1 via 192.0.2.1 dev eth0 src 192.0.2.98'
|
||||
export XCAT_UPTIME_FILE="$root/uptime"
|
||||
export XCAT_GENESIS_FUNCTIONS="$(require_repo_file "$INIT_FILES/genesis-functions")"
|
||||
@@ -141,14 +151,20 @@ response()
|
||||
'</node>' '</xcatresponse>' >"$XCAT_TEST_RESPONSE_FILE"
|
||||
}
|
||||
|
||||
# Start a command, send it SIGTERM after 0.2 s, and print its exit status. Prints "hung"
|
||||
# when it is still running 3 s later.
|
||||
# Stop the command and print its exit status, or "hung" if it does not exit.
|
||||
terminate_after()
|
||||
{
|
||||
local pid rc=0
|
||||
"$@" </dev/null >/dev/null 2>&1 &
|
||||
pid=$!
|
||||
sleep 0.2
|
||||
if [ "${XCAT_TEST_WAIT_REQUEST:-0}" -eq 1 ]; then
|
||||
for _ in $(seq 50); do
|
||||
[ -s "$XCAT_TEST_REQUEST_PID" ] && break
|
||||
sleep 0.1
|
||||
done
|
||||
else
|
||||
sleep 0.2
|
||||
fi
|
||||
kill -TERM "$pid"
|
||||
for _ in $(seq 30); do
|
||||
kill -0 "$pid" 2>/dev/null || { wait "$pid" || rc=$?; echo "$rc"; return; }
|
||||
@@ -227,6 +243,126 @@ network_state()
|
||||
[ "$(terminate_after /bin/bash "$NEXTDESTINY" 192.0.2.10:3001)" = 143 ]
|
||||
}
|
||||
|
||||
@test "destiny commands send distinct requests and honor endpoint precedence" {
|
||||
response standby
|
||||
local command name
|
||||
for command in "$GETDESTINY" "$NEXTDESTINY"; do
|
||||
name=${command##*/}
|
||||
run env XCATDEST=192.0.2.20:3001 /bin/bash "$command" 192.0.2.10:3001 --once
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$(cat "$XCAT_TEST_REQUEST_FILE")" = "<xcatrequest>
|
||||
<command>$name</command>
|
||||
<callback_port>300</callback_port>
|
||||
</xcatrequest>" ]
|
||||
grep -q -- '-connect 192.0.2.20:3001 -quiet$' "$XCAT_TEST_LOG"
|
||||
: >"$XCAT_TEST_LOG"
|
||||
run env XCATDEST=192.0.2.20:3001 /bin/bash "$command" --once -- 192.0.2.30:3001
|
||||
[ "$status" -eq 0 ]
|
||||
grep -q -- '-connect 192.0.2.30:3001 -quiet$' "$XCAT_TEST_LOG"
|
||||
: >"$XCAT_TEST_LOG"
|
||||
done
|
||||
}
|
||||
|
||||
@test "destiny commands accept a complete response despite a TLS exit error" {
|
||||
response standby
|
||||
local command
|
||||
for command in "$GETDESTINY" "$NEXTDESTINY"; do
|
||||
run env XCAT_TEST_OPENSSL_AFTER_STATUS=1 /bin/bash "$command" 192.0.2.10:3001 --once
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$output" = standby ]
|
||||
done
|
||||
}
|
||||
|
||||
@test "destiny clients use a certificate only when both credential files exist" {
|
||||
command -v bwrap >/dev/null || { echo 'bubblewrap is required for isolated credential tests' >&2; return 1; }
|
||||
response standby
|
||||
mkdir -p "$root/cert-etc/xcat"
|
||||
local command credentials expected
|
||||
for credentials in absent certificate key both; do
|
||||
rm -f "$root/cert-etc/xcat/cert.pem" "$root/cert-etc/xcat/certkey.pem"
|
||||
case "$credentials" in
|
||||
certificate|both) printf certificate >"$root/cert-etc/xcat/cert.pem" ;;
|
||||
esac
|
||||
case "$credentials" in
|
||||
key|both) printf key >"$root/cert-etc/xcat/certkey.pem" ;;
|
||||
esac
|
||||
expected='openssl s_client -connect 192.0.2.10:3001 -quiet'
|
||||
if [ "$credentials" = both ]; then
|
||||
expected='openssl s_client -key /etc/xcat/certkey.pem -cert /etc/xcat/cert.pem -connect 192.0.2.10:3001 -quiet'
|
||||
fi
|
||||
for command in "$GETDESTINY" "$NEXTDESTINY"; do
|
||||
: >"$XCAT_TEST_LOG"
|
||||
run bwrap --unshare-all --die-with-parent --ro-bind / / \
|
||||
--proc /proc --dev /dev --bind "$root" "$root" \
|
||||
--ro-bind "$root/cert-etc" /etc --setenv TMPDIR "$root" \
|
||||
/bin/bash "$command" 192.0.2.10:3001 --once
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$(cat "$XCAT_TEST_LOG")" = "$expected" ]
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
@test "destiny metadata uses private modes and supports a relative output name" {
|
||||
response osimage=test-image
|
||||
local command
|
||||
for command in "$GETDESTINY" "$NEXTDESTINY"; do
|
||||
(cd "$state_dir" && /bin/bash "$command" 192.0.2.10:3001 --once --metadata response.env)
|
||||
[ "$(cat "$state_dir/response.env")" = "$EXPECTED_METADATA" ]
|
||||
[ "$(stat -c %a "$state_dir/response.env")" = 600 ]
|
||||
[ -z "$(find "$state_dir" -name '.*destiny.*' -print)" ]
|
||||
done
|
||||
}
|
||||
|
||||
@test "destiny metadata retains the first printable element and empty fields" {
|
||||
printf '<xcatresponse>\n<name>node\001042</name>\n<name>other</name>\n<destiny>standby</destiny>\n</xcatresponse>\n' >"$XCAT_TEST_RESPONSE_FILE"
|
||||
local command
|
||||
for command in "$GETDESTINY" "$NEXTDESTINY"; do
|
||||
/bin/bash "$command" 192.0.2.10:3001 --once --metadata "$metadata"
|
||||
[ "$(cat "$metadata")" = 'XCAT_NODE_NAME=node042
|
||||
XCAT_DESTINY=standby
|
||||
XCAT_IMAGE_SERVER=
|
||||
XCAT_KERNEL=
|
||||
XCAT_INITRD=
|
||||
XCAT_KERNEL_COMMAND_LINE=' ]
|
||||
done
|
||||
}
|
||||
|
||||
@test "destiny commands reject a missing metadata directory without leaving request files" {
|
||||
response standby
|
||||
mkdir "$root/requests"
|
||||
local command
|
||||
for command in "$GETDESTINY" "$NEXTDESTINY"; do
|
||||
run env TMPDIR="$root/requests" /bin/bash "$command" 192.0.2.10:3001 --once --metadata "$root/absent/response"
|
||||
[ "$status" -eq 1 ]
|
||||
[ -z "$(find "$root/requests" -mindepth 1 -print)" ]
|
||||
done
|
||||
}
|
||||
|
||||
@test "terminating a destiny request stops its TLS child and removes temporary files" {
|
||||
export XCAT_TEST_OPENSSL_DELAY=30 XCAT_TEST_WAIT_REQUEST=1
|
||||
mkdir "$root/requests"
|
||||
local command
|
||||
for command in "$GETDESTINY" "$NEXTDESTINY"; do
|
||||
rm -f "$XCAT_TEST_REQUEST_PID"
|
||||
[ "$(TMPDIR="$root/requests" terminate_after /bin/bash "$command" 192.0.2.10:3001)" = 143 ]
|
||||
[ -s "$XCAT_TEST_REQUEST_PID" ]
|
||||
run kill -0 "$(cat "$XCAT_TEST_REQUEST_PID")"
|
||||
[ "$status" -ne 0 ]
|
||||
[ -z "$(find "$root/requests" -mindepth 1 -print)" ]
|
||||
done
|
||||
}
|
||||
|
||||
@test "legacy getdestiny retries an error before accepting the next response" {
|
||||
response standby
|
||||
printf '#!/bin/sh\nprintf "sleep %%s\\n" "$*" >>"$XCAT_TEST_LOG"\n' >"$bin/sleep"
|
||||
chmod 0755 "$bin/sleep"
|
||||
run env XCAT_TEST_FIRST_ERROR="$root/first-error" timeout 30 /bin/bash "$GETDESTINY" 192.0.2.10:3001
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$(grep -c '^openssl ' "$XCAT_TEST_LOG")" = 2 ]
|
||||
[ "$(grep -c '^sleep 1$' "$XCAT_TEST_LOG")" = 60 ]
|
||||
[[ "$output" == *standby ]]
|
||||
}
|
||||
|
||||
@test "the network state accepts DHCP boot state, probes the xCAT port, and publishes readiness" {
|
||||
printf 'xcatd=127.0.0.1:%s BOOTIF=01-52-54-00-00-00-35\n' "$port" >"$XCAT_CMDLINE_FILE"
|
||||
accepted_before="$(wc -l <"$LISTENER_LOG")"
|
||||
@@ -259,6 +395,17 @@ VERIFIED_SECONDS=123' ]
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "network status failures use the caller diagnostic and propagate a logging failure" {
|
||||
export XCAT_STATUS_COMMAND=/bin/false
|
||||
printf 'xcatd=127.0.0.1:%s BOOTIF=01-52-54-00-00-00-35\n' "$port" >"$XCAT_CMDLINE_FILE"
|
||||
network_state
|
||||
[ "$status" -eq 0 ]
|
||||
grep -Fx 'logger -t xcat-genesis-network-state -- unable to publish network status' "$XCAT_TEST_LOG"
|
||||
[ -s "$state_dir/genesis.env" ]
|
||||
XCAT_TEST_LOGGER_FAIL=1 network_state
|
||||
[ "$status" -eq 1 ]
|
||||
}
|
||||
|
||||
@test "unsafe network data is rejected, never evaluated, and does not replace the prior state" {
|
||||
printf 'xcatd=127.0.0.1:%s BOOTIF=01-52-54-00-00-00-35\n' "$port" >"$XCAT_CMDLINE_FILE"
|
||||
network_state
|
||||
@@ -340,6 +487,17 @@ with_network_state()
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "registration status failures use the caller diagnostic and propagate a logging failure" {
|
||||
export XCATDEST=192.0.2.213:3001 XCAT_STATUS_COMMAND=/bin/false
|
||||
printf 'xcatd=192.0.2.213:3001 destiny=shell\n' >"$XCAT_CMDLINE_FILE"
|
||||
run /bin/bash "$REGISTER_SCRIPT"
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$(cat "$state_dir/destiny")" = shell ]
|
||||
grep -Fx 'logger -t xcat-genesis-register -- unable to publish registration status' "$XCAT_TEST_LOG"
|
||||
XCAT_TEST_LOGGER_FAIL=1 run /bin/bash "$REGISTER_SCRIPT"
|
||||
[ "$status" -eq 1 ]
|
||||
}
|
||||
|
||||
@test "registration requests a missing destiny, and rejects an empty one without replacing state" {
|
||||
export XCATDEST=192.0.2.213:3001
|
||||
printf 'xcatd=192.0.2.213:3001\n' >"$XCAT_CMDLINE_FILE"
|
||||
|
||||
@@ -175,6 +175,10 @@ permissions and an init process that reaps orphaned children.
|
||||
evaluate machine and package policy, install local-file recipes into temporary
|
||||
directories, generate extension manifests and reject invalid extension
|
||||
metadata. It does not extract or evaluate recipe source fragments.
|
||||
The POWER patch case keeps BitBake's machine prepends and replaces only the
|
||||
upstream kernel task body with a no-op. It applies the real patch to a small
|
||||
metadata fixture, checks repeat runs and verifies that patch errors propagate.
|
||||
It does not run the complete upstream kernel configuration task.
|
||||
|
||||
For example, from the source checkout:
|
||||
|
||||
@@ -197,6 +201,17 @@ ppc64, ppc64le, armv7hf, aarch64, riscv64 and s390x.
|
||||
console with Meson, then runs the installed program. It requires a C compiler,
|
||||
Meson, Ninja, pkg-config, and development packages for Newt and systemd.
|
||||
|
||||
`python3 xCAT-test/native/genesis_console_dialogs.py` needs the same build
|
||||
dependencies plus the `pyte` Python package. It runs the linked console in a
|
||||
pseudo-terminal and checks help and diagnostics navigation, scrolling and
|
||||
return to the main screen. It checks termination from the main screen, not
|
||||
from inside a dialog.
|
||||
|
||||
The Genesis runtime BATS cases need Bash, Perl, OpenSSL, coreutils, diffutils
|
||||
and bubblewrap. The credential-file cases use a private `/etc` in a Linux
|
||||
namespace. The credential-file case fails if bubblewrap or user namespaces
|
||||
are unavailable; it does not skip.
|
||||
|
||||
The unit test retains checks of the vendored release key, requested kernel
|
||||
fragments and console service configuration. The BATS tests retain runtime,
|
||||
console, hardware and packet behavior. None of these checks replaces a full
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Exercise the linked Genesis console through a terminal, including newt dialogs."""
|
||||
|
||||
import fcntl
|
||||
import os
|
||||
from pathlib import Path
|
||||
import pty
|
||||
import select
|
||||
import signal
|
||||
import struct
|
||||
import subprocess
|
||||
import tempfile
|
||||
import termios
|
||||
import time
|
||||
import unittest
|
||||
|
||||
import pyte
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
SOURCE = ROOT / "xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console"
|
||||
|
||||
|
||||
class ConsoleDialogs(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.build_root = tempfile.TemporaryDirectory(prefix="genesis-dialog-build-")
|
||||
cls.addClassCleanup(cls.build_root.cleanup)
|
||||
build = Path(cls.build_root.name) / "build"
|
||||
subprocess.run(["meson", "setup", str(build), str(SOURCE)], check=True)
|
||||
subprocess.run(["meson", "compile", "-C", str(build)], check=True)
|
||||
cls.binary = build / "xcat-genesis-console"
|
||||
|
||||
def setUp(self):
|
||||
temporary = tempfile.TemporaryDirectory(prefix="genesis-dialog-state-")
|
||||
self.addCleanup(temporary.cleanup)
|
||||
root = Path(temporary.name)
|
||||
environment = {"PATH": os.environ["PATH"], "LC_ALL": "C.UTF-8", "TERM": "xterm"}
|
||||
for name in ("CMDLINE_FILE", "UPTIME_FILE", "OS_RELEASE", "STATUS_DIR", "STATE_FILE",
|
||||
"DESTINY_FILE", "RESPONSE_FILE", "SYS_ROOT", "PROC_ROOT", "EXTENSION_DIR", "PROVIDER_DIR"):
|
||||
environment["XCAT_" + name] = str(root / name.lower())
|
||||
Path(environment["XCAT_RESPONSE_FILE"]).write_text("XCAT_NODE_NAME=dialog-test-node\n")
|
||||
self.master, slave = pty.openpty()
|
||||
self.addCleanup(os.close, self.master)
|
||||
fcntl.ioctl(slave, termios.TIOCSWINSZ, struct.pack("HHHH", 30, 100, 0, 0))
|
||||
try:
|
||||
self.process = subprocess.Popen([str(self.binary)], stdin=slave, stdout=slave,
|
||||
stderr=slave, env=environment, start_new_session=True)
|
||||
finally:
|
||||
os.close(slave)
|
||||
self.addCleanup(self.stop)
|
||||
self.screen = pyte.Screen(100, 30)
|
||||
self.stream = pyte.ByteStream(self.screen)
|
||||
self.wait_text("Genesis status")
|
||||
|
||||
def stop(self):
|
||||
if self.process.poll() is None:
|
||||
self.process.terminate()
|
||||
os.write(self.master, b"\x1b")
|
||||
try:
|
||||
self.process.wait(timeout=5)
|
||||
except subprocess.TimeoutExpired:
|
||||
self.process.kill()
|
||||
self.process.wait(timeout=5)
|
||||
|
||||
def read_screen(self, timeout):
|
||||
if select.select([self.master], [], [], timeout)[0]:
|
||||
self.stream.feed(os.read(self.master, 65536))
|
||||
return "\n".join(self.screen.display)
|
||||
|
||||
def wait_text(self, text):
|
||||
deadline = time.monotonic() + 5
|
||||
while time.monotonic() < deadline:
|
||||
display = self.read_screen(0.1)
|
||||
if text in display:
|
||||
return display
|
||||
self.assertIsNone(self.process.poll(), display)
|
||||
self.fail(f"Terminal did not show {text!r}:\n{display}")
|
||||
|
||||
def key(self, sequence):
|
||||
os.write(self.master, sequence)
|
||||
|
||||
def test_help_and_diagnostics_scroll_close_and_reopen(self):
|
||||
dialogs = [
|
||||
(b"\x1bOP", "Genesis help", "This console reports Genesis status.",
|
||||
"Up and Down scroll this page.", "F1 Back", b"\x1bOQ"),
|
||||
(b"\x1bOQ", "Genesis diagnostics", "Node: dialog-test-node",
|
||||
"Provider names:", "F2 Back", b"\x1bOP"),
|
||||
]
|
||||
for key, title, first, last, help_line, other_key in dialogs:
|
||||
for close_key in (key, b"\x1b"):
|
||||
with self.subTest(dialog=title, close_key=close_key):
|
||||
self.key(key)
|
||||
self.wait_text(title)
|
||||
self.wait_text(first)
|
||||
self.wait_text(help_line)
|
||||
self.key(other_key)
|
||||
self.assertIn(title, self.read_screen(0.2))
|
||||
self.key(b"\x1b[6~" * 20)
|
||||
self.wait_text(last)
|
||||
self.key(close_key)
|
||||
self.wait_text("Genesis status")
|
||||
self.wait_text("F1 Help")
|
||||
self.process.send_signal(signal.SIGTERM)
|
||||
self.assertEqual(self.process.wait(timeout=5), 0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main(verbosity=2)
|
||||
@@ -4,13 +4,17 @@
|
||||
import configparser
|
||||
import hashlib
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import stat
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
import unittest
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
BITBAKE = shutil.which("bitbake")
|
||||
if not BITBAKE:
|
||||
@@ -139,9 +143,12 @@ class GenesisMetadata(unittest.TestCase):
|
||||
"DISTRO_CODENAME": "cheetah", "LINUX_VERSION_EXTENSION": "-xCAT-genesis",
|
||||
"TCLIBC": "glibc", "INIT_MANAGER": "systemd", "NO_RECOMMENDATIONS": "1",
|
||||
"QB_DEFAULT_FSTYPE": "ext4",
|
||||
"MACHINE_EXTRA_RRECOMMENDS": "",
|
||||
}.items():
|
||||
self.assertEqual(data.getVar(variable), value, variable)
|
||||
self.assertEqual(self.words(data, "IMAGE_FSTYPES"), {"cpio.gz", "ext4"})
|
||||
self.assertEqual(self.words(data, "MACHINE_ESSENTIAL_EXTRA_RDEPENDS"), set())
|
||||
self.assertEqual(self.words(data, "MACHINE_FEATURES_DEFAULTS"), {"rtc", "qemu-usermode"})
|
||||
self.assertEqual(self.words(data, "DISTRO_FEATURES"),
|
||||
{"acl", "ipv4", "ipv6", "largefile", "pci", "seccomp", "systemd", "usrmerge"})
|
||||
self.assertEqual("pci" in self.words(data, "MACHINE_FEATURES"), self.arch != "s390x")
|
||||
@@ -197,6 +204,9 @@ class GenesisMetadata(unittest.TestCase):
|
||||
}.items():
|
||||
self.assertEqual(self.dependencies(recipe), set(expected.split()), recipe)
|
||||
self.assertLessEqual({"coreutils", "util-linux-logger"}, self.dependencies("xcat-genesis-init"))
|
||||
discovery = self.dependencies("xcat-genesis-discovery") - {"xcat-genesis-common"}
|
||||
self.assertEqual(discovery, set("bash coreutils gzip iproute2 openssl-bin "
|
||||
"util-linux-logger util-linux-lsblk".split()))
|
||||
console = self.recipe("xcat-genesis-console")
|
||||
self.assertLessEqual({"libnewt", "systemd"}, self.words(console, "DEPENDS"))
|
||||
self.assertIn("sysext", self.words(self.recipe("systemd"), "PACKAGECONFIG"))
|
||||
@@ -242,11 +252,62 @@ class GenesisMetadata(unittest.TestCase):
|
||||
|
||||
def test_installed_protocol(self):
|
||||
data, root = self.install("xcat-genesis-protocol")
|
||||
work = Path(data.getVar("WORKDIR"))
|
||||
tools = work / "tools"
|
||||
tools.mkdir()
|
||||
openssl = tools / "openssl"
|
||||
openssl.write_text('#!/bin/sh\n[ "$1" = s_client ] || exit 99\n'
|
||||
'cat >"$GENESIS_TEST_REQUEST"\n'
|
||||
"printf '<xcatresponse><destiny>standby</destiny></xcatresponse>\\n'\n")
|
||||
openssl.chmod(0o755)
|
||||
for name in ("getdestiny", "nextdestiny"):
|
||||
file = self.installed(data, root, "bindir", name, 0o755)
|
||||
self.assertEqual(file.read_bytes(), (ROOT / "xCAT-genesis-scripts/usr/bin" / name).read_bytes())
|
||||
request_file = work / (name + ".xml")
|
||||
environment = {"PATH": str(tools) + os.pathsep + os.environ["PATH"],
|
||||
"GENESIS_TEST_REQUEST": str(request_file), "TMPDIR": str(work), "LC_ALL": "C"}
|
||||
result = subprocess.run([str(file), "192.0.2.10:3001", "--once"],
|
||||
env=environment, capture_output=True, text=True, timeout=30)
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertEqual(result.stdout, "standby\n")
|
||||
request = ET.parse(request_file).getroot()
|
||||
self.assertEqual(request.tag, "xcatrequest")
|
||||
self.assertEqual(request.findtext("command"), name)
|
||||
self.assertEqual(request.findtext("callback_port"), "300")
|
||||
self.assertFalse(list(root.rglob("doxcat")))
|
||||
|
||||
def test_power_metadata_patch(self):
|
||||
data = self.task_data("linux-yocto")
|
||||
# Replace the upstream task body but retain BitBake's machine prepends.
|
||||
data.setVar("do_kernel_metadata", ":\n", parsing=True)
|
||||
patch_name = "0001-qemu-ppc64-drop-removed-crc32-option.patch"
|
||||
fetcher = bb.fetch2.Fetch(["file://" + patch_name], data)
|
||||
fetcher.download()
|
||||
fetcher.unpack(data.getVar("UNPACKDIR"))
|
||||
sources = Path(data.getVar("UNPACKDIR"))
|
||||
metadata = sources / data.getVar("KMETA") / "bsp/qemu-ppc64/qemu-ppc64.cfg"
|
||||
metadata.parent.mkdir(parents=True)
|
||||
if self.arch == "x86":
|
||||
x86_metadata = sources / data.getVar("KMETA") / "bsp/common-pc/common-pc-cpu.cfg"
|
||||
x86_metadata.parent.mkdir(parents=True)
|
||||
x86_metadata.write_text("CONFIG_M686=y\n")
|
||||
before = ("CONFIG_DUMMY_CONSOLE=y\nCONFIG_FRAMEBUFFER_CONSOLE=y\n"
|
||||
"CONFIG_CRC32_SLICEBY8=y\nCONFIG_ZLIB_INFLATE=y\n"
|
||||
"CONFIG_GENERIC_ALLOCATOR=y\nCONFIG_TEXTSEARCH=y\n")
|
||||
after = before.replace("CONFIG_CRC32_SLICEBY8=y\n", "")
|
||||
power = self.arch in {"ppc64", "ppc64le"}
|
||||
for content in (before, after):
|
||||
with self.subTest(option_present=content == before):
|
||||
metadata.write_text(content)
|
||||
for unused in range(2):
|
||||
bb.build.exec_func("do_kernel_metadata", data, dirs=[data.getVar("WORKDIR")])
|
||||
self.assertEqual(metadata.read_text(), after if power else content)
|
||||
if power:
|
||||
metadata.write_text(before)
|
||||
(sources / patch_name).write_text("invalid patch\n")
|
||||
with self.assertRaises(bb.process.ExecutionError):
|
||||
bb.build.exec_func("do_kernel_metadata", data, dirs=[data.getVar("WORKDIR")])
|
||||
|
||||
def test_installed_network(self):
|
||||
data, root = self.install("xcat-genesis-network")
|
||||
file = self.installed(data, root, "sysconfdir", "NetworkManager/conf.d/10-xcat-genesis.conf", 0o644)
|
||||
@@ -273,7 +334,15 @@ class GenesisMetadata(unittest.TestCase):
|
||||
|
||||
def test_installed_init(self):
|
||||
data, root = self.install("xcat-genesis-init")
|
||||
self.installed(data, root, "libexecdir", "xcat/genesis-functions", 0o644)
|
||||
library_owner = "xcat-genesis-init"
|
||||
if "xcat-genesis-common" in self.dependencies(library_owner):
|
||||
library_owner = "xcat-genesis-common"
|
||||
self.assertIn(library_owner, self.dependencies("xcat-genesis-discovery"))
|
||||
self.assertEqual(self.dependencies(library_owner), {"bash", "util-linux-logger"})
|
||||
library_data, library_root = self.install(library_owner)
|
||||
library = self.installed(library_data, library_root, "libexecdir", "xcat/genesis-functions", 0o644)
|
||||
self.assertEqual(library.read_bytes(),
|
||||
(LAYER / "recipes-core/xcat-genesis-init/files/genesis-functions").read_bytes())
|
||||
for name in ("genesis-status", "genesis-maintenance-shell"):
|
||||
self.installed(data, root, "libexecdir", "xcat/" + name, 0o755)
|
||||
self.assertFalse(list(root.rglob("*debug-shell*")))
|
||||
@@ -347,6 +416,14 @@ class GenesisMetadata(unittest.TestCase):
|
||||
with self.assertLogs("BitBake", level="ERROR") as errors:
|
||||
with self.assertRaises(bb.BBHandledException):
|
||||
self.tinfoil.parse_recipe_file(recipe, config_data=config)
|
||||
deadline = time.monotonic() + 10
|
||||
while not any(message in error for error in errors.output):
|
||||
remaining = deadline - time.monotonic()
|
||||
if remaining <= 0:
|
||||
break
|
||||
event = self.tinfoil.wait_event(timeout=min(remaining, 1))
|
||||
if isinstance(event, logging.LogRecord):
|
||||
self.tinfoil.logger.handle(event)
|
||||
self.assertTrue(any(message in error for error in errors.output), errors.output)
|
||||
for values in ({"ARCHITECTURE": "armv7hf"},
|
||||
{"LICENSE_CLASS": "restricted"},
|
||||
|
||||
@@ -15,8 +15,6 @@ my $META = 'xCAT-genesis-base/oe/meta-xcat-genesis';
|
||||
|
||||
my $recipe = slurp_repo_file(
|
||||
"$META/recipes-xcat/xcat-genesis-discovery/xcat-genesis-discovery_1.0.bb");
|
||||
like( $recipe, qr/^RDEPENDS:\$\{PN\} = "bash coreutils gzip iproute2 openssl-bin util-linux-logger util-linux-lsblk"$/m,
|
||||
'discovery dependencies are explicit' );
|
||||
like( $recipe, qr/xcat-genesis-discovery\.socket/,
|
||||
'discovery callback socket is packaged' );
|
||||
like( $recipe, qr/xcat-genesis-credential\.socket/,
|
||||
|
||||
Reference in New Issue
Block a user