2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-09-21 08:33:20 +00:00

Merge branch 'master' of https://github.com/xcat2/xcat-core into release/2.19-rc1

master moved 149 commits ahead of the branch point and four files needed a
decision.

xCAT/debian/control and xCATsn/debian/control: master moved nmap and
ipmitool-xcat into Depends, raised the ipmitool version and added the s390x
OpenEmbedded Genesis recommendation. The branch made the genesis-scripts
dependency per architecture. Both are kept, so the ppc64el metapackage depends
on xcat-genesis-scripts-ppc64el and no longer on the amd64 package.

build-utils/lib/XCAT/BuildUtils.pm and xCAT-test/unit/build_utils.t: master
replaced @DEB_ARCHES plus the branch's %NO_RISCV64 exception list with
%ARCH_PACKAGES, which carries the architecture list per package.
deb_package_arches returns the same answer for every package, so master's form
is kept and %NO_RISCV64 is dropped.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
This commit is contained in:
Daniel Hilst
2026-09-11 15:59:58 -03:00
171 changed files with 7047 additions and 830 deletions
+1
View File
@@ -0,0 +1 @@
xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-kernel/linux/linux-yocto/0001-s390-use-stable-generator-name.patch whitespace=-space-before-tab
+10 -5
View File
@@ -7,7 +7,7 @@ jobs:
steps:
- uses: actions/checkout@v6
- name: Install dependencies
run: sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends --no-install-suggests build-essential fakeroot reprepro devscripts debhelper libcapture-tiny-perl libfile-slurper-perl libjson-perl libparallel-forkmanager-perl libsoap-lite-perl libdbi-perl libcgi-pm-perl quilt openssh-server dpkg looptools genometools software-properties-common
run: sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends --no-install-suggests bats build-essential fakeroot reprepro devscripts debhelper libcapture-tiny-perl libfile-slurper-perl libjson-perl libparallel-forkmanager-perl libsoap-lite-perl libdbi-perl libcgi-pm-perl quilt openssh-server dpkg looptools genometools software-properties-common
- name: Run tests
run: perl github_action_xcat_test.pl
@@ -38,16 +38,21 @@ jobs:
KAS_WORK_DIR: ${{ runner.temp }}/kas-work
run: |
set -euo pipefail
for architecture in x86 x86_64 ppc64 ppc64le armv7hf aarch64 riscv64; do
for architecture in x86 x86_64 ppc64 ppc64le armv7hf aarch64 riscv64 s390x; do
export KAS_BUILD_DIR="${RUNNER_TEMP}/kas-build-${architecture}"
configuration="xCAT-genesis-builder/oe/kas/${architecture}.yml"
kas dump "${configuration}" >/dev/null
kas shell "${configuration}" -c 'bitbake -p'
done
- name: Validate the image task graph
shell: bash
env:
KAS_WORK_DIR: ${{ runner.temp }}/kas-work
KAS_BUILD_DIR: ${{ runner.temp }}/kas-build-x86_64
run: >-
kas shell xCAT-genesis-builder/oe/kas/x86_64.yml -c
'bitbake -n xcat-genesis-image xcat-genesis-extension-smoke'
run: |
set -euo pipefail
kas shell xCAT-genesis-builder/oe/kas/x86_64.yml -c \
'bitbake -n xcat-genesis-image xcat-genesis-extension-smoke'
export KAS_BUILD_DIR="${RUNNER_TEMP}/kas-build-s390x"
kas shell xCAT-genesis-builder/oe/kas/s390x.yml -c \
'bitbake -n xcat-genesis-image xcat-genesis-extension-smoke'
+12 -14
View File
@@ -147,10 +147,16 @@ use constant XCAT_PROBE_HELPERS => qw(
ServiceNodeUtils.pm
);
# Packages whose .deb carries a real architecture. Everything else in xcat-core is
# Perl and ships as Architecture: all -- one binary serving every Ubuntu release and
# every arch, which is why this build never needs a per-codename chroot.
my %ARCH_PACKAGES = map { $_ => 1 } qw(xCAT xCATsn xCAT-genesis-scripts);
# Packages whose .deb carries a real architecture, and the architectures each is built
# for. Everything else in xcat-core is Perl and ships as Architecture: all -- one binary
# serving every Ubuntu release and every arch, which is why this build never needs a
# per-codename chroot. xCAT-genesis-scripts has no riscv64 control file: riscv64 Genesis
# ships as an OpenEmbedded package.
my %ARCH_PACKAGES = (
'xCAT' => [qw(amd64 ppc64el riscv64)],
'xCATsn' => [qw(amd64 ppc64el riscv64)],
'xCAT-genesis-scripts' => [qw(amd64 ppc64el)],
);
# Ubuntu releases predating ppc64el. Kept as data rather than an `if` in the caller so
# the repo-assembly and the package-selection paths cannot disagree about it.
@@ -158,13 +164,6 @@ my %NO_PPC64EL = map { $_ => 1 } qw(saucy);
my @DEB_ARCHES = qw(amd64 ppc64el riscv64);
# Packages that are NOT built for riscv64. xcat-genesis-scripts-<arch> Depends on
# xcat-genesis-base-<arch>, and no riscv64 genesis-base deb exists: riscv64 takes the
# OpenEmbedded Genesis image from the shared xcat-dep pool instead. Building it here would
# publish a package nothing can install, which is what happens when the arch list is one
# global constant.
my %NO_RISCV64 = map { $_ => 1 } qw(xCAT-genesis-scripts);
# The Ubuntu releases the apt repository serves by default. Single source of truth:
# the builder, the repo assembly and the tests all read it here, so they cannot drift.
my @DEFAULT_DISTS = qw(focal jammy noble resolute);
@@ -330,9 +329,8 @@ sub stage_probe_helpers {
# 'all' is a single arch-independent build; the three arch packages get one per arch.
sub deb_package_arches {
my ($package) = @_;
return ('all') unless $ARCH_PACKAGES{$package // ''};
return grep { $_ ne 'riscv64' } @DEB_ARCHES if $NO_RISCV64{$package};
return @DEB_ARCHES;
my $arches = $ARCH_PACKAGES{ $package // '' };
return $arches ? @{$arches} : ('all');
}
# dist_arches: the architectures a release's apt repo declares.
+5 -5
View File
@@ -317,11 +317,11 @@ sub write_repo_metadata {
. /etc/lsb-release
cd `dirname $0`
host_arch=`uname -m`
if [ "$host_arch" != "ppc64le" ];then
host_arch="amd64"
else
host_arch="ppc64el"
fi
case "$host_arch" in
ppc64le) host_arch="ppc64el" ;;
riscv64) host_arch="riscv64" ;;
*) host_arch="amd64" ;;
esac
echo deb [arch=$host_arch] file://"`pwd`" $DISTRIB_CODENAME main > /etc/apt/sources.list.d/xcat-core.list
SCRIPT
@@ -9,7 +9,7 @@ In a homogeneous cluster, the management node is the same hardware architecture
The issues arises in a heterogeneous cluster, where the management node is running a different level operating system *or* hardware architecture as the compute nodes in which to deploy the image. The ``genimage`` command that builds stateless images depends on various utilities provided by the base operating system and needs to be run on a node with the same hardware architecture and *major* Operating System release as the nodes that will be booted from the image.
When running xCAT >= 2.17 on EL >= 8 based management node with x86_64 architecture, qemu-user-static can be used to cross-build ppc64*, aarch64 and riscv64 osimages. Therefore, you don't need to build images on systems with the target architecture anymore.
When running xCAT >= 2.17 on EL >= 8 based management node with x86_64 architecture, qemu-user-static can be used to cross-build ppc64*, aarch64 and riscv64 osimages. Therefore, you don't need to build images on systems with the target architecture anymore. The same applies to an Ubuntu management node, where ``qemu-user-static`` and ``binfmt-support`` are packages of the distribution and register the handler on install.
Cross-build ppc64*/aarch64/riscv64 stateless/statelite image on x86_64 management node
--------------------------------------------------------------------------------------
@@ -1,43 +1,43 @@
Support Matrix
==============
+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| | RHEL | SLES | RHEL | SLES | Ubuntu | RHEL | SLES | Ubuntu | RHEL | SLES | Ubuntu | RHEL |
| | ppc64 | ppc64 | x86_64 | x86_64 | x86_64 | ppc64le | ppc64le | ppc64el | aarch64 | aarch64 | aarch64 | riscv64 |
| | CN | CN | CN | CN | CN | CN | CN | CN | CN | CN | CN | CN |
+=========+=========+=========+=========+=========+=========+=========+=========+=========+=========+=========+=========+=========+
| RHEL | | | | | | | | | | | | |
| ppc64 | yes | yes | yes | yes | yes | yes | yes | yes | no | no | no | no |
| MN/SN | | | [1]_ | [1]_ | [1]_ | | | | | | | |
+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| SLES | | | | | | | | | | | | |
| ppc64 | yes | yes | yes | yes | yes | yes | yes | yes | no | no | no | no |
| MN/SN | | | [1]_ | [1]_ | [1]_ | | | | | | | |
+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| RHEL | | | | | | | | | | | | |
| x86_64 | yes | yes | yes | yes | yes | yes | yes | yes | yes | no | no | yes |
| MN/SN | [4]_ | [4]_ | | | | | | | | | | [6]_ |
+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| SLES | | | | | | | | | | | | |
| x86_64 | yes | yes | yes | yes | yes | yes | yes | yes | yes | no | no | no |
| MN/SN | [4]_ | [4]_ | | | | | | | | | | |
+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| Ubuntu | | | | | | | | | | | | |
| x86_64 | yes | yes | yes | yes | yes | yes | yes | yes | yes | no | no | no |
| MN/SN | [5]_ | [5]_ | | | | | | | | | | |
+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| RHEL | | | | | | | | | | | | |
| ppc64le | yes | yes | yes | yes | yes | yes | yes | yes | no | no | no | no |
| MN/SN | [2]_ | [2]_ | | | | | | | | | | |
+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| SLES | | | | | | | | | | | | |
| ppc64le | no | no | yes | yes | yes | yes | yes | yes | no | no | no | no |
| MN/SN | | | | | | | | | | | | |
+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| Ubuntu | | | | | | | | | | | | |
| ppc64el | yes | yes | yes | yes | yes | yes | yes | yes | no | no | no | no |
| MN/SN | [3]_ | [3]_ | | | | | | | | | | |
+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| | RHEL | SLES | RHEL | SLES | Ubuntu | RHEL | SLES | Ubuntu | RHEL | SLES | Ubuntu | RHEL | Ubuntu |
| | ppc64 | ppc64 | x86_64 | x86_64 | x86_64 | ppc64le | ppc64le | ppc64el | aarch64 | aarch64 | aarch64 | riscv64 | riscv64 |
| | CN | CN | CN | CN | CN | CN | CN | CN | CN | CN | CN | CN | CN |
+=========+=========+=========+=========+=========+=========+=========+=========+=========+=========+=========+=========+=========+=========+
| RHEL | | | | | | | | | | | | | |
| ppc64 | yes | yes | yes | yes | yes | yes | yes | yes | no | no | no | no | no |
| MN/SN | | | [1]_ | [1]_ | [1]_ | | | | | | | | |
+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| SLES | | | | | | | | | | | | | |
| ppc64 | yes | yes | yes | yes | yes | yes | yes | yes | no | no | no | no | no |
| MN/SN | | | [1]_ | [1]_ | [1]_ | | | | | | | | |
+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| RHEL | | | | | | | | | | | | | |
| x86_64 | yes | yes | yes | yes | yes | yes | yes | yes | yes | no | no | yes | no |
| MN/SN | [4]_ | [4]_ | | | | | | | | | | [6]_ | |
+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| SLES | | | | | | | | | | | | | |
| x86_64 | yes | yes | yes | yes | yes | yes | yes | yes | yes | no | no | no | no |
| MN/SN | [4]_ | [4]_ | | | | | | | | | | | |
+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| Ubuntu | | | | | | | | | | | | | |
| x86_64 | yes | yes | yes | yes | yes | yes | yes | yes | yes | no | no | no | yes |
| MN/SN | [5]_ | [5]_ | | | | | | | | | | | [7]_ |
+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| RHEL | | | | | | | | | | | | | |
| ppc64le | yes | yes | yes | yes | yes | yes | yes | yes | no | no | no | no | no |
| MN/SN | [2]_ | [2]_ | | | | | | | | | | | |
+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| SLES | | | | | | | | | | | | | |
| ppc64le | no | no | yes | yes | yes | yes | yes | yes | no | no | no | no | no |
| MN/SN | | | | | | | | | | | | | |
+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| Ubuntu | | | | | | | | | | | | | |
| ppc64el | yes | yes | yes | yes | yes | yes | yes | yes | no | no | no | no | no |
| MN/SN | [3]_ | [3]_ | | | | | | | | | | | |
+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
Notes:
@@ -54,3 +54,4 @@ Notes:
.. [4] If the compute nodes are DFM managed systems, will need the ppc64le DFM and ppc64le hardware server on the management node.
.. [5] Does not support DFM managed compute nodes, hardware control does not work.
.. [6] riscv64 compute nodes boot through UEFI firmware and grub2 only. The management node needs the riscv64 Genesis image (``xCAT-genesis-openembedded-riscv64``) and ``/tftpboot/boot/grub2/grub2.riscv64`` (see :doc:`/guides/install-guides/yum/grub2`). EL10 compute nodes are supported, validated from an EL10 x86_64 management node; a riscv64 management node is documented in :doc:`/guides/admin-guides/manage_clusters/riscv64/index`.
.. [7] Ubuntu 24.04 and 26.04 riscv64 compute nodes, stateful and stateless, validated from an Ubuntu x86_64 management node. The riscv64 packages that carry a binary (``goconserver``, ``ipmitool-xcat``, ``conserver-xcat``) come from a riscv64 xcat-dep apt repository; see :doc:`/guides/admin-guides/manage_clusters/riscv64/index`.
@@ -146,6 +146,14 @@ You can enable **secureroot** feature for more secure consideration. ::
Then, after the new ``packimage`` or ``nodeset`` command, the root password hash can only be acquired on-the-fly with strict security control.
The ``sudoer`` postscript creates a login user with passwordless ``sudo``, named ``xcat`` unless the postscript runs as ``sudoer -u <username>``, and adds the cluster SSH host key to its ``authorized_keys``. An existing login account is kept, together with the other keys in its ``authorized_keys``. Root, service accounts, and accounts without a login shell are refused. The password of the account is always managed: it comes from the ``passwd`` table, and the account is locked when the table has no row for it: ::
chtab key=system,username=xcat passwd.password=<password>
The node acquires the password hash on-the-fly, the same way as the **secureroot** root password hash. The management node serves the hash only for the sudoer named in the ``postscripts`` or ``postbootscripts`` of the node, its osimage, or ``xcatdefaults``. The postscript grants ``sudo`` and installs the key only after the password is set or locked. If the management node does not answer or refuses the request, the postscript fails and leaves the account unprivileged.
The ``sudo`` rule lives in ``/etc/sudoers.d/xcat-sudoer``, which each run replaces and which records the account it grants. A rerun with another name revokes the previous account: it loses the rule, its password is locked, and the cluster key is removed from its ``authorized_keys``, while its other keys and its login stay. Lines that an older version of the postscript appended to ``/etc/sudoers`` are moved out on the first run, after ``visudo`` accepts the result. A failed run removes the managed rule, so a node keeps no grant from an earlier run. On a node without ``/etc/sudoers.d`` the rule is appended once to ``/etc/sudoers`` and a rename does not revoke the previous name.
Nodes Inter-Access in The Cluster
---------------------------------
@@ -58,7 +58,10 @@ the repository declares. Only ``xCAT``, ``xCATsn`` and ``xCAT-genesis-scripts``
carry an architecture, and there the difference is packaging metadata rather than
compiled output. That is why this build needs no ``sbuild`` and no per-codename
chroot -- unlike xcat-deps, whose packages are compiled and genuinely differ per
release.
release. ``xCAT`` and ``xCATsn`` are built for riscv64 as well as amd64 and
ppc64el, and every release the repository serves declares the architecture;
``xCAT-genesis-scripts`` keeps the two architectures it has control files for,
because riscv64 Genesis ships as an OpenEmbedded package instead.
Helpers shared by both builders live in ``build-utils/lib/XCAT/BuildUtils.pm``.
@@ -129,6 +129,13 @@ The following matrix is the default live validation gate for DHCP backend work.
- ``makedhcp -n``; ``kea-dhcp4 -t``; reservation add/query/delete;
xNBA shell boot; stateful subiquity and stateless compute-image handoff
through kernel, initrd, and root image when image validation is in scope
* - QEMU ``s390-ccw``
- ``s390x``
- ``ISC``
- ``s390-ccw BIOS network IPL``
- ``makedhcp -n``; backend parser; architecture ``0x001f``; DHCP option
209; TFTP fetch of the generated ``pxelinux.cfg``-style configuration,
Genesis kernel, and initramfs
Kea Boot and Reservation Regression Matrix
------------------------------------------
@@ -57,6 +57,9 @@ Supported architectures
* - ``riscv64``
- RV64GC with OpenSBI
- QEMU ``virt``
* - ``s390x``
- 64-bit z/Architecture, z10 or later
- QEMU ``s390-ccw-virtio``
``x86_64`` is the first release target and ``ppc64le`` is the second because
both have physical test systems. The other targets have the same software
@@ -66,7 +69,7 @@ trees, or controllers.
Architecture names are exact. In particular, ``ppc64`` and ``ppc64le`` are
different artifacts. The build does not preserve the old xCAT alias between
them. ``riscv32``, pre-ARMv7 processors, and i586-only x86 processors are not
supported.
supported. The ``s390x`` target does not support the 31-bit ``s390`` ABI.
Networking
----------
@@ -90,6 +93,26 @@ IPv6-only deployment also needs matching support in xCAT server code, DHCP,
boot firmware, and boot configuration. Those changes are outside this layer
and must not be hidden inside the Genesis image.
On ``s390x``, Genesis activates qeth devices before NetworkManager starts. It
accepts the standard ``rd.znet=qeth,read,write,data[,option=value]`` parameter.
Without ``rd.znet``, it activates unconfigured qeth devices in layer 2 mode so
NetworkManager can request DHCP leases. Devices reported as configured by
``znetconf`` are not regrouped. Genesis does not import DPM auto-configuration
data. Layer 3 and IP-mode VSWITCH configurations require ``layer2=0`` in
``rd.znet``.
The ``s390x`` image uses virtio networking under QEMU. QEMU does not emulate
qeth, so the activation service exits without changing the virtio interface.
Physical LPAR and z/VM Genesis networking remain unvalidated.
Genesis does not use the shared CEC serial as an s390x node identifier.
s390x hypervisor guests report themselves as virtual nodes, so sequential and
switch discovery ignore them. Use profile discovery or assign a pending
discovery record with ``nodediscoverdef``.
QEMU validation covers network IPL, DHCP option 209, TFTP, and the
network-specific ``pxelinux.cfg``-style configuration written by ``mknb``.
This release does not advertise a DPM boot configuration.
Legacy xCAT z/VM operating-system provisioning remains unchanged.
xCAT protocol
-------------
@@ -99,6 +122,7 @@ the xcatd wire protocol.
The boot sequence is split into ordered systemd services:
#. On ``s390x``, Genesis activates qeth channel groups.
#. NetworkManager configures candidate interfaces.
#. The network state service selects a management path.
#. Registration asks xcatd for the node destiny.
@@ -234,12 +258,13 @@ checksums, reports, and optional signed extensions. Packages install each
export under ``genesis-openembedded/ARCH``. ``mknb`` verifies and publishes
that export when present, while retaining the old Genesis path as a fallback.
The management-node and service-node packages recommend the ``x86_64`` and
``ppc64le`` images. These are weak dependencies so an older or partial mirror
does not block an xCAT upgrade. Other target images can be installed from the
same common repository before running ``mknb ARCH``. RPM builds based on RPM
4.11 omit the recommendations because that version cannot parse weak dependency
tags. Install the required image package explicitly on those systems.
The management-node and service-node packages recommend the ``x86_64``,
``ppc64le``, ``riscv64``, and ``s390x`` images. These are weak dependencies so
an older or partial mirror does not block an xCAT upgrade. Other target images
can be installed from the same common repository before running ``mknb ARCH``.
RPM builds based on RPM 4.11 omit the recommendations because that version
cannot parse weak dependency tags. Install the required image package
explicitly on those systems.
Server integration should be reviewed separately from the image. Independent
bugs found while testing Genesis, such as TFTP path handling or Kea policy,
@@ -259,7 +284,8 @@ actions.
``x86_64`` and ``ppc64le`` require physical tests before release. VM tests
cannot certify platform firmware, BMC behavior, storage-controller tools,
RDMA firmware operations, GPUs, Secure Boot on vendor firmware, or
board-specific device trees.
board-specific device trees. Physical ``s390x`` support also requires a qeth
channel test on an IBM Z LPAR or z/VM guest.
References
----------
@@ -270,6 +296,10 @@ References
<https://kas.readthedocs.io/en/latest/userguide/project-configuration.html>`_
* `NetworkManager dispatcher interface
<https://networkmanager.dev/docs/api/latest/NetworkManager-dispatcher.html>`_
* `QEMU s390x network boot
<https://www.qemu.org/docs/master/system/s390x/bootdevices.html>`_
* `IBM znetconf
<https://www.ibm.com/docs/en/linux-on-systems?topic=linuxonz-znetconf>`_
* `NetworkManager initrd generator
<https://networkmanager.dev/docs/api/latest/nm-initrd-generator.html>`_
* `systemd system extensions
@@ -45,7 +45,7 @@ Key Attributes
+--------------------------+----------------------+-----------------------------------+
| aarch64 | >=el8 | grub2 |
+--------------------------+----------------------+-----------------------------------+
| riscv64 | >=el10 | grub2,grub2-http,grub2-tftp |
| riscv64 | >=el10, >=ubuntu24.04| grub2,grub2-http,grub2-tftp |
+--------------------------+----------------------+-----------------------------------+
* postscripts:
@@ -6,6 +6,8 @@ The name of the packages that will be installed on the node are stored in the pa
* The package list file contains the names of the packages that comes from the os distro. They are stored in .pkglist file.
* The other package list file contains the names of the packages that do NOT come from the os distro. They are stored in .otherpkgs.pkglist file.
On Ubuntu releases that install with Subiquity, the packages in the .pkglist file are installed during the autoinstall from the configured apt mirror and the pkgdir mirrors, without recommended packages as ``ospkgs`` installs them, and ``ospkgs`` applies the whole list again after the first boot. A version pin, a target release or an architecture qualifier in the list, a list that carries a ``#ENV:`` setting, and the list of an osimage with ``environvar`` are installed by ``ospkgs`` only. The apt sources the installer uses for the pkgdir mirrors and the otherpkgs repository do not remain on the node: ``ospkgs`` and ``otherpkgs`` write their own after the first boot, as before. ``ospkgs`` writes http mirrors only, so an https mirror or a local directory in pkgdir serves the autoinstall and is not an apt source after the first boot.
The path to the package lists will be read from the osimage definition. Which osimage a node is using is specified by the provmethod attribute. To display this value for a node: ::
lsdef node1 -i provmethod
@@ -1,9 +1,10 @@
RISC-V 64-bit (riscv64)
=======================
xCAT manages RISC-V 64-bit (``riscv64``) compute nodes running EL10. Rocky
Linux 10 is the reference distribution; the RHEL 10 RISC-V developer preview
uses the same media layout. The general cluster management documentation under
xCAT manages RISC-V 64-bit (``riscv64``) compute nodes running EL10 or Ubuntu.
Rocky Linux 10 is the reference EL distribution and the RHEL 10 RISC-V developer
preview uses the same media layout; on the Ubuntu side, 24.04 and 26.04 are
supported from the live-server media. The general cluster management documentation under
:doc:`/guides/admin-guides/manage_clusters/index` applies; this page
only covers what is specific to the architecture.
@@ -21,16 +22,31 @@ What riscv64 nodes need
``grub2-http`` is recommended for installers, whose initrd is large.
* ``nodetype.arch`` and ``osimage.osarch`` are ``riscv64``. No alias is
needed: ``uname -m``, rpm and dpkg all use the same token.
* ``/tftpboot/boot/grub2/grub2.riscv64``: the EL grub2 UEFI image for riscv64
(the ``EFI/BOOT/grubriscv64.efi`` of the EL10 riscv64 BaseOS tree).
``copycds`` publishes it from the installation media when the management node
does not have it yet, the ``grub2-xcat`` package installs the same image, and
:doc:`/guides/install-guides/yum/grub2` describes copying it by hand. An image
that is already there is never replaced.
* ``/tftpboot/boot/grub2/grub2.riscv64``: the grub2 UEFI image the firmware
loads. On EL media it is the ``EFI/BOOT/grubriscv64.efi`` of the riscv64
BaseOS tree, which the ``grub2-xcat`` package also installs and
:doc:`/guides/install-guides/yum/grub2` describes copying by hand; ``copycds``
publishes it when the management node does not have it yet and keeps an image
that is already there.
Ubuntu media are different: the loader they carry boots only from the media,
because it holds a built-in configuration that searches for the live
filesystem and never reads the configuration ``nodeset`` writes. ``copycds``
therefore builds a netboot image from the ``grub-efi-riscv64-bin`` package on
the media, with the network modules and the ``/boot/grub2`` prefix compiled
in, and installs it under that name. An image already there is kept when it
carries that prefix and those modules, which is what the loader this path
builds looks like; anything else is replaced, because the image the media
carry cannot reach the configuration. When the media cannot produce a
replacement, whatever is there is left untouched and ``copycds`` says so:
check that the nodes still boot, since the file was not built for this path.
Building the loader needs ``grub-mkimage``, which ``grub-common`` provides and
``xcat-server`` requires.
* The riscv64 Genesis image (``xCAT-genesis-openembedded-riscv64``) for
discovery, BMC setup and flashing. Its kernel is loaded by grub2 through the
EFI stub. ``go-xcat`` installs the package; on a management node built another
way, install it explicitly (``dnf install xCAT-genesis-openembedded-riscv64``),
way, install it explicitly (``dnf install xCAT-genesis-openembedded-riscv64``,
or ``apt install xcat-genesis-openembedded-riscv64`` on Ubuntu),
the same way the images of other architectures are installed for a mixed
cluster. ``xcatconfig`` runs ``mknb riscv64`` for every installed image.
The management node itself is x86_64 (the validated combination, see
@@ -65,6 +81,9 @@ for the other architectures.
Stateful (diskful) installation
--------------------------------
EL10
~~~~
Import the Rocky Linux 10 riscv64 DVD with ``copycds``; it creates the
``rocky10.x-riscv64-install-compute`` osimage. The installer kernel and initrd
come from ``images/pxeboot`` on the media, like x86_64 and aarch64.
@@ -84,6 +103,28 @@ package lists add ``grub2-efi-riscv64`` and ``efibootmgr``, and the
these files. After ``nodeset <node> boot`` the firmware boots the installed
system because the per-node ``grub2-<node>`` loader link is removed.
Ubuntu
~~~~~~
Import the Ubuntu 24.04 or 26.04 riscv64 live-server ISO with ``copycds``; it
creates the ``ubuntu<version>-riscv64-install-compute`` osimage. The installer
kernel and initrd come from ``casper/vmlinux`` and ``casper/initrd``, where the
riscv64 media keep them.
The installer needs no riscv64 accommodation of the kind EL10 requires: Subiquity
installs ``grub-efi-riscv64`` itself, writes both ``\EFI\ubuntu\grubriscv64.efi``
and the removable-media fallback ``\EFI\BOOT\BOOTRISCV64.EFI``, and registers the
UEFI boot entry. The shared ``compute.subiquity.tmpl`` is used unchanged.
The autoinstall configuration is fetched from the management node over HTTP with
``ds=nocloud-net``. That argument holds a semicolon, which grub2 reads as a
command separator, so the boot loader configuration quotes it; a node whose
kernel command line ends before the seed URL is a sign of an unquoted separator.
Packages the media do not carry are taken from ``ports.ubuntu.com``, which is
where every architecture other than amd64 and i386 is published. Set
``site.ubuntu_apt_mirror`` to point at a local mirror instead.
Crash dumps
~~~~~~~~~~~
@@ -116,6 +157,15 @@ management node needs the riscv64 user-mode emulator registered with
systemd-binfmt, as described in
:doc:`/advanced/mixed_cluster/building_stateless_images`.
On Ubuntu, ``genimage`` builds the image with ``debootstrap`` from the
``compute.ubuntu24.04.riscv64`` and ``compute.ubuntu26.04.riscv64`` package
lists. It bootstraps from ``ports.ubuntu.com``, because ``archive.ubuntu.com``
publishes amd64 and i386 only; ``site.ubuntu_apt_mirror`` overrides that for a
local mirror serving every architecture. A management node of another
architecture needs the same ``qemu-user-static`` binfmt registration as EL, and
a release older than the one being built needs the target's ``debootstrap``
script, which is a symlink to ``gutsy`` for every modern Ubuntu.
Management node on riscv64
--------------------------
@@ -142,6 +192,15 @@ that disables weak dependencies (``install_weak_deps=False``) has to install
``perl-DB_File`` explicitly to keep the Confluent client working.
xCAT does not install anything from CPAN; every dependency is an rpm.
On Ubuntu the ``xcat`` and ``xcatsn`` packages are built for riscv64 and the
apt repository indexes the architecture, so ``apt install xcat`` brings up a
riscv64 management node. Everything else xCAT needs comes from the Ubuntu
riscv64 archive, except the xcat-dep packages that carry a binary:
``goconserver``, ``ipmitool-xcat`` and ``conserver-xcat`` must come from a
riscv64 xcat-dep apt repository. The remaining xcat-dep packages, including
``grub2-xcat`` and the x86-only boot loaders, are ``Architecture: all`` and
install anywhere.
Limitations
-----------
@@ -151,7 +210,9 @@ Limitations
HTTP boot firmware yet, and a node that ``nodeset`` has configured is offered
its per-node boot loader over TFTP, as on the other architectures, so keep PXE
boot enabled in the firmware.
* Ubuntu riscv64 is not supported yet.
* Ubuntu 26.04 riscv64 requires the RVA23 profile. A machine that implements
only the older profile stops with an illegal instruction early in userspace;
24.04 runs on the older profile.
* The serial console defaults to ``ttyS<site.defserialport>``; boards whose
firmware exposes the console on another device need
``linuximage.addkcmdline`` or the serial settings adjusted.
@@ -62,7 +62,7 @@ noderes Attributes:
ppc64le NonVirtualize ALL petitboot
ppc64le PowerKVM Guest ALL grub2,grub2-http,grub2-tftp
aarch64 >=el8 grub2
riscv64 >=el10 grub2,grub2-http,grub2-tftp
riscv64 >=el10, >=ubuntu24.04 grub2,grub2-http,grub2-tftp
@@ -547,7 +547,7 @@ group Attributes:
ppc64le NonVirtualize ALL petitboot
ppc64le PowerKVM Guest ALL grub2,grub2-http,grub2-tftp
aarch64 >=el8 grub2
riscv64 >=el10 grub2,grub2-http,grub2-tftp
riscv64 >=el10, >=ubuntu24.04 grub2,grub2-http,grub2-tftp
@@ -547,7 +547,7 @@ node Attributes:
ppc64le NonVirtualize ALL petitboot
ppc64le PowerKVM Guest ALL grub2,grub2-http,grub2-tftp
aarch64 >=el8 grub2
riscv64 >=el10 grub2,grub2-http,grub2-tftp
riscv64 >=el10, >=ubuntu24.04 grub2,grub2-http,grub2-tftp
@@ -42,11 +42,17 @@ An export is identified by \ ``xcat-genesis.manifest``\ , which records its form
When multiple IPv4 addresses are configured for the same network, \ **mknb**\ uses a locally assigned \ ``site.master``\ for the xcatd endpoint, or the first address reported by the operating system when \ ``site.master``\ is not local. POWER discovery configurations also use this address for their kernel and initrd URLs.
OpenEmbedded images use the exact architecture names \ ``x86``\ , \ ``x86_64``\ , \ ``ppc64``\ , \ ``ppc64le``\ , \ ``armv7hf``\ , \ ``aarch64``\ , and \ ``riscv64``\ . If an OpenEmbedded \ ``ppc64le``\ image is not installed, \ **mknb**\ keeps the old behavior and uses the legacy \ ``ppc64``\ image.
OpenEmbedded images use the exact architecture names \ ``x86``\ , \ ``x86_64``\ , \ ``ppc64``\ , \ ``ppc64le``\ , \ ``armv7hf``\ , \ ``aarch64``\ , \ ``riscv64``\ , and \ ``s390x``\ . If an OpenEmbedded \ ``ppc64le``\ image is not installed, \ **mknb**\ keeps the old behavior and uses the legacy \ ``ppc64``\ image.
Canonical \ ``ppc64``\ images are big-endian. xCAT marks them so \ ``ppc64le``\ nodes do not use them as a legacy little-endian fallback. \ **mknb**\ also refuses to replace a marked \ ``ppc64``\ image with that fallback.
riscv64 nodes boot through UEFI firmware and grub2. For riscv64, \ **mknb**\ publishes the Genesis kernel and initramfs and writes one grub2 configuration per network under ``/tftpboot/boot/grub2``, named ``grub.cfg-`` followed by the network hex prefix, so that ``grub2.riscv64`` loaded by the firmware can start node discovery. The per-node files written by \ **nodeset**\ take priority over these network files. Networks served by a ``:noboot`` interface in ``site.dhcpinterfaces`` get no discovery configuration.
riscv64 nodes boot through UEFI firmware and grub2. For riscv64, \ **mknb**\ publishes the Genesis kernel and initramfs and writes one grub2 configuration per network under \ ``/tftpboot/boot/grub2``\ , named \ ``grub.cfg-``\ followed by the network hex prefix, so that \ ``grub2.riscv64``\ loaded by the firmware can start node discovery. The per-node files written by \ **nodeset**\ take priority over these network files. Networks served by a \ ``:noboot``\ interface in \ ``site.dhcpinterfaces``\ get no discovery configuration.
QEMU s390-ccw guests receive a network-specific \ ``pxelinux.cfg``\ -style configuration through DHCP option 209. The firmware resolves this name below \ ``pxelinux.cfg``\ . Leave \ ``noderes.netboot``\ unset while a QEMU s390x node uses Genesis discovery. s390x Genesis does not use the shared IBM Z machine serial as a node identifier. On physical IBM Z, Genesis accepts \ ``rd.znet=qeth,read,write,data[,option=value]``\ and activates qeth before NetworkManager starts. Without \ ``rd.znet``\ , it activates unconfigured qeth devices in layer 2 mode. Devices reported as configured by \ ``znetconf``\ are not regrouped. Genesis does not import DPM auto-configuration data. Layer 3 and IP-mode VSWITCH configurations require \ ``layer2=0``\ . QEMU validation covers network IPL through Genesis startup; the discovery exchange with xcatd remains unvalidated for s390x. Legacy xCAT z/VM operating-system provisioning remains unchanged. Physical LPAR and z/VM Genesis networking remain unvalidated because QEMU does not emulate qeth.
s390x hypervisor guests report themselves as virtual nodes, so sequential and switch discovery ignore them. Use profile discovery or assign a pending discovery record with \ **nodediscoverdef**\ .
Run \ **makedhcp -n**\ after installing the s390x image and before booting s390x nodes. This regenerates the DHCP configuration with the required option.
*******
+1 -1
View File
@@ -16,7 +16,7 @@ Differentiators
* Support Multiple Hardware
IBM Power, IBM Power LE, x86_64, aarch64 (alpha support), riscv64 (EL10, UEFI + grub2)
IBM Power, IBM Power LE, x86_64, aarch64 (alpha support), riscv64 (EL10 and Ubuntu, UEFI + grub2)
* Support Multiple Virtualization Infrastructures
+2 -2
View File
@@ -11,7 +11,7 @@ Operating System & Hardware Support Matrix
|SLES | yes | yes | yes | yes | yes | yes | yes | no | no |
| | | | | | | | | | |
+-------+-------+-------+-----+-------+--------+--------+--------+----------+----------+
|Ubuntu | no | yes | no | yes | yes | yes | yes | no | no |
|Ubuntu | no | yes | no | yes | yes | yes | yes | no | yes |
| | | | | | | | | | |
+-------+-------+-------+-----+-------+--------+--------+--------+----------+----------+
|CentOS | no | no | no | no | yes | yes | yes | no | no |
@@ -21,4 +21,4 @@ Operating System & Hardware Support Matrix
| | | | | | | | | | |
+-------+-------+-------+-----+-------+--------+--------+--------+----------+----------+
riscv64 support covers EL10 compute nodes (Rocky Linux 10 and the RHEL 10 RISC-V developer preview) that boot through UEFI firmware and grub2. See :doc:`/guides/admin-guides/manage_clusters/riscv64/index` for details.
riscv64 support covers EL10 (Rocky Linux 10 and the RHEL 10 RISC-V developer preview) and Ubuntu 24.04 and 26.04, on nodes that boot through UEFI firmware and grub2. Both stateful and stateless nodes are supported, and the management node itself can run on riscv64. See :doc:`/guides/admin-guides/manage_clusters/riscv64/index` for details.
+47 -2
View File
@@ -308,8 +308,11 @@ sub preserve_source_tree{
return 1;
}
@output = runcmd("ls $unitsrc/xCAT-test/unit/*.t | wc -l");
print "[preserve_source_tree] preserved $srcdir in $unitsrc ($output[0] unit tests)\n";
@output = runcmd("find $unitsrc/xCAT-test/unit -name '*.t' | wc -l");
my $perl_count = $output[0];
@output = runcmd("find $unitsrc/xCAT-test/bats -name '*.bats' 2>/dev/null | wc -l");
my $bats_count = $output[0];
print "[preserve_source_tree] preserved $srcdir in $unitsrc ($perl_count Perl unit tests, $bats_count BATS tests)\n";
return 0;
}
@@ -463,6 +466,39 @@ sub run_unit_tests{
return 0;
}
#--------------------------------------------------------
# Fuction name: run_bats_tests
# Description: Run shell-script unit tests under xCAT-test/bats.
# Runs against the pre-build copy of the source tree taken by
# preserve_source_tree(), like the Perl unit tests.
# Attributes:
# Return code: 0 all tests passed, 1 otherwise
#--------------------------------------------------------
sub run_bats_tests{
my $testdir = "$unitsrc/xCAT-test/bats";
my @output = runcmd("find $testdir -name '*.bats' -print -quit 2>/dev/null");
if (!@output) {
print "[run_bats_tests] no BATS tests found under $testdir\n";
return 0;
}
my $cmd = "cd $unitsrc && bats -r xCAT-test/bats";
print "[run_bats_tests] running $cmd\n";
@output = runcmd("$cmd");
print Dumper \@output;
if($::RUNCMD_RC){
print RED "[run_bats_tests] $cmd ....[Failed]\n";
$check_result_str .= "> **BATS TESTS Failed** : Please click ``Details`` label in ``Merge pull request`` box for detailed information\n";
print $check_result_str;
return 1;
}
print "[run_bats_tests] $cmd ....[Pass]\n";
$check_result_str .= "> **BATS TESTS Successful**\n";
print $check_result_str;
return 0;
}
#--------------------------------------------------------
# Fuction name: check_syntax
# Description:
@@ -686,6 +722,15 @@ if($rst){
}
mark_time("run_unit_tests");
#Run shell-script unit tests.
print GREEN "\n------Running xCAT-test BATS tests ------\n";
$rst = run_bats_tests();
if($rst){
print RED "Run of xCAT-test BATS tests failed\n";
exit $rst;
}
mark_time("run_bats_tests");
#Check the syntax of changing code
print GREEN "\n------ Checking the syntax of changed code------\n";
$rst = check_syntax();
+1 -1
View File
@@ -8,7 +8,7 @@ Homepage: https://xcat.org/
Package: perl-xcat
Architecture: all
Depends: ${perl:Depends}, libhtml-form-perl
Depends: ${perl:Depends}, libhtml-form-perl, libxml-simple-perl, libxml-parser-perl, libio-socket-ssl-perl, libdbi-perl, libjson-perl, libwww-perl, libxml-libxml-perl, libexpect-perl, libsnmp-perl, libsocket6-perl, libio-socket-inet6-perl
Description: xCAT perl libraries
Provides perl xCAT libraries for core functionality. Required for all xCAT installations.
Includes xCAT::Table, xCAT::NodeRange, among others.
+29
View File
@@ -103,6 +103,33 @@ sub kea_httpboot_network_classes {
return \@classes;
}
sub kea_s390x_network_classes {
my ( $class, %opts ) = @_;
if ( !$opts{net} || !defined $opts{prefix} ) {
return [];
}
my $network_id = "$opts{net}_$opts{prefix}";
my $safe_network = $network_id;
$safe_network =~ s{[^A-Za-z0-9_.-]}{_}gxms;
return [
{
name => "xcat-s390x-qemu-$safe_network",
test => 'option[93].hex == 0x001f',
additional_only => 1,
'option-data' => [
{
name => 'conf-file',
data => "s390x/$network_id",
'always-send' => 1,
},
],
},
];
}
sub isc_client_architecture_lines {
my ( $class, %opts ) = @_;
@@ -136,6 +163,8 @@ sub isc_client_architecture_lines {
" } else if option client-architecture = 00:1c { #riscv64 uefi http boot\n ",
" option vendor-class-identifier \"HTTPClient\";\n",
" filename \"http://$tftp$portsuffix/tftpboot/boot/grub2/grub2.riscv64\";\n",
" } else if option client-architecture = 00:1f { #QEMU s390x\n ",
" option conf-file = \"s390x/${net}_${maskbits}\";\n",
" } else if option client-architecture = 00:0e { #OPAL-v3\n ",
" option conf-file = \"http://$tftp$portsuffix/tftpboot/pxelinux.cfg/p/${net}_${maskbits}\";\n",
" } else if substring (option vendor-class-identifier,0,11) = \"onie_vendor\" { #for onie on cumulus switch\n",
+3 -3
View File
@@ -676,7 +676,7 @@ passed as argument rather than by table value',
ppc64le NonVirtualize ALL petitboot
ppc64le PowerKVM Guest ALL grub2,grub2-http,grub2-tftp
aarch64 >=el8 grub2
riscv64 >=el10 grub2,grub2-http,grub2-tftp
riscv64 >=el10, >=ubuntu24.04 grub2,grub2-http,grub2-tftp
',
tftpserver => 'The TFTP server for this node (as known by this node). If not set, it defaults to networks.tftpserver.',
@@ -751,7 +751,7 @@ passed as argument rather than by table value',
descriptions => {
node => 'The node name or group name.',
os => 'The operating system deployed on this node. Valid values: AIX, rhels*,rhelc*, rhas*,centos*, alma*, rocky*,SL*, fedora*, sles* (where * is the version #). As a special case, if this is set to "boottarget", then it will use the initrd/kernel/parameters specified in the row in the boottarget table in which boottarget.bprofile equals nodetype.profile.',
arch => 'The hardware architecture of this node. Valid values: x86_64, ppc64, x86, ia64, aarch64, riscv64.',
arch => 'The hardware architecture of this node. Valid values: x86_64, ppc64, x86, ia64, aarch64, riscv64, s390x.',
profile => 'The string to use to locate a kickstart or autoyast template to use for OS deployment of this node. If the provmethod attribute is set to an osimage name, that takes precedence, and profile need not be defined. Otherwise, the os, profile, and arch are used to search for the files in /install/custom first, and then in /opt/xcat/share/xcat.',
provmethod => 'The provisioning method for node deployment. The valid values are install, netboot, statelite or an os image name from the osimage table. If an image name is specified, the osimage definition stored in the osimage table and the linuximage table (for Linux) or nimimage table (for AIX) are used to locate the files for templates, pkglists, syncfiles, etc. On Linux, if install, netboot or statelite is specified, the os, profile, and arch are used to search for the files in /install/custom first, and then in /opt/xcat/share/xcat.',
supportedarchs => 'Comma delimited list of architectures this node can execute.',
@@ -795,7 +795,7 @@ passed as argument rather than by table value',
profile => 'The node usage category. For example compute, service.',
osname => 'Operating system name- AIX or Linux.',
osvers => 'The Linux operating system deployed on this node. Valid values: rhels*,rhelc*, rhas*,centos*,alma*, rocky*,SL*, fedora*, sles* (where * is the version #).',
osarch => 'The hardware architecture of this node. For netboot/statelite images, QEMU emulation for non-native architectures is used if qemu-user-static is installed and configured via systemd-binfmt. Valid values: x86_64, ppc64, x86, ia64, aarch64, riscv64.',
osarch => 'The hardware architecture of this node. For netboot/statelite images, QEMU emulation for non-native architectures is used if qemu-user-static is installed and configured via systemd-binfmt. Valid values: x86_64, ppc64, x86, ia64, aarch64, riscv64, s390x.',
synclists => 'The fully qualified name of a file containing a list of files to synchronize on the nodes. Can be a comma separated list of multiple synclist files. The synclist generated by PCM named /install/osimages/<imagename>/synclist.cfm is reserved for use only by PCM and should not be edited by the admin.',
postscripts => 'Comma separated list of scripts that should be run on this image after diskful installation or diskless boot. For installation of RedHat, CentOS, Fedora, the scripts will be run before the reboot. For installation of SLES, the scripts will be run after the reboot but before the init.d process. For diskless deployment, the scripts will be run at the init.d time, and xCAT will automatically add the list of scripts from the postbootscripts attribute to run after postscripts list. For installation of AIX, the scripts will run after the reboot and acts the same as the postbootscripts attribute. For AIX, use the postbootscripts attribute. See the site table runbootscripts attribute.',
postbootscripts => 'Comma separated list of scripts that should be run on this after diskful installation or diskless boot. On AIX these scripts are run during the processing of /etc/inittab. On Linux they are run at the init.d time. xCAT automatically adds the scripts in the xcatdefaults.postbootscripts attribute to run first in the list. See the site table runbootscripts attribute.',
+2
View File
@@ -4876,6 +4876,7 @@ sub splitkcmdline {
# without network drivers instead of stopping at debootstrap.
my %DEBIAN_ARCH = (
'x86_64' => 'amd64',
'x86' => 'i386',
);
sub debian_arch {
@@ -4898,6 +4899,7 @@ my @XCAT_ARCH_FROM_DEBIAN = (
[ qr/^ppc64el$/ => 'ppc64el' ],
[ qr/ppc|powerpc/ => 'ppc64' ],
[ qr/^amd64$/ => 'x86_64' ],
[ qr/^riscv64$/ => 'riscv64' ],
);
sub xcat_arch_from_debian {
+2 -2
View File
@@ -7,8 +7,8 @@ Standards-Version: 3.9.4
Package: xcat-client
Architecture: all
Depends: ${perl:Depends}, perl-xcat (>= 2.13-snap000000000000)
Recommends: libsort-versions-perl, nmap
Depends: ${perl:Depends}, perl-xcat (>= 2.13-snap000000000000), libcapture-tiny-perl, nmap
Recommends: libsort-versions-perl
Description: Core executables and data of the xCAT management project
xCAT-client provides the fundamental xCAT commands (chtab, chnode, rpower,
etc) helpful in administrating systems at scale, with particular attention
+7 -1
View File
@@ -24,12 +24,18 @@ An export is identified by C<xcat-genesis.manifest>, which records its format ve
When multiple IPv4 addresses are configured for the same network, B<mknb> uses a locally assigned C<site.master> for the xcatd endpoint, or the first address reported by the operating system when C<site.master> is not local. POWER discovery configurations also use this address for their kernel and initrd URLs.
OpenEmbedded images use the exact architecture names C<x86>, C<x86_64>, C<ppc64>, C<ppc64le>, C<armv7hf>, C<aarch64>, and C<riscv64>. If an OpenEmbedded C<ppc64le> image is not installed, B<mknb> keeps the old behavior and uses the legacy C<ppc64> image.
OpenEmbedded images use the exact architecture names C<x86>, C<x86_64>, C<ppc64>, C<ppc64le>, C<armv7hf>, C<aarch64>, C<riscv64>, and C<s390x>. If an OpenEmbedded C<ppc64le> image is not installed, B<mknb> keeps the old behavior and uses the legacy C<ppc64> image.
Canonical C<ppc64> images are big-endian. xCAT marks them so C<ppc64le> nodes do not use them as a legacy little-endian fallback. B<mknb> also refuses to replace a marked C<ppc64> image with that fallback.
riscv64 nodes boot through UEFI firmware and grub2. For riscv64, B<mknb> publishes the Genesis kernel and initramfs and writes one grub2 configuration per network under C</tftpboot/boot/grub2>, named C<grub.cfg-> followed by the network hex prefix, so that C<grub2.riscv64> loaded by the firmware can start node discovery. The per-node files written by B<nodeset> take priority over these network files. Networks served by a C<:noboot> interface in C<site.dhcpinterfaces> get no discovery configuration.
QEMU s390-ccw guests receive a network-specific C<pxelinux.cfg>-style configuration through DHCP option 209. The firmware resolves this name below C<pxelinux.cfg>. Leave C<noderes.netboot> unset while a QEMU s390x node uses Genesis discovery. s390x Genesis does not use the shared IBM Z machine serial as a node identifier. On physical IBM Z, Genesis accepts C<rd.znet=qeth,read,write,data[,option=value]> and activates qeth before NetworkManager starts. Without C<rd.znet>, it activates unconfigured qeth devices in layer 2 mode. Devices reported as configured by C<znetconf> are not regrouped. Genesis does not import DPM auto-configuration data. Layer 3 and IP-mode VSWITCH configurations require C<layer2=0>. QEMU validation covers network IPL through Genesis startup; the discovery exchange with xcatd remains unvalidated for s390x. Legacy xCAT z/VM operating-system provisioning remains unchanged. Physical LPAR and z/VM Genesis networking remain unvalidated because QEMU does not emulate qeth.
s390x hypervisor guests report themselves as virtual nodes, so sequential and switch discovery ignore them. Use profile discovery or assign a pending discovery record with B<nodediscoverdef>.
Run B<makedhcp -n> after installing the s390x image and before booting s390x nodes. This regenerates the DHCP configuration with the required option.
=head1 OPTIONS
=over 12
+3 -1
View File
@@ -16,7 +16,9 @@ xCAT-genesis-builder/oe/build x86_64
```
The build command accepts `x86`, `x86_64`, `ppc64`, `ppc64le`, `armv7hf`,
`aarch64`, and `riscv64`. Multiple architectures are built in the order given.
`aarch64`, `riscv64`, and `s390x`. Multiple architectures are built in the
order given.
Run `xCAT-genesis-builder/oe/build --list-architectures` to print this list.
Artifacts are written below `xCAT-genesis-builder/oe/.work/build/tmp/deploy/images`.
The `x86` artifact uses an i686 CPU baseline. The build carries the reviewed
Yocto release key in `oe/keys` and verifies its fingerprint locally.
@@ -9,14 +9,15 @@ depends() {
}
installkernel() {
local modules_dep modfile modname
local modules_dep modules_root modfile modname
if [[ -n "${kernel:-}" && -r "/lib/modules/$kernel/modules.dep" ]]; then
modules_dep="/lib/modules/$kernel/modules.dep"
elif [[ -n "${KERNELVERSION:-}" && -r "/lib/modules/$KERNELVERSION/modules.dep" ]]; then
modules_dep="/lib/modules/$KERNELVERSION/modules.dep"
modules_root="${DRACUT_MODULES_ROOT:-/lib/modules}"
if [[ -n "${kernel:-}" && -r "$modules_root/$kernel/modules.dep" ]]; then
modules_dep="$modules_root/$kernel/modules.dep"
elif [[ -n "${KERNELVERSION:-}" && -r "$modules_root/$KERNELVERSION/modules.dep" ]]; then
modules_dep="$modules_root/$KERNELVERSION/modules.dep"
else
modules_dep=$(ls -1 /lib/modules/*/modules.dep 2>/dev/null | head -n 1)
modules_dep=$(ls -1 "$modules_root"/*/modules.dep 2>/dev/null | head -n 1)
fi
[[ -r "$modules_dep" ]] || return 0
@@ -9,14 +9,15 @@ depends() {
}
installkernel() {
local modules_dep modfile modname
local modules_dep modules_root modfile modname
if [[ -n "${kernel:-}" && -r "/lib/modules/$kernel/modules.dep" ]]; then
modules_dep="/lib/modules/$kernel/modules.dep"
elif [[ -n "${KERNELVERSION:-}" && -r "/lib/modules/$KERNELVERSION/modules.dep" ]]; then
modules_dep="/lib/modules/$KERNELVERSION/modules.dep"
modules_root="${DRACUT_MODULES_ROOT:-/lib/modules}"
if [[ -n "${kernel:-}" && -r "$modules_root/$kernel/modules.dep" ]]; then
modules_dep="$modules_root/$kernel/modules.dep"
elif [[ -n "${KERNELVERSION:-}" && -r "$modules_root/$KERNELVERSION/modules.dep" ]]; then
modules_dep="$modules_root/$KERNELVERSION/modules.dep"
else
modules_dep=$(ls -1 /lib/modules/*/modules.dep 2>/dev/null | head -n 1)
modules_dep=$(ls -1 "$modules_root"/*/modules.dep 2>/dev/null | head -n 1)
fi
[[ -r "$modules_dep" ]] || return 0
+17 -7
View File
@@ -6,6 +6,12 @@ oe_dir=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd)
: "${XCAT_GENESIS_WORK_DIR:=$oe_dir/.work}"
: "${KAS:=kas}"
supported_architectures='aarch64 armv7hf riscv64 s390x x86 x86_64 ppc64 ppc64le'
if [ "$#" -eq 1 ] && [ "$1" = '--list-architectures' ]; then
printf '%s\n' $supported_architectures
exit 0
fi
mkdir -p "$XCAT_GENESIS_WORK_DIR"
export KAS_WORK_DIR="$XCAT_GENESIS_WORK_DIR"
@@ -16,12 +22,16 @@ if [ "$#" -eq 0 ]; then
fi
for architecture do
case "$architecture" in
aarch64|armv7hf|riscv64|x86|x86_64|ppc64|ppc64le) ;;
*)
printf 'Unsupported Genesis architecture: %s\n' "$architecture" >&2
exit 2
;;
esac
architecture_supported=0
for candidate in $supported_architectures; do
if [ "$architecture" = "$candidate" ]; then
architecture_supported=1
break
fi
done
if [ "$architecture_supported" -ne 1 ]; then
printf 'Unsupported Genesis architecture: %s\n' "$architecture" >&2
exit 2
fi
"$KAS" build "$oe_dir/kas/$architecture.yml"
done
+1
View File
@@ -23,6 +23,7 @@ case "$architecture" in
armv7hf) kernel_name=zImage ;;
aarch64) kernel_name=Image ;;
riscv64) kernel_name=Image ;;
s390x) kernel_name=bzImage ;;
*) fail "unsupported architecture: $architecture" ;;
esac
+1 -1
View File
@@ -21,7 +21,7 @@ output_dir=${6%/}
script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
case "$architecture" in
x86|x86_64|ppc64|ppc64le|armv7hf|aarch64|riscv64) ;;
x86|x86_64|ppc64|ppc64le|armv7hf|aarch64|riscv64|s390x) ;;
*) fail "unsupported architecture: $architecture" ;;
esac
[[ $extension =~ ^[a-z0-9][a-z0-9._-]{0,63}$ ]] \
+6
View File
@@ -0,0 +1,6 @@
header:
version: 19
includes:
- xCAT-genesis-builder/oe/kas/common.yml
machine: xcat-genesis-s390x
@@ -34,7 +34,7 @@ python __anonymous() {
architecture = d.getVar("XCAT_GENESIS_EXTENSION_ARCHITECTURE")
architectures = {
"x86", "x86_64", "ppc64", "ppc64le", "armv7hf", "aarch64", "riscv64"
"x86", "x86_64", "ppc64", "ppc64le", "armv7hf", "aarch64", "riscv64", "s390x"
}
if architecture not in architectures:
bb.fatal("Invalid Genesis extension architecture: %s" % architecture)
@@ -0,0 +1,29 @@
DEFAULTTUNE ?= "s390x"
TUNEVALID[m64] = "64-bit z/Architecture ABI"
TUNEVALID[bigendian] = "Big-endian byte order"
TUNEVALID[z10] = "IBM System z10 instruction set"
TUNE_ARCH = "s390x"
TUNE_PKGARCH = "${TUNE_PKGARCH:tune-${DEFAULTTUNE}}"
TUNE_CCARGS .= "${@bb.utils.contains('TUNE_FEATURES', 'm64', ' -m64', '', d)}"
TUNE_CCARGS .= "${@bb.utils.contains('TUNE_FEATURES', 'z10', ' -march=z10 -mtune=z10', '', d)}"
AVAILTUNES += "s390x"
TUNE_FEATURES:tune-s390x = "m64 bigendian z10"
BASE_LIB:tune-s390x = "lib64"
BASELIB:libc-glibc:s390x = "lib64"
TUNE_PKGARCH:tune-s390x = "s390x"
PACKAGE_EXTRA_ARCHS:tune-s390x = "s390x"
def xcat_genesis_s390x_siteinfo(archinfo, osinfo, targetinfo, d):
archinfo["s390x"] = "endian-big bit-64"
return archinfo, osinfo, targetinfo
SITEINFO_EXTRA_DATAFUNCS += "xcat_genesis_s390x_siteinfo"
def xcat_genesis_s390x_packageqa_machdata(machdata, d):
machdata["linux"]["s390x"] = (22, 0, 0, False, 64)
return machdata
PACKAGEQA_EXTRA_MACHDEFFUNCS += "xcat_genesis_s390x_packageqa_machdata"
@@ -0,0 +1,35 @@
#@TYPE: Machine
#@NAME: xCAT Genesis s390x
#@DESCRIPTION: QEMU s390x Genesis target
DEFAULTTUNE = "s390x"
XCAT_GENESIS_ARCHITECTURE = "s390x"
require conf/machine/include/s390x/tune-s390x.inc
require conf/machine/include/qemu.inc
ARCH:s390x = "s390"
QEMU_TARGETS:append = " s390x"
KERNEL_IMAGETYPE = "bzImage"
SERIAL_CONSOLES = "115200;ttysclp0"
XCAT_GENESIS_CONSOLE_TTY = "ttysclp0"
MACHINE_FEATURES_DEFAULTS = ""
MACHINE_FEATURES = ""
MACHINE_ESSENTIAL_EXTRA_RDEPENDS = ""
MACHINE_EXTRA_RRECOMMENDS = ""
IMAGE_FSTYPES = "cpio.gz ext4"
QB_SYSTEM_NAME = "qemu-system-s390x"
QB_MACHINE = "-machine s390-ccw-virtio"
# TCG lacks optional z10 facilities, so disable them while retaining the z10 instruction floor.
QB_CPU = "-cpu z10EC-base,msa=off,msa1=off,dfp=off,dfphp=off,parseh=off,hfpue=off,hfpm=off,pfpo=off,dateh2=off,csske=off,asnlxr=off,tods=off"
QB_SMP = "-smp 2"
QB_MEM = "-m 4096"
QB_DEFAULT_FSTYPE = "ext4"
QB_KERNEL_CMDLINE_APPEND = "console=ttysclp0"
QB_NETWORK_DEVICE = "-device virtio-net-ccw,netdev=net0,mac=@MAC@"
QB_ROOTFS_OPT = "-drive id=disk0,file=@ROOTFS@,if=none,format=raw -device virtio-blk-ccw,drive=disk0"
QB_RNG = "-object rng-random,filename=/dev/urandom,id=rng0 -device virtio-rng-ccw,rng=rng0"
QB_OPT_APPEND = ""
@@ -0,0 +1,7 @@
# SPDX-License-Identifier: EPL-1.0
my %targets = (
"linux-s390x" => {
inherit_from => [ "linux64-s390x" ],
},
);
@@ -0,0 +1,7 @@
FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
SRC_URI:append:s390x = " file://50-xcat-s390x.conf"
do_configure:prepend:s390x() {
install -m 0644 ${UNPACKDIR}/50-xcat-s390x.conf ${S}/Configurations/
}
@@ -0,0 +1,167 @@
#!/bin/bash
set -u
LC_ALL=C
export LC_ALL
cmdline_file=${XCAT_CMDLINE_FILE:-/proc/cmdline}
cio_settle_file=${XCAT_CIO_SETTLE_FILE:-/proc/cio_settle}
status_command=${XCAT_STATUS_COMMAND:-/usr/libexec/xcat/genesis-status}
znetconf_command=${XCAT_ZNETCONF_COMMAND:-/sbin/znetconf}
log() {
logger -t xcat-genesis-qeth -- "$*" || true
}
publish_status() {
[ -x "$status_command" ] || return 0
"$status_command" network "$@" || true
}
normalize_channel() {
local channel
channel=$(printf '%s' "$1" | tr 'A-F' 'a-f')
if [[ $channel =~ ^[[:xdigit:]]{4}$ ]]; then
printf '0.0.%s\n' "$channel"
elif [[ $channel =~ ^[[:xdigit:]]\.[[:xdigit:]]\.[[:xdigit:]]{4}$ ]]; then
printf '%s\n' "$channel"
else
return 1
fi
}
configured_channels() {
local channels=$1 configured normalized rest
while read -r configured rest; do
normalized=$(printf '%s' "$configured" | tr 'A-F' 'a-f')
[ "$normalized" = "$channels" ] && return 0
done <<<"$configured_output"
return 1
}
processed_channels() {
local channels=$1 processed
for processed in "${ready_channels[@]}" "${failed_channels[@]}"; do
[ "$processed" = "$channels" ] && return 0
done
return 1
}
activate_channels() {
local channels=$1
shift
local -a command=("$znetconf_command" -a "$channels" -d qeth)
local option output result
local have_layer2=false
if processed_channels "$channels"; then
log "qeth channels already processed: $channels"
return 0
fi
for option in "$@"; do
if [[ ! $option =~ ^[A-Za-z_][A-Za-z0-9_./-]*=[A-Za-z0-9_./:+-]+$ ]]; then
log "Invalid qeth option for $channels: $option"
failed_channels+=("$channels")
return 1
fi
[[ $option = layer2=* ]] && have_layer2=true
command+=(-o "$option")
done
$have_layer2 || command+=(-o layer2=1)
if configured_channels "$channels"; then
log "qeth channels already configured: $channels"
ready_channels+=("$channels")
return 0
fi
publish_status CONFIGURING_NETWORK "Activating qeth channels $channels"
output=$("${command[@]}" 2>&1)
result=$?
if [ "$result" -ne 0 ]; then
log "qeth activation failed for $channels: $output"
failed_channels+=("$channels")
return 1
fi
log "qeth channels ready: $channels"
ready_channels+=("$channels")
return 0
}
if [ -w "$cio_settle_file" ]; then
printf '1\n' >"$cio_settle_file" || log 'Unable to wait for IBM Z channel devices'
fi
configured_output=$("$znetconf_command" -c 2>/dev/null || true)
declare -a cmdline_arguments=() explicit_specs=() ready_channels=() failed_channels=()
rd_znet_present=false
read -r -a cmdline_arguments <"$cmdline_file"
for argument in "${cmdline_arguments[@]}"; do
case "$argument" in
rd.znet=*)
rd_znet_present=true
[[ $argument = rd.znet=qeth,* ]] && explicit_specs+=("${argument#rd.znet=}")
;;
esac
done
if [ "${#explicit_specs[@]}" -gt 0 ]; then
for specification in "${explicit_specs[@]}"; do
IFS=, read -r -a fields <<<"$specification"
if [ "${#fields[@]}" -lt 4 ]; then
log "Invalid rd.znet value: $specification"
failed_channels+=("$specification")
continue
fi
first=$(normalize_channel "${fields[1]}") || first=
second=$(normalize_channel "${fields[2]}") || second=
third=$(normalize_channel "${fields[3]}") || third=
if [ -z "$first" ] || [ -z "$second" ] || [ -z "$third" ]; then
log "Invalid qeth channel in rd.znet: $specification"
failed_channels+=("$specification")
continue
fi
channels="$first,$second,$third"
activate_channels "$channels" "${fields[@]:4}" || true
done
elif $rd_znet_present; then
exit 0
else
unconfigured_output=$("$znetconf_command" -u 2>/dev/null)
unconfigured_result=$?
if [ "$unconfigured_result" -eq 31 ]; then
exit 0
elif [ "$unconfigured_result" -ne 0 ]; then
log "Unable to inspect qeth devices"
publish_status DEGRADED 'Unable to inspect qeth devices' \
'CODE=QETH_DISCOVERY_FAILED' \
'RECOVERY=Check the IBM Z channel devices and qeth driver'
exit "$unconfigured_result"
fi
while IFS= read -r line; do
if [[ $line =~ ^([[:xdigit:].,]+)[[:space:]].*[[:space:]]qeth[[:space:]]*$ ]]; then
channels=$(printf '%s' "${BASH_REMATCH[1]}" | tr 'A-F' 'a-f')
activate_channels "$channels" || true
fi
done <<<"$unconfigured_output"
fi
if [ "${#failed_channels[@]}" -gt 0 ]; then
publish_status DEGRADED 'One or more qeth devices could not be activated' \
'CODE=QETH_ACTIVATION_FAILED' \
'RECOVERY=Check rd.znet and the IBM Z channel configuration'
exit 1
fi
if [ "${#ready_channels[@]}" -gt 0 ]; then
publish_status CONFIGURING_NETWORK 'qeth devices are ready'
fi
@@ -0,0 +1,15 @@
[Unit]
Description=Activate IBM Z qeth network devices
ConditionArchitecture=s390x
ConditionKernelCommandLine=xcatd
After=systemd-udev-trigger.service
Before=NetworkManager.service xcat-genesis-network-state.service
[Service]
Type=oneshot
ExecStart=/usr/libexec/xcat/genesis-qeth
TimeoutStartSec=120
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
@@ -0,0 +1,31 @@
SUMMARY = "xCAT Genesis qeth activation"
LICENSE = "EPL-1.0"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/EPL-1.0;md5=57f8d5e2b3e98ac6e088986c12bf94e6"
SRC_URI = "file://genesis-qeth \
file://xcat-genesis-qeth.service \
"
S = "${UNPACKDIR}"
COMPATIBLE_HOST = "s390x.*-linux"
inherit systemd
RDEPENDS:${PN} = "bash s390-tools-znetconf util-linux-logger xcat-genesis-init"
SYSTEMD_SERVICE:${PN} = "xcat-genesis-qeth.service"
SYSTEMD_AUTO_ENABLE = "enable"
do_install() {
install -d ${D}${libexecdir}/xcat
install -m 0755 ${UNPACKDIR}/genesis-qeth \
${D}${libexecdir}/xcat/genesis-qeth
install -d ${D}${systemd_system_unitdir}
install -m 0644 ${UNPACKDIR}/xcat-genesis-qeth.service \
${D}${systemd_system_unitdir}/xcat-genesis-qeth.service
}
FILES:${PN} = "${libexecdir}/xcat/genesis-qeth \
${systemd_system_unitdir}/xcat-genesis-qeth.service \
"
@@ -48,3 +48,4 @@ RDEPENDS:${PN}:append:xcat-genesis-aarch64 = " dmidecode lshw"
RDEPENDS:${PN}:append:xcat-genesis-ppc64 = " dmidecode xcat-genesis-hardware-control-iprutils"
RDEPENDS:${PN}:append:xcat-genesis-ppc64le = " dmidecode xcat-genesis-hardware-control-iprutils"
RDEPENDS:${PN}:append:xcat-genesis-riscv64 = " lshw"
RDEPENDS:${PN}:append:xcat-genesis-s390x = " xcat-genesis-qeth"
@@ -107,6 +107,7 @@ void xcat_set_text(char *destination, size_t size, const char *format, ...);
char *xcat_read_allocated_line(const char *path);
bool xcat_read_line(const char *path, char *value, size_t size);
bool xcat_read_key(const char *path, const char *key, char *value, size_t size);
bool xcat_read_colon_key(const char *path, const char *key, char *value, size_t size);
bool xcat_safe_name(const char *value);
bool xcat_cmdline_value(const char *cmdline, const char *key, char *value, size_t size);
unsigned long long xcat_read_uptime(void);
@@ -201,6 +201,8 @@ static void set_boot_loader(const char *cmdline, char *loader, size_t size) {
xcat_set_text(loader, size, "PXELINUX");
else if (strcmp(value, "elilo") == 0)
xcat_set_text(loader, size, "ELILO");
else if (strcmp(value, "s390-ccw") == 0)
xcat_set_text(loader, size, "QEMU TFTP loader");
else
xcat_set_text(loader, size, "unrecognized");
} else if (xcat_cmdline_value(cmdline, "BOOTIF", value, sizeof(value))) {
@@ -216,6 +218,21 @@ static bool useful_identity(const char *value) {
strcmp(value, "unknown") != 0;
}
static void set_s390_identity(const char *proc_root, struct console_state *state) {
char path[VALUE_SIZE * 2];
char control_program[VALUE_SIZE] = "";
bool guest;
snprintf(path, sizeof(path), "%s/sysinfo", proc_root);
if (!useful_identity(state->uuid)) {
guest = xcat_read_colon_key(path, "VM00 Control Program", control_program,
sizeof(control_program));
xcat_read_colon_key(path, "VM00 UUID", state->uuid, sizeof(state->uuid));
if (!useful_identity(state->uuid) && !guest)
xcat_read_colon_key(path, "LPAR UUID", state->uuid, sizeof(state->uuid));
}
}
static void split_action(const char *destiny, char *action, size_t action_size, char *target,
size_t target_size) {
const char *separator = strchr(destiny, '=');
@@ -293,6 +310,8 @@ void xcat_load_console_state(struct console_state *state) {
char release_version[32] = "";
bool xcat_configured;
bool interface_selected;
bool s390_system;
bool s390_ccw_boot;
memset(state, 0, sizeof(*state));
xcat_configured = xcat_cmdline_value(cmdline_text, "xcatd", state->xcat_endpoint,
@@ -332,6 +351,12 @@ void xcat_load_console_state(struct console_state *state) {
xcat_set_text(state->architecture, sizeof(state->architecture), "unknown");
xcat_set_text(state->kernel, sizeof(state->kernel), "unknown");
}
s390_system = strcmp(state->architecture, "s390x") == 0;
s390_ccw_boot = s390_system &&
xcat_cmdline_value(cmdline_text, "xcat.bootloader", value,
sizeof(value)) &&
strcmp(value, "s390-ccw") == 0;
snprintf(path, sizeof(path), "%s/firmware/efi", sys_root);
if (strncmp(state->architecture, "ppc64", 5) == 0) {
@@ -345,6 +370,9 @@ void xcat_load_console_state(struct console_state *state) {
access(path, F_OK) == 0 ? "UEFI" : "Device Tree");
} else if (strncmp(state->architecture, "riscv", 5) == 0) {
xcat_set_text(state->firmware, sizeof(state->firmware), "OpenSBI");
} else if (s390_system) {
xcat_set_text(state->firmware, sizeof(state->firmware), "%s",
s390_ccw_boot ? "s390-ccw BIOS" : "not reported");
} else {
snprintf(path, sizeof(path), "%s/firmware/efi", sys_root);
xcat_set_text(state->firmware, sizeof(state->firmware), "%s",
@@ -365,7 +393,12 @@ void xcat_load_console_state(struct console_state *state) {
if (!useful_identity(state->serial))
xcat_set_text(state->serial, sizeof(state->serial), "not reported");
snprintf(path, sizeof(path), "%s/class/dmi/id/product_uuid", sys_root);
if (!xcat_read_line(path, state->uuid, sizeof(state->uuid)) || !useful_identity(state->uuid))
xcat_read_line(path, state->uuid, sizeof(state->uuid));
if (s390_system) {
xcat_set_text(state->serial, sizeof(state->serial), "not reported");
set_s390_identity(proc_root, state);
}
if (!useful_identity(state->uuid))
xcat_set_text(state->uuid, sizeof(state->uuid), "not reported");
interface_selected =
@@ -123,6 +123,42 @@ bool xcat_read_key(const char *path, const char *key, char *value, size_t size)
return found;
}
bool xcat_read_colon_key(const char *path, const char *key, char *value, size_t size) {
FILE *stream;
char *line = NULL;
size_t capacity = 0;
size_t key_length = strlen(key);
bool found = false;
if (key_length == 0 || size == 0)
return false;
stream = fopen(path, "r");
if (stream == NULL)
return false;
while (getline(&line, &capacity, stream) >= 0) {
char *start;
char *end;
size_t line_length = strlen(line);
if (line_length <= key_length || strncmp(line, key, key_length) != 0 ||
line[key_length] != ':')
continue;
start = line + key_length + 1;
while (isspace((unsigned char)*start))
start++;
end = start + strlen(start);
while (end > start && isspace((unsigned char)end[-1]))
end--;
*end = '\0';
xcat_copy_printable(value, size, start);
found = true;
break;
}
free(line);
fclose(stream);
return found;
}
bool xcat_safe_name(const char *value) {
const unsigned char *cursor = (const unsigned char *)value;
@@ -46,7 +46,7 @@ canonical_architecture() {
case "$machine" in
i?86) printf '%s\n' x86 ;;
armv7*) printf '%s\n' armv7hf ;;
x86_64|ppc64|ppc64le|aarch64|riscv64) printf '%s\n' "$machine" ;;
x86_64|ppc64|ppc64le|aarch64|riscv64|s390x) printf '%s\n' "$machine" ;;
*) fail "unsupported runtime architecture: $machine" ;;
esac
}
@@ -75,7 +75,7 @@ validate_manifest() {
.schema == 1 and
(.name | type == "string" and test("^[a-z0-9][a-z0-9._-]{0,63}$")) and
(.version | type == "string" and test("^[A-Za-z0-9][A-Za-z0-9._+-]{0,63}$")) and
(.architecture | IN("x86", "x86_64", "ppc64", "ppc64le", "armv7hf", "aarch64", "riscv64")) and
(.architecture | IN("x86", "x86_64", "ppc64", "ppc64le", "armv7hf", "aarch64", "riscv64", "s390x")) and
(.genesis_release | type == "string" and test("^[A-Za-z0-9._-]+$")) and
(.key_id | type == "string" and test("^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$")) and
(.license_class | IN("open", "redistributable", "restricted")) and
@@ -0,0 +1,26 @@
From: xCAT Genesis Builder <xcat@users.noreply.github.com>
Subject: [PATCH] configure: disable s390x CRC vector code by default
The Genesis s390x baseline is z10, which lacks the vector facility.
Upstream-Status: Inappropriate [xCAT Genesis targets z10]
Signed-off-by: xCAT Genesis Builder <xcat@users.noreply.github.com>
---
configure | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/configure b/configure
index 08b9e98..1ae8a98 100755
--- a/configure
+++ b/configure
@@ -95,7 +95,7 @@ memory=0
undefined=0
insecure=0
unknown=0
-enable_crcvx=1
+enable_crcvx=0
old_cc="$CC"
old_cflags="$CFLAGS"
OBJC='$(OBJZ) $(OBJG)'
--
2.51.0
@@ -0,0 +1,3 @@
FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
SRC_URI:append:s390x = " file://0001-configure-disable-s390x-crcvx-by-default.patch"
@@ -7,7 +7,4 @@ inherit xcat-genesis-extension
IMAGE_INSTALL = "xcat-genesis-extension-smoke-payload"
XCAT_GENESIS_EXTENSION_NAME = "xcat-smoke"
XCAT_GENESIS_EXTENSION_ARCHITECTURE = "x86_64"
XCAT_GENESIS_EXTENSION_CAPABILITIES = '["diagnostic.smoke"]'
COMPATIBLE_HOST = "x86_64.*-linux"
@@ -0,0 +1 @@
COMPATIBLE_HOST:s390x = "s390x.*-linux"
@@ -0,0 +1,26 @@
From: xCAT Genesis Builder <xcat@users.noreply.github.com>
Date: Thu, 3 Sep 2026 22:46:00 -0300
Subject: [PATCH] s390: use stable generator name
__FILE__ contains the build directory when the generator is compiled with an
absolute source path. Do not copy that path into the exported source package.
Upstream-Status: Pending
Signed-off-by: xCAT Genesis Builder <xcat@users.noreply.github.com>
---
arch/s390/tools/gen_opcode_table.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/s390/tools/gen_opcode_table.c b/arch/s390/tools/gen_opcode_table.c
index 0fbb061..8cd17d0 100644
--- a/arch/s390/tools/gen_opcode_table.c
+++ b/arch/s390/tools/gen_opcode_table.c
@@ -341,7 +341,7 @@ int main(int argc, char **argv)
printf("/*\n");
printf(" * DO NOT MODIFY.\n");
printf(" *\n");
- printf(" * This file was generated by %s\n", __FILE__);
+ printf(" * This file was generated by arch/s390/tools/gen_opcode_table.c\n");
printf(" */\n\n");
print_formats(desc);
print_long_insn(desc);
@@ -0,0 +1,28 @@
CONFIG_MARCH_Z10=y
CONFIG_TUNE_Z10=y
CONFIG_NET=y
CONFIG_INET=y
CONFIG_IPV6=y
CONFIG_NETDEVICES=y
CONFIG_VIRTIO=y
CONFIG_VIRTIO_BLK=y
CONFIG_VIRTIO_NET=y
CONFIG_HW_RANDOM_VIRTIO=y
CONFIG_CCWGROUP=y
CONFIG_QDIO=y
CONFIG_QETH=y
CONFIG_QETH_L2=y
CONFIG_QETH_L3=y
CONFIG_DASD=y
CONFIG_DASD_ECKD=y
CONFIG_DASD_FBA=y
CONFIG_CHSC_SCH=y
CONFIG_SCSI=y
CONFIG_SCSI_FC_ATTRS=y
CONFIG_ZFCP=y
CONFIG_SCLP_TTY=y
CONFIG_SCLP_CONSOLE=y
CONFIG_SCLP_VT220_TTY=y
CONFIG_SCLP_VT220_CONSOLE=y
CONFIG_TN3270=y
CONFIG_TN3270_CONSOLE=y
@@ -84,3 +84,15 @@ do_kernel_metadata:prepend:xcat-genesis-ppc64() {
fi
}
do_kernel_metadata[depends] += "patch-native:do_populate_sysroot"
COMPATIBLE_MACHINE:xcat-genesis-s390x = "^xcat-genesis-s390x$"
KMACHINE:xcat-genesis-s390x = "xcat-genesis-s390x"
KBRANCH:xcat-genesis-s390x = "v6.18/standard/base"
SRCREV_machine:xcat-genesis-s390x = "b1ba5428513b52c2bd6acfd3ad0a910f699bc395"
KBUILD_DEFCONFIG:xcat-genesis-s390x = "defconfig"
KCONFIG_MODE:xcat-genesis-s390x = "--alldefconfig"
KERNEL_FEATURES:remove:xcat-genesis-s390x = "features/drm-bochs/drm-bochs.scc"
SRC_URI:append:xcat-genesis-s390x = " \
file://0001-s390-use-stable-generator-name.patch \
file://xcat-genesis-s390x.cfg \
"
@@ -0,0 +1,2 @@
# libspdm has no s390x architecture mapping.
COMPATIBLE_HOST:s390x = "null"
@@ -0,0 +1,26 @@
SUMMARY = "IBM Z network configuration utility"
HOMEPAGE = "https://github.com/ibm-s390-linux/s390-tools"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://LICENSE;md5=f5118f167b055bfd7c3450803f1847af"
SRC_URI = "git://github.com/ibm-s390-linux/s390-tools;protocol=https;branch=master;tag=v${PV}"
SRCREV = "5e07b30bdf67623e7a6d3850e26208b642d416d7"
COMPATIBLE_HOST = "s390x.*-linux"
RDEPENDS:${PN} = "bash coreutils findutils gawk grep kmod sed udev util-linux-getopt util-linux-logger"
do_compile[noexec] = "1"
do_install() {
install -d ${D}${base_sbindir} ${D}${nonarch_base_libdir}/s390-tools
sed 's/%S390_TOOLS_VERSION%/${PV}/g' ${S}/zconf/znetconf \
>${D}${base_sbindir}/znetconf
chmod 0755 ${D}${base_sbindir}/znetconf
install -m 0755 ${S}/zconf/lsznet.raw \
${D}${nonarch_base_libdir}/s390-tools/lsznet.raw
install -m 0755 ${S}/zconf/znetcontrolunits \
${D}${nonarch_base_libdir}/s390-tools/znetcontrolunits
}
FILES:${PN} = "${base_sbindir}/znetconf ${nonarch_base_libdir}/s390-tools"
@@ -26,7 +26,7 @@ publish_status() {
|| logger -t xcat-genesis-discover -- 'unable to publish discovery status'
}
# shellcheck disable=SC2329
# shellcheck disable=SC2317,SC2329
finish_status() {
local result=$?
@@ -73,6 +73,22 @@ read_value() {
tr -d '\000\r\n' <"$value_file" | head -c 512
}
read_sysinfo_value() {
local name=$1
awk -F ':' -v name="$name" \
'$1 == name {sub(/^[[:space:]]+/, "", $2); sub(/[[:space:]]+$/, "", $2); print $2; exit}' \
"$proc_root/sysinfo" 2>/dev/null || true
}
sysinfo_has_key() {
local name=$1
awk -F ':' -v name="$name" \
'$1 == name {found = 1; exit} END {exit !found}' \
"$proc_root/sysinfo" 2>/dev/null
}
send_packet() {
if [[ -n ${XCAT_DISCOVERY_SEND_COMMAND:-} ]]; then
"$XCAT_DISCOVERY_SEND_COMMAND" "$compressed_packet" "$XCATMASTER" "$XCATPORT"
@@ -125,12 +141,31 @@ system_vendor=$(read_value "$sys_root/sys/devices/virtual/dmi/id/sys_vendor" ||
serial=$(read_value "$sys_root/sys/devices/virtual/dmi/id/product_serial" || true)
uuid=$(read_value "$sys_root/sys/devices/virtual/dmi/id/product_uuid" || true)
machine_type=
s390_virtual=
[[ -z $system_vendor && -z $product_name ]] \
|| machine_type=${system_vendor:+$system_vendor:}$product_name
platform=$(awk -F ':' \
'/^platform[[:space:]]*:/ {sub(/^[[:space:]]+/, "", $2); print $2; exit}' \
"$proc_root/cpuinfo" 2>/dev/null || true)
if [[ -z $product_name ]]; then
if [[ $architecture == s390x ]]; then
system_vendor=$(read_sysinfo_value Manufacturer)
product_type=$(read_sysinfo_value Type)
product_model=$(read_sysinfo_value Model)
product_model=${product_model##* }
product_name=${product_type}${product_model:+-$product_model}
machine_type=$product_name
serial=
if sysinfo_has_key 'VM00 Control Program'; then
s390_virtual=1
fi
uuid=$(read_sysinfo_value 'VM00 UUID')
[[ -n $uuid || -n $s390_virtual ]] \
|| uuid=$(read_sysinfo_value 'LPAR UUID')
platform=$(read_sysinfo_value 'VM00 Control Program')
if [[ -z $s390_virtual ]]; then
platform=LPAR
fi
elif [[ -z $product_name ]]; then
product_name=$(read_value "$proc_root/device-tree/model" || true)
[[ -n $product_name ]] \
|| product_name=$(read_value \
@@ -148,12 +183,21 @@ node_type=
case "$system_vendor $product_name" in
*KVM*|*QEMU*|*VMware*|*VirtualBox*|*Bochs*|*Hyper-V*) node_type=virtual ;;
esac
[[ -z $s390_virtual ]] || node_type=virtual
cpu_count=$(awk '/^(processor|cpu)[[:space:]]*:/ {count++} END {print count + 0}' \
"$proc_root/cpuinfo" 2>/dev/null || printf '0')
cpu_type=$(awk -F ':' \
'/^(model name|cpu)[[:space:]]*:/ {sub(/^[[:space:]]+/, "", $2); print $2; exit}' \
"$proc_root/cpuinfo" 2>/dev/null || true)
if [[ $architecture == s390x ]]; then
cpu_count=$(awk '/^processor[[:space:]]+[0-9]+[[:space:]]*:/ {count++} END {print count + 0}' \
"$proc_root/cpuinfo" 2>/dev/null || printf '0')
cpu_type=$(awk -F ':' \
'/^vendor_id[[:space:]]*:/ {sub(/^[[:space:]]+/, "", $2); print $2; exit}' \
"$proc_root/cpuinfo" 2>/dev/null || true)
else
cpu_count=$(awk '/^(processor|cpu)[[:space:]]*:/ {count++} END {print count + 0}' \
"$proc_root/cpuinfo" 2>/dev/null || printf '0')
cpu_type=$(awk -F ':' \
'/^(model name|cpu)[[:space:]]*:/ {sub(/^[[:space:]]+/, "", $2); print $2; exit}' \
"$proc_root/cpuinfo" 2>/dev/null || true)
fi
memory=$(awk '/^MemTotal:/ {printf "%.0fMB", $2 / 1024; exit}' \
"$proc_root/meminfo" 2>/dev/null || true)
disk_size=$(lsblk -b -dn -o NAME,SIZE,TYPE 2>/dev/null \
+1 -1
View File
@@ -59,7 +59,7 @@ done
architecture=$1
image_dir=${2%/}
case "$architecture" in
aarch64|armv7hf|riscv64|x86|x86_64|ppc64|ppc64le) ;;
aarch64|armv7hf|riscv64|s390x|x86|x86_64|ppc64|ppc64le) ;;
*) fail "unsupported architecture: $architecture" ;;
esac
+64 -21
View File
@@ -5,7 +5,10 @@ BEGIN { $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : -d '/opt/xcat' ? '/o
use lib "$::XCATROOT/probe/lib/perl";
use probe_utils;
use xCAT::CommandUtils;
use File::Basename;
use File::Temp qw(tempfile);
use POSIX qw(_exit sigprocmask WNOHANG SIG_BLOCK SIG_SETMASK SIGINT SIGTERM);
use IO::Socket::INET;
use Time::HiRes qw(gettimeofday sleep);
use Getopt::Long;
@@ -16,7 +19,6 @@ my $program_name = basename("$0");
my $output = "stdout";
my $duration = 10;
my $test = 0;
my $dumpfile = "/tmp/dhcpdumpfile.log";
my $nic;
$::USAGE = "Usage:
@@ -63,7 +65,8 @@ if ($::TEST) {
exit 0;
}
unless (-x "/usr/sbin/tcpdump") {
my $tcpdump = xCAT::CommandUtils::find_executable('tcpdump');
unless ($tcpdump) {
probe_utils->send_msg("$output", "f", "Tool 'tcpdump' is installed on current server");
probe_utils->send_msg("$output", "d", "$program_name needs to leverage 'tcpdump', please install 'tcpdump' first");
exit 1;
@@ -151,44 +154,57 @@ my $package = packdhcppkg($MAC);
probe_utils->send_msg("$output", "i", "Start to detect DHCP, please wait $duration seconds");
$msg = "fork a process to capture the packet by tcpdump";
my ($dumpfh, $dumpfile) = tempfile("detect_dhcpd.XXXXXX", TMPDIR => 1, UNLINK => 1);
close($dumpfh);
# INT and TERM stay blocked from the fork until the handler that stops the capture is in place.
my $stop_signals = POSIX::SigSet->new(SIGINT, SIGTERM);
my $signal_mask = POSIX::SigSet->new();
sigprocmask(SIG_BLOCK, $stop_signals, $signal_mask);
my $pid = fork;
if (!defined $pid) {
sigprocmask(SIG_SETMASK, $signal_mask);
probe_utils->send_msg("$output", "f", $msg);
exit 1;
} elsif ($pid == 0) {
# Child process
my $cmd = "tcpdump -i $nic port 68 -n -vvvvvv > $dumpfile 2>/dev/null";
`$cmd`;
exit 0;
# Child process: tcpdump itself, so the parent owns exactly this pid.
sigprocmask(SIG_SETMASK, $signal_mask);
open(STDOUT, '>', $dumpfile) or _exit(1);
open(STDERR, '>', '/dev/null');
exec($tcpdump, '-i', $nic, 'port', '68', '-n', '-vvvvvv');
_exit(1);
}
$SIG{INT} = $SIG{TERM} = sub { kill_child(); exit 1; };
sigprocmask(SIG_SETMASK, $signal_mask);
probe_utils->send_msg("$output", "d", "The id of process which is used to capture the packet by tcpdump is $pid") if ($::VERBOSE);
my $start = Time::HiRes::gettimeofday();
$start =~ s/(\d.*)\.(\d.*)/$1/;
my $end = $start;
while ($end - $start <= $duration) {
$sock->send($package);
probe_utils->send_msg("$output", "d", "Send DHCP rquest result: $@") if ($::VERBOSE && $@);
unless ($sock->send($package)) {
probe_utils->send_msg("$output", "d", "Send DHCP discover error: $!") if ($::VERBOSE);
probe_utils->send_msg("$output", "f", "Send out DHCP discover");
kill_child();
exit 1;
}
sleep 2;
$end = Time::HiRes::gettimeofday();
$end =~ s/(\d.*)\.(\d.*)/$1/;
}
$msg = "Kill the process which is used to capture the packet by tcpdump";
kill_child();
waitpid($pid, 0);
sleep 1;
`ps aux|grep -v grep |grep $pid > /dev/null 2>&1`;
if (!$?) {
$msg = "Capture the packets by tcpdump";
my $capture_problem = kill_child();
if ($capture_problem) {
probe_utils->send_msg("$output", "d", "tcpdump $capture_problem") if ($::VERBOSE);
probe_utils->send_msg("$output", "f", $msg);
exit 1;
}
$msg = "Dump test result";
unless (open(FILE, "<$dumpfile")) {
probe_utils->send_msg("$output", "d", "Open dump file $dumpfile failed") if ($::VERBOSE);
probe_utils->send_msg("$output", "f", $msg);
`rm -f $dumpfile` if (-e "$dumpfile");
exit 1;
}
my %output;
@@ -271,7 +287,6 @@ if (scalar(@server)) {
}
}
`rm -f $dumpfile` if (-e "$dumpfile");
exit 0;
@@ -377,11 +392,39 @@ sub packdhcppkg {
return $package;
}
# Stop the capture and reap it, once: INT and TERM are blocked while the pid is taken and until
# tcpdump is reaped, so an interrupt in that window cannot orphan it or reach a recycled pid. The
# handlers stay installed, so a later signal still leaves through exit and the END cleanup.
# Returns '' when tcpdump ran until this TERM and stopped cleanly, otherwise what went wrong: it
# ended on its own, ignored TERM and was killed, or left on another signal or with a non-zero
# status.
sub kill_child {
kill 15, $pid;
my @pidoftcpdump = `ps -ef | grep -E "[0-9]+:[0-9]+:[0-9]+ tcpdump -i $nic" | awk -F' ' '{print \$2}'`;
foreach my $cpid (@pidoftcpdump) {
kill 15, $cpid;
sigprocmask(SIG_BLOCK, $stop_signals);
my $child = $pid;
$pid = undef;
unless ($child) {
sigprocmask(SIG_SETMASK, $signal_mask);
return '';
}
probe_utils->send_msg("$output", "d", "Kill process $pid used to capture the packet by 'tcpdump'") if ($::VERBOSE);
my $reaped = waitpid($child, WNOHANG);
my $early = ($reaped == $child) ? 1 : 0;
if ($reaped == 0) {
kill 'TERM', $child;
foreach (1 .. 50) {
last if ($reaped = waitpid($child, WNOHANG)) != 0;
select(undef, undef, undef, 0.1);
}
if ($reaped == 0) {
kill 'KILL', $child;
$reaped = waitpid($child, 0);
}
}
sigprocmask(SIG_SETMASK, $signal_mask);
return "could not be reaped" if $reaped != $child;
my ($signal, $status) = ($? & 127, $? >> 8);
probe_utils->send_msg("$output", "d", "Kill process $child used to capture the packet by 'tcpdump'") if ($::VERBOSE);
my $how = $signal ? "on signal $signal" : "with status $status";
return "ended before the capture window did, $how" if $early;
return '' if $signal == 15 || (!$signal && !$status);
return "left the capture $how";
}
+1 -1
View File
@@ -8,7 +8,7 @@ Homepage: https://xcat.org/
Package: xcat-server
Architecture: all
Depends: ${perl:Depends}, grub2-xcat (>= 2.02-0.76.el7.1.snap201905160255), perl-xcat (>= 2.13-snap000000000000), xcat-client (>= 2.13-snap000000000000), libsys-syslog-perl, libio-socket-ssl-perl, libxml-simple-perl, make, ucf, libdbd-sqlite3-perl, libexpect-perl, libnet-dns-perl, libsoap-lite-perl, libxml-libxml-perl, libsnmp-perl, debootstrap, libdigest-sha-perl,libcrypt-rijndael-perl,libcrypt-cbc-perl,libjson-perl, libnet-https-nb-perl, libhttp-async-perl
Depends: ${perl:Depends}, grub-common, libcgi-pm-perl, grub2-xcat (>= 2.02-0.76.el7.1.snap201905160255), perl-xcat (>= 2.13-snap000000000000), xcat-client (>= 2.13-snap000000000000), libsys-syslog-perl, libio-socket-ssl-perl, libxml-simple-perl, make, ucf, libdbd-sqlite3-perl, libexpect-perl, libnet-dns-perl, libsoap-lite-perl, libxml-libxml-perl, libsnmp-perl, debootstrap, libdigest-sha-perl,libcrypt-rijndael-perl,libcrypt-cbc-perl,libjson-perl, libnet-https-nb-perl, libhttp-async-perl
Description: Server and configuration utilities of xCAT
xCAT-server provides the core server and configuration management components
of xCAT.
+62
View File
@@ -1889,6 +1889,68 @@ sub get_pkglist_tex
#----------------------------------------------------------------------------
=head3 get_pkglist_records
The records of one or more pkglist files, one per line kept whole and
includes followed. get_pkglist_tex joins records with commas, so it
cannot separate a record that itself contains a comma.
Arguments: comma-separated pkglist file names
Returns: list of records
=cut
#-----------------------------------------------------------------------------
sub get_pkglist_records
{
my $allfiles_pkglist = shift;
if ($allfiles_pkglist =~ "xCAT::") {
$allfiles_pkglist = shift;
}
my @records;
foreach my $pkglist (split(/,/, $allfiles_pkglist // ''))
{
next if $pkglist eq '';
push(@records, pkglist_file_records($pkglist, dirname($pkglist), 0));
}
return @records;
}
# pkglist_file_records: the records of one pkglist file, read as get_pkglist_tex reads them, with an
# #INCLUDE: record replaced by the records of the named file.
# A nested include resolves against the directory of the listed pkglist, as get_pkglist_tex resolves it.
sub pkglist_file_records
{
my ($file, $idir, $depth) = @_;
my @records;
open(my $fh, '<', $file) or return ("#INCLUDEBAD:cannot open pkglist file $file#");
while (my $line = <$fh>)
{
chomp($line);
$line =~ s/\s+$//;
$line =~ s/^\s*//;
next if $line eq '';
next
if ($line =~ /^#/
&& $line !~ /^#INCLUDE:[^#^\n]+#/
&& $line !~ /^#NEW_INSTALL_LIST#/
&& $line !~ /^#ENV:[^#^\n]+#/);
if ($line =~ /^#INCLUDE:([^#^\n]+)#(.*)$/ && $depth < 20)
{
my ($name, $note) = ($1, $2);
my $include = xCAT::Utils->varsubinline($name, \%ENV);
$include = "$idir/$include" unless $include =~ m{^/};
my @included = pkglist_file_records($include, $idir, $depth + 1);
$included[-1] .= $note if @included && $note ne '';
push(@records, @included);
next;
}
push(@records, $line);
}
close($fh);
return @records;
}
#----------------------------------------------------------------------------
=head3 includefile
handles #INCLUDE# in otherpkg.pkglist file
+236 -14
View File
@@ -173,6 +173,11 @@ sub subvars {
$inc =~ s/#INCLUDE_DEFAULT_RMPKGLIST_S#/#INCLUDE_RMPKGLIST:$pkglistfile#/g;
}
# osimage environvar reaches apt-get through ospkgs alone, so such an image installs its list there,
# and its pkgdir mirrors, which may need those variables too, stay out of the installer's sources
my $environvar_set = ( $namedargs{environvar} // '' ) =~ /\S/;
my $installer_pkgdirs = $environvar_set ? undef : $namedargs{pkgdirs};
my @autoinstall;
if (("ubuntu" eq $platform) || ("debian" eq $platform)) {
# since debian/ubuntu uses a preseed file instead of a kickstart file, pkglist
@@ -186,6 +191,7 @@ sub subvars {
if ($allpkglist =~ /#INCLUDEBAD:(.*)#/) {
return "$1";
}
@autoinstall = ubuntu_autoinstall_packages( xCAT::Postage->get_pkglist_records($pkglistfile) ) unless $environvar_set;
$allpkglist =~ s/,/ /g;
$inc =~ s/#INCLUDE_DEFAULT_PKGLIST_PRESEED#/$allpkglist/g;
@@ -364,7 +370,7 @@ sub subvars {
$inc =~ s/#INSTALL_SOURCES_IN_PRE#/$source_in_pre/g;
if (("ubuntu" eq $platform) || ("debian" eq $platform)) {
$inc =~ s/#INCLUDE_OSIMAGE_PKGDIR#/$pkgdirs[-1]/;
$inc =~ s/#UBUNTU_SUBIQUITY_APT_CONFIG#/ubuntu_subiquity_apt_config($media_dir)/eg;
$inc =~ s/#UBUNTU_SUBIQUITY_APT_CONFIG#/ubuntu_subiquity_apt_config($media_dir, $namedargs{osarch}, $installer_pkgdirs)/eg;
}
$inc =~ s/#WRITEREPO#/$writerepo/g;
}
@@ -377,7 +383,9 @@ sub subvars {
$inc =~ s/#INCLUDE_NOP:([^#^\n]+)#/includefile($1,1,0)/eg;
$inc =~ s/#XCATVAR:([^#]+)#/envvar($1)/eg;
$inc =~ s/#ENV:([^#]+)#/envvar($1)/eg;
$inc =~ s/#UBUNTU_SUBIQUITY_APT_CONFIG#/ubuntu_subiquity_apt_config($media_dir)/eg;
$inc =~ s/#UBUNTU_SUBIQUITY_APT_CONFIG#/ubuntu_subiquity_apt_config($media_dir, $namedargs{osarch}, $installer_pkgdirs)/eg;
# in the include pass, so a template that includes the stock Subiquity one gets its list as well
$inc =~ s/^((?:[ \t]*- [^\n]*\n)*)([ \t]*)- #INCLUDE_DEFAULT_PKGLIST_AUTOINSTALL#[ \t]*\n/$1 . ubuntu_autoinstall_items($2, $1, \@autoinstall)/meg;
$inc =~ s/#SUBIQUITYINSTALLNIC#/subiquity_install_nic()/eg;
$inc =~ s/#SUBIQUITYINSTALLMAC#/subiquity_install_mac()/eg;
$inc =~ s/#MACHINEPASSWORD#/machinepassword()/eg;
@@ -1764,26 +1772,103 @@ sub subiquity_install_mac {
return $macaddress;
}
# ubuntu_autoinstall_packages: the packages of the pkglist records (whole lines, as
# get_pkglist_records returns them) that a Subiquity autoinstall can install through its packages
# list. A record holds one or more space-separated packages, as the preseed path reads it, each a
# plain name or a task. A version pin or a target release stays with ospkgs, because the installer
# runs apt-get without --allow-downgrades and a pin can require one, and so does a name with an
# architecture qualifier, because a foreign architecture is enabled by a postscript that runs later. A preseed directive, told by its question type, a record that begins with a removal
# or a group, which ospkgs removes or installs whole, a removal written with a trailing hyphen as
# apt-get reads it, or a marker has
# no autoinstall form, a comment ends the packages of a record, and a record with a token that is
# none of these is left out whole. A list
# that carries a #ENV: setting, which only ospkgs can pass to apt-get, or an unreadable include is
# left to ospkgs whole; ospkgs still applies the whole list after the install.
my %PRESEED_TYPE = map { $_ => 1 } qw(string boolean select multiselect note password text seen title error);
sub ubuntu_autoinstall_packages
{
my @records = grep { defined } @_;
return () if grep { /#(?:ENV:|INCLUDEBAD:)/ } @records;
my (@packages, %seen);
RECORD: foreach my $record (@records) {
my @tokens = grep { length } split( /\s+/, $record );
next unless @tokens;
next if @tokens >= 3 && $PRESEED_TYPE{ $tokens[2] };
next if $tokens[0] =~ /^[-@]/; # ospkgs removes or installs the whole record
my @found;
foreach my $token (@tokens) {
last if $token =~ /^#/;
next if $token =~ /^[-@]/ || $token =~ /-$/;
next RECORD unless $token =~ m{^[a-z0-9][a-z0-9+.-]*(?::[a-z0-9-]+)?(?:[=/][^\s/=]+|\^)?$};
next if $token =~ m{[:=/]};
push @found, $token;
}
push @packages, grep { !$seen{$_}++ } @found;
}
return @packages;
}
# ubuntu_autoinstall_items: the list items for the pkglist packages at the token's indentation,
# leaving out packages the items above the token already name, each quoted so a name such as null
# or true stays a string. The time daemons exclude each
# other, so when the template names one, the pkglist's stay with ospkgs, as they did before.
my %UBUNTU_TIME_DAEMON = map { $_ => 1 } qw(chrony ntp ntpsec ntpdate ntpsec-ntpdate openntpd systemd-timesyncd);
sub ubuntu_autoinstall_items
{
my ($indent, $listed, $packages) = @_;
my %named = map { $_ => 1 } ( $listed =~ /^[ \t]*- (\S+)[ \t]*$/mg );
my $fixed_time_daemon = grep { $UBUNTU_TIME_DAEMON{$_} } keys %named;
my @items = grep { !$named{$_} } @$packages;
@items = grep { !$UBUNTU_TIME_DAEMON{ (split /[:=\/^]/, $_)[0] } } @items if $fixed_time_daemon;
return join( '', map { "$indent- \"$_\"\n" } @items );
}
sub ubuntu_subiquity_apt_mirror
{
my ($osarch) = @_;
# Apt mirror for Subiquity installs. site.ubuntu_apt_mirror overrides; otherwise default to the
# public archive. The minimal live-server install media is not a complete package source, so a
# real mirror is always required -- set site.ubuntu_apt_mirror to a local full mirror for
# airgapped clusters (or to a geo/ports mirror as needed).
my $default = 'http://archive.ubuntu.com/ubuntu';
#
# archive.ubuntu.com publishes amd64 and i386 only. Every other architecture, ppc64el and
# riscv64 included, is on the ports archive. The osimage's architecture decides it, because
# pkgdir is whatever path the administrator configured.
my $default = (!$osarch || $osarch =~ /^(?:amd64|i386|x86|x86_64)$/)
? 'http://archive.ubuntu.com/ubuntu'
: 'http://ports.ubuntu.com/ubuntu-ports';
my $site_tab = xCAT::Table->new('site');
return $default unless $site_tab;
my $ent = $site_tab->getAttribs({ key => 'ubuntu_apt_mirror' }, 'value');
return ($ent && defined($ent->{value}) && length($ent->{value})) ? $ent->{value} : $default;
}
# The key curtin names in the Deb822 source it writes for the primary apt mirror on 24.04 and later.
my $UBUNTU_ARCHIVE_KEYRING = '/usr/share/keyrings/ubuntu-archive-keyring.gpg';
sub ubuntu_subiquity_apt_config
{
my ($media_dir) = @_;
my ($media_dir, $osarch, $pkgdirs) = @_;
my $use_deb822 = ubuntu_subiquity_uses_deb822_sources($media_dir);
my @otherpkg_sources = ubuntu_subiquity_otherpkg_sources();
my $online_mirror = ubuntu_subiquity_apt_mirror($osarch);
my $mirror_key = $use_deb822 ? $UBUNTU_ARCHIVE_KEYRING : '';
my @otherpkg_sources = map { ubuntu_subiquity_otherpkg_source_spec( $_, $mirror_key, $online_mirror ) } ubuntu_subiquity_otherpkg_sources();
my @pkgdir_sources = ubuntu_subiquity_pkgdir_source_specs( $pkgdirs, $mirror_key, $online_mirror );
# apt rejects two sources for one repository whose options differ, so a pkgdir entry that repeats an
# otherpkgs repository adds its components to that source instead
my %otherpkg_by_key = map { ( my $uri = $_->{uri} ) =~ s{/+$}{}; ( "$uri $_->{suites}" => $_ ) } @otherpkg_sources;
@pkgdir_sources = grep {
( my $uri = $_->{uri} ) =~ s{/+$}{};
my $other = $otherpkg_by_key{"$uri $_->{suites}"};
ubuntu_subiquity_add_components( $other, $_->{components} ) if $other;
!$other;
} @pkgdir_sources;
my $online_mirror = ubuntu_subiquity_apt_mirror();
if ($online_mirror) {
# Online install: use the configured archive as the primary apt mirror so
# Subiquity/curtin can fetch whatever the minimal media lacks. No
@@ -1792,6 +1877,7 @@ sub ubuntu_subiquity_apt_config
' apt:',
' preserve_sources_list: false',
' geoip: false',
q( conf: 'APT::Install-Recommends "false";'),
' mirror-selection:',
' primary:',
" - uri: $online_mirror",
@@ -1809,12 +1895,16 @@ sub ubuntu_subiquity_apt_config
push @lines, ' xcat-ubuntu-updates.list:';
push @lines, qq( source: "deb $online_mirror \$RELEASE-updates main restricted universe multiverse");
}
if (@otherpkg_sources) {
if (@otherpkg_sources || @pkgdir_sources) {
push @lines, ' sources:' unless $need_sources_block;
my $index = 0;
foreach my $source (@otherpkg_sources) {
push @lines, " xcat-otherpkgs-$index.list:";
push @lines, qq( source: "deb [trusted=yes] $source ./");
push @lines, ubuntu_subiquity_source_lines( "xcat-otherpkgs-$index", $source, $use_deb822 );
$index++;
}
$index = 0;
foreach my $source (@pkgdir_sources) {
push @lines, ubuntu_subiquity_source_lines( "xcat-pkgdir-$index", $source, $use_deb822 );
$index++;
}
}
@@ -1826,6 +1916,7 @@ sub ubuntu_subiquity_apt_config
' preserve_sources_list: false',
' fallback: offline-install',
' geoip: false',
q( conf: 'APT::Install-Recommends "false";'),
' disable_suites:',
' - updates',
' - backports',
@@ -1855,22 +1946,36 @@ sub ubuntu_subiquity_apt_config
foreach my $source (@otherpkg_sources) {
push @lines, '';
push @lines, ' Types: deb';
push @lines, " URIs: $source";
push @lines, ' Suites: ./';
push @lines, ' Components:';
push @lines, " URIs: $source->{uri}";
push @lines, " Suites: $source->{suites}";
push @lines, ' Components:' . ( length $source->{components} ? " $source->{components}" : '' );
push @lines, ' Trusted: yes';
}
foreach my $source (@pkgdir_sources) {
push @lines, '';
push @lines, ' Types: deb';
push @lines, " URIs: $source->{uri}";
push @lines, " Suites: $source->{suites}";
push @lines, ' Components:' . ( length $source->{components} ? " $source->{components}" : '' );
push @lines, ' Trusted: yes' if $source->{trusted};
}
} else {
push @lines, ' mirror-selection:';
push @lines, ' primary:';
push @lines, ' - uri: file:/cdrom';
if (@otherpkg_sources) {
if (@otherpkg_sources || @pkgdir_sources) {
push @lines, ' sources:';
my $index = 0;
foreach my $source (@otherpkg_sources) {
push @lines, " xcat-otherpkgs-$index.list:";
push @lines, qq( source: "deb [trusted=yes] $source ./");
push @lines, qq( source: "$source->{line}");
$index++;
}
$index = 0;
foreach my $source (@pkgdir_sources) {
push @lines, " xcat-pkgdir-$index.list:";
push @lines, qq( source: "$source->{line}");
$index++;
}
}
@@ -1879,6 +1984,56 @@ sub ubuntu_subiquity_apt_config
return join( "\n", @lines );
}
# ubuntu_subiquity_pkgdir_source_specs: the apt sources of the entries after the install media in
# an osimage pkgdir value, which mkinstall hands over as pkgdirs and ospkgs receives as OSPKGDIR.
# An entry written as "URL suite components" is an apt source line, as ospkgs writes it, and a
# suite that is an exact path needs no component. A local directory that is a flat repository is
# served by the management node and trusted, as an otherpkgdir is. Anything else is no apt source
# for ospkgs either and is left out. An entry that names an Ubuntu archive mirror the installer
# already has a source for, the configured one or a default one, carries that source's signing
# key, the archive keyring on the Deb822 releases and none before them: apt rejects a second source
# for the same suite whose signing key differs.
sub ubuntu_subiquity_pkgdir_source_specs
{
my ( $pkgdirval, $mirror_key, @mirrors ) = @_;
$mirror_key //= '';
my %signed_uri = ubuntu_subiquity_signed_mirror_uris(@mirrors);
my @specs;
foreach my $entry ( split( /,/, $pkgdirval // '' ) ) {
$entry =~ s/^\s+|\s+$//g;
next if $entry eq '';
if ( $entry =~ m{^https?://} ) {
my ( $uri, $suite, @components ) = split( /\s+/, $entry );
next unless defined $suite && ( @components || $suite =~ m{/$} );
( my $bare = $uri ) =~ s{/+$}{};
my %spec = ( uri => $uri, suites => $suite, components => join( ' ', @components ), trusted => 0, signed_by => $signed_uri{$bare} ? $mirror_key : '' );
$spec{line} = ubuntu_subiquity_source_line( \%spec );
push @specs, \%spec;
}
elsif ( $entry !~ m{^[a-z]+://} && ubuntu_subiquity_local_apt_repo($entry) ) {
my $uri = ubuntu_subiquity_pkgdir_uri($entry);
my %spec = ( uri => $uri, suites => './', components => '', trusted => 1, signed_by => '' );
$spec{line} = ubuntu_subiquity_source_line( \%spec );
push @specs, \%spec;
}
}
# a directory and its own URL are one repository: one source, with the trust and components of both
my ( %kept, @unique );
foreach my $spec (@specs) {
( my $uri = $spec->{uri} ) =~ s{/+$}{};
if ( my $first = $kept{"$uri $spec->{suites}"} ) {
$first->{trusted} ||= $spec->{trusted};
$first->{signed_by} ||= $spec->{signed_by};
ubuntu_subiquity_add_components( $first, $spec->{components} );
next;
}
push @unique, $kept{"$uri $spec->{suites}"} = $spec;
}
return @unique;
}
sub ubuntu_subiquity_otherpkg_sources
{
my $nodetype_tab = xCAT::Table->new('nodetype');
@@ -1906,6 +2061,73 @@ sub ubuntu_subiquity_otherpkg_sources
return @sources;
}
# ubuntu_subiquity_add_components: the components of a repeated repository join the source kept for it.
sub ubuntu_subiquity_add_components
{
my ( $spec, $components ) = @_;
my %have = map { $_ => 1 } split( ' ', $spec->{components} );
$spec->{components} = join( ' ', split( ' ', $spec->{components} ), grep { !$have{$_}++ } split( ' ', $components // '' ) );
$spec->{line} = ubuntu_subiquity_source_line($spec);
return;
}
# ubuntu_subiquity_signed_mirror_uris: the apt mirror the installer already has a source for, the
# configured one or the architecture default, without a trailing slash.
sub ubuntu_subiquity_signed_mirror_uris
{
my (@mirrors) = @_;
return map { ( my $uri = $_ ) =~ s{/+$}{}; ( $uri => 1 ) } grep { defined && length } @mirrors;
}
# ubuntu_subiquity_source_line: the one-line form of a source, with the option its Deb822 form carries.
sub ubuntu_subiquity_source_line
{
my ($spec) = @_;
my @option = $spec->{trusted} ? ('[trusted=yes]') : $spec->{signed_by} ? ("[signed-by=$spec->{signed_by}]") : ();
return join( ' ', 'deb', @option, $spec->{uri}, $spec->{suites}, grep { length } $spec->{components} );
}
# ubuntu_subiquity_otherpkg_source_spec: the apt source of one otherpkgdir entry the installer gets.
# A bare URL or a local repository is a flat trusted repository, as otherpkgs treats it. An entry
# written as URL, suite and components is that source, trusted as well, unless the URL is an Ubuntu
# archive mirror the installer already has a source for: that one gets the same signing key and no
# trust, since apt rejects a second source for one suite whose options differ.
sub ubuntu_subiquity_otherpkg_source_spec
{
my ( $entry, $mirror_key, @mirrors ) = @_;
my ( $uri, $suite, @components ) = split( /\s+/, $entry );
my %spec = ( uri => $uri, suites => './', components => '', trusted => 1, signed_by => '' );
if ( defined $suite && length $suite ) {
my %signed = ubuntu_subiquity_signed_mirror_uris(@mirrors);
( my $bare = $uri ) =~ s{/+$}{};
@spec{qw(suites components)} = ( $suite, join( ' ', @components ) );
@spec{qw(trusted signed_by)} = ( 0, $mirror_key // '' ) if $signed{$bare};
}
$spec{line} = ubuntu_subiquity_source_line( \%spec );
return \%spec;
}
# ubuntu_subiquity_source_lines: one entry of the autoinstall sources mapping, a one-line source
# before Deb822 and a Deb822 stanza from 24.04 on: curtin converts a one-line source to Deb822
# there and keeps only its type, URI, suite and components, so a trusted repository would come out
# unsigned and be rejected.
sub ubuntu_subiquity_source_lines
{
my ( $name, $source, $use_deb822 ) = @_;
return ( " $name.list:", qq( source: "$source->{line}") ) unless $use_deb822;
my @lines = (
" $name.sources:",
' source: |',
' Types: deb',
" URIs: $source->{uri}",
" Suites: $source->{suites}",
' Components:' . ( length $source->{components} ? " $source->{components}" : '' ),
);
push @lines, " Signed-By: $source->{signed_by}" if $source->{signed_by};
push @lines, ' Trusted: yes' if $source->{trusted};
return @lines;
}
sub ubuntu_subiquity_uses_deb822_sources
{
my ($media_dir) = @_;
+64 -6
View File
@@ -328,12 +328,13 @@ sub process_request
next;
} elsif ($parm =~ /xcat_secure_pw:/) {
xCAT::MsgUtils->trace(0, 'I', "credentials: sending $parm to $client");
my @users=split(/:/,$parm);
if (defined($users[1]) and $users[1] eq 'root') {
my $pass = xCAT::PasswordUtils::crypt_system_password();
if ($pass) {
push @{$rsp->{'data'}}, { content => [ $pass ], desc => [ $parm ] };
}
my (undef, $user) = split(/:/, $parm);
my ($hash, $error) = system_password_hash($client, $user);
if ($hash) {
push @{ $rsp->{'data'} }, { content => [$hash], desc => [$parm] };
} else {
push @{ $rsp->{'error'} }, "Unable to get the password hash for $parm: $error";
xCAT::MsgUtils->trace(0, 'W', "credentials: Unable to get the password hash for $parm: $error");
}
next;
} else {
@@ -550,6 +551,63 @@ sub _sign_x509_certificate {
return $certificate;
}
# A sudoer without a passwd row gets the locked field so the node applies the reply as is.
sub system_password_hash {
my ($node, $user) = @_;
unless (defined($user) and $user =~ /^[A-Za-z_][A-Za-z0-9_.-]{0,31}$/) {
return (undef, 'invalid user name');
}
unless ($user eq 'root' or configured_sudoers($node)->{$user}) {
return (undef, "$user is not a configured sudoer of $node");
}
my %key = (key => 'system', username => $user);
my $passwd = xCAT::Table->new('passwd', -create => 0);
my $entry = $passwd ? $passwd->getAttribs(\%key, 'password') : undef;
$passwd->close() if $passwd;
unless ($entry and defined($entry->{password})) {
return ('!', undef) unless $user eq 'root';
return (undef, 'no password in the passwd table for root');
}
my $hash = xCAT::PasswordUtils::crypt_system_password('passwd', \%key, [ 'password', 'cryptmethod' ]);
return ($hash, $hash ? undef : "unable to hash the password of $user");
}
# The sudoer postscript entries of a node, from the same three sources
# Postage.pm uses: xcatdefaults, the osimage of provmethod, and the node.
sub configured_sudoers {
my $node = shift;
my @lists;
my $posttab = xCAT::Table->new('postscripts', -create => 0);
if ($posttab) {
my $defaults = $posttab->getAttribs({ node => 'xcatdefaults' }, 'postscripts', 'postbootscripts');
my $own = $posttab->getNodeAttribs($node, [ 'postscripts', 'postbootscripts' ]);
push @lists, map { ($_->{postscripts}, $_->{postbootscripts}) } grep { $_ } ($defaults, $own);
$posttab->close();
}
my $typetab = xCAT::Table->new('nodetype', -create => 0);
my $type = $typetab ? $typetab->getNodeAttribs($node, ['provmethod']) : undef;
$typetab->close() if $typetab;
if ($type and $type->{provmethod} and $type->{provmethod} !~ /^(?:install|netboot|statelite)$/) {
my $imagetab = xCAT::Table->new('osimage', -create => 0);
my $image = $imagetab ? $imagetab->getAttribs({ imagename => $type->{provmethod} }, 'postscripts', 'postbootscripts') : undef;
$imagetab->close() if $imagetab;
push @lists, ($image->{postscripts}, $image->{postbootscripts}) if $image;
}
my %sudoers;
foreach my $entry (map { split /,/, $_ } grep { defined } @lists) {
next unless $entry =~ /^\s*sudoer(?:\s+(.*?))?\s*$/;
my $args = defined $1 ? $1 : '';
my $name = $args =~ /(?:^|\s)-u\s*(\S+)/ ? $1 : 'xcat';
$sudoers{$name} = 1;
}
return \%sudoers;
}
sub ok_with_node {
my $node = shift;
+188 -17
View File
@@ -192,6 +192,9 @@ my %INSTALL_BOOT_FILES = (
[ 'install/netboot/ubuntu-installer/{darch}/vmlinux', 'install/netboot/ubuntu-installer/{darch}/initrd.gz' ],
[ 'install/vmlinux', 'install/netboot/initrd.gz' ],
],
'riscv64' => [
[ 'casper/vmlinux', 'casper/initrd' ],
],
);
sub install_boot_files
@@ -201,9 +204,10 @@ sub install_boot_files
$darch = '' unless defined $darch;
my $family =
$arch =~ /x86/i ? 'x86'
: $arch =~ /ppc64/i ? 'ppc64'
: undef;
$arch =~ /x86/i ? 'x86'
: $arch =~ /ppc64/i ? 'ppc64'
: $arch =~ /riscv64/i ? 'riscv64'
: undef;
return unless $family;
foreach my $candidate (@{ $INSTALL_BOOT_FILES{$family} }) {
@@ -215,6 +219,166 @@ sub install_boot_files
return;
}
# The grub2 image on the media boots only from the media: it carries a built-in
# configuration that looks for the live filesystem and never reads the network
# configuration nodeset writes. A netboot image is built from the grub2 package
# the media ships instead.
my %MEDIA_GRUB2_BUILDS = (
'riscv64' => {
format => 'riscv64-efi',
package => 'grub-efi-riscv64-bin',
machine => 0x5064,
},
);
# Where the loader looks for the configuration nodeset writes, stored inside the image.
my $GRUB2_PREFIX = '/boot/grub2';
# The modules the network path needs before it can read a configuration file.
my @GRUB2_NETBOOT_MODULES = qw(
efinet tftp http net normal linux echo test configfile
search search_label search_fs_uuid search_fs_file
gzio part_gpt part_msdos ext2 fat all_video video font terminal reboot halt
);
sub install_media_grub2_loader {
my ($path, $arch, $callback) = @_;
my $build = $MEDIA_GRUB2_BUILDS{$arch};
return unless $build;
my $tftpdir = xCAT::TableUtils->getTftpDir();
unless ($tftpdir) {
_no_grub2_loader($arch, 'the TFTP directory is not known', $callback);
return;
}
my $target = "$tftpdir/boot/grub2/grub2.$arch";
return $target if _is_netboot_loader($target, $build);
# A file that failed the check is left where it is. The check cannot tell an image built for
# another boot path from one this plugin did not build: the loader on the media carries the
# same modules and differs only in the prefix. Moving a file aside on that evidence can take
# a working loader away from every node of the architecture, so it is replaced only once a
# working one exists, by the rename below.
my $kept = -e $target ? 1 : 0;
my ($package) = glob("$path/pool/main/g/grub2/$build->{package}_*_$arch.deb");
unless ($package && -r $package) {
_no_grub2_loader($arch, "the media carry no $build->{package} package", $callback, $kept);
return;
}
my $workdir = tempdir(CLEANUP => 1);
if (system('dpkg-deb', '-x', $package, $workdir) != 0) {
_no_grub2_loader($arch, "$package could not be unpacked", $callback, $kept);
return;
}
my $moduledir = "$workdir/usr/lib/grub/$build->{format}";
unless (-d $moduledir) {
_no_grub2_loader($arch, "$package carries no $build->{format} modules", $callback, $kept);
return;
}
mkpath("$tftpdir/boot/grub2");
# Built beside the target and renamed, so an interrupted run cannot leave a partial
# loader that nodeset would hand to every node of the architecture.
my $partial = "$target.$$";
my $rc = system('grub-mkimage', '-O', $build->{format}, '-d', $moduledir,
'-p', $GRUB2_PREFIX, '-o', $partial, @GRUB2_NETBOOT_MODULES);
unless ($rc == 0 and _is_netboot_loader($partial, $build)) {
unlink $partial;
_no_grub2_loader($arch, 'grub-mkimage could not build it', $callback, $kept);
return;
}
chmod 0644, $partial;
unless (rename($partial, $target)) {
unlink $partial;
_no_grub2_loader($arch, "it could not be renamed to $target: $!", $callback, $kept);
return;
}
$callback->({ data => "Installed $target from the media" }) if $callback;
return $target;
}
# UEFI loads the loader as a PE image for one machine, so anything else -- a truncated
# file, a stub carrying only headers, or the loader of another architecture -- cannot boot
# a node and is replaced. The fields below are the ones an image must have to be executed
# at all: sections to load, an entry point to jump to, and the subsystem UEFI runs.
sub _is_uefi_image {
my ($file, $machine) = @_;
my $size = -s $file;
return 0 unless ($machine and $size);
open(my $fh, '<', $file) or return 0;
binmode($fh);
my $ok = 0;
my ($dos, $coff, $optional);
if (read($fh, $dos, 64) == 64
and substr($dos, 0, 2) eq 'MZ'
and seek($fh, unpack('V', substr($dos, 60, 4)), 0)
and read($fh, $coff, 24) == 24
and substr($coff, 0, 4) eq "PE\0\0"
and unpack('v', substr($coff, 4, 2)) == $machine
and unpack('v', substr($coff, 6, 2)) > 0
and read($fh, $optional, 72) == 72
and unpack('v', substr($optional, 0, 2)) == 0x20b)
{
my $entry = unpack('V', substr($optional, 16, 4));
my $image = unpack('V', substr($optional, 56, 4));
my $headers = unpack('V', substr($optional, 60, 4));
my $system = unpack('v', substr($optional, 68, 2));
# 10 is the EFI application subsystem, the only one the firmware loads.
$ok = ($entry and $image and $headers and $system == 10
and $headers <= $size and $image <= $size);
}
close($fh);
return $ok ? 1 : 0;
}
# The modules a net boot cannot happen without. grub-mkimage records the name of every module
# it embeds, so their absence says the file is not a grub2 image built for this boot path,
# whatever its headers claim.
my @GRUB2_REQUIRED_MODULES = qw(efinet tftp http linux normal configfile search);
# grub-mkimage stores the prefix inside the image, so a loader built for this boot path
# carries the directory nodeset writes its configuration into. The image on the media has
# the same modules but no such prefix, which is why it cannot find that configuration: a
# file without it is replaced rather than trusted.
sub _is_netboot_loader {
my ($file, $build) = @_;
return 0 unless _is_uefi_image($file, $build->{machine});
open(my $fh, '<', $file) or return 0;
binmode($fh);
my $image = do { local $/; <$fh> };
close($fh);
return 0 unless defined $image and index($image, $GRUB2_PREFIX) >= 0;
for my $module (@GRUB2_REQUIRED_MODULES) {
return 0 if index($image, "\0$module\0") < 0;
}
return 1;
}
# Nothing else on an Ubuntu management node installs this loader, so a node of this
# architecture cannot boot until an administrator supplies one.
sub _no_grub2_loader {
my ($arch, $reason, $callback, $kept) = @_;
return unless $callback;
my $consequence = $kept
? "The grub2.$arch already in the boot/grub2 directory of the TFTP root was left alone. It "
. "was not built for this boot path, so check that nodes of this architecture still boot."
: "Nodes of this architecture will not boot until one is placed in the boot/grub2 "
. "directory of the TFTP root.";
$callback->({
warning => [ "No grub2.$arch boot loader was installed because $reason. $consequence" ] });
return;
}
sub is_ubuntu_live_media
{
my $media_path = shift;
@@ -517,6 +681,7 @@ sub copycd
}
$callback->({ data => "Media copy operation successful" });
install_media_grub2_loader($temppath, $arch, $callback);
unless ($noosimage) {
my @ret = xCAT::SvrUtils->update_tables_with_templates($distname, $arch, $temppath, $osdistroname, $legacyUB20);
if ($ret[0] != 0) {
@@ -645,6 +810,15 @@ sub subiquity_boot_params {
return (subiquity_kcmdline($base, $nfsip, $pkgdir, $instserver, $httpport, $node), undef);
}
# The Debian name of an install architecture, and whether xCAT installs Ubuntu
# on it. ppc64le is kept as is: the media paths key on both spellings.
my %INSTALL_ARCH = map { $_ => 1 } qw(x86_64 x86 ppc64le ppc64el riscv64);
sub install_darch {
my ($arch) = @_;
return ( xCAT::Utils::debian_arch($arch), $INSTALL_ARCH{ $arch // '' } ? 1 : 0 );
}
sub mkinstall {
xCAT::MsgUtils->message("S", "Doing debian mkinstall");
my $request = shift;
@@ -721,6 +895,7 @@ sub mkinstall {
my $partitionfile;
my $pkgdir;
my $pkgdirval;
my $environvar;
my @mirrors;
my $pkglistfile;
my $imagename; # set it if running of 'nodeset osimage=xxx'
@@ -743,12 +918,13 @@ sub mkinstall {
if (!$osimagetab) {
$osimagetab = xCAT::Table->new('osimage', -create => 1);
}
(my $ref) = $osimagetab->getAttribs({ imagename => $imagename }, 'osvers', 'osarch', 'profile', 'provmethod');
(my $ref) = $osimagetab->getAttribs({ imagename => $imagename }, 'osvers', 'osarch', 'profile', 'provmethod', 'environvar');
if ($ref) {
$img_hash{$imagename}->{osver} = $ref->{'osvers'};
$img_hash{$imagename}->{osarch} = $ref->{'osarch'};
$img_hash{$imagename}->{profile} = $ref->{'profile'};
$img_hash{$imagename}->{provmethod} = $ref->{'provmethod'};
$img_hash{$imagename}->{environvar} = $ref->{'environvar'};
if (!$linuximagetab) {
$linuximagetab = xCAT::Table->new('linuximage', -create => 1);
}
@@ -821,6 +997,7 @@ sub mkinstall {
$tmplfile = $ph->{template};
$pkgdirval = $ph->{pkgdir};
$environvar = $ph->{environvar};
my @pkgdirlist = split(/,/, $pkgdirval);
foreach (@pkgdirlist) {
if ($_ =~ /^http|ssh/) {
@@ -868,18 +1045,9 @@ sub mkinstall {
xCAT::MsgUtils->trace($verbose_on_off, "d", "debian->mkinstall: pkgdir=$pkgdir pkglistfile=$pkglistfile tmplfile=$tmplfile");
}
if ($arch eq "x86_64") {
$darch = "amd64";
}
elsif ($arch eq "x86") {
$darch = "i386";
}
else {
if ($arch ne "ppc64le" and $arch ne "ppc64el") {
xCAT::MsgUtils->message("S", "debian.pm: Unknown arch ($arch)");
}
$darch = $arch;
}
my $known;
($darch, $known) = install_darch($arch);
xCAT::MsgUtils->message("S", "debian.pm: Unknown arch ($arch)") unless $known;
my @missingparms;
unless ($os) {
@@ -957,7 +1125,10 @@ sub mkinstall {
$pkgdir,
$platform,
$partitionfile,
\%tmpl_hash
\%tmpl_hash,
osarch => $arch,
pkgdirs => $pkgdirval,
environvar => $environvar
);
}
+6
View File
@@ -3362,6 +3362,12 @@ sub kea_subnet4_intent
loader_present => sub { -e $_[0] },
)
};
push @client_classes, @{
xCAT::DHCP::BootPolicy->kea_s390x_network_classes(
net => $net,
prefix => $prefix,
)
};
if (@client_classes) {
$subnet{additional_client_classes} = [ map { $_->{name} } @client_classes ];
$subnet{client_classes} = \@client_classes;
+25 -1
View File
@@ -85,6 +85,29 @@ sub getstate {
}
}
# grub2 reads its configuration as a script, so an unquoted word carrying one of the
# characters below ends the linux command and the rest of the kernel command line is lost.
# The Ubuntu installer seed (ds=nocloud-net;s=<url>) is the usual casualty.
my $GRUB2_TERMINATOR = qr/[;{}|&<>()]/;
sub quote_kcmdline {
my $kcmdline = shift;
return $kcmdline unless (defined $kcmdline and $kcmdline =~ $GRUB2_TERMINATOR);
# Escaped in place rather than quoted as a whole: a value the caller quoted keeps the
# quoting it was given, which grub2 removes before the kernel sees the value.
my $escaped = '';
while (length $kcmdline) {
if ($kcmdline =~ s/^('[^']*'|"[^"]*")//) { $escaped .= $1; next; }
if ($kcmdline =~ s/^(\\.)//) { $escaped .= $1; next; }
$kcmdline =~ s/^(.)//s;
my $char = $1;
$escaped .= ($char =~ $GRUB2_TERMINATOR) ? "\\$char" : $char;
}
return $escaped;
}
sub setstate {
=pod
@@ -260,7 +283,8 @@ sub setstate {
}
if ($kern and $kern->{kcmdline}) {
print $pcfg " linux$efi $protocolrootdir/$kern->{kernel} $kern->{kcmdline} BOOTIF=\$net_default_mac\n";
my $kcmdline = quote_kcmdline($kern->{kcmdline});
print $pcfg " linux$efi $protocolrootdir/$kern->{kernel} $kcmdline BOOTIF=\$net_default_mac\n";
} else {
print $pcfg " linux$efi $protocolrootdir/$kern->{kernel} BOOTIF=\$net_default_mac\n";
}
+144 -9
View File
@@ -1,16 +1,17 @@
package xCAT_plugin::mknb;
use strict;
use Digest::SHA ();
use File::Temp qw(tempdir);
use File::Temp qw(tempdir tempfile);
use xCAT::Utils;
use xCAT::TableUtils;
use xCAT::NodeRange;
use File::Path;
use File::Copy;
use English qw(-no_match_vars);
my $GENESIS_EXPORT_MANIFEST = 'xcat-genesis.manifest';
my %GENESIS_ARCHITECTURES = map { $_ => 1 }
qw(x86 x86_64 ppc64 ppc64le armv7hf aarch64 riscv64);
qw(x86 x86_64 ppc64 ppc64le armv7hf aarch64 riscv64 s390x);
sub _canonical_genesis_arch {
my ($arch) = @_;
@@ -300,6 +301,21 @@ sub _remove_openembedded_genesis {
"$directory/genesis.fs.$arch.lzma",
"$directory/genesis.exact-arch.$arch",
);
if ($arch eq 's390x') {
my $config_directory = "$tftpdir/pxelinux.cfg/s390x";
if (opendir my $config_stream, $config_directory) {
foreach my $name (readdir $config_stream) {
if ($name =~ m{\A[.][.]?\z}xms) {
next;
}
my $path = "$config_directory/$name";
if (_is_generated_s390x_config($path)) {
push @artifacts, $path;
}
}
closedir $config_stream;
}
}
my $removed = 0;
my @failed;
foreach my $artifact (@artifacts) {
@@ -320,6 +336,19 @@ sub _remove_openembedded_genesis {
return ($removed, undef);
}
sub _is_generated_s390x_config {
my ($path) = @_;
if (-l $path || !-f $path) {
return 0;
}
open my $config, '<', $path or return 0;
my $header = <$config>;
if (!close $config) {
return 0;
}
return defined($header) && $header eq "# pxelinux.cfg xCAT Genesis s390x\n";
}
sub genesis_lzma_command {
my ($have_lzma, $have_xz) = @_;
return 'lzma -C crc32 -9' if $have_lzma;
@@ -456,7 +485,7 @@ sub process_request {
my $tftpdir = xCAT::TableUtils->getTftpDir();
my $requested_arch = $request->{arg}->[0];
if (!$requested_arch) {
$callback->({ error => "Need to specify architecture (x86, x86_64, ppc64, ppc64le, armv7hf, aarch64 or riscv64)" }, { errorcode => [1] });
$callback->({ error => "Need to specify architecture (x86, x86_64, ppc64, ppc64le, armv7hf, aarch64, riscv64 or s390x)" }, { errorcode => [1] });
return;
}
@@ -711,7 +740,9 @@ sub process_request {
$normnet_addresses, \@master_addresses
);
my $consolecmdline;
if (defined($serialport) and $serialspeed) {
if ($arch eq 's390x') {
$consolecmdline = 'console=ttysclp0';
} elsif (defined($serialport) and $serialspeed) {
if ($arch =~ /ppc/) {
$consolecmdline = "console=tty0 console=hvc$serialport,$serialspeed";
} else {
@@ -746,15 +777,30 @@ sub process_request {
mkpath("$tftpdir/boot/grub2");
chmod(0755, "$tftpdir/boot/grub2");
}
if ($arch eq 's390x') {
mkpath "$tftpdir/pxelinux.cfg/s390x";
chmod 0755, "$tftpdir/pxelinux.cfg";
chmod 0755, "$tftpdir/pxelinux.cfg/s390x";
}
my $dopxe = 0;
my $s390x_config_error = 0;
foreach (keys %{$normnets}) {
my $net = $_;
my $nicip = $normnets->{$net};
my $xcatd_address = defined($xcatdnormnets->{$net}) ? $xcatdnormnets->{$net} : $nicip;
$net =~ s/\//_/;
if (defined($nobootnicips{$nicip})) {
if (defined($nobootnicips{$nicip})
|| ($arch eq 's390x' && defined($nobootnicips{$xcatd_address}))) {
if ($arch =~ /ppc/ and -r "$tftpdir/pxelinux.cfg/p/$net") {
unlink("$tftpdir/pxelinux.cfg/p/$net");
} elsif ($arch eq 's390x') {
my $path = "$tftpdir/pxelinux.cfg/s390x/$net";
if (_is_generated_s390x_config($path)) {
if (!unlink $path) {
$callback->({ error => ["Unable to remove s390x Genesis configuration: $path: $OS_ERROR"], errorcode => [1] });
$s390x_config_error = 1;
}
}
}
next;
}
@@ -816,6 +862,22 @@ sub process_request {
print $cfgfile " initrd http://" . $xcatd_address . "$portsuffix/$initrd_file\n";
print $cfgfile ' append "xcatd=' . $xcatd_address . ":$xcatdport $consolecmdline\"\n";
close($cfgfile);
} elsif ($arch eq 's390x') {
my (undef, $config_error) = _write_s390x_discovery_config(
tftpdir => $tftpdir,
network => $net,
xcatd_address => $xcatd_address,
xcatdport => $xcatdport,
consolecmdline => $consolecmdline,
kernel => $invisibletouch
? "xcat/genesis.kernel.$arch"
: "xcat/nbk.$arch",
initrd => $initrd_file,
);
if ($config_error) {
$callback->({ error => [$config_error], errorcode => [1] });
$s390x_config_error = 1;
}
}
}
$dopxe = 0;
@@ -876,15 +938,88 @@ sub process_request {
}
}
if (exists $GRUB2_DISCOVERY_ARCHES{$arch} && !-e "$tftpdir/boot/grub2/grub2.$arch") {
# These configurations are only reachable through grub2.<arch>, which xCAT
# does not build.
$callback->({ data => ["Note: $tftpdir/boot/grub2/grub2.$arch is missing; $arch nodes need it to reach these configurations (it is installed by grub2-xcat, or copied from the EL $arch installation media)"] });
# These configurations are only reachable through grub2.<arch>. copycd builds it from
# Ubuntu media; on EL it is supplied by grub2-xcat or copied from the media.
$callback->({ data => ["Note: $tftpdir/boot/grub2/grub2.$arch is missing; $arch nodes need it to reach these configurations (copycd builds it from Ubuntu media; on EL it is installed by grub2-xcat or copied from the $arch installation media)"] });
}
if ($configfileonly) {
if ($configfileonly && !$s390x_config_error) {
$callback->({ data => ["Write netboot config file done"] });
}
}
sub _write_s390x_discovery_config {
my (%args) = @_;
my $tftpdir = $args{tftpdir};
my $initrd = $args{initrd};
$initrd =~ s{^\Q$tftpdir\E/?}{}xms;
my $cmdline = "xcatd=$args{xcatd_address}:$args{xcatdport} xcat.bootloader=s390-ccw";
if (defined $args{consolecmdline} and length $args{consolecmdline}) {
$cmdline .= " $args{consolecmdline}";
}
my $qemu_path = "$tftpdir/pxelinux.cfg/s390x/$args{network}";
my $qemu_config = "# pxelinux.cfg xCAT Genesis s390x\n"
. "DEFAULT xCAT\n"
. "LABEL xCAT\n"
. " KERNEL $args{kernel}\n"
. " INITRD $initrd\n"
. " APPEND $cmdline\n";
my $error = _write_s390x_config($qemu_path, $qemu_config);
return (undef, $error) if $error;
return ($qemu_path, undef);
}
sub _write_s390x_config {
my ($path, $contents) = @_;
if ((-e $path || -l $path) && !_is_generated_s390x_config($path)) {
return "Refusing to replace unmanaged s390x configuration: $path";
}
my $directory = $path;
$directory =~ s{/[^/]+\z}{}xms;
my ($config, $temporary);
my $created = eval {
($config, $temporary) = tempfile(
'.mknb-s390x-XXXXXX', DIR => $directory, UNLINK => 0
);
1;
};
if (!$created) {
my $create_error = $EVAL_ERROR || $OS_ERROR;
chomp $create_error;
return "Unable to write s390x Genesis configuration: $path: $create_error";
}
my $write_ok = print {$config} $contents;
my $write_error = $OS_ERROR;
my $close_ok = close $config;
my $close_error = $OS_ERROR;
if (!$write_ok) {
unlink $temporary;
return "Unable to write s390x Genesis configuration: $path: $write_error";
}
if (!$close_ok) {
unlink $temporary;
return "Unable to write s390x Genesis configuration: $path: $close_error";
}
if (!chmod 0644, $temporary) {
my $chmod_error = $OS_ERROR;
unlink $temporary;
return "Unable to set s390x Genesis configuration permissions: $path: $chmod_error";
}
if ((-e $path || -l $path) && !_is_generated_s390x_config($path)) {
unlink $temporary;
return "Refusing to replace unmanaged s390x configuration: $path";
}
if (!rename $temporary, $path) {
my $rename_error = $OS_ERROR;
unlink $temporary;
return "Unable to install s390x Genesis configuration: $path: $rename_error";
}
return;
}
# Return the grub2-class architectures whose Genesis kernel and initrd are
# published under $tftpdir/xcat, as [arch, grub_cpu, kernel, initrd] with the
# file names relative to the TFTP root.
+1 -1
View File
@@ -1998,7 +1998,7 @@ sub setupLinuxexports
sub _installed_genesis_architectures
{
my ($xcatroot) = @_;
my @supported = qw(aarch64 armv7hf ppc64 ppc64le riscv64 x86 x86_64);
my @supported = qw(aarch64 armv7hf ppc64 ppc64le riscv64 s390x x86 x86_64);
my %supported = map { $_ => 1 } @supported;
my %installed;
@@ -100,10 +100,7 @@ if [ ! -x /usr/bin/wget ]; then
sleep 36500d
fi
# These dispatcher scripts are not needed by the legacy post.xcat path. Newer
# wget parses HTML-looking regex strings inside downloaded scripts and fails the
# whole recursive download on bogus URLs.
wget -l inf -N -r --waitretry=10 --random-wait --retry-connrefused -e robots=off -nH --cut-dirs=2 --reject "index.html*,post.xcat.ng,post.xcat.rhels10" --no-parent -t 20 -T 60 http://${MASTER_IP}:${HTTPPORT}${INSTALLDIR}/postscripts/ -P /xcatpost 2> /tmp/wget.log
xcat_download_postscripts "${MASTER_IP}:${HTTPPORT}" "$INSTALLDIR" "/xcatpost" "/tmp/wget.log"
if [ "$?" != "0" ]; then
msgutil_r "$MASTER_IP" "error" "failed to download postscripts from http://$MASTER_IP$INSTALLDIR/postscripts/,check /tmp/wget.log on the node, halt ..." "/var/log/xcat/xcat.log" "$log_label"
/tmp/updateflag $MASTER $XCATIPORT "installstatus failed"
@@ -191,17 +191,6 @@ if [ -e "/tmp/xcat.install_disk" ]; then
fi
msgutil_r "$MASTER_IP" "info" "Found $instdisk, generate partition file..." "/var/log/xcat/xcat.log" "$log_label"
set_sles11_uefi_bootloader()
{
if grep -E 'install=.*sles11' /proc/cmdline >/dev/null 2>&1; then
# SLES 11 AutoYaST keeps the template's legacy MBR bootloader
# location unless the UEFI path explicitly selects elilo.
sed -i -e '/<lba_support /d' \
-e '/<linear /d' \
-e 's!<location>mbr</location>!<loader_type>elilo</loader_type>!' \
/tmp/profile/modified.xml
fi
}
if [ -d /sys/firmware/efi ]; then
sed -e 's!<device>XCATPARTITIONHOOK</device>!<device>'$instdisk'</device><partitions config:type="list"><partition><filesystem config:type="symbol">vfat</filesystem><mount>/boot/efi</mount><size>128mb</size></partition><partition><mount>swap</mount><size>auto</size></partition><partition><mount>/</mount><size>auto</size></partition></partitions>!' /tmp/profile/autoinst.xml > /tmp/profile/modified.xml
@@ -63,3 +63,27 @@ declare -F xcat_enable_active_nm_autoconnect &>/dev/null || function xcat_enable
nmcli con mod "$con_name" connection.autoconnect yes
done
}
declare -F xcat_download_postscripts &>/dev/null || function xcat_download_postscripts {
local server="$1"
local install_dir="${2:-/install}"
local postroot="${3:-/xcatpost}"
local log_file="${4:-/tmp/wget.log}"
export LANG=C
wget -l inf -N -r --waitretry=10 --random-wait --retry-connrefused -e robots=off -nH --cut-dirs=2 --reject "index.html*,post.xcat.ng,post.xcat.rhels10" --no-parent -t 20 -T 60 "http://${server}${install_dir}/postscripts/" -P "$postroot" 2> "$log_file"
}
declare -F set_sles11_uefi_bootloader &>/dev/null || function set_sles11_uefi_bootloader {
local cmdline="${1:-/proc/cmdline}"
local profile="${2:-/tmp/profile/modified.xml}"
if grep -E 'install=.*sles11' "$cmdline" >/dev/null 2>&1; then
# SLES 11 AutoYaST keeps the template's legacy MBR bootloader
# location unless the UEFI path explicitly selects elilo.
sed -i -e '/<lba_support /d' \
-e '/<linear /d' \
-e 's!<location>mbr</location>!<loader_type>elilo</loader_type>!' \
"$profile"
fi
}
@@ -43,6 +43,7 @@ autoinstall:
- bind9-dnsutils
- chrony
- gpg
- #INCLUDE_DEFAULT_PKGLIST_AUTOINSTALL#
early-commands:
- |
exec >/tmp/pre-install.log 2>&1
@@ -111,6 +112,11 @@ autoinstall:
cp ./#HOSTNAME#.post /target/root/post.script;
curtin in-target --target /target /root/post.script;
} >>/target/var/log/xcat/xcat.log 2>&1'
# The installer's sources for the otherpkgs repository and the pkgdir mirrors, and the apt
# configuration that kept recommended packages out, served the install; ospkgs and otherpkgs
# write their own after the first boot. A separate item, so the status of the post script above
# still decides whether the install goes on.
- rm -f /target/etc/apt/sources.list.d/xcat-otherpkgs-*.list /target/etc/apt/sources.list.d/xcat-otherpkgs-*.sources /target/etc/apt/sources.list.d/xcat-pkgdir-*.list /target/etc/apt/sources.list.d/xcat-pkgdir-*.sources /target/etc/apt/apt.conf.d/94curtin-config
# Flip the node to local-disk boot, or it PXE-loops back into the installer on reboot.
# xcatd's install monitor greets with "ready", then answers "next" with "done" and runs
# "nodeset <node> next". Require both tokens: another service on that port is not a flipped
@@ -0,0 +1,5 @@
openssh-server
chrony
gawk
nfs-common
snmpd
@@ -0,0 +1,5 @@
openssh-server
chrony
gawk
nfs-common
snmpd
@@ -0,0 +1,5 @@
openssh-server
chrony
gawk
nfs-common
snmpd
@@ -0,0 +1,5 @@
openssh-server
chrony
gawk
nfs-common
snmpd
@@ -0,0 +1,15 @@
bash
nfs-common
openssl
isc-dhcp-client
libc-bin
openssh-server
openssh-client
wget
vim
rsync
busybox-static
gawk
bind9-dnsutils
chrony
gpg
@@ -3,8 +3,10 @@ ntp
gawk
nfs-common
snmpd
qemu-kvm
libvirt-bin
qemu-system
qemu-utils
libvirt-daemon-system
libvirt-clients
bridge-utils
libcap2-bin
vlan
@@ -0,0 +1,13 @@
openssh-server
ntp
gawk
nfs-common
snmpd
qemu-system-ppc
qemu-utils
libvirt-daemon-system
libvirt-clients
bridge-utils
libcap2-bin
vlan
tmux
@@ -0,0 +1 @@
kvm.ppc64el.pkglist
@@ -0,0 +1,11 @@
openssh-server
ntp
gawk
nfs-common
snmpd
qemu-kvm
libvirt-bin
bridge-utils
libcap2-bin
vlan
tmux
@@ -0,0 +1,11 @@
openssh-server
ntp
gawk
nfs-common
snmpd
qemu-kvm
libvirt-bin
bridge-utils
libcap2-bin
vlan
tmux
@@ -0,0 +1,11 @@
openssh-server
ntp
gawk
nfs-common
snmpd
qemu-kvm
libvirt-bin
bridge-utils
libcap2-bin
vlan
tmux
@@ -0,0 +1,13 @@
openssh-server
ntp
gawk
nfs-common
snmpd
qemu-system-misc
qemu-utils
libvirt-daemon-system
libvirt-clients
bridge-utils
libcap2-bin
vlan
tmux
@@ -0,0 +1,13 @@
openssh-server
chrony
gawk
nfs-common
snmpd
qemu-system
qemu-utils
libvirt-daemon-system
libvirt-clients
bridge-utils
libcap2-bin
vlan
tmux
@@ -0,0 +1,13 @@
openssh-server
chrony
gawk
nfs-common
snmpd
qemu-system-ppc
qemu-utils
libvirt-daemon-system
libvirt-clients
bridge-utils
libcap2-bin
vlan
tmux
@@ -0,0 +1 @@
kvm.ubuntu26.04.ppc64el.pkglist
@@ -0,0 +1,13 @@
openssh-server
chrony
gawk
nfs-common
snmpd
qemu-system-x86
qemu-utils
libvirt-daemon-system
libvirt-clients
bridge-utils
libcap2-bin
vlan
tmux
@@ -0,0 +1,13 @@
openssh-server
ntp
gawk
nfs-common
snmpd
qemu-system-x86
qemu-utils
libvirt-daemon-system
libvirt-clients
bridge-utils
libcap2-bin
vlan
tmux
@@ -4,5 +4,5 @@ gawk
nfs-common
snmpd
libdbd-mysql-perl
libodbc1
libdbd-pg-perl
unixodbc
@@ -0,0 +1,8 @@
openssh-server
chrony
gawk
nfs-common
snmpd
libdbd-mysql-perl
libdbd-pg-perl
unixodbc
@@ -278,6 +278,7 @@ sub default_net_drivers {
x86_64 => [qw(tg3 bnx2 bnx2x e1000 e1000e igb mlx_en mlx5_core virtio_net overlay)],
ppc64el => [qw(tg3 bnx2 bnx2x e1000 e1000e igb ibmveth ehea mlx_en mlx4_en mlx5_core virtio_net overlay)],
ppc64 => [qw(e1000 e1000e igb ibmveth ehea)],
riscv64 => [qw(e1000 e1000e igb ixgbe r8169 tg3 bnx2x mlx5_core virtio_net overlay)],
s390x => [qw(qdio ccwgroup)],
},
);
@@ -0,0 +1,22 @@
bash
nfs-common
openssl
isc-dhcp-client
libc-bin
linux-image-generic
openssh-server
openssh-client
wget
rsync
busybox-static
gawk
bind9-dnsutils
tar
gzip
xz-utils
cpio
chrony
util-linux-extra
iproute2
dracut
dracut-network
@@ -0,0 +1,17 @@
bash
nfs-common
openssl
isc-dhcp-client
linux-image-generic
openssh-server
openssh-client
wget
vim
chrony
rsyslog
rsync
busybox-static
gawk
tar
gzip
xz-utils
@@ -0,0 +1,18 @@
bash
nfs-common
openssl
isc-dhcp-client
libc-bin
linux-image-generic
openssh-server
openssh-client
wget
rsync
busybox-static
gawk
bind9-dnsutils
tar
gzip
xz-utils
cpio
chrony
+11 -1
View File
@@ -259,8 +259,13 @@ unless ($onlyinitrd) {
# site.ubuntu_apt_mirror overrides; otherwise default to the public archive. A live-server
# ISO is never a complete debootstrap source, so a real mirror is always required here.
my @aptmirror = xCAT::TableUtils->get_site_attribute("ubuntu_apt_mirror");
# archive.ubuntu.com publishes amd64 and i386 only. Every other architecture, ppc64el
# and riscv64 included, is on the ports archive.
my $default = ($uarch =~ /^(?:amd64|i386)$/)
? 'http://archive.ubuntu.com/ubuntu'
: 'http://ports.ubuntu.com/ubuntu-ports';
my $mirror = (defined $aptmirror[0] && length $aptmirror[0])
? $aptmirror[0] : 'http://archive.ubuntu.com/ubuntu';
? $aptmirror[0] : $default;
(my $codename = $osver) =~ s/^ubuntu//;
# Strip ONLY a trailing third component. A bare s/\.\d+$// also strips the minor from a
# two-part osvers (ubuntu26.04 -> "26"), which misses the %cn map below and reaches
@@ -1786,6 +1791,11 @@ EOMS
push @filestoadd, $_ if (-e "$rootimg_dir/$_");
}
} elsif ($arch =~ /riscv64/) {
foreach ("lib/riscv64-linux-gnu/libnss_files.so.2", "lib/riscv64-linux-gnu/libnss_dns.so.2") {
push @filestoadd, $_ if (-e "$rootimg_dir/$_");
}
} else {
push @filestoadd, "lib/libnss_dns.so.2" if (-e "$rootimg_dir/lib/libnss_dns.so.2");
}
+65 -24
View File
@@ -1,5 +1,12 @@
#!/usr/bin/perl
BEGIN {
$::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
}
use lib "$::XCATROOT/lib/perl";
use xCAT::CommandUtils;
use IO::Socket::INET;
use File::Temp qw(tempfile);
use POSIX qw(_exit sigprocmask WNOHANG SIG_BLOCK SIG_SETMASK SIGINT SIGTERM);
use Time::HiRes qw(gettimeofday sleep);
use Getopt::Long;
Getopt::Long::Configure("bundling");
@@ -27,7 +34,8 @@ if (!GetOptions(
if ($::HELP) { print $::USAGE; exit 0; }
unless (-x "/usr/sbin/tcpdump") {
my $tcpdump = xCAT::CommandUtils::find_executable('tcpdump');
unless ($tcpdump) {
print "Error: Please install tcpdump before the detecting.\n";
exit 1;
}
@@ -86,16 +94,25 @@ if (-f "/etc/redhat-release") {
}
# fork a process to capture the packet by tcpdump
my ($dumpfh, $dumpfile) = tempfile("detect_dhcpd.XXXXXX", TMPDIR => 1, UNLINK => 1);
close($dumpfh);
# INT and TERM stay blocked from the fork until the handler that stops the capture is in place.
my $stop_signals = POSIX::SigSet->new(SIGINT, SIGTERM);
my $signal_mask = POSIX::SigSet->new();
sigprocmask(SIG_BLOCK, $stop_signals, $signal_mask);
my $pid = fork;
if (!defined $pid) { print "Fork failed.\n"; exit 1; }
my $dumpfile = "/tmp/dhcpdumpfile.log";
if (!defined $pid) { sigprocmask(SIG_SETMASK, $signal_mask); print "Fork failed.\n"; exit 1; }
if ($pid == 0) {
# Child process
my $cmd = "tcpdump -i $IF port 68 -n -vvvvvv > $dumpfile 2>/dev/null";
`$cmd`;
exit 0;
# Child process: tcpdump itself, so the parent owns exactly this pid.
sigprocmask(SIG_SETMASK, $signal_mask);
open(STDOUT, '>', $dumpfile) or _exit(1);
open(STDERR, '>', '/dev/null');
exec($tcpdump, '-i', $nic, 'port', '68', '-n', '-vvvvvv');
_exit(1);
}
$SIG{INT} = $SIG{TERM} = sub { kill_child(); exit 1; };
sigprocmask(SIG_SETMASK, $signal_mask);
# generate the discover package
my $package = packdhcppkg($MAC);
@@ -135,25 +152,24 @@ if ($::TIMEOUT) {
my $end = Time::HiRes::gettimeofday();
$end =~ s/(\d.*)\.(\d.*)/$1/;
while ($end - $start <= $timeout) {
$sock->send($package) or die "Send discover error: $@\n";
unless ($sock->send($package)) {
print "Send discover error: $!\n";
kill_child();
exit 1;
}
sleep 2;
$end = Time::HiRes::gettimeofday();
$end =~ s/(\d.*)\.(\d.*)/$1/;
}
kill_child();
#kill the child process
kill 15, $pid;
my @pidoftcpdump = `ps -ef | grep -E "[0-9]+:[0-9]+:[0-9]+ tcpdump -i $IF" | awk -F' ' '{print \$2}'`;
foreach my $cpid (@pidoftcpdump) {
kill 15, $cpid;
# print "try to kill $cpid\n";
my $capture_problem = kill_child();
if ($capture_problem) {
print "tcpdump $capture_problem.\n";
exit 1;
}
sleep 2;
open(FILE, "<$dumpfile") or die "Cannot open $dumpfile\n";
my %output;
my @snack = ();
@@ -233,7 +249,6 @@ if (scalar(@server)) {
}
}
#`rm -f $dumpfile`;
exit 0;
@@ -339,12 +354,38 @@ sub packdhcppkg {
return $package;
}
# Stop the capture and reap it, once: INT and TERM are blocked while the pid is taken and until
# tcpdump is reaped, so an interrupt in that window cannot orphan it or reach a recycled pid. The
# handlers stay installed, so a later signal still leaves through exit and the END cleanup.
# Returns '' when tcpdump ran until this TERM and stopped cleanly, otherwise what went wrong: it
# ended on its own, ignored TERM and was killed, or left on another signal or with a non-zero
# status.
sub kill_child {
kill 15, $pid;
my @pidoftcpdump = `ps -ef | grep -E "[0-9]+:[0-9]+:[0-9]+ tcpdump -i $IF" | awk -F' ' '{print \$2}'`;
foreach my $cpid (@pidoftcpdump) {
kill 15, $cpid;
#print "try to kill $cpid\n";
sigprocmask(SIG_BLOCK, $stop_signals);
my $child = $pid;
$pid = undef;
unless ($child) {
sigprocmask(SIG_SETMASK, $signal_mask);
return '';
}
my $reaped = waitpid($child, WNOHANG);
my $early = ($reaped == $child) ? 1 : 0;
if ($reaped == 0) {
kill 'TERM', $child;
foreach (1 .. 50) {
last if ($reaped = waitpid($child, WNOHANG)) != 0;
select(undef, undef, undef, 0.1);
}
if ($reaped == 0) {
kill 'KILL', $child;
$reaped = waitpid($child, 0);
}
}
sigprocmask(SIG_SETMASK, $signal_mask);
return "could not be reaped" if $reaped != $child;
my ($signal, $status) = ($? & 127, $? >> 8);
my $how = $signal ? "on signal $signal" : "with status $status";
return "ended before the capture window did, $how" if $early;
return '' if $signal == 15 || (!$signal && !$status);
return "left the capture $how";
}
+74 -37
View File
@@ -43,7 +43,7 @@
# 2022-11-01 Mark Gurevich <gurevich@us.ibm.com>
# - Make sure initscripts installed on RH family of OSes
# 2022-12-06 Mark Gurevich <gurevich@us.ibm.com>
# - Check for EPEL and CRB repository on EL9 family of OSes
# - Check for EPEL and CRB repositories on EL9 and EL10 family of OSes
# 2023-01-19 Mark Gurevich <gurevich@us.ibm.com>
# - Add support for Alma Linux
# 2026-07-20 Daniel Hilst <392820+dhilst@users.noreply.github.com>
@@ -208,7 +208,7 @@ GO_XCAT_UNINSTALL_LIST=("${GO_XCAT_INSTALL_LIST[@]}"
xCAT-genesis-openembedded-x86 xCAT-genesis-openembedded-x86_64
xCAT-genesis-openembedded-ppc64 xCAT-genesis-openembedded-ppc64le
xCAT-genesis-openembedded-armv7hf xCAT-genesis-openembedded-aarch64
xCAT-genesis-openembedded-riscv64
xCAT-genesis-openembedded-riscv64 xCAT-genesis-openembedded-s390x
xCAT-openbmc-py xCAT-probe xCAT-test xCAT-vlan xCATsn xCAT-UI-deps
xCAT-buildkit conserver-xcat yaboot-xcat)
# For Debian/Ubuntu, it will need a slightly different package list
@@ -218,13 +218,13 @@ GO_XCAT_UNINSTALL_LIST=("${GO_XCAT_INSTALL_LIST[@]}"
xcat-genesis-openembedded-x86 xcat-genesis-openembedded-x86-64
xcat-genesis-openembedded-ppc64 xcat-genesis-openembedded-ppc64le
xcat-genesis-openembedded-armv7hf xcat-genesis-openembedded-aarch64
xcat-genesis-openembedded-riscv64
xcat-genesis-openembedded-riscv64 xcat-genesis-openembedded-s390x
xcat-buildkit conserver-xcat)
PATH="/usr/sbin:/usr/bin:/sbin:/bin"
export PATH
EL9_EPEL_TEST_RPM="perl-Crypt-CBC"
EL9_CRB_TEST_RPM="perl-IO-Tty"
EL_EPEL_TEST_RPM="perl-Crypt-CBC"
EL_CRB_TEST_RPM="perl-IO-Tty"
#
# warn_if_bad Put out warning message(s) if $1 has bad RC.
@@ -1815,7 +1815,7 @@ function update_repo()
function install_packages_dnf()
{
type dnf >/dev/null 2>&1 || return 255
el9_epel_and_crb_check dnf
el_epel_and_crb_check dnf
local -a yes=()
[[ "$1" = "-y" ]] && yes=("-y") && shift
dnf --nogpgcheck "${yes[@]}" install initscripts
@@ -1825,7 +1825,7 @@ function install_packages_dnf()
function install_packages_yum()
{
type yum >/dev/null 2>&1 || return 255
el9_epel_and_crb_check yum
el_epel_and_crb_check yum
local -a yes=()
[[ "$1" = "-y" ]] && yes=("-y") && shift
yum --nogpgcheck "${yes[@]}" install initscripts
@@ -1850,10 +1850,31 @@ function github_issue_6525_workaround()
return 0
}
# Check for EPEL and CRB repositories when installing on RH family of OSes
function el9_epel_and_crb_check()
# Whether an enabled repository carries the package for this architecture. An
# installed copy and a source repository do not count, so a binary repository
# that was disabled is still reported. A failed query stops the run with the
# package manager's own error.
function repo_carries()
{
local rpm="$1"
local found errors
errors="$(mktemp)"
found="$(${action} repoquery -q --arch "${GO_XCAT_ARCH},noarch" --qf '%{name}' "${rpm}" 2>"${errors}")" || {
cat "${errors}"
rm -f "${errors}"
echo "Could not query the package repositories with '${action} repoquery'"
exit 1
}
rm -f "${errors}"
grep -Fxq -- "${rpm}" <<<"${found}"
}
# Check for EPEL and CRB repositories when installing on RH family of OSes.
# CentOS Stream reports the major version alone, the others report major.minor.
function el_epel_and_crb_check()
{
action="$*" # Passed parameter will only be 'yum' or 'dnf'
local major
if [[ "${GO_XCAT_LINUX_DISTRO}" = "fedora" ]]
then
# For Fedora, version 35 is equivalent to EL9
@@ -1862,40 +1883,24 @@ function el9_epel_and_crb_check()
# found
return 0
else
[[ "${GO_XCAT_LINUX_VERSION}" =~ ^9(\.[0-9]) ]] || return 0
[[ "${GO_XCAT_LINUX_VERSION}" =~ ^(9|10)(\.[0-9]+)?$ ]] || return 0
fi
${action} list -q "${EL9_EPEL_TEST_RPM}"
ret="$?"
case "${ret}" in
"1")
# Can not find EL9_EPEL_TEST_RPM
major="${GO_XCAT_LINUX_VERSION%%.*}"
if ! repo_carries "${EL_EPEL_TEST_RPM}"
then
echo "
Installation on ${GO_XCAT_LINUX_DISTRO} ${GO_XCAT_LINUX_VERSION} requires EPEL repository to be enabled"
echo "Running '${action} install https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm' will enable EPEL repository"
echo "Running '${action} install https://dl.fedoraproject.org/pub/epel/epel-release-latest-${major}.noarch.rpm' will enable EPEL repository"
exit 1
;;
esac
${action} list -q "${EL9_CRB_TEST_RPM}"
ret="$?"
case "${ret}" in
"1")
# Can not find EL9_CRB_TEST_RPM
fi
if ! repo_carries "${EL_CRB_TEST_RPM}"
then
echo "
Installation on ${GO_XCAT_LINUX_DISTRO} ${GO_XCAT_LINUX_VERSION} requires CRB repository to be enabled"
echo "Try adding the following entries to new or existing '.repo' file:"
echo "
[crb]
name=CentOS Stream \$releasever - CRB
metalink=https://mirrors.centos.org/metalink?repo=centos-crb-\$stream&arch=\$basearch&protocol=https,http
gpgcheck=0
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=1
"
echo "Running '${action} update epel-release' and then 'crb enable' will enable it on Red Hat Enterprise Linux, Rocky Linux, AlmaLinux and CentOS Stream"
echo "On Oracle Linux, running 'dnf config-manager --enable ol${major}_codeready_builder' will enable it"
exit 1
;;
esac
fi
return 0
}
@@ -2456,13 +2461,45 @@ case "${GO_XCAT_OS}" in
esac
case "${GO_XCAT_ARCH}" in
"ppc64"|"ppc64le"|"x86_64")
"ppc64"|"ppc64le"|"riscv64"|"x86_64")
;;
*)
exit_if_bad 1 "${GO_XCAT_ARCH}: unsupported instruction set architecture"
;;
esac
# A riscv64 management node has no legacy Genesis: its image ships as the OpenEmbedded package,
# which mknb installs. The legacy Genesis scripts and bases are therefore dropped and the
# OpenEmbedded package of the architecture is asked for instead.
#
# The x86 boot loaders stay: they are payload a management node SERVES to x86 nodes over TFTP,
# not host binaries, so a riscv64 management node needs them to boot a mixed cluster.
function riscv64_install_list()
{
local genesis_package="xCAT-genesis-openembedded-riscv64"
type dpkg >/dev/null 2>&1 && genesis_package="xcat-genesis-openembedded-riscv64"
local package
for package in "$@"; do
case "${package}" in
*genesis-scripts-*|*genesis-base-*)
;;
*)
printf '%s\n' "${package}"
;;
esac
done
printf '%s\n' "${genesis_package}"
}
if [ "${GO_XCAT_ARCH}" = "riscv64" ]; then
riscv64_list=()
while read -r package; do
riscv64_list+=("${package}")
done < <(riscv64_install_list "${GO_XCAT_INSTALL_LIST[@]}")
GO_XCAT_INSTALL_LIST=("${riscv64_list[@]}")
unset riscv64_list package
fi
GO_XCAT_LINUX_DISTRO="$(check_linux_distro)"
GO_XCAT_LINUX_VERSION="$(check_linux_version)"
+21
View File
@@ -0,0 +1,21 @@
# xCAT-test
Unit tests that run from the source checkout are split by implementation
language:
| Test type | Location | Runner |
| --------- | -------- | ------ |
| Perl unit tests | `xCAT-test/unit/*.t` | `prove -r xCAT-test/unit` |
| Shell unit tests | `xCAT-test/bats/*.bats` | `bats -r xCAT-test/bats` |
| CLI functional tests | `xCAT-test/autotest/testcase/` and `xCAT-test/autotest/bundle/` | `xcattest -f <cluster.conf> -t <case>` or `xcattest -f <cluster.conf> -b <bundle>` |
Use Perl `.t` tests for Perl modules, Perl scripts, templates, and repository
artifacts. Use BATS tests for shell-script behavior that can be exercised from
the checkout by sourcing a shell library or script and shadowing external
commands.
Shell behavior should not be tested by Perl tests that grep shell source. Put
those tests under `xCAT-test/bats` instead.
See `unit/README.md` and `bats/README.md` for the detailed rules for
each unit-test suite.
@@ -4,5 +4,5 @@ os:Linux
label:mn_only,ci_test,integration
cmd:prove -I/opt/xcat/lib/perl -I/opt/xcat/lib/perl/xCAT -r /opt/xcat/share/xcat/tools/autotest/integration
check:rc==0
check:output=~Files=4
check:output=~Files=5,
end
+21
View File
@@ -0,0 +1,21 @@
# xCAT-test/bats
Shell-script unit tests live here and run with:
```bash
bats -r xCAT-test/bats
```
The GitHub Actions `xcat_test` workflow runs this command after the Perl `.t`
unit tests. Use BATS for shell behavior that can be exercised from the source
tree without an installed xCAT, a live management node, or real services.
Prefer sourcing an existing shell library or sourceable script and calling the
function under test. Keep reusable install-template helpers in
`xCAT-server/share/xcat/install/scripts/scriptlib`, and reusable postscript
helpers in `xCAT/postscripts/xcatlib.sh`. Use scratch directories and shadowed
commands so tests cannot write to the host.
Extraction helpers in `helpers/shell_source.bash` are only for legacy code that
cannot safely be sourced yet. Do not add Perl `.t` tests that grep shell source
when the behavior can be tested with BATS.

Some files were not shown because too many files have changed in this diff Show More