mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-09-09 14:36:43 +00:00
Merge pull request #7821 from VersatusHPC/feature/ubuntu-riscv64
feat(ubuntu): support riscv64 management and provisioned nodes on 24.04 and 26.04
This commit is contained in:
@@ -147,16 +147,22 @@ 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.
|
||||
my %NO_PPC64EL = map { $_ => 1 } qw(saucy);
|
||||
|
||||
my @DEB_ARCHES = qw(amd64 ppc64el);
|
||||
my @DEB_ARCHES = qw(amd64 ppc64el riscv64);
|
||||
|
||||
# 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.
|
||||
@@ -323,8 +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 @DEB_ARCHES if $ARCH_PACKAGES{$package // ''};
|
||||
return ('all');
|
||||
my $arches = $ARCH_PACKAGES{ $package // '' };
|
||||
return $arches ? @{$arches} : ('all');
|
||||
}
|
||||
|
||||
# dist_arches: the architectures a release's apt repo declares.
|
||||
|
||||
+5
-5
@@ -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`.
|
||||
|
||||
@@ -60,7 +60,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``.
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.',
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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, 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.
|
||||
|
||||
@@ -364,7 +364,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})/eg;
|
||||
}
|
||||
$inc =~ s/#WRITEREPO#/$writerepo/g;
|
||||
}
|
||||
@@ -377,7 +377,7 @@ 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})/eg;
|
||||
$inc =~ s/#SUBIQUITYINSTALLNIC#/subiquity_install_nic()/eg;
|
||||
$inc =~ s/#SUBIQUITYINSTALLMAC#/subiquity_install_mac()/eg;
|
||||
$inc =~ s/#MACHINEPASSWORD#/machinepassword()/eg;
|
||||
@@ -1766,11 +1766,20 @@ sub subiquity_install_mac {
|
||||
|
||||
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');
|
||||
@@ -1779,11 +1788,11 @@ sub ubuntu_subiquity_apt_mirror
|
||||
|
||||
sub ubuntu_subiquity_apt_config
|
||||
{
|
||||
my ($media_dir) = @_;
|
||||
my ($media_dir, $osarch) = @_;
|
||||
my $use_deb822 = ubuntu_subiquity_uses_deb822_sources($media_dir);
|
||||
my @otherpkg_sources = ubuntu_subiquity_otherpkg_sources();
|
||||
|
||||
my $online_mirror = ubuntu_subiquity_apt_mirror();
|
||||
my $online_mirror = ubuntu_subiquity_apt_mirror($osarch);
|
||||
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
|
||||
|
||||
@@ -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;
|
||||
@@ -868,18 +1042,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 +1122,8 @@ sub mkinstall {
|
||||
$pkgdir,
|
||||
$platform,
|
||||
$partitionfile,
|
||||
\%tmpl_hash
|
||||
\%tmpl_hash,
|
||||
osarch => $arch
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -907,9 +907,9 @@ 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 && !$s390x_config_error) {
|
||||
$callback->({ data => ["Write netboot config file done"] });
|
||||
|
||||
@@ -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
|
||||
@@ -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,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
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -2461,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)"
|
||||
|
||||
|
||||
@@ -60,15 +60,19 @@ is_deeply( [deb_package_arches('perl-xCAT')], ['all'],
|
||||
'a Perl package is built once, arch-independent' );
|
||||
is_deeply( [deb_package_arches('xCAT-probe')], ['all'],
|
||||
'xCAT-probe is arch-independent too' );
|
||||
for my $pkg (qw(xCAT xCATsn xCAT-genesis-scripts)) {
|
||||
is_deeply( [deb_package_arches($pkg)], ['amd64', 'ppc64el'],
|
||||
"$pkg is built per architecture" );
|
||||
for my $pkg (qw(xCAT xCATsn)) {
|
||||
is_deeply( [deb_package_arches($pkg)], ['amd64', 'ppc64el', 'riscv64'],
|
||||
"$pkg is built for every architecture a management node runs on" );
|
||||
}
|
||||
# xCAT-genesis-scripts has one control file per architecture and there is no riscv64 one,
|
||||
# so asking for that build would stop the whole run.
|
||||
is_deeply( [deb_package_arches('xCAT-genesis-scripts')], ['amd64', 'ppc64el'],
|
||||
'xCAT-genesis-scripts is built only for the architectures it has a control file for' );
|
||||
is_deeply( [deb_package_arches(undef)], ['all'],
|
||||
'an undefined package name does not blow up the arch lookup' );
|
||||
|
||||
is_deeply( [dist_arches('noble')], ['amd64', 'ppc64el'],
|
||||
'a current release serves both architectures' );
|
||||
is_deeply( [dist_arches('noble')], ['amd64', 'ppc64el', 'riscv64'],
|
||||
'a current release serves every architecture' );
|
||||
is_deeply( [dist_arches('saucy')], ['amd64'],
|
||||
'saucy predates ppc64el and serves only amd64' );
|
||||
|
||||
@@ -151,8 +155,8 @@ is( scalar( () = $rewritten =~ /^ -- xCAT Build /mg ), 1,
|
||||
|
||||
my $dists = reprepro_distributions([qw(focal noble)], 'DEADBEEF');
|
||||
is( scalar(() = $dists =~ /^Codename:/mg), 2, 'one stanza per release' );
|
||||
like( $dists, qr/^Codename: focal\nArchitectures: amd64 ppc64el$/m,
|
||||
'a release declares both architectures, on the line after its codename' );
|
||||
like( $dists, qr/^Codename: focal\nArchitectures: amd64 ppc64el riscv64$/m,
|
||||
'a release declares every architecture, on the line after its codename' );
|
||||
is( scalar(() = $dists =~ /^SignWith: DEADBEEF$/mg), 2,
|
||||
'every stanza is signed when a key is given' );
|
||||
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use File::Spec;
|
||||
use File::Temp qw(tempdir);
|
||||
use FindBin;
|
||||
use Test::More;
|
||||
|
||||
# A riscv64 management node needs an xcat and xcatsn deb built for the architecture and a
|
||||
# mklocalrepo.sh that points the host at the matching repository instead of amd64.
|
||||
#
|
||||
# The generated script is extracted from builddebs.pl and run with a stub uname ahead of
|
||||
# $PATH, so the mapping under test is the shipped code. Only the path it writes is
|
||||
# redirected into the sandbox, because it writes an apt source list.
|
||||
|
||||
my $repo_root = File::Spec->rel2abs( File::Spec->catdir( $FindBin::Bin, '..', '..' ) );
|
||||
my $builder = File::Spec->catfile( $repo_root, 'builddebs.pl' );
|
||||
plan skip_all => "builddebs.pl not found" unless -f $builder;
|
||||
|
||||
my $src = do { local $/; open my $fh, '<', $builder or die $!; <$fh> };
|
||||
|
||||
# BAIL_OUT rather than skip: a rename that stops this matching must fail loudly instead of
|
||||
# silently covering nothing.
|
||||
my ($script) = $src =~ /write_script\("\$repodir\/mklocalrepo\.sh", <<'SCRIPT'\);\n(.*?)\nSCRIPT\n/ms;
|
||||
BAIL_OUT('could not extract mklocalrepo.sh from builddebs.pl') unless defined $script;
|
||||
|
||||
my $dir = tempdir( CLEANUP => 1 );
|
||||
my $run = 0;
|
||||
|
||||
# Run the generated script for one host architecture and return the apt source line it wrote.
|
||||
sub sources_line_for {
|
||||
my ($uname) = @_;
|
||||
$run++;
|
||||
my $root = File::Spec->catdir( $dir, "run$run" );
|
||||
mkdir $root;
|
||||
mkdir "$root/bin";
|
||||
|
||||
open( my $stub, '>', "$root/bin/uname" ) or die $!;
|
||||
print {$stub} "#!/bin/bash\necho $uname\n";
|
||||
close($stub);
|
||||
chmod 0755, "$root/bin/uname";
|
||||
|
||||
my $release = File::Spec->catfile( $root, 'lsb-release' );
|
||||
open( my $rel, '>', $release ) or die $!;
|
||||
print {$rel} "DISTRIB_CODENAME=noble\n";
|
||||
close($rel);
|
||||
|
||||
my $listed = File::Spec->catfile( $root, 'sources.list' );
|
||||
( my $sandboxed = $script ) =~ s{/etc/lsb-release}{$release};
|
||||
$sandboxed =~ s{/etc/apt/sources\.list\.d/\S+}{$listed};
|
||||
|
||||
my $harness = File::Spec->catfile( $root, 'harness.sh' );
|
||||
open( my $fh, '>', $harness ) or die $!;
|
||||
print {$fh} "#!/bin/bash\n$sandboxed\n";
|
||||
close($fh);
|
||||
|
||||
local $ENV{PATH} = "$root/bin:$ENV{PATH}";
|
||||
system( '/bin/bash', $harness );
|
||||
open( my $out, '<', $listed ) or die $!;
|
||||
my $line = do { local $/; <$out> };
|
||||
close($out);
|
||||
return $line;
|
||||
}
|
||||
|
||||
for my $case (
|
||||
[ 'riscv64', 'riscv64' ],
|
||||
[ 'ppc64le', 'ppc64el' ],
|
||||
[ 'x86_64', 'amd64' ],
|
||||
)
|
||||
{
|
||||
my ( $uname, $want ) = @$case;
|
||||
like( sources_line_for($uname), qr/^deb \[arch=\Q$want\E\] /,
|
||||
"mklocalrepo.sh gives a $uname host the $want repository" );
|
||||
}
|
||||
|
||||
# The debs themselves must exist for the architecture.
|
||||
for my $case ( [ 'xCAT', 'xcat' ], [ 'xCATsn', 'xcatsn' ] ) {
|
||||
my ( $component, $package ) = @$case;
|
||||
my $control = File::Spec->catfile( $repo_root, $component, 'debian', 'control' );
|
||||
my $text = do { local $/; open my $fh, '<', $control or die $!; <$fh> };
|
||||
my ($arches) = $text =~ /^Package: \Q$package\E\nArchitecture: (.*)$/m;
|
||||
ok( defined $arches, "$package declares an architecture" );
|
||||
like( $arches || '', qr/\briscv64\b/, "$package is built for riscv64" );
|
||||
like( $arches || '', qr/\bamd64\b.*\bppc64el\b/, "$package keeps amd64 and ppc64el" );
|
||||
}
|
||||
|
||||
done_testing();
|
||||
@@ -16,6 +16,11 @@ use xCAT::Utils;
|
||||
# --- what debootstrap and the package lists are given ----------------------
|
||||
is(xCAT::Utils->debian_arch('x86_64'), 'amd64',
|
||||
'Debian calls x86_64 amd64');
|
||||
# genimage passes this value to debootstrap --arch and reads it to pick the apt mirror, and
|
||||
# debootstrap knows i386, not xCAT's x86.
|
||||
is(xCAT::Utils->debian_arch('x86'), 'i386',
|
||||
'debian_arch: the 32-bit x86 token becomes i386');
|
||||
|
||||
is(xCAT::Utils->debian_arch('ppc64el'), 'ppc64el',
|
||||
'the Debian name for POWER LE is unchanged');
|
||||
is(xCAT::Utils->debian_arch('ppc64le'), 'ppc64le',
|
||||
@@ -38,6 +43,10 @@ is(xCAT::Utils->xcat_arch_from_debian('ppc64el'), 'ppc64el',
|
||||
'POWER LE media keeps the Debian name xCAT uses for Ubuntu');
|
||||
is(xCAT::Utils->xcat_arch_from_debian('powerpc'), 'ppc64',
|
||||
'POWER BE media installs ppc64 nodes');
|
||||
is(xCAT::Utils->xcat_arch_from_debian('riscv64'), 'riscv64',
|
||||
'riscv64 media installs riscv64 nodes');
|
||||
is(xCAT::Utils->debian_arch('riscv64'), 'riscv64',
|
||||
'Debian and xCAT agree on the riscv64 name');
|
||||
is(xCAT::Utils->xcat_arch_from_debian('nonesuch'), undef,
|
||||
'media xCAT has no name for resolves to nothing');
|
||||
is(xCAT::Utils->xcat_arch_from_debian(''), undef,
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use File::Path qw(make_path);
|
||||
use File::Temp qw(tempdir);
|
||||
use FindBin;
|
||||
use Test::More;
|
||||
|
||||
# mkinstall logged "Unknown arch" for every architecture other than x86_64, x86, ppc64le and
|
||||
# ppc64el, so each diskful riscv64 install produced a false message. install_darch names the
|
||||
# architectures xCAT installs Ubuntu on and the Debian name each one maps to.
|
||||
|
||||
# xCAT modules put $::XCATROOT/lib/perl ahead of @INC as they compile, so on a host with xCAT
|
||||
# installed the modules loaded after the first one would come from /opt/xcat. XCATROOT points
|
||||
# at this checkout before any of them compiles.
|
||||
BEGIN {
|
||||
my $root = tempdir( CLEANUP => 1 );
|
||||
make_path("$root/lib");
|
||||
symlink( "$FindBin::Bin/../../perl-xCAT", "$root/lib/perl" ) or die "symlink: $!";
|
||||
$ENV{XCATROOT} = $root;
|
||||
}
|
||||
|
||||
BEGIN {
|
||||
package xCAT::TableUtils;
|
||||
our $tftpdir;
|
||||
sub getTftpDir { return $tftpdir; }
|
||||
$INC{'xCAT/TableUtils.pm'} = __FILE__;
|
||||
}
|
||||
|
||||
use lib "$FindBin::Bin/../../perl-xCAT";
|
||||
use lib "$FindBin::Bin/../../xCAT-server/lib/perl";
|
||||
my $plugin = "$FindBin::Bin/../../xCAT-server/lib/xcat/plugins/debian.pm";
|
||||
require $plugin;
|
||||
|
||||
my @cases = (
|
||||
# arch Debian name installs Ubuntu
|
||||
[ 'x86_64', 'amd64', 1 ],
|
||||
[ 'x86', 'i386', 1 ],
|
||||
[ 'ppc64le', 'ppc64le', 1 ],
|
||||
[ 'ppc64el', 'ppc64el', 1 ],
|
||||
[ 'riscv64', 'riscv64', 1 ],
|
||||
[ 'aarch64', 'aarch64', 0 ],
|
||||
[ 'sparc', 'sparc', 0 ],
|
||||
);
|
||||
foreach my $case (@cases) {
|
||||
my ( $arch, $darch, $known ) = @$case;
|
||||
my ( $got_darch, $got_known ) = xCAT_plugin::debian::install_darch($arch);
|
||||
is( $got_darch, $darch, "$arch installs from the $darch tree" );
|
||||
is( $got_known, $known, $known ? "... and is an architecture xCAT installs Ubuntu on" : "... and is reported as unknown" );
|
||||
}
|
||||
|
||||
done_testing();
|
||||
@@ -91,6 +91,18 @@ is(
|
||||
'POWER does not accept the x86 live layout',
|
||||
);
|
||||
|
||||
# --- riscv64 -------------------------------------------------------------
|
||||
is(
|
||||
resolved('riscv64', 'riscv64', media('casper/vmlinux', 'casper/initrd')),
|
||||
'casper/vmlinux|casper/initrd',
|
||||
'the riscv64 live image keeps its kernel under a different name',
|
||||
);
|
||||
is(
|
||||
resolved('riscv64', 'riscv64', media('casper/vmlinuz', 'casper/initrd')),
|
||||
undef,
|
||||
'riscv64 does not accept the kernel name the other live images use',
|
||||
);
|
||||
|
||||
# --- nothing to boot -------------------------------------------------------
|
||||
is(resolved('x86_64', 'amd64', media('casper/vmlinuz')), undef,
|
||||
'a kernel without its initrd is not a match');
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use File::Temp qw(tempdir);
|
||||
use FindBin;
|
||||
use Test::More;
|
||||
|
||||
# go-xcat installs a fixed package list. On riscv64 that list asked for the amd64 and ppc64
|
||||
# Genesis scripts and bases, so the supported install path put the x86 Genesis on a riscv64
|
||||
# management node even though the xcat metapackage excludes it there.
|
||||
#
|
||||
# The x86 boot loaders are a different matter: they are payload the management node serves to x86
|
||||
# nodes, so they belong on a riscv64 management node of a mixed cluster and must survive the filter.
|
||||
#
|
||||
# The function that builds the riscv64 list is taken from the shipped script and run, so the
|
||||
# assertions read the package names go-xcat would hand to the package manager.
|
||||
|
||||
my $go_xcat = "$FindBin::Bin/../../xCAT-server/share/xcat/tools/go-xcat";
|
||||
plan skip_all => 'go-xcat not found' unless -r $go_xcat;
|
||||
|
||||
my $tmpdir = tempdir( CLEANUP => 1 );
|
||||
my $driver = "$tmpdir/driver.sh";
|
||||
open( my $fh, '>', $driver ) or die "open $driver: $!";
|
||||
print {$fh} <<'DRIVER';
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
eval "$(awk '
|
||||
$0 == "function riscv64_install_list()" { copy = 1 }
|
||||
copy { print }
|
||||
copy && /^}$/ { exit }
|
||||
' "$GO_XCAT_SOURCE")"
|
||||
|
||||
# The real list, as go-xcat defines it for each package manager.
|
||||
if [[ ${WITH_DPKG:-0} == 1 ]]; then
|
||||
dpkg() { :; }
|
||||
list=(perl-xcat xcat-client xcat xcat-buildkit
|
||||
xcat-genesis-scripts-amd64 xcat-genesis-scripts-ppc64 xcat-server
|
||||
elilo-xcat grub2-xcat ipmitool-xcat syslinux-xcat
|
||||
xcat-genesis-base-amd64 xcat-genesis-base-ppc64 xnba-undi)
|
||||
else
|
||||
type() { return 1; }
|
||||
list=(perl-xCAT xCAT-client xCAT xCAT-buildkit
|
||||
xCAT-genesis-scripts-ppc64 xCAT-genesis-scripts-x86_64 xCAT-server
|
||||
elilo-xcat grub2-xcat ipmitool-xcat syslinux-xcat
|
||||
xCAT-genesis-base-ppc64 xCAT-genesis-base-x86_64 xnba-undi)
|
||||
fi
|
||||
riscv64_install_list "${list[@]}"
|
||||
DRIVER
|
||||
close($fh);
|
||||
chmod 0755, $driver;
|
||||
|
||||
sub riscv64_list {
|
||||
my ($with_dpkg) = @_;
|
||||
my $out = `GO_XCAT_SOURCE='$go_xcat' WITH_DPKG=$with_dpkg bash '$driver' 2>&1`;
|
||||
is( $?, 0, "the riscv64 list builds (dpkg=$with_dpkg)" ) or diag($out);
|
||||
return [ grep { length } split( /\n/, $out ) ];
|
||||
}
|
||||
|
||||
foreach my $case ( [ 1, 'deb', 'xcat-genesis-openembedded-riscv64', 'xcat-server' ],
|
||||
[ 0, 'rpm', 'xCAT-genesis-openembedded-riscv64', 'xCAT-server' ] )
|
||||
{
|
||||
my ( $with_dpkg, $name, $genesis, $server ) = @{$case};
|
||||
my $list = riscv64_list($with_dpkg);
|
||||
|
||||
is_deeply( [ grep { /genesis-scripts-/i } @{$list} ], [],
|
||||
"$name: no legacy Genesis scripts on riscv64" );
|
||||
is_deeply( [ grep { /genesis-base-/i } @{$list} ], [],
|
||||
"$name: no legacy Genesis base on riscv64" );
|
||||
is_deeply( [ sort grep { /^(elilo-xcat|syslinux-xcat|xnba-undi)$/ } @{$list} ],
|
||||
[ sort qw(elilo-xcat syslinux-xcat xnba-undi) ],
|
||||
"$name: the x86 boot payload a mixed cluster needs is still installed" );
|
||||
|
||||
ok( scalar( grep { $_ eq $genesis } @{$list} ),
|
||||
"$name: the riscv64 OpenEmbedded Genesis package is asked for" );
|
||||
|
||||
# The filter must take nothing else with it.
|
||||
foreach my $keep ( $server, 'grub2-xcat', 'ipmitool-xcat' ) {
|
||||
ok( scalar( grep { $_ eq $keep } @{$list} ), "$name: $keep is still installed" );
|
||||
}
|
||||
}
|
||||
|
||||
done_testing();
|
||||
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use FindBin;
|
||||
use Test::More;
|
||||
|
||||
# grub2 reads its configuration as a script. A word carrying a command separator ends the
|
||||
# linux command, so the kernel never sees the rest of the line. The Ubuntu installer seed
|
||||
# (ds=nocloud-net;s=<url>) is written as one such word: the node booted without the seed URL
|
||||
# and without BOOTIF, and the installer waited for someone to answer its questions.
|
||||
|
||||
use lib "$FindBin::Bin/../../perl-xCAT";
|
||||
use lib "$FindBin::Bin/../../xCAT-server/lib/perl";
|
||||
my $plugin = "$FindBin::Bin/../../xCAT-server/lib/xcat/plugins/grub2.pm";
|
||||
plan skip_all => 'grub2.pm not found' unless -r $plugin;
|
||||
eval { require $plugin; 1 } or plan skip_all => "could not load grub2.pm: $@";
|
||||
|
||||
my $quote = \&xCAT_plugin::grub2::quote_kcmdline;
|
||||
|
||||
my $seed = 'imgurl=http://mn/rootimg.cpio.gz ds=nocloud-net;s=http://mn/install/autoinst/n1/ quiet';
|
||||
is( $quote->($seed),
|
||||
'imgurl=http://mn/rootimg.cpio.gz ds=nocloud-net\;s=http://mn/install/autoinst/n1/ quiet',
|
||||
'the installer seed keeps the arguments after it' );
|
||||
|
||||
my $plain = 'imgurl=http://mn/rootimg.cpio.gz XCAT=10.0.0.1:3001 console=ttyS0,115200 quiet';
|
||||
is( $quote->($plain), $plain, 'a command line without a separator is unchanged' );
|
||||
|
||||
is( $quote->(undef), undef, 'an undefined command line stays undefined' );
|
||||
is( $quote->(''), '', 'an empty command line stays empty' );
|
||||
|
||||
for my $char ( ';', '{', '}', '|', '&', '<', '>', '(', ')' ) {
|
||||
is( $quote->("first opt=a${char}b last"), "first opt=a\\${char}b last",
|
||||
"a separator $char is escaped" );
|
||||
}
|
||||
|
||||
# grub2 removes the quoting before the kernel sees the value, so a value the caller quoted
|
||||
# must keep the quotes it was given rather than gaining a second layer.
|
||||
is( $quote->('first opt="a;b c" last'), 'first opt="a;b c" last',
|
||||
'a double quoted value is passed through unchanged' );
|
||||
is( $quote->(q{first 'ds=nocloud-net;s=http://mn/seed' last}),
|
||||
q{first 'ds=nocloud-net;s=http://mn/seed' last},
|
||||
'a single quoted value is passed through unchanged' );
|
||||
|
||||
# A quoted span and a bare separator in the same word: the span keeps its meaning and only
|
||||
# the separator outside it is escaped.
|
||||
is( $quote->(q{opt='a b';c}), q{opt='a b'\;c},
|
||||
'a separator beside a quoted span is escaped without touching the span' );
|
||||
is( $quote->('opt="a;b c" other=x;y'), 'opt="a;b c" other=x\;y',
|
||||
'a bare separator beside a quoted word is escaped' );
|
||||
|
||||
# An escaped separator is already literal to grub2, so escaping it again would hand the
|
||||
# kernel a backslash the caller never wrote.
|
||||
is( $quote->('first ds=nocloud-net\;s=http://mn/seed last'),
|
||||
'first ds=nocloud-net\;s=http://mn/seed last',
|
||||
'a separator the caller escaped is passed through unchanged' );
|
||||
is( $quote->('a\;b c;d'), 'a\;b c\;d',
|
||||
'an escaped separator does not stop a real one being escaped' );
|
||||
|
||||
is( $quote->('BOOTIF=$net_default_mac x;y'), 'BOOTIF=$net_default_mac x\;y',
|
||||
'a variable reference is left alone so BOOTIF still expands' );
|
||||
|
||||
done_testing();
|
||||
@@ -2,11 +2,23 @@
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use File::Path qw(make_path);
|
||||
use File::Temp qw(tempdir);
|
||||
use FindBin;
|
||||
use lib "$FindBin::Bin/../lib";
|
||||
use lib "$FindBin::Bin/../../perl-xCAT";
|
||||
use Test::More;
|
||||
|
||||
# xCAT modules put $::XCATROOT/lib/perl ahead of @INC as they compile, so on a host with xCAT
|
||||
# installed the schema, loaded after them, would come from /opt/xcat. XCATROOT points at this
|
||||
# checkout before any of them compiles.
|
||||
BEGIN {
|
||||
my $root = tempdir( CLEANUP => 1 );
|
||||
make_path("$root/lib");
|
||||
symlink( "$FindBin::Bin/../../perl-xCAT", "$root/lib/perl" ) or die "symlink: $!";
|
||||
$ENV{XCATROOT} = $root;
|
||||
}
|
||||
|
||||
use XCAT::Test::File qw(repo_path);
|
||||
use xCAT::ProfiledNodeUtils;
|
||||
use xCAT::Utils;
|
||||
@@ -56,9 +68,13 @@ is( xCAT::ProfiledNodeUtils::cal_netboot( $rule_table, [ 'aarch64', 'rhels', '9'
|
||||
# ---------------------------------------------------------------------------
|
||||
# Schema descriptions
|
||||
# ---------------------------------------------------------------------------
|
||||
SKIP: {
|
||||
skip 'xCAT::Schema is not loadable here', 5
|
||||
unless eval { require lib; lib->import( repo_path('perl-xCAT') ); require xCAT::Schema; 1 };
|
||||
# The schema comes from this checkout, not from an installed xCAT, or the descriptions checked
|
||||
# below would be those of whatever version the host carries.
|
||||
{
|
||||
require xCAT::Schema;
|
||||
require Cwd;
|
||||
like( Cwd::realpath( $INC{'xCAT/Schema.pm'} ), qr/^\Q@{[ Cwd::realpath( repo_path('perl-xCAT') ) ]}\E/,
|
||||
'xCAT::Schema is loaded from the checkout' );
|
||||
|
||||
like( $xCAT::Schema::tabspec{nodetype}{descriptions}{arch}, qr/\briscv64\b/,
|
||||
'nodetype.arch documents riscv64 as a valid value' );
|
||||
@@ -68,8 +84,9 @@ SKIP: {
|
||||
'osimage.osarch documents riscv64 as a valid value' );
|
||||
like( $xCAT::Schema::tabspec{osimage}{descriptions}{osarch}, qr/\bs390x\b/,
|
||||
'osimage.osarch documents s390x as a valid value' );
|
||||
like( $xCAT::Schema::tabspec{noderes}{descriptions}{netboot}, qr/riscv64\s+>=el10\s+grub2,grub2-http,grub2-tftp/,
|
||||
'noderes.netboot documents the riscv64 grub2 methods' );
|
||||
like( $xCAT::Schema::tabspec{noderes}{descriptions}{netboot},
|
||||
qr/riscv64\s+>=el10, >=ubuntu24\.04\s+grub2,grub2-http,grub2-tftp/,
|
||||
'noderes.netboot documents the riscv64 grub2 methods for EL and Ubuntu' );
|
||||
}
|
||||
|
||||
done_testing();
|
||||
|
||||
@@ -0,0 +1,256 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use File::Path qw(make_path);
|
||||
use File::Temp qw(tempdir);
|
||||
use FindBin;
|
||||
use Test::More;
|
||||
|
||||
# riscv64 nodes have no boot loader unless one reaches /tftpboot/boot/grub2. The image on
|
||||
# the media cannot be used: it carries a built-in configuration that looks for the live
|
||||
# filesystem, so a node that loads it drops to a grub prompt instead of reading the network
|
||||
# configuration. copycd builds a netboot image from the grub2 package the media ship.
|
||||
#
|
||||
# dpkg-deb and grub-mkimage are shadowed by stubs ahead of $PATH, because a management node
|
||||
# is the only place they exist. They record what copycd asked for, so the arguments that
|
||||
# decide whether the image can boot over the network are what the assertions read.
|
||||
|
||||
BEGIN {
|
||||
package xCAT::TableUtils;
|
||||
our $tftpdir;
|
||||
sub getTftpDir { return $tftpdir; }
|
||||
$INC{'xCAT/TableUtils.pm'} = __FILE__;
|
||||
}
|
||||
|
||||
use lib "$FindBin::Bin/../../perl-xCAT";
|
||||
use lib "$FindBin::Bin/../../xCAT-server/lib/perl";
|
||||
my $plugin = "$FindBin::Bin/../../xCAT-server/lib/xcat/plugins/debian.pm";
|
||||
plan skip_all => 'debian.pm not found' unless -r $plugin;
|
||||
eval { require $plugin; 1 } or plan skip_all => "could not load debian.pm: $@";
|
||||
|
||||
my $stubs = tempdir(CLEANUP => 1);
|
||||
my $log = "$stubs/mkimage.args";
|
||||
|
||||
sub write_stub {
|
||||
my ($name, $body) = @_;
|
||||
open(my $fh, '>', "$stubs/$name") or die $!;
|
||||
print {$fh} "#!/bin/bash\n$body";
|
||||
close($fh);
|
||||
chmod 0755, "$stubs/$name";
|
||||
}
|
||||
|
||||
# dpkg-deb -x <package> <dir> lays down the module tree the package carries.
|
||||
write_stub('dpkg-deb', <<'SH');
|
||||
dir="${!#}"
|
||||
mkdir -p "$dir/usr/lib/grub/riscv64-efi"
|
||||
: > "$dir/usr/lib/grub/riscv64-efi/kernel.img"
|
||||
SH
|
||||
|
||||
# grub-mkimage records its arguments and writes a riscv64 PE image to the -o path. Like the real
|
||||
# one it embeds the prefix and the name of every module it was asked for, which is what tells a
|
||||
# grub2 image apart from a file that merely carries the headers.
|
||||
write_stub('grub-mkimage', <<'SH');
|
||||
echo "$@" >> "$MKIMAGE_LOG"
|
||||
[ -n "$MKIMAGE_FAIL" ] && exit 1
|
||||
out=""; prefix=""; modules=()
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
-o) out="$2"; shift 2 ;;
|
||||
-p) prefix="$2"; shift 2 ;;
|
||||
-O|-d) shift 2 ;;
|
||||
*) modules+=("$1"); shift ;;
|
||||
esac
|
||||
done
|
||||
perl -e 'my ($prefix, @modules) = @ARGV;
|
||||
my $i = "MZ" . "\0" x 58 . pack("V", 64) . "PE\0\0" . pack("v", 0x5064)
|
||||
. pack("v", 1) . "\0" x 16 . pack("v", 0x20b) . "\0" x 14 . pack("V", 0x1000)
|
||||
. "\0" x 36 . pack("V", 4096) . pack("V", 4096) . "\0" x 4 . pack("v", 10) . "\0" x 2;
|
||||
$i .= "$prefix\0";
|
||||
$i .= "\0$_\0" for @modules;
|
||||
print $i, "\0" x (4096 - length $i)' "$prefix" "${modules[@]}" > "$out"
|
||||
SH
|
||||
|
||||
$ENV{PATH} = "$stubs:$ENV{PATH}";
|
||||
$ENV{MKIMAGE_LOG} = $log;
|
||||
|
||||
sub media_with {
|
||||
my (@files) = @_;
|
||||
my $root = tempdir(CLEANUP => 1);
|
||||
foreach my $file (@files) {
|
||||
my $full = "$root/$file";
|
||||
($full =~ m{^(.*)/[^/]+$}) and make_path($1);
|
||||
open(my $fh, '>', $full) or die $!;
|
||||
print {$fh} "content of $file";
|
||||
close($fh);
|
||||
}
|
||||
return $root;
|
||||
}
|
||||
|
||||
sub publish {
|
||||
my ($arch, $media) = @_;
|
||||
$xCAT::TableUtils::tftpdir = tempdir(CLEANUP => 1);
|
||||
unlink $log;
|
||||
my @said;
|
||||
xCAT_plugin::debian::install_media_grub2_loader(
|
||||
$media, $arch,
|
||||
sub { push @said, ($_[0]->{data} // ()), @{ $_[0]->{warning} || [] } });
|
||||
my $target = "$xCAT::TableUtils::tftpdir/boot/grub2/grub2.$arch";
|
||||
return {
|
||||
published => (-e $target ? 1 : 0),
|
||||
built => (-r $log ? do { open my $fh, '<', $log; local $/; <$fh> } : ''),
|
||||
said => join(' ', @said),
|
||||
};
|
||||
}
|
||||
|
||||
my $package = 'pool/main/g/grub2/grub-efi-riscv64-bin_2.12-1ubuntu7.3_riscv64.deb';
|
||||
|
||||
my $riscv = publish('riscv64', media_with($package, 'casper/vmlinux'));
|
||||
is($riscv->{published}, 1, 'riscv64 media publish a grub2 loader');
|
||||
like($riscv->{built}, qr/-O riscv64-efi/, 'the image is built for the riscv64 firmware');
|
||||
like($riscv->{built}, qr{-p /boot/grub2},
|
||||
'the image looks for its configuration where nodeset writes it');
|
||||
like($riscv->{built}, qr{-d \S+/usr/lib/grub/riscv64-efi},
|
||||
'the modules come from the package the media ship');
|
||||
like($riscv->{built}, qr/\befinet\b.*\btftp\b/s, 'the image can reach the network');
|
||||
like($riscv->{built}, qr/\bhttp\b/, 'the image can read a configuration over HTTP');
|
||||
like($riscv->{said}, qr/Installed .*grub2\.riscv64 from the media/,
|
||||
'copycd says where the loader came from');
|
||||
|
||||
my $x86 = publish('x86_64', media_with('EFI/boot/bootx64.efi', 'casper/vmlinuz'));
|
||||
is($x86->{published}, 0, 'media of another architecture publish nothing');
|
||||
|
||||
my $none = publish('riscv64', media_with('casper/vmlinux'));
|
||||
is($none->{published}, 0, 'riscv64 media without the grub2 package publish nothing');
|
||||
like($none->{said}, qr/No grub2\.riscv64 boot loader was installed/,
|
||||
'copycd says a riscv64 node will not boot without a loader');
|
||||
|
||||
# a build that fails must leave no loader behind, so nodeset reports the missing file
|
||||
$ENV{MKIMAGE_FAIL} = 1;
|
||||
my $failed = publish('riscv64', media_with($package));
|
||||
delete $ENV{MKIMAGE_FAIL};
|
||||
is($failed->{published}, 0, 'a failed build leaves no loader');
|
||||
like($failed->{said}, qr/No grub2\.riscv64 boot loader was installed/,
|
||||
'copycd reports a build that failed');
|
||||
|
||||
# The image is built beside the target and renamed, so an interrupted run cannot leave a
|
||||
# partial loader behind for nodeset to hand out.
|
||||
$xCAT::TableUtils::tftpdir = tempdir(CLEANUP => 1);
|
||||
make_path("$xCAT::TableUtils::tftpdir/boot/grub2");
|
||||
$ENV{MKIMAGE_FAIL} = 1;
|
||||
xCAT_plugin::debian::install_media_grub2_loader(media_with($package), 'riscv64', undef);
|
||||
delete $ENV{MKIMAGE_FAIL};
|
||||
my @leftovers = glob("$xCAT::TableUtils::tftpdir/boot/grub2/grub2.riscv64*");
|
||||
is(scalar @leftovers, 0, 'a failed build leaves nothing beside the target either');
|
||||
|
||||
# An empty target is what an interrupted older run left behind, so it must not be mistaken
|
||||
# for an installed loader.
|
||||
$xCAT::TableUtils::tftpdir = tempdir(CLEANUP => 1);
|
||||
make_path("$xCAT::TableUtils::tftpdir/boot/grub2");
|
||||
open(my $efh, '>', "$xCAT::TableUtils::tftpdir/boot/grub2/grub2.riscv64") or die $!;
|
||||
close($efh);
|
||||
unlink $log;
|
||||
xCAT_plugin::debian::install_media_grub2_loader(media_with($package), 'riscv64', undef);
|
||||
ok(-s "$xCAT::TableUtils::tftpdir/boot/grub2/grub2.riscv64",
|
||||
'an empty loader left by an earlier run is replaced');
|
||||
|
||||
# UEFI loads the loader as a PE image for one machine. A real riscv64 one is kept, and
|
||||
# anything else -- text, a header-only stub, another architecture's loader -- is replaced,
|
||||
# because a node given one of those cannot boot.
|
||||
sub uefi_image {
|
||||
my (%f) = @_;
|
||||
my $size = $f{size} // 4096;
|
||||
my $image =
|
||||
"MZ" . ( "\0" x 58 ) . pack( 'V', 64 )
|
||||
. "PE\0\0"
|
||||
. pack( 'v', $f{machine} // 0x5064 )
|
||||
. pack( 'v', $f{sections} // 1 ) . ( "\0" x 16 )
|
||||
. pack( 'v', 0x20b ) . ( "\0" x 14 )
|
||||
. pack( 'V', $f{entry} // 0x1000 ) . ( "\0" x 36 )
|
||||
. pack( 'V', $size ) . pack( 'V', $size ) . ( "\0" x 4 )
|
||||
. pack( 'v', $f{subsystem} // 10 ) . ( "\0" x 2 );
|
||||
my $prefix = exists $f{prefix} ? $f{prefix} : '/boot/grub2';
|
||||
$image .= "$prefix\0" if length $prefix;
|
||||
# grub-mkimage records the name of every embedded module, so a real loader carries them.
|
||||
my @modules = exists $f{modules}
|
||||
? @{ $f{modules} }
|
||||
: qw(efinet tftp http linux normal configfile search);
|
||||
$image .= "\0$_\0" for @modules;
|
||||
return $image . ( "\0" x ( $size - length $image ) );
|
||||
}
|
||||
|
||||
sub existing_loader_kept {
|
||||
my ($bytes) = @_;
|
||||
$xCAT::TableUtils::tftpdir = tempdir( CLEANUP => 1 );
|
||||
make_path("$xCAT::TableUtils::tftpdir/boot/grub2");
|
||||
my $target = "$xCAT::TableUtils::tftpdir/boot/grub2/grub2.riscv64";
|
||||
open( my $fh, '>', $target ) or die $!;
|
||||
binmode($fh);
|
||||
print {$fh} $bytes;
|
||||
close($fh);
|
||||
xCAT_plugin::debian::install_media_grub2_loader( media_with($package), 'riscv64', undef );
|
||||
open( my $rfh, '<', $target ) or die $!;
|
||||
binmode($rfh);
|
||||
my $now = do { local $/; <$rfh> };
|
||||
close($rfh);
|
||||
return $now eq $bytes;
|
||||
}
|
||||
|
||||
ok( existing_loader_kept( uefi_image() ), 'a riscv64 loader already in place is kept' );
|
||||
|
||||
ok( !existing_loader_kept('not a loader at all'),
|
||||
'a file that is not a UEFI image is replaced' );
|
||||
ok( !existing_loader_kept('MZ and nothing else'),
|
||||
'a file that only starts with the DOS signature is replaced' );
|
||||
ok( !existing_loader_kept( substr( uefi_image(), 0, 200 ) ),
|
||||
'a loader truncated to its headers is replaced' );
|
||||
ok( !existing_loader_kept( uefi_image( machine => 0x8664 ) ),
|
||||
"another architecture's loader is replaced" );
|
||||
|
||||
# The fields below are what makes a PE image executable at all. A file carrying the
|
||||
# signatures but none of them cannot boot a node, so it must not be mistaken for a loader.
|
||||
ok( !existing_loader_kept( uefi_image( sections => 0 ) ),
|
||||
'an image with no sections to load is replaced' );
|
||||
ok( !existing_loader_kept( uefi_image( entry => 0 ) ),
|
||||
'an image with no entry point is replaced' );
|
||||
ok( !existing_loader_kept( uefi_image( subsystem => 3 ) ),
|
||||
'an image that is not an EFI application is replaced' );
|
||||
|
||||
# The image on the media carries the same modules but not the prefix this plugin builds with,
|
||||
# so it looks for the live filesystem instead of the configuration nodeset writes.
|
||||
ok( !existing_loader_kept( uefi_image( prefix => '' ) ),
|
||||
'a valid EFI image built for another boot path is replaced' );
|
||||
|
||||
# Headers and a prefix are cheap to fabricate and say nothing about what the image can do.
|
||||
# Without the modules a net boot goes through, the file cannot fetch a kernel over the network.
|
||||
ok( !existing_loader_kept( uefi_image( modules => [] ) ),
|
||||
'an image carrying no grub2 modules is replaced' );
|
||||
ok( !existing_loader_kept( uefi_image( modules => [qw(linux normal configfile search)] ) ),
|
||||
'an image with no network modules is replaced' );
|
||||
ok( existing_loader_kept( uefi_image( modules => [qw(efinet tftp http linux normal configfile search gzio)] ) ),
|
||||
'an image carrying more modules than the minimum is kept' );
|
||||
|
||||
# A rebuild that cannot run must leave the loader alone. The check cannot tell an image this
|
||||
# plugin did not build from one built for another boot path -- the media loader carries the same
|
||||
# modules -- so removing one on that evidence can take a working loader away from every node.
|
||||
$xCAT::TableUtils::tftpdir = tempdir( CLEANUP => 1 );
|
||||
make_path("$xCAT::TableUtils::tftpdir/boot/grub2");
|
||||
my $rejected = "$xCAT::TableUtils::tftpdir/boot/grub2/grub2.riscv64";
|
||||
open( my $bad, '>', $rejected ) or die $!;
|
||||
print {$bad} 'not a loader at all';
|
||||
close($bad);
|
||||
my @told;
|
||||
xCAT_plugin::debian::install_media_grub2_loader(
|
||||
media_with('casper/vmlinux'), 'riscv64',
|
||||
sub { push @told, ( $_[0]->{data} // () ), @{ $_[0]->{warning} || [] } } );
|
||||
ok( -e $rejected, 'a rejected loader survives a media that cannot replace it' );
|
||||
my $left = -e $rejected
|
||||
? do { open my $fh, '<', $rejected or die $!; local $/; <$fh> }
|
||||
: '(removed)';
|
||||
is( $left, 'not a loader at all', '... byte for byte' );
|
||||
like( join( ' ', @told ), qr/No grub2\.riscv64 boot loader was installed/,
|
||||
'and copycd says no loader was installed' );
|
||||
like( join( ' ', @told ), qr/was left alone/,
|
||||
'... and that it did not touch what was there' );
|
||||
|
||||
done_testing();
|
||||
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use File::Temp qw(tempdir);
|
||||
use FindBin;
|
||||
use Test::More;
|
||||
|
||||
# The stubbed test proves the decisions copycd makes. This one proves the artifact: it runs the
|
||||
# real grub-mkimage over the grub2 package of real Ubuntu media and checks that what comes out is
|
||||
# what the management node will serve to a riscv64 node.
|
||||
#
|
||||
# It needs copied riscv64 media and the grub2 build tools, so it runs where those exist:
|
||||
# XCAT_TEST_UBUNTU_RISCV64_MEDIA=/install/ubuntu24.04.4/riscv64 perl <this test>
|
||||
# Without them it skips, which is why the stubbed test still covers the decisions.
|
||||
|
||||
BEGIN {
|
||||
package xCAT::TableUtils;
|
||||
our $tftpdir;
|
||||
sub getTftpDir { return $tftpdir; }
|
||||
$INC{'xCAT/TableUtils.pm'} = __FILE__;
|
||||
}
|
||||
|
||||
my $media = $ENV{XCAT_TEST_UBUNTU_RISCV64_MEDIA};
|
||||
plan skip_all => 'set XCAT_TEST_UBUNTU_RISCV64_MEDIA to copied riscv64 media' unless $media;
|
||||
plan skip_all => "no media at $media" unless -d $media;
|
||||
foreach my $tool (qw(dpkg-deb grub-mkimage)) {
|
||||
my $found = grep { -x "$_/$tool" } split( /:/, $ENV{PATH} // '' );
|
||||
plan skip_all => "$tool is not installed" unless $found;
|
||||
}
|
||||
|
||||
use lib "$FindBin::Bin/../../perl-xCAT";
|
||||
use lib "$FindBin::Bin/../../xCAT-server/lib/perl";
|
||||
my $plugin = "$FindBin::Bin/../../xCAT-server/lib/xcat/plugins/debian.pm";
|
||||
plan skip_all => 'debian.pm not found' unless -r $plugin;
|
||||
eval { require $plugin; 1 } or plan skip_all => "could not load debian.pm: $@";
|
||||
|
||||
$xCAT::TableUtils::tftpdir = tempdir( CLEANUP => 1 );
|
||||
my @told;
|
||||
my $target = xCAT_plugin::debian::install_media_grub2_loader(
|
||||
$media, 'riscv64',
|
||||
sub { push @told, ( $_[0]->{data} // () ), @{ $_[0]->{warning} || [] } } );
|
||||
|
||||
ok( defined $target, 'the media produced a loader' )
|
||||
or diag( join( "\n", @told ) ), done_testing(), exit;
|
||||
is( $target, "$xCAT::TableUtils::tftpdir/boot/grub2/grub2.riscv64",
|
||||
'it is where nodeset serves it from' );
|
||||
ok( -s $target, '... and it is not empty' );
|
||||
|
||||
# The same checks nodeset depends on, against a real grub-mkimage image rather than a fabricated one.
|
||||
my $build = { machine => 0x5064, format => 'riscv64-efi', package => 'grub-efi-riscv64-bin' };
|
||||
ok( xCAT_plugin::debian::_is_uefi_image( $target, $build->{machine} ),
|
||||
'the image is a riscv64 UEFI application' );
|
||||
ok( xCAT_plugin::debian::_is_netboot_loader( $target, $build ),
|
||||
'... carrying the prefix and the modules a net boot needs' );
|
||||
|
||||
# A second import must not rebuild it: the image already in place is the one the nodes booted.
|
||||
my $before = ( stat($target) )[9];
|
||||
my $again = xCAT_plugin::debian::install_media_grub2_loader( $media, 'riscv64', undef );
|
||||
is( $again, $target, 'a second import returns the loader already in place' );
|
||||
is( ( stat($target) )[9], $before, '... without rewriting it' );
|
||||
|
||||
done_testing();
|
||||
@@ -0,0 +1,80 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use File::Spec;
|
||||
use FindBin;
|
||||
use Test::More;
|
||||
|
||||
# archive.ubuntu.com publishes amd64 and i386 only. A ppc64el or riscv64 netboot image built
|
||||
# against it finds no package at all, and debootstrap fails before it copies anything, so the
|
||||
# default mirror has to follow the image architecture.
|
||||
#
|
||||
# Driven by the real selection code: the two statements are extracted from genimage and
|
||||
# evaluated here, so this test tracks the script rather than a copy of it.
|
||||
|
||||
my $repo_root = File::Spec->rel2abs( File::Spec->catdir( $FindBin::Bin, '..', '..' ) );
|
||||
use lib "$FindBin::Bin/../../perl-xCAT";
|
||||
require xCAT::Utils;
|
||||
my $genimage_path = File::Spec->catfile(
|
||||
$repo_root, 'xCAT-server', 'share', 'xcat', 'netboot', 'ubuntu', 'genimage'
|
||||
);
|
||||
plan skip_all => "genimage not found at $genimage_path" unless -f $genimage_path;
|
||||
|
||||
my $src = do { local $/; open my $fh, '<', $genimage_path or die $!; <$fh> };
|
||||
|
||||
my ($default) = $src =~ /^\s*(my \$default = \(\$uarch =~.*?;)\s*$/ms;
|
||||
ok( defined $default, 'found the default mirror selection in genimage' )
|
||||
or done_testing(), exit;
|
||||
|
||||
my ($pick) = $src =~ /^\s*(my \$mirror = \(defined \$aptmirror\[0\].*?;)\s*$/ms;
|
||||
ok( defined $pick, 'found the mirror override in genimage' )
|
||||
or done_testing(), exit;
|
||||
|
||||
# genimage does not read osimage.osarch directly: it converts it first, so the value the selection
|
||||
# sees is whatever xCAT::Utils::debian_arch returns. Driving the xCAT token through that conversion
|
||||
# is what catches a token the map does not know.
|
||||
sub choose_osarch {
|
||||
my ( $osarch, $site ) = @_;
|
||||
return choose( xCAT::Utils->debian_arch($osarch), $site );
|
||||
}
|
||||
|
||||
sub choose {
|
||||
my ( $uarch, $site ) = @_;
|
||||
my $set = defined $site ? "('$site')" : "()";
|
||||
## no critic (BuiltinFunctions::ProhibitStringyEval)
|
||||
my $mirror = eval "my \$uarch = '$uarch'; my \@aptmirror = $set; $default $pick \$mirror";
|
||||
## use critic
|
||||
die "failed to evaluate the genimage mirror selection: $@" if $@;
|
||||
return $mirror;
|
||||
}
|
||||
|
||||
is( choose('riscv64'), 'http://ports.ubuntu.com/ubuntu-ports',
|
||||
'a riscv64 image takes the ports archive' );
|
||||
is( choose('ppc64el'), 'http://ports.ubuntu.com/ubuntu-ports',
|
||||
'a ppc64el image takes the ports archive' );
|
||||
is( choose('amd64'), 'http://archive.ubuntu.com/ubuntu',
|
||||
'an amd64 image keeps the main archive' );
|
||||
is( choose('i386'), 'http://archive.ubuntu.com/ubuntu',
|
||||
'an i386 image keeps the main archive' );
|
||||
|
||||
is( choose( 'riscv64', 'http://mirror.example.invalid/ubuntu' ),
|
||||
'http://mirror.example.invalid/ubuntu',
|
||||
'site.ubuntu_apt_mirror overrides the ports archive' );
|
||||
is( choose( 'amd64', 'http://mirror.example.invalid/ubuntu' ),
|
||||
'http://mirror.example.invalid/ubuntu',
|
||||
'site.ubuntu_apt_mirror overrides the main archive' );
|
||||
is( choose( 'riscv64', '' ), 'http://ports.ubuntu.com/ubuntu-ports',
|
||||
'an empty site.ubuntu_apt_mirror does not blank the mirror' );
|
||||
|
||||
# The architecture reaches the selection as an xCAT osarch value, not as a Debian one.
|
||||
is( choose_osarch('x86_64'), 'http://archive.ubuntu.com/ubuntu',
|
||||
'osarch x86_64 reaches the archive' );
|
||||
is( choose_osarch('x86'), 'http://archive.ubuntu.com/ubuntu',
|
||||
'osarch x86 reaches the archive, which is where i386 lives' );
|
||||
is( choose_osarch('riscv64'), 'http://ports.ubuntu.com/ubuntu-ports',
|
||||
'osarch riscv64 reaches the ports archive' );
|
||||
is( choose_osarch('ppc64el'), 'http://ports.ubuntu.com/ubuntu-ports',
|
||||
'osarch ppc64el reaches the ports archive' );
|
||||
|
||||
done_testing();
|
||||
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use File::Path qw(make_path);
|
||||
use File::Spec;
|
||||
use File::Temp qw(tempdir);
|
||||
use FindBin;
|
||||
use Test::More;
|
||||
|
||||
# A netboot image resolves names with the libnss libraries of its own architecture, and
|
||||
# Ubuntu keeps them in a per-architecture directory. Build a root filesystem for each
|
||||
# architecture and run genimage's selection over it.
|
||||
|
||||
my $repo_root = File::Spec->rel2abs(File::Spec->catdir($FindBin::Bin, '..', '..'));
|
||||
my $genimage = File::Spec->catfile(
|
||||
$repo_root, 'xCAT-server', 'share', 'xcat', 'netboot', 'ubuntu', 'genimage');
|
||||
plan skip_all => "genimage not found at $genimage" unless -f $genimage;
|
||||
|
||||
my $src = do { local $/; open my $fh, '<', $genimage or die $!; <$fh> };
|
||||
my ($selection) =
|
||||
$src =~ /^(\s*if \(\$arch =~ \/x86_64\/\) \{.*?\n\s*\} else \{.*?\n\s*\}\n)/ms;
|
||||
ok(defined $selection, 'found the resolver library selection in genimage')
|
||||
or do { done_testing(); exit };
|
||||
|
||||
sub selected {
|
||||
my ($arch, @present) = @_;
|
||||
my $rootimg_dir = tempdir(CLEANUP => 1);
|
||||
foreach my $file (@present) {
|
||||
my $full = "$rootimg_dir/$file";
|
||||
($full =~ m{^(.*)/[^/]+$}) and make_path($1);
|
||||
open(my $fh, '>', $full) or die $!;
|
||||
close($fh);
|
||||
}
|
||||
my @filestoadd;
|
||||
eval "$selection 1" or die $@; ## no critic (BuiltinFunctions::ProhibitStringyEval)
|
||||
return join(',', sort @filestoadd);
|
||||
}
|
||||
|
||||
is(
|
||||
selected('riscv64', 'lib/riscv64-linux-gnu/libnss_files.so.2',
|
||||
'lib/riscv64-linux-gnu/libnss_dns.so.2'),
|
||||
'lib/riscv64-linux-gnu/libnss_dns.so.2,lib/riscv64-linux-gnu/libnss_files.so.2',
|
||||
'a riscv64 image takes the riscv64 resolver libraries',
|
||||
);
|
||||
is(
|
||||
selected('riscv64', 'lib/libnss_dns.so.2'),
|
||||
'',
|
||||
'riscv64 does not fall back to the path that holds no riscv64 library',
|
||||
);
|
||||
is(
|
||||
selected('x86_64', 'lib/x86_64-linux-gnu/libnss_dns.so.2'),
|
||||
'lib/x86_64-linux-gnu/libnss_dns.so.2',
|
||||
'x86_64 selection is unchanged',
|
||||
);
|
||||
is(
|
||||
selected('ppc64el', 'lib/powerpc64le-linux-gnu/libnss_files.so.2'),
|
||||
'lib/powerpc64le-linux-gnu/libnss_files.so.2',
|
||||
'ppc64el selection is unchanged',
|
||||
);
|
||||
is(
|
||||
selected('s390x', 'lib/libnss_dns.so.2'),
|
||||
'lib/libnss_dns.so.2',
|
||||
'an architecture with no branch keeps the generic library',
|
||||
);
|
||||
|
||||
done_testing();
|
||||
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use FindBin;
|
||||
use Test::More;
|
||||
|
||||
# Without a driver list the Ubuntu netboot image ships no network module at all, and a node
|
||||
# whose NIC is not built into the kernel cannot reach its root filesystem. QEMU hides this,
|
||||
# because virtio-net is built into the Ubuntu riscv64 kernel; a machine with an r8169 or an
|
||||
# e1000e does not boot.
|
||||
|
||||
use lib "$FindBin::Bin/../../perl-xCAT";
|
||||
use lib "$FindBin::Bin/../../xCAT-server/lib/perl";
|
||||
use lib "$FindBin::Bin/../../xCAT-server/share/xcat/netboot/imgutils";
|
||||
require imgutils;
|
||||
|
||||
my @riscv = imgutils::default_net_drivers( 'ubuntu', 'riscv64' );
|
||||
ok( scalar @riscv, 'a riscv64 Ubuntu image is given network drivers' );
|
||||
|
||||
for my $driver (qw(virtio_net e1000 e1000e igb r8169 tg3 mlx5_core)) {
|
||||
ok( scalar( grep { $_ eq $driver } @riscv ), "the list carries $driver" );
|
||||
}
|
||||
|
||||
# Every Ubuntu row carries overlay, because the netboot root is an overlay mount.
|
||||
ok( scalar( grep { $_ eq 'overlay' } @riscv ), 'the list carries overlay' );
|
||||
|
||||
# The architectures that already worked must keep the drivers they had.
|
||||
is_deeply(
|
||||
[ imgutils::default_net_drivers( 'ubuntu', 'x86_64' ) ],
|
||||
[qw(tg3 bnx2 bnx2x e1000 e1000e igb mlx_en mlx5_core virtio_net overlay)],
|
||||
'x86_64 keeps its drivers' );
|
||||
ok( scalar( imgutils::default_net_drivers( 'ubuntu', 'ppc64el' ) ),
|
||||
'ppc64el keeps its drivers' );
|
||||
is_deeply( [ imgutils::default_net_drivers( 'ubuntu', 'sparc' ) ], [],
|
||||
'an architecture with no entry still gets an empty list' );
|
||||
|
||||
done_testing();
|
||||
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use File::Spec;
|
||||
use FindBin;
|
||||
use Test::More;
|
||||
|
||||
# A diskless image needs a kernel and the tools its boot scripts call. Without a package
|
||||
# list for the architecture, genimage debootstraps whatever the generic list holds and the
|
||||
# image cannot boot. Compare the riscv64 lists with the x86_64 ones they follow.
|
||||
|
||||
use lib "$FindBin::Bin/../lib";
|
||||
use XCAT::Test::File qw(repo_path);
|
||||
|
||||
sub packages {
|
||||
my ($path) = @_;
|
||||
my $full = repo_path($path);
|
||||
return unless -r $full;
|
||||
open(my $fh, '<', $full) or die "cannot read $full: $!";
|
||||
my @packages = grep { length && !/^#/ } map { my $l = $_; chomp $l; $l =~ s/\s+//g; $l } <$fh>;
|
||||
close($fh);
|
||||
return \@packages;
|
||||
}
|
||||
|
||||
foreach my $release (qw(24.04 26.04)) {
|
||||
my $netboot = "xCAT-server/share/xcat/netboot/ubuntu/compute.ubuntu$release.riscv64.pkglist";
|
||||
my $x86 = "xCAT-server/share/xcat/netboot/ubuntu/compute.ubuntu$release.x86_64.pkglist";
|
||||
my $riscv_packages = packages($netboot);
|
||||
ok($riscv_packages, "$release has a riscv64 netboot package list");
|
||||
is_deeply($riscv_packages, packages($x86),
|
||||
"the $release riscv64 image installs what the x86_64 image installs");
|
||||
ok(scalar(grep { $_ eq 'linux-image-generic' } @{$riscv_packages}),
|
||||
"the $release riscv64 image installs a kernel");
|
||||
ok(scalar(grep { $_ eq 'nfs-common' } @{$riscv_packages}),
|
||||
"the $release riscv64 image can mount its root over NFS");
|
||||
}
|
||||
|
||||
my $install = packages(
|
||||
'xCAT-server/share/xcat/install/ubuntu/compute.ubuntu26.04.riscv64.pkglist');
|
||||
ok($install, '26.04 has a riscv64 install package list');
|
||||
is_deeply($install,
|
||||
packages('xCAT-server/share/xcat/install/ubuntu/compute.ubuntu26.04.x86_64.pkglist'),
|
||||
'the 26.04 riscv64 install takes what the x86_64 install takes');
|
||||
|
||||
done_testing();
|
||||
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use FindBin;
|
||||
use Test::More;
|
||||
|
||||
# archive.ubuntu.com publishes amd64 and i386 only, so a ppc64el or riscv64 stateful install
|
||||
# is handed a mirror that carries no package for it. The architecture being installed is the
|
||||
# last component of the media directory.
|
||||
|
||||
BEGIN {
|
||||
package xCAT::Table;
|
||||
our $value;
|
||||
sub new { return bless {}, shift }
|
||||
sub getAttribs { return defined $value ? { value => $value } : undef }
|
||||
$INC{'xCAT/Table.pm'} = __FILE__;
|
||||
}
|
||||
|
||||
use lib "$FindBin::Bin/../../perl-xCAT";
|
||||
use lib "$FindBin::Bin/../../xCAT-server/lib/perl";
|
||||
my $module = "$FindBin::Bin/../../xCAT-server/lib/perl/xCAT/Template.pm";
|
||||
plan skip_all => 'Template.pm not found' unless -r $module;
|
||||
eval { require $module; 1 } or plan skip_all => "could not load Template.pm: $@";
|
||||
|
||||
my $mirror = \&xCAT::Template::ubuntu_subiquity_apt_mirror;
|
||||
|
||||
# The osimage's architecture decides the mirror. pkgdir cannot: it is whatever path the
|
||||
# administrator configured, so an amd64 image under /srv/custom-media would be read as a
|
||||
# non-x86 architecture and sent to the ports archive.
|
||||
is( $mirror->('riscv64'), 'http://ports.ubuntu.com/ubuntu-ports',
|
||||
'a riscv64 image takes the ports archive' );
|
||||
is( $mirror->('ppc64el'), 'http://ports.ubuntu.com/ubuntu-ports',
|
||||
'a ppc64el image takes the ports archive' );
|
||||
is( $mirror->('ppc64le'), 'http://ports.ubuntu.com/ubuntu-ports',
|
||||
'the other spelling of ppc64 takes the ports archive' );
|
||||
|
||||
for my $x86 (qw(x86_64 amd64 x86 i386)) {
|
||||
is( $mirror->($x86), 'http://archive.ubuntu.com/ubuntu',
|
||||
"an $x86 image keeps the main archive" );
|
||||
}
|
||||
|
||||
# With no architecture there is nothing to key on, so the previous default stands.
|
||||
is( $mirror->(undef), 'http://archive.ubuntu.com/ubuntu',
|
||||
'an unknown architecture keeps the previous default' );
|
||||
|
||||
# site.ubuntu_apt_mirror still wins, which is how an airgapped cluster points at its own.
|
||||
{
|
||||
local $xCAT::Table::value = 'http://mirror.example.invalid/ubuntu';
|
||||
is( $mirror->('riscv64'), 'http://mirror.example.invalid/ubuntu',
|
||||
'site.ubuntu_apt_mirror overrides the ports archive' );
|
||||
is( $mirror->('x86_64'), 'http://mirror.example.invalid/ubuntu',
|
||||
'site.ubuntu_apt_mirror overrides the main archive' );
|
||||
}
|
||||
{
|
||||
local $xCAT::Table::value = '';
|
||||
is( $mirror->('riscv64'), 'http://ports.ubuntu.com/ubuntu-ports',
|
||||
'an empty site.ubuntu_apt_mirror does not blank the mirror' );
|
||||
}
|
||||
|
||||
done_testing();
|
||||
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use File::Spec;
|
||||
use FindBin;
|
||||
use Test::More;
|
||||
|
||||
# A riscv64 management node has no legacy Genesis: the image ships as the OpenEmbedded package
|
||||
# xcat-genesis-openembedded-riscv64, which mknb consumes. xCAT.spec already says so for the rpm
|
||||
# side and gives riscv64 its own dependency block.
|
||||
#
|
||||
# The deb side named xcat-genesis-scripts-amd64 in a plain Depends, and that package is
|
||||
# Architecture: all, so apt installed the x86 Genesis scripts (and, through them, the x86 Genesis
|
||||
# base) on a riscv64 management node. Restrict the dependency to the architectures that have a
|
||||
# legacy Genesis, and leave amd64 and ppc64el untouched.
|
||||
|
||||
my $repo_root = File::Spec->rel2abs(
|
||||
File::Spec->catdir( $FindBin::Bin, '..', '..' )
|
||||
);
|
||||
|
||||
sub read_file {
|
||||
my ($filename) = @_;
|
||||
open( my $fh, '<', $filename ) or die "Unable to read $filename: $!";
|
||||
my $content = do { local $/; <$fh> };
|
||||
close($fh);
|
||||
return $content;
|
||||
}
|
||||
|
||||
my $have_dpkg_deps = eval { require Dpkg::Deps; 1 } ? 1 : 0;
|
||||
|
||||
foreach my $pkg ( [ 'xCAT', 'xcat' ], [ 'xCATsn', 'xcatsn' ] ) {
|
||||
my ( $dir, $name ) = @$pkg;
|
||||
my $control = read_file( File::Spec->catfile( $repo_root, $dir, 'debian', 'control' ) );
|
||||
my ($depends) = $control =~ /^Depends:\s*(.*)$/m;
|
||||
ok( defined $depends, "$name debian/control has a Depends line" );
|
||||
my ($recommends) = $control =~ /^Recommends:\s*(.*)$/m;
|
||||
ok( defined $recommends, "$name debian/control has a Recommends line" );
|
||||
|
||||
my ($entry) = grep { /xcat-genesis-scripts/ } split( /\s*,\s*/, $depends );
|
||||
ok( defined $entry, "$name depends on a legacy Genesis scripts package" );
|
||||
like( $entry, qr/\[!riscv64\]/,
|
||||
"$name excludes riscv64 from the legacy Genesis scripts dependency" );
|
||||
|
||||
SKIP: {
|
||||
skip( "Dpkg::Deps is not available", 7 ) unless $have_dpkg_deps;
|
||||
|
||||
# Dpkg::Deps cannot parse a substvar, which dpkg-gencontrol expands before it gets here.
|
||||
( my $parsable = $depends ) =~ s/\$\{[^}]*\}\s*,?\s*//g;
|
||||
my %reduced = map {
|
||||
my $d = Dpkg::Deps::deps_parse( $parsable, reduce_arch => 1, host_arch => $_ );
|
||||
$_ => ( defined $d ? $d->output() : '' )
|
||||
} qw(riscv64 amd64 ppc64el);
|
||||
|
||||
unlike( $reduced{riscv64}, qr/xcat-genesis-scripts/,
|
||||
"$name on riscv64 does not pull the legacy Genesis scripts" );
|
||||
like( $reduced{amd64}, qr/xcat-genesis-scripts-amd64/,
|
||||
"$name on amd64 still pulls them" );
|
||||
like( $reduced{ppc64el}, qr/xcat-genesis-scripts-amd64/,
|
||||
"$name on ppc64el still pulls them" );
|
||||
|
||||
# The restriction must not take anything else with it: every other dependency of the
|
||||
# amd64 package must survive on riscv64.
|
||||
my @lost = grep { $reduced{riscv64} !~ /\Q$_\E/ }
|
||||
grep { !/xcat-genesis-scripts/ }
|
||||
map { my $d = $_; $d =~ s/\s*\(.*//; $d =~ s/\s*\[.*//; $d }
|
||||
split( /\s*,\s*/, $reduced{amd64} );
|
||||
is_deeply( \@lost, [],
|
||||
"$name on riscv64 keeps every other dependency" )
|
||||
or diag( "dropped: @lost" );
|
||||
|
||||
# A management node of any architecture can provision nodes of another, so what it SERVES
|
||||
# to those nodes -- the x86 boot payload and the Genesis images of the other architectures
|
||||
# -- stays recommended everywhere. Only the legacy Genesis of this node is architecture
|
||||
# specific, and that one is a dependency, not a recommendation.
|
||||
my %reduced_recommends = map {
|
||||
my $d = Dpkg::Deps::deps_parse( $recommends, reduce_arch => 1, host_arch => $_ );
|
||||
$_ => ( defined $d ? $d->output() : '' )
|
||||
} qw(riscv64 amd64);
|
||||
|
||||
like( $reduced_recommends{riscv64}, qr/\bsyslinux-xcat\b/,
|
||||
"$name on riscv64 still recommends the x86 boot payload it serves" );
|
||||
like( $reduced_recommends{riscv64}, qr/xcat-genesis-openembedded-x86-64/,
|
||||
"$name on riscv64 still recommends the Genesis image of the other architectures" );
|
||||
like( $reduced_recommends{amd64}, qr/\bsyslinux-xcat\b/,
|
||||
"$name on amd64 is unchanged" );
|
||||
}
|
||||
}
|
||||
|
||||
done_testing();
|
||||
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use File::Spec;
|
||||
use FindBin;
|
||||
use Test::More;
|
||||
|
||||
# copycd builds the riscv64 boot loader by running grub-mkimage, and the plugin that runs it ships
|
||||
# in xcat-server. Nothing declared the package that provides that command, so a management or
|
||||
# service node installed without it copies riscv64 media and then produces no loader at all, while
|
||||
# DHCP keeps pointing every riscv64 node at the path where the loader should be.
|
||||
#
|
||||
# grub-common provides /usr/bin/grub-mkimage on every supported Ubuntu release. That the plugin
|
||||
# runs grub-mkimage is shown by ubuntu_copycd_grub2_loader.t, which drives it through a stub.
|
||||
|
||||
my $repo_root = File::Spec->rel2abs( File::Spec->catdir( $FindBin::Bin, '..', '..' ) );
|
||||
|
||||
sub depends_of {
|
||||
my ($package) = @_;
|
||||
my $file = File::Spec->catfile( $repo_root, $package, 'debian', 'control' );
|
||||
open( my $fh, '<', $file ) or die "Unable to read $file: $!";
|
||||
my $control = do { local $/; <$fh> };
|
||||
close($fh);
|
||||
my ($depends) = $control =~ /^Depends:\s*(.*)$/m;
|
||||
return [ split( /\s*,\s*/, $depends // '' ) ];
|
||||
}
|
||||
|
||||
my $server = depends_of('xCAT-server');
|
||||
ok( scalar( grep { /^grub-common\b/ } @{$server} ),
|
||||
'xcat-server depends on the package that provides grub-mkimage' );
|
||||
|
||||
# The plugin runs there, so the metapackages inherit it and must not carry their own copy.
|
||||
foreach my $package (qw(xCAT xCATsn)) {
|
||||
is_deeply( [ grep { /^grub-common\b/ } @{ depends_of($package) } ], [],
|
||||
"$package leaves the dependency with the package that runs the command" );
|
||||
}
|
||||
|
||||
done_testing();
|
||||
+2
-2
@@ -8,8 +8,8 @@ Vcs-browser: https://github.com/xcat2/xcat-core.git
|
||||
Homepage: https://xcat.org/
|
||||
|
||||
Package: xcat
|
||||
Architecture: amd64 ppc64el
|
||||
Depends: ${perl:Depends}, goconserver(>= 0.3.3-snap000000000000), xcat-server (>= 2.13-snap000000000000), xcat-client (>= 2.13-snap000000000000), libdbd-sqlite3-perl, isc-dhcp-server | kea, bind9, apache2, nfs-kernel-server, libxml-parser-perl, rsync, tftpd-hpa, libnet-telnet-perl, chrony | ntp, xcat-genesis-scripts-amd64 (>= 2.13-snap000000000000)
|
||||
Architecture: amd64 ppc64el riscv64
|
||||
Depends: ${perl:Depends}, goconserver(>= 0.3.3-snap000000000000), xcat-server (>= 2.13-snap000000000000), xcat-client (>= 2.13-snap000000000000), libdbd-sqlite3-perl, isc-dhcp-server | kea, bind9, apache2, nfs-kernel-server, libxml-parser-perl, rsync, tftpd-hpa, libnet-telnet-perl, chrony | ntp, xcat-genesis-scripts-amd64 (>= 2.13-snap000000000000) [!riscv64]
|
||||
Recommends: net-tools, nmap, kea, tftp-hpa, ipmitool-xcat (>= 1.8.17-1), syslinux[any-amd64], libsys-virt-perl, syslinux-xcat, xnba-undi, elilo-xcat, util-linux-extra, xcat-buildkit (>= 2.13-snap000000000000), xcat-probe (>= 2.13-snap000000000000), xcat-genesis-openembedded-x86-64, xcat-genesis-openembedded-ppc64le, xcat-genesis-openembedded-riscv64, xcat-genesis-openembedded-s390x
|
||||
Suggests: yaboot-xcat
|
||||
Description: Metapackage for a common, default xCAT setup
|
||||
|
||||
@@ -7,8 +7,8 @@ Standards-Version: 3.9.4
|
||||
Homepage: https://xcat.org/
|
||||
|
||||
Package: xcatsn
|
||||
Architecture: amd64 ppc64el
|
||||
Depends: ${perl:Depends}, goconserver (>=0.3.3-snap000000000000), xcat-server (>= 2.13-snap000000000000), xcat-client (>= 2.13-snap000000000000), libdbd-sqlite3-perl, libxml-parser-perl, tftpd-hpa, libnet-telnet-perl, isc-dhcp-server | kea, bind9, apache2, nfs-kernel-server, xcat-genesis-scripts-amd64 (>= 2.13-snap000000000000)
|
||||
Architecture: amd64 ppc64el riscv64
|
||||
Depends: ${perl:Depends}, goconserver (>=0.3.3-snap000000000000), xcat-server (>= 2.13-snap000000000000), xcat-client (>= 2.13-snap000000000000), libdbd-sqlite3-perl, libxml-parser-perl, tftpd-hpa, libnet-telnet-perl, isc-dhcp-server | kea, bind9, apache2, nfs-kernel-server, xcat-genesis-scripts-amd64 (>= 2.13-snap000000000000) [!riscv64]
|
||||
Recommends: net-tools, nmap, kea, tftp-hpa, ipmitool-xcat (>= 1.8.17-1), syslinux[any-amd64], libsys-virt-perl, syslinux-xcat, xnba-undi, elilo-xcat, xcat-buildkit (>= 2.13-snap000000000000), xcat-probe (>= 2.13-snap000000000000), xcat-genesis-openembedded-x86-64, xcat-genesis-openembedded-ppc64le, xcat-genesis-openembedded-riscv64, xcat-genesis-openembedded-s390x
|
||||
Suggests: yaboot-xcat
|
||||
Description: Metapackage for a common, default xCAT service node setup
|
||||
|
||||
Reference in New Issue
Block a user