From b63e695601285f40ccfb4df81abb6eecc5fe362f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 00:53:58 -0300 Subject: [PATCH 01/65] feat(genesis): add s390x OpenEmbedded target --- .gitattributes | 1 + xCAT-genesis-builder/oe/build | 2 +- xCAT-genesis-builder/oe/export | 1 + xCAT-genesis-builder/oe/export-extension | 2 +- xCAT-genesis-builder/oe/kas/s390x.yml | 6 +++ .../xcat-genesis-extension.bbclass | 2 +- .../conf/machine/include/s390x/tune-s390x.inc | 29 +++++++++++++++ .../conf/machine/xcat-genesis-s390x.conf | 37 +++++++++++++++++++ .../openssl/files/50-xcat-s390x.conf | 7 ++++ .../openssl/openssl_%.bbappend | 7 ++++ .../files/genesis-sysext | 4 +- .../recipes-core/zlib/zlib_%.bbappend | 3 ++ .../kexec/kexec-tools_%.bbappend | 1 + .../0001-s390-use-stable-generator-name.patch | 26 +++++++++++++ .../linux/linux-yocto/xcat-genesis-s390x.cfg | 28 ++++++++++++++ .../linux/linux-yocto_%.bbappend | 12 ++++++ .../libspdm/libspdm_%.bbappend | 2 + xCAT-genesis-builder/oe/report | 2 +- 18 files changed, 166 insertions(+), 6 deletions(-) create mode 100644 .gitattributes create mode 100644 xCAT-genesis-builder/oe/kas/s390x.yml create mode 100644 xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/include/s390x/tune-s390x.inc create mode 100644 xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/xcat-genesis-s390x.conf create mode 100644 xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/openssl/files/50-xcat-s390x.conf create mode 100644 xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/openssl/openssl_%.bbappend create mode 100644 xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/zlib/zlib_%.bbappend create mode 100644 xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/kexec/kexec-tools_%.bbappend create mode 100644 xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/0001-s390-use-stable-generator-name.patch create mode 100644 xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-s390x.cfg create mode 100644 xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-support/libspdm/libspdm_%.bbappend diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..082f88533 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/0001-s390-use-stable-generator-name.patch whitespace=-space-before-tab diff --git a/xCAT-genesis-builder/oe/build b/xCAT-genesis-builder/oe/build index 8874d74d3..93656d61c 100755 --- a/xCAT-genesis-builder/oe/build +++ b/xCAT-genesis-builder/oe/build @@ -17,7 +17,7 @@ fi for architecture do case "$architecture" in - aarch64|armv7hf|riscv64|x86|x86_64|ppc64|ppc64le) ;; + aarch64|armv7hf|riscv64|s390x|x86|x86_64|ppc64|ppc64le) ;; *) printf 'Unsupported Genesis architecture: %s\n' "$architecture" >&2 exit 2 diff --git a/xCAT-genesis-builder/oe/export b/xCAT-genesis-builder/oe/export index e0ce0bc39..bcc3f164f 100755 --- a/xCAT-genesis-builder/oe/export +++ b/xCAT-genesis-builder/oe/export @@ -23,6 +23,7 @@ case "$architecture" in armv7hf) kernel_name=zImage ;; aarch64) kernel_name=Image ;; riscv64) kernel_name=Image ;; + s390x) kernel_name=bzImage ;; *) fail "unsupported architecture: $architecture" ;; esac diff --git a/xCAT-genesis-builder/oe/export-extension b/xCAT-genesis-builder/oe/export-extension index 62b482ad8..0c36915b9 100755 --- a/xCAT-genesis-builder/oe/export-extension +++ b/xCAT-genesis-builder/oe/export-extension @@ -21,7 +21,7 @@ output_dir=${6%/} script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) case "$architecture" in - x86|x86_64|ppc64|ppc64le|armv7hf|aarch64|riscv64) ;; + x86|x86_64|ppc64|ppc64le|armv7hf|aarch64|riscv64|s390x) ;; *) fail "unsupported architecture: $architecture" ;; esac [[ $extension =~ ^[a-z0-9][a-z0-9._-]{0,63}$ ]] \ diff --git a/xCAT-genesis-builder/oe/kas/s390x.yml b/xCAT-genesis-builder/oe/kas/s390x.yml new file mode 100644 index 000000000..ac06515bd --- /dev/null +++ b/xCAT-genesis-builder/oe/kas/s390x.yml @@ -0,0 +1,6 @@ +header: + version: 19 + includes: + - xCAT-genesis-builder/oe/kas/common.yml + +machine: xcat-genesis-s390x diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/classes-recipe/xcat-genesis-extension.bbclass b/xCAT-genesis-builder/oe/meta-xcat-genesis/classes-recipe/xcat-genesis-extension.bbclass index 67e185286..74a0de953 100644 --- a/xCAT-genesis-builder/oe/meta-xcat-genesis/classes-recipe/xcat-genesis-extension.bbclass +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/classes-recipe/xcat-genesis-extension.bbclass @@ -34,7 +34,7 @@ python __anonymous() { architecture = d.getVar("XCAT_GENESIS_EXTENSION_ARCHITECTURE") architectures = { - "x86", "x86_64", "ppc64", "ppc64le", "armv7hf", "aarch64", "riscv64" + "x86", "x86_64", "ppc64", "ppc64le", "armv7hf", "aarch64", "riscv64", "s390x" } if architecture not in architectures: bb.fatal("Invalid Genesis extension architecture: %s" % architecture) diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/include/s390x/tune-s390x.inc b/xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/include/s390x/tune-s390x.inc new file mode 100644 index 000000000..0a4939201 --- /dev/null +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/include/s390x/tune-s390x.inc @@ -0,0 +1,29 @@ +DEFAULTTUNE ?= "s390x" + +TUNEVALID[m64] = "64-bit z/Architecture ABI" +TUNEVALID[bigendian] = "Big-endian byte order" +TUNEVALID[z10] = "IBM System z10 instruction set" + +TUNE_ARCH = "s390x" +TUNE_PKGARCH = "${TUNE_PKGARCH:tune-${DEFAULTTUNE}}" +TUNE_CCARGS .= "${@bb.utils.contains('TUNE_FEATURES', 'm64', ' -m64', '', d)}" +TUNE_CCARGS .= "${@bb.utils.contains('TUNE_FEATURES', 'z10', ' -march=z10 -mtune=z10', '', d)}" + +AVAILTUNES += "s390x" +TUNE_FEATURES:tune-s390x = "m64 bigendian z10" +BASE_LIB:tune-s390x = "lib64" +BASELIB:libc-glibc:s390x = "lib64" +TUNE_PKGARCH:tune-s390x = "s390x" +PACKAGE_EXTRA_ARCHS:tune-s390x = "s390x" + +def xcat_genesis_s390x_siteinfo(archinfo, osinfo, targetinfo, d): + archinfo["s390x"] = "endian-big bit-64" + return archinfo, osinfo, targetinfo + +SITEINFO_EXTRA_DATAFUNCS += "xcat_genesis_s390x_siteinfo" + +def xcat_genesis_s390x_packageqa_machdata(machdata, d): + machdata["linux"]["s390x"] = (22, 0, 0, False, 64) + return machdata + +PACKAGEQA_EXTRA_MACHDEFFUNCS += "xcat_genesis_s390x_packageqa_machdata" diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/xcat-genesis-s390x.conf b/xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/xcat-genesis-s390x.conf new file mode 100644 index 000000000..4e8b7b9ed --- /dev/null +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/xcat-genesis-s390x.conf @@ -0,0 +1,37 @@ +#@TYPE: Machine +#@NAME: xCAT Genesis s390x +#@DESCRIPTION: QEMU s390x Genesis target + +DEFAULTTUNE = "s390x" +XCAT_GENESIS_ARCHITECTURE = "s390x" +require conf/machine/include/s390x/tune-s390x.inc +require conf/machine/include/qemu.inc + +ARCH:s390x = "s390" +QEMU_TARGETS:append = " s390x" + +MACHINEOVERRIDES =. "qemus390x:" + +KERNEL_IMAGETYPE = "bzImage" +SERIAL_CONSOLES = "115200;ttysclp0" +XCAT_GENESIS_CONSOLE_TTY = "ttysclp0" + +MACHINE_FEATURES_DEFAULTS = "" +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" +# TCG lacks optional z10 facilities, so disable them while retaining the z10 instruction floor. +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" +QB_RNG = "-object rng-random,filename=/dev/urandom,id=rng0 -device virtio-rng-ccw,rng=rng0" +QB_OPT_APPEND = "" diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/openssl/files/50-xcat-s390x.conf b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/openssl/files/50-xcat-s390x.conf new file mode 100644 index 000000000..75aed4869 --- /dev/null +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/openssl/files/50-xcat-s390x.conf @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: EPL-1.0 + +my %targets = ( + "linux-s390x" => { + inherit_from => [ "linux64-s390x" ], + }, +); diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/openssl/openssl_%.bbappend b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/openssl/openssl_%.bbappend new file mode 100644 index 000000000..94de9a5f0 --- /dev/null +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/openssl/openssl_%.bbappend @@ -0,0 +1,7 @@ +FILESEXTRAPATHS:prepend := "${THISDIR}/files:" + +SRC_URI:append:s390x = " file://50-xcat-s390x.conf" + +do_configure:prepend:s390x() { + install -m 0644 ${UNPACKDIR}/50-xcat-s390x.conf ${S}/Configurations/ +} diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-extensions/files/genesis-sysext b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-extensions/files/genesis-sysext index 0e89c817e..7c530123f 100644 --- a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-extensions/files/genesis-sysext +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-extensions/files/genesis-sysext @@ -46,7 +46,7 @@ canonical_architecture() { case "$machine" in i?86) printf '%s\n' x86 ;; armv7*) printf '%s\n' armv7hf ;; - x86_64|ppc64|ppc64le|aarch64|riscv64) printf '%s\n' "$machine" ;; + x86_64|ppc64|ppc64le|aarch64|riscv64|s390x) printf '%s\n' "$machine" ;; *) fail "unsupported runtime architecture: $machine" ;; esac } @@ -75,7 +75,7 @@ validate_manifest() { .schema == 1 and (.name | type == "string" and test("^[a-z0-9][a-z0-9._-]{0,63}$")) and (.version | type == "string" and test("^[A-Za-z0-9][A-Za-z0-9._+-]{0,63}$")) and - (.architecture | IN("x86", "x86_64", "ppc64", "ppc64le", "armv7hf", "aarch64", "riscv64")) and + (.architecture | IN("x86", "x86_64", "ppc64", "ppc64le", "armv7hf", "aarch64", "riscv64", "s390x")) and (.genesis_release | type == "string" and test("^[A-Za-z0-9._-]+$")) and (.key_id | type == "string" and test("^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$")) and (.license_class | IN("open", "redistributable", "restricted")) and diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/zlib/zlib_%.bbappend b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/zlib/zlib_%.bbappend new file mode 100644 index 000000000..b97f6034a --- /dev/null +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/zlib/zlib_%.bbappend @@ -0,0 +1,3 @@ +do_configure:s390x() { + LDCONFIG=true ${S}/configure --prefix=${prefix} --shared --libdir=${libdir} --uname=GNU --disable-crcvx +} diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/kexec/kexec-tools_%.bbappend b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/kexec/kexec-tools_%.bbappend new file mode 100644 index 000000000..ad990b4f6 --- /dev/null +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/kexec/kexec-tools_%.bbappend @@ -0,0 +1 @@ +COMPATIBLE_HOST:s390x = "s390x.*-linux" diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/0001-s390-use-stable-generator-name.patch b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/0001-s390-use-stable-generator-name.patch new file mode 100644 index 000000000..4637452bf --- /dev/null +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/0001-s390-use-stable-generator-name.patch @@ -0,0 +1,26 @@ +From: xCAT Genesis Builder +Date: Thu, 3 Sep 2026 22:46:00 -0300 +Subject: [PATCH] s390: use stable generator name + +__FILE__ contains the build directory when the generator is compiled with an +absolute source path. Do not copy that path into the exported source package. + +Upstream-Status: Pending +Signed-off-by: xCAT Genesis Builder +--- + arch/s390/tools/gen_opcode_table.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/s390/tools/gen_opcode_table.c b/arch/s390/tools/gen_opcode_table.c +index 0fbb061..8cd17d0 100644 +--- a/arch/s390/tools/gen_opcode_table.c ++++ b/arch/s390/tools/gen_opcode_table.c +@@ -341,7 +341,7 @@ int main(int argc, char **argv) + printf("/*\n"); + printf(" * DO NOT MODIFY.\n"); + printf(" *\n"); +- printf(" * This file was generated by %s\n", __FILE__); ++ printf(" * This file was generated by arch/s390/tools/gen_opcode_table.c\n"); + printf(" */\n\n"); + print_formats(desc); + print_long_insn(desc); diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-s390x.cfg b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-s390x.cfg new file mode 100644 index 000000000..e64fc5963 --- /dev/null +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-s390x.cfg @@ -0,0 +1,28 @@ +CONFIG_MARCH_Z10=y +CONFIG_TUNE_Z10=y +CONFIG_NET=y +CONFIG_INET=y +CONFIG_IPV6=y +CONFIG_NETDEVICES=y +CONFIG_VIRTIO=y +CONFIG_VIRTIO_BLK=y +CONFIG_VIRTIO_NET=y +CONFIG_HW_RANDOM_VIRTIO=y +CONFIG_CCWGROUP=y +CONFIG_QDIO=y +CONFIG_QETH=y +CONFIG_QETH_L2=y +CONFIG_QETH_L3=y +CONFIG_DASD=y +CONFIG_DASD_ECKD=y +CONFIG_DASD_FBA=y +CONFIG_CHSC_SCH=y +CONFIG_SCSI=y +CONFIG_SCSI_FC_ATTRS=y +CONFIG_ZFCP=y +CONFIG_SCLP_TTY=y +CONFIG_SCLP_CONSOLE=y +CONFIG_SCLP_VT220_TTY=y +CONFIG_SCLP_VT220_CONSOLE=y +CONFIG_TN3270=y +CONFIG_TN3270_CONSOLE=y diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto_%.bbappend b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto_%.bbappend index ffc02101d..09197365e 100644 --- a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto_%.bbappend +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto_%.bbappend @@ -84,3 +84,15 @@ do_kernel_metadata:prepend:xcat-genesis-ppc64() { fi } do_kernel_metadata[depends] += "patch-native:do_populate_sysroot" + +COMPATIBLE_MACHINE:xcat-genesis-s390x = "^xcat-genesis-s390x$" +KMACHINE:xcat-genesis-s390x = "xcat-genesis-s390x" +KBRANCH:xcat-genesis-s390x = "v6.18/standard/base" +SRCREV_machine:xcat-genesis-s390x = "b1ba5428513b52c2bd6acfd3ad0a910f699bc395" +KBUILD_DEFCONFIG:xcat-genesis-s390x = "defconfig" +KCONFIG_MODE:xcat-genesis-s390x = "--alldefconfig" +KERNEL_FEATURES:remove:xcat-genesis-s390x = "features/drm-bochs/drm-bochs.scc" +SRC_URI:append:xcat-genesis-s390x = " \ + file://0001-s390-use-stable-generator-name.patch \ + file://xcat-genesis-s390x.cfg \ +" diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-support/libspdm/libspdm_%.bbappend b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-support/libspdm/libspdm_%.bbappend new file mode 100644 index 000000000..9f72194d1 --- /dev/null +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-support/libspdm/libspdm_%.bbappend @@ -0,0 +1,2 @@ +# libspdm has no s390x architecture mapping. +COMPATIBLE_HOST:s390x = "null" diff --git a/xCAT-genesis-builder/oe/report b/xCAT-genesis-builder/oe/report index e596bcced..08ad6726f 100755 --- a/xCAT-genesis-builder/oe/report +++ b/xCAT-genesis-builder/oe/report @@ -59,7 +59,7 @@ done architecture=$1 image_dir=${2%/} case "$architecture" in - aarch64|armv7hf|riscv64|x86|x86_64|ppc64|ppc64le) ;; + aarch64|armv7hf|riscv64|s390x|x86|x86_64|ppc64|ppc64le) ;; *) fail "unsupported architecture: $architecture" ;; esac From e08804d0e8ef42460b7a8d4abb40e422ba8d78d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 12:44:37 -0300 Subject: [PATCH 02/65] test(genesis): cover s390x OpenEmbedded target --- .github/workflows/xcat_test.yml | 13 ++- xCAT-test/unit/genesis_openembedded.t | 102 +++++++++++++++++- .../unit/genesis_openembedded_extensions.t | 16 +++ 3 files changed, 126 insertions(+), 5 deletions(-) diff --git a/.github/workflows/xcat_test.yml b/.github/workflows/xcat_test.yml index 92c12244d..6510fee54 100644 --- a/.github/workflows/xcat_test.yml +++ b/.github/workflows/xcat_test.yml @@ -38,16 +38,21 @@ jobs: KAS_WORK_DIR: ${{ runner.temp }}/kas-work run: | set -euo pipefail - for architecture in x86 x86_64 ppc64 ppc64le armv7hf aarch64 riscv64; do + for architecture in x86 x86_64 ppc64 ppc64le armv7hf aarch64 riscv64 s390x; do export KAS_BUILD_DIR="${RUNNER_TEMP}/kas-build-${architecture}" configuration="xCAT-genesis-builder/oe/kas/${architecture}.yml" kas dump "${configuration}" >/dev/null kas shell "${configuration}" -c 'bitbake -p' done - name: Validate the image task graph + shell: bash env: KAS_WORK_DIR: ${{ runner.temp }}/kas-work KAS_BUILD_DIR: ${{ runner.temp }}/kas-build-x86_64 - run: >- - kas shell xCAT-genesis-builder/oe/kas/x86_64.yml -c - 'bitbake -n xcat-genesis-image xcat-genesis-extension-smoke' + run: | + set -euo pipefail + kas shell xCAT-genesis-builder/oe/kas/x86_64.yml -c \ + 'bitbake -n xcat-genesis-image xcat-genesis-extension-smoke' + export KAS_BUILD_DIR="${RUNNER_TEMP}/kas-build-s390x" + kas shell xCAT-genesis-builder/oe/kas/s390x.yml -c \ + 'bitbake -n xcat-genesis-image' diff --git a/xCAT-test/unit/genesis_openembedded.t b/xCAT-test/unit/genesis_openembedded.t index 368f5c7ab..c18bc76a4 100644 --- a/xCAT-test/unit/genesis_openembedded.t +++ b/xCAT-test/unit/genesis_openembedded.t @@ -84,6 +84,12 @@ like( $riscv64_kas, qr{includes:\s*\n\s*- xCAT-genesis-builder/oe/kas/common\.ym like( $riscv64_kas, qr/^machine: xcat-genesis-riscv64$/m, 'riscv64 build selects its machine' ); +my $s390x_kas = read_file('xCAT-genesis-builder/oe/kas/s390x.yml'); +like( $s390x_kas, qr{includes:\s*\n\s*- xCAT-genesis-builder/oe/kas/common\.yml}, + 's390x build includes the common configuration' ); +like( $s390x_kas, qr/^machine: xcat-genesis-s390x$/m, + 's390x build selects its machine' ); + my $ppc64le_kas = read_file('xCAT-genesis-builder/oe/kas/ppc64le.yml'); like( $ppc64le_kas, qr{includes:\s*\n\s*- xCAT-genesis-builder/oe/kas/common\.yml}, 'ppc64le build includes the common configuration' ); @@ -97,7 +103,8 @@ like( $ppc64_kas, qr/^machine: xcat-genesis-ppc64$/m, 'ppc64 build selects its machine' ); my $build = read_file('xCAT-genesis-builder/oe/build'); -like( $build, qr/aarch64\|armv7hf\|riscv64\|x86\|x86_64\|ppc64\|ppc64le/, +like( $build, + qr/aarch64\|armv7hf\|riscv64\|s390x\|x86\|x86_64\|ppc64\|ppc64le/, 'build accepts each supported architecture' ); like( $build, qr{kas/\$architecture\.yml}, 'build selects the architecture configuration directly' ); @@ -255,6 +262,41 @@ like( $riscv64_machine, qr/^QB_CPU = "-cpu rv64"$/m, like( $riscv64_machine, qr/^XCAT_GENESIS_CONSOLE_TTY = "ttyS0"$/m, 'riscv64 status console uses its 8250 terminal' ); +my $s390x_machine = read_file( + 'xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/xcat-genesis-s390x.conf' +); +like( $s390x_machine, qr/^DEFAULTTUNE = "s390x"$/m, + 's390x uses the z10 baseline tune' ); +like( $s390x_machine, qr/^ARCH:s390x = "s390"$/m, + 's390x maps to the Linux s390 source architecture' ); +like( $s390x_machine, qr/^XCAT_GENESIS_ARCHITECTURE = "s390x"$/m, + 's390x exports its canonical extension identity' ); +like( $s390x_machine, qr/^QEMU_TARGETS:append = " s390x"$/m, + 's390x cross builds have a user-mode emulator' ); +like( $s390x_machine, qr/^QB_MACHINE = "-machine s390-ccw-virtio"$/m, + 's390x QEMU target uses the CCW virtual machine' ); +like( $s390x_machine, qr/^QB_CPU = "-cpu z10EC-base,/m, + 's390x QEMU target enforces its oldest CPU generation' ); +like( $s390x_machine, qr/^QB_MEM = "-m 4096"$/m, + 's390x QEMU target has room for the initramfs' ); +like( $s390x_machine, qr/^XCAT_GENESIS_CONSOLE_TTY = "ttysclp0"$/m, + 's390x status console uses the SCLP terminal' ); +like( $s390x_machine, qr/^QB_KERNEL_CMDLINE_APPEND = "console=ttysclp0"$/m, + 's390x QEMU target selects the same SCLP terminal' ); + +my $s390x_tune = read_file( + 'xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/include/s390x/tune-s390x.inc' +); +like( $s390x_tune, qr/^TUNE_ARCH = "s390x"$/m, + 's390x tune selects the 64-bit architecture' ); +like( $s390x_tune, + qr/^TUNE_FEATURES:tune-s390x = "m64 bigendian z10"$/m, + 's390x tune uses the z10 CPU floor' ); +like( $s390x_tune, qr/^BASE_LIB:tune-s390x = "lib64"$/m, + 's390x tune uses the 64-bit library directory' ); +like( $s390x_tune, qr/^BASELIB:libc-glibc:s390x = "lib64"$/m, + 's390x glibc uses its 64-bit library directory' ); + for my $machine_console ( [ x86_64 => $machine ], [ x86 => $x86_machine ], @@ -263,6 +305,7 @@ for my $machine_console ( [ armv7hf => $armv7hf_machine ], [ aarch64 => $aarch64_machine ], [ riscv64 => $riscv64_machine ], + [ s390x => $s390x_machine ], ) { unlike( $machine_console->[1], @@ -429,6 +472,19 @@ like( $kernel_append, qr/file:\/\/xcat-genesis-riscv64\.cfg/, like( $kernel_append, qr/^SRCREV_machine:xcat-genesis-riscv64 = "b1ba5428513b52c2bd6acfd3ad0a910f699bc395"$/m, 'riscv64 kernel revision is pinned' ); +like( $kernel_append, qr/file:\/\/xcat-genesis-s390x\.cfg/, + 's390x kernel uses the Genesis hardware fragment' ); +like( $kernel_append, qr/file:\/\/0001-s390-use-stable-generator-name\.patch/, + 's390x kernel source export omits the build directory' ); +like( $kernel_append, + qr/^SRCREV_machine:xcat-genesis-s390x = "b1ba5428513b52c2bd6acfd3ad0a910f699bc395"$/m, + 's390x kernel revision is pinned' ); +like( $kernel_append, + qr/^KCONFIG_MODE:xcat-genesis-s390x = "--alldefconfig"$/m, + 's390x kernel retains architecture defaults' ); +like( $kernel_append, + qr/^KERNEL_FEATURES:remove:xcat-genesis-s390x = "features\/drm-bochs\/drm-bochs\.scc"$/m, + 's390x kernel excludes the QEMU PC graphics fragment' ); like( $kernel_append, qr/file:\/\/xcat-genesis-powerpc64\.cfg/, '64-bit Power kernels share the Genesis hardware fragment' ); like( $kernel_append, @@ -469,6 +525,50 @@ for my $symbol ( "x86_64 kernel enables $symbol" ); } +my $s390x_kernel_config = read_file( + 'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-s390x.cfg' +); +for my $symbol ( + qw( + CONFIG_MARCH_Z10 CONFIG_TUNE_Z10 CONFIG_IPV6 CONFIG_VIRTIO + CONFIG_VIRTIO_BLK CONFIG_VIRTIO_NET CONFIG_HW_RANDOM_VIRTIO + CONFIG_CCWGROUP CONFIG_QDIO CONFIG_QETH CONFIG_QETH_L2 CONFIG_QETH_L3 + CONFIG_DASD CONFIG_DASD_ECKD CONFIG_DASD_FBA CONFIG_ZFCP + CONFIG_SCLP_CONSOLE CONFIG_SCLP_VT220_CONSOLE CONFIG_TN3270_CONSOLE + ) + ) +{ + like( $s390x_kernel_config, qr/^\Q$symbol\E=[ym]$/m, + "s390x kernel enables $symbol" ); +} + +my $kexec_append = read_file( + 'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/kexec/kexec-tools_%.bbappend' +); +like( $kexec_append, qr/^COMPATIBLE_HOST:s390x = "s390x\.\*-linux"$/m, + 'kexec-tools accepts the s390x target' ); + +my $zlib_append = read_file( + 'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/zlib/zlib_%.bbappend' +); +like( $zlib_append, qr/^do_configure:s390x\(\).*--disable-crcvx/ms, + 'zlib keeps the s390x image compatible with z10' ); + +my $openssl_append = read_file( + 'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/openssl/openssl_%.bbappend' +); +like( $openssl_append, + qr/file:\/\/50-xcat-s390x\.conf/, + 'OpenSSL receives the OpenEmbedded s390x target alias' ); +like( $openssl_append, + qr/install -m 0644 \$\{UNPACKDIR\}\/50-xcat-s390x\.conf \$\{S\}\/Configurations\//, + 'OpenSSL installs the target definition before configuration' ); +my $openssl_config = read_file( + 'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/openssl/files/50-xcat-s390x.conf' +); +like( $openssl_config, qr/^ "linux-s390x" => \{$/m, + 'OpenSSL maps the s390x target to its 64-bit configuration' ); + my $x86_kernel_config = read_file( 'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-x86.cfg' ); diff --git a/xCAT-test/unit/genesis_openembedded_extensions.t b/xCAT-test/unit/genesis_openembedded_extensions.t index bf01281c5..86352f070 100644 --- a/xCAT-test/unit/genesis_openembedded_extensions.t +++ b/xCAT-test/unit/genesis_openembedded_extensions.t @@ -192,6 +192,22 @@ is( $verify_status, 0, 'valid extension is accepted' ) or diag($verify_output); or diag($arm_output); } +{ + local $ENV{XCAT_GENESIS_UNAME_M} = 's390x'; + my $s390x_manifest = File::Spec->catfile( $root, 'xcat-smoke-s390x.json' ); + my $s390x_signature = File::Spec->catfile( $root, 'xcat-smoke-s390x.sig' ); + write_manifest( $s390x_manifest, $hash, { architecture => 's390x' } ); + my ( $s390x_sign_status, $s390x_sign_output ) = + run_command( $signer, $s390x_manifest, $private_key, $s390x_signature ); + is( $s390x_sign_status, 0, 's390x extension manifest is signed' ) + or diag($s390x_sign_output); + my ( $s390x_status, $s390x_output ) = run_command( + '/bin/bash', $loader, 'verify', $s390x_manifest, $image, $s390x_signature + ); + is( $s390x_status, 0, 's390x runtime uses its canonical extension identity' ) + or diag($s390x_output); +} + my ( $install_status, $install_output ) = run_command( '/bin/bash', $loader, 'install', $manifest, $image, $signature ); is( $install_status, 0, 'valid extension is installed' ) or diag($install_output); From cb09b5033de1b1efa3d8728d8ed0cf88e5f7a078 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 00:54:04 -0300 Subject: [PATCH 03/65] feat(genesis): report IBM Z identity --- .../files/xcat-genesis-console/src/console.h | 1 + .../files/xcat-genesis-console/src/state.c | 35 ++++++++++++- .../files/xcat-genesis-console/src/support.c | 36 +++++++++++++ .../files/genesis-discover | 51 +++++++++++++++++-- 4 files changed, 117 insertions(+), 6 deletions(-) diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/console.h b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/console.h index 80987474d..8a9624448 100644 --- a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/console.h +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/console.h @@ -107,6 +107,7 @@ void xcat_set_text(char *destination, size_t size, const char *format, ...); char *xcat_read_allocated_line(const char *path); bool xcat_read_line(const char *path, char *value, size_t size); bool xcat_read_key(const char *path, const char *key, char *value, size_t size); +bool xcat_read_colon_key(const char *path, const char *key, char *value, size_t size); bool xcat_safe_name(const char *value); bool xcat_cmdline_value(const char *cmdline, const char *key, char *value, size_t size); unsigned long long xcat_read_uptime(void); diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/state.c b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/state.c index 231495ebc..d9a8cdcd3 100644 --- a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/state.c +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/state.c @@ -201,6 +201,8 @@ static void set_boot_loader(const char *cmdline, char *loader, size_t size) { xcat_set_text(loader, size, "PXELINUX"); else if (strcmp(value, "elilo") == 0) xcat_set_text(loader, size, "ELILO"); + else if (strcmp(value, "s390-ccw") == 0) + xcat_set_text(loader, size, "QEMU TFTP loader"); else xcat_set_text(loader, size, "unrecognized"); } else if (xcat_cmdline_value(cmdline, "BOOTIF", value, sizeof(value))) { @@ -216,6 +218,15 @@ static bool useful_identity(const char *value) { strcmp(value, "unknown") != 0; } +static void set_s390_identity(const char *proc_root, struct console_state *state) { + char path[VALUE_SIZE * 2]; + + snprintf(path, sizeof(path), "%s/sysinfo", proc_root); + if (!useful_identity(state->uuid) && + !xcat_read_colon_key(path, "VM00 UUID", state->uuid, sizeof(state->uuid))) + xcat_read_colon_key(path, "LPAR UUID", state->uuid, sizeof(state->uuid)); +} + static void split_action(const char *destiny, char *action, size_t action_size, char *target, size_t target_size) { const char *separator = strchr(destiny, '='); @@ -291,8 +302,12 @@ void xcat_load_console_state(struct console_state *state) { char path[VALUE_SIZE * 2]; char release_name[64] = "xCAT Genesis"; char release_version[32] = ""; +#ifdef XCAT_CONSOLE_TEST + const char *test_architecture = getenv("XCAT_TEST_ARCH"); +#endif bool xcat_configured; bool interface_selected; + bool s390_ccw_boot; memset(state, 0, sizeof(*state)); xcat_configured = xcat_cmdline_value(cmdline_text, "xcatd", state->xcat_endpoint, @@ -332,6 +347,16 @@ void xcat_load_console_state(struct console_state *state) { xcat_set_text(state->architecture, sizeof(state->architecture), "unknown"); xcat_set_text(state->kernel, sizeof(state->kernel), "unknown"); } +#ifdef XCAT_CONSOLE_TEST + if (test_architecture != NULL) + xcat_set_text(state->architecture, sizeof(state->architecture), "%s", + test_architecture); +#endif + + s390_ccw_boot = strcmp(state->architecture, "s390x") == 0 && + xcat_cmdline_value(cmdline_text, "xcat.bootloader", value, + sizeof(value)) && + strcmp(value, "s390-ccw") == 0; snprintf(path, sizeof(path), "%s/firmware/efi", sys_root); if (strncmp(state->architecture, "ppc64", 5) == 0) { @@ -345,6 +370,9 @@ void xcat_load_console_state(struct console_state *state) { access(path, F_OK) == 0 ? "UEFI" : "Device Tree"); } else if (strncmp(state->architecture, "riscv", 5) == 0) { xcat_set_text(state->firmware, sizeof(state->firmware), "OpenSBI"); + } else if (strcmp(state->architecture, "s390x") == 0) { + xcat_set_text(state->firmware, sizeof(state->firmware), "%s", + s390_ccw_boot ? "s390-ccw BIOS" : "not reported"); } else { snprintf(path, sizeof(path), "%s/firmware/efi", sys_root); xcat_set_text(state->firmware, sizeof(state->firmware), "%s", @@ -365,7 +393,12 @@ void xcat_load_console_state(struct console_state *state) { if (!useful_identity(state->serial)) xcat_set_text(state->serial, sizeof(state->serial), "not reported"); snprintf(path, sizeof(path), "%s/class/dmi/id/product_uuid", sys_root); - if (!xcat_read_line(path, state->uuid, sizeof(state->uuid)) || !useful_identity(state->uuid)) + xcat_read_line(path, state->uuid, sizeof(state->uuid)); + if (strcmp(state->architecture, "s390x") == 0) { + xcat_set_text(state->serial, sizeof(state->serial), "not reported"); + set_s390_identity(proc_root, state); + } + if (!useful_identity(state->uuid)) xcat_set_text(state->uuid, sizeof(state->uuid), "not reported"); interface_selected = diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/support.c b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/support.c index 5890b7cbd..5a710b435 100644 --- a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/support.c +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/support.c @@ -123,6 +123,42 @@ bool xcat_read_key(const char *path, const char *key, char *value, size_t size) return found; } +bool xcat_read_colon_key(const char *path, const char *key, char *value, size_t size) { + FILE *stream; + char *line = NULL; + size_t capacity = 0; + size_t key_length = strlen(key); + bool found = false; + + if (key_length == 0 || size == 0) + return false; + stream = fopen(path, "r"); + if (stream == NULL) + return false; + while (getline(&line, &capacity, stream) >= 0) { + char *start; + char *end; + size_t line_length = strlen(line); + + if (line_length <= key_length || strncmp(line, key, key_length) != 0 || + line[key_length] != ':') + continue; + start = line + key_length + 1; + while (isspace((unsigned char)*start)) + start++; + end = start + strlen(start); + while (end > start && isspace((unsigned char)end[-1])) + end--; + *end = '\0'; + xcat_copy_printable(value, size, start); + found = true; + break; + } + free(line); + fclose(stream); + return found; +} + bool xcat_safe_name(const char *value) { const unsigned char *cursor = (const unsigned char *)value; diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-discover b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-discover index 9140cd99a..8346b4c06 100755 --- a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-discover +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-discover @@ -73,6 +73,14 @@ read_value() { tr -d '\000\r\n' <"$value_file" | head -c 512 } +read_sysinfo_value() { + local name=$1 + + awk -F ':' -v name="$name" \ + '$1 == name {sub(/^[[:space:]]+/, "", $2); sub(/[[:space:]]+$/, "", $2); print $2; exit}' \ + "$proc_root/sysinfo" 2>/dev/null || true +} + send_packet() { if [[ -n ${XCAT_DISCOVERY_SEND_COMMAND:-} ]]; then "$XCAT_DISCOVERY_SEND_COMMAND" "$compressed_packet" "$XCATMASTER" "$XCATPORT" @@ -125,12 +133,29 @@ system_vendor=$(read_value "$sys_root/sys/devices/virtual/dmi/id/sys_vendor" || serial=$(read_value "$sys_root/sys/devices/virtual/dmi/id/product_serial" || true) uuid=$(read_value "$sys_root/sys/devices/virtual/dmi/id/product_uuid" || true) machine_type= +s390_virtual= [[ -z $system_vendor && -z $product_name ]] \ || machine_type=${system_vendor:+$system_vendor:}$product_name platform=$(awk -F ':' \ '/^platform[[:space:]]*:/ {sub(/^[[:space:]]+/, "", $2); print $2; exit}' \ "$proc_root/cpuinfo" 2>/dev/null || true) -if [[ -z $product_name ]]; then +if [[ $architecture == s390x ]]; then + system_vendor=$(read_sysinfo_value Manufacturer) + product_type=$(read_sysinfo_value Type) + product_model=$(read_sysinfo_value Model) + product_model=${product_model##* } + product_name=${product_type}${product_model:+-$product_model} + machine_type=$product_name + serial= + uuid=$(read_sysinfo_value 'VM00 UUID') + [[ -n $uuid ]] || uuid=$(read_sysinfo_value 'LPAR UUID') + platform=$(read_sysinfo_value 'VM00 Control Program') + if [[ -n $platform ]]; then + s390_virtual=1 + else + platform=LPAR + fi +elif [[ -z $product_name ]]; then product_name=$(read_value "$proc_root/device-tree/model" || true) [[ -n $product_name ]] \ || product_name=$(read_value \ @@ -148,12 +173,28 @@ node_type= case "$system_vendor $product_name" in *KVM*|*QEMU*|*VMware*|*VirtualBox*|*Bochs*|*Hyper-V*) node_type=virtual ;; esac +[[ -z $s390_virtual ]] || node_type=virtual -cpu_count=$(awk '/^(processor|cpu)[[:space:]]*:/ {count++} END {print count + 0}' \ +cpu_count=$(awk ' + /^processor([[:space:]]+[0-9]+)?[[:space:]]*:/ {processors++} + /^cpu[[:space:]]*:/ {cpus++} + END {print (processors > 0 ? processors : cpus + 0)}' \ "$proc_root/cpuinfo" 2>/dev/null || printf '0') -cpu_type=$(awk -F ':' \ - '/^(model name|cpu)[[:space:]]*:/ {sub(/^[[:space:]]+/, "", $2); print $2; exit}' \ - "$proc_root/cpuinfo" 2>/dev/null || true) +cpu_type=$(awk -F ':' ' + /^model name[[:space:]]*:/ && model == "" { + sub(/^[[:space:]]+/, "", $2); model = $2 + } + /^cpu[[:space:]]*:/ && cpu == "" { + sub(/^[[:space:]]+/, "", $2); cpu = $2 + } + /^vendor_id[[:space:]]*:/ && vendor == "" { + sub(/^[[:space:]]+/, "", $2); vendor = $2 + } + END { + if (model != "") print model + else if (cpu != "") print cpu + else if (vendor != "") print vendor + }' "$proc_root/cpuinfo" 2>/dev/null || true) memory=$(awk '/^MemTotal:/ {printf "%.0fMB", $2 / 1024; exit}' \ "$proc_root/meminfo" 2>/dev/null || true) disk_size=$(lsblk -b -dn -o NAME,SIZE,TYPE 2>/dev/null \ From d140b941ad1ee117a4d838c5fbe2ed7a1cb63792 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 12:44:44 -0300 Subject: [PATCH 04/65] test(genesis): cover s390x identity --- xCAT-test/unit/genesis_openembedded_console.t | 88 +++++++++++++++++++ .../unit/genesis_openembedded_discovery.t | 43 ++++++++- 2 files changed, 130 insertions(+), 1 deletion(-) diff --git a/xCAT-test/unit/genesis_openembedded_console.t b/xCAT-test/unit/genesis_openembedded_console.t index 62dab4330..332358eb8 100644 --- a/xCAT-test/unit/genesis_openembedded_console.t +++ b/xCAT-test/unit/genesis_openembedded_console.t @@ -19,11 +19,14 @@ my $root = tempdir( CLEANUP => 1 ); my $binary = File::Spec->catfile( $root, 'xcat-genesis-console' ); my $header_test_source = File::Spec->catfile( $root, 'header-test.c' ); my $header_test_binary = File::Spec->catfile( $root, 'header-test' ); +my $identity_test_source = File::Spec->catfile( $root, 'identity-test.c' ); +my $identity_test_binary = File::Spec->catfile( $root, 'identity-test' ); my $compiler = $ENV{CC} || 'cc'; is( system( $compiler, '-D_POSIX_C_SOURCE=200809L', '-DXCAT_CONSOLE_PLAIN_ONLY', + '-DXCAT_CONSOLE_TEST', '-std=c17', '-Wall', '-Wextra', '-Wpedantic', '-Werror', @plain_sources, '-o', $binary ) >> 8, @@ -75,6 +78,36 @@ is( is( system($header_test_binary) >> 8, 0, 'narrow terminals leave no writable header context' ); +write_file( + $identity_test_source, + <<'C' +#include "console.h" + +#include + +int main(void) { + struct console_state state; + + xcat_load_console_state(&state); + printf("uuid=%s\nfirmware=%s\nboot=%s\n", state.uuid, state.firmware, + state.boot_method); + return 0; +} +C +); +is( + system( + $compiler, '-D_POSIX_C_SOURCE=200809L', '-DXCAT_CONSOLE_TEST', + '-std=c17', '-Wall', '-Wextra', '-Wpedantic', '-Werror', + '-I', $source_dir, $identity_test_source, + File::Spec->catfile( $source_dir, 'state.c' ), + File::Spec->catfile( $source_dir, 'support.c' ), + '-o', $identity_test_binary + ) >> 8, + 0, + 'identity probe builds with strict warnings' +); + my $cmdline = File::Spec->catfile( $root, 'cmdline' ); my $uptime = File::Spec->catfile( $root, 'uptime' ); my $os_release_real = File::Spec->catfile( $root, 'os-release.real' ); @@ -170,6 +203,15 @@ sub run_console { return ( $? >> 8, $output ); } +sub run_identity_probe { + local %ENV = ( %ENV, %environment ); + open( my $stream, '-|', $identity_test_binary ) + or die "Unable to run $identity_test_binary: $!"; + my $output = do { local $/; <$stream> }; + close($stream); + return ( $? >> 8, $output ); +} + my ( $status, $output ) = run_console(); is( $status, 0, 'plain console renders a status snapshot' ); like( $output, @@ -193,6 +235,52 @@ unlike( $output, qr/extensions:|providers:|Linux /, 'inventory details stay off the main page' ); unlike( $output, qr/\e/, 'plain output has no terminal escapes' ); +unlink( + File::Spec->catfile( $dmi_root, 'product_serial' ), + File::Spec->catfile( $dmi_root, 'product_uuid' ), +); +write_file( File::Spec->catfile( $proc_root, 'sysinfo' ), <<'SYSINFO' ); +Plant: 02 +Sequence Code: 0000000012345 +LPAR UUID: 93724168-fda3-429b-8b28-a5d245dcb3ff +VM00 UUID: 82038f2a-1344-aaf7-1a85-2a7250be2076 +SYSINFO +$environment{XCAT_TEST_ARCH} = 's390x'; +write_file( $cmdline, "xcatd=192.0.2.10:3001\n" ); +( $status, $output ) = run_console(); +is( $status, 0, 'plain console accepts IBM Z identity' ); +like( $output, qr/^serial: not reported$/m, + 'console does not present the shared IBM Z machine serial as guest identity' ); +my ( $probe_status, $probe_output ) = run_identity_probe(); +is( $probe_status, 0, 'console loads IBM Z diagnostics' ); +is( + $probe_output, + "uuid=82038f2a-1344-aaf7-1a85-2a7250be2076\n" + . "firmware=not reported\nboot=not reported\n", + 'console does not infer the boot path from the architecture', +); +write_file( $cmdline, + "xcatd=192.0.2.10:3001 xcat.bootloader=s390-ccw\n" ); +( $probe_status, $probe_output ) = run_identity_probe(); +is( + $probe_output, + "uuid=82038f2a-1344-aaf7-1a85-2a7250be2076\n" + . "firmware=s390-ccw BIOS\nboot=QEMU TFTP loader\n", + 'console reports the marked QEMU network boot path', +); +write_file( File::Spec->catfile( $proc_root, 'sysinfo' ), <<'SYSINFO' ); +LPAR UUID: 93724168-fda3-429b-8b28-a5d245dcb3ff +SYSINFO +( $probe_status, $probe_output ) = run_identity_probe(); +like( $probe_output, + qr/^uuid=93724168-fda3-429b-8b28-a5d245dcb3ff$/m, + 'console uses the LPAR UUID when no guest UUID is available' ); +delete $environment{XCAT_TEST_ARCH}; +write_file( File::Spec->catfile( $dmi_root, 'product_serial' ), + "TEST-SERIAL-001\n" ); +write_file( File::Spec->catfile( $dmi_root, 'product_uuid' ), + "11111111-2222-3333-4444-555555555555\n" ); + my $cmdline_without_xcat = $ipv4_network_state; $cmdline_without_xcat =~ s/^XCATDEST=.*\n//m; write_file( $genesis_env, $cmdline_without_xcat ); diff --git a/xCAT-test/unit/genesis_openembedded_discovery.t b/xCAT-test/unit/genesis_openembedded_discovery.t index eebb5c700..f45e1e826 100644 --- a/xCAT-test/unit/genesis_openembedded_discovery.t +++ b/xCAT-test/unit/genesis_openembedded_discovery.t @@ -121,6 +121,7 @@ write_file( File::Spec->catfile( $dmi_dir, 'product_uuid' ), "00112233-4455-6677-8899-aabbccddeeff\n" ); write_file( File::Spec->catfile( $proc_root, 'cpuinfo' ), <<'CPU' ); processor : 0 +vendor_id : Test Vendor model name : Test CPU & Controller processor : 1 CPU @@ -400,7 +401,7 @@ write_file( File::Spec->catfile( $device_tree, 'model' ), write_file( File::Spec->catfile( $device_tree, 'system-id' ), "IBM,02AB123\0" ); write_file( File::Spec->catfile( $proc_root, 'cpuinfo' ), - "cpu : POWER9\ncpu : POWER9\nplatform : PowerNV\n" ); + "processor : 0\ncpu : POWER9\nprocessor : 1\ncpu : POWER9\nplatform : PowerNV\n" ); $environment{XCAT_TEST_ARCH} = 'ppc64le'; $environment{XCAT_TEST_RESPONSE} = 'restart'; is( run_script( $discover_script, \%environment ), 0, @@ -420,6 +421,46 @@ like( $packet, qr{9009-42a-02ab123-525400000002}, 'Power discovery creates a stable fallback UUID' ); +unlink( + File::Spec->catfile( $device_tree, 'model' ), + File::Spec->catfile( $device_tree, 'system-id' ), +); +write_file( File::Spec->catfile( $proc_root, 'sysinfo' ), <<'SYSINFO' ); +Manufacturer: IBM +Type: 3931 +Model: 701 A01 +Sequence Code: 0000000012345 +Plant: 02 +LPAR Name: LP4KVM09 +LPAR UUID: 93724168-fda3-429b-8b28-a5d245dcb3ff +VM00 Name: GENESIS1 +VM00 Control Program: KVM/Linux +VM00 UUID: 82038f2a-1344-aaf7-1a85-2a7250be2076 +SYSINFO +write_file( File::Spec->catfile( $proc_root, 'cpuinfo' ), + "vendor_id : IBM/S390\nprocessor 0: version = 00\nprocessor 1: version = 00\n" ); +$environment{XCAT_TEST_ARCH} = 's390x'; +is( run_script( $discover_script, \%environment ), 0, + 's390x discovery accepts proc sysinfo identity' ); +$packet = read_file($packet_file); +like( $packet, qr{s390x}, + 's390x discovery keeps the canonical architecture' ); +like( $packet, qr{virtual}, + 's390x discovery identifies a hypervisor guest' ); +like( $packet, qr{3931-A01}, + 's390x discovery reports the machine type and model' ); +unlike( $packet, qr{}, + 's390x discovery does not treat the shared machine serial as guest identity' ); +like( $packet, qr{KVM/Linux}, + 's390x discovery reports the closest hypervisor' ); +like( $packet, qr{2}, + 's390x discovery counts processor records' ); +like( $packet, qr{IBM/S390}, + 's390x discovery reports the CPU vendor' ); +like( $packet, + qr{82038f2a-1344-aaf7-1a85-2a7250be2076}, + 's390x discovery uses the closest guest UUID' ); + unlink($response_file); is( run_script( $callback_script, \%environment, "restart (eth0)" ), 0, 'callback accepts a restart response' ); From a4109f6865bc2ac845d206d1951ec3932fed51b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 00:54:08 -0300 Subject: [PATCH 05/65] feat(genesis): add s390x network boot --- perl-xCAT/xCAT/DHCP/BootPolicy.pm | 43 ++++++++++++++ xCAT-server/lib/xcat/plugins/dhcp.pm | 12 ++++ xCAT-server/lib/xcat/plugins/mknb.pm | 85 ++++++++++++++++++++++++++-- xCAT-server/sbin/xcatconfig | 2 +- xCAT-server/share/xcat/tools/go-xcat | 4 +- xCAT/debian/control | 2 +- xCAT/xCAT.spec | 1 + xCATsn/debian/control | 2 +- xCATsn/xCATsn.spec | 1 + 9 files changed, 143 insertions(+), 9 deletions(-) diff --git a/perl-xCAT/xCAT/DHCP/BootPolicy.pm b/perl-xCAT/xCAT/DHCP/BootPolicy.pm index 3c6c976ef..8b11fd815 100644 --- a/perl-xCAT/xCAT/DHCP/BootPolicy.pm +++ b/perl-xCAT/xCAT/DHCP/BootPolicy.pm @@ -103,6 +103,46 @@ sub kea_httpboot_network_classes { return \@classes; } +sub kea_s390x_network_classes { + my ( $class, %opts ) = @_; + + return [] unless $opts{net} && defined( $opts{prefix} ); + + my $network_id = "$opts{net}_$opts{prefix}"; + my $safe_network = $network_id; + $safe_network =~ s/[^A-Za-z0-9_.-]/_/g; + + return [ + { + name => "xcat-s390x-qemu-$safe_network", + test => 'option[93].hex == 0x001f', + additional_only => 1, + 'option-data' => [ + { + name => 'conf-file', + data => $network_id, + 'always-send' => 1, + }, + { + name => 'path-prefix', + data => 'pxelinux.cfg/s390x/', + 'always-send' => 1, + }, + ], + }, + ]; +} + +sub ensure_isc_path_prefix_definition { + my ($class, $config) = @_; + + return 0 if grep { /^\s*option\s+path-prefix\s+code\s+210\b/ } @{$config}; + + my $index = @{$config} && $config->[0] =~ /^#xCAT/ ? 1 : 0; + splice @{$config}, $index, 0, "option path-prefix code 210 = text;\n"; + return 1; +} + sub isc_client_architecture_lines { my ( $class, %opts ) = @_; @@ -136,6 +176,9 @@ sub isc_client_architecture_lines { " } else if option client-architecture = 00:1c { #riscv64 uefi http boot\n ", " option vendor-class-identifier \"HTTPClient\";\n", " filename \"http://$tftp$portsuffix/tftpboot/boot/grub2/grub2.riscv64\";\n", + " } else if option client-architecture = 00:1f { #QEMU s390x\n ", + " option path-prefix = \"pxelinux.cfg/s390x/\";\n", + " option conf-file = \"${net}_${maskbits}\";\n", " } else if option client-architecture = 00:0e { #OPAL-v3\n ", " option conf-file = \"http://$tftp$portsuffix/tftpboot/pxelinux.cfg/p/${net}_${maskbits}\";\n", " } else if substring (option vendor-class-identifier,0,11) = \"onie_vendor\" { #for onie on cumulus switch\n", diff --git a/xCAT-server/lib/xcat/plugins/dhcp.pm b/xCAT-server/lib/xcat/plugins/dhcp.pm index 3b3807ffd..d7e6d67c7 100644 --- a/xCAT-server/lib/xcat/plugins/dhcp.pm +++ b/xCAT-server/lib/xcat/plugins/dhcp.pm @@ -2354,6 +2354,10 @@ sub process_request $restartdhcp = 1; newconfig(); } + if ($^O ne 'aix' && + xCAT::DHCP::BootPolicy->ensure_isc_path_prefix_definition(\@dhcpconf)) { + $restartdhcp = 1; + } if ($usingipv6 and not $dhcp6conf[0]) { $restartdhcp6 = 1; newconfig6(); @@ -3362,6 +3366,12 @@ sub kea_subnet4_intent loader_present => sub { -e $_[0] }, ) }; + push @client_classes, @{ + xCAT::DHCP::BootPolicy->kea_s390x_network_classes( + net => $net, + prefix => $prefix, + ) + }; if (@client_classes) { $subnet{additional_client_classes} = [ map { $_->{name} } @client_classes ]; $subnet{client_classes} = \@client_classes; @@ -3885,6 +3895,7 @@ sub kea_option_defs { return [ { name => 'conf-file', code => 209, type => 'string', space => 'dhcp4' }, + { name => 'path-prefix', code => 210, type => 'string', space => 'dhcp4' }, { name => 'iscsi-initiator-iqn', code => 203, type => 'string', space => 'dhcp4' }, { name => 'cumulus-provision-url', code => 239, type => 'string', space => 'dhcp4' }, ]; @@ -4792,6 +4803,7 @@ sub newconfig push @dhcpconf, "#xCAT generated dhcp configuration\n"; push @dhcpconf, "\n"; push @dhcpconf, "option conf-file code 209 = text;\n"; + push @dhcpconf, "option path-prefix code 210 = text;\n"; push @dhcpconf, "option space isan;\n"; push @dhcpconf, "option isan-encap-opts code 43 = encapsulate isan;\n"; push @dhcpconf, "option isan.iqn code 203 = string;\n"; diff --git a/xCAT-server/lib/xcat/plugins/mknb.pm b/xCAT-server/lib/xcat/plugins/mknb.pm index 891310e2d..f08f8b7e5 100644 --- a/xCAT-server/lib/xcat/plugins/mknb.pm +++ b/xCAT-server/lib/xcat/plugins/mknb.pm @@ -10,7 +10,7 @@ use File::Copy; my $GENESIS_EXPORT_MANIFEST = 'xcat-genesis.manifest'; my %GENESIS_ARCHITECTURES = map { $_ => 1 } - qw(x86 x86_64 ppc64 ppc64le armv7hf aarch64 riscv64); + qw(x86 x86_64 ppc64 ppc64le armv7hf aarch64 riscv64 s390x); sub _canonical_genesis_arch { my ($arch) = @_; @@ -300,6 +300,11 @@ sub _remove_openembedded_genesis { "$directory/genesis.fs.$arch.lzma", "$directory/genesis.exact-arch.$arch", ); + if ($arch eq 's390x') { + foreach my $path (glob("$tftpdir/pxelinux.cfg/s390x/*")) { + push(@artifacts, $path) if _is_generated_s390x_config($path); + } + } my $removed = 0; my @failed; foreach my $artifact (@artifacts) { @@ -320,6 +325,15 @@ sub _remove_openembedded_genesis { return ($removed, undef); } +sub _is_generated_s390x_config { + my ($path) = @_; + return 0 if -l $path || !-f $path; + open(my $config, '<', $path) or return 0; + my $header = <$config>; + close($config); + return defined($header) && $header eq "# pxelinux.cfg xCAT Genesis s390x\n"; +} + sub genesis_lzma_command { my ($have_lzma, $have_xz) = @_; return 'lzma -C crc32 -9' if $have_lzma; @@ -417,7 +431,7 @@ sub process_request { my $tftpdir = xCAT::TableUtils->getTftpDir(); my $requested_arch = $request->{arg}->[0]; if (!$requested_arch) { - $callback->({ error => "Need to specify architecture (x86, x86_64, ppc64, ppc64le, armv7hf, aarch64 or riscv64)" }, { errorcode => [1] }); + $callback->({ error => "Need to specify architecture (x86, x86_64, ppc64, ppc64le, armv7hf, aarch64, riscv64 or s390x)" }, { errorcode => [1] }); return; } @@ -680,7 +694,9 @@ sub process_request { $normnet_addresses, \@master_addresses ); my $consolecmdline; - if (defined($serialport) and $serialspeed) { + if ($arch eq 's390x') { + $consolecmdline = 'console=ttysclp0'; + } elsif (defined($serialport) and $serialspeed) { if ($arch =~ /ppc/) { $consolecmdline = "console=tty0 console=hvc$serialport,$serialspeed"; } else { @@ -714,6 +730,9 @@ sub process_request { } elsif (exists $GRUB2_DISCOVERY_ARCHES{$arch}) { mkpath("$tftpdir/boot/grub2"); chmod(0755, "$tftpdir/boot/grub2"); + } elsif ($arch eq 's390x') { + mkpath("$tftpdir/pxelinux.cfg/s390x"); + chmod(0755, "$tftpdir/pxelinux.cfg/s390x"); } my $dopxe = 0; foreach (keys %{$normnets}) { @@ -721,9 +740,13 @@ sub process_request { my $nicip = $normnets->{$net}; my $xcatd_address = defined($xcatdnormnets->{$net}) ? $xcatdnormnets->{$net} : $nicip; $net =~ s/\//_/; - if (defined($nobootnicips{$nicip})) { + if (defined($nobootnicips{$nicip}) + || ($arch eq 's390x' && defined($nobootnicips{$xcatd_address}))) { if ($arch =~ /ppc/ and -r "$tftpdir/pxelinux.cfg/p/$net") { unlink("$tftpdir/pxelinux.cfg/p/$net"); + } elsif ($arch eq 's390x') { + my $path = "$tftpdir/pxelinux.cfg/s390x/$net"; + unlink($path) if _is_generated_s390x_config($path); } next; } @@ -785,6 +808,19 @@ sub process_request { print $cfgfile " initrd http://" . $xcatd_address . "$portsuffix/$initrd_file\n"; print $cfgfile ' append "xcatd=' . $xcatd_address . ":$xcatdport $consolecmdline\"\n"; close($cfgfile); + } elsif ($arch eq 's390x') { + my (undef, $config_error) = _write_s390x_discovery_config( + tftpdir => $tftpdir, + network => $net, + xcatd_address => $xcatd_address, + xcatdport => $xcatdport, + consolecmdline => $consolecmdline, + initrd => $initrd_file, + ); + if ($config_error) { + $callback->({ error => [$config_error], errorcode => [1] }); + return; + } } } $dopxe = 0; @@ -854,6 +890,47 @@ sub process_request { } } +sub _write_s390x_discovery_config { + my (%args) = @_; + my $tftpdir = $args{tftpdir}; + my $initrd = $args{initrd}; + $initrd =~ s{^\Q$tftpdir\E/?}{}; + my $cmdline = "xcatd=$args{xcatd_address}:$args{xcatdport} xcat.bootloader=s390-ccw"; + if (defined($args{consolecmdline}) and $args{consolecmdline} ne '') { + $cmdline .= " $args{consolecmdline}"; + } + + my $qemu_path = "$tftpdir/pxelinux.cfg/s390x/$args{network}"; + my $qemu_config = "# pxelinux.cfg xCAT Genesis s390x\n" + . "DEFAULT xCAT\n" + . "LABEL xCAT\n" + . " KERNEL xcat/genesis.kernel.s390x\n" + . " INITRD $initrd\n" + . " APPEND $cmdline\n"; + my $error = _write_s390x_config($qemu_path, $qemu_config); + return (undef, $error) if $error; + + return ($qemu_path, undef); +} + +sub _write_s390x_config { + my ($path, $contents) = @_; + open(my $config, '>', $path) + or return "Unable to write s390x Genesis configuration: $path: $!"; + unless (print {$config} $contents) { + my $error = $!; + close($config); + unlink($path); + return "Unable to write s390x Genesis configuration: $path: $error"; + } + unless (close($config)) { + my $error = $!; + unlink($path); + return "Unable to write s390x Genesis configuration: $path: $error"; + } + return; +} + # Return the grub2-class architectures whose Genesis kernel and initrd are # published under $tftpdir/xcat, as [arch, grub_cpu, kernel, initrd] with the # file names relative to the TFTP root. diff --git a/xCAT-server/sbin/xcatconfig b/xCAT-server/sbin/xcatconfig index f4c1394ab..612f4d20b 100755 --- a/xCAT-server/sbin/xcatconfig +++ b/xCAT-server/sbin/xcatconfig @@ -1998,7 +1998,7 @@ sub setupLinuxexports sub _installed_genesis_architectures { my ($xcatroot) = @_; - my @supported = qw(aarch64 armv7hf ppc64 ppc64le riscv64 x86 x86_64); + my @supported = qw(aarch64 armv7hf ppc64 ppc64le riscv64 s390x x86 x86_64); my %supported = map { $_ => 1 } @supported; my %installed; diff --git a/xCAT-server/share/xcat/tools/go-xcat b/xCAT-server/share/xcat/tools/go-xcat index 09aee446d..bf3a0f267 100755 --- a/xCAT-server/share/xcat/tools/go-xcat +++ b/xCAT-server/share/xcat/tools/go-xcat @@ -208,7 +208,7 @@ GO_XCAT_UNINSTALL_LIST=("${GO_XCAT_INSTALL_LIST[@]}" xCAT-genesis-openembedded-x86 xCAT-genesis-openembedded-x86_64 xCAT-genesis-openembedded-ppc64 xCAT-genesis-openembedded-ppc64le xCAT-genesis-openembedded-armv7hf xCAT-genesis-openembedded-aarch64 - xCAT-genesis-openembedded-riscv64 + xCAT-genesis-openembedded-riscv64 xCAT-genesis-openembedded-s390x xCAT-openbmc-py xCAT-probe xCAT-test xCAT-vlan xCATsn xCAT-UI-deps xCAT-buildkit conserver-xcat yaboot-xcat) # For Debian/Ubuntu, it will need a slightly different package list @@ -218,7 +218,7 @@ GO_XCAT_UNINSTALL_LIST=("${GO_XCAT_INSTALL_LIST[@]}" xcat-genesis-openembedded-x86 xcat-genesis-openembedded-x86-64 xcat-genesis-openembedded-ppc64 xcat-genesis-openembedded-ppc64le xcat-genesis-openembedded-armv7hf xcat-genesis-openembedded-aarch64 - xcat-genesis-openembedded-riscv64 + xcat-genesis-openembedded-riscv64 xcat-genesis-openembedded-s390x xcat-buildkit conserver-xcat) PATH="/usr/sbin:/usr/bin:/sbin:/bin" diff --git a/xCAT/debian/control b/xCAT/debian/control index 74ff70c64..57a82ffdf 100644 --- a/xCAT/debian/control +++ b/xCAT/debian/control @@ -10,7 +10,7 @@ Homepage: https://xcat.org/ Package: xcat Architecture: amd64 ppc64el Depends: ${perl:Depends}, goconserver(>= 0.3.3-snap000000000000), xcat-server (>= 2.13-snap000000000000), xcat-client (>= 2.13-snap000000000000), libdbd-sqlite3-perl, isc-dhcp-server | kea, bind9, apache2, nfs-kernel-server, libxml-parser-perl, rsync, tftpd-hpa, libnet-telnet-perl, chrony | ntp, xcat-genesis-scripts-amd64 (>= 2.13-snap000000000000) -Recommends: net-tools, nmap, kea, tftp-hpa, ipmitool-xcat (>= 1.8.17-1), syslinux[any-amd64], libsys-virt-perl, syslinux-xcat, xnba-undi, elilo-xcat, util-linux-extra, xcat-buildkit (>= 2.13-snap000000000000), xcat-probe (>= 2.13-snap000000000000), xcat-genesis-openembedded-x86-64, xcat-genesis-openembedded-ppc64le, xcat-genesis-openembedded-riscv64 +Recommends: net-tools, nmap, kea, tftp-hpa, ipmitool-xcat (>= 1.8.17-1), syslinux[any-amd64], libsys-virt-perl, syslinux-xcat, xnba-undi, elilo-xcat, util-linux-extra, xcat-buildkit (>= 2.13-snap000000000000), xcat-probe (>= 2.13-snap000000000000), xcat-genesis-openembedded-x86-64, xcat-genesis-openembedded-ppc64le, xcat-genesis-openembedded-riscv64, xcat-genesis-openembedded-s390x Suggests: yaboot-xcat Description: Metapackage for a common, default xCAT setup xCAT is Extreme Cluster/Cloud Administration Toolkit. xCAT offers complete diff --git a/xCAT/xCAT.spec b/xCAT/xCAT.spec index 8772828b9..d48accaac 100644 --- a/xCAT/xCAT.spec +++ b/xCAT/xCAT.spec @@ -63,6 +63,7 @@ Requires: xCAT-probe = 4:%{version}-%{release} Recommends: xCAT-genesis-openembedded-x86_64 Recommends: xCAT-genesis-openembedded-ppc64le Recommends: xCAT-genesis-openembedded-riscv64 +Recommends: xCAT-genesis-openembedded-s390x %endif %endif diff --git a/xCATsn/debian/control b/xCATsn/debian/control index 281c92066..063bf4716 100644 --- a/xCATsn/debian/control +++ b/xCATsn/debian/control @@ -9,7 +9,7 @@ Homepage: https://xcat.org/ Package: xcatsn Architecture: amd64 ppc64el Depends: ${perl:Depends}, goconserver (>=0.3.3-snap000000000000), xcat-server (>= 2.13-snap000000000000), xcat-client (>= 2.13-snap000000000000), libdbd-sqlite3-perl, libxml-parser-perl, tftpd-hpa, libnet-telnet-perl, isc-dhcp-server | kea, bind9, apache2, nfs-kernel-server, xcat-genesis-scripts-amd64 (>= 2.13-snap000000000000) -Recommends: net-tools, nmap, kea, tftp-hpa, ipmitool-xcat (>= 1.8.17-1), syslinux[any-amd64], libsys-virt-perl, syslinux-xcat, xnba-undi, elilo-xcat, xcat-buildkit (>= 2.13-snap000000000000), xcat-probe (>= 2.13-snap000000000000), xcat-genesis-openembedded-x86-64, xcat-genesis-openembedded-ppc64le, xcat-genesis-openembedded-riscv64 +Recommends: net-tools, nmap, kea, tftp-hpa, ipmitool-xcat (>= 1.8.17-1), syslinux[any-amd64], libsys-virt-perl, syslinux-xcat, xnba-undi, elilo-xcat, xcat-buildkit (>= 2.13-snap000000000000), xcat-probe (>= 2.13-snap000000000000), xcat-genesis-openembedded-x86-64, xcat-genesis-openembedded-ppc64le, xcat-genesis-openembedded-riscv64, xcat-genesis-openembedded-s390x Suggests: yaboot-xcat Description: Metapackage for a common, default xCAT service node setup xCATsn is a service node management package intended for at-scale diff --git a/xCATsn/xCATsn.spec b/xCATsn/xCATsn.spec index 1f8d9f47d..718c3ecb3 100644 --- a/xCATsn/xCATsn.spec +++ b/xCATsn/xCATsn.spec @@ -26,6 +26,7 @@ Requires: xCAT-probe = 4:%{version}-%{release} Recommends: xCAT-genesis-openembedded-x86_64 Recommends: xCAT-genesis-openembedded-ppc64le Recommends: xCAT-genesis-openembedded-riscv64 +Recommends: xCAT-genesis-openembedded-s390x %endif # Match xCAT-genesis-scripts package naming by build architecture. From 36ca4a369f1b78d5ad50ba13d2d537784258f691 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 12:45:07 -0300 Subject: [PATCH 06/65] test(genesis): cover s390x network boot --- .../integration/dhcp_isc_config_validation.t | 52 ++++++++++++ .../integration/dhcp_kea_config_validation.t | 15 +++- xCAT-test/unit/dhcp_boot_policy.t | 59 ++++++++++++++ xCAT-test/unit/dhcp_isc_client_arch.t | 9 ++- xCAT-test/unit/dhcp_kea_plugin_intent.t | 32 ++++++++ .../unit/genesis_openembedded_activation.t | 13 ++- xCAT-test/unit/mknb_exported_genesis.t | 23 ++++++ xCAT-test/unit/mknb_xcatd_address.t | 79 +++++++++++++++++++ .../unit/xcatconfig_genesis_openembedded.t | 11 +-- 9 files changed, 285 insertions(+), 8 deletions(-) create mode 100644 xCAT-test/integration/dhcp_isc_config_validation.t diff --git a/xCAT-test/integration/dhcp_isc_config_validation.t b/xCAT-test/integration/dhcp_isc_config_validation.t new file mode 100644 index 000000000..35dc3bf0e --- /dev/null +++ b/xCAT-test/integration/dhcp_isc_config_validation.t @@ -0,0 +1,52 @@ +#!/usr/bin/env perl +use strict; +use warnings; + +use FindBin; +use lib "$FindBin::Bin/../../perl-xCAT"; + +use File::Spec; +use File::Temp qw(tempdir); +use Test::More; +use xCAT::DHCP::BootPolicy; + +my ($dhcpd) = grep { -x $_ } qw(/usr/sbin/dhcpd /usr/local/sbin/dhcpd); +plan skip_all => 'dhcpd is required for ISC configuration validation' unless $dhcpd; +plan skip_all => 'root is required to validate from the ISC configuration directory' + unless $> == 0; + +my @config = ( + "#xCAT generated dhcp configuration\n", + "\n", + "option conf-file code 209 = text;\n", + "option user-class-identifier code 77 = string;\n", + "option client-architecture code 93 = unsigned integer 16;\n", + "option www-server code 114 = string;\n", + "default-lease-time 600;\n", + "max-lease-time 600;\n", + "subnet 192.0.2.0 netmask 255.255.255.0 {\n", + " range 192.0.2.100 192.0.2.110;\n", +); + +ok( + xCAT::DHCP::BootPolicy->ensure_isc_path_prefix_definition(\@config), + 'the upgrade path adds option 210 to an existing configuration', +); +push @config, @{ xCAT::DHCP::BootPolicy->isc_client_architecture_lines( + next_server => '192.0.2.1', + portsuffix => '', + net => '192.0.2.0', + prefix => 24, + ) }, "}\n"; + +my $configuration_root = -d '/etc/dhcp' ? '/etc/dhcp' : '/etc'; +my $directory = tempdir(DIR => $configuration_root, CLEANUP => 1); +my $path = File::Spec->catfile($directory, 'dhcpd.conf'); +open(my $config_file, '>', $path) or die "Cannot create $path: $!"; +print {$config_file} @config; +close($config_file) or die "Cannot close $path: $!"; + +my $status = system($dhcpd, '-t', '-cf', $path); +is($status, 0, 'ISC accepts the upgraded s390x boot policy'); + +done_testing(); diff --git a/xCAT-test/integration/dhcp_kea_config_validation.t b/xCAT-test/integration/dhcp_kea_config_validation.t index 5afecaedc..125189705 100644 --- a/xCAT-test/integration/dhcp_kea_config_validation.t +++ b/xCAT-test/integration/dhcp_kea_config_validation.t @@ -24,6 +24,7 @@ my $json = $backend->render_dhcp4_config( interfaces => ['*'], 'option-def' => [ { name => 'conf-file', code => 209, type => 'string', space => 'dhcp4' }, + { name => 'path-prefix', code => 210, type => 'string', space => 'dhcp4' }, { name => 'iscsi-initiator-iqn', code => 203, type => 'string', space => 'dhcp4' }, { name => 'cumulus-provision-url', code => 239, type => 'string', space => 'dhcp4' }, ], @@ -41,6 +42,15 @@ my $json = $backend->render_dhcp4_config( { name => 'conf-file', data => 'http://192.168.122.1:80/tftpboot/pxelinux.cfg/p/192.168.122.0_24' }, ], }, + { + name => 'xcat-s390x-qemu-192.168.122.0_24', + test => 'option[93].hex == 0x001f', + additional_only => JSON::true, + 'option-data' => [ + { name => 'conf-file', data => '192.168.122.0_24' }, + { name => 'path-prefix', data => 'pxelinux.cfg/s390x/' }, + ], + }, { name => 'xcat-uefi-x64', test => "(option[93].hex == 0x0007 or option[93].hex == 0x0009 or option[93].hex == 0x0010) and not ((option[77].exists and (option[77].text == 'xNBA' or option[77].hex == 0x784e4241 or substring(option[77].hex,1,4) == 'xNBA')))", @@ -65,7 +75,10 @@ my $json = $backend->render_dhcp4_config( subnet => '192.168.122.0/24', dynamicrange => '192.168.122.100-192.168.122.120', next_server => '192.168.122.1', - additional_client_classes => ['xcat-opal-v3-192.168.122.0-24'], + additional_client_classes => [ + 'xcat-opal-v3-192.168.122.0-24', + 'xcat-s390x-qemu-192.168.122.0_24', + ], option_data => [ { name => 'routers', data => '192.168.122.1' }, { name => 'domain-name', data => 'cluster.test' }, diff --git a/xCAT-test/unit/dhcp_boot_policy.t b/xCAT-test/unit/dhcp_boot_policy.t index e06aafdda..7dd200a1b 100644 --- a/xCAT-test/unit/dhcp_boot_policy.t +++ b/xCAT-test/unit/dhcp_boot_policy.t @@ -217,4 +217,63 @@ unlike( 'the global class list keeps HTTP boot out: it needs the address of the management node', ); +my $s390x = xCAT::DHCP::BootPolicy->kea_s390x_network_classes( + net => '10.0.0.0', + prefix => 24, +); +is_deeply( + $s390x, + [ + { + name => 'xcat-s390x-qemu-10.0.0.0_24', + test => 'option[93].hex == 0x001f', + additional_only => 1, + 'option-data' => [ + { + name => 'conf-file', + data => '10.0.0.0_24', + 'always-send' => 1, + }, + { + name => 'path-prefix', + data => 'pxelinux.cfg/s390x/', + 'always-send' => 1, + }, + ], + }, + ], + 'QEMU s390x stays within its architecture-specific configuration path', +); +is_deeply( + xCAT::DHCP::BootPolicy->kea_s390x_network_classes(prefix => 24), + [], + 's390x classes require a network', +); + +my @legacy_isc_config = ( + "#xCAT generated dhcp configuration\n", + "\n", + "option conf-file code 209 = text;\n", +); +ok( + xCAT::DHCP::BootPolicy->ensure_isc_path_prefix_definition( + \@legacy_isc_config + ), + 'an older ISC configuration receives the path-prefix definition', +); +is( + $legacy_isc_config[1], + "option path-prefix code 210 = text;\n", + 'the definition is inserted in the global configuration header', +); +ok( + !xCAT::DHCP::BootPolicy->ensure_isc_path_prefix_definition( + \@legacy_isc_config + ), + 'an existing path-prefix definition is preserved', +); +my $definition_count = + grep { /^\s*option\s+path-prefix\s+code\s+210\b/ } @legacy_isc_config; +is($definition_count, 1, 'repeated updates do not duplicate the definition'); + done_testing(); diff --git a/xCAT-test/unit/dhcp_isc_client_arch.t b/xCAT-test/unit/dhcp_isc_client_arch.t index 8cec79091..dc584f8b2 100644 --- a/xCAT-test/unit/dhcp_isc_client_arch.t +++ b/xCAT-test/unit/dhcp_isc_client_arch.t @@ -36,6 +36,11 @@ like( qr/option conf-file = "http:\/\/192\.0\.2\.10:8080\/tftpboot\/pxelinux\.cfg\/p\/192\.0\.2\.0_24";/, 'the existing OPAL branch keeps its subnet URL', ); +like( + $rendered, + qr/client-architecture = 00:1f \{ #QEMU s390x\n\s+option path-prefix = "pxelinux\.cfg\/s390x\/";\n\s+option conf-file = "192\.0\.2\.0_24";/, + 'QEMU s390x receives its subnet configuration and fallback path', +); my @riscv_ids = $rendered =~ /client-architecture = (00:1[9a-e])/g; is_deeply( @@ -47,12 +52,14 @@ is_deeply( my $aarch64_pos = index($rendered, 'client-architecture = 00:0b'); my $tftp_pos = index($rendered, 'client-architecture = 00:1b'); my $http_pos = index($rendered, 'client-architecture = 00:1c'); +my $s390_qemu_pos = index($rendered, 'client-architecture = 00:1f'); my $opal_pos = index($rendered, 'client-architecture = 00:0e'); my $fallback_pos = index($rendered, 'substring(filename,0,1) = null'); cmp_ok($aarch64_pos, '<', $tftp_pos, 'riscv64 follows the aarch64 branch'); cmp_ok($tftp_pos, '<', $http_pos, 'the TFTP branch precedes the HTTP branch'); -cmp_ok($http_pos, '<', $opal_pos, 'the HTTP branch precedes the OPAL branch'); +cmp_ok($http_pos, '<', $s390_qemu_pos, 's390x follows the RISC-V HTTP branch'); +cmp_ok($s390_qemu_pos, '<', $opal_pos, 's390x precedes the OPAL branch'); cmp_ok($http_pos, '<', $fallback_pos, 'the HTTP branch is reachable before the fallback'); like($rendered, qr/filename "\/yaboot";\n\s*\}\n\z/, 'the policy ends with the existing yaboot fallback'); diff --git a/xCAT-test/unit/dhcp_kea_plugin_intent.t b/xCAT-test/unit/dhcp_kea_plugin_intent.t index 4e338ae6d..eb117d740 100644 --- a/xCAT-test/unit/dhcp_kea_plugin_intent.t +++ b/xCAT-test/unit/dhcp_kea_plugin_intent.t @@ -120,6 +120,38 @@ my %network_entry = ( tftpserver => '', ); +{ + no warnings 'redefine'; + local *xCAT::NetworkUtils::thishostisnot = sub { return 0; }; + my $nettab = DHCPKeaIntentNetTable->new(\%network_entry); + my $subnet = xCAT_plugin::dhcp::kea_subnet4_intent( + $nettab, '10.0.0.0', '255.255.255.0', 'eth0', 0, 1, 80 + ); + my %classes = map { $_->{name} => $_ } @{ $subnet->{client_classes} }; + ok($classes{'xcat-s390x-qemu-10.0.0.0_24'}, 'the Kea subnet includes QEMU s390x boot policy'); + is_deeply( + [ grep { /^xcat-s390x-/ } @{ $subnet->{additional_client_classes} } ], + ['xcat-s390x-qemu-10.0.0.0_24'], + 'the s390x policy is evaluated only for its subnet', + ); + is_deeply( + $classes{'xcat-s390x-qemu-10.0.0.0_24'}{'option-data'}, + [ + { + name => 'conf-file', + data => '10.0.0.0_24', + 'always-send' => 1, + }, + { + name => 'path-prefix', + data => 'pxelinux.cfg/s390x/', + 'always-send' => 1, + }, + ], + 'the rendered subnet keeps s390x fallback lookups in their own path', + ); +} + my @sysconfig_policy_cases = ( [ 'sles10', 0, 'SLES 10' ], [ 'sles11', 1, 'SLES 11' ], diff --git a/xCAT-test/unit/genesis_openembedded_activation.t b/xCAT-test/unit/genesis_openembedded_activation.t index 7aeef2c5c..9fd0e6e1a 100644 --- a/xCAT-test/unit/genesis_openembedded_activation.t +++ b/xCAT-test/unit/genesis_openembedded_activation.t @@ -14,6 +14,7 @@ my $rpm_weak_dependencies = join( 'Recommends: xCAT-genesis-openembedded-x86_64', 'Recommends: xCAT-genesis-openembedded-ppc64le', 'Recommends: xCAT-genesis-openembedded-riscv64', + 'Recommends: xCAT-genesis-openembedded-s390x', '%endif', ); @@ -45,6 +46,11 @@ like( qr/^Recommends:.*\bxcat-genesis-openembedded-riscv64\b/m, 'DEB installations recommend the first-class riscv64 image', ); +like( + $deb_control, + qr/^Recommends:.*\bxcat-genesis-openembedded-s390x\b/m, + 'DEB installations recommend the s390x image', +); unlike( $deb_control, qr/^Depends:.*\bxcat-genesis-openembedded-/m, @@ -74,6 +80,11 @@ like( qr/^Recommends:.*\bxcat-genesis-openembedded-riscv64\b/m, 'DEB service nodes recommend the first-class riscv64 image', ); +like( + $sn_deb_control, + qr/^Recommends:.*\bxcat-genesis-openembedded-s390x\b/m, + 'DEB service nodes recommend the s390x image', +); my $go_xcat = slurp_repo_file('xCAT-server/share/xcat/tools/go-xcat'); unlike( @@ -81,7 +92,7 @@ unlike( qr/GO_XCAT_LIBRARY_ONLY/, 'go-xcat cannot be disabled by an inherited test environment variable', ); -for my $architecture (qw(x86 x86_64 ppc64 ppc64le armv7hf aarch64 riscv64)) { +for my $architecture (qw(x86 x86_64 ppc64 ppc64le armv7hf aarch64 riscv64 s390x)) { like( $go_xcat, qr/\bxCAT-genesis-openembedded-\Q$architecture\E\b/, diff --git a/xCAT-test/unit/mknb_exported_genesis.t b/xCAT-test/unit/mknb_exported_genesis.t index faaf980a2..1cd01ada5 100644 --- a/xCAT-test/unit/mknb_exported_genesis.t +++ b/xCAT-test/unit/mknb_exported_genesis.t @@ -156,6 +156,29 @@ ok(!-e "$tftpdir/xcat/genesis.exact-arch.ppc64le", ok(-f "$tftpdir/xcat/genesis.kernel.ppc64", 'retiring ppc64le does not remove the legacy ppc64 fallback'); +for my $artifact (qw( + genesis.kernel.s390x + genesis.fs.s390x.gz + genesis.fs.s390x.lzma + genesis.exact-arch.s390x +)) { + write_file("$tftpdir/xcat/$artifact", 'stale artifact'); +} +make_path("$tftpdir/pxelinux.cfg/s390x"); +write_file( + "$tftpdir/pxelinux.cfg/s390x/192.0.2.0_24", + "# pxelinux.cfg xCAT Genesis s390x\nstale config\n", +); +write_file("$tftpdir/pxelinux.cfg/s390x/default", "admin fallback\n"); +($removed, $remove_error) = + xCAT_plugin::mknb::_remove_openembedded_genesis($tftpdir, 's390x'); +is($remove_error, undef, 's390x boot artifacts can be retired cleanly'); +is($removed, 5, 's390x image and generated discovery configuration are retired'); +ok(!-e "$tftpdir/pxelinux.cfg/s390x/192.0.2.0_24", + 'retiring s390x removes the generated discovery configuration'); +is(read_file("$tftpdir/pxelinux.cfg/s390x/default"), "admin fallback\n", + 'retiring s390x preserves administrator-owned configurations'); + my $failed_kernel = "$tftpdir/xcat/genesis.kernel.aarch64"; make_path($failed_kernel); for my $artifact (qw( diff --git a/xCAT-test/unit/mknb_xcatd_address.t b/xCAT-test/unit/mknb_xcatd_address.t index aa2c77d4b..fc641e58a 100644 --- a/xCAT-test/unit/mknb_xcatd_address.t +++ b/xCAT-test/unit/mknb_xcatd_address.t @@ -508,4 +508,83 @@ ok( %xCAT::TableUtils::site_extra = (); $xCAT::NetworkUtils::nic_ips = undef; +make_path("$::XCATROOT/share/xcat/netboot/genesis-openembedded/s390x"); +use_reporter_address_maps(); +prepare_tftpdir($tmpdir, 'tftpboot-s390x', 's390x'); +$responses = run_mknb('s390x'); +generation_succeeded($responses, 's390x configuration generation succeeds'); + +my $s390x_qemu_path = + "$xCAT::TableUtils::tftpdir/pxelinux.cfg/s390x/192.168.144.0_20"; +ok(-f $s390x_qemu_path, 's390x writes a QEMU network IPL configuration'); +is( + read_config($s390x_qemu_path), + "# pxelinux.cfg xCAT Genesis s390x\n" + . "DEFAULT xCAT\n" + . "LABEL xCAT\n" + . " KERNEL xcat/genesis.kernel.s390x\n" + . " INITRD xcat/genesis.fs.s390x.gz\n" + . " APPEND xcatd=192.168.148.10:3001 xcat.bootloader=s390-ccw console=ttysclp0\n", + 'the QEMU configuration selects Genesis and the SCLP console', +); +unlike( + read_config($s390x_qemu_path), + qr/192\.168\.149\.100/, + 's390x configurations do not use the later floating address', +); + +write_text($s390x_qemu_path, "admin network configuration\n"); +%xCAT::TableUtils::site_extra = ( dhcpinterfaces => 'eth0,eth1:noboot' ); +$xCAT::NetworkUtils::nic_ips = { eth0 => '10.0.0.1', eth1 => '192.168.148.10' }; +$responses = run_mknb('s390x'); +generation_succeeded($responses, 's390x configuration generation accepts a :noboot interface'); +is( + read_config($s390x_qemu_path), + "admin network configuration\n", + 'a :noboot interface preserves an administrator-owned s390x configuration', +); +%xCAT::TableUtils::site_extra = (); +$xCAT::NetworkUtils::nic_ips = undef; +$responses = run_mknb('s390x'); +generation_succeeded($responses, 's390x configuration is restored after removing :noboot'); + +my $s390x_failure_root = "$tmpdir/tftpboot-s390x-failure"; +make_path("$s390x_failure_root/pxelinux.cfg/s390x/192.168.144.0_20"); +my (undef, $s390x_write_error) = + xCAT_plugin::mknb::_write_s390x_discovery_config( + tftpdir => $s390x_failure_root, + network => '192.168.144.0_20', + xcatd_address => '192.168.148.10', + xcatdport => 3001, + consolecmdline => 'console=ttysclp0', + initrd => "$s390x_failure_root/xcat/genesis.fs.s390x.gz", + ); +like( + $s390x_write_error, + qr/^Unable to write s390x Genesis configuration:/, + 's390x configuration write failures are reported', +); + +%xCAT::TableUtils::site_extra = ( + defserialport => '2', + defserialspeed => '9600', + defserialflow => 'hard', + xcatdport => '3002', +); +$responses = run_mknb('s390x'); +generation_succeeded($responses, 's390x ignores PC serial settings'); +like( + read_config($s390x_qemu_path), + qr/^ APPEND xcatd=192\.168\.148\.10:3002 xcat\.bootloader=s390-ccw console=ttysclp0$/m, + 's390x keeps its SCLP console and the configured xcatd port', +); + +%xCAT::TableUtils::site_extra = ( dhcpinterfaces => 'eth0,eth1:noboot' ); +$xCAT::NetworkUtils::nic_ips = { eth0 => '10.0.0.1', eth1 => '192.168.148.10' }; +$responses = run_mknb('s390x'); +generation_succeeded($responses, 's390x configuration generation honors :noboot'); +ok(!-e $s390x_qemu_path, 'a :noboot network gets no QEMU s390x configuration'); +%xCAT::TableUtils::site_extra = (); +$xCAT::NetworkUtils::nic_ips = undef; + done_testing(); diff --git a/xCAT-test/unit/xcatconfig_genesis_openembedded.t b/xCAT-test/unit/xcatconfig_genesis_openembedded.t index b59d6eba6..59c8fc3e7 100644 --- a/xCAT-test/unit/xcatconfig_genesis_openembedded.t +++ b/xCAT-test/unit/xcatconfig_genesis_openembedded.t @@ -27,6 +27,7 @@ make_path( "$tmpdir/share/xcat/netboot/genesis/x86_64/fs", "$tmpdir/share/xcat/netboot/genesis-openembedded/aarch64", "$tmpdir/share/xcat/netboot/genesis-openembedded/ppc64le", + "$tmpdir/share/xcat/netboot/genesis-openembedded/s390x", "$tmpdir/share/xcat/netboot/genesis-openembedded/x86_64", ); my $symlink_target = "$tmpdir/openembedded-riscv64"; @@ -38,25 +39,25 @@ symlink( is_deeply( [ _installed_genesis_architectures($tmpdir) ], - [ qw(aarch64 ppc64 ppc64le x86_64) ], + [ qw(aarch64 ppc64 ppc64le s390x x86_64) ], 'xcatconfig ignores linked OpenEmbedded images and finds legacy images', ); is_deeply( [ _genesis_architectures_to_build($tmpdir, 0) ], - [ qw(aarch64 ppc64le x86_64) ], + [ qw(aarch64 ppc64le s390x x86_64) ], 'an ordinary xCAT update rebuilds only installed OpenEmbedded images', ); is_deeply( [ _genesis_architectures_to_build($tmpdir, 1) ], - [ qw(aarch64 ppc64 ppc64le x86_64) ], + [ qw(aarch64 ppc64 ppc64le s390x x86_64) ], 'a legacy package trigger rebuilds every installed Genesis image', ); make_path("$tmpdir/share/xcat/netboot/genesis-openembedded/unsupported"); is_deeply( [ _installed_genesis_architectures($tmpdir) ], - [ qw(aarch64 ppc64 ppc64le x86_64) ], + [ qw(aarch64 ppc64 ppc64le s390x x86_64) ], 'unknown directories are not passed to mknb', ); @@ -67,7 +68,7 @@ symlink($legacy_target, "$tmpdir/share/xcat/netboot/genesis/ppc64") or die "create legacy directory symlink: $!"; is_deeply( [ _installed_genesis_architectures($tmpdir) ], - [ qw(aarch64 ppc64 ppc64le x86_64) ], + [ qw(aarch64 ppc64 ppc64le s390x x86_64) ], 'legacy Genesis directory symlinks remain supported', ); From 1fe530431cc6045bbbc35c81df6346980ca1fa8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 00:54:19 -0300 Subject: [PATCH 07/65] docs(genesis): document s390x target --- .../code/dhcp_backend_validation_matrix.rst | 7 +++++ .../guides/code/genesis_openembedded_plan.rst | 29 ++++++++++++++----- .../admin-guides/references/man8/mknb.8.rst | 6 +++- xCAT-client/pods/man8/mknb.8.pod | 6 +++- xCAT-genesis-builder/README.md | 3 +- 5 files changed, 40 insertions(+), 11 deletions(-) diff --git a/docs/source/developers/guides/code/dhcp_backend_validation_matrix.rst b/docs/source/developers/guides/code/dhcp_backend_validation_matrix.rst index dbcf15339..364ff63a2 100644 --- a/docs/source/developers/guides/code/dhcp_backend_validation_matrix.rst +++ b/docs/source/developers/guides/code/dhcp_backend_validation_matrix.rst @@ -129,6 +129,13 @@ The following matrix is the default live validation gate for DHCP backend work. - ``makedhcp -n``; ``kea-dhcp4 -t``; reservation add/query/delete; xNBA shell boot; stateful subiquity and stateless compute-image handoff through kernel, initrd, and root image when image validation is in scope + * - QEMU ``s390-ccw`` + - ``s390x`` + - ``ISC`` and ``Kea`` + - ``s390-ccw BIOS network IPL`` + - ``makedhcp -n``; backend parser; architecture ``0x001f``; DHCP options + 209 and 210; TFTP fetch of the generated ``pxelinux.cfg``-style configuration, + Genesis kernel, and initramfs Kea Boot and Reservation Regression Matrix ------------------------------------------ diff --git a/docs/source/developers/guides/code/genesis_openembedded_plan.rst b/docs/source/developers/guides/code/genesis_openembedded_plan.rst index 92472c44e..2c39585d7 100644 --- a/docs/source/developers/guides/code/genesis_openembedded_plan.rst +++ b/docs/source/developers/guides/code/genesis_openembedded_plan.rst @@ -57,6 +57,9 @@ Supported architectures * - ``riscv64`` - RV64GC with OpenSBI - QEMU ``virt`` + * - ``s390x`` + - 64-bit z/Architecture, z10 or later + - QEMU ``s390-ccw-virtio`` ``x86_64`` is the first release target and ``ppc64le`` is the second because both have physical test systems. The other targets have the same software @@ -66,7 +69,7 @@ trees, or controllers. Architecture names are exact. In particular, ``ppc64`` and ``ppc64le`` are different artifacts. The build does not preserve the old xCAT alias between them. ``riscv32``, pre-ARMv7 processors, and i586-only x86 processors are not -supported. +supported. The ``s390x`` target does not support the 31-bit ``s390`` ABI. Networking ---------- @@ -90,6 +93,14 @@ IPv6-only deployment also needs matching support in xCAT server code, DHCP, boot firmware, and boot configuration. Those changes are outside this layer and must not be hidden inside the Genesis image. +The ``s390x`` image uses virtio networking under QEMU. QEMU does not emulate +qeth, and Genesis does not yet configure qeth channel groups. Physical LPAR +and z/VM Genesis networking therefore remain unvalidated. +Genesis does not use the shared CEC serial as an s390x node identifier. +QEMU validation covers network IPL, DHCP options 209 and 210, TFTP, and the +network-specific PXELINUX configuration written by ``mknb``. Existing xCAT +s390x operating-system provisioning remains unchanged. + xCAT protocol ------------- @@ -234,12 +245,13 @@ checksums, reports, and optional signed extensions. Packages install each export under ``genesis-openembedded/ARCH``. ``mknb`` verifies and publishes that export when present, while retaining the old Genesis path as a fallback. -The management-node and service-node packages recommend the ``x86_64`` and -``ppc64le`` images. These are weak dependencies so an older or partial mirror -does not block an xCAT upgrade. Other target images can be installed from the -same common repository before running ``mknb ARCH``. RPM builds based on RPM -4.11 omit the recommendations because that version cannot parse weak dependency -tags. Install the required image package explicitly on those systems. +The management-node and service-node packages recommend the ``x86_64``, +``ppc64le``, ``riscv64``, and ``s390x`` images. These are weak dependencies so +an older or partial mirror does not block an xCAT upgrade. Other target images +can be installed from the same common repository before running ``mknb ARCH``. +RPM builds based on RPM 4.11 omit the recommendations because that version +cannot parse weak dependency tags. Install the required image package +explicitly on those systems. Server integration should be reviewed separately from the image. Independent bugs found while testing Genesis, such as TFTP path handling or Kea policy, @@ -259,7 +271,8 @@ actions. ``x86_64`` and ``ppc64le`` require physical tests before release. VM tests cannot certify platform firmware, BMC behavior, storage-controller tools, RDMA firmware operations, GPUs, Secure Boot on vendor firmware, or -board-specific device trees. +board-specific device trees. Physical ``s390x`` support also requires a qeth +activation path and IBM Z LPAR or z/VM validation. References ---------- diff --git a/docs/source/guides/admin-guides/references/man8/mknb.8.rst b/docs/source/guides/admin-guides/references/man8/mknb.8.rst index acb753076..975e9a8f0 100644 --- a/docs/source/guides/admin-guides/references/man8/mknb.8.rst +++ b/docs/source/guides/admin-guides/references/man8/mknb.8.rst @@ -42,12 +42,16 @@ An export is identified by \ ``xcat-genesis.manifest``\ , which records its form When multiple IPv4 addresses are configured for the same network, \ **mknb**\ uses a locally assigned \ ``site.master``\ for the xcatd endpoint, or the first address reported by the operating system when \ ``site.master``\ is not local. POWER discovery configurations also use this address for their kernel and initrd URLs. -OpenEmbedded images use the exact architecture names \ ``x86``\ , \ ``x86_64``\ , \ ``ppc64``\ , \ ``ppc64le``\ , \ ``armv7hf``\ , \ ``aarch64``\ , and \ ``riscv64``\ . If an OpenEmbedded \ ``ppc64le``\ image is not installed, \ **mknb**\ keeps the old behavior and uses the legacy \ ``ppc64``\ image. +OpenEmbedded images use the exact architecture names \ ``x86``\ , \ ``x86_64``\ , \ ``ppc64``\ , \ ``ppc64le``\ , \ ``armv7hf``\ , \ ``aarch64``\ , \ ``riscv64``\ , and \ ``s390x``\ . If an OpenEmbedded \ ``ppc64le``\ image is not installed, \ **mknb**\ keeps the old behavior and uses the legacy \ ``ppc64``\ image. Canonical \ ``ppc64``\ images are big-endian. xCAT marks them so \ ``ppc64le``\ nodes do not use them as a legacy little-endian fallback. \ **mknb**\ also refuses to replace a marked \ ``ppc64``\ image with that fallback. riscv64 nodes boot through UEFI firmware and grub2. For riscv64, \ **mknb**\ publishes the Genesis kernel and initramfs and writes one grub2 configuration per network under ``/tftpboot/boot/grub2``, named ``grub.cfg-`` followed by the network hex prefix, so that ``grub2.riscv64`` loaded by the firmware can start node discovery. The per-node files written by \ **nodeset**\ take priority over these network files. Networks served by a ``:noboot`` interface in ``site.dhcpinterfaces`` get no discovery configuration. +QEMU s390-ccw guests receive a network-specific ``pxelinux.cfg`` -style configuration through DHCP options 209 and 210. s390x Genesis does not use the shared IBM Z machine serial as a node identifier. This target supports Genesis discovery under QEMU. Existing s390x operating-system provisioning remains unchanged. Physical LPAR and z/VM Genesis networking remain unvalidated because QEMU does not emulate qeth and Genesis does not yet configure qeth channel groups. + +Run \ **makedhcp -n**\ after installing the s390x image and before booting s390x nodes. This adds the required DHCP options to existing configurations. + ******* OPTIONS diff --git a/xCAT-client/pods/man8/mknb.8.pod b/xCAT-client/pods/man8/mknb.8.pod index 6ffa74517..c1f01c3f4 100644 --- a/xCAT-client/pods/man8/mknb.8.pod +++ b/xCAT-client/pods/man8/mknb.8.pod @@ -24,12 +24,16 @@ An export is identified by C, which records its format ve When multiple IPv4 addresses are configured for the same network, B uses a locally assigned C for the xcatd endpoint, or the first address reported by the operating system when C is not local. POWER discovery configurations also use this address for their kernel and initrd URLs. -OpenEmbedded images use the exact architecture names C, C, C, C, C, C, and C. If an OpenEmbedded C image is not installed, B keeps the old behavior and uses the legacy C image. +OpenEmbedded images use the exact architecture names C, C, C, C, C, C, C, and C. If an OpenEmbedded C image is not installed, B keeps the old behavior and uses the legacy C image. Canonical C images are big-endian. xCAT marks them so C nodes do not use them as a legacy little-endian fallback. B also refuses to replace a marked C image with that fallback. riscv64 nodes boot through UEFI firmware and grub2. For riscv64, B publishes the Genesis kernel and initramfs and writes one grub2 configuration per network under C, named C followed by the network hex prefix, so that C loaded by the firmware can start node discovery. The per-node files written by B take priority over these network files. Networks served by a C<:noboot> interface in C get no discovery configuration. +QEMU s390-ccw guests receive a network-specific C-style configuration through DHCP options 209 and 210. s390x Genesis does not use the shared IBM Z machine serial as a node identifier. This target supports Genesis discovery under QEMU. Existing s390x operating-system provisioning remains unchanged. Physical LPAR and z/VM Genesis networking remain unvalidated because QEMU does not emulate qeth and Genesis does not yet configure qeth channel groups. + +Run B after installing the s390x image and before booting s390x nodes. This adds the required DHCP options to existing configurations. + =head1 OPTIONS =over 12 diff --git a/xCAT-genesis-builder/README.md b/xCAT-genesis-builder/README.md index fd6b56d4d..cd7324825 100644 --- a/xCAT-genesis-builder/README.md +++ b/xCAT-genesis-builder/README.md @@ -16,7 +16,8 @@ xCAT-genesis-builder/oe/build x86_64 ``` The build command accepts `x86`, `x86_64`, `ppc64`, `ppc64le`, `armv7hf`, -`aarch64`, and `riscv64`. Multiple architectures are built in the order given. +`aarch64`, `riscv64`, and `s390x`. Multiple architectures are built in the +order given. Artifacts are written below `xCAT-genesis-builder/oe/.work/build/tmp/deploy/images`. The `x86` artifact uses an i686 CPU baseline. The build carries the reviewed Yocto release key in `oe/keys` and verifies its fingerprint locally. From 5a89bb5bd06c63847ffd7470e1a1232e12d59646 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 12:45:27 -0300 Subject: [PATCH 08/65] test(docs): cover the s390x Genesis target --- xCAT-test/unit/genesis_openembedded_activation.t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xCAT-test/unit/genesis_openembedded_activation.t b/xCAT-test/unit/genesis_openembedded_activation.t index 9fd0e6e1a..774f73049 100644 --- a/xCAT-test/unit/genesis_openembedded_activation.t +++ b/xCAT-test/unit/genesis_openembedded_activation.t @@ -114,7 +114,7 @@ like( ); like( $mknb_pod, - qr/x86.*x86_64.*ppc64.*ppc64le.*armv7hf.*aarch64.*riscv64/s, + qr/x86.*x86_64.*ppc64.*ppc64le.*armv7hf.*aarch64.*riscv64.*s390x/s, 'the mknb man page lists every exact OpenEmbedded architecture', ); unlike( From 75b162af9a75cb013a6391d6524e0b2d1e802d7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 13:21:28 -0300 Subject: [PATCH 09/65] docs(genesis): clarify s390x network boot --- .../developers/guides/code/genesis_openembedded_plan.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/developers/guides/code/genesis_openembedded_plan.rst b/docs/source/developers/guides/code/genesis_openembedded_plan.rst index 2c39585d7..d2afa4914 100644 --- a/docs/source/developers/guides/code/genesis_openembedded_plan.rst +++ b/docs/source/developers/guides/code/genesis_openembedded_plan.rst @@ -98,8 +98,8 @@ qeth, and Genesis does not yet configure qeth channel groups. Physical LPAR and z/VM Genesis networking therefore remain unvalidated. Genesis does not use the shared CEC serial as an s390x node identifier. QEMU validation covers network IPL, DHCP options 209 and 210, TFTP, and the -network-specific PXELINUX configuration written by ``mknb``. Existing xCAT -s390x operating-system provisioning remains unchanged. +network-specific ``pxelinux.cfg``-style configuration written by ``mknb``. +Existing xCAT s390x operating-system provisioning remains unchanged. xCAT protocol ------------- From 5203c17a87db753e5710f4e1ff6d93cf8fd958db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 13:39:50 -0300 Subject: [PATCH 10/65] refactor(genesis): clean s390x Perl code --- perl-xCAT/xCAT/DHCP/BootPolicy.pm | 14 +++++-- xCAT-server/lib/xcat/plugins/dhcp.pm | 2 +- xCAT-server/lib/xcat/plugins/mknb.pm | 60 +++++++++++++++++----------- 3 files changed, 48 insertions(+), 28 deletions(-) diff --git a/perl-xCAT/xCAT/DHCP/BootPolicy.pm b/perl-xCAT/xCAT/DHCP/BootPolicy.pm index 8b11fd815..c884fa0f8 100644 --- a/perl-xCAT/xCAT/DHCP/BootPolicy.pm +++ b/perl-xCAT/xCAT/DHCP/BootPolicy.pm @@ -106,11 +106,13 @@ sub kea_httpboot_network_classes { sub kea_s390x_network_classes { my ( $class, %opts ) = @_; - return [] unless $opts{net} && defined( $opts{prefix} ); + if ( !$opts{net} || !defined $opts{prefix} ) { + return []; + } my $network_id = "$opts{net}_$opts{prefix}"; my $safe_network = $network_id; - $safe_network =~ s/[^A-Za-z0-9_.-]/_/g; + $safe_network =~ s{[^[:alnum:]_.-]}{_}gxms; return [ { @@ -136,9 +138,13 @@ sub kea_s390x_network_classes { sub ensure_isc_path_prefix_definition { my ($class, $config) = @_; - return 0 if grep { /^\s*option\s+path-prefix\s+code\s+210\b/ } @{$config}; + foreach my $line ( @{$config} ) { + if ( $line =~ m{^\s* option \s+ path-prefix \s+ code \s+ 210\b}xms ) { + return 0; + } + } - my $index = @{$config} && $config->[0] =~ /^#xCAT/ ? 1 : 0; + my $index = @{$config} && $config->[0] =~ m{^\#xCAT}xms ? 1 : 0; splice @{$config}, $index, 0, "option path-prefix code 210 = text;\n"; return 1; } diff --git a/xCAT-server/lib/xcat/plugins/dhcp.pm b/xCAT-server/lib/xcat/plugins/dhcp.pm index d7e6d67c7..6f170f106 100644 --- a/xCAT-server/lib/xcat/plugins/dhcp.pm +++ b/xCAT-server/lib/xcat/plugins/dhcp.pm @@ -2354,7 +2354,7 @@ sub process_request $restartdhcp = 1; newconfig(); } - if ($^O ne 'aix' && + if (!xCAT::Utils->isAIX() && xCAT::DHCP::BootPolicy->ensure_isc_path_prefix_definition(\@dhcpconf)) { $restartdhcp = 1; } diff --git a/xCAT-server/lib/xcat/plugins/mknb.pm b/xCAT-server/lib/xcat/plugins/mknb.pm index f08f8b7e5..3a03709a8 100644 --- a/xCAT-server/lib/xcat/plugins/mknb.pm +++ b/xCAT-server/lib/xcat/plugins/mknb.pm @@ -7,6 +7,7 @@ use xCAT::TableUtils; use xCAT::NodeRange; use File::Path; use File::Copy; +use English qw(-no_match_vars); my $GENESIS_EXPORT_MANIFEST = 'xcat-genesis.manifest'; my %GENESIS_ARCHITECTURES = map { $_ => 1 } @@ -301,8 +302,10 @@ sub _remove_openembedded_genesis { "$directory/genesis.exact-arch.$arch", ); if ($arch eq 's390x') { - foreach my $path (glob("$tftpdir/pxelinux.cfg/s390x/*")) { - push(@artifacts, $path) if _is_generated_s390x_config($path); + foreach my $path (glob "$tftpdir/pxelinux.cfg/s390x/*") { + if (_is_generated_s390x_config($path)) { + push @artifacts, $path; + } } } my $removed = 0; @@ -327,10 +330,14 @@ sub _remove_openembedded_genesis { sub _is_generated_s390x_config { my ($path) = @_; - return 0 if -l $path || !-f $path; - open(my $config, '<', $path) or return 0; + if (-l $path || !-f $path) { + return 0; + } + open my $config, '<', $path or return 0; my $header = <$config>; - close($config); + if (!close $config) { + return 0; + } return defined($header) && $header eq "# pxelinux.cfg xCAT Genesis s390x\n"; } @@ -731,8 +738,8 @@ sub process_request { mkpath("$tftpdir/boot/grub2"); chmod(0755, "$tftpdir/boot/grub2"); } elsif ($arch eq 's390x') { - mkpath("$tftpdir/pxelinux.cfg/s390x"); - chmod(0755, "$tftpdir/pxelinux.cfg/s390x"); + mkpath "$tftpdir/pxelinux.cfg/s390x"; + chmod 0755, "$tftpdir/pxelinux.cfg/s390x"; } my $dopxe = 0; foreach (keys %{$normnets}) { @@ -740,13 +747,15 @@ sub process_request { my $nicip = $normnets->{$net}; my $xcatd_address = defined($xcatdnormnets->{$net}) ? $xcatdnormnets->{$net} : $nicip; $net =~ s/\//_/; - if (defined($nobootnicips{$nicip}) - || ($arch eq 's390x' && defined($nobootnicips{$xcatd_address}))) { + if (defined $nobootnicips{$nicip} + || ($arch eq 's390x' && defined $nobootnicips{$xcatd_address})) { if ($arch =~ /ppc/ and -r "$tftpdir/pxelinux.cfg/p/$net") { unlink("$tftpdir/pxelinux.cfg/p/$net"); } elsif ($arch eq 's390x') { my $path = "$tftpdir/pxelinux.cfg/s390x/$net"; - unlink($path) if _is_generated_s390x_config($path); + if (_is_generated_s390x_config($path)) { + unlink $path; + } } next; } @@ -894,9 +903,9 @@ sub _write_s390x_discovery_config { my (%args) = @_; my $tftpdir = $args{tftpdir}; my $initrd = $args{initrd}; - $initrd =~ s{^\Q$tftpdir\E/?}{}; + $initrd =~ s{^\Q$tftpdir\E/?}{}xms; my $cmdline = "xcatd=$args{xcatd_address}:$args{xcatdport} xcat.bootloader=s390-ccw"; - if (defined($args{consolecmdline}) and $args{consolecmdline} ne '') { + if (defined $args{consolecmdline} and length $args{consolecmdline}) { $cmdline .= " $args{consolecmdline}"; } @@ -915,18 +924,23 @@ sub _write_s390x_discovery_config { sub _write_s390x_config { my ($path, $contents) = @_; - open(my $config, '>', $path) - or return "Unable to write s390x Genesis configuration: $path: $!"; - unless (print {$config} $contents) { - my $error = $!; - close($config); - unlink($path); - return "Unable to write s390x Genesis configuration: $path: $error"; + my $config; + if (!open $config, '>', $path) { + my $open_error = $OS_ERROR; + return "Unable to write s390x Genesis configuration: $path: $open_error"; } - unless (close($config)) { - my $error = $!; - unlink($path); - return "Unable to write s390x Genesis configuration: $path: $error"; + + my $write_ok = print {$config} $contents; + my $write_error = $OS_ERROR; + my $close_ok = close $config; + my $close_error = $OS_ERROR; + if (!$write_ok) { + unlink $path; + return "Unable to write s390x Genesis configuration: $path: $write_error"; + } + if (!$close_ok) { + unlink $path; + return "Unable to write s390x Genesis configuration: $path: $close_error"; } return; } From f549f46b515d8cd9598b90a72ae1e61264728d7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 14:42:35 -0300 Subject: [PATCH 11/65] fix(genesis): harden s390x boot handoff --- perl-xCAT/xCAT/DHCP/BootPolicy.pm | 30 ++++++++++++++++++++------ xCAT-server/lib/xcat/plugins/dhcp.pm | 10 +++++++-- xCAT-server/lib/xcat/plugins/mknb.pm | 32 ++++++++++++++++++++++------ 3 files changed, 57 insertions(+), 15 deletions(-) diff --git a/perl-xCAT/xCAT/DHCP/BootPolicy.pm b/perl-xCAT/xCAT/DHCP/BootPolicy.pm index c884fa0f8..7a672ac86 100644 --- a/perl-xCAT/xCAT/DHCP/BootPolicy.pm +++ b/perl-xCAT/xCAT/DHCP/BootPolicy.pm @@ -114,8 +114,9 @@ sub kea_s390x_network_classes { my $safe_network = $network_id; $safe_network =~ s{[^[:alnum:]_.-]}{_}gxms; - return [ - { + my @classes; + if ( $opts{qemu_config_present} ) { + push @classes, { name => "xcat-s390x-qemu-$safe_network", test => 'option[93].hex == 0x001f', additional_only => 1, @@ -131,8 +132,17 @@ sub kea_s390x_network_classes { 'always-send' => 1, }, ], - }, - ]; + }; + } + if ( $opts{dpm_config_present} ) { + push @classes, { + name => "xcat-s390x-dpm-$safe_network", + test => 'option[93].hex == 0x0020', + additional_only => 1, + 'boot-file-name' => "pxelinux.cfg/s390x/$network_id.dpm", + }; + } + return \@classes; } sub ensure_isc_path_prefix_definition { @@ -182,9 +192,15 @@ sub isc_client_architecture_lines { " } else if option client-architecture = 00:1c { #riscv64 uefi http boot\n ", " option vendor-class-identifier \"HTTPClient\";\n", " filename \"http://$tftp$portsuffix/tftpboot/boot/grub2/grub2.riscv64\";\n", - " } else if option client-architecture = 00:1f { #QEMU s390x\n ", - " option path-prefix = \"pxelinux.cfg/s390x/\";\n", - " option conf-file = \"${net}_${maskbits}\";\n", + ($opts{s390x_qemu_config_present} ? ( + " } else if option client-architecture = 00:1f { #QEMU s390x\n ", + " option path-prefix = \"pxelinux.cfg/s390x/\";\n", + " option conf-file = \"${net}_${maskbits}\";\n", + ) : ()), + ($opts{s390x_dpm_config_present} ? ( + " } else if option client-architecture = 00:20 { #IBM Z DPM\n ", + " filename \"pxelinux.cfg/s390x/${net}_${maskbits}.dpm\";\n", + ) : ()), " } else if option client-architecture = 00:0e { #OPAL-v3\n ", " option conf-file = \"http://$tftp$portsuffix/tftpboot/pxelinux.cfg/p/${net}_${maskbits}\";\n", " } else if substring (option vendor-class-identifier,0,11) = \"onie_vendor\" { #for onie on cumulus switch\n", diff --git a/xCAT-server/lib/xcat/plugins/dhcp.pm b/xCAT-server/lib/xcat/plugins/dhcp.pm index 6f170f106..fdbf36eca 100644 --- a/xCAT-server/lib/xcat/plugins/dhcp.pm +++ b/xCAT-server/lib/xcat/plugins/dhcp.pm @@ -3368,8 +3368,10 @@ sub kea_subnet4_intent }; push @client_classes, @{ xCAT::DHCP::BootPolicy->kea_s390x_network_classes( - net => $net, - prefix => $prefix, + net => $net, + prefix => $prefix, + qemu_config_present => -f "$tftpdir/pxelinux.cfg/s390x/${net}_${prefix}", + dpm_config_present => -f "$tftpdir/pxelinux.cfg/s390x/${net}_${prefix}.dpm", ) }; if (@client_classes) { @@ -4534,6 +4536,10 @@ sub addnet tftpdir => $tftpdir, net => $net, prefix => $maskbits, + s390x_qemu_config_present => + -f "$tftpdir/pxelinux.cfg/s390x/${net}_${maskbits}", + s390x_dpm_config_present => + -f "$tftpdir/pxelinux.cfg/s390x/${net}_${maskbits}.dpm", ) }; if ($range) { diff --git a/xCAT-server/lib/xcat/plugins/mknb.pm b/xCAT-server/lib/xcat/plugins/mknb.pm index 3a03709a8..41cce0136 100644 --- a/xCAT-server/lib/xcat/plugins/mknb.pm +++ b/xCAT-server/lib/xcat/plugins/mknb.pm @@ -302,10 +302,14 @@ sub _remove_openembedded_genesis { "$directory/genesis.exact-arch.$arch", ); if ($arch eq 's390x') { - foreach my $path (glob "$tftpdir/pxelinux.cfg/s390x/*") { - if (_is_generated_s390x_config($path)) { - push @artifacts, $path; + my $config_directory = "$tftpdir/pxelinux.cfg/s390x"; + if (opendir(my $config_stream, $config_directory)) { + foreach my $name (readdir $config_stream) { + next if $name eq '.' || $name eq '..'; + my $path = "$config_directory/$name"; + push @artifacts, $path if _is_generated_s390x_config($path); } + closedir $config_stream; } } my $removed = 0; @@ -739,6 +743,7 @@ sub process_request { chmod(0755, "$tftpdir/boot/grub2"); } elsif ($arch eq 's390x') { mkpath "$tftpdir/pxelinux.cfg/s390x"; + chmod 0755, "$tftpdir/pxelinux.cfg"; chmod 0755, "$tftpdir/pxelinux.cfg/s390x"; } my $dopxe = 0; @@ -752,9 +757,11 @@ sub process_request { if ($arch =~ /ppc/ and -r "$tftpdir/pxelinux.cfg/p/$net") { unlink("$tftpdir/pxelinux.cfg/p/$net"); } elsif ($arch eq 's390x') { - my $path = "$tftpdir/pxelinux.cfg/s390x/$net"; - if (_is_generated_s390x_config($path)) { - unlink $path; + foreach my $path ( + "$tftpdir/pxelinux.cfg/s390x/$net", + "$tftpdir/pxelinux.cfg/s390x/$net.dpm", + ) { + unlink $path if _is_generated_s390x_config($path); } } next; @@ -919,6 +926,19 @@ sub _write_s390x_discovery_config { my $error = _write_s390x_config($qemu_path, $qemu_config); return (undef, $error) if $error; + my $dpm_path = "$qemu_path.dpm"; + my $dpm_config = "# pxelinux.cfg xCAT Genesis s390x\n" + . "DEFAULT xCAT\n" + . "label xCAT\n" + . " kernel=xcat/genesis.kernel.s390x\n" + . " initrd=$initrd\n" + . " append=$cmdline\n"; + $error = _write_s390x_config($dpm_path, $dpm_config); + if ($error) { + unlink $qemu_path; + return (undef, $error); + } + return ($qemu_path, undef); } From 2bb76b523e1c47432ff6fa28bc0b1db93f32de6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 14:42:42 -0300 Subject: [PATCH 12/65] test(genesis): cover s390x boot handoff --- .../integration/dhcp_isc_config_validation.t | 2 + .../integration/dhcp_kea_config_validation.t | 7 +++ xCAT-test/unit/dhcp_boot_policy.t | 36 ++++++++++++-- xCAT-test/unit/dhcp_isc_client_arch.t | 15 ++++-- xCAT-test/unit/dhcp_kea_plugin_intent.t | 47 ++++++++++++------- xCAT-test/unit/mknb_exported_genesis.t | 30 +++++++++++- xCAT-test/unit/mknb_xcatd_address.t | 16 +++++++ 7 files changed, 125 insertions(+), 28 deletions(-) diff --git a/xCAT-test/integration/dhcp_isc_config_validation.t b/xCAT-test/integration/dhcp_isc_config_validation.t index 35dc3bf0e..792739f48 100644 --- a/xCAT-test/integration/dhcp_isc_config_validation.t +++ b/xCAT-test/integration/dhcp_isc_config_validation.t @@ -37,6 +37,8 @@ push @config, @{ xCAT::DHCP::BootPolicy->isc_client_architecture_lines( portsuffix => '', net => '192.0.2.0', prefix => 24, + s390x_qemu_config_present => 1, + s390x_dpm_config_present => 1, ) }, "}\n"; my $configuration_root = -d '/etc/dhcp' ? '/etc/dhcp' : '/etc'; diff --git a/xCAT-test/integration/dhcp_kea_config_validation.t b/xCAT-test/integration/dhcp_kea_config_validation.t index 125189705..acc7e5d2d 100644 --- a/xCAT-test/integration/dhcp_kea_config_validation.t +++ b/xCAT-test/integration/dhcp_kea_config_validation.t @@ -51,6 +51,12 @@ my $json = $backend->render_dhcp4_config( { name => 'path-prefix', data => 'pxelinux.cfg/s390x/' }, ], }, + { + name => 'xcat-s390x-dpm-192.168.122.0_24', + test => 'option[93].hex == 0x0020', + additional_only => JSON::true, + 'boot-file-name' => 'pxelinux.cfg/s390x/192.168.122.0_24.dpm', + }, { name => 'xcat-uefi-x64', test => "(option[93].hex == 0x0007 or option[93].hex == 0x0009 or option[93].hex == 0x0010) and not ((option[77].exists and (option[77].text == 'xNBA' or option[77].hex == 0x784e4241 or substring(option[77].hex,1,4) == 'xNBA')))", @@ -78,6 +84,7 @@ my $json = $backend->render_dhcp4_config( additional_client_classes => [ 'xcat-opal-v3-192.168.122.0-24', 'xcat-s390x-qemu-192.168.122.0_24', + 'xcat-s390x-dpm-192.168.122.0_24', ], option_data => [ { name => 'routers', data => '192.168.122.1' }, diff --git a/xCAT-test/unit/dhcp_boot_policy.t b/xCAT-test/unit/dhcp_boot_policy.t index 7dd200a1b..94e8477ff 100644 --- a/xCAT-test/unit/dhcp_boot_policy.t +++ b/xCAT-test/unit/dhcp_boot_policy.t @@ -218,8 +218,10 @@ unlike( ); my $s390x = xCAT::DHCP::BootPolicy->kea_s390x_network_classes( - net => '10.0.0.0', - prefix => 24, + net => '10.0.0.0', + prefix => 24, + qemu_config_present => 1, + dpm_config_present => 1, ); is_deeply( $s390x, @@ -241,13 +243,37 @@ is_deeply( }, ], }, + { + name => 'xcat-s390x-dpm-10.0.0.0_24', + test => 'option[93].hex == 0x0020', + additional_only => 1, + 'boot-file-name' => 'pxelinux.cfg/s390x/10.0.0.0_24.dpm', + }, ], - 'QEMU s390x stays within its architecture-specific configuration path', + 's390x firmware receives its supported network configuration method', ); is_deeply( - xCAT::DHCP::BootPolicy->kea_s390x_network_classes(prefix => 24), + xCAT::DHCP::BootPolicy->kea_s390x_network_classes( + net => '10.0.0.0', prefix => 24 + ), [], - 's390x classes require a network', + 's390x classes require a generated network configuration', +); +is_deeply( + xCAT::DHCP::BootPolicy->kea_s390x_network_classes( + net => '10.0.0.0', + prefix => 24, + dpm_config_present => 1, + ), + [ + { + name => 'xcat-s390x-dpm-10.0.0.0_24', + test => 'option[93].hex == 0x0020', + additional_only => 1, + 'boot-file-name' => 'pxelinux.cfg/s390x/10.0.0.0_24.dpm', + }, + ], + 's390x policies follow the configurations available on the network', ); my @legacy_isc_config = ( diff --git a/xCAT-test/unit/dhcp_isc_client_arch.t b/xCAT-test/unit/dhcp_isc_client_arch.t index dc584f8b2..f87bf4294 100644 --- a/xCAT-test/unit/dhcp_isc_client_arch.t +++ b/xCAT-test/unit/dhcp_isc_client_arch.t @@ -14,6 +14,8 @@ my $rendered = join '', @{ xCAT::DHCP::BootPolicy->isc_client_architecture_lines tftpdir => '/srv/tftp', net => '192.0.2.0', prefix => 24, + s390x_qemu_config_present => 1, + s390x_dpm_config_present => 1, ) }; like( @@ -38,8 +40,8 @@ like( ); like( $rendered, - qr/client-architecture = 00:1f \{ #QEMU s390x\n\s+option path-prefix = "pxelinux\.cfg\/s390x\/";\n\s+option conf-file = "192\.0\.2\.0_24";/, - 'QEMU s390x receives its subnet configuration and fallback path', + qr/client-architecture = 00:1f \{ #QEMU s390x\n\s+option path-prefix = "pxelinux\.cfg\/s390x\/";\n\s+option conf-file = "192\.0\.2\.0_24";\n\s+\} else if option client-architecture = 00:20 \{ #IBM Z DPM\n\s+filename "pxelinux\.cfg\/s390x\/192\.0\.2\.0_24\.dpm";/, + 's390x firmware receives its supported network configuration method', ); my @riscv_ids = $rendered =~ /client-architecture = (00:1[9a-e])/g; @@ -52,15 +54,18 @@ is_deeply( my $aarch64_pos = index($rendered, 'client-architecture = 00:0b'); my $tftp_pos = index($rendered, 'client-architecture = 00:1b'); my $http_pos = index($rendered, 'client-architecture = 00:1c'); -my $s390_qemu_pos = index($rendered, 'client-architecture = 00:1f'); my $opal_pos = index($rendered, 'client-architecture = 00:0e'); my $fallback_pos = index($rendered, 'substring(filename,0,1) = null'); cmp_ok($aarch64_pos, '<', $tftp_pos, 'riscv64 follows the aarch64 branch'); cmp_ok($tftp_pos, '<', $http_pos, 'the TFTP branch precedes the HTTP branch'); -cmp_ok($http_pos, '<', $s390_qemu_pos, 's390x follows the RISC-V HTTP branch'); -cmp_ok($s390_qemu_pos, '<', $opal_pos, 's390x precedes the OPAL branch'); cmp_ok($http_pos, '<', $fallback_pos, 'the HTTP branch is reachable before the fallback'); like($rendered, qr/filename "\/yaboot";\n\s*\}\n\z/, 'the policy ends with the existing yaboot fallback'); +my $without_s390x = join '', @{ xCAT::DHCP::BootPolicy->isc_client_architecture_lines( + next_server => '192.0.2.10', net => '192.0.2.0', prefix => 24, + ) }; +unlike($without_s390x, qr/client-architecture = 00:(?:1f|20)/, + 'ISC does not advertise s390x without a generated network configuration'); + done_testing(); diff --git a/xCAT-test/unit/dhcp_kea_plugin_intent.t b/xCAT-test/unit/dhcp_kea_plugin_intent.t index eb117d740..f1285ca90 100644 --- a/xCAT-test/unit/dhcp_kea_plugin_intent.t +++ b/xCAT-test/unit/dhcp_kea_plugin_intent.t @@ -8,6 +8,7 @@ use lib "$FindBin::Bin/../lib"; use lib "$FindBin::Bin/../../perl-xCAT"; use lib "$FindBin::Bin/../../xCAT-server/lib/perl"; +use File::Path qw(make_path); use File::Temp qw(tempdir); use Socket (); use Test::More; @@ -23,7 +24,8 @@ BEGIN { $INC{'xCAT/Table.pm'} = __FILE__; package xCAT::TableUtils; - sub getTftpDir { return '/tftpboot'; } + our $tftpdir; + sub getTftpDir { return $tftpdir; } sub get_site_attribute { return; } $INC{'xCAT/TableUtils.pm'} = __FILE__; @@ -83,6 +85,7 @@ require xCAT::Utils; *xCAT::Utils::runcmd = sub { return; }; } +$xCAT::TableUtils::tftpdir = tempdir(CLEANUP => 1); my $source_dhcp_plugin = repo_path('xCAT-server/lib/xcat/plugins/dhcp.pm'); require $source_dhcp_plugin; require xCAT::DHCP::Backend::Kea; @@ -120,6 +123,28 @@ my %network_entry = ( tftpserver => '', ); +{ + no warnings 'redefine'; + local *xCAT::NetworkUtils::thishostisnot = sub { return 0; }; + my $nettab = DHCPKeaIntentNetTable->new(\%network_entry); + my $subnet = xCAT_plugin::dhcp::kea_subnet4_intent( + $nettab, '10.0.0.0', '255.255.255.0', 'eth0', 0, 1, 80 + ); + my %classes = map { $_->{name} => $_ } @{ $subnet->{client_classes} }; + ok(!grep(/^xcat-s390x-/, keys %classes), + 'Kea omits s390x policy without a generated network configuration'); +} + +my $s390x_config_dir = + "$xCAT::TableUtils::tftpdir/pxelinux.cfg/s390x"; +make_path($s390x_config_dir); +open(my $s390x_config, '>', "$s390x_config_dir/10.0.0.0_24") + or die "Unable to create s390x test configuration: $!"; +close($s390x_config); +open($s390x_config, '>', "$s390x_config_dir/10.0.0.0_24.dpm") + or die "Unable to create s390x DPM test configuration: $!"; +close($s390x_config); + { no warnings 'redefine'; local *xCAT::NetworkUtils::thishostisnot = sub { return 0; }; @@ -129,26 +154,14 @@ my %network_entry = ( ); my %classes = map { $_->{name} => $_ } @{ $subnet->{client_classes} }; ok($classes{'xcat-s390x-qemu-10.0.0.0_24'}, 'the Kea subnet includes QEMU s390x boot policy'); + ok($classes{'xcat-s390x-dpm-10.0.0.0_24'}, 'the Kea subnet includes IBM Z DPM boot policy'); is_deeply( [ grep { /^xcat-s390x-/ } @{ $subnet->{additional_client_classes} } ], - ['xcat-s390x-qemu-10.0.0.0_24'], - 'the s390x policy is evaluated only for its subnet', - ); - is_deeply( - $classes{'xcat-s390x-qemu-10.0.0.0_24'}{'option-data'}, [ - { - name => 'conf-file', - data => '10.0.0.0_24', - 'always-send' => 1, - }, - { - name => 'path-prefix', - data => 'pxelinux.cfg/s390x/', - 'always-send' => 1, - }, + 'xcat-s390x-qemu-10.0.0.0_24', + 'xcat-s390x-dpm-10.0.0.0_24', ], - 'the rendered subnet keeps s390x fallback lookups in their own path', + 'the s390x policy is evaluated only for its subnet', ); } diff --git a/xCAT-test/unit/mknb_exported_genesis.t b/xCAT-test/unit/mknb_exported_genesis.t index 1cd01ada5..f9b0ae860 100644 --- a/xCAT-test/unit/mknb_exported_genesis.t +++ b/xCAT-test/unit/mknb_exported_genesis.t @@ -169,16 +169,44 @@ write_file( "$tftpdir/pxelinux.cfg/s390x/192.0.2.0_24", "# pxelinux.cfg xCAT Genesis s390x\nstale config\n", ); +write_file( + "$tftpdir/pxelinux.cfg/s390x/192.0.2.0_24.dpm", + "# pxelinux.cfg xCAT Genesis s390x\nstale DPM config\n", +); write_file("$tftpdir/pxelinux.cfg/s390x/default", "admin fallback\n"); ($removed, $remove_error) = xCAT_plugin::mknb::_remove_openembedded_genesis($tftpdir, 's390x'); is($remove_error, undef, 's390x boot artifacts can be retired cleanly'); -is($removed, 5, 's390x image and generated discovery configuration are retired'); +is($removed, 6, 's390x image and generated discovery configurations are retired'); ok(!-e "$tftpdir/pxelinux.cfg/s390x/192.0.2.0_24", 'retiring s390x removes the generated discovery configuration'); +ok(!-e "$tftpdir/pxelinux.cfg/s390x/192.0.2.0_24.dpm", + 'retiring s390x removes the generated DPM configuration'); is(read_file("$tftpdir/pxelinux.cfg/s390x/default"), "admin fallback\n", 'retiring s390x preserves administrator-owned configurations'); +my $special_tftpdir = "$tmpdir/tftp root {s390x}"; +make_path("$special_tftpdir/xcat", "$special_tftpdir/pxelinux.cfg/s390x"); +for my $artifact (qw(genesis.kernel.s390x genesis.fs.s390x.gz genesis.exact-arch.s390x)) { + write_file("$special_tftpdir/xcat/$artifact", "artifact\n"); +} +write_file( + "$special_tftpdir/pxelinux.cfg/s390x/192.0.2.0_24", + "# pxelinux.cfg xCAT Genesis s390x\nconfiguration\n", +); +write_file( + "$special_tftpdir/pxelinux.cfg/s390x/192.0.2.0_24.dpm", + "# pxelinux.cfg xCAT Genesis s390x\nDPM configuration\n", +); +($removed, $remove_error) = + xCAT_plugin::mknb::_remove_openembedded_genesis($special_tftpdir, 's390x'); +is($remove_error, undef, 's390x cleanup accepts a TFTP path with shell metacharacters'); +is($removed, 5, 's390x cleanup removes every generated artifact from that path'); +ok(!-e "$special_tftpdir/pxelinux.cfg/s390x/192.0.2.0_24", + 's390x cleanup does not expand the TFTP path as a glob'); +ok(!-e "$special_tftpdir/pxelinux.cfg/s390x/192.0.2.0_24.dpm", + 's390x cleanup removes DPM configurations from the literal path'); + my $failed_kernel = "$tftpdir/xcat/genesis.kernel.aarch64"; make_path($failed_kernel); for my $artifact (qw( diff --git a/xCAT-test/unit/mknb_xcatd_address.t b/xCAT-test/unit/mknb_xcatd_address.t index fc641e58a..c822d3651 100644 --- a/xCAT-test/unit/mknb_xcatd_address.t +++ b/xCAT-test/unit/mknb_xcatd_address.t @@ -511,8 +511,12 @@ $xCAT::NetworkUtils::nic_ips = undef; make_path("$::XCATROOT/share/xcat/netboot/genesis-openembedded/s390x"); use_reporter_address_maps(); prepare_tftpdir($tmpdir, 'tftpboot-s390x', 's390x'); +make_path("$xCAT::TableUtils::tftpdir/pxelinux.cfg"); +chmod(0700, "$xCAT::TableUtils::tftpdir/pxelinux.cfg"); $responses = run_mknb('s390x'); generation_succeeded($responses, 's390x configuration generation succeeds'); +is((stat "$xCAT::TableUtils::tftpdir/pxelinux.cfg")[2] & 07777, 0755, + 's390x configuration generation repairs the parent directory mode'); my $s390x_qemu_path = "$xCAT::TableUtils::tftpdir/pxelinux.cfg/s390x/192.168.144.0_20"; @@ -532,6 +536,17 @@ unlike( qr/192\.168\.149\.100/, 's390x configurations do not use the later floating address', ); +my $s390x_dpm_path = "$s390x_qemu_path.dpm"; +is( + read_config($s390x_dpm_path), + "# pxelinux.cfg xCAT Genesis s390x\n" + . "DEFAULT xCAT\n" + . "label xCAT\n" + . " kernel=xcat/genesis.kernel.s390x\n" + . " initrd=xcat/genesis.fs.s390x.gz\n" + . " append=xcatd=192.168.148.10:3001 xcat.bootloader=s390-ccw console=ttysclp0\n", + 'the DPM configuration uses the IBM Z network-boot syntax', +); write_text($s390x_qemu_path, "admin network configuration\n"); %xCAT::TableUtils::site_extra = ( dhcpinterfaces => 'eth0,eth1:noboot' ); @@ -584,6 +599,7 @@ $xCAT::NetworkUtils::nic_ips = { eth0 => '10.0.0.1', eth1 => '192.168.148.10' }; $responses = run_mknb('s390x'); generation_succeeded($responses, 's390x configuration generation honors :noboot'); ok(!-e $s390x_qemu_path, 'a :noboot network gets no QEMU s390x configuration'); +ok(!-e $s390x_dpm_path, 'a :noboot network gets no IBM Z DPM configuration'); %xCAT::TableUtils::site_extra = (); $xCAT::NetworkUtils::nic_ips = undef; From dec2227c8ed632e19f4064fd861b5fcb00c0ea8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 14:42:51 -0300 Subject: [PATCH 13/65] fix(genesis): scope s390x identity handling --- perl-xCAT/xCAT/Schema.pm | 4 +-- .../files/xcat-genesis-console/src/state.c | 20 +++++------ .../files/genesis-discover | 33 ++++++++----------- 3 files changed, 24 insertions(+), 33 deletions(-) diff --git a/perl-xCAT/xCAT/Schema.pm b/perl-xCAT/xCAT/Schema.pm index e1317827d..50dde765c 100644 --- a/perl-xCAT/xCAT/Schema.pm +++ b/perl-xCAT/xCAT/Schema.pm @@ -751,7 +751,7 @@ passed as argument rather than by table value', descriptions => { node => 'The node name or group name.', os => 'The operating system deployed on this node. Valid values: AIX, rhels*,rhelc*, rhas*,centos*, alma*, rocky*,SL*, fedora*, sles* (where * is the version #). As a special case, if this is set to "boottarget", then it will use the initrd/kernel/parameters specified in the row in the boottarget table in which boottarget.bprofile equals nodetype.profile.', - arch => 'The hardware architecture of this node. Valid values: x86_64, ppc64, x86, ia64, aarch64, riscv64.', + arch => 'The hardware architecture of this node. Valid values: x86_64, ppc64, x86, ia64, aarch64, riscv64, s390x.', profile => 'The string to use to locate a kickstart or autoyast template to use for OS deployment of this node. If the provmethod attribute is set to an osimage name, that takes precedence, and profile need not be defined. Otherwise, the os, profile, and arch are used to search for the files in /install/custom first, and then in /opt/xcat/share/xcat.', provmethod => 'The provisioning method for node deployment. The valid values are install, netboot, statelite or an os image name from the osimage table. If an image name is specified, the osimage definition stored in the osimage table and the linuximage table (for Linux) or nimimage table (for AIX) are used to locate the files for templates, pkglists, syncfiles, etc. On Linux, if install, netboot or statelite is specified, the os, profile, and arch are used to search for the files in /install/custom first, and then in /opt/xcat/share/xcat.', supportedarchs => 'Comma delimited list of architectures this node can execute.', @@ -795,7 +795,7 @@ passed as argument rather than by table value', profile => 'The node usage category. For example compute, service.', osname => 'Operating system name- AIX or Linux.', osvers => 'The Linux operating system deployed on this node. Valid values: rhels*,rhelc*, rhas*,centos*,alma*, rocky*,SL*, fedora*, sles* (where * is the version #).', - osarch => 'The hardware architecture of this node. For netboot/statelite images, QEMU emulation for non-native architectures is used if qemu-user-static is installed and configured via systemd-binfmt. Valid values: x86_64, ppc64, x86, ia64, aarch64, riscv64.', + osarch => 'The hardware architecture of this node. For netboot/statelite images, QEMU emulation for non-native architectures is used if qemu-user-static is installed and configured via systemd-binfmt. Valid values: x86_64, ppc64, x86, ia64, aarch64, riscv64, s390x.', synclists => 'The fully qualified name of a file containing a list of files to synchronize on the nodes. Can be a comma separated list of multiple synclist files. The synclist generated by PCM named /install/osimages//synclist.cfm is reserved for use only by PCM and should not be edited by the admin.', postscripts => 'Comma separated list of scripts that should be run on this image after diskful installation or diskless boot. For installation of RedHat, CentOS, Fedora, the scripts will be run before the reboot. For installation of SLES, the scripts will be run after the reboot but before the init.d process. For diskless deployment, the scripts will be run at the init.d time, and xCAT will automatically add the list of scripts from the postbootscripts attribute to run after postscripts list. For installation of AIX, the scripts will run after the reboot and acts the same as the postbootscripts attribute. For AIX, use the postbootscripts attribute. See the site table runbootscripts attribute.', postbootscripts => 'Comma separated list of scripts that should be run on this after diskful installation or diskless boot. On AIX these scripts are run during the processing of /etc/inittab. On Linux they are run at the init.d time. xCAT automatically adds the scripts in the xcatdefaults.postbootscripts attribute to run first in the list. See the site table runbootscripts attribute.', diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/state.c b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/state.c index d9a8cdcd3..776cb6296 100644 --- a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/state.c +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/state.c @@ -302,11 +302,9 @@ void xcat_load_console_state(struct console_state *state) { char path[VALUE_SIZE * 2]; char release_name[64] = "xCAT Genesis"; char release_version[32] = ""; -#ifdef XCAT_CONSOLE_TEST - const char *test_architecture = getenv("XCAT_TEST_ARCH"); -#endif bool xcat_configured; bool interface_selected; + bool s390_system; bool s390_ccw_boot; memset(state, 0, sizeof(*state)); @@ -347,13 +345,13 @@ void xcat_load_console_state(struct console_state *state) { xcat_set_text(state->architecture, sizeof(state->architecture), "unknown"); xcat_set_text(state->kernel, sizeof(state->kernel), "unknown"); } -#ifdef XCAT_CONSOLE_TEST - if (test_architecture != NULL) - xcat_set_text(state->architecture, sizeof(state->architecture), "%s", - test_architecture); -#endif + snprintf(path, sizeof(path), "%s/sysinfo", proc_root); + s390_system = strcmp(state->architecture, "s390x") == 0 || + access(path, F_OK) == 0; + if (s390_system) + xcat_set_text(state->architecture, sizeof(state->architecture), "s390x"); - s390_ccw_boot = strcmp(state->architecture, "s390x") == 0 && + s390_ccw_boot = s390_system && xcat_cmdline_value(cmdline_text, "xcat.bootloader", value, sizeof(value)) && strcmp(value, "s390-ccw") == 0; @@ -370,7 +368,7 @@ void xcat_load_console_state(struct console_state *state) { access(path, F_OK) == 0 ? "UEFI" : "Device Tree"); } else if (strncmp(state->architecture, "riscv", 5) == 0) { xcat_set_text(state->firmware, sizeof(state->firmware), "OpenSBI"); - } else if (strcmp(state->architecture, "s390x") == 0) { + } else if (s390_system) { xcat_set_text(state->firmware, sizeof(state->firmware), "%s", s390_ccw_boot ? "s390-ccw BIOS" : "not reported"); } else { @@ -394,7 +392,7 @@ void xcat_load_console_state(struct console_state *state) { xcat_set_text(state->serial, sizeof(state->serial), "not reported"); snprintf(path, sizeof(path), "%s/class/dmi/id/product_uuid", sys_root); xcat_read_line(path, state->uuid, sizeof(state->uuid)); - if (strcmp(state->architecture, "s390x") == 0) { + if (s390_system) { xcat_set_text(state->serial, sizeof(state->serial), "not reported"); set_s390_identity(proc_root, state); } diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-discover b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-discover index 8346b4c06..82526fe29 100755 --- a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-discover +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-discover @@ -175,26 +175,19 @@ case "$system_vendor $product_name" in esac [[ -z $s390_virtual ]] || node_type=virtual -cpu_count=$(awk ' - /^processor([[:space:]]+[0-9]+)?[[:space:]]*:/ {processors++} - /^cpu[[:space:]]*:/ {cpus++} - END {print (processors > 0 ? processors : cpus + 0)}' \ - "$proc_root/cpuinfo" 2>/dev/null || printf '0') -cpu_type=$(awk -F ':' ' - /^model name[[:space:]]*:/ && model == "" { - sub(/^[[:space:]]+/, "", $2); model = $2 - } - /^cpu[[:space:]]*:/ && cpu == "" { - sub(/^[[:space:]]+/, "", $2); cpu = $2 - } - /^vendor_id[[:space:]]*:/ && vendor == "" { - sub(/^[[:space:]]+/, "", $2); vendor = $2 - } - END { - if (model != "") print model - else if (cpu != "") print cpu - else if (vendor != "") print vendor - }' "$proc_root/cpuinfo" 2>/dev/null || true) +if [[ $architecture == s390x ]]; then + cpu_count=$(awk '/^processor[[:space:]]+[0-9]+[[:space:]]*:/ {count++} END {print count + 0}' \ + "$proc_root/cpuinfo" 2>/dev/null || printf '0') + cpu_type=$(awk -F ':' \ + '/^vendor_id[[:space:]]*:/ {sub(/^[[:space:]]+/, "", $2); print $2; exit}' \ + "$proc_root/cpuinfo" 2>/dev/null || true) +else + cpu_count=$(awk '/^(processor|cpu)[[:space:]]*:/ {count++} END {print count + 0}' \ + "$proc_root/cpuinfo" 2>/dev/null || printf '0') + cpu_type=$(awk -F ':' \ + '/^(model name|cpu)[[:space:]]*:/ {sub(/^[[:space:]]+/, "", $2); print $2; exit}' \ + "$proc_root/cpuinfo" 2>/dev/null || true) +fi memory=$(awk '/^MemTotal:/ {printf "%.0fMB", $2 / 1024; exit}' \ "$proc_root/meminfo" 2>/dev/null || true) disk_size=$(lsblk -b -dn -o NAME,SIZE,TYPE 2>/dev/null \ From 059438d2660b6d417b475d113f3dd8b684424620 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 14:42:57 -0300 Subject: [PATCH 14/65] test(genesis): cover s390x identity fallbacks --- xCAT-test/unit/genesis_openembedded_console.t | 5 +---- .../unit/genesis_openembedded_discovery.t | 18 ++++++++++++++++-- xCAT-test/unit/lookup_netboot_arch.t | 6 +++++- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/xCAT-test/unit/genesis_openembedded_console.t b/xCAT-test/unit/genesis_openembedded_console.t index 332358eb8..de015dbfb 100644 --- a/xCAT-test/unit/genesis_openembedded_console.t +++ b/xCAT-test/unit/genesis_openembedded_console.t @@ -26,7 +26,6 @@ my $compiler = $ENV{CC} || 'cc'; is( system( $compiler, '-D_POSIX_C_SOURCE=200809L', '-DXCAT_CONSOLE_PLAIN_ONLY', - '-DXCAT_CONSOLE_TEST', '-std=c17', '-Wall', '-Wextra', '-Wpedantic', '-Werror', @plain_sources, '-o', $binary ) >> 8, @@ -97,7 +96,7 @@ C ); is( system( - $compiler, '-D_POSIX_C_SOURCE=200809L', '-DXCAT_CONSOLE_TEST', + $compiler, '-D_POSIX_C_SOURCE=200809L', '-std=c17', '-Wall', '-Wextra', '-Wpedantic', '-Werror', '-I', $source_dir, $identity_test_source, File::Spec->catfile( $source_dir, 'state.c' ), @@ -245,7 +244,6 @@ Sequence Code: 0000000012345 LPAR UUID: 93724168-fda3-429b-8b28-a5d245dcb3ff VM00 UUID: 82038f2a-1344-aaf7-1a85-2a7250be2076 SYSINFO -$environment{XCAT_TEST_ARCH} = 's390x'; write_file( $cmdline, "xcatd=192.0.2.10:3001\n" ); ( $status, $output ) = run_console(); is( $status, 0, 'plain console accepts IBM Z identity' ); @@ -275,7 +273,6 @@ SYSINFO like( $probe_output, qr/^uuid=93724168-fda3-429b-8b28-a5d245dcb3ff$/m, 'console uses the LPAR UUID when no guest UUID is available' ); -delete $environment{XCAT_TEST_ARCH}; write_file( File::Spec->catfile( $dmi_root, 'product_serial' ), "TEST-SERIAL-001\n" ); write_file( File::Spec->catfile( $dmi_root, 'product_uuid' ), diff --git a/xCAT-test/unit/genesis_openembedded_discovery.t b/xCAT-test/unit/genesis_openembedded_discovery.t index f45e1e826..0913e5259 100644 --- a/xCAT-test/unit/genesis_openembedded_discovery.t +++ b/xCAT-test/unit/genesis_openembedded_discovery.t @@ -415,8 +415,8 @@ like( $packet, qr{02AB123}, 'Power discovery reports the system serial' ); like( $packet, qr{PowerNV}, 'Power discovery reports the firmware platform' ); -like( $packet, qr{2}, - 'Power discovery counts processor records' ); +like( $packet, qr{4}, + 'Power discovery keeps the existing processor count' ); like( $packet, qr{9009-42a-02ab123-525400000002}, 'Power discovery creates a stable fallback UUID' ); @@ -461,6 +461,20 @@ like( $packet, qr{82038f2a-1344-aaf7-1a85-2a7250be2076}, 's390x discovery uses the closest guest UUID' ); +write_file( File::Spec->catfile( $proc_root, 'sysinfo' ), <<'SYSINFO' ); +Manufacturer: IBM +Type: 3931 +Model: 701 A01 +VM00 Control Program: KVM/Linux +SYSINFO +is( run_script( $discover_script, \%environment ), 0, + 's390x discovery accepts a guest without a reported UUID' ); +$packet = read_file($packet_file); +unlike( $packet, qr{}, + 'a UUID-less s390x guest still omits the shared machine serial' ); +like( $packet, qr{3931-a01-unknown-525400000002}, + 'a UUID-less s390x guest retains the MAC-based fallback identity' ); + unlink($response_file); is( run_script( $callback_script, \%environment, "restart (eth0)" ), 0, 'callback accepts a restart response' ); diff --git a/xCAT-test/unit/lookup_netboot_arch.t b/xCAT-test/unit/lookup_netboot_arch.t index 00d22cc15..d0491991b 100644 --- a/xCAT-test/unit/lookup_netboot_arch.t +++ b/xCAT-test/unit/lookup_netboot_arch.t @@ -57,13 +57,17 @@ is( xCAT::ProfiledNodeUtils::cal_netboot( $rule_table, [ 'aarch64', 'rhels', '9' # Schema descriptions # --------------------------------------------------------------------------- SKIP: { - skip 'xCAT::Schema is not loadable here', 3 + skip 'xCAT::Schema is not loadable here', 5 unless eval { require lib; lib->import( repo_path('perl-xCAT') ); require xCAT::Schema; 1 }; like( $xCAT::Schema::tabspec{nodetype}{descriptions}{arch}, qr/\briscv64\b/, 'nodetype.arch documents riscv64 as a valid value' ); + like( $xCAT::Schema::tabspec{nodetype}{descriptions}{arch}, qr/\bs390x\b/, + 'nodetype.arch documents s390x as a valid value' ); like( $xCAT::Schema::tabspec{osimage}{descriptions}{osarch}, qr/\briscv64\b/, 'osimage.osarch documents riscv64 as a valid value' ); + like( $xCAT::Schema::tabspec{osimage}{descriptions}{osarch}, qr/\bs390x\b/, + 'osimage.osarch documents s390x as a valid value' ); like( $xCAT::Schema::tabspec{noderes}{descriptions}{netboot}, qr/riscv64\s+>=el10\s+grub2,grub2-http,grub2-tftp/, 'noderes.netboot documents the riscv64 grub2 methods' ); } From f7e3bd4a96d4c64592b8361482cb043cabacba91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 14:43:18 -0300 Subject: [PATCH 15/65] fix(genesis): keep s390x metadata composable --- .../conf/machine/xcat-genesis-s390x.conf | 2 -- ...igure-disable-s390x-crcvx-by-default.patch | 26 +++++++++++++++++++ .../recipes-core/zlib/zlib_%.bbappend | 6 ++--- .../images/xcat-genesis-extension-smoke.bb | 4 +-- 4 files changed, 30 insertions(+), 8 deletions(-) create mode 100644 xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/zlib/files/0001-configure-disable-s390x-crcvx-by-default.patch diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/xcat-genesis-s390x.conf b/xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/xcat-genesis-s390x.conf index 4e8b7b9ed..9524b1d3a 100644 --- a/xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/xcat-genesis-s390x.conf +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/xcat-genesis-s390x.conf @@ -10,8 +10,6 @@ require conf/machine/include/qemu.inc ARCH:s390x = "s390" QEMU_TARGETS:append = " s390x" -MACHINEOVERRIDES =. "qemus390x:" - KERNEL_IMAGETYPE = "bzImage" SERIAL_CONSOLES = "115200;ttysclp0" XCAT_GENESIS_CONSOLE_TTY = "ttysclp0" diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/zlib/files/0001-configure-disable-s390x-crcvx-by-default.patch b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/zlib/files/0001-configure-disable-s390x-crcvx-by-default.patch new file mode 100644 index 000000000..aeaa4f851 --- /dev/null +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/zlib/files/0001-configure-disable-s390x-crcvx-by-default.patch @@ -0,0 +1,26 @@ +From: xCAT Project +Subject: [PATCH] configure: disable s390x CRC vector code by default + +The Genesis s390x baseline is z10, which lacks the vector facility. + +Upstream-Status: Inappropriate [xCAT Genesis targets z10] +Signed-off-by: xCAT Project +--- + configure | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/configure b/configure +index 08b9e98..1ae8a98 100755 +--- a/configure ++++ b/configure +@@ -95,7 +95,7 @@ memory=0 + undefined=0 + insecure=0 + unknown=0 +-enable_crcvx=1 ++enable_crcvx=0 + old_cc="$CC" + old_cflags="$CFLAGS" + OBJC='$(OBJZ) $(OBJG)' +-- +2.51.0 diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/zlib/zlib_%.bbappend b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/zlib/zlib_%.bbappend index b97f6034a..2226ee79f 100644 --- a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/zlib/zlib_%.bbappend +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/zlib/zlib_%.bbappend @@ -1,3 +1,3 @@ -do_configure:s390x() { - LDCONFIG=true ${S}/configure --prefix=${prefix} --shared --libdir=${libdir} --uname=GNU --disable-crcvx -} +FILESEXTRAPATHS:prepend := "${THISDIR}/files:" + +SRC_URI:append:s390x = " file://0001-configure-disable-s390x-crcvx-by-default.patch" diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-extensions/images/xcat-genesis-extension-smoke.bb b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-extensions/images/xcat-genesis-extension-smoke.bb index c6a3d7aed..b1f92c2ff 100644 --- a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-extensions/images/xcat-genesis-extension-smoke.bb +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-extensions/images/xcat-genesis-extension-smoke.bb @@ -7,7 +7,5 @@ inherit xcat-genesis-extension IMAGE_INSTALL = "xcat-genesis-extension-smoke-payload" XCAT_GENESIS_EXTENSION_NAME = "xcat-smoke" -XCAT_GENESIS_EXTENSION_ARCHITECTURE = "x86_64" +XCAT_GENESIS_EXTENSION_ARCHITECTURE = "${XCAT_GENESIS_ARCHITECTURE}" XCAT_GENESIS_EXTENSION_CAPABILITIES = '["diagnostic.smoke"]' - -COMPATIBLE_HOST = "x86_64.*-linux" From cfbbdb283b4f3b1a4c0345254138c3ccc7861b23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 14:43:27 -0300 Subject: [PATCH 16/65] test(genesis): validate s390x build metadata --- .github/workflows/xcat_test.yml | 2 +- xCAT-test/unit/genesis_openembedded.t | 101 ------------------ .../unit/genesis_openembedded_activation.t | 4 +- 3 files changed, 3 insertions(+), 104 deletions(-) diff --git a/.github/workflows/xcat_test.yml b/.github/workflows/xcat_test.yml index 6510fee54..0fe2cd911 100644 --- a/.github/workflows/xcat_test.yml +++ b/.github/workflows/xcat_test.yml @@ -55,4 +55,4 @@ jobs: 'bitbake -n xcat-genesis-image xcat-genesis-extension-smoke' export KAS_BUILD_DIR="${RUNNER_TEMP}/kas-build-s390x" kas shell xCAT-genesis-builder/oe/kas/s390x.yml -c \ - 'bitbake -n xcat-genesis-image' + 'bitbake -n xcat-genesis-image xcat-genesis-extension-smoke' diff --git a/xCAT-test/unit/genesis_openembedded.t b/xCAT-test/unit/genesis_openembedded.t index c18bc76a4..d7d502e2f 100644 --- a/xCAT-test/unit/genesis_openembedded.t +++ b/xCAT-test/unit/genesis_openembedded.t @@ -262,41 +262,6 @@ like( $riscv64_machine, qr/^QB_CPU = "-cpu rv64"$/m, like( $riscv64_machine, qr/^XCAT_GENESIS_CONSOLE_TTY = "ttyS0"$/m, 'riscv64 status console uses its 8250 terminal' ); -my $s390x_machine = read_file( - 'xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/xcat-genesis-s390x.conf' -); -like( $s390x_machine, qr/^DEFAULTTUNE = "s390x"$/m, - 's390x uses the z10 baseline tune' ); -like( $s390x_machine, qr/^ARCH:s390x = "s390"$/m, - 's390x maps to the Linux s390 source architecture' ); -like( $s390x_machine, qr/^XCAT_GENESIS_ARCHITECTURE = "s390x"$/m, - 's390x exports its canonical extension identity' ); -like( $s390x_machine, qr/^QEMU_TARGETS:append = " s390x"$/m, - 's390x cross builds have a user-mode emulator' ); -like( $s390x_machine, qr/^QB_MACHINE = "-machine s390-ccw-virtio"$/m, - 's390x QEMU target uses the CCW virtual machine' ); -like( $s390x_machine, qr/^QB_CPU = "-cpu z10EC-base,/m, - 's390x QEMU target enforces its oldest CPU generation' ); -like( $s390x_machine, qr/^QB_MEM = "-m 4096"$/m, - 's390x QEMU target has room for the initramfs' ); -like( $s390x_machine, qr/^XCAT_GENESIS_CONSOLE_TTY = "ttysclp0"$/m, - 's390x status console uses the SCLP terminal' ); -like( $s390x_machine, qr/^QB_KERNEL_CMDLINE_APPEND = "console=ttysclp0"$/m, - 's390x QEMU target selects the same SCLP terminal' ); - -my $s390x_tune = read_file( - 'xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/include/s390x/tune-s390x.inc' -); -like( $s390x_tune, qr/^TUNE_ARCH = "s390x"$/m, - 's390x tune selects the 64-bit architecture' ); -like( $s390x_tune, - qr/^TUNE_FEATURES:tune-s390x = "m64 bigendian z10"$/m, - 's390x tune uses the z10 CPU floor' ); -like( $s390x_tune, qr/^BASE_LIB:tune-s390x = "lib64"$/m, - 's390x tune uses the 64-bit library directory' ); -like( $s390x_tune, qr/^BASELIB:libc-glibc:s390x = "lib64"$/m, - 's390x glibc uses its 64-bit library directory' ); - for my $machine_console ( [ x86_64 => $machine ], [ x86 => $x86_machine ], @@ -305,7 +270,6 @@ for my $machine_console ( [ armv7hf => $armv7hf_machine ], [ aarch64 => $aarch64_machine ], [ riscv64 => $riscv64_machine ], - [ s390x => $s390x_machine ], ) { unlike( $machine_console->[1], @@ -472,19 +436,6 @@ like( $kernel_append, qr/file:\/\/xcat-genesis-riscv64\.cfg/, like( $kernel_append, qr/^SRCREV_machine:xcat-genesis-riscv64 = "b1ba5428513b52c2bd6acfd3ad0a910f699bc395"$/m, 'riscv64 kernel revision is pinned' ); -like( $kernel_append, qr/file:\/\/xcat-genesis-s390x\.cfg/, - 's390x kernel uses the Genesis hardware fragment' ); -like( $kernel_append, qr/file:\/\/0001-s390-use-stable-generator-name\.patch/, - 's390x kernel source export omits the build directory' ); -like( $kernel_append, - qr/^SRCREV_machine:xcat-genesis-s390x = "b1ba5428513b52c2bd6acfd3ad0a910f699bc395"$/m, - 's390x kernel revision is pinned' ); -like( $kernel_append, - qr/^KCONFIG_MODE:xcat-genesis-s390x = "--alldefconfig"$/m, - 's390x kernel retains architecture defaults' ); -like( $kernel_append, - qr/^KERNEL_FEATURES:remove:xcat-genesis-s390x = "features\/drm-bochs\/drm-bochs\.scc"$/m, - 's390x kernel excludes the QEMU PC graphics fragment' ); like( $kernel_append, qr/file:\/\/xcat-genesis-powerpc64\.cfg/, '64-bit Power kernels share the Genesis hardware fragment' ); like( $kernel_append, @@ -525,50 +476,6 @@ for my $symbol ( "x86_64 kernel enables $symbol" ); } -my $s390x_kernel_config = read_file( - 'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-s390x.cfg' -); -for my $symbol ( - qw( - CONFIG_MARCH_Z10 CONFIG_TUNE_Z10 CONFIG_IPV6 CONFIG_VIRTIO - CONFIG_VIRTIO_BLK CONFIG_VIRTIO_NET CONFIG_HW_RANDOM_VIRTIO - CONFIG_CCWGROUP CONFIG_QDIO CONFIG_QETH CONFIG_QETH_L2 CONFIG_QETH_L3 - CONFIG_DASD CONFIG_DASD_ECKD CONFIG_DASD_FBA CONFIG_ZFCP - CONFIG_SCLP_CONSOLE CONFIG_SCLP_VT220_CONSOLE CONFIG_TN3270_CONSOLE - ) - ) -{ - like( $s390x_kernel_config, qr/^\Q$symbol\E=[ym]$/m, - "s390x kernel enables $symbol" ); -} - -my $kexec_append = read_file( - 'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/kexec/kexec-tools_%.bbappend' -); -like( $kexec_append, qr/^COMPATIBLE_HOST:s390x = "s390x\.\*-linux"$/m, - 'kexec-tools accepts the s390x target' ); - -my $zlib_append = read_file( - 'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/zlib/zlib_%.bbappend' -); -like( $zlib_append, qr/^do_configure:s390x\(\).*--disable-crcvx/ms, - 'zlib keeps the s390x image compatible with z10' ); - -my $openssl_append = read_file( - 'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/openssl/openssl_%.bbappend' -); -like( $openssl_append, - qr/file:\/\/50-xcat-s390x\.conf/, - 'OpenSSL receives the OpenEmbedded s390x target alias' ); -like( $openssl_append, - qr/install -m 0644 \$\{UNPACKDIR\}\/50-xcat-s390x\.conf \$\{S\}\/Configurations\//, - 'OpenSSL installs the target definition before configuration' ); -my $openssl_config = read_file( - 'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/openssl/files/50-xcat-s390x.conf' -); -like( $openssl_config, qr/^ "linux-s390x" => \{$/m, - 'OpenSSL maps the s390x target to its 64-bit configuration' ); - my $x86_kernel_config = read_file( 'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-x86.cfg' ); @@ -965,14 +872,6 @@ like( $extension_class, qr/Restricted Genesis extensions must set LICENSE_FLAGS/ like( $extension_class, qr/"sha256":/, 'extension manifests record the image digest' ); -my $smoke_extension = read_file( - 'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-extensions/images/xcat-genesis-extension-smoke.bb' -); -like( $smoke_extension, qr/^XCAT_GENESIS_EXTENSION_NAME = "xcat-smoke"$/m, - 'open smoke extension exercises the build path' ); -like( $smoke_extension, qr/^COMPATIBLE_HOST = "x86_64\.\*-linux"$/m, - 'smoke extension is restricted to its declared architecture' ); - my $preset = read_file( 'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/00-xcat-genesis.preset' ); diff --git a/xCAT-test/unit/genesis_openembedded_activation.t b/xCAT-test/unit/genesis_openembedded_activation.t index 774f73049..2aaee8baa 100644 --- a/xCAT-test/unit/genesis_openembedded_activation.t +++ b/xCAT-test/unit/genesis_openembedded_activation.t @@ -114,8 +114,8 @@ like( ); like( $mknb_pod, - qr/x86.*x86_64.*ppc64.*ppc64le.*armv7hf.*aarch64.*riscv64.*s390x/s, - 'the mknb man page lists every exact OpenEmbedded architecture', + qr/\bs390x\b/, + 'the mknb man page lists the s390x target', ); unlike( $mknb_pod, From 582a3611453d674558bfa88c4b5d40eedfb570fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 14:43:41 -0300 Subject: [PATCH 17/65] docs(genesis): define s390x validation limits --- .../guides/code/dhcp_backend_validation_matrix.rst | 6 ++++++ .../developers/guides/code/genesis_openembedded_plan.rst | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/docs/source/developers/guides/code/dhcp_backend_validation_matrix.rst b/docs/source/developers/guides/code/dhcp_backend_validation_matrix.rst index 364ff63a2..5e80d2876 100644 --- a/docs/source/developers/guides/code/dhcp_backend_validation_matrix.rst +++ b/docs/source/developers/guides/code/dhcp_backend_validation_matrix.rst @@ -136,6 +136,12 @@ The following matrix is the default live validation gate for DHCP backend work. - ``makedhcp -n``; backend parser; architecture ``0x001f``; DHCP options 209 and 210; TFTP fetch of the generated ``pxelinux.cfg``-style configuration, Genesis kernel, and initramfs + * - IBM Z DPM + - ``s390x`` + - ``ISC`` and ``Kea`` + - configuration handoff only + - backend parser; architecture ``0x0020``; generated network configuration + offered as the DHCP boot filename; physical boot remains unvalidated Kea Boot and Reservation Regression Matrix ------------------------------------------ diff --git a/docs/source/developers/guides/code/genesis_openembedded_plan.rst b/docs/source/developers/guides/code/genesis_openembedded_plan.rst index d2afa4914..60c518a46 100644 --- a/docs/source/developers/guides/code/genesis_openembedded_plan.rst +++ b/docs/source/developers/guides/code/genesis_openembedded_plan.rst @@ -99,6 +99,8 @@ and z/VM Genesis networking therefore remain unvalidated. Genesis does not use the shared CEC serial as an s390x node identifier. QEMU validation covers network IPL, DHCP options 209 and 210, TFTP, and the network-specific ``pxelinux.cfg``-style configuration written by ``mknb``. +For DPM clients, DHCP architecture 32 receives a DPM-formatted configuration +as its boot filename. This path has parser coverage but no physical validation. Existing xCAT s390x operating-system provisioning remains unchanged. xCAT protocol @@ -283,6 +285,8 @@ References `_ * `NetworkManager dispatcher interface `_ +* `IBM Z DPM network boot server configuration + `_ * `NetworkManager initrd generator `_ * `systemd system extensions From 77ada4131997baa00604815e7440ca35567c731d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 14:55:21 -0300 Subject: [PATCH 18/65] refactor(genesis): keep s390x Perl policy neutral --- xCAT-server/lib/xcat/plugins/mknb.pm | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/xCAT-server/lib/xcat/plugins/mknb.pm b/xCAT-server/lib/xcat/plugins/mknb.pm index 41cce0136..88c489279 100644 --- a/xCAT-server/lib/xcat/plugins/mknb.pm +++ b/xCAT-server/lib/xcat/plugins/mknb.pm @@ -305,9 +305,13 @@ sub _remove_openembedded_genesis { my $config_directory = "$tftpdir/pxelinux.cfg/s390x"; if (opendir(my $config_stream, $config_directory)) { foreach my $name (readdir $config_stream) { - next if $name eq '.' || $name eq '..'; + if ($name =~ m{\A[.][.]?\z}xms) { + next; + } my $path = "$config_directory/$name"; - push @artifacts, $path if _is_generated_s390x_config($path); + if (_is_generated_s390x_config($path)) { + push @artifacts, $path; + } } closedir $config_stream; } @@ -741,7 +745,8 @@ sub process_request { } elsif (exists $GRUB2_DISCOVERY_ARCHES{$arch}) { mkpath("$tftpdir/boot/grub2"); chmod(0755, "$tftpdir/boot/grub2"); - } elsif ($arch eq 's390x') { + } + if ($arch eq 's390x') { mkpath "$tftpdir/pxelinux.cfg/s390x"; chmod 0755, "$tftpdir/pxelinux.cfg"; chmod 0755, "$tftpdir/pxelinux.cfg/s390x"; @@ -761,7 +766,9 @@ sub process_request { "$tftpdir/pxelinux.cfg/s390x/$net", "$tftpdir/pxelinux.cfg/s390x/$net.dpm", ) { - unlink $path if _is_generated_s390x_config($path); + if (_is_generated_s390x_config($path)) { + unlink $path; + } } } next; From a1e994899799b63502b61ee5f233ecedba4f6953 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 15:31:33 -0300 Subject: [PATCH 19/65] fix(genesis): limit s390x boot to validated path --- perl-xCAT/xCAT/DHCP/BootPolicy.pm | 12 --------- .../files/xcat-genesis-console/src/state.c | 6 +---- xCAT-server/lib/xcat/plugins/dhcp.pm | 3 --- xCAT-server/lib/xcat/plugins/mknb.pm | 26 +++++-------------- 4 files changed, 7 insertions(+), 40 deletions(-) diff --git a/perl-xCAT/xCAT/DHCP/BootPolicy.pm b/perl-xCAT/xCAT/DHCP/BootPolicy.pm index 7a672ac86..c4c348da1 100644 --- a/perl-xCAT/xCAT/DHCP/BootPolicy.pm +++ b/perl-xCAT/xCAT/DHCP/BootPolicy.pm @@ -134,14 +134,6 @@ sub kea_s390x_network_classes { ], }; } - if ( $opts{dpm_config_present} ) { - push @classes, { - name => "xcat-s390x-dpm-$safe_network", - test => 'option[93].hex == 0x0020', - additional_only => 1, - 'boot-file-name' => "pxelinux.cfg/s390x/$network_id.dpm", - }; - } return \@classes; } @@ -197,10 +189,6 @@ sub isc_client_architecture_lines { " option path-prefix = \"pxelinux.cfg/s390x/\";\n", " option conf-file = \"${net}_${maskbits}\";\n", ) : ()), - ($opts{s390x_dpm_config_present} ? ( - " } else if option client-architecture = 00:20 { #IBM Z DPM\n ", - " filename \"pxelinux.cfg/s390x/${net}_${maskbits}.dpm\";\n", - ) : ()), " } else if option client-architecture = 00:0e { #OPAL-v3\n ", " option conf-file = \"http://$tftp$portsuffix/tftpboot/pxelinux.cfg/p/${net}_${maskbits}\";\n", " } else if substring (option vendor-class-identifier,0,11) = \"onie_vendor\" { #for onie on cumulus switch\n", diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/state.c b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/state.c index 776cb6296..26d80e34e 100644 --- a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/state.c +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/state.c @@ -345,11 +345,7 @@ void xcat_load_console_state(struct console_state *state) { xcat_set_text(state->architecture, sizeof(state->architecture), "unknown"); xcat_set_text(state->kernel, sizeof(state->kernel), "unknown"); } - snprintf(path, sizeof(path), "%s/sysinfo", proc_root); - s390_system = strcmp(state->architecture, "s390x") == 0 || - access(path, F_OK) == 0; - if (s390_system) - xcat_set_text(state->architecture, sizeof(state->architecture), "s390x"); + s390_system = strcmp(state->architecture, "s390x") == 0; s390_ccw_boot = s390_system && xcat_cmdline_value(cmdline_text, "xcat.bootloader", value, diff --git a/xCAT-server/lib/xcat/plugins/dhcp.pm b/xCAT-server/lib/xcat/plugins/dhcp.pm index fdbf36eca..235f43032 100644 --- a/xCAT-server/lib/xcat/plugins/dhcp.pm +++ b/xCAT-server/lib/xcat/plugins/dhcp.pm @@ -3371,7 +3371,6 @@ sub kea_subnet4_intent net => $net, prefix => $prefix, qemu_config_present => -f "$tftpdir/pxelinux.cfg/s390x/${net}_${prefix}", - dpm_config_present => -f "$tftpdir/pxelinux.cfg/s390x/${net}_${prefix}.dpm", ) }; if (@client_classes) { @@ -4538,8 +4537,6 @@ sub addnet prefix => $maskbits, s390x_qemu_config_present => -f "$tftpdir/pxelinux.cfg/s390x/${net}_${maskbits}", - s390x_dpm_config_present => - -f "$tftpdir/pxelinux.cfg/s390x/${net}_${maskbits}.dpm", ) }; if ($range) { diff --git a/xCAT-server/lib/xcat/plugins/mknb.pm b/xCAT-server/lib/xcat/plugins/mknb.pm index 88c489279..3c1e8ba86 100644 --- a/xCAT-server/lib/xcat/plugins/mknb.pm +++ b/xCAT-server/lib/xcat/plugins/mknb.pm @@ -762,13 +762,9 @@ sub process_request { if ($arch =~ /ppc/ and -r "$tftpdir/pxelinux.cfg/p/$net") { unlink("$tftpdir/pxelinux.cfg/p/$net"); } elsif ($arch eq 's390x') { - foreach my $path ( - "$tftpdir/pxelinux.cfg/s390x/$net", - "$tftpdir/pxelinux.cfg/s390x/$net.dpm", - ) { - if (_is_generated_s390x_config($path)) { - unlink $path; - } + my $path = "$tftpdir/pxelinux.cfg/s390x/$net"; + if (_is_generated_s390x_config($path)) { + unlink $path; } } next; @@ -933,24 +929,14 @@ sub _write_s390x_discovery_config { my $error = _write_s390x_config($qemu_path, $qemu_config); return (undef, $error) if $error; - my $dpm_path = "$qemu_path.dpm"; - my $dpm_config = "# pxelinux.cfg xCAT Genesis s390x\n" - . "DEFAULT xCAT\n" - . "label xCAT\n" - . " kernel=xcat/genesis.kernel.s390x\n" - . " initrd=$initrd\n" - . " append=$cmdline\n"; - $error = _write_s390x_config($dpm_path, $dpm_config); - if ($error) { - unlink $qemu_path; - return (undef, $error); - } - return ($qemu_path, undef); } sub _write_s390x_config { my ($path, $contents) = @_; + if (-f $path && !_is_generated_s390x_config($path)) { + return "Refusing to replace unmanaged s390x configuration: $path"; + } my $config; if (!open $config, '>', $path) { my $open_error = $OS_ERROR; From 6711a5ce9ecb76c56ec7b23f4598b6240cb23a06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 15:31:33 -0300 Subject: [PATCH 20/65] test(genesis): tighten s390x coverage --- .../integration/dhcp_isc_config_validation.t | 1 - .../integration/dhcp_kea_config_validation.t | 7 ------ xCAT-test/unit/dhcp_boot_policy.t | 24 ------------------- xCAT-test/unit/dhcp_isc_client_arch.t | 8 +++---- xCAT-test/unit/dhcp_kea_plugin_intent.t | 10 +------- xCAT-test/unit/genesis_openembedded.t | 6 +++++ .../unit/genesis_openembedded_activation.t | 4 ++-- xCAT-test/unit/genesis_openembedded_console.t | 22 +++++++++++++---- .../unit/genesis_openembedded_discovery.t | 6 ++--- xCAT-test/unit/mknb_exported_genesis.t | 16 ++----------- xCAT-test/unit/mknb_xcatd_address.t | 23 +++++++----------- 11 files changed, 45 insertions(+), 82 deletions(-) diff --git a/xCAT-test/integration/dhcp_isc_config_validation.t b/xCAT-test/integration/dhcp_isc_config_validation.t index 792739f48..7a80e209a 100644 --- a/xCAT-test/integration/dhcp_isc_config_validation.t +++ b/xCAT-test/integration/dhcp_isc_config_validation.t @@ -38,7 +38,6 @@ push @config, @{ xCAT::DHCP::BootPolicy->isc_client_architecture_lines( net => '192.0.2.0', prefix => 24, s390x_qemu_config_present => 1, - s390x_dpm_config_present => 1, ) }, "}\n"; my $configuration_root = -d '/etc/dhcp' ? '/etc/dhcp' : '/etc'; diff --git a/xCAT-test/integration/dhcp_kea_config_validation.t b/xCAT-test/integration/dhcp_kea_config_validation.t index acc7e5d2d..125189705 100644 --- a/xCAT-test/integration/dhcp_kea_config_validation.t +++ b/xCAT-test/integration/dhcp_kea_config_validation.t @@ -51,12 +51,6 @@ my $json = $backend->render_dhcp4_config( { name => 'path-prefix', data => 'pxelinux.cfg/s390x/' }, ], }, - { - name => 'xcat-s390x-dpm-192.168.122.0_24', - test => 'option[93].hex == 0x0020', - additional_only => JSON::true, - 'boot-file-name' => 'pxelinux.cfg/s390x/192.168.122.0_24.dpm', - }, { name => 'xcat-uefi-x64', test => "(option[93].hex == 0x0007 or option[93].hex == 0x0009 or option[93].hex == 0x0010) and not ((option[77].exists and (option[77].text == 'xNBA' or option[77].hex == 0x784e4241 or substring(option[77].hex,1,4) == 'xNBA')))", @@ -84,7 +78,6 @@ my $json = $backend->render_dhcp4_config( additional_client_classes => [ 'xcat-opal-v3-192.168.122.0-24', 'xcat-s390x-qemu-192.168.122.0_24', - 'xcat-s390x-dpm-192.168.122.0_24', ], option_data => [ { name => 'routers', data => '192.168.122.1' }, diff --git a/xCAT-test/unit/dhcp_boot_policy.t b/xCAT-test/unit/dhcp_boot_policy.t index 94e8477ff..a5d1e6610 100644 --- a/xCAT-test/unit/dhcp_boot_policy.t +++ b/xCAT-test/unit/dhcp_boot_policy.t @@ -221,7 +221,6 @@ my $s390x = xCAT::DHCP::BootPolicy->kea_s390x_network_classes( net => '10.0.0.0', prefix => 24, qemu_config_present => 1, - dpm_config_present => 1, ); is_deeply( $s390x, @@ -243,12 +242,6 @@ is_deeply( }, ], }, - { - name => 'xcat-s390x-dpm-10.0.0.0_24', - test => 'option[93].hex == 0x0020', - additional_only => 1, - 'boot-file-name' => 'pxelinux.cfg/s390x/10.0.0.0_24.dpm', - }, ], 's390x firmware receives its supported network configuration method', ); @@ -259,23 +252,6 @@ is_deeply( [], 's390x classes require a generated network configuration', ); -is_deeply( - xCAT::DHCP::BootPolicy->kea_s390x_network_classes( - net => '10.0.0.0', - prefix => 24, - dpm_config_present => 1, - ), - [ - { - name => 'xcat-s390x-dpm-10.0.0.0_24', - test => 'option[93].hex == 0x0020', - additional_only => 1, - 'boot-file-name' => 'pxelinux.cfg/s390x/10.0.0.0_24.dpm', - }, - ], - 's390x policies follow the configurations available on the network', -); - my @legacy_isc_config = ( "#xCAT generated dhcp configuration\n", "\n", diff --git a/xCAT-test/unit/dhcp_isc_client_arch.t b/xCAT-test/unit/dhcp_isc_client_arch.t index f87bf4294..d3b2fd85b 100644 --- a/xCAT-test/unit/dhcp_isc_client_arch.t +++ b/xCAT-test/unit/dhcp_isc_client_arch.t @@ -15,7 +15,6 @@ my $rendered = join '', @{ xCAT::DHCP::BootPolicy->isc_client_architecture_lines net => '192.0.2.0', prefix => 24, s390x_qemu_config_present => 1, - s390x_dpm_config_present => 1, ) }; like( @@ -40,8 +39,8 @@ like( ); like( $rendered, - qr/client-architecture = 00:1f \{ #QEMU s390x\n\s+option path-prefix = "pxelinux\.cfg\/s390x\/";\n\s+option conf-file = "192\.0\.2\.0_24";\n\s+\} else if option client-architecture = 00:20 \{ #IBM Z DPM\n\s+filename "pxelinux\.cfg\/s390x\/192\.0\.2\.0_24\.dpm";/, - 's390x firmware receives its supported network configuration method', + qr/client-architecture = 00:1f \{ #QEMU s390x\n\s+option path-prefix = "pxelinux\.cfg\/s390x\/";\n\s+option conf-file = "192\.0\.2\.0_24";/, + 'QEMU s390x receives its network configuration', ); my @riscv_ids = $rendered =~ /client-architecture = (00:1[9a-e])/g; @@ -59,13 +58,14 @@ my $fallback_pos = index($rendered, 'substring(filename,0,1) = null'); cmp_ok($aarch64_pos, '<', $tftp_pos, 'riscv64 follows the aarch64 branch'); cmp_ok($tftp_pos, '<', $http_pos, 'the TFTP branch precedes the HTTP branch'); +cmp_ok($http_pos, '<', $opal_pos, 'the HTTP branch precedes the OPAL branch'); cmp_ok($http_pos, '<', $fallback_pos, 'the HTTP branch is reachable before the fallback'); like($rendered, qr/filename "\/yaboot";\n\s*\}\n\z/, 'the policy ends with the existing yaboot fallback'); my $without_s390x = join '', @{ xCAT::DHCP::BootPolicy->isc_client_architecture_lines( next_server => '192.0.2.10', net => '192.0.2.0', prefix => 24, ) }; -unlike($without_s390x, qr/client-architecture = 00:(?:1f|20)/, +unlike($without_s390x, qr/client-architecture = 00:1f/, 'ISC does not advertise s390x without a generated network configuration'); done_testing(); diff --git a/xCAT-test/unit/dhcp_kea_plugin_intent.t b/xCAT-test/unit/dhcp_kea_plugin_intent.t index f1285ca90..530690d2d 100644 --- a/xCAT-test/unit/dhcp_kea_plugin_intent.t +++ b/xCAT-test/unit/dhcp_kea_plugin_intent.t @@ -141,10 +141,6 @@ make_path($s390x_config_dir); open(my $s390x_config, '>', "$s390x_config_dir/10.0.0.0_24") or die "Unable to create s390x test configuration: $!"; close($s390x_config); -open($s390x_config, '>', "$s390x_config_dir/10.0.0.0_24.dpm") - or die "Unable to create s390x DPM test configuration: $!"; -close($s390x_config); - { no warnings 'redefine'; local *xCAT::NetworkUtils::thishostisnot = sub { return 0; }; @@ -154,13 +150,9 @@ close($s390x_config); ); my %classes = map { $_->{name} => $_ } @{ $subnet->{client_classes} }; ok($classes{'xcat-s390x-qemu-10.0.0.0_24'}, 'the Kea subnet includes QEMU s390x boot policy'); - ok($classes{'xcat-s390x-dpm-10.0.0.0_24'}, 'the Kea subnet includes IBM Z DPM boot policy'); is_deeply( [ grep { /^xcat-s390x-/ } @{ $subnet->{additional_client_classes} } ], - [ - 'xcat-s390x-qemu-10.0.0.0_24', - 'xcat-s390x-dpm-10.0.0.0_24', - ], + ['xcat-s390x-qemu-10.0.0.0_24'], 'the s390x policy is evaluated only for its subnet', ); } diff --git a/xCAT-test/unit/genesis_openembedded.t b/xCAT-test/unit/genesis_openembedded.t index d7d502e2f..d595725df 100644 --- a/xCAT-test/unit/genesis_openembedded.t +++ b/xCAT-test/unit/genesis_openembedded.t @@ -872,6 +872,12 @@ like( $extension_class, qr/Restricted Genesis extensions must set LICENSE_FLAGS/ like( $extension_class, qr/"sha256":/, 'extension manifests record the image digest' ); +my $smoke_extension = read_file( + 'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-extensions/images/xcat-genesis-extension-smoke.bb' +); +like( $smoke_extension, qr/^XCAT_GENESIS_EXTENSION_NAME = "xcat-smoke"$/m, + 'open smoke extension exercises the build path' ); + my $preset = read_file( 'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/00-xcat-genesis.preset' ); diff --git a/xCAT-test/unit/genesis_openembedded_activation.t b/xCAT-test/unit/genesis_openembedded_activation.t index 2aaee8baa..774f73049 100644 --- a/xCAT-test/unit/genesis_openembedded_activation.t +++ b/xCAT-test/unit/genesis_openembedded_activation.t @@ -114,8 +114,8 @@ like( ); like( $mknb_pod, - qr/\bs390x\b/, - 'the mknb man page lists the s390x target', + qr/x86.*x86_64.*ppc64.*ppc64le.*armv7hf.*aarch64.*riscv64.*s390x/s, + 'the mknb man page lists every exact OpenEmbedded architecture', ); unlike( $mknb_pod, diff --git a/xCAT-test/unit/genesis_openembedded_console.t b/xCAT-test/unit/genesis_openembedded_console.t index de015dbfb..ae639bcf8 100644 --- a/xCAT-test/unit/genesis_openembedded_console.t +++ b/xCAT-test/unit/genesis_openembedded_console.t @@ -83,6 +83,19 @@ write_file( #include "console.h" #include +#include +#include +#include + +int xcat_test_uname(struct utsname *name) { + const char *architecture = getenv("XCAT_TEST_ARCH"); + + memset(name, 0, sizeof(*name)); + snprintf(name->machine, sizeof(name->machine), "%s", + architecture != NULL ? architecture : "x86_64"); + snprintf(name->release, sizeof(name->release), "test-kernel"); + return 0; +} int main(void) { struct console_state state; @@ -97,6 +110,7 @@ C is( system( $compiler, '-D_POSIX_C_SOURCE=200809L', + '-Duname=xcat_test_uname', '-std=c17', '-Wall', '-Wextra', '-Wpedantic', '-Werror', '-I', $source_dir, $identity_test_source, File::Spec->catfile( $source_dir, 'state.c' ), @@ -191,6 +205,7 @@ my %environment = ( XCAT_PROC_ROOT => $proc_root, XCAT_EXTENSION_DIR => $extensions, XCAT_PROVIDER_DIR => $providers, + XCAT_TEST_ARCH => 'x86_64', ); sub run_console { @@ -245,10 +260,7 @@ LPAR UUID: 93724168-fda3-429b-8b28-a5d245dcb3ff VM00 UUID: 82038f2a-1344-aaf7-1a85-2a7250be2076 SYSINFO write_file( $cmdline, "xcatd=192.0.2.10:3001\n" ); -( $status, $output ) = run_console(); -is( $status, 0, 'plain console accepts IBM Z identity' ); -like( $output, qr/^serial: not reported$/m, - 'console does not present the shared IBM Z machine serial as guest identity' ); +$environment{XCAT_TEST_ARCH} = 's390x'; my ( $probe_status, $probe_output ) = run_identity_probe(); is( $probe_status, 0, 'console loads IBM Z diagnostics' ); is( @@ -273,6 +285,8 @@ SYSINFO like( $probe_output, qr/^uuid=93724168-fda3-429b-8b28-a5d245dcb3ff$/m, 'console uses the LPAR UUID when no guest UUID is available' ); +unlink( File::Spec->catfile( $proc_root, 'sysinfo' ) ); +$environment{XCAT_TEST_ARCH} = 'x86_64'; write_file( File::Spec->catfile( $dmi_root, 'product_serial' ), "TEST-SERIAL-001\n" ); write_file( File::Spec->catfile( $dmi_root, 'product_uuid' ), diff --git a/xCAT-test/unit/genesis_openembedded_discovery.t b/xCAT-test/unit/genesis_openembedded_discovery.t index 0913e5259..b68a899d1 100644 --- a/xCAT-test/unit/genesis_openembedded_discovery.t +++ b/xCAT-test/unit/genesis_openembedded_discovery.t @@ -401,7 +401,7 @@ write_file( File::Spec->catfile( $device_tree, 'model' ), write_file( File::Spec->catfile( $device_tree, 'system-id' ), "IBM,02AB123\0" ); write_file( File::Spec->catfile( $proc_root, 'cpuinfo' ), - "processor : 0\ncpu : POWER9\nprocessor : 1\ncpu : POWER9\nplatform : PowerNV\n" ); + "cpu : POWER9\ncpu : POWER9\nplatform : PowerNV\n" ); $environment{XCAT_TEST_ARCH} = 'ppc64le'; $environment{XCAT_TEST_RESPONSE} = 'restart'; is( run_script( $discover_script, \%environment ), 0, @@ -415,8 +415,8 @@ like( $packet, qr{02AB123}, 'Power discovery reports the system serial' ); like( $packet, qr{PowerNV}, 'Power discovery reports the firmware platform' ); -like( $packet, qr{4}, - 'Power discovery keeps the existing processor count' ); +like( $packet, qr{2}, + 'Power discovery counts processor records' ); like( $packet, qr{9009-42a-02ab123-525400000002}, 'Power discovery creates a stable fallback UUID' ); diff --git a/xCAT-test/unit/mknb_exported_genesis.t b/xCAT-test/unit/mknb_exported_genesis.t index f9b0ae860..5f523a9bf 100644 --- a/xCAT-test/unit/mknb_exported_genesis.t +++ b/xCAT-test/unit/mknb_exported_genesis.t @@ -169,19 +169,13 @@ write_file( "$tftpdir/pxelinux.cfg/s390x/192.0.2.0_24", "# pxelinux.cfg xCAT Genesis s390x\nstale config\n", ); -write_file( - "$tftpdir/pxelinux.cfg/s390x/192.0.2.0_24.dpm", - "# pxelinux.cfg xCAT Genesis s390x\nstale DPM config\n", -); write_file("$tftpdir/pxelinux.cfg/s390x/default", "admin fallback\n"); ($removed, $remove_error) = xCAT_plugin::mknb::_remove_openembedded_genesis($tftpdir, 's390x'); is($remove_error, undef, 's390x boot artifacts can be retired cleanly'); -is($removed, 6, 's390x image and generated discovery configurations are retired'); +is($removed, 5, 's390x image and generated discovery configurations are retired'); ok(!-e "$tftpdir/pxelinux.cfg/s390x/192.0.2.0_24", 'retiring s390x removes the generated discovery configuration'); -ok(!-e "$tftpdir/pxelinux.cfg/s390x/192.0.2.0_24.dpm", - 'retiring s390x removes the generated DPM configuration'); is(read_file("$tftpdir/pxelinux.cfg/s390x/default"), "admin fallback\n", 'retiring s390x preserves administrator-owned configurations'); @@ -194,18 +188,12 @@ write_file( "$special_tftpdir/pxelinux.cfg/s390x/192.0.2.0_24", "# pxelinux.cfg xCAT Genesis s390x\nconfiguration\n", ); -write_file( - "$special_tftpdir/pxelinux.cfg/s390x/192.0.2.0_24.dpm", - "# pxelinux.cfg xCAT Genesis s390x\nDPM configuration\n", -); ($removed, $remove_error) = xCAT_plugin::mknb::_remove_openembedded_genesis($special_tftpdir, 's390x'); is($remove_error, undef, 's390x cleanup accepts a TFTP path with shell metacharacters'); -is($removed, 5, 's390x cleanup removes every generated artifact from that path'); +is($removed, 4, 's390x cleanup removes every generated artifact from that path'); ok(!-e "$special_tftpdir/pxelinux.cfg/s390x/192.0.2.0_24", 's390x cleanup does not expand the TFTP path as a glob'); -ok(!-e "$special_tftpdir/pxelinux.cfg/s390x/192.0.2.0_24.dpm", - 's390x cleanup removes DPM configurations from the literal path'); my $failed_kernel = "$tftpdir/xcat/genesis.kernel.aarch64"; make_path($failed_kernel); diff --git a/xCAT-test/unit/mknb_xcatd_address.t b/xCAT-test/unit/mknb_xcatd_address.t index c822d3651..d2c0ecdc5 100644 --- a/xCAT-test/unit/mknb_xcatd_address.t +++ b/xCAT-test/unit/mknb_xcatd_address.t @@ -536,18 +536,6 @@ unlike( qr/192\.168\.149\.100/, 's390x configurations do not use the later floating address', ); -my $s390x_dpm_path = "$s390x_qemu_path.dpm"; -is( - read_config($s390x_dpm_path), - "# pxelinux.cfg xCAT Genesis s390x\n" - . "DEFAULT xCAT\n" - . "label xCAT\n" - . " kernel=xcat/genesis.kernel.s390x\n" - . " initrd=xcat/genesis.fs.s390x.gz\n" - . " append=xcatd=192.168.148.10:3001 xcat.bootloader=s390-ccw console=ttysclp0\n", - 'the DPM configuration uses the IBM Z network-boot syntax', -); - write_text($s390x_qemu_path, "admin network configuration\n"); %xCAT::TableUtils::site_extra = ( dhcpinterfaces => 'eth0,eth1:noboot' ); $xCAT::NetworkUtils::nic_ips = { eth0 => '10.0.0.1', eth1 => '192.168.148.10' }; @@ -561,7 +549,15 @@ is( %xCAT::TableUtils::site_extra = (); $xCAT::NetworkUtils::nic_ips = undef; $responses = run_mknb('s390x'); -generation_succeeded($responses, 's390x configuration is restored after removing :noboot'); +ok( + scalar(grep { ref($_) eq 'HASH' && $_->{error} && "@{$_->{error}}" =~ /Refusing to replace unmanaged/ } @{$responses}), + 's390x refuses to replace an administrator-owned configuration', +); +is(read_config($s390x_qemu_path), "admin network configuration\n", + 'the administrator-owned configuration is unchanged'); +unlink($s390x_qemu_path); +$responses = run_mknb('s390x'); +generation_succeeded($responses, 's390x configuration is restored after removing the unmanaged file'); my $s390x_failure_root = "$tmpdir/tftpboot-s390x-failure"; make_path("$s390x_failure_root/pxelinux.cfg/s390x/192.168.144.0_20"); @@ -599,7 +595,6 @@ $xCAT::NetworkUtils::nic_ips = { eth0 => '10.0.0.1', eth1 => '192.168.148.10' }; $responses = run_mknb('s390x'); generation_succeeded($responses, 's390x configuration generation honors :noboot'); ok(!-e $s390x_qemu_path, 'a :noboot network gets no QEMU s390x configuration'); -ok(!-e $s390x_dpm_path, 'a :noboot network gets no IBM Z DPM configuration'); %xCAT::TableUtils::site_extra = (); $xCAT::NetworkUtils::nic_ips = undef; From bc366dde81f7fb93a540d1e9c8c65e0a414af695 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 15:31:34 -0300 Subject: [PATCH 21/65] docs(genesis): narrow s390x support --- .../guides/code/dhcp_backend_validation_matrix.rst | 7 ------- .../developers/guides/code/genesis_openembedded_plan.rst | 7 +++---- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/docs/source/developers/guides/code/dhcp_backend_validation_matrix.rst b/docs/source/developers/guides/code/dhcp_backend_validation_matrix.rst index 5e80d2876..606d4175a 100644 --- a/docs/source/developers/guides/code/dhcp_backend_validation_matrix.rst +++ b/docs/source/developers/guides/code/dhcp_backend_validation_matrix.rst @@ -136,13 +136,6 @@ The following matrix is the default live validation gate for DHCP backend work. - ``makedhcp -n``; backend parser; architecture ``0x001f``; DHCP options 209 and 210; TFTP fetch of the generated ``pxelinux.cfg``-style configuration, Genesis kernel, and initramfs - * - IBM Z DPM - - ``s390x`` - - ``ISC`` and ``Kea`` - - configuration handoff only - - backend parser; architecture ``0x0020``; generated network configuration - offered as the DHCP boot filename; physical boot remains unvalidated - Kea Boot and Reservation Regression Matrix ------------------------------------------ diff --git a/docs/source/developers/guides/code/genesis_openembedded_plan.rst b/docs/source/developers/guides/code/genesis_openembedded_plan.rst index 60c518a46..40df7b2b3 100644 --- a/docs/source/developers/guides/code/genesis_openembedded_plan.rst +++ b/docs/source/developers/guides/code/genesis_openembedded_plan.rst @@ -99,8 +99,7 @@ and z/VM Genesis networking therefore remain unvalidated. Genesis does not use the shared CEC serial as an s390x node identifier. QEMU validation covers network IPL, DHCP options 209 and 210, TFTP, and the network-specific ``pxelinux.cfg``-style configuration written by ``mknb``. -For DPM clients, DHCP architecture 32 receives a DPM-formatted configuration -as its boot filename. This path has parser coverage but no physical validation. +This release does not advertise a DPM boot configuration. Existing xCAT s390x operating-system provisioning remains unchanged. xCAT protocol @@ -285,8 +284,8 @@ References `_ * `NetworkManager dispatcher interface `_ -* `IBM Z DPM network boot server configuration - `_ +* `QEMU s390x network boot + `_ * `NetworkManager initrd generator `_ * `systemd system extensions From 9d30f127b5171eda25c0d984470f2b2842273238 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 16:20:38 -0300 Subject: [PATCH 22/65] fix(genesis): simplify s390x network IPL --- perl-xCAT/xCAT/DHCP/BootPolicy.pm | 40 ++++++---------------------- xCAT-server/lib/xcat/plugins/dhcp.pm | 13 ++------- xCAT-server/lib/xcat/plugins/mknb.pm | 8 +++--- 3 files changed, 15 insertions(+), 46 deletions(-) diff --git a/perl-xCAT/xCAT/DHCP/BootPolicy.pm b/perl-xCAT/xCAT/DHCP/BootPolicy.pm index c4c348da1..2fb5fe612 100644 --- a/perl-xCAT/xCAT/DHCP/BootPolicy.pm +++ b/perl-xCAT/xCAT/DHCP/BootPolicy.pm @@ -112,43 +112,22 @@ sub kea_s390x_network_classes { my $network_id = "$opts{net}_$opts{prefix}"; my $safe_network = $network_id; - $safe_network =~ s{[^[:alnum:]_.-]}{_}gxms; + $safe_network =~ s{[^A-Za-z0-9_.-]}{_}gxms; - my @classes; - if ( $opts{qemu_config_present} ) { - push @classes, { + return [ + { name => "xcat-s390x-qemu-$safe_network", test => 'option[93].hex == 0x001f', additional_only => 1, 'option-data' => [ { name => 'conf-file', - data => $network_id, - 'always-send' => 1, - }, - { - name => 'path-prefix', - data => 'pxelinux.cfg/s390x/', + data => "pxelinux.cfg/s390x/$network_id", 'always-send' => 1, }, ], - }; - } - return \@classes; -} - -sub ensure_isc_path_prefix_definition { - my ($class, $config) = @_; - - foreach my $line ( @{$config} ) { - if ( $line =~ m{^\s* option \s+ path-prefix \s+ code \s+ 210\b}xms ) { - return 0; - } - } - - my $index = @{$config} && $config->[0] =~ m{^\#xCAT}xms ? 1 : 0; - splice @{$config}, $index, 0, "option path-prefix code 210 = text;\n"; - return 1; + }, + ]; } sub isc_client_architecture_lines { @@ -184,11 +163,8 @@ sub isc_client_architecture_lines { " } else if option client-architecture = 00:1c { #riscv64 uefi http boot\n ", " option vendor-class-identifier \"HTTPClient\";\n", " filename \"http://$tftp$portsuffix/tftpboot/boot/grub2/grub2.riscv64\";\n", - ($opts{s390x_qemu_config_present} ? ( - " } else if option client-architecture = 00:1f { #QEMU s390x\n ", - " option path-prefix = \"pxelinux.cfg/s390x/\";\n", - " option conf-file = \"${net}_${maskbits}\";\n", - ) : ()), + " } else if option client-architecture = 00:1f { #QEMU s390x\n ", + " option conf-file = \"pxelinux.cfg/s390x/${net}_${maskbits}\";\n", " } else if option client-architecture = 00:0e { #OPAL-v3\n ", " option conf-file = \"http://$tftp$portsuffix/tftpboot/pxelinux.cfg/p/${net}_${maskbits}\";\n", " } else if substring (option vendor-class-identifier,0,11) = \"onie_vendor\" { #for onie on cumulus switch\n", diff --git a/xCAT-server/lib/xcat/plugins/dhcp.pm b/xCAT-server/lib/xcat/plugins/dhcp.pm index 235f43032..d4a15af75 100644 --- a/xCAT-server/lib/xcat/plugins/dhcp.pm +++ b/xCAT-server/lib/xcat/plugins/dhcp.pm @@ -2354,10 +2354,6 @@ sub process_request $restartdhcp = 1; newconfig(); } - if (!xCAT::Utils->isAIX() && - xCAT::DHCP::BootPolicy->ensure_isc_path_prefix_definition(\@dhcpconf)) { - $restartdhcp = 1; - } if ($usingipv6 and not $dhcp6conf[0]) { $restartdhcp6 = 1; newconfig6(); @@ -3368,9 +3364,8 @@ sub kea_subnet4_intent }; push @client_classes, @{ xCAT::DHCP::BootPolicy->kea_s390x_network_classes( - net => $net, - prefix => $prefix, - qemu_config_present => -f "$tftpdir/pxelinux.cfg/s390x/${net}_${prefix}", + net => $net, + prefix => $prefix, ) }; if (@client_classes) { @@ -3896,7 +3891,6 @@ sub kea_option_defs { return [ { name => 'conf-file', code => 209, type => 'string', space => 'dhcp4' }, - { name => 'path-prefix', code => 210, type => 'string', space => 'dhcp4' }, { name => 'iscsi-initiator-iqn', code => 203, type => 'string', space => 'dhcp4' }, { name => 'cumulus-provision-url', code => 239, type => 'string', space => 'dhcp4' }, ]; @@ -4535,8 +4529,6 @@ sub addnet tftpdir => $tftpdir, net => $net, prefix => $maskbits, - s390x_qemu_config_present => - -f "$tftpdir/pxelinux.cfg/s390x/${net}_${maskbits}", ) }; if ($range) { @@ -4806,7 +4798,6 @@ sub newconfig push @dhcpconf, "#xCAT generated dhcp configuration\n"; push @dhcpconf, "\n"; push @dhcpconf, "option conf-file code 209 = text;\n"; - push @dhcpconf, "option path-prefix code 210 = text;\n"; push @dhcpconf, "option space isan;\n"; push @dhcpconf, "option isan-encap-opts code 43 = encapsulate isan;\n"; push @dhcpconf, "option isan.iqn code 203 = string;\n"; diff --git a/xCAT-server/lib/xcat/plugins/mknb.pm b/xCAT-server/lib/xcat/plugins/mknb.pm index 3c1e8ba86..9a6c7fbcd 100644 --- a/xCAT-server/lib/xcat/plugins/mknb.pm +++ b/xCAT-server/lib/xcat/plugins/mknb.pm @@ -757,8 +757,7 @@ sub process_request { my $nicip = $normnets->{$net}; my $xcatd_address = defined($xcatdnormnets->{$net}) ? $xcatdnormnets->{$net} : $nicip; $net =~ s/\//_/; - if (defined $nobootnicips{$nicip} - || ($arch eq 's390x' && defined $nobootnicips{$xcatd_address})) { + if (defined($nobootnicips{$nicip})) { if ($arch =~ /ppc/ and -r "$tftpdir/pxelinux.cfg/p/$net") { unlink("$tftpdir/pxelinux.cfg/p/$net"); } elsif ($arch eq 's390x') { @@ -834,6 +833,9 @@ sub process_request { xcatd_address => $xcatd_address, xcatdport => $xcatdport, consolecmdline => $consolecmdline, + kernel => $invisibletouch + ? "xcat/genesis.kernel.$arch" + : "xcat/nbk.$arch", initrd => $initrd_file, ); if ($config_error) { @@ -923,7 +925,7 @@ sub _write_s390x_discovery_config { my $qemu_config = "# pxelinux.cfg xCAT Genesis s390x\n" . "DEFAULT xCAT\n" . "LABEL xCAT\n" - . " KERNEL xcat/genesis.kernel.s390x\n" + . " KERNEL $args{kernel}\n" . " INITRD $initrd\n" . " APPEND $cmdline\n"; my $error = _write_s390x_config($qemu_path, $qemu_config); From 7183fd8414152fef87d57bba24e5013283b7b4e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 16:20:38 -0300 Subject: [PATCH 23/65] test(genesis): cover s390x boot behavior --- .../integration/dhcp_isc_config_validation.t | 7 +-- .../integration/dhcp_kea_config_validation.t | 4 +- xCAT-test/unit/dhcp_boot_policy.t | 44 ++----------------- xCAT-test/unit/dhcp_isc_client_arch.t | 9 +--- xCAT-test/unit/dhcp_kea_plugin_intent.t | 26 +++-------- xCAT-test/unit/genesis_openembedded.t | 44 ++++++++++++++++--- .../unit/genesis_openembedded_activation.t | 12 ++--- xCAT-test/unit/mknb_xcatd_address.t | 33 +++++++++----- 8 files changed, 78 insertions(+), 101 deletions(-) diff --git a/xCAT-test/integration/dhcp_isc_config_validation.t b/xCAT-test/integration/dhcp_isc_config_validation.t index 7a80e209a..aae60d304 100644 --- a/xCAT-test/integration/dhcp_isc_config_validation.t +++ b/xCAT-test/integration/dhcp_isc_config_validation.t @@ -28,16 +28,11 @@ my @config = ( " range 192.0.2.100 192.0.2.110;\n", ); -ok( - xCAT::DHCP::BootPolicy->ensure_isc_path_prefix_definition(\@config), - 'the upgrade path adds option 210 to an existing configuration', -); push @config, @{ xCAT::DHCP::BootPolicy->isc_client_architecture_lines( next_server => '192.0.2.1', portsuffix => '', net => '192.0.2.0', prefix => 24, - s390x_qemu_config_present => 1, ) }, "}\n"; my $configuration_root = -d '/etc/dhcp' ? '/etc/dhcp' : '/etc'; @@ -48,6 +43,6 @@ print {$config_file} @config; close($config_file) or die "Cannot close $path: $!"; my $status = system($dhcpd, '-t', '-cf', $path); -is($status, 0, 'ISC accepts the upgraded s390x boot policy'); +is($status, 0, 'ISC accepts the s390x boot policy'); done_testing(); diff --git a/xCAT-test/integration/dhcp_kea_config_validation.t b/xCAT-test/integration/dhcp_kea_config_validation.t index 125189705..86596f026 100644 --- a/xCAT-test/integration/dhcp_kea_config_validation.t +++ b/xCAT-test/integration/dhcp_kea_config_validation.t @@ -24,7 +24,6 @@ my $json = $backend->render_dhcp4_config( interfaces => ['*'], 'option-def' => [ { name => 'conf-file', code => 209, type => 'string', space => 'dhcp4' }, - { name => 'path-prefix', code => 210, type => 'string', space => 'dhcp4' }, { name => 'iscsi-initiator-iqn', code => 203, type => 'string', space => 'dhcp4' }, { name => 'cumulus-provision-url', code => 239, type => 'string', space => 'dhcp4' }, ], @@ -47,8 +46,7 @@ my $json = $backend->render_dhcp4_config( test => 'option[93].hex == 0x001f', additional_only => JSON::true, 'option-data' => [ - { name => 'conf-file', data => '192.168.122.0_24' }, - { name => 'path-prefix', data => 'pxelinux.cfg/s390x/' }, + { name => 'conf-file', data => 'pxelinux.cfg/s390x/192.168.122.0_24' }, ], }, { diff --git a/xCAT-test/unit/dhcp_boot_policy.t b/xCAT-test/unit/dhcp_boot_policy.t index a5d1e6610..2e6974708 100644 --- a/xCAT-test/unit/dhcp_boot_policy.t +++ b/xCAT-test/unit/dhcp_boot_policy.t @@ -218,9 +218,8 @@ unlike( ); my $s390x = xCAT::DHCP::BootPolicy->kea_s390x_network_classes( - net => '10.0.0.0', - prefix => 24, - qemu_config_present => 1, + net => '10.0.0.0', + prefix => 24, ); is_deeply( $s390x, @@ -232,12 +231,7 @@ is_deeply( 'option-data' => [ { name => 'conf-file', - data => '10.0.0.0_24', - 'always-send' => 1, - }, - { - name => 'path-prefix', - data => 'pxelinux.cfg/s390x/', + data => 'pxelinux.cfg/s390x/10.0.0.0_24', 'always-send' => 1, }, ], @@ -245,37 +239,5 @@ is_deeply( ], 's390x firmware receives its supported network configuration method', ); -is_deeply( - xCAT::DHCP::BootPolicy->kea_s390x_network_classes( - net => '10.0.0.0', prefix => 24 - ), - [], - 's390x classes require a generated network configuration', -); -my @legacy_isc_config = ( - "#xCAT generated dhcp configuration\n", - "\n", - "option conf-file code 209 = text;\n", -); -ok( - xCAT::DHCP::BootPolicy->ensure_isc_path_prefix_definition( - \@legacy_isc_config - ), - 'an older ISC configuration receives the path-prefix definition', -); -is( - $legacy_isc_config[1], - "option path-prefix code 210 = text;\n", - 'the definition is inserted in the global configuration header', -); -ok( - !xCAT::DHCP::BootPolicy->ensure_isc_path_prefix_definition( - \@legacy_isc_config - ), - 'an existing path-prefix definition is preserved', -); -my $definition_count = - grep { /^\s*option\s+path-prefix\s+code\s+210\b/ } @legacy_isc_config; -is($definition_count, 1, 'repeated updates do not duplicate the definition'); done_testing(); diff --git a/xCAT-test/unit/dhcp_isc_client_arch.t b/xCAT-test/unit/dhcp_isc_client_arch.t index d3b2fd85b..4757f56df 100644 --- a/xCAT-test/unit/dhcp_isc_client_arch.t +++ b/xCAT-test/unit/dhcp_isc_client_arch.t @@ -14,7 +14,6 @@ my $rendered = join '', @{ xCAT::DHCP::BootPolicy->isc_client_architecture_lines tftpdir => '/srv/tftp', net => '192.0.2.0', prefix => 24, - s390x_qemu_config_present => 1, ) }; like( @@ -39,7 +38,7 @@ like( ); like( $rendered, - qr/client-architecture = 00:1f \{ #QEMU s390x\n\s+option path-prefix = "pxelinux\.cfg\/s390x\/";\n\s+option conf-file = "192\.0\.2\.0_24";/, + qr/client-architecture = 00:1f \{ #QEMU s390x\n\s+option conf-file = "pxelinux\.cfg\/s390x\/192\.0\.2\.0_24";/, 'QEMU s390x receives its network configuration', ); @@ -62,10 +61,4 @@ cmp_ok($http_pos, '<', $opal_pos, 'the HTTP branch precedes the OPAL bran cmp_ok($http_pos, '<', $fallback_pos, 'the HTTP branch is reachable before the fallback'); like($rendered, qr/filename "\/yaboot";\n\s*\}\n\z/, 'the policy ends with the existing yaboot fallback'); -my $without_s390x = join '', @{ xCAT::DHCP::BootPolicy->isc_client_architecture_lines( - next_server => '192.0.2.10', net => '192.0.2.0', prefix => 24, - ) }; -unlike($without_s390x, qr/client-architecture = 00:1f/, - 'ISC does not advertise s390x without a generated network configuration'); - done_testing(); diff --git a/xCAT-test/unit/dhcp_kea_plugin_intent.t b/xCAT-test/unit/dhcp_kea_plugin_intent.t index 530690d2d..a3c97628a 100644 --- a/xCAT-test/unit/dhcp_kea_plugin_intent.t +++ b/xCAT-test/unit/dhcp_kea_plugin_intent.t @@ -8,7 +8,6 @@ use lib "$FindBin::Bin/../lib"; use lib "$FindBin::Bin/../../perl-xCAT"; use lib "$FindBin::Bin/../../xCAT-server/lib/perl"; -use File::Path qw(make_path); use File::Temp qw(tempdir); use Socket (); use Test::More; @@ -85,7 +84,7 @@ require xCAT::Utils; *xCAT::Utils::runcmd = sub { return; }; } -$xCAT::TableUtils::tftpdir = tempdir(CLEANUP => 1); +$xCAT::TableUtils::tftpdir = '/srv/tftp'; my $source_dhcp_plugin = repo_path('xCAT-server/lib/xcat/plugins/dhcp.pm'); require $source_dhcp_plugin; require xCAT::DHCP::Backend::Kea; @@ -123,24 +122,6 @@ my %network_entry = ( tftpserver => '', ); -{ - no warnings 'redefine'; - local *xCAT::NetworkUtils::thishostisnot = sub { return 0; }; - my $nettab = DHCPKeaIntentNetTable->new(\%network_entry); - my $subnet = xCAT_plugin::dhcp::kea_subnet4_intent( - $nettab, '10.0.0.0', '255.255.255.0', 'eth0', 0, 1, 80 - ); - my %classes = map { $_->{name} => $_ } @{ $subnet->{client_classes} }; - ok(!grep(/^xcat-s390x-/, keys %classes), - 'Kea omits s390x policy without a generated network configuration'); -} - -my $s390x_config_dir = - "$xCAT::TableUtils::tftpdir/pxelinux.cfg/s390x"; -make_path($s390x_config_dir); -open(my $s390x_config, '>', "$s390x_config_dir/10.0.0.0_24") - or die "Unable to create s390x test configuration: $!"; -close($s390x_config); { no warnings 'redefine'; local *xCAT::NetworkUtils::thishostisnot = sub { return 0; }; @@ -150,6 +131,11 @@ close($s390x_config); ); my %classes = map { $_->{name} => $_ } @{ $subnet->{client_classes} }; ok($classes{'xcat-s390x-qemu-10.0.0.0_24'}, 'the Kea subnet includes QEMU s390x boot policy'); + is( + $classes{'xcat-s390x-qemu-10.0.0.0_24'}{'option-data'}[0]{data}, + 'pxelinux.cfg/s390x/10.0.0.0_24', + 'the Kea subnet sends the complete s390x configuration path', + ); is_deeply( [ grep { /^xcat-s390x-/ } @{ $subnet->{additional_client_classes} } ], ['xcat-s390x-qemu-10.0.0.0_24'], diff --git a/xCAT-test/unit/genesis_openembedded.t b/xCAT-test/unit/genesis_openembedded.t index d595725df..4fd8cb537 100644 --- a/xCAT-test/unit/genesis_openembedded.t +++ b/xCAT-test/unit/genesis_openembedded.t @@ -2,12 +2,14 @@ use strict; use warnings; +use Cwd qw(abs_path); use Digest::SHA qw(sha256_hex); use File::Spec; +use File::Temp qw(tempdir); use FindBin; use Test::More; -my $repo_root = File::Spec->catdir( $FindBin::Bin, '..', '..' ); +my $repo_root = abs_path(File::Spec->catdir( $FindBin::Bin, '..', '..' )); sub read_file { my ($relative_path) = @_; @@ -103,11 +105,38 @@ like( $ppc64_kas, qr/^machine: xcat-genesis-ppc64$/m, 'ppc64 build selects its machine' ); my $build = read_file('xCAT-genesis-builder/oe/build'); -like( $build, - qr/aarch64\|armv7hf\|riscv64\|s390x\|x86\|x86_64\|ppc64\|ppc64le/, - 'build accepts each supported architecture' ); -like( $build, qr{kas/\$architecture\.yml}, - 'build selects the architecture configuration directly' ); +my $build_path = File::Spec->catfile( + $repo_root, qw(xCAT-genesis-builder oe build) +); +my $build_test_dir = tempdir(CLEANUP => 1); +my $kas_stub = File::Spec->catfile($build_test_dir, 'kas'); +my $kas_log = File::Spec->catfile($build_test_dir, 'kas.log'); +open(my $kas_stub_file, '>', $kas_stub) + or die "Unable to create $kas_stub: $!"; +print {$kas_stub_file} <<'KAS_STUB'; +#!/bin/sh +printf '%s\n' "$*" >> "$XCAT_TEST_KAS_LOG" +KAS_STUB +close($kas_stub_file) or die "Unable to close $kas_stub: $!"; +chmod(0755, $kas_stub) or die "Unable to make $kas_stub executable: $!"; +{ + local $ENV{KAS} = $kas_stub; + local $ENV{XCAT_TEST_KAS_LOG} = $kas_log; + local $ENV{XCAT_GENESIS_WORK_DIR} = + File::Spec->catdir($build_test_dir, 'work'); + is(system($build_path, 's390x') >> 8, 0, + 'build accepts s390x'); + is(system($build_path, 'not-an-architecture') >> 8, 2, + 'build rejects an unsupported architecture'); +} +open(my $kas_log_file, '<', $kas_log) or die "Unable to read $kas_log: $!"; +my @kas_invocations = <$kas_log_file>; +close($kas_log_file) or die "Unable to close $kas_log: $!"; +is_deeply( + \@kas_invocations, + ["build $repo_root/xCAT-genesis-builder/oe/kas/s390x.yml\n"], + 'build invokes the s390x kas configuration once', +); my $distro = read_file( 'xCAT-genesis-builder/oe/meta-xcat-genesis/conf/distro/xcat-genesis.conf' @@ -877,6 +906,9 @@ my $smoke_extension = read_file( ); like( $smoke_extension, qr/^XCAT_GENESIS_EXTENSION_NAME = "xcat-smoke"$/m, 'open smoke extension exercises the build path' ); +like( $smoke_extension, + qr/^XCAT_GENESIS_EXTENSION_ARCHITECTURE = "\$\{XCAT_GENESIS_ARCHITECTURE\}"$/m, + 'smoke extension uses the target architecture identity' ); my $preset = read_file( 'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/00-xcat-genesis.preset' diff --git a/xCAT-test/unit/genesis_openembedded_activation.t b/xCAT-test/unit/genesis_openembedded_activation.t index 774f73049..da2c7c572 100644 --- a/xCAT-test/unit/genesis_openembedded_activation.t +++ b/xCAT-test/unit/genesis_openembedded_activation.t @@ -112,11 +112,13 @@ like( qr{/opt/xcat/share/xcat/netboot/genesis-openembedded/ARCH}, 'the mknb man page documents the OpenEmbedded install namespace', ); -like( - $mknb_pod, - qr/x86.*x86_64.*ppc64.*ppc64le.*armv7hf.*aarch64.*riscv64.*s390x/s, - 'the mknb man page lists every exact OpenEmbedded architecture', -); +for my $architecture (qw(x86 x86_64 ppc64 ppc64le armv7hf aarch64 riscv64 s390x)) { + like( + $mknb_pod, + qr/\b\Q$architecture\E\b/, + "the mknb man page lists $architecture", + ); +} unlike( $mknb_pod, qr/For ppc64le, use the ppc64 architecture/, diff --git a/xCAT-test/unit/mknb_xcatd_address.t b/xCAT-test/unit/mknb_xcatd_address.t index d2c0ecdc5..699daf3f6 100644 --- a/xCAT-test/unit/mknb_xcatd_address.t +++ b/xCAT-test/unit/mknb_xcatd_address.t @@ -537,17 +537,6 @@ unlike( 's390x configurations do not use the later floating address', ); write_text($s390x_qemu_path, "admin network configuration\n"); -%xCAT::TableUtils::site_extra = ( dhcpinterfaces => 'eth0,eth1:noboot' ); -$xCAT::NetworkUtils::nic_ips = { eth0 => '10.0.0.1', eth1 => '192.168.148.10' }; -$responses = run_mknb('s390x'); -generation_succeeded($responses, 's390x configuration generation accepts a :noboot interface'); -is( - read_config($s390x_qemu_path), - "admin network configuration\n", - 'a :noboot interface preserves an administrator-owned s390x configuration', -); -%xCAT::TableUtils::site_extra = (); -$xCAT::NetworkUtils::nic_ips = undef; $responses = run_mknb('s390x'); ok( scalar(grep { ref($_) eq 'HASH' && $_->{error} && "@{$_->{error}}" =~ /Refusing to replace unmanaged/ } @{$responses}), @@ -559,6 +548,25 @@ unlink($s390x_qemu_path); $responses = run_mknb('s390x'); generation_succeeded($responses, 's390x configuration is restored after removing the unmanaged file'); +my $s390x_legacy_root = "$tmpdir/tftpboot-s390x-legacy"; +make_path("$s390x_legacy_root/pxelinux.cfg/s390x"); +my ($s390x_legacy_path, $s390x_legacy_error) = + xCAT_plugin::mknb::_write_s390x_discovery_config( + tftpdir => $s390x_legacy_root, + network => '192.168.144.0_20', + xcatd_address => '192.168.148.10', + xcatdport => 3001, + consolecmdline => 'console=ttysclp0', + kernel => 'xcat/nbk.s390x', + initrd => "$s390x_legacy_root/xcat/nbfs.s390x.gz", + ); +is($s390x_legacy_error, undef, 'legacy s390x configuration is written'); +like( + read_config($s390x_legacy_path), + qr/^ KERNEL xcat\/nbk\.s390x$/m, + 'legacy s390x configuration selects its published kernel', +); + my $s390x_failure_root = "$tmpdir/tftpboot-s390x-failure"; make_path("$s390x_failure_root/pxelinux.cfg/s390x/192.168.144.0_20"); my (undef, $s390x_write_error) = @@ -568,6 +576,7 @@ my (undef, $s390x_write_error) = xcatd_address => '192.168.148.10', xcatdport => 3001, consolecmdline => 'console=ttysclp0', + kernel => 'xcat/genesis.kernel.s390x', initrd => "$s390x_failure_root/xcat/genesis.fs.s390x.gz", ); like( @@ -591,7 +600,7 @@ like( ); %xCAT::TableUtils::site_extra = ( dhcpinterfaces => 'eth0,eth1:noboot' ); -$xCAT::NetworkUtils::nic_ips = { eth0 => '10.0.0.1', eth1 => '192.168.148.10' }; +$xCAT::NetworkUtils::nic_ips = { eth0 => '10.0.0.1', eth1 => '192.168.149.100' }; $responses = run_mknb('s390x'); generation_succeeded($responses, 's390x configuration generation honors :noboot'); ok(!-e $s390x_qemu_path, 'a :noboot network gets no QEMU s390x configuration'); From b7f9864528b74790f71aad3b277f162e3651664a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 16:20:38 -0300 Subject: [PATCH 24/65] docs(genesis): clarify s390x support --- .../developers/guides/code/dhcp_backend_validation_matrix.rst | 3 ++- .../developers/guides/code/genesis_openembedded_plan.rst | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/source/developers/guides/code/dhcp_backend_validation_matrix.rst b/docs/source/developers/guides/code/dhcp_backend_validation_matrix.rst index 606d4175a..4b62f1582 100644 --- a/docs/source/developers/guides/code/dhcp_backend_validation_matrix.rst +++ b/docs/source/developers/guides/code/dhcp_backend_validation_matrix.rst @@ -134,8 +134,9 @@ The following matrix is the default live validation gate for DHCP backend work. - ``ISC`` and ``Kea`` - ``s390-ccw BIOS network IPL`` - ``makedhcp -n``; backend parser; architecture ``0x001f``; DHCP options - 209 and 210; TFTP fetch of the generated ``pxelinux.cfg``-style configuration, + 209; TFTP fetch of the generated ``pxelinux.cfg``-style configuration, Genesis kernel, and initramfs + Kea Boot and Reservation Regression Matrix ------------------------------------------ diff --git a/docs/source/developers/guides/code/genesis_openembedded_plan.rst b/docs/source/developers/guides/code/genesis_openembedded_plan.rst index 40df7b2b3..07917459e 100644 --- a/docs/source/developers/guides/code/genesis_openembedded_plan.rst +++ b/docs/source/developers/guides/code/genesis_openembedded_plan.rst @@ -97,10 +97,10 @@ The ``s390x`` image uses virtio networking under QEMU. QEMU does not emulate qeth, and Genesis does not yet configure qeth channel groups. Physical LPAR and z/VM Genesis networking therefore remain unvalidated. Genesis does not use the shared CEC serial as an s390x node identifier. -QEMU validation covers network IPL, DHCP options 209 and 210, TFTP, and the +QEMU validation covers network IPL, DHCP option 209, TFTP, and the network-specific ``pxelinux.cfg``-style configuration written by ``mknb``. This release does not advertise a DPM boot configuration. -Existing xCAT s390x operating-system provisioning remains unchanged. +Legacy xCAT z/VM operating-system provisioning remains unchanged. xCAT protocol ------------- From 8217422a70beb62f0e2b0f5c0ec7ede2741d66d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 16:37:38 -0300 Subject: [PATCH 25/65] fix(genesis): use s390x config name --- perl-xCAT/xCAT/DHCP/BootPolicy.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/perl-xCAT/xCAT/DHCP/BootPolicy.pm b/perl-xCAT/xCAT/DHCP/BootPolicy.pm index 2fb5fe612..8dbcd3e26 100644 --- a/perl-xCAT/xCAT/DHCP/BootPolicy.pm +++ b/perl-xCAT/xCAT/DHCP/BootPolicy.pm @@ -122,7 +122,7 @@ sub kea_s390x_network_classes { 'option-data' => [ { name => 'conf-file', - data => "pxelinux.cfg/s390x/$network_id", + data => "s390x/$network_id", 'always-send' => 1, }, ], @@ -164,7 +164,7 @@ sub isc_client_architecture_lines { " option vendor-class-identifier \"HTTPClient\";\n", " filename \"http://$tftp$portsuffix/tftpboot/boot/grub2/grub2.riscv64\";\n", " } else if option client-architecture = 00:1f { #QEMU s390x\n ", - " option conf-file = \"pxelinux.cfg/s390x/${net}_${maskbits}\";\n", + " option conf-file = \"s390x/${net}_${maskbits}\";\n", " } else if option client-architecture = 00:0e { #OPAL-v3\n ", " option conf-file = \"http://$tftp$portsuffix/tftpboot/pxelinux.cfg/p/${net}_${maskbits}\";\n", " } else if substring (option vendor-class-identifier,0,11) = \"onie_vendor\" { #for onie on cumulus switch\n", From 6aba71b44ccd052daaa415fe8d2bfcb159a752ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 16:37:39 -0300 Subject: [PATCH 26/65] test(genesis): cover s390x config name --- xCAT-test/integration/dhcp_kea_config_validation.t | 2 +- xCAT-test/unit/dhcp_boot_policy.t | 2 +- xCAT-test/unit/dhcp_isc_client_arch.t | 2 +- xCAT-test/unit/dhcp_kea_plugin_intent.t | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/xCAT-test/integration/dhcp_kea_config_validation.t b/xCAT-test/integration/dhcp_kea_config_validation.t index 86596f026..7e011c2ed 100644 --- a/xCAT-test/integration/dhcp_kea_config_validation.t +++ b/xCAT-test/integration/dhcp_kea_config_validation.t @@ -46,7 +46,7 @@ my $json = $backend->render_dhcp4_config( test => 'option[93].hex == 0x001f', additional_only => JSON::true, 'option-data' => [ - { name => 'conf-file', data => 'pxelinux.cfg/s390x/192.168.122.0_24' }, + { name => 'conf-file', data => 's390x/192.168.122.0_24' }, ], }, { diff --git a/xCAT-test/unit/dhcp_boot_policy.t b/xCAT-test/unit/dhcp_boot_policy.t index 2e6974708..6430caad2 100644 --- a/xCAT-test/unit/dhcp_boot_policy.t +++ b/xCAT-test/unit/dhcp_boot_policy.t @@ -231,7 +231,7 @@ is_deeply( 'option-data' => [ { name => 'conf-file', - data => 'pxelinux.cfg/s390x/10.0.0.0_24', + data => 's390x/10.0.0.0_24', 'always-send' => 1, }, ], diff --git a/xCAT-test/unit/dhcp_isc_client_arch.t b/xCAT-test/unit/dhcp_isc_client_arch.t index 4757f56df..556267992 100644 --- a/xCAT-test/unit/dhcp_isc_client_arch.t +++ b/xCAT-test/unit/dhcp_isc_client_arch.t @@ -38,7 +38,7 @@ like( ); like( $rendered, - qr/client-architecture = 00:1f \{ #QEMU s390x\n\s+option conf-file = "pxelinux\.cfg\/s390x\/192\.0\.2\.0_24";/, + qr/client-architecture = 00:1f \{ #QEMU s390x\n\s+option conf-file = "s390x\/192\.0\.2\.0_24";/, 'QEMU s390x receives its network configuration', ); diff --git a/xCAT-test/unit/dhcp_kea_plugin_intent.t b/xCAT-test/unit/dhcp_kea_plugin_intent.t index a3c97628a..95c4dbe35 100644 --- a/xCAT-test/unit/dhcp_kea_plugin_intent.t +++ b/xCAT-test/unit/dhcp_kea_plugin_intent.t @@ -133,8 +133,8 @@ my %network_entry = ( ok($classes{'xcat-s390x-qemu-10.0.0.0_24'}, 'the Kea subnet includes QEMU s390x boot policy'); is( $classes{'xcat-s390x-qemu-10.0.0.0_24'}{'option-data'}[0]{data}, - 'pxelinux.cfg/s390x/10.0.0.0_24', - 'the Kea subnet sends the complete s390x configuration path', + 's390x/10.0.0.0_24', + 'the Kea subnet sends the s390x configuration name', ); is_deeply( [ grep { /^xcat-s390x-/ } @{ $subnet->{additional_client_classes} } ], From 0cbeb3bb56ebd0c4dca7d08309b0d942972a11b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 16:54:13 -0300 Subject: [PATCH 27/65] feat(genesis): report build architectures --- xCAT-genesis-builder/oe/build | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/xCAT-genesis-builder/oe/build b/xCAT-genesis-builder/oe/build index 93656d61c..8a53b0525 100755 --- a/xCAT-genesis-builder/oe/build +++ b/xCAT-genesis-builder/oe/build @@ -6,6 +6,12 @@ oe_dir=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd) : "${XCAT_GENESIS_WORK_DIR:=$oe_dir/.work}" : "${KAS:=kas}" +supported_architectures='aarch64 armv7hf riscv64 s390x x86 x86_64 ppc64 ppc64le' +if [ "$#" -eq 1 ] && [ "$1" = '--list-architectures' ]; then + printf '%s\n' $supported_architectures + exit 0 +fi + mkdir -p "$XCAT_GENESIS_WORK_DIR" export KAS_WORK_DIR="$XCAT_GENESIS_WORK_DIR" @@ -16,8 +22,8 @@ if [ "$#" -eq 0 ]; then fi for architecture do - case "$architecture" in - aarch64|armv7hf|riscv64|s390x|x86|x86_64|ppc64|ppc64le) ;; + case " $supported_architectures " in + *" $architecture "*) ;; *) printf 'Unsupported Genesis architecture: %s\n' "$architecture" >&2 exit 2 From ccac4f57eb62b762526a7e67726722805f93ab26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 16:54:13 -0300 Subject: [PATCH 28/65] test(genesis): cover architecture reporting --- xCAT-test/unit/genesis_openembedded.t | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/xCAT-test/unit/genesis_openembedded.t b/xCAT-test/unit/genesis_openembedded.t index 4fd8cb537..45f9f0764 100644 --- a/xCAT-test/unit/genesis_openembedded.t +++ b/xCAT-test/unit/genesis_openembedded.t @@ -108,6 +108,14 @@ my $build = read_file('xCAT-genesis-builder/oe/build'); my $build_path = File::Spec->catfile( $repo_root, qw(xCAT-genesis-builder oe build) ); +my @listed_architectures = `$build_path --list-architectures`; +is($? >> 8, 0, 'build reports its supported architectures'); +chomp @listed_architectures; +is_deeply( + \@listed_architectures, + [qw(aarch64 armv7hf riscv64 s390x x86 x86_64 ppc64 ppc64le)], + 'build reports each supported architecture once', +); my $build_test_dir = tempdir(CLEANUP => 1); my $kas_stub = File::Spec->catfile($build_test_dir, 'kas'); my $kas_log = File::Spec->catfile($build_test_dir, 'kas.log'); From a8c6bd2a4a6de8bd85677ba5dc20c433756da518 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 17:15:23 -0300 Subject: [PATCH 29/65] fix(genesis): harden s390x configurations --- xCAT-server/lib/xcat/plugins/mknb.pm | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/xCAT-server/lib/xcat/plugins/mknb.pm b/xCAT-server/lib/xcat/plugins/mknb.pm index 9a6c7fbcd..f00df75bb 100644 --- a/xCAT-server/lib/xcat/plugins/mknb.pm +++ b/xCAT-server/lib/xcat/plugins/mknb.pm @@ -757,13 +757,17 @@ sub process_request { my $nicip = $normnets->{$net}; my $xcatd_address = defined($xcatdnormnets->{$net}) ? $xcatdnormnets->{$net} : $nicip; $net =~ s/\//_/; - if (defined($nobootnicips{$nicip})) { + if (defined($nobootnicips{$nicip}) + || ($arch eq 's390x' && defined($nobootnicips{$xcatd_address}))) { if ($arch =~ /ppc/ and -r "$tftpdir/pxelinux.cfg/p/$net") { unlink("$tftpdir/pxelinux.cfg/p/$net"); } elsif ($arch eq 's390x') { my $path = "$tftpdir/pxelinux.cfg/s390x/$net"; if (_is_generated_s390x_config($path)) { - unlink $path; + if (!unlink($path)) { + $callback->({ error => ["Unable to remove s390x Genesis configuration: $path: $OS_ERROR"], errorcode => [1] }); + return; + } } } next; @@ -936,7 +940,7 @@ sub _write_s390x_discovery_config { sub _write_s390x_config { my ($path, $contents) = @_; - if (-f $path && !_is_generated_s390x_config($path)) { + if ((-e $path || -l $path) && !_is_generated_s390x_config($path)) { return "Refusing to replace unmanaged s390x configuration: $path"; } my $config; From 22646de0a8c6f7f45081158bce7048a9e07e2258 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 17:15:23 -0300 Subject: [PATCH 30/65] fix(genesis): match architectures literally --- xCAT-genesis-builder/oe/build | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/xCAT-genesis-builder/oe/build b/xCAT-genesis-builder/oe/build index 8a53b0525..2f3f28ef7 100755 --- a/xCAT-genesis-builder/oe/build +++ b/xCAT-genesis-builder/oe/build @@ -22,12 +22,16 @@ if [ "$#" -eq 0 ]; then fi for architecture do - case " $supported_architectures " in - *" $architecture "*) ;; - *) - printf 'Unsupported Genesis architecture: %s\n' "$architecture" >&2 - exit 2 - ;; - esac + architecture_supported=0 + for candidate in $supported_architectures; do + if [ "$architecture" = "$candidate" ]; then + architecture_supported=1 + break + fi + done + if [ "$architecture_supported" -ne 1 ]; then + printf 'Unsupported Genesis architecture: %s\n' "$architecture" >&2 + exit 2 + fi "$KAS" build "$oe_dir/kas/$architecture.yml" done From bfd4442cc23403f596abeb9f0626c2088ea85bb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 17:15:23 -0300 Subject: [PATCH 31/65] refactor(genesis): use the extension arch default --- .../recipes-extensions/images/xcat-genesis-extension-smoke.bb | 1 - 1 file changed, 1 deletion(-) diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-extensions/images/xcat-genesis-extension-smoke.bb b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-extensions/images/xcat-genesis-extension-smoke.bb index b1f92c2ff..d2a01298b 100644 --- a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-extensions/images/xcat-genesis-extension-smoke.bb +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-extensions/images/xcat-genesis-extension-smoke.bb @@ -7,5 +7,4 @@ inherit xcat-genesis-extension IMAGE_INSTALL = "xcat-genesis-extension-smoke-payload" XCAT_GENESIS_EXTENSION_NAME = "xcat-smoke" -XCAT_GENESIS_EXTENSION_ARCHITECTURE = "${XCAT_GENESIS_ARCHITECTURE}" XCAT_GENESIS_EXTENSION_CAPABILITIES = '["diagnostic.smoke"]' From 9e65f9c45db82533abb26665aa979d65ad5d7d0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 17:15:24 -0300 Subject: [PATCH 32/65] test(genesis): cover s390x integration boundaries --- xCAT-test/unit/genesis_openembedded.t | 12 +++--- .../unit/genesis_openembedded_activation.t | 5 ++- xCAT-test/unit/mknb_xcatd_address.t | 40 ++++++++++++++++++- 3 files changed, 49 insertions(+), 8 deletions(-) diff --git a/xCAT-test/unit/genesis_openembedded.t b/xCAT-test/unit/genesis_openembedded.t index 45f9f0764..a1615fda8 100644 --- a/xCAT-test/unit/genesis_openembedded.t +++ b/xCAT-test/unit/genesis_openembedded.t @@ -104,12 +104,13 @@ like( $ppc64_kas, qr{includes:\s*\n\s*- xCAT-genesis-builder/oe/kas/common\.yml} like( $ppc64_kas, qr/^machine: xcat-genesis-ppc64$/m, 'ppc64 build selects its machine' ); -my $build = read_file('xCAT-genesis-builder/oe/build'); my $build_path = File::Spec->catfile( $repo_root, qw(xCAT-genesis-builder oe build) ); -my @listed_architectures = `$build_path --list-architectures`; -is($? >> 8, 0, 'build reports its supported architectures'); +open(my $architecture_report, '-|', $build_path, '--list-architectures') + or die "Unable to query $build_path: $!"; +my @listed_architectures = <$architecture_report>; +ok(close($architecture_report), 'build reports its supported architectures'); chomp @listed_architectures; is_deeply( \@listed_architectures, @@ -136,6 +137,8 @@ chmod(0755, $kas_stub) or die "Unable to make $kas_stub executable: $!"; 'build accepts s390x'); is(system($build_path, 'not-an-architecture') >> 8, 2, 'build rejects an unsupported architecture'); + is(system($build_path, 'x86*') >> 8, 2, + 'build treats architecture names literally'); } open(my $kas_log_file, '<', $kas_log) or die "Unable to read $kas_log: $!"; my @kas_invocations = <$kas_log_file>; @@ -914,9 +917,6 @@ my $smoke_extension = read_file( ); like( $smoke_extension, qr/^XCAT_GENESIS_EXTENSION_NAME = "xcat-smoke"$/m, 'open smoke extension exercises the build path' ); -like( $smoke_extension, - qr/^XCAT_GENESIS_EXTENSION_ARCHITECTURE = "\$\{XCAT_GENESIS_ARCHITECTURE\}"$/m, - 'smoke extension uses the target architecture identity' ); my $preset = read_file( 'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/00-xcat-genesis.preset' diff --git a/xCAT-test/unit/genesis_openembedded_activation.t b/xCAT-test/unit/genesis_openembedded_activation.t index da2c7c572..a550cbe09 100644 --- a/xCAT-test/unit/genesis_openembedded_activation.t +++ b/xCAT-test/unit/genesis_openembedded_activation.t @@ -112,9 +112,12 @@ like( qr{/opt/xcat/share/xcat/netboot/genesis-openembedded/ARCH}, 'the mknb man page documents the OpenEmbedded install namespace', ); +my ($architecture_names) = $mknb_pod =~ + /OpenEmbedded images use the exact architecture names ([^.]+)\./; +ok(defined($architecture_names), 'the mknb man page has an architecture list'); for my $architecture (qw(x86 x86_64 ppc64 ppc64le armv7hf aarch64 riscv64 s390x)) { like( - $mknb_pod, + $architecture_names, qr/\b\Q$architecture\E\b/, "the mknb man page lists $architecture", ); diff --git a/xCAT-test/unit/mknb_xcatd_address.t b/xCAT-test/unit/mknb_xcatd_address.t index 699daf3f6..7983f879e 100644 --- a/xCAT-test/unit/mknb_xcatd_address.t +++ b/xCAT-test/unit/mknb_xcatd_address.t @@ -10,6 +10,7 @@ use File::Path qw(make_path); use File::Temp qw(tempdir); use Test::More; use XCAT::Test::File qw(repo_path); +use xCAT::DHCP::BootPolicy; BEGIN { package xCAT::Utils; @@ -521,6 +522,17 @@ is((stat "$xCAT::TableUtils::tftpdir/pxelinux.cfg")[2] & 07777, 0755, my $s390x_qemu_path = "$xCAT::TableUtils::tftpdir/pxelinux.cfg/s390x/192.168.144.0_20"; ok(-f $s390x_qemu_path, 's390x writes a QEMU network IPL configuration'); +my $s390x_classes = xCAT::DHCP::BootPolicy->kea_s390x_network_classes( + net => '192.168.144.0', prefix => 20, +); +my ($s390x_conf_file) = map { $_->{data} } + grep { $_->{name} eq 'conf-file' } + @{ $s390x_classes->[0]{'option-data'} }; +is( + "$xCAT::TableUtils::tftpdir/pxelinux.cfg/$s390x_conf_file", + $s390x_qemu_path, + 'the DHCP configuration name resolves to the file written by mknb', +); is( read_config($s390x_qemu_path), "# pxelinux.cfg xCAT Genesis s390x\n" @@ -536,6 +548,17 @@ unlike( qr/192\.168\.149\.100/, 's390x configurations do not use the later floating address', ); +%xCAT::TableUtils::site_extra = ( dhcpinterfaces => 'eth0,eth1:noboot' ); +$xCAT::NetworkUtils::nic_ips = { eth0 => '10.0.0.1', eth1 => '192.168.148.10' }; +$responses = run_mknb('s390x'); +generation_succeeded($responses, 's390x honors :noboot on the selected xcatd address'); +ok(!-e $s390x_qemu_path, + 'a selected xcatd address on :noboot gets no s390x configuration'); +%xCAT::TableUtils::site_extra = (); +$xCAT::NetworkUtils::nic_ips = undef; +$responses = run_mknb('s390x'); +generation_succeeded($responses, 's390x configuration is restored after :noboot'); + write_text($s390x_qemu_path, "admin network configuration\n"); $responses = run_mknb('s390x'); ok( @@ -548,6 +571,21 @@ unlink($s390x_qemu_path); $responses = run_mknb('s390x'); generation_succeeded($responses, 's390x configuration is restored after removing the unmanaged file'); +unlink($s390x_qemu_path); +my $s390x_symlink_target = "$tmpdir/s390x-symlink-target"; +symlink($s390x_symlink_target, $s390x_qemu_path) + or die "Unable to create s390x test symlink: $!"; +$responses = run_mknb('s390x'); +ok( + scalar(grep { ref($_) eq 'HASH' && $_->{error} && "@{$_->{error}}" =~ /Refusing to replace unmanaged/ } @{$responses}), + 's390x refuses to follow an unmanaged configuration symlink', +); +ok(!-e $s390x_symlink_target, + 'an unmanaged configuration symlink target is unchanged'); +unlink($s390x_qemu_path); +$responses = run_mknb('s390x'); +generation_succeeded($responses, 's390x configuration is restored after removing the symlink'); + my $s390x_legacy_root = "$tmpdir/tftpboot-s390x-legacy"; make_path("$s390x_legacy_root/pxelinux.cfg/s390x"); my ($s390x_legacy_path, $s390x_legacy_error) = @@ -568,7 +606,7 @@ like( ); my $s390x_failure_root = "$tmpdir/tftpboot-s390x-failure"; -make_path("$s390x_failure_root/pxelinux.cfg/s390x/192.168.144.0_20"); +make_path("$s390x_failure_root/pxelinux.cfg"); my (undef, $s390x_write_error) = xCAT_plugin::mknb::_write_s390x_discovery_config( tftpdir => $s390x_failure_root, From ecbafb4a2e9290b66360edecbb94d817bcd5b0d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 17:15:24 -0300 Subject: [PATCH 33/65] docs(genesis): explain s390x config lookup --- docs/source/guides/admin-guides/references/man8/mknb.8.rst | 4 ++-- xCAT-client/pods/man8/mknb.8.pod | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/source/guides/admin-guides/references/man8/mknb.8.rst b/docs/source/guides/admin-guides/references/man8/mknb.8.rst index 975e9a8f0..024a3aabb 100644 --- a/docs/source/guides/admin-guides/references/man8/mknb.8.rst +++ b/docs/source/guides/admin-guides/references/man8/mknb.8.rst @@ -48,9 +48,9 @@ Canonical \ ``ppc64``\ images are big-endian. xCAT marks them so \ ``ppc64le``\ riscv64 nodes boot through UEFI firmware and grub2. For riscv64, \ **mknb**\ publishes the Genesis kernel and initramfs and writes one grub2 configuration per network under ``/tftpboot/boot/grub2``, named ``grub.cfg-`` followed by the network hex prefix, so that ``grub2.riscv64`` loaded by the firmware can start node discovery. The per-node files written by \ **nodeset**\ take priority over these network files. Networks served by a ``:noboot`` interface in ``site.dhcpinterfaces`` get no discovery configuration. -QEMU s390-ccw guests receive a network-specific ``pxelinux.cfg`` -style configuration through DHCP options 209 and 210. s390x Genesis does not use the shared IBM Z machine serial as a node identifier. This target supports Genesis discovery under QEMU. Existing s390x operating-system provisioning remains unchanged. Physical LPAR and z/VM Genesis networking remain unvalidated because QEMU does not emulate qeth and Genesis does not yet configure qeth channel groups. +QEMU s390-ccw guests receive a network-specific ``pxelinux.cfg`` -style configuration through DHCP option 209. The firmware resolves this name below ``pxelinux.cfg`` . s390x Genesis does not use the shared IBM Z machine serial as a node identifier. This target supports Genesis discovery under QEMU. Legacy xCAT z/VM operating-system provisioning remains unchanged. Physical LPAR and z/VM Genesis networking remain unvalidated because QEMU does not emulate qeth and Genesis does not yet configure qeth channel groups. -Run \ **makedhcp -n**\ after installing the s390x image and before booting s390x nodes. This adds the required DHCP options to existing configurations. +Run \ **makedhcp -n**\ after installing the s390x image and before booting s390x nodes. This adds the required DHCP option to existing configurations. ******* diff --git a/xCAT-client/pods/man8/mknb.8.pod b/xCAT-client/pods/man8/mknb.8.pod index c1f01c3f4..7999149f3 100644 --- a/xCAT-client/pods/man8/mknb.8.pod +++ b/xCAT-client/pods/man8/mknb.8.pod @@ -30,9 +30,9 @@ Canonical C images are big-endian. xCAT marks them so C nodes do riscv64 nodes boot through UEFI firmware and grub2. For riscv64, B publishes the Genesis kernel and initramfs and writes one grub2 configuration per network under C, named C followed by the network hex prefix, so that C loaded by the firmware can start node discovery. The per-node files written by B take priority over these network files. Networks served by a C<:noboot> interface in C get no discovery configuration. -QEMU s390-ccw guests receive a network-specific C-style configuration through DHCP options 209 and 210. s390x Genesis does not use the shared IBM Z machine serial as a node identifier. This target supports Genesis discovery under QEMU. Existing s390x operating-system provisioning remains unchanged. Physical LPAR and z/VM Genesis networking remain unvalidated because QEMU does not emulate qeth and Genesis does not yet configure qeth channel groups. +QEMU s390-ccw guests receive a network-specific C-style configuration through DHCP option 209. The firmware resolves this name below C. s390x Genesis does not use the shared IBM Z machine serial as a node identifier. This target supports Genesis discovery under QEMU. Legacy xCAT z/VM operating-system provisioning remains unchanged. Physical LPAR and z/VM Genesis networking remain unvalidated because QEMU does not emulate qeth and Genesis does not yet configure qeth channel groups. -Run B after installing the s390x image and before booting s390x nodes. This adds the required DHCP options to existing configurations. +Run B after installing the s390x image and before booting s390x nodes. This adds the required DHCP option to existing configurations. =head1 OPTIONS From 288ca8b78a25c8ccb8f0644f5b0ffbfc6c7b78ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 17:41:08 -0300 Subject: [PATCH 34/65] fix(genesis): install s390x configs safely --- xCAT-server/lib/xcat/plugins/mknb.pm | 45 +++++++++++++++++++++------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/xCAT-server/lib/xcat/plugins/mknb.pm b/xCAT-server/lib/xcat/plugins/mknb.pm index f00df75bb..5b29ce9c3 100644 --- a/xCAT-server/lib/xcat/plugins/mknb.pm +++ b/xCAT-server/lib/xcat/plugins/mknb.pm @@ -1,7 +1,7 @@ package xCAT_plugin::mknb; use strict; use Digest::SHA (); -use File::Temp qw(tempdir); +use File::Temp qw(tempdir tempfile); use xCAT::Utils; use xCAT::TableUtils; use xCAT::NodeRange; @@ -752,6 +752,7 @@ sub process_request { chmod 0755, "$tftpdir/pxelinux.cfg/s390x"; } my $dopxe = 0; + my $s390x_config_error = 0; foreach (keys %{$normnets}) { my $net = $_; my $nicip = $normnets->{$net}; @@ -766,7 +767,7 @@ sub process_request { if (_is_generated_s390x_config($path)) { if (!unlink($path)) { $callback->({ error => ["Unable to remove s390x Genesis configuration: $path: $OS_ERROR"], errorcode => [1] }); - return; + $s390x_config_error = 1; } } } @@ -844,7 +845,7 @@ sub process_request { ); if ($config_error) { $callback->({ error => [$config_error], errorcode => [1] }); - return; + $s390x_config_error = 1; } } } @@ -910,7 +911,7 @@ sub process_request { # does not build. $callback->({ data => ["Note: $tftpdir/boot/grub2/grub2.$arch is missing; $arch nodes need it to reach these configurations (it is installed by grub2-xcat, or copied from the EL $arch installation media)"] }); } - if ($configfileonly) { + if ($configfileonly && !$s390x_config_error) { $callback->({ data => ["Write netboot config file done"] }); } } @@ -943,10 +944,20 @@ sub _write_s390x_config { if ((-e $path || -l $path) && !_is_generated_s390x_config($path)) { return "Refusing to replace unmanaged s390x configuration: $path"; } - my $config; - if (!open $config, '>', $path) { - my $open_error = $OS_ERROR; - return "Unable to write s390x Genesis configuration: $path: $open_error"; + + my $directory = $path; + $directory =~ s{/[^/]+\z}{}xms; + my ($config, $temporary); + my $created = eval { + ($config, $temporary) = tempfile( + '.mknb-s390x-XXXXXX', DIR => $directory, UNLINK => 0 + ); + 1; + }; + if (!$created) { + my $create_error = $EVAL_ERROR || $OS_ERROR; + chomp $create_error; + return "Unable to write s390x Genesis configuration: $path: $create_error"; } my $write_ok = print {$config} $contents; @@ -954,13 +965,27 @@ sub _write_s390x_config { my $close_ok = close $config; my $close_error = $OS_ERROR; if (!$write_ok) { - unlink $path; + unlink $temporary; return "Unable to write s390x Genesis configuration: $path: $write_error"; } if (!$close_ok) { - unlink $path; + unlink $temporary; return "Unable to write s390x Genesis configuration: $path: $close_error"; } + if (!chmod 0644, $temporary) { + my $chmod_error = $OS_ERROR; + unlink $temporary; + return "Unable to set s390x Genesis configuration permissions: $path: $chmod_error"; + } + if ((-e $path || -l $path) && !_is_generated_s390x_config($path)) { + unlink $temporary; + return "Refusing to replace unmanaged s390x configuration: $path"; + } + if (!rename $temporary, $path) { + my $rename_error = $OS_ERROR; + unlink $temporary; + return "Unable to install s390x Genesis configuration: $path: $rename_error"; + } return; } From 0ad83ce257af453dd207e2e454ba2cc60a9bf6bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 17:41:08 -0300 Subject: [PATCH 35/65] test(genesis): cover s390x config failures --- xCAT-test/unit/mknb_xcatd_address.t | 30 +++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/xCAT-test/unit/mknb_xcatd_address.t b/xCAT-test/unit/mknb_xcatd_address.t index 7983f879e..7039b1999 100644 --- a/xCAT-test/unit/mknb_xcatd_address.t +++ b/xCAT-test/unit/mknb_xcatd_address.t @@ -514,7 +514,9 @@ use_reporter_address_maps(); prepare_tftpdir($tmpdir, 'tftpboot-s390x', 's390x'); make_path("$xCAT::TableUtils::tftpdir/pxelinux.cfg"); chmod(0700, "$xCAT::TableUtils::tftpdir/pxelinux.cfg"); +my $original_umask = umask 0077; $responses = run_mknb('s390x'); +umask $original_umask; generation_succeeded($responses, 's390x configuration generation succeeds'); is((stat "$xCAT::TableUtils::tftpdir/pxelinux.cfg")[2] & 07777, 0755, 's390x configuration generation repairs the parent directory mode'); @@ -522,6 +524,8 @@ is((stat "$xCAT::TableUtils::tftpdir/pxelinux.cfg")[2] & 07777, 0755, my $s390x_qemu_path = "$xCAT::TableUtils::tftpdir/pxelinux.cfg/s390x/192.168.144.0_20"; ok(-f $s390x_qemu_path, 's390x writes a QEMU network IPL configuration'); +is((stat $s390x_qemu_path)[2] & 07777, 0644, + 's390x writes a TFTP-readable configuration under a restrictive umask'); my $s390x_classes = xCAT::DHCP::BootPolicy->kea_s390x_network_classes( net => '192.168.144.0', prefix => 20, ); @@ -560,6 +564,7 @@ $responses = run_mknb('s390x'); generation_succeeded($responses, 's390x configuration is restored after :noboot'); write_text($s390x_qemu_path, "admin network configuration\n"); +$xCAT::NetworkUtils::normnet_addresses->{'203.0.113.0/24'} = ['203.0.113.10']; $responses = run_mknb('s390x'); ok( scalar(grep { ref($_) eq 'HASH' && $_->{error} && "@{$_->{error}}" =~ /Refusing to replace unmanaged/ } @{$responses}), @@ -567,6 +572,11 @@ ok( ); is(read_config($s390x_qemu_path), "admin network configuration\n", 'the administrator-owned configuration is unchanged'); +ok( + -f "$xCAT::TableUtils::tftpdir/pxelinux.cfg/s390x/203.0.113.0_24", + 'an unmanaged configuration does not block other networks', +); +use_reporter_address_maps(); unlink($s390x_qemu_path); $responses = run_mknb('s390x'); generation_succeeded($responses, 's390x configuration is restored after removing the unmanaged file'); @@ -605,6 +615,26 @@ like( 'legacy s390x configuration selects its published kernel', ); +my $s390x_preserved = read_config($s390x_legacy_path); +{ + no warnings qw(once redefine); + local *xCAT_plugin::mknb::tempfile = sub { die "injected temporary-file failure\n" }; + my (undef, $s390x_atomic_error) = + xCAT_plugin::mknb::_write_s390x_discovery_config( + tftpdir => $s390x_legacy_root, + network => '192.168.144.0_20', + xcatd_address => '192.0.2.20', + xcatdport => 3001, + consolecmdline => 'console=ttysclp0', + kernel => 'xcat/nbk.s390x', + initrd => "$s390x_legacy_root/xcat/nbfs.s390x.gz", + ); + like($s390x_atomic_error, qr/injected temporary-file failure/, + 'temporary-file creation failures are reported'); +} +is(read_config($s390x_legacy_path), $s390x_preserved, + 'a failed replacement preserves the installed configuration'); + my $s390x_failure_root = "$tmpdir/tftpboot-s390x-failure"; make_path("$s390x_failure_root/pxelinux.cfg"); my (undef, $s390x_write_error) = From 4ad9db20a4bbe263fa9174f6948768d47591f492 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 18:03:05 -0300 Subject: [PATCH 36/65] refactor(genesis): follow mknb perl style --- xCAT-server/lib/xcat/plugins/mknb.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xCAT-server/lib/xcat/plugins/mknb.pm b/xCAT-server/lib/xcat/plugins/mknb.pm index 5b29ce9c3..c7a6ced38 100644 --- a/xCAT-server/lib/xcat/plugins/mknb.pm +++ b/xCAT-server/lib/xcat/plugins/mknb.pm @@ -303,7 +303,7 @@ sub _remove_openembedded_genesis { ); if ($arch eq 's390x') { my $config_directory = "$tftpdir/pxelinux.cfg/s390x"; - if (opendir(my $config_stream, $config_directory)) { + if (opendir my $config_stream, $config_directory) { foreach my $name (readdir $config_stream) { if ($name =~ m{\A[.][.]?\z}xms) { next; @@ -765,7 +765,7 @@ sub process_request { } elsif ($arch eq 's390x') { my $path = "$tftpdir/pxelinux.cfg/s390x/$net"; if (_is_generated_s390x_config($path)) { - if (!unlink($path)) { + if (!unlink $path) { $callback->({ error => ["Unable to remove s390x Genesis configuration: $path: $OS_ERROR"], errorcode => [1] }); $s390x_config_error = 1; } From 34abff1ae9024eb154fb6f4f7b0bb03216f3259d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 18:03:05 -0300 Subject: [PATCH 37/65] docs(genesis): state s390x validation limits --- .../developers/guides/code/dhcp_backend_validation_matrix.rst | 2 +- docs/source/guides/admin-guides/references/man8/mknb.8.rst | 4 ++-- xCAT-client/pods/man8/mknb.8.pod | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/source/developers/guides/code/dhcp_backend_validation_matrix.rst b/docs/source/developers/guides/code/dhcp_backend_validation_matrix.rst index 4b62f1582..170b770f7 100644 --- a/docs/source/developers/guides/code/dhcp_backend_validation_matrix.rst +++ b/docs/source/developers/guides/code/dhcp_backend_validation_matrix.rst @@ -131,7 +131,7 @@ The following matrix is the default live validation gate for DHCP backend work. through kernel, initrd, and root image when image validation is in scope * - QEMU ``s390-ccw`` - ``s390x`` - - ``ISC`` and ``Kea`` + - ``ISC`` - ``s390-ccw BIOS network IPL`` - ``makedhcp -n``; backend parser; architecture ``0x001f``; DHCP options 209; TFTP fetch of the generated ``pxelinux.cfg``-style configuration, diff --git a/docs/source/guides/admin-guides/references/man8/mknb.8.rst b/docs/source/guides/admin-guides/references/man8/mknb.8.rst index 024a3aabb..58c9bedb6 100644 --- a/docs/source/guides/admin-guides/references/man8/mknb.8.rst +++ b/docs/source/guides/admin-guides/references/man8/mknb.8.rst @@ -46,9 +46,9 @@ OpenEmbedded images use the exact architecture names \ ``x86``\ , \ ``x86_64``\ Canonical \ ``ppc64``\ images are big-endian. xCAT marks them so \ ``ppc64le``\ nodes do not use them as a legacy little-endian fallback. \ **mknb**\ also refuses to replace a marked \ ``ppc64``\ image with that fallback. -riscv64 nodes boot through UEFI firmware and grub2. For riscv64, \ **mknb**\ publishes the Genesis kernel and initramfs and writes one grub2 configuration per network under ``/tftpboot/boot/grub2``, named ``grub.cfg-`` followed by the network hex prefix, so that ``grub2.riscv64`` loaded by the firmware can start node discovery. The per-node files written by \ **nodeset**\ take priority over these network files. Networks served by a ``:noboot`` interface in ``site.dhcpinterfaces`` get no discovery configuration. +riscv64 nodes boot through UEFI firmware and grub2. For riscv64, \ **mknb**\ publishes the Genesis kernel and initramfs and writes one grub2 configuration per network under \ ``/tftpboot/boot/grub2``\ , named \ ``grub.cfg-``\ followed by the network hex prefix, so that \ ``grub2.riscv64``\ loaded by the firmware can start node discovery. The per-node files written by \ **nodeset**\ take priority over these network files. Networks served by a \ ``:noboot``\ interface in \ ``site.dhcpinterfaces``\ get no discovery configuration. -QEMU s390-ccw guests receive a network-specific ``pxelinux.cfg`` -style configuration through DHCP option 209. The firmware resolves this name below ``pxelinux.cfg`` . s390x Genesis does not use the shared IBM Z machine serial as a node identifier. This target supports Genesis discovery under QEMU. Legacy xCAT z/VM operating-system provisioning remains unchanged. Physical LPAR and z/VM Genesis networking remain unvalidated because QEMU does not emulate qeth and Genesis does not yet configure qeth channel groups. +QEMU s390-ccw guests receive a network-specific \ ``pxelinux.cfg``\ -style configuration through DHCP option 209. The firmware resolves this name below \ ``pxelinux.cfg``\ . Leave \ ``noderes.netboot``\ unset while a QEMU s390x node uses Genesis discovery. s390x Genesis does not use the shared IBM Z machine serial as a node identifier. QEMU validation covers network IPL through Genesis startup; the discovery exchange with xcatd remains unvalidated for s390x. Legacy xCAT z/VM operating-system provisioning remains unchanged. Physical LPAR and z/VM Genesis networking remain unvalidated because QEMU does not emulate qeth and Genesis does not yet configure qeth channel groups. Run \ **makedhcp -n**\ after installing the s390x image and before booting s390x nodes. This adds the required DHCP option to existing configurations. diff --git a/xCAT-client/pods/man8/mknb.8.pod b/xCAT-client/pods/man8/mknb.8.pod index 7999149f3..fe0fca60b 100644 --- a/xCAT-client/pods/man8/mknb.8.pod +++ b/xCAT-client/pods/man8/mknb.8.pod @@ -30,7 +30,7 @@ Canonical C images are big-endian. xCAT marks them so C nodes do riscv64 nodes boot through UEFI firmware and grub2. For riscv64, B publishes the Genesis kernel and initramfs and writes one grub2 configuration per network under C, named C followed by the network hex prefix, so that C loaded by the firmware can start node discovery. The per-node files written by B take priority over these network files. Networks served by a C<:noboot> interface in C get no discovery configuration. -QEMU s390-ccw guests receive a network-specific C-style configuration through DHCP option 209. The firmware resolves this name below C. s390x Genesis does not use the shared IBM Z machine serial as a node identifier. This target supports Genesis discovery under QEMU. Legacy xCAT z/VM operating-system provisioning remains unchanged. Physical LPAR and z/VM Genesis networking remain unvalidated because QEMU does not emulate qeth and Genesis does not yet configure qeth channel groups. +QEMU s390-ccw guests receive a network-specific C-style configuration through DHCP option 209. The firmware resolves this name below C. Leave C unset while a QEMU s390x node uses Genesis discovery. s390x Genesis does not use the shared IBM Z machine serial as a node identifier. QEMU validation covers network IPL through Genesis startup; the discovery exchange with xcatd remains unvalidated for s390x. Legacy xCAT z/VM operating-system provisioning remains unchanged. Physical LPAR and z/VM Genesis networking remain unvalidated because QEMU does not emulate qeth and Genesis does not yet configure qeth channel groups. Run B after installing the s390x image and before booting s390x nodes. This adds the required DHCP option to existing configurations. From c31d270e30c7ec6cc5375ba0ada59d90d2a60308 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 18:22:16 -0300 Subject: [PATCH 38/65] docs(genesis): fix DHCP option wording --- .../developers/guides/code/dhcp_backend_validation_matrix.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/developers/guides/code/dhcp_backend_validation_matrix.rst b/docs/source/developers/guides/code/dhcp_backend_validation_matrix.rst index 170b770f7..e895ca66d 100644 --- a/docs/source/developers/guides/code/dhcp_backend_validation_matrix.rst +++ b/docs/source/developers/guides/code/dhcp_backend_validation_matrix.rst @@ -133,7 +133,7 @@ The following matrix is the default live validation gate for DHCP backend work. - ``s390x`` - ``ISC`` - ``s390-ccw BIOS network IPL`` - - ``makedhcp -n``; backend parser; architecture ``0x001f``; DHCP options + - ``makedhcp -n``; backend parser; architecture ``0x001f``; DHCP option 209; TFTP fetch of the generated ``pxelinux.cfg``-style configuration, Genesis kernel, and initramfs From 17d2965f18caadfe53589b49f0d8b71718565a30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 4 Sep 2026 18:22:16 -0300 Subject: [PATCH 39/65] chore(genesis): align patch author identity --- .../files/0001-configure-disable-s390x-crcvx-by-default.patch | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/zlib/files/0001-configure-disable-s390x-crcvx-by-default.patch b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/zlib/files/0001-configure-disable-s390x-crcvx-by-default.patch index aeaa4f851..6b327a68d 100644 --- a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/zlib/files/0001-configure-disable-s390x-crcvx-by-default.patch +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/zlib/files/0001-configure-disable-s390x-crcvx-by-default.patch @@ -1,10 +1,10 @@ -From: xCAT Project +From: xCAT Genesis Builder Subject: [PATCH] configure: disable s390x CRC vector code by default The Genesis s390x baseline is z10, which lacks the vector facility. Upstream-Status: Inappropriate [xCAT Genesis targets z10] -Signed-off-by: xCAT Project +Signed-off-by: xCAT Genesis Builder --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) From 1ad58570e8773c42e7ab8ae8b0f5bfd2a07ad83e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Sun, 6 Sep 2026 00:21:01 -0300 Subject: [PATCH 40/65] feat(genesis): add s390x network tooling --- .../packagegroup-xcat-genesis-hardware.bb | 1 + .../s390-tools/s390-tools-znetconf_2.41.0.bb | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-support/s390-tools/s390-tools-znetconf_2.41.0.bb diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/packagegroups/packagegroup-xcat-genesis-hardware.bb b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/packagegroups/packagegroup-xcat-genesis-hardware.bb index 1f53dc4ad..86dc30a07 100644 --- a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/packagegroups/packagegroup-xcat-genesis-hardware.bb +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/packagegroups/packagegroup-xcat-genesis-hardware.bb @@ -48,3 +48,4 @@ RDEPENDS:${PN}:append:xcat-genesis-aarch64 = " dmidecode lshw" RDEPENDS:${PN}:append:xcat-genesis-ppc64 = " dmidecode xcat-genesis-hardware-control-iprutils" RDEPENDS:${PN}:append:xcat-genesis-ppc64le = " dmidecode xcat-genesis-hardware-control-iprutils" RDEPENDS:${PN}:append:xcat-genesis-riscv64 = " lshw" +RDEPENDS:${PN}:append:xcat-genesis-s390x = " s390-tools-znetconf" diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-support/s390-tools/s390-tools-znetconf_2.41.0.bb b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-support/s390-tools/s390-tools-znetconf_2.41.0.bb new file mode 100644 index 000000000..37ad5d5b7 --- /dev/null +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-support/s390-tools/s390-tools-znetconf_2.41.0.bb @@ -0,0 +1,28 @@ +SUMMARY = "IBM Z network configuration utility" +HOMEPAGE = "https://github.com/ibm-s390-linux/s390-tools" +LICENSE = "MIT" +LIC_FILES_CHKSUM = "file://LICENSE;md5=f5118f167b055bfd7c3450803f1847af" + +SRC_URI = "git://github.com/ibm-s390-linux/s390-tools;protocol=https;branch=master;tag=v${PV}" +SRCREV = "5e07b30bdf67623e7a6d3850e26208b642d416d7" + +S = "${WORKDIR}/git" + +COMPATIBLE_HOST = "s390x.*-linux" + +RDEPENDS:${PN} = "bash coreutils findutils gawk grep kmod sed udev util-linux-getopt util-linux-logger" + +do_compile[noexec] = "1" + +do_install() { + install -d ${D}${base_sbindir} ${D}${base_libdir}/s390-tools + sed 's/%S390_TOOLS_VERSION%/${PV}/g' ${S}/zconf/znetconf \ + >${D}${base_sbindir}/znetconf + chmod 0755 ${D}${base_sbindir}/znetconf + install -m 0755 ${S}/zconf/lsznet.raw \ + ${D}${base_libdir}/s390-tools/lsznet.raw + install -m 0755 ${S}/zconf/znetcontrolunits \ + ${D}${base_libdir}/s390-tools/znetcontrolunits +} + +FILES:${PN} = "${base_sbindir}/znetconf ${base_libdir}/s390-tools" From 0823c368644677d44569cdd65daf3ace4bb4422c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Sun, 6 Sep 2026 00:25:56 -0300 Subject: [PATCH 41/65] feat(genesis): activate s390x qeth devices --- .../xcat-genesis-qeth/files/genesis-qeth | 148 ++++++++++++++++++ .../files/xcat-genesis-qeth.service | 14 ++ .../xcat-genesis-qeth_1.0.bb | 31 ++++ .../packagegroup-xcat-genesis-hardware.bb | 2 +- 4 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/files/genesis-qeth create mode 100644 xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/files/xcat-genesis-qeth.service create mode 100644 xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/xcat-genesis-qeth_1.0.bb diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/files/genesis-qeth b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/files/genesis-qeth new file mode 100644 index 000000000..65e3d3d42 --- /dev/null +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/files/genesis-qeth @@ -0,0 +1,148 @@ +#!/bin/bash +set -u + +LC_ALL=C +export LC_ALL + +cmdline_file=${XCAT_CMDLINE_FILE:-/proc/cmdline} +status_command=${XCAT_STATUS_COMMAND:-/usr/libexec/xcat/genesis-status} +znetconf_command=${XCAT_ZNETCONF_COMMAND:-/sbin/znetconf} + +log() { + logger -t xcat-genesis-qeth -- "$*" || true +} + +publish_status() { + [ -x "$status_command" ] || return 0 + "$status_command" network "$@" || true +} + +normalize_channel() { + local channel + + channel=$(printf '%s' "$1" | tr 'A-F' 'a-f') + + if [[ $channel =~ ^[[:xdigit:]]{4}$ ]]; then + printf '0.0.%s\n' "$channel" + elif [[ $channel =~ ^[[:xdigit:]]\.[[:xdigit:]]\.[[:xdigit:]]{4}$ ]]; then + printf '%s\n' "$channel" + else + return 1 + fi +} + +configured_channels() { + local channels=$1 configured normalized rest + + while read -r configured rest; do + normalized=$(printf '%s' "$configured" | tr 'A-F' 'a-f') + [ "$normalized" = "$channels" ] && return 0 + done <<<"$configured_output" + return 1 +} + +activate_channels() { + local channels=$1 + shift + local -a command=("$znetconf_command" -a "$channels" -d qeth) + local option output result + local have_layer2=false + + for option in "$@"; do + if [[ ! $option =~ ^[A-Za-z_][A-Za-z0-9_./-]*=[A-Za-z0-9_./:+-]+$ ]]; then + log "Invalid qeth option for $channels: $option" + failed_channels+=("$channels") + return 1 + fi + [[ $option = layer2=* ]] && have_layer2=true + command+=(-o "$option") + done + $have_layer2 || command+=(-o layer2=1) + + if configured_channels "$channels"; then + log "qeth channels already configured: $channels" + ready_channels+=("$channels") + return 0 + fi + + publish_status CONFIGURING_NETWORK "Activating qeth channels $channels" + output=$("${command[@]}" 2>&1) + result=$? + if [ "$result" -ne 0 ]; then + log "qeth activation failed for $channels: $output" + failed_channels+=("$channels") + return 1 + fi + + log "qeth channels ready: $channels" + ready_channels+=("$channels") + return 0 +} + +configured_output=$("$znetconf_command" -c 2>/dev/null || true) +declare -a cmdline_arguments explicit_specs ready_channels failed_channels +rd_znet_present=false +read -r -a cmdline_arguments <"$cmdline_file" +for argument in "${cmdline_arguments[@]}"; do + case "$argument" in + rd.znet=*) + rd_znet_present=true + [[ $argument = rd.znet=qeth,* ]] && explicit_specs+=("${argument#rd.znet=}") + ;; + esac +done + +if [ "${#explicit_specs[@]}" -gt 0 ]; then + for specification in "${explicit_specs[@]}"; do + IFS=, read -r -a fields <<<"$specification" + if [ "${#fields[@]}" -lt 4 ]; then + log "Invalid rd.znet value: $specification" + failed_channels+=("$specification") + continue + fi + + first=$(normalize_channel "${fields[1]}") || first= + second=$(normalize_channel "${fields[2]}") || second= + third=$(normalize_channel "${fields[3]}") || third= + if [ -z "$first" ] || [ -z "$second" ] || [ -z "$third" ]; then + log "Invalid qeth channel in rd.znet: $specification" + failed_channels+=("$specification") + continue + fi + + channels="$first,$second,$third" + activate_channels "$channels" "${fields[@]:4}" || true + done +elif $rd_znet_present; then + exit 0 +else + unconfigured_output=$("$znetconf_command" -u 2>/dev/null) + unconfigured_result=$? + if [ "$unconfigured_result" -eq 31 ]; then + exit 0 + elif [ "$unconfigured_result" -ne 0 ]; then + log "Unable to inspect qeth devices" + publish_status DEGRADED 'Unable to inspect qeth devices' \ + 'CODE=QETH_DISCOVERY_FAILED' \ + 'RECOVERY=Check the IBM Z channel devices and qeth driver' + exit "$unconfigured_result" + fi + + while IFS= read -r line; do + if [[ $line =~ ^([[:xdigit:].,]+)[[:space:]].*[[:space:]]qeth[[:space:]]*$ ]]; then + channels=$(printf '%s' "${BASH_REMATCH[1]}" | tr 'A-F' 'a-f') + activate_channels "$channels" || true + fi + done <<<"$unconfigured_output" +fi + +if [ "${#failed_channels[@]}" -gt 0 ]; then + publish_status DEGRADED 'One or more qeth devices could not be activated' \ + 'CODE=QETH_ACTIVATION_FAILED' \ + 'RECOVERY=Check rd.znet and the IBM Z channel configuration' + exit 1 +fi + +if [ "${#ready_channels[@]}" -gt 0 ]; then + publish_status CONFIGURING_NETWORK 'qeth devices are ready' +fi diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/files/xcat-genesis-qeth.service b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/files/xcat-genesis-qeth.service new file mode 100644 index 000000000..31cf27052 --- /dev/null +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/files/xcat-genesis-qeth.service @@ -0,0 +1,14 @@ +[Unit] +Description=Activate IBM Z qeth network devices +ConditionArchitecture=s390x +ConditionKernelCommandLine=xcatd +After=systemd-udev-trigger.service +Before=NetworkManager.service xcat-genesis-network-state.service + +[Service] +Type=oneshot +ExecStart=/usr/libexec/xcat/genesis-qeth +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/xcat-genesis-qeth_1.0.bb b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/xcat-genesis-qeth_1.0.bb new file mode 100644 index 000000000..a009a3efd --- /dev/null +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/xcat-genesis-qeth_1.0.bb @@ -0,0 +1,31 @@ +SUMMARY = "xCAT Genesis qeth activation" +LICENSE = "EPL-1.0" +LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/EPL-1.0;md5=57f8d5e2b3e98ac6e088986c12bf94e6" + +SRC_URI = "file://genesis-qeth \ + file://xcat-genesis-qeth.service \ +" +S = "${UNPACKDIR}" + +COMPATIBLE_HOST = "s390x.*-linux" + +inherit systemd + +RDEPENDS:${PN} = "bash s390-tools-znetconf util-linux-logger xcat-genesis-init" + +SYSTEMD_SERVICE:${PN} = "xcat-genesis-qeth.service" +SYSTEMD_AUTO_ENABLE = "enable" + +do_install() { + install -d ${D}${libexecdir}/xcat + install -m 0755 ${UNPACKDIR}/genesis-qeth \ + ${D}${libexecdir}/xcat/genesis-qeth + + install -d ${D}${systemd_system_unitdir} + install -m 0644 ${UNPACKDIR}/xcat-genesis-qeth.service \ + ${D}${systemd_system_unitdir}/xcat-genesis-qeth.service +} + +FILES:${PN} = "${libexecdir}/xcat/genesis-qeth \ + ${systemd_system_unitdir}/xcat-genesis-qeth.service \ +" diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/packagegroups/packagegroup-xcat-genesis-hardware.bb b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/packagegroups/packagegroup-xcat-genesis-hardware.bb index 86dc30a07..c848a74f6 100644 --- a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/packagegroups/packagegroup-xcat-genesis-hardware.bb +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/packagegroups/packagegroup-xcat-genesis-hardware.bb @@ -48,4 +48,4 @@ RDEPENDS:${PN}:append:xcat-genesis-aarch64 = " dmidecode lshw" RDEPENDS:${PN}:append:xcat-genesis-ppc64 = " dmidecode xcat-genesis-hardware-control-iprutils" RDEPENDS:${PN}:append:xcat-genesis-ppc64le = " dmidecode xcat-genesis-hardware-control-iprutils" RDEPENDS:${PN}:append:xcat-genesis-riscv64 = " lshw" -RDEPENDS:${PN}:append:xcat-genesis-s390x = " s390-tools-znetconf" +RDEPENDS:${PN}:append:xcat-genesis-s390x = " xcat-genesis-qeth" From e1b679724935604b8b9f30828f7a1e8ff07f5478 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Sun, 6 Sep 2026 00:26:17 -0300 Subject: [PATCH 42/65] test(genesis): cover s390x qeth activation --- xCAT-test/unit/genesis_openembedded_qeth.t | 256 +++++++++++++++++++++ 1 file changed, 256 insertions(+) create mode 100644 xCAT-test/unit/genesis_openembedded_qeth.t diff --git a/xCAT-test/unit/genesis_openembedded_qeth.t b/xCAT-test/unit/genesis_openembedded_qeth.t new file mode 100644 index 000000000..a0203efdf --- /dev/null +++ b/xCAT-test/unit/genesis_openembedded_qeth.t @@ -0,0 +1,256 @@ +#!/usr/bin/env perl +use strict; +use warnings; + +use File::Path qw(make_path); +use File::Spec; +use File::Temp qw(tempdir); +use FindBin; +use Test::More; + +my $repo_root = File::Spec->catdir( $FindBin::Bin, '..', '..' ); +my $qeth_dir = File::Spec->catdir( + $repo_root, + qw(xCAT-genesis-builder oe meta-xcat-genesis recipes-connectivity xcat-genesis-qeth) +); +my $qeth_script = File::Spec->catfile( $qeth_dir, qw(files genesis-qeth) ); +my $qeth_service = File::Spec->catfile( $qeth_dir, qw(files xcat-genesis-qeth.service) ); +my $qeth_recipe = File::Spec->catfile( $qeth_dir, 'xcat-genesis-qeth_1.0.bb' ); +my $s390_tools_recipe = File::Spec->catfile( + $repo_root, + qw(xCAT-genesis-builder oe meta-xcat-genesis recipes-support s390-tools + s390-tools-znetconf_2.41.0.bb) +); + +sub write_file { + my ( $path, $contents, $mode ) = @_; + open( my $fh, '>', $path ) or die "Unable to write $path: $!"; + print {$fh} $contents; + close($fh); + chmod( $mode, $path ) if defined($mode); +} + +sub read_file { + my ($path) = @_; + open( my $fh, '<', $path ) or die "Unable to read $path: $!"; + my $contents = do { local $/; <$fh> }; + close($fh); + return $contents; +} + +my $root = tempdir( CLEANUP => 1 ); +my $bin = File::Spec->catdir( $root, 'bin' ); +my $cmdline = File::Spec->catfile( $root, 'cmdline' ); +my $configured = File::Spec->catfile( $root, 'configured' ); +my $unconfigured = File::Spec->catfile( $root, 'unconfigured' ); +my $command_log = File::Spec->catfile( $root, 'commands.log' ); +my $znetconf = File::Spec->catfile( $bin, 'znetconf' ); +my $status = File::Spec->catfile( $bin, 'genesis-status' ); + +make_path($bin); +write_file( + $znetconf, + <<'SH', 0755 +#!/bin/sh +printf 'znetconf %s\n' "$*" >>"$XCAT_TEST_LOG" +case "$1" in + -c) + cat "$XCAT_TEST_CONFIGURED" + exit "${XCAT_TEST_CONFIGURED_STATUS:-0}" + ;; + -u) + cat "$XCAT_TEST_UNCONFIGURED" + exit "${XCAT_TEST_UNCONFIGURED_STATUS:-0}" + ;; + -a) + [ "${XCAT_TEST_FAIL_CHANNELS:-}" != "$2" ] + ;; +esac +SH +); +write_file( + $status, + <<'SH', 0755 +#!/bin/sh +printf 'status %s\n' "$*" >>"$XCAT_TEST_LOG" +SH +); +write_file( + File::Spec->catfile( $bin, 'logger' ), + <<'SH', 0755 +#!/bin/sh +printf 'logger %s\n' "$*" >>"$XCAT_TEST_LOG" +SH +); + +my %base_environment = ( + PATH => "$bin:$ENV{PATH}", + XCAT_CMDLINE_FILE => $cmdline, + XCAT_STATUS_COMMAND => $status, + XCAT_TEST_CONFIGURED => $configured, + XCAT_TEST_LOG => $command_log, + XCAT_TEST_UNCONFIGURED => $unconfigured, + XCAT_ZNETCONF_COMMAND => $znetconf, +); + +sub run_qeth { + my ( $command_line, $environment ) = @_; + write_file( $cmdline, "$command_line\n" ); + write_file( $command_log, '' ); + write_file( $configured, $environment->{XCAT_TEST_CONFIGURED_OUTPUT} // '' ); + write_file( $unconfigured, $environment->{XCAT_TEST_UNCONFIGURED_OUTPUT} // '' ); + local %ENV = ( %ENV, %base_environment, %{$environment} ); + return system( '/bin/bash', $qeth_script ) >> 8; +} + +sub command_log { + return read_file($command_log); +} + +is( + run_qeth( 'console=ttysclp0 xcatd=192.0.2.1', + { XCAT_TEST_UNCONFIGURED_STATUS => 31 } ), + 0, + 'a system without ccwgroup devices needs no qeth setup' +); +unlike( command_log(), qr/^znetconf -a /m, 'the no-device path activates nothing' ); + +is( + run_qeth( + 'console=ttysclp0 xcatd=192.0.2.1', + { + XCAT_TEST_CONFIGURED_OUTPUT => + "0.0.0500,0.0.0501,0.0.0502 1731/01 OSA 10 qeth enc500 online\n" + } + ), + 0, + 'firmware-configured qeth devices need no activation' +); +unlike( command_log(), qr/^znetconf -a /m, 'firmware-configured devices are unchanged' ); + +is( + run_qeth( + 'rd.znet=qeth,0600,0.0.0601,0.0.0602,portno=1 xcatd=192.0.2.1', {} + ), + 0, + 'an explicit qeth triplet is accepted' +); +like( + command_log(), + qr/^znetconf -a 0\.0\.0600,0\.0\.0601,0\.0\.0602 -d qeth -o portno=1 -o layer2=1$/m, + 'explicit channels are normalized and default to layer 2' +); +like( command_log(), qr/^status network CONFIGURING_NETWORK qeth devices are ready$/m, + 'successful activation is published' ); + +is( + run_qeth( + 'rd.znet=qeth,0.0.0610,0.0.0611,0.0.0612,layer2=0,portname=test ' + . 'xcatd=192.0.2.1', + {} + ), + 0, + 'explicit qeth options are accepted' +); +like( + command_log(), + qr/^znetconf -a 0\.0\.0610,0\.0\.0611,0\.0\.0612 -d qeth -o layer2=0 -o portname=test$/m, + 'an explicit layer setting is not overridden' +); + +my $configured_channels = '0.0.0620,0.0.0621,0.0.0622'; +is( + run_qeth( + "rd.znet=qeth,$configured_channels xcatd=192.0.2.1", + { XCAT_TEST_CONFIGURED_OUTPUT => "$configured_channels 1731/01 OSA 10 qeth enc600 online\n" } + ), + 0, + 'an already configured triplet is left unchanged' +); +unlike( command_log(), qr/^znetconf -a /m, 'configured qeth channels are not regrouped' ); + +my $automatic_channels = '0.0.0700,0.0.0701,0.0.0702'; +is( + run_qeth( + 'xcatd=192.0.2.1', + { + XCAT_TEST_UNCONFIGURED_OUTPUT => + "Scanning for network devices...\n" + . "Device IDs Type Card Type CHPID Drv.\n" + . "0.0.0710,0.0.0711 3088/60 LCS OSA 20 lcs\n" + . "$automatic_channels 1731/01 OSA (QDIO) 10 qeth\n" + } + ), + 0, + 'unconfigured qeth devices are discovered' +); +like( + command_log(), + qr/^znetconf -a \Q$automatic_channels\E -d qeth -o layer2=1$/m, + 'discovered qeth devices are activated for DHCP' +); +unlike( command_log(), qr/^znetconf -a 0\.0\.0710/m, + 'other channel network types are not activated as qeth' ); + +is( + run_qeth( 'xcatd=192.0.2.1', { XCAT_TEST_UNCONFIGURED_STATUS => 9 } ), + 9, + 'a qeth discovery error is returned' +); +like( command_log(), qr/^status network DEGRADED Unable to inspect qeth devices/m, + 'a discovery error is published' ); + +is( + run_qeth( + 'rd.znet=ctc,0.0.0800,0.0.0801 xcatd=192.0.2.1', + { XCAT_TEST_UNCONFIGURED_OUTPUT => "$automatic_channels 1731/01 OSA 10 qeth\n" } + ), + 0, + 'a non-qeth rd.znet entry is not handled' +); +unlike( command_log(), qr/^znetconf -u$/m, 'explicit non-qeth setup disables qeth discovery' ); +unlike( command_log(), qr/^znetconf -a /m, 'non-qeth channels are not activated as qeth' ); + +is( + run_qeth( 'rd.znet=qeth,0.0.0900,0.0.0901 xcatd=192.0.2.1', {} ), + 1, + 'an incomplete qeth triplet fails' +); +like( command_log(), qr/^status network DEGRADED One or more qeth devices/m, + 'invalid boot parameters publish a degraded status' ); + +is( + run_qeth( + 'rd.znet=qeth,0.0.0910,0.0.0911,0.0.0912,bad?=1 xcatd=192.0.2.1', {} + ), + 1, + 'an invalid qeth option fails' +); +unlike( command_log(), qr/^znetconf -a /m, 'invalid options are rejected before activation' ); + +is( + run_qeth( + 'rd.znet=qeth,0.0.0a00,0.0.0a01,0.0.0a02 ' + . 'rd.znet=qeth,0.0.0b00,0.0.0b01,0.0.0b02 xcatd=192.0.2.1', + { XCAT_TEST_FAIL_CHANNELS => '0.0.0a00,0.0.0a01,0.0.0a02' } + ), + 1, + 'failure of one qeth triplet is reported' +); +like( command_log(), qr/^znetconf -a 0\.0\.0b00,0\.0\.0b01,0\.0\.0b02 /m, + 'remaining qeth triplets are still attempted' ); + +my $service_contents = read_file($qeth_service); +like( $service_contents, qr/^ConditionArchitecture=s390x$/m, + 'the activation service is limited to s390x' ); +like( $service_contents, qr/^ConditionKernelCommandLine=xcatd$/m, + 'the activation service requires a Genesis boot' ); +like( $service_contents, qr/^Before=.*\bNetworkManager\.service\b/m, + 'qeth activation precedes NetworkManager' ); +like( $service_contents, qr/^Before=.*\bxcat-genesis-network-state\.service\b/m, + 'qeth activation precedes management network selection' ); + +ok( -f $qeth_recipe, 'the qeth service has an OpenEmbedded recipe' ); +ok( -f $s390_tools_recipe, 'the minimal znetconf package has an OpenEmbedded recipe' ); + +done_testing(); From d62cf522904f80bdff4dff9050a898675e06c3bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Sun, 6 Sep 2026 00:26:57 -0300 Subject: [PATCH 43/65] docs(genesis): describe s390x qeth setup --- .../guides/code/genesis_openembedded_plan.rst | 15 ++++++++++++--- .../admin-guides/references/man8/mknb.8.rst | 2 +- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/docs/source/developers/guides/code/genesis_openembedded_plan.rst b/docs/source/developers/guides/code/genesis_openembedded_plan.rst index 07917459e..19e0fa6d9 100644 --- a/docs/source/developers/guides/code/genesis_openembedded_plan.rst +++ b/docs/source/developers/guides/code/genesis_openembedded_plan.rst @@ -93,9 +93,15 @@ IPv6-only deployment also needs matching support in xCAT server code, DHCP, boot firmware, and boot configuration. Those changes are outside this layer and must not be hidden inside the Genesis image. +On ``s390x``, Genesis activates qeth devices before NetworkManager starts. It +accepts the standard ``rd.znet=qeth,read,write,data[,option=value]`` parameter. +Without ``rd.znet``, it activates unconfigured qeth devices in layer 2 mode so +NetworkManager can request DHCP leases. Devices already configured by DPM or +another firmware path are left unchanged. + The ``s390x`` image uses virtio networking under QEMU. QEMU does not emulate -qeth, and Genesis does not yet configure qeth channel groups. Physical LPAR -and z/VM Genesis networking therefore remain unvalidated. +qeth, so the activation service exits without changing the virtio interface. +Physical LPAR and z/VM Genesis networking remain unvalidated. Genesis does not use the shared CEC serial as an s390x node identifier. QEMU validation covers network IPL, DHCP option 209, TFTP, and the network-specific ``pxelinux.cfg``-style configuration written by ``mknb``. @@ -111,6 +117,7 @@ the xcatd wire protocol. The boot sequence is split into ordered systemd services: +#. On ``s390x``, Genesis activates qeth channel groups. #. NetworkManager configures candidate interfaces. #. The network state service selects a management path. #. Registration asks xcatd for the node destiny. @@ -273,7 +280,7 @@ actions. cannot certify platform firmware, BMC behavior, storage-controller tools, RDMA firmware operations, GPUs, Secure Boot on vendor firmware, or board-specific device trees. Physical ``s390x`` support also requires a qeth -activation path and IBM Z LPAR or z/VM validation. +channel test on an IBM Z LPAR or z/VM guest. References ---------- @@ -286,6 +293,8 @@ References `_ * `QEMU s390x network boot `_ +* `IBM znetconf + `_ * `NetworkManager initrd generator `_ * `systemd system extensions diff --git a/docs/source/guides/admin-guides/references/man8/mknb.8.rst b/docs/source/guides/admin-guides/references/man8/mknb.8.rst index 58c9bedb6..edb3579c9 100644 --- a/docs/source/guides/admin-guides/references/man8/mknb.8.rst +++ b/docs/source/guides/admin-guides/references/man8/mknb.8.rst @@ -48,7 +48,7 @@ Canonical \ ``ppc64``\ images are big-endian. xCAT marks them so \ ``ppc64le``\ riscv64 nodes boot through UEFI firmware and grub2. For riscv64, \ **mknb**\ publishes the Genesis kernel and initramfs and writes one grub2 configuration per network under \ ``/tftpboot/boot/grub2``\ , named \ ``grub.cfg-``\ followed by the network hex prefix, so that \ ``grub2.riscv64``\ loaded by the firmware can start node discovery. The per-node files written by \ **nodeset**\ take priority over these network files. Networks served by a \ ``:noboot``\ interface in \ ``site.dhcpinterfaces``\ get no discovery configuration. -QEMU s390-ccw guests receive a network-specific \ ``pxelinux.cfg``\ -style configuration through DHCP option 209. The firmware resolves this name below \ ``pxelinux.cfg``\ . Leave \ ``noderes.netboot``\ unset while a QEMU s390x node uses Genesis discovery. s390x Genesis does not use the shared IBM Z machine serial as a node identifier. QEMU validation covers network IPL through Genesis startup; the discovery exchange with xcatd remains unvalidated for s390x. Legacy xCAT z/VM operating-system provisioning remains unchanged. Physical LPAR and z/VM Genesis networking remain unvalidated because QEMU does not emulate qeth and Genesis does not yet configure qeth channel groups. +QEMU s390-ccw guests receive a network-specific \ ``pxelinux.cfg``\ -style configuration through DHCP option 209. The firmware resolves this name below \ ``pxelinux.cfg``\ . Leave \ ``noderes.netboot``\ unset while a QEMU s390x node uses Genesis discovery. s390x Genesis does not use the shared IBM Z machine serial as a node identifier. On physical IBM Z systems, Genesis accepts \ ``rd.znet=qeth,read,write,data[,option=value]``\ and activates qeth before NetworkManager starts. Without \ ``rd.znet``\ , it activates unconfigured qeth devices in layer 2 mode. Devices already configured by DPM or firmware are unchanged. QEMU validation covers network IPL through Genesis startup; the discovery exchange with xcatd remains unvalidated for s390x. Legacy xCAT z/VM operating-system provisioning remains unchanged. Physical LPAR and z/VM Genesis networking remain unvalidated because QEMU does not emulate qeth. Run \ **makedhcp -n**\ after installing the s390x image and before booting s390x nodes. This adds the required DHCP option to existing configurations. From 3b69095b73cd79c57c0cb360e39070a9740b587e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Sun, 6 Sep 2026 00:32:52 -0300 Subject: [PATCH 44/65] fix(genesis): use correct s390-tools paths --- .../s390-tools/s390-tools-znetconf_2.41.0.bb | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-support/s390-tools/s390-tools-znetconf_2.41.0.bb b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-support/s390-tools/s390-tools-znetconf_2.41.0.bb index 37ad5d5b7..a97b5379e 100644 --- a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-support/s390-tools/s390-tools-znetconf_2.41.0.bb +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-support/s390-tools/s390-tools-znetconf_2.41.0.bb @@ -6,8 +6,6 @@ LIC_FILES_CHKSUM = "file://LICENSE;md5=f5118f167b055bfd7c3450803f1847af" SRC_URI = "git://github.com/ibm-s390-linux/s390-tools;protocol=https;branch=master;tag=v${PV}" SRCREV = "5e07b30bdf67623e7a6d3850e26208b642d416d7" -S = "${WORKDIR}/git" - COMPATIBLE_HOST = "s390x.*-linux" RDEPENDS:${PN} = "bash coreutils findutils gawk grep kmod sed udev util-linux-getopt util-linux-logger" @@ -15,14 +13,14 @@ RDEPENDS:${PN} = "bash coreutils findutils gawk grep kmod sed udev util-linux-ge do_compile[noexec] = "1" do_install() { - install -d ${D}${base_sbindir} ${D}${base_libdir}/s390-tools + install -d ${D}${base_sbindir} ${D}${nonarch_base_libdir}/s390-tools sed 's/%S390_TOOLS_VERSION%/${PV}/g' ${S}/zconf/znetconf \ >${D}${base_sbindir}/znetconf chmod 0755 ${D}${base_sbindir}/znetconf install -m 0755 ${S}/zconf/lsznet.raw \ - ${D}${base_libdir}/s390-tools/lsznet.raw + ${D}${nonarch_base_libdir}/s390-tools/lsznet.raw install -m 0755 ${S}/zconf/znetcontrolunits \ - ${D}${base_libdir}/s390-tools/znetcontrolunits + ${D}${nonarch_base_libdir}/s390-tools/znetcontrolunits } -FILES:${PN} = "${base_sbindir}/znetconf ${base_libdir}/s390-tools" +FILES:${PN} = "${base_sbindir}/znetconf ${nonarch_base_libdir}/s390-tools" From 173b66f1e1ce1ccb8bb08bd9f7cf41b135a35828 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Sun, 6 Sep 2026 00:54:58 -0300 Subject: [PATCH 45/65] fix(genesis): initialize qeth state arrays --- .../recipes-connectivity/xcat-genesis-qeth/files/genesis-qeth | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/files/genesis-qeth b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/files/genesis-qeth index 65e3d3d42..9a4ab8f64 100644 --- a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/files/genesis-qeth +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/files/genesis-qeth @@ -80,7 +80,7 @@ activate_channels() { } configured_output=$("$znetconf_command" -c 2>/dev/null || true) -declare -a cmdline_arguments explicit_specs ready_channels failed_channels +declare -a cmdline_arguments=() explicit_specs=() ready_channels=() failed_channels=() rd_znet_present=false read -r -a cmdline_arguments <"$cmdline_file" for argument in "${cmdline_arguments[@]}"; do From 9f9f535c6af242e9d212d2c55eedaa616119e5b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Sun, 6 Sep 2026 01:35:28 -0300 Subject: [PATCH 46/65] test(genesis): load boot policy from source --- xCAT-test/unit/mknb_xcatd_address.t | 1 + 1 file changed, 1 insertion(+) diff --git a/xCAT-test/unit/mknb_xcatd_address.t b/xCAT-test/unit/mknb_xcatd_address.t index 7039b1999..0a69dd2e0 100644 --- a/xCAT-test/unit/mknb_xcatd_address.t +++ b/xCAT-test/unit/mknb_xcatd_address.t @@ -5,6 +5,7 @@ use warnings; use FindBin; use lib "$FindBin::Bin/../lib"; +use lib "$FindBin::Bin/../../perl-xCAT"; use File::Slurper qw(write_text); use File::Path qw(make_path); use File::Temp qw(tempdir); From e53d351d170fb80e52c1d3256a46c917ac9f7d08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Sun, 6 Sep 2026 02:02:58 -0300 Subject: [PATCH 47/65] fix(genesis): harden qeth activation --- .../xcat-genesis-qeth/files/genesis-qeth | 19 +++++++++++++++++++ .../files/xcat-genesis-qeth.service | 1 + 2 files changed, 20 insertions(+) diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/files/genesis-qeth b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/files/genesis-qeth index 9a4ab8f64..a1f124d1c 100644 --- a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/files/genesis-qeth +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/files/genesis-qeth @@ -5,6 +5,7 @@ LC_ALL=C export LC_ALL cmdline_file=${XCAT_CMDLINE_FILE:-/proc/cmdline} +cio_settle_file=${XCAT_CIO_SETTLE_FILE:-/proc/cio_settle} status_command=${XCAT_STATUS_COMMAND:-/usr/libexec/xcat/genesis-status} znetconf_command=${XCAT_ZNETCONF_COMMAND:-/sbin/znetconf} @@ -41,6 +42,15 @@ configured_channels() { return 1 } +processed_channels() { + local channels=$1 processed + + for processed in "${ready_channels[@]}" "${failed_channels[@]}"; do + [ "$processed" = "$channels" ] && return 0 + done + return 1 +} + activate_channels() { local channels=$1 shift @@ -48,6 +58,11 @@ activate_channels() { local option output result local have_layer2=false + if processed_channels "$channels"; then + log "qeth channels already processed: $channels" + return 0 + fi + for option in "$@"; do if [[ ! $option =~ ^[A-Za-z_][A-Za-z0-9_./-]*=[A-Za-z0-9_./:+-]+$ ]]; then log "Invalid qeth option for $channels: $option" @@ -79,6 +94,10 @@ activate_channels() { return 0 } +if [ -w "$cio_settle_file" ]; then + printf '1\n' >"$cio_settle_file" || log 'Unable to wait for IBM Z channel devices' +fi + configured_output=$("$znetconf_command" -c 2>/dev/null || true) declare -a cmdline_arguments=() explicit_specs=() ready_channels=() failed_channels=() rd_znet_present=false diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/files/xcat-genesis-qeth.service b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/files/xcat-genesis-qeth.service index 31cf27052..29222c48a 100644 --- a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/files/xcat-genesis-qeth.service +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/files/xcat-genesis-qeth.service @@ -8,6 +8,7 @@ Before=NetworkManager.service xcat-genesis-network-state.service [Service] Type=oneshot ExecStart=/usr/libexec/xcat/genesis-qeth +TimeoutStartSec=120 RemainAfterExit=yes [Install] From 974d1250c4eca2b14235a57b5a592c61f91aaf62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Sun, 6 Sep 2026 02:03:03 -0300 Subject: [PATCH 48/65] test(genesis): cover qeth safeguards --- xCAT-test/unit/genesis_openembedded_qeth.t | 23 ++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/xCAT-test/unit/genesis_openembedded_qeth.t b/xCAT-test/unit/genesis_openembedded_qeth.t index a0203efdf..d5d724bd1 100644 --- a/xCAT-test/unit/genesis_openembedded_qeth.t +++ b/xCAT-test/unit/genesis_openembedded_qeth.t @@ -42,6 +42,7 @@ my $root = tempdir( CLEANUP => 1 ); my $bin = File::Spec->catdir( $root, 'bin' ); my $cmdline = File::Spec->catfile( $root, 'cmdline' ); my $configured = File::Spec->catfile( $root, 'configured' ); +my $cio_settle = File::Spec->catfile( $root, 'cio_settle' ); my $unconfigured = File::Spec->catfile( $root, 'unconfigured' ); my $command_log = File::Spec->catfile( $root, 'commands.log' ); my $znetconf = File::Spec->catfile( $bin, 'znetconf' ); @@ -85,6 +86,7 @@ SH my %base_environment = ( PATH => "$bin:$ENV{PATH}", + XCAT_CIO_SETTLE_FILE => $cio_settle, XCAT_CMDLINE_FILE => $cmdline, XCAT_STATUS_COMMAND => $status, XCAT_TEST_CONFIGURED => $configured, @@ -97,6 +99,7 @@ sub run_qeth { my ( $command_line, $environment ) = @_; write_file( $cmdline, "$command_line\n" ); write_file( $command_log, '' ); + write_file( $cio_settle, '' ); write_file( $configured, $environment->{XCAT_TEST_CONFIGURED_OUTPUT} // '' ); write_file( $unconfigured, $environment->{XCAT_TEST_UNCONFIGURED_OUTPUT} // '' ); local %ENV = ( %ENV, %base_environment, %{$environment} ); @@ -114,6 +117,7 @@ is( 'a system without ccwgroup devices needs no qeth setup' ); unlike( command_log(), qr/^znetconf -a /m, 'the no-device path activates nothing' ); +is( read_file($cio_settle), "1\n", 'channel discovery waits for pending CIO work' ); is( run_qeth( @@ -124,9 +128,9 @@ is( } ), 0, - 'firmware-configured qeth devices need no activation' + 'configured qeth devices need no activation' ); -unlike( command_log(), qr/^znetconf -a /m, 'firmware-configured devices are unchanged' ); +unlike( command_log(), qr/^znetconf -a /m, 'configured devices are unchanged' ); is( run_qeth( @@ -143,6 +147,19 @@ like( like( command_log(), qr/^status network CONFIGURING_NETWORK qeth devices are ready$/m, 'successful activation is published' ); +is( + run_qeth( + 'rd.znet=qeth,0.0.0600,0.0.0601,0.0.0602 ' + . 'rd.znet=qeth,0.0.0600,0.0.0601,0.0.0602 xcatd=192.0.2.1', + {} + ), + 0, + 'a duplicate qeth triplet is accepted once' +); +my @duplicate_activations = + command_log() =~ /^znetconf -a 0\.0\.0600,0\.0\.0601,0\.0\.0602 /mg; +is( scalar @duplicate_activations, 1, 'duplicate qeth channels are not regrouped' ); + is( run_qeth( 'rd.znet=qeth,0.0.0610,0.0.0611,0.0.0612,layer2=0,portname=test ' @@ -249,6 +266,8 @@ like( $service_contents, qr/^Before=.*\bNetworkManager\.service\b/m, 'qeth activation precedes NetworkManager' ); like( $service_contents, qr/^Before=.*\bxcat-genesis-network-state\.service\b/m, 'qeth activation precedes management network selection' ); +like( $service_contents, qr/^TimeoutStartSec=120$/m, + 'a stalled qeth command cannot block network startup indefinitely' ); ok( -f $qeth_recipe, 'the qeth service has an OpenEmbedded recipe' ); ok( -f $s390_tools_recipe, 'the minimal znetconf package has an OpenEmbedded recipe' ); From 959b0d2d3c4ce10d3d3bc66699d67321ff2c4737 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Sun, 6 Sep 2026 02:03:08 -0300 Subject: [PATCH 49/65] fix(genesis): preserve s390x LPAR identity --- .../files/xcat-genesis-console/src/state.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/state.c b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/state.c index 26d80e34e..b6a356eab 100644 --- a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/state.c +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/state.c @@ -222,9 +222,11 @@ static void set_s390_identity(const char *proc_root, struct console_state *state char path[VALUE_SIZE * 2]; snprintf(path, sizeof(path), "%s/sysinfo", proc_root); - if (!useful_identity(state->uuid) && - !xcat_read_colon_key(path, "VM00 UUID", state->uuid, sizeof(state->uuid))) - xcat_read_colon_key(path, "LPAR UUID", state->uuid, sizeof(state->uuid)); + if (!useful_identity(state->uuid)) { + xcat_read_colon_key(path, "VM00 UUID", state->uuid, sizeof(state->uuid)); + if (!useful_identity(state->uuid)) + xcat_read_colon_key(path, "LPAR UUID", state->uuid, sizeof(state->uuid)); + } } static void split_action(const char *destiny, char *action, size_t action_size, char *target, From 9a6906044832592f02e8f1b88045f5b21e2127bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Sun, 6 Sep 2026 02:03:11 -0300 Subject: [PATCH 50/65] test(genesis): cover empty s390x guest UUID --- xCAT-test/unit/genesis_openembedded_console.t | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/xCAT-test/unit/genesis_openembedded_console.t b/xCAT-test/unit/genesis_openembedded_console.t index ae639bcf8..069c9ddcb 100644 --- a/xCAT-test/unit/genesis_openembedded_console.t +++ b/xCAT-test/unit/genesis_openembedded_console.t @@ -285,6 +285,14 @@ SYSINFO like( $probe_output, qr/^uuid=93724168-fda3-429b-8b28-a5d245dcb3ff$/m, 'console uses the LPAR UUID when no guest UUID is available' ); +write_file( File::Spec->catfile( $proc_root, 'sysinfo' ), <<'SYSINFO' ); +VM00 UUID: +LPAR UUID: 93724168-fda3-429b-8b28-a5d245dcb3ff +SYSINFO +( $probe_status, $probe_output ) = run_identity_probe(); +like( $probe_output, + qr/^uuid=93724168-fda3-429b-8b28-a5d245dcb3ff$/m, + 'console ignores an empty guest UUID and uses the LPAR UUID' ); unlink( File::Spec->catfile( $proc_root, 'sysinfo' ) ); $environment{XCAT_TEST_ARCH} = 'x86_64'; write_file( File::Spec->catfile( $dmi_root, 'product_serial' ), From 20234cdacff098f1ada4c116d4704d277c51763a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Sun, 6 Sep 2026 02:03:16 -0300 Subject: [PATCH 51/65] docs(genesis): clarify physical qeth limits --- .../developers/guides/code/genesis_openembedded_plan.rst | 6 ++++-- docs/source/guides/admin-guides/references/man8/mknb.8.rst | 2 +- xCAT-client/pods/man8/mknb.8.pod | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/source/developers/guides/code/genesis_openembedded_plan.rst b/docs/source/developers/guides/code/genesis_openembedded_plan.rst index 19e0fa6d9..7d05bfb8a 100644 --- a/docs/source/developers/guides/code/genesis_openembedded_plan.rst +++ b/docs/source/developers/guides/code/genesis_openembedded_plan.rst @@ -96,8 +96,10 @@ and must not be hidden inside the Genesis image. On ``s390x``, Genesis activates qeth devices before NetworkManager starts. It accepts the standard ``rd.znet=qeth,read,write,data[,option=value]`` parameter. Without ``rd.znet``, it activates unconfigured qeth devices in layer 2 mode so -NetworkManager can request DHCP leases. Devices already configured by DPM or -another firmware path are left unchanged. +NetworkManager can request DHCP leases. Devices reported as configured by +``znetconf`` are not regrouped. Genesis does not import DPM auto-configuration +data. Layer 3 and IP-mode VSWITCH configurations require ``layer2=0`` in +``rd.znet``. The ``s390x`` image uses virtio networking under QEMU. QEMU does not emulate qeth, so the activation service exits without changing the virtio interface. diff --git a/docs/source/guides/admin-guides/references/man8/mknb.8.rst b/docs/source/guides/admin-guides/references/man8/mknb.8.rst index edb3579c9..f64294fee 100644 --- a/docs/source/guides/admin-guides/references/man8/mknb.8.rst +++ b/docs/source/guides/admin-guides/references/man8/mknb.8.rst @@ -48,7 +48,7 @@ Canonical \ ``ppc64``\ images are big-endian. xCAT marks them so \ ``ppc64le``\ riscv64 nodes boot through UEFI firmware and grub2. For riscv64, \ **mknb**\ publishes the Genesis kernel and initramfs and writes one grub2 configuration per network under \ ``/tftpboot/boot/grub2``\ , named \ ``grub.cfg-``\ followed by the network hex prefix, so that \ ``grub2.riscv64``\ loaded by the firmware can start node discovery. The per-node files written by \ **nodeset**\ take priority over these network files. Networks served by a \ ``:noboot``\ interface in \ ``site.dhcpinterfaces``\ get no discovery configuration. -QEMU s390-ccw guests receive a network-specific \ ``pxelinux.cfg``\ -style configuration through DHCP option 209. The firmware resolves this name below \ ``pxelinux.cfg``\ . Leave \ ``noderes.netboot``\ unset while a QEMU s390x node uses Genesis discovery. s390x Genesis does not use the shared IBM Z machine serial as a node identifier. On physical IBM Z systems, Genesis accepts \ ``rd.znet=qeth,read,write,data[,option=value]``\ and activates qeth before NetworkManager starts. Without \ ``rd.znet``\ , it activates unconfigured qeth devices in layer 2 mode. Devices already configured by DPM or firmware are unchanged. QEMU validation covers network IPL through Genesis startup; the discovery exchange with xcatd remains unvalidated for s390x. Legacy xCAT z/VM operating-system provisioning remains unchanged. Physical LPAR and z/VM Genesis networking remain unvalidated because QEMU does not emulate qeth. +QEMU s390-ccw guests receive a network-specific \ ``pxelinux.cfg``\ -style configuration through DHCP option 209. The firmware resolves this name below \ ``pxelinux.cfg``\ . Leave \ ``noderes.netboot``\ unset while a QEMU s390x node uses Genesis discovery. s390x Genesis does not use the shared IBM Z machine serial as a node identifier. On physical IBM Z, Genesis accepts \ ``rd.znet=qeth,read,write,data[,option=value]``\ and activates qeth before NetworkManager starts. Without \ ``rd.znet``\ , it activates unconfigured qeth devices in layer 2 mode. Devices reported as configured by \ ``znetconf``\ are not regrouped. Genesis does not import DPM auto-configuration data. Layer 3 and IP-mode VSWITCH configurations require \ ``layer2=0``\ . QEMU validation covers network IPL through Genesis startup; the discovery exchange with xcatd remains unvalidated for s390x. Legacy xCAT z/VM operating-system provisioning remains unchanged. Physical LPAR and z/VM Genesis networking remain unvalidated because QEMU does not emulate qeth. Run \ **makedhcp -n**\ after installing the s390x image and before booting s390x nodes. This adds the required DHCP option to existing configurations. diff --git a/xCAT-client/pods/man8/mknb.8.pod b/xCAT-client/pods/man8/mknb.8.pod index fe0fca60b..bd37db74f 100644 --- a/xCAT-client/pods/man8/mknb.8.pod +++ b/xCAT-client/pods/man8/mknb.8.pod @@ -30,7 +30,7 @@ Canonical C images are big-endian. xCAT marks them so C nodes do riscv64 nodes boot through UEFI firmware and grub2. For riscv64, B publishes the Genesis kernel and initramfs and writes one grub2 configuration per network under C, named C followed by the network hex prefix, so that C loaded by the firmware can start node discovery. The per-node files written by B take priority over these network files. Networks served by a C<:noboot> interface in C get no discovery configuration. -QEMU s390-ccw guests receive a network-specific C-style configuration through DHCP option 209. The firmware resolves this name below C. Leave C unset while a QEMU s390x node uses Genesis discovery. s390x Genesis does not use the shared IBM Z machine serial as a node identifier. QEMU validation covers network IPL through Genesis startup; the discovery exchange with xcatd remains unvalidated for s390x. Legacy xCAT z/VM operating-system provisioning remains unchanged. Physical LPAR and z/VM Genesis networking remain unvalidated because QEMU does not emulate qeth and Genesis does not yet configure qeth channel groups. +QEMU s390-ccw guests receive a network-specific C-style configuration through DHCP option 209. The firmware resolves this name below C. Leave C unset while a QEMU s390x node uses Genesis discovery. s390x Genesis does not use the shared IBM Z machine serial as a node identifier. On physical IBM Z, Genesis accepts C and activates qeth before NetworkManager starts. Without C, it activates unconfigured qeth devices in layer 2 mode. Devices reported as configured by C are not regrouped. Genesis does not import DPM auto-configuration data. Layer 3 and IP-mode VSWITCH configurations require C. QEMU validation covers network IPL through Genesis startup; the discovery exchange with xcatd remains unvalidated for s390x. Legacy xCAT z/VM operating-system provisioning remains unchanged. Physical LPAR and z/VM Genesis networking remain unvalidated because QEMU does not emulate qeth. Run B after installing the s390x image and before booting s390x nodes. This adds the required DHCP option to existing configurations. From 5edadd2247fd9b2c00a7f92bb75ab7b7fbdb6bd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Sun, 6 Sep 2026 02:03:17 -0300 Subject: [PATCH 52/65] docs(genesis): document architecture query --- xCAT-genesis-builder/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/xCAT-genesis-builder/README.md b/xCAT-genesis-builder/README.md index cd7324825..dd3cbb26a 100644 --- a/xCAT-genesis-builder/README.md +++ b/xCAT-genesis-builder/README.md @@ -18,6 +18,7 @@ xCAT-genesis-builder/oe/build x86_64 The build command accepts `x86`, `x86_64`, `ppc64`, `ppc64le`, `armv7hf`, `aarch64`, `riscv64`, and `s390x`. Multiple architectures are built in the order given. +Run `xCAT-genesis-builder/oe/build --list-architectures` to print this list. Artifacts are written below `xCAT-genesis-builder/oe/.work/build/tmp/deploy/images`. The `x86` artifact uses an i686 CPU baseline. The build carries the reviewed Yocto release key in `oe/keys` and verifies its fingerprint locally. From 86988d5edcd472ed1d3adc8fae72cf1a25366c92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Sun, 6 Sep 2026 02:11:20 -0300 Subject: [PATCH 53/65] fix(genesis): resolve KAS paths locally --- xCAT-genesis-builder/oe/kas/aarch64.yml | 2 +- xCAT-genesis-builder/oe/kas/armv7hf.yml | 2 +- xCAT-genesis-builder/oe/kas/common.yml | 4 ++-- xCAT-genesis-builder/oe/kas/ppc64.yml | 2 +- xCAT-genesis-builder/oe/kas/ppc64le.yml | 2 +- xCAT-genesis-builder/oe/kas/riscv64.yml | 2 +- xCAT-genesis-builder/oe/kas/s390x.yml | 2 +- xCAT-genesis-builder/oe/kas/x86.yml | 2 +- xCAT-genesis-builder/oe/kas/x86_64.yml | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/xCAT-genesis-builder/oe/kas/aarch64.yml b/xCAT-genesis-builder/oe/kas/aarch64.yml index 1e9a76978..c2480affc 100644 --- a/xCAT-genesis-builder/oe/kas/aarch64.yml +++ b/xCAT-genesis-builder/oe/kas/aarch64.yml @@ -1,6 +1,6 @@ header: version: 19 includes: - - xCAT-genesis-builder/oe/kas/common.yml + - common.yml machine: xcat-genesis-aarch64 diff --git a/xCAT-genesis-builder/oe/kas/armv7hf.yml b/xCAT-genesis-builder/oe/kas/armv7hf.yml index 4a690f8a5..f3992499c 100644 --- a/xCAT-genesis-builder/oe/kas/armv7hf.yml +++ b/xCAT-genesis-builder/oe/kas/armv7hf.yml @@ -1,6 +1,6 @@ header: version: 19 includes: - - xCAT-genesis-builder/oe/kas/common.yml + - common.yml machine: xcat-genesis-armv7hf diff --git a/xCAT-genesis-builder/oe/kas/common.yml b/xCAT-genesis-builder/oe/kas/common.yml index d30fdaeaf..fd28a5f9a 100644 --- a/xCAT-genesis-builder/oe/kas/common.yml +++ b/xCAT-genesis-builder/oe/kas/common.yml @@ -5,7 +5,7 @@ signers: yocto-release: fingerprint: 2AFB13F28FBBB0D1B9DAF63087EB3D32FB631AD9 repo: xcat-core - path: xCAT-genesis-builder/oe/keys/yocto-release.asc + path: ../keys/yocto-release.asc distro: xcat-genesis target: xcat-genesis-image @@ -40,7 +40,7 @@ repos: xcat-core: layers: - xCAT-genesis-builder/oe/meta-xcat-genesis: + ../meta-xcat-genesis: local_conf_header: xcat-genesis: | diff --git a/xCAT-genesis-builder/oe/kas/ppc64.yml b/xCAT-genesis-builder/oe/kas/ppc64.yml index 73d223879..4ab3491b3 100644 --- a/xCAT-genesis-builder/oe/kas/ppc64.yml +++ b/xCAT-genesis-builder/oe/kas/ppc64.yml @@ -1,6 +1,6 @@ header: version: 19 includes: - - xCAT-genesis-builder/oe/kas/common.yml + - common.yml machine: xcat-genesis-ppc64 diff --git a/xCAT-genesis-builder/oe/kas/ppc64le.yml b/xCAT-genesis-builder/oe/kas/ppc64le.yml index ba347c249..ad2b26e2b 100644 --- a/xCAT-genesis-builder/oe/kas/ppc64le.yml +++ b/xCAT-genesis-builder/oe/kas/ppc64le.yml @@ -1,6 +1,6 @@ header: version: 19 includes: - - xCAT-genesis-builder/oe/kas/common.yml + - common.yml machine: xcat-genesis-ppc64le diff --git a/xCAT-genesis-builder/oe/kas/riscv64.yml b/xCAT-genesis-builder/oe/kas/riscv64.yml index a7ec93801..c4bbbbd78 100644 --- a/xCAT-genesis-builder/oe/kas/riscv64.yml +++ b/xCAT-genesis-builder/oe/kas/riscv64.yml @@ -1,6 +1,6 @@ header: version: 19 includes: - - xCAT-genesis-builder/oe/kas/common.yml + - common.yml machine: xcat-genesis-riscv64 diff --git a/xCAT-genesis-builder/oe/kas/s390x.yml b/xCAT-genesis-builder/oe/kas/s390x.yml index ac06515bd..2201cd8f9 100644 --- a/xCAT-genesis-builder/oe/kas/s390x.yml +++ b/xCAT-genesis-builder/oe/kas/s390x.yml @@ -1,6 +1,6 @@ header: version: 19 includes: - - xCAT-genesis-builder/oe/kas/common.yml + - common.yml machine: xcat-genesis-s390x diff --git a/xCAT-genesis-builder/oe/kas/x86.yml b/xCAT-genesis-builder/oe/kas/x86.yml index e7d7334ff..795232389 100644 --- a/xCAT-genesis-builder/oe/kas/x86.yml +++ b/xCAT-genesis-builder/oe/kas/x86.yml @@ -1,6 +1,6 @@ header: version: 19 includes: - - xCAT-genesis-builder/oe/kas/common.yml + - common.yml machine: xcat-genesis-x86 diff --git a/xCAT-genesis-builder/oe/kas/x86_64.yml b/xCAT-genesis-builder/oe/kas/x86_64.yml index 9bc7a83f4..05fa600be 100644 --- a/xCAT-genesis-builder/oe/kas/x86_64.yml +++ b/xCAT-genesis-builder/oe/kas/x86_64.yml @@ -1,6 +1,6 @@ header: version: 19 includes: - - xCAT-genesis-builder/oe/kas/common.yml + - common.yml machine: xcat-genesis-x86-64 From 947ac1c565e1c6fe1439753f229719405a125e49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Sun, 6 Sep 2026 02:11:20 -0300 Subject: [PATCH 54/65] test(genesis): cover local KAS paths --- xCAT-test/unit/genesis_openembedded.t | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/xCAT-test/unit/genesis_openembedded.t b/xCAT-test/unit/genesis_openembedded.t index a1615fda8..b5fe34d4d 100644 --- a/xCAT-test/unit/genesis_openembedded.t +++ b/xCAT-test/unit/genesis_openembedded.t @@ -25,10 +25,10 @@ like( $kas, qr/tag: yocto-6\.0\.2/, 'build uses Yocto 6.0.2' ); like( $kas, qr/fingerprint: 2AFB13F28FBBB0D1B9DAF63087EB3D32FB631AD9/, 'build trusts the Yocto release key' ); like( $kas, - qr{yocto-release:.*?repo: xcat-core.*?path: xCAT-genesis-builder/oe/keys/yocto-release\.asc}s, + qr{yocto-release:.*?repo: xcat-core.*?path: \.\./keys/yocto-release\.asc}s, 'build loads the Yocto release key from the source tree' ); like( $kas, - qr{xcat-core:.*?layers:.*?xCAT-genesis-builder/oe/meta-xcat-genesis:}s, + qr{xcat-core:.*?layers:.*?\.\./meta-xcat-genesis:}s, 'build loads the Genesis layer from the source tree' ); unlike( $kas, qr/gpg_keyserver:/, 'build does not depend on a public keyserver' ); @@ -57,49 +57,49 @@ like( $kas, 'meta-openembedded revision is pinned' ); my $x86_kas = read_file('xCAT-genesis-builder/oe/kas/x86_64.yml'); -like( $x86_kas, qr{includes:\s*\n\s*- xCAT-genesis-builder/oe/kas/common\.yml}, +like( $x86_kas, qr{includes:\s*\n\s*- common\.yml}, 'x86_64 build includes the common configuration' ); like( $x86_kas, qr/^machine: xcat-genesis-x86-64$/m, 'x86_64 build selects its machine' ); my $x86_32_kas = read_file('xCAT-genesis-builder/oe/kas/x86.yml'); -like( $x86_32_kas, qr{includes:\s*\n\s*- xCAT-genesis-builder/oe/kas/common\.yml}, +like( $x86_32_kas, qr{includes:\s*\n\s*- common\.yml}, 'x86 build includes the common configuration' ); like( $x86_32_kas, qr/^machine: xcat-genesis-x86$/m, 'x86 build selects its machine' ); my $armv7hf_kas = read_file('xCAT-genesis-builder/oe/kas/armv7hf.yml'); -like( $armv7hf_kas, qr{includes:\s*\n\s*- xCAT-genesis-builder/oe/kas/common\.yml}, +like( $armv7hf_kas, qr{includes:\s*\n\s*- common\.yml}, 'armv7hf build includes the common configuration' ); like( $armv7hf_kas, qr/^machine: xcat-genesis-armv7hf$/m, 'armv7hf build selects its machine' ); my $aarch64_kas = read_file('xCAT-genesis-builder/oe/kas/aarch64.yml'); -like( $aarch64_kas, qr{includes:\s*\n\s*- xCAT-genesis-builder/oe/kas/common\.yml}, +like( $aarch64_kas, qr{includes:\s*\n\s*- common\.yml}, 'aarch64 build includes the common configuration' ); like( $aarch64_kas, qr/^machine: xcat-genesis-aarch64$/m, 'aarch64 build selects its machine' ); my $riscv64_kas = read_file('xCAT-genesis-builder/oe/kas/riscv64.yml'); -like( $riscv64_kas, qr{includes:\s*\n\s*- xCAT-genesis-builder/oe/kas/common\.yml}, +like( $riscv64_kas, qr{includes:\s*\n\s*- common\.yml}, 'riscv64 build includes the common configuration' ); like( $riscv64_kas, qr/^machine: xcat-genesis-riscv64$/m, 'riscv64 build selects its machine' ); my $s390x_kas = read_file('xCAT-genesis-builder/oe/kas/s390x.yml'); -like( $s390x_kas, qr{includes:\s*\n\s*- xCAT-genesis-builder/oe/kas/common\.yml}, +like( $s390x_kas, qr{includes:\s*\n\s*- common\.yml}, 's390x build includes the common configuration' ); like( $s390x_kas, qr/^machine: xcat-genesis-s390x$/m, 's390x build selects its machine' ); my $ppc64le_kas = read_file('xCAT-genesis-builder/oe/kas/ppc64le.yml'); -like( $ppc64le_kas, qr{includes:\s*\n\s*- xCAT-genesis-builder/oe/kas/common\.yml}, +like( $ppc64le_kas, qr{includes:\s*\n\s*- common\.yml}, 'ppc64le build includes the common configuration' ); like( $ppc64le_kas, qr/^machine: xcat-genesis-ppc64le$/m, 'ppc64le build selects its machine' ); my $ppc64_kas = read_file('xCAT-genesis-builder/oe/kas/ppc64.yml'); -like( $ppc64_kas, qr{includes:\s*\n\s*- xCAT-genesis-builder/oe/kas/common\.yml}, +like( $ppc64_kas, qr{includes:\s*\n\s*- common\.yml}, 'ppc64 build includes the common configuration' ); like( $ppc64_kas, qr/^machine: xcat-genesis-ppc64$/m, 'ppc64 build selects its machine' ); From 2404f71785ea965f9b0e2e7be914bb60ed4b56b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Sun, 6 Sep 2026 03:32:20 -0300 Subject: [PATCH 55/65] fix(genesis): restore repository KAS paths --- xCAT-genesis-builder/oe/kas/aarch64.yml | 2 +- xCAT-genesis-builder/oe/kas/armv7hf.yml | 2 +- xCAT-genesis-builder/oe/kas/common.yml | 4 ++-- xCAT-genesis-builder/oe/kas/ppc64.yml | 2 +- xCAT-genesis-builder/oe/kas/ppc64le.yml | 2 +- xCAT-genesis-builder/oe/kas/riscv64.yml | 2 +- xCAT-genesis-builder/oe/kas/s390x.yml | 2 +- xCAT-genesis-builder/oe/kas/x86.yml | 2 +- xCAT-genesis-builder/oe/kas/x86_64.yml | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/xCAT-genesis-builder/oe/kas/aarch64.yml b/xCAT-genesis-builder/oe/kas/aarch64.yml index c2480affc..1e9a76978 100644 --- a/xCAT-genesis-builder/oe/kas/aarch64.yml +++ b/xCAT-genesis-builder/oe/kas/aarch64.yml @@ -1,6 +1,6 @@ header: version: 19 includes: - - common.yml + - xCAT-genesis-builder/oe/kas/common.yml machine: xcat-genesis-aarch64 diff --git a/xCAT-genesis-builder/oe/kas/armv7hf.yml b/xCAT-genesis-builder/oe/kas/armv7hf.yml index f3992499c..4a690f8a5 100644 --- a/xCAT-genesis-builder/oe/kas/armv7hf.yml +++ b/xCAT-genesis-builder/oe/kas/armv7hf.yml @@ -1,6 +1,6 @@ header: version: 19 includes: - - common.yml + - xCAT-genesis-builder/oe/kas/common.yml machine: xcat-genesis-armv7hf diff --git a/xCAT-genesis-builder/oe/kas/common.yml b/xCAT-genesis-builder/oe/kas/common.yml index fd28a5f9a..d30fdaeaf 100644 --- a/xCAT-genesis-builder/oe/kas/common.yml +++ b/xCAT-genesis-builder/oe/kas/common.yml @@ -5,7 +5,7 @@ signers: yocto-release: fingerprint: 2AFB13F28FBBB0D1B9DAF63087EB3D32FB631AD9 repo: xcat-core - path: ../keys/yocto-release.asc + path: xCAT-genesis-builder/oe/keys/yocto-release.asc distro: xcat-genesis target: xcat-genesis-image @@ -40,7 +40,7 @@ repos: xcat-core: layers: - ../meta-xcat-genesis: + xCAT-genesis-builder/oe/meta-xcat-genesis: local_conf_header: xcat-genesis: | diff --git a/xCAT-genesis-builder/oe/kas/ppc64.yml b/xCAT-genesis-builder/oe/kas/ppc64.yml index 4ab3491b3..73d223879 100644 --- a/xCAT-genesis-builder/oe/kas/ppc64.yml +++ b/xCAT-genesis-builder/oe/kas/ppc64.yml @@ -1,6 +1,6 @@ header: version: 19 includes: - - common.yml + - xCAT-genesis-builder/oe/kas/common.yml machine: xcat-genesis-ppc64 diff --git a/xCAT-genesis-builder/oe/kas/ppc64le.yml b/xCAT-genesis-builder/oe/kas/ppc64le.yml index ad2b26e2b..ba347c249 100644 --- a/xCAT-genesis-builder/oe/kas/ppc64le.yml +++ b/xCAT-genesis-builder/oe/kas/ppc64le.yml @@ -1,6 +1,6 @@ header: version: 19 includes: - - common.yml + - xCAT-genesis-builder/oe/kas/common.yml machine: xcat-genesis-ppc64le diff --git a/xCAT-genesis-builder/oe/kas/riscv64.yml b/xCAT-genesis-builder/oe/kas/riscv64.yml index c4bbbbd78..a7ec93801 100644 --- a/xCAT-genesis-builder/oe/kas/riscv64.yml +++ b/xCAT-genesis-builder/oe/kas/riscv64.yml @@ -1,6 +1,6 @@ header: version: 19 includes: - - common.yml + - xCAT-genesis-builder/oe/kas/common.yml machine: xcat-genesis-riscv64 diff --git a/xCAT-genesis-builder/oe/kas/s390x.yml b/xCAT-genesis-builder/oe/kas/s390x.yml index 2201cd8f9..ac06515bd 100644 --- a/xCAT-genesis-builder/oe/kas/s390x.yml +++ b/xCAT-genesis-builder/oe/kas/s390x.yml @@ -1,6 +1,6 @@ header: version: 19 includes: - - common.yml + - xCAT-genesis-builder/oe/kas/common.yml machine: xcat-genesis-s390x diff --git a/xCAT-genesis-builder/oe/kas/x86.yml b/xCAT-genesis-builder/oe/kas/x86.yml index 795232389..e7d7334ff 100644 --- a/xCAT-genesis-builder/oe/kas/x86.yml +++ b/xCAT-genesis-builder/oe/kas/x86.yml @@ -1,6 +1,6 @@ header: version: 19 includes: - - common.yml + - xCAT-genesis-builder/oe/kas/common.yml machine: xcat-genesis-x86 diff --git a/xCAT-genesis-builder/oe/kas/x86_64.yml b/xCAT-genesis-builder/oe/kas/x86_64.yml index 05fa600be..9bc7a83f4 100644 --- a/xCAT-genesis-builder/oe/kas/x86_64.yml +++ b/xCAT-genesis-builder/oe/kas/x86_64.yml @@ -1,6 +1,6 @@ header: version: 19 includes: - - common.yml + - xCAT-genesis-builder/oe/kas/common.yml machine: xcat-genesis-x86-64 From 18b36f02bac8858b8b2561630ab413e34f3c478e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Sun, 6 Sep 2026 03:33:24 -0300 Subject: [PATCH 56/65] test(genesis): cover repository KAS paths --- xCAT-test/unit/genesis_openembedded.t | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/xCAT-test/unit/genesis_openembedded.t b/xCAT-test/unit/genesis_openembedded.t index b5fe34d4d..a1615fda8 100644 --- a/xCAT-test/unit/genesis_openembedded.t +++ b/xCAT-test/unit/genesis_openembedded.t @@ -25,10 +25,10 @@ like( $kas, qr/tag: yocto-6\.0\.2/, 'build uses Yocto 6.0.2' ); like( $kas, qr/fingerprint: 2AFB13F28FBBB0D1B9DAF63087EB3D32FB631AD9/, 'build trusts the Yocto release key' ); like( $kas, - qr{yocto-release:.*?repo: xcat-core.*?path: \.\./keys/yocto-release\.asc}s, + qr{yocto-release:.*?repo: xcat-core.*?path: xCAT-genesis-builder/oe/keys/yocto-release\.asc}s, 'build loads the Yocto release key from the source tree' ); like( $kas, - qr{xcat-core:.*?layers:.*?\.\./meta-xcat-genesis:}s, + qr{xcat-core:.*?layers:.*?xCAT-genesis-builder/oe/meta-xcat-genesis:}s, 'build loads the Genesis layer from the source tree' ); unlike( $kas, qr/gpg_keyserver:/, 'build does not depend on a public keyserver' ); @@ -57,49 +57,49 @@ like( $kas, 'meta-openembedded revision is pinned' ); my $x86_kas = read_file('xCAT-genesis-builder/oe/kas/x86_64.yml'); -like( $x86_kas, qr{includes:\s*\n\s*- common\.yml}, +like( $x86_kas, qr{includes:\s*\n\s*- xCAT-genesis-builder/oe/kas/common\.yml}, 'x86_64 build includes the common configuration' ); like( $x86_kas, qr/^machine: xcat-genesis-x86-64$/m, 'x86_64 build selects its machine' ); my $x86_32_kas = read_file('xCAT-genesis-builder/oe/kas/x86.yml'); -like( $x86_32_kas, qr{includes:\s*\n\s*- common\.yml}, +like( $x86_32_kas, qr{includes:\s*\n\s*- xCAT-genesis-builder/oe/kas/common\.yml}, 'x86 build includes the common configuration' ); like( $x86_32_kas, qr/^machine: xcat-genesis-x86$/m, 'x86 build selects its machine' ); my $armv7hf_kas = read_file('xCAT-genesis-builder/oe/kas/armv7hf.yml'); -like( $armv7hf_kas, qr{includes:\s*\n\s*- common\.yml}, +like( $armv7hf_kas, qr{includes:\s*\n\s*- xCAT-genesis-builder/oe/kas/common\.yml}, 'armv7hf build includes the common configuration' ); like( $armv7hf_kas, qr/^machine: xcat-genesis-armv7hf$/m, 'armv7hf build selects its machine' ); my $aarch64_kas = read_file('xCAT-genesis-builder/oe/kas/aarch64.yml'); -like( $aarch64_kas, qr{includes:\s*\n\s*- common\.yml}, +like( $aarch64_kas, qr{includes:\s*\n\s*- xCAT-genesis-builder/oe/kas/common\.yml}, 'aarch64 build includes the common configuration' ); like( $aarch64_kas, qr/^machine: xcat-genesis-aarch64$/m, 'aarch64 build selects its machine' ); my $riscv64_kas = read_file('xCAT-genesis-builder/oe/kas/riscv64.yml'); -like( $riscv64_kas, qr{includes:\s*\n\s*- common\.yml}, +like( $riscv64_kas, qr{includes:\s*\n\s*- xCAT-genesis-builder/oe/kas/common\.yml}, 'riscv64 build includes the common configuration' ); like( $riscv64_kas, qr/^machine: xcat-genesis-riscv64$/m, 'riscv64 build selects its machine' ); my $s390x_kas = read_file('xCAT-genesis-builder/oe/kas/s390x.yml'); -like( $s390x_kas, qr{includes:\s*\n\s*- common\.yml}, +like( $s390x_kas, qr{includes:\s*\n\s*- xCAT-genesis-builder/oe/kas/common\.yml}, 's390x build includes the common configuration' ); like( $s390x_kas, qr/^machine: xcat-genesis-s390x$/m, 's390x build selects its machine' ); my $ppc64le_kas = read_file('xCAT-genesis-builder/oe/kas/ppc64le.yml'); -like( $ppc64le_kas, qr{includes:\s*\n\s*- common\.yml}, +like( $ppc64le_kas, qr{includes:\s*\n\s*- xCAT-genesis-builder/oe/kas/common\.yml}, 'ppc64le build includes the common configuration' ); like( $ppc64le_kas, qr/^machine: xcat-genesis-ppc64le$/m, 'ppc64le build selects its machine' ); my $ppc64_kas = read_file('xCAT-genesis-builder/oe/kas/ppc64.yml'); -like( $ppc64_kas, qr{includes:\s*\n\s*- common\.yml}, +like( $ppc64_kas, qr{includes:\s*\n\s*- xCAT-genesis-builder/oe/kas/common\.yml}, 'ppc64 build includes the common configuration' ); like( $ppc64_kas, qr/^machine: xcat-genesis-ppc64$/m, 'ppc64 build selects its machine' ); From 6c3293afc5dcbe12862248d6d53934791b0cdeed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Sun, 6 Sep 2026 03:34:19 -0300 Subject: [PATCH 57/65] fix(genesis): isolate s390x guest identity --- .../files/xcat-genesis-console/src/state.c | 6 +++++- .../files/genesis-discover | 20 ++++++++++++++----- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/state.c b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/state.c index b6a356eab..b059ab7c4 100644 --- a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/state.c +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/state.c @@ -220,11 +220,15 @@ static bool useful_identity(const char *value) { static void set_s390_identity(const char *proc_root, struct console_state *state) { char path[VALUE_SIZE * 2]; + char control_program[VALUE_SIZE] = ""; + bool guest; snprintf(path, sizeof(path), "%s/sysinfo", proc_root); if (!useful_identity(state->uuid)) { + guest = xcat_read_colon_key(path, "VM00 Control Program", control_program, + sizeof(control_program)); xcat_read_colon_key(path, "VM00 UUID", state->uuid, sizeof(state->uuid)); - if (!useful_identity(state->uuid)) + if (!useful_identity(state->uuid) && !guest) xcat_read_colon_key(path, "LPAR UUID", state->uuid, sizeof(state->uuid)); } } diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-discover b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-discover index 82526fe29..b22f2c8e2 100755 --- a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-discover +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-discover @@ -81,6 +81,14 @@ read_sysinfo_value() { "$proc_root/sysinfo" 2>/dev/null || true } +sysinfo_has_key() { + local name=$1 + + awk -F ':' -v name="$name" \ + '$1 == name {found = 1; exit} END {exit !found}' \ + "$proc_root/sysinfo" 2>/dev/null +} + send_packet() { if [[ -n ${XCAT_DISCOVERY_SEND_COMMAND:-} ]]; then "$XCAT_DISCOVERY_SEND_COMMAND" "$compressed_packet" "$XCATMASTER" "$XCATPORT" @@ -147,12 +155,14 @@ if [[ $architecture == s390x ]]; then product_name=${product_type}${product_model:+-$product_model} machine_type=$product_name serial= - uuid=$(read_sysinfo_value 'VM00 UUID') - [[ -n $uuid ]] || uuid=$(read_sysinfo_value 'LPAR UUID') - platform=$(read_sysinfo_value 'VM00 Control Program') - if [[ -n $platform ]]; then + if sysinfo_has_key 'VM00 Control Program'; then s390_virtual=1 - else + fi + uuid=$(read_sysinfo_value 'VM00 UUID') + [[ -n $uuid || -n $s390_virtual ]] \ + || uuid=$(read_sysinfo_value 'LPAR UUID') + platform=$(read_sysinfo_value 'VM00 Control Program') + if [[ -z $s390_virtual ]]; then platform=LPAR fi elif [[ -z $product_name ]]; then From d2e20fb08c6d18d5297d354a1c373bafe5a1976b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Sun, 6 Sep 2026 03:35:19 -0300 Subject: [PATCH 58/65] test(genesis): distinguish s390x guest identity --- xCAT-test/unit/genesis_openembedded_console.t | 6 +++--- xCAT-test/unit/genesis_openembedded_discovery.t | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/xCAT-test/unit/genesis_openembedded_console.t b/xCAT-test/unit/genesis_openembedded_console.t index 069c9ddcb..44e7e1aee 100644 --- a/xCAT-test/unit/genesis_openembedded_console.t +++ b/xCAT-test/unit/genesis_openembedded_console.t @@ -286,13 +286,13 @@ like( $probe_output, qr/^uuid=93724168-fda3-429b-8b28-a5d245dcb3ff$/m, 'console uses the LPAR UUID when no guest UUID is available' ); write_file( File::Spec->catfile( $proc_root, 'sysinfo' ), <<'SYSINFO' ); +VM00 Control Program: z/VM 7.3.0 VM00 UUID: LPAR UUID: 93724168-fda3-429b-8b28-a5d245dcb3ff SYSINFO ( $probe_status, $probe_output ) = run_identity_probe(); -like( $probe_output, - qr/^uuid=93724168-fda3-429b-8b28-a5d245dcb3ff$/m, - 'console ignores an empty guest UUID and uses the LPAR UUID' ); +like( $probe_output, qr/^uuid=not reported$/m, + 'console does not reuse the LPAR UUID for a guest' ); unlink( File::Spec->catfile( $proc_root, 'sysinfo' ) ); $environment{XCAT_TEST_ARCH} = 'x86_64'; write_file( File::Spec->catfile( $dmi_root, 'product_serial' ), diff --git a/xCAT-test/unit/genesis_openembedded_discovery.t b/xCAT-test/unit/genesis_openembedded_discovery.t index b68a899d1..28bb40aae 100644 --- a/xCAT-test/unit/genesis_openembedded_discovery.t +++ b/xCAT-test/unit/genesis_openembedded_discovery.t @@ -465,6 +465,7 @@ write_file( File::Spec->catfile( $proc_root, 'sysinfo' ), <<'SYSINFO' ); Manufacturer: IBM Type: 3931 Model: 701 A01 +LPAR UUID: 93724168-fda3-429b-8b28-a5d245dcb3ff VM00 Control Program: KVM/Linux SYSINFO is( run_script( $discover_script, \%environment ), 0, From 17ca2c9c7956e975a48e64fd3f00a13ea8c2cf89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Sun, 6 Sep 2026 03:36:22 -0300 Subject: [PATCH 59/65] docs(genesis): clarify s390x discovery --- .../developers/guides/code/genesis_openembedded_plan.rst | 3 +++ docs/source/guides/admin-guides/references/man8/mknb.8.rst | 2 +- xCAT-client/pods/man8/mknb.8.pod | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/source/developers/guides/code/genesis_openembedded_plan.rst b/docs/source/developers/guides/code/genesis_openembedded_plan.rst index 7d05bfb8a..5b89a4e6a 100644 --- a/docs/source/developers/guides/code/genesis_openembedded_plan.rst +++ b/docs/source/developers/guides/code/genesis_openembedded_plan.rst @@ -105,6 +105,9 @@ The ``s390x`` image uses virtio networking under QEMU. QEMU does not emulate qeth, so the activation service exits without changing the virtio interface. Physical LPAR and z/VM Genesis networking remain unvalidated. Genesis does not use the shared CEC serial as an s390x node identifier. +QEMU guests report themselves as virtual nodes, so sequential and switch +discovery ignore them. Use profile discovery or assign a pending discovery +record with ``nodediscoverdef``. QEMU validation covers network IPL, DHCP option 209, TFTP, and the network-specific ``pxelinux.cfg``-style configuration written by ``mknb``. This release does not advertise a DPM boot configuration. diff --git a/docs/source/guides/admin-guides/references/man8/mknb.8.rst b/docs/source/guides/admin-guides/references/man8/mknb.8.rst index f64294fee..8a925e657 100644 --- a/docs/source/guides/admin-guides/references/man8/mknb.8.rst +++ b/docs/source/guides/admin-guides/references/man8/mknb.8.rst @@ -50,7 +50,7 @@ riscv64 nodes boot through UEFI firmware and grub2. For riscv64, \ **mknb**\ pu QEMU s390-ccw guests receive a network-specific \ ``pxelinux.cfg``\ -style configuration through DHCP option 209. The firmware resolves this name below \ ``pxelinux.cfg``\ . Leave \ ``noderes.netboot``\ unset while a QEMU s390x node uses Genesis discovery. s390x Genesis does not use the shared IBM Z machine serial as a node identifier. On physical IBM Z, Genesis accepts \ ``rd.znet=qeth,read,write,data[,option=value]``\ and activates qeth before NetworkManager starts. Without \ ``rd.znet``\ , it activates unconfigured qeth devices in layer 2 mode. Devices reported as configured by \ ``znetconf``\ are not regrouped. Genesis does not import DPM auto-configuration data. Layer 3 and IP-mode VSWITCH configurations require \ ``layer2=0``\ . QEMU validation covers network IPL through Genesis startup; the discovery exchange with xcatd remains unvalidated for s390x. Legacy xCAT z/VM operating-system provisioning remains unchanged. Physical LPAR and z/VM Genesis networking remain unvalidated because QEMU does not emulate qeth. -Run \ **makedhcp -n**\ after installing the s390x image and before booting s390x nodes. This adds the required DHCP option to existing configurations. +Run \ **makedhcp -n**\ after installing the s390x image and before booting s390x nodes. This regenerates the DHCP configuration with the required option. ******* diff --git a/xCAT-client/pods/man8/mknb.8.pod b/xCAT-client/pods/man8/mknb.8.pod index bd37db74f..6ea2b7077 100644 --- a/xCAT-client/pods/man8/mknb.8.pod +++ b/xCAT-client/pods/man8/mknb.8.pod @@ -32,7 +32,7 @@ riscv64 nodes boot through UEFI firmware and grub2. For riscv64, B publish QEMU s390-ccw guests receive a network-specific C-style configuration through DHCP option 209. The firmware resolves this name below C. Leave C unset while a QEMU s390x node uses Genesis discovery. s390x Genesis does not use the shared IBM Z machine serial as a node identifier. On physical IBM Z, Genesis accepts C and activates qeth before NetworkManager starts. Without C, it activates unconfigured qeth devices in layer 2 mode. Devices reported as configured by C are not regrouped. Genesis does not import DPM auto-configuration data. Layer 3 and IP-mode VSWITCH configurations require C. QEMU validation covers network IPL through Genesis startup; the discovery exchange with xcatd remains unvalidated for s390x. Legacy xCAT z/VM operating-system provisioning remains unchanged. Physical LPAR and z/VM Genesis networking remain unvalidated because QEMU does not emulate qeth. -Run B after installing the s390x image and before booting s390x nodes. This adds the required DHCP option to existing configurations. +Run B after installing the s390x image and before booting s390x nodes. This regenerates the DHCP configuration with the required option. =head1 OPTIONS From 560de199a9eb34b5fde06b55700398892960583d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Sun, 6 Sep 2026 03:46:17 -0300 Subject: [PATCH 60/65] fix(genesis): align shellcheck trap annotation --- .../recipes-xcat/xcat-genesis-discovery/files/genesis-discover | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-discover b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-discover index b22f2c8e2..392078604 100755 --- a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-discover +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-discover @@ -26,7 +26,7 @@ publish_status() { || logger -t xcat-genesis-discover -- 'unable to publish discovery status' } -# shellcheck disable=SC2329 +# shellcheck disable=SC2317,SC2329 finish_status() { local result=$? From ddba7567fc4584396a618eee4e895ec7ffff96fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Sun, 6 Sep 2026 04:02:24 -0300 Subject: [PATCH 61/65] fix(genesis): remove unused s390x tune value --- .../meta-xcat-genesis/conf/machine/include/s390x/tune-s390x.inc | 1 - 1 file changed, 1 deletion(-) diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/include/s390x/tune-s390x.inc b/xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/include/s390x/tune-s390x.inc index 0a4939201..8a67f7259 100644 --- a/xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/include/s390x/tune-s390x.inc +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/include/s390x/tune-s390x.inc @@ -12,7 +12,6 @@ TUNE_CCARGS .= "${@bb.utils.contains('TUNE_FEATURES', 'z10', ' -march=z10 -mtune AVAILTUNES += "s390x" TUNE_FEATURES:tune-s390x = "m64 bigendian z10" BASE_LIB:tune-s390x = "lib64" -BASELIB:libc-glibc:s390x = "lib64" TUNE_PKGARCH:tune-s390x = "s390x" PACKAGE_EXTRA_ARCHS:tune-s390x = "s390x" From 3ad1ef888a5772076a4afbd6a719fa8e65ff55b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Sun, 6 Sep 2026 04:02:40 -0300 Subject: [PATCH 62/65] test(genesis): cover absent s390x guest UUID --- xCAT-test/unit/genesis_openembedded_console.t | 1 - 1 file changed, 1 deletion(-) diff --git a/xCAT-test/unit/genesis_openembedded_console.t b/xCAT-test/unit/genesis_openembedded_console.t index 44e7e1aee..3572972df 100644 --- a/xCAT-test/unit/genesis_openembedded_console.t +++ b/xCAT-test/unit/genesis_openembedded_console.t @@ -287,7 +287,6 @@ like( $probe_output, 'console uses the LPAR UUID when no guest UUID is available' ); write_file( File::Spec->catfile( $proc_root, 'sysinfo' ), <<'SYSINFO' ); VM00 Control Program: z/VM 7.3.0 -VM00 UUID: LPAR UUID: 93724168-fda3-429b-8b28-a5d245dcb3ff SYSINFO ( $probe_status, $probe_output ) = run_identity_probe(); From 1d6c373a8685d5e0f1f8292d6fc8f795d291202e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Sun, 6 Sep 2026 04:03:35 -0300 Subject: [PATCH 63/65] docs(genesis): clarify virtual s390x discovery --- .../developers/guides/code/genesis_openembedded_plan.rst | 6 +++--- docs/source/guides/admin-guides/references/man8/mknb.8.rst | 2 ++ xCAT-client/pods/man8/mknb.8.pod | 2 ++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/source/developers/guides/code/genesis_openembedded_plan.rst b/docs/source/developers/guides/code/genesis_openembedded_plan.rst index 5b89a4e6a..ec8bf6e7c 100644 --- a/docs/source/developers/guides/code/genesis_openembedded_plan.rst +++ b/docs/source/developers/guides/code/genesis_openembedded_plan.rst @@ -105,9 +105,9 @@ The ``s390x`` image uses virtio networking under QEMU. QEMU does not emulate qeth, so the activation service exits without changing the virtio interface. Physical LPAR and z/VM Genesis networking remain unvalidated. Genesis does not use the shared CEC serial as an s390x node identifier. -QEMU guests report themselves as virtual nodes, so sequential and switch -discovery ignore them. Use profile discovery or assign a pending discovery -record with ``nodediscoverdef``. +s390x hypervisor guests report themselves as virtual nodes, so sequential and +switch discovery ignore them. Use profile discovery or assign a pending +discovery record with ``nodediscoverdef``. QEMU validation covers network IPL, DHCP option 209, TFTP, and the network-specific ``pxelinux.cfg``-style configuration written by ``mknb``. This release does not advertise a DPM boot configuration. diff --git a/docs/source/guides/admin-guides/references/man8/mknb.8.rst b/docs/source/guides/admin-guides/references/man8/mknb.8.rst index 8a925e657..638a8894f 100644 --- a/docs/source/guides/admin-guides/references/man8/mknb.8.rst +++ b/docs/source/guides/admin-guides/references/man8/mknb.8.rst @@ -50,6 +50,8 @@ riscv64 nodes boot through UEFI firmware and grub2. For riscv64, \ **mknb**\ pu QEMU s390-ccw guests receive a network-specific \ ``pxelinux.cfg``\ -style configuration through DHCP option 209. The firmware resolves this name below \ ``pxelinux.cfg``\ . Leave \ ``noderes.netboot``\ unset while a QEMU s390x node uses Genesis discovery. s390x Genesis does not use the shared IBM Z machine serial as a node identifier. On physical IBM Z, Genesis accepts \ ``rd.znet=qeth,read,write,data[,option=value]``\ and activates qeth before NetworkManager starts. Without \ ``rd.znet``\ , it activates unconfigured qeth devices in layer 2 mode. Devices reported as configured by \ ``znetconf``\ are not regrouped. Genesis does not import DPM auto-configuration data. Layer 3 and IP-mode VSWITCH configurations require \ ``layer2=0``\ . QEMU validation covers network IPL through Genesis startup; the discovery exchange with xcatd remains unvalidated for s390x. Legacy xCAT z/VM operating-system provisioning remains unchanged. Physical LPAR and z/VM Genesis networking remain unvalidated because QEMU does not emulate qeth. +s390x hypervisor guests report themselves as virtual nodes, so sequential and switch discovery ignore them. Use profile discovery or assign a pending discovery record with \ **nodediscoverdef**\ . + Run \ **makedhcp -n**\ after installing the s390x image and before booting s390x nodes. This regenerates the DHCP configuration with the required option. diff --git a/xCAT-client/pods/man8/mknb.8.pod b/xCAT-client/pods/man8/mknb.8.pod index 6ea2b7077..e5cdf0f32 100644 --- a/xCAT-client/pods/man8/mknb.8.pod +++ b/xCAT-client/pods/man8/mknb.8.pod @@ -32,6 +32,8 @@ riscv64 nodes boot through UEFI firmware and grub2. For riscv64, B publish QEMU s390-ccw guests receive a network-specific C-style configuration through DHCP option 209. The firmware resolves this name below C. Leave C unset while a QEMU s390x node uses Genesis discovery. s390x Genesis does not use the shared IBM Z machine serial as a node identifier. On physical IBM Z, Genesis accepts C and activates qeth before NetworkManager starts. Without C, it activates unconfigured qeth devices in layer 2 mode. Devices reported as configured by C are not regrouped. Genesis does not import DPM auto-configuration data. Layer 3 and IP-mode VSWITCH configurations require C. QEMU validation covers network IPL through Genesis startup; the discovery exchange with xcatd remains unvalidated for s390x. Legacy xCAT z/VM operating-system provisioning remains unchanged. Physical LPAR and z/VM Genesis networking remain unvalidated because QEMU does not emulate qeth. +s390x hypervisor guests report themselves as virtual nodes, so sequential and switch discovery ignore them. Use profile discovery or assign a pending discovery record with B. + Run B after installing the s390x image and before booting s390x nodes. This regenerates the DHCP configuration with the required option. =head1 OPTIONS From 76e8d7069de95ac52f86a8ee905edd94522fa101 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Sun, 6 Sep 2026 04:08:59 -0300 Subject: [PATCH 64/65] fix(genesis): retain s390x glibc library path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Vinícius Ferrão <2031761+viniciusferrao@users.noreply.github.com> --- .../meta-xcat-genesis/conf/machine/include/s390x/tune-s390x.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/include/s390x/tune-s390x.inc b/xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/include/s390x/tune-s390x.inc index 8a67f7259..0a4939201 100644 --- a/xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/include/s390x/tune-s390x.inc +++ b/xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/include/s390x/tune-s390x.inc @@ -12,6 +12,7 @@ TUNE_CCARGS .= "${@bb.utils.contains('TUNE_FEATURES', 'z10', ' -march=z10 -mtune AVAILTUNES += "s390x" TUNE_FEATURES:tune-s390x = "m64 bigendian z10" BASE_LIB:tune-s390x = "lib64" +BASELIB:libc-glibc:s390x = "lib64" TUNE_PKGARCH:tune-s390x = "s390x" PACKAGE_EXTRA_ARCHS:tune-s390x = "s390x" From 0302670320c05670c1d759f84e46fd88f6316e0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Sun, 6 Sep 2026 11:01:34 -0300 Subject: [PATCH 65/65] test(genesis): update integration test count --- .../autotest/testcase/integration/cases0 | 2 +- xCAT-test/integration/README.md | 22 ++++++++++--------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/xCAT-test/autotest/testcase/integration/cases0 b/xCAT-test/autotest/testcase/integration/cases0 index 099122180..97ccef194 100644 --- a/xCAT-test/autotest/testcase/integration/cases0 +++ b/xCAT-test/autotest/testcase/integration/cases0 @@ -4,5 +4,5 @@ os:Linux label:mn_only,ci_test,integration cmd:prove -I/opt/xcat/lib/perl -I/opt/xcat/lib/perl/xCAT -r /opt/xcat/share/xcat/tools/autotest/integration check:rc==0 -check:output=~Files=4 +check:output=~Files=5, end diff --git a/xCAT-test/integration/README.md b/xCAT-test/integration/README.md index f9e336caf..65749c4d1 100644 --- a/xCAT-test/integration/README.md +++ b/xCAT-test/integration/README.md @@ -27,13 +27,13 @@ against it. Note the `-I` flags: unlike the unit tests these run from the installed location, so they pick up xCAT modules from `/opt/xcat/lib/perl` rather than from a source tree. -The case checks `rc==0` and `output=~Files=4`. The second assertion is there because +The case checks `rc==0` and `output=~Files=5,`. The second assertion is there because `prove` exits 0 both when tests pass and when they all skip, so `rc==0` alone would let -the case report green having run nothing. Matching `Files=4` proves `prove` actually -found all four files, which catches a packaging regression or a file being renamed -without the count being updated here. **Add to that number when you add a test.** A -missing directory is already caught by `rc==0` -- `prove -r` on a path that does not -exist exits 2. +the case report green having run nothing. Matching `Files=5,` proves `prove` actually +found all five files, which catches a packaging regression or a test renamed away from +`.t` without updating the count. **Add to that number when you add a test.** A missing +directory is already caught by `rc==0` -- `prove -r` on a path that does not exist exits +2. Note also that `github_action_xcat_test.pl` invokes each case through `sudo`, so in CI these tests run as **root** while the unit tests run unprivileged. That is the right @@ -48,15 +48,17 @@ A test belongs in `integration/` when it needs something the checkout cannot pro | Test | Requires | | --- | --- | | `copycds_packages_integrity.t` | `/install` populated by a real `copycds` | +| `dhcp_isc_config_validation.t` | a `dhcpd` binary that can read the generated config, as root | | `dhcp_kea_config_validation.t` | a `kea-dhcp4` binary that can read the generated config | | `dhcp_kea_control_agent_smoke.t` | live `kea-dhcp4` and `kea-ctrl-agent`, root, and the Kea host-commands hook | +| `html_form_runtime_dependency.t` | the installed `HTML::Form` module | ## Environment guards -Tests here still guard with `plan skip_all` so the case does not fail on a node that -legitimately lacks the dependency -- an MN with no Kea installed should skip the Kea -tests, not go red. A skip in this directory is therefore expected and normal, which is -precisely why these tests do not belong alongside the unit tests. +Tests with optional runtime dependencies guard with `plan skip_all` -- an MN with no +Kea installed should skip the Kea tests, not go red. The `HTML::Form` test is different: +it verifies a required installed dependency and fails when that module is absent. A +skip in this directory is therefore expected and normal. Which tests actually run consequently varies by node. On a GitHub runner, for example, `/install` is empty so `copycds_packages_integrity.t` skips, while