2
0
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:
Daniel Hilst
2026-10-14 11:47:15 -03:00
committed by GitHub
38 changed files with 729 additions and 241 deletions
@@ -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
+2 -7
View File
@@ -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")
+1 -7
View File
@@ -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"
@@ -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"
@@ -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) {
@@ -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'
@@ -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() {
@@ -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() {
@@ -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() {
@@ -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"
@@ -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
@@ -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
@@ -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 \
@@ -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"
}
+4 -52
View File
@@ -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"
+4 -52
View File
@@ -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"
+15
View 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
+109
View File
@@ -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)
+78 -1
View File
@@ -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/,