From f46106acef82b698ca156a0908128ef54b6b0696 Mon Sep 17 00:00:00 2001 From: Daniel Hilst <392820+dhilst@users.noreply.github.com> Date: Sat, 12 Sep 2026 08:45:38 -0300 Subject: [PATCH 01/17] test(xcat-core): the Genesis build sources live in a directory named for a builder xCAT-genesis-builder holds two unrelated things. Most of it is a builder that nothing calls: buildrpm, debuild-xcat-genesis-base, install, check, findmcprpms and a spec for a package no pipeline makes. The rest is the Genesis image itself -- the dracut modules, the OpenEmbedded layers, the deb packaging and 80-net-name-slot.rules -- which buildrpms.pl and the GitHub OE job read on every build. The name says the wrong thing, and a reader who follows it finds the dead half first. The directory is renamed for what it holds. genesis_base_sources_staged.t drives buildsources_genesis_base against a scratch checkout and asserts the tarball xCAT-genesis-base.spec reads carries the EL and the Ubuntu dracut modules and 80-net-name-slot.rules. It fails on this commit because the staging still names xCAT-genesis-builder. Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com> --- xCAT-test/unit/genesis_base_sources_staged.t | 91 ++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 xCAT-test/unit/genesis_base_sources_staged.t diff --git a/xCAT-test/unit/genesis_base_sources_staged.t b/xCAT-test/unit/genesis_base_sources_staged.t new file mode 100644 index 000000000..839fcbab5 --- /dev/null +++ b/xCAT-test/unit/genesis_base_sources_staged.t @@ -0,0 +1,91 @@ +#!/usr/bin/env perl +# xCAT-genesis-base.spec builds the Genesis image from a tarball that buildrpms.pl stages: +# the dracut_105 modules and 80-net-name-slot.rules. Renaming the directory they live in +# breaks that staging silently -- the spec then builds an image with no 97xcat module, and +# the failure appears as a node that netboots into a plain dracut shell. +# +# buildsources_genesis_base() needs a management-node-free environment but not much else, so +# it is lifted out of buildrpms.pl and run against a scratch checkout. +use strict; +use warnings; + +use Cwd qw(getcwd); +use File::Path qw(make_path); +use File::Slurper qw(read_text write_text); +use File::Temp qw(tempdir); +use FindBin; +use lib "$FindBin::Bin/../lib"; +use Test::More; + +use XCAT::Test::File qw(repo_path); + +my $builder = repo_path('buildrpms.pl'); +plan skip_all => 'buildrpms.pl not found' unless -f $builder; + +my $source = read_text($builder); +our ($body) = $source =~ /(sub\s+buildsources_genesis_base\s*\(\$\).*?\n\}\n)/s; +# BAIL_OUT rather than skip: a rename that stops this matching must fail loudly instead of +# quietly covering nothing. +BAIL_OUT("could not extract buildsources_genesis_base from buildrpms.pl") unless $body; + +# The staged directory the spec reads, as buildrpms.pl names it. Read back from the code so +# the test cannot disagree with it about the name. +my ($staged_dir) = $body =~ m{\bcp -a "([^/"]+)/dracut_105"}; +BAIL_OUT('buildsources_genesis_base stages no dracut_105 directory') unless $staged_dir; +is($staged_dir, 'xCAT-genesis-base', 'the dracut assets are staged from xCAT-genesis-base'); +ok(-d repo_path($staged_dir), "$staged_dir is in the checkout"); + +{ + package Scratch; + use File::Copy qw(cp); + use File::Path qw(make_path remove_tree); + our ($SOURCES, $SOURCE_DATE_EPOCH); + sub sh_or_die { + my ($cmd, $message) = @_; + system($cmd) == 0 or die "$message\n"; + return 0; + } + eval $main::body; ## no critic + die $@ if $@; +} + +my $scratch = tempdir(CLEANUP => 1); +my $checkout = "$scratch/checkout"; +make_path("$checkout/$staged_dir/dracut_105/el", "$checkout/$staged_dir/dracut_105/ubuntu"); +write_text("$checkout/$staged_dir/dracut_105/el/module-setup.sh", "el module\n"); +write_text("$checkout/$staged_dir/dracut_105/ubuntu/module-setup.sh", "ubuntu module\n"); +write_text("$checkout/$staged_dir/80-net-name-slot.rules", "rules\n"); + +$Scratch::SOURCES = "$scratch/SOURCES"; +$Scratch::SOURCE_DATE_EPOCH = 1700000000; +make_path($Scratch::SOURCES); + +my $cwd = getcwd(); +chdir $checkout or die "chdir $checkout: $!"; +my $ok = eval { Scratch::buildsources_genesis_base('alma+epel-10-x86_64'); 1 }; +my $err = $@; +chdir $cwd or die "chdir back: $!"; +ok($ok, 'buildsources_genesis_base stages the Genesis build sources') or diag($err); + +my $tarball = "$Scratch::SOURCES/xCAT-genesis-base-build-support.tar.bz2"; +ok(-s $tarball, 'the build support tarball is written'); + +my @members = split /\n/, (`tar tjf '$tarball' 2>/dev/null` // ''); +ok(scalar(grep { m{dracut_105/el/module-setup\.sh$} } @members), + 'the EL dracut module is in the tarball'); +ok(scalar(grep { m{dracut_105/ubuntu/module-setup\.sh$} } @members), + 'the Ubuntu dracut module is in the tarball'); +ok(scalar(grep { m{/80-net-name-slot\.rules$} } @members), + '80-net-name-slot.rules is in the tarball'); + +# A checkout without the directory must stop the build, not produce an empty tarball. +my $empty = "$scratch/empty"; +make_path($empty); +chdir $empty or die "chdir $empty: $!"; +my $died = !eval { Scratch::buildsources_genesis_base('alma+epel-10-x86_64'); 1 }; +my $message = $@; +chdir $cwd or die "chdir back: $!"; +ok($died, 'a checkout without the Genesis directory stops the build'); +like($message, qr/\Q$staged_dir\E/, 'the message names the directory it wants'); + +done_testing(); From 2a7364476e5640300cc767e106553b247290b1b4 Mon Sep 17 00:00:00 2001 From: Daniel Hilst <392820+dhilst@users.noreply.github.com> Date: Sat, 12 Sep 2026 08:45:49 -0300 Subject: [PATCH 02/17] fix(xcat-core): xCAT-genesis-builder is named for a builder nothing calls The directory holds the Genesis image sources and, beside them, a builder that no pipeline runs. buildrpm, debuild-xcat-genesis-base, install, install.ubuntu, check, cmdlist_check, findmcprpms, xCAT-genesis-builder.spec and the root makerpm-genesisbuilder have no caller. So has build-ubunturepo, which builddebs.pl replaced, and its -r flag has set an unread variable since 2013. docs/source/references/coral/known_issues/genesis_base.rst tells the reader to install an rpm no build produces, and go-xcat offers to remove that package. Delete all of them, and rename the directory xCAT-genesis-base for what it holds: the dracut modules the EL spec and the Ubuntu builder read, the OpenEmbedded layers the GitHub job builds, the deb packaging, the image scripts and 80-net-name-slot.rules. Every reader of the old path is updated: buildrpms.pl, .gitattributes, the OE workflow and the OpenEmbedded tests. buildrpms.pl mapped the package xCAT-genesis-base to the directory xCAT-genesis-builder; the two names are the same now, so the mapping goes. xcat_probe_package_payload.t read build-ubunturepo for the probe helper staging. builddebs.pl stages them with XCAT::BuildUtils::stage_probe_helpers, which build_utils.t drives directly. genesis_base_sources_staged.t passes with this change. Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com> --- .gitattributes | 2 +- .github/workflows/xcat_test.yml | 10 +- build-ubunturepo | 711 ------------------ buildrpms.pl | 15 +- docs/source/developers/guides/code/builds.rst | 12 +- docs/source/references/coral/index.rst | 1 - .../coral/known_issues/genesis_base.rst | 24 - .../references/coral/known_issues/index.rst | 7 - makerpm-genesisbuilder | 25 - .../80-net-name-slot.rules | 0 .../LICENSE.html | 0 .../builddeb-genesis-base | 0 .../debian/changelog | 0 .../debian/control | 0 .../debian/copyright | 0 .../debian/dirs | 0 .../debian/docs | 0 .../debian/install | 0 .../debian/postinst | 0 .../debian/postrm | 0 .../debian/preinst | 0 .../debian/prerm | 0 .../debian/rules | 0 .../debian/source/format | 0 .../dhclient-script | 0 .../dhclient.conf | 0 .../dracut_105/README | 0 .../dracut_105/el/dhclient-script | 0 .../dracut_105/el/dhclient.conf | 0 .../dracut_105/el/module-setup.sh | 0 .../dracut_105/el/rsyslog.conf | 0 .../dracut_105/el/xcat-cmdline.sh | 0 .../dracut_105/el/xcatroot | 0 .../dracut_105/ubuntu/dhclient-script | 0 .../dracut_105/ubuntu/dhclient.conf | 0 .../dracut_105/ubuntu/module-setup.sh | 0 .../dracut_105/ubuntu/rsyslog.conf | 0 .../dracut_105/ubuntu/xcat-cmdline.sh | 0 .../dracut_105/ubuntu/xcatroot | 0 .../oe/.gitignore | 0 .../oe/build | 0 .../oe/export | 0 .../oe/export-extension | 0 .../oe/kas/aarch64.yml | 2 +- .../oe/kas/armv7hf.yml | 2 +- .../oe/kas/common.yml | 4 +- .../oe/kas/ppc64.yml | 2 +- .../oe/kas/ppc64le.yml | 2 +- .../oe/kas/riscv64.yml | 2 +- .../oe/kas/s390x.yml | 2 +- .../oe/kas/x86.yml | 2 +- .../oe/kas/x86_64.yml | 2 +- .../oe/keys/yocto-release.asc | 0 .../xcat-genesis-extension.bbclass | 0 .../conf/distro/xcat-genesis.conf | 0 .../oe/meta-xcat-genesis/conf/layer.conf | 0 .../conf/machine/include/s390x/tune-s390x.inc | 0 .../conf/machine/xcat-genesis-aarch64.conf | 0 .../conf/machine/xcat-genesis-armv7hf.conf | 0 .../conf/machine/xcat-genesis-ppc64.conf | 0 .../conf/machine/xcat-genesis-ppc64le.conf | 0 .../conf/machine/xcat-genesis-riscv64.conf | 0 .../conf/machine/xcat-genesis-s390x.conf | 0 .../conf/machine/xcat-genesis-x86-64.conf | 0 .../conf/machine/xcat-genesis-x86.conf | 0 .../networkmanager/networkmanager_%.bbappend | 0 .../openssl/files/50-xcat-s390x.conf | 0 .../openssl/openssl_%.bbappend | 0 .../files/10-xcat-genesis.conf | 0 .../xcat-genesis-network_1.0.bb | 0 .../xcat-genesis-qeth/files/genesis-qeth | 0 .../files/xcat-genesis-qeth.service | 0 .../xcat-genesis-qeth_1.0.bb | 0 .../recipes-core/images/xcat-genesis-image.bb | 0 .../packagegroup-xcat-genesis-hardware.bb | 0 .../files/xcat-genesis-console/.clang-format | 0 .../files/xcat-genesis-console/meson.build | 0 .../files/xcat-genesis-console/src/console.h | 0 .../files/xcat-genesis-console/src/main.c | 0 .../files/xcat-genesis-console/src/newt_ui.c | 0 .../files/xcat-genesis-console/src/plain_ui.c | 0 .../files/xcat-genesis-console/src/shell.c | 0 .../files/xcat-genesis-console/src/state.c | 0 .../files/xcat-genesis-console/src/support.c | 0 .../files/xcat-genesis-console@.service | 0 .../xcat-genesis-console_1.0.bb | 0 .../files/genesis-sysext | 0 .../files/xcat-genesis-extensions.service | 0 .../xcat-genesis-extensions_1.0.bb | 0 .../files/genesis-hardware | 0 .../files/iprutils.json | 0 .../files/mstflint.json | 0 .../files/nvme.json | 0 .../files/provider-iprutils | 0 .../files/provider-mstflint | 0 .../files/provider-nvme | 0 .../xcat-genesis-hardware-control_1.0.bb | 0 .../files/00-xcat-genesis.preset | 0 .../xcat-genesis-init/files/genesis-action | 0 .../xcat-genesis-init/files/genesis-functions | 0 .../files/genesis-maintenance-shell | 0 .../xcat-genesis-init/files/genesis-metrics | 0 .../files/genesis-network-refresh | 0 .../files/genesis-network-state | 0 .../xcat-genesis-init/files/genesis-register | 0 .../xcat-genesis-init/files/genesis-status | 0 .../files/xcat-genesis-action.service | 0 .../files/xcat-genesis-metrics.service | 0 .../files/xcat-genesis-network-ready.target | 0 .../files/xcat-genesis-network-state.service | 0 .../files/xcat-genesis-register.service | 0 .../xcat-genesis-init_1.0.bb | 0 ...igure-disable-s390x-crcvx-by-default.patch | 0 .../recipes-core/zlib/zlib_%.bbappend | 0 .../images/xcat-genesis-extension-smoke.bb | 0 .../files/xcat-extension-smoke | 0 ...cat-genesis-extension-smoke-payload_1.0.bb | 0 .../kexec/kexec-tools_%.bbappend | 0 ...qemu-ppc64-drop-removed-crc32-option.patch | 0 .../0001-qemu-x86-use-i686-baseline.patch | 0 .../0001-s390-use-stable-generator-name.patch | 0 .../linux-yocto/xcat-genesis-aarch64.cfg | 0 .../linux-yocto/xcat-genesis-armv7hf.cfg | 0 .../linux-yocto/xcat-genesis-powerpc64.cfg | 0 .../linux/linux-yocto/xcat-genesis-ppc64.cfg | 0 .../linux-yocto/xcat-genesis-riscv64.cfg | 0 .../linux/linux-yocto/xcat-genesis-s390x.cfg | 0 .../linux/linux-yocto/xcat-genesis-x86-64.cfg | 0 .../linux-yocto/xcat-genesis-x86-common.cfg | 0 .../linux/linux-yocto/xcat-genesis-x86.cfg | 0 .../linux/linux-yocto_%.bbappend | 0 ...-configure-avoid-host-ncurses-config.patch | 0 .../iprutils/iprutils_2.4.19.bb | 0 .../libspdm/libspdm_%.bbappend | 0 ...001-python-tools-use-libtool-objects.patch | 0 .../mstflint/mstflint_4.36.0.bb | 0 .../s390-tools/s390-tools-znetconf_2.41.0.bb | 0 .../files/genesis-bmcsetup | 0 .../files/genesis-credential-wait | 0 .../xcat-genesis-bmcsetup_1.0.bb | 0 .../files/genesis-credential-callback | 0 .../files/genesis-discover | 0 .../files/genesis-discovery-callback | 0 .../files/genesis-getcert | 0 .../files/genesis-udp-send.c | 0 .../files/xcat-genesis-credential.socket | 0 .../files/xcat-genesis-credential@.service | 0 .../files/xcat-genesis-discovery.socket | 0 .../files/xcat-genesis-discovery@.service | 0 .../xcat-genesis-discovery_1.0.bb | 0 .../xcat-genesis-protocol_1.0.bb | 0 .../oe/report | 0 .../oe/requirements.txt | 0 .../oe/scripts/sign-extension | 0 .../rsyslog.conf | 0 .../xCAT-genesis-base.spec | 0 .../xcat-cmdline.sh | 0 .../xcatroot | 0 xCAT-genesis-builder/README.md | 266 ------- xCAT-genesis-builder/buildrpm | 242 ------ xCAT-genesis-builder/check | 3 - xCAT-genesis-builder/cmdlist_check | 0 .../debuild-xcat-genesis-base | 64 -- xCAT-genesis-builder/findmcprpms | 26 - xCAT-genesis-builder/install | 628 ---------------- xCAT-genesis-builder/install.ubuntu | 659 ---------------- .../xCAT-genesis-builder.spec | 50 -- xCAT-server/share/xcat/tools/go-xcat | 2 +- xCAT-test/bats/genesis_ib_modules.bats | 4 +- xCAT-test/unit/genesis_openembedded.t | 136 ++-- xCAT-test/unit/genesis_openembedded_actions.t | 10 +- .../unit/genesis_openembedded_bmcsetup.t | 8 +- xCAT-test/unit/genesis_openembedded_console.t | 2 +- .../unit/genesis_openembedded_discovery.t | 6 +- xCAT-test/unit/genesis_openembedded_export.t | 2 +- .../unit/genesis_openembedded_extensions.t | 8 +- .../unit/genesis_openembedded_hardware.t | 8 +- xCAT-test/unit/genesis_openembedded_metrics.t | 8 +- xCAT-test/unit/genesis_openembedded_qeth.t | 4 +- xCAT-test/unit/genesis_openembedded_runtime.t | 12 +- xCAT-test/unit/test_file_support.t | 2 +- xCAT-test/unit/xcat_probe_package_payload.t | 6 - 182 files changed, 132 insertions(+), 2851 deletions(-) delete mode 100755 build-ubunturepo delete mode 100644 docs/source/references/coral/known_issues/genesis_base.rst delete mode 100644 docs/source/references/coral/known_issues/index.rst delete mode 100755 makerpm-genesisbuilder rename {xCAT-genesis-builder => xCAT-genesis-base}/80-net-name-slot.rules (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/LICENSE.html (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/builddeb-genesis-base (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/debian/changelog (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/debian/control (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/debian/copyright (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/debian/dirs (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/debian/docs (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/debian/install (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/debian/postinst (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/debian/postrm (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/debian/preinst (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/debian/prerm (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/debian/rules (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/debian/source/format (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/dhclient-script (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/dhclient.conf (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/dracut_105/README (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/dracut_105/el/dhclient-script (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/dracut_105/el/dhclient.conf (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/dracut_105/el/module-setup.sh (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/dracut_105/el/rsyslog.conf (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/dracut_105/el/xcat-cmdline.sh (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/dracut_105/el/xcatroot (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/dracut_105/ubuntu/dhclient-script (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/dracut_105/ubuntu/dhclient.conf (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/dracut_105/ubuntu/module-setup.sh (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/dracut_105/ubuntu/rsyslog.conf (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/dracut_105/ubuntu/xcat-cmdline.sh (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/dracut_105/ubuntu/xcatroot (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/.gitignore (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/build (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/export (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/export-extension (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/kas/aarch64.yml (59%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/kas/armv7hf.yml (59%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/kas/common.yml (91%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/kas/ppc64.yml (58%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/kas/ppc64le.yml (59%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/kas/riscv64.yml (59%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/kas/s390x.yml (58%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/kas/x86.yml (57%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/kas/x86_64.yml (58%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/keys/yocto-release.asc (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/classes-recipe/xcat-genesis-extension.bbclass (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/conf/distro/xcat-genesis.conf (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/conf/layer.conf (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/conf/machine/include/s390x/tune-s390x.inc (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/conf/machine/xcat-genesis-aarch64.conf (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/conf/machine/xcat-genesis-armv7hf.conf (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/conf/machine/xcat-genesis-ppc64.conf (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/conf/machine/xcat-genesis-ppc64le.conf (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/conf/machine/xcat-genesis-riscv64.conf (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/conf/machine/xcat-genesis-s390x.conf (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/conf/machine/xcat-genesis-x86-64.conf (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/conf/machine/xcat-genesis-x86.conf (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-connectivity/networkmanager/networkmanager_%.bbappend (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-connectivity/openssl/files/50-xcat-s390x.conf (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-connectivity/openssl/openssl_%.bbappend (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-network/files/10-xcat-genesis.conf (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-network/xcat-genesis-network_1.0.bb (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/files/genesis-qeth (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/files/xcat-genesis-qeth.service (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/xcat-genesis-qeth_1.0.bb (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/images/xcat-genesis-image.bb (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/packagegroups/packagegroup-xcat-genesis-hardware.bb (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/.clang-format (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/meson.build (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/console.h (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/main.c (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/newt_ui.c (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/plain_ui.c (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/shell.c (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/state.c (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/support.c (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console@.service (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/xcat-genesis-console_1.0.bb (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/xcat-genesis-extensions/files/genesis-sysext (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/xcat-genesis-extensions/files/xcat-genesis-extensions.service (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/xcat-genesis-extensions/xcat-genesis-extensions_1.0.bb (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files/genesis-hardware (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files/iprutils.json (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files/mstflint.json (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files/nvme.json (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files/provider-iprutils (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files/provider-mstflint (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files/provider-nvme (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/xcat-genesis-hardware-control_1.0.bb (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/00-xcat-genesis.preset (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-action (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-functions (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-maintenance-shell (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-metrics (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-network-refresh (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-network-state (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-register (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-status (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/xcat-genesis-action.service (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/xcat-genesis-metrics.service (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/xcat-genesis-network-ready.target (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/xcat-genesis-network-state.service (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/xcat-genesis-register.service (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/xcat-genesis-init_1.0.bb (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/zlib/files/0001-configure-disable-s390x-crcvx-by-default.patch (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-core/zlib/zlib_%.bbappend (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-extensions/images/xcat-genesis-extension-smoke.bb (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-extensions/xcat-genesis-extension-smoke/files/xcat-extension-smoke (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-extensions/xcat-genesis-extension-smoke/xcat-genesis-extension-smoke-payload_1.0.bb (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-kernel/kexec/kexec-tools_%.bbappend (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/0001-qemu-ppc64-drop-removed-crc32-option.patch (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/0001-qemu-x86-use-i686-baseline.patch (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/0001-s390-use-stable-generator-name.patch (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-aarch64.cfg (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-armv7hf.cfg (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-powerpc64.cfg (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-ppc64.cfg (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-riscv64.cfg (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-s390x.cfg (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-x86-64.cfg (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-x86-common.cfg (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-x86.cfg (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto_%.bbappend (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-support/iprutils/files/0001-configure-avoid-host-ncurses-config.patch (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-support/iprutils/iprutils_2.4.19.bb (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-support/libspdm/libspdm_%.bbappend (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-support/mstflint/files/0001-python-tools-use-libtool-objects.patch (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-support/mstflint/mstflint_4.36.0.bb (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-support/s390-tools/s390-tools-znetconf_2.41.0.bb (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-bmcsetup/files/genesis-bmcsetup (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-bmcsetup/files/genesis-credential-wait (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-bmcsetup/xcat-genesis-bmcsetup_1.0.bb (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-credential-callback (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-discover (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-discovery-callback (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-getcert (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-udp-send.c (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/xcat-genesis-credential.socket (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/xcat-genesis-credential@.service (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/xcat-genesis-discovery.socket (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/xcat-genesis-discovery@.service (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/xcat-genesis-discovery_1.0.bb (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-protocol/xcat-genesis-protocol_1.0.bb (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/report (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/requirements.txt (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/oe/scripts/sign-extension (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/rsyslog.conf (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/xCAT-genesis-base.spec (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/xcat-cmdline.sh (100%) rename {xCAT-genesis-builder => xCAT-genesis-base}/xcatroot (100%) delete mode 100644 xCAT-genesis-builder/README.md delete mode 100755 xCAT-genesis-builder/buildrpm delete mode 100755 xCAT-genesis-builder/check delete mode 100644 xCAT-genesis-builder/cmdlist_check delete mode 100755 xCAT-genesis-builder/debuild-xcat-genesis-base delete mode 100755 xCAT-genesis-builder/findmcprpms delete mode 100755 xCAT-genesis-builder/install delete mode 100755 xCAT-genesis-builder/install.ubuntu delete mode 100644 xCAT-genesis-builder/xCAT-genesis-builder.spec diff --git a/.gitattributes b/.gitattributes index 082f88533..311612c57 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1 @@ -xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/0001-s390-use-stable-generator-name.patch whitespace=-space-before-tab +xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/0001-s390-use-stable-generator-name.patch whitespace=-space-before-tab diff --git a/.github/workflows/xcat_test.yml b/.github/workflows/xcat_test.yml index 04d50aff0..4542349ec 100644 --- a/.github/workflows/xcat_test.yml +++ b/.github/workflows/xcat_test.yml @@ -21,7 +21,7 @@ jobs: with: python-version: '3.12' cache: pip - cache-dependency-path: xCAT-genesis-builder/oe/requirements.txt + cache-dependency-path: xCAT-genesis-base/oe/requirements.txt - name: Install OpenEmbedded host dependencies run: >- sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y @@ -29,7 +29,7 @@ jobs: build-essential chrpath cpio diffstat file gawk git locales lz4 openssl socat texinfo unzip wget xz-utils zstd - name: Install KAS - run: python -m pip install -r xCAT-genesis-builder/oe/requirements.txt + run: python -m pip install -r xCAT-genesis-base/oe/requirements.txt - name: Enable BitBake user namespaces run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 - name: Validate every architecture configuration @@ -40,7 +40,7 @@ jobs: set -euo pipefail 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" + configuration="xCAT-genesis-base/oe/kas/${architecture}.yml" kas dump "${configuration}" >/dev/null kas shell "${configuration}" -c 'bitbake -p' done @@ -51,8 +51,8 @@ jobs: KAS_BUILD_DIR: ${{ runner.temp }}/kas-build-x86_64 run: | set -euo pipefail - kas shell xCAT-genesis-builder/oe/kas/x86_64.yml -c \ + kas shell xCAT-genesis-base/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 \ + kas shell xCAT-genesis-base/oe/kas/s390x.yml -c \ 'bitbake -n xcat-genesis-image xcat-genesis-extension-smoke' diff --git a/build-ubunturepo b/build-ubunturepo deleted file mode 100755 index 8a36a17e4..000000000 --- a/build-ubunturepo +++ /dev/null @@ -1,711 +0,0 @@ -#!/bin/bash -# Update GSA Ubuntu Repositories or create a local repository -# -# Author: Leonardo Tonetto (tonetto@linux.vnet.ibm.com) -# Revisor: Arif Ali (aali@ocf.co.uk) -# -# -# Getting Started: -# - Clone the xcat-core git repository under a directory named "xcat-core/src" -# - make sure reprepro is installed on the build machine -# - Run this script from the local git repository you just created. -# ./build-ubunturepo -c BUILDALL=1 - -# Usage: attr=value attr=value ... ./build-ubunturepo { -c | -d } -# PROMOTE=1 - if the attribute "PROMOTE" is specified, means an official dot release. This does not -# actually build xcat, just uploads the most recent snap build to http://xcat.org/files/xcat/ . -# If not specified, a snap build is assumed, which uploads to https://xcat.org/files/xcat/ -# PREGA=1 - use this option with PROMOTE=1 on a branch that already has a released dot release, but this -# build is a GA candidate build, not to be released yet. This will result in the tarball -# being uploaded to http://xcat.org/files/xcat/repos/apt -# (but the tarball file name will be like a released tarball, not a snap build). -# When you are ready to release this build, use PROMOTE=1 without PREGA -# BUILDALL=1 - build all rpms, whether they changed or not. Should be used for snap builds that are in -# prep for a release. -# GPGSIGN=0 - Do not sign the repo in the end of the build. The repo will be signed by default -# -# LOCAL_KEY=1 Use local keys to sign repo instead of WGET from GSA. By default use GSA. -# -# GPG_HOME= - Use the specified directory as GNUPGHOME for signing (no passphrase assumed). -# Bypasses GSA download and LOCAL_KEY. -# -# SETUP=1 Setup environment for build. By default do not setup environment. -# -# LOG= - provide an LOG file option to redirect some output into log file -# -# DEST= - provide a directory to contains the build result -# -# Running builds in parallel on one host: the build lock is scoped to the source checkout (see -# the "build-lock" block below), so two builds from DIFFERENT -# checkouts -- e.g. the devel and stable Ubuntu CD lanes -- run -# concurrently, while two builds of the SAME checkout still fail-fast -# (they build in-place and would corrupt each other). For parallel -# builds give each a separate checkout and a separate output tree -# (a distinct DEST). GPG_HOME may be SHARED between parallel builds -# -- it is used read-only for signing. -# -# For the dependency packages 1. All the xcat dependency deb packages should be uploaded to -# "pokgsa/projects/x/xcat/build/ubuntu/xcat-dep/debs/" on GSA -# 2. run ./build-ubunturepo -d -# -# 3. the built xcat-dep deb packages tarball can be found in "../../xcat-dep" -# related to the path of this script -############################ -printusage() -{ - printf "Usage: %s {-c | -d} \n" $(basename $0) >&2 - echo " -c : Build the xcat-core packages and create the repo" - echo " -d : Create the xcat-dep repo." -} -# For the purpose of getting the distribution name -if [[ ! -f /etc/lsb-release ]]; then - echo "ERROR: Could not find /etc/lsb-release, is this script executed on a Ubuntu machine?" - exit 1 -fi -. /etc/lsb-release - -export HOME=/root - - -# Process cmd line variable assignments, assigning each attr=val pair to a variable of same name -for i in $*; do - echo $i | grep '=' -q - if [ $? != 0 ];then - continue - fi - # upper case the variable name - varstring=`echo "$i"|cut -d '=' -f 1|tr '[a-z]' '[A-Z]'`=`echo "$i"|cut -d '=' -f 2` - export $varstring -done - -#Setup environment so the xcat-deps can be built on a FVT test machine -if [ "$SETUP" = "1" ];then - #Mount GSA - POKGSA="/gsa/pokgsa" - POKGSA2="/gsa/pokgsa-p2" - POKGSAIBM="pokgsa.ibm.com" - if [ ! -d $POKGSA ];then - mkdir -p $POKGSA - mount ${POKGSAIBM}:${POKGSA} ${POKGSA} - fi - if [ ! -d $POKGSA2 ];then - mkdir -p $POKGSA2 - mount ${POKGSAIBM}:${POKGSA2} ${POKGSA2} - fi - - # Verify needed packages installed - REPREPO="reprepro" - DEVSCRIPTS="devscripts" - DEBHELPER="debhelper" - QUILT="quilt" - - apt-get -y install $REPREPO $DEVSCRIPTS $DEBHELPER $QUILT - - echo "Finished setup for xcat-dep build. Rerun this script with SETUP=0 LOCAL_KEY=1 flags" - exit 1 -fi - -# Check the necessary packages before starting the build -declare -a packages=( "reprepro" "devscripts" "debhelper" "libsoap-lite-perl" "libdbi-perl" "quilt" "git") - -for package in ${packages[@]}; do - RC=`dpkg -l | grep $package >> /dev/null 2>&1; echo $?` - if [[ ${RC} != 0 ]]; then - echo "ERROR: Could not find $package, install using 'apt-get install $package' to continue" - exit 1 - fi -done - -# Supported distributions. Set DISTS="jammy noble resolute" to limit local validation builds. -dists="${DISTS:-saucy trusty utopic xenial bionic focal jammy noble resolute}" - -# GPG key used to sign the apt repo (reprepro SignWith). Defaults to the historic -# name. Override with GPG_KEY_ID= (space-free, since it is passed via -# the attr=value parser above), e.g. GPG_KEY_ID=xcat-build@xcat.org -GPG_KEY_ID="${GPG_KEY_ID:-xCAT Automatic Signing Key}" - -c_flag= # xcat-core (trunk-delvel) path -d_flag= # xcat-dep (trunk) path -r_flag= #genesis base rpm package path - -while getopts 'cdr:' OPTION -do - case $OPTION in - c) c_flag=1 - ;; - d) d_flag=1 - ;; - r) r_flag=1 - genesis_rpm_path="$OPTARG" - ;; - ?) printusage - exit 2 - ;; - esac -done -shift $(($OPTIND - 1)) - -if [ -z "$c_flag" -a -z "$d_flag" ];then - printusage - exit 2 -fi - -if [ "$c_flag" -a "$d_flag" ];then - printusage - exit 2 -fi - -if [ -z "$BUILDALL" ]; then - BUILDALL=1 -fi - -# Find where this script is located to set some build variables -old_pwd=`pwd` -cd `dirname $0` -curdir=`pwd` - -# Scope the build lock to THIS checkout. build-ubunturepo builds the packages in-place -# in its own source tree (it rewrites debian/changelog and debian/control, drops -# *.orig.tar.gz at the checkout root and runs dpkg-buildpackage inside the package -# dirs), so the resource two builds actually contend for is the checkout -- not the -# host. The historic single /var/lock/xcatbld.lock was host-global and fail-fast, so -# two builds from *different* checkouts (e.g. the devel and stable Ubuntu CD lanes on -# one build host) collided and the loser failed the pipeline even though they share -# nothing. Key the lock on the checkout path instead: builds of the SAME checkout -# still fail-fast (they would corrupt each other in-place), while builds of DISTINCT -# checkouts get distinct locks and run in parallel. The lock file stays on the local -# /var/lock (reliable flock; the checkout may live on NFS/virtiofs where flock is not) -# and the source tree is left byte-pristine. -# -# NOTE: the two marked regions below are extracted verbatim and exercised by the unit -# test xCAT-test/unit/build_ubunturepo_lock.t (which runs them with a chosen $curdir) -# -- keep the markers, and keep each region self-contained. -# BEGIN build-lock-id -lock_id_for() { printf '%s' "$1" | md5sum | cut -c1-12; } -LOCKFILE="/var/lock/xcatbld-$(lock_id_for "$curdir").lock" -# END build-lock-id -# BEGIN build-lock-acquire -exec 8>"$LOCKFILE" -if ! flock -n 8; then - echo "ERROR: Can't get lock $LOCKFILE for checkout $curdir. Another build is already using this checkout. Exiting...." - exit 1 -fi -# END build-lock-acquire - -# for the git case, query the current branch and set REL (changing master to devel if necessary) -function setbranch { - # Get the current branch name. safe.directory='*' so this still works when the - # build runs as root against a repo owned by another user (otherwise git errors - # with "dubious ownership", returns empty, and REL collapses to an unstable value). - branch=`git -c safe.directory='*' rev-parse --abbrev-ref HEAD 2>/dev/null` - if [ "$branch" = "master" ]; then - REL="devel" - elif [ "$branch" = "HEAD" ] || [ -z "$branch" ]; then - # Special handling when in a 'detached HEAD' state - branch=`git -c safe.directory='*' describe --abbrev=0 HEAD 2>/dev/null` - [[ -n "$branch" ]] && REL=`echo $branch|cut -d. -f 1,2` - else - REL=$branch - fi -} - -WGET_CMD="wget" -if [ ! -z ${LOG} ]; then - WGET_CMD="wget -o ${LOG}" -fi - -if [ "$GPGSIGN" = "0" ];then - echo "GPGSIGN=$GPGSIGN specified, skip gnupg key downloading" -elif [ -n "$GPG_HOME" ];then - echo "GPG_HOME=$GPG_HOME specified, using provided GNUPGHOME" - export GNUPGHOME="$GPG_HOME" -else - #sync the gpg key to the build machine local - gsa_url=http://pokgsa.ibm.com/projects/x/xcat/build/linux - mkdir -p $HOME/.gnupg - for key_name in pubring.gpg secring.gpg trustdb.gpg; do - if [ "$LOCAL_KEY" = "1" ];then - # Keys are already in the local $HOME/.gnupg directory - chmod 600 $HOME/.gnupg/$key_name - else - # Need to download keys from GSA - if [ ! -f $HOME/.gnupg/$key_name ] || [ `wc -c $HOME/.gnupg/$key_name|cut -f 1 -d' '` == 0 ]; then - rm -f $HOME/.gnupg/$key_name - ${WGET_CMD} -P $HOME/.gnupg $gsa_url/keys/$key_name - chmod 600 $HOME/.gnupg/$key_name - fi - fi - done -fi - -REL=xcat-core -if [ "$c_flag" ] -then - setbranch - # Sanitize REL into a stable, filesystem-safe token: replace any character that - # isn't [A-Za-z0-9._-] (e.g. the '/' in a branch like feat/ubuntu-e2e, which would - # otherwise create nested dirs) with '-', and never let it be empty. - REL=${REL//[^A-Za-z0-9._-]/-} - [ -z "$REL" ] && REL="local" - package_dir_name=debs$REL - - #define the dep source code path, core build target path and dep build target path - if [ -z "$DEST" ]; then - local_core_repo_path="$curdir/../../xcat-core" - PKGDIR="../../$package_dir_name" - else - local_core_repo_path="$DEST/$package_dir_name/xcat-core" - PKGDIR="$DEST/$package_dir_name/$package_dir_name" - fi - if [ ! -d "$PKGDIR" ];then - mkdir -p "$PKGDIR" - fi - - echo "#############################################################" - echo "Building xcat-core on branch ($REL) to $local_core_repo_path" - echo "#############################################################" - if [ "$PROMOTE" != 1 ]; then - code_change=0 - update_log='' - - if [ -z "$GITUP" ];then - update_log=../coregitup - echo "git pull > $update_log" - git pull > $update_log - else - update_log=$GITUP - fi - - if ! grep -q 'Already up-to-date' $update_log; then - code_change=1 - fi - ver=`cat Version` - short_ver=`cat Version|cut -d. -f 1,2` - short_short_ver=`cat Version|cut -d. -f 1` - commit_id_long=`git rev-parse HEAD` - commit_id="${commit_id_long:0:7}" - if [ -f Gitepoch ]; then - source_date_epoch=$(cat Gitepoch) - else - source_date_epoch=$(git log -1 --format=%ct HEAD 2>/dev/null || date +%s) - fi - export SOURCE_DATE_EPOCH="$source_date_epoch" - export DEBEMAIL="xcat-build@xcat.org" - export DEBFULLNAME="xCAT Build" - build_time=$(date -d "@$source_date_epoch" --utc '+%Y-%m-%dT%H:%M:%SZ' 2>/dev/null || date -u) - build_machine=`hostname` - - if [ $code_change == 0 -a "$UP" != 1 -a "$BUILDALL" != 1 ]; then - echo "Nothing new detected. Exiting...." - exit 0 - fi - - echo "###############################" - echo "# Building xcat-core packages #" - echo "###############################" - - #the package type: local | snap | alpha - #the build introduce string - build_string="Snap_Build" - if [ -f Release ]; then - xcat_release=$(cat Release) - else - xcat_release="snap$(date -d "@$source_date_epoch" --utc '+%Y%m%d%H%M')" - fi - pkg_version="${ver}-${xcat_release}" - - packages="xCAT-client xCAT-genesis-scripts perl-xCAT xCAT-server xCAT xCATsn xCAT-test xCAT-buildkit xCAT-vlan xCAT-confluent xCAT-probe" - if [ -n "$PACKAGE" ]; then - match="" - for p in $packages; do - p_low=$(echo "$p" | tr '[A-Z]' '[a-z]') - pkg_low=$(echo "$PACKAGE" | tr '[A-Z]' '[a-z]') - if [ "$p_low" = "$pkg_low" ]; then - match="$p" - break - fi - done - if [ -z "$match" ]; then - echo "ERROR: Package '$PACKAGE' not found. Valid packages: $packages" - exit 1 - fi - packages="$match" - fi - target_archs=(amd64 ppc64el) - for file in $packages - do - file_low=`echo $file | tr '[A-Z]' '[a-z]'` - if [ "$file" = "xCAT" -o "$file" = "xCAT-genesis-scripts" -o "$file" = "xCATsn" ]; then - target_archs="amd64 ppc64el" - else - target_archs="all" - fi - for target_arch in $target_archs - do - tar_orig="${file_low}_${ver}.orig.tar.gz" - if grep -q "3.0 (quilt)" "${file}/debian/source/format" && [ ! -f "$tar_orig" ]; then - tar czf "$tar_orig" --exclude debian -C "$file" . - fi - - if grep -q $file $update_log || [ "$BUILDALL" == 1 -o "$file" = "perl-xCAT" ]; then - rm -f $PKGDIR/${file_low}_*.$target_arch.deb - cd $file - CURDIR=$(pwd) - - find . -name '*.dch' -delete - deterministic_date=$(date -R -d "@$SOURCE_DATE_EPOCH" --utc 2>/dev/null || date -R --utc) - sed -i "1s/(.*)/(${pkg_version})/" debian/changelog - sed -i "s/^ -- .*/ -- $DEBFULLNAME <$DEBEMAIL> $deterministic_date/" debian/changelog - if [ "$target_arch" = "all" ]; then - #xcat probe use some functions shipped by xCAT, for below reasons we need to copy files to xCAT-probe directory - #1 make xcat probe code to be self-contained - #2 don't maintain two files for each script - #3 symbolic link can't work during package - if [ $file_low = "xcat-probe" ]; then - mkdir -p ${CURDIR}/lib/perl/xCAT/ - cp -f ${CURDIR}/../perl-xCAT/xCAT/CommandUtils.pm ${CURDIR}/lib/perl/xCAT/ - cp -f ${CURDIR}/../perl-xCAT/xCAT/NetworkUtils.pm ${CURDIR}/lib/perl/xCAT/ - cp -f ${CURDIR}/../perl-xCAT/xCAT/GlobalDef.pm ${CURDIR}/lib/perl/xCAT/ - cp -f ${CURDIR}/../perl-xCAT/xCAT/ServiceNodeUtils.pm ${CURDIR}/lib/perl/xCAT/ - fi - CURDIR=$(pwd) - cp ${CURDIR}/debian/control ${CURDIR}/debian/control.save.998 - # Magic string used here - sed -i -e "s#>= 2.13-snap000000000000#= ${pkg_version}#g" ${CURDIR}/debian/control - dpkg-buildpackage -rfakeroot -uc -us - mv ${CURDIR}/debian/control.save.998 ${CURDIR}/debian/control - else - if [ "$file" = "xCAT-genesis-scripts" ]; then - echo "Rename control file to build pkg: mv ${CURDIR}/debian/control-${target_arch} ${CURDIR}/debian/control" - cp ${CURDIR}/debian/control-${target_arch} ${CURDIR}/debian/control - elif [ "$file" = "xCAT" ]; then - # shipping bmcsetup and getipmi scripts as part of postscripts - files=("bmcsetup" "getipmi") - for f in "${files[@]}"; do - cp ${CURDIR}/../xCAT-genesis-scripts/usr/bin/$f ${CURDIR}/postscripts/$f - sed -i "s/xcat.genesis.$f/$f/g" ${CURDIR}/postscripts/$f - done - fi - CURDIR=$(pwd) - cp ${CURDIR}/debian/control ${CURDIR}/debian/control.save.998 - # Magic string used here - sed -i -e "s#>= 2.13-snap000000000000#= ${pkg_version}#g" ${CURDIR}/debian/control - dpkg-buildpackage -rfakeroot -uc -us -a$target_arch - mv ${CURDIR}/debian/control.save.998 ${CURDIR}/debian/control - if [ "$file" = "xCAT-genesis-scripts" ]; then - echo "Move control file back: mv ${CURDIR}/debian/control ${CURDIR}/debian/control-${target_arch}" - rm ${CURDIR}/debian/control - elif [ "$file" = "xCAT" ]; then - files=("bmcsetup" "getipmi") - for f in "${files[@]}"; do - rm -f ${CURDIR}/postscripts/$f - done - fi - fi - rc=$? - if [ $rc -gt 0 ]; then - echo "Error: $file build package failed exit code $rc" - exit $rc - fi - cd - - find $file -maxdepth 3 -type d -name "${file_low}*" | grep debian | xargs rm -rf - find $file -maxdepth 3 -type f -name "files" | grep debian | xargs rm -rf - mv ${file_low}* $PKGDIR/ - fi - done - done - - find $PKGDIR/* ! -name '*.deb' | xargs rm -f - fi - - if [ "$PROMOTE" = 1 ]; then - upload_dir="xcat-core" - tar_name="xcat-core-$ver.tar.bz2" - else - upload_dir="core-snap" - tar_name="core-debs-snap.tar.bz2" - fi - - echo "#################################" - echo "# Creating xcat-core repository #" - echo "#################################" - - #clean the repo directory - if [ -e $local_core_repo_path ]; then - rm -rf $local_core_repo_path - fi - mkdir -p $local_core_repo_path - cd $local_core_repo_path - mkdir conf - - for dist in $dists; do - # for all releases moving forward, support amd64 and ppc64el - tmp_out_arch="amd64 ppc64el" - if [ "$dist" = "saucy" ]; then - # for older releases of Ubuntu that does not support ppc64el - tmp_out_arch="amd64" - fi - cat << __EOF__ >> conf/distributions -Origin: xCAT internal repository -Label: xcat-core bazaar repository -Codename: $dist -Architectures: $tmp_out_arch -Components: main -Description: Repository automatically genereted conf -__EOF__ - - if [ "$GPGSIGN" = "0" ];then - #echo "GPGSIGN=$GPGSIGN specified, the repo will not be signed" - echo "" >> conf/distributions - else - keyid=$(gpg --list-keys --keyid-format long "$GPG_KEY_ID" | grep '^pub' | sed -e 's/.*\///' -e 's/ .*//') - echo "SignWith: $keyid" >> conf/distributions - echo "" >> conf/distributions - fi - done - - if [ -n "$GPG_HOME" ]; then - cat << __EOF__ > conf/options -verbose -basedir . -__EOF__ - else - cat << __EOF__ > conf/options -verbose -ask-passphrase -basedir . -__EOF__ - fi - - #import the deb packages into the repo - amd_files=`ls ../$package_dir_name/*.deb | grep -v "ppc64el"` - all_files=`ls ../$package_dir_name/*.deb` - for dist in $dists; do - deb_files=$all_files - if [ "$dist" = "saucy" ]; then - # for older releases of Ubuntu that does not support ppc64el - deb_files=$amd_files - fi - for file in $deb_files; do - reprepro -b ./ includedeb $dist $file; - done - done - #create the mklocalrepo script - cat << '__EOF__' > mklocalrepo.sh -. /etc/lsb-release -cd `dirname $0` -host_arch=`uname -m` -if [ "$host_arch" != "ppc64le" ];then - host_arch="amd64" -else - host_arch="ppc64el" -fi -echo deb [arch=$host_arch] file://"`pwd`" $DISTRIB_CODENAME main > /etc/apt/sources.list.d/xcat-core.list -__EOF__ - - chmod 775 mklocalrepo.sh - - # - # Add a buildinfo file into the tar.bz2 file to track information about the build - # - BUILDINFO=$local_core_repo_path/buildinfo - echo "VERSION=$ver" > $BUILDINFO - echo "RELEASE=$xcat_release" >> $BUILDINFO - echo "BUILD_TIME=$build_time" >> $BUILDINFO - echo "BUILD_MACHINE=$build_machine" >> $BUILDINFO - echo "COMMIT_ID=$commit_id" >> $BUILDINFO - echo "COMMIT_ID_LONG=$commit_id_long" >> $BUILDINFO - - #create the xcat-core.list file - - cd ../ - if ! grep xcat /etc/group ; then - groupadd xcat - fi - - chgrp -R root xcat-core - chmod -R g+w xcat-core - - #build the tar ball - echo "Creating `pwd`/$tar_name ..." - tar -hjcf $tar_name xcat-core - chgrp root $tar_name - chmod g+w $tar_name - - if [ -n "$DEST" ]; then - ln -sf $(basename `pwd`)/$tar_name ../$tar_name - if [ $? != 0 ]; then - echo "ERROR: Failed to make symbol link $DEST/$tar_name" - fi - fi - - if [ ! -e core-snap ]; then - ln -s xcat-core core-snap - fi - - cd $old_pwd - exit 0 -fi - -if [ "$d_flag" ] -then - echo "################################" - echo "# Creating xcat-dep repository #" - echo "################################" - - #the path of ubuntu xcat-dep deb packages on GSA - GSA="/gsa/pokgsa/projects/x/xcat/build/ubuntu/xcat-dep" - if [ ! -d $GSA ]; then - echo "build-ubunturepo: It appears that you do not have GSA to access the xcat-dep pkgs." - exit 1; - fi - - #define the dep source code path, core build target path and dep build target path - if [ -z "$DEST" ]; then - local_dep_repo_path="$curdir/../../xcat-dep/xcat-dep" - else - local_dep_repo_path="$DEST/xcat-dep/xcat-dep" - fi - - # Sync from the GSA master copy of the dep rpms - echo "Creating directory $local_dep_repo_path" - mkdir -p $local_dep_repo_path/ - - echo "Syncing RPMs from $GSA/ to $local_dep_repo_path/../ ..." - rsync -ilrtpu --delete $GSA/ $local_dep_repo_path/../ - if [ $? -ne 0 ]; then - echo "Error from rsync, cannot continue!" - exit 1 - fi - - #clean all old files - if [ -e $local_dep_repo_path ];then - rm -rf $local_dep_repo_path - fi - mkdir -p $local_dep_repo_path - cd $local_dep_repo_path - mkdir conf - - - #create the conf/distributions file - for dist in $dists; do - tmp_out_arch="amd64 ppc64el" - if [ "$dist" = "saucy" ]; then - # for older releases of Ubuntu that does not support ppc64el - tmp_out_arch="amd64" - fi - cat << __EOF__ >> conf/distributions -Origin: xCAT internal repository -Label: xcat-dep bazaar repository -Codename: $dist -Architectures: $tmp_out_arch -Components: main -Description: Repository automatically genereted conf -__EOF__ - - if [ "$GPGSIGN" = "0" ];then - echo "GPGSIGN=$GPGSIGN specified, the repo will not be signed" - echo "" >> conf/distributions - else - keyid=$(gpg --list-keys --keyid-format long "$GPG_KEY_ID" | grep '^pub' | sed -e 's/.*\///' -e 's/ .*//') - echo "SignWith: $keyid" >> conf/distributions - echo "" >> conf/distributions - fi - - done - - - - if [ -n "$GPG_HOME" ]; then - cat << __EOF__ > conf/options -verbose -basedir . -__EOF__ - else - cat << __EOF__ > conf/options -verbose -ask-passphrase -basedir . -__EOF__ - fi - - #import the deb packages into the repo - amd_files=`ls ../debs/*.deb | grep -v "ppc64el"` - all_files=`ls ../debs/*.deb` - for dist in $dists; do - deb_files=$all_files - if [ "$dist" = "saucy" ]; then - # for older releases of Ubuntu that does not support ppc64el - deb_files=$amd_files - fi - for file in $deb_files; do - reprepro -b ./ includedeb $dist $file; - done - done - - cat << '__EOF__' > mklocalrepo.sh -. /etc/lsb-release -cd `dirname $0` -host_arch=`uname -m` -if [ "$host_arch" != "ppc64le" ];then - host_arch="amd64" -else - host_arch="ppc64el" -fi -echo deb [arch=$host_arch] file://"`pwd`" $DISTRIB_CODENAME main > /etc/apt/sources.list.d/xcat-dep.list -__EOF__ - - chmod 775 mklocalrepo.sh - - cd .. - if ! grep xcat /etc/group ; then - groupadd xcat - fi - - chgrp -R root xcat-dep - chmod -R g+w xcat-dep - - #create the tar ball - dep_tar_name=xcat-dep-ubuntu-`date +%Y%m%d%H%M`.tar.bz2 - tar -hjcf $dep_tar_name xcat-dep - chgrp root $dep_tar_name - chmod g+w $dep_tar_name - - - USER="xcat" - SERVER="xcat.org" - FILES_PATH="files" - FRS="/var/www/${SERVER}/${FILES_PATH}" - APT_DIR="${FRS}/xcat" - APT_REPO_DIR="${APT_DIR}/repos/apt/devel" - - # Decide whether to upload the xcat-dep package or NOT (default is to NOT upload xcat-dep - if [ "$UP" != "1" ]; then - echo "Upload not specified, Done! (rerun with UP=1, to upload)" - cd $old_pwd - exit 0 - fi - - #upload the dep packages - i=0 - echo "Uploading debs from xcat-dep to ${APT_REPO_DIR}/xcat-dep/ ..." - while [ $((i+=1)) -le 5 ] && ! rsync -urLv --delete xcat-dep $USER@${SERVER}:${APT_REPO_DIR}/ - do : ; done - - #upload the tarball - i=0 - echo "Uploading $dep_tar_name to ${APT_DIR}/xcat-dep/2.x_Ubuntu/ ..." - while [ $((i+=1)) -le 5 ] && ! rsync -v --force $dep_tar_name $USER@${SERVER}:${APT_DIR}/xcat-dep/2.x_Ubuntu/ - do : ; done - - #upload the README file - cd debs - i=0 - echo "Uploading README to ${APT_DIR}/xcat-dep/2.x_Ubuntu/ ..." - while [ $((i+=1)) -le 5 ] && ! rsync -v --force README $USER@${SERVER}:${APT_DIR}/xcat-dep/2.x_Ubuntu/ - do : ; done - -fi - -cd $old_pwd -exit 0 diff --git a/buildrpms.pl b/buildrpms.pl index 1c8b86990..9ed6ba321 100755 --- a/buildrpms.pl +++ b/buildrpms.pl @@ -333,8 +333,8 @@ sub createmockconfig { sub buildsources_genesis_base($) { my ($target) = @_; - die "Assertion failed! No directory xCAT-genesis-builder in the current directory" - unless -d "./xCAT-genesis-builder"; + die "Assertion failed! No directory xCAT-genesis-base in the current directory" + unless -d "./xCAT-genesis-base"; my $staging_parent = "/tmp/xcat-genesis-base-build-support.$$"; my $staging_root = "$staging_parent/xCAT-genesis-base-build-support"; my $support_tarball = "$SOURCES/xCAT-genesis-base-build-support.tar.bz2"; @@ -342,9 +342,9 @@ sub buildsources_genesis_base($) { remove_tree($staging_parent) if -e $staging_parent; make_path("$staging_root/dracut_105"); - sh_or_die(qq(cp -a "xCAT-genesis-builder/dracut_105" "$staging_root/"), + sh_or_die(qq(cp -a "xCAT-genesis-base/dracut_105" "$staging_root/"), "Error copying dracut_105 sources"); - cp "xCAT-genesis-builder/80-net-name-slot.rules", + cp "xCAT-genesis-base/80-net-name-slot.rules", "$staging_root/80-net-name-slot.rules"; unlink $support_tarball if -f $support_tarball; @@ -444,11 +444,8 @@ sub buildspkgs { : "dist/$target/rpms/SRPMS/$pkg-$VERSION-$RELEASE.src.rpm"; return if -f $diskcache and not $opts{force}; - my $dir = sub { - return "xCAT-genesis-builder" - if $pkg eq "xCAT-genesis-base"; - $pkg; - }->(); + # The source directory is the package name now that xCAT-genesis-builder is gone. + my $dir = $pkg; my @opts; push @opts, "--quiet" unless $opts{verbose}; diff --git a/docs/source/developers/guides/code/builds.rst b/docs/source/developers/guides/code/builds.rst index 1146e4225..41a1b6dae 100644 --- a/docs/source/developers/guides/code/builds.rst +++ b/docs/source/developers/guides/code/builds.rst @@ -32,14 +32,10 @@ emitted, because a source-only run has no binary packages to advertise. .. note:: - ``buildcore.sh``, ``makerpm`` and ``buildlocal.sh`` were removed in 2.19; - ``buildrpms.pl`` replaces all three, and its ``--source-only`` replaces the - old ``SRCONLY=1``. - - ``build-ubunturepo`` is superseded by ``builddebs.pl`` but is **still in the - tree for now**, as a differential oracle: it is the reference the new builder - is checked against, and it is removed once the CD pipelines have been moved - over. Do not add features to it. + ``buildcore.sh``, ``makerpm``, ``buildlocal.sh`` and ``build-ubunturepo`` + were removed in 2.19. ``buildrpms.pl`` replaces the first three, and its + ``--source-only`` replaces the old ``SRCONLY=1``; ``builddebs.pl`` replaces + ``build-ubunturepo``. Debian and Ubuntu packages -------------------------- diff --git a/docs/source/references/coral/index.rst b/docs/source/references/coral/index.rst index 17b70b2ab..cc92603f7 100644 --- a/docs/source/references/coral/index.rst +++ b/docs/source/references/coral/index.rst @@ -8,7 +8,6 @@ CORAL stands for Collaboration of Oak Ridge, Argonne, and Livermore and is solut :maxdepth: 2 cluster_mgmt/index.rst - known_issues/index.rst ha/index.rst .. mn/index.rst .. discovery/index.rst diff --git a/docs/source/references/coral/known_issues/genesis_base.rst b/docs/source/references/coral/known_issues/genesis_base.rst deleted file mode 100644 index 26ea74bd0..000000000 --- a/docs/source/references/coral/known_issues/genesis_base.rst +++ /dev/null @@ -1,24 +0,0 @@ -xCAT Genesis Base -================= - -xCAT ships a ``xCAT-genesis-base`` package as part of xcat-deps. This is a light-weight diskless linux image based currently on Fedora28, that is used by xCAT to do hardware discovery. - -Follow the steps below to build your own version of the ``xCAT-genesis-base`` on-site. You can include additional drivers or modules or you can build your own version of the ``xCAT-genesis-base`` on-site using a server running OS other than Fedora28, like Red Hat Enterprise Linux 8. Building ``xCAT-genesis-base`` on a server running Red Hat Enterprise Linux 7 or earlier, is no longer supported. - -1. Download the latest timestamp version of the ``xCAT-genesis-builder`` RPM provided here: http://xcat.org/files/xcat/xcat-dep/2.x_Linux/beta/ - -2. Install the ``xCAT-genesis-builder`` RPM on a node installed with desired OS (currently verified with Fedora28 and Red Hat Enterprise Linux 8). For more details: https://github.com/xcat2/xcat-core/tree/master/xCAT-genesis-builder#readme - -3. If additional drivers or modules need to be loaded when genesis kernel boots, edit ``xCAT-genesis-builder/xcat-cmdline.sh`` and add ``modprobe`` statements for each one. - -4. Build the new ``xCAT-genesis-base`` RPM: :: - - /opt/xcat/share/xcat/netboot/genesis/builder/buildrpm - -To use the generated RPM from the step above for node discovery: - -1. Uninstall the old ``xCAT-genesis-base`` RPM and install the newly built ``xCAT-genesis-base`` RPM on the xCAT Management node -2. Execute: ``mknb ppc64`` - -3. Follow :doc:`discover nodes ` - diff --git a/docs/source/references/coral/known_issues/index.rst b/docs/source/references/coral/known_issues/index.rst deleted file mode 100644 index 359cd90de..000000000 --- a/docs/source/references/coral/known_issues/index.rst +++ /dev/null @@ -1,7 +0,0 @@ -Known Issues -============ - -.. toctree:: - :maxdepth: 2 - - genesis_base.rst diff --git a/makerpm-genesisbuilder b/makerpm-genesisbuilder deleted file mode 100755 index 443539643..000000000 --- a/makerpm-genesisbuilder +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/sh -VER=`cat Version` -VER=${VER/-/.post} -VER=${VER/-/.} -rpmbuild --version > /dev/null -if [ $? -gt 0 ] -then - echo "Error: Is rpmbuild installed and working?" - exit 1 -fi -RPMROOT=`rpmbuild --eval '%_topdir' xCATsn/xCATsn.spec` -if [ $? -gt 0 ] -then - echo "Could not determine rpmbuild's root" - exit 1 -fi -echo "The location for rpm building is ${RPMROOT}" -cd `dirname $0`/xCAT-genesis-builder -tar --exclude .svn -cjvf $RPMROOT/SOURCES/xCAT-genesis-builder.tar.bz2 . -#mv -f *.gz $RPMROOT/SOURCES -cp LICENSE.html $RPMROOT/BUILD -cp xCAT-genesis-builder.spec $RPMROOT/SOURCES -cd - -rm -f $RPMROOT/SRPMS/xCAT-genesis-builder-$VER*rpm $RPMROOT/RPMS/*/xCAT-genesis-builder-$VER*rpm -rpmbuild -ba xCAT-genesis-builder/xCAT-genesis-builder.spec --define "version $VER" --define "release 1" diff --git a/xCAT-genesis-builder/80-net-name-slot.rules b/xCAT-genesis-base/80-net-name-slot.rules similarity index 100% rename from xCAT-genesis-builder/80-net-name-slot.rules rename to xCAT-genesis-base/80-net-name-slot.rules diff --git a/xCAT-genesis-builder/LICENSE.html b/xCAT-genesis-base/LICENSE.html similarity index 100% rename from xCAT-genesis-builder/LICENSE.html rename to xCAT-genesis-base/LICENSE.html diff --git a/xCAT-genesis-builder/builddeb-genesis-base b/xCAT-genesis-base/builddeb-genesis-base similarity index 100% rename from xCAT-genesis-builder/builddeb-genesis-base rename to xCAT-genesis-base/builddeb-genesis-base diff --git a/xCAT-genesis-builder/debian/changelog b/xCAT-genesis-base/debian/changelog similarity index 100% rename from xCAT-genesis-builder/debian/changelog rename to xCAT-genesis-base/debian/changelog diff --git a/xCAT-genesis-builder/debian/control b/xCAT-genesis-base/debian/control similarity index 100% rename from xCAT-genesis-builder/debian/control rename to xCAT-genesis-base/debian/control diff --git a/xCAT-genesis-builder/debian/copyright b/xCAT-genesis-base/debian/copyright similarity index 100% rename from xCAT-genesis-builder/debian/copyright rename to xCAT-genesis-base/debian/copyright diff --git a/xCAT-genesis-builder/debian/dirs b/xCAT-genesis-base/debian/dirs similarity index 100% rename from xCAT-genesis-builder/debian/dirs rename to xCAT-genesis-base/debian/dirs diff --git a/xCAT-genesis-builder/debian/docs b/xCAT-genesis-base/debian/docs similarity index 100% rename from xCAT-genesis-builder/debian/docs rename to xCAT-genesis-base/debian/docs diff --git a/xCAT-genesis-builder/debian/install b/xCAT-genesis-base/debian/install similarity index 100% rename from xCAT-genesis-builder/debian/install rename to xCAT-genesis-base/debian/install diff --git a/xCAT-genesis-builder/debian/postinst b/xCAT-genesis-base/debian/postinst similarity index 100% rename from xCAT-genesis-builder/debian/postinst rename to xCAT-genesis-base/debian/postinst diff --git a/xCAT-genesis-builder/debian/postrm b/xCAT-genesis-base/debian/postrm similarity index 100% rename from xCAT-genesis-builder/debian/postrm rename to xCAT-genesis-base/debian/postrm diff --git a/xCAT-genesis-builder/debian/preinst b/xCAT-genesis-base/debian/preinst similarity index 100% rename from xCAT-genesis-builder/debian/preinst rename to xCAT-genesis-base/debian/preinst diff --git a/xCAT-genesis-builder/debian/prerm b/xCAT-genesis-base/debian/prerm similarity index 100% rename from xCAT-genesis-builder/debian/prerm rename to xCAT-genesis-base/debian/prerm diff --git a/xCAT-genesis-builder/debian/rules b/xCAT-genesis-base/debian/rules similarity index 100% rename from xCAT-genesis-builder/debian/rules rename to xCAT-genesis-base/debian/rules diff --git a/xCAT-genesis-builder/debian/source/format b/xCAT-genesis-base/debian/source/format similarity index 100% rename from xCAT-genesis-builder/debian/source/format rename to xCAT-genesis-base/debian/source/format diff --git a/xCAT-genesis-builder/dhclient-script b/xCAT-genesis-base/dhclient-script similarity index 100% rename from xCAT-genesis-builder/dhclient-script rename to xCAT-genesis-base/dhclient-script diff --git a/xCAT-genesis-builder/dhclient.conf b/xCAT-genesis-base/dhclient.conf similarity index 100% rename from xCAT-genesis-builder/dhclient.conf rename to xCAT-genesis-base/dhclient.conf diff --git a/xCAT-genesis-builder/dracut_105/README b/xCAT-genesis-base/dracut_105/README similarity index 100% rename from xCAT-genesis-builder/dracut_105/README rename to xCAT-genesis-base/dracut_105/README diff --git a/xCAT-genesis-builder/dracut_105/el/dhclient-script b/xCAT-genesis-base/dracut_105/el/dhclient-script similarity index 100% rename from xCAT-genesis-builder/dracut_105/el/dhclient-script rename to xCAT-genesis-base/dracut_105/el/dhclient-script diff --git a/xCAT-genesis-builder/dracut_105/el/dhclient.conf b/xCAT-genesis-base/dracut_105/el/dhclient.conf similarity index 100% rename from xCAT-genesis-builder/dracut_105/el/dhclient.conf rename to xCAT-genesis-base/dracut_105/el/dhclient.conf diff --git a/xCAT-genesis-builder/dracut_105/el/module-setup.sh b/xCAT-genesis-base/dracut_105/el/module-setup.sh similarity index 100% rename from xCAT-genesis-builder/dracut_105/el/module-setup.sh rename to xCAT-genesis-base/dracut_105/el/module-setup.sh diff --git a/xCAT-genesis-builder/dracut_105/el/rsyslog.conf b/xCAT-genesis-base/dracut_105/el/rsyslog.conf similarity index 100% rename from xCAT-genesis-builder/dracut_105/el/rsyslog.conf rename to xCAT-genesis-base/dracut_105/el/rsyslog.conf diff --git a/xCAT-genesis-builder/dracut_105/el/xcat-cmdline.sh b/xCAT-genesis-base/dracut_105/el/xcat-cmdline.sh similarity index 100% rename from xCAT-genesis-builder/dracut_105/el/xcat-cmdline.sh rename to xCAT-genesis-base/dracut_105/el/xcat-cmdline.sh diff --git a/xCAT-genesis-builder/dracut_105/el/xcatroot b/xCAT-genesis-base/dracut_105/el/xcatroot similarity index 100% rename from xCAT-genesis-builder/dracut_105/el/xcatroot rename to xCAT-genesis-base/dracut_105/el/xcatroot diff --git a/xCAT-genesis-builder/dracut_105/ubuntu/dhclient-script b/xCAT-genesis-base/dracut_105/ubuntu/dhclient-script similarity index 100% rename from xCAT-genesis-builder/dracut_105/ubuntu/dhclient-script rename to xCAT-genesis-base/dracut_105/ubuntu/dhclient-script diff --git a/xCAT-genesis-builder/dracut_105/ubuntu/dhclient.conf b/xCAT-genesis-base/dracut_105/ubuntu/dhclient.conf similarity index 100% rename from xCAT-genesis-builder/dracut_105/ubuntu/dhclient.conf rename to xCAT-genesis-base/dracut_105/ubuntu/dhclient.conf diff --git a/xCAT-genesis-builder/dracut_105/ubuntu/module-setup.sh b/xCAT-genesis-base/dracut_105/ubuntu/module-setup.sh similarity index 100% rename from xCAT-genesis-builder/dracut_105/ubuntu/module-setup.sh rename to xCAT-genesis-base/dracut_105/ubuntu/module-setup.sh diff --git a/xCAT-genesis-builder/dracut_105/ubuntu/rsyslog.conf b/xCAT-genesis-base/dracut_105/ubuntu/rsyslog.conf similarity index 100% rename from xCAT-genesis-builder/dracut_105/ubuntu/rsyslog.conf rename to xCAT-genesis-base/dracut_105/ubuntu/rsyslog.conf diff --git a/xCAT-genesis-builder/dracut_105/ubuntu/xcat-cmdline.sh b/xCAT-genesis-base/dracut_105/ubuntu/xcat-cmdline.sh similarity index 100% rename from xCAT-genesis-builder/dracut_105/ubuntu/xcat-cmdline.sh rename to xCAT-genesis-base/dracut_105/ubuntu/xcat-cmdline.sh diff --git a/xCAT-genesis-builder/dracut_105/ubuntu/xcatroot b/xCAT-genesis-base/dracut_105/ubuntu/xcatroot similarity index 100% rename from xCAT-genesis-builder/dracut_105/ubuntu/xcatroot rename to xCAT-genesis-base/dracut_105/ubuntu/xcatroot diff --git a/xCAT-genesis-builder/oe/.gitignore b/xCAT-genesis-base/oe/.gitignore similarity index 100% rename from xCAT-genesis-builder/oe/.gitignore rename to xCAT-genesis-base/oe/.gitignore diff --git a/xCAT-genesis-builder/oe/build b/xCAT-genesis-base/oe/build similarity index 100% rename from xCAT-genesis-builder/oe/build rename to xCAT-genesis-base/oe/build diff --git a/xCAT-genesis-builder/oe/export b/xCAT-genesis-base/oe/export similarity index 100% rename from xCAT-genesis-builder/oe/export rename to xCAT-genesis-base/oe/export diff --git a/xCAT-genesis-builder/oe/export-extension b/xCAT-genesis-base/oe/export-extension similarity index 100% rename from xCAT-genesis-builder/oe/export-extension rename to xCAT-genesis-base/oe/export-extension diff --git a/xCAT-genesis-builder/oe/kas/aarch64.yml b/xCAT-genesis-base/oe/kas/aarch64.yml similarity index 59% rename from xCAT-genesis-builder/oe/kas/aarch64.yml rename to xCAT-genesis-base/oe/kas/aarch64.yml index 1e9a76978..0ff8b41e6 100644 --- a/xCAT-genesis-builder/oe/kas/aarch64.yml +++ b/xCAT-genesis-base/oe/kas/aarch64.yml @@ -1,6 +1,6 @@ header: version: 19 includes: - - xCAT-genesis-builder/oe/kas/common.yml + - xCAT-genesis-base/oe/kas/common.yml machine: xcat-genesis-aarch64 diff --git a/xCAT-genesis-builder/oe/kas/armv7hf.yml b/xCAT-genesis-base/oe/kas/armv7hf.yml similarity index 59% rename from xCAT-genesis-builder/oe/kas/armv7hf.yml rename to xCAT-genesis-base/oe/kas/armv7hf.yml index 4a690f8a5..00fa8bee4 100644 --- a/xCAT-genesis-builder/oe/kas/armv7hf.yml +++ b/xCAT-genesis-base/oe/kas/armv7hf.yml @@ -1,6 +1,6 @@ header: version: 19 includes: - - xCAT-genesis-builder/oe/kas/common.yml + - xCAT-genesis-base/oe/kas/common.yml machine: xcat-genesis-armv7hf diff --git a/xCAT-genesis-builder/oe/kas/common.yml b/xCAT-genesis-base/oe/kas/common.yml similarity index 91% rename from xCAT-genesis-builder/oe/kas/common.yml rename to xCAT-genesis-base/oe/kas/common.yml index d30fdaeaf..4df8d89fe 100644 --- a/xCAT-genesis-builder/oe/kas/common.yml +++ b/xCAT-genesis-base/oe/kas/common.yml @@ -5,7 +5,7 @@ signers: yocto-release: fingerprint: 2AFB13F28FBBB0D1B9DAF63087EB3D32FB631AD9 repo: xcat-core - path: xCAT-genesis-builder/oe/keys/yocto-release.asc + path: xCAT-genesis-base/oe/keys/yocto-release.asc distro: xcat-genesis target: xcat-genesis-image @@ -40,7 +40,7 @@ repos: xcat-core: layers: - xCAT-genesis-builder/oe/meta-xcat-genesis: + xCAT-genesis-base/oe/meta-xcat-genesis: local_conf_header: xcat-genesis: | diff --git a/xCAT-genesis-builder/oe/kas/ppc64.yml b/xCAT-genesis-base/oe/kas/ppc64.yml similarity index 58% rename from xCAT-genesis-builder/oe/kas/ppc64.yml rename to xCAT-genesis-base/oe/kas/ppc64.yml index 73d223879..5ed808297 100644 --- a/xCAT-genesis-builder/oe/kas/ppc64.yml +++ b/xCAT-genesis-base/oe/kas/ppc64.yml @@ -1,6 +1,6 @@ header: version: 19 includes: - - xCAT-genesis-builder/oe/kas/common.yml + - xCAT-genesis-base/oe/kas/common.yml machine: xcat-genesis-ppc64 diff --git a/xCAT-genesis-builder/oe/kas/ppc64le.yml b/xCAT-genesis-base/oe/kas/ppc64le.yml similarity index 59% rename from xCAT-genesis-builder/oe/kas/ppc64le.yml rename to xCAT-genesis-base/oe/kas/ppc64le.yml index ba347c249..086b44b36 100644 --- a/xCAT-genesis-builder/oe/kas/ppc64le.yml +++ b/xCAT-genesis-base/oe/kas/ppc64le.yml @@ -1,6 +1,6 @@ header: version: 19 includes: - - xCAT-genesis-builder/oe/kas/common.yml + - xCAT-genesis-base/oe/kas/common.yml machine: xcat-genesis-ppc64le diff --git a/xCAT-genesis-builder/oe/kas/riscv64.yml b/xCAT-genesis-base/oe/kas/riscv64.yml similarity index 59% rename from xCAT-genesis-builder/oe/kas/riscv64.yml rename to xCAT-genesis-base/oe/kas/riscv64.yml index a7ec93801..ef9743b04 100644 --- a/xCAT-genesis-builder/oe/kas/riscv64.yml +++ b/xCAT-genesis-base/oe/kas/riscv64.yml @@ -1,6 +1,6 @@ header: version: 19 includes: - - xCAT-genesis-builder/oe/kas/common.yml + - xCAT-genesis-base/oe/kas/common.yml machine: xcat-genesis-riscv64 diff --git a/xCAT-genesis-builder/oe/kas/s390x.yml b/xCAT-genesis-base/oe/kas/s390x.yml similarity index 58% rename from xCAT-genesis-builder/oe/kas/s390x.yml rename to xCAT-genesis-base/oe/kas/s390x.yml index ac06515bd..32fd1cd86 100644 --- a/xCAT-genesis-builder/oe/kas/s390x.yml +++ b/xCAT-genesis-base/oe/kas/s390x.yml @@ -1,6 +1,6 @@ header: version: 19 includes: - - xCAT-genesis-builder/oe/kas/common.yml + - xCAT-genesis-base/oe/kas/common.yml machine: xcat-genesis-s390x diff --git a/xCAT-genesis-builder/oe/kas/x86.yml b/xCAT-genesis-base/oe/kas/x86.yml similarity index 57% rename from xCAT-genesis-builder/oe/kas/x86.yml rename to xCAT-genesis-base/oe/kas/x86.yml index e7d7334ff..4a4cabb45 100644 --- a/xCAT-genesis-builder/oe/kas/x86.yml +++ b/xCAT-genesis-base/oe/kas/x86.yml @@ -1,6 +1,6 @@ header: version: 19 includes: - - xCAT-genesis-builder/oe/kas/common.yml + - xCAT-genesis-base/oe/kas/common.yml machine: xcat-genesis-x86 diff --git a/xCAT-genesis-builder/oe/kas/x86_64.yml b/xCAT-genesis-base/oe/kas/x86_64.yml similarity index 58% rename from xCAT-genesis-builder/oe/kas/x86_64.yml rename to xCAT-genesis-base/oe/kas/x86_64.yml index 9bc7a83f4..6fdceb723 100644 --- a/xCAT-genesis-builder/oe/kas/x86_64.yml +++ b/xCAT-genesis-base/oe/kas/x86_64.yml @@ -1,6 +1,6 @@ header: version: 19 includes: - - xCAT-genesis-builder/oe/kas/common.yml + - xCAT-genesis-base/oe/kas/common.yml machine: xcat-genesis-x86-64 diff --git a/xCAT-genesis-builder/oe/keys/yocto-release.asc b/xCAT-genesis-base/oe/keys/yocto-release.asc similarity index 100% rename from xCAT-genesis-builder/oe/keys/yocto-release.asc rename to xCAT-genesis-base/oe/keys/yocto-release.asc diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/classes-recipe/xcat-genesis-extension.bbclass b/xCAT-genesis-base/oe/meta-xcat-genesis/classes-recipe/xcat-genesis-extension.bbclass similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/classes-recipe/xcat-genesis-extension.bbclass rename to xCAT-genesis-base/oe/meta-xcat-genesis/classes-recipe/xcat-genesis-extension.bbclass diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/conf/distro/xcat-genesis.conf b/xCAT-genesis-base/oe/meta-xcat-genesis/conf/distro/xcat-genesis.conf similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/conf/distro/xcat-genesis.conf rename to xCAT-genesis-base/oe/meta-xcat-genesis/conf/distro/xcat-genesis.conf diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/conf/layer.conf b/xCAT-genesis-base/oe/meta-xcat-genesis/conf/layer.conf similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/conf/layer.conf rename to xCAT-genesis-base/oe/meta-xcat-genesis/conf/layer.conf diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/include/s390x/tune-s390x.inc b/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/include/s390x/tune-s390x.inc similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/include/s390x/tune-s390x.inc rename to xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/include/s390x/tune-s390x.inc diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/xcat-genesis-aarch64.conf b/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-aarch64.conf similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/xcat-genesis-aarch64.conf rename to xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-aarch64.conf diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/xcat-genesis-armv7hf.conf b/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-armv7hf.conf similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/xcat-genesis-armv7hf.conf rename to xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-armv7hf.conf diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/xcat-genesis-ppc64.conf b/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-ppc64.conf similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/xcat-genesis-ppc64.conf rename to xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-ppc64.conf diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/xcat-genesis-ppc64le.conf b/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-ppc64le.conf similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/xcat-genesis-ppc64le.conf rename to xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-ppc64le.conf diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/xcat-genesis-riscv64.conf b/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-riscv64.conf similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/xcat-genesis-riscv64.conf rename to xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-riscv64.conf diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/xcat-genesis-s390x.conf b/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-s390x.conf similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/xcat-genesis-s390x.conf rename to xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-s390x.conf diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/xcat-genesis-x86-64.conf b/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-x86-64.conf similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/xcat-genesis-x86-64.conf rename to xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-x86-64.conf diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/xcat-genesis-x86.conf b/xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-x86.conf similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/conf/machine/xcat-genesis-x86.conf rename to xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-x86.conf diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/networkmanager/networkmanager_%.bbappend b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-connectivity/networkmanager/networkmanager_%.bbappend similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/networkmanager/networkmanager_%.bbappend rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-connectivity/networkmanager/networkmanager_%.bbappend diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/openssl/files/50-xcat-s390x.conf b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-connectivity/openssl/files/50-xcat-s390x.conf similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/openssl/files/50-xcat-s390x.conf rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-connectivity/openssl/files/50-xcat-s390x.conf diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/openssl/openssl_%.bbappend b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-connectivity/openssl/openssl_%.bbappend similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/openssl/openssl_%.bbappend rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-connectivity/openssl/openssl_%.bbappend diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-network/files/10-xcat-genesis.conf b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-network/files/10-xcat-genesis.conf similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-network/files/10-xcat-genesis.conf rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-network/files/10-xcat-genesis.conf diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-network/xcat-genesis-network_1.0.bb b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-network/xcat-genesis-network_1.0.bb similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-network/xcat-genesis-network_1.0.bb rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-network/xcat-genesis-network_1.0.bb diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/files/genesis-qeth b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/files/genesis-qeth similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/files/genesis-qeth rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/files/genesis-qeth diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/files/xcat-genesis-qeth.service b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/files/xcat-genesis-qeth.service similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/files/xcat-genesis-qeth.service rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/files/xcat-genesis-qeth.service diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/xcat-genesis-qeth_1.0.bb b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/xcat-genesis-qeth_1.0.bb similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/xcat-genesis-qeth_1.0.bb rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/xcat-genesis-qeth_1.0.bb diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/images/xcat-genesis-image.bb b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/images/xcat-genesis-image.bb similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/images/xcat-genesis-image.bb rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/images/xcat-genesis-image.bb diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/packagegroups/packagegroup-xcat-genesis-hardware.bb b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/packagegroups/packagegroup-xcat-genesis-hardware.bb similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/packagegroups/packagegroup-xcat-genesis-hardware.bb rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/packagegroups/packagegroup-xcat-genesis-hardware.bb diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/.clang-format b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/.clang-format similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/.clang-format rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/.clang-format diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/meson.build b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/meson.build similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/meson.build rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/meson.build diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/console.h b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/console.h similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/console.h rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/console.h diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/main.c b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/main.c similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/main.c rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/main.c diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/newt_ui.c b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/newt_ui.c similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/newt_ui.c rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/newt_ui.c diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/plain_ui.c b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/plain_ui.c similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/plain_ui.c rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/plain_ui.c diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/shell.c b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/shell.c similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/shell.c rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/shell.c diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/state.c b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/state.c similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/state.c rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/state.c diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/support.c b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/support.c similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/support.c rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src/support.c diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console@.service b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console@.service similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console@.service rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console@.service diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/xcat-genesis-console_1.0.bb b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/xcat-genesis-console_1.0.bb similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/xcat-genesis-console_1.0.bb rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/xcat-genesis-console_1.0.bb diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-extensions/files/genesis-sysext b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-extensions/files/genesis-sysext similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-extensions/files/genesis-sysext rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-extensions/files/genesis-sysext diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-extensions/files/xcat-genesis-extensions.service b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-extensions/files/xcat-genesis-extensions.service similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-extensions/files/xcat-genesis-extensions.service rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-extensions/files/xcat-genesis-extensions.service diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-extensions/xcat-genesis-extensions_1.0.bb b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-extensions/xcat-genesis-extensions_1.0.bb similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-extensions/xcat-genesis-extensions_1.0.bb rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-extensions/xcat-genesis-extensions_1.0.bb diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files/genesis-hardware b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files/genesis-hardware similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files/genesis-hardware rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files/genesis-hardware diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files/iprutils.json b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files/iprutils.json similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files/iprutils.json rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files/iprutils.json diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files/mstflint.json b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files/mstflint.json similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files/mstflint.json rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files/mstflint.json diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files/nvme.json b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files/nvme.json similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files/nvme.json rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files/nvme.json diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files/provider-iprutils b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files/provider-iprutils similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files/provider-iprutils rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files/provider-iprutils diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files/provider-mstflint b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files/provider-mstflint similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files/provider-mstflint rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files/provider-mstflint diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files/provider-nvme b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files/provider-nvme similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files/provider-nvme rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files/provider-nvme diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/xcat-genesis-hardware-control_1.0.bb b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/xcat-genesis-hardware-control_1.0.bb similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/xcat-genesis-hardware-control_1.0.bb rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/xcat-genesis-hardware-control_1.0.bb diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/00-xcat-genesis.preset b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/00-xcat-genesis.preset similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/00-xcat-genesis.preset rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/00-xcat-genesis.preset diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-action b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-action similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-action rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-action diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-functions b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-functions similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-functions rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-functions diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-maintenance-shell b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-maintenance-shell similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-maintenance-shell rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-maintenance-shell diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-metrics b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-metrics similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-metrics rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-metrics diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-network-refresh b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-network-refresh similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-network-refresh rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-network-refresh diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-network-state b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-network-state similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-network-state rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-network-state diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-register b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-register similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-register rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-register diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-status b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-status similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-status rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-status diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/xcat-genesis-action.service b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/xcat-genesis-action.service similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/xcat-genesis-action.service rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/xcat-genesis-action.service diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/xcat-genesis-metrics.service b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/xcat-genesis-metrics.service similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/xcat-genesis-metrics.service rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/xcat-genesis-metrics.service diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/xcat-genesis-network-ready.target b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/xcat-genesis-network-ready.target similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/xcat-genesis-network-ready.target rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/xcat-genesis-network-ready.target diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/xcat-genesis-network-state.service b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/xcat-genesis-network-state.service similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/xcat-genesis-network-state.service rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/xcat-genesis-network-state.service diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/xcat-genesis-register.service b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/xcat-genesis-register.service similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/xcat-genesis-register.service rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/xcat-genesis-register.service diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/xcat-genesis-init_1.0.bb b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/xcat-genesis-init_1.0.bb similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/xcat-genesis-init_1.0.bb rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/xcat-genesis-init_1.0.bb diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/zlib/files/0001-configure-disable-s390x-crcvx-by-default.patch b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/zlib/files/0001-configure-disable-s390x-crcvx-by-default.patch similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/zlib/files/0001-configure-disable-s390x-crcvx-by-default.patch rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/zlib/files/0001-configure-disable-s390x-crcvx-by-default.patch diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/zlib/zlib_%.bbappend b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/zlib/zlib_%.bbappend similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/zlib/zlib_%.bbappend rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/zlib/zlib_%.bbappend diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-extensions/images/xcat-genesis-extension-smoke.bb b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-extensions/images/xcat-genesis-extension-smoke.bb similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-extensions/images/xcat-genesis-extension-smoke.bb rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-extensions/images/xcat-genesis-extension-smoke.bb diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-extensions/xcat-genesis-extension-smoke/files/xcat-extension-smoke b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-extensions/xcat-genesis-extension-smoke/files/xcat-extension-smoke similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-extensions/xcat-genesis-extension-smoke/files/xcat-extension-smoke rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-extensions/xcat-genesis-extension-smoke/files/xcat-extension-smoke diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-extensions/xcat-genesis-extension-smoke/xcat-genesis-extension-smoke-payload_1.0.bb b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-extensions/xcat-genesis-extension-smoke/xcat-genesis-extension-smoke-payload_1.0.bb similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-extensions/xcat-genesis-extension-smoke/xcat-genesis-extension-smoke-payload_1.0.bb rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-extensions/xcat-genesis-extension-smoke/xcat-genesis-extension-smoke-payload_1.0.bb diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/kexec/kexec-tools_%.bbappend b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/kexec/kexec-tools_%.bbappend similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/kexec/kexec-tools_%.bbappend rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/kexec/kexec-tools_%.bbappend diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/0001-qemu-ppc64-drop-removed-crc32-option.patch b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/0001-qemu-ppc64-drop-removed-crc32-option.patch similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/0001-qemu-ppc64-drop-removed-crc32-option.patch rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/0001-qemu-ppc64-drop-removed-crc32-option.patch diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/0001-qemu-x86-use-i686-baseline.patch b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/0001-qemu-x86-use-i686-baseline.patch similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/0001-qemu-x86-use-i686-baseline.patch rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/0001-qemu-x86-use-i686-baseline.patch diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/0001-s390-use-stable-generator-name.patch b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/0001-s390-use-stable-generator-name.patch similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/0001-s390-use-stable-generator-name.patch rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/0001-s390-use-stable-generator-name.patch diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-aarch64.cfg b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-aarch64.cfg similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-aarch64.cfg rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-aarch64.cfg diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-armv7hf.cfg b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-armv7hf.cfg similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-armv7hf.cfg rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-armv7hf.cfg diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-powerpc64.cfg b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-powerpc64.cfg similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-powerpc64.cfg rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-powerpc64.cfg diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-ppc64.cfg b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-ppc64.cfg similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-ppc64.cfg rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-ppc64.cfg diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-riscv64.cfg b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-riscv64.cfg similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-riscv64.cfg rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-riscv64.cfg diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-s390x.cfg b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-s390x.cfg similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-s390x.cfg rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-s390x.cfg diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-x86-64.cfg b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-x86-64.cfg similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-x86-64.cfg rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-x86-64.cfg diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-x86-common.cfg b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-x86-common.cfg similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-x86-common.cfg rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-x86-common.cfg diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-x86.cfg b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-x86.cfg similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-x86.cfg rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-x86.cfg diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto_%.bbappend b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto_%.bbappend similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto_%.bbappend rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto_%.bbappend diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-support/iprutils/files/0001-configure-avoid-host-ncurses-config.patch b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-support/iprutils/files/0001-configure-avoid-host-ncurses-config.patch similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-support/iprutils/files/0001-configure-avoid-host-ncurses-config.patch rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-support/iprutils/files/0001-configure-avoid-host-ncurses-config.patch diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-support/iprutils/iprutils_2.4.19.bb b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-support/iprutils/iprutils_2.4.19.bb similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-support/iprutils/iprutils_2.4.19.bb rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-support/iprutils/iprutils_2.4.19.bb diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-support/libspdm/libspdm_%.bbappend b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-support/libspdm/libspdm_%.bbappend similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-support/libspdm/libspdm_%.bbappend rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-support/libspdm/libspdm_%.bbappend diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-support/mstflint/files/0001-python-tools-use-libtool-objects.patch b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-support/mstflint/files/0001-python-tools-use-libtool-objects.patch similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-support/mstflint/files/0001-python-tools-use-libtool-objects.patch rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-support/mstflint/files/0001-python-tools-use-libtool-objects.patch diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-support/mstflint/mstflint_4.36.0.bb b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-support/mstflint/mstflint_4.36.0.bb similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-support/mstflint/mstflint_4.36.0.bb rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-support/mstflint/mstflint_4.36.0.bb diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-support/s390-tools/s390-tools-znetconf_2.41.0.bb b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-support/s390-tools/s390-tools-znetconf_2.41.0.bb similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-support/s390-tools/s390-tools-znetconf_2.41.0.bb rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-support/s390-tools/s390-tools-znetconf_2.41.0.bb diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-bmcsetup/files/genesis-bmcsetup b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-bmcsetup/files/genesis-bmcsetup similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-bmcsetup/files/genesis-bmcsetup rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-bmcsetup/files/genesis-bmcsetup diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-bmcsetup/files/genesis-credential-wait b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-bmcsetup/files/genesis-credential-wait similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-bmcsetup/files/genesis-credential-wait rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-bmcsetup/files/genesis-credential-wait diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-bmcsetup/xcat-genesis-bmcsetup_1.0.bb b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-bmcsetup/xcat-genesis-bmcsetup_1.0.bb similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-bmcsetup/xcat-genesis-bmcsetup_1.0.bb rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-bmcsetup/xcat-genesis-bmcsetup_1.0.bb diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-credential-callback b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-credential-callback similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-credential-callback rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-credential-callback diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-discover b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-discover similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-discover rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-discover diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-discovery-callback b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-discovery-callback similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-discovery-callback rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-discovery-callback diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-getcert b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-getcert similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-getcert rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-getcert diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-udp-send.c b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-udp-send.c similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-udp-send.c rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-udp-send.c diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/xcat-genesis-credential.socket b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/xcat-genesis-credential.socket similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/xcat-genesis-credential.socket rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/xcat-genesis-credential.socket diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/xcat-genesis-credential@.service b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/xcat-genesis-credential@.service similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/xcat-genesis-credential@.service rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/xcat-genesis-credential@.service diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/xcat-genesis-discovery.socket b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/xcat-genesis-discovery.socket similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/xcat-genesis-discovery.socket rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/xcat-genesis-discovery.socket diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/xcat-genesis-discovery@.service b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/xcat-genesis-discovery@.service similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/xcat-genesis-discovery@.service rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/xcat-genesis-discovery@.service diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/xcat-genesis-discovery_1.0.bb b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/xcat-genesis-discovery_1.0.bb similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/xcat-genesis-discovery_1.0.bb rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/xcat-genesis-discovery_1.0.bb diff --git a/xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-protocol/xcat-genesis-protocol_1.0.bb b/xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-protocol/xcat-genesis-protocol_1.0.bb similarity index 100% rename from xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-protocol/xcat-genesis-protocol_1.0.bb rename to xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-protocol/xcat-genesis-protocol_1.0.bb diff --git a/xCAT-genesis-builder/oe/report b/xCAT-genesis-base/oe/report similarity index 100% rename from xCAT-genesis-builder/oe/report rename to xCAT-genesis-base/oe/report diff --git a/xCAT-genesis-builder/oe/requirements.txt b/xCAT-genesis-base/oe/requirements.txt similarity index 100% rename from xCAT-genesis-builder/oe/requirements.txt rename to xCAT-genesis-base/oe/requirements.txt diff --git a/xCAT-genesis-builder/oe/scripts/sign-extension b/xCAT-genesis-base/oe/scripts/sign-extension similarity index 100% rename from xCAT-genesis-builder/oe/scripts/sign-extension rename to xCAT-genesis-base/oe/scripts/sign-extension diff --git a/xCAT-genesis-builder/rsyslog.conf b/xCAT-genesis-base/rsyslog.conf similarity index 100% rename from xCAT-genesis-builder/rsyslog.conf rename to xCAT-genesis-base/rsyslog.conf diff --git a/xCAT-genesis-builder/xCAT-genesis-base.spec b/xCAT-genesis-base/xCAT-genesis-base.spec similarity index 100% rename from xCAT-genesis-builder/xCAT-genesis-base.spec rename to xCAT-genesis-base/xCAT-genesis-base.spec diff --git a/xCAT-genesis-builder/xcat-cmdline.sh b/xCAT-genesis-base/xcat-cmdline.sh similarity index 100% rename from xCAT-genesis-builder/xcat-cmdline.sh rename to xCAT-genesis-base/xcat-cmdline.sh diff --git a/xCAT-genesis-builder/xcatroot b/xCAT-genesis-base/xcatroot similarity index 100% rename from xCAT-genesis-builder/xcatroot rename to xCAT-genesis-base/xcatroot diff --git a/xCAT-genesis-builder/README.md b/xCAT-genesis-builder/README.md deleted file mode 100644 index dd3cbb26a..000000000 --- a/xCAT-genesis-builder/README.md +++ /dev/null @@ -1,266 +0,0 @@ -# xCAT Genesis builder - -## OpenEmbedded builder - -OpenEmbedded is the current Genesis build path. It builds a small, pinned -system instead of copying files from the build host. - -Install the host packages required by OpenEmbedded, then install `kas` in a -Python virtual environment: - -```sh -python3 -m venv .venv -. .venv/bin/activate -pip install -r xCAT-genesis-builder/oe/requirements.txt -xCAT-genesis-builder/oe/build x86_64 -``` - -The build command accepts `x86`, `x86_64`, `ppc64`, `ppc64le`, `armv7hf`, -`aarch64`, `riscv64`, and `s390x`. Multiple architectures are built in the -order given. -Run `xCAT-genesis-builder/oe/build --list-architectures` to print this list. -Artifacts are written below `xCAT-genesis-builder/oe/.work/build/tmp/deploy/images`. -The `x86` artifact uses an i686 CPU baseline. The build carries the reviewed -Yocto release key in `oe/keys` and verifies its fingerprint locally. - -The serial console normally opens a read-only Newt status screen. `F1` shows -help, `F2` shows diagnostics, and `F3` shows live logs. `F12` opens a confirmed -root maintenance shell; exiting returns to the status screen. Add -`xcat.console=plain` to the kernel command line for line output. In plain mode, -type `shell` and press Enter to open the same confirmed maintenance shell. - -Export an image for xCAT after the build: - -```sh -xCAT-genesis-builder/oe/export x86_64 \ - xCAT-genesis-builder/oe/.work/build/tmp/deploy \ - /tmp/xcat-genesis-x86_64 -``` - -Copy the exported directory intact to the management node at -`/opt/xcat/share/xcat/netboot/genesis/x86_64`, then publish it: - -```sh -mknb x86_64 -``` - -`mknb` verifies the kernel and initramfs checksums before replacing the files -under the configured TFTP root. - -### Signed extensions - -Build an extension recipe with the same machine configuration as the Genesis -image. Then export it with the site's Ed25519 release key: - -```sh -xCAT-genesis-builder/oe/export-extension x86_64 my-extension \ - xCAT-genesis-builder/oe/.work/build/tmp/deploy \ - /secure/genesis-extension.key /secure/genesis-extension.pub \ - /tmp/my-extension-bundle -``` - -The private key stays outside the source tree. The exported bundle contains -the extension image, manifest, signature, public key, and checksums. - -A site layer can include that directory in its Genesis image with a small -append file: - -```bitbake -FILESEXTRAPATHS:prepend := "${THISDIR}/files:" -SRC_URI += "file://my-extension-bundle" -XCAT_GENESIS_EXTENSION_BUNDLE = "my-extension-bundle" -``` - -Place the exported directory below the append file's `files` directory. -Genesis verifies every bundled extension before registration and stops the -boot workflow if the image, manifest, signature, key, release, or architecture -does not match. - -Genesis records registration time and memory use in `/run/xcat/metrics.env`. -After copying that file from a test VM, create a report with image sizes and -runtime measurements: - -```sh -xCAT-genesis-builder/oe/report --runtime /tmp/metrics.env \ - x86_64 /tmp/xcat-genesis-x86_64 > /tmp/report.env -``` - -Compare a later build with the saved report: - -```sh -xCAT-genesis-builder/oe/report --runtime /tmp/metrics.env \ - --baseline /tmp/report.env \ - x86_64 /tmp/xcat-genesis-x86_64 -``` - -Positive deltas mean a larger image, more memory use, or a slower registration. - -See the [architecture plan](../docs/source/developers/guides/code/genesis_openembedded_plan.rst) -for the network, hardware, license, and signed-extension contracts. - -## Legacy RPM builder - -The distribution-derived builder below is retained for the existing Genesis -packages. It is not used by the OpenEmbedded images. - -For every architecture in your cluster, be it x86_64, or ppc64, you need to have a default -`initrd` image for performing the initial boot and deploying the diskless operating system. - -If for some reason, the versions included in the xCAT `xcat-dep` repository are insufficient, you can simply -run this utility on the target architecture which will build a `xCAT-genesis-base` RPM designed for the target -architecture. You can then transfer that RPM to your xCAT Management Node, install it, and then rebuild -the various netboot images. - -# Pre-requisites - -The `xCAT-genesis-builder` package is designed to be run from a Red Hat compatible operating system -that support the Red Hat Package Manager (rpm) development tools. The script `buildrpm` will -attempt to install some of these core packages if they are not already present. - -## Instructions for buiding `xCAT-genesis-builder` RPM -Latest version 2.16.3 of `xCAT-genesis-builder` RPM available at https://www.xcat.org/files/xcat/xcat-dep/2.x_Linux/beta and was verified to install and run on Fedora28 and Red Hat 8. Earlier version 2.14.5 of `xCAT-genesis-builder` RPM available at https://www.xcat.org/files/xcat/xcat-dep/2.x_Linux/beta and was verified to install and run on Fedora26 and Red Hat 7. -If a new version of `xCAT-genesis-builder` RPM needs to be built: - -1. Clone `xcat-core` git repository: - -```sh -git clone -b master https://github.com/xcat2/xcat-core.git -``` - -2. Build new `xCAT-genesis-builder` RPM: - -```sh -cd xcat-core -./makerpm-genesisbuilder -``` - -## Instructions for buiding `xCAT-genesis-base` RPM - -First, you need to clone the `xcat-core` repo to the target architecture using the following command: - -```sh -git clone -b master https://github.com/xcat2/xcat-core.git -``` - -Once there, you can choose to either build the entire package, or more simply, -do the following (assuming 2.16.10 is your xCAT version): - -```sh -cd xcat-core/xCAT-genesis-builder -``` - -If needed, update version number inside `xCAT-genesis-base.spec` - -``` -./buildrpm -``` - -If this command is successful, runs error free, it will generate a `xCAT-genesis-base` RPM that you can transfer -to your xCAT server and install and then rebuild your netboot images prior to deploying -the netboot images to your nodes. - -The RPM will be placed into the following directory `/root/rpmbuild/RPMS/noarch` if the build is successful. - -When running the `buildrpm` the output should look similar to the following: - -```sh -[root@vmhost6 xcat-core]# cd xCAT-genesis-builder -[root@vmhost6 xCAT-genesis-builder]# ./buildrpm -Last metadata expiration check: 3:03:21 ago on Fri 27 Aug 2021 06:07:01 AM EDT. -Package rpmdevtools-8.10-8.el8.noarch is already installed. -Package rpm-build-4.14.3-14.el8_4.x86_64 is already installed. -Package screen-4.6.2-12.el8.x86_64 is already installed. -Package lldpad-1.0.1-13.git036e314.el8.x86_64 is already installed. -Package mstflint-4.15.0-1.el8.x86_64 is already installed. -Dependencies resolved. -Nothing to do. -Complete! -Last metadata expiration check: 3:03:23 ago on Fri 27 Aug 2021 06:07:01 AM EDT. -Package efibootmgr-16-1.el8.x86_64 is already installed. -Package bc-1.07.1-5.el8.x86_64 is already installed. -Dependencies resolved. -Nothing to do. -Complete! -/root/xcat-core/xCAT-genesis-builder -cp: -r not specified; omitting directory '/root/xcat-core/xCAT-genesis-builder/debian' -Creating the initramfs in /tmp/xcatgenesis.351827.rfs using dracut ... - -Expanding the initramfs into /tmp/xcatgenesis.351827/opt/xcat/share/xcat/netboot/genesis/x86_64/fs ... -623218 blocks -Adding perl libary /usr/share/perl5 -Adding perl libary /usr/lib64/perl5 -Adding kernel /boot/vmlinuz-x86_64 ... -/root/xcat-core/xCAT-genesis-builder -Tarring /tmp/xcatgenesis.351827/opt into /root/rpmbuild/SOURCES/xCAT-genesis-base-x86_64.tar.bz2 ... -Building xCAT-genesis-base rpm from /root/rpmbuild/SOURCES/xCAT-genesis-base-x86_64.tar.bz2 and /root/xcat-core/xCAT-genesis-builder/xCAT-genesis-base.spec ... -Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.z5jzb9 -+ umask 022 -+ cd /root/rpmbuild/BUILD -+ exit 0 -Executing(%build): /bin/sh -e /var/tmp/rpm-tmp.Q5Rg87 -+ umask 022 -+ cd /root/rpmbuild/BUILD -+ exit 0 -Executing(%install): /bin/sh -e /var/tmp/rpm-tmp.F7t435 -+ umask 022 -+ cd /root/rpmbuild/BUILD -+ '[' /root/rpmbuild/BUILDROOT/xCAT-genesis-base-x86_64-2.16.10-snap202108270912.x86_64 '!=' / ']' -+ rm -rf /root/rpmbuild/BUILDROOT/xCAT-genesis-base-x86_64-2.16.10-snap202108270912.x86_64 -++ dirname /root/rpmbuild/BUILDROOT/xCAT-genesis-base-x86_64-2.16.10-snap202108270912.x86_64 -+ mkdir -p /root/rpmbuild/BUILDROOT -+ mkdir /root/rpmbuild/BUILDROOT/xCAT-genesis-base-x86_64-2.16.10-snap202108270912.x86_64 -+ rm -rf /root/rpmbuild/BUILDROOT/xCAT-genesis-base-x86_64-2.16.10-snap202108270912.x86_64 -+ mkdir -p /root/rpmbuild/BUILDROOT/xCAT-genesis-base-x86_64-2.16.10-snap202108270912.x86_64 -+ cd /root/rpmbuild/BUILDROOT/xCAT-genesis-base-x86_64-2.16.10-snap202108270912.x86_64 -+ tar jxf /root/rpmbuild/SOURCES/xCAT-genesis-base-x86_64.tar.bz2 -+ cd - -/root/rpmbuild/BUILD -+ : -Processing files: xCAT-genesis-base-x86_64-2.16.10-snap202108270912.noarch -Provides: xCAT-genesis-base-x86_64 = 2:2.16.10-snap202108270912 -Requires(interp): /bin/sh -Requires(rpmlib): rpmlib(BuiltinLuaScripts) <= 4.2.2-1 rpmlib(CompressedFileNames) <= 3.0.4-1 rpmlib(FileDigests) <= 4.6.0-1 rpmlib(PartialHardlinkSets) <= 4.0.4-1 rpmlib(PayloadFilesHavePrefix) <= 4.0-1 -Requires(post): /bin/sh -Conflicts: xCAT-genesis-scripts-x86_64 < 1:2.13.10 -warning: Arch dependent binaries in noarch package -Checking for unpackaged file(s): /usr/lib/rpm/check-files /root/rpmbuild/BUILDROOT/xCAT-genesis-base-x86_64-2.16.10-snap202108270912.x86_64 -Wrote: /root/rpmbuild/SRPMS/xCAT-genesis-base-x86_64-2.16.10-snap202108270912.src.rpm -Wrote: /root/rpmbuild/RPMS/noarch/xCAT-genesis-base-x86_64-2.16.10-snap202108270912.noarch.rpm -Executing(%clean): /bin/sh -e /var/tmp/rpm-tmp.yShjx5 -+ umask 022 -+ cd /root/rpmbuild/BUILD -+ /usr/bin/rm -rf /root/rpmbuild/BUILDROOT/xCAT-genesis-base-x86_64-2.16.10-snap202108270912.x86_64 -+ exit 0 -``` - -Once you transfer the RPM to your xCAT server, you should install it. However, if there is -already an xCAT-genesis-base-* RPM for your architecture installed, you will have to uninstall -it first using the command below, assuming x86_64 architecture. We use the -`--nodeps` option as the uninstall will fail due to other dependencies. - -```sh -rpm -e --nodeps xCAT-genesis-base-x86_64* -``` - -To install the new RPM, issue the following command, using the 2.16.10 example from above: - -```sh -rpm -ivh xCAT-genesis-base*.rpm -``` - -Now, assuming that the architecture that you have built the `xCAT-genesis-base` for was `ppc64`, -you would then, on the xCAT server run the following command: - -```sh -mknb ppc64 -``` - -At this point in time, you can discover nodes by following https://xcat-docs.readthedocs.io/en/stable/guides/admin-guides/manage_clusters/ppc64le/discovery/mtms/discovery.html - -# Open Source License - -xCAT is made available under the EPL license: https://opensource.org/licenses/eclipse-1.0.php - -# Developers - -Want to help? Check out the [developers guide](http://xcat-docs.readthedocs.io/en/latest/developers)! diff --git a/xCAT-genesis-builder/buildrpm b/xCAT-genesis-builder/buildrpm deleted file mode 100755 index 0c852ea9a..000000000 --- a/xCAT-genesis-builder/buildrpm +++ /dev/null @@ -1,242 +0,0 @@ -#!/bin/bash -# Build the xCAT-genesis-base rpm on a host system. -# Option 1: install the xCAT-genesis-builder rpm (that includes this file) on a system installed with -# the distro you want to use to build xCAT-genesis-base. -# Option 2: untar the root file system of the distro you want to use and then chroot into it and copy -# this whole dir into it somewhere (like /tmp). -# Then run this script. The optional 1st arg should be mcp if you are building against mcp. - -# Currently, *Fedora 28* and *RedHat 8* are the only OSes supported to build genesis-base for ppc64, and Centos 6.5 for x86_64 - -HOSTOS="$1" -DIR=`dirname $0` -#DIR=`realpath $DIR` -DIR=`readlink -f $DIR` -BUILDARCH=`uname -m` -REQUIRED_PACKAGES="rpmdevtools rpm-build screen lldpad mstflint ipmitool pciutils mdadm dosfstools usbutils bind-utils psmisc nmap-ncat ethtool kexec-tools" - -# Install required packages -for required_package in $REQUIRED_PACKAGES; do - yum -y install $required_package - if [ $? -ne 0 ]; then - echo "ERROR: Can not install required package $required_package, make sure EPEL repository is configured" - exit 1 - fi -done - -rpmdev-setuptree - -#For Openpower -if [ -z $1 ]; then - if [ $BUILDARCH = "ppc64le" ]; then - HOSTOS="Pegas1.0" - BUILDARCH="ppc64" - elif [ $BUILDARCH = "ppc64" ]; then - HOSTOS="fedora26" - elif [ $BUILDARCH = "x86_64" ]; then - yum install efibootmgr bc -y - HOSTOS="centos7" - fi -fi - -# get the input files for dracut in the right place -# Fedora 20 ppc64 uses /usr/lib/dracut/modules.d -# CentOS 7 probably uses /usr/lib/dracut/modules.d also -if [ -e "/usr/share/dracut/modules.d" ] -then - DRACUTMODDIR=/usr/share/dracut/modules.d/97xcat -else - DRACUTMODDIR=/usr/lib/dracut/modules.d/97xcat -fi - -# get all modules in the kernel -cd $DIR -KERVER=`uname -r` - -echo "#!/bin/bash" > ./installkernel -for line in `cat /lib/modules/$KERVER/modules.dep | awk -F: '{print \$1}'`; do - basename $line >> ./installkernel; -done -sed -i 's/\(.*\)\.ko.*/instmods \1/g' ./installkernel -chmod +x ./installkernel -cd - - -mkdir -p $DRACUTMODDIR -cp $DIR/* $DRACUTMODDIR -# Remove the ipr(IBM Power RAID) stuff when building on x86_64 -if [ $BUILDARCH = "x86_64" ]; then - sed -i 's/dracut_install \/lib64\/libform.so.5//' $DRACUTMODDIR/install - sed -i 's/\/lib\/libtinfo.so.5.7/\/lib64\/libtinfo.so.5.7/' $DRACUTMODDIR/install - sed -i 's/\/lib64\/libpanel.so.5//' $DRACUTMODDIR/install - sed -i 's/\/lib64\/libmenu.so.5//' $DRACUTMODDIR/install - sed -i 's/\/lib64\/libsysfs.so.2//' $DRACUTMODDIR/install - sed -i '/\/usr\/sbin\/iprconfig/ d' $DRACUTMODDIR/install - sed -i '/hwdb.bin/ d' $DRACUTMODDIR/install - sed -i 's/instmods ipr//' $DRACUTMODDIR/installkernel - sed -i 's/ mkreiserfs//' $DRACUTMODDIR/install - sed -i 's/ reiserfstune//' $DRACUTMODDIR/install - sed -i 's/ vconfig//' $DRACUTMODDIR/install - sed -i 's/mkfs.btrfs//' $DRACUTMODDIR/install - sed -i 's/\/lib\/terminfo\/l\/linux/\/usr\/share\/terminfo\/l\/linux/g' $DRACUTMODDIR/install - sed -i 's/\/lib\/terminfo\/v\/vt100/\/usr\/share\/terminfo\/v\/vt100/g' $DRACUTMODDIR/install - sed -i 's/mkfs.btrfs//' $DRACUTMODDIR/install -fi -if [ "$HOSTOS" = "mcp" ]; then - #Special handlings for MCP PPC64 platform building. - if [ $BUILDARCH = "ppc64" ]; then - sed -i 's/dracut_install efibootmgr//' $DRACUTMODDIR/install - sed -i 's/dracut_install dmidecode \/usr\/lib64\/libstdc++.so.5//' $DRACUTMODDIR/install - sed -i 's/dmidecode//' $DRACUTMODDIR/install - sed -i 's/\/lib\/ld-linux.so.2/\/usr\/lib64\/ld-2.17.so/' $DRACUTMODDIR/install - sed -i 's/\/lib64\/libsysfs.so.2//' $DRACUTMODDIR/install - sed -i '/\/usr\/sbin\/iprconfig/ d' $DRACUTMODDIR/install - else - sed -i 's/\/lib\/ld-linux.so.2/\/usr\/lib64\/ld-linux-x86-64.so.2/' $DRACUTMODDIR/install - fi - sed -i 's/mkfs.btrfs//' $DRACUTMODDIR/install - sed -i 's/\/etc\/redhat-release/\/etc\/base-release \/etc\/system-release/' $DRACUTMODDIR/install - sed -i 's/btrfs//' $DRACUTMODDIR/installkernel - - sed -i 's/\/usr\/bin\/basename/\/bin\/basename/' $DRACUTMODDIR/install - - sed -i 's/\/lib64\/libnss_dns-2.12.so/\/usr\/lib64\/libnss_dns-2.17.so/' $DRACUTMODDIR/install - sed -i 's/\/lib64\/libnss_dns.so.2/\/usr\/lib64\/libnss_dns.so.2/' $DRACUTMODDIR/install - sed -i 's/\/lib\/libc.so.6/\/usr\/lib64\/libc.so.6/' $DRACUTMODDIR/install - sed -i 's/\/lib\/libdl.so.2/\/usr\/lib64\/libdl.so.2/' $DRACUTMODDIR/install - sed -i 's/\/lib\/libm.so.6/\/usr\/lib64\/libm.so.6/' $DRACUTMODDIR/install - sed -i 's/\/usr\/lib\/libstdc++.so.6.0.13/\/usr\/lib64\/libstdc++.so.6.0.19/' $DRACUTMODDIR/install - sed -i 's/\/usr\/lib64\/libstdc++.so.5//' $DRACUTMODDIR/install - sed -i 's/\/lib\/libpthread.so.0/\/usr\/lib64\/libpthread.so.0/' $DRACUTMODDIR/install - sed -i 's/\/lib\/libncurses.so.5.7/\/usr\/lib64\/libncurses.so.5.9/' $DRACUTMODDIR/install - sed -i 's/\/lib\/libgcc_s.so.1/\/usr\/lib64\/libgcc_s.so.1/' $DRACUTMODDIR/install - sed -i 's/\/lib\/libtinfo.so.5.7/\/usr\/lib64\/libtinfo.so.5.9/' $DRACUTMODDIR/install - sed -i 's/\/usr\/lib64\/libsasl2.so.2/\/usr\/lib64\/libsasl2.so.3/' $DRACUTMODDIR/install - - sed -i 's/\/lib\/terminfo\/l\/linux/\/usr\/share\/terminfo\/l\/linux/g' $DRACUTMODDIR/install - sed -i 's/\/lib\/terminfo\/v\/vt100/\/usr\/share\/terminfo\/v\/vt100/g' $DRACUTMODDIR/install - - # these are needed for sysclone, but are not yet in mcp - sed -i 's/ bc//' $DRACUTMODDIR/install - sed -i 's/ mkdosfs//' $DRACUTMODDIR/install - sed -i 's/ rsync//' $DRACUTMODDIR/install - sed -i 's/ kexec//' $DRACUTMODDIR/install - sed -i 's/ klogd//' $DRACUTMODDIR/install - sed -i 's/ mdadm//' $DRACUTMODDIR/install - sed -i 's/ mkfs\.xfs//' $DRACUTMODDIR/install - sed -i 's/ xfs_db//' $DRACUTMODDIR/install - sed -i 's/ mkreiserfs//' $DRACUTMODDIR/install - sed -i 's/ reiserfstune//' $DRACUTMODDIR/install - sed -i 's/ vconfig//' $DRACUTMODDIR/install - sed -i 's/ killall//' $DRACUTMODDIR/install - - # These timezone files are not available in the latest mcp build - sed -i 's/dracut_install \/usr\/share\/zoneinfo\/posix\/Asia\/Riyadh87//' $DRACUTMODDIR/install - sed -i 's/dracut_install \/usr\/share\/zoneinfo\/posix\/Asia\/Riyadh88//' $DRACUTMODDIR/install - sed -i 's/dracut_install \/usr\/share\/zoneinfo\/posix\/Asia\/Riyadh89//' $DRACUTMODDIR/install - sed -i 's/dracut_install \/usr\/share\/zoneinfo\/posix\/Mideast\/Riyadh87//' $DRACUTMODDIR/install - sed -i 's/dracut_install \/usr\/share\/zoneinfo\/posix\/Mideast\/Riyadh88//' $DRACUTMODDIR/install - sed -i 's/dracut_install \/usr\/share\/zoneinfo\/posix\/Mideast\/Riyadh89//' $DRACUTMODDIR/install - -# For ppc64 platform, needs to remove some files, -# # and some files are in different directories -elif [ $BUILDARCH = "ppc64" ]; then - if [ "$HOSTOS" = "Pegas1.0" ]; then - sed -i 's/ mkreiserfs//' $DRACUTMODDIR/install - sed -i 's/ reiserfstune//' $DRACUTMODDIR/install - sed -i 's/ vconfig//' $DRACUTMODDIR/install - fi - sed -i 's/ efibootmgr//' $DRACUTMODDIR/install - sed -i 's/ dmidecode//' $DRACUTMODDIR/install - sed -i 's/\/lib\/terminfo\/l\/linux/\/usr\/share\/terminfo\/l\/linux/g' $DRACUTMODDIR/install - sed -i 's/\/lib\/terminfo\/v\/vt100/\/usr\/share\/terminfo\/v\/vt100/g' $DRACUTMODDIR/install -fi - -libnss_pkgname=`find /usr/lib64/ -name libnss_dns-2*.so | xargs basename` -sed -i "s/\/lib64\/libnss_dns-2.12.so/\/usr\/lib64\/$libnss_pkgname/g" $DRACUTMODDIR/install -sed -i 's/\/lib64\/libnss_dns.so.2/\/usr\/lib64\/libnss_dns.so.2/' $DRACUTMODDIR/install - -# Based on cmdlist_check, make sure the commands are included in $DRACUTMODDIR/install -if [ -e "${DRACUTMODDIR}/cmdlist_check" ]; then - miss_cmd=0 - for cmd in `cat ${DRACUTMODDIR}/cmdlist_check`; do - if ! grep $cmd $DRACUTMODDIR/install; then - echo "ERROR: The required $cmd is not included in the install file to build the Genesis base package" - miss_cmd=1 - fi - done - if [ "$miss_cmd" = 1 ]; then - exit 1 - fi -fi - -mkdir -p /tmp/xcatgenesis.$$/opt/xcat/share/xcat/netboot/genesis/$BUILDARCH/fs - -# run dracut -if [ "$HOSTOS" = "mcp" ]; then - KPATH=`/bin/ls -d /lib/modules/*` - KERNELVERSION=`basename $KPATH` - echo Creating the initramfs in /tmp/xcatgenesis.$$.rfs using dracut and kernel $KERNELVERSION ... -else - echo Creating the initramfs in /tmp/xcatgenesis.$$.rfs using dracut ... -fi -# On Fedora 20 ppc64, dracut uses host-only mode by default -if [ $BUILDARCH = "ppc64" ]; then - dracut -m "xcat base" -N -f /tmp/xcatgenesis.$$.rfs $KERNELVERSION -else - dracut -m "xcat base" --no-early-microcode -N -f /tmp/xcatgenesis.$$.rfs $KERNELVERSION -fi - -if [ $? -ne 0 ]; then - echo "ERROR - creating the initramfs, please correct the issues and try again" - exit 1 -fi - -echo Expanding the initramfs into /tmp/xcatgenesis.$$/opt/xcat/share/xcat/netboot/genesis/$BUILDARCH/fs ... -cd /tmp/xcatgenesis.$$/opt/xcat/share/xcat/netboot/genesis/$BUILDARCH/fs - -zcat /tmp/xcatgenesis.$$.rfs|cpio -dumi - -# add the perl library -# add /usr/share/ntp/lib for Fedora26 ppc64, the ntp-perl will installed libraries under it -PERL_LIB_DIR="/usr/share/perl5 /usr/lib64/perl5 /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/share/ntp/lib" -for d in `echo $PERL_LIB_DIR`; do - if [ -e $d ]; then - echo Adding perl libary "$d" - TEMP_DIR=/tmp/xcatgenesis.$$/opt/xcat/share/xcat/netboot/genesis/$BUILDARCH/fs/"$d" - mkdir -p $TEMP_DIR - cp -a -t $TEMP_DIR $d/. - fi -done - -# create directory for ssh-keygen -mkdir -p /tmp/xcatgenesis.$$/opt/xcat/share/xcat/netboot/genesis/$BUILDARCH/fs/etc/ssh - -# create the predictable naming for nics -LIB_UDEV_RULES="/lib/udev/rules.d/" -if [ ! -e "$LIB_UDEV_RULES/80-net-name-slot.rules" ]; then - cp $DRACUTMODDIR/80-net-name-slot.rules /tmp/xcatgenesis.$$/opt/xcat/share/xcat/netboot/genesis/$BUILDARCH/fs/lib/udev/rules.d/ - if [ $? -ne 0 ]; then - echo "ERROR - expanding the initramfs, please correct the issues and try again" - exit 1 - fi -fi - -# add the kernel -if [ "$HOSTOS" = "mcp" ]; then - echo Adding kernel /boot/vmlinuz-* ... - cp /boot/vmlinuz-* /tmp/xcatgenesis.$$/opt/xcat/share/xcat/netboot/genesis/$BUILDARCH/kernel -else - echo Adding kernel /boot/vmlinuz-$BUILDARCH ... - cp /boot/vmlinuz-`uname -r` /tmp/xcatgenesis.$$/opt/xcat/share/xcat/netboot/genesis/$BUILDARCH/kernel -fi -cd - - -# create tar file -echo Tarring /tmp/xcatgenesis.$$/opt into ~/rpmbuild/SOURCES/xCAT-genesis-base-$BUILDARCH.tar.bz2 ... -cd /tmp/xcatgenesis.$$ -tar jcf ~/rpmbuild/SOURCES/xCAT-genesis-base-$BUILDARCH.tar.bz2 opt - -# build the rpm -echo Building xCAT-genesis-base rpm from ~/rpmbuild/SOURCES/xCAT-genesis-base-$BUILDARCH.tar.bz2 and $DIR/xCAT-genesis-base.spec ... -rpmbuild -ba $DIR/xCAT-genesis-base.spec -rm -rf $DRACUTMODDIR diff --git a/xCAT-genesis-builder/check b/xCAT-genesis-builder/check deleted file mode 100755 index ad76f12ff..000000000 --- a/xCAT-genesis-builder/check +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -#[ "$1" = "-d" ] && echo network #they pull in too much -exit 0 diff --git a/xCAT-genesis-builder/cmdlist_check b/xCAT-genesis-builder/cmdlist_check deleted file mode 100644 index e69de29bb..000000000 diff --git a/xCAT-genesis-builder/debuild-xcat-genesis-base b/xCAT-genesis-builder/debuild-xcat-genesis-base deleted file mode 100755 index 39ed5d4e8..000000000 --- a/xCAT-genesis-builder/debuild-xcat-genesis-base +++ /dev/null @@ -1,64 +0,0 @@ -#!/bin/bash -# -# DEPRECATED: Use builddeb-genesis-base for native Ubuntu builds. -# This script converts an EL-built RPM to .deb via alien. -# Kept for backward compatibility only. -# - -# xCAT-genesis-base-x86_64-2.13.10-snap201801090246.noarch.rpm -RPM_PACKAGE="$1" -if [ -z "${RPM_PACKAGE}" ] -then - echo "Usage: ${0##*/} /path/to/xCAT-genesis-base.rpm" - exit 0 -fi - -set -x - -[ -n "${RPM_PACKAGE}" ] || exit 1 -[ -f "${RPM_PACKAGE}" ] || exit 1 - -umask 0022 - -EXTRACT_DIR="${RPM_PACKAGE##*/}" -EXTRACT_DIR="${EXTRACT_DIR%%-snap*}" - -rm -rf "${EXTRACT_DIR}" -rm -rf "${EXTRACT_DIR}.orig" -alien -d -g -c -k "${RPM_PACKAGE}" || exit 1 - -PACKAGE_ARCH="${EXTRACT_DIR%-*}" -PACKAGE_ARCH="${PACKAGE_ARCH##*-}" - -if [[ ${EXTRACT_DIR} =~ -x86_64- ]] -then - rm -rf "${EXTRACT_DIR//x86_64/amd64}" - mv "${EXTRACT_DIR}" "${EXTRACT_DIR//x86_64/amd64}" - EXTRACT_DIR="${EXTRACT_DIR//x86_64/amd64}" - - sed -i -e 's/x86-64/amd64/g' "${EXTRACT_DIR}/debian/control" - sed -i -e 's/x86-64/amd64/g' "${EXTRACT_DIR}/debian/changelog" -fi - -sed -i -e "/^Description:/i Breaks: xcat-genesis-scripts-${PACKAGE_ARCH//x86_64/amd64} (<< 2.13.10)" "${EXTRACT_DIR}/debian/control" - -cat >"${EXTRACT_DIR}/debian/preinst" <catfile( - $repo_root, qw(xCAT-genesis-builder oe build) + $repo_root, qw(xCAT-genesis-base oe build) ); open(my $architecture_report, '-|', $build_path, '--list-architectures') or die "Unable to query $build_path: $!"; @@ -145,12 +145,12 @@ 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 $repo_root/xCAT-genesis-base/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' + 'xCAT-genesis-base/oe/meta-xcat-genesis/conf/distro/xcat-genesis.conf' ); like( $distro, qr/^DISTRO_CODENAME = "cheetah"$/m, 'distro uses the Cheetah codename' ); @@ -185,7 +185,7 @@ 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' + 'xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-x86-64.conf' ); like( $machine, qr/^DEFAULTTUNE = "x86-64"$/m, 'x86_64 uses the baseline tune' ); @@ -205,7 +205,7 @@ 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' + 'xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-ppc64le.conf' ); like( $ppc64le_machine, qr/^DEFAULTTUNE = "ppc64p8le"$/m, 'ppc64le uses the POWER8 little-endian tune' ); @@ -223,7 +223,7 @@ 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' + 'xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-ppc64.conf' ); like( $ppc64_machine, qr/^DEFAULTTUNE = "powerpc64"$/m, 'ppc64 uses the generic big-endian ABI' ); @@ -239,7 +239,7 @@ 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' + 'xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-x86.conf' ); like( $x86_machine, qr/^DEFAULTTUNE = "i686"$/m, 'x86 uses the i686 baseline' ); @@ -253,7 +253,7 @@ 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' + 'xCAT-genesis-base/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' ); @@ -271,7 +271,7 @@ 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' + 'xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-aarch64.conf' ); like( $aarch64_machine, qr/^DEFAULTTUNE = "aarch64"$/m, 'aarch64 uses the generic ARMv8-A tune' ); @@ -285,7 +285,7 @@ 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' + 'xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-riscv64.conf' ); like( $riscv64_machine, qr/^DEFAULTTUNE = "riscv64"$/m, 'riscv64 uses the RV64GC tune' ); @@ -318,7 +318,7 @@ for my $machine_console ( } my $image = read_file( - 'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/images/xcat-genesis-image.bb' + 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/images/xcat-genesis-image.bb' ); unlike( $image, qr/\b(?:dhclient|dhcpcd|busybox-udhcpc|systemd-networkd)\b/, @@ -335,7 +335,7 @@ 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' + 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/packagegroups/packagegroup-xcat-genesis-hardware.bb' ); for my $tool ( qw( @@ -374,7 +374,7 @@ unlike( $hardware_group, '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' + 'xCAT-genesis-base/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, @@ -398,7 +398,7 @@ unlike( $hardware_recipe, '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' + 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files/genesis-hardware' ); like( $hardware_dispatcher, qr/destructive capability requires a task identity/, @@ -415,14 +415,14 @@ like( $hardware_dispatcher, qr/write_audit failed/, 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" + "xCAT-genesis-base/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' + 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-support/mstflint/mstflint_4.36.0.bb' ); like( $mstflint, qr/^LICENSE = "Linux-OpenIB & MIT & BSD-2-Clause"$/m, @@ -438,7 +438,7 @@ 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' + 'xCAT-genesis-base/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' ); @@ -451,7 +451,7 @@ 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' + 'xCAT-genesis-base/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' ); @@ -492,7 +492,7 @@ 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' + 'xCAT-genesis-base/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' ); @@ -500,7 +500,7 @@ 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' + 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-x86-common.cfg' ); for my $symbol ( qw( @@ -517,7 +517,7 @@ for my $symbol ( } my $x86_kernel_config = read_file( - 'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-x86.cfg' + 'xCAT-genesis-base/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' ); @@ -534,13 +534,13 @@ for my $vendor (qw(AMD INTEL)) { } my $x86_64_kernel_config = read_file( - 'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-x86-64.cfg' + 'xCAT-genesis-base/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' + 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-armv7hf.cfg' ); for my $symbol ( qw( @@ -558,7 +558,7 @@ for my $symbol ( } my $aarch64_kernel_config = read_file( - 'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-aarch64.cfg' + 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-aarch64.cfg' ); for my $symbol ( qw( @@ -577,7 +577,7 @@ for my $symbol ( } my $riscv64_kernel_config = read_file( - 'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-riscv64.cfg' + 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-riscv64.cfg' ); for my $symbol ( qw( @@ -607,7 +607,7 @@ for my $symbol ( } my $powerpc64_kernel_config = read_file( - 'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-powerpc64.cfg' + 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-powerpc64.cfg' ); for my $symbol ( qw( @@ -627,7 +627,7 @@ 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' + 'xCAT-genesis-base/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, @@ -640,7 +640,7 @@ 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' + 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-connectivity/networkmanager/networkmanager_%.bbappend' ); like( $networkmanager, qr/^EXTRA_OEMESON:append = " -Dtests=no"$/m, 'NetworkManager omits its test suite' ); @@ -649,14 +649,14 @@ like( $networkmanager, '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' + 'xCAT-genesis-base/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' + 'xCAT-genesis-base/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' ); @@ -666,7 +666,7 @@ 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' + 'xCAT-genesis-base/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' ); @@ -686,17 +686,17 @@ unlike( $init_recipe, qr/genesis-debug-shell|XCAT_GENESIS_DEBUG/, 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) + qw(xCAT-genesis-base 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) + qw(xCAT-genesis-base 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' + 'xCAT-genesis-base/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' ); @@ -710,7 +710,7 @@ 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' + 'xCAT-genesis-base/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' ); @@ -718,7 +718,7 @@ 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' + 'xCAT-genesis-base/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' ); @@ -731,7 +731,7 @@ 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' + 'xCAT-genesis-base/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' ); @@ -741,7 +741,7 @@ 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' + 'xCAT-genesis-base/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' ); @@ -755,13 +755,13 @@ 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'; + 'xCAT-genesis-base/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) + qw(xCAT-genesis-base 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/, @@ -841,7 +841,7 @@ 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' + 'xCAT-genesis-base/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' ); @@ -850,7 +850,7 @@ like( $network_state_service, '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' + 'xCAT-genesis-base/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, @@ -862,14 +862,14 @@ 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' + 'xCAT-genesis-base/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' + 'xCAT-genesis-base/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' ); @@ -883,7 +883,7 @@ 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' + 'xCAT-genesis-base/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, @@ -892,7 +892,7 @@ 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' + 'xCAT-genesis-base/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' ); @@ -913,13 +913,13 @@ 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' + 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-extensions/images/xcat-genesis-extension-smoke.bb' ); like( $smoke_extension, qr/^XCAT_GENESIS_EXTENSION_NAME = "xcat-smoke"$/m, 'open smoke extension exercises the build path' ); my $preset = read_file( - 'xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/00-xcat-genesis.preset' + 'xCAT-genesis-base/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' ); diff --git a/xCAT-test/unit/genesis_openembedded_actions.t b/xCAT-test/unit/genesis_openembedded_actions.t index a288987e4..9d31bf965 100644 --- a/xCAT-test/unit/genesis_openembedded_actions.t +++ b/xCAT-test/unit/genesis_openembedded_actions.t @@ -11,23 +11,23 @@ use Test::More; my $repo_root = File::Spec->catdir( $FindBin::Bin, '..', '..' ); my $action_script = File::Spec->catfile( $repo_root, - qw(xCAT-genesis-builder oe meta-xcat-genesis recipes-core xcat-genesis-init files genesis-action) + qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-init files genesis-action) ); my $status_script = File::Spec->catfile( $repo_root, - qw(xCAT-genesis-builder oe meta-xcat-genesis recipes-core xcat-genesis-init files genesis-status) + qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-init files genesis-status) ); my $functions_file = File::Spec->catfile( $repo_root, - qw(xCAT-genesis-builder oe meta-xcat-genesis recipes-core xcat-genesis-init files genesis-functions) + qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-init files genesis-functions) ); my $action_service = File::Spec->catfile( $repo_root, - qw(xCAT-genesis-builder oe meta-xcat-genesis recipes-core xcat-genesis-init files xcat-genesis-action.service) + qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-init files xcat-genesis-action.service) ); my $init_recipe = File::Spec->catfile( $repo_root, - qw(xCAT-genesis-builder oe meta-xcat-genesis recipes-core xcat-genesis-init xcat-genesis-init_1.0.bb) + qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-init xcat-genesis-init_1.0.bb) ); sub write_file { diff --git a/xCAT-test/unit/genesis_openembedded_bmcsetup.t b/xCAT-test/unit/genesis_openembedded_bmcsetup.t index d62a902e2..76ca16c61 100644 --- a/xCAT-test/unit/genesis_openembedded_bmcsetup.t +++ b/xCAT-test/unit/genesis_openembedded_bmcsetup.t @@ -11,19 +11,19 @@ use Test::More; my $repo_root = File::Spec->catdir( $FindBin::Bin, '..', '..' ); my $recipe = File::Spec->catfile( $repo_root, - qw(xCAT-genesis-builder oe meta-xcat-genesis recipes-xcat xcat-genesis-bmcsetup xcat-genesis-bmcsetup_1.0.bb) + qw(xCAT-genesis-base oe meta-xcat-genesis recipes-xcat xcat-genesis-bmcsetup xcat-genesis-bmcsetup_1.0.bb) ); my $wrapper = File::Spec->catfile( $repo_root, - qw(xCAT-genesis-builder oe meta-xcat-genesis recipes-xcat xcat-genesis-bmcsetup files genesis-bmcsetup) + qw(xCAT-genesis-base oe meta-xcat-genesis recipes-xcat xcat-genesis-bmcsetup files genesis-bmcsetup) ); my $credential_wait = File::Spec->catfile( $repo_root, - qw(xCAT-genesis-builder oe meta-xcat-genesis recipes-xcat xcat-genesis-bmcsetup files genesis-credential-wait) + qw(xCAT-genesis-base oe meta-xcat-genesis recipes-xcat xcat-genesis-bmcsetup files genesis-credential-wait) ); my $image_recipe = File::Spec->catfile( $repo_root, - qw(xCAT-genesis-builder oe meta-xcat-genesis recipes-core images xcat-genesis-image.bb) + qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core images xcat-genesis-image.bb) ); my $legacy_dir = File::Spec->catdir( $repo_root, qw(xCAT-genesis-scripts usr bin) diff --git a/xCAT-test/unit/genesis_openembedded_console.t b/xCAT-test/unit/genesis_openembedded_console.t index 3572972df..2de444666 100644 --- a/xCAT-test/unit/genesis_openembedded_console.t +++ b/xCAT-test/unit/genesis_openembedded_console.t @@ -11,7 +11,7 @@ use Test::More; my $repo_root = File::Spec->catdir( $FindBin::Bin, '..', '..' ); my $source_dir = File::Spec->catdir( $repo_root, - qw(xCAT-genesis-builder oe meta-xcat-genesis recipes-core xcat-genesis-console files xcat-genesis-console src) + qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-console files xcat-genesis-console src) ); my @plain_sources = map { File::Spec->catfile( $source_dir, $_ ) } qw(main.c plain_ui.c shell.c state.c support.c); diff --git a/xCAT-test/unit/genesis_openembedded_discovery.t b/xCAT-test/unit/genesis_openembedded_discovery.t index 28bb40aae..5d48106b0 100644 --- a/xCAT-test/unit/genesis_openembedded_discovery.t +++ b/xCAT-test/unit/genesis_openembedded_discovery.t @@ -14,7 +14,7 @@ use Test::More; my $repo_root = File::Spec->catdir( $FindBin::Bin, '..', '..' ); my $discovery_dir = File::Spec->catdir( $repo_root, - qw(xCAT-genesis-builder oe meta-xcat-genesis recipes-xcat xcat-genesis-discovery) + qw(xCAT-genesis-base oe meta-xcat-genesis recipes-xcat xcat-genesis-discovery) ); my $discover_script = File::Spec->catfile( $discovery_dir, qw(files genesis-discover) @@ -33,7 +33,7 @@ my $udp_sender_source = File::Spec->catfile( ); my $status_script = File::Spec->catfile( $repo_root, - qw(xCAT-genesis-builder oe meta-xcat-genesis recipes-core xcat-genesis-init files genesis-status) + qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-init files genesis-status) ); sub write_file { @@ -583,7 +583,7 @@ like( read_file($discover_script), qr{/usr/libexec/xcat/genesis-udp-send}, my $image = read_file( File::Spec->catfile( $repo_root, - qw(xCAT-genesis-builder oe meta-xcat-genesis recipes-core images xcat-genesis-image.bb) + qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core images xcat-genesis-image.bb) ) ); like( $image, qr/\bxcat-genesis-discovery\b/, diff --git a/xCAT-test/unit/genesis_openembedded_export.t b/xCAT-test/unit/genesis_openembedded_export.t index cba997dce..c2208f9b2 100644 --- a/xCAT-test/unit/genesis_openembedded_export.t +++ b/xCAT-test/unit/genesis_openembedded_export.t @@ -24,7 +24,7 @@ sub read_file { } my $root = File::Spec->catdir($FindBin::Bin, '..', '..'); -my $export = File::Spec->catfile($root, 'xCAT-genesis-builder', 'oe', 'export'); +my $export = File::Spec->catfile($root, 'xCAT-genesis-base', 'oe', 'export'); my $tmpdir = tempdir(CLEANUP => 1); my $deploy = File::Spec->catdir($tmpdir, 'deploy'); my $machine = 'xcat-genesis-x86-64'; diff --git a/xCAT-test/unit/genesis_openembedded_extensions.t b/xCAT-test/unit/genesis_openembedded_extensions.t index 86352f070..709e6fa1b 100644 --- a/xCAT-test/unit/genesis_openembedded_extensions.t +++ b/xCAT-test/unit/genesis_openembedded_extensions.t @@ -13,17 +13,17 @@ use Test::More; my $repo_root = File::Spec->catdir( $FindBin::Bin, '..', '..' ); my $loader = File::Spec->catfile( $repo_root, - qw(xCAT-genesis-builder oe meta-xcat-genesis recipes-core xcat-genesis-extensions files genesis-sysext) + qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-extensions files genesis-sysext) ); my $signer = File::Spec->catfile( - $repo_root, qw(xCAT-genesis-builder oe scripts sign-extension) + $repo_root, qw(xCAT-genesis-base oe scripts sign-extension) ); my $exporter = File::Spec->catfile( - $repo_root, qw(xCAT-genesis-builder oe export-extension) + $repo_root, qw(xCAT-genesis-base oe export-extension) ); my $extension_recipe = File::Spec->catfile( $repo_root, - qw(xCAT-genesis-builder oe meta-xcat-genesis recipes-core xcat-genesis-extensions xcat-genesis-extensions_1.0.bb) + qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-extensions xcat-genesis-extensions_1.0.bb) ); sub write_file { diff --git a/xCAT-test/unit/genesis_openembedded_hardware.t b/xCAT-test/unit/genesis_openembedded_hardware.t index 770004e01..631723a6f 100644 --- a/xCAT-test/unit/genesis_openembedded_hardware.t +++ b/xCAT-test/unit/genesis_openembedded_hardware.t @@ -12,19 +12,19 @@ use Test::More; my $repo_root = File::Spec->catdir( $FindBin::Bin, '..', '..' ); my $dispatcher = File::Spec->catfile( $repo_root, - qw(xCAT-genesis-builder oe meta-xcat-genesis recipes-core xcat-genesis-hardware-control files genesis-hardware) + qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-hardware-control files genesis-hardware) ); my $nvme_provider = File::Spec->catfile( $repo_root, - qw(xCAT-genesis-builder oe meta-xcat-genesis recipes-core xcat-genesis-hardware-control files provider-nvme) + qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-hardware-control files provider-nvme) ); my $mstflint_provider = File::Spec->catfile( $repo_root, - qw(xCAT-genesis-builder oe meta-xcat-genesis recipes-core xcat-genesis-hardware-control files provider-mstflint) + qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-hardware-control files provider-mstflint) ); my $iprutils_provider = File::Spec->catfile( $repo_root, - qw(xCAT-genesis-builder oe meta-xcat-genesis recipes-core xcat-genesis-hardware-control files provider-iprutils) + qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-hardware-control files provider-iprutils) ); sub write_file { diff --git a/xCAT-test/unit/genesis_openembedded_metrics.t b/xCAT-test/unit/genesis_openembedded_metrics.t index 65f0e06ae..b03595431 100644 --- a/xCAT-test/unit/genesis_openembedded_metrics.t +++ b/xCAT-test/unit/genesis_openembedded_metrics.t @@ -11,19 +11,19 @@ use Test::More; my $repo_root = File::Spec->catdir( $FindBin::Bin, '..', '..' ); my $report = File::Spec->catfile( - $repo_root, qw(xCAT-genesis-builder oe report) + $repo_root, qw(xCAT-genesis-base oe report) ); my $metrics = File::Spec->catfile( $repo_root, - qw(xCAT-genesis-builder oe meta-xcat-genesis recipes-core xcat-genesis-init files genesis-metrics) + qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-init files genesis-metrics) ); my $recipe = File::Spec->catfile( $repo_root, - qw(xCAT-genesis-builder oe meta-xcat-genesis recipes-core xcat-genesis-init xcat-genesis-init_1.0.bb) + qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-init xcat-genesis-init_1.0.bb) ); my $service = File::Spec->catfile( $repo_root, - qw(xCAT-genesis-builder oe meta-xcat-genesis recipes-core xcat-genesis-init files xcat-genesis-metrics.service) + qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-init files xcat-genesis-metrics.service) ); sub write_file { diff --git a/xCAT-test/unit/genesis_openembedded_qeth.t b/xCAT-test/unit/genesis_openembedded_qeth.t index d5d724bd1..a0a0fa040 100644 --- a/xCAT-test/unit/genesis_openembedded_qeth.t +++ b/xCAT-test/unit/genesis_openembedded_qeth.t @@ -11,14 +11,14 @@ 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) + qw(xCAT-genesis-base 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 + qw(xCAT-genesis-base oe meta-xcat-genesis recipes-support s390-tools s390-tools-znetconf_2.41.0.bb) ); diff --git a/xCAT-test/unit/genesis_openembedded_runtime.t b/xCAT-test/unit/genesis_openembedded_runtime.t index 435f2525f..242cd6abf 100644 --- a/xCAT-test/unit/genesis_openembedded_runtime.t +++ b/xCAT-test/unit/genesis_openembedded_runtime.t @@ -13,27 +13,27 @@ use Test::More; my $repo_root = File::Spec->catdir( $FindBin::Bin, '..', '..' ); my $network_script = File::Spec->catfile( $repo_root, - qw(xCAT-genesis-builder oe meta-xcat-genesis recipes-core xcat-genesis-init files genesis-network-state) + qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-init files genesis-network-state) ); my $network_refresh_script = File::Spec->catfile( $repo_root, - qw(xCAT-genesis-builder oe meta-xcat-genesis recipes-core xcat-genesis-init files genesis-network-refresh) + qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-init files genesis-network-refresh) ); my $register_script = File::Spec->catfile( $repo_root, - qw(xCAT-genesis-builder oe meta-xcat-genesis recipes-core xcat-genesis-init files genesis-register) + qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-init files genesis-register) ); my $status_script = File::Spec->catfile( $repo_root, - qw(xCAT-genesis-builder oe meta-xcat-genesis recipes-core xcat-genesis-init files genesis-status) + qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-init files genesis-status) ); my $maintenance_shell_script = File::Spec->catfile( $repo_root, - qw(xCAT-genesis-builder oe meta-xcat-genesis recipes-core xcat-genesis-init files genesis-maintenance-shell) + qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-init files genesis-maintenance-shell) ); my $functions_file = File::Spec->catfile( $repo_root, - qw(xCAT-genesis-builder oe meta-xcat-genesis recipes-core xcat-genesis-init files genesis-functions) + qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-init files genesis-functions) ); my $getdestiny_script = File::Spec->catfile( $repo_root, diff --git a/xCAT-test/unit/test_file_support.t b/xCAT-test/unit/test_file_support.t index 7a9796870..ce310e0d4 100644 --- a/xCAT-test/unit/test_file_support.t +++ b/xCAT-test/unit/test_file_support.t @@ -19,7 +19,7 @@ is( $readme_path, File::Spec->catfile( $expected_root, $readme_relative ), 'repo my $readme = slurp_repo_file($readme_relative); like( $readme, qr/^# xCAT-test\/unit\n.*\n.*source tree/s, 'multiline text contents are preserved' ); -is( slurp_repo_file('xCAT-genesis-builder/cmdlist_check'), '', 'empty file contents are preserved' ); +is( slurp_repo_file('xCAT-genesis-scripts/debian/docs'), '', 'empty file contents are preserved' ); my $favicon = slurp_repo_file('xCAT-UI/images/favicon.ico'); is( length($favicon), 5686, 'binary file length is preserved' ); diff --git a/xCAT-test/unit/xcat_probe_package_payload.t b/xCAT-test/unit/xcat_probe_package_payload.t index 26659bcdf..453dc3b46 100644 --- a/xCAT-test/unit/xcat_probe_package_payload.t +++ b/xCAT-test/unit/xcat_probe_package_payload.t @@ -29,7 +29,6 @@ my @affected_subcommands = qw( ); my $builder = slurp_repo_file('buildrpms.pl'); -my $debian_builder = slurp_repo_file('build-ubunturepo'); my $installed_probe_test = slurp_repo_file('xCAT-test/autotest/testcase/probe/xcatproble_list'); my $rpm_spec = slurp_repo_file('xCAT-probe/xCAT-probe.spec'); @@ -75,11 +74,6 @@ for my $helper (@helpers) { scalar(grep { $_ eq $helper } XCAT_PROBE_HELPERS), "the shared builder helper list carries $helper" ); - like( - $debian_builder, - qr{cp -f [^\n]*/perl-xCAT/xCAT/\Q$helper\E\s+[^\n]*/lib/perl/xCAT/}, - "Debian builder stages $helper" - ); like( $installed_probe_test, qr/cmd:for module in [^;]*\b\Q$helper\E\b[^;]*; do test -r/, From 5bbc2377b4c5220b6b0d766ffa428ec06fbd6aad Mon Sep 17 00:00:00 2001 From: Daniel Hilst <392820+dhilst@users.noreply.github.com> Date: Mon, 14 Sep 2026 08:28:48 -0300 Subject: [PATCH 03/17] test(xcat-core): a failed extraction in the Genesis sources test stops the whole suite genesis_base_sources_staged.t called BAIL_OUT at three places where an extraction stopped matching. prove stops every remaining file on a bail-out, so a rename that breaks the regex in this file also hides every test that would have run after it. die is just as loud and costs only this file. The file header also traced the failure down to the node console. The staging is what this test measures; the dracut shell is not. Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com> --- xCAT-test/unit/genesis_base_sources_staged.t | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/xCAT-test/unit/genesis_base_sources_staged.t b/xCAT-test/unit/genesis_base_sources_staged.t index 839fcbab5..b9b28b376 100644 --- a/xCAT-test/unit/genesis_base_sources_staged.t +++ b/xCAT-test/unit/genesis_base_sources_staged.t @@ -1,11 +1,10 @@ #!/usr/bin/env perl # xCAT-genesis-base.spec builds the Genesis image from a tarball that buildrpms.pl stages: # the dracut_105 modules and 80-net-name-slot.rules. Renaming the directory they live in -# breaks that staging silently -- the spec then builds an image with no 97xcat module, and -# the failure appears as a node that netboots into a plain dracut shell. +# breaks that staging silently. # -# buildsources_genesis_base() needs a management-node-free environment but not much else, so -# it is lifted out of buildrpms.pl and run against a scratch checkout. +# buildsources_genesis_base() is lifted out of buildrpms.pl and run against a scratch +# checkout, because buildrpms.pl itself does not load outside a build. use strict; use warnings; @@ -24,14 +23,14 @@ plan skip_all => 'buildrpms.pl not found' unless -f $builder; my $source = read_text($builder); our ($body) = $source =~ /(sub\s+buildsources_genesis_base\s*\(\$\).*?\n\}\n)/s; -# BAIL_OUT rather than skip: a rename that stops this matching must fail loudly instead of +# die rather than skip: a rename that stops this matching must fail loudly instead of # quietly covering nothing. -BAIL_OUT("could not extract buildsources_genesis_base from buildrpms.pl") unless $body; +die("could not extract buildsources_genesis_base from buildrpms.pl") unless $body; # The staged directory the spec reads, as buildrpms.pl names it. Read back from the code so # the test cannot disagree with it about the name. my ($staged_dir) = $body =~ m{\bcp -a "([^/"]+)/dracut_105"}; -BAIL_OUT('buildsources_genesis_base stages no dracut_105 directory') unless $staged_dir; +die('buildsources_genesis_base stages no dracut_105 directory') unless $staged_dir; is($staged_dir, 'xCAT-genesis-base', 'the dracut assets are staged from xCAT-genesis-base'); ok(-d repo_path($staged_dir), "$staged_dir is in the checkout"); From 165c99ce2b2ce48571482d660ff005d4f5e91df2 Mon Sep 17 00:00:00 2001 From: Daniel Hilst <392820+dhilst@users.noreply.github.com> Date: Mon, 14 Sep 2026 08:33:10 -0300 Subject: [PATCH 04/17] test(xcat-core): two more tests this branch touches stop the whole suite genesis_openembedded_console.t and xcat_probe_package_payload.t still called BAIL_OUT for a missing fixture. prove stops every remaining file on a bail-out, so one of them hides the results of the tests that would have run after it. Every test file this branch touches now uses die. Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com> --- xCAT-test/unit/genesis_openembedded_console.t | 2 +- xCAT-test/unit/xcat_probe_package_payload.t | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/xCAT-test/unit/genesis_openembedded_console.t b/xCAT-test/unit/genesis_openembedded_console.t index 2de444666..9b4a7e8c9 100644 --- a/xCAT-test/unit/genesis_openembedded_console.t +++ b/xCAT-test/unit/genesis_openembedded_console.t @@ -31,7 +31,7 @@ is( ) >> 8, 0, 'plain console builds with strict warnings' -) or BAIL_OUT('unable to build the console test binary'); +) or die('unable to build the console test binary'); sub write_file { my ( $path, $contents ) = @_; diff --git a/xCAT-test/unit/xcat_probe_package_payload.t b/xCAT-test/unit/xcat_probe_package_payload.t index 453dc3b46..e3449223d 100644 --- a/xCAT-test/unit/xcat_probe_package_payload.t +++ b/xCAT-test/unit/xcat_probe_package_payload.t @@ -138,7 +138,7 @@ sub copy_tree { my ($source, $destination) = @_; my $rc = system('cp', '-R', $source, $destination); is($rc, 0, "copied $source into the package fixture") - or BAIL_OUT("unable to create package fixture from $source"); + or die("unable to create package fixture from $source"); } sub run_command { From 19a81c38a1ddbf33bee1fc5306e601d6cff89d1a Mon Sep 17 00:00:00 2001 From: Daniel Hilst <392820+dhilst@users.noreply.github.com> Date: Wed, 16 Sep 2026 18:18:21 -0300 Subject: [PATCH 05/17] test(xcat-core): go-xcat stops removing a package it used to install This branch drops xCAT-genesis-builder from GO_XCAT_UNINSTALL_LIST because no build produces that rpm any more. The list is not a record of what the build produces, it is what `go-xcat uninstall` asks the package manager to remove, and a management node installed before 2.19 still has the package. Removing the name leaves it installed with nothing to take it away and no message. The test evaluates both assignments with `type` shadowed so the rpm branch stands, and asserts the name is in the list. Two neighbours are asserted with it, so a list that evaluated to nothing cannot pass. Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com> --- xCAT-test/unit/go_xcat_uninstall_legacy.t | 53 +++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100755 xCAT-test/unit/go_xcat_uninstall_legacy.t diff --git a/xCAT-test/unit/go_xcat_uninstall_legacy.t b/xCAT-test/unit/go_xcat_uninstall_legacy.t new file mode 100755 index 000000000..17c3c27c4 --- /dev/null +++ b/xCAT-test/unit/go_xcat_uninstall_legacy.t @@ -0,0 +1,53 @@ +#!/usr/bin/env perl +use strict; +use warnings; + +use File::Temp qw(tempdir); +use FindBin; +use Test::More; + +# `go-xcat uninstall` removes what go-xcat could have installed, plus the packages xCAT used to +# ship. A name dropped from that list is a package left behind on every management node that +# still has it: nothing else asks the package manager to remove it, and the operator is not +# told. So the list outlives the build that produced each package. +# +# The two assignments are evaluated rather than read, with `type` shadowed so the rpm branch +# stands, and the names asserted are the ones go-xcat would hand to the package manager. + +my $go_xcat = "$FindBin::Bin/../../xCAT-server/share/xcat/tools/go-xcat"; +plan skip_all => 'go-xcat not found' unless -r $go_xcat; +plan tests => 3; + +my @uninstall = uninstall_list($go_xcat); + +# xCAT-genesis-builder was built until 2.19 and is installed on existing management nodes. +ok(scalar(grep { $_ eq 'xCAT-genesis-builder' } @uninstall), + 'go-xcat uninstall still removes xCAT-genesis-builder'); + +# Two neighbours, so a list that evaluated to nothing cannot pass the assertion above. +ok(scalar(grep { $_ eq 'xCAT-genesis-openembedded-x86_64' } @uninstall), + 'go-xcat uninstall removes the OpenEmbedded Genesis packages'); +ok(scalar(grep { $_ eq 'xCAT-server' } @uninstall), + 'go-xcat uninstall removes what it installs'); + +# Evaluate both assignments: the uninstall list is built from the install one, and the Debian +# reassignment below it is guarded by `type dpkg`, which is shadowed away. +sub uninstall_list { + my ($path) = @_; + my $dir = tempdir(CLEANUP => 1); + my $driver = "$dir/driver.sh"; + open my $fh, '>', $driver or die "$driver: $!"; + print {$fh} <<'BASH'; +set -u +type() { return 1; } +eval "$(awk '/^GO_XCAT_INSTALL_LIST=\(/ { copy = 1 } copy { print } copy && /^$/ { exit }' "$GO_XCAT_SOURCE")" +printf '%s\n' "${GO_XCAT_UNINSTALL_LIST[@]}" +BASH + close $fh; + local $ENV{GO_XCAT_SOURCE} = $path; + my @out = qx{bash '$driver' 2>/dev/null}; + chomp @out; + my @names = grep { length } @out; + BAIL_OUT("no package list read from $path") unless @names; + return @names; +} From 1e939d4cb0c6a5f91b8ec251403cbfcadbf340ad Mon Sep 17 00:00:00 2001 From: Daniel Hilst <392820+dhilst@users.noreply.github.com> Date: Wed, 16 Sep 2026 18:19:40 -0300 Subject: [PATCH 06/17] fix(xcat-core): the rename took the OpenEmbedded build instructions with it Deleting xCAT-genesis-builder/README.md removed 99 lines that describe the build path this branch keeps: the kas virtualenv, the architectures `oe/build` accepts, the console keys, `oe/export`, the signed-extension workflow with `oe/export-extension`, and the `oe/report` baseline comparison. Only the legacy RPM builder half was dead. The reader is now told nothing about how to build a Genesis image. xCAT-genesis-base/README.md carries that half with its paths on the new directory. The legacy RPM builder chapter stays deleted. go-xcat keeps xCAT-genesis-builder in GO_XCAT_UNINSTALL_LIST. No build produces that rpm any more, but a management node installed before 2.19 still has it, and the list is what `go-xcat uninstall` hands to the package manager. genesis_readme_paths.t asserts every in-tree path the README names exists, and that its link to the architecture plan resolves. It reads the file because the file is the artifact: the text is the instruction. It matches any xCAT-* directory rather than the new name alone, so a path left on the old name fails instead of going uncollected, and it bails below a fixed floor so a pattern that stops matching cannot pass on an empty plan. Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com> --- xCAT-genesis-base/README.md | 107 ++++++++++++++++++++++++++ xCAT-server/share/xcat/tools/go-xcat | 2 +- xCAT-test/unit/genesis_readme_paths.t | 39 ++++++++++ 3 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 xCAT-genesis-base/README.md create mode 100755 xCAT-test/unit/genesis_readme_paths.t diff --git a/xCAT-genesis-base/README.md b/xCAT-genesis-base/README.md new file mode 100644 index 000000000..11871b3f0 --- /dev/null +++ b/xCAT-genesis-base/README.md @@ -0,0 +1,107 @@ +# xCAT Genesis + +## OpenEmbedded builder + +OpenEmbedded is the current Genesis build path. It builds a small, pinned +system instead of copying files from the build host. + +Install the host packages required by OpenEmbedded, then install `kas` in a +Python virtual environment: + +```sh +python3 -m venv .venv +. .venv/bin/activate +pip install -r xCAT-genesis-base/oe/requirements.txt +xCAT-genesis-base/oe/build x86_64 +``` + +The build command accepts `x86`, `x86_64`, `ppc64`, `ppc64le`, `armv7hf`, +`aarch64`, `riscv64`, and `s390x`. Multiple architectures are built in the +order given. +Run `xCAT-genesis-base/oe/build --list-architectures` to print this list. +Artifacts are written below `xCAT-genesis-base/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. + +The serial console normally opens a read-only Newt status screen. `F1` shows +help, `F2` shows diagnostics, and `F3` shows live logs. `F12` opens a confirmed +root maintenance shell; exiting returns to the status screen. Add +`xcat.console=plain` to the kernel command line for line output. In plain mode, +type `shell` and press Enter to open the same confirmed maintenance shell. + +Export an image for xCAT after the build: + +```sh +xCAT-genesis-base/oe/export x86_64 \ + xCAT-genesis-base/oe/.work/build/tmp/deploy \ + /tmp/xcat-genesis-x86_64 +``` + +Copy the exported directory intact to the management node at +`/opt/xcat/share/xcat/netboot/genesis/x86_64`, then publish it: + +```sh +mknb x86_64 +``` + +`mknb` verifies the kernel and initramfs checksums before replacing the files +under the configured TFTP root. + +### Signed extensions + +Build an extension recipe with the same machine configuration as the Genesis +image. Then export it with the site's Ed25519 release key: + +```sh +xCAT-genesis-base/oe/export-extension x86_64 my-extension \ + xCAT-genesis-base/oe/.work/build/tmp/deploy \ + /secure/genesis-extension.key /secure/genesis-extension.pub \ + /tmp/my-extension-bundle +``` + +The private key stays outside the source tree. The exported bundle contains +the extension image, manifest, signature, public key, and checksums. + +A site layer can include that directory in its Genesis image with a small +append file: + +```bitbake +FILESEXTRAPATHS:prepend := "${THISDIR}/files:" +SRC_URI += "file://my-extension-bundle" +XCAT_GENESIS_EXTENSION_BUNDLE = "my-extension-bundle" +``` + +Place the exported directory below the append file's `files` directory. +Genesis verifies every bundled extension before registration and stops the +boot workflow if the image, manifest, signature, key, release, or architecture +does not match. + +Genesis records registration time and memory use in `/run/xcat/metrics.env`. +After copying that file from a test VM, create a report with image sizes and +runtime measurements: + +```sh +xCAT-genesis-base/oe/report --runtime /tmp/metrics.env \ + x86_64 /tmp/xcat-genesis-x86_64 > /tmp/report.env +``` + +Compare a later build with the saved report: + +```sh +xCAT-genesis-base/oe/report --runtime /tmp/metrics.env \ + --baseline /tmp/report.env \ + x86_64 /tmp/xcat-genesis-x86_64 +``` + +Positive deltas mean a larger image, more memory use, or a slower registration. + +See the [architecture plan](../docs/source/developers/guides/code/genesis_openembedded_plan.rst) +for the network, hardware, license, and signed-extension contracts. + +# Open Source License + +xCAT is made available under the EPL license: https://opensource.org/licenses/eclipse-1.0.php + +# Developers + +Want to help? Check out the [developers guide](http://xcat-docs.readthedocs.io/en/latest/developers)! diff --git a/xCAT-server/share/xcat/tools/go-xcat b/xCAT-server/share/xcat/tools/go-xcat index 565d2126a..f1c452c7c 100755 --- a/xCAT-server/share/xcat/tools/go-xcat +++ b/xCAT-server/share/xcat/tools/go-xcat @@ -204,7 +204,7 @@ GO_XCAT_INSTALL_LIST=(perl-xcat xcat-client xcat xcat-buildkit xcat-genesis-base-amd64 xcat-genesis-base-ppc64 xnba-undi) # The package list of all the packages should be uninstalled GO_XCAT_UNINSTALL_LIST=("${GO_XCAT_INSTALL_LIST[@]}" - goconserver xCAT-SoftLayer xCAT-confluent xCAT-csm + goconserver xCAT-SoftLayer xCAT-confluent xCAT-csm xCAT-genesis-builder 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 diff --git a/xCAT-test/unit/genesis_readme_paths.t b/xCAT-test/unit/genesis_readme_paths.t new file mode 100755 index 000000000..681901e93 --- /dev/null +++ b/xCAT-test/unit/genesis_readme_paths.t @@ -0,0 +1,39 @@ +#!/usr/bin/env perl +use strict; +use warnings; + +use FindBin; +use Test::More; + +# The Genesis README is the build instructions: every command in it is meant to be pasted. A +# path that no longer exists is the whole document failing, and a rename is what breaks it -- +# the directory was xCAT-genesis-builder until 2.19 and every path in the README carried that +# name. The file is the artifact here, so reading it is the test. + +my $root = "$FindBin::Bin/../.."; +my $readme = "$root/xCAT-genesis-base/README.md"; +plan skip_all => 'xCAT-genesis-base/README.md not found' unless -r $readme; + +my $text = do { open my $fh, '<', $readme or die "$readme: $!"; local $/; <$fh> }; + +# Every in-tree path the README names, whatever directory it names -- a rename that half +# happened leaves the old name here, and a pattern matching only the new one would collect +# nothing and pass. .work is the build's own output directory and is not in the checkout. +my %named = map { $_ => 1 } grep { !m{/\.work/} } + ($text =~ m{(xCAT-[A-Za-z0-9._-]+/[A-Za-z0-9._/-]+)}g); +my @named = sort keys %named; + +# A fixed floor, so a pattern that stops matching cannot shrink the plan to nothing and pass. +my $EXPECTED_AT_LEAST = 5; +BAIL_OUT(sprintf('the README names %d in-tree paths, fewer than the %d it should', + scalar(@named), $EXPECTED_AT_LEAST)) + if @named < $EXPECTED_AT_LEAST; + +plan tests => scalar(@named) + 2; + +ok(-e "$root/$_", "the README names $_, and it is there") for @named; + +my ($link) = $text =~ m{\]\((\.\./docs/[^)]+)\)}; +ok($link, 'the README links to the architecture plan'); +ok($link && -e "$root/xCAT-genesis-base/$link", + 'and the link resolves from the directory the README sits in'); From 87974d91c40ceb7ae2a595de4a02708c08bb8f2d Mon Sep 17 00:00:00 2001 From: Daniel Hilst <392820+dhilst@users.noreply.github.com> Date: Wed, 16 Sep 2026 18:24:31 -0300 Subject: [PATCH 07/17] test(xcat-core): go-xcat asks to remove packages that are not installed uninstall_xcat hands the whole GO_XCAT_UNINSTALL_LIST to the package manager. Most of that list is not installed on any one node -- it names packages xCAT stopped producing, so that `go-xcat uninstall` still removes them where they are present. apt answers "Unable to locate package" for a name it does not know and go-xcat prints a warning for each; dnf answers "No match for argument" and fails the whole call when nothing in the list matches. The test runs uninstall_xcat with rpm and dpkg-query answering from a fake package database and remove_package shadowed, and reads the argument list the package manager would have been given. A fourth case hands over a database where everything is installed, so a filter that dropped everything could not pass the other three. Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com> --- .../unit/go_xcat_uninstall_installed_only.t | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100755 xCAT-test/unit/go_xcat_uninstall_installed_only.t diff --git a/xCAT-test/unit/go_xcat_uninstall_installed_only.t b/xCAT-test/unit/go_xcat_uninstall_installed_only.t new file mode 100755 index 000000000..31464393c --- /dev/null +++ b/xCAT-test/unit/go_xcat_uninstall_installed_only.t @@ -0,0 +1,109 @@ +#!/usr/bin/env perl +use strict; +use warnings; + +use File::Temp qw(tempdir); +use FindBin; +use Test::More; + +# `go-xcat uninstall` hands GO_XCAT_UNINSTALL_LIST to the package manager. That list names +# packages xCAT stopped producing -- xCAT-genesis-builder, yaboot-xcat, conserver-xcat -- so +# most of it is not installed on any one node. apt answers "Unable to locate package" for a +# name it does not know, and go-xcat prints a warning for each; dnf answers "No match for +# argument" and fails the whole call when nothing in the list matches. +# +# So what is not installed does not reach the package manager. That is also what makes keeping +# a retired name in the list free, which is why the list outlives the build. +# +# uninstall_xcat is taken from the shipped script and run with the package database faked and +# remove_package shadowed, so the assertions read the argument list the package manager would +# have been given. + +my $go_xcat = "$FindBin::Bin/../../xCAT-server/share/xcat/tools/go-xcat"; +plan skip_all => 'go-xcat not found' unless -r $go_xcat; +plan tests => 4; + +{ + my @handed = uninstall_args($go_xcat, ['xCAT-server', 'xCAT-client']); + is_deeply([sort @handed], ['xCAT-client', 'xCAT-server'], + 'only the installed packages reach the package manager'); +} + +{ + # The point of keeping a retired name: where it IS installed, it is still removed. + my @handed = uninstall_args($go_xcat, ['xCAT-genesis-builder']); + is_deeply(\@handed, ['xCAT-genesis-builder'], + 'a retired package that is installed is still removed'); +} + +{ + my @handed = uninstall_args($go_xcat, []); + is_deeply(\@handed, [], + 'a node with none of them installed hands the package manager nothing'); +} + +{ + # Guards the fake: a database that says everything is installed must hand over the list, + # or the three assertions above would pass against a filter that drops everything. + my @handed = uninstall_args($go_xcat, ['*']); + ok(scalar(@handed) > 10, 'with everything installed the whole list is handed over'); +} + +# Extract `function NAME()` blocks by name, in the order asked for. +sub extract { + my ($text, @names) = @_; + my @blocks; + for my $name (@names) { + my ($block) = $text =~ /^(function \Q$name\E\(\)\s*\n\{.*?^\})/ms; + push @blocks, $block if $block; + } + return @blocks; +} + +# Run the shipped uninstall path with rpm and dpkg-query answering from a fake database and the +# package manager shadowed, and collect what it was asked to remove. +sub uninstall_args { + my ($path, $installed) = @_; + my $text = do { open my $fh, '<', $path or die "$path: $!"; local $/; <$fh> }; + + my ($lists) = $text =~ /^(GO_XCAT_INSTALL_LIST=\(.*?\n)\n/ms; + BAIL_OUT("no package lists in $path") unless $lists; + my @functions = extract($text, qw(function_dispatch installed_packages + installed_packages_rpm installed_packages_deb + uninstall_xcat)); + BAIL_OUT("no uninstall_xcat() in $path") + unless grep { /^function uninstall_xcat\(\)/ } @functions; + + my $dir = tempdir(CLEANUP => 1); + my $driver = "$dir/driver.sh"; + my $present = join ' ', @{$installed}; + open my $fh, '>', $driver or die "$driver: $!"; + print {$fh} "set -u\n"; + print {$fh} "type() { return 1; }\n"; # the rpm branch of the lists + print {$fh} $lists, "\n"; + print {$fh} <<"BASH"; +INSTALLED="$present" +present() { + case "\$INSTALLED" in *"*"*) return 0 ;; esac + case " \$INSTALLED " in *" \$1 "*) return 0 ;; *) return 1 ;; esac +} +rpm() { for a in "\$\@"; do case "\$a" in -*) continue ;; esac; present "\$a" || return 1; done; return 0; } +dpkg-query() { + for a in "\$\@"; do + case "\$a" in -*) continue ;; esac + present "\$a" && echo "install ok installed" + done + return 0 +} +exit_if_bad() { :; } +warn_if_bad() { :; } +remove_package() { for a in "\$\@"; do case "\$a" in -y) continue ;; esac; echo "\$a"; done; } +BASH + print {$fh} join("\n", @functions), "\n"; + print {$fh} "uninstall_xcat -y\n"; + close $fh; + + my @out = qx{bash '$driver' 2>/dev/null}; + chomp @out; + return grep { length } @out; +} From 6edeafa438eb560e0cef0e15dfa9a61e25e83ccb Mon Sep 17 00:00:00 2001 From: Daniel Hilst <392820+dhilst@users.noreply.github.com> Date: Wed, 16 Sep 2026 18:25:29 -0300 Subject: [PATCH 08/17] fix(xcat-core): go-xcat asks to remove packages that are not installed GO_XCAT_UNINSTALL_LIST names every package go-xcat installs plus the ones xCAT has stopped producing, so a node installed before they went away is still cleaned up. uninstall_xcat handed that whole list to the package manager, and most of it is not installed on any one node: apt answers "Unable to locate package" for a name it does not know and go-xcat warns once per package, while dnf answers "No match for argument" and fails the call when nothing matches. installed_packages() asks the local package database which of them this system has, and uninstall_xcat and uninstall_xcat_completely pass only those on. A node with none of them installed calls the package manager not at all. This is also what makes a retired name in the list free to keep. go_xcat_uninstall_installed_only.t runs uninstall_xcat with rpm and dpkg-query answering from a fake package database and remove_package shadowed, and reads the argument list the package manager would have been given. A filter that kept everything and a filter that kept nothing each fail three of its four assertions. Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com> --- xCAT-server/share/xcat/tools/go-xcat | 43 ++++++++++++++++++- .../unit/go_xcat_uninstall_installed_only.t | 5 ++- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/xCAT-server/share/xcat/tools/go-xcat b/xCAT-server/share/xcat/tools/go-xcat index f1c452c7c..b63b05c0a 100755 --- a/xCAT-server/share/xcat/tools/go-xcat +++ b/xCAT-server/share/xcat/tools/go-xcat @@ -2033,10 +2033,47 @@ function update_xcat() install_packages "$@" "${install_list[@]}" } +# $@ package names +# Prints the ones this system actually has. `go-xcat uninstall` names packages xCAT no +# longer produces, so that a node installed before they went away is still cleaned up. +# Handing those names to the package manager on a node that never had them costs a +# warning each on apt, and fails the whole call on dnf when none of them match. +function installed_packages_rpm() +{ + type rpm >/dev/null 2>&1 || return 255 + local package="" + for package in "$@" + do + rpm -q --quiet "${package}" && echo "${package}" + done + return 0 +} + +# $@ package names +function installed_packages_deb() +{ + type dpkg-query >/dev/null 2>&1 || return 255 + local package="" + for package in "$@" + do + [[ "$(dpkg-query --show '--showformat=${Status}' "${package}" 2>/dev/null)" \ + = "install ok installed" ]] && echo "${package}" + done + return 0 +} + +function installed_packages() +{ + function_dispatch "${FUNCNAME[0]}" "$@" +} + # $1 -y function uninstall_xcat() { - remove_package "$@" "${GO_XCAT_UNINSTALL_LIST[@]}" + local -a present=() + readarray -t present < <(installed_packages "${GO_XCAT_UNINSTALL_LIST[@]}") + [[ "${#present[@]}" -gt "0" ]] || return 0 + remove_package "$@" "${present[@]}" } function kill_xcat() @@ -2091,7 +2128,9 @@ function trash_xcat() function uninstall_xcat_completely() { - purge_package -y "${GO_XCAT_UNINSTALL_LIST[@]}" + local -a present=() + readarray -t present < <(installed_packages "${GO_XCAT_UNINSTALL_LIST[@]}") + [[ "${#present[@]}" -eq "0" ]] || purge_package -y "${present[@]}" kill_xcat trash_xcat diff --git a/xCAT-test/unit/go_xcat_uninstall_installed_only.t b/xCAT-test/unit/go_xcat_uninstall_installed_only.t index 31464393c..168aa443d 100755 --- a/xCAT-test/unit/go_xcat_uninstall_installed_only.t +++ b/xCAT-test/unit/go_xcat_uninstall_installed_only.t @@ -79,7 +79,10 @@ sub uninstall_args { my $present = join ' ', @{$installed}; open my $fh, '>', $driver or die "$driver: $!"; print {$fh} "set -u\n"; - print {$fh} "type() { return 1; }\n"; # the rpm branch of the lists + # An rpm host: dpkg is absent, so the lists take the rpm branch and the deb arm of the + # dispatch stands down. Shadowing `type` to fail for everything would stand both arms + # down and the filter would answer nothing, whatever it does. + print {$fh} "type() { case \"\$1\" in dpkg|dpkg-query|apt-get) return 1 ;; *) return 0 ;; esac; }\n"; print {$fh} $lists, "\n"; print {$fh} <<"BASH"; INSTALLED="$present" From cdb9360c5312dda1e9361ff75756cd0976a63457 Mon Sep 17 00:00:00 2001 From: Daniel Hilst <392820+dhilst@users.noreply.github.com> Date: Thu, 24 Sep 2026 16:05:31 -0300 Subject: [PATCH 09/17] fix(xcat-core): Genesis base staging cannot run outside buildrpms.pl buildsources_genesis_base() lived in buildrpms.pl, which runs mkdir, git and file writes at file scope and cannot be loaded. Its test therefore extracted the sub with a regex and evaluated a copy of the source. Move the staging into XCAT::BuildUtils as stage_genesis_base_sources(), which takes the checkout, the tarball path and SOURCE_DATE_EPOCH. buildsources_genesis_base() now calls it. The tarball content and the failure on a missing xCAT-genesis-base directory do not change. Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com> --- build-utils/lib/XCAT/BuildUtils.pm | 45 +++++++++++++++++++++++++++++- buildrpms.pl | 24 ++-------------- 2 files changed, 47 insertions(+), 22 deletions(-) diff --git a/build-utils/lib/XCAT/BuildUtils.pm b/build-utils/lib/XCAT/BuildUtils.pm index 54df11487..adf0a7e0f 100644 --- a/build-utils/lib/XCAT/BuildUtils.pm +++ b/build-utils/lib/XCAT/BuildUtils.pm @@ -24,7 +24,7 @@ use feature 'say'; our @EXPORT_OK = qw( source_date_epoch snap_release deb_version - stage_probe_helpers XCAT_PROBE_HELPERS + stage_probe_helpers XCAT_PROBE_HELPERS stage_genesis_base_sources deb_package_arches dist_arches default_dists orig_tarball_name upstream_version resolve_dest pin_control_version rewrite_changelog_header @@ -325,6 +325,49 @@ sub stage_probe_helpers { return @staged; } +#------------------------------------------------------------------------------- + +=head3 stage_genesis_base_sources + + Descriptions: + Write the build support tarball that xCAT-genesis-base.spec unpacks: + the dracut_105 modules and 80-net-name-slot.rules, taken from the + xCAT-genesis-base directory of a checkout. + Arguments: + $checkout: the top of the xcat-core checkout + $tarball: the path of the tarball to write + $epoch: SOURCE_DATE_EPOCH, the mtime of every member + Returns: + $tarball. Dies when the checkout has no xCAT-genesis-base directory, + so a rename stops the build instead of producing an empty tarball. + +=cut + +#------------------------------------------------------------------------------- +sub stage_genesis_base_sources { + my ($checkout, $tarball, $epoch) = @_; + my $source = "$checkout/xCAT-genesis-base"; + die "Assertion failed! No directory xCAT-genesis-base in $checkout\n" + unless -d $source; + + my $staging_parent = "/tmp/xcat-genesis-base-build-support.$$"; + my $staging_root = "$staging_parent/xCAT-genesis-base-build-support"; + remove_tree($staging_parent) if -e $staging_parent; + make_path("$staging_root/dracut_105"); + + sh_or_die(qq(cp -a "$source/dracut_105" "$staging_root/"), + "Error copying dracut_105 sources"); + copy("$source/80-net-name-slot.rules", "$staging_root/80-net-name-slot.rules") + or die "Unable to stage $source/80-net-name-slot.rules: $!\n"; + + unlink $tarball if -f $tarball; + sh_or_die(qq(tar --sort=name --owner=0 --group=0 --mtime="\@$epoch" -cjf "$tarball" -C "$staging_parent" xCAT-genesis-base-build-support), + "Error creating $tarball"); + + remove_tree($staging_parent); + return $tarball; +} + # deb_package_arches: the architectures to build a package for. # 'all' is a single arch-independent build; the three arch packages get one per arch. sub deb_package_arches { diff --git a/buildrpms.pl b/buildrpms.pl index 9ed6ba321..39923d87f 100755 --- a/buildrpms.pl +++ b/buildrpms.pl @@ -44,6 +44,7 @@ use File::Temp qw(tempdir tempfile); use FindBin qw($Bin); use lib "$Bin/build-utils/lib"; use XCAT::BuildUtils qw(git_revision source_date_epoch sh sh_or_die usage buildinfo_text + stage_genesis_base_sources write_script read_line targetarch_from_target); use Fcntl qw(:flock); # per-target build lock (concurrency guard; see main()) use Getopt::Long qw(GetOptions); @@ -331,27 +332,8 @@ sub createmockconfig { } sub buildsources_genesis_base($) { - my ($target) = @_; - - die "Assertion failed! No directory xCAT-genesis-base in the current directory" - unless -d "./xCAT-genesis-base"; - my $staging_parent = "/tmp/xcat-genesis-base-build-support.$$"; - my $staging_root = "$staging_parent/xCAT-genesis-base-build-support"; - my $support_tarball = "$SOURCES/xCAT-genesis-base-build-support.tar.bz2"; - - remove_tree($staging_parent) if -e $staging_parent; - make_path("$staging_root/dracut_105"); - - sh_or_die(qq(cp -a "xCAT-genesis-base/dracut_105" "$staging_root/"), - "Error copying dracut_105 sources"); - cp "xCAT-genesis-base/80-net-name-slot.rules", - "$staging_root/80-net-name-slot.rules"; - - unlink $support_tarball if -f $support_tarball; - sh_or_die(qq(tar --sort=name --owner=0 --group=0 --mtime="\@$SOURCE_DATE_EPOCH" -cjf "$support_tarball" -C "$staging_parent" xCAT-genesis-base-build-support), - "Error creating $support_tarball"); - - remove_tree($staging_parent); + stage_genesis_base_sources(".", "$SOURCES/xCAT-genesis-base-build-support.tar.bz2", + $SOURCE_DATE_EPOCH); } sub prepare_xcat_probe_source_tar { From 263247532dce1ea459a5943bf1670c9473919f9a Mon Sep 17 00:00:00 2001 From: Daniel Hilst <392820+dhilst@users.noreply.github.com> Date: Thu, 24 Sep 2026 16:05:31 -0300 Subject: [PATCH 10/17] fix(xcat-core): genesis_base_sources_staged.t evaluates a copy of buildrpms.pl The test lifted buildsources_genesis_base() out of buildrpms.pl with a regex, evaluated it with a stubbed sh_or_die, and read the staged directory name back out of the source text. A change to the regex or to the formatting of the sub broke the test without a change in behaviour. The test now loads XCAT::BuildUtils and calls stage_genesis_base_sources() on a scratch checkout, on the real checkout and on a checkout that still uses the xCAT-genesis-builder name. It asserts the exact tarball members, their owner and mtime, and the error message. Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com> --- xCAT-test/unit/genesis_base_sources_staged.t | 115 ++++++++----------- 1 file changed, 50 insertions(+), 65 deletions(-) diff --git a/xCAT-test/unit/genesis_base_sources_staged.t b/xCAT-test/unit/genesis_base_sources_staged.t index b9b28b376..9b47034e2 100644 --- a/xCAT-test/unit/genesis_base_sources_staged.t +++ b/xCAT-test/unit/genesis_base_sources_staged.t @@ -2,89 +2,74 @@ # xCAT-genesis-base.spec builds the Genesis image from a tarball that buildrpms.pl stages: # the dracut_105 modules and 80-net-name-slot.rules. Renaming the directory they live in # breaks that staging silently. -# -# buildsources_genesis_base() is lifted out of buildrpms.pl and run against a scratch -# checkout, because buildrpms.pl itself does not load outside a build. use strict; use warnings; -use Cwd qw(getcwd); use File::Path qw(make_path); -use File::Slurper qw(read_text write_text); +use File::Slurper qw(write_text); use File::Temp qw(tempdir); use FindBin; use lib "$FindBin::Bin/../lib"; +use lib "$FindBin::Bin/../../build-utils/lib"; use Test::More; use XCAT::Test::File qw(repo_path); +use XCAT::BuildUtils qw(stage_genesis_base_sources); -my $builder = repo_path('buildrpms.pl'); -plan skip_all => 'buildrpms.pl not found' unless -f $builder; +my $EPOCH = 1700000000; +my $MODULE = 'module-setup.sh'; +my $scratch = tempdir(CLEANUP => 1); -my $source = read_text($builder); -our ($body) = $source =~ /(sub\s+buildsources_genesis_base\s*\(\$\).*?\n\}\n)/s; -# die rather than skip: a rename that stops this matching must fail loudly instead of -# quietly covering nothing. -die("could not extract buildsources_genesis_base from buildrpms.pl") unless $body; - -# The staged directory the spec reads, as buildrpms.pl names it. Read back from the code so -# the test cannot disagree with it about the name. -my ($staged_dir) = $body =~ m{\bcp -a "([^/"]+)/dracut_105"}; -die('buildsources_genesis_base stages no dracut_105 directory') unless $staged_dir; -is($staged_dir, 'xCAT-genesis-base', 'the dracut assets are staged from xCAT-genesis-base'); -ok(-d repo_path($staged_dir), "$staged_dir is in the checkout"); - -{ - package Scratch; - use File::Copy qw(cp); - use File::Path qw(make_path remove_tree); - our ($SOURCES, $SOURCE_DATE_EPOCH); - sub sh_or_die { - my ($cmd, $message) = @_; - system($cmd) == 0 or die "$message\n"; - return 0; - } - eval $main::body; ## no critic - die $@ if $@; +sub members { + my ($tarball) = @_; + my @members = `TZ=UTC tar tjvf '$tarball'`; + die "tar cannot list $tarball\n" if $?; + chomp @members; + return @members; } -my $scratch = tempdir(CLEANUP => 1); +sub names { return sort map { (split ' ', $_)[-1] } @_ } + +# A scratch checkout in the layout the spec expects. my $checkout = "$scratch/checkout"; -make_path("$checkout/$staged_dir/dracut_105/el", "$checkout/$staged_dir/dracut_105/ubuntu"); -write_text("$checkout/$staged_dir/dracut_105/el/module-setup.sh", "el module\n"); -write_text("$checkout/$staged_dir/dracut_105/ubuntu/module-setup.sh", "ubuntu module\n"); -write_text("$checkout/$staged_dir/80-net-name-slot.rules", "rules\n"); +make_path("$checkout/xCAT-genesis-base/dracut_105/el", + "$checkout/xCAT-genesis-base/dracut_105/ubuntu"); +write_text("$checkout/xCAT-genesis-base/dracut_105/$_/$MODULE", "$_ module\n") for qw(el ubuntu); +write_text("$checkout/xCAT-genesis-base/80-net-name-slot.rules", "rules\n"); -$Scratch::SOURCES = "$scratch/SOURCES"; -$Scratch::SOURCE_DATE_EPOCH = 1700000000; -make_path($Scratch::SOURCES); +my $tarball = "$scratch/scratch.tar.bz2"; +is(stage_genesis_base_sources($checkout, $tarball, $EPOCH), $tarball, + 'the dracut assets are staged from xCAT-genesis-base'); -my $cwd = getcwd(); -chdir $checkout or die "chdir $checkout: $!"; -my $ok = eval { Scratch::buildsources_genesis_base('alma+epel-10-x86_64'); 1 }; -my $err = $@; -chdir $cwd or die "chdir back: $!"; -ok($ok, 'buildsources_genesis_base stages the Genesis build sources') or diag($err); +my @listing = members($tarball); +is_deeply([ names(@listing) ], [ + 'xCAT-genesis-base-build-support/', + 'xCAT-genesis-base-build-support/80-net-name-slot.rules', + 'xCAT-genesis-base-build-support/dracut_105/', + 'xCAT-genesis-base-build-support/dracut_105/el/', + "xCAT-genesis-base-build-support/dracut_105/el/$MODULE", + 'xCAT-genesis-base-build-support/dracut_105/ubuntu/', + "xCAT-genesis-base-build-support/dracut_105/ubuntu/$MODULE", + ], + 'the tarball holds the EL and Ubuntu dracut modules and 80-net-name-slot.rules'); +is_deeply([ grep { !m{ root/root .* 2023-11-14 22:13 } } @listing ], [], + 'every member is owned by root and dated SOURCE_DATE_EPOCH'); -my $tarball = "$Scratch::SOURCES/xCAT-genesis-base-build-support.tar.bz2"; -ok(-s $tarball, 'the build support tarball is written'); +# The checkout itself, so the directory the spec needs is known to be there. +my %shipped = map { $_ => 1 } + names(members(stage_genesis_base_sources(repo_path('.'), "$scratch/shipped.tar.bz2", $EPOCH))); +is_deeply([ grep { !$shipped{"xCAT-genesis-base-build-support/$_"} } + "dracut_105/el/$MODULE", "dracut_105/ubuntu/$MODULE", '80-net-name-slot.rules' ], [], + 'xCAT-genesis-base in the checkout carries both dracut modules and the rules file'); -my @members = split /\n/, (`tar tjf '$tarball' 2>/dev/null` // ''); -ok(scalar(grep { m{dracut_105/el/module-setup\.sh$} } @members), - 'the EL dracut module is in the tarball'); -ok(scalar(grep { m{dracut_105/ubuntu/module-setup\.sh$} } @members), - 'the Ubuntu dracut module is in the tarball'); -ok(scalar(grep { m{/80-net-name-slot\.rules$} } @members), - '80-net-name-slot.rules is in the tarball'); - -# A checkout without the directory must stop the build, not produce an empty tarball. -my $empty = "$scratch/empty"; -make_path($empty); -chdir $empty or die "chdir $empty: $!"; -my $died = !eval { Scratch::buildsources_genesis_base('alma+epel-10-x86_64'); 1 }; -my $message = $@; -chdir $cwd or die "chdir back: $!"; -ok($died, 'a checkout without the Genesis directory stops the build'); -like($message, qr/\Q$staged_dir\E/, 'the message names the directory it wants'); +# A checkout that still uses the old name must stop the build, not produce an empty tarball. +my $old = "$scratch/old"; +make_path("$old/xCAT-genesis-builder/dracut_105"); +my $empty_tarball = "$scratch/empty.tar.bz2"; +my $died = !eval { stage_genesis_base_sources($old, $empty_tarball, $EPOCH); 1 }; +ok($died, 'a checkout without xCAT-genesis-base stops the build'); +like($@, qr/No directory xCAT-genesis-base in \Q$old\E/, + 'the message names the directory it wants'); +ok(!-e $empty_tarball, 'and no tarball is written'); done_testing(); From 052d2d5fbe1c95442b7292554c7a5d220740516f Mon Sep 17 00:00:00 2001 From: Daniel Hilst <392820+dhilst@users.noreply.github.com> Date: Thu, 24 Sep 2026 16:42:45 -0300 Subject: [PATCH 11/17] fix(xcat-core): Genesis base tests from master read the old directory and evaluate shell Master added a workflow and several tests after this branch renamed xCAT-genesis-builder to xCAT-genesis-base. They still name files under xCAT-genesis-builder/, which no longer exists, so they fail or read nothing. The OpenEmbedded workflow, six bats files and four unit tests now use the new paths. Two of those unit tests could reach the code only as shell. genesis_ubuntu_build_root.t cut the package list out of builddeb-genesis-base and evaluated it through bash. genesis_payload_verification.t ran the bash verifier and parsed its stderr. XCAT::GenesisBuildRoot::required_packages() now returns the build-root package list, and builddeb-genesis-base calls it. XCAT::GenesisPayload holds the mandatory-command list and the payload check, and verify-genesis-payload runs its main() with the same arguments, exit codes and messages. stage_genesis_base_sources() stages the module beside the script. Both modules use core Perl only. The two tests now call these functions with chosen inputs and assert the exact results. genesis_base_sources_staged.t expects the module in the staged tarball. Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com> --- .github/workflows/genesis_openembedded.yml | 12 +- build-utils/lib/XCAT/BuildUtils.pm | 9 +- xCAT-genesis-base/builddeb-genesis-base | 32 +- .../lib/XCAT/GenesisBuildRoot.pm | 73 +++++ xCAT-genesis-base/lib/XCAT/GenesisPayload.pm | 196 ++++++++++++ xCAT-genesis-base/verify-genesis-payload | 116 +------ xCAT-test/bats/genesis_base_deb_arch.bats | 2 +- .../genesis_base_deb_control_rewrite.bats | 4 +- xCAT-test/bats/genesis_console_mode.bats | 4 +- xCAT-test/bats/genesis_dhcp_client.bats | 4 +- xCAT-test/bats/genesis_root_home.bats | 6 +- .../bats/go_xcat_genesis_package_names.bats | 2 +- xCAT-test/unit/genesis_base_sources_staged.t | 13 +- .../unit/genesis_base_spec_buildrequires.t | 2 +- xCAT-test/unit/genesis_payload_verification.t | 291 +++++++++--------- xCAT-test/unit/genesis_spec_target_arch.t | 2 +- xCAT-test/unit/genesis_ubuntu_build_root.t | 104 ++----- 17 files changed, 491 insertions(+), 381 deletions(-) create mode 100644 xCAT-genesis-base/lib/XCAT/GenesisBuildRoot.pm create mode 100644 xCAT-genesis-base/lib/XCAT/GenesisPayload.pm diff --git a/.github/workflows/genesis_openembedded.yml b/.github/workflows/genesis_openembedded.yml index b2ee14521..4d38f7131 100644 --- a/.github/workflows/genesis_openembedded.yml +++ b/.github/workflows/genesis_openembedded.yml @@ -2,7 +2,7 @@ name: genesis_openembedded on: pull_request: paths: - - 'xCAT-genesis-builder/oe/**' + - 'xCAT-genesis-base/oe/**' - 'xCAT-genesis-scripts/**' - '.github/workflows/genesis_openembedded.yml' workflow_dispatch: @@ -17,7 +17,7 @@ jobs: with: python-version: '3.12' cache: pip - cache-dependency-path: xCAT-genesis-builder/oe/requirements.txt + cache-dependency-path: xCAT-genesis-base/oe/requirements.txt - name: Install OpenEmbedded host dependencies run: >- sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y @@ -25,7 +25,7 @@ jobs: build-essential chrpath cpio diffstat file gawk git locales lz4 openssl socat texinfo unzip wget xz-utils zstd - name: Install KAS - run: python -m pip install -r xCAT-genesis-builder/oe/requirements.txt + run: python -m pip install -r xCAT-genesis-base/oe/requirements.txt - name: Enable BitBake user namespaces run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 - name: Validate every architecture configuration @@ -36,7 +36,7 @@ jobs: set -euo pipefail 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" + configuration="xCAT-genesis-base/oe/kas/${architecture}.yml" kas dump "${configuration}" >/dev/null kas shell "${configuration}" -c 'bitbake -p' done @@ -47,8 +47,8 @@ jobs: KAS_BUILD_DIR: ${{ runner.temp }}/kas-build-x86_64 run: | set -euo pipefail - kas shell xCAT-genesis-builder/oe/kas/x86_64.yml -c \ + kas shell xCAT-genesis-base/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 \ + kas shell xCAT-genesis-base/oe/kas/s390x.yml -c \ 'bitbake -n xcat-genesis-image xcat-genesis-extension-smoke' diff --git a/build-utils/lib/XCAT/BuildUtils.pm b/build-utils/lib/XCAT/BuildUtils.pm index 3e3bbcd52..db835ce5e 100644 --- a/build-utils/lib/XCAT/BuildUtils.pm +++ b/build-utils/lib/XCAT/BuildUtils.pm @@ -382,9 +382,9 @@ sub stage_probe_helpers { Descriptions: Write the build support tarball that xCAT-genesis-base.spec unpacks: - the dracut_105 modules, 80-net-name-slot.rules and - verify-genesis-payload, taken from the xCAT-genesis-base directory of - a checkout. + the dracut_105 modules, 80-net-name-slot.rules, and + verify-genesis-payload with its XCAT::GenesisPayload module, taken from + the xCAT-genesis-base directory of a checkout. Arguments: $checkout: the top of the xcat-core checkout $tarball: the path of the tarball to write @@ -414,6 +414,9 @@ sub stage_genesis_base_sources { # The spec runs the verifier against the extracted payload in %install. copy("$source/verify-genesis-payload", "$staging_root/verify-genesis-payload") or die "Unable to stage $source/verify-genesis-payload: $!\n"; + make_path("$staging_root/lib/XCAT"); + copy("$source/lib/XCAT/GenesisPayload.pm", "$staging_root/lib/XCAT/GenesisPayload.pm") + or die "Unable to stage $source/lib/XCAT/GenesisPayload.pm: $!\n"; unlink $tarball if -f $tarball; sh_or_die(qq(tar --sort=name --owner=0 --group=0 --mtime="\@$epoch" -cjf "$tarball" -C "$staging_parent" xCAT-genesis-base-build-support), diff --git a/xCAT-genesis-base/builddeb-genesis-base b/xCAT-genesis-base/builddeb-genesis-base index 5b9bc9141..8758aa7a1 100755 --- a/xCAT-genesis-base/builddeb-genesis-base +++ b/xCAT-genesis-base/builddeb-genesis-base @@ -37,36 +37,12 @@ echo "Building xcat-genesis-base for $BUILDARCH ($TARCH) on Ubuntu $CODENAME" export DEBIAN_FRONTEND=noninteractive -REQUIRED_PACKAGES=" - dracut linux-image-generic - ipmitool lldpad ethtool iproute2 kexec-tools screen - openssh-server openssh-client rsyslog chrony - nfs-common rpcbind pciutils usbutils parted - dosfstools e2fsprogs lvm2 mdadm net-tools - bc psmisc rsync wget cpio - isc-dhcp-client ifenslave - dpkg-dev debhelper fakeroot devscripts vim-tiny -" -if [ "$BUILDARCH" = "amd64" ]; then - REQUIRED_PACKAGES="$REQUIRED_PACKAGES dmidecode efibootmgr" -fi - -# Keep only the packages this release actually has. hwclock moved out of util-linux into -# util-linux-extra, so focal and jammy have no such package and naming it there fails the -# whole install. util-linux only Suggests it, and --no-install-recommends is passed, so the -# releases that did split it must name it. -optional_packages() { - local package - for package in "$@"; do - if apt-cache show "$package" >/dev/null 2>&1; then - echo "$package" - fi - done -} - echo "Installing build dependencies..." apt-get update -qq -REQUIRED_PACKAGES="$REQUIRED_PACKAGES $(optional_packages util-linux-extra)" + +# XCAT::GenesisBuildRoot holds the package list, so the unit tests can call it. +REQUIRED_PACKAGES=$(perl -I"$DIR/lib" -MXCAT::GenesisBuildRoot=required_packages \ + -e 'print "$_\n" for required_packages(@ARGV)' "$BUILDARCH" "$CODENAME") apt-get install -y --no-install-recommends $REQUIRED_PACKAGES # Set up dracut module diff --git a/xCAT-genesis-base/lib/XCAT/GenesisBuildRoot.pm b/xCAT-genesis-base/lib/XCAT/GenesisBuildRoot.pm new file mode 100644 index 000000000..afc7c9f62 --- /dev/null +++ b/xCAT-genesis-base/lib/XCAT/GenesisBuildRoot.pm @@ -0,0 +1,73 @@ +package XCAT::GenesisBuildRoot; + +# builddeb-genesis-base runs this module inside an Ubuntu build root, where only perl-base is +# installed. Use core modules only. +use strict; +use warnings; +use Exporter 'import'; + +our @EXPORT_OK = qw(required_packages apt_carries); + +my @BASE_PACKAGES = qw( + dracut linux-image-generic + ipmitool lldpad ethtool iproute2 kexec-tools screen + openssh-server openssh-client rsyslog chrony + nfs-common rpcbind pciutils usbutils parted + dosfstools e2fsprogs lvm2 mdadm net-tools + bc psmisc rsync wget cpio + isc-dhcp-client ifenslave + dpkg-dev debhelper fakeroot devscripts vim-tiny +); + +# hwclock moved out of util-linux into util-linux-extra. focal and jammy have no such package, +# and naming it there fails the whole install. util-linux only Suggests it, and the build +# passes --no-install-recommends, so the releases that split it must name it. +my @OPTIONAL_PACKAGES = qw(util-linux-extra); + +#------------------------------------------------------------------------------- + +=head3 apt_carries + + Descriptions: ask apt-cache whether the release carries a package. + Arguments: + $package: the package name + Returns: + 1 when apt-cache knows the package, 0 when it does not + +=cut + +#------------------------------------------------------------------------------- +sub apt_carries { + my ($package) = @_; + return system('sh', '-c', 'apt-cache show "$1" >/dev/null 2>&1', 'sh', $package) == 0 + ? 1 : 0; +} + +#------------------------------------------------------------------------------- + +=head3 required_packages + + Descriptions: list the packages the Genesis build root needs. + An optional package is listed only when the release carries it. + Arguments: + $arch: the dpkg architecture (amd64, ppc64el) + $codename: the release name + $carries: optional code ref. It takes a package name and returns true when the + release carries that package. The default is apt_carries. + Returns: + the package names, in install order. + +=cut + +#------------------------------------------------------------------------------- +sub required_packages { + my ($arch, $codename, $carries) = @_; + $carries ||= \&apt_carries; + + my @packages = @BASE_PACKAGES; + push @packages, qw(dmidecode efibootmgr) if $arch eq 'amd64'; + push @packages, grep { $carries->($_) } @OPTIONAL_PACKAGES; + return @packages; +} + +1; diff --git a/xCAT-genesis-base/lib/XCAT/GenesisPayload.pm b/xCAT-genesis-base/lib/XCAT/GenesisPayload.pm new file mode 100644 index 000000000..135bd925e --- /dev/null +++ b/xCAT-genesis-base/lib/XCAT/GenesisPayload.pm @@ -0,0 +1,196 @@ +package XCAT::GenesisPayload; + +# verify-genesis-payload runs this module in the rpm %install of xCAT-genesis-base and inside +# the Ubuntu build root, where only perl-base is installed. Use core modules only. +use strict; +use warnings; +use Exporter 'import'; + +our @EXPORT_OK = qw(module_commands missing_paths check_payload main); + +my $ME = 'verify-genesis-payload'; + +#------------------------------------------------------------------------------- + +=head3 module_commands + + Descriptions: read back the names a dracut module installs. + Only the top level of install() counts. A name under a condition is + release-dependent, so the caller names it as a required path instead. + An option to dracut_install (a word that starts with "-") is not a name. + Arguments: + $module_setup: the path of the module-setup.sh + Returns: + the names, sorted, each one once. A name that starts with "/" is an + absolute path; any other name is a command. + Dies with "verify-genesis-payload: cannot read " when the file + cannot be read, and with "verify-genesis-payload: no command name read + from " when install() names nothing. + +=cut + +#------------------------------------------------------------------------------- +sub module_commands { + my ($module_setup) = @_; + open(my $fh, '<', $module_setup) + or die "$ME: cannot read $module_setup\n"; + + my ($in_install, %names); + while (my $line = <$fh>) { + if ($line =~ /^install\(\)/) { $in_install = 1; next } + $in_install = 0 if $in_install && $line =~ /^}/; + next unless $in_install && $line =~ s/^ dracut_install //; + $line =~ s/#.*//s; + $names{$_} = 1 for grep { length && !/^-/ } split /\s+/, $line; + } + close($fh); + + die "$ME: no command name read from $module_setup\n" unless %names; + return sort keys %names; +} + +#------------------------------------------------------------------------------- + +=head3 missing_paths + + Descriptions: list what a Genesis payload lacks. + dracut_install reports a missing binary and returns, so the image can + ship without it. This check runs on the extracted payload before it is + packaged. + Arguments: + $have: code ref. It takes a path relative to the payload root and + returns true when the payload carries it. + %opt: + required: paths relative to the payload root that the caller needs + commands: names from module_commands. A bare command is looked + for in bin, sbin, usr/bin and usr/sbin; an absolute + path is looked for under the payload root. + source: the module the commands came from, for the message + sshd: the content of usr/sbin/sshd, or undef without one + Returns: + one " ()" string per missing item, in the order checked. + An empty list means the payload is complete. + +=cut + +#------------------------------------------------------------------------------- +sub missing_paths { + my ($have, %opt) = @_; + my @missing; + + for my $path (@{ $opt{required} || [] }) { + push @missing, "$path (required by the build)" unless $have->($path); + } + + for my $want (@{ $opt{commands} || [] }) { + my $found = + $want =~ m{^/(.*)} + ? $have->($1) + : scalar(grep { $have->("$_/$want") } qw(bin sbin usr/bin usr/sbin)); + push @missing, "$want (installed by $opt{source})" unless $found; + } + + push @missing, "usr/sbin/sshd (Genesis is reached over ssh)" + unless $have->('usr/sbin/sshd'); + push @missing, "usr/bin/mktemp (getdestiny makes its request file with it)" + unless $have->('usr/bin/mktemp'); + + # OpenSSH 9.8 split the per-connection work into sshd-session, which sshd execs by + # absolute path. EL9 carries OpenSSH 9.9, so an image with sshd alone refuses every + # connection. + if (index($opt{sshd} // '', 'sshd-session') >= 0 + && !$have->('usr/libexec/openssh/sshd-session') + && !$have->('usr/lib/openssh/sshd-session')) + { + push @missing, + "usr/libexec/openssh/sshd-session (this sshd execs it for every connection)"; + } + + # tmux exits under the C locale. The hook then runs doxcat directly, but a Genesis + # shell without tmux loses the console attach. + if ($have->('usr/bin/tmux') && !$have->('usr/lib/locale/C.utf8/LC_CTYPE')) { + push @missing, + "usr/lib/locale/C.utf8/LC_CTYPE (tmux refuses to start without a UTF-8 locale)"; + } + return @missing; +} + +#------------------------------------------------------------------------------- + +=head3 check_payload + + Descriptions: the command line of verify-genesis-payload, without the output: + [--commands-from ] [required-path ...] + Arguments: + @args: the command line + Returns: + ($status, $message). $status is the exit status: 0 when the payload is + complete, 1 when it lacks something, 2 on a usage error. $message is + the text to print, one line or a list of missing items. + +=cut + +#------------------------------------------------------------------------------- +sub check_payload { + my @args = @_; + my $commands_from = ''; + while (@args) { + if ($args[0] eq '--commands-from') { + shift @args; + $commands_from = shift(@args) // ''; + } elsif ($args[0] =~ /^--commands-from=(.*)/s) { + $commands_from = $1; + shift @args; + } else { + last; + } + } + + my $payload = shift(@args) // ''; + if ($payload eq '' || !-d $payload) { + return (2, "$ME: not a payload directory: " . ($payload eq '' ? '' : $payload) . "\n"); + } + + my @commands; + if ($commands_from ne '') { + @commands = eval { module_commands($commands_from) }; + return (2, $@) if $@; + } + + my $sshd; + if (open(my $fh, '<:raw', "$payload/usr/sbin/sshd")) { + local $/; + $sshd = <$fh> // ''; + close($fh); + } + my @missing = missing_paths(sub { -e "$payload/$_[0]" }, + required => \@args, + commands => \@commands, + source => $commands_from, + sshd => $sshd); + return (1, "$ME: $payload is incomplete:" . join('', map { "\n $_" } @missing) . "\n") + if @missing; + return (0, "$ME: $payload is complete\n"); +} + +#------------------------------------------------------------------------------- + +=head3 main + + Descriptions: run check_payload and print its message: on STDOUT when the + payload is complete, on STDERR otherwise. + Arguments: + @args: the command line + Returns: + the exit status from check_payload + +=cut + +#------------------------------------------------------------------------------- +sub main { + my ($status, $message) = check_payload(@_); + print { $status ? *STDERR : *STDOUT } $message; + return $status; +} + +1; diff --git a/xCAT-genesis-base/verify-genesis-payload b/xCAT-genesis-base/verify-genesis-payload index 1f44898c1..d5d0369b0 100755 --- a/xCAT-genesis-base/verify-genesis-payload +++ b/xCAT-genesis-base/verify-genesis-payload @@ -6,115 +6,9 @@ # going and the image ships without it. Check the extracted payload before it is packaged. # # Paths given on the command line are relative to . --commands-from reads back -# what the dracut module installs: a bare command name is looked for in the four binary -# directories, an absolute path under itself. The caller adds what only it -# knows (the DHCP client is not the same package on every release); the rules below come from -# the payload itself. +# what the dracut module installs. XCAT::GenesisPayload, in lib/ beside this script, holds +# the rules, so the unit tests call the same code. +# +# Exit status: 0 complete, 1 something missing, 2 usage error. -set -u - -commands_from="" -while [ $# -gt 0 ]; do - case "$1" in - --commands-from) - commands_from=${2:-} - shift 2 || true - ;; - --commands-from=*) - commands_from=${1#*=} - shift - ;; - *) - break - ;; - esac -done - -payload=${1:-} -if [ -z "$payload" ] || [ ! -d "$payload" ]; then - echo "verify-genesis-payload: not a payload directory: ${payload:-}" >&2 - exit 2 -fi -shift - -missing="" - -# have PATH: true when the payload carries PATH as a file, following the usr-merge symlinks -# the image ships (/sbin -> usr/sbin). -have() { - [ -e "$payload/$1" ] -} - -require() { - local path=$1 why=$2 - have "$path" || missing="$missing - $path ($why)" -} - -for path in "$@"; do - require "$path" "required by the build" -done - -# The dracut module names every command and every data file Genesis needs. A name the build -# root does not supply installs nothing and says nothing, so read the names back and check -# each one. Names under a condition are release-dependent, so only the top level of install() -# counts. -if [ -n "$commands_from" ]; then - if [ ! -r "$commands_from" ]; then - echo "verify-genesis-payload: cannot read $commands_from" >&2 - exit 2 - fi - commands=$(awk ' - /^install\(\)/ { in_install = 1; next } - in_install && /^}/ { in_install = 0 } - in_install && /^ dracut_install / { - sub(/#.*/, "") - sub(/^ dracut_install /, "") - print - }' "$commands_from" | tr ' \t' '\n\n' | grep -v '^$' | grep -v '^-' | sort -u) - if [ -z "$commands" ]; then - echo "verify-genesis-payload: no command name read from $commands_from" >&2 - exit 2 - fi - for want in $commands; do - case "$want" in - # dracut_install installs an absolute path at that same path, so read it back - # under the payload root. Dropping these let an image with no /usr/bin/awk pass. - /*) have "${want#/}" || missing="$missing - $want (installed by $commands_from)" - ;; - *) have "bin/$want" || have "sbin/$want" \ - || have "usr/bin/$want" || have "usr/sbin/$want" \ - || missing="$missing - $want (installed by $commands_from)" - ;; - esac - done -fi - -require usr/sbin/sshd "Genesis is reached over ssh" -require usr/bin/mktemp "getdestiny makes its request file with it" - -# OpenSSH 9.8 split the per-connection work into sshd-session, which sshd execs by absolute -# path. EL9 carries OpenSSH 9.9, so an image with sshd alone refuses every connection. -if have usr/sbin/sshd && grep -qa 'sshd-session' "$payload/usr/sbin/sshd" 2>/dev/null; then - if ! have usr/libexec/openssh/sshd-session && ! have usr/lib/openssh/sshd-session; then - missing="$missing - usr/libexec/openssh/sshd-session (this sshd execs it for every connection)" - fi -fi - -# tmux exits under the C locale. The hook falls back to running doxcat directly, so this is -# not fatal to booting, but a Genesis shell without tmux loses the console attach. -if have usr/bin/tmux && ! have usr/lib/locale/C.utf8/LC_CTYPE; then - missing="$missing - usr/lib/locale/C.utf8/LC_CTYPE (tmux refuses to start without a UTF-8 locale)" -fi - -if [ -n "$missing" ]; then - echo "verify-genesis-payload: $payload is incomplete:$missing" >&2 - exit 1 -fi - -echo "verify-genesis-payload: $payload is complete" -exit 0 +exec perl -I"$(dirname "$0")/lib" -MXCAT::GenesisPayload=main -e 'exit main(@ARGV)' -- "$@" diff --git a/xCAT-test/bats/genesis_base_deb_arch.bats b/xCAT-test/bats/genesis_base_deb_arch.bats index addf31146..b681dec28 100644 --- a/xCAT-test/bats/genesis_base_deb_arch.bats +++ b/xCAT-test/bats/genesis_base_deb_arch.bats @@ -12,7 +12,7 @@ load 'helpers/shell_source' setup() { - SCRIPT="$(repo_path 'xCAT-genesis-builder/debuild-xcat-genesis-base')" + SCRIPT="$(repo_path 'xCAT-genesis-base/debuild-xcat-genesis-base')" # Fail rather than skip: a checkout without the converter has no deb rename to measure, # and a skip there covers nothing while reading green. [ -r "$SCRIPT" ] diff --git a/xCAT-test/bats/genesis_base_deb_control_rewrite.bats b/xCAT-test/bats/genesis_base_deb_control_rewrite.bats index 188671c67..1d18359e5 100644 --- a/xCAT-test/bats/genesis_base_deb_control_rewrite.bats +++ b/xCAT-test/bats/genesis_base_deb_control_rewrite.bats @@ -13,8 +13,8 @@ load 'helpers/shell_source' setup() { - SCRIPT="$(repo_path 'xCAT-genesis-builder/builddeb-genesis-base')" - CONTROL="$(repo_path 'xCAT-genesis-builder/debian/control')" + SCRIPT="$(repo_path 'xCAT-genesis-base/builddeb-genesis-base')" + CONTROL="$(repo_path 'xCAT-genesis-base/debian/control')" [ -r "$SCRIPT" ] || skip "$SCRIPT is required" [ -r "$CONTROL" ] || skip "$CONTROL is required" export SCRIPT CONTROL diff --git a/xCAT-test/bats/genesis_console_mode.bats b/xCAT-test/bats/genesis_console_mode.bats index 98d36f95d..0473235b9 100644 --- a/xCAT-test/bats/genesis_console_mode.bats +++ b/xCAT-test/bats/genesis_console_mode.bats @@ -9,8 +9,8 @@ load 'helpers/shell_source' setup() { - EL_HOOK="$(repo_path 'xCAT-genesis-builder/dracut_105/el/xcat-cmdline.sh')" - UBUNTU_HOOK="$(repo_path 'xCAT-genesis-builder/dracut_105/ubuntu/xcat-cmdline.sh')" + EL_HOOK="$(repo_path 'xCAT-genesis-base/dracut_105/el/xcat-cmdline.sh')" + UBUNTU_HOOK="$(repo_path 'xCAT-genesis-base/dracut_105/ubuntu/xcat-cmdline.sh')" [ -r "$EL_HOOK" ] || skip "$EL_HOOK is required" [ -r "$UBUNTU_HOOK" ] || skip "$UBUNTU_HOOK is required" export EL_HOOK UBUNTU_HOOK diff --git a/xCAT-test/bats/genesis_dhcp_client.bats b/xCAT-test/bats/genesis_dhcp_client.bats index f45ec2cca..2b4233a93 100644 --- a/xCAT-test/bats/genesis_dhcp_client.bats +++ b/xCAT-test/bats/genesis_dhcp_client.bats @@ -14,8 +14,8 @@ ISC6='dhclient -6 -pf /var/run/dhclient6.eth0.pid eth0 -lf /var/lib/dhclient/dhc setup() { DOXCAT="$(repo_path 'xCAT-genesis-scripts/usr/bin/doxcat')" - SPEC="$(repo_path 'xCAT-genesis-builder/xCAT-genesis-base.spec')" - MODULE="$(repo_path 'xCAT-genesis-builder/dracut_105/el/module-setup.sh')" + SPEC="$(repo_path 'xCAT-genesis-base/xCAT-genesis-base.spec')" + MODULE="$(repo_path 'xCAT-genesis-base/dracut_105/el/module-setup.sh')" [ -r "$DOXCAT" ] || skip "$DOXCAT is required" [ -r "$SPEC" ] || skip "$SPEC is required" [ -r "$MODULE" ] || skip "$MODULE is required" diff --git a/xCAT-test/bats/genesis_root_home.bats b/xCAT-test/bats/genesis_root_home.bats index cab5e780d..71a266e3f 100644 --- a/xCAT-test/bats/genesis_root_home.bats +++ b/xCAT-test/bats/genesis_root_home.bats @@ -77,13 +77,13 @@ assert_hook() } @test "the legacy hook gives root the home directory /" { - assert_hook 'xCAT-genesis-builder/xcat-cmdline.sh' + assert_hook 'xCAT-genesis-base/xcat-cmdline.sh' } @test "the el dracut 105 hook gives root the home directory /" { - assert_hook 'xCAT-genesis-builder/dracut_105/el/xcat-cmdline.sh' + assert_hook 'xCAT-genesis-base/dracut_105/el/xcat-cmdline.sh' } @test "the ubuntu dracut 105 hook gives root the home directory /" { - assert_hook 'xCAT-genesis-builder/dracut_105/ubuntu/xcat-cmdline.sh' + assert_hook 'xCAT-genesis-base/dracut_105/ubuntu/xcat-cmdline.sh' } diff --git a/xCAT-test/bats/go_xcat_genesis_package_names.bats b/xCAT-test/bats/go_xcat_genesis_package_names.bats index 871ae884b..68913d0c2 100644 --- a/xCAT-test/bats/go_xcat_genesis_package_names.bats +++ b/xCAT-test/bats/go_xcat_genesis_package_names.bats @@ -15,7 +15,7 @@ setup() { go_xcat_require_source SCRIPTS_DEBIAN="$(repo_path 'xCAT-genesis-scripts/debian')" - SPEC="$(repo_path 'xCAT-genesis-builder/xCAT-genesis-base.spec')" + SPEC="$(repo_path 'xCAT-genesis-base/xCAT-genesis-base.spec')" [ -d "$SCRIPTS_DEBIAN" ] || skip "$SCRIPTS_DEBIAN is required" [ -r "$SPEC" ] || skip "$SPEC is required" export SCRIPTS_DEBIAN SPEC diff --git a/xCAT-test/unit/genesis_base_sources_staged.t b/xCAT-test/unit/genesis_base_sources_staged.t index 96325c10f..f6f3d13a0 100644 --- a/xCAT-test/unit/genesis_base_sources_staged.t +++ b/xCAT-test/unit/genesis_base_sources_staged.t @@ -1,7 +1,7 @@ #!/usr/bin/env perl # xCAT-genesis-base.spec builds the Genesis image from a tarball that buildrpms.pl stages: -# the dracut_105 modules, 80-net-name-slot.rules and verify-genesis-payload. Renaming the -# directory they live in breaks that staging silently. +# the dracut_105 modules, 80-net-name-slot.rules, and verify-genesis-payload with its module. +# Renaming the directory they live in breaks that staging silently. use strict; use warnings; @@ -37,6 +37,8 @@ make_path("$checkout/xCAT-genesis-base/dracut_105/el", write_text("$checkout/xCAT-genesis-base/dracut_105/$_/$MODULE", "$_ module\n") for qw(el ubuntu); write_text("$checkout/xCAT-genesis-base/80-net-name-slot.rules", "rules\n"); write_text("$checkout/xCAT-genesis-base/verify-genesis-payload", "verifier\n"); +make_path("$checkout/xCAT-genesis-base/lib/XCAT"); +write_text("$checkout/xCAT-genesis-base/lib/XCAT/GenesisPayload.pm", "1;\n"); my $tarball = "$scratch/scratch.tar.bz2"; is(stage_genesis_base_sources($checkout, $tarball, $EPOCH), $tarball, @@ -51,9 +53,12 @@ is_deeply([ names(@listing) ], [ "xCAT-genesis-base-build-support/dracut_105/el/$MODULE", 'xCAT-genesis-base-build-support/dracut_105/ubuntu/', "xCAT-genesis-base-build-support/dracut_105/ubuntu/$MODULE", + 'xCAT-genesis-base-build-support/lib/', + 'xCAT-genesis-base-build-support/lib/XCAT/', + 'xCAT-genesis-base-build-support/lib/XCAT/GenesisPayload.pm', 'xCAT-genesis-base-build-support/verify-genesis-payload', ], - 'the tarball holds the dracut modules, the rules file and the payload verifier'); + 'the tarball holds the dracut modules, the rules file and the payload verifier with its module'); is_deeply([ grep { !m{ root/root .* 2023-11-14 22:13 } } @listing ], [], 'every member is owned by root and dated SOURCE_DATE_EPOCH'); @@ -62,7 +67,7 @@ my %shipped = map { $_ => 1 } names(members(stage_genesis_base_sources(repo_path('.'), "$scratch/shipped.tar.bz2", $EPOCH))); is_deeply([ grep { !$shipped{"xCAT-genesis-base-build-support/$_"} } "dracut_105/el/$MODULE", "dracut_105/ubuntu/$MODULE", '80-net-name-slot.rules', - 'verify-genesis-payload' ], [], + 'verify-genesis-payload', 'lib/XCAT/GenesisPayload.pm' ], [], 'xCAT-genesis-base in the checkout carries the dracut modules, the rules file and the verifier'); # A checkout that still uses the old name must stop the build, not produce an empty tarball. diff --git a/xCAT-test/unit/genesis_base_spec_buildrequires.t b/xCAT-test/unit/genesis_base_spec_buildrequires.t index 621f3a001..fa4fa66c0 100644 --- a/xCAT-test/unit/genesis_base_spec_buildrequires.t +++ b/xCAT-test/unit/genesis_base_spec_buildrequires.t @@ -10,7 +10,7 @@ use Test::More; use XCAT::Test::File qw(repo_path slurp_repo_file); -my $relative = 'xCAT-genesis-builder/xCAT-genesis-base.spec'; +my $relative = 'xCAT-genesis-base/xCAT-genesis-base.spec'; plan skip_all => "$relative not found" unless -f repo_path($relative); plan tests => 4; diff --git a/xCAT-test/unit/genesis_payload_verification.t b/xCAT-test/unit/genesis_payload_verification.t index d1334b24b..2d8ee7b6c 100644 --- a/xCAT-test/unit/genesis_payload_verification.t +++ b/xCAT-test/unit/genesis_payload_verification.t @@ -1,186 +1,185 @@ #!/usr/bin/env perl -# Drive verify-genesis-payload against payload trees that each leave out one thing the image -# needs. +# XCAT::GenesisPayload decides whether an extracted Genesis payload is complete. +# verify-genesis-payload calls it in the EL spec and in the Ubuntu builder. Each payload +# below leaves out one thing the image needs. use strict; use warnings; +use File::Basename qw(dirname); use File::Path qw(make_path); -use File::Slurper qw(read_text write_text); +use File::Slurper qw(write_text); use File::Temp qw(tempdir); use FindBin; use lib "$FindBin::Bin/../lib"; +use lib "$FindBin::Bin/../../xCAT-genesis-base/lib"; use Test::More; -use XCAT::Test::File qw(repo_path); +use XCAT::GenesisPayload qw(module_commands missing_paths check_payload); -my $verifier = repo_path('xCAT-genesis-builder/verify-genesis-payload'); -plan skip_all => 'verify-genesis-payload not found' unless -f $verifier; -plan tests => 22; - -my $tmpdir = tempdir(CLEANUP => 1); -my $module_seq = 0; +my $OPENSSH_99 = "OpenSSH_9.9p1\n/usr/libexec/openssh/sshd-session\n"; +my $OPENSSH_80 = "OpenSSH_8.0p1\n"; # A complete payload: OpenSSH 9.9 sshd plus its session helper, tmux plus a UTF-8 locale. -my $good = build_payload(sshd_execs_session => 1, session_helper => 1, tmux => 1, locale => 1, dhclient => 1, mktemp => 1); -my ($rc, $err) = run($good, 'usr/sbin/dhclient'); -is($rc, 0, 'a complete payload passes') or diag($err); +my @COMPLETE = qw( + usr/sbin/sshd usr/libexec/openssh/sshd-session usr/bin/tmux + usr/lib/locale/C.utf8/LC_CTYPE usr/sbin/dhclient usr/bin/mktemp + usr/bin/awk etc/services usr/bin/openssl usr/bin/wget usr/bin/tar +); + +# The payload carries exactly the paths it is given. +sub carries { + my %present = map { $_ => 1 } @_; + return sub { $present{ $_[0] } }; +} +sub all_but { my %gone = map { $_ => 1 } @_; return carries(grep { !$gone{$_} } @COMPLETE) } + +is_deeply([ missing_paths(carries(@COMPLETE), sshd => $OPENSSH_99, required => ['usr/sbin/dhclient']) ], + [], 'a complete payload passes'); # doxcat calls dhclient with ISC flags. dhclient.conf and dhclient-script are not enough. -my $nodhcp = build_payload(sshd_execs_session => 1, session_helper => 1, tmux => 1, locale => 1, dhclient => 0, mktemp => 1); -($rc, $err) = run($nodhcp, 'usr/sbin/dhclient'); -isnt($rc, 0, 'a payload without dhclient fails'); -like($err, qr{usr/sbin/dhclient}, 'the missing dhclient is named'); +is_deeply([ missing_paths(all_but('usr/sbin/dhclient'), sshd => $OPENSSH_99, required => ['usr/sbin/dhclient']) ], + ['usr/sbin/dhclient (required by the build)'], + 'a payload without dhclient fails and names it'); # sshd 9.9 execs /usr/libexec/openssh/sshd-session for every connection. -my $nohelper = build_payload(sshd_execs_session => 1, session_helper => 0, tmux => 1, locale => 1, dhclient => 1, mktemp => 1); -($rc, $err) = run($nohelper, 'usr/sbin/dhclient'); -isnt($rc, 0, 'a payload whose sshd execs sshd-session but does not ship it fails'); -like($err, qr{sshd-session}, 'the missing sshd-session is named'); - -# OpenSSH 8 does not use the helper, so el8 must still pass without it. -my $openssh8 = build_payload(sshd_execs_session => 0, session_helper => 0, tmux => 1, locale => 1, dhclient => 1, mktemp => 1); -($rc, $err) = run($openssh8, 'usr/sbin/dhclient'); -is($rc, 0, 'an OpenSSH 8 payload passes without sshd-session') or diag($err); +is_deeply([ missing_paths(all_but('usr/libexec/openssh/sshd-session'), sshd => $OPENSSH_99) ], + ['usr/libexec/openssh/sshd-session (this sshd execs it for every connection)'], + 'a payload whose sshd execs sshd-session but does not ship it fails'); # tmux without a UTF-8 locale is what stopped doxcat from ever running. -my $nolocale = build_payload(sshd_execs_session => 1, session_helper => 1, tmux => 1, locale => 0, dhclient => 1, mktemp => 1); -($rc, $err) = run($nolocale, 'usr/sbin/dhclient'); -isnt($rc, 0, 'a payload with tmux and no UTF-8 locale fails'); -like($err, qr{C\.utf8}, 'the missing locale is named'); +is_deeply([ missing_paths(all_but('usr/lib/locale/C.utf8/LC_CTYPE'), sshd => $OPENSSH_99) ], + ['usr/lib/locale/C.utf8/LC_CTYPE (tmux refuses to start without a UTF-8 locale)'], + 'a payload with tmux and no UTF-8 locale fails'); # getdestiny makes its request file with mktemp. -my $nomktemp = build_payload(sshd_execs_session => 1, session_helper => 1, tmux => 1, locale => 1, dhclient => 1, mktemp => 0); -($rc, $err) = run($nomktemp, 'usr/sbin/dhclient'); -isnt($rc, 0, 'a payload without mktemp fails'); -like($err, qr{usr/bin/mktemp}, 'the missing mktemp is named'); +is_deeply([ missing_paths(all_but('usr/bin/mktemp'), sshd => $OPENSSH_99) ], + ['usr/bin/mktemp (getdestiny makes its request file with it)'], + 'a payload without mktemp fails'); + +# Genesis is reached over ssh. +is_deeply([ missing_paths(all_but('usr/sbin/sshd')) ], + ['usr/sbin/sshd (Genesis is reached over ssh)'], + 'a payload without sshd fails'); + +is_deeply([ missing_paths(carries(grep({ $_ ne 'usr/libexec/openssh/sshd-session' } @COMPLETE), + 'usr/lib/openssh/sshd-session'), sshd => $OPENSSH_99) ], + [], 'the Debian path of sshd-session counts'); + +# OpenSSH 8 does not use the helper, so el8 must still pass without it. +is_deeply([ missing_paths(all_but('usr/libexec/openssh/sshd-session'), sshd => $OPENSSH_80) ], + [], 'an OpenSSH 8 payload passes without sshd-session'); + +is_deeply([ missing_paths(all_but('usr/lib/locale/C.utf8/LC_CTYPE', 'usr/bin/tmux'), sshd => $OPENSSH_99) ], + [], 'a payload without tmux needs no locale'); # dracut_install reports a missing binary and returns, so every name the dracut module -# installs has to be checked against the payload. -my $module = write_module_setup([qw(openssl wget tar)]); -my $full = build_payload(sshd_execs_session => 1, session_helper => 1, tmux => 1, locale => 1, - dhclient => 1, mktemp => 1, commands => [qw(openssl wget tar)]); -($rc, $err) = run_with_commands($module, $full); -is($rc, 0, 'a payload carrying every command the module names passes') or diag($err); +# installs has to be checked against the payload. A name starting with "/" is installed at +# that same path; the rest are commands. +my $tmpdir = tempdir(CLEANUP => 1); +my $module = "$tmpdir/module-setup.sh"; +write_text($module, <<'SH'); +#!/bin/bash -my $noopenssl = build_payload(sshd_execs_session => 1, session_helper => 1, tmux => 1, locale => 1, - dhclient => 1, mktemp => 1, commands => [qw(wget tar)]); -($rc, $err) = run_with_commands($module, $noopenssl); -isnt($rc, 0, 'a payload without openssl fails'); -like($err, qr/openssl/, 'the missing openssl is named'); +install() { + dracut_install -o openssl wget tar # a trailing comment + dracut_install /usr/bin/awk /etc/services + if command -v dhclient >/dev/null 2>&1; then + dracut_install dhclient + fi +} -# dracut_install installs an absolute path at that same path, so a name starting with "/" is a -# command the payload must carry. doxcat, getdestiny and the firmware wrappers all run awk. -my $noawk = build_payload(sshd_execs_session => 1, session_helper => 1, tmux => 1, locale => 1, - dhclient => 1, mktemp => 1, commands => [qw(openssl wget tar)], absent => ['usr/bin/awk']); -($rc, $err) = run_with_commands($module, $noawk); -isnt($rc, 0, 'a payload without the absolute path /usr/bin/awk fails'); -like($err, qr{/usr/bin/awk}, 'the missing /usr/bin/awk is named'); - -# The module names data files by absolute path too. Genesis resolves service names with -# /etc/services. -my $noservices = build_payload(sshd_execs_session => 1, session_helper => 1, tmux => 1, locale => 1, - dhclient => 1, mktemp => 1, commands => [qw(openssl wget tar)], absent => ['etc/services']); -($rc, $err) = run_with_commands($module, $noservices); -isnt($rc, 0, 'a payload without the absolute path /etc/services fails'); -like($err, qr{/etc/services}, 'the missing /etc/services is named'); +installkernel() { + dracut_install notacommand +} +SH +my @commands = module_commands($module); # The DHCP client is release-dependent, so the module installs it inside a conditional. Those # names are not the contract; the spec passes the one it wants as a required path. -my $conditional = write_module_setup(['wget'], ['dhclient']); -my $nodhclient = build_payload(sshd_execs_session => 1, session_helper => 1, tmux => 1, locale => 1, - dhclient => 0, mktemp => 1, commands => ['wget']); -($rc, $err) = run_with_commands($conditional, $nodhclient); -is($rc, 0, 'a name installed under a condition is not required') or diag($err); +is_deeply(\@commands, [qw(/etc/services /usr/bin/awk openssl tar wget)], + 'the top-level names of install() are read back, without options, comments, conditionals + or other functions'); -# A module the verifier cannot read names for covers nothing, so say so instead of passing. +my %names = (commands => \@commands, source => $module, sshd => $OPENSSH_99); +is_deeply([ missing_paths(carries(@COMPLETE), %names) ], [], + 'a payload carrying every command the module names passes'); +is_deeply([ missing_paths(carries(grep({ $_ ne 'usr/bin/wget' } @COMPLETE), 'sbin/wget'), %names) ], + [], 'a command under sbin counts as present'); + +is_deeply([ missing_paths(all_but('usr/bin/openssl'), %names) ], + ["openssl (installed by $module)"], + 'a payload without openssl fails and names it'); + +# doxcat, getdestiny and the firmware wrappers all run awk. +is_deeply([ missing_paths(all_but('usr/bin/awk'), %names) ], + ["/usr/bin/awk (installed by $module)"], + 'a payload without the absolute path /usr/bin/awk fails'); + +# Genesis resolves service names with /etc/services. +is_deeply([ missing_paths(all_but('etc/services'), %names) ], + ["/etc/services (installed by $module)"], + 'a payload without the absolute path /etc/services fails'); + +# A module the verifier cannot read names from covers nothing, so say so instead of passing. my $unparsable = "$tmpdir/module-setup-unparsable.sh"; write_text($unparsable, "#!/bin/bash\nsetup() {\n dracut_install wget\n}\n"); -($rc, $err) = run_with_commands($unparsable, $full); -is($rc, 2, 'a module the verifier finds no command names in is a usage error'); -like($err, qr/command name/, 'the empty command list is named'); +ok(!eval { module_commands($unparsable); 1 }, 'a module with no install() names is refused'); +is($@, "verify-genesis-payload: no command name read from $unparsable\n", + 'the empty command list is named'); -($rc, $err) = run_with_commands("$tmpdir/no-such-module", $full); -is($rc, 2, 'a module file that cannot be read is a usage error'); +ok(!eval { module_commands("$tmpdir/no-such-module"); 1 }, 'an unreadable module is refused'); +is($@, "verify-genesis-payload: cannot read $tmpdir/no-such-module\n", + 'the unreadable module is named'); -($rc, $err) = run("$tmpdir/does-not-exist"); -is($rc >> 0, 2, 'a missing payload directory is a usage error'); +# --- the command line reads a real payload tree --------------------------------------------- +my $good = payload_tree(@COMPLETE); +write_text("$good/usr/sbin/sshd", $OPENSSH_99); +my $nodhcp = payload_tree(grep { $_ ne 'usr/sbin/dhclient' } @COMPLETE); +my $nohelper = payload_tree(grep { $_ ne 'usr/libexec/openssh/sshd-session' } @COMPLETE); +write_text("$nohelper/usr/sbin/sshd", $OPENSSH_99); + +is_deeply([ check_payload($good, 'usr/sbin/dhclient') ], + [ 0, "verify-genesis-payload: $good is complete\n" ], + 'a complete payload exits 0 and says so'); +is_deeply([ check_payload($nodhcp, 'usr/sbin/dhclient') ], + [ 1, "verify-genesis-payload: $nodhcp is incomplete:\n" + . " usr/sbin/dhclient (required by the build)\n" ], + 'an incomplete payload exits 1 and lists what is missing'); +is_deeply([ check_payload($nohelper) ], + [ 1, "verify-genesis-payload: $nohelper is incomplete:\n" + . " usr/libexec/openssh/sshd-session (this sshd execs it for every connection)\n" ], + 'the command line reads usr/sbin/sshd to decide on sshd-session'); +is_deeply([ check_payload('--commands-from', $module, $nodhcp) ], + [ 0, "verify-genesis-payload: $nodhcp is complete\n" ], + '--commands-from does not require a name installed under a condition'); +my $noopenssl = payload_tree(grep { $_ ne 'usr/bin/openssl' } @COMPLETE); +is_deeply([ check_payload("--commands-from=$module", $noopenssl) ], + [ 1, "verify-genesis-payload: $noopenssl is incomplete:\n" + . " openssl (installed by $module)\n" ], + '--commands-from= checks the names of the module'); +is_deeply([ check_payload('--commands-from', $unparsable, $good) ], + [ 2, "verify-genesis-payload: no command name read from $unparsable\n" ], + 'a module with no command names is a usage error'); +is_deeply([ check_payload('--commands-from', "$tmpdir/no-such-module", $good) ], + [ 2, "verify-genesis-payload: cannot read $tmpdir/no-such-module\n" ], + 'a module file that cannot be read is a usage error'); +is_deeply([ check_payload("$tmpdir/does-not-exist") ], + [ 2, "verify-genesis-payload: not a payload directory: $tmpdir/does-not-exist\n" ], + 'a missing payload directory is a usage error'); +is_deeply([ check_payload() ], + [ 2, "verify-genesis-payload: not a payload directory: \n" ], + 'no payload directory at all is a usage error'); + +done_testing(); #--- -# build_payload: make a payload tree with the pieces the verifier reasons about. +# payload_tree: a payload directory that carries the given paths as empty files. #--- -sub build_payload { - my (%opt) = @_; +sub payload_tree { my $root = tempdir(DIR => $tmpdir, CLEANUP => 1); - make_path("$root/usr/sbin", "$root/usr/bin", "$root/usr/libexec/openssh"); - write_text("$root/usr/sbin/sshd", - $opt{sshd_execs_session} - ? "OpenSSH_9.9p1\n/usr/libexec/openssh/sshd-session\n" - : "OpenSSH_8.0p1\n"); - write_text("$root/usr/libexec/openssh/sshd-session", "helper\n") if $opt{session_helper}; - write_text("$root/usr/bin/tmux", "tmux\n") if $opt{tmux}; - if ($opt{locale}) { - make_path("$root/usr/lib/locale/C.utf8"); - write_text("$root/usr/lib/locale/C.utf8/LC_CTYPE", "ctype\n"); - } - write_text("$root/usr/sbin/dhclient", "dhclient\n") if $opt{dhclient}; - write_text("$root/usr/bin/mktemp", "mktemp\n") if $opt{mktemp}; - write_text("$root/usr/bin/$_", "$_\n") for @{ $opt{commands} || [] }; - - # The module written by write_module_setup names these two by absolute path. - my %absent = map { $_ => 1 } @{ $opt{absent} || [] }; - for my $path (qw(usr/bin/awk etc/services)) { - next if $absent{$path}; - my ($dir) = $path =~ m{^(.*)/}; - make_path("$root/$dir"); - write_text("$root/$path", "$path\n"); + for my $path (@_) { + make_path(dirname("$root/$path")); + write_text("$root/$path", ''); } return $root; } - -#--- -# run: run the verifier and return its exit status and stderr. -#--- -sub run { - my ($root, @required) = @_; - my $errfile = "$tmpdir/err.$$"; - my $cmd = join ' ', map { "'$_'" } ($verifier, $root, @required); - system("/bin/bash $cmd >/dev/null 2>$errfile"); - my $status = $? >> 8; - my $err = -f $errfile ? read_text($errfile) : ''; - unlink $errfile; - return ($status, $err); -} - -#--- -# write_module_setup: a dracut module whose install() names commands at the top level, and -# optionally more inside a conditional. -#--- -sub write_module_setup { - my ($top, $conditional) = @_; - my $path = "$tmpdir/module-setup." . ++$module_seq . ".sh"; - my $text = "#!/bin/bash\n\ninstall() {\n"; - $text .= " dracut_install " . join(' ', @$top) . " # a trailing comment\n"; - $text .= " dracut_install /usr/bin/awk /etc/services\n"; - if ($conditional) { - $text .= " if command -v " . $conditional->[0] . " >/dev/null 2>&1; then\n"; - $text .= " dracut_install " . join(' ', @$conditional) . "\n"; - $text .= " fi\n"; - } - $text .= "}\n"; - write_text($path, $text); - return $path; -} - -#--- -# run_with_commands: run the verifier with the command list read back from a dracut module. -#--- -sub run_with_commands { - my ($module, $root) = @_; - my $errfile = "$tmpdir/err.commands.$$"; - my $cmd = join ' ', map { "'$_'" } ($verifier, '--commands-from', $module, $root); - system("/bin/bash $cmd >/dev/null 2>$errfile"); - my $status = $? >> 8; - my $err = -f $errfile ? read_text($errfile) : ''; - unlink $errfile; - return ($status, $err); -} diff --git a/xCAT-test/unit/genesis_spec_target_arch.t b/xCAT-test/unit/genesis_spec_target_arch.t index fa69c4c19..41c880079 100644 --- a/xCAT-test/unit/genesis_spec_target_arch.t +++ b/xCAT-test/unit/genesis_spec_target_arch.t @@ -29,7 +29,7 @@ my %tarch = ( my %spec = ( 'xCAT-genesis-scripts' => "$root/xCAT-genesis-scripts/xCAT-genesis-scripts.spec", - 'xCAT-genesis-base' => "$root/xCAT-genesis-builder/xCAT-genesis-base.spec", + 'xCAT-genesis-base' => "$root/xCAT-genesis-base/xCAT-genesis-base.spec", ); for my $pkg (sort keys %spec) { diff --git a/xCAT-test/unit/genesis_ubuntu_build_root.t b/xCAT-test/unit/genesis_ubuntu_build_root.t index fb598ccd9..3e706b2f7 100755 --- a/xCAT-test/unit/genesis_ubuntu_build_root.t +++ b/xCAT-test/unit/genesis_ubuntu_build_root.t @@ -3,24 +3,22 @@ # mandatory. dracut_install reports a missing command and returns 0, so a hole in the image # does not fail the build. # -# The mandatory list comes from RUNNING the module: module-setup.sh is sourced with -# dracut_install shadowed, _dracut_install_opt neutralised, and install() called. The -# package list comes from evaluating the REQUIRED_PACKAGES assignment in the build script. +# XCAT::GenesisBuildRoot::required_packages lists the packages of the build root, and +# XCAT::GenesisPayload::module_commands reads the mandatory commands from the module, as +# verify-genesis-payload does. use strict; use warnings; -use File::Temp qw(tempdir); use FindBin; use lib "$FindBin::Bin/../lib"; +use lib "$FindBin::Bin/../../xCAT-genesis-base/lib"; use Test::More; +use XCAT::GenesisBuildRoot qw(required_packages); +use XCAT::GenesisPayload qw(module_commands); use XCAT::Test::File qw(repo_path); -my $builder = repo_path('xCAT-genesis-builder/builddeb-genesis-base'); -my $module = repo_path('xCAT-genesis-builder/dracut_105/ubuntu/module-setup.sh'); -plan skip_all => 'builddeb-genesis-base not found' unless -f $builder; -plan skip_all => 'ubuntu module-setup.sh not found' unless -f $module; -plan tests => 9; +my $module = repo_path('xCAT-genesis-base/dracut_105/ubuntu/module-setup.sh'); # Mandatory commands a minimal Ubuntu server root does NOT already provide, and the package # that supplies each one on every release xCAT builds for. @@ -29,87 +27,53 @@ my %PACKAGE_FOR = ( ifenslave => 'ifenslave', ); +# A release carries exactly the names in its list. +sub release { + my %carried = map { $_ => 1 } @_; + return sub { $carried{ $_[0] } }; +} + # hwclock is not in that list because the package that carries it moved. Measured on the # four Ubuntu management nodes: focal and jammy have it in util-linux, which is essential # and always in the build root, and no util-linux-extra exists to install; noble and # resolute have it in util-linux-extra. util-linux only Suggests that package, and this # build passes --no-install-recommends, so the releases that split it must name it and the # releases that did not must not. +my @noble = required_packages('amd64', 'noble', release('util-linux-extra')); +my @jammy = required_packages('amd64', 'jammy', release()); -my %mandatory = map { $_ => 1 } mandatory_commands($module); -my @packages = required_packages($builder); +# An absolute path is a data file, not a command. +my %mandatory = map { $_ => 1 } grep { !m{^/} } module_commands($module); +my %packages = map { $_ => 1 } @jammy; for my $command (sort keys %PACKAGE_FOR) { ok($mandatory{$command}, "the Ubuntu dracut module installs '$command' unconditionally"); - ok(scalar(grep { $_ eq $PACKAGE_FOR{$command} } @packages), + ok($packages{ $PACKAGE_FOR{$command} }, "the build root installs $PACKAGE_FOR{$command}, which provides '$command'"); } # doxcat asks dhclient for the provisioning lease. -ok($mandatory{dhclient} && scalar(grep { $_ eq 'isc-dhcp-client' } @packages), +ok($mandatory{dhclient} && $packages{'isc-dhcp-client'}, 'the Genesis image can obtain a DHCP lease'); ok($mandatory{hwclock}, "the Ubuntu dracut module installs 'hwclock' unconditionally"); # Naming a package apt cannot locate fails the whole install, and the script runs under # set -e, so an unconditional util-linux-extra stops the build on focal and jammy. -ok(!scalar(grep { $_ eq 'util-linux-extra' } @packages), - 'the unconditional list does not name util-linux-extra'); +is_deeply([ grep { $_ eq 'util-linux-extra' } @jammy ], [], + 'a release without util-linux-extra does not get it'); +is_deeply([ @noble[ 0 .. $#noble - 1 ] ], \@jammy, + 'a release that carries util-linux-extra gets the same list ...'); +is($noble[-1], 'util-linux-extra', '... with util-linux-extra last'); -# What the script does instead: keep a package only where apt has a candidate for it. -{ - is_deeply(optional_packages($builder, 'util-linux-extra', 0), ['util-linux-extra'], - 'a release that carries util-linux-extra installs it'); - is_deeply(optional_packages($builder, 'util-linux-extra', 1), [], - 'a release without it installs nothing in its place'); -} +my @ppc = required_packages('ppc64el', 'noble', release('util-linux-extra')); +is_deeply([ @noble[ -3 .. -1 ] ], [qw(dmidecode efibootmgr util-linux-extra)], + 'amd64 adds dmidecode and efibootmgr'); +is_deeply([ @ppc[ 0 .. $#ppc - 1 ] ], [ @noble[ 0 .. $#noble - 3 ] ], + 'ppc64el gets neither dmidecode nor efibootmgr'); -# An absolute path in the install() output is a data file, not a command. -sub mandatory_commands { - my ($path) = @_; - my $dir = tempdir(CLEANUP => 1); - my $driver = "$dir/collect.sh"; - open my $fh, '>', $driver or die "$driver: $!"; - print $fh <<"BASH"; -dracut_install() { printf '%s\\n' "\$\@"; } -instmods() { :; } -inst_multiple() { :; } -inst() { :; } -dpkg-architecture() { echo x86_64-linux-gnu; } -. '$path' -# _dracut_install_opt installs only what the build root already has. Neutralise it after -# sourcing, so its commands stay out of the mandatory set. -_dracut_install_opt() { :; } -install -BASH - close $fh; - my @out = qx{bash '$driver' 2>/dev/null}; - die("running install() from $path produced nothing") unless @out; - my %seen; - my @names = grep { !$seen{$_}++ } grep { length && !m{^/} } map { chomp; $_ } @out; - die("install() from $path named no bare commands") unless @names; - return @names; -} +my @asked; +required_packages('amd64', 'noble', sub { push @asked, $_[0]; 1 }); +is_deeply(\@asked, ['util-linux-extra'], 'apt is asked only about the optional package'); -# Run the script's own selector with apt-cache shadowed, so the decision is exercised -# rather than read. $rc is what the shadow returns: 0 for a release that has the package. -sub optional_packages { - my ($path, $package, $rc) = @_; - my $text = do { open my $fh, '<', $path or die "$path: $!"; local $/; <$fh> }; - my ($block) = $text =~ /^(optional_packages\(\)\s*\{.*?^\})/ms; - BAIL_OUT("no optional_packages() in $path") unless $block; - my $out = qx{bash -c 'set -u; apt-cache() { return $rc; }; $block; optional_packages $package' 2>/dev/null}; - return [ grep { length } split /\s+/, ($out // '') ]; -} - -# Evaluate the assignment rather than parse it, so the list is the value the script uses. -sub required_packages { - my ($path) = @_; - my $text = do { open my $fh, '<', $path or die "$path: $!"; local $/; <$fh> }; - my ($block) = $text =~ /^(REQUIRED_PACKAGES="[^"]*")/ms; - die("no REQUIRED_PACKAGES assignment in $path") unless $block; - my $out = qx{bash -c 'set -u; $block; printf "%s\\n" \$REQUIRED_PACKAGES' 2>/dev/null}; - my @packages = grep { length } split /\s+/, ($out // ''); - die("REQUIRED_PACKAGES in $path evaluated to nothing") unless @packages; - return @packages; -} +done_testing(); From 0d8c61d8c3fc482186620b1dd8b68a8d740fd418 Mon Sep 17 00:00:00 2001 From: Daniel Hilst <392820+dhilst@users.noreply.github.com> Date: Thu, 24 Sep 2026 16:05:32 -0300 Subject: [PATCH 12/17] fix(xcat-core): go-xcat cannot be sourced without running the installer go-xcat runs internal_setup and the main program at file scope. A test that sources the script to read its package lists or call its functions starts the installer, so the tests extracted function bodies with awk and evaluated them instead. When go-xcat is sourced, it now skips internal_setup and returns before the main program. A direct run and a run from a pipe do not change. Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com> --- xCAT-server/share/xcat/tools/go-xcat | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/xCAT-server/share/xcat/tools/go-xcat b/xCAT-server/share/xcat/tools/go-xcat index e0c34f4dc..d21e69b6c 100755 --- a/xCAT-server/share/xcat/tools/go-xcat +++ b/xCAT-server/share/xcat/tools/go-xcat @@ -487,7 +487,8 @@ function debug_trace() return "${rc}" } -internal_setup +# Sourced by a test: define the functions and lists, and change nothing on the host. +(return 0 2>/dev/null) || internal_setup # Check operating system function check_os() @@ -2394,6 +2395,9 @@ function boo_boo_if_bad() # # Main program goes here. +# Sourced by a test: stop before the main program. +(return 0 2>/dev/null) && return 0 + declare -a GO_XCAT_YES=() GO_XCAT_ACTION="" GO_XCAT_CORE_URL="" From 61cbf08f7834bdd2065c944b1d29fd11aa2419ae Mon Sep 17 00:00:00 2001 From: Daniel Hilst <392820+dhilst@users.noreply.github.com> Date: Thu, 24 Sep 2026 16:05:32 -0300 Subject: [PATCH 13/17] fix(xcat-core): go-xcat uninstall tests run extracted code against a fake rpm go_xcat_uninstall_installed_only.t copied five functions out of go-xcat with a regex and ran them in a generated script with fake rpm and dpkg-query. go_xcat_uninstall_legacy.t evaluated an awk extract of the list assignments. Both measured the extract, not the script. Both tests now source go-xcat. The installed-only test asks installed_packages and uninstall_xcat about the host package database, with bash as the installed package and xcat-test-absent-* as absent ones. Only remove_package is replaced. The legacy test reads GO_XCAT_UNINSTALL_LIST as the script builds it. Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com> --- .../unit/go_xcat_uninstall_installed_only.t | 108 ++++-------------- xCAT-test/unit/go_xcat_uninstall_legacy.t | 44 ++----- 2 files changed, 34 insertions(+), 118 deletions(-) diff --git a/xCAT-test/unit/go_xcat_uninstall_installed_only.t b/xCAT-test/unit/go_xcat_uninstall_installed_only.t index 168aa443d..18fbbde35 100755 --- a/xCAT-test/unit/go_xcat_uninstall_installed_only.t +++ b/xCAT-test/unit/go_xcat_uninstall_installed_only.t @@ -2,7 +2,6 @@ use strict; use warnings; -use File::Temp qw(tempdir); use FindBin; use Test::More; @@ -15,98 +14,35 @@ use Test::More; # So what is not installed does not reach the package manager. That is also what makes keeping # a retired name in the list free, which is why the list outlives the build. # -# uninstall_xcat is taken from the shipped script and run with the package database faked and -# remove_package shadowed, so the assertions read the argument list the package manager would -# have been given. +# go-xcat is sourced and asked about the host's own package database. bash is installed on +# every build host, and the xcat-test-absent-* names are installed on none. my $go_xcat = "$FindBin::Bin/../../xCAT-server/share/xcat/tools/go-xcat"; plan skip_all => 'go-xcat not found' unless -r $go_xcat; -plan tests => 4; -{ - my @handed = uninstall_args($go_xcat, ['xCAT-server', 'xCAT-client']); - is_deeply([sort @handed], ['xCAT-client', 'xCAT-server'], - 'only the installed packages reach the package manager'); +# Run a bash snippet after go-xcat is sourced, and return its output lines. +sub go_xcat { + my ($snippet) = @_; + my @lines = `bash -c 'source "\$1" && eval "\$2"' go-xcat '$go_xcat' '$snippet'`; + chomp @lines; + return \@lines; } -{ - # The point of keeping a retired name: where it IS installed, it is still removed. - my @handed = uninstall_args($go_xcat, ['xCAT-genesis-builder']); - is_deeply(\@handed, ['xCAT-genesis-builder'], - 'a retired package that is installed is still removed'); -} +is_deeply(go_xcat('installed_packages xcat-test-absent-1 bash xcat-test-absent-2'), ['bash'], + 'only the installed packages are reported'); -{ - my @handed = uninstall_args($go_xcat, []); - is_deeply(\@handed, [], - 'a node with none of them installed hands the package manager nothing'); -} +is_deeply(go_xcat('installed_packages xcat-test-absent-1 xcat-test-absent-2'), [], + 'a list with nothing installed reports nothing'); -{ - # Guards the fake: a database that says everything is installed must hand over the list, - # or the three assertions above would pass against a filter that drops everything. - my @handed = uninstall_args($go_xcat, ['*']); - ok(scalar(@handed) > 10, 'with everything installed the whole list is handed over'); -} +# remove_package is replaced so the test removes nothing from the host. +is_deeply(go_xcat('remove_package() { printf "remove %s\n" "$@"; } + GO_XCAT_UNINSTALL_LIST=(xCAT-genesis-builder xcat-test-absent-1 bash) + uninstall_xcat -y'), ['remove -y', 'remove bash'], + 'only the installed packages reach the package manager'); -# Extract `function NAME()` blocks by name, in the order asked for. -sub extract { - my ($text, @names) = @_; - my @blocks; - for my $name (@names) { - my ($block) = $text =~ /^(function \Q$name\E\(\)\s*\n\{.*?^\})/ms; - push @blocks, $block if $block; - } - return @blocks; -} +is_deeply(go_xcat('remove_package() { printf "remove %s\n" "$@"; } + GO_XCAT_UNINSTALL_LIST=(xCAT-genesis-builder xcat-test-absent-1) + uninstall_xcat -y; echo "uninstall_xcat returns $?"'), ['uninstall_xcat returns 0'], + 'a node with none of them installed hands the package manager nothing, and succeeds'); -# Run the shipped uninstall path with rpm and dpkg-query answering from a fake database and the -# package manager shadowed, and collect what it was asked to remove. -sub uninstall_args { - my ($path, $installed) = @_; - my $text = do { open my $fh, '<', $path or die "$path: $!"; local $/; <$fh> }; - - my ($lists) = $text =~ /^(GO_XCAT_INSTALL_LIST=\(.*?\n)\n/ms; - BAIL_OUT("no package lists in $path") unless $lists; - my @functions = extract($text, qw(function_dispatch installed_packages - installed_packages_rpm installed_packages_deb - uninstall_xcat)); - BAIL_OUT("no uninstall_xcat() in $path") - unless grep { /^function uninstall_xcat\(\)/ } @functions; - - my $dir = tempdir(CLEANUP => 1); - my $driver = "$dir/driver.sh"; - my $present = join ' ', @{$installed}; - open my $fh, '>', $driver or die "$driver: $!"; - print {$fh} "set -u\n"; - # An rpm host: dpkg is absent, so the lists take the rpm branch and the deb arm of the - # dispatch stands down. Shadowing `type` to fail for everything would stand both arms - # down and the filter would answer nothing, whatever it does. - print {$fh} "type() { case \"\$1\" in dpkg|dpkg-query|apt-get) return 1 ;; *) return 0 ;; esac; }\n"; - print {$fh} $lists, "\n"; - print {$fh} <<"BASH"; -INSTALLED="$present" -present() { - case "\$INSTALLED" in *"*"*) return 0 ;; esac - case " \$INSTALLED " in *" \$1 "*) return 0 ;; *) return 1 ;; esac -} -rpm() { for a in "\$\@"; do case "\$a" in -*) continue ;; esac; present "\$a" || return 1; done; return 0; } -dpkg-query() { - for a in "\$\@"; do - case "\$a" in -*) continue ;; esac - present "\$a" && echo "install ok installed" - done - return 0 -} -exit_if_bad() { :; } -warn_if_bad() { :; } -remove_package() { for a in "\$\@"; do case "\$a" in -y) continue ;; esac; echo "\$a"; done; } -BASH - print {$fh} join("\n", @functions), "\n"; - print {$fh} "uninstall_xcat -y\n"; - close $fh; - - my @out = qx{bash '$driver' 2>/dev/null}; - chomp @out; - return grep { length } @out; -} +done_testing(); diff --git a/xCAT-test/unit/go_xcat_uninstall_legacy.t b/xCAT-test/unit/go_xcat_uninstall_legacy.t index 17c3c27c4..e318ebf23 100755 --- a/xCAT-test/unit/go_xcat_uninstall_legacy.t +++ b/xCAT-test/unit/go_xcat_uninstall_legacy.t @@ -2,7 +2,6 @@ use strict; use warnings; -use File::Temp qw(tempdir); use FindBin; use Test::More; @@ -11,43 +10,24 @@ use Test::More; # still has it: nothing else asks the package manager to remove it, and the operator is not # told. So the list outlives the build that produced each package. # -# The two assignments are evaluated rather than read, with `type` shadowed so the rpm branch -# stands, and the names asserted are the ones go-xcat would hand to the package manager. +# go-xcat is sourced, so the list is the one the script builds. `type dpkg` fails, so the rpm +# list is read on any build host. my $go_xcat = "$FindBin::Bin/../../xCAT-server/share/xcat/tools/go-xcat"; plan skip_all => 'go-xcat not found' unless -r $go_xcat; -plan tests => 3; -my @uninstall = uninstall_list($go_xcat); +my @uninstall = `bash -c 'type() { [[ \$1 == dpkg ]] && return 1; builtin type "\$@"; } + source "\$1" && printf "%s\\n" "\${GO_XCAT_UNINSTALL_LIST[\@]}"' go-xcat '$go_xcat'`; +is($?, 0, 'go-xcat sources'); +chomp @uninstall; +my %uninstall = map { $_ => 1 } @uninstall; # xCAT-genesis-builder was built until 2.19 and is installed on existing management nodes. -ok(scalar(grep { $_ eq 'xCAT-genesis-builder' } @uninstall), - 'go-xcat uninstall still removes xCAT-genesis-builder'); +ok($uninstall{'xCAT-genesis-builder'}, 'go-xcat uninstall still removes xCAT-genesis-builder'); # Two neighbours, so a list that evaluated to nothing cannot pass the assertion above. -ok(scalar(grep { $_ eq 'xCAT-genesis-openembedded-x86_64' } @uninstall), - 'go-xcat uninstall removes the OpenEmbedded Genesis packages'); -ok(scalar(grep { $_ eq 'xCAT-server' } @uninstall), - 'go-xcat uninstall removes what it installs'); +ok($uninstall{'xCAT-genesis-openembedded-x86_64'}, + 'go-xcat uninstall removes the OpenEmbedded Genesis packages'); +ok($uninstall{'xCAT-server'}, 'go-xcat uninstall removes what it installs'); -# Evaluate both assignments: the uninstall list is built from the install one, and the Debian -# reassignment below it is guarded by `type dpkg`, which is shadowed away. -sub uninstall_list { - my ($path) = @_; - my $dir = tempdir(CLEANUP => 1); - my $driver = "$dir/driver.sh"; - open my $fh, '>', $driver or die "$driver: $!"; - print {$fh} <<'BASH'; -set -u -type() { return 1; } -eval "$(awk '/^GO_XCAT_INSTALL_LIST=\(/ { copy = 1 } copy { print } copy && /^$/ { exit }' "$GO_XCAT_SOURCE")" -printf '%s\n' "${GO_XCAT_UNINSTALL_LIST[@]}" -BASH - close $fh; - local $ENV{GO_XCAT_SOURCE} = $path; - my @out = qx{bash '$driver' 2>/dev/null}; - chomp @out; - my @names = grep { length } @out; - BAIL_OUT("no package list read from $path") unless @names; - return @names; -} +done_testing(); From c6fc094c931e32b3349e02db85ddd63451ea4191 Mon Sep 17 00:00:00 2001 From: Daniel Hilst <392820+dhilst@users.noreply.github.com> Date: Thu, 24 Sep 2026 22:00:28 -0300 Subject: [PATCH 14/17] fix(xcat-core): test Genesis shell and C code by running it, not from Perl The Perl tests of the OpenEmbedded Genesis image ran its shell scripts and C programs through system(), qx, pipes and fork, wrote fake commands through copied read_file and write_file helpers, and matched regexes against shell and C source. Many rejections checked only a non-zero exit, so an earlier, unrelated failure also passed. The quality gate judged 91% of their 762 checks bad. The shell and C checks move to bats in xCAT-test/bats. The C programs are compiled there and run: the plain console, test drivers for the shared status view and the shell launcher, and genesis-udp-send against a loopback listener. Each rejection now matches the message of its own guard. The .t files keep the artifact checks (kas, recipes, kernel fragments, units, README), read with XCAT::Test::File. Test files left with no checks are deleted. Claim map (file -> where its claims went): - go_xcat_uninstall_installed_only.t -> go_xcat_uninstall_installed_only.bats - genesis_readme_paths.t, genesis_base_sources_staged.t -> kept in the .t (README read by slurp_repo_file, tarball read by Archive::Tar) - genesis_openembedded_{export,hardware,runtime}.t -> the bats file of the same name - genesis_openembedded_{bmcsetup,metrics,extensions,qeth,actions, discovery}.t -> scripts to the bats file of the same name, recipes and units kept in the .t - genesis_openembedded_console.t -> built and run checks, state.c, plain_ui.c and shell.c to genesis_openembedded_console.bats; Newt source checks kept in the .t - genesis_openembedded.t -> oe/build to genesis_openembedded_build.bats; shell matches to the runtime, hardware and extensions bats; console matches to the console bats or the console .t; artifacts kept Red proof: every moved or new test was run against a mutated copy of the code it covers and went red, for example: rpm -q not checked, tar without --mtime, VEX report not exported, support dir after PATH, digest check off, layer2 forced, wildcard device allowed, arguments eval'd, tcp probe skipped, SOURCE_PORT 302, AF_INET only, stage timer compared, launch error ignored, kas run twice, status written in place. One mutation stayed green: tar without --owner=0 when the test runs as root. Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com> --- .../bats/genesis_openembedded_actions.bats | 223 ++++++ .../bats/genesis_openembedded_bmcsetup.bats | 39 + .../bats/genesis_openembedded_build.bats | 45 ++ .../bats/genesis_openembedded_console.bats | 457 +++++++++++ .../bats/genesis_openembedded_discovery.bats | 372 +++++++++ .../bats/genesis_openembedded_export.bats | 45 ++ .../bats/genesis_openembedded_extensions.bats | 154 ++++ .../bats/genesis_openembedded_hardware.bats | 264 +++++++ .../bats/genesis_openembedded_metrics.bats | 86 +++ xCAT-test/bats/genesis_openembedded_qeth.bats | 145 ++++ .../bats/genesis_openembedded_runtime.bats | 439 +++++++++++ .../go_xcat_uninstall_installed_only.bats | 83 ++ xCAT-test/unit/genesis_base_sources_staged.t | 14 +- xCAT-test/unit/genesis_openembedded.t | 303 ++------ xCAT-test/unit/genesis_openembedded_actions.t | 284 +------ .../unit/genesis_openembedded_bmcsetup.t | 88 +-- xCAT-test/unit/genesis_openembedded_console.t | 540 ++----------- .../unit/genesis_openembedded_discovery.t | 575 +------------- xCAT-test/unit/genesis_openembedded_export.t | 69 -- .../unit/genesis_openembedded_extensions.t | 292 +------ .../unit/genesis_openembedded_hardware.t | 381 --------- xCAT-test/unit/genesis_openembedded_metrics.t | 167 +--- xCAT-test/unit/genesis_openembedded_qeth.t | 263 +------ xCAT-test/unit/genesis_openembedded_runtime.t | 731 ------------------ xCAT-test/unit/genesis_readme_paths.t | 22 +- .../unit/go_xcat_uninstall_installed_only.t | 48 -- 26 files changed, 2529 insertions(+), 3600 deletions(-) create mode 100644 xCAT-test/bats/genesis_openembedded_actions.bats create mode 100644 xCAT-test/bats/genesis_openembedded_bmcsetup.bats create mode 100644 xCAT-test/bats/genesis_openembedded_build.bats create mode 100644 xCAT-test/bats/genesis_openembedded_console.bats create mode 100644 xCAT-test/bats/genesis_openembedded_discovery.bats create mode 100644 xCAT-test/bats/genesis_openembedded_export.bats create mode 100644 xCAT-test/bats/genesis_openembedded_extensions.bats create mode 100644 xCAT-test/bats/genesis_openembedded_hardware.bats create mode 100644 xCAT-test/bats/genesis_openembedded_metrics.bats create mode 100644 xCAT-test/bats/genesis_openembedded_qeth.bats create mode 100644 xCAT-test/bats/genesis_openembedded_runtime.bats create mode 100644 xCAT-test/bats/go_xcat_uninstall_installed_only.bats delete mode 100644 xCAT-test/unit/genesis_openembedded_export.t delete mode 100644 xCAT-test/unit/genesis_openembedded_hardware.t delete mode 100644 xCAT-test/unit/genesis_openembedded_runtime.t delete mode 100755 xCAT-test/unit/go_xcat_uninstall_installed_only.t diff --git a/xCAT-test/bats/genesis_openembedded_actions.bats b/xCAT-test/bats/genesis_openembedded_actions.bats new file mode 100644 index 000000000..e859360c8 --- /dev/null +++ b/xCAT-test/bats/genesis_openembedded_actions.bats @@ -0,0 +1,223 @@ +#!/usr/bin/env bats +# +# genesis-action runs in the OpenEmbedded Genesis image. It reads the node action (the destiny) +# from xCAT and runs it: discovery, a wait state, an approved command, a reboot into an +# install, or a poweroff. getdestiny and nextdestiny are scratch scripts that answer from a +# queue file. The other commands are scratch scripts that log their arguments. + +load 'helpers/shell_source' + +INIT_FILES='xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files' + +setup() +{ + ACTION_SCRIPT="$(require_repo_file "$INIT_FILES/genesis-action")" + root="$BATS_TEST_TMPDIR" + bin="$root/bin" + state_dir="$root/run" + status_dir="$state_dir/status" + approved_dir="$root/actions" + destiny_file="$state_dir/destiny" + command_log="$root/commands.log" + mkdir -p "$bin" "$status_dir" "$approved_dir" + printf '%s\n' XCATDEST=192.0.2.10:3001 XCATMASTER=192.0.2.10 XCATPORT=3001 \ + XCAT_INTERFACE=eth0 XCAT_SOURCE_ADDRESS=192.0.2.98 >"$state_dir/genesis.env" + printf '42.00 80.00\n' >"$root/uptime" + + cat >"$bin/getdestiny" <<'SH' +#!/bin/sh +printf 'getdestiny %s\n' "$*" >>"$XCAT_TEST_LOG" +metadata= +while [ "$#" -gt 0 ]; do + case "$1" in + --metadata) metadata=$2; shift 2 ;; + *) shift ;; + esac +done +[ -s "$XCAT_TEST_GETDESTINY_QUEUE" ] || exit 1 +response=$(head -n 1 "$XCAT_TEST_GETDESTINY_QUEUE") +tail -n +2 "$XCAT_TEST_GETDESTINY_QUEUE" >"$XCAT_TEST_GETDESTINY_QUEUE.new" +mv "$XCAT_TEST_GETDESTINY_QUEUE.new" "$XCAT_TEST_GETDESTINY_QUEUE" +if [ -n "$metadata" ]; then + printf '%s\n' 'XCAT_NODE_NAME=node042' "XCAT_DESTINY=$response" >"$metadata" +fi +printf '%s\n' "$response" +SH + cat >"$bin/nextdestiny" <<'SH' +#!/bin/sh +printf 'nextdestiny %s\n' "$*" >>"$XCAT_TEST_LOG" +[ -s "$XCAT_TEST_NEXTDESTINY_QUEUE" ] || exit 1 +response=$(head -n 1 "$XCAT_TEST_NEXTDESTINY_QUEUE") +tail -n +2 "$XCAT_TEST_NEXTDESTINY_QUEUE" >"$XCAT_TEST_NEXTDESTINY_QUEUE.new" +mv "$XCAT_TEST_NEXTDESTINY_QUEUE.new" "$XCAT_TEST_NEXTDESTINY_QUEUE" +printf '%s\n' "$response" +SH + printf '#!/bin/sh\nprintf "logger %%s\\n" "$*" >>"$XCAT_TEST_LOG"\n' >"$bin/logger" + printf '#!/bin/sh\nprintf "%%s\\n" discover >>"$XCAT_TEST_LOG"\n' >"$bin/discover" + printf '#!/bin/sh\nprintf "%%s\\n" getcert >>"$XCAT_TEST_LOG"\n: >"$XCAT_TEST_CERTIFICATE_FILE"\n' >"$bin/getcert" + printf '#!/bin/sh\nprintf "reboot-control %%s\\n" "$*" >>"$XCAT_TEST_LOG"\n' >"$bin/reboot-control" + printf '#!/bin/sh\nprintf "poweroff-control %%s\\n" "$*" >>"$XCAT_TEST_LOG"\n' >"$bin/poweroff-control" + printf '#!/bin/sh\nprintf "ipmitool %%s\\n" "$*" >>"$XCAT_TEST_LOG"\n[ "${XCAT_TEST_IPMI-0}" = 1 ]\n' >"$bin/ipmitool" + printf '#!/bin/sh\nprintf inventory >>"$XCAT_TEST_LOG"\nprintf " <%%s>" "$@" >>"$XCAT_TEST_LOG"\nprintf "\\n" >>"$XCAT_TEST_LOG"\n' >"$approved_dir/inventory" + printf '#!/bin/sh\nprintf "%%s\\n" bmcsetup >>"$XCAT_TEST_LOG"\n' >"$approved_dir/bmcsetup" + chmod 0755 "$bin"/* "$approved_dir"/* + + export PATH="$bin:$PATH" + export XCAT_STATE_DIR="$state_dir" + export XCAT_STATUS_COMMAND="$(require_repo_file "$INIT_FILES/genesis-status")" + export XCAT_STATUS_DIR="$status_dir" + export XCAT_DESTINY_FILE="$destiny_file" + export XCAT_METADATA_FILE="$state_dir/xcat-response.env" + export XCAT_NETWORK_FILE="$state_dir/genesis.env" + export XCAT_DISCOVER_COMMAND="$bin/discover" + export XCAT_CERTIFICATE_COMMAND="$bin/getcert" + export XCAT_CERTIFICATE_FILE="$root/cert.pem" + export XCAT_GETDESTINY_COMMAND="$bin/getdestiny" + export XCAT_NEXTDESTINY_COMMAND="$bin/nextdestiny" + export XCAT_ACTION_COMMAND_DIR="$approved_dir" + export XCAT_REBOOT_COMMAND="$bin/reboot-control" + export XCAT_POWEROFF_COMMAND="$bin/poweroff-control" + export XCAT_ACTION_POLL_SECONDS=0 + export XCAT_ACTION_REQUEST_TIMEOUT=2 + export XCAT_ACTION_OPERATION_TIMEOUT=2 + export XCAT_UPTIME_FILE="$root/uptime" + export XCAT_GENESIS_FUNCTIONS="$(require_repo_file "$INIT_FILES/genesis-functions")" + export XCAT_TEST_LOG="$command_log" + export XCAT_TEST_GETDESTINY_QUEUE="$root/getdestiny.queue" + export XCAT_TEST_NEXTDESTINY_QUEUE="$root/nextdestiny.queue" + export XCAT_TEST_CERTIFICATE_FILE="$root/cert.pem" +} + +# Run genesis-action for one destiny. GETDESTINY and NEXTDESTINY set the queued xCAT +# answers, NO_CERTIFICATE starts without a certificate, MAX_STEPS and IPMI pass through. +# Sets $status, $log (the command log) and $record (the published action status). +run_action() +{ + printf '%s\n' "$1" >"$destiny_file" + printf '%s' "${GETDESTINY-$1 +}" >"$XCAT_TEST_GETDESTINY_QUEUE" + printf '%s' "${NEXTDESTINY-standby +}" >"$XCAT_TEST_NEXTDESTINY_QUEUE" + rm -f "$command_log" "$XCAT_CERTIFICATE_FILE" "$status_dir/action.env" + [ -n "${NO_CERTIFICATE-}" ] || printf 'certificate\n' >"$XCAT_CERTIFICATE_FILE" + status=0 + XCAT_ACTION_MAX_STEPS="${MAX_STEPS:-1}" XCAT_TEST_IPMI="${IPMI:-0}" \ + /bin/bash "$ACTION_SCRIPT" /dev/null 2>&1 || status=$? + log="$(cat "$command_log" 2>/dev/null || true)" + record="$(cat "$status_dir/action.env" 2>/dev/null || true)" +} + +has_line() +{ + grep -Eq "$2" <<<"$1" +} + +# Not "! has_line": bash ignores errexit for a command inverted with "!". +has_no_line() +{ + ! grep -Eq "$2" <<<"$1" +} + +@test "discovery sends inventory, enrolls a certificate, then queries xCAT and loads the node action" { + GETDESTINY=$'standby\n' run_action discover + [ "$status" -eq 0 ] + has_line "$log" '^discover$' + has_line "$log" '^getcert$' + [ "$(grep -nx discover <<<"$log" | cut -d: -f1)" -lt "$(grep -n '^getdestiny ' <<<"$log" | head -n1 | cut -d: -f1)" ] + [ "$(cat "$destiny_file")" = standby ] +} + +@test "shell becomes a managed wait state that accepts a remote action change, and opens no shell" { + GETDESTINY=$'install test-image\n' run_action shell + [ "$status" -eq 0 ] + has_no_line "$log" '(^|/)bash(\s|$)' + [ "$(cat "$destiny_file")" = 'install test-image' ] +} + +@test "standby polls xCAT, obtains a missing certificate, and publishes an idle state" { + GETDESTINY=$'standby\nstandby\n' NO_CERTIFICATE=1 MAX_STEPS=2 run_action standby + [ "$status" -eq 0 ] + has_line "$log" '^getcert$' + has_line "$record" '^STATE=IDLE$' + has_line "$record" '^VERIFIED_SECONDS=42$' +} + +@test "osimage and ondiscover complete and advance the action chain" { + for action in osimage ondiscover; do + run_action "$action" + [ "$status" -eq 0 ] + has_line "$log" '^nextdestiny ' + done +} + +@test "an approved command completes, gets its arguments unevaluated, and advances the chain" { + run_action 'runcmd=inventory storage safe;reboot' + [ "$status" -eq 0 ] + has_line "$log" '^inventory $' + has_line "$log" '^nextdestiny ' +} + +@test "sequential discovery can run BMC setup, which advances the chain" { + run_action runcmd=bmcsetup + [ "$status" -eq 0 ] + has_line "$log" '^bmcsetup$' + has_line "$log" '^nextdestiny ' +} + +@test "an unpackaged command is rejected with a specific failure code" { + run_action runcmd=unpackaged + [ "$status" -ne 0 ] + has_line "$record" '^CODE=ACTION_COMMAND_NOT_APPROVED$' +} + +@test "runimage, configraid and sysclone fail closed and report their migration status" { + for pair in runimage:UNSAFE_LEGACY_ACTION configraid:LEGACY_STORAGE_ACTION \ + sysclone:LEGACY_SYSCLONE_ACTION; do + run_action "${pair%%:*}" + [ "$status" -ne 0 ] + has_line "$record" "^CODE=${pair#*:}\$" + done +} + +@test "boot and reboot advance the chain, request a one-time network boot and a reboot" { + for action in boot reboot; do + IPMI=1 run_action "$action" + [ "$status" -eq 0 ] + has_line "$log" '^nextdestiny ' + has_line "$log" '^ipmitool chassis bootdev pxe$' + has_line "$log" '^reboot-control reboot$' + done +} + +@test "install, netboot and statelite keep the server-selected chain position and reboot" { + for action in install netboot statelite; do + run_action "$action" + [ "$status" -eq 0 ] + has_no_line "$log" '^nextdestiny ' + has_line "$log" '^reboot-control reboot$' + done +} + +@test "shutdown requests a poweroff" { + run_action shutdown + [ "$status" -eq 0 ] + has_line "$log" '^poweroff-control poweroff$' +} + +@test "an xCAT error action fails and stays visible" { + run_action 'error=policy denied' + [ "$status" -ne 0 ] + has_line "$record" '^CODE=XCAT_ACTION_ERROR$' +} + +@test "an unknown action fails with a specific failure code" { + run_action unknown + [ "$status" -ne 0 ] + has_line "$record" '^CODE=XCAT_ACTION_UNSUPPORTED$' +} + +@test "a failed refresh uses the previously received action, and the failed poll stays visible" { + GETDESTINY='' run_action standby + [ "$status" -eq 0 ] + has_line "$record" '^STATE=DEGRADED$' +} diff --git a/xCAT-test/bats/genesis_openembedded_bmcsetup.bats b/xCAT-test/bats/genesis_openembedded_bmcsetup.bats new file mode 100644 index 000000000..0ccd3df3e --- /dev/null +++ b/xCAT-test/bats/genesis_openembedded_bmcsetup.bats @@ -0,0 +1,39 @@ +#!/usr/bin/env bats +# +# The OpenEmbedded Genesis image runs the legacy BMC scripts through the genesis-bmcsetup +# wrapper. The wrapper puts the BMC support commands first in PATH and starts the packaged +# implementation with the arguments it was given. + +load 'helpers/shell_source' + +BMC_FILES='xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-bmcsetup/files' + +@test "the legacy BMC scripts are valid Bash" { + for script in bmcsetup getipmi remoteimmsetup; do + /bin/bash -n "$(require_repo_file "xCAT-genesis-scripts/usr/bin/$script")" + done +} + +@test "the BMC action helpers are valid Bash" { + /bin/bash -n "$(require_repo_file "$BMC_FILES/genesis-bmcsetup")" + /bin/bash -n "$(require_repo_file "$BMC_FILES/genesis-credential-wait")" +} + +@test "the BMC action starts the packaged implementation, support commands first, arguments kept" { + wrapper="$(require_repo_file "$BMC_FILES/genesis-bmcsetup")" + support_dir="$BATS_TEST_TMPDIR/support" + log="$BATS_TEST_TMPDIR/wrapper.log" + mkdir -p "$support_dir" + cat >"$BATS_TEST_TMPDIR/implementation" <<'SH' +#!/bin/bash +printf 'path=%s\n' "$PATH" >"$XCAT_TEST_LOG" +printf 'arguments=%s\n' "$*" >>"$XCAT_TEST_LOG" +SH + XCAT_BMC_SUPPORT_DIR="$support_dir" \ + XCAT_BMC_SETUP_IMPLEMENTATION="$BATS_TEST_TMPDIR/implementation" \ + XCAT_TEST_LOG="$log" \ + run /bin/bash "$wrapper" first second + [ "$status" -eq 0 ] + grep -q "^path=$support_dir:" "$log" + grep -qx 'arguments=first second' "$log" +} diff --git a/xCAT-test/bats/genesis_openembedded_build.bats b/xCAT-test/bats/genesis_openembedded_build.bats new file mode 100644 index 000000000..aba4050b4 --- /dev/null +++ b/xCAT-test/bats/genesis_openembedded_build.bats @@ -0,0 +1,45 @@ +#!/usr/bin/env bats +# +# oe/build runs kas once for each Genesis architecture it is given. kas is a scratch script +# that logs its arguments. + +load 'helpers/shell_source' + +setup() +{ + BUILD="$(require_repo_file 'xCAT-genesis-base/oe/build')" + OE_DIR="$(cd "$(dirname "$BUILD")" && pwd)" + printf '#!/bin/sh\nprintf "%%s\\n" "$*" >>"$XCAT_TEST_KAS_LOG"\n' >"$BATS_TEST_TMPDIR/kas" + chmod 0755 "$BATS_TEST_TMPDIR/kas" + export KAS="$BATS_TEST_TMPDIR/kas" + export XCAT_TEST_KAS_LOG="$BATS_TEST_TMPDIR/kas.log" + export XCAT_GENESIS_WORK_DIR="$BATS_TEST_TMPDIR/work" + : >"$XCAT_TEST_KAS_LOG" +} + +@test "the build reports each supported architecture once" { + run "$BUILD" --list-architectures + [ "$status" -eq 0 ] + [ "$output" = 'aarch64 +armv7hf +riscv64 +s390x +x86 +x86_64 +ppc64 +ppc64le' ] +} + +@test "the build accepts s390x and runs its kas configuration once" { + run "$BUILD" s390x + [ "$status" -eq 0 ] + [ "$(cat "$XCAT_TEST_KAS_LOG")" = "build $OE_DIR/kas/s390x.yml" ] +} + +@test "the build rejects an unsupported architecture and takes names literally" { + run "$BUILD" not-an-architecture + [ "$status" -eq 2 ] + run "$BUILD" 'x86*' + [ "$status" -eq 2 ] + [ ! -s "$XCAT_TEST_KAS_LOG" ] +} diff --git a/xCAT-test/bats/genesis_openembedded_console.bats b/xCAT-test/bats/genesis_openembedded_console.bats new file mode 100644 index 000000000..0fb892898 --- /dev/null +++ b/xCAT-test/bats/genesis_openembedded_console.bats @@ -0,0 +1,457 @@ +#!/usr/bin/env bats +# +# The Genesis status console is C. setup_file builds its plain-only binary and small test +# programs against state.c, support.c and shell.c with strict warnings. The tests run them +# against a scratch status tree. The Newt interface needs libnewt and is not built here. + +load 'helpers/shell_source' + +CONSOLE_SRC='xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src' +STRICT='-D_POSIX_C_SOURCE=200809L -std=c17 -Wall -Wextra -Wpedantic -Werror' + +setup_file() +{ + command -v "${CC:-cc}" >/dev/null || skip 'a C compiler is required' + local src out + src="$(require_repo_file "$CONSOLE_SRC/console.h")" + src="${src%/console.h}" + out="$BATS_FILE_TMPDIR" + + # shellcheck disable=SC2086 + "${CC:-cc}" $STRICT -DXCAT_CONSOLE_PLAIN_ONLY \ + "$src/main.c" "$src/plain_ui.c" "$src/shell.c" "$src/state.c" "$src/support.c" \ + -o "$out/xcat-genesis-console" + + cat >"$out/header-test.c" <<'C' +#include "console.h" + +#include +#include + +int main(int argc, char **argv) { + for (int index = 1; index < argc; index++) + printf("%d ", xcat_header_context_columns(atoi(argv[index]))); + printf("\n"); + return 0; +} +C + # shellcheck disable=SC2086 + "${CC:-cc}" $STRICT -I "$src" "$out/header-test.c" "$src/support.c" -o "$out/header-test" + + # uname is replaced so the architecture comes from XCAT_TEST_ARCH. + cat >"$out/identity-test.c" <<'C' +#include "console.h" + +#include +#include +#include +#include + +int xcat_test_uname(struct utsname *name) { + const char *architecture = getenv("XCAT_TEST_ARCH"); + + memset(name, 0, sizeof(*name)); + snprintf(name->machine, sizeof(name->machine), "%s", + architecture != NULL ? architecture : "x86_64"); + snprintf(name->release, sizeof(name->release), "test-kernel"); + return 0; +} + +int main(void) { + struct console_state state; + + xcat_load_console_state(&state); + printf("uuid=%s\nfirmware=%s\nboot=%s\n", state.uuid, state.firmware, state.boot_method); + return 0; +} +C + # shellcheck disable=SC2086 + "${CC:-cc}" $STRICT -Duname=xcat_test_uname -I "$src" "$out/identity-test.c" \ + "$src/state.c" "$src/support.c" -o "$out/identity-test" + + # Drives the shared status view: its fields, its change detection and the diagnostics. + cat >"$out/view-test.c" <<'C' +#include "console.h" + +#include +#include + +int main(int argc, char **argv) { + static char text[8192]; + struct console_state state; + struct status_view view; + struct status_view changed; + + xcat_load_console_state(&state); + xcat_build_status_view(&state, &view); + if (argc > 1 && strcmp(argv[1], "identity") == 0) { + printf("%s\n", view.identity); + } else if (argc > 1 && strcmp(argv[1], "fields") == 0) { + for (int field = 0; field < STATUS_FIELD_COUNT; field++) + printf("%d|%s|%s\n", field, view.fields[field].label, view.fields[field].value); + } else if (argc > 1 && strcmp(argv[1], "changes") == 0) { + printf("same %d\n", xcat_status_view_changed(&view, &view)); + for (int field = 0; field < STATUS_FIELD_COUNT; field++) { + changed = view; + strcat(changed.fields[field].value, "x"); + printf("%d %d\n", field, xcat_status_view_changed(&view, &changed)); + } + } else { + xcat_format_diagnostics(&state, text, sizeof(text)); + printf("%s\n", text); + } + return 0; +} +C + # shellcheck disable=SC2086 + "${CC:-cc}" $STRICT -I "$src" "$out/view-test.c" "$src/state.c" "$src/support.c" \ + -o "$out/view-test" + + # execl is replaced: it logs its arguments, then fails like a missing file or runs sh. + cat >"$out/shell-test.c" <<'C' +#include "console.h" + +#include +#include +#include +#include +#include + +int xcat_test_execl(const char *path, const char *arg, ...) { + const char *mode = getenv("XCAT_TEST_EXEC"); + char *const shell[] = {"sh", "-c", (char *)mode, NULL}; + FILE *log = fopen(getenv("XCAT_TEST_EXEC_LOG"), "w"); + + if (log != NULL) { + fprintf(log, "%s %s\n", path, arg); + fclose(log); + } + if (strcmp(mode, "missing") == 0) { + errno = ENOENT; + return -1; + } + return execv("/bin/sh", shell); +} + +int main(void) { + printf("%d\n", xcat_run_maintenance_shell()); + return 0; +} +C + # shellcheck disable=SC2086 + "${CC:-cc}" $STRICT -Dexecl=xcat_test_execl -I "$src" "$out/shell-test.c" \ + "$src/shell.c" "$src/support.c" -o "$out/shell-test" +} + +setup() +{ + [ -x "$BATS_FILE_TMPDIR/view-test" ] || skip 'the console test programs were not built' + CONSOLE="$BATS_FILE_TMPDIR/xcat-genesis-console" + root="$BATS_TEST_TMPDIR" + status_dir="$root/status" + net="$root/sys/class/net/eth0" + dmi="$root/sys/class/dmi/id" + mkdir -p "$status_dir" "$net" "$dmi" "$root/proc" "$root/extensions" "$root/providers" \ + "$root/sys/firmware/efi" + printf 'xcatd=192.0.2.10:3001 BOOTIF=01-52-54-00-00-00-01 gateway=192.0.2.1\n' >"$root/cmdline" + printf '125.90 200.00\n' >"$root/uptime" + printf 'NAME="xCAT Genesis"\nVERSION_ID="0.1"\n' >"$root/os-release.real" + ln -s "$root/os-release.real" "$root/os-release" + printf '%s\n' SCHEMA=1 STATE=READY 'DETAIL=Management network ready on eth0' \ + STARTED_SECONDS=100 UPDATED_SECONDS=100 VERIFIED_SECONDS=100 >"$status_dir/network.env" + printf '%s\n' SCHEMA=1 STATE=ACTION_RECEIVED 'DETAIL=Action osimage received' \ + STARTED_SECONDS=110 UPDATED_SECONDS=120 VERIFIED_SECONDS=120 NODE_NAME=compute01 \ + ACTION=osimage TARGET=rocky9 >"$status_dir/registration.env" + extensions_ready + ipv4_network_state + printf 'osimage=rocky9\n' >"$root/destiny" + printf 'XCAT_NODE_NAME=compute01\n' >"$root/xcat-response.env" + printf 'up\n' >"$net/operstate" + printf '52:54:00:00:00:01\n' >"$net/address" + printf 'TEST-SERIAL-001\n' >"$dmi/product_serial" + printf '11111111-2222-3333-4444-555555555555\n' >"$dmi/product_uuid" + : >"$root/extensions/one.raw" + : >"$root/extensions/two.raw" + ln -s "$root/extensions/one.raw" "$root/extensions/linked.raw" + printf '{}\n' >"$root/providers/one.json" + printf '{}\n' >"$root/providers/two.json" + ln -s "$root/providers/one.json" "$root/providers/linked.json" + + export XCAT_CMDLINE_FILE="$root/cmdline" + export XCAT_UPTIME_FILE="$root/uptime" + export XCAT_OS_RELEASE="$root/os-release" + export XCAT_STATUS_DIR="$status_dir" + export XCAT_STATE_FILE="$root/genesis.env" + export XCAT_DESTINY_FILE="$root/destiny" + export XCAT_RESPONSE_FILE="$root/xcat-response.env" + export XCAT_SYS_ROOT="$root/sys" + export XCAT_PROC_ROOT="$root/proc" + export XCAT_EXTENSION_DIR="$root/extensions" + export XCAT_PROVIDER_DIR="$root/providers" + export XCAT_TEST_ARCH=x86_64 +} + +extensions_ready() +{ + printf '%s\n' SCHEMA=1 STATE=READY 'DETAIL=Genesis extensions loaded' \ + STARTED_SECONDS=105 UPDATED_SECONDS=106 >"$status_dir/extensions.env" +} + +ipv4_network_state() +{ + printf '%s\n' XCATDEST=192.0.2.10:3001 XCAT_INTERFACE=eth0 XCAT_SOURCE_ADDRESS=192.0.2.20 \ + XCAT_SOURCE_PREFIXED_ADDRESS=192.0.2.20/24 XCAT_GATEWAY=192.0.2.1 \ + XCAT_DNS_SERVERS=192.0.2.53 "XCAT_NETWORK_METHOD=${1:-auto}" XCAT_LINK_STATE=up \ + XCAT_MAC_ADDRESS=52:54:00:00:00:01 >"$root/genesis.env" +} + +console_once() +{ + run "$CONSOLE" --once +} + +# $output holds these lines in this order, one after the other. +has_lines() +{ + local expected + expected="$(printf '%s\n' "$@")" + [[ "$output" == *"$expected"* ]] || { printf 'missing:\n%s\n' "$expected" >&2; return 1; } +} + +has_no_match() +{ + ! grep -Eq -- "$1" <<<"$output" +} + +@test "narrow terminals leave no writable header context" { + run "$BATS_FILE_TMPDIR/header-test" 0 19 20 21 80 + [ "$output" = '0 0 0 1 60 ' ] +} + +@test "the plain console renders a status snapshot with explicit, separate fields" { + console_once + [ "$status" -eq 0 ] + has_lines 'xCAT Genesis | ACTION_RECEIVED | in stage 00:00:15' + has_lines 'node: compute01' 'serial: TEST-SERIAL-001' + has_lines 'interface: eth0' 'link: up' 'method: DHCP' 'address: 192.0.2.20/24' 'MAC: 52:54:00:00:00:01' + has_lines 'xCAT server: 192.0.2.10:3001' 'xCAT contact: Action received' + has_lines 'action: Boot assigned image' 'target: rocky9' 'progress: none' + has_no_match '[Ll]ast contact' + has_no_match 'extensions:|providers:|Linux ' + has_no_match $'\e' +} + +@test "the console reads IBM Z identity and reports only a marked boot path" { + rm "$dmi/product_serial" "$dmi/product_uuid" + printf '%s\n' 'Plant: 02' 'Sequence Code: 0000000012345' \ + 'LPAR UUID: 93724168-fda3-429b-8b28-a5d245dcb3ff' \ + 'VM00 UUID: 82038f2a-1344-aaf7-1a85-2a7250be2076' >"$root/proc/sysinfo" + printf 'xcatd=192.0.2.10:3001\n' >"$root/cmdline" + export XCAT_TEST_ARCH=s390x + run "$BATS_FILE_TMPDIR/identity-test" + [ "$status" -eq 0 ] + [ "$output" = 'uuid=82038f2a-1344-aaf7-1a85-2a7250be2076 +firmware=not reported +boot=not reported' ] + printf 'xcatd=192.0.2.10:3001 xcat.bootloader=s390-ccw\n' >"$root/cmdline" + run "$BATS_FILE_TMPDIR/identity-test" + [ "$output" = 'uuid=82038f2a-1344-aaf7-1a85-2a7250be2076 +firmware=s390-ccw BIOS +boot=QEMU TFTP loader' ] +} + +@test "the console uses the LPAR UUID only when the LPAR is the guest" { + rm "$dmi/product_serial" "$dmi/product_uuid" + export XCAT_TEST_ARCH=s390x + printf 'LPAR UUID: 93724168-fda3-429b-8b28-a5d245dcb3ff\n' >"$root/proc/sysinfo" + run "$BATS_FILE_TMPDIR/identity-test" + has_lines 'uuid=93724168-fda3-429b-8b28-a5d245dcb3ff' + printf '%s\n' 'VM00 Control Program: z/VM 7.3.0' \ + 'LPAR UUID: 93724168-fda3-429b-8b28-a5d245dcb3ff' >"$root/proc/sysinfo" + run "$BATS_FILE_TMPDIR/identity-test" + has_lines 'uuid=not reported' +} + +@test "the console reads xCAT parameters after byte 512 of the command line" { + grep -v '^XCATDEST=' "$root/genesis.env" >"$root/genesis.env.new" + mv "$root/genesis.env.new" "$root/genesis.env" + { for _ in $(seq 100); do printf 'quiet '; done + printf 'xcatd=198.51.100.10:3001 BOOTIF=01-52-54-00-00-00-01\n'; } >"$root/cmdline" + console_once + has_lines 'xCAT server: 198.51.100.10:3001' +} + +@test "an extension verification failure stops the main status with a recovery hint" { + printf '%s\n' SCHEMA=1 STATE=FAILED 'DETAIL=extension signature verification failed' \ + STARTED_SECONDS=123 UPDATED_SECONDS=124 CODE=EXTENSION_VERIFICATION_FAILED \ + 'RECOVERY=Check extension images, manifests, signatures, and trusted keys' \ + >"$status_dir/extensions.env" + console_once + [ "$status" -eq 0 ] + has_lines 'error: EXTENSION_VERIFICATION_FAILED: extension signature verification failed' + has_lines 'recovery: Check extension images, manifests, signatures, and trusted keys' +} + +@test "automatic IPv6 and manual network setup have accurate method labels" { + printf '%s\n' 'XCATDEST=[2001:db8::10]:3001' XCAT_INTERFACE=eth0 XCAT_SOURCE_ADDRESS=2001:db8::20 \ + XCAT_SOURCE_PREFIXED_ADDRESS=2001:db8::20/64 XCAT_GATEWAY=2001:db8::1 \ + XCAT_DNS_SERVERS=2001:db8::53 XCAT_NETWORK_METHOD=auto XCAT_LINK_STATE=up \ + XCAT_MAC_ADDRESS=52:54:00:00:00:01 >"$root/genesis.env" + console_once + [ "$status" -eq 0 ] + has_lines 'method: SLAAC/DHCPv6' 'address: 2001:db8::20/64' + ipv4_network_state manual + console_once + [ "$status" -eq 0 ] + has_lines 'method: Static' +} + +@test "action execution becomes the overall state and overrides the registration snapshot" { + printf '%s\n' SCHEMA=1 STATE=RUNNING 'DETAIL=Rebooting into the assigned image' \ + STARTED_SECONDS=122 UPDATED_SECONDS=124 VERIFIED_SECONDS=124 ACTION=install \ + 'TARGET=rocky9 install image' >"$status_dir/action.env" + console_once + [ "$status" -eq 0 ] + has_lines 'xCAT Genesis | RUNNING | in stage 00:00:03' + has_lines 'action: Install assigned image' 'target: rocky9 install image' 'progress: none' +} + +@test "an action failure stays on the main page with the same detail rows" { + printf '%s\n' SCHEMA=1 STATE=FAILED 'DETAIL=Unsigned runimage actions are not supported' \ + STARTED_SECONDS=124 UPDATED_SECONDS=124 CODE=UNSAFE_LEGACY_ACTION \ + 'RECOVERY=Package the operation as a signed Genesis system extension' \ + ACTION=runimage TARGET=legacy.tgz >"$status_dir/action.env" + console_once + [ "$status" -eq 0 ] + has_lines 'error: UNSAFE_LEGACY_ACTION: Unsigned runimage actions are not supported' + has_no_match '^(target|progress):' +} + +@test "a failed component becomes the overall state with an exact error and a recovery hint" { + printf '%s\n' SCHEMA=1 STATE=FAILED 'DETAIL=No valid response from xCAT' \ + STARTED_SECONDS=120 UPDATED_SECONDS=124 CODE=XCAT_RESPONSE_UNAVAILABLE \ + 'RECOVERY=Check xcatd and the management network' >"$status_dir/registration.env" + console_once + [ "$status" -eq 0 ] + has_lines 'xCAT Genesis | FAILED | in stage 00:00:05' + has_lines 'error: XCAT_RESPONSE_UNAVAILABLE: No valid response from xCAT' + has_lines 'xCAT contact: Failed: No valid response from xCAT' + has_lines 'recovery: Check xcatd and the management network' +} + +@test "the retry countdown uses the structured status fields" { + printf '%s\n' SCHEMA=1 STATE=CONTACTING_XCAT 'DETAIL=xCAT has not answered yet' \ + STARTED_SECONDS=120 UPDATED_SECONDS=124 ATTEMPT=2 ATTEMPT_LIMIT=6 NEXT_RETRY_SECONDS=5 \ + >"$status_dir/registration.env" + console_once + [ "$status" -eq 0 ] + has_lines 'action: Boot assigned image' 'target: rocky9' 'progress: Attempt 2 of 6; retry in 4s' +} + +@test "an empty node response is shown as unassigned" { + # A registration record without NODE_NAME, so the response file decides. + printf '%s\n' SCHEMA=1 STATE=CONTACTING_XCAT 'DETAIL=xCAT has not answered yet' \ + STARTED_SECONDS=120 UPDATED_SECONDS=124 >"$status_dir/registration.env" + printf 'XCAT_NODE_NAME=\n' >"$root/xcat-response.env" + console_once + [ "$status" -eq 0 ] + has_lines 'node: unassigned' +} + +@test "the shared status view defines every main-page field" { + run "$BATS_FILE_TMPDIR/view-test" fields + [ "$status" -eq 0 ] + [ "$(wc -l <<<"$output")" -eq 15 ] + [ -z "$(awk -F'|' '$2 == ""' <<<"$output")" ] +} + +@test "plain output prints the shared status view, in order" { + run "$BATS_FILE_TMPDIR/view-test" fields + view="$(cut -d'|' -f3- <<<"$output")" + console_once + plain="$(sed -n 's/^xCAT Genesis | \(.*\) | in stage \(.*\)$/\1\n\2/p; /^[A-Za-z][A-Za-z ]*: /s/^[^:]*: //p' <<<"$output")" + [ "$plain" = "$view" ] +} + +@test "the header identity is the node and serial, or the local hostname before assignment" { + run "$BATS_FILE_TMPDIR/view-test" identity + [ "$output" = 'compute01 | TEST-SERIAL-001' ] + printf '%s\n' SCHEMA=1 STATE=CONTACTING_XCAT 'DETAIL=xCAT has not answered yet' \ + STARTED_SECONDS=120 UPDATED_SECONDS=124 >"$status_dir/registration.env" + printf 'XCAT_NODE_NAME=\n' >"$root/xcat-response.env" + run "$BATS_FILE_TMPDIR/view-test" identity + [ "$output" = "$(hostname) | TEST-SERIAL-001" ] + [[ "$output" != *x86_64* ]] +} + +@test "every shared field but the stage timer takes part in change detection" { + run "$BATS_FILE_TMPDIR/view-test" changes + [ "$status" -eq 0 ] + has_lines 'same 0' + [ "$(grep -c ' 1$' <<<"$output")" -eq 14 ] + has_lines '1 0' +} + +@test "diagnostics separate each section with an empty line and omit component timers" { + run "$BATS_FILE_TMPDIR/view-test" diagnostics + [ "$status" -eq 0 ] + for section in 'Identity' 'System' 'Management network' 'xCAT' 'Action' 'Runtime'; do + grep -A1 -x "$section" <<<"$output" | tail -n1 | grep -qx '' + done + for section in 'System' 'Management network' 'xCAT' 'Action' 'Runtime'; do + grep -B1 -x "$section" <<<"$output" | head -n1 | grep -qx '' + done + has_no_match 'Last contact|Uptime' +} + +@test "the maintenance shell runs the packaged executable and reports a launch failure" { + export XCAT_TEST_EXEC_LOG="$root/exec.log" + XCAT_TEST_EXEC=missing run "$BATS_FILE_TMPDIR/shell-test" + [ "$output" = "$(printf '%d' 2)" ] + [ "$(cat "$XCAT_TEST_EXEC_LOG")" = '/usr/libexec/xcat/genesis-maintenance-shell genesis-maintenance-shell' ] +} + +@test "the maintenance shell accepts any exit status of the shell it started" { + export XCAT_TEST_EXEC_LOG="$root/exec.log" + XCAT_TEST_EXEC='exit 3' run "$BATS_FILE_TMPDIR/shell-test" + [ "$output" = 0 ] +} + +# Run the plain console on a terminal with this input, for 4 s. +plain_on_terminal() +{ + command -v script >/dev/null || skip 'script is required' + run bash -c 'printf "%b" "$1" | timeout 4 script -qec "$2 --plain" /dev/null' \ + plain "$1" "$CONSOLE" +} + +@test "plain mode advertises and accepts the shell command, and confirms root access" { + plain_on_terminal 'shell\nn\n' + has_lines 'Type shell and press Enter for maintenance.' + [[ "$output" == *'Open a root maintenance shell? [y/N]'* ]] + [[ "$output" == *'Maintenance shell cancelled.'* ]] +} + +@test "plain mode opens the shell through the common launcher" { + [ ! -e /usr/libexec/xcat/genesis-maintenance-shell ] || skip 'the host has a maintenance shell' + plain_on_terminal 'shell\ny\n' + [[ "$output" == *'Maintenance shell could not be opened: No such file or directory'* ]] + [[ "$output" == *'Returned to the Genesis status console.'* ]] +} + +@test "diagnostics recognize the xNBA and PXELINUX markers, with an honest PXE fallback" { + for pair in xnba:xNBA pxelinux:PXELINUX; do + printf 'xcatd=192.0.2.10:3001 BOOTIF=01-52-54-00-00-00-01 xcat.bootloader=%s\n' \ + "${pair%%:*}" >"$root/cmdline" + run "$BATS_FILE_TMPDIR/identity-test" + has_lines "boot=${pair#*:}" + done + printf 'xcatd=192.0.2.10:3001 BOOTIF=01-52-54-00-00-00-01\n' >"$root/cmdline" + run "$BATS_FILE_TMPDIR/identity-test" + has_lines 'boot=PXE (unknown loader)' +} + +@test "the stage duration is labelled In stage" { + run "$BATS_FILE_TMPDIR/view-test" fields + has_lines '1|In stage|00:00:15' +} diff --git a/xCAT-test/bats/genesis_openembedded_discovery.bats b/xCAT-test/bats/genesis_openembedded_discovery.bats new file mode 100644 index 000000000..0331a3f5b --- /dev/null +++ b/xCAT-test/bats/genesis_openembedded_discovery.bats @@ -0,0 +1,372 @@ +#!/usr/bin/env bats +# +# Genesis discovery in the OpenEmbedded image. genesis-discover builds the findme inventory +# from a scratch sysfs, procfs and DMI tree and hands it to a send command; the callbacks +# record the xCAT answers; genesis-getcert enrolls a certificate. ipmitool, lldpcli, lsblk, +# ip, openssl, uname and logger are scratch scripts. genesis-udp-send.c is compiled and sends +# to a local UDP listener. + +load 'helpers/shell_source' + +DISCOVERY='xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files' + +setup() +{ + DISCOVER="$(require_repo_file "$DISCOVERY/genesis-discover")" + CALLBACK="$(require_repo_file "$DISCOVERY/genesis-discovery-callback")" + GETCERT="$(require_repo_file "$DISCOVERY/genesis-getcert")" + CREDENTIAL_CALLBACK="$(require_repo_file "$DISCOVERY/genesis-credential-callback")" + + root="$BATS_TEST_TMPDIR" + bin="$root/bin" + state_dir="$root/run" + proc="$root/proc" + dmi="$root/sys/devices/virtual/dmi/id" + eth0="$root/sys/class/net/eth0" + packet="$root/packet.xml" + mkdir -p "$bin" "$state_dir" "$root/keys" "$proc" "$dmi" "$eth0/device" \ + "$root/drivers/virtio_net" "$root/dev" + ln -s "$root/drivers/virtio_net" "$eth0/device/driver" + printf '52:54:00:00:00:02\n' >"$eth0/address" + printf 'PCI_SLOT_NAME=0000:00:03.0\n' >"$eth0/device/uevent" + printf 'Slot 4\n' >"$eth0/device/physical_slot" + : >"$root/dev/ipmi0" + printf 'Acme & Co\n' >"$dmi/sys_vendor" + printf 'Rack \n' >"$dmi/product_name" + printf 'SN-0042\n' >"$dmi/product_serial" + printf '00112233-4455-6677-8899-aabbccddeeff\n' >"$dmi/product_uuid" + printf '%s\n' 'processor : 0' 'vendor_id : Test Vendor' \ + 'model name : Test CPU & Controller' 'processor : 1' >"$proc/cpuinfo" + printf 'MemTotal: 2097152 kB\n' >"$proc/meminfo" + printf '42.00 80.00\n' >"$root/uptime" + printf '%s\n' XCATDEST=192.0.2.10:3001 XCATMASTER=192.0.2.10 XCATPORT=3001 \ + XCAT_INTERFACE=eth0 XCAT_SOURCE_ADDRESS=192.0.2.98 >"$state_dir/genesis.env" + + printf '#!/bin/sh\n[ -z "${XCAT_TEST_LOGGER_FAIL-}" ]\n' >"$bin/logger" + printf '#!/bin/sh\n[ "$1" = "-m" ] && printf "%%s\\n" "${XCAT_TEST_ARCH-x86_64}"\n' >"$bin/uname" + cat >"$bin/ipmitool" <<'SH' +#!/bin/sh +case "$*" in + 'mc info') exit 0 ;; + 'sol info') + [ -z "${XCAT_TEST_NO_SOL-}" ] || exit 1 + printf '%s\n' 'Payload Channel : 1' + ;; + 'lan print 1'|'lan print') + printf '%s\n' 'IP Address Source : Static Address' \ + 'IP Address : 192.0.2.101' \ + 'MAC Address : 52:54:00:aa:bb:cc' + ;; +esac +SH + cat >"$bin/lldpcli" <<'SH' +#!/bin/sh +printf '%s\n' 'lldp.eth0.chassis.name=switch01' \ + 'lldp.eth0.chassis.mgmt-ip=192.0.2.2' \ + 'lldp.eth0.chassis.descr=Test switch' \ + 'lldp.eth0.port.descr=Ethernet1/4' +SH + cat >"$bin/lsblk" <<'SH' +#!/bin/sh +[ -z "${XCAT_TEST_LSBLK_FAIL-}" ] || exit 1 +printf '%s\n' 'vda 21474836480 disk' 'vda1 1073741824 part' +SH + cat >"$bin/ip" <<'SH' +#!/bin/sh +[ -z "${XCAT_TEST_IP_FAIL-}" ] || exit 1 +case "$*" in + '-4 -o address show dev eth0 scope global') + [ -z "$XCAT_TEST_IPV4" ] || printf '2: eth0 inet %s scope global eth0\n' "$XCAT_TEST_IPV4" + ;; + '-6 -o address show dev eth0 scope global') + [ -z "$XCAT_TEST_IPV6" ] || printf '2: eth0 inet6 %s scope global eth0\n' "$XCAT_TEST_IPV6" + ;; +esac +SH + cat >"$bin/openssl" <<'SH' +#!/bin/sh +printf 'openssl %s\n' "$*" >>"$XCAT_TEST_LOG" +out_file() { + while [ "$#" -gt 0 ]; do + if [ "$1" = '-out' ]; then printf '%s\n' "$2"; return; fi + shift + done +} +case "$1" in + genpkey) printf '%s\n' key >"$(out_file "$@")" ;; + pkey) + case " $* " in + *' -pubout '*) + printf '%s\n' '-----BEGIN PUBLIC KEY-----' 'UFVCS0VZ' '-----END PUBLIC KEY-----' + ;; + esac + ;; + dgst) printf '%s\n' signature >"$(out_file "$@")" ;; + base64) printf '%s' U0lH ;; + req) + printf '%s\n' '-----BEGIN CERTIFICATE REQUEST-----' 'Q1NS' \ + '-----END CERTIFICATE REQUEST-----' >"$(out_file "$@")" + ;; + s_client) + cat >"$XCAT_TEST_CREDENTIAL_REQUEST" + cat "$XCAT_TEST_CREDENTIAL_RESPONSE" + ;; + x509) exit 0 ;; +esac +SH + cat >"$bin/send-discovery" <<'SH' +#!/bin/sh +gzip -dc "$1" >"$XCAT_TEST_PACKET" +printf '%s %s\n' "$2" "$3" >>"$XCAT_TEST_LOG" +printf '%s\n' "$XCAT_TEST_RESPONSE" >"$XCAT_DISCOVERY_RESPONSE_FILE" +SH + printf '#!/bin/sh\nprintf "network-refresh %%s\\n" "$1" >>"$XCAT_TEST_LOG"\n' >"$bin/network-refresh" + chmod 0755 "$bin"/* + + export PATH="$bin:$PATH" + export XCAT_DISCOVERY_ATTEMPTS=1 + export XCAT_DISCOVERY_RESPONSE_FILE="$state_dir/discovery-response" + export XCAT_DISCOVERY_RETRY_SECONDS=1 + export XCAT_DISCOVERY_SEND_COMMAND="$bin/send-discovery" + export XCAT_KEY_DIR="$root/keys" + export XCAT_METADATA_FILE="$state_dir/xcat-response.env" + export XCAT_NETWORK_FILE="$state_dir/genesis.env" + export XCAT_NETWORK_REFRESH_COMMAND="$bin/network-refresh" + export XCAT_PROC_ROOT="$proc" + export XCAT_STATE_DIR="$state_dir" + export XCAT_STATUS_COMMAND="$(require_repo_file 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/genesis-status')" + export XCAT_STATUS_DIR="$state_dir/status" + export XCAT_SYS_ROOT="$root" + export XCAT_TEST_LOG="$root/commands.log" + export XCAT_TEST_PACKET="$packet" + export XCAT_TEST_IPV4=192.0.2.98/24 + export XCAT_TEST_IPV6= + export XCAT_TEST_CREDENTIAL_REQUEST="$root/credential-request.xml" + export XCAT_TEST_CREDENTIAL_RESPONSE="$root/credential-response.xml" + export XCAT_TEST_RESPONSE='restart (eth0)' + export XCAT_TEST_ARCH=x86_64 + export XCAT_UPTIME_FILE="$root/uptime" + : >"$XCAT_TEST_LOG" +} + +discover() +{ + run /bin/bash "$DISCOVER" +} + +# The packet holds a line, or several lines, exactly. +in_packet() +{ + grep -Fqx -- "$1" "$packet" || { printf 'not in packet: %s\n' "$1" >&2; return 1; } +} + +not_in_packet() +{ + ! grep -Fq -- "$1" "$packet" +} + +@test "discovery sends a findme inventory of the x86_64 node to the selected xCAT endpoint" { + discover + [ "$status" -eq 0 ] + in_packet 'findme' + in_packet 'x86_64' + not_in_packet 'virtual' + in_packet '2' + in_packet 'Test CPU & Controller' + in_packet '2048MB' + in_packet 'vda:20GB' + in_packet 'Acme & Co:Rack <Node>' + in_packet 'virtio_net|eth0|52:54:00:00:00:02|192.0.2.98/24' + in_packet 'Slot 4' + in_packet 'switch01' + in_packet 'Ethernet1/4' + in_packet '1' + in_packet '192.0.2.101' + in_packet '52:54:00:aa:bb:cc' + grep -A2 -x '' "$packet" | paste -sd' ' | grep -qx ' U0lH ' + grep -qx '192.0.2.10 3001' "$XCAT_TEST_LOG" + grep -qx 'network-refresh restart (eth0)' "$XCAT_TEST_LOG" + grep -qx STATE=READY "$XCAT_STATUS_DIR/discovery.env" +} + +@test "successful discovery does not depend on logging" { + XCAT_TEST_LOGGER_FAIL=1 discover + [ "$status" -eq 0 ] +} + +@test "discovery tolerates devices disappearing during inventory" { + XCAT_TEST_LSBLK_FAIL=1 XCAT_TEST_IP_FAIL=1 discover + [ "$status" -eq 0 ] + not_in_packet '' + in_packet 'virtio_net|eth0|52:54:00:00:00:02|' +} + +@test "discovery accepts a BMC without SOL support, on the default LAN channel" { + XCAT_TEST_NO_SOL=1 discover + [ "$status" -eq 0 ] + in_packet '52:54:00:aa:bb:cc' +} + +@test "discovery accepts an IPv6-only network and sends to the IPv6 endpoint" { + printf '%s\n' 'XCATDEST=[2001:db8::10]:3001' XCATMASTER=2001:db8::10 XCATPORT=3001 \ + XCAT_INTERFACE=eth0 XCAT_SOURCE_ADDRESS=2001:db8::98 >"$state_dir/genesis.env" + XCAT_TEST_IPV4='' XCAT_TEST_IPV6=2001:db8::98/64 discover + [ "$status" -eq 0 ] + in_packet 'virtio_net|eth0|52:54:00:00:00:02|2001:db8::98/64' + in_packet '2001:db8::98/64' + not_in_packet '' + grep -qx '2001:db8::10 3001' "$XCAT_TEST_LOG" +} + +@test "an unmatched discovery fails with a specific code, and virtual DMI is reported" { + printf 'QEMU\n' >"$dmi/sys_vendor" + XCAT_TEST_RESPONSE=processed discover + [ "$status" -ne 0 ] + in_packet 'virtual' + grep -qx CODE=DISCOVERY_NOT_MATCHED "$XCAT_STATUS_DIR/discovery.env" +} + +@test "Power discovery reports the device-tree identity" { + rm "$dmi"/* + mkdir -p "$proc/device-tree" + printf 'IBM,9009-42A\0' >"$proc/device-tree/model" + printf 'IBM,02AB123\0' >"$proc/device-tree/system-id" + printf 'cpu : POWER9\ncpu : POWER9\nplatform : PowerNV\n' >"$proc/cpuinfo" + XCAT_TEST_ARCH=ppc64le XCAT_TEST_RESPONSE=restart discover + [ "$status" -eq 0 ] + in_packet 'ppc64le' + in_packet '9009-42A' + in_packet '02AB123' + in_packet 'PowerNV' + in_packet '2' + in_packet '9009-42a-02ab123-525400000002' +} + +@test "s390x discovery reports the closest guest identity from proc sysinfo" { + rm "$dmi"/* + printf '%s\n' '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' >"$proc/sysinfo" + printf '%s\n' 'vendor_id : IBM/S390' 'processor 0: version = 00' \ + 'processor 1: version = 00' >"$proc/cpuinfo" + XCAT_TEST_ARCH=s390x XCAT_TEST_RESPONSE=restart discover + [ "$status" -eq 0 ] + in_packet 's390x' + in_packet 'virtual' + in_packet '3931-A01' + not_in_packet '' + in_packet 'KVM/Linux' + in_packet '2' + in_packet 'IBM/S390' + in_packet '82038f2a-1344-aaf7-1a85-2a7250be2076' +} + +@test "a UUID-less s390x guest omits the shared serial and keeps the MAC-based identity" { + rm "$dmi"/* + printf '%s\n' 'Manufacturer: IBM' 'Type: 3931' \ + 'Model: 701 A01' \ + 'LPAR UUID: 93724168-fda3-429b-8b28-a5d245dcb3ff' \ + 'VM00 Control Program: KVM/Linux' >"$proc/sysinfo" + printf 'vendor_id : IBM/S390\n' >"$proc/cpuinfo" + XCAT_TEST_ARCH=s390x XCAT_TEST_RESPONSE=restart discover + [ "$status" -eq 0 ] + not_in_packet '' + in_packet '3931-a01-unknown-525400000002' +} + +@test "without a send command, discovery sends through the source-port-aware UDP sender" { + unset XCAT_DISCOVERY_SEND_COMMAND + discover + [[ "$output" == *'/usr/libexec/xcat/genesis-udp-send'* ]] +} + +@test "the discovery callback records a restart response and rejects an unknown one" { + run /bin/bash -c 'printf "%s" "restart (eth0)" | /bin/bash "$1"' callback "$CALLBACK" + [ "$status" -eq 0 ] + [ "$(cat "$XCAT_DISCOVERY_RESPONSE_FILE")" = 'restart (eth0)' ] + run /bin/bash -c 'printf "%s" malformed | /bin/bash "$1"' callback "$CALLBACK" + [ "$status" -ne 0 ] +} + +@test "the certificate client installs only the PEM certificate from a signed x509 request" { + printf '%s\n' 'XCATDEST=[2001:db8::10]:3001' XCATMASTER=2001:db8::10 XCATPORT=3001 \ + XCAT_INTERFACE=eth0 XCAT_SOURCE_ADDRESS=2001:db8::98 >"$state_dir/genesis.env" + printf '%s\n' XCAT_NODE_NAME=node042 XCAT_DESTINY=standby >"$XCAT_METADATA_FILE" + printf '%s\n' '' '' '-----BEGIN CERTIFICATE-----' 'Q0VSVA==' \ + '-----END CERTIFICATE-----' '' '' >"$XCAT_TEST_CREDENTIAL_RESPONSE" + run /bin/bash "$GETCERT" + [ "$status" -eq 0 ] + [ "$(cat "$XCAT_KEY_DIR/cert.pem")" = '-----BEGIN CERTIFICATE----- +Q0VSVA== +-----END CERTIFICATE-----' ] + grep -qx 'getcredentials' "$XCAT_TEST_CREDENTIAL_REQUEST" + grep -qx '300' "$XCAT_TEST_CREDENTIAL_REQUEST" + grep -A2 -x '' "$XCAT_TEST_CREDENTIAL_REQUEST" | paste -sd' ' | grep -qx ' U0lH ' + grep -Fq 'openssl s_client -connect [2001:db8::10]:3001 -quiet' "$XCAT_TEST_LOG" + grep -qx STATE=READY "$XCAT_STATUS_DIR/credentials.env" + XCAT_TEST_LOGGER_FAIL=1 run /bin/bash "$GETCERT" + [ "$status" -eq 0 ] +} + +@test "the certificate client rejects an unsafe node name with a specific code" { + printf '%s\n' XCAT_NODE_NAME=invalid/name >"$XCAT_METADATA_FILE" + run /bin/bash "$GETCERT" + [ "$status" -ne 0 ] + grep -qx CODE=CERTIFICATE_NODE_IDENTITY_MISSING "$XCAT_STATUS_DIR/credentials.env" +} + +@test "the credential callback answers the xCAT challenge before xcatd closes the connection" { + run /bin/bash -c 'printf "%s" "CREDOKBYYOU?" | /bin/bash "$1"' callback "$CREDENTIAL_CALLBACK" + [ "$status" -eq 0 ] + coproc CB { /bin/bash "$CREDENTIAL_CALLBACK" 2>/dev/null; } + printf 'CREDOKBYYOU?\n' >&"${CB[1]}" + answer= + IFS= read -t 2 -r answer <&"${CB[0]}" || true + eval "exec ${CB[1]}>&-" + wait "$CB_PID" || true + [ "$answer" = CREDOKBYME ] + run /bin/bash -c 'printf "%s" unknown | /bin/bash "$1"' callback "$CREDENTIAL_CALLBACK" + [ "$status" -ne 0 ] +} + +# Compile genesis-udp-send.c with the recipe's strict flags, and start a UDP listener on a +# loopback address. The listener writes the sender's source port and the payload it got. +start_udp_sender_test() +{ + local family="$1" address="$2" + command -v cc >/dev/null || skip 'cc is required' + [ "$(id -u)" -eq 0 ] || skip 'binding source port 301 needs root' + cc -std=c17 -Wall -Wextra -Werror -o "$root/genesis-udp-send" \ + "$(require_repo_file "$DISCOVERY/genesis-udp-send.c")" + perl -MSocket -MIO::Socket::IP -e ' + my $s = IO::Socket::IP->new(LocalHost => $ARGV[0], LocalPort => 0, Proto => "udp") + or exit 3; + open(my $p, ">", "$ARGV[1].port") or die; print {$p} $s->sockport, "\n"; close $p; + $s->recv(my $data, 65535) or die; + open(my $o, ">", "$ARGV[1].got") or die; print {$o} $s->peerport, " ", $data; close $o; + ' "$address" "$root/udp-$family" /dev/null 2>&1 3>&- & + listener=$! + for _ in $(seq 50); do [ -s "$root/udp-$family.port" ] && break; sleep 0.1; done + [ -s "$root/udp-$family.port" ] || { wait "$listener"; skip "no $family loopback"; } +} + +@test "the UDP sender sends the discovery packet from source port 301 over IPv4" { + start_udp_sender_test ipv4 127.0.0.1 + printf 'findme' >"$root/packet" + run "$root/genesis-udp-send" "$root/packet" 127.0.0.1 "$(cat "$root/udp-ipv4.port")" + [ "$status" -eq 0 ] + wait "$listener" + [ "$(cat "$root/udp-ipv4.got")" = '301 findme' ] +} + +@test "the UDP sender resolves an IPv6 endpoint and sends from source port 301" { + start_udp_sender_test ipv6 ::1 + printf 'findme' >"$root/packet" + run "$root/genesis-udp-send" "$root/packet" ::1 "$(cat "$root/udp-ipv6.port")" + [ "$status" -eq 0 ] + wait "$listener" + [ "$(cat "$root/udp-ipv6.got")" = '301 findme' ] +} diff --git a/xCAT-test/bats/genesis_openembedded_export.bats b/xCAT-test/bats/genesis_openembedded_export.bats new file mode 100644 index 000000000..38affab73 --- /dev/null +++ b/xCAT-test/bats/genesis_openembedded_export.bats @@ -0,0 +1,45 @@ +#!/usr/bin/env bats +# +# oe/export copies an OpenEmbedded Genesis image out of a Yocto deploy directory, with its +# SBOM, its VEX report, a format manifest and SHA256SUMS. The deploy directory here is a +# scratch tree in the layout Yocto writes. + +load 'helpers/shell_source' + +setup() +{ + EXPORT="$(require_repo_file 'xCAT-genesis-base/oe/export')" + machine=xcat-genesis-x86-64 + image="xcat-genesis-image-$machine.rootfs" + deploy="$BATS_TEST_TMPDIR/deploy" + out_dir="$BATS_TEST_TMPDIR/export" + machine_dir="$deploy/images/$machine" + license_dir="$deploy/licenses/xcat_genesis_x86_64/$image" + mkdir -p "$machine_dir" "$license_dir" + printf kernel >"$machine_dir/bzImage" + printf initramfs >"$machine_dir/$image.cpio.gz" + printf packages >"$machine_dir/$image.manifest" + printf '{}' >"$machine_dir/$image.spdx.json" + printf '{}' >"$machine_dir/$image.vex.json" + printf licenses >"$license_dir/license.manifest" +} + +@test "the image exports, with its SBOM and VEX report" { + run "$EXPORT" x86_64 "$deploy" "$out_dir" + [ "$status" -eq 0 ] + [ -f "$out_dir/image.spdx.json" ] + [ -f "$out_dir/image.vex.json" ] +} + +@test "the export identifies its format and architecture" { + "$EXPORT" x86_64 "$deploy" "$out_dir" + [ "$(cat "$out_dir/xcat-genesis.manifest")" = "format=xcat-genesis +version=1 +architecture=x86_64" ] +} + +@test "the export manifest has a checksum, and all exported checksums verify" { + "$EXPORT" x86_64 "$deploy" "$out_dir" + grep -Eq '^[0-9a-f]{64} xcat-genesis\.manifest$' "$out_dir/SHA256SUMS" + (cd "$out_dir" && sha256sum -c SHA256SUMS >/dev/null) +} diff --git a/xCAT-test/bats/genesis_openembedded_extensions.bats b/xCAT-test/bats/genesis_openembedded_extensions.bats new file mode 100644 index 000000000..84465faf5 --- /dev/null +++ b/xCAT-test/bats/genesis_openembedded_extensions.bats @@ -0,0 +1,154 @@ +#!/usr/bin/env bats +# +# Genesis extensions are signed squashfs images. oe/scripts/sign-extension signs a manifest, +# oe/export-extension exports a signed bundle, and genesis-sysext verifies and installs an +# extension in the running image. systemd-sysext and genesis-status are shell functions +# written to a scratch PATH; they log their arguments. + +load 'helpers/shell_source' + +EXT_FILES='xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-extensions/files' + +setup() +{ + command -v openssl >/dev/null || skip 'openssl is required' + command -v jq >/dev/null || skip 'jq is required' + LOADER="$(require_repo_file "$EXT_FILES/genesis-sysext")" + SIGNER="$(require_repo_file 'xCAT-genesis-base/oe/scripts/sign-extension')" + EXPORTER="$(require_repo_file 'xCAT-genesis-base/oe/export-extension')" + + root="$BATS_TEST_TMPDIR" + mkdir -p "$root/bin" "$root/keys" "$root/run" + image="$root/xcat-smoke.squashfs-zst" + manifest="$root/xcat-smoke.manifest.json" + signature="$root/xcat-smoke.sig" + private_key="$root/private.pem" + public_key="$root/keys/xcat-release.pem" + printf 'extension payload\n' >"$image" + printf 'ID=xcat-genesis\nVERSION_ID=0.1\n' >"$root/os-release" + : >"$root/commands.log" + : >"$root/status.log" + printf '#!/bin/sh\nprintf "systemd-sysext %%s\\n" "$*" >>"$XCAT_TEST_LOG"\n' >"$root/bin/systemd-sysext" + printf '#!/bin/sh\nprintf "%%s\\n" "$*" >>"$XCAT_STATUS_LOG"\n' >"$root/bin/genesis-status" + chmod 0755 "$root/bin/systemd-sysext" "$root/bin/genesis-status" + + openssl genpkey -algorithm ED25519 -out "$private_key" 2>/dev/null + openssl pkey -in "$private_key" -pubout -out "$public_key" + hash="$(sha256sum -- "$image" | cut -c1-64)" + write_manifest "$manifest" '.' + "$SIGNER" "$manifest" "$private_key" "$signature" + + export PATH="$root/bin:$PATH" + export XCAT_GENESIS_EXTENSION_KEY_DIR="$root/keys" + export XCAT_GENESIS_EXTENSION_RUN_DIR="$root/run" + export XCAT_GENESIS_OS_RELEASE="$root/os-release" + export XCAT_GENESIS_STATUS_COMMAND="$root/bin/genesis-status" + export XCAT_GENESIS_UNAME_M=x86_64 + export XCAT_GENESIS_UNAME_R=6.18.24-test + export XCAT_TEST_LOG="$root/commands.log" + export XCAT_STATUS_LOG="$root/status.log" +} + +# Write a manifest for $image, changed by a jq filter. +write_manifest() +{ + local path="$1" filter="$2" + jq -n --arg sha "$hash" '{ + architecture: "x86_64", capabilities: ["diagnostic.smoke"], genesis_release: "0.1", + kernel_modules: false, kernel_release: null, key_id: "xcat-release", + license_class: "open", name: "xcat-smoke", pci_ids: [], schema: 1, + sha256: $sha, version: "1.0" + }' | jq -S "$filter" >"$path" +} + +# Write and sign a changed manifest, then verify it against $image. +verify_changed() +{ + local name="$1" filter="$2" + write_manifest "$root/$name.json" "$filter" + "$SIGNER" "$root/$name.json" "$private_key" "$root/$name.sig" + /bin/bash "$LOADER" verify "$root/$name.json" "$image" "$root/$name.sig" +} + +@test "the extension manifest is signed" { + [ -s "$signature" ] +} + +@test "the built extension exports as a signed bundle with its key and checksums" { + stem=xcat-genesis-extension-smoke-xcat-genesis-x86-64 + machine_dir="$root/deploy/images/xcat-genesis-x86-64" + mkdir -p "$machine_dir" + cp "$image" "$machine_dir/$stem.sysext.squashfs-zst" + cp "$manifest" "$machine_dir/$stem.sysext.manifest.json" + run "$EXPORTER" x86_64 xcat-genesis-extension-smoke "$root/deploy" \ + "$private_key" "$public_key" "$root/bundle" + [ "$status" -eq 0 ] + [ -f "$root/bundle/extensions/$stem.squashfs-zst" ] + [ -f "$root/bundle/extensions/$stem.manifest.json" ] + [ -f "$root/bundle/extensions/$stem.sig" ] + [ -f "$root/bundle/extension-keys/xcat-release.pem" ] + (cd "$root/bundle" && sha256sum -c SHA256SUMS >/dev/null) +} + +@test "a valid extension is accepted" { + run /bin/bash "$LOADER" verify "$manifest" "$image" "$signature" + [ "$status" -eq 0 ] +} + +@test "i586, armv7l and s390x runtimes use their extension identities" { + XCAT_GENESIS_UNAME_M=i586 verify_changed x86 '.architecture = "x86"' + XCAT_GENESIS_UNAME_M=armv7l verify_changed armv7hf '.architecture = "armv7hf"' + XCAT_GENESIS_UNAME_M=s390x verify_changed s390x '.architecture = "s390x"' +} + +@test "a valid extension is installed under its manifest name, and extensions are refreshed" { + run /bin/bash "$LOADER" install "$manifest" "$image" "$signature" + [ "$status" -eq 0 ] + [ -f "$root/run/xcat-smoke.raw" ] + grep -qx 'systemd-sysext refresh' "$root/commands.log" +} + +@test "loading a valid extension directory publishes the active and ready states" { + run /bin/bash "$LOADER" load-all "$root" + [ "$status" -eq 0 ] + grep -qx 'extensions RUNNING Verifying Genesis extensions' "$root/status.log" + grep -qx 'extensions READY Genesis extensions loaded' "$root/status.log" +} + +@test "an invalid signature is rejected" { + printf '%064d' 0 | tr 0 x >"$root/bad.sig" + run /bin/bash "$LOADER" verify "$manifest" "$image" "$root/bad.sig" + [ "$status" -ne 0 ] + [[ "$output" == *'extension signature verification failed'* ]] +} + +@test "a wrong architecture, release, digest or kernel ABI is rejected" { + run verify_changed arch '.architecture = "riscv64"' + [ "$status" -ne 0 ] + [[ "$output" == *'extension architecture riscv64 does not match x86_64'* ]] + run verify_changed release '.genesis_release = "9.9"' + [ "$status" -ne 0 ] + [[ "$output" == *'extension release 9.9 does not match 0.1'* ]] + run verify_changed digest ".sha256 = \"$(printf '%064d' 0)\"" + [ "$status" -ne 0 ] + [[ "$output" == *'extension digest does not match'* ]] + run verify_changed abi '.kernel_modules = true | .kernel_release = "0.0-wrong"' + [ "$status" -ne 0 ] + [[ "$output" == *'extension kernel release does not match'* ]] +} + +@test "a linked manifest is rejected" { + ln -s "$manifest" "$root/linked.json" + run /bin/bash "$LOADER" verify "$root/linked.json" "$image" "$signature" + [ "$status" -ne 0 ] + [[ "$output" == *"invalid extension input: $root/linked.json"* ]] +} + +@test "an empty extension directory is rejected, with a structured recovery status" { + mkdir -p "$root/empty" + run /bin/bash "$LOADER" load-all "$root/empty" + [ "$status" -ne 0 ] + grep -q '^extensions FAILED no extension manifests found in:' "$root/status.log" + grep -q 'CODE=EXTENSION_VERIFICATION_FAILED .*RECOVERY=Check extension images, manifests, signatures, and trusted keys' \ + "$root/status.log" +} diff --git a/xCAT-test/bats/genesis_openembedded_hardware.bats b/xCAT-test/bats/genesis_openembedded_hardware.bats new file mode 100644 index 000000000..11de065ef --- /dev/null +++ b/xCAT-test/bats/genesis_openembedded_hardware.bats @@ -0,0 +1,264 @@ +#!/usr/bin/env bats +# +# genesis-hardware runs in the OpenEmbedded Genesis image. It lists hardware providers from +# their JSON manifests, runs a provider capability, and writes an audit trail for each +# destructive change. The nvme, mstflint and iprutils providers wrap one tool each. The +# providers, the tools and the sysfs trees here are scratch files. + +load 'helpers/shell_source' + +HW_FILES='xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/files' + +setup() +{ + command -v jq >/dev/null || skip 'jq is required' + DISPATCHER="$(require_repo_file "$HW_FILES/genesis-hardware")" + root="$BATS_TEST_TMPDIR" + bin="$root/bin" + manifests="$root/manifests" + executables="$root/providers" + audit="$root/audit/hardware.jsonl" + request="$root/request.json" + mkdir -p "$bin" "$manifests" "$executables" + + cat >"$executables/mock" <<'SH' +#!/bin/sh +case "$1" in + probe) + printf '%s\n' '{"detected":true,"devices":["controller0"]}' + ;; + run) + case "$2" in + storage.inventory) printf '%s\n' '{"devices":["controller0"]}' ;; + storage.array.create) printf '{"created":"%s"}\n' "$3" ;; + storage.array.delete) printf '%s\n' 'delete failed' >&2; exit 9 ;; + storage.logs) printf '%s\n' 'not-json' ;; + *) exit 8 ;; + esac + ;; + *) exit 7 ;; +esac +SH + chmod 0755 "$executables/mock" + jq -n '{ + capabilities: [ + {destructive: false, name: "storage.inventory"}, + {destructive: false, name: "storage.logs"}, + {destructive: true, name: "storage.array.create"}, + {destructive: true, name: "storage.array.delete"} + ], + kind: "storage", name: "mock", schema: 1, version: "1.0" + }' >"$manifests/mock.json" + printf '{"level":"1"}\n' >"$request" + + export PATH="$bin:$PATH" + export XCAT_GENESIS_HARDWARE_AUDIT="$audit" + export XCAT_GENESIS_PROVIDER_DIR="$manifests" + export XCAT_GENESIS_PROVIDER_EXEC_DIR="$executables" + export XCAT_GENESIS_PROVIDER_TIMEOUT=5 +} + +hardware() +{ + run /bin/bash "$DISPATCHER" "$@" +} + +create_array() +{ + hardware run mock storage.array.create "$@" +} + +audit_phases() +{ + jq -r .phase "$audit" | paste -sd' ' +} + +@test "the provider list returns the manifest" { + hardware providers + [ "$status" -eq 0 ] + [ "$(jq -r '.providers[0].name' <<<"$output")" = mock ] +} + +@test "the provider capabilities are returned" { + hardware capabilities mock + [ "$status" -eq 0 ] + [ "$(jq '.capabilities | length' <<<"$output")" -eq 4 ] +} + +@test "a provider probe returns the detected hardware" { + hardware probe mock + [ "$status" -eq 0 ] + [ "$(jq '.probes[0].result.detected' <<<"$output")" = true ] +} + +@test "a read-only capability succeeds, is normalized, and is not audited as a change" { + hardware run mock storage.inventory + [ "$status" -eq 0 ] + [ "$(jq -r '.result.devices[0]' <<<"$output")" = controller0 ] + [ ! -e "$audit" ] +} + +@test "a destructive capability requires a task identity, an exact device and a request" { + create_array + [ "$status" -ne 0 ] + [[ "$output" == *'destructive capability requires a task identity'* ]] + create_array --task-id task-1 --request "$request" + [ "$status" -ne 0 ] + [[ "$output" == *'destructive capability requires an exact device identity'* ]] + create_array --task-id task-1 --device-id controller0 + [ "$status" -ne 0 ] + [[ "$output" == *'destructive capability requires a request file'* ]] + create_array --task-id task-1 --device-id all --request "$request" + [ "$status" -ne 0 ] + [[ "$output" == *'destructive capability requires an exact device identity'* ]] +} + +@test "an authorized destructive capability succeeds with a closed audit trail" { + create_array --task-id task-1 --device-id controller0 --request "$request" + [ "$status" -eq 0 ] + [ "$(jq -r '.result.created' <<<"$output")" = controller0 ] + [ "$(audit_phases)" = 'started completed' ] + [ "$(head -n1 "$audit" | jq -r .task_id)" = task-1 ] + head -n1 "$audit" | jq -r .request_sha256 | grep -Eqx '[0-9a-f]{64}' +} + +@test "a provider failure is returned, and the failed change has a closed audit trail" { + create_array --task-id task-1 --device-id controller0 --request "$request" + hardware run mock storage.array.delete --task-id task-2 --device-id controller0 --request "$request" + [ "$status" -ne 0 ] + [[ "$output" == *'provider mock failed capability storage.array.delete'* ]] + [ "$(audit_phases)" = 'started completed started failed' ] +} + +@test "invalid provider JSON and an undeclared capability are rejected" { + hardware run mock storage.logs + [ "$status" -ne 0 ] + [[ "$output" == *'provider mock returned invalid JSON'* ]] + hardware run mock storage.unknown + [ "$status" -ne 0 ] + [[ "$output" == *'provider mock does not support storage.unknown'* ]] +} + +@test "a linked or invalid provider manifest is rejected" { + # The target is a valid manifest for "linked", so only the link itself is wrong. + jq '.name = "linked"' "$manifests/mock.json" >"$root/linked.json" + ln -s "$root/linked.json" "$manifests/linked.json" + hardware providers + [ "$status" -ne 0 ] + [[ "$output" == *'invalid provider manifest: linked'* ]] + rm "$manifests/linked.json" + printf '{}\n' >"$manifests/invalid.json" + hardware providers + [ "$status" -ne 0 ] + [[ "$output" == *'invalid provider manifest: invalid'* ]] +} + +@test "a linked provider executable is rejected" { + mv "$executables/mock" "$executables/mock.real" + ln -s "$executables/mock.real" "$executables/mock" + hardware probe mock + [ "$status" -ne 0 ] + [[ "$output" == *'provider executable not found: mock'* ]] +} + +@test "a linked hardware request is rejected" { + ln -s "$request" "$root/linked-request.json" + create_array --task-id task-3 --device-id controller0 --request "$root/linked-request.json" + [ "$status" -ne 0 ] + [[ "$output" == *"invalid request file: $root/linked-request.json"* ]] +} + +@test "an invalid provider timeout is rejected" { + XCAT_GENESIS_PROVIDER_TIMEOUT=0 hardware providers + [ "$status" -ne 0 ] + [[ "$output" == *'invalid provider timeout'* ]] +} + +@test "a linked audit lock is rejected, and its target is untouched" { + mkdir -p "$(dirname "$audit")" + printf 'unchanged\n' >"$root/lock-target" + ln -s "$root/lock-target" "$audit.lock" + create_array --task-id task-3 --device-id controller0 --request "$request" + [ "$status" -ne 0 ] + [[ "$output" == *'invalid hardware audit lock'* ]] + [ "$(cat "$root/lock-target")" = unchanged ] +} + +@test "the NVMe provider returns exact controllers and queries the selected one" { + provider="$(require_repo_file "$HW_FILES/provider-nvme")" + printf '#!/bin/sh\nprintf "{\\"arguments\\":\\"%%s\\"}\\n" "$*"\n' >"$bin/nvme" + chmod 0755 "$bin/nvme" + mkdir -p "$root/sys/class/nvme/nvme0" "$root/dev" + : >"$root/dev/nvme0" + export XCAT_GENESIS_DEV_DIR="$root/dev" XCAT_GENESIS_SYS_CLASS_NVME="$root/sys/class/nvme" + + run /bin/bash "$provider" probe + [ "$status" -eq 0 ] + [ "$(jq -r '.devices[0]' <<<"$output")" = nvme0 ] + + run /bin/bash "$provider" run storage.health nvme0 - + [ "$status" -eq 0 ] + jq -r .arguments <<<"$output" | grep -Eq 'smart-log .*/nvme0 -o json' + + run /bin/bash "$provider" run storage.health nvme0n1 - + [ "$status" -ne 0 ] + [[ "$output" == *'invalid NVMe controller identity'* ]] +} + +@test "the mstflint provider returns Mellanox devices only, with the tool output" { + provider="$(require_repo_file "$HW_FILES/provider-mstflint")" + printf '#!/bin/sh\nprintf "%%s\\n" "firmware query"\n' >"$bin/mstflint" + chmod 0755 "$bin/mstflint" + pci="$root/sys/bus/pci/devices" + mkdir -p "$pci/0000:03:00.0" "$pci/0000:04:00.0" + printf '0x15b3\n' >"$pci/0000:03:00.0/vendor" + printf '0x8086\n' >"$pci/0000:04:00.0/vendor" + export XCAT_GENESIS_SYS_PCI_DEVICES="$pci" + + run /bin/bash "$provider" probe + [ "$status" -eq 0 ] + [ "$(jq -c .devices <<<"$output")" = '["0000:03:00.0"]' ] + + run /bin/bash "$provider" run network.firmware.inventory 0000:03:00.0 - + [ "$status" -eq 0 ] + [ "$(jq -r .raw <<<"$output")" = 'firmware query' ] + + run /bin/bash "$provider" run network.firmware.inventory 0000:04:00.0 - + [ "$status" -ne 0 ] + [[ "$output" == *'device is not NVIDIA or Mellanox: 0000:04:00.0'* ]] +} + +@test "the iprutils provider returns Power RAID hosts, the tool output, and rejects changes" { + provider="$(require_repo_file "$HW_FILES/provider-iprutils")" + cat >"$bin/iprconfig" <<'SH' +#!/bin/sh +case "$*" in + '-c show-config') printf '%s\n' 'Power RAID configuration' ;; + '-c show-arrays') printf '%s\n' 'Power RAID arrays healthy' ;; + '-c show-ucode-levels') printf '%s\n' 'Power RAID firmware levels' ;; + *) exit 2 ;; +esac +SH + chmod 0755 "$bin/iprconfig" + hosts="$root/sys/class/scsi_host" + mkdir -p "$hosts/host0" "$hosts/host1" + printf 'ipr\n' >"$hosts/host0/proc_name" + printf 'megaraid_sas\n' >"$hosts/host1/proc_name" + export XCAT_GENESIS_SYS_SCSI_HOSTS="$hosts" + + run /bin/bash "$provider" probe + [ "$status" -eq 0 ] + [ "$(jq -c .devices <<<"$output")" = '["host0"]' ] + + for pair in 'storage.inventory:Power RAID configuration' \ + 'storage.health:Power RAID arrays healthy' \ + 'storage.firmware.inventory:Power RAID firmware levels'; do + run /bin/bash "$provider" run "${pair%%:*}" host0 - + [ "$status" -eq 0 ] + [ "$(jq -r .raw <<<"$output")" = "${pair#*:}" ] + done + + run /bin/bash "$provider" run storage.array.create host0 - + [ "$status" -ne 0 ] + [[ "$output" == *'unsupported capability: storage.array.create'* ]] +} diff --git a/xCAT-test/bats/genesis_openembedded_metrics.bats b/xCAT-test/bats/genesis_openembedded_metrics.bats new file mode 100644 index 000000000..4df429fac --- /dev/null +++ b/xCAT-test/bats/genesis_openembedded_metrics.bats @@ -0,0 +1,86 @@ +#!/usr/bin/env bats +# +# genesis-metrics runs in the OpenEmbedded Genesis image and records the boot-ready time and +# memory use. oe/report runs on the build host and reports the image sizes with those metrics, +# and the growth against a baseline. Inputs are scratch files named by environment variables. + +load 'helpers/shell_source' + +INIT_FILES='xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files' + +setup() +{ + METRICS="$(require_repo_file "$INIT_FILES/genesis-metrics")" + REPORT="$(require_repo_file 'xCAT-genesis-base/oe/report')" + export XCAT_REGISTRATION_STATUS_FILE="$BATS_TEST_TMPDIR/registration.env" + export XCAT_MEMINFO_FILE="$BATS_TEST_TMPDIR/meminfo" + export XCAT_UPTIME_FILE="$BATS_TEST_TMPDIR/uptime" + runtime="$BATS_TEST_TMPDIR/metrics.env" + printf 'SCHEMA=1\nSTATE=ACTION_RECEIVED\nDETAIL=Destiny shell\nUPDATED_SECONDS=42\n' \ + >"$XCAT_REGISTRATION_STATUS_FILE" + printf 'MemTotal: 1048576 kB\nMemFree: 524288 kB\nMemAvailable: 786432 kB\n' \ + >"$XCAT_MEMINFO_FILE" + printf '45.92 12.34\n' >"$XCAT_UPTIME_FILE" + + image_dir="$BATS_TEST_TMPDIR/image" + mkdir -p "$image_dir" + printf 'kernel\n' >"$image_dir/kernel" + printf 'cpio payload\n' | gzip -c >"$image_dir/initramfs.cpio.gz" +} + +EXPECTED_RUNTIME='SCHEMA=1 +BOOT_READY_SECONDS=42 +CAPTURE_UPTIME_SECONDS=45 +MEMORY_TOTAL_KIB=1048576 +MEMORY_AVAILABLE_KIB=786432 +MEMORY_USED_KIB=262144' + +# The value of one NAME=VALUE line of $output. +value() +{ + sed -n "s/^$1=//p" <<<"$output" +} + +@test "runtime metrics are collected: boot time and memory use" { + run "$METRICS" + [ "$status" -eq 0 ] + [ "$(sort <<<"$output")" = "$(sort <<<"$EXPECTED_RUNTIME")" ] +} + +@test "runtime metrics are stored atomically, complete and readable" { + run "$METRICS" --output "$runtime" + [ "$status" -eq 0 ] + [ "$(cat "$runtime")" = "$EXPECTED_RUNTIME" ] + [ "$(stat -c %a "$runtime")" = 644 ] +} + +@test "image and runtime metrics are reported together" { + "$METRICS" --output "$runtime" + run "$REPORT" --runtime "$runtime" x86_64 "$image_dir" + [ "$status" -eq 0 ] + [ "$(value ARCHITECTURE)" = x86_64 ] + [ "$(value KERNEL_BYTES)" = "$(stat -c %s "$image_dir/kernel")" ] + [ "$(value COMPRESSED_INITRAMFS_BYTES)" = "$(stat -c %s "$image_dir/initramfs.cpio.gz")" ] + [ "$(value UNPACKED_INITRAMFS_BYTES)" = 13 ] + [ "$(value BOOT_READY_SECONDS)" = 42 ] + [ "$(value MEMORY_USED_KIB)" = 262144 ] +} + +@test "the report compares against a matching baseline and reports the growth" { + "$METRICS" --output "$runtime" + baseline="$BATS_TEST_TMPDIR/baseline.env" + { + printf 'SCHEMA=1\nARCHITECTURE=x86_64\n' + printf 'KERNEL_BYTES=%d\n' $(($(stat -c %s "$image_dir/kernel") - 2)) + printf 'COMPRESSED_INITRAMFS_BYTES=%d\n' $(($(stat -c %s "$image_dir/initramfs.cpio.gz") - 3)) + printf 'UNPACKED_INITRAMFS_BYTES=%d\n' $((13 - 4)) + printf 'BOOT_READY_SECONDS=40\nMEMORY_USED_KIB=250000\n' + } >"$baseline" + run "$REPORT" --runtime "$runtime" --baseline "$baseline" x86_64 "$image_dir" + [ "$status" -eq 0 ] + [ "$(value KERNEL_BYTES_DELTA)" = 2 ] + [ "$(value COMPRESSED_INITRAMFS_BYTES_DELTA)" = 3 ] + [ "$(value UNPACKED_INITRAMFS_BYTES_DELTA)" = 4 ] + [ "$(value BOOT_READY_SECONDS_DELTA)" = 2 ] + [ "$(value MEMORY_USED_KIB_DELTA)" = 12144 ] +} diff --git a/xCAT-test/bats/genesis_openembedded_qeth.bats b/xCAT-test/bats/genesis_openembedded_qeth.bats new file mode 100644 index 000000000..d6cb2f58e --- /dev/null +++ b/xCAT-test/bats/genesis_openembedded_qeth.bats @@ -0,0 +1,145 @@ +#!/usr/bin/env bats +# +# genesis-qeth runs in the s390x Genesis image before NetworkManager. It groups the qeth +# channels named by rd.znet=qeth on the kernel command line, or found by znetconf -u, and +# publishes the result. znetconf, genesis-status and logger are scratch scripts that log +# their arguments; znetconf answers -c and -u from files. + +load 'helpers/shell_source' + +QETH='xCAT-genesis-base/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-qeth/files/genesis-qeth' + +setup() +{ + QETH_SCRIPT="$(require_repo_file "$QETH")" + bin="$BATS_TEST_TMPDIR/bin" + mkdir -p "$bin" + cat >"$bin/znetconf" <<'SH' +#!/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 + printf '#!/bin/sh\nprintf "status %%s\\n" "$*" >>"$XCAT_TEST_LOG"\n' >"$bin/genesis-status" + printf '#!/bin/sh\nprintf "logger %%s\\n" "$*" >>"$XCAT_TEST_LOG"\n' >"$bin/logger" + chmod 0755 "$bin"/* + + export PATH="$bin:$PATH" + export XCAT_CIO_SETTLE_FILE="$BATS_TEST_TMPDIR/cio_settle" + export XCAT_CMDLINE_FILE="$BATS_TEST_TMPDIR/cmdline" + export XCAT_STATUS_COMMAND="$bin/genesis-status" + export XCAT_ZNETCONF_COMMAND="$bin/znetconf" + export XCAT_TEST_CONFIGURED="$BATS_TEST_TMPDIR/configured" + export XCAT_TEST_UNCONFIGURED="$BATS_TEST_TMPDIR/unconfigured" + export XCAT_TEST_LOG="$BATS_TEST_TMPDIR/commands.log" + : >"$XCAT_CIO_SETTLE_FILE" + : >"$XCAT_TEST_CONFIGURED" + : >"$XCAT_TEST_UNCONFIGURED" + : >"$XCAT_TEST_LOG" +} + +# Run genesis-qeth with a kernel command line. $status is its exit status. +run_qeth() +{ + printf '%s\n' "$1" >"$XCAT_CMDLINE_FILE" + run /bin/bash "$QETH_SCRIPT" +} + +activations() +{ + grep '^znetconf -a ' "$XCAT_TEST_LOG" || true +} + +@test "a system without ccwgroup devices needs no qeth setup, after CIO work settles" { + XCAT_TEST_UNCONFIGURED_STATUS=31 run_qeth 'console=ttysclp0 xcatd=192.0.2.1' + [ "$status" -eq 0 ] + [ -z "$(activations)" ] + [ "$(cat "$XCAT_CIO_SETTLE_FILE")" = 1 ] +} + +@test "configured qeth devices need no activation" { + printf '0.0.0500,0.0.0501,0.0.0502 1731/01 OSA 10 qeth enc500 online\n' >"$XCAT_TEST_CONFIGURED" + run_qeth 'console=ttysclp0 xcatd=192.0.2.1' + [ "$status" -eq 0 ] + [ -z "$(activations)" ] +} + +@test "an explicit qeth triplet is normalized, defaults to layer 2, and is published" { + run_qeth 'rd.znet=qeth,0600,0.0.0601,0.0.0602,portno=1 xcatd=192.0.2.1' + [ "$status" -eq 0 ] + [ "$(activations)" = 'znetconf -a 0.0.0600,0.0.0601,0.0.0602 -d qeth -o portno=1 -o layer2=1' ] + grep -qx 'status network CONFIGURING_NETWORK qeth devices are ready' "$XCAT_TEST_LOG" +} + +@test "a duplicate qeth triplet is grouped once" { + 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' + [ "$status" -eq 0 ] + [ "$(activations | grep -c '^znetconf -a 0\.0\.0600,0\.0\.0601,0\.0\.0602 ')" -eq 1 ] +} + +@test "explicit qeth options are accepted, and an explicit layer is not overridden" { + run_qeth 'rd.znet=qeth,0.0.0610,0.0.0611,0.0.0612,layer2=0,portname=test xcatd=192.0.2.1' + [ "$status" -eq 0 ] + [ "$(activations)" = 'znetconf -a 0.0.0610,0.0.0611,0.0.0612 -d qeth -o layer2=0 -o portname=test' ] +} + +@test "an already configured triplet is left unchanged" { + printf '0.0.0620,0.0.0621,0.0.0622 1731/01 OSA 10 qeth enc600 online\n' >"$XCAT_TEST_CONFIGURED" + run_qeth 'rd.znet=qeth,0.0.0620,0.0.0621,0.0.0622 xcatd=192.0.2.1' + [ "$status" -eq 0 ] + [ -z "$(activations)" ] +} + +@test "unconfigured qeth devices are discovered and activated for DHCP, other types are not" { + printf '%s\n' 'Scanning for network devices...' \ + 'Device IDs Type Card Type CHPID Drv.' \ + '0.0.0710,0.0.0711 3088/60 LCS OSA 20 lcs' \ + '0.0.0700,0.0.0701,0.0.0702 1731/01 OSA (QDIO) 10 qeth' >"$XCAT_TEST_UNCONFIGURED" + run_qeth 'xcatd=192.0.2.1' + [ "$status" -eq 0 ] + [ "$(activations)" = 'znetconf -a 0.0.0700,0.0.0701,0.0.0702 -d qeth -o layer2=1' ] +} + +@test "a qeth discovery error is returned and published" { + XCAT_TEST_UNCONFIGURED_STATUS=9 run_qeth 'xcatd=192.0.2.1' + [ "$status" -eq 9 ] + grep -q '^status network DEGRADED Unable to inspect qeth devices' "$XCAT_TEST_LOG" +} + +@test "a non-qeth rd.znet entry disables qeth discovery and activates nothing" { + printf '0.0.0700,0.0.0701,0.0.0702 1731/01 OSA 10 qeth\n' >"$XCAT_TEST_UNCONFIGURED" + run_qeth 'rd.znet=ctc,0.0.0800,0.0.0801 xcatd=192.0.2.1' + [ "$status" -eq 0 ] + refute_grep -qx 'znetconf -u' "$XCAT_TEST_LOG" + [ -z "$(activations)" ] +} + +@test "an incomplete qeth triplet fails with a degraded status" { + run_qeth 'rd.znet=qeth,0.0.0900,0.0.0901 xcatd=192.0.2.1' + [ "$status" -eq 1 ] + grep -q '^status network DEGRADED One or more qeth devices' "$XCAT_TEST_LOG" +} + +@test "an invalid qeth option fails before activation" { + run_qeth 'rd.znet=qeth,0.0.0910,0.0.0911,0.0.0912,bad?=1 xcatd=192.0.2.1' + [ "$status" -eq 1 ] + [ -z "$(activations)" ] +} + +@test "failure of one qeth triplet is reported, and the others are still attempted" { + XCAT_TEST_FAIL_CHANNELS=0.0.0a00,0.0.0a01,0.0.0a02 \ + 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' + [ "$status" -eq 1 ] + activations | grep -q '^znetconf -a 0\.0\.0b00,0\.0\.0b01,0\.0\.0b02 ' +} diff --git a/xCAT-test/bats/genesis_openembedded_runtime.bats b/xCAT-test/bats/genesis_openembedded_runtime.bats new file mode 100644 index 000000000..973d7d72e --- /dev/null +++ b/xCAT-test/bats/genesis_openembedded_runtime.bats @@ -0,0 +1,439 @@ +#!/usr/bin/env bats +# +# The runtime scripts of the OpenEmbedded Genesis image: getdestiny and nextdestiny ask xCAT +# for the node action, genesis-network-state writes the management network state, +# genesis-network-refresh renews the DHCP lease, genesis-register records the action, +# genesis-status publishes one status record, and genesis-maintenance-shell reports them. +# openssl, ip, nmcli and logger are scratch scripts. A local TCP listener stands in for xcatd. + +load 'helpers/shell_source' + +INIT_FILES='xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files' + +setup_file() +{ + # A listener that accepts and closes connections and logs one line for each. + export LISTENER_LOG="$BATS_FILE_TMPDIR/listener.log" + : >"$LISTENER_LOG" + perl -MIO::Socket::INET -e ' + my $s = IO::Socket::INET->new(LocalAddr => "127.0.0.1", LocalPort => 0, + Listen => 5, ReuseAddr => 1) or die "listen: $!"; + open(my $port, ">", $ARGV[0]) or die; print {$port} $s->sockport, "\n"; close $port; + while (my $c = $s->accept) { + open(my $log, ">>", $ARGV[1]) or die; print {$log} "accept\n"; close $log; close $c; + }' "$BATS_FILE_TMPDIR/port" "$LISTENER_LOG" /dev/null 2>&1 3>&- & + echo "$!" >"$BATS_FILE_TMPDIR/listener.pid" + for _ in $(seq 50); do [ -s "$BATS_FILE_TMPDIR/port" ] && break; sleep 0.1; done + [ -s "$BATS_FILE_TMPDIR/port" ] +} + +teardown_file() +{ + kill "$(cat "$BATS_FILE_TMPDIR/listener.pid")" 2>/dev/null || true +} + +setup() +{ + NETWORK_SCRIPT="$(require_repo_file "$INIT_FILES/genesis-network-state")" + REFRESH_SCRIPT="$(require_repo_file "$INIT_FILES/genesis-network-refresh")" + REGISTER_SCRIPT="$(require_repo_file "$INIT_FILES/genesis-register")" + STATUS_SCRIPT="$(require_repo_file "$INIT_FILES/genesis-status")" + SHELL_SCRIPT="$(require_repo_file "$INIT_FILES/genesis-maintenance-shell")" + GETDESTINY="$(require_repo_file 'xCAT-genesis-scripts/usr/bin/getdestiny')" + NEXTDESTINY="$(require_repo_file 'xCAT-genesis-scripts/usr/bin/nextdestiny')" + port="$(cat "$BATS_FILE_TMPDIR/port")" + + root="$BATS_TEST_TMPDIR" + bin="$root/bin" + state_dir="$root/run" + status_dir="$state_dir/status" + metadata="$state_dir/xcat-response.env" + mkdir -p "$bin" "$state_dir" "$root/sys/class/net/eth0" "$root/sys/class/net/eth1" + printf '52:54:00:00:00:35\n' >"$root/sys/class/net/eth0/address" + printf 'up\n' >"$root/sys/class/net/eth0/operstate" + printf '123.45 456.78\n' >"$root/uptime" + : >"$root/commands.log" + + printf '#!/bin/sh\nprintf "logger %%s\\n" "$*" >>"$XCAT_TEST_LOG"\n[ -z "${XCAT_TEST_LOGGER_FAIL-}" ]\n' >"$bin/logger" + cat >"$bin/ip" <<'SH' +#!/bin/sh +case "$*" in + '-4 -o route get '*) printf '%s\n' "$XCAT_TEST_ROUTE" ;; + '-4 -o address show dev eth0 scope global') + printf '%s\n' '2: eth0 inet 192.0.2.98/24 scope global eth0' + ;; +esac +SH + cat >"$bin/nmcli" <<'SH' +#!/bin/sh +printf 'nmcli %s\n' "$*" >>"$XCAT_TEST_LOG" +case "$*" in + '-t -f DEVICE,TYPE device status') + printf '%s\n' 'eth0:ethernet' 'eth1:ethernet' 'lo:loopback' + ;; + '-g GENERAL.CON-UUID device show eth0') printf '%s\n' 'connection-eth0' ;; + '-g GENERAL.CON-UUID device show eth1') printf '%s\n' 'connection-eth1' ;; + '-g GENERAL.STATE device show '*) printf '%s\n' '100 (connected)' ;; + '--terse --escape no -g IP4.DNS,IP6.DNS device show eth0') + printf '%s\n' "${XCAT_TEST_DNS-192.0.2.53 | 2001:db8::53}" + ;; + '--wait 30 device connect eth0') + [ -z "${XCAT_TEST_CONNECT_FAIL-}" ] || exit 1 + ;; +esac +SH + printf '#!/bin/sh\nprintf "%%s\\n" network-state-refresh >>"$XCAT_TEST_LOG"\n' >"$bin/network-state-refresh" + cat >"$bin/getdestiny" <<'SH' +#!/bin/sh +printf 'getdestiny %s\n' "$*" >>"$XCAT_TEST_LOG" +metadata= +while [ "$#" -gt 0 ]; do + case "$1" in + --metadata) metadata=$2; shift 2 ;; + *) shift ;; + esac +done +if [ -n "$metadata" ]; then + printf '%s\n' XCAT_NODE_NAME=node042 XCAT_DESTINY=osimage=test-image \ + XCAT_IMAGE_SERVER=192.0.2.10 XCAT_KERNEL=/tftp/vmlinuz XCAT_INITRD=/tftp/initrd \ + 'XCAT_KERNEL_COMMAND_LINE=console=ttyS0 quiet' >"$metadata" +fi +printf '%s\n' "${XCAT_TEST_DESTINY-standby}" +SH + cat >"$bin/openssl" <<'SH' +#!/bin/sh +printf 'openssl %s\n' "$*" >>"$XCAT_TEST_LOG" +[ -z "${XCAT_TEST_OPENSSL_DELAY-}" ] || exec sleep "$XCAT_TEST_OPENSSL_DELAY" +[ -z "${XCAT_TEST_OPENSSL_STATUS-}" ] || exit "$XCAT_TEST_OPENSSL_STATUS" +cat "$XCAT_TEST_RESPONSE_FILE" +SH + chmod 0755 "$bin"/* + + export PATH="$bin:$PATH" + export XCAT_CMDLINE_FILE="$root/cmdline" + export XCAT_STATE_DIR="$state_dir" + export XCAT_STATUS_COMMAND="$STATUS_SCRIPT" + export XCAT_STATUS_DIR="$status_dir" + export XCAT_SYS_CLASS_NET="$root/sys/class/net" + export XCAT_TEST_LOG="$root/commands.log" + export XCAT_TEST_RESPONSE_FILE="$root/destiny-response.xml" + export XCAT_TEST_ROUTE='127.0.0.1 via 192.0.2.1 dev eth0 src 192.0.2.98' + export XCAT_UPTIME_FILE="$root/uptime" + export XCAT_GENESIS_FUNCTIONS="$(require_repo_file "$INIT_FILES/genesis-functions")" + export XCAT_REGISTRATION_ATTEMPTS=1 + export XCAT_REGISTRATION_RETRY_SECONDS=0 + export XCAT_REGISTRATION_REQUEST_TIMEOUT=1 +} + +EXPECTED_METADATA='XCAT_NODE_NAME=node042 +XCAT_DESTINY=osimage=test-image +XCAT_IMAGE_SERVER=192.0.2.10 +XCAT_KERNEL=/tftp/vmlinuz +XCAT_INITRD=/tftp/initrd +XCAT_KERNEL_COMMAND_LINE=console=ttyS0 quiet' + +# An xCAT response naming node042, its image server, and an action. +response() +{ + printf '%s\n' '' '' 'node042' "$1" \ + '/tftp/vmlinuz' '/tftp/initrd' \ + 'console=ttyS0 quiet' '192.0.2.10' \ + '' '' >"$XCAT_TEST_RESPONSE_FILE" +} + +# Start a command, send it SIGTERM after 0.2 s, and print its exit status. Prints "hung" +# when it is still running 3 s later. +terminate_after() +{ + local pid rc=0 + "$@" /dev/null 2>&1 & + pid=$! + sleep 0.2 + kill -TERM "$pid" + for _ in $(seq 30); do + kill -0 "$pid" 2>/dev/null || { wait "$pid" || rc=$?; echo "$rc"; return; } + sleep 0.1 + done + kill -KILL "$pid" + echo hung +} + +network_state() +{ + status=0 + /bin/bash "$NETWORK_SCRIPT" /dev/null 2>&1 || status=$? +} + +@test "getdestiny accepts a complete response, keeps the complete action, and records the metadata" { + response osimage=test-image + run /bin/bash "$GETDESTINY" 192.0.2.10:3001 --once --metadata "$metadata" + [ "$status" -eq 0 ] + [ "$output" = osimage=test-image ] + [ "$(cat "$metadata")" = "$EXPECTED_METADATA" ] +} + +@test "getdestiny rejects an error response, and the failed request keeps the prior metadata" { + response osimage=test-image + /bin/bash "$GETDESTINY" 192.0.2.10:3001 --once --metadata "$metadata" + printf 'denied\n' >"$XCAT_TEST_RESPONSE_FILE" + run /bin/bash "$GETDESTINY" 192.0.2.10:3001 --once --metadata "$metadata" + [ "$status" -ne 0 ] + [ "$(cat "$metadata")" = "$EXPECTED_METADATA" ] +} + +@test "legacy getdestiny retries an error response and a connection failure until stopped" { + printf 'denied\n' >"$XCAT_TEST_RESPONSE_FILE" + [ "$(terminate_after /bin/bash "$GETDESTINY" 192.0.2.10:3001)" = 143 ] + [ "$(XCAT_TEST_OPENSSL_STATUS=1 terminate_after /bin/bash "$GETDESTINY" 192.0.2.10:3001)" = 143 ] +} + +@test "without a destiny, legacy getdestiny returns an empty one and one-shot getdestiny fails" { + printf '\n' >"$XCAT_TEST_RESPONSE_FILE" + run /bin/bash "$GETDESTINY" 192.0.2.10:3001 + [ "$status" -eq 0 ] + [ "$output" = '' ] + [ "$(/bin/bash "$GETDESTINY" 192.0.2.10:3001 | od -An -c | tr -d ' ')" = '\n' ] + run /bin/bash "$GETDESTINY" 192.0.2.10:3001 --once + [ "$status" -ne 0 ] +} + +@test "nextdestiny accepts a complete response and keeps a legacy action with arguments" { + response 'install rocky9.7-x86_64-compute' + run /bin/bash "$NEXTDESTINY" 192.0.2.10:3001 --once --metadata "$metadata" + [ "$status" -eq 0 ] + [ "$output" = 'install rocky9.7-x86_64-compute' ] + grep -qx 'XCAT_DESTINY=install rocky9.7-x86_64-compute' "$metadata" +} + +@test "nextdestiny returns a server error as an action" { + printf 'chain unavailable\n' >"$XCAT_TEST_RESPONSE_FILE" + run /bin/bash "$NEXTDESTINY" 192.0.2.10:3001 --once + [ "$status" -eq 0 ] + [ "$output" = 'error=chain unavailable' ] +} + +@test "one-shot nextdestiny rejects an incomplete response, legacy nextdestiny returns it" { + printf '\n' >"$XCAT_TEST_RESPONSE_FILE" + run /bin/bash "$NEXTDESTINY" 192.0.2.10:3001 --once + [ "$status" -ne 0 ] + run /bin/bash "$NEXTDESTINY" 192.0.2.10:3001 + [ "$status" -eq 0 ] + [ "$output" = 'error=No destiny command received' ] +} + +@test "SIGTERM stops getdestiny and nextdestiny during a request" { + export XCAT_TEST_OPENSSL_DELAY=30 + [ "$(terminate_after /bin/bash "$GETDESTINY" 192.0.2.10:3001)" = 143 ] + [ "$(terminate_after /bin/bash "$NEXTDESTINY" 192.0.2.10:3001)" = 143 ] +} + +@test "the network state accepts DHCP boot state, probes the xCAT port, and publishes readiness" { + printf 'xcatd=127.0.0.1:%s BOOTIF=01-52-54-00-00-00-35\n' "$port" >"$XCAT_CMDLINE_FILE" + accepted_before="$(wc -l <"$LISTENER_LOG")" + network_state + [ "$status" -eq 0 ] + [ "$(wc -l <"$LISTENER_LOG")" -gt "$accepted_before" ] + [ "$(cat "$state_dir/genesis.env")" = "XCATDEST=127.0.0.1:$port +XCATMASTER=127.0.0.1 +XCATPORT=$port +XCAT_INTERFACE=eth0 +XCAT_SOURCE_ADDRESS=192.0.2.98 +XCAT_SOURCE_PREFIXED_ADDRESS=192.0.2.98/24 +XCAT_GATEWAY=192.0.2.1 +XCAT_DNS_SERVERS=192.0.2.53,2001:db8::53 +XCAT_NETWORK_METHOD=auto +XCAT_LINK_STATE=up +XCAT_MAC_ADDRESS=52:54:00:00:00:35 +XCAT_VERIFIED_SECONDS=123" ] + [ "$(cat "$status_dir/network.env")" = 'SCHEMA=1 +STATE=READY +DETAIL=Management network ready on eth0 +STARTED_SECONDS=123 +UPDATED_SECONDS=123 +VERIFIED_SECONDS=123' ] +} + +@test "network readiness does not depend on logging" { + printf 'xcatd=127.0.0.1:%s BOOTIF=01-52-54-00-00-00-35\n' "$port" >"$XCAT_CMDLINE_FILE" + XCAT_TEST_LOGGER_FAIL=1 network_state + [ "$status" -eq 0 ] +} + +@test "unsafe network data is rejected, never evaluated, and does not replace the prior state" { + printf 'xcatd=127.0.0.1:%s BOOTIF=01-52-54-00-00-00-35\n' "$port" >"$XCAT_CMDLINE_FILE" + network_state + safe="$(cat "$state_dir/genesis.env")" + XCAT_TEST_DNS="192.0.2.53;touch$root/injected" network_state + [ "$status" -ne 0 ] + [ "$(cat "$state_dir/genesis.env")" = "$safe" ] + [ ! -e "$root/injected" ] + grep -qx CODE=UNSAFE_NETWORK_STATE "$status_dir/network.env" +} + +# Refresh renews the lease of the interface named in the current network state. +with_network_state() +{ + printf 'xcatd=127.0.0.1:%s BOOTIF=01-52-54-00-00-00-35\n' "$port" >"$XCAT_CMDLINE_FILE" + network_state + [ "$status" -eq 0 ] + : >"$XCAT_TEST_LOG" +} + +@test "discovery refreshes its DHCP identity on the selected interface and rebuilds the state" { + with_network_state + XCAT_NETWORK_STATE_COMMAND="$bin/network-state-refresh" run /bin/bash "$REFRESH_SCRIPT" 'restart (eth0)' + [ "$status" -eq 0 ] + grep -qx 'nmcli --wait 10 device disconnect eth1' "$XCAT_TEST_LOG" + grep -qx 'nmcli --wait 10 device disconnect eth0' "$XCAT_TEST_LOG" + grep -qx 'nmcli --wait 30 device connect eth0' "$XCAT_TEST_LOG" + grep -qx network-state-refresh "$XCAT_TEST_LOG" +} + +@test "a failed DHCP renewal returns an error, restores the connections, and publishes nothing" { + with_network_state + XCAT_TEST_CONNECT_FAIL=1 XCAT_NETWORK_STATE_COMMAND="$bin/network-state-refresh" \ + run /bin/bash "$REFRESH_SCRIPT" 'restart (eth0)' + [ "$status" -ne 0 ] + grep -qx 'nmcli --wait 30 connection up uuid connection-eth0 ifname eth0' "$XCAT_TEST_LOG" + grep -qx 'nmcli --wait 30 connection up uuid connection-eth1 ifname eth1' "$XCAT_TEST_LOG" + refute_grep -qx network-state-refresh "$XCAT_TEST_LOG" +} + +@test "static boot settings are accepted, and a dotted netmask becomes a CIDR prefix" { + printf 'xcatd=127.0.0.1:%s BOOTIF=01-52-54-00-00-00-35 hostip=192.0.2.98 netmask=255.255.255.192 gateway=192.0.2.1\n' \ + "$port" >"$XCAT_CMDLINE_FILE" + network_state + [ "$status" -eq 0 ] + grep -q 'ipv4\.addresses 192\.0\.2\.98/26' "$XCAT_TEST_LOG" +} + +@test "static IPv6 settings disable IPv4 and configure the IPv6 route" { + printf 'xcatd=127.0.0.1:%s BOOTIF=01-52-54-00-00-00-35 hostip=2001:db8::98/64 netmask=64 gateway=2001:db8::1\n' \ + "$port" >"$XCAT_CMDLINE_FILE" + network_state + [ "$status" -eq 0 ] + grep -q 'ipv4\.method disabled ipv6\.method manual ipv6\.addresses 2001:db8::98/64 ipv6\.gateway 2001:db8::1' \ + "$XCAT_TEST_LOG" +} + +@test "partial static settings fail closed and replace the status with the rejected configuration" { + printf 'xcatd=127.0.0.1:%s BOOTIF=01-52-54-00-00-00-35\n' "$port" >"$XCAT_CMDLINE_FILE" + network_state + printf 'xcatd=127.0.0.1:%s BOOTIF=01-52-54-00-00-00-35 hostip=192.0.2.98 netmask=255.255.255.0\n' \ + "$port" >"$XCAT_CMDLINE_FILE" + network_state + [ "$status" -ne 0 ] + grep -qx STATE=FAILED "$status_dir/network.env" + grep -qx CODE=INVALID_STATIC_NETWORK "$status_dir/network.env" +} + +@test "registration keeps a kernel destiny, still contacts xCAT, and publishes the identity" { + export XCATDEST=192.0.2.213:3001 + printf 'xcatd=192.0.2.213:3001 destiny=shell\n' >"$XCAT_CMDLINE_FILE" + run /bin/bash "$REGISTER_SCRIPT" + [ "$status" -eq 0 ] + [ "$(cat "$state_dir/destiny")" = shell ] + grep -q 'getdestiny 192\.0\.2\.213:3001' "$XCAT_TEST_LOG" + grep -qx STATE=ACTION_RECEIVED "$status_dir/registration.env" + grep -A2 -x ACTION=shell "$status_dir/registration.env" | paste -sd' ' | grep -qx 'ACTION=shell TARGET= NODE_NAME=node042' + XCAT_TEST_LOGGER_FAIL=1 run /bin/bash "$REGISTER_SCRIPT" + [ "$status" -eq 0 ] +} + +@test "registration requests a missing destiny, and rejects an empty one without replacing state" { + export XCATDEST=192.0.2.213:3001 + printf 'xcatd=192.0.2.213:3001\n' >"$XCAT_CMDLINE_FILE" + run /bin/bash "$REGISTER_SCRIPT" + [ "$status" -eq 0 ] + [ "$(cat "$state_dir/destiny")" = standby ] + XCAT_TEST_DESTINY='' run /bin/bash "$REGISTER_SCRIPT" + [ "$status" -ne 0 ] + [ "$(cat "$state_dir/destiny")" = standby ] + grep -qx STATE=FAILED "$status_dir/registration.env" + grep -A1 -x CODE=XCAT_RESPONSE_UNAVAILABLE "$status_dir/registration.env" | tail -n1 | grep -q '^RECOVERY=' +} + +@test "registration separates a legacy action from its argument" { + export XCATDEST=192.0.2.213:3001 + printf 'xcatd=192.0.2.213:3001\n' >"$XCAT_CMDLINE_FILE" + XCAT_TEST_DESTINY='install rocky9.7-x86_64-compute' run /bin/bash "$REGISTER_SCRIPT" + [ "$status" -eq 0 ] + grep -A1 -x ACTION=install "$status_dir/registration.env" | tail -n1 | grep -qx 'TARGET=rocky9.7-x86_64-compute' +} + +@test "the status helper reduces the detail to printable text and keeps the stage start time" { + run /bin/sh "$STATUS_SCRIPT" console DEGRADED $'bad\n\tvalue\x01' + [ "$status" -eq 0 ] + grep -qx 'DETAIL=bad value' "$status_dir/console.env" + printf '130.00 500.00\n' >"$XCAT_UPTIME_FILE" + run /bin/sh "$STATUS_SCRIPT" console DEGRADED 'waiting for operator' CODE=OPERATOR_WAIT \ + 'RECOVERY=Review diagnostics' + [ "$status" -eq 0 ] + [ "$(cat "$status_dir/console.env")" = 'SCHEMA=1 +STATE=DEGRADED +DETAIL=waiting for operator +STARTED_SECONDS=123 +UPDATED_SECONDS=130 +CODE=OPERATOR_WAIT +RECOVERY=Review diagnostics' ] +} + +@test "the status helper rejects an unsafe component, an unknown state and a bad number" { + run /bin/sh "$STATUS_SCRIPT" ../console READY + [ "$status" -ne 0 ] + [[ "$output" == *'invalid component'* ]] + [ ! -e "$state_dir/console.env" ] + run /bin/sh "$STATUS_SCRIPT" console UNKNOWN + [ "$status" -ne 0 ] + [[ "$output" == *'invalid state'* ]] + run /bin/sh "$STATUS_SCRIPT" console READY '' ATTEMPT=invalid + [ "$status" -ne 0 ] + [[ "$output" == *'invalid numeric field'* ]] + [ ! -e "$status_dir/console.env" ] +} + +@test "the maintenance shell reports a failure as the overall state, then prefers the action state" { + mkdir -p "$status_dir" + printf 'STATE=READY\n' >"$status_dir/network.env" + printf 'STATE=FAILED\n' >"$status_dir/extensions.env" + printf 'STATE=READY\n' >"$status_dir/registration.env" + printf 'STATE=IDLE\n' >"$status_dir/action.env" + run /bin/bash -c 'printf "exit\n" | /bin/bash "$1" 2>&1' genesis-maintenance-shell "$SHELL_SCRIPT" + [ "$status" -eq 0 ] + grep -qx 'Overall state: FAILED' <<<"$output" + printf 'STATE=READY\n' >"$status_dir/extensions.env" + run /bin/bash -c 'printf "exit\n" | /bin/bash "$1" 2>&1' genesis-maintenance-shell "$SHELL_SCRIPT" + grep -qx 'Overall state: IDLE' <<<"$output" +} + +@test "the maintenance shell runs by its own interpreter, identifies Genesis, and explains the return" { + mkdir -p "$status_dir" + IFS= read -r shebang <"$SHELL_SCRIPT" + [ "$shebang" = '#!/bin/bash' ] + run bash -c 'printf "exit\n" | "$1" "$2" 2>&1' genesis-maintenance-shell "${shebang#\#!}" "$SHELL_SCRIPT" + [ "$status" -eq 0 ] + grep -qx 'xCAT Genesis maintenance shell' <<<"$output" + grep -qx 'Exit returns to the status console.' <<<"$output" +} + +@test "the maintenance shell starts an isolated Bash with the genesis prompt" { + mkdir -p "$status_dir" "$root/home" + printf 'echo profile-was-read\n' >"$root/home/.bash_profile" + printf 'echo rc-was-read\n' >"$root/home/.bashrc" + run env HOME="$root/home" /bin/bash -c 'printf "exit\n" | /bin/bash "$1" 2>&1' \ + genesis-maintenance-shell "$SHELL_SCRIPT" + [ "$status" -eq 0 ] + [[ "$output" == *'genesis# '* ]] + [[ "$output" != *profile-was-read* ]] + [[ "$output" != *rc-was-read* ]] +} + +@test "a status record replaces the previous one through a temporary file in the same directory" { + # mv logs its arguments, then moves. + printf '#!/bin/sh\nprintf "mv %%s\\n" "$*" >>"$XCAT_TEST_LOG"\nexec /bin/mv "$@"\n' >"$bin/mv" + chmod 0755 "$bin/mv" + run /bin/sh "$STATUS_SCRIPT" console READY 'ready' + [ "$status" -eq 0 ] + grep -Eqx "mv -f -- $status_dir/\.console\.[A-Za-z0-9]{6} $status_dir/console\.env" "$XCAT_TEST_LOG" + [ "$(ls -A "$status_dir")" = console.env ] +} diff --git a/xCAT-test/bats/go_xcat_uninstall_installed_only.bats b/xCAT-test/bats/go_xcat_uninstall_installed_only.bats new file mode 100644 index 000000000..9921eb1ce --- /dev/null +++ b/xCAT-test/bats/go_xcat_uninstall_installed_only.bats @@ -0,0 +1,83 @@ +#!/usr/bin/env bats +# +# `go-xcat uninstall` hands GO_XCAT_UNINSTALL_LIST to the package manager. That list names +# packages xCAT stopped producing -- xCAT-genesis-builder, yaboot-xcat, conserver-xcat -- so +# most of it is not installed on a given node. dnf answers "No match for argument" and fails +# the whole call when nothing in the list matches, and apt answers "Unable to locate package". +# So go-xcat asks the package database first, and only the installed names reach it. +# +# go-xcat is sourced. rpm and dpkg-query are shell functions here, so the answer does not +# depend on what the build host has installed. Only "bash" is installed. + +load 'helpers/go_xcat' + +setup() +{ + go_xcat_require_source + # shellcheck disable=SC1090 + source "$GO_XCAT_SOURCE" + # Replaces the go-xcat function, so the test removes nothing from the host. + remove_package() { printf 'remove %s\n' "$@"; } +} + +# The real package tools must not answer. A shell function still resolves with an empty PATH. +without_path() +{ + local PATH="" + "$@" +} + +use_rpm() +{ + rpm() { [ "$1 $2" = "-q --quiet" ] && [ "$3" = bash ]; } +} + +use_deb() +{ + dpkg-query() { [ "$3" = bash ] && printf 'install ok installed'; } +} + +@test "rpm: only the installed packages are reported" { + use_rpm + run without_path installed_packages xcat-test-absent-1 bash xcat-test-absent-2 + [ "$status" -eq 0 ] + [ "$output" = "bash" ] +} + +@test "deb: only the installed packages are reported" { + use_deb + run without_path installed_packages xcat-test-absent-1 bash xcat-test-absent-2 + [ "$status" -eq 0 ] + [ "$output" = "bash" ] +} + +@test "rpm: a list with nothing installed reports nothing" { + use_rpm + run without_path installed_packages xcat-test-absent-1 xcat-test-absent-2 + [ "$status" -eq 0 ] + [ -z "$output" ] +} + +@test "deb: a list with nothing installed reports nothing" { + use_deb + run without_path installed_packages xcat-test-absent-1 xcat-test-absent-2 + [ "$status" -eq 0 ] + [ -z "$output" ] +} + +@test "only the installed packages reach the package manager" { + use_rpm + GO_XCAT_UNINSTALL_LIST=(xCAT-genesis-builder xcat-test-absent-1 bash) + run without_path uninstall_xcat -y + [ "$status" -eq 0 ] + [ "$output" = "remove -y +remove bash" ] +} + +@test "a node with none of them installed hands the package manager nothing, and succeeds" { + use_rpm + GO_XCAT_UNINSTALL_LIST=(xCAT-genesis-builder xcat-test-absent-1) + run without_path uninstall_xcat -y + [ "$status" -eq 0 ] + [ -z "$output" ] +} diff --git a/xCAT-test/unit/genesis_base_sources_staged.t b/xCAT-test/unit/genesis_base_sources_staged.t index f6f3d13a0..e61d29d52 100644 --- a/xCAT-test/unit/genesis_base_sources_staged.t +++ b/xCAT-test/unit/genesis_base_sources_staged.t @@ -5,6 +5,7 @@ use strict; use warnings; +use Archive::Tar; use File::Path qw(make_path); use File::Slurper qw(write_text); use File::Temp qw(tempdir); @@ -20,15 +21,16 @@ my $EPOCH = 1700000000; my $MODULE = 'module-setup.sh'; my $scratch = tempdir(CLEANUP => 1); +# The members of a tarball: name (a directory ends in /), uid, gid and mtime. sub members { my ($tarball) = @_; - my @members = `TZ=UTC tar tjvf '$tarball'`; - die "tar cannot list $tarball\n" if $?; - chomp @members; - return @members; + my $tar = Archive::Tar->new($tarball) or die "cannot read $tarball: " . Archive::Tar->error . "\n"; + return map { + { name => ($_->full_path =~ s{/+\z}{}r) . ($_->is_dir ? '/' : ''), uid => $_->uid, gid => $_->gid, mtime => $_->mtime } + } $tar->get_files; } -sub names { return sort map { (split ' ', $_)[-1] } @_ } +sub names { return sort map { $_->{name} } @_ } # A scratch checkout in the layout the spec expects. my $checkout = "$scratch/checkout"; @@ -59,7 +61,7 @@ is_deeply([ names(@listing) ], [ 'xCAT-genesis-base-build-support/verify-genesis-payload', ], 'the tarball holds the dracut modules, the rules file and the payload verifier with its module'); -is_deeply([ grep { !m{ root/root .* 2023-11-14 22:13 } } @listing ], [], +is_deeply([ map { $_->{name} } grep { $_->{uid} != 0 || $_->{gid} != 0 || $_->{mtime} != $EPOCH } @listing ], [], 'every member is owned by root and dated SOURCE_DATE_EPOCH'); # The checkout itself, so the directory the spec needs is known to be there. diff --git a/xCAT-test/unit/genesis_openembedded.t b/xCAT-test/unit/genesis_openembedded.t index 8099dcf89..978ac6c6f 100644 --- a/xCAT-test/unit/genesis_openembedded.t +++ b/xCAT-test/unit/genesis_openembedded.t @@ -2,25 +2,19 @@ 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 lib "$FindBin::Bin/../lib"; use Test::More; -my $repo_root = abs_path(File::Spec->catdir( $FindBin::Bin, '..', '..' )); +use XCAT::Test::File qw(repo_path slurp_repo_file); -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; -} +# The Yocto configuration, recipes, kernel fragments and units of the OpenEmbedded Genesis +# image are the artifact: kas and bitbake read these lines. The scripts and programs the image +# runs are tested in xCAT-test/bats/genesis_openembedded_*.bats, and the Newt console source in +# genesis_openembedded_console.t. -my $kas = read_file('xCAT-genesis-base/oe/kas/common.yml'); +my $kas = slurp_repo_file('xCAT-genesis-base/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' ); @@ -42,7 +36,7 @@ like( $kas, qr/ARCHIVER_MODE\[recipe\] = "1"/, 'release archives retain recipe metadata' ); my $yocto_release_key = - read_file('xCAT-genesis-base/oe/keys/yocto-release.asc'); + slurp_repo_file('xCAT-genesis-base/oe/keys/yocto-release.asc'); is( sha256_hex($yocto_release_key), '42d49e59f2aa01a1c1417a52d3c915a24a64060c852f33e3bd04fbb738457e70', 'vendored Yocto release key matches the reviewed key material' ); @@ -56,100 +50,56 @@ like( $kas, qr{meta-openembedded:.*?commit: af8b6d6b2f0b11595b0a0d5b82efa3129d52a628}s, 'meta-openembedded revision is pinned' ); -my $x86_kas = read_file('xCAT-genesis-base/oe/kas/x86_64.yml'); +my $x86_kas = slurp_repo_file('xCAT-genesis-base/oe/kas/x86_64.yml'); like( $x86_kas, qr{includes:\s*\n\s*- xCAT-genesis-base/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-base/oe/kas/x86.yml'); +my $x86_32_kas = slurp_repo_file('xCAT-genesis-base/oe/kas/x86.yml'); like( $x86_32_kas, qr{includes:\s*\n\s*- xCAT-genesis-base/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-base/oe/kas/armv7hf.yml'); +my $armv7hf_kas = slurp_repo_file('xCAT-genesis-base/oe/kas/armv7hf.yml'); like( $armv7hf_kas, qr{includes:\s*\n\s*- xCAT-genesis-base/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-base/oe/kas/aarch64.yml'); +my $aarch64_kas = slurp_repo_file('xCAT-genesis-base/oe/kas/aarch64.yml'); like( $aarch64_kas, qr{includes:\s*\n\s*- xCAT-genesis-base/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-base/oe/kas/riscv64.yml'); +my $riscv64_kas = slurp_repo_file('xCAT-genesis-base/oe/kas/riscv64.yml'); like( $riscv64_kas, qr{includes:\s*\n\s*- xCAT-genesis-base/oe/kas/common\.yml}, 'riscv64 build includes the common configuration' ); like( $riscv64_kas, qr/^machine: xcat-genesis-riscv64$/m, 'riscv64 build selects its machine' ); -my $s390x_kas = read_file('xCAT-genesis-base/oe/kas/s390x.yml'); +my $s390x_kas = slurp_repo_file('xCAT-genesis-base/oe/kas/s390x.yml'); like( $s390x_kas, qr{includes:\s*\n\s*- xCAT-genesis-base/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-base/oe/kas/ppc64le.yml'); +my $ppc64le_kas = slurp_repo_file('xCAT-genesis-base/oe/kas/ppc64le.yml'); like( $ppc64le_kas, qr{includes:\s*\n\s*- xCAT-genesis-base/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-base/oe/kas/ppc64.yml'); +my $ppc64_kas = slurp_repo_file('xCAT-genesis-base/oe/kas/ppc64.yml'); like( $ppc64_kas, qr{includes:\s*\n\s*- xCAT-genesis-base/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_path = File::Spec->catfile( - $repo_root, qw(xCAT-genesis-base 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-base/oe/kas/s390x.yml\n"], - 'build invokes the s390x kas configuration once', -); -my $distro = read_file( +my $distro = slurp_repo_file( 'xCAT-genesis-base/oe/meta-xcat-genesis/conf/distro/xcat-genesis.conf' ); like( $distro, qr/^DISTRO_CODENAME = "cheetah"$/m, @@ -184,7 +134,7 @@ like( $distro, unlike( $distro, qr/\b(?:dhclient|dhcpcd)\b/, 'distro excludes external DHCP clients' ); -my $machine = read_file( +my $machine = slurp_repo_file( 'xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-x86-64.conf' ); like( $machine, qr/^DEFAULTTUNE = "x86-64"$/m, @@ -204,7 +154,7 @@ like( $machine, qr/^XCAT_GENESIS_CONSOLE_TTY = "ttyS0"$/m, like( $machine, qr/^XCAT_GENESIS_ARCHITECTURE = "x86_64"$/m, 'x86_64 exports its canonical extension identity' ); -my $ppc64le_machine = read_file( +my $ppc64le_machine = slurp_repo_file( 'xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-ppc64le.conf' ); like( $ppc64le_machine, qr/^DEFAULTTUNE = "ppc64p8le"$/m, @@ -222,7 +172,7 @@ unlike( $ppc64le_machine, qr/^DEFAULTTUNE = "ppc64"$/m, like( $ppc64le_machine, qr/^XCAT_GENESIS_ARCHITECTURE = "ppc64le"$/m, 'ppc64le exports its canonical extension identity' ); -my $ppc64_machine = read_file( +my $ppc64_machine = slurp_repo_file( 'xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-ppc64.conf' ); like( $ppc64_machine, qr/^DEFAULTTUNE = "powerpc64"$/m, @@ -238,7 +188,7 @@ like( $ppc64_machine, qr/^XCAT_GENESIS_CONSOLE_TTY = "hvc0"$/m, like( $ppc64_machine, qr/^XCAT_GENESIS_ARCHITECTURE = "ppc64"$/m, 'ppc64 exports its canonical extension identity' ); -my $x86_machine = read_file( +my $x86_machine = slurp_repo_file( 'xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-x86.conf' ); like( $x86_machine, qr/^DEFAULTTUNE = "i686"$/m, @@ -252,7 +202,7 @@ like( $x86_machine, qr/^XCAT_GENESIS_CONSOLE_TTY = "ttyS0"$/m, like( $x86_machine, qr/^XCAT_GENESIS_ARCHITECTURE = "x86"$/m, 'x86 exports its canonical extension identity' ); -my $armv7hf_machine = read_file( +my $armv7hf_machine = slurp_repo_file( 'xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-armv7hf.conf' ); like( $armv7hf_machine, qr/^DEFAULTTUNE = "armv7ahf"$/m, @@ -270,7 +220,7 @@ like( $armv7hf_machine, qr/^QB_CPU = "-cpu cortex-a15"$/m, like( $armv7hf_machine, qr/^XCAT_GENESIS_CONSOLE_TTY = "ttyAMA0"$/m, 'armv7hf status console uses its PL011 terminal' ); -my $aarch64_machine = read_file( +my $aarch64_machine = slurp_repo_file( 'xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-aarch64.conf' ); like( $aarch64_machine, qr/^DEFAULTTUNE = "aarch64"$/m, @@ -284,7 +234,7 @@ like( $aarch64_machine, qr/^QB_CPU = "-cpu cortex-a57"$/m, like( $aarch64_machine, qr/^XCAT_GENESIS_CONSOLE_TTY = "ttyAMA0"$/m, 'aarch64 status console uses its PL011 terminal' ); -my $riscv64_machine = read_file( +my $riscv64_machine = slurp_repo_file( 'xCAT-genesis-base/oe/meta-xcat-genesis/conf/machine/xcat-genesis-riscv64.conf' ); like( $riscv64_machine, qr/^DEFAULTTUNE = "riscv64"$/m, @@ -317,7 +267,7 @@ for my $machine_console ( "$machine_console->[0] VM starts the status console" ); } -my $image = read_file( +my $image = slurp_repo_file( 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/images/xcat-genesis-image.bb' ); unlike( $image, @@ -334,7 +284,7 @@ like( $image, qr/\bxcat-genesis-extensions\b/, like( $image, qr/\bxcat-genesis-console\b/, 'base image includes the status console' ); -my $hardware_group = read_file( +my $hardware_group = slurp_repo_file( 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/packagegroups/packagegroup-xcat-genesis-hardware.bb' ); for my $tool ( @@ -373,7 +323,7 @@ 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( +my $hardware_recipe = slurp_repo_file( 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-hardware-control/xcat-genesis-hardware-control_1.0.bb' ); like( $hardware_recipe, @@ -397,31 +347,16 @@ 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-base/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( + my $manifest = slurp_repo_file( "xCAT-genesis-base/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( +my $mstflint = slurp_repo_file( 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-support/mstflint/mstflint_4.36.0.bb' ); like( $mstflint, @@ -437,7 +372,7 @@ like( $mstflint, qr/^PACKAGECONFIG \?\?= "adb cables dc inband openssl"$/m, unlike( $mstflint, qr/--enable-(?:fw-mgr|nvml)/, 'mstflint excludes vendor-coupled features' ); -my $iprutils = read_file( +my $iprutils = slurp_repo_file( 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-support/iprutils/iprutils_2.4.19.bb' ); like( $iprutils, qr/^LICENSE = "CPL-1\.0"$/m, @@ -450,7 +385,7 @@ like( $iprutils, qr/^COMPATIBLE_HOST = "powerpc64\.\*-linux"$/m, like( $iprutils, qr/--without-systemd --without-initscripts/, 'iprutils omits unused background services' ); -my $kernel_append = read_file( +my $kernel_append = slurp_repo_file( 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto_%.bbappend' ); like( $kernel_append, qr/file:\/\/xcat-genesis-x86-64\.cfg/, @@ -491,7 +426,7 @@ like( $kernel_append, qr/do_kernel_metadata:prepend:xcat-genesis-ppc64\(\)/, like( $kernel_append, qr/file:\/\/xcat-genesis-ppc64\.cfg/, 'ppc64 kernel uses its big-endian fragment' ); -my $ppc64_kernel_config = read_file( +my $ppc64_kernel_config = slurp_repo_file( 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-ppc64.cfg' ); like( $ppc64_kernel_config, qr/^CONFIG_CPU_BIG_ENDIAN=y$/m, @@ -499,7 +434,7 @@ like( $ppc64_kernel_config, qr/^CONFIG_CPU_BIG_ENDIAN=y$/m, like( $ppc64_kernel_config, qr/^# CONFIG_CPU_LITTLE_ENDIAN is not set$/m, 'ppc64 kernel is not little endian' ); -my $kernel_config = read_file( +my $kernel_config = slurp_repo_file( 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-x86-common.cfg' ); for my $symbol ( @@ -516,7 +451,7 @@ for my $symbol ( "x86_64 kernel enables $symbol" ); } -my $x86_kernel_config = read_file( +my $x86_kernel_config = slurp_repo_file( 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-x86.cfg' ); like( $x86_kernel_config, qr/^CONFIG_M686=y$/m, @@ -533,13 +468,13 @@ for my $vendor (qw(AMD INTEL)) { "x86 kernel disables the $vendor machine-check feature" ); } -my $x86_64_kernel_config = read_file( +my $x86_64_kernel_config = slurp_repo_file( 'xCAT-genesis-base/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( +my $armv7hf_kernel_config = slurp_repo_file( 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-armv7hf.cfg' ); for my $symbol ( @@ -557,7 +492,7 @@ for my $symbol ( "armv7hf kernel enables $symbol" ); } -my $aarch64_kernel_config = read_file( +my $aarch64_kernel_config = slurp_repo_file( 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-aarch64.cfg' ); for my $symbol ( @@ -576,7 +511,7 @@ for my $symbol ( "aarch64 kernel enables $symbol" ); } -my $riscv64_kernel_config = read_file( +my $riscv64_kernel_config = slurp_repo_file( 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-riscv64.cfg' ); for my $symbol ( @@ -606,7 +541,7 @@ for my $symbol ( "riscv64 kernel enables $symbol to boot on ACPI firmware" ); } -my $powerpc64_kernel_config = read_file( +my $powerpc64_kernel_config = slurp_repo_file( 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/xcat-genesis-powerpc64.cfg' ); for my $symbol ( @@ -626,7 +561,7 @@ for my $symbol ( like( $powerpc64_kernel_config, qr/^# CONFIG_PPC_VAS is not set$/m, 'POWER8 image disables unsupported VAS facilities' ); -my $protocol_recipe = read_file( +my $protocol_recipe = slurp_repo_file( 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-protocol/xcat-genesis-protocol_1.0.bb' ); like( $protocol_recipe, @@ -639,7 +574,7 @@ like( $protocol_recipe, qr/^RDEPENDS:\$\{PN\} = "bash openssl-bin"$/m, unlike( $protocol_recipe, qr/\bdoxcat\b/, 'obsolete bootstrap script is excluded' ); -my $networkmanager = read_file( +my $networkmanager = slurp_repo_file( 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-connectivity/networkmanager/networkmanager_%.bbappend' ); like( $networkmanager, qr/^EXTRA_OEMESON:append = " -Dtests=no"$/m, @@ -648,14 +583,14 @@ like( $networkmanager, qr{sed -i '/\^Also=NetworkManager-wait-online\.service\$/d'}, 'NetworkManager does not enable wait-online' ); -my $network_recipe = read_file( +my $network_recipe = slurp_repo_file( 'xCAT-genesis-base/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( +my $network_config = slurp_repo_file( 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-connectivity/xcat-genesis-network/files/10-xcat-genesis.conf' ); like( $network_config, qr/^ipv4\.dhcp-timeout=20$/m, @@ -665,7 +600,7 @@ like( $network_config, qr/^ipv6\.dhcp-duid=ll$/m, like( $network_config, qr/^ipv6\.dhcp-timeout=20$/m, 'IPv6 DHCP has a bounded timeout' ); -my $init_recipe = read_file( +my $init_recipe = slurp_repo_file( 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/xcat-genesis-init_1.0.bb' ); like( $init_recipe, qr/\bcoreutils\b/, @@ -684,40 +619,14 @@ like( $init_recipe, 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-base 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-base oe meta-xcat-genesis recipes-core xcat-genesis-init files xcat-genesis-debug-shell@.service) -); +my $INIT_FILES = 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files'; +my $legacy_debug_shell = repo_path("$INIT_FILES/genesis-debug-shell"); +my $legacy_debug_service = repo_path("$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-base/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-base/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( +my $console_recipe = slurp_repo_file( 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/xcat-genesis-console_1.0.bb' ); like( $console_recipe, qr/^DEPENDS = "libnewt systemd"$/m, @@ -730,7 +639,7 @@ like( $console_recipe, qr/^inherit meson pkgconfig systemd$/m, unlike( $console_recipe, qr/\b(?:whiptail|popt)\b/, 'status console omits the dialog wrapper' ); -my $console_build = read_file( +my $console_build = slurp_repo_file( 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/meson.build' ); like( $console_build, qr/'c_std=c17'/, @@ -740,7 +649,7 @@ like( $console_build, qr/dependency\('libnewt'\).*dependency\('libsystemd'\)/s, like( $console_build, qr/'werror=true'/, 'status console treats compiler warnings as build failures' ); -my $console_service = read_file( +my $console_service = slurp_repo_file( 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console@.service' ); like( $console_service, qr/^Environment=TERM=xterm$/m, @@ -754,93 +663,12 @@ like( $console_service, qr/^TTYPath=\/dev\/%I$/m, unlike( $console_service, qr/xcat\.debug-shell|genesis-debug-shell/, 'status console has no boot-time debug escape' ); -my $console_source_dir = - 'xCAT-genesis-base/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-base oe meta-xcat-genesis recipes-core xcat-genesis-console files xcat-genesis-console.c) +ok( !-e repo_path( + 'xCAT-genesis-base/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( +my $network_state_service = slurp_repo_file( 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/xcat-genesis-network-state.service' ); like( $network_state_service, qr/^After=NetworkManager\.service$/m, @@ -849,7 +677,7 @@ like( $network_state_service, qr/^Before=xcat-genesis-network-ready\.target$/m, 'network selection gates Genesis readiness' ); -my $register_service = read_file( +my $register_service = slurp_repo_file( 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/xcat-genesis-register.service' ); like( $register_service, @@ -861,28 +689,15 @@ like( $register_service, unlike( $register_service, qr/network-online\.target/, 'registration avoids generic network readiness' ); -my $extension_recipe = read_file( +my $extension_recipe = slurp_repo_file( 'xCAT-genesis-base/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-base/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( +my $extension_service = slurp_repo_file( 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-extensions/files/xcat-genesis-extensions.service' ); like( $extension_service, @@ -891,7 +706,7 @@ like( $extension_service, like( $extension_service, qr/^Before=xcat-genesis-register\.service$/m, 'extensions load before xCAT registration' ); -my $extension_class = read_file( +my $extension_class = slurp_repo_file( 'xCAT-genesis-base/oe/meta-xcat-genesis/classes-recipe/xcat-genesis-extension.bbclass' ); like( $extension_class, qr/^inherit sysext-image$/m, @@ -912,13 +727,13 @@ like( $extension_class, qr/Restricted Genesis extensions must set LICENSE_FLAGS/ like( $extension_class, qr/"sha256":/, 'extension manifests record the image digest' ); -my $smoke_extension = read_file( +my $smoke_extension = slurp_repo_file( 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-extensions/images/xcat-genesis-extension-smoke.bb' ); like( $smoke_extension, qr/^XCAT_GENESIS_EXTENSION_NAME = "xcat-smoke"$/m, 'open smoke extension exercises the build path' ); -my $preset = read_file( +my $preset = slurp_repo_file( 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init/files/00-xcat-genesis.preset' ); is( $preset, "disable getty\@.service\n", diff --git a/xCAT-test/unit/genesis_openembedded_actions.t b/xCAT-test/unit/genesis_openembedded_actions.t index 9d31bf965..9c563f6ed 100644 --- a/xCAT-test/unit/genesis_openembedded_actions.t +++ b/xCAT-test/unit/genesis_openembedded_actions.t @@ -2,288 +2,18 @@ use strict; use warnings; -use File::Path qw(make_path); -use File::Spec; -use File::Temp qw(tempdir); use FindBin; +use lib "$FindBin::Bin/../lib"; use Test::More; -my $repo_root = File::Spec->catdir( $FindBin::Bin, '..', '..' ); -my $action_script = File::Spec->catfile( - $repo_root, - qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-init files genesis-action) -); -my $status_script = File::Spec->catfile( - $repo_root, - qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-init files genesis-status) -); -my $functions_file = File::Spec->catfile( - $repo_root, - qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-init files genesis-functions) -); -my $action_service = File::Spec->catfile( - $repo_root, - qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-init files xcat-genesis-action.service) -); -my $init_recipe = File::Spec->catfile( - $repo_root, - qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-init xcat-genesis-init_1.0.bb) -); +use XCAT::Test::File qw(slurp_repo_file); -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); -} +# The systemd unit and the recipe are the artifact. genesis-action is shell and is tested in +# xCAT-test/bats/genesis_openembedded_actions.bats. -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 $INIT = 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init'; -my $root = tempdir( CLEANUP => 1 ); -my $bin = File::Spec->catdir( $root, 'bin' ); -my $state_dir = File::Spec->catdir( $root, 'run' ); -my $status_dir = File::Spec->catdir( $state_dir, 'status' ); -my $approved_dir = File::Spec->catdir( $root, 'actions' ); -my $destiny_file = File::Spec->catfile( $state_dir, 'destiny' ); -my $metadata_file = File::Spec->catfile( $state_dir, 'xcat-response.env' ); -my $network_file = File::Spec->catfile( $state_dir, 'genesis.env' ); -my $certificate_file = File::Spec->catfile( $root, 'cert.pem' ); -my $uptime_file = File::Spec->catfile( $root, 'uptime' ); -my $command_log = File::Spec->catfile( $root, 'commands.log' ); -my $getdestiny_queue = File::Spec->catfile( $root, 'getdestiny.queue' ); -my $nextdestiny_queue = File::Spec->catfile( $root, 'nextdestiny.queue' ); - -make_path( $bin, $state_dir, $status_dir, $approved_dir ); -write_file( $network_file, <<'ENV' ); -XCATDEST=192.0.2.10:3001 -XCATMASTER=192.0.2.10 -XCATPORT=3001 -XCAT_INTERFACE=eth0 -XCAT_SOURCE_ADDRESS=192.0.2.98 -ENV -write_file( $uptime_file, "42.00 80.00\n" ); - -write_file( File::Spec->catfile( $bin, 'logger' ), <<'SH', 0755 ); -#!/bin/sh -printf 'logger %s\n' "$*" >>"$XCAT_TEST_LOG" -SH -write_file( File::Spec->catfile( $bin, 'getdestiny' ), <<'SH', 0755 ); -#!/bin/sh -printf 'getdestiny %s\n' "$*" >>"$XCAT_TEST_LOG" -metadata= -while [ "$#" -gt 0 ]; do - case "$1" in - --metadata) metadata=$2; shift 2 ;; - *) shift ;; - esac -done -[ -s "$XCAT_TEST_GETDESTINY_QUEUE" ] || exit 1 -response=$(head -n 1 "$XCAT_TEST_GETDESTINY_QUEUE") -tail -n +2 "$XCAT_TEST_GETDESTINY_QUEUE" >"$XCAT_TEST_GETDESTINY_QUEUE.new" -mv "$XCAT_TEST_GETDESTINY_QUEUE.new" "$XCAT_TEST_GETDESTINY_QUEUE" -if [ -n "$metadata" ]; then - printf '%s\n' 'XCAT_NODE_NAME=node042' "XCAT_DESTINY=$response" >"$metadata" -fi -printf '%s\n' "$response" -SH -write_file( File::Spec->catfile( $bin, 'nextdestiny' ), <<'SH', 0755 ); -#!/bin/sh -printf 'nextdestiny %s\n' "$*" >>"$XCAT_TEST_LOG" -[ -s "$XCAT_TEST_NEXTDESTINY_QUEUE" ] || exit 1 -response=$(head -n 1 "$XCAT_TEST_NEXTDESTINY_QUEUE") -tail -n +2 "$XCAT_TEST_NEXTDESTINY_QUEUE" >"$XCAT_TEST_NEXTDESTINY_QUEUE.new" -mv "$XCAT_TEST_NEXTDESTINY_QUEUE.new" "$XCAT_TEST_NEXTDESTINY_QUEUE" -printf '%s\n' "$response" -SH -write_file( File::Spec->catfile( $bin, 'discover' ), <<'SH', 0755 ); -#!/bin/sh -printf '%s\n' discover >>"$XCAT_TEST_LOG" -SH -write_file( File::Spec->catfile( $bin, 'getcert' ), <<'SH', 0755 ); -#!/bin/sh -printf '%s\n' getcert >>"$XCAT_TEST_LOG" -: >"$XCAT_TEST_CERTIFICATE_FILE" -SH -write_file( File::Spec->catfile( $bin, 'reboot-control' ), <<'SH', 0755 ); -#!/bin/sh -printf 'reboot-control %s\n' "$*" >>"$XCAT_TEST_LOG" -SH -write_file( File::Spec->catfile( $bin, 'poweroff-control' ), <<'SH', 0755 ); -#!/bin/sh -printf 'poweroff-control %s\n' "$*" >>"$XCAT_TEST_LOG" -SH -write_file( File::Spec->catfile( $bin, 'ipmitool' ), <<'SH', 0755 ); -#!/bin/sh -printf 'ipmitool %s\n' "$*" >>"$XCAT_TEST_LOG" -[ "${XCAT_TEST_IPMI-0}" = 1 ] -SH -write_file( File::Spec->catfile( $approved_dir, 'inventory' ), <<'SH', 0755 ); -#!/bin/sh -printf 'inventory' >>"$XCAT_TEST_LOG" -printf ' <%s>' "$@" >>"$XCAT_TEST_LOG" -printf '\n' >>"$XCAT_TEST_LOG" -SH -write_file( File::Spec->catfile( $approved_dir, 'bmcsetup' ), <<'SH', 0755 ); -#!/bin/sh -printf '%s\n' bmcsetup >>"$XCAT_TEST_LOG" -SH - -my %base_environment = ( - PATH => "$bin:$ENV{PATH}", - XCAT_STATE_DIR => $state_dir, - XCAT_STATUS_COMMAND => $status_script, - XCAT_STATUS_DIR => $status_dir, - XCAT_DESTINY_FILE => $destiny_file, - XCAT_METADATA_FILE => $metadata_file, - XCAT_NETWORK_FILE => $network_file, - XCAT_DISCOVER_COMMAND => File::Spec->catfile( $bin, 'discover' ), - XCAT_CERTIFICATE_COMMAND => File::Spec->catfile( $bin, 'getcert' ), - XCAT_CERTIFICATE_FILE => $certificate_file, - XCAT_GETDESTINY_COMMAND => File::Spec->catfile( $bin, 'getdestiny' ), - XCAT_NEXTDESTINY_COMMAND => File::Spec->catfile( $bin, 'nextdestiny' ), - XCAT_ACTION_COMMAND_DIR => $approved_dir, - XCAT_REBOOT_COMMAND => File::Spec->catfile( $bin, 'reboot-control' ), - XCAT_POWEROFF_COMMAND => File::Spec->catfile( $bin, 'poweroff-control' ), - XCAT_ACTION_POLL_SECONDS => 0, - XCAT_ACTION_REQUEST_TIMEOUT => 2, - XCAT_ACTION_OPERATION_TIMEOUT => 2, - XCAT_ACTION_MAX_STEPS => 1, - XCAT_UPTIME_FILE => $uptime_file, - XCAT_GENESIS_FUNCTIONS => $functions_file, - XCAT_TEST_LOG => $command_log, - XCAT_TEST_GETDESTINY_QUEUE => $getdestiny_queue, - XCAT_TEST_NEXTDESTINY_QUEUE => $nextdestiny_queue, - XCAT_TEST_CERTIFICATE_FILE => $certificate_file, -); - -sub run_action { - my ( $action, %options ) = @_; - - unlink( $command_log, $certificate_file ); - unlink( File::Spec->catfile( $status_dir, 'action.env' ) ); - write_file( $destiny_file, "$action\n" ); - write_file( $getdestiny_queue, - $options{getdestiny} // "$action\n" ); - write_file( $nextdestiny_queue, - $options{nextdestiny} // "standby\n" ); - write_file( $certificate_file, "certificate\n" ) - unless $options{without_certificate}; - local %ENV = ( %ENV, %base_environment, - XCAT_ACTION_MAX_STEPS => ( $options{maximum_steps} // 1 ), - XCAT_TEST_IPMI => ( $options{ipmi} // 0 ) ); - my $status = system( '/bin/bash', $action_script ) >> 8; - my $log = -r $command_log ? read_file($command_log) : ''; - my $action_status = File::Spec->catfile( $status_dir, 'action.env' ); - my $record = -r $action_status ? read_file($action_status) : ''; - return ( $status, $log, $record ); -} - -my ( $status, $log, $record ) = run_action( - 'discover', getdestiny => "standby\n" ); -is( $status, 0, 'discovery action completes' ); -like( $log, qr/^discover$/m, 'discovery action sends inventory' ); -like( $log, qr/^getcert$/m, 'discovery action enrolls a certificate' ); -ok( index( $log, "discover\n" ) < index( $log, 'getdestiny ' ), - 'registered discovery runs before the next xCAT query' ); -like( read_file($destiny_file), qr/^standby$/, - 'discovery loads the assigned node action' ); - -( $status, $log, $record ) = run_action( - 'shell', getdestiny => "install test-image\n" ); -is( $status, 0, 'shell action becomes a managed wait state' ); -unlike( $log, qr{(?:^|/)bash(?:\s|$)}, 'shell action does not open a local shell' ); -like( read_file($destiny_file), qr/^install test-image$/, - 'wait state accepts a remote action change' ); - -( $status, $log, $record ) = run_action( - 'standby', getdestiny => "standby\nstandby\n", - without_certificate => 1, maximum_steps => 2 ); -is( $status, 0, 'standby action polls xCAT' ); -like( $log, qr/^getcert$/m, 'known nodes obtain a missing certificate' ); -like( $record, qr/^STATE=IDLE$/m, 'standby publishes an idle state' ); -like( $record, qr/^VERIFIED_SECONDS=42$/m, - 'standby records its latest successful xCAT contact' ); - -for my $action (qw(osimage ondiscover)) { - ( $status, $log, $record ) = run_action($action); - is( $status, 0, "$action action completes" ); - like( $log, qr/^nextdestiny /m, "$action advances the action chain" ); -} - -( $status, $log, $record ) = run_action( - 'runcmd=inventory storage safe;reboot' ); -is( $status, 0, 'approved command completes' ); -like( $log, qr/^inventory $/m, - 'approved command arguments are not evaluated by a shell' ); -like( $log, qr/^nextdestiny /m, 'approved command advances the chain' ); - -( $status, $log, $record ) = run_action('runcmd=bmcsetup'); -is( $status, 0, 'BMC setup action completes' ); -like( $log, qr/^bmcsetup$/m, 'sequential discovery can run BMC setup' ); -like( $log, qr/^nextdestiny /m, 'BMC setup advances the action chain' ); - -( $status, $log, $record ) = run_action('runcmd=unpackaged'); -isnt( $status, 0, 'unpackaged command is rejected' ); -like( $record, qr/^CODE=ACTION_COMMAND_NOT_APPROVED$/m, - 'unpackaged command has a specific failure code' ); - -for my $case ( - [ runimage => 'UNSAFE_LEGACY_ACTION' ], - [ configraid => 'LEGACY_STORAGE_ACTION' ], - [ sysclone => 'LEGACY_SYSCLONE_ACTION' ], -) { - ( $status, $log, $record ) = run_action( $case->[0] ); - isnt( $status, 0, "$case->[0] fails closed" ); - like( $record, qr/^CODE=\Q$case->[1]\E$/m, - "$case->[0] reports its migration status" ); -} - -for my $action (qw(boot reboot)) { - ( $status, $log, $record ) = run_action( $action, ipmi => 1 ); - is( $status, 0, "$action action completes" ); - like( $log, qr/^nextdestiny /m, "$action advances the chain" ); - like( $log, qr/^ipmitool chassis bootdev pxe$/m, - "$action requests a one-time network boot" ); - like( $log, qr/^reboot-control reboot$/m, "$action requests a reboot" ); -} - -for my $action (qw(install netboot statelite)) { - ( $status, $log, $record ) = run_action($action); - is( $status, 0, "$action action completes" ); - unlike( $log, qr/^nextdestiny /m, - "$action preserves the server-selected chain position" ); - like( $log, qr/^reboot-control reboot$/m, "$action requests a reboot" ); -} - -( $status, $log, $record ) = run_action('shutdown'); -is( $status, 0, 'shutdown action completes' ); -like( $log, qr/^poweroff-control poweroff$/m, - 'shutdown action requests a poweroff' ); - -( $status, $log, $record ) = run_action('error=policy denied'); -isnt( $status, 0, 'xCAT error action fails' ); -like( $record, qr/^CODE=XCAT_ACTION_ERROR$/m, - 'xCAT error remains visible' ); - -( $status, $log, $record ) = run_action('unknown'); -isnt( $status, 0, 'unknown action fails' ); -like( $record, qr/^CODE=XCAT_ACTION_UNSUPPORTED$/m, - 'unknown action has a specific failure code' ); - -( $status, $log, $record ) = run_action( - 'standby', getdestiny => '', without_certificate => 0 ); -is( $status, 0, 'a failed refresh uses the previously received action' ); -like( $record, qr/^STATE=DEGRADED$/m, - 'a failed action poll remains visible' ); - -my $service = read_file($action_service); +my $service = slurp_repo_file("$INIT/files/xcat-genesis-action.service"); like( $service, qr/^ConditionKernelCommandLine=xcatd$/m, 'action execution requires an xCAT boot' ); like( $service, qr/^Requires=xcat-genesis-register\.service$/m, @@ -293,7 +23,7 @@ unlike( $service, qr/^Restart=/m, unlike( $service, qr/^StartLimitIntervalSec=/m, 'the action service has no unlimited restart policy' ); -my $recipe = read_file($init_recipe); +my $recipe = slurp_repo_file("$INIT/xcat-genesis-init_1.0.bb"); like( $recipe, qr/\bxcat-genesis-discovery\b/, 'action runtime includes discovery support' ); like( $recipe, qr/file:\/\/genesis-action/, diff --git a/xCAT-test/unit/genesis_openembedded_bmcsetup.t b/xCAT-test/unit/genesis_openembedded_bmcsetup.t index 76ca16c61..fdacfa08c 100644 --- a/xCAT-test/unit/genesis_openembedded_bmcsetup.t +++ b/xCAT-test/unit/genesis_openembedded_bmcsetup.t @@ -2,90 +2,19 @@ use strict; use warnings; -use File::Path qw(make_path); -use File::Spec; -use File::Temp qw(tempdir); use FindBin; +use lib "$FindBin::Bin/../lib"; use Test::More; -my $repo_root = File::Spec->catdir( $FindBin::Bin, '..', '..' ); -my $recipe = File::Spec->catfile( - $repo_root, - qw(xCAT-genesis-base oe meta-xcat-genesis recipes-xcat xcat-genesis-bmcsetup xcat-genesis-bmcsetup_1.0.bb) -); -my $wrapper = File::Spec->catfile( - $repo_root, - qw(xCAT-genesis-base oe meta-xcat-genesis recipes-xcat xcat-genesis-bmcsetup files genesis-bmcsetup) -); -my $credential_wait = File::Spec->catfile( - $repo_root, - qw(xCAT-genesis-base oe meta-xcat-genesis recipes-xcat xcat-genesis-bmcsetup files genesis-credential-wait) -); -my $image_recipe = File::Spec->catfile( - $repo_root, - qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core images xcat-genesis-image.bb) -); -my $legacy_dir = File::Spec->catdir( - $repo_root, qw(xCAT-genesis-scripts usr bin) -); +use XCAT::Test::File qw(slurp_repo_file); -sub read_file { - my ($path) = @_; - open( my $fh, '<', $path ) or die "Unable to read $path: $!"; - my $contents = do { local $/; <$fh> }; - close($fh); - return $contents; -} +# The recipes are the artifact: bitbake reads these lines to build the image. The wrapper and +# the legacy BMC scripts are shell and are tested in xCAT-test/bats/genesis_openembedded_bmcsetup.bats. -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 ); -} +my $META = 'xCAT-genesis-base/oe/meta-xcat-genesis'; +my $recipe_text = slurp_repo_file( + "$META/recipes-xcat/xcat-genesis-bmcsetup/xcat-genesis-bmcsetup_1.0.bb"); -for my $script (qw(bmcsetup getipmi remoteimmsetup)) { - is( - system( - '/bin/bash', '-n', - File::Spec->catfile( $legacy_dir, $script ) - ) >> 8, - 0, - "$script is valid Bash" - ); -} -is( system( '/bin/bash', '-n', $wrapper, $credential_wait ) >> 8, - 0, 'BMC action helpers are valid Bash' ); - -my $root = tempdir( CLEANUP => 1 ); -my $support_dir = File::Spec->catdir( $root, 'support' ); -my $implementation = File::Spec->catfile( $root, 'implementation' ); -my $log = File::Spec->catfile( $root, 'wrapper.log' ); -make_path($support_dir); -write_file( - $implementation, - <<'SH', 0755 -#!/bin/bash -printf 'path=%s\n' "$PATH" >"$XCAT_TEST_LOG" -printf 'arguments=%s\n' "$*" >>"$XCAT_TEST_LOG" -SH -); -local %ENV = ( - %ENV, - XCAT_BMC_SUPPORT_DIR => $support_dir, - XCAT_BMC_SETUP_IMPLEMENTATION => $implementation, - XCAT_TEST_LOG => $log, -); -is( system( '/bin/bash', $wrapper, 'first', 'second' ) >> 8, - 0, 'BMC action starts the packaged implementation' ); -my $wrapper_log = read_file($log); -like( $wrapper_log, qr/^path=\Q$support_dir\E:/m, - 'BMC support commands take precedence' ); -like( $wrapper_log, qr/^arguments=first second$/m, - 'BMC action preserves its arguments' ); - -my $recipe_text = read_file($recipe); for my $source (qw(bmcsetup getipmi remoteimmsetup updateflag.awk)) { like( $recipe_text, qr/file:\/\/\Q$source\E\b/, "$source is sourced from the existing Genesis implementation" ); @@ -97,7 +26,8 @@ like( $recipe_text, qr/\bxcat-genesis-discovery\b/, 'the BMC action uses the credential callback socket' ); like( $recipe_text, qr/\bgawk\b/, 'the legacy status callback has its required awk implementation' ); -like( read_file($image_recipe), qr/\bxcat-genesis-bmcsetup\b/, +like( slurp_repo_file("$META/recipes-core/images/xcat-genesis-image.bb"), + qr/\bxcat-genesis-bmcsetup\b/, 'the base Genesis image includes BMC setup' ); done_testing(); diff --git a/xCAT-test/unit/genesis_openembedded_console.t b/xCAT-test/unit/genesis_openembedded_console.t index 9b4a7e8c9..55f487aec 100644 --- a/xCAT-test/unit/genesis_openembedded_console.t +++ b/xCAT-test/unit/genesis_openembedded_console.t @@ -2,458 +2,19 @@ use strict; use warnings; -use File::Path qw(make_path); -use File::Spec; -use File::Temp qw(tempdir); use FindBin; +use lib "$FindBin::Bin/../lib"; use Test::More; -my $repo_root = File::Spec->catdir( $FindBin::Bin, '..', '..' ); -my $source_dir = File::Spec->catdir( - $repo_root, - qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-console files xcat-genesis-console src) -); -my @plain_sources = map { File::Spec->catfile( $source_dir, $_ ) } - qw(main.c plain_ui.c shell.c state.c support.c); -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'; +use XCAT::Test::File qw(slurp_repo_file); -is( - system( - $compiler, '-D_POSIX_C_SOURCE=200809L', '-DXCAT_CONSOLE_PLAIN_ONLY', - '-std=c17', '-Wall', '-Wextra', '-Wpedantic', '-Werror', - @plain_sources, '-o', $binary - ) >> 8, - 0, - 'plain console builds with strict warnings' -) or die('unable to build the console test binary'); +# The Newt interface of the Genesis console. newt_ui.c needs libnewt and a terminal, so no +# test builds or drives it, and these checks read its source. The plain console, the shared +# status view, the diagnostics text and the shell launcher are built and run by +# xCAT-test/bats/genesis_openembedded_console.bats. -sub write_file { - my ( $path, $contents ) = @_; - open( my $stream, '>', $path ) or die "Unable to write $path: $!"; - print {$stream} $contents; - close($stream); -} - -sub read_file { - my ($path) = @_; - open( my $stream, '<', $path ) or die "Unable to read $path: $!"; - my $contents = do { local $/; <$stream> }; - close($stream); - return $contents; -} - -write_file( - $header_test_source, - <<'C' -#include "console.h" - -#include - -int main(void) { - assert(xcat_header_context_columns(0) == 0); - assert(xcat_header_context_columns(19) == 0); - assert(xcat_header_context_columns(20) == 0); - assert(xcat_header_context_columns(21) == 1); - assert(xcat_header_context_columns(80) == 60); - return 0; -} -C -); -is( - system( - $compiler, '-D_POSIX_C_SOURCE=200809L', '-std=c17', '-Wall', '-Wextra', - '-Wpedantic', '-Werror', '-I', $source_dir, $header_test_source, - File::Spec->catfile( $source_dir, 'support.c' ), '-o', $header_test_binary - ) >> 8, - 0, - 'header bounds test builds with strict warnings' -); -is( system($header_test_binary) >> 8, 0, - 'narrow terminals leave no writable header context' ); - -write_file( - $identity_test_source, - <<'C' -#include "console.h" - -#include -#include -#include -#include - -int xcat_test_uname(struct utsname *name) { - const char *architecture = getenv("XCAT_TEST_ARCH"); - - memset(name, 0, sizeof(*name)); - snprintf(name->machine, sizeof(name->machine), "%s", - architecture != NULL ? architecture : "x86_64"); - snprintf(name->release, sizeof(name->release), "test-kernel"); - return 0; -} - -int main(void) { - struct console_state state; - - 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' ); -my $os_release = File::Spec->catfile( $root, 'os-release' ); -my $state_dir = File::Spec->catdir( $root, 'status' ); -my $genesis_env = File::Spec->catfile( $root, 'genesis.env' ); -my $destiny = File::Spec->catfile( $root, 'destiny' ); -my $response = File::Spec->catfile( $root, 'xcat-response.env' ); -my $sys_root = File::Spec->catdir( $root, 'sys' ); -my $proc_root = File::Spec->catdir( $root, 'proc' ); -my $net_root = File::Spec->catdir( $sys_root, 'class', 'net', 'eth0' ); -my $dmi_root = File::Spec->catdir( $sys_root, 'class', 'dmi', 'id' ); -my $extensions = File::Spec->catdir( $root, 'extensions' ); -my $providers = File::Spec->catdir( $root, 'providers' ); - -make_path( - $state_dir, $net_root, $dmi_root, $proc_root, $extensions, $providers, - File::Spec->catdir( $sys_root, 'firmware', 'efi' ) -); -write_file( $cmdline, - "xcatd=192.0.2.10:3001 BOOTIF=01-52-54-00-00-00-01 gateway=192.0.2.1\n" ); -write_file( $uptime, "125.90 200.00\n" ); -write_file( $os_release_real, - "NAME=\"xCAT Genesis\"\nVERSION_ID=\"0.1\"\n" ); -symlink( $os_release_real, $os_release ) - or die "Unable to create os-release link: $!"; -write_file( - File::Spec->catfile( $state_dir, 'network.env' ), - "SCHEMA=1\nSTATE=READY\nDETAIL=Management network ready on eth0\n" - . "STARTED_SECONDS=100\nUPDATED_SECONDS=100\nVERIFIED_SECONDS=100\n" -); -write_file( - File::Spec->catfile( $state_dir, 'registration.env' ), - "SCHEMA=1\nSTATE=ACTION_RECEIVED\nDETAIL=Action osimage received\n" - . "STARTED_SECONDS=110\nUPDATED_SECONDS=120\nVERIFIED_SECONDS=120\n" - . "NODE_NAME=compute01\nACTION=osimage\nTARGET=rocky9\n" -); -my $extension_ready = - "SCHEMA=1\nSTATE=READY\nDETAIL=Genesis extensions loaded\n" - . "STARTED_SECONDS=105\nUPDATED_SECONDS=106\n"; -write_file( - File::Spec->catfile( $state_dir, 'extensions.env' ), - $extension_ready -); -my $ipv4_network_state = - "XCATDEST=192.0.2.10:3001\nXCAT_INTERFACE=eth0\n" - . "XCAT_SOURCE_ADDRESS=192.0.2.20\n" - . "XCAT_SOURCE_PREFIXED_ADDRESS=192.0.2.20/24\n" - . "XCAT_GATEWAY=192.0.2.1\nXCAT_DNS_SERVERS=192.0.2.53\n" - . "XCAT_NETWORK_METHOD=auto\nXCAT_LINK_STATE=up\n" - . "XCAT_MAC_ADDRESS=52:54:00:00:00:01\n"; -write_file( $genesis_env, $ipv4_network_state ); -write_file( $destiny, "osimage=rocky9\n" ); -write_file( $response, "XCAT_NODE_NAME=compute01\n" ); -write_file( File::Spec->catfile( $net_root, 'operstate' ), "up\n" ); -write_file( File::Spec->catfile( $net_root, 'address' ), - "52:54:00:00:00:01\n" ); -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" ); -write_file( File::Spec->catfile( $extensions, 'one.raw' ), '' ); -write_file( File::Spec->catfile( $extensions, 'two.raw' ), '' ); -symlink( File::Spec->catfile( $extensions, 'one.raw' ), - File::Spec->catfile( $extensions, 'linked.raw' ) ) - or die "Unable to create extension link: $!"; -write_file( File::Spec->catfile( $providers, 'one.json' ), "{}\n" ); -write_file( File::Spec->catfile( $providers, 'two.json' ), "{}\n" ); -symlink( File::Spec->catfile( $providers, 'one.json' ), - File::Spec->catfile( $providers, 'linked.json' ) ) - or die "Unable to create provider link: $!"; - -my %environment = ( - XCAT_CMDLINE_FILE => $cmdline, - XCAT_UPTIME_FILE => $uptime, - XCAT_OS_RELEASE => $os_release, - XCAT_STATUS_DIR => $state_dir, - XCAT_STATE_FILE => $genesis_env, - XCAT_DESTINY_FILE => $destiny, - XCAT_RESPONSE_FILE => $response, - XCAT_SYS_ROOT => $sys_root, - XCAT_PROC_ROOT => $proc_root, - XCAT_EXTENSION_DIR => $extensions, - XCAT_PROVIDER_DIR => $providers, - XCAT_TEST_ARCH => 'x86_64', -); - -sub run_console { - local %ENV = ( %ENV, %environment ); - open( my $stream, '-|', $binary, '--once' ) - or die "Unable to run $binary: $!"; - my $output = do { local $/; <$stream> }; - close($stream); - 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, - qr/^xCAT Genesis \| ACTION_RECEIVED \| in stage 00:00:15$/m, - 'header shows the actual state and stage duration' ); -like( $output, - qr/^node: compute01\nserial: TEST-SERIAL-001$/m, - 'node and hardware serial are separate fields' ); -like( $output, - qr/^interface: eth0\nlink: up\nmethod: DHCP\naddress: 192\.0\.2\.20\/24\nMAC: 52:54:00:00:00:01$/m, - 'network fields use explicit labels and describe automatic setup as DHCP' ); -like( $output, - qr/^xCAT server: 192\.0\.2\.10:3001\nxCAT contact: Action received$/m, - 'xCAT endpoint and contact result are separate fields' ); -like( $output, - qr/^action: Boot assigned image\ntarget: rocky9\nprogress: none$/m, - 'action and target remain distinct' ); -unlike( $output, qr/last contact/i, - 'main status omits the redundant contact timer' ); -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 ); -write_file( - $cmdline, - ( 'quiet ' x 100 ) - . "xcatd=198.51.100.10:3001 BOOTIF=01-52-54-00-00-00-01\n" -); -( $status, $output ) = run_console(); -like( $output, qr/^xCAT server: 198\.51\.100\.10:3001$/m, - 'console reads xCAT parameters after byte 512' ); -write_file( $genesis_env, $ipv4_network_state ); -write_file( $cmdline, - "xcatd=192.0.2.10:3001 BOOTIF=01-52-54-00-00-00-01 gateway=192.0.2.1\n" ); - -write_file( - File::Spec->catfile( $state_dir, 'extensions.env' ), - "SCHEMA=1\nSTATE=FAILED\nDETAIL=extension signature verification failed\n" - . "STARTED_SECONDS=123\nUPDATED_SECONDS=124\n" - . "CODE=EXTENSION_VERIFICATION_FAILED\n" - . "RECOVERY=Check extension images, manifests, signatures, and trusted keys\n" -); -( $status, $output ) = run_console(); -is( $status, 0, 'plain console renders an extension failure' ); -like( $output, - qr/^error: EXTENSION_VERIFICATION_FAILED: extension signature verification failed$/m, - 'extension verification failures stop the main status' ); -like( $output, - qr/^recovery: Check extension images, manifests, signatures, and trusted keys$/m, - 'extension failures include a recovery hint' ); -write_file( - File::Spec->catfile( $state_dir, 'extensions.env' ), - $extension_ready -); - -write_file( - $genesis_env, - "XCATDEST=[2001:db8::10]:3001\nXCAT_INTERFACE=eth0\n" - . "XCAT_SOURCE_ADDRESS=2001:db8::20\n" - . "XCAT_SOURCE_PREFIXED_ADDRESS=2001:db8::20/64\n" - . "XCAT_GATEWAY=2001:db8::1\nXCAT_DNS_SERVERS=2001:db8::53\n" - . "XCAT_NETWORK_METHOD=auto\nXCAT_LINK_STATE=up\n" - . "XCAT_MAC_ADDRESS=52:54:00:00:00:01\n" -); -( $status, $output ) = run_console(); -is( $status, 0, 'plain console renders an automatic IPv6 network' ); -like( $output, - qr/^method: SLAAC\/DHCPv6\naddress: 2001:db8::20\/64$/m, - 'automatic IPv6 setup has an accurate method label' ); - -my $static_network_state = $ipv4_network_state; -$static_network_state =~ s/XCAT_NETWORK_METHOD=auto/XCAT_NETWORK_METHOD=manual/; -write_file( $genesis_env, $static_network_state ); -( $status, $output ) = run_console(); -is( $status, 0, 'plain console renders a static network' ); -like( $output, qr/^method: Static$/m, - 'manual network setup is labeled Static' ); -write_file( $genesis_env, $ipv4_network_state ); - -write_file( - File::Spec->catfile( $state_dir, 'action.env' ), - "SCHEMA=1\nSTATE=RUNNING\nDETAIL=Rebooting into the assigned image\n" - . "STARTED_SECONDS=122\nUPDATED_SECONDS=124\nVERIFIED_SECONDS=124\n" - . "ACTION=install\nTARGET=rocky9 install image\n" -); -( $status, $output ) = run_console(); -is( $status, 0, 'plain console renders action execution' ); -like( $output, - qr/^xCAT Genesis \| RUNNING \| in stage 00:00:03$/m, - 'action execution becomes the overall state' ); -like( $output, - qr/^action: Install assigned image\ntarget: rocky9 install image\nprogress: none$/m, - 'action status overrides the registration snapshot' ); - -write_file( - File::Spec->catfile( $state_dir, 'action.env' ), - "SCHEMA=1\nSTATE=FAILED\nDETAIL=Unsigned runimage actions are not supported\n" - . "STARTED_SECONDS=124\nUPDATED_SECONDS=124\n" - . "CODE=UNSAFE_LEGACY_ACTION\n" - . "RECOVERY=Package the operation as a signed Genesis system extension\n" - . "ACTION=runimage\nTARGET=legacy.tgz\n" -); -( $status, $output ) = run_console(); -is( $status, 0, 'plain console renders an action failure' ); -like( $output, - qr/^error: UNSAFE_LEGACY_ACTION: Unsigned runimage actions are not supported$/m, - 'action failures remain visible on the main page' ); -unlike( $output, qr/^(?:target|progress):/m, - 'failure output uses the same detail rows as the status screen' ); - -unlink( File::Spec->catfile( $state_dir, 'action.env' ) ); -write_file( - File::Spec->catfile( $state_dir, 'registration.env' ), - "SCHEMA=1\nSTATE=FAILED\nDETAIL=No valid response from xCAT\n" - . "STARTED_SECONDS=120\nUPDATED_SECONDS=124\n" - . "CODE=XCAT_RESPONSE_UNAVAILABLE\n" - . "RECOVERY=Check xcatd and the management network\n" -); -( $status, $output ) = run_console(); -is( $status, 0, 'plain console renders a failed state' ); -like( $output, - qr/^xCAT Genesis \| FAILED \| in stage 00:00:05$/m, - 'failed component becomes the overall state' ); -like( $output, - qr/^error: XCAT_RESPONSE_UNAVAILABLE: No valid response from xCAT$/m, - 'failed component supplies an exact error' ); -like( $output, - qr/^xCAT contact: Failed: No valid response from xCAT$/m, - 'xCAT failure appears on the contact line' ); -like( $output, - qr/^recovery: Check xcatd and the management network$/m, - 'failed component supplies a recovery hint' ); - -write_file( - File::Spec->catfile( $state_dir, 'registration.env' ), - "SCHEMA=1\nSTATE=CONTACTING_XCAT\nDETAIL=xCAT has not answered yet\n" - . "STARTED_SECONDS=120\nUPDATED_SECONDS=124\n" - . "ATTEMPT=2\nATTEMPT_LIMIT=6\nNEXT_RETRY_SECONDS=5\n" -); -( $status, $output ) = run_console(); -is( $status, 0, 'plain console renders retry progress' ); -like( $output, - qr/^action: Boot assigned image\ntarget: rocky9\nprogress: Attempt 2 of 6; retry in 4s$/m, - 'retry countdown uses structured status fields' ); - -write_file( $response, "XCAT_NODE_NAME=\n" ); -( $status, $output ) = run_console(); -is( $status, 0, 'plain console renders an empty node response' ); -like( $output, qr/^node: unassigned$/m, - 'an empty node response is shown as unassigned' ); - -my $state_source = read_file( File::Spec->catfile( $source_dir, 'state.c' ) ); -my $plain_source = read_file( File::Spec->catfile( $source_dir, 'plain_ui.c' ) ); -my $newt_source = read_file( File::Spec->catfile( $source_dir, 'newt_ui.c' ) ); -my $shell_launcher = read_file( File::Spec->catfile( $source_dir, 'shell.c' ) ); -my $header = read_file( File::Spec->catfile( $source_dir, 'console.h' ) ); -my $console_source = join "\n", $state_source, $plain_source, $newt_source, - $shell_launcher, $header; - -my ($view_builder) = $state_source =~ - /(void xcat_build_status_view.*?)(?=\nbool xcat_status_view_changed)/s; -ok( defined($view_builder), 'console builds one shared main status view' ); -foreach my $field (qw/STATE STAGE_TIME ACTIVITY NODE SERIAL INTERFACE LINK METHOD ADDRESS MAC XCAT_SERVER XCAT_STATUS ACTION DETAIL_ONE DETAIL_TWO/) { - like( $view_builder, qr/STATUS_FIELD_\Q$field\E/, - "shared view defines the $field field" ); -} - -my ($plain_renderer) = $plain_source =~ - /(static void print_plain\(.*?)(?=\nstatic bool read_plain_command)/s; -ok( defined($plain_renderer), 'console defines the plain renderer' ); -like( $plain_renderer, - qr/const struct status_view \*view.*?STATUS_FIELD_COUNT/s, - 'plain output consumes the shared view' ); -unlike( $plain_renderer, qr/struct console_state|state->/, - 'plain output cannot select fields from raw state' ); +my $SRC = 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src'; +my $newt_source = slurp_repo_file("$SRC/newt_ui.c"); my ($newt_renderer) = $newt_source =~ /(static void update_form\(.*?)(?=\nstatic const char help_text)/s; @@ -464,15 +25,6 @@ like( $newt_renderer, unlike( $newt_renderer, qr/struct console_state|state->/, 'Newt output cannot select fields from raw state' ); -my ($view_change_detection) = $state_source =~ - /(bool xcat_status_view_changed.*?)(?=\nvoid xcat_format_diagnostics)/s; -ok( defined($view_change_detection), 'console compares shared status views' ); -like( $view_change_detection, - qr/STATUS_FIELD_STATE.*?STATUS_FIELD_COUNT.*?fields\[field\]\.value/s, - 'every shared field participates in change detection' ); -like( $view_change_detection, qr/field == STATUS_FIELD_STAGE_TIME/, - 'the one-second stage timer remains the only cadence exception' ); - my ($help_source) = $newt_source =~ /(static const char help_text\[\].*?)(?=\nstatic void show_help)/s; ok( defined($help_source), 'console defines its interface help text' ); @@ -492,13 +44,6 @@ foreach my $state (qw/STARTING IDLE WAITING_FOR_LINK CONFIGURING_NETWORK CONTACT unlike( $help_source, qr/xcat\.(?:console|debug-shell)|updates automatically/, 'interface help omits boot overrides and redundant refresh advice' ); -my ($diagnostics_source) = $state_source =~ - /(void xcat_format_diagnostics.*)\z/s; -ok( defined($diagnostics_source), 'console defines the diagnostics view' ); -like( $diagnostics_source, qr/Identity\\n\\n.*System\\n\\n.*Management network\\n\\n.*xCAT\\n\\n.*Action\\n\\n.*Runtime\\n\\n/s, - 'diagnostics separates each section with an empty line' ); -unlike( $diagnostics_source, qr/Last contact|Uptime|network_age|registration_age|action_age/, - 'diagnostics omits component timers' ); my ($diagnostics_ui) = $newt_source =~ /(static void show_diagnostics.*?)(?=\nstatic bool journal_value)/s; ok( defined($diagnostics_ui), 'console defines the diagnostics window' ); @@ -511,12 +56,8 @@ like( $diagnostics_ui, my ($header_source) = $newt_source =~ /(static void draw_header.*?)(?=\nstatic void update_form)/s; ok( defined($header_source), 'console defines the status header' ); -like( $console_source, qr/state->local_hostname/, - 'header uses the local hostname before node assignment' ); -like( $console_source, qr/state->serial/, - 'header shows the firmware serial when available' ); -unlike( $header_source, qr/state->architecture/, - 'header leaves architecture in diagnostics' ); +like( $header_source, qr/view->identity/, + 'the header draws the shared view identity' ); like( $header_source, qr/xcat_header_context_columns\(columns\)/, 'header clamps its context to the available terminal width' ); @@ -547,29 +88,46 @@ like( $shell_source, qr/newtResume\(\).*newtResizeScreen\(1\)/s, 'Newt restores and repaints its saved screen after the shell' ); unlike( $shell_source, qr/newtCls\(\)/, 'maintenance-shell return preserves the window frame' ); -like( $shell_launcher, - qr{execl\("/usr/libexec/xcat/genesis-maintenance-shell"}, - 'maintenance shell uses the packaged executable' ); -like( $shell_launcher, qr/FD_CLOEXEC.*exec_error_size/s, - 'maintenance shell distinguishes launch failure from shell exit' ); -unlike( $shell_launcher, qr/WEXITSTATUS/, - 'maintenance shell accepts any later shell exit status' ); -my ($plain_runner) = $plain_source =~ - /(int xcat_run_plain.*)\z/s; -ok( defined($plain_runner), 'console defines the plain-mode loop' ); -like( $plain_runner, qr/Type shell and press Enter for maintenance/, - 'plain mode advertises its maintenance command' ); -like( $plain_runner, - qr/read_plain_command.*?strcmp\(command, "shell"\).*?open_plain_maintenance_shell/s, - 'plain mode accepts the shell command' ); +# Checks over every console source file. +my $console_source = join "\n", map { slurp_repo_file("$SRC/$_") } + qw(console.h main.c newt_ui.c plain_ui.c shell.c state.c support.c); +unlike( $console_source, qr/genesis-debug-shell|XCAT_ON_DEMAND_SHELL/, + 'status console only uses the common maintenance-shell launcher' ); +unlike( $console_source, qr/\b(?:system|popen)\s*\(/, + 'status console avoids command-string execution' ); +like( $console_source, qr/LOG_LINE_COUNT = 128/, + 'log view keeps a bounded serial-friendly history' ); -my ($plain_shell) = $plain_source =~ - /(static void open_plain_maintenance_shell.*?)(?=\nint xcat_run_plain)/s; -ok( defined($plain_shell), 'console defines plain shell confirmation' ); -like( $plain_shell, qr/Open a root maintenance shell\? \[y\/N\]/, - 'plain mode confirms root shell access' ); -like( $plain_shell, qr/xcat_run_maintenance_shell\(\)/, - 'plain mode uses the common maintenance-shell launcher' ); +like( $newt_source, qr/newtDrawRootText\(1, 0, "xCAT Genesis"\)/, + 'status console keeps the product name in the header' ); +like( $newt_source, + qr/F1 Help F2 Diagnostics F3 Logs F12 Shell/, + 'status console keeps useful shortcuts in the footer' ); +unlike( $newt_source, qr/NEWT_KEY_F5|F5 Refresh|Ctrl-L Redraw/, + 'status console omits redundant redraw shortcuts' ); +like( $newt_source, qr/newtFormSetTimer\(screen\.form, 1000\)/, + 'status console updates timers once per second' ); +like( $newt_source, qr/newtCenteredWindow\(72, 17, "Genesis status"\)/, + 'main status removes unused top and bottom rows' ); +like( $newt_source, qr/newtCenteredWindow\(72, 19, "Genesis diagnostics"\)/, + 'F2 opens diagnostics' ); +like( $newt_source, qr/update_form\(&screen, &(?:state|view), changed\)/, + 'stable status updates only the timers' ); +like( $newt_source, qr/if \(\+\+redraw >= 30\)/, + 'serial console limits periodic full redraws' ); +like( $newt_source, qr/newtFormAddHotKey\(screen\.form, NEWT_KEY_F3\)/, + 'F3 opens recent Genesis logs' ); +like( $newt_source, qr/sd_journal_open\(&journal,/, + 'log view reads the journal without a subprocess' ); +like( $newt_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( $newt_source, + qr/newtFormAddHotKey\(screen\.form, NEWT_KEY_F12\).*?show_maintenance_shell\(\)/s, + 'F12 opens the maintenance-shell confirmation' ); +like( $newt_source, + qr/newtWinChoice\(.*?Open a root maintenance shell\?/s, + 'the Newt confirmation names root shell access' ); done_testing(); diff --git a/xCAT-test/unit/genesis_openembedded_discovery.t b/xCAT-test/unit/genesis_openembedded_discovery.t index 5d48106b0..8966c85aa 100644 --- a/xCAT-test/unit/genesis_openembedded_discovery.t +++ b/xCAT-test/unit/genesis_openembedded_discovery.t @@ -2,559 +2,19 @@ use strict; use warnings; -use File::Path qw(make_path); -use File::Spec; -use File::Temp qw(tempdir); use FindBin; -use IO::Select; -use IPC::Open3; -use Symbol qw(gensym); +use lib "$FindBin::Bin/../lib"; use Test::More; -my $repo_root = File::Spec->catdir( $FindBin::Bin, '..', '..' ); -my $discovery_dir = File::Spec->catdir( - $repo_root, - qw(xCAT-genesis-base oe meta-xcat-genesis recipes-xcat xcat-genesis-discovery) -); -my $discover_script = File::Spec->catfile( - $discovery_dir, qw(files genesis-discover) -); -my $callback_script = File::Spec->catfile( - $discovery_dir, qw(files genesis-discovery-callback) -); -my $getcert_script = File::Spec->catfile( - $discovery_dir, qw(files genesis-getcert) -); -my $credential_callback_script = File::Spec->catfile( - $discovery_dir, qw(files genesis-credential-callback) -); -my $udp_sender_source = File::Spec->catfile( - $discovery_dir, qw(files genesis-udp-send.c) -); -my $status_script = File::Spec->catfile( - $repo_root, - qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-init files genesis-status) -); +use XCAT::Test::File qw(slurp_repo_file); -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); -} +# The recipes are the artifact. The discovery scripts and the UDP sender run in the Genesis +# image and are tested in xCAT-test/bats/genesis_openembedded_discovery.bats. -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 $META = 'xCAT-genesis-base/oe/meta-xcat-genesis'; -sub run_script { - my ( $script, $environment, $input ) = @_; - local %ENV = ( %ENV, %{$environment} ); - if ( defined($input) ) { - open( my $pipe, '|-', '/bin/bash', $script ) - or die "Unable to run $script: $!"; - print {$pipe} $input; - close($pipe); - return $? >> 8; - } - return system( '/bin/bash', $script ) >> 8; -} - -sub read_callback_before_eof { - my ( $script, $environment, $input ) = @_; - local %ENV = ( %ENV, %{$environment} ); - my ( $child_in, $child_out ); - my $child_err = gensym; - my $pid = open3( $child_in, $child_out, $child_err, - '/bin/bash', $script ); - print {$child_in} $input; - my $ready = IO::Select->new($child_out)->can_read(2); - my $response = $ready ? <$child_out> : undef; - close($child_in); - waitpid( $pid, 0 ); - return $response; -} - -my $root = tempdir( CLEANUP => 1 ); -my $bin = File::Spec->catdir( $root, 'bin' ); -my $state_dir = File::Spec->catdir( $root, 'run' ); -my $key_dir = File::Spec->catdir( $root, 'keys' ); -my $proc_root = File::Spec->catdir( $root, 'proc' ); -my $dmi_dir = File::Spec->catdir( - $root, qw(sys devices virtual dmi id) -); -my $eth0 = File::Spec->catdir( $root, qw(sys class net eth0) ); -my $driver = File::Spec->catdir( $root, qw(drivers virtio_net) ); -my $network_file = File::Spec->catfile( $state_dir, 'genesis.env' ); -my $response_file = File::Spec->catfile( $state_dir, 'discovery-response' ); -my $packet_file = File::Spec->catfile( $root, 'packet.xml' ); -my $credential_request = File::Spec->catfile( $root, 'credential-request.xml' ); -my $credential_response = File::Spec->catfile( $root, 'credential-response.xml' ); -my $metadata_file = File::Spec->catfile( $state_dir, 'xcat-response.env' ); -my $command_log = File::Spec->catfile( $root, 'commands.log' ); -my $uptime = File::Spec->catfile( $root, 'uptime' ); - -make_path( $bin, $state_dir, $key_dir, $proc_root, $dmi_dir, - File::Spec->catdir( $eth0, 'device' ), $driver, - File::Spec->catdir( $root, 'dev' ) ); -symlink( $driver, File::Spec->catfile( $eth0, 'device', 'driver' ) ) - or die "Unable to create driver link: $!"; -write_file( File::Spec->catfile( $eth0, 'address' ), - "52:54:00:00:00:02\n" ); -write_file( File::Spec->catfile( $eth0, 'device', 'uevent' ), - "PCI_SLOT_NAME=0000:00:03.0\n" ); -write_file( File::Spec->catfile( $eth0, 'device', 'physical_slot' ), - "Slot 4\n" ); -write_file( File::Spec->catfile( $root, 'dev', 'ipmi0' ), '' ); -write_file( File::Spec->catfile( $dmi_dir, 'sys_vendor' ), - "Acme & Co\n" ); -write_file( File::Spec->catfile( $dmi_dir, 'product_name' ), - "Rack \n" ); -write_file( File::Spec->catfile( $dmi_dir, 'product_serial' ), - "SN-0042\n" ); -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 -write_file( File::Spec->catfile( $proc_root, 'meminfo' ), - "MemTotal: 2097152 kB\n" ); -write_file( $uptime, "42.00 80.00\n" ); -write_file( $network_file, <<'ENV' ); -XCATDEST=192.0.2.10:3001 -XCATMASTER=192.0.2.10 -XCATPORT=3001 -XCAT_INTERFACE=eth0 -XCAT_SOURCE_ADDRESS=192.0.2.98 -ENV - -write_file( File::Spec->catfile( $bin, 'logger' ), - <<'SH', 0755 ); -#!/bin/sh -[ -z "${XCAT_TEST_LOGGER_FAIL-}" ] -SH -write_file( File::Spec->catfile( $bin, 'uname' ), <<'SH', 0755 ); -#!/bin/sh -[ "$1" = "-m" ] && printf '%s\n' "${XCAT_TEST_ARCH-x86_64}" -SH -write_file( File::Spec->catfile( $bin, 'ipmitool' ), <<'SH', 0755 ); -#!/bin/sh -case "$*" in - 'mc info') exit 0 ;; - 'sol info') - [ -z "${XCAT_TEST_NO_SOL-}" ] || exit 1 - printf '%s\n' 'Payload Channel : 1' - ;; - 'lan print 1'|'lan print') - printf '%s\n' 'IP Address Source : Static Address' \ - 'IP Address : 192.0.2.101' \ - 'MAC Address : 52:54:00:aa:bb:cc' - ;; -esac -SH -write_file( File::Spec->catfile( $bin, 'lldpcli' ), <<'SH', 0755 ); -#!/bin/sh -printf '%s\n' 'lldp.eth0.chassis.name=switch01' \ - 'lldp.eth0.chassis.mgmt-ip=192.0.2.2' \ - 'lldp.eth0.chassis.descr=Test switch' \ - 'lldp.eth0.port.descr=Ethernet1/4' -SH -write_file( File::Spec->catfile( $bin, 'lsblk' ), <<'SH', 0755 ); -#!/bin/sh -[ -z "${XCAT_TEST_LSBLK_FAIL-}" ] || exit 1 -printf '%s\n' 'vda 21474836480 disk' 'vda1 1073741824 part' -SH -write_file( File::Spec->catfile( $bin, 'ip' ), <<'SH', 0755 ); -#!/bin/sh -[ -z "${XCAT_TEST_IP_FAIL-}" ] || exit 1 -case "$*" in - '-4 -o address show dev eth0 scope global') - [ -z "$XCAT_TEST_IPV4" ] \ - || printf '2: eth0 inet %s scope global eth0\n' "$XCAT_TEST_IPV4" - ;; - '-6 -o address show dev eth0 scope global') - [ -z "$XCAT_TEST_IPV6" ] \ - || printf '2: eth0 inet6 %s scope global eth0\n' "$XCAT_TEST_IPV6" - ;; -esac -SH -write_file( File::Spec->catfile( $bin, 'openssl' ), <<'SH', 0755 ); -#!/bin/sh -printf 'openssl %s\n' "$*" >>"$XCAT_TEST_LOG" -case "$1" in - genpkey) - while [ "$#" -gt 0 ]; do - if [ "$1" = '-out' ]; then - printf '%s\n' key >"$2" - break - fi - shift - done - ;; - pkey) - case " $* " in - *' -pubout '*) - printf '%s\n' '-----BEGIN PUBLIC KEY-----' 'UFVCS0VZ' \ - '-----END PUBLIC KEY-----' - ;; - esac - ;; - dgst) - while [ "$#" -gt 0 ]; do - if [ "$1" = '-out' ]; then - printf '%s\n' signature >"$2" - break - fi - shift - done - ;; - base64) printf '%s' U0lH ;; - req) - while [ "$#" -gt 0 ]; do - if [ "$1" = '-out' ]; then - printf '%s\n' '-----BEGIN CERTIFICATE REQUEST-----' 'Q1NS' \ - '-----END CERTIFICATE REQUEST-----' >"$2" - break - fi - shift - done - ;; - s_client) - cat >"$XCAT_TEST_CREDENTIAL_REQUEST" - cat "$XCAT_TEST_CREDENTIAL_RESPONSE" - ;; - x509) exit 0 ;; -esac -SH -write_file( File::Spec->catfile( $bin, 'timeout' ), <<'SH', 0755 ); -#!/bin/sh -shift -exec "$@" -SH -write_file( File::Spec->catfile( $bin, 'send-discovery' ), <<'SH', 0755 ); -#!/bin/sh -gzip -dc "$1" >"$XCAT_TEST_PACKET" -printf '%s %s\n' "$2" "$3" >>"$XCAT_TEST_LOG" -printf '%s\n' "$XCAT_TEST_RESPONSE" >"$XCAT_DISCOVERY_RESPONSE_FILE" -SH -write_file( File::Spec->catfile( $bin, 'network-refresh' ), <<'SH', 0755 ); -#!/bin/sh -printf 'network-refresh %s\n' "$1" >>"$XCAT_TEST_LOG" -SH - -my %environment = ( - PATH => "$bin:$ENV{PATH}", - XCAT_DISCOVERY_ATTEMPTS => 1, - XCAT_DISCOVERY_RESPONSE_FILE => $response_file, - XCAT_DISCOVERY_RETRY_SECONDS => 1, - XCAT_DISCOVERY_SEND_COMMAND => File::Spec->catfile( $bin, - 'send-discovery' ), - XCAT_KEY_DIR => $key_dir, - XCAT_METADATA_FILE => $metadata_file, - XCAT_NETWORK_FILE => $network_file, - XCAT_NETWORK_REFRESH_COMMAND => File::Spec->catfile( $bin, - 'network-refresh' ), - XCAT_PROC_ROOT => $proc_root, - XCAT_STATE_DIR => $state_dir, - XCAT_STATUS_COMMAND => $status_script, - XCAT_STATUS_DIR => File::Spec->catdir( $state_dir, 'status' ), - XCAT_SYS_ROOT => $root, - XCAT_TEST_LOG => $command_log, - XCAT_TEST_PACKET => $packet_file, - XCAT_TEST_IPV4 => '192.0.2.98/24', - XCAT_TEST_IPV6 => '', - XCAT_TEST_CREDENTIAL_REQUEST => $credential_request, - XCAT_TEST_CREDENTIAL_RESPONSE => $credential_response, - XCAT_TEST_RESPONSE => 'restart (eth0)', - XCAT_TEST_ARCH => 'x86_64', - XCAT_UPTIME_FILE => $uptime, -); - -is( run_script( $discover_script, \%environment ), 0, - 'discovery accepts an xCAT node match' ); -my $packet = read_file($packet_file); -like( $packet, qr{findme}, - 'discovery sends a findme request' ); -like( $packet, qr{x86_64}, - 'discovery sends the canonical architecture' ); -unlike( $packet, qr{virtual}, - 'physical DMI does not claim a virtual node' ); -like( $packet, qr{2}, - 'discovery reports the processor count' ); -like( $packet, qr{Test CPU & Controller}, - 'discovery escapes inventory text' ); -like( $packet, qr{2048MB}, - 'discovery reports memory' ); -like( $packet, qr{vda:20GB}, - 'discovery reports disk capacity' ); -like( $packet, - qr{Acme & Co:Rack <Node>}, - 'discovery escapes system identity' ); -like( $packet, - qr{virtio_net\|eth0\|52:54:00:00:00:02\|192\.0\.2\.98/24}, - 'discovery reports the management NIC' ); -like( $packet, qr{Slot 4}, - 'discovery reports the NIC location' ); -like( $packet, qr{switch01}, - 'discovery reports the LLDP neighbor' ); -like( $packet, qr{Ethernet1/4}, - 'discovery reports the LLDP port' ); -like( $packet, qr{1}, - 'discovery reports in-band BMC access' ); -like( $packet, qr{192\.0\.2\.101}, - 'discovery reports a static BMC address' ); -like( $packet, qr{52:54:00:aa:bb:cc}, - 'discovery reports the BMC MAC address' ); -like( $packet, qr{\nU0lH\n}, - 'discovery signs the inventory' ); -is( read_file($command_log) =~ /192\.0\.2\.10 3001/ ? 1 : 0, 1, - 'discovery sends to the selected xCAT endpoint' ); -like( read_file($command_log), qr/^network-refresh restart \(eth0\)$/m, - 'discovery applies the assigned network identity' ); -like( - read_file( - File::Spec->catfile( $state_dir, 'status', 'discovery.env' ) - ), - qr/^STATE=READY$/m, - 'discovery publishes completion' -); -$environment{XCAT_TEST_LOGGER_FAIL} = 1; -is( run_script( $discover_script, \%environment ), 0, - 'successful discovery does not depend on logging' ); -delete $environment{XCAT_TEST_LOGGER_FAIL}; - -$environment{XCAT_TEST_LSBLK_FAIL} = 1; -$environment{XCAT_TEST_IP_FAIL} = 1; -is( run_script( $discover_script, \%environment ), 0, - 'discovery tolerates devices disappearing during inventory' ); -$packet = read_file($packet_file); -unlike( $packet, qr{}, - 'missing disks leave the optional inventory field empty' ); -like( $packet, qr{virtio_net\|eth0\|52:54:00:00:00:02\|}, - 'a disappearing interface retains its stable hardware identity' ); -delete $environment{XCAT_TEST_LSBLK_FAIL}; -delete $environment{XCAT_TEST_IP_FAIL}; - -$environment{XCAT_TEST_NO_SOL} = 1; -is( run_script( $discover_script, \%environment ), 0, - 'discovery accepts a BMC without SOL support' ); -like( read_file($packet_file), qr{52:54:00:aa:bb:cc}, - 'discovery falls back to the default BMC LAN channel' ); -delete $environment{XCAT_TEST_NO_SOL}; - -write_file( $network_file, <<'ENV' ); -XCATDEST=[2001:db8::10]:3001 -XCATMASTER=2001:db8::10 -XCATPORT=3001 -XCAT_INTERFACE=eth0 -XCAT_SOURCE_ADDRESS=2001:db8::98 -ENV -$environment{XCAT_TEST_IPV4} = ''; -$environment{XCAT_TEST_IPV6} = '2001:db8::98/64'; -is( run_script( $discover_script, \%environment ), 0, - 'discovery accepts an IPv6-only network' ); -$packet = read_file($packet_file); -like( $packet, - qr{virtio_net\|eth0\|52:54:00:00:00:02\|2001:db8::98/64}, - 'IPv6 supplies the legacy management NIC address' ); -like( $packet, qr{2001:db8::98/64}, - 'discovery reports the IPv6 interface address' ); -unlike( $packet, qr{}, - 'an IPv6-only interface does not claim an IPv4 address' ); -like( read_file($command_log), qr/^2001:db8::10 3001$/m, - 'discovery sends to the IPv6 xCAT endpoint' ); -$environment{XCAT_TEST_IPV4} = '192.0.2.98/24'; -$environment{XCAT_TEST_IPV6} = ''; - -write_file( File::Spec->catfile( $dmi_dir, 'sys_vendor' ), "QEMU\n" ); -$environment{XCAT_TEST_RESPONSE} = 'processed'; -isnt( run_script( $discover_script, \%environment ), 0, - 'an unmatched discovery request fails' ); -like( read_file($packet_file), qr{virtual}, - 'virtual DMI is reported to xCAT' ); -like( - read_file( - File::Spec->catfile( $state_dir, 'status', 'discovery.env' ) - ), - qr/^CODE=DISCOVERY_NOT_MATCHED$/m, - 'an unmatched node has a specific failure code' -); - -unlink( - File::Spec->catfile( $dmi_dir, 'sys_vendor' ), - File::Spec->catfile( $dmi_dir, 'product_name' ), - File::Spec->catfile( $dmi_dir, 'product_serial' ), - File::Spec->catfile( $dmi_dir, 'product_uuid' ), -); -my $device_tree = File::Spec->catdir( $proc_root, 'device-tree' ); -make_path($device_tree); -write_file( File::Spec->catfile( $device_tree, 'model' ), - "IBM,9009-42A\0" ); -write_file( File::Spec->catfile( $device_tree, 'system-id' ), - "IBM,02AB123\0" ); -write_file( File::Spec->catfile( $proc_root, 'cpuinfo' ), - "cpu : POWER9\ncpu : POWER9\nplatform : PowerNV\n" ); -$environment{XCAT_TEST_ARCH} = 'ppc64le'; -$environment{XCAT_TEST_RESPONSE} = 'restart'; -is( run_script( $discover_script, \%environment ), 0, - 'Power discovery accepts device-tree identity' ); -$packet = read_file($packet_file); -like( $packet, qr{ppc64le}, - 'Power discovery keeps the canonical architecture' ); -like( $packet, qr{9009-42A}, - 'Power discovery reports the machine type' ); -like( $packet, qr{02AB123}, - 'Power discovery reports the system serial' ); -like( $packet, qr{PowerNV}, - 'Power discovery reports the firmware platform' ); -like( $packet, qr{2}, - 'Power discovery counts processor records' ); -like( $packet, - qr{9009-42a-02ab123-525400000002}, - 'Power discovery creates a stable fallback UUID' ); - -unlink( - File::Spec->catfile( $device_tree, 'model' ), - File::Spec->catfile( $device_tree, 'system-id' ), -); -write_file( File::Spec->catfile( $proc_root, 'sysinfo' ), <<'SYSINFO' ); -Manufacturer: IBM -Type: 3931 -Model: 701 A01 -Sequence Code: 0000000012345 -Plant: 02 -LPAR Name: LP4KVM09 -LPAR UUID: 93724168-fda3-429b-8b28-a5d245dcb3ff -VM00 Name: GENESIS1 -VM00 Control Program: KVM/Linux -VM00 UUID: 82038f2a-1344-aaf7-1a85-2a7250be2076 -SYSINFO -write_file( File::Spec->catfile( $proc_root, 'cpuinfo' ), - "vendor_id : IBM/S390\nprocessor 0: version = 00\nprocessor 1: version = 00\n" ); -$environment{XCAT_TEST_ARCH} = 's390x'; -is( run_script( $discover_script, \%environment ), 0, - 's390x discovery accepts proc sysinfo identity' ); -$packet = read_file($packet_file); -like( $packet, qr{s390x}, - 's390x discovery keeps the canonical architecture' ); -like( $packet, qr{virtual}, - 's390x discovery identifies a hypervisor guest' ); -like( $packet, qr{3931-A01}, - 's390x discovery reports the machine type and model' ); -unlike( $packet, qr{}, - 's390x discovery does not treat the shared machine serial as guest identity' ); -like( $packet, qr{KVM/Linux}, - 's390x discovery reports the closest hypervisor' ); -like( $packet, qr{2}, - 's390x discovery counts processor records' ); -like( $packet, qr{IBM/S390}, - 's390x discovery reports the CPU vendor' ); -like( $packet, - qr{82038f2a-1344-aaf7-1a85-2a7250be2076}, - 's390x discovery uses the closest guest UUID' ); - -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{}, - 'a UUID-less s390x guest still omits the shared machine serial' ); -like( $packet, qr{3931-a01-unknown-525400000002}, - 'a UUID-less s390x guest retains the MAC-based fallback identity' ); - -unlink($response_file); -is( run_script( $callback_script, \%environment, "restart (eth0)" ), 0, - 'callback accepts a restart response' ); -is( read_file($response_file), "restart (eth0)\n", - 'callback records the requested interface' ); -isnt( run_script( $callback_script, \%environment, "malformed" ), 0, - 'callback rejects an unknown response' ); - -write_file( $metadata_file, <<'ENV' ); -XCAT_NODE_NAME=node042 -XCAT_DESTINY=standby -ENV -write_file( $credential_response, <<'XML' ); - - ------BEGIN CERTIFICATE----- -Q0VSVA== ------END CERTIFICATE----- - - -XML -is( run_script( $getcert_script, \%environment ), 0, - 'certificate client installs an xCAT certificate' ); -is( read_file( File::Spec->catfile( $key_dir, 'cert.pem' ) ), <<'PEM', ------BEGIN CERTIFICATE----- -Q0VSVA== ------END CERTIFICATE----- -PEM - 'certificate client stores only the PEM certificate' -); -my $certificate_request = read_file($credential_request); -like( $certificate_request, qr{getcredentials}, - 'certificate client requests x509 credentials' ); -like( $certificate_request, qr{300}, - 'certificate client declares the privileged callback port' ); -like( $certificate_request, qr{\nU0lH\n}, - 'certificate request is signed by the discovery identity' ); -like( read_file($command_log), - qr{openssl s_client -connect \[2001:db8::10\]:3001 -quiet}, - 'certificate client keeps the bracketed IPv6 endpoint' ); -like( - read_file( - File::Spec->catfile( $state_dir, 'status', 'credentials.env' ) - ), - qr/^STATE=READY$/m, - 'certificate client publishes completion' -); -$environment{XCAT_TEST_LOGGER_FAIL} = 1; -is( run_script( $getcert_script, \%environment ), 0, - 'certificate enrollment does not depend on logging' ); -delete $environment{XCAT_TEST_LOGGER_FAIL}; - -write_file( $metadata_file, "XCAT_NODE_NAME=invalid/name\n" ); -isnt( run_script( $getcert_script, \%environment ), 0, - 'certificate client rejects an unsafe node name' ); -like( - read_file( - File::Spec->catfile( $state_dir, 'status', 'credentials.env' ) - ), - qr/^CODE=CERTIFICATE_NODE_IDENTITY_MISSING$/m, - 'missing confirmed identity has a specific failure code' -); - -is( run_script( $credential_callback_script, \%environment, - "CREDOKBYYOU?" ), - 0, 'credential callback accepts the xCAT challenge' ); -is( - read_callback_before_eof( - $credential_callback_script, \%environment, "CREDOKBYYOU?\n" - ), - "CREDOKBYME\n", - 'credential callback answers before xcatd closes the connection' -); -isnt( run_script( $credential_callback_script, \%environment, "unknown" ), 0, - 'credential callback rejects an unknown challenge' ); - -my $recipe = read_file( - File::Spec->catfile( $discovery_dir, 'xcat-genesis-discovery_1.0.bb' ) -); +my $recipe = slurp_repo_file( + "$META/recipes-xcat/xcat-genesis-discovery/xcat-genesis-discovery_1.0.bb"); like( $recipe, qr/^RDEPENDS:\$\{PN\} = "bash coreutils gzip iproute2 openssl-bin util-linux-logger util-linux-lsblk"$/m, 'discovery dependencies are explicit' ); like( $recipe, qr/xcat-genesis-discovery\.socket/, @@ -568,25 +28,8 @@ like( $recipe, qr/-std=c17.*-Werror/s, like( $recipe, qr{\$\{libexecdir\}/xcat/genesis-udp-send}, 'discovery UDP sender is packaged' ); -my $udp_sender = read_file($udp_sender_source); -like( $udp_sender, qr/^\s*SOURCE_PORT = 301,$/m, - 'discovery sender uses the legacy privileged source port' ); -like( $udp_sender, qr/^\s*hints\.ai_family = AF_UNSPEC;$/m, - 'discovery sender resolves both address families' ); -like( $udp_sender, qr/source4->sin_port = htons\(SOURCE_PORT\)/, - 'discovery sender binds the IPv4 source port' ); -like( $udp_sender, qr/source6->sin6_port = htons\(SOURCE_PORT\)/, - 'discovery sender binds the IPv6 source port' ); -like( read_file($discover_script), qr{/usr/libexec/xcat/genesis-udp-send}, - 'discovery uses the source-port-aware UDP sender' ); - -my $image = read_file( - File::Spec->catfile( - $repo_root, - qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core images xcat-genesis-image.bb) - ) -); -like( $image, qr/\bxcat-genesis-discovery\b/, +like( slurp_repo_file("$META/recipes-core/images/xcat-genesis-image.bb"), + qr/\bxcat-genesis-discovery\b/, 'base image includes the discovery client' ); done_testing(); diff --git a/xCAT-test/unit/genesis_openembedded_export.t b/xCAT-test/unit/genesis_openembedded_export.t deleted file mode 100644 index c2208f9b2..000000000 --- a/xCAT-test/unit/genesis_openembedded_export.t +++ /dev/null @@ -1,69 +0,0 @@ -#!/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; - -sub write_file { - my ($path, $content) = @_; - open(my $fh, '>:raw', $path) or die "Unable to write $path: $!"; - print {$fh} $content; - close($fh) or die "Unable to close $path: $!"; -} - -sub read_file { - my ($path) = @_; - open(my $fh, '<:raw', $path) or die "Unable to read $path: $!"; - my $content = do { local $/; <$fh> }; - close($fh); - return $content; -} - -my $root = File::Spec->catdir($FindBin::Bin, '..', '..'); -my $export = File::Spec->catfile($root, 'xCAT-genesis-base', 'oe', 'export'); -my $tmpdir = tempdir(CLEANUP => 1); -my $deploy = File::Spec->catdir($tmpdir, 'deploy'); -my $machine = 'xcat-genesis-x86-64'; -my $image = "xcat-genesis-image-$machine.rootfs"; -my $machine_dir = File::Spec->catdir($deploy, 'images', $machine); -my $license_dir = File::Spec->catdir( - $deploy, 'licenses', 'xcat_genesis_x86_64', $image, -); -my $output = File::Spec->catdir($tmpdir, 'export'); - -make_path($machine_dir, $license_dir); -write_file(File::Spec->catfile($machine_dir, 'bzImage'), 'kernel'); -write_file(File::Spec->catfile($machine_dir, "$image.cpio.gz"), 'initramfs'); -write_file(File::Spec->catfile($machine_dir, "$image.manifest"), 'packages'); -write_file(File::Spec->catfile($machine_dir, "$image.spdx.json"), '{}'); -write_file(File::Spec->catfile($machine_dir, "$image.vex.json"), '{}'); -write_file(File::Spec->catfile($license_dir, 'license.manifest'), 'licenses'); - -is(system($export, 'x86_64', $deploy, $output), 0, 'the image exports'); -ok( -f File::Spec->catfile( $output, 'image.spdx.json' ), - 'the export includes the image SBOM' ); -ok( -f File::Spec->catfile( $output, 'image.vex.json' ), - 'the export includes the VEX report' ); - -my $manifest = File::Spec->catfile($output, 'xcat-genesis.manifest'); -is( - read_file($manifest), - "format=xcat-genesis\nversion=1\narchitecture=x86_64\n", - 'the export identifies its format and architecture', -); - -my $checksums = File::Spec->catfile($output, 'SHA256SUMS'); -like( - read_file($checksums), - qr{^[0-9a-f]{64} xcat-genesis\.manifest$}m, - 'the export manifest has a checksum', -); - -my $verify_status = system('sh', '-c', 'cd "$1" && sha256sum -c SHA256SUMS >/dev/null', 'sh', $output); -is($verify_status, 0, 'all exported checksums verify'); - -done_testing(); diff --git a/xCAT-test/unit/genesis_openembedded_extensions.t b/xCAT-test/unit/genesis_openembedded_extensions.t index 709e6fa1b..84ff68e21 100644 --- a/xCAT-test/unit/genesis_openembedded_extensions.t +++ b/xCAT-test/unit/genesis_openembedded_extensions.t @@ -2,297 +2,17 @@ use strict; use warnings; -use File::Copy qw(copy); -use File::Path qw(make_path); -use File::Spec; -use File::Temp qw(tempdir); use FindBin; -use JSON::PP; +use lib "$FindBin::Bin/../lib"; use Test::More; -my $repo_root = File::Spec->catdir( $FindBin::Bin, '..', '..' ); -my $loader = File::Spec->catfile( - $repo_root, - qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-extensions files genesis-sysext) -); -my $signer = File::Spec->catfile( - $repo_root, qw(xCAT-genesis-base oe scripts sign-extension) -); -my $exporter = File::Spec->catfile( - $repo_root, qw(xCAT-genesis-base oe export-extension) -); -my $extension_recipe = File::Spec->catfile( - $repo_root, - qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-extensions xcat-genesis-extensions_1.0.bb) -); +use XCAT::Test::File qw(slurp_repo_file); -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); -} +# The recipe is the artifact. The signer, the exporter and genesis-sysext are shell and are +# tested in xCAT-test/bats/genesis_openembedded_extensions.bats. -sub shell_quote { - my ($value) = @_; - $value =~ s/'/'"'"'/g; - return "'$value'"; -} - -sub run_command { - my (@command) = @_; - my $shell_command = join( ' ', map { shell_quote($_) } @command ); - my $output = qx{$shell_command 2>&1}; - return ( $? >> 8, $output ); -} - -sub write_manifest { - my ( $path, $hash, $changes ) = @_; - my $manifest = { - architecture => 'x86_64', - capabilities => ['diagnostic.smoke'], - genesis_release => '0.1', - kernel_modules => JSON::PP::false, - kernel_release => undef, - key_id => 'xcat-release', - license_class => 'open', - name => 'xcat-smoke', - pci_ids => [], - schema => 1, - sha256 => $hash, - version => '1.0', - }; - @{$manifest}{ keys %{$changes} } = values %{$changes}; - write_file( $path, JSON::PP->new->canonical->pretty->encode($manifest) ); -} - -my $root = tempdir( CLEANUP => 1 ); -my $bin = File::Spec->catdir( $root, 'bin' ); -my $keys = File::Spec->catdir( $root, 'keys' ); -my $run_dir = File::Spec->catdir( $root, 'run' ); -my $image = File::Spec->catfile( $root, 'xcat-smoke.squashfs-zst' ); -my $manifest = File::Spec->catfile( $root, 'xcat-smoke.manifest.json' ); -my $signature = File::Spec->catfile( $root, 'xcat-smoke.sig' ); -my $private_key = File::Spec->catfile( $root, 'private.pem' ); -my $public_key = File::Spec->catfile( $keys, 'xcat-release.pem' ); -my $os_release = File::Spec->catfile( $root, 'os-release' ); -my $command_log = File::Spec->catfile( $root, 'commands.log' ); -my $status_log = File::Spec->catfile( $root, 'status.log' ); - -make_path( $bin, $keys, $run_dir ); -write_file( $image, "extension payload\n" ); -write_file( $os_release, "ID=xcat-genesis\nVERSION_ID=0.1\n" ); -write_file( $command_log, '' ); -write_file( $status_log, '' ); -write_file( - File::Spec->catfile( $bin, 'systemd-sysext' ), - "#!/bin/sh\nprintf 'systemd-sysext %s\\n' \"\$*\" >>\"\$XCAT_TEST_LOG\"\n", - 0755 -); -write_file( - File::Spec->catfile( $bin, 'genesis-status' ), - "#!/bin/sh\nprintf '%s\\n' \"\$*\" >>\"\$XCAT_STATUS_LOG\"\n", - 0755 -); - -is( system( 'openssl', 'genpkey', '-algorithm', 'ED25519', '-out', $private_key ) >> 8, - 0, 'test private key is generated' ); -is( system( 'openssl', 'pkey', '-in', $private_key, '-pubout', '-out', $public_key ) >> 8, - 0, 'test public key is generated' ); - -my ( $hash_status, $hash_output ) = run_command( 'sha256sum', '--', $image ); -is( $hash_status, 0, 'extension digest is generated' ); -my ($hash) = $hash_output =~ /^([0-9a-f]{64})/; -write_manifest( $manifest, $hash, {} ); - -my ( $sign_status, $sign_output ) = - run_command( $signer, $manifest, $private_key, $signature ); -is( $sign_status, 0, 'extension manifest is signed' ) or diag($sign_output); - -my $deploy = File::Spec->catdir( $root, 'deploy' ); -my $machine = 'xcat-genesis-x86-64'; -my $extension = 'xcat-genesis-extension-smoke'; -my $stem = "$extension-$machine"; -my $artifact_stem = "$stem.sysext"; -my $machine_dir = File::Spec->catdir( $deploy, 'images', $machine ); -my $bundle = File::Spec->catdir( $root, 'bundle' ); -make_path($machine_dir); -copy( $image, File::Spec->catfile( $machine_dir, "$artifact_stem.squashfs-zst" ) ) - or die "Unable to stage extension image: $!"; -copy( $manifest, File::Spec->catfile( $machine_dir, "$artifact_stem.manifest.json" ) ) - or die "Unable to stage extension manifest: $!"; -my ( $export_status, $export_output ) = run_command( - $exporter, 'x86_64', $extension, $deploy, - $private_key, $public_key, $bundle -); -is( $export_status, 0, 'built extension exports as a signed bundle' ) - or diag($export_output); -for my $artifact ( - [ extensions => "$stem.squashfs-zst" ], - [ extensions => "$stem.manifest.json" ], - [ extensions => "$stem.sig" ], - [ 'extension-keys' => 'xcat-release.pem' ], -) { - ok( -f File::Spec->catfile( $bundle, @{$artifact} ), - "extension bundle includes $artifact->[1]" ); -} -my ( $bundle_checksum_status, undef ) = run_command( - 'sh', '-c', 'cd "$1" && sha256sum -c SHA256SUMS >/dev/null', - 'sh', $bundle -); -is( $bundle_checksum_status, 0, 'extension bundle checksums verify' ); - -local %ENV = ( - %ENV, - PATH => "$bin:$ENV{PATH}", - XCAT_GENESIS_EXTENSION_KEY_DIR => $keys, - XCAT_GENESIS_EXTENSION_RUN_DIR => $run_dir, - XCAT_GENESIS_OS_RELEASE => $os_release, - XCAT_GENESIS_STATUS_COMMAND => File::Spec->catfile( $bin, 'genesis-status' ), - XCAT_GENESIS_UNAME_M => 'x86_64', - XCAT_GENESIS_UNAME_R => '6.18.24-test', - XCAT_TEST_LOG => $command_log, - XCAT_STATUS_LOG => $status_log, -); - -my ( $verify_status, $verify_output ) = - run_command( '/bin/bash', $loader, 'verify', $manifest, $image, $signature ); -is( $verify_status, 0, 'valid extension is accepted' ) or diag($verify_output); - -{ - local $ENV{XCAT_GENESIS_UNAME_M} = 'i586'; - my $x86_manifest = File::Spec->catfile( $root, 'xcat-smoke-x86.json' ); - my $x86_signature = File::Spec->catfile( $root, 'xcat-smoke-x86.sig' ); - write_manifest( $x86_manifest, $hash, { architecture => 'x86' } ); - my ( $x86_sign_status, $x86_sign_output ) = - run_command( $signer, $x86_manifest, $private_key, $x86_signature ); - is( $x86_sign_status, 0, 'x86 extension manifest is signed' ) - or diag($x86_sign_output); - my ( $x86_status, $x86_output ) = run_command( - '/bin/bash', $loader, 'verify', $x86_manifest, $image, $x86_signature - ); - is( $x86_status, 0, 'i586 runtime uses the x86 extension identity' ) - or diag($x86_output); -} - -{ - local $ENV{XCAT_GENESIS_UNAME_M} = 'armv7l'; - my $arm_manifest = File::Spec->catfile( $root, 'xcat-smoke-armv7hf.json' ); - my $arm_signature = File::Spec->catfile( $root, 'xcat-smoke-armv7hf.sig' ); - write_manifest( $arm_manifest, $hash, { architecture => 'armv7hf' } ); - my ( $arm_sign_status, $arm_sign_output ) = - run_command( $signer, $arm_manifest, $private_key, $arm_signature ); - is( $arm_sign_status, 0, 'armv7hf extension manifest is signed' ) - or diag($arm_sign_output); - my ( $arm_status, $arm_output ) = run_command( - '/bin/bash', $loader, 'verify', $arm_manifest, $image, $arm_signature - ); - is( $arm_status, 0, 'armv7l runtime uses the armv7hf extension identity' ) - 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); -ok( -f File::Spec->catfile( $run_dir, 'xcat-smoke.raw' ), - 'installed image uses the manifest name' ); -open( my $log_fh, '<', $command_log ) or die "Unable to read $command_log: $!"; -my $log = do { local $/; <$log_fh> }; -close($log_fh); -like( $log, qr/^systemd-sysext refresh$/m, - 'installation refreshes system extensions' ); - -my ( $load_status, $load_output ) = - run_command( '/bin/bash', $loader, 'load-all', $root ); -is( $load_status, 0, 'valid extension directory is loaded' ) - or diag($load_output); -open( my $status_fh, '<', $status_log ) - or die "Unable to read $status_log: $!"; -my $status_events = do { local $/; <$status_fh> }; -close($status_fh); -like( $status_events, - qr/^extensions RUNNING Verifying Genesis extensions$/m, - 'extension loading publishes its active state' ); -like( $status_events, - qr/^extensions READY Genesis extensions loaded$/m, - 'extension loading publishes its ready state' ); - -my $bad_signature = File::Spec->catfile( $root, 'bad.sig' ); -write_file( $bad_signature, 'x' x 64 ); -my ( $bad_signature_status, undef ) = - run_command( '/bin/bash', $loader, 'verify', $manifest, $image, $bad_signature ); -isnt( $bad_signature_status, 0, 'invalid signature is rejected' ); - -for my $case ( - [ architecture => 'riscv64', 'wrong architecture is rejected' ], - [ genesis_release => '9.9', 'wrong release is rejected' ], - [ sha256 => ( '0' x 64 ), 'wrong digest is rejected' ], - [ kernel_modules => JSON::PP::true, 'wrong kernel ABI is rejected', - kernel_release => '0.0-wrong' ], - ) -{ - my ( $field, $value, $label, @extra ) = @{$case}; - my %changes = ( $field => $value, @extra ); - my $case_manifest = File::Spec->catfile( $root, "$field.json" ); - my $case_signature = File::Spec->catfile( $root, "$field.sig" ); - write_manifest( $case_manifest, $hash, \%changes ); - my ( $case_sign_status, $case_sign_output ) = - run_command( $signer, $case_manifest, $private_key, $case_signature ); - is( $case_sign_status, 0, "$field manifest is signed" ) - or diag($case_sign_output); - my ( $case_status, undef ) = - run_command( '/bin/bash', $loader, 'verify', $case_manifest, $image, - $case_signature ); - isnt( $case_status, 0, $label ); -} - -my $linked_manifest = File::Spec->catfile( $root, 'linked.json' ); -symlink( $manifest, $linked_manifest ) or die "Unable to link $linked_manifest: $!"; -my ( $linked_status, undef ) = - run_command( '/bin/bash', $loader, 'verify', $linked_manifest, $image, - $signature ); -isnt( $linked_status, 0, 'linked manifest is rejected' ); - -my $empty_dir = File::Spec->catdir( $root, 'empty' ); -make_path($empty_dir); -write_file( $status_log, '' ); -my ( $empty_status, undef ) = - run_command( '/bin/bash', $loader, 'load-all', $empty_dir ); -isnt( $empty_status, 0, 'empty extension directory is rejected' ); -open( $status_fh, '<', $status_log ) - or die "Unable to read $status_log: $!"; -$status_events = do { local $/; <$status_fh> }; -close($status_fh); -like( $status_events, - qr/^extensions FAILED no extension manifests found in:/m, - 'extension loading publishes verification failures' ); -like( $status_events, - qr/CODE=EXTENSION_VERIFICATION_FAILED .*RECOVERY=Check extension images, manifests, signatures, and trusted keys/m, - 'extension failures include structured recovery data' ); - -my $recipe_text = do { - open( my $fh, '<', $extension_recipe ) - or die "Unable to read $extension_recipe: $!"; - local $/; - <$fh>; -}; +my $recipe_text = slurp_repo_file( + 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-extensions/xcat-genesis-extensions_1.0.bb'); like( $recipe_text, qr/\$\{localstatedir\}\/lib\/xcat\/genesis\/extensions/, 'the image creates the extension staging directory' ); like( $recipe_text, qr/^XCAT_GENESIS_EXTENSION_BUNDLE \?\?= ""$/m, diff --git a/xCAT-test/unit/genesis_openembedded_hardware.t b/xCAT-test/unit/genesis_openembedded_hardware.t deleted file mode 100644 index 631723a6f..000000000 --- a/xCAT-test/unit/genesis_openembedded_hardware.t +++ /dev/null @@ -1,381 +0,0 @@ -#!/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 JSON::PP; -use Test::More; - -my $repo_root = File::Spec->catdir( $FindBin::Bin, '..', '..' ); -my $dispatcher = File::Spec->catfile( - $repo_root, - qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-hardware-control files genesis-hardware) -); -my $nvme_provider = File::Spec->catfile( - $repo_root, - qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-hardware-control files provider-nvme) -); -my $mstflint_provider = File::Spec->catfile( - $repo_root, - qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-hardware-control files provider-mstflint) -); -my $iprutils_provider = File::Spec->catfile( - $repo_root, - qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-hardware-control files provider-iprutils) -); - -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; -} - -sub shell_quote { - my ($value) = @_; - $value =~ s/'/'"'"'/g; - return "'$value'"; -} - -sub run_command { - my ( $environment, @command ) = @_; - local %ENV = ( %ENV, %{$environment} ); - my $shell_command = join( ' ', map { shell_quote($_) } @command ); - my $output = qx{$shell_command 2>&1}; - return ( $? >> 8, $output ); -} - -sub decode_output { - my ($output) = @_; - return JSON::PP->new->decode($output); -} - -my $root = tempdir( CLEANUP => 1 ); -my $bin = File::Spec->catdir( $root, 'bin' ); -my $manifests = File::Spec->catdir( $root, 'manifests' ); -my $executables = File::Spec->catdir( $root, 'providers' ); -my $audit = File::Spec->catfile( $root, 'audit', 'hardware.jsonl' ); -my $request = File::Spec->catfile( $root, 'request.json' ); -make_path( $bin, $manifests, $executables ); - -write_file( - File::Spec->catfile( $bin, 'timeout' ), - <<'SH', 0755 -#!/bin/sh -while [ "$#" -gt 0 ]; do - case "$1" in - --signal=*|--kill-after=*) shift ;; - *) break ;; - esac -done -shift -exec "$@" -SH -); -write_file( File::Spec->catfile( $bin, 'flock' ), "#!/bin/sh\nexit 0\n", 0755 ); -write_file( - File::Spec->catfile( $executables, 'mock' ), - <<'SH', 0755 -#!/bin/sh -case "$1" in - probe) - printf '%s\n' '{"detected":true,"devices":["controller0"]}' - ;; - run) - case "$2" in - storage.inventory) printf '%s\n' '{"devices":["controller0"]}' ;; - storage.array.create) printf '{"created":"%s"}\n' "$3" ;; - storage.array.delete) printf '%s\n' 'delete failed' >&2; exit 9 ;; - storage.logs) printf '%s\n' 'not-json' ;; - *) exit 8 ;; - esac - ;; - *) exit 7 ;; -esac -SH -); -write_file( - File::Spec->catfile( $manifests, 'mock.json' ), - JSON::PP->new->canonical->pretty->encode( - { - capabilities => [ - { destructive => JSON::PP::false, name => 'storage.inventory' }, - { destructive => JSON::PP::false, name => 'storage.logs' }, - { destructive => JSON::PP::true, name => 'storage.array.create' }, - { destructive => JSON::PP::true, name => 'storage.array.delete' }, - ], - kind => 'storage', - name => 'mock', - schema => 1, - version => '1.0', - } - ) -); -write_file( $request, "{\"level\":\"1\"}\n" ); - -my %environment = ( - PATH => "$bin:$ENV{PATH}", - XCAT_GENESIS_HARDWARE_AUDIT => $audit, - XCAT_GENESIS_PROVIDER_DIR => $manifests, - XCAT_GENESIS_PROVIDER_EXEC_DIR => $executables, - XCAT_GENESIS_PROVIDER_TIMEOUT => 5, -); - -my ( $status, $output ) = - run_command( \%environment, '/bin/bash', $dispatcher, 'providers' ); -is( $status, 0, 'provider list is accepted' ) or diag($output); -is( decode_output($output)->{providers}->[0]->{name}, 'mock', - 'provider list returns the manifest' ); - -( $status, $output ) = - run_command( \%environment, '/bin/bash', $dispatcher, 'capabilities', 'mock' ); -is( $status, 0, 'provider capabilities are accepted' ) or diag($output); -is( scalar @{ decode_output($output)->{capabilities} }, 4, - 'provider capabilities are returned' ); - -( $status, $output ) = - run_command( \%environment, '/bin/bash', $dispatcher, 'probe', 'mock' ); -is( $status, 0, 'provider probe succeeds' ) or diag($output); -ok( decode_output($output)->{probes}->[0]->{result}->{detected}, - 'provider probe returns detected hardware' ); - -( $status, $output ) = run_command( - \%environment, '/bin/bash', $dispatcher, 'run', 'mock', 'storage.inventory' -); -is( $status, 0, 'read-only capability succeeds' ) or diag($output); -is( decode_output($output)->{result}->{devices}->[0], 'controller0', - 'read-only result is normalized' ); -ok( !-e $audit, 'read-only capability is not audited as a change' ); - -for my $case ( - [ [], 'destructive capability requires a task identity' ], - [ [ '--task-id', 'task-1', '--request', $request ], - 'destructive capability requires an exact device' ], - [ [ '--task-id', 'task-1', '--device-id', 'controller0' ], - 'destructive capability requires a request' ], - [ [ '--task-id', 'task-1', '--device-id', 'all', '--request', $request ], - 'destructive capability rejects a wildcard device' ], - ) -{ - ( $status, $output ) = run_command( - \%environment, '/bin/bash', $dispatcher, 'run', 'mock', - 'storage.array.create', @{ $case->[0] } - ); - isnt( $status, 0, $case->[1] ); -} - -( $status, $output ) = run_command( - \%environment, '/bin/bash', $dispatcher, 'run', 'mock', - 'storage.array.create', '--task-id', 'task-1', '--device-id', 'controller0', - '--request', $request -); -is( $status, 0, 'authorized destructive capability succeeds' ) or diag($output); -is( decode_output($output)->{result}->{created}, 'controller0', - 'destructive result is normalized' ); -my @audit_records = map { decode_output($_) } split( /\n/, read_file($audit) ); -is_deeply( [ map { $_->{phase} } @audit_records ], [ 'started', 'completed' ], - 'successful change has a closed audit trail' ); -is( $audit_records[0]->{task_id}, 'task-1', - 'audit record carries the task identity' ); -like( $audit_records[0]->{request_sha256}, qr/^[0-9a-f]{64}$/, - 'audit record carries the request digest' ); - -( $status, $output ) = run_command( - \%environment, '/bin/bash', $dispatcher, 'run', 'mock', - 'storage.array.delete', '--task-id', 'task-2', '--device-id', 'controller0', - '--request', $request -); -isnt( $status, 0, 'provider failure is returned' ); -@audit_records = map { decode_output($_) } split( /\n/, read_file($audit) ); -is_deeply( [ map { $_->{phase} } @audit_records ], - [ 'started', 'completed', 'started', 'failed' ], - 'failed change has a closed audit trail' ); - -( $status, $output ) = run_command( - \%environment, '/bin/bash', $dispatcher, 'run', 'mock', 'storage.logs' -); -isnt( $status, 0, 'invalid provider JSON is rejected' ); -( $status, $output ) = run_command( - \%environment, '/bin/bash', $dispatcher, 'run', 'mock', 'storage.unknown' -); -isnt( $status, 0, 'undeclared capability is rejected' ); - -my $linked_manifest = File::Spec->catfile( $manifests, 'linked.json' ); -symlink( File::Spec->catfile( $manifests, 'mock.json' ), $linked_manifest ) - or die "Unable to create $linked_manifest: $!"; -( $status, $output ) = - run_command( \%environment, '/bin/bash', $dispatcher, 'providers' ); -isnt( $status, 0, 'linked provider manifest is rejected' ); -unlink($linked_manifest) or die "Unable to remove $linked_manifest: $!"; - -my $invalid_manifest = File::Spec->catfile( $manifests, 'invalid.json' ); -write_file( $invalid_manifest, "{}\n" ); -( $status, $output ) = - run_command( \%environment, '/bin/bash', $dispatcher, 'providers' ); -isnt( $status, 0, 'invalid provider manifest is rejected' ); -unlink($invalid_manifest) or die "Unable to remove $invalid_manifest: $!"; - -my $mock_executable = File::Spec->catfile( $executables, 'mock' ); -my $real_executable = File::Spec->catfile( $executables, 'mock.real' ); -rename( $mock_executable, $real_executable ) - or die "Unable to rename $mock_executable: $!"; -symlink( $real_executable, $mock_executable ) - or die "Unable to link $mock_executable: $!"; -( $status, $output ) = - run_command( \%environment, '/bin/bash', $dispatcher, 'probe', 'mock' ); -isnt( $status, 0, 'linked provider executable is rejected' ); -unlink($mock_executable) or die "Unable to remove $mock_executable: $!"; -rename( $real_executable, $mock_executable ) - or die "Unable to restore $mock_executable: $!"; - -my $linked_request = File::Spec->catfile( $root, 'linked-request.json' ); -symlink( $request, $linked_request ) or die "Unable to link $linked_request: $!"; -( $status, $output ) = run_command( - \%environment, '/bin/bash', $dispatcher, 'run', 'mock', - 'storage.array.create', '--task-id', 'task-3', '--device-id', 'controller0', - '--request', $linked_request -); -isnt( $status, 0, 'linked hardware request is rejected' ); - -my %bad_timeout = ( %environment, XCAT_GENESIS_PROVIDER_TIMEOUT => 0 ); -( $status, $output ) = - run_command( \%bad_timeout, '/bin/bash', $dispatcher, 'providers' ); -isnt( $status, 0, 'invalid provider timeout is rejected' ); - -unlink("$audit.lock") or die "Unable to remove $audit.lock: $!"; -my $lock_target = File::Spec->catfile( $root, 'lock-target' ); -write_file( $lock_target, "unchanged\n" ); -symlink( $lock_target, "$audit.lock" ) or die "Unable to link audit lock: $!"; -( $status, $output ) = run_command( - \%environment, '/bin/bash', $dispatcher, 'run', 'mock', - 'storage.array.create', '--task-id', 'task-3', '--device-id', 'controller0', - '--request', $request -); -isnt( $status, 0, 'linked audit lock is rejected' ); -is( read_file($lock_target), "unchanged\n", 'linked audit target is untouched' ); - -write_file( - File::Spec->catfile( $bin, 'nvme' ), - "#!/bin/sh\nprintf '{\"arguments\":\"%s\"}\\n' \"\$*\"\n", 0755 -); -my $sys_nvme = File::Spec->catdir( $root, 'sys', 'class', 'nvme' ); -my $dev = File::Spec->catdir( $root, 'dev' ); -make_path( File::Spec->catdir( $sys_nvme, 'nvme0' ), $dev ); -write_file( File::Spec->catfile( $dev, 'nvme0' ), '' ); -my %nvme_environment = ( - PATH => "$bin:$ENV{PATH}", - XCAT_GENESIS_DEV_DIR => $dev, - XCAT_GENESIS_SYS_CLASS_NVME => $sys_nvme, -); -( $status, $output ) = - run_command( \%nvme_environment, '/bin/bash', $nvme_provider, 'probe' ); -is( $status, 0, 'NVMe probe succeeds' ) or diag($output); -is( decode_output($output)->{devices}->[0], 'nvme0', - 'NVMe probe returns an exact controller identity' ); -( $status, $output ) = run_command( - \%nvme_environment, '/bin/bash', $nvme_provider, 'run', 'storage.health', - 'nvme0', '-' -); -is( $status, 0, 'NVMe health query succeeds' ) or diag($output); -like( decode_output($output)->{arguments}, qr{smart-log .*/nvme0 -o json}, - 'NVMe health query uses the selected controller' ); -( $status, $output ) = run_command( - \%nvme_environment, '/bin/bash', $nvme_provider, 'run', 'storage.health', - 'nvme0n1', '-' -); -isnt( $status, 0, 'NVMe provider rejects a namespace identity' ); - -write_file( - File::Spec->catfile( $bin, 'mstflint' ), - "#!/bin/sh\nprintf '%s\\n' 'firmware query'\n", 0755 -); -my $pci = File::Spec->catdir( $root, 'sys', 'bus', 'pci', 'devices' ); -my $mellanox = File::Spec->catdir( $pci, '0000:03:00.0' ); -my $other = File::Spec->catdir( $pci, '0000:04:00.0' ); -make_path( $mellanox, $other ); -write_file( File::Spec->catfile( $mellanox, 'vendor' ), "0x15b3\n" ); -write_file( File::Spec->catfile( $other, 'vendor' ), "0x8086\n" ); -my %mstflint_environment = ( - PATH => "$bin:$ENV{PATH}", - XCAT_GENESIS_SYS_PCI_DEVICES => $pci, -); -( $status, $output ) = - run_command( \%mstflint_environment, '/bin/bash', $mstflint_provider, 'probe' ); -is( $status, 0, 'mstflint probe succeeds' ) or diag($output); -is_deeply( decode_output($output)->{devices}, ['0000:03:00.0'], - 'mstflint probe returns supported PCI devices' ); -( $status, $output ) = run_command( - \%mstflint_environment, '/bin/bash', $mstflint_provider, 'run', - 'network.firmware.inventory', '0000:03:00.0', '-' -); -is( $status, 0, 'mstflint inventory succeeds' ) or diag($output); -is( decode_output($output)->{raw}, 'firmware query', - 'mstflint inventory returns the tool output' ); -( $status, $output ) = run_command( - \%mstflint_environment, '/bin/bash', $mstflint_provider, 'run', - 'network.firmware.inventory', '0000:04:00.0', '-' -); -isnt( $status, 0, 'mstflint provider rejects another vendor' ); - -write_file( - File::Spec->catfile( $bin, 'iprconfig' ), - <<'SH', 0755 -#!/bin/sh -case "$*" in - '-c show-config') printf '%s\n' 'Power RAID configuration' ;; - '-c show-arrays') printf '%s\n' 'Power RAID arrays healthy' ;; - '-c show-ucode-levels') printf '%s\n' 'Power RAID firmware levels' ;; - *) exit 2 ;; -esac -SH -); -my $scsi_hosts = File::Spec->catdir( $root, 'sys', 'class', 'scsi_host' ); -my $ipr_host = File::Spec->catdir( $scsi_hosts, 'host0' ); -my $other_host = File::Spec->catdir( $scsi_hosts, 'host1' ); -make_path( $ipr_host, $other_host ); -write_file( File::Spec->catfile( $ipr_host, 'proc_name' ), "ipr\n" ); -write_file( File::Spec->catfile( $other_host, 'proc_name' ), "megaraid_sas\n" ); -my %iprutils_environment = ( - PATH => "$bin:$ENV{PATH}", - XCAT_GENESIS_SYS_SCSI_HOSTS => $scsi_hosts, -); -( $status, $output ) = - run_command( \%iprutils_environment, '/bin/bash', $iprutils_provider, 'probe' ); -is( $status, 0, 'iprutils probe succeeds' ) or diag($output); -is_deeply( decode_output($output)->{devices}, ['host0'], - 'iprutils probe returns Power RAID hosts' ); - -for my $case ( - [ 'storage.inventory', 'Power RAID configuration' ], - [ 'storage.health', 'Power RAID arrays healthy' ], - [ 'storage.firmware.inventory', 'Power RAID firmware levels' ], - ) -{ - ( $status, $output ) = run_command( - \%iprutils_environment, '/bin/bash', $iprutils_provider, 'run', - $case->[0], 'host0', '-' - ); - is( $status, 0, "iprutils $case->[0] succeeds" ) or diag($output); - is( decode_output($output)->{raw}, $case->[1], - "iprutils $case->[0] returns the tool output" ); -} - -( $status, $output ) = run_command( - \%iprutils_environment, '/bin/bash', $iprutils_provider, 'run', - 'storage.array.create', 'host0', '-' -); -isnt( $status, 0, 'iprutils provider rejects destructive operations' ); - -done_testing(); diff --git a/xCAT-test/unit/genesis_openembedded_metrics.t b/xCAT-test/unit/genesis_openembedded_metrics.t index b03595431..afa45e49c 100644 --- a/xCAT-test/unit/genesis_openembedded_metrics.t +++ b/xCAT-test/unit/genesis_openembedded_metrics.t @@ -2,72 +2,24 @@ use strict; use warnings; -use File::Path qw(make_path); -use File::Spec; -use File::Temp qw(tempdir); use FindBin; -use IO::Compress::Gzip qw(gzip $GzipError); +use lib "$FindBin::Bin/../lib"; use Test::More; -my $repo_root = File::Spec->catdir( $FindBin::Bin, '..', '..' ); -my $report = File::Spec->catfile( - $repo_root, qw(xCAT-genesis-base oe report) -); -my $metrics = File::Spec->catfile( - $repo_root, - qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-init files genesis-metrics) -); -my $recipe = File::Spec->catfile( - $repo_root, - qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-init xcat-genesis-init_1.0.bb) -); -my $service = File::Spec->catfile( - $repo_root, - qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-init files xcat-genesis-metrics.service) -); +use XCAT::Test::File qw(slurp_repo_file); -sub write_file { - my ( $path, $contents ) = @_; - open( my $stream, '>', $path ) or die "Unable to write $path: $!"; - print {$stream} $contents; - close($stream); -} +# The recipe and the systemd unit are the artifact. genesis-metrics and oe/report are shell and +# are tested in xCAT-test/bats/genesis_openembedded_metrics.bats. -sub read_file { - my ($path) = @_; - open( my $stream, '<', $path ) or die "Unable to read $path: $!"; - my $contents = do { local $/; <$stream> }; - close($stream); - return $contents; -} +my $INIT = 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-init'; -sub run_command { - my ( $environment, @command ) = @_; - local %ENV = ( %ENV, %{$environment} ); - open( my $stream, '-|', @command ) - or die "Unable to run @command: $!"; - my $output = do { local $/; <$stream> }; - close($stream); - return ( $? >> 8, $output ); -} - -sub parse_report { - my ($contents) = @_; - my %values; - foreach my $line ( split( /\n/, $contents ) ) { - my ( $name, $value ) = split( /=/, $line, 2 ); - $values{$name} = $value; - } - return \%values; -} - -my $recipe_contents = read_file($recipe); +my $recipe_contents = slurp_repo_file("$INIT/xcat-genesis-init_1.0.bb"); like( $recipe_contents, qr/file:\/\/genesis-metrics/, 'metrics collector is included in the init package' ); like( $recipe_contents, qr/SYSTEMD_SERVICE.*?xcat-genesis-metrics\.service/s, 'metrics service is enabled with the init package' ); -my $service_contents = read_file($service); +my $service_contents = slurp_repo_file("$INIT/files/xcat-genesis-metrics.service"); like( $service_contents, qr/Requires=xcat-genesis-register\.service\nAfter=xcat-genesis-register\.service/, 'metrics are captured after successful registration' ); @@ -75,109 +27,4 @@ like( $service_contents, qr{ExecStart=/usr/libexec/xcat/genesis-metrics --output /run/xcat/metrics\.env}, 'metrics are stored in the runtime state directory' ); -my $root = tempdir( CLEANUP => 1 ); -my $registration = File::Spec->catfile( $root, 'registration.env' ); -my $meminfo = File::Spec->catfile( $root, 'meminfo' ); -my $uptime = File::Spec->catfile( $root, 'uptime' ); -my $runtime = File::Spec->catfile( $root, 'metrics.env' ); - -write_file( - $registration, - "SCHEMA=1\nSTATE=ACTION_RECEIVED\nDETAIL=Destiny shell\nUPDATED_SECONDS=42\n" -); -write_file( - $meminfo, - "MemTotal: 1048576 kB\nMemFree: 524288 kB\n" - . "MemAvailable: 786432 kB\n" -); -write_file( $uptime, "45.92 12.34\n" ); - -my %metrics_environment = ( - XCAT_REGISTRATION_STATUS_FILE => $registration, - XCAT_MEMINFO_FILE => $meminfo, - XCAT_UPTIME_FILE => $uptime, -); -my ( $status, $output ) = run_command( \%metrics_environment, $metrics ); -is( $status, 0, 'runtime metrics are collected' ); -is_deeply( - parse_report($output), - { - SCHEMA => '1', - BOOT_READY_SECONDS => '42', - CAPTURE_UPTIME_SECONDS => '45', - MEMORY_TOTAL_KIB => '1048576', - MEMORY_AVAILABLE_KIB => '786432', - MEMORY_USED_KIB => '262144', - }, - 'runtime report records boot time and memory use' -); - -( $status, $output ) = run_command( - \%metrics_environment, $metrics, '--output', $runtime -); -is( $status, 0, 'runtime metrics can be stored atomically' ); -is( read_file($runtime), - "SCHEMA=1\nBOOT_READY_SECONDS=42\nCAPTURE_UPTIME_SECONDS=45\n" - . "MEMORY_TOTAL_KIB=1048576\nMEMORY_AVAILABLE_KIB=786432\n" - . "MEMORY_USED_KIB=262144\n", - 'stored runtime report is complete' ); -is( ( stat($runtime) )[2] & 07777, 0644, - 'stored runtime report is readable' ); - -my $image_dir = File::Spec->catdir( $root, 'image' ); -make_path($image_dir); -my $kernel = File::Spec->catfile( $image_dir, 'kernel' ); -my $initramfs = File::Spec->catfile( $image_dir, 'initramfs.cpio.gz' ); -my $payload = "cpio payload\n"; -write_file( $kernel, "kernel\n" ); -gzip( \$payload => $initramfs ) - or die "Unable to create test initramfs: $GzipError"; - -( $status, $output ) = run_command( - {}, $report, '--runtime', $runtime, 'x86_64', $image_dir -); -is( $status, 0, 'image and runtime metrics are reported together' ); -my $current = parse_report($output); -is( $current->{ARCHITECTURE}, 'x86_64', - 'report records the Genesis architecture' ); -is( $current->{KERNEL_BYTES}, -s $kernel, - 'report measures the kernel' ); -is( $current->{COMPRESSED_INITRAMFS_BYTES}, -s $initramfs, - 'report measures the compressed initramfs' ); -is( $current->{UNPACKED_INITRAMFS_BYTES}, length($payload), - 'report measures the unpacked initramfs' ); -is( $current->{BOOT_READY_SECONDS}, 42, - 'report includes the registration-ready time' ); -is( $current->{MEMORY_USED_KIB}, 262144, - 'report includes captured memory use' ); - -my $baseline = File::Spec->catfile( $root, 'baseline.env' ); -write_file( - $baseline, - "SCHEMA=1\nARCHITECTURE=x86_64\nKERNEL_BYTES=" - . ( ( -s $kernel ) - 2 ) - . "\nCOMPRESSED_INITRAMFS_BYTES=" - . ( ( -s $initramfs ) - 3 ) - . "\nUNPACKED_INITRAMFS_BYTES=" - . ( length($payload) - 4 ) - . "\nBOOT_READY_SECONDS=40\nMEMORY_USED_KIB=250000\n" -); - -( $status, $output ) = run_command( - {}, $report, '--runtime', $runtime, '--baseline', $baseline, - 'x86_64', $image_dir -); -is( $status, 0, 'report compares against a matching baseline' ); -my $comparison = parse_report($output); -is( $comparison->{KERNEL_BYTES_DELTA}, 2, - 'kernel growth is reported' ); -is( $comparison->{COMPRESSED_INITRAMFS_BYTES_DELTA}, 3, - 'compressed image growth is reported' ); -is( $comparison->{UNPACKED_INITRAMFS_BYTES_DELTA}, 4, - 'unpacked image growth is reported' ); -is( $comparison->{BOOT_READY_SECONDS_DELTA}, 2, - 'slower registration is reported' ); -is( $comparison->{MEMORY_USED_KIB_DELTA}, 12144, - 'additional memory use is reported' ); - done_testing(); diff --git a/xCAT-test/unit/genesis_openembedded_qeth.t b/xCAT-test/unit/genesis_openembedded_qeth.t index a0a0fa040..1dad115c6 100644 --- a/xCAT-test/unit/genesis_openembedded_qeth.t +++ b/xCAT-test/unit/genesis_openembedded_qeth.t @@ -2,262 +2,19 @@ use strict; use warnings; -use File::Path qw(make_path); -use File::Spec; -use File::Temp qw(tempdir); use FindBin; +use lib "$FindBin::Bin/../lib"; use Test::More; -my $repo_root = File::Spec->catdir( $FindBin::Bin, '..', '..' ); -my $qeth_dir = File::Spec->catdir( - $repo_root, - qw(xCAT-genesis-base 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-base oe meta-xcat-genesis recipes-support s390-tools - s390-tools-znetconf_2.41.0.bb) -); +use XCAT::Test::File qw(repo_path slurp_repo_file); -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); -} +# The systemd unit and the recipes are the artifact. genesis-qeth is shell and is tested in +# xCAT-test/bats/genesis_openembedded_qeth.bats. -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 $META = 'xCAT-genesis-base/oe/meta-xcat-genesis'; +my $QETH = "$META/recipes-connectivity/xcat-genesis-qeth"; -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); +my $service_contents = slurp_repo_file("$QETH/files/xcat-genesis-qeth.service"); like( $service_contents, qr/^ConditionArchitecture=s390x$/m, 'the activation service is limited to s390x' ); like( $service_contents, qr/^ConditionKernelCommandLine=xcatd$/m, @@ -269,7 +26,9 @@ like( $service_contents, qr/^Before=.*\bxcat-genesis-network-state\.service\b/m, 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' ); +ok( -f repo_path("$QETH/xcat-genesis-qeth_1.0.bb"), + 'the qeth service has an OpenEmbedded recipe' ); +ok( -f repo_path("$META/recipes-support/s390-tools/s390-tools-znetconf_2.41.0.bb"), + 'the minimal znetconf package has an OpenEmbedded recipe' ); done_testing(); diff --git a/xCAT-test/unit/genesis_openembedded_runtime.t b/xCAT-test/unit/genesis_openembedded_runtime.t deleted file mode 100644 index 242cd6abf..000000000 --- a/xCAT-test/unit/genesis_openembedded_runtime.t +++ /dev/null @@ -1,731 +0,0 @@ -#!/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 IO::Socket::INET; -use POSIX qw(WNOHANG); -use Test::More; - -my $repo_root = File::Spec->catdir( $FindBin::Bin, '..', '..' ); -my $network_script = File::Spec->catfile( - $repo_root, - qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-init files genesis-network-state) -); -my $network_refresh_script = File::Spec->catfile( - $repo_root, - qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-init files genesis-network-refresh) -); -my $register_script = File::Spec->catfile( - $repo_root, - qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-init files genesis-register) -); -my $status_script = File::Spec->catfile( - $repo_root, - qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-init files genesis-status) -); -my $maintenance_shell_script = File::Spec->catfile( - $repo_root, - qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-init files genesis-maintenance-shell) -); -my $functions_file = File::Spec->catfile( - $repo_root, - qw(xCAT-genesis-base oe meta-xcat-genesis recipes-core xcat-genesis-init files genesis-functions) -); -my $getdestiny_script = File::Spec->catfile( - $repo_root, - qw(xCAT-genesis-scripts usr bin getdestiny) -); -my $nextdestiny_script = File::Spec->catfile( - $repo_root, - qw(xCAT-genesis-scripts usr bin nextdestiny) -); - -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; -} - -sub run_script { - my ( $script, $environment, @arguments ) = @_; - local %ENV = ( %ENV, %{$environment} ); - return system( '/bin/bash', $script, @arguments ) >> 8; -} - -sub capture_command { - my ( $environment, @command ) = @_; - local %ENV = ( %ENV, %{$environment} ); - open( my $fh, '-|', @command ) or die "Unable to run @command: $!"; - my $output = do { local $/; <$fh> }; - close($fh); - return ( $? >> 8, $output ); -} - -sub terminate_command { - my ( $environment, @command ) = @_; - my $pid = fork(); - die "Unable to fork signal test: $!" unless defined($pid); - if ( $pid == 0 ) { - local %ENV = ( %ENV, %{$environment} ); - open( STDOUT, '>', File::Spec->devnull() ) or exit 125; - open( STDERR, '>', File::Spec->devnull() ) or exit 125; - exec @command; - exit 125; - } - - select( undef, undef, undef, 0.2 ); - kill 'TERM', $pid; - for ( 1 .. 30 ) { - my $result = waitpid( $pid, WNOHANG ); - return $? >> 8 if $result == $pid; - select( undef, undef, undef, 0.1 ); - } - kill 'KILL', $pid; - waitpid( $pid, 0 ); - return undef; -} - -my $root = tempdir( CLEANUP => 1 ); -my $bin = File::Spec->catdir( $root, 'bin' ); -my $state_dir = File::Spec->catdir( $root, 'run' ); -my $sys_class_net = File::Spec->catdir( $root, 'sys', 'class', 'net' ); -my $eth0 = File::Spec->catdir( $sys_class_net, 'eth0' ); -my $eth1 = File::Spec->catdir( $sys_class_net, 'eth1' ); -my $cmdline = File::Spec->catfile( $root, 'cmdline' ); -my $uptime = File::Spec->catfile( $root, 'uptime' ); -my $command_log = File::Spec->catfile( $root, 'commands.log' ); -my $destiny_response = File::Spec->catfile( $root, 'destiny-response.xml' ); -my $destiny_metadata = File::Spec->catfile( $state_dir, 'xcat-response.env' ); -my $listener = IO::Socket::INET->new( - LocalAddr => '127.0.0.1', - LocalPort => 0, - Listen => 2, - ReuseAddr => 1, -) or die "Unable to open test listener: $!"; -my $listener_port = $listener->sockport(); -my $listener_pid = fork(); -die "Unable to fork test listener: $!" unless defined($listener_pid); -if ( $listener_pid == 0 ) { - for ( 1 .. 4 ) { - my $client = $listener->accept() or exit 1; - close($client); - } - exit 0; -} - -make_path( $bin, $state_dir, $eth0, $eth1 ); -write_file( File::Spec->catfile( $eth0, 'address' ), - "52:54:00:00:00:35\n" ); -write_file( File::Spec->catfile( $eth0, 'operstate' ), "up\n" ); -write_file( $uptime, "123.45 456.78\n" ); -write_file( - File::Spec->catfile( $bin, 'logger' ), - <<'SH', 0755 -#!/bin/sh -printf 'logger %s\n' "$*" >>"$XCAT_TEST_LOG" -[ -z "${XCAT_TEST_LOGGER_FAIL-}" ] -SH -); -write_file( - File::Spec->catfile( $bin, 'ip' ), - <<'SH', 0755 -#!/bin/sh -case "$*" in - '-4 -o route get '*) printf '%s\n' "$XCAT_TEST_ROUTE" ;; - '-4 -o address show dev eth0 scope global') - printf '%s\n' '2: eth0 inet 192.0.2.98/24 scope global eth0' - ;; -esac -SH -); -write_file( - File::Spec->catfile( $bin, 'nmcli' ), - <<'SH', 0755 -#!/bin/sh -printf 'nmcli %s\n' "$*" >>"$XCAT_TEST_LOG" -case "$*" in - '-t -f DEVICE,TYPE device status') - printf '%s\n' 'eth0:ethernet' 'eth1:ethernet' 'lo:loopback' - ;; - '-g GENERAL.CON-UUID device show eth0') - printf '%s\n' 'connection-eth0' - ;; - '-g GENERAL.CON-UUID device show eth1') - printf '%s\n' 'connection-eth1' - ;; - '-g GENERAL.STATE device show '*) printf '%s\n' '100 (connected)' ;; - '--terse --escape no -g IP4.DNS,IP6.DNS device show eth0') - printf '%s\n' "${XCAT_TEST_DNS-192.0.2.53 | 2001:db8::53}" - ;; - '--wait 30 device connect eth0') - [ -z "${XCAT_TEST_CONNECT_FAIL-}" ] || exit 1 - ;; -esac -SH -); -write_file( - File::Spec->catfile( $bin, 'network-state-refresh' ), - <<'SH', 0755 -#!/bin/sh -printf '%s\n' 'network-state-refresh' >>"$XCAT_TEST_LOG" -SH -); -write_file( - File::Spec->catfile( $bin, 'getdestiny' ), - <<'SH', 0755 -#!/bin/sh -printf 'getdestiny %s\n' "$*" >>"$XCAT_TEST_LOG" -metadata= -while [ "$#" -gt 0 ]; do - case "$1" in - --metadata) metadata=$2; shift 2 ;; - *) shift ;; - esac -done -if [ -n "$metadata" ]; then - cat >"$metadata" <<'ENV' -XCAT_NODE_NAME=node042 -XCAT_DESTINY=osimage=test-image -XCAT_IMAGE_SERVER=192.0.2.10 -XCAT_KERNEL=/tftp/vmlinuz -XCAT_INITRD=/tftp/initrd -XCAT_KERNEL_COMMAND_LINE=console=ttyS0 quiet -ENV -fi -printf '%s\n' "${XCAT_TEST_DESTINY-standby}" -SH -); -write_file( - File::Spec->catfile( $bin, 'openssl' ), - <<'SH', 0755 -#!/bin/sh -printf 'openssl %s\n' "$*" >>"$XCAT_TEST_LOG" -[ -z "${XCAT_TEST_OPENSSL_DELAY-}" ] \ - || exec sleep "$XCAT_TEST_OPENSSL_DELAY" -[ -z "${XCAT_TEST_OPENSSL_STATUS-}" ] \ - || exit "$XCAT_TEST_OPENSSL_STATUS" -cat "$XCAT_TEST_RESPONSE_FILE" -SH -); - -my %environment = ( - PATH => "$bin:$ENV{PATH}", - XCAT_CMDLINE_FILE => $cmdline, - XCAT_STATE_DIR => $state_dir, - XCAT_STATUS_COMMAND => $status_script, - XCAT_STATUS_DIR => File::Spec->catdir( $state_dir, 'status' ), - XCAT_SYS_CLASS_NET => $sys_class_net, - XCAT_TEST_LOG => $command_log, - XCAT_TEST_RESPONSE_FILE => $destiny_response, - XCAT_TEST_ROUTE => '127.0.0.1 via 192.0.2.1 dev eth0 src 192.0.2.98', - XCAT_UPTIME_FILE => $uptime, - XCAT_GENESIS_FUNCTIONS => $functions_file, - XCAT_REGISTRATION_ATTEMPTS => 1, - XCAT_REGISTRATION_RETRY_SECONDS => 0, - XCAT_REGISTRATION_REQUEST_TIMEOUT => 1, -); - -write_file( - $destiny_response, - <<'XML' - - -node042 -osimage=test-image -/tftp/vmlinuz -/tftp/initrd -console=ttyS0 quiet -192.0.2.10 - - -XML -); -my ( $destiny_status, $destiny_output ) = capture_command( - \%environment, - '/bin/bash', $getdestiny_script, - '192.0.2.10:3001', '--once', '--metadata', $destiny_metadata -); -is( $destiny_status, 0, 'getdestiny accepts a complete response' ); -is( $destiny_output, "osimage=test-image\n", - 'getdestiny preserves the complete action' ); -is( - read_file($destiny_metadata), - <<'ENV', -XCAT_NODE_NAME=node042 -XCAT_DESTINY=osimage=test-image -XCAT_IMAGE_SERVER=192.0.2.10 -XCAT_KERNEL=/tftp/vmlinuz -XCAT_INITRD=/tftp/initrd -XCAT_KERNEL_COMMAND_LINE=console=ttyS0 quiet -ENV - 'getdestiny records the response metadata' -); - -write_file( $destiny_response, "denied\n" ); -( $destiny_status, $destiny_output ) = capture_command( - \%environment, - '/bin/bash', $getdestiny_script, - '192.0.2.10:3001', '--once', '--metadata', $destiny_metadata -); -isnt( $destiny_status, 0, 'getdestiny rejects an error response' ); -is( read_file($destiny_metadata), <<'ENV', -XCAT_NODE_NAME=node042 -XCAT_DESTINY=osimage=test-image -XCAT_IMAGE_SERVER=192.0.2.10 -XCAT_KERNEL=/tftp/vmlinuz -XCAT_INITRD=/tftp/initrd -XCAT_KERNEL_COMMAND_LINE=console=ttyS0 quiet -ENV - 'a failed request preserves prior metadata' -); - -is( - terminate_command( - \%environment, - '/bin/bash', $getdestiny_script, '192.0.2.10:3001' - ), - 143, - 'legacy getdestiny retries an explicit error response' -); - -{ - my %failure_environment = ( - %environment, - XCAT_TEST_OPENSSL_STATUS => 1, - ); - is( - terminate_command( - \%failure_environment, - '/bin/bash', $getdestiny_script, '192.0.2.10:3001' - ), - 143, - 'legacy getdestiny retries a connection failure' - ); -} - -write_file( $destiny_response, "\n" ); -( $destiny_status, $destiny_output ) = capture_command( - \%environment, - '/bin/bash', $getdestiny_script, '192.0.2.10:3001' -); -is( $destiny_status, 0, - 'legacy getdestiny accepts a response without a destiny' ); -is( $destiny_output, "\n", - 'legacy getdestiny returns an empty destiny' ); - -( $destiny_status, $destiny_output ) = capture_command( - \%environment, - '/bin/bash', $getdestiny_script, '192.0.2.10:3001', '--once' -); -isnt( $destiny_status, 0, - 'one-shot getdestiny rejects a response without a destiny' ); - -write_file( - $destiny_response, - <<'XML' - - -node042 -install rocky9.7-x86_64-compute -/tftp/vmlinuz -/tftp/initrd -console=ttyS0 quiet -192.0.2.10 - - -XML -); -( $destiny_status, $destiny_output ) = capture_command( - \%environment, - '/bin/bash', $nextdestiny_script, - '192.0.2.10:3001', '--once', '--metadata', $destiny_metadata -); -is( $destiny_status, 0, 'nextdestiny accepts a complete response' ); -is( $destiny_output, "install rocky9.7-x86_64-compute\n", - 'nextdestiny preserves a legacy action with arguments' ); -like( read_file($destiny_metadata), - qr/^XCAT_DESTINY=install rocky9\.7-x86_64-compute$/m, - 'nextdestiny records the complete action' ); - -write_file( $destiny_response, - "chain unavailable\n" ); -( $destiny_status, $destiny_output ) = capture_command( - \%environment, - '/bin/bash', $nextdestiny_script, - '192.0.2.10:3001', '--once' -); -is( $destiny_status, 0, 'nextdestiny returns a server error as an action' ); -is( $destiny_output, "error=chain unavailable\n", - 'nextdestiny preserves legacy error handling' ); - -write_file( $destiny_response, "\n" ); -( $destiny_status, $destiny_output ) = capture_command( - \%environment, - '/bin/bash', $nextdestiny_script, - '192.0.2.10:3001', '--once' -); -isnt( $destiny_status, 0, 'nextdestiny rejects an incomplete response' ); - -( $destiny_status, $destiny_output ) = capture_command( - \%environment, - '/bin/bash', $nextdestiny_script, '192.0.2.10:3001' -); -is( $destiny_status, 0, 'legacy nextdestiny returns an incomplete response' ); -is( $destiny_output, "error=No destiny command received\n", - 'legacy nextdestiny leaves retry pacing to doxcat' ); - -{ - my %signal_environment = ( - %environment, - XCAT_TEST_OPENSSL_DELAY => 30, - ); - is( - terminate_command( - \%signal_environment, - '/bin/bash', $getdestiny_script, '192.0.2.10:3001' - ), - 143, - 'SIGTERM stops getdestiny during a request' - ); - is( - terminate_command( - \%signal_environment, - '/bin/bash', $nextdestiny_script, '192.0.2.10:3001' - ), - 143, - 'SIGTERM stops nextdestiny during a request' - ); -} - -write_file( - $cmdline, - "xcatd=127.0.0.1:$listener_port BOOTIF=01-52-54-00-00-00-35\n" -); -is( run_script( $network_script, \%environment ), 0, - 'network state accepts DHCP boot state' ); -is( - read_file( File::Spec->catfile( $state_dir, 'genesis.env' ) ), - "XCATDEST=127.0.0.1:$listener_port\n" - . "XCATMASTER=127.0.0.1\n" - . "XCATPORT=$listener_port\n" - . <<'ENV', -XCAT_INTERFACE=eth0 -XCAT_SOURCE_ADDRESS=192.0.2.98 -XCAT_SOURCE_PREFIXED_ADDRESS=192.0.2.98/24 -XCAT_GATEWAY=192.0.2.1 -XCAT_DNS_SERVERS=192.0.2.53,2001:db8::53 -XCAT_NETWORK_METHOD=auto -XCAT_LINK_STATE=up -XCAT_MAC_ADDRESS=52:54:00:00:00:35 -XCAT_VERIFIED_SECONDS=123 -ENV - 'network state is written atomically' -); -is( - read_file( - File::Spec->catfile( $state_dir, 'status', 'network.env' ) - ), - <<'ENV', -SCHEMA=1 -STATE=READY -DETAIL=Management network ready on eth0 -STARTED_SECONDS=123 -UPDATED_SECONDS=123 -VERIFIED_SECONDS=123 -ENV - 'network readiness publishes status' -); -{ - my %failed_logger_environment = ( - %environment, - XCAT_TEST_LOGGER_FAIL => 1, - ); - is( run_script( $network_script, \%failed_logger_environment ), 0, - 'network readiness does not depend on logging' ); -} - -my $safe_network_state = read_file( - File::Spec->catfile( $state_dir, 'genesis.env' ) -); -my $injection_marker = File::Spec->catfile( $root, 'injected' ); -my %unsafe_network_environment = ( - %environment, - XCAT_TEST_DNS => "192.0.2.53;touch$injection_marker", -); -isnt( run_script( $network_script, \%unsafe_network_environment ), 0, - 'unsafe generated network state is rejected' ); -is( - read_file( File::Spec->catfile( $state_dir, 'genesis.env' ) ), - $safe_network_state, - 'unsafe data does not replace prior network state' -); -ok( !-e $injection_marker, 'generated network data is never evaluated' ); -like( - read_file( File::Spec->catfile( $state_dir, 'status', 'network.env' ) ), - qr/^CODE=UNSAFE_NETWORK_STATE$/m, - 'unsafe generated state has a specific failure code' -); - -write_file( $command_log, '' ); -$environment{XCAT_NETWORK_STATE_COMMAND} = File::Spec->catfile( - $bin, 'network-state-refresh' ); -is( run_script( $network_refresh_script, \%environment, 'restart (eth0)' ), - 0, 'discovery refreshes its DHCP identity' ); -my $refresh_log = read_file($command_log); -like( $refresh_log, qr/^nmcli --wait 10 device disconnect eth1$/m, - 'forced interface selection disconnects other Ethernet devices' ); -like( $refresh_log, qr/^nmcli --wait 10 device disconnect eth0$/m, - 'the discovery interface releases its temporary lease' ); -like( $refresh_log, qr/^nmcli --wait 30 device connect eth0$/m, - 'the discovery interface requests its assigned lease' ); -like( $refresh_log, qr/^network-state-refresh$/m, - 'network state is rebuilt after DHCP renewal' ); - -write_file( $command_log, '' ); -my %failed_refresh_environment = ( - %environment, - XCAT_TEST_CONNECT_FAIL => 1, -); -isnt( - run_script( - $network_refresh_script, \%failed_refresh_environment, - 'restart (eth0)' - ), - 0, - 'a failed DHCP renewal returns an error' -); -$refresh_log = read_file($command_log); -like( - $refresh_log, - qr/^nmcli --wait 30 connection up uuid connection-eth0 ifname eth0$/m, - 'a failed renewal restores the management connection' -); -like( - $refresh_log, - qr/^nmcli --wait 30 connection up uuid connection-eth1 ifname eth1$/m, - 'a failed renewal restores other disconnected connections' -); -unlike( $refresh_log, qr/^network-state-refresh$/m, - 'failed renewal does not publish incomplete network state' ); -delete $environment{XCAT_NETWORK_STATE_COMMAND}; - -write_file( - $cmdline, - "xcatd=127.0.0.1:$listener_port BOOTIF=01-52-54-00-00-00-35 " - . 'hostip=192.0.2.98 netmask=255.255.255.192 ' - . "gateway=192.0.2.1\n" -); -write_file( $command_log, '' ); -is( run_script( $network_script, \%environment ), 0, - 'network state accepts static boot settings' ); -like( read_file($command_log), qr/ipv4\.addresses 192\.0\.2\.98\/26/, - 'dotted netmask becomes a CIDR prefix' ); - -write_file( - $cmdline, - "xcatd=127.0.0.1:$listener_port BOOTIF=01-52-54-00-00-00-35 " - . 'hostip=2001:db8::98/64 netmask=64 ' - . "gateway=2001:db8::1\n" -); -write_file( $command_log, '' ); -is( run_script( $network_script, \%environment ), 0, - 'network state accepts static IPv6 boot settings' ); -like( read_file($command_log), - qr/ipv4\.method disabled ipv6\.method manual ipv6\.addresses 2001:db8::98\/64 ipv6\.gateway 2001:db8::1/, - 'static IPv6 disables IPv4 and configures the IPv6 route' ); - -write_file( - $cmdline, - "xcatd=127.0.0.1:$listener_port BOOTIF=01-52-54-00-00-00-35 " - . "hostip=192.0.2.98 netmask=255.255.255.0\n" -); -isnt( run_script( $network_script, \%environment ), 0, - 'partial static settings fail closed' ); -like( - read_file( - File::Spec->catfile( $state_dir, 'status', 'network.env' ) - ), - qr/^STATE=FAILED$/m, - 'network failure replaces the prior status' -); -like( - read_file( - File::Spec->catfile( $state_dir, 'status', 'network.env' ) - ), - qr/^CODE=INVALID_STATIC_NETWORK$/m, - 'network failure identifies the rejected configuration' -); - -$environment{XCATDEST} = '192.0.2.213:3001'; -write_file( $command_log, '' ); -write_file( $cmdline, "xcatd=192.0.2.213:3001 destiny=shell\n" ); -is( run_script( $register_script, \%environment ), 0, - 'registration accepts a kernel destiny' ); -is( read_file( File::Spec->catfile( $state_dir, 'destiny' ) ), "shell\n", - 'kernel destiny remains authoritative' ); -like( read_file($command_log), qr/getdestiny 192\.0\.2\.213:3001/, - 'kernel destiny still records xCAT contact' ); -like( - read_file( - File::Spec->catfile( $state_dir, 'status', 'registration.env' ) - ), - qr/^STATE=ACTION_RECEIVED$/m, - 'registration records the received action' -); -like( - read_file( - File::Spec->catfile( $state_dir, 'status', 'registration.env' ) - ), - qr/^ACTION=shell\nTARGET=\nNODE_NAME=node042$/m, - 'registration publishes confirmed identity and the selected action' -); -{ - my %failed_logger_environment = ( - %environment, - XCAT_TEST_LOGGER_FAIL => 1, - ); - is( run_script( $register_script, \%failed_logger_environment ), 0, - 'successful registration does not depend on logging' ); -} - -write_file( $command_log, '' ); -write_file( $cmdline, "xcatd=192.0.2.213:3001\n" ); -is( run_script( $register_script, \%environment ), 0, - 'registration requests a missing destiny' ); -is( read_file( File::Spec->catfile( $state_dir, 'destiny' ) ), "standby\n", - 'xCAT supplies a missing destiny' ); - -$environment{XCAT_TEST_DESTINY} = ''; -isnt( run_script( $register_script, \%environment ), 0, - 'registration rejects an empty destiny' ); -is( read_file( File::Spec->catfile( $state_dir, 'destiny' ) ), "standby\n", - 'an empty destiny does not replace prior state' ); -like( - read_file( - File::Spec->catfile( $state_dir, 'status', 'registration.env' ) - ), - qr/^STATE=FAILED$/m, - 'registration failure replaces the prior status' -); -like( - read_file( - File::Spec->catfile( $state_dir, 'status', 'registration.env' ) - ), - qr/^CODE=XCAT_RESPONSE_UNAVAILABLE\nRECOVERY=/m, - 'registration failure includes a recovery hint' -); - -$environment{XCAT_TEST_DESTINY} = 'install rocky9.7-x86_64-compute'; -is( run_script( $register_script, \%environment ), 0, - 'registration accepts a legacy action with arguments' ); -like( - read_file( - File::Spec->catfile( $state_dir, 'status', 'registration.env' ) - ), - qr/^ACTION=install\nTARGET=rocky9\.7-x86_64-compute$/m, - 'registration separates the action from its argument' -); - -{ - local %ENV = ( %ENV, %environment ); - is( system( '/bin/sh', $status_script, 'console', 'DEGRADED', - "bad\n\tvalue\x01" ) >> 8, - 0, 'status helper accepts a valid record' ); - like( - read_file( - File::Spec->catfile( $state_dir, 'status', 'console.env' ) - ), - qr/^DETAIL=bad value$/m, - 'status detail is reduced to printable text' - ); - write_file( $uptime, "130.00 500.00\n" ); - is( system( '/bin/sh', $status_script, 'console', 'DEGRADED', - 'waiting for operator', 'CODE=OPERATOR_WAIT', - 'RECOVERY=Review diagnostics' ) >> 8, - 0, 'status helper accepts structured fields' ); - is( - read_file( - File::Spec->catfile( $state_dir, 'status', 'console.env' ) - ), - <<'ENV', -SCHEMA=1 -STATE=DEGRADED -DETAIL=waiting for operator -STARTED_SECONDS=123 -UPDATED_SECONDS=130 -CODE=OPERATOR_WAIT -RECOVERY=Review diagnostics -ENV - 'status helper preserves the stage start time' - ); - open( my $saved_stderr, '>&', \*STDERR ) - or die "Unable to duplicate stderr: $!"; - open( STDERR, '>', File::Spec->devnull() ) - or die "Unable to redirect stderr: $!"; - isnt( system( '/bin/sh', $status_script, '../console', 'READY' ) >> 8, - 0, 'status helper rejects an unsafe component' ); - isnt( system( '/bin/sh', $status_script, 'console', 'UNKNOWN' ) >> 8, - 0, 'status helper rejects an unknown state' ); - isnt( system( '/bin/sh', $status_script, 'console', 'READY', '', - 'ATTEMPT=invalid' ) >> 8, - 0, 'status helper rejects invalid numeric fields' ); - open( STDERR, '>&', $saved_stderr ) - or die "Unable to restore stderr: $!"; -} - -my $component_status_dir = File::Spec->catdir( $state_dir, 'status' ); -for my $component (qw(network extensions registration action)) { - my $state = $component eq 'extensions' ? 'FAILED' : - $component eq 'action' ? 'IDLE' : 'READY'; - write_file( - File::Spec->catfile( $component_status_dir, "$component.env" ), - "STATE=$state\n" - ); -} -my ( $shell_status, $shell_output ) = capture_command( - \%environment, - '/bin/bash', '-c', - 'printf "exit\\n" | /bin/bash "$1" 2>&1', - 'genesis-maintenance-shell', $maintenance_shell_script -); -is( $shell_status, 0, 'maintenance shell exits cleanly' ); -like( - $shell_output, - qr/^Overall state: FAILED$/m, - 'maintenance shell reports an extension failure as the overall state' -); - -write_file( - File::Spec->catfile( $component_status_dir, 'extensions.env' ), - "STATE=READY\n" -); -( $shell_status, $shell_output ) = capture_command( - \%environment, - '/bin/bash', '-c', - 'printf "exit\\n" | /bin/bash "$1" 2>&1', - 'genesis-maintenance-shell', $maintenance_shell_script -); -like( - $shell_output, - qr/^Overall state: IDLE$/m, - 'maintenance shell prefers the current action state' -); - -waitpid( $listener_pid, 0 ); -is( $? >> 8, 0, 'network readiness probes the xCAT port' ); - -done_testing(); diff --git a/xCAT-test/unit/genesis_readme_paths.t b/xCAT-test/unit/genesis_readme_paths.t index 681901e93..30a46baba 100755 --- a/xCAT-test/unit/genesis_readme_paths.t +++ b/xCAT-test/unit/genesis_readme_paths.t @@ -3,18 +3,20 @@ use strict; use warnings; use FindBin; +use lib "$FindBin::Bin/../lib"; use Test::More; +use XCAT::Test::File qw(repo_path slurp_repo_file); + # The Genesis README is the build instructions: every command in it is meant to be pasted. A # path that no longer exists is the whole document failing, and a rename is what breaks it -- # the directory was xCAT-genesis-builder until 2.19 and every path in the README carried that # name. The file is the artifact here, so reading it is the test. -my $root = "$FindBin::Bin/../.."; -my $readme = "$root/xCAT-genesis-base/README.md"; -plan skip_all => 'xCAT-genesis-base/README.md not found' unless -r $readme; +my $README = 'xCAT-genesis-base/README.md'; +plan skip_all => "$README not found" unless -r repo_path($README); -my $text = do { open my $fh, '<', $readme or die "$readme: $!"; local $/; <$fh> }; +my $text = slurp_repo_file($README); # Every in-tree path the README names, whatever directory it names -- a rename that half # happened leaves the old name here, and a pattern matching only the new one would collect @@ -25,15 +27,15 @@ my @named = sort keys %named; # A fixed floor, so a pattern that stops matching cannot shrink the plan to nothing and pass. my $EXPECTED_AT_LEAST = 5; -BAIL_OUT(sprintf('the README names %d in-tree paths, fewer than the %d it should', - scalar(@named), $EXPECTED_AT_LEAST)) +die sprintf("the README names %d in-tree paths, fewer than the %d it should\n", + scalar(@named), $EXPECTED_AT_LEAST) if @named < $EXPECTED_AT_LEAST; -plan tests => scalar(@named) + 2; - -ok(-e "$root/$_", "the README names $_, and it is there") for @named; +ok(-e repo_path($_), "the README names $_, and it is there") for @named; my ($link) = $text =~ m{\]\((\.\./docs/[^)]+)\)}; ok($link, 'the README links to the architecture plan'); -ok($link && -e "$root/xCAT-genesis-base/$link", +ok($link && -e repo_path("docs/" . ($link =~ s{^\.\./docs/}{}r)), 'and the link resolves from the directory the README sits in'); + +done_testing(); diff --git a/xCAT-test/unit/go_xcat_uninstall_installed_only.t b/xCAT-test/unit/go_xcat_uninstall_installed_only.t deleted file mode 100755 index 18fbbde35..000000000 --- a/xCAT-test/unit/go_xcat_uninstall_installed_only.t +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env perl -use strict; -use warnings; - -use FindBin; -use Test::More; - -# `go-xcat uninstall` hands GO_XCAT_UNINSTALL_LIST to the package manager. That list names -# packages xCAT stopped producing -- xCAT-genesis-builder, yaboot-xcat, conserver-xcat -- so -# most of it is not installed on any one node. apt answers "Unable to locate package" for a -# name it does not know, and go-xcat prints a warning for each; dnf answers "No match for -# argument" and fails the whole call when nothing in the list matches. -# -# So what is not installed does not reach the package manager. That is also what makes keeping -# a retired name in the list free, which is why the list outlives the build. -# -# go-xcat is sourced and asked about the host's own package database. bash is installed on -# every build host, and the xcat-test-absent-* names are installed on none. - -my $go_xcat = "$FindBin::Bin/../../xCAT-server/share/xcat/tools/go-xcat"; -plan skip_all => 'go-xcat not found' unless -r $go_xcat; - -# Run a bash snippet after go-xcat is sourced, and return its output lines. -sub go_xcat { - my ($snippet) = @_; - my @lines = `bash -c 'source "\$1" && eval "\$2"' go-xcat '$go_xcat' '$snippet'`; - chomp @lines; - return \@lines; -} - -is_deeply(go_xcat('installed_packages xcat-test-absent-1 bash xcat-test-absent-2'), ['bash'], - 'only the installed packages are reported'); - -is_deeply(go_xcat('installed_packages xcat-test-absent-1 xcat-test-absent-2'), [], - 'a list with nothing installed reports nothing'); - -# remove_package is replaced so the test removes nothing from the host. -is_deeply(go_xcat('remove_package() { printf "remove %s\n" "$@"; } - GO_XCAT_UNINSTALL_LIST=(xCAT-genesis-builder xcat-test-absent-1 bash) - uninstall_xcat -y'), ['remove -y', 'remove bash'], - 'only the installed packages reach the package manager'); - -is_deeply(go_xcat('remove_package() { printf "remove %s\n" "$@"; } - GO_XCAT_UNINSTALL_LIST=(xCAT-genesis-builder xcat-test-absent-1) - uninstall_xcat -y; echo "uninstall_xcat returns $?"'), ['uninstall_xcat returns 0'], - 'a node with none of them installed hands the package manager nothing, and succeeds'); - -done_testing(); From 5a66b20aea774844e0dde85f4e1edb153953301a Mon Sep 17 00:00:00 2001 From: Daniel Hilst <392820+dhilst@users.noreply.github.com> Date: Fri, 25 Sep 2026 12:41:28 -0300 Subject: [PATCH 15/17] test(xcat-core): remove the regex checks over the Genesis console C source genesis_openembedded_console.t matched regexes against newt_ui.c and the other console sources. The checks pass when the code moves or stops running, and fail when it is only reformatted. The console logic outside the Newt interface is built and run by genesis_openembedded_console.bats. The Newt interface has no test until a C test with a stub libnewt is added. Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com> --- xCAT-test/unit/genesis_openembedded.t | 3 +- xCAT-test/unit/genesis_openembedded_console.t | 133 ------------------ 2 files changed, 1 insertion(+), 135 deletions(-) delete mode 100644 xCAT-test/unit/genesis_openembedded_console.t diff --git a/xCAT-test/unit/genesis_openembedded.t b/xCAT-test/unit/genesis_openembedded.t index 978ac6c6f..1fd313d20 100644 --- a/xCAT-test/unit/genesis_openembedded.t +++ b/xCAT-test/unit/genesis_openembedded.t @@ -11,8 +11,7 @@ use XCAT::Test::File qw(repo_path slurp_repo_file); # The Yocto configuration, recipes, kernel fragments and units of the OpenEmbedded Genesis # image are the artifact: kas and bitbake read these lines. The scripts and programs the image -# runs are tested in xCAT-test/bats/genesis_openembedded_*.bats, and the Newt console source in -# genesis_openembedded_console.t. +# runs are tested in xCAT-test/bats/genesis_openembedded_*.bats. my $kas = slurp_repo_file('xCAT-genesis-base/oe/kas/common.yml'); like( $kas, qr/tag: yocto-6\.0\.2/, 'build uses Yocto 6.0.2' ); diff --git a/xCAT-test/unit/genesis_openembedded_console.t b/xCAT-test/unit/genesis_openembedded_console.t deleted file mode 100644 index 55f487aec..000000000 --- a/xCAT-test/unit/genesis_openembedded_console.t +++ /dev/null @@ -1,133 +0,0 @@ -#!/usr/bin/env perl -use strict; -use warnings; - -use FindBin; -use lib "$FindBin::Bin/../lib"; -use Test::More; - -use XCAT::Test::File qw(slurp_repo_file); - -# The Newt interface of the Genesis console. newt_ui.c needs libnewt and a terminal, so no -# test builds or drives it, and these checks read its source. The plain console, the shared -# status view, the diagnostics text and the shell launcher are built and run by -# xCAT-test/bats/genesis_openembedded_console.bats. - -my $SRC = 'xCAT-genesis-base/oe/meta-xcat-genesis/recipes-core/xcat-genesis-console/files/xcat-genesis-console/src'; -my $newt_source = slurp_repo_file("$SRC/newt_ui.c"); - -my ($newt_renderer) = $newt_source =~ - /(static void update_form\(.*?)(?=\nstatic const char help_text)/s; -ok( defined($newt_renderer), 'console defines the Newt renderer' ); -like( $newt_renderer, - qr/const struct status_view \*view.*?STATUS_FIELD_COUNT/s, - 'Newt output consumes the shared view' ); -unlike( $newt_renderer, qr/struct console_state|state->/, - 'Newt output cannot select fields from raw state' ); - -my ($help_source) = $newt_source =~ - /(static const char help_text\[\].*?)(?=\nstatic void show_help)/s; -ok( defined($help_source), 'console defines its interface help text' ); -like( $help_source, qr/Screen fields/, 'help explains the visible fields' ); -like( $help_source, - qr/xCAT contact.*Not configured.*Action received.*Failed/s, - 'help explains each xCAT contact result' ); -like( $help_source, qr/Method is DHCP, Static, or.*SLAAC\/DHCPv6/s, - 'help explains the network-method labels' ); -like( $help_source, qr/F3.*follows new entries.*End to resume/s, - 'help explains log following' ); -like( $help_source, qr/F12.*root maintenance shell/s, - 'help explains the maintenance shell' ); -foreach my $state (qw/STARTING IDLE WAITING_FOR_LINK CONFIGURING_NETWORK CONTACTING_XCAT ACTION_RECEIVED RUNNING READY DEGRADED FAILED/) { - like( $help_source, qr/\b\Q$state\E\b/, "help explains the $state state" ); -} -unlike( $help_source, qr/xcat\.(?:console|debug-shell)|updates automatically/, - 'interface help omits boot overrides and redundant refresh advice' ); - -my ($diagnostics_ui) = $newt_source =~ - /(static void show_diagnostics.*?)(?=\nstatic bool journal_value)/s; -ok( defined($diagnostics_ui), 'console defines the diagnostics window' ); -unlike( $diagnostics_ui, qr/newtFormSetTimer/, - 'diagnostics does not refresh while the operator scrolls' ); -like( $diagnostics_ui, - qr/newtTextboxSetText\(text_box, text\);\s+newtRefresh\(\);\s+while/s, - 'diagnostics loads one stable snapshot before handling input' ); - -my ($header_source) = $newt_source =~ - /(static void draw_header.*?)(?=\nstatic void update_form)/s; -ok( defined($header_source), 'console defines the status header' ); -like( $header_source, qr/view->identity/, - 'the header draws the shared view identity' ); -like( $header_source, qr/xcat_header_context_columns\(columns\)/, - 'header clamps its context to the available terminal width' ); - -my ($logs_source) = $newt_source =~ - /(static void show_logs.*?)(?=\nstatic void show_maintenance_shell)/s; -ok( defined($logs_source), 'console defines the log view' ); -like( $logs_source, qr/newtListbox.*NEWT_FLAG_SCROLL/s, - 'log view provides a scrollable list' ); -like( $logs_source, qr/bool follow = true/, - 'log view starts in follow mode' ); -like( $logs_source, - qr/NEWT_KEY_UP.*?NEWT_KEY_PGUP.*?NEWT_KEY_HOME.*?follow = false/s, - 'upward navigation pauses log following' ); -like( $logs_source, - qr/NEWT_KEY_DOWN.*?NEWT_KEY_PGDN.*?follow = selected == item_count/s, - 'downward navigation resumes following only at the tail' ); -like( $logs_source, qr/NEWT_KEY_END.*?follow = true/s, - 'End resumes log following' ); - -my ($shell_source) = $newt_source =~ - /(static void show_maintenance_shell.*?)(?=\nint xcat_run_newt)/s; -ok( defined($shell_source), 'console defines the maintenance-shell action' ); -like( $shell_source, qr/newtWinChoice.*Open.*Cancel/s, - 'maintenance shell requires confirmation' ); -like( $shell_source, qr/xcat_run_maintenance_shell\(\)/, - 'Newt uses the common maintenance-shell launcher' ); -like( $shell_source, qr/newtResume\(\).*newtResizeScreen\(1\)/s, - 'Newt restores and repaints its saved screen after the shell' ); -unlike( $shell_source, qr/newtCls\(\)/, - 'maintenance-shell return preserves the window frame' ); - -# Checks over every console source file. -my $console_source = join "\n", map { slurp_repo_file("$SRC/$_") } - qw(console.h main.c newt_ui.c plain_ui.c shell.c state.c support.c); -unlike( $console_source, qr/genesis-debug-shell|XCAT_ON_DEMAND_SHELL/, - 'status console only uses the common maintenance-shell launcher' ); -unlike( $console_source, qr/\b(?:system|popen)\s*\(/, - 'status console avoids command-string execution' ); -like( $console_source, qr/LOG_LINE_COUNT = 128/, - 'log view keeps a bounded serial-friendly history' ); - -like( $newt_source, qr/newtDrawRootText\(1, 0, "xCAT Genesis"\)/, - 'status console keeps the product name in the header' ); -like( $newt_source, - qr/F1 Help F2 Diagnostics F3 Logs F12 Shell/, - 'status console keeps useful shortcuts in the footer' ); -unlike( $newt_source, qr/NEWT_KEY_F5|F5 Refresh|Ctrl-L Redraw/, - 'status console omits redundant redraw shortcuts' ); -like( $newt_source, qr/newtFormSetTimer\(screen\.form, 1000\)/, - 'status console updates timers once per second' ); -like( $newt_source, qr/newtCenteredWindow\(72, 17, "Genesis status"\)/, - 'main status removes unused top and bottom rows' ); -like( $newt_source, qr/newtCenteredWindow\(72, 19, "Genesis diagnostics"\)/, - 'F2 opens diagnostics' ); -like( $newt_source, qr/update_form\(&screen, &(?:state|view), changed\)/, - 'stable status updates only the timers' ); -like( $newt_source, qr/if \(\+\+redraw >= 30\)/, - 'serial console limits periodic full redraws' ); -like( $newt_source, qr/newtFormAddHotKey\(screen\.form, NEWT_KEY_F3\)/, - 'F3 opens recent Genesis logs' ); -like( $newt_source, qr/sd_journal_open\(&journal,/, - 'log view reads the journal without a subprocess' ); -like( $newt_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( $newt_source, - qr/newtFormAddHotKey\(screen\.form, NEWT_KEY_F12\).*?show_maintenance_shell\(\)/s, - 'F12 opens the maintenance-shell confirmation' ); -like( $newt_source, - qr/newtWinChoice\(.*?Open a root maintenance shell\?/s, - 'the Newt confirmation names root shell access' ); - -done_testing(); From a69debb41b6f09bf30c24ec14cda180ad888df15 Mon Sep 17 00:00:00 2001 From: Daniel Hilst <392820+dhilst@users.noreply.github.com> Date: Mon, 28 Sep 2026 14:55:50 -0300 Subject: [PATCH 16/17] test(xcat-core): go-xcat complete uninstall skips packages with config files left After `apt-get remove`, dpkg reports a package as "deinstall ok config-files". uninstall_xcat_completely() selects packages with installed_packages(), which accepts only "install ok installed". A complete uninstall after an ordinary uninstall therefore does not purge the xCAT packages, and their configuration files stay on the node. The new bats test runs both uninstall paths against a dpkg-query stub. The two purge tests fail on this tree. The ordinary uninstall keeps its installed-only filter. Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com> --- .../bats/go_xcat_uninstall_config_files.bats | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 xCAT-test/bats/go_xcat_uninstall_config_files.bats diff --git a/xCAT-test/bats/go_xcat_uninstall_config_files.bats b/xCAT-test/bats/go_xcat_uninstall_config_files.bats new file mode 100644 index 000000000..cf65fa9dc --- /dev/null +++ b/xCAT-test/bats/go_xcat_uninstall_config_files.bats @@ -0,0 +1,67 @@ +#!/usr/bin/env bats +# +# `apt-get remove` leaves a package in the "deinstall ok config-files" state (dpkg `rc`). +# `go-xcat uninstall` removes, so it skips those. `go-xcat uninstall --completely` purges, +# so it must include them, or a remove followed by a complete uninstall leaves the +# configuration files behind. +# +# go-xcat is sourced. dpkg-query and the package, kill and trash steps are shell functions +# here, and PATH is empty, so no test reads or changes the host package database or files. + +load 'helpers/go_xcat' + +setup() +{ + go_xcat_require_source + # shellcheck disable=SC1090 + source "$GO_XCAT_SOURCE" + remove_package() { printf 'remove %s\n' "$@"; } + purge_package() { printf 'purge %s\n' "$@"; } + kill_xcat() { :; } + trash_xcat() { :; } + # The state dpkg reports after `apt-get remove xcat-test-removed`. + dpkg-query() + { + case "$3" in + xcat-test-installed) printf 'install ok installed' ;; + xcat-test-removed) printf 'deinstall ok config-files' ;; + *) return 1 ;; + esac + } + GO_XCAT_UNINSTALL_LIST=(xcat-test-absent xcat-test-installed xcat-test-removed) +} + +without_path() +{ + local PATH="" + "$@" +} + +@test "deb: complete uninstall purges packages that only have configuration files left" { + run without_path uninstall_xcat_completely + [ "$status" -eq 0 ] + [ "$output" = "purge -y +purge xcat-test-installed +purge xcat-test-removed" ] +} + +@test "deb: complete uninstall after a remove purges the removed package" { + GO_XCAT_UNINSTALL_LIST=(xcat-test-absent xcat-test-removed) + run without_path uninstall_xcat_completely + [ "$status" -eq 0 ] + [ "$output" = "purge -y +purge xcat-test-removed" ] +} + +@test "deb: ordinary uninstall removes only the installed packages" { + run without_path uninstall_xcat -y + [ "$status" -eq 0 ] + [ "$output" = "remove -y +remove xcat-test-installed" ] +} + +@test "deb: a package with only configuration files left is not reported as installed" { + run without_path installed_packages xcat-test-removed xcat-test-installed + [ "$status" -eq 0 ] + [ "$output" = "xcat-test-installed" ] +} From b2e76e61e33ab75ed37aae1ea6da86f14f821c7e Mon Sep 17 00:00:00 2001 From: Daniel Hilst <392820+dhilst@users.noreply.github.com> Date: Mon, 28 Sep 2026 14:56:23 -0300 Subject: [PATCH 17/17] fix(xcat-core): go-xcat complete uninstall does not purge removed packages After `apt-get remove`, dpkg reports a package as "deinstall ok config-files". uninstall_xcat_completely() selected packages with installed_packages(), which accepts only "install ok installed". So `go-xcat completely uninstall` after `go-xcat uninstall` did not purge the xCAT packages, and their configuration files stayed on the node. uninstall_xcat_completely() now uses purgeable_packages(). On Debian it also accepts packages whose state ends in "ok config-files". On rpm systems it is installed_packages(). uninstall_xcat() keeps the installed-only filter. Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com> --- xCAT-server/share/xcat/tools/go-xcat | 31 +++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/xCAT-server/share/xcat/tools/go-xcat b/xCAT-server/share/xcat/tools/go-xcat index d21e69b6c..51c886de8 100755 --- a/xCAT-server/share/xcat/tools/go-xcat +++ b/xCAT-server/share/xcat/tools/go-xcat @@ -2068,6 +2068,35 @@ function installed_packages() function_dispatch "${FUNCNAME[0]}" "$@" } +# $@ package names +# Prints the ones this system can purge. rpm has no removed-but-configured state. +function purgeable_packages_rpm() +{ + installed_packages_rpm "$@" +} + +# $@ package names +# `apt-get remove` leaves "deinstall ok config-files". `apt-get purge` still removes those files. +function purgeable_packages_deb() +{ + type dpkg-query >/dev/null 2>&1 || return 255 + local package="" + for package in "$@" + do + case "$(dpkg-query --show '--showformat=${Status}' "${package}" 2>/dev/null)" in + "install ok installed"|*" ok config-files") + echo "${package}" + ;; + esac + done + return 0 +} + +function purgeable_packages() +{ + function_dispatch "${FUNCNAME[0]}" "$@" +} + # $1 -y function uninstall_xcat() { @@ -2130,7 +2159,7 @@ function trash_xcat() function uninstall_xcat_completely() { local -a present=() - readarray -t present < <(installed_packages "${GO_XCAT_UNINSTALL_LIST[@]}") + readarray -t present < <(purgeable_packages "${GO_XCAT_UNINSTALL_LIST[@]}") [[ "${#present[@]}" -eq "0" ]] || purge_package -y "${present[@]}" kill_xcat