2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-09-08 12:40:45 +00:00

Merge pull request #7819 from VersatusHPC/feat/genesis-s390x

feat(genesis): add s390x OpenEmbedded support
This commit is contained in:
Daniel Hilst
2026-09-06 15:19:56 -03:00
committed by GitHub
62 changed files with 1696 additions and 85 deletions
+1
View File
@@ -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
+9 -4
View File
@@ -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 xcat-genesis-extension-smoke'
@@ -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``
- ``s390-ccw BIOS network IPL``
- ``makedhcp -n``; backend parser; architecture ``0x001f``; DHCP option
209; TFTP fetch of the generated ``pxelinux.cfg``-style configuration,
Genesis kernel, and initramfs
Kea Boot and Reservation Regression Matrix
------------------------------------------
@@ -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,26 @@ 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 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.
Physical LPAR and z/VM Genesis networking remain unvalidated.
Genesis does not use the shared CEC serial as an s390x node identifier.
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.
Legacy xCAT z/VM operating-system provisioning remains unchanged.
xCAT protocol
-------------
@@ -99,6 +122,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.
@@ -234,12 +258,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 +284,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
channel test on an IBM Z LPAR or z/VM guest.
References
----------
@@ -270,6 +296,10 @@ References
<https://kas.readthedocs.io/en/latest/userguide/project-configuration.html>`_
* `NetworkManager dispatcher interface
<https://networkmanager.dev/docs/api/latest/NetworkManager-dispatcher.html>`_
* `QEMU s390x network boot
<https://www.qemu.org/docs/master/system/s390x/bootdevices.html>`_
* `IBM znetconf
<https://www.ibm.com/docs/en/linux-on-systems?topic=linuxonz-znetconf>`_
* `NetworkManager initrd generator
<https://networkmanager.dev/docs/api/latest/nm-initrd-generator.html>`_
* `systemd system extensions
@@ -42,11 +42,17 @@ 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.
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, 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.
*******
+29
View File
@@ -103,6 +103,33 @@ sub kea_httpboot_network_classes {
return \@classes;
}
sub kea_s390x_network_classes {
my ( $class, %opts ) = @_;
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_.-]}{_}gxms;
return [
{
name => "xcat-s390x-qemu-$safe_network",
test => 'option[93].hex == 0x001f',
additional_only => 1,
'option-data' => [
{
name => 'conf-file',
data => "s390x/$network_id",
'always-send' => 1,
},
],
},
];
}
sub isc_client_architecture_lines {
my ( $class, %opts ) = @_;
@@ -136,6 +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",
" } else if option client-architecture = 00:1f { #QEMU s390x\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",
+2 -2
View File
@@ -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/<imagename>/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.',
+7 -1
View File
@@ -24,12 +24,18 @@ An export is identified by C<xcat-genesis.manifest>, which records its format ve
When multiple IPv4 addresses are configured for the same network, B<mknb> uses a locally assigned C<site.master> for the xcatd endpoint, or the first address reported by the operating system when C<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 C<x86>, C<x86_64>, C<ppc64>, C<ppc64le>, C<armv7hf>, C<aarch64>, and C<riscv64>. If an OpenEmbedded C<ppc64le> image is not installed, B<mknb> keeps the old behavior and uses the legacy C<ppc64> image.
OpenEmbedded images use the exact architecture names C<x86>, C<x86_64>, C<ppc64>, C<ppc64le>, C<armv7hf>, C<aarch64>, C<riscv64>, and C<s390x>. If an OpenEmbedded C<ppc64le> image is not installed, B<mknb> keeps the old behavior and uses the legacy C<ppc64> image.
Canonical C<ppc64> images are big-endian. xCAT marks them so C<ppc64le> nodes do not use them as a legacy little-endian fallback. B<mknb> also refuses to replace a marked C<ppc64> image with that fallback.
riscv64 nodes boot through UEFI firmware and grub2. For riscv64, B<mknb> publishes the Genesis kernel and initramfs and writes one grub2 configuration per network under C</tftpboot/boot/grub2>, named C<grub.cfg-> followed by the network hex prefix, so that C<grub2.riscv64> loaded by the firmware can start node discovery. The per-node files written by B<nodeset> take priority over these network files. Networks served by a C<:noboot> interface in C<site.dhcpinterfaces> get no discovery configuration.
QEMU s390-ccw guests receive a network-specific C<pxelinux.cfg>-style configuration through DHCP option 209. The firmware resolves this name below C<pxelinux.cfg>. Leave C<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 C<rd.znet=qeth,read,write,data[,option=value]> and activates qeth before NetworkManager starts. Without C<rd.znet>, it activates unconfigured qeth devices in layer 2 mode. Devices reported as configured by C<znetconf> are not regrouped. Genesis does not import DPM auto-configuration data. Layer 3 and IP-mode VSWITCH configurations require C<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 B<nodediscoverdef>.
Run B<makedhcp -n> after installing the s390x image and before booting s390x nodes. This regenerates the DHCP configuration with the required option.
=head1 OPTIONS
=over 12
+3 -1
View File
@@ -16,7 +16,9 @@ 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.
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.
+17 -7
View File
@@ -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,12 +22,16 @@ if [ "$#" -eq 0 ]; then
fi
for architecture do
case "$architecture" in
aarch64|armv7hf|riscv64|x86|x86_64|ppc64|ppc64le) ;;
*)
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
+1
View File
@@ -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
+1 -1
View File
@@ -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}$ ]] \
+6
View File
@@ -0,0 +1,6 @@
header:
version: 19
includes:
- xCAT-genesis-builder/oe/kas/common.yml
machine: xcat-genesis-s390x
@@ -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)
@@ -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"
@@ -0,0 +1,35 @@
#@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"
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 = ""
@@ -0,0 +1,7 @@
# SPDX-License-Identifier: EPL-1.0
my %targets = (
"linux-s390x" => {
inherit_from => [ "linux64-s390x" ],
},
);
@@ -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/
}
@@ -0,0 +1,167 @@
#!/bin/bash
set -u
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}
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
}
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
local -a command=("$znetconf_command" -a "$channels" -d qeth)
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"
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
}
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
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
@@ -0,0 +1,15 @@
[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
TimeoutStartSec=120
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
@@ -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 \
"
@@ -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 = " xcat-genesis-qeth"
@@ -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);
@@ -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,21 @@ 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];
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) && !guest)
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, '=');
@@ -293,6 +310,8 @@ void xcat_load_console_state(struct console_state *state) {
char release_version[32] = "";
bool xcat_configured;
bool interface_selected;
bool s390_system;
bool s390_ccw_boot;
memset(state, 0, sizeof(*state));
xcat_configured = xcat_cmdline_value(cmdline_text, "xcatd", state->xcat_endpoint,
@@ -332,6 +351,12 @@ 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");
}
s390_system = 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;
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 (s390_system) {
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 (s390_system) {
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 =
@@ -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;
@@ -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
@@ -0,0 +1,26 @@
From: xCAT Genesis Builder <xcat@users.noreply.github.com>
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 Genesis Builder <xcat@users.noreply.github.com>
---
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
@@ -0,0 +1,3 @@
FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
SRC_URI:append:s390x = " file://0001-configure-disable-s390x-crcvx-by-default.patch"
@@ -7,7 +7,4 @@ 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_CAPABILITIES = '["diagnostic.smoke"]'
COMPATIBLE_HOST = "x86_64.*-linux"
@@ -0,0 +1 @@
COMPATIBLE_HOST:s390x = "s390x.*-linux"
@@ -0,0 +1,26 @@
From: xCAT Genesis Builder <xcat@users.noreply.github.com>
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 <xcat@users.noreply.github.com>
---
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);
@@ -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
@@ -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 \
"
@@ -0,0 +1,2 @@
# libspdm has no s390x architecture mapping.
COMPATIBLE_HOST:s390x = "null"
@@ -0,0 +1,26 @@
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"
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}${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}${nonarch_base_libdir}/s390-tools/lsznet.raw
install -m 0755 ${S}/zconf/znetcontrolunits \
${D}${nonarch_base_libdir}/s390-tools/znetcontrolunits
}
FILES:${PN} = "${base_sbindir}/znetconf ${nonarch_base_libdir}/s390-tools"
@@ -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=$?
@@ -73,6 +73,22 @@ 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
}
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"
@@ -125,12 +141,31 @@ 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=
if sysinfo_has_key 'VM00 Control Program'; then
s390_virtual=1
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
product_name=$(read_value "$proc_root/device-tree/model" || true)
[[ -n $product_name ]] \
|| product_name=$(read_value \
@@ -148,12 +183,21 @@ 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}' \
"$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)
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 \
+1 -1
View File
@@ -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
+6
View File
@@ -3362,6 +3362,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;
+141 -6
View File
@@ -1,16 +1,17 @@
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;
use File::Path;
use File::Copy;
use English qw(-no_match_vars);
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 +301,21 @@ sub _remove_openembedded_genesis {
"$directory/genesis.fs.$arch.lzma",
"$directory/genesis.exact-arch.$arch",
);
if ($arch eq 's390x') {
my $config_directory = "$tftpdir/pxelinux.cfg/s390x";
if (opendir my $config_stream, $config_directory) {
foreach my $name (readdir $config_stream) {
if ($name =~ m{\A[.][.]?\z}xms) {
next;
}
my $path = "$config_directory/$name";
if (_is_generated_s390x_config($path)) {
push @artifacts, $path;
}
}
closedir $config_stream;
}
}
my $removed = 0;
my @failed;
foreach my $artifact (@artifacts) {
@@ -320,6 +336,19 @@ sub _remove_openembedded_genesis {
return ($removed, undef);
}
sub _is_generated_s390x_config {
my ($path) = @_;
if (-l $path || !-f $path) {
return 0;
}
open my $config, '<', $path or return 0;
my $header = <$config>;
if (!close $config) {
return 0;
}
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 +446,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 +709,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 {
@@ -715,15 +746,30 @@ sub process_request {
mkpath("$tftpdir/boot/grub2");
chmod(0755, "$tftpdir/boot/grub2");
}
if ($arch eq 's390x') {
mkpath "$tftpdir/pxelinux.cfg/s390x";
chmod 0755, "$tftpdir/pxelinux.cfg";
chmod 0755, "$tftpdir/pxelinux.cfg/s390x";
}
my $dopxe = 0;
my $s390x_config_error = 0;
foreach (keys %{$normnets}) {
my $net = $_;
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)) {
if (!unlink $path) {
$callback->({ error => ["Unable to remove s390x Genesis configuration: $path: $OS_ERROR"], errorcode => [1] });
$s390x_config_error = 1;
}
}
}
next;
}
@@ -785,6 +831,22 @@ 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,
kernel => $invisibletouch
? "xcat/genesis.kernel.$arch"
: "xcat/nbk.$arch",
initrd => $initrd_file,
);
if ($config_error) {
$callback->({ error => [$config_error], errorcode => [1] });
$s390x_config_error = 1;
}
}
}
$dopxe = 0;
@@ -849,11 +911,84 @@ 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"] });
}
}
sub _write_s390x_discovery_config {
my (%args) = @_;
my $tftpdir = $args{tftpdir};
my $initrd = $args{initrd};
$initrd =~ s{^\Q$tftpdir\E/?}{}xms;
my $cmdline = "xcatd=$args{xcatd_address}:$args{xcatdport} xcat.bootloader=s390-ccw";
if (defined $args{consolecmdline} and length $args{consolecmdline}) {
$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 $args{kernel}\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) = @_;
if ((-e $path || -l $path) && !_is_generated_s390x_config($path)) {
return "Refusing to replace unmanaged s390x configuration: $path";
}
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;
my $write_error = $OS_ERROR;
my $close_ok = close $config;
my $close_error = $OS_ERROR;
if (!$write_ok) {
unlink $temporary;
return "Unable to write s390x Genesis configuration: $path: $write_error";
}
if (!$close_ok) {
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;
}
# 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.
+1 -1
View File
@@ -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;
+2 -2
View File
@@ -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"
@@ -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
+12 -10
View File
@@ -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
@@ -0,0 +1,48 @@
#!/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",
);
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 s390x boot policy');
done_testing();
@@ -41,6 +41,14 @@ 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 => 's390x/192.168.122.0_24' },
],
},
{
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 +73,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' },
+23
View File
@@ -217,4 +217,27 @@ 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 => 's390x/10.0.0.0_24',
'always-send' => 1,
},
],
},
],
's390x firmware receives its supported network configuration method',
);
done_testing();
+5
View File
@@ -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 conf-file = "s390x\/192\.0\.2\.0_24";/,
'QEMU s390x receives its network configuration',
);
my @riscv_ids = $rendered =~ /client-architecture = (00:1[9a-e])/g;
is_deeply(
+24 -1
View File
@@ -23,7 +23,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 +84,7 @@ require xCAT::Utils;
*xCAT::Utils::runcmd = sub { return; };
}
$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;
@@ -120,6 +122,27 @@ my %network_entry = (
tftpserver => '<xcatmaster>',
);
{
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(
$classes{'xcat-s390x-qemu-10.0.0.0_24'}{'option-data'}[0]{data},
's390x/10.0.0.0_24',
'the Kea subnet sends the s390x configuration name',
);
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',
);
}
my @sysconfig_policy_cases = (
[ 'sles10', 0, 'SLES 10' ],
[ 'sles11', 1, 'SLES 11' ],
+53 -8
View File
@@ -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) = @_;
@@ -84,6 +86,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' );
@@ -96,11 +104,50 @@ 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');
like( $build, qr/aarch64\|armv7hf\|riscv64\|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)
);
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,
[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');
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');
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>;
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'
@@ -870,8 +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/^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'
@@ -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/,
@@ -101,11 +112,16 @@ 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/s,
'the mknb man page lists every exact OpenEmbedded architecture',
);
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(
$architecture_names,
qr/\b\Q$architecture\E\b/,
"the mknb man page lists $architecture",
);
}
unlike(
$mknb_pod,
qr/For ppc64le, use the ppc64 architecture/,
@@ -19,6 +19,8 @@ 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(
@@ -75,6 +77,50 @@ 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/utsname.h>
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;
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',
'-Duname=xcat_test_uname',
'-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' );
@@ -159,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 {
@@ -170,6 +217,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 +249,56 @@ 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
write_file( $cmdline, "xcatd=192.0.2.10:3001\n" );
$environment{XCAT_TEST_ARCH} = 's390x';
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' );
write_file( File::Spec->catfile( $proc_root, 'sysinfo' ), <<'SYSINFO' );
VM00 Control Program: z/VM 7.3.0
LPAR UUID: 93724168-fda3-429b-8b28-a5d245dcb3ff
SYSINFO
( $probe_status, $probe_output ) = run_identity_probe();
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' ),
"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 );
@@ -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
@@ -420,6 +421,61 @@ like( $packet,
qr{<uuid>9009-42a-02ab123-525400000002</uuid>},
'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{<arch>s390x</arch>},
's390x discovery keeps the canonical architecture' );
like( $packet, qr{<nodetype>virtual</nodetype>},
's390x discovery identifies a hypervisor guest' );
like( $packet, qr{<mtm>3931-A01</mtm>},
's390x discovery reports the machine type and model' );
unlike( $packet, qr{<serial>},
's390x discovery does not treat the shared machine serial as guest identity' );
like( $packet, qr{<platform>KVM/Linux</platform>},
's390x discovery reports the closest hypervisor' );
like( $packet, qr{<cpucount>2</cpucount>},
's390x discovery counts processor records' );
like( $packet, qr{<cputype>IBM/S390</cputype>},
's390x discovery reports the CPU vendor' );
like( $packet,
qr{<uuid>82038f2a-1344-aaf7-1a85-2a7250be2076</uuid>},
's390x discovery uses the closest guest UUID' );
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,
's390x discovery accepts a guest without a reported UUID' );
$packet = read_file($packet_file);
unlike( $packet, qr{<serial>},
'a UUID-less s390x guest still omits the shared machine serial' );
like( $packet, qr{<uuid>3931-a01-unknown-525400000002</uuid>},
'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' );
@@ -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);
+275
View File
@@ -0,0 +1,275 @@
#!/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 $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' );
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_CIO_SETTLE_FILE => $cio_settle,
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( $cio_settle, '' );
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( read_file($cio_settle), "1\n", 'channel discovery waits for pending CIO work' );
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,
'configured qeth devices need no activation'
);
unlike( command_log(), qr/^znetconf -a /m, '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.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 '
. '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' );
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' );
done_testing();
+5 -1
View File
@@ -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' );
}
+39
View File
@@ -156,6 +156,45 @@ 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 configurations 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 $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",
);
($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, 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');
my $failed_kernel = "$tftpdir/xcat/genesis.kernel.aarch64";
make_path($failed_kernel);
for my $artifact (qw(
+168
View File
@@ -5,11 +5,13 @@ 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);
use Test::More;
use XCAT::Test::File qw(repo_path);
use xCAT::DHCP::BootPolicy;
BEGIN {
package xCAT::Utils;
@@ -508,4 +510,170 @@ 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');
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');
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,
);
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"
. "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',
);
%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");
$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}),
's390x refuses to replace an administrator-owned configuration',
);
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');
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) =
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_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) =
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',
kernel => 'xcat/genesis.kernel.s390x',
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.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');
%xCAT::TableUtils::site_extra = ();
$xCAT::NetworkUtils::nic_ips = undef;
done_testing();
@@ -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',
);
+1 -1
View File
@@ -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
+1
View File
@@ -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
+1 -1
View File
@@ -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
+1
View File
@@ -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.