2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-08-27 09:06:39 +00:00
Files
xcat-core/xCAT-test/unit/genesis_openembedded.t
T
Vinícius Ferrão a5b12caf93 test(xCAT-test): cover the ACPI firmware description of the riscv64 kernel
Pin the configuration symbols that let the riscv64 Genesis kernel describe
its hardware on firmware that publishes ACPI tables and no device tree, and
the EFI stub grub2 boots it through. Without them the image dies before the
console exists and the node goes silent instead of discovering.
2026-08-24 15:45:52 -03:00

883 lines
40 KiB
Perl

#!/usr/bin/env perl
use strict;
use warnings;
use Digest::SHA qw(sha256_hex);
use File::Spec;
use FindBin;
use Test::More;
my $repo_root = File::Spec->catdir( $FindBin::Bin, '..', '..' );
sub read_file {
my ($relative_path) = @_;
my $path = File::Spec->catfile( $repo_root, split( '/', $relative_path ) );
open( my $fh, '<', $path ) or die "Unable to read $path: $!";
my $contents = do { local $/; <$fh> };
close($fh);
return $contents;
}
my $kas = read_file('xCAT-genesis-builder/oe/kas/common.yml');
like( $kas, qr/tag: yocto-6\.0\.2/, 'build uses Yocto 6.0.2' );
like( $kas, qr/fingerprint: 2AFB13F28FBBB0D1B9DAF63087EB3D32FB631AD9/,
'build trusts the Yocto release key' );
like( $kas,
qr{yocto-release:.*?repo: xcat-core.*?path: xCAT-genesis-builder/oe/keys/yocto-release\.asc}s,
'build loads the Yocto release key from the source tree' );
like( $kas,
qr{xcat-core:.*?layers:.*?xCAT-genesis-builder/oe/meta-xcat-genesis:}s,
'build loads the Genesis layer from the source tree' );
unlike( $kas, qr/gpg_keyserver:/,
'build does not depend on a public keyserver' );
like( $kas, qr/INHERIT \+= "archiver create-spdx vex"/,
'build generates source archives, an SBOM, and VEX metadata' );
like( $kas, qr/ARCHIVER_MODE\[src\] = "original"/,
'release archives retain original sources' );
like( $kas, qr/ARCHIVER_MODE\[diff\] = "1"/,
'release archives retain applied changes' );
like( $kas, qr/ARCHIVER_MODE\[recipe\] = "1"/,
'release archives retain recipe metadata' );
my $yocto_release_key =
read_file('xCAT-genesis-builder/oe/keys/yocto-release.asc');
is( sha256_hex($yocto_release_key),
'42d49e59f2aa01a1c1417a52d3c915a24a64060c852f33e3bd04fbb738457e70',
'vendored Yocto release key matches the reviewed key material' );
like( $kas,
qr{bitbake:.*?commit: acfe02fa38b5da9e6a36c6cedcf91d4fcbefbfbd.*?signed: true}s,
'BitBake revision requires a signed tag' );
like( $kas,
qr{openembedded-core:.*?commit: 5d1aa5c806c061a2994f4decb59016610f093213.*?signed: true}s,
'OpenEmbedded-Core revision requires a signed tag' );
like( $kas,
qr{meta-openembedded:.*?commit: af8b6d6b2f0b11595b0a0d5b82efa3129d52a628}s,
'meta-openembedded revision is pinned' );
my $x86_kas = read_file('xCAT-genesis-builder/oe/kas/x86_64.yml');
like( $x86_kas, qr{includes:\s*\n\s*- xCAT-genesis-builder/oe/kas/common\.yml},
'x86_64 build includes the common configuration' );
like( $x86_kas, qr/^machine: xcat-genesis-x86-64$/m,
'x86_64 build selects its machine' );
my $x86_32_kas = read_file('xCAT-genesis-builder/oe/kas/x86.yml');
like( $x86_32_kas, qr{includes:\s*\n\s*- xCAT-genesis-builder/oe/kas/common\.yml},
'x86 build includes the common configuration' );
like( $x86_32_kas, qr/^machine: xcat-genesis-x86$/m,
'x86 build selects its machine' );
my $armv7hf_kas = read_file('xCAT-genesis-builder/oe/kas/armv7hf.yml');
like( $armv7hf_kas, qr{includes:\s*\n\s*- xCAT-genesis-builder/oe/kas/common\.yml},
'armv7hf build includes the common configuration' );
like( $armv7hf_kas, qr/^machine: xcat-genesis-armv7hf$/m,
'armv7hf build selects its machine' );
my $aarch64_kas = read_file('xCAT-genesis-builder/oe/kas/aarch64.yml');
like( $aarch64_kas, qr{includes:\s*\n\s*- xCAT-genesis-builder/oe/kas/common\.yml},
'aarch64 build includes the common configuration' );
like( $aarch64_kas, qr/^machine: xcat-genesis-aarch64$/m,
'aarch64 build selects its machine' );
my $riscv64_kas = read_file('xCAT-genesis-builder/oe/kas/riscv64.yml');
like( $riscv64_kas, qr{includes:\s*\n\s*- xCAT-genesis-builder/oe/kas/common\.yml},
'riscv64 build includes the common configuration' );
like( $riscv64_kas, qr/^machine: xcat-genesis-riscv64$/m,
'riscv64 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' );
like( $ppc64le_kas, qr/^machine: xcat-genesis-ppc64le$/m,
'ppc64le build selects its machine' );
my $ppc64_kas = read_file('xCAT-genesis-builder/oe/kas/ppc64.yml');
like( $ppc64_kas, qr{includes:\s*\n\s*- xCAT-genesis-builder/oe/kas/common\.yml},
'ppc64 build includes the common configuration' );
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 $distro = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/conf/distro/xcat-genesis.conf'
);
like( $distro, qr/^DISTRO_CODENAME = "cheetah"$/m,
'distro uses the Cheetah codename' );
like( $distro, qr/^LINUX_VERSION_EXTENSION = "-xCAT-genesis"$/m,
'kernel release identifies xCAT Genesis' );
like( $distro,
qr/^KERNEL_MODULE_PACKAGE_SUFFIX = "-\$\{KERNEL_VERSION_PKG_NAME\}"$/m,
'kernel module package names use the normalized kernel release' );
like( $distro, qr/^TCLIBC = "glibc"$/m, 'distro uses glibc' );
like( $distro, qr/^INIT_MANAGER = "systemd"$/m,
'distro uses systemd' );
like( $distro, qr/^DISTRO_FEATURES_DEFAULTS = ""$/m,
'distro disables inherited feature defaults' );
like( $distro,
qr/^DISTRO_FEATURES = "acl ipv4 ipv6 largefile pci seccomp"$/m,
'distro keeps an explicit feature list' );
like( $distro, qr/^NO_RECOMMENDATIONS = "1"$/m,
'image excludes package recommendations' );
like( $distro, qr/PACKAGECONFIG:pn-systemd = ".*\bsysext\b.*"/,
'systemd includes system extensions' );
like( $distro,
qr/^PACKAGECONFIG:pn-networkmanager = "crypto-null libedit man-resolv-conf nmcli systemd"$/m,
'NetworkManager keeps the minimal feature set' );
like( $distro, qr/^PACKAGECONFIG:append:pn-iproute2 = " rdma"$/m,
'iproute2 includes the RDMA command' );
like( $distro, qr/^NETWORKMANAGER_DHCP_DEFAULT:pn-networkmanager = "internal"$/m,
'NetworkManager uses its internal DHCP client' );
like( $distro,
qr/^NETWORKMANAGER_DNS_RC_MANAGER_DEFAULT:pn-networkmanager = "symlink"$/m,
'NetworkManager manages the resolver symlink' );
unlike( $distro, qr/\b(?:dhclient|dhcpcd)\b/,
'distro excludes external DHCP clients' );
my $machine = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/xcat-genesis-x86-64.conf'
);
like( $machine, qr/^DEFAULTTUNE = "x86-64"$/m,
'x86_64 uses the baseline tune' );
unlike( $machine, qr/x86-64-v[234]/,
'x86_64 does not require a newer ISA level' );
like( $machine, qr/^MACHINE_FEATURES_DEFAULTS = ""$/m,
'machine disables inherited feature defaults' );
like( $machine, qr/^MACHINE_FEATURES = "pci"$/m,
'machine keeps an explicit feature list' );
like( $machine, qr/^IMAGE_FSTYPES = "cpio\.gz ext4"$/m,
'machine builds netboot and VM filesystems' );
like( $machine, qr/^QB_DEFAULT_FSTYPE = "ext4"$/m,
'QEMU uses a generated filesystem type' );
like( $machine, qr/^XCAT_GENESIS_CONSOLE_TTY = "ttyS0"$/m,
'x86_64 status console uses its serial terminal' );
like( $machine, qr/^XCAT_GENESIS_ARCHITECTURE = "x86_64"$/m,
'x86_64 exports its canonical extension identity' );
my $ppc64le_machine = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/xcat-genesis-ppc64le.conf'
);
like( $ppc64le_machine, qr/^DEFAULTTUNE = "ppc64p8le"$/m,
'ppc64le uses the POWER8 little-endian tune' );
like( $ppc64le_machine, qr/^KERNEL_IMAGETYPE = "vmlinux"$/m,
'ppc64le builds a directly bootable kernel' );
like( $ppc64le_machine, qr/^XCAT_GENESIS_CONSOLE_TTY = "hvc0"$/m,
'ppc64le status console uses its hypervisor terminal' );
like( $ppc64le_machine, qr/^QB_MACHINE = "-machine pseries"$/m,
'ppc64le QEMU target uses pSeries' );
like( $ppc64le_machine, qr/^QB_CPU = "-cpu POWER8"$/m,
'ppc64le QEMU target uses the supported CPU baseline' );
unlike( $ppc64le_machine, qr/^DEFAULTTUNE = "ppc64"$/m,
'ppc64le is not aliased to big-endian ppc64' );
like( $ppc64le_machine, qr/^XCAT_GENESIS_ARCHITECTURE = "ppc64le"$/m,
'ppc64le exports its canonical extension identity' );
my $ppc64_machine = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/xcat-genesis-ppc64.conf'
);
like( $ppc64_machine, qr/^DEFAULTTUNE = "powerpc64"$/m,
'ppc64 uses the generic big-endian ABI' );
unlike( $ppc64_machine, qr/^DEFAULTTUNE = "ppc64p8le"$/m,
'ppc64 is not aliased to little-endian ppc64le' );
like( $ppc64_machine, qr/^QB_MACHINE = "-machine pseries"$/m,
'ppc64 QEMU target uses pSeries' );
like( $ppc64_machine, qr/^QB_CPU = "-cpu POWER8"$/m,
'ppc64 QEMU target can emulate the generic ABI' );
like( $ppc64_machine, qr/^XCAT_GENESIS_CONSOLE_TTY = "hvc0"$/m,
'ppc64 status console uses its hypervisor terminal' );
like( $ppc64_machine, qr/^XCAT_GENESIS_ARCHITECTURE = "ppc64"$/m,
'ppc64 exports its canonical extension identity' );
my $x86_machine = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/xcat-genesis-x86.conf'
);
like( $x86_machine, qr/^DEFAULTTUNE = "i686"$/m,
'x86 uses the i686 baseline' );
unlike( $x86_machine, qr/^DEFAULTTUNE = "i586"$/m,
'x86 does not retain the i586 baseline' );
like( $x86_machine, qr/^QB_SYSTEM_NAME = "qemu-system-i386"$/m,
'x86 uses the 32-bit QEMU system target' );
like( $x86_machine, qr/^XCAT_GENESIS_CONSOLE_TTY = "ttyS0"$/m,
'x86 status console uses its serial terminal' );
like( $x86_machine, qr/^XCAT_GENESIS_ARCHITECTURE = "x86"$/m,
'x86 exports its canonical extension identity' );
my $armv7hf_machine = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/xcat-genesis-armv7hf.conf'
);
like( $armv7hf_machine, qr/^DEFAULTTUNE = "armv7ahf"$/m,
'armv7hf uses the ARMv7-A hard-float tune' );
like( $armv7hf_machine, qr/^ARM_INSTRUCTION_SET = "arm"$/m,
'armv7hf does not require Thumb instructions' );
unlike( $armv7hf_machine, qr/neon/i,
'armv7hf does not require NEON' );
like( $armv7hf_machine, qr/^XCAT_GENESIS_ARCHITECTURE = "armv7hf"$/m,
'armv7hf exports its canonical extension identity' );
like( $armv7hf_machine, qr/^QB_MACHINE = "-machine virt,highmem=off"$/m,
'armv7hf QEMU target uses the virtual platform' );
like( $armv7hf_machine, qr/^QB_CPU = "-cpu cortex-a15"$/m,
'armv7hf QEMU target uses an ARMv7 CPU' );
like( $armv7hf_machine, qr/^XCAT_GENESIS_CONSOLE_TTY = "ttyAMA0"$/m,
'armv7hf status console uses its PL011 terminal' );
my $aarch64_machine = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/xcat-genesis-aarch64.conf'
);
like( $aarch64_machine, qr/^DEFAULTTUNE = "aarch64"$/m,
'aarch64 uses the generic ARMv8-A tune' );
like( $aarch64_machine, qr/^XCAT_GENESIS_ARCHITECTURE = "aarch64"$/m,
'aarch64 exports its canonical extension identity' );
like( $aarch64_machine, qr/^QB_MACHINE = "-machine virt"$/m,
'aarch64 QEMU target uses the virtual platform' );
like( $aarch64_machine, qr/^QB_CPU = "-cpu cortex-a57"$/m,
'aarch64 QEMU target uses its conservative CPU baseline' );
like( $aarch64_machine, qr/^XCAT_GENESIS_CONSOLE_TTY = "ttyAMA0"$/m,
'aarch64 status console uses its PL011 terminal' );
my $riscv64_machine = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/xcat-genesis-riscv64.conf'
);
like( $riscv64_machine, qr/^DEFAULTTUNE = "riscv64"$/m,
'riscv64 uses the RV64GC tune' );
like( $riscv64_machine, qr/^XCAT_GENESIS_ARCHITECTURE = "riscv64"$/m,
'riscv64 exports its canonical extension identity' );
like( $riscv64_machine, qr/^EXTRA_IMAGEDEPENDS = "opensbi"$/m,
'riscv64 builds its OpenSBI firmware' );
like( $riscv64_machine, qr/^QB_DEFAULT_BIOS = "fw_jump\.elf"$/m,
'riscv64 QEMU target boots the built OpenSBI firmware' );
like( $riscv64_machine, qr/^QB_MACHINE = "-machine virt"$/m,
'riscv64 QEMU target uses the virtual platform' );
like( $riscv64_machine, qr/^QB_CPU = "-cpu rv64"$/m,
'riscv64 QEMU target uses the generic CPU' );
like( $riscv64_machine, qr/^XCAT_GENESIS_CONSOLE_TTY = "ttyS0"$/m,
'riscv64 status console uses its 8250 terminal' );
for my $machine_console (
[ x86_64 => $machine ],
[ x86 => $x86_machine ],
[ ppc64 => $ppc64_machine ],
[ ppc64le => $ppc64le_machine ],
[ armv7hf => $armv7hf_machine ],
[ aarch64 => $aarch64_machine ],
[ riscv64 => $riscv64_machine ],
)
{
unlike( $machine_console->[1],
qr/^QB_KERNEL_CMDLINE_APPEND = ".*xcat\.debug-shell/m,
"$machine_console->[0] VM starts the status console" );
}
my $image = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/images/xcat-genesis-image.bb'
);
unlike( $image,
qr/\b(?:dhclient|dhcpcd|busybox-udhcpc|systemd-networkd)\b/,
'base image excludes alternate network clients' );
like( $image, qr/\bxcat-genesis-network\b/,
'base image includes the Genesis network policy' );
like( $image, qr/\bxcat-genesis-protocol\b/,
'base image includes the destiny clients' );
like( $image, qr/\bpackagegroup-xcat-genesis-hardware\b/,
'base image includes open hardware tools' );
like( $image, qr/\bxcat-genesis-extensions\b/,
'base image includes the signed extension loader' );
like( $image, qr/\bxcat-genesis-console\b/,
'base image includes the status console' );
my $hardware_group = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/packagegroups/packagegroup-xcat-genesis-hardware.bb'
);
for my $tool (
qw(
pciutils usbutils hwloc ethtool rdma-core iproute2-rdma lldpd ipmitool curl jq
nvme-cli smartmontools sg3-utils lsscsi mdadm hdparm parted lvm2
memtester stress-ng edac-utils kernel-modules mstflint
xcat-genesis-hardware-control
)
)
{
like( $hardware_group, qr/\b\Q$tool\E\b/,
"hardware group includes $tool" );
}
like( $hardware_group,
qr/^RDEPENDS:\$\{PN\}:append:xcat-genesis-x86-64 = " dmidecode lshw mcelog"$/m,
'x86_64 diagnostics are architecture scoped' );
like( $hardware_group,
qr/^RDEPENDS:\$\{PN\}:append:xcat-genesis-x86 = " dmidecode lshw"$/m,
'x86 omits the unsupported machine-check daemon' );
like( $hardware_group,
qr/^RDEPENDS:\$\{PN\}:append:xcat-genesis-armv7hf = " dmidecode lshw"$/m,
'armv7hf diagnostics are architecture scoped' );
unlike( $hardware_group, qr/xcat-genesis-armv7(?:\s|\b)/,
'hardware packages do not use the old ARMv7 name' );
like( $hardware_group,
qr/^RDEPENDS:\$\{PN\}:append:xcat-genesis-ppc64 = " dmidecode xcat-genesis-hardware-control-iprutils"$/m,
'ppc64 includes the Power RAID provider' );
like( $hardware_group,
qr/^RDEPENDS:\$\{PN\}:append:xcat-genesis-ppc64le = " dmidecode xcat-genesis-hardware-control-iprutils"$/m,
'ppc64le includes the Power RAID provider' );
like( $hardware_group,
qr/^RDEPENDS:\$\{PN\}:append:xcat-genesis-riscv64 = " lshw"$/m,
'RISC-V omits unsupported DMI tools' );
unlike( $hardware_group,
qr/\b(?:storcli|perccli|ssacli|arcconf|nvidia-smi|rocm-smi)\b/i,
'base image excludes vendor-only tools' );
my $hardware_recipe = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/xcat-genesis-hardware-control_1.0.bb'
);
like( $hardware_recipe,
qr/^RDEPENDS:\$\{PN\} = "bash coreutils jq mstflint nvme-cli util-linux-flock"$/m,
'hardware control dependencies are explicit' );
like( $hardware_recipe,
qr/^PACKAGES:prepend:xcat-genesis-ppc64 = "\$\{PN\}-iprutils "$/m,
'iprutils provider package supports ppc64' );
like( $hardware_recipe,
qr/^PACKAGES:prepend:xcat-genesis-ppc64le = "\$\{PN\}-iprutils "$/m,
'iprutils provider package supports ppc64le' );
like( $hardware_recipe,
qr/^RDEPENDS:\$\{PN\}-iprutils:xcat-genesis-ppc64 = "\$\{PN\} bash iprutils"$/m,
'ppc64 iprutils dependencies are architecture scoped' );
like( $hardware_recipe,
qr/^RDEPENDS:\$\{PN\}-iprutils:xcat-genesis-ppc64le = "\$\{PN\} bash iprutils"$/m,
'iprutils provider dependencies are architecture scoped' );
like( $hardware_recipe, qr/\$\{datadir\}\/xcat\/genesis\/providers/,
'provider manifests use an architecture-neutral data path' );
unlike( $hardware_recipe,
qr/\b(?:storcli|perccli|ssacli|arcconf|nvidia-smi|rocm-smi)\b/i,
'hardware control excludes vendor-only tools' );
my $hardware_dispatcher = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files/genesis-hardware'
);
like( $hardware_dispatcher,
qr/destructive capability requires a task identity/,
'destructive operations require a task identity' );
like( $hardware_dispatcher,
qr/destructive capability requires an exact device identity/,
'destructive operations require an exact device' );
like( $hardware_dispatcher, qr/write_audit started/,
'destructive operations start an audit record' );
like( $hardware_dispatcher, qr/write_audit completed/,
'successful destructive operations complete their audit record' );
like( $hardware_dispatcher, qr/write_audit failed/,
'failed destructive operations close their audit record' );
for my $provider (qw(nvme mstflint iprutils)) {
my $manifest = read_file(
"xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files/$provider.json"
);
unlike( $manifest, qr/"destructive"\s*:\s*true/,
"$provider base provider is read-only" );
}
my $mstflint = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-support/mstflint/mstflint_4.36.0.bb'
);
like( $mstflint,
qr/^LICENSE = "Linux-OpenIB & MIT & BSD-2-Clause"$/m,
'mstflint records every linked source license' );
like( $mstflint,
qr/^SRCREV = "b9d9e844a14ae1c85cb90c76ccd4a56a0eccd599"$/m,
'mstflint source revision is pinned' );
like( $mstflint, qr/^RDEPENDS:\$\{PN\} = "python3-modules"$/m,
'mstflint installs its Python runtime' );
like( $mstflint, qr/^PACKAGECONFIG \?\?= "adb cables dc inband openssl"$/m,
'mstflint omits the non-portable rdmem tool' );
unlike( $mstflint, qr/--enable-(?:fw-mgr|nvml)/,
'mstflint excludes vendor-coupled features' );
my $iprutils = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-support/iprutils/iprutils_2.4.19.bb'
);
like( $iprutils, qr/^LICENSE = "CPL-1\.0"$/m,
'iprutils records its source license' );
like( $iprutils,
qr/^SRCREV = "9961e538736a6e81f1072a926c3ad91b8513c8c1"$/m,
'iprutils source revision is pinned' );
like( $iprutils, qr/^COMPATIBLE_HOST = "powerpc64\.\*-linux"$/m,
'iprutils is restricted to 64-bit Power targets' );
like( $iprutils, qr/--without-systemd --without-initscripts/,
'iprutils omits unused background services' );
my $kernel_append = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto_%.bbappend'
);
like( $kernel_append, qr/file:\/\/xcat-genesis-x86-64\.cfg/,
'x86_64 kernel uses the Genesis hardware fragment' );
like( $kernel_append, qr/file:\/\/xcat-genesis-x86-common\.cfg/,
'x86 targets share their hardware fragment' );
like( $kernel_append, qr/file:\/\/xcat-genesis-x86\.cfg/,
'x86 kernel uses its baseline fragment' );
like( $kernel_append, qr/do_kernel_metadata:prepend:xcat-genesis-x86/,
'x86 replaces the upstream QEMU CPU policy' );
like( $kernel_append, qr/file:\/\/xcat-genesis-armv7hf\.cfg/,
'armv7hf kernel uses the Genesis hardware fragment' );
like( $kernel_append,
qr/^SRCREV_machine:xcat-genesis-armv7hf = "b1ba5428513b52c2bd6acfd3ad0a910f699bc395"$/m,
'armv7hf kernel revision is pinned' );
like( $kernel_append, qr/file:\/\/xcat-genesis-aarch64\.cfg/,
'aarch64 kernel uses the Genesis hardware fragment' );
like( $kernel_append,
qr/^SRCREV_machine:xcat-genesis-aarch64 = "b1ba5428513b52c2bd6acfd3ad0a910f699bc395"$/m,
'aarch64 kernel revision is pinned' );
like( $kernel_append, qr/file:\/\/xcat-genesis-riscv64\.cfg/,
'riscv64 kernel uses the Genesis hardware fragment' );
like( $kernel_append,
qr/^SRCREV_machine:xcat-genesis-riscv64 = "b1ba5428513b52c2bd6acfd3ad0a910f699bc395"$/m,
'riscv64 kernel revision is pinned' );
like( $kernel_append, qr/file:\/\/xcat-genesis-powerpc64\.cfg/,
'64-bit Power kernels share the Genesis hardware fragment' );
like( $kernel_append,
qr/^SRCREV_machine:xcat-genesis-ppc64le = "b1ba5428513b52c2bd6acfd3ad0a910f699bc395"$/m,
'ppc64le kernel revision is pinned' );
like( $kernel_append, qr/do_kernel_metadata:prepend:xcat-genesis-ppc64le/,
'ppc64le repairs the upstream kernel metadata before configuration' );
like( $kernel_append,
qr/^SRCREV_machine:xcat-genesis-ppc64 = "b1ba5428513b52c2bd6acfd3ad0a910f699bc395"$/m,
'ppc64 kernel revision is pinned' );
like( $kernel_append, qr/do_kernel_metadata:prepend:xcat-genesis-ppc64\(\)/,
'ppc64 repairs the upstream kernel metadata before configuration' );
like( $kernel_append, qr/file:\/\/xcat-genesis-ppc64\.cfg/,
'ppc64 kernel uses its big-endian fragment' );
my $ppc64_kernel_config = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-ppc64.cfg'
);
like( $ppc64_kernel_config, qr/^CONFIG_CPU_BIG_ENDIAN=y$/m,
'ppc64 kernel is big endian' );
like( $ppc64_kernel_config, qr/^# CONFIG_CPU_LITTLE_ENDIAN is not set$/m,
'ppc64 kernel is not little endian' );
my $kernel_config = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-x86-common.cfg'
);
for my $symbol (
qw(
CONFIG_MODULES CONFIG_MEGARAID_SAS CONFIG_SCSI_MPT3SAS CONFIG_SCSI_HPSA
CONFIG_BLK_DEV_NVME CONFIG_IXGBE CONFIG_I40E CONFIG_ICE CONFIG_MLX5_CORE
CONFIG_INFINIBAND CONFIG_MLX5_INFINIBAND CONFIG_IPMI_HANDLER CONFIG_EDAC
CONFIG_USB_STORAGE CONFIG_BLK_DEV_LOOP CONFIG_OVERLAY_FS CONFIG_SQUASHFS
CONFIG_SQUASHFS_XATTR CONFIG_SQUASHFS_ZSTD
)
)
{
like( $kernel_config, qr/^\Q$symbol\E=[ym]$/m,
"x86_64 kernel enables $symbol" );
}
my $x86_kernel_config = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-x86.cfg'
);
like( $x86_kernel_config, qr/^CONFIG_M686=y$/m,
'x86 kernel selects the i686 processor family' );
like( $x86_kernel_config, qr/^# CONFIG_M586 is not set$/m,
'x86 kernel does not retain i586' );
like( $x86_kernel_config, qr/^CONFIG_HIGHMEM4G=y$/m,
'x86 kernel supports the 32-bit high-memory model' );
like( $x86_kernel_config, qr/^# CONFIG_X86_MCE is not set$/m,
'x86 kernel supports processors without machine-check registers' );
for my $vendor (qw(AMD INTEL)) {
like( $x86_kernel_config,
qr/^# CONFIG_X86_MCE_\Q$vendor\E is not set$/m,
"x86 kernel disables the $vendor machine-check feature" );
}
my $x86_64_kernel_config = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-x86-64.cfg'
);
like( $x86_64_kernel_config, qr/^CONFIG_EDAC_AMD64=m$/m,
'x86_64 keeps its architecture-specific EDAC driver' );
my $armv7hf_kernel_config = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-armv7hf.cfg'
);
for my $symbol (
qw(
CONFIG_ARCH_VIRT CONFIG_AEABI CONFIG_VFP CONFIG_SERIAL_AMBA_PL011
CONFIG_VIRTIO_MMIO CONFIG_VIRTIO_BLK CONFIG_VIRTIO_NET
CONFIG_SCSI_MPT3SAS CONFIG_MEGARAID_SAS CONFIG_BLK_DEV_NVME
CONFIG_IXGBE CONFIG_MLX4_CORE CONFIG_INFINIBAND CONFIG_IPMI_HANDLER
CONFIG_USB_XHCI_HCD CONFIG_USB_STORAGE CONFIG_BLK_DEV_LOOP
CONFIG_OVERLAY_FS CONFIG_SQUASHFS
)
)
{
like( $armv7hf_kernel_config, qr/^\Q$symbol\E=[ym]$/m,
"armv7hf kernel enables $symbol" );
}
my $aarch64_kernel_config = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-aarch64.cfg'
);
for my $symbol (
qw(
CONFIG_ARM64 CONFIG_ARCH_VIRT CONFIG_SERIAL_AMBA_PL011
CONFIG_VIRTIO_PCI CONFIG_VIRTIO_BLK CONFIG_VIRTIO_NET
CONFIG_SCSI_MPT3SAS CONFIG_SCSI_HPSA CONFIG_MEGARAID_SAS
CONFIG_BLK_DEV_NVME CONFIG_IXGBE CONFIG_I40E CONFIG_ICE
CONFIG_MLX5_CORE CONFIG_INFINIBAND CONFIG_MLX5_INFINIBAND
CONFIG_IPMI_HANDLER CONFIG_EDAC CONFIG_USB_XHCI_HCD
CONFIG_USB_STORAGE CONFIG_BLK_DEV_LOOP CONFIG_OVERLAY_FS CONFIG_SQUASHFS
)
)
{
like( $aarch64_kernel_config, qr/^\Q$symbol\E=[ym]$/m,
"aarch64 kernel enables $symbol" );
}
my $riscv64_kernel_config = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-riscv64.cfg'
);
for my $symbol (
qw(
CONFIG_64BIT CONFIG_RISCV CONFIG_RISCV_SBI CONFIG_SERIAL_8250_CONSOLE
CONFIG_VIRTIO_PCI CONFIG_VIRTIO_BLK CONFIG_VIRTIO_NET
CONFIG_SCSI_MPT3SAS CONFIG_SCSI_HPSA CONFIG_MEGARAID_SAS
CONFIG_BLK_DEV_NVME CONFIG_IXGBE CONFIG_I40E CONFIG_ICE
CONFIG_MLX5_CORE CONFIG_INFINIBAND CONFIG_MLX5_INFINIBAND
CONFIG_IPMI_HANDLER CONFIG_EDAC CONFIG_USB_XHCI_HCD
CONFIG_USB_STORAGE CONFIG_BLK_DEV_LOOP CONFIG_OVERLAY_FS CONFIG_SQUASHFS
)
)
{
like( $riscv64_kernel_config, qr/^\Q$symbol\E=[ym]$/m,
"riscv64 kernel enables $symbol" );
}
for my $symbol (
qw(
CONFIG_ACPI CONFIG_ACPI_APEI CONFIG_ACPI_APEI_GHES
CONFIG_EFI CONFIG_EFI_STUB
)
)
{
like( $riscv64_kernel_config, qr/^\Q$symbol\E=[ym]$/m,
"riscv64 kernel enables $symbol to boot on ACPI firmware" );
}
my $powerpc64_kernel_config = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-powerpc64.cfg'
);
for my $symbol (
qw(
CONFIG_PPC_PSERIES CONFIG_PPC_POWERNV CONFIG_PPC_64S_HASH_MMU
CONFIG_PPC_RADIX_MMU CONFIG_SCSI_IPR CONFIG_SCSI_IBMVSCSI
CONFIG_SCSI_IBMVFC CONFIG_MEGARAID_SAS CONFIG_BLK_DEV_NVME
CONFIG_IBMVETH CONFIG_IBMVNIC CONFIG_MLX5_CORE CONFIG_INFINIBAND
CONFIG_IPMI_HANDLER CONFIG_USB_XHCI_HCD CONFIG_USB_STORAGE
CONFIG_BLK_DEV_LOOP CONFIG_OVERLAY_FS CONFIG_SQUASHFS
)
)
{
like( $powerpc64_kernel_config, qr/^\Q$symbol\E=[ym]$/m,
"64-bit Power kernel enables $symbol" );
}
like( $powerpc64_kernel_config, qr/^# CONFIG_PPC_VAS is not set$/m,
'POWER8 image disables unsupported VAS facilities' );
my $protocol_recipe = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-protocol/xcat-genesis-protocol_1.0.bb'
);
like( $protocol_recipe,
qr{^FILESEXTRAPATHS:prepend := "\$\{THISDIR\}/\.\./\.\./\.\./\.\./\.\./xCAT-genesis-scripts/usr/bin:"$}m,
'destiny clients use the canonical scripts' );
like( $protocol_recipe, qr/file:\/\/getdestiny.*file:\/\/nextdestiny/s,
'both destiny clients are packaged' );
like( $protocol_recipe, qr/^RDEPENDS:\$\{PN\} = "bash openssl-bin"$/m,
'destiny client dependencies are explicit' );
unlike( $protocol_recipe, qr/\bdoxcat\b/,
'obsolete bootstrap script is excluded' );
my $networkmanager = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/networkmanager/networkmanager_%.bbappend'
);
like( $networkmanager, qr/^EXTRA_OEMESON:append = " -Dtests=no"$/m,
'NetworkManager omits its test suite' );
like( $networkmanager,
qr{sed -i '/\^Also=NetworkManager-wait-online\.service\$/d'},
'NetworkManager does not enable wait-online' );
my $network_recipe = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-network/xcat-genesis-network_1.0.bb'
);
like( $network_recipe,
qr/^RDEPENDS:\$\{PN\} = "networkmanager-daemon networkmanager-nmcli"$/m,
'network policy installs the daemon and nmcli' );
my $network_config = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-network/files/10-xcat-genesis.conf'
);
like( $network_config, qr/^ipv4\.dhcp-timeout=20$/m,
'IPv4 DHCP has a bounded timeout' );
like( $network_config, qr/^ipv6\.dhcp-duid=ll$/m,
'IPv6 DHCP uses the link-layer DUID' );
like( $network_config, qr/^ipv6\.dhcp-timeout=20$/m,
'IPv6 DHCP has a bounded timeout' );
my $init_recipe = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/xcat-genesis-init_1.0.bb'
);
like( $init_recipe, qr/\bcoreutils\b/,
'runtime status dependencies are explicit' );
like( $init_recipe, qr/\butil-linux-logger\b/,
'runtime logging dependency is explicit' );
like( $init_recipe, qr/file:\/\/genesis-status/,
'runtime status helper is packaged' );
like( $init_recipe, qr/file:\/\/genesis-maintenance-shell/,
'maintenance shell is packaged' );
like( $init_recipe, qr/file:\/\/genesis-functions/,
'shared runtime functions are packaged' );
like( $init_recipe,
qr/install -m 0644 .*genesis-functions.*\$\{libexecdir\}\/xcat\/genesis-functions/s,
'shared runtime functions are installed as data' );
unlike( $init_recipe, qr/genesis-debug-shell|XCAT_GENESIS_DEBUG/,
'legacy debug shell is absent from the image recipe' );
my $legacy_debug_shell = File::Spec->catfile(
$repo_root,
qw(xCAT-genesis-builder oe meta-xcat-genesis recipes-core xcat-genesis-init files genesis-debug-shell)
);
my $legacy_debug_service = File::Spec->catfile(
$repo_root,
qw(xCAT-genesis-builder oe meta-xcat-genesis recipes-core xcat-genesis-init files xcat-genesis-debug-shell@.service)
);
ok( !-e $legacy_debug_shell, 'legacy debug shell script is removed' );
ok( !-e $legacy_debug_service, 'legacy debug shell service is removed' );
my $maintenance_shell = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-maintenance-shell'
);
like( $maintenance_shell, qr/^#!\/bin\/bash$/m,
'maintenance shell uses the packaged Bash runtime' );
like( $maintenance_shell, qr/^printf 'xCAT Genesis maintenance shell\\n'$/m,
'maintenance shell identifies Genesis' );
like( $maintenance_shell, qr/^printf 'Exit returns to the status console\.\\n\\n'$/m,
'maintenance shell explains how to return to the console' );
like( $maintenance_shell, qr/^export PS1='genesis# '$/m,
'maintenance shell has an explicit prompt' );
like( $maintenance_shell, qr{^exec /bin/bash --noprofile --norc -i$}m,
'maintenance shell starts isolated Bash' );
my $status_helper = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-status'
);
like( $status_helper, qr/mktemp .*\.\$\{component\}\.XXXXXX/,
'runtime status records use atomic temporary files' );
like( $status_helper, qr/tr -cd '\\040-\\176'/,
'runtime status details are printable text' );
my $console_recipe = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/xcat-genesis-console_1.0.bb'
);
like( $console_recipe, qr/^DEPENDS = "libnewt systemd"$/m,
'status console builds against libnewt and the journal API' );
like( $console_recipe,
qr/^RDEPENDS:\$\{PN\} = "libnewt libsystemd ncurses-terminfo-base xcat-genesis-init"$/m,
'status console installs its terminal and maintenance-shell runtime' );
like( $console_recipe, qr/^inherit meson pkgconfig systemd$/m,
'status console uses the project build definition' );
unlike( $console_recipe, qr/\b(?:whiptail|popt)\b/,
'status console omits the dialog wrapper' );
my $console_build = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/meson.build'
);
like( $console_build, qr/'c_std=c17'/,
'status console uses the C17 language contract' );
like( $console_build, qr/dependency\('libnewt'\).*dependency\('libsystemd'\)/s,
'status console links Newt and the journal client library' );
like( $console_build, qr/'werror=true'/,
'status console treats compiler warnings as build failures' );
my $console_service = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console@.service'
);
like( $console_service, qr/^Environment=TERM=xterm$/m,
'status console redraws line graphics after a late attach' );
like( $console_service, qr/^Environment=COLUMNS=80$/m,
'status console fixes the serial width' );
like( $console_service, qr/^Environment=LINES=24$/m,
'status console fixes the serial height' );
like( $console_service, qr/^TTYPath=\/dev\/%I$/m,
'status console binds to the machine console' );
unlike( $console_service, qr/xcat\.debug-shell|genesis-debug-shell/,
'status console has no boot-time debug escape' );
my $console_source_dir =
'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src';
my $console_source = join "\n", map {
read_file("$console_source_dir/$_")
} qw(console.h main.c newt_ui.c plain_ui.c shell.c state.c support.c);
ok( !-e File::Spec->catfile(
$repo_root,
qw(xCAT-genesis-builder oe meta-xcat-genesis recipes-core xcat-genesis-console files xcat-genesis-console.c)
),
'status console is not kept as a monolithic source file' );
unlike( $console_source, qr/genesis-debug-shell|XCAT_ON_DEMAND_SHELL/,
'status console only uses the common maintenance-shell launcher' );
like( $console_source, qr/newtDrawRootText\(1, 0, "xCAT Genesis"\)/,
'status console keeps the product name in the header' );
like( $console_source,
qr/F1 Help F2 Diagnostics F3 Logs F12 Shell/,
'status console keeps useful shortcuts in the footer' );
unlike( $console_source, qr/NEWT_KEY_F5|F5 Refresh|Ctrl-L Redraw/,
'status console omits redundant redraw shortcuts' );
like( $console_source, qr/newtFormSetTimer\(screen\.form, 1000\)/,
'status console updates timers once per second' );
like( $console_source, qr/"In stage"/,
'status console labels the stage duration precisely' );
like( $console_source,
qr/identity = strcmp\(state->node, "unassigned"\).*?state->local_hostname/s,
'status header falls back to the local hostname before assignment' );
like( $console_source,
qr/show_serial = useful_identity\(state->serial\).*?state->serial/s,
'status header includes a useful firmware serial' );
unlike( $console_source,
qr/xcat_set_text\(context,\s*sizeof\(context\),[^;]*state->architecture/s,
'status header omits the architecture' );
like( $console_source, qr/newtCenteredWindow\(72, 17, "Genesis status"\)/,
'main status removes unused top and bottom rows' );
like( $console_source, qr/newtCenteredWindow\(72, 19, "Genesis diagnostics"\)/,
'F2 opens diagnostics' );
like( $console_source,
qr/xcat\.bootloader.*?"xnba".*?"xNBA".*?"pxelinux".*?"PXELINUX"/s,
'diagnostics recognizes xNBA and PXELINUX markers' );
like( $console_source, qr/PXE \(unknown loader\)/,
'older boot configurations have an honest PXE fallback' );
like( $console_source,
qr/"Identity\\n\\n".*"\\nManagement network\\n\\n".*"\\nxCAT\\n\\n".*"\\nRuntime\\n\\n"/s,
'diagnostics group identity, connection, and runtime data' );
like( $console_source,
qr/(?:add_row\(&screen, LABEL_SERIAL, 4, "Serial"\)|STATUS_FIELD_SERIAL)/,
'main status includes the hardware serial' );
unlike( $console_source,
qr/add_row\(&screen, LABEL_(?:RELEASE|SYSTEM|BOOT|EXTENSIONS|HARDWARE|DEBUG),/,
'inventory and maintenance controls stay off the main status' );
like( $console_source, qr/update_form\(&screen, &(?:state|view), changed\)/,
'stable status updates only the timers' );
like( $console_source, qr/if \(\+\+redraw >= 30\)/,
'serial console limits periodic full redraws' );
like( $console_source, qr/newtFormAddHotKey\(screen\.form, NEWT_KEY_F3\)/,
'F3 opens recent Genesis logs' );
like( $console_source, qr/sd_journal_open\(&journal,/,
'log view reads the journal without a subprocess' );
like( $console_source, qr/LOG_LINE_COUNT = 128/,
'log view keeps a bounded serial-friendly history' );
like( $console_source,
qr/newtListbox\(1, 1, LOG_VIEW_HEIGHT, NEWT_FLAG_SCROLL\).*?newtFormSetTimer\(form, 1000\)/s,
'log view is scrollable and refreshes once per second' );
like( $console_source,
qr/bool follow = true.*?NEWT_KEY_UP.*?follow = false/s,
'upward log navigation pauses following' );
like( $console_source,
qr/NEWT_KEY_END.*?follow = true.*?newtListboxSetCurrentByKey/s,
'End resumes log following' );
like( $console_source,
qr/newtFormAddHotKey\(screen\.form, NEWT_KEY_F12\).*?show_maintenance_shell\(\)/s,
'F12 opens the maintenance-shell confirmation' );
like( $console_source,
qr/newtWinChoice\(.*?Open a root maintenance shell\?/s,
'maintenance shell requires confirmation' );
like( $console_source,
qr{execl\("/usr/libexec/xcat/genesis-maintenance-shell"},
'maintenance shell uses a fixed packaged executable' );
like( $console_source,
qr/FD_CLOEXEC.*?write\(exec_error_pipe\[1\].*?exec_error_size ==/s,
'maintenance shell reports only direct launch failures' );
unlike( $console_source, qr/WEXITSTATUS/,
'maintenance shell does not reinterpret the shell exit status' );
unlike( $console_source, qr/\b(?:system|popen)\s*\(/,
'status console avoids command-string execution' );
my $network_state_service = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/xcat-genesis-network-state.service'
);
like( $network_state_service, qr/^After=NetworkManager\.service$/m,
'network selection follows NetworkManager' );
like( $network_state_service,
qr/^Before=xcat-genesis-network-ready\.target$/m,
'network selection gates Genesis readiness' );
my $register_service = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/xcat-genesis-register.service'
);
like( $register_service,
qr/^Requires=xcat-genesis-network-ready\.target xcat-genesis-extensions\.service$/m,
'registration requires Genesis network readiness' );
like( $register_service,
qr/^After=xcat-genesis-network-ready\.target xcat-genesis-extensions\.service$/m,
'registration waits for extension verification' );
unlike( $register_service, qr/network-online\.target/,
'registration avoids generic network readiness' );
my $extension_recipe = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-extensions/xcat-genesis-extensions_1.0.bb'
);
like( $extension_recipe,
qr/^RDEPENDS:\$\{PN\} = "bash coreutils jq openssl-bin systemd xcat-genesis-init"$/m,
'extension verifier dependencies are explicit' );
my $extension_loader = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-extensions/files/genesis-sysext'
);
like( $extension_loader, qr/openssl pkeyutl -verify -pubin .* -rawin/s,
'extension manifests use Ed25519 verification' );
like( $extension_loader, qr/extension architecture .* does not match/,
'extension architecture mismatches fail closed' );
like( $extension_loader, qr/armv7\*\) printf '%s\\n' armv7hf/,
'ARMv7 runtimes use the armv7hf extension identity' );
like( $extension_loader, qr/extension kernel release does not match/,
'kernel extensions require an exact release' );
like( $extension_loader, qr/systemd-sysext refresh/,
'verified extensions are merged by systemd' );
my $extension_service = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-extensions/files/xcat-genesis-extensions.service'
);
like( $extension_service,
qr{^ConditionDirectoryNotEmpty=/var/lib/xcat/genesis/extensions$}m,
'extension loading requires staged artifacts' );
like( $extension_service, qr/^Before=xcat-genesis-register\.service$/m,
'extensions load before xCAT registration' );
my $extension_class = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/classes-recipe/xcat-genesis-extension.bbclass'
);
like( $extension_class, qr/^inherit sysext-image$/m,
'extensions use the upstream systemd image class' );
like( $extension_class, qr/^IMAGE_FSTYPES = "squashfs-zst"$/m,
'extensions are immutable SquashFS images' );
like( $extension_class,
qr/^XCAT_GENESIS_EXTENSION_ARCHITECTURE \?\?= "\$\{XCAT_GENESIS_ARCHITECTURE\}"$/m,
'extension manifests use the canonical machine identity' );
like( $extension_class, qr/"armv7hf"/,
'extension manifests accept the canonical ARMv7 identity' );
unlike( $extension_class, qr/"armv7"/,
'extension manifests reject the ambiguous ARMv7 identity' );
like( $extension_class, qr/XCAT_GENESIS_EXTENSION_KERNEL_RELEASE/,
'extension metadata supports kernel ABI pinning' );
like( $extension_class, qr/Restricted Genesis extensions must set LICENSE_FLAGS/,
'restricted extensions require explicit license acceptance' );
like( $extension_class, qr/"sha256":/,
'extension manifests record the image digest' );
my $smoke_extension = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-extensions/images/xcat-genesis-extension-smoke.bb'
);
like( $smoke_extension, qr/^XCAT_GENESIS_EXTENSION_NAME = "xcat-smoke"$/m,
'open smoke extension exercises the build path' );
like( $smoke_extension, qr/^COMPATIBLE_HOST = "x86_64\.\*-linux"$/m,
'smoke extension is restricted to its declared architecture' );
my $preset = read_file(
'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/00-xcat-genesis.preset'
);
is( $preset, "disable getty\@.service\n",
'preset disables the unused virtual-terminal getty' );
done_testing();