diff --git a/.github/workflows/genesis_openembedded.yml b/.github/workflows/genesis_openembedded.yml index 5ffed2b38..b0f6b98de 100644 --- a/.github/workflows/genesis_openembedded.yml +++ b/.github/workflows/genesis_openembedded.yml @@ -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 diff --git a/xCAT-genesis-base/oe/export b/xCAT-genesis-base/oe/export index bcc3f164f..292056480 100755 --- a/xCAT-genesis-base/oe/export +++ b/xCAT-genesis-base/oe/export @@ -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") diff --git a/xCAT-genesis-base/oe/export-extension b/xCAT-genesis-base/oe/export-extension index 0c36915b9..3b535cf00 100755 --- a/xCAT-genesis-base/oe/export-extension +++ b/xCAT-genesis-base/oe/export-extension @@ -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") diff --git a/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/include/xcat-genesis-base.inc b/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/include/xcat-genesis-base.inc new file mode 100644 index 000000000..fb5a66ddf --- /dev/null +++ b/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/include/xcat-genesis-base.inc @@ -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" diff --git a/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-aarch64.conf b/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-aarch64.conf index 24b9b0a00..fb0ed52d9 100644 --- a/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-aarch64.conf +++ b/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-aarch64.conf @@ -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 = "" diff --git a/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-armv7hf.conf b/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-armv7hf.conf index 82c372154..8d63bbd60 100644 --- a/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-armv7hf.conf +++ b/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-armv7hf.conf @@ -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 = "" diff --git a/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-ppc64.conf b/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-ppc64.conf index 33a4cb3b3..68625b60d 100644 --- a/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-ppc64.conf +++ b/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-ppc64.conf @@ -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 = "" diff --git a/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-ppc64le.conf b/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-ppc64le.conf index d48341db9..744ec0d83 100644 --- a/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-ppc64le.conf +++ b/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-ppc64le.conf @@ -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 = "" diff --git a/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-riscv64.conf b/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-riscv64.conf index bd8bac40e..5aa6acb44 100644 --- a/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-riscv64.conf +++ b/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-riscv64.conf @@ -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 = "" diff --git a/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-s390x.conf b/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-s390x.conf index 9524b1d3a..84ea9dc0b 100644 --- a/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-s390x.conf +++ b/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-s390x.conf @@ -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" diff --git a/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-x86-64.conf b/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-x86-64.conf index fa0642c3b..1968466ca 100644 --- a/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-x86-64.conf +++ b/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-x86-64.conf @@ -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" diff --git a/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-x86.conf b/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-x86.conf index 461e794af..8f207ecf7 100644 --- a/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-x86.conf +++ b/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-x86.conf @@ -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" diff --git a/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-common/xcat-genesis-common_1.0.bb b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-common/xcat-genesis-common_1.0.bb new file mode 100644 index 000000000..a6d647a0d --- /dev/null +++ b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-common/xcat-genesis-common_1.0.bb @@ -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" diff --git a/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/newt_ui.c b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/newt_ui.c index 4efd0840a..2cac85221 100644 --- a/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/newt_ui.c +++ b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/newt_ui.c @@ -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) { diff --git a/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-action b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-action index da26cde09..64b5acf85 100755 --- a/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-action +++ b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-action @@ -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' diff --git a/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-functions b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-functions index e62db52d3..2c805202c 100644 --- a/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-functions +++ b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-functions @@ -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() { diff --git a/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-network-state b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-network-state index c9b342246..706213a0c 100644 --- a/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-network-state +++ b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-network-state @@ -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() { diff --git a/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-register b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-register index ec01820d7..831f27f97 100644 --- a/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-register +++ b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-register @@ -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() { diff --git a/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/xcat-genesis-init_1.0.bb b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/xcat-genesis-init_1.0.bb index 29cdb1289..6848f9c69 100644 --- a/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/xcat-genesis-init_1.0.bb +++ b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/xcat-genesis-init_1.0.bb @@ -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 \ diff --git a/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto_%.bbappend b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto_%.bbappend index 09197365e..69c46b88b 100644 --- a/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto_%.bbappend +++ b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto_%.bbappend @@ -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" diff --git a/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-discover b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-discover index 392078604..cb0b4b3d4 100755 --- a/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-discover +++ b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-discover @@ -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 diff --git a/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-getcert b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-getcert index a8f7d8cdc..787cddef6 100755 --- a/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-getcert +++ b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-getcert @@ -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 diff --git a/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/xcat-genesis-discovery_1.0.bb b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/xcat-genesis-discovery_1.0.bb index 47d307cec..5b4e31752 100644 --- a/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/xcat-genesis-discovery_1.0.bb +++ b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/xcat-genesis-discovery_1.0.bb @@ -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 \ diff --git a/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-protocol/xcat-genesis-protocol_1.0.bb b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-protocol/xcat-genesis-protocol_1.0.bb index bb0721469..8dfeb8256 100644 --- a/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-protocol/xcat-genesis-protocol_1.0.bb +++ b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-protocol/xcat-genesis-protocol_1.0.bb @@ -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" diff --git a/xCAT-genesis-base/oe/scripts/export-functions b/xCAT-genesis-base/oe/scripts/export-functions new file mode 100644 index 000000000..80eecca59 --- /dev/null +++ b/xCAT-genesis-base/oe/scripts/export-functions @@ -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" +} diff --git a/xCAT-genesis-scripts/usr/bin/getdestiny b/xCAT-genesis-scripts/usr/bin/getdestiny index 434857fa5..06da07ddb 100755 --- a/xCAT-genesis-scripts/usr/bin/getdestiny +++ b/xCAT-genesis-scripts/usr/bin/getdestiny @@ -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>\([^<]*\).*:\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" diff --git a/xCAT-genesis-scripts/usr/bin/nextdestiny b/xCAT-genesis-scripts/usr/bin/nextdestiny index 499f78dd6..a7d66d055 100755 --- a/xCAT-genesis-scripts/usr/bin/nextdestiny +++ b/xCAT-genesis-scripts/usr/bin/nextdestiny @@ -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' \ '' >"$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>\([^<]*\).*:\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" diff --git a/xCAT-genesis-scripts/usr/lib/xcat/destiny.sh b/xCAT-genesis-scripts/usr/lib/xcat/destiny.sh new file mode 100644 index 000000000..2c745a742 --- /dev/null +++ b/xCAT-genesis-scripts/usr/lib/xcat/destiny.sh @@ -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>\([^<]*\).*:\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" +} diff --git a/xCAT-genesis-scripts/xCAT-genesis-scripts.spec b/xCAT-genesis-scripts/xCAT-genesis-scripts.spec index 277e8b69b..6ec302e6f 100644 --- a/xCAT-genesis-scripts/xCAT-genesis-scripts.spec +++ b/xCAT-genesis-scripts/xCAT-genesis-scripts.spec @@ -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 diff --git a/xCAT-test/bats/genesis_openembedded_actions.bats b/xCAT-test/bats/genesis_openembedded_actions.bats index e859360c8..e3f95e57b 100644 --- a/xCAT-test/bats/genesis_openembedded_actions.bats +++ b/xCAT-test/bats/genesis_openembedded_actions.bats @@ -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 ] +} diff --git a/xCAT-test/bats/genesis_openembedded_discovery.bats b/xCAT-test/bats/genesis_openembedded_discovery.bats index 0331a3f5b..39bd62d8d 100644 --- a/xCAT-test/bats/genesis_openembedded_discovery.bats +++ b/xCAT-test/bats/genesis_openembedded_discovery.bats @@ -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' '' '' '-----BEGIN CERTIFICATE-----' 'Q0VSVA==' \ '-----END CERTIFICATE-----' '' '' >"$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" diff --git a/xCAT-test/bats/genesis_openembedded_export.bats b/xCAT-test/bats/genesis_openembedded_export.bats index 38affab73..78c38b745 100644 --- a/xCAT-test/bats/genesis_openembedded_export.bats +++ b/xCAT-test/bats/genesis_openembedded_export.bats @@ -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)" ] +} diff --git a/xCAT-test/bats/genesis_openembedded_extensions.bats b/xCAT-test/bats/genesis_openembedded_extensions.bats index 84465faf5..2672af692 100644 --- a/xCAT-test/bats/genesis_openembedded_extensions.bats +++ b/xCAT-test/bats/genesis_openembedded_extensions.bats @@ -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 ] diff --git a/xCAT-test/bats/genesis_openembedded_runtime.bats b/xCAT-test/bats/genesis_openembedded_runtime.bats index 973d7d72e..524cebc79 100644 --- a/xCAT-test/bats/genesis_openembedded_runtime.bats +++ b/xCAT-test/bats/genesis_openembedded_runtime.bats @@ -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 'busy\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() '' '' >"$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 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")" = " +$name +300 +" ] + 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 '\nnode\001042\nother\nstandby\n\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" diff --git a/xCAT-test/native/README.md b/xCAT-test/native/README.md index b1dc9fa05..58d3240c4 100644 --- a/xCAT-test/native/README.md +++ b/xCAT-test/native/README.md @@ -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 diff --git a/xCAT-test/native/genesis_console_dialogs.py b/xCAT-test/native/genesis_console_dialogs.py new file mode 100644 index 000000000..ec4671e6e --- /dev/null +++ b/xCAT-test/native/genesis_console_dialogs.py @@ -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) diff --git a/xCAT-test/native/genesis_metadata.py b/xCAT-test/native/genesis_metadata.py index 39898c737..ca12ba174 100644 --- a/xCAT-test/native/genesis_metadata.py +++ b/xCAT-test/native/genesis_metadata.py @@ -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 'standby\\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"}, diff --git a/xCAT-test/unit/genesis_openembedded_discovery.t b/xCAT-test/unit/genesis_openembedded_discovery.t index 8966c85aa..b2c6ebbcd 100644 --- a/xCAT-test/unit/genesis_openembedded_discovery.t +++ b/xCAT-test/unit/genesis_openembedded_discovery.t @@ -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/,