2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-09-05 04:27:55 +00:00

feat(mknb): write grub2 discovery configs for riscv64

mknb only knew how to publish a discovery boot configuration for x86
(PXELINUX and xNBA) and POWER (petitboot). Any other architecture got a
Genesis kernel and initramfs under /tftpboot/xcat and nothing that would
make a firmware boot them, so riscv64 discovery could not start.

riscv64 nodes boot through UEFI and grub2. Write one grub2 configuration
per network, /tftpboot/boot/grub2/grub.cfg-<network hex prefix>, using
the same network keys as the PXELINUX files. A net-booted grub2.riscv64
searches grub.cfg-01-<mac>, grub.cfg-<8 hex ip> and then shorter prefixes
of the ip, so the per-node files that nodeset writes keep priority and
the network file is only reached by clients without a node configuration.
The file is regenerated from the published Genesis artifacts (lzma
preferred over gzip), guarded by $grub_cpu so other grub2 architectures
can share it later, carries the xcatd endpoint, the serial console and
BOOTIF=$net_default_mac, and is dropped for networks served by a
:noboot interface. It is written by name rather than into an existing
file, because on a /32 network nodeset's hard link for the node carries
the same name.

Publishing a Genesis image now also drops the other compression variant
of that architecture, so a leftover genesis.fs.<arch>.lzma can no longer
be paired with a freshly published kernel by this configuration or by
--configfileonly. And since these configurations are only reachable
through grub2.<arch>, which xCAT does not build, a missing boot loader is
reported instead of leaving the nodes to time out in firmware.

xcatconfig now also runs mknb riscv64 when xCAT-genesis-base-riscv64 is
installed, and the usage text lists the architecture.
This commit is contained in:
Vinícius Ferrão
2026-08-20 16:39:34 -03:00
parent 360beccb73
commit 033b16d522
+97
View File
@@ -18,6 +18,14 @@ sub _canonical_genesis_arch {
return $arch eq 'ppc64el' ? 'ppc64le' : $arch;
}
# Architectures whose discovery boot goes through UEFI firmware and grub2.
# mknb writes one grub2 configuration per network for them. The value is the
# $grub_cpu string reported by that architecture's GRUB build, so one file can
# carry a menu entry per architecture that shares a network.
my %GRUB2_DISCOVERY_ARCHES = (
riscv64 => 'riscv64',
);
sub handled_commands {
return {
mknb => 'mknb',
@@ -703,6 +711,9 @@ sub process_request {
}
} elsif ($arch =~ /ppc/) {
mkpath("$tftpdir/pxelinux.cfg/p/");
} elsif (exists $GRUB2_DISCOVERY_ARCHES{$arch}) {
mkpath("$tftpdir/boot/grub2");
chmod(0755, "$tftpdir/boot/grub2");
}
my $dopxe = 0;
foreach (keys %{$normnets}) {
@@ -816,11 +827,97 @@ sub process_request {
print $cfgfile " initrd http://" . $xcatd_address . "$portsuffix/$initrd_file\n";
print $cfgfile ' append "xcatd=' . $xcatd_address . ":$xcatdport $consolecmdline\"\n";
close($cfgfile);
} elsif (exists $GRUB2_DISCOVERY_ARCHES{$arch}) {
# Unlike the PXELINUX files, which only look at the legacy address of the network,
# the discovery configuration is also dropped when the xcatd address chosen for the
# network sits on a :noboot interface: it would send Genesis to an interface that
# must not serve booting.
if (defined($nobootnicips{ $hexnets->{$_} }) or defined($nobootnicips{$xcatd_address})) {
unlink("$tftpdir/boot/grub2/grub.cfg-" . uc($_));
next;
}
_write_grub2_discovery_config(
tftpdir => $tftpdir,
hexnet => $_,
xcatd_address => $xcatd_address,
xcatdport => $xcatdport,
consolecmdline => $consolecmdline,
);
}
}
if (exists $GRUB2_DISCOVERY_ARCHES{$arch} and !-e "$tftpdir/boot/grub2/grub2.$arch") {
# The discovery configurations are only reached through this boot loader, which
# xCAT does not build: say so instead of letting the nodes time out in firmware.
$callback->({ data => ["Note: $tftpdir/boot/grub2/grub2.$arch is missing; $arch nodes need it to reach these configurations (it is installed by grub2-xcat, or copied from the EL $arch installation media)"] });
}
if ($configfileonly) {
$callback->({ data => ["Write netboot config file done"] });
}
}
# Return the grub2-class architectures whose Genesis kernel and initrd are
# published under $tftpdir/xcat, as [arch, grub_cpu, kernel, initrd] with the
# file names relative to the TFTP root.
sub _grub2_discovery_arches {
my ($tftpdir) = @_;
my @present;
foreach my $arch (sort keys %GRUB2_DISCOVERY_ARCHES) {
next unless -e "$tftpdir/xcat/genesis.kernel.$arch";
my ($initrd) = grep { -e "$tftpdir/$_" }
map { "xcat/genesis.fs.$arch.$_" } qw(lzma gz);
next unless $initrd;
push @present,
[ $arch, $GRUB2_DISCOVERY_ARCHES{$arch}, "xcat/genesis.kernel.$arch", $initrd ];
}
return @present;
}
# Write the grub2 discovery configuration for one network.
#
# grub2.<arch>, net booted from $tftpdir/boot/grub2, looks for grub.cfg-01-<mac>,
# then grub.cfg-<8 hex digit ip>, then ever shorter prefixes of that ip, and
# finally grub.cfg. nodeset writes the per-node files (full ip and mac), so a
# per-network prefix is only reached by clients without a node configuration:
# discovery. The file is rebuilt from the Genesis artifacts present under
# $tftpdir/xcat and removed when none are left. Returns the path written.
sub _write_grub2_discovery_config {
my (%args) = @_;
my $tftpdir = $args{tftpdir};
my $hexnet = uc($args{hexnet});
my $cfgpath = "$tftpdir/boot/grub2/grub.cfg-$hexnet";
my @arches = _grub2_discovery_arches($tftpdir);
unless (@arches) {
unlink($cfgpath);
return;
}
my $cmdline = "xcatd=$args{xcatd_address}:$args{xcatdport}";
if (defined($args{consolecmdline}) and $args{consolecmdline} ne '') {
$cmdline .= " $args{consolecmdline}";
}
my $content = "# xCAT Genesis discovery for network $hexnet - generated by mknb, do not edit\n";
$content .= "set default=0\n";
$content .= "set timeout=5\n";
my $keyword = 'if';
foreach my $entry (@arches) {
my ($arch, $grub_cpu, $kernel, $initrd) = @{$entry};
$content .= "$keyword [ \"\$grub_cpu\" = \"$grub_cpu\" ]; then\n";
$content .= "menuentry \"xCAT Genesis $arch\" {\n";
$content .= " linux /$kernel $cmdline BOOTIF=\$net_default_mac\n";
$content .= " initrd /$initrd\n";
$content .= "}\n";
$keyword = 'elif';
}
$content .= "fi\n";
# nodeset hard-links grub.cfg-<8 hex digit ip> to the node file; on a /32 network that is
# this file's name, so replace the name instead of truncating a shared inode.
unlink($cfgpath);
my $cfg;
open($cfg, ">", $cfgpath) or return;
print $cfg $content;
close($cfg);
chmod(0644, $cfgpath);
return $cfgpath;
}
1;