2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-10-21 07:08:12 +00:00

feat(dhcp): boot unknown x86 clients with the upstream iPXE loader

An x86 client without a node definition, such as a node in discovery,
now gets xcat/ipxe/i386/undionly.kpxe for BIOS and
xcat/ipxe/x86_64-sb/snponly-shim.efi for UEFI. Once it reports the iPXE
features of its path, it gets the Genesis script of its network from
xcat/ipxe/nets, which mknb writes. ISC and Kea apply the same test. As
for xNBA, neither backend names a loader that the local TFTP directory
lacks, or the second stage that the loader would fetch.

Kea gave every x86 client its first-stage loader from the global
classes, which now name the upstream loader. An xnba node therefore
carries its own first-stage classes, which name xcat/xnba.kpxe and
xcat/xnba.efi when the local xNBA BIOS file exists, as its ISC host
statements do. An ipxe node carries its own classes for the upstream
loader, because an upgrade keeps the global classes of an older
makedhcp -n until the next makedhcp -n. The ISC host statements of a
node do not change.
This commit is contained in:
Vinícius Ferrão
2026-10-02 19:44:12 -03:00
parent 76bf639910
commit 5e4993255b
2 changed files with 42 additions and 28 deletions
+27 -21
View File
@@ -6,14 +6,15 @@ use warnings;
sub kea_client_classes {
my ( $class, %opts ) = @_;
my $loader = $class->x86_loader();
# An unknown x86 client boots the upstream loader.
my $loader = $class->x86_loader( method => 'ipxe' );
my $uefi_x64_arch_match = uefi_x64_client_architecture_match_expr();
my $etherboot = etherboot_vendor_class_test();
# No substitute when the loader is not on disk: naming a file the TFTP
# server does not have costs the client a timeout it cannot diagnose, and a
# different loader boots something nobody asked for.
my $bios_boot = $opts{xnba_kpxe} ? $loader->{bios} : '';
my $uefi_boot = $opts{xnba_efi} ? $loader->{uefi} : '';
my $bios_boot = $opts{ipxe_bios} ? $loader->{bios} : '';
my $uefi_boot = $opts{ipxe_uefi} ? $loader->{uefi} : '';
my @classes;
push @classes, @{ $opts{xnba_node_classes} || [] };
@@ -288,7 +289,8 @@ sub isc_client_architecture_lines {
my $portsuffix = $opts{portsuffix} // '';
my $net = $opts{net} // '';
my $maskbits = $opts{prefix} // '';
my $loader = $class->x86_loader();
my $loader = $class->x86_loader( method => 'ipxe' );
my $nets = "http://$tftp$portsuffix/tftpboot/$loader->{scripts}/nets/${net}_${maskbits}";
# Which loaders are actually on disk. A branch naming a file the TFTP server
# does not have costs the client a full timeout it cannot diagnose, so the
@@ -306,19 +308,19 @@ sub isc_client_architecture_lines {
if ($kpxe) {
push @branches, [
"$loader->{isc_second_stage_bios} and option client-architecture = 00:00 { #x86, xCAT Network Boot Agent\n",
"$loader->{isc_second_stage_bios} and option client-architecture = 00:00 { #x86, iPXE second stage\n",
" always-broadcast on;\n",
" filename = \"http://$tftp$portsuffix/tftpboot/xcat/xnba/nets/${net}_${maskbits}\";\n",
" filename = \"$nets\";\n",
];
}
if ($efi) {
push @branches, [
"$loader->{isc_second_stage_uefi} and option client-architecture = 00:09 { #x86, xCAT Network Boot Agent\n",
" filename = \"http://$tftp$portsuffix/tftpboot/xcat/xnba/nets/${net}_${maskbits}.uefi\";\n",
"$loader->{isc_second_stage_uefi} and option client-architecture = 00:09 { #x86, iPXE second stage\n",
" filename = \"$nets.uefi\";\n",
],
[
"$loader->{isc_second_stage_uefi} and option client-architecture = 00:07 { #x86-64 UEFI, xCAT Network Boot Agent\n",
" filename = \"http://$tftp$portsuffix/tftpboot/xcat/xnba/nets/${net}_${maskbits}.uefi\";\n",
"$loader->{isc_second_stage_uefi} and option client-architecture = 00:07 { #x86-64 UEFI, iPXE second stage\n",
" filename = \"$nets.uefi\";\n",
];
}
if ($kpxe) {
@@ -455,20 +457,22 @@ sub kea_xnba_node_classes {
};
}
# The global first-stage classes give the loader of unknown clients, and do not know which
# nodes boot from SAN.
next unless $loader->{method} eq 'ipxe';
# Each node carries its own first-stage classes, as its ISC host statements do, because an
# upgrade keeps the global classes of an older makedhcp -n until the next makedhcp -n. An
# xnba node has them only with the local xNBA BIOS file, as it has its ISC host statements.
next if $loader->{method} eq 'xnba' && !$opts{xnba_kpxe};
my $first_context = $loader->{method} eq 'ipxe' ? $context : _node_user_context( $node, 'xnba-first-stage' );
push @classes, {
name => "$class_base-bios-first-stage",
test => "option[93].hex == 0x0000 and not ($loader->{kea_second_stage_bios}) and $mac_test",
'boot-file-name' => $loader->{bios},
'user-context' => $context,
'user-context' => $first_context,
},
{
name => "$class_base-uefi-first-stage",
test => "($uefi_x64_arch_match) and not ($loader->{kea_second_stage_uefi}) and $mac_test",
'boot-file-name' => $loader->{uefi},
'user-context' => $context,
'user-context' => $first_context,
};
}
@@ -683,7 +687,7 @@ sub kea_xnba_network_classes {
return [] unless $opts{net} && defined $opts{prefix} && $opts{next_server};
my $loader = $class->x86_loader();
my $loader = $class->x86_loader( method => 'ipxe' );
my $uefi_x64_arch_match = uefi_x64_client_architecture_match_expr();
my $httpport = $opts{httpport} || '80';
my $portsuffix = ( $httpport eq '80' ) ? '' : ":$httpport";
@@ -691,21 +695,23 @@ sub kea_xnba_network_classes {
my $safe_network = $network_id;
$safe_network =~ s/[^A-Za-z0-9_.-]/_/g;
my $base_url = 'http://' . $opts{next_server} . $portsuffix
. '/tftpboot/xcat/xnba/nets/' . $network_id;
. "/tftpboot/$loader->{scripts}/nets/" . $network_id;
my @classes;
if ( $opts{xnba_kpxe} ) {
# The second stage is fetched over HTTP, but it is the first stage that asks for it.
if ( $opts{ipxe_bios} ) {
push @classes, {
name => "xcat-xnba-net-$safe_network-bios",
name => "xcat-ipxe-net-$safe_network-bios",
test => "$loader->{kea_second_stage_bios} and option[93].hex == 0x0000",
'boot-file-name' => $base_url,
additional_only => 1,
};
}
if ( $opts{xnba_efi} ) {
if ( $opts{ipxe_uefi} ) {
push @classes, {
name => "xcat-xnba-net-$safe_network-uefi",
name => "xcat-ipxe-net-$safe_network-uefi",
test => "$loader->{kea_second_stage_uefi} and ($uefi_x64_arch_match)",
'boot-file-name' => "$base_url.uefi",
additional_only => 1,
+15 -7
View File
@@ -3549,8 +3549,7 @@ sub kea_subnet4_intent
prefix => $prefix,
next_server => $tftp,
httpport => $httpport,
xnba_kpxe => -f "$tftpdir/xcat/xnba.kpxe" ? 1 : 0,
xnba_efi => -f "$tftpdir/xcat/xnba.efi" ? 1 : 0,
kea_ipxe_loader_flags(),
);
}
my %subnet = (
@@ -3912,6 +3911,7 @@ sub kea_set_drop_client_class
#: that changes netboot method loses the classes the old one wrote.
my %KEA_NODE_CLASS_PURPOSES = map { $_ => 1 } qw(
xnba-second-stage
xnba-first-stage
ipxe-boot
pxe-vendor
proxydhcp-deferral
@@ -4061,8 +4061,9 @@ sub kea_node_client_classes_for_nodes
return {
classes => [
@{ xCAT::DHCP::BootPolicy->kea_xnba_node_classes(
nodes => \@xnba,
xnba_efi => -f "$tftpdir/xcat/xnba.efi" ? 1 : 0,
nodes => \@xnba,
xnba_kpxe => -f "$tftpdir/xcat/xnba.kpxe" ? 1 : 0,
xnba_efi => -f "$tftpdir/xcat/xnba.efi" ? 1 : 0,
) },
@{ xCAT::DHCP::BootPolicy->kea_pxe_node_classes( nodes => \@pxe ) },
@{ xCAT::DHCP::BootPolicy->kea_proxydhcp_node_classes( nodes => \@proxydhcp ) },
@@ -4401,9 +4402,16 @@ sub kea_onie_url_for_node
sub kea_boot_client_classes
{
return xCAT::DHCP::BootPolicy->kea_client_classes(
xnba_kpxe => -f "$tftpdir/xcat/xnba.kpxe" ? 1 : 0,
xnba_efi => -f "$tftpdir/xcat/xnba.efi" ? 1 : 0,
return xCAT::DHCP::BootPolicy->kea_client_classes( kea_ipxe_loader_flags() );
}
# Which upstream loader files the local TFTP tree has, as the Kea class builders take them.
sub kea_ipxe_loader_flags
{
my $loader = xCAT::DHCP::BootPolicy->x86_loader( method => 'ipxe' );
return (
ipxe_bios => -f "$tftpdir/$loader->{bios}" ? 1 : 0,
ipxe_uefi => -f "$tftpdir/$loader->{uefi}" ? 1 : 0,
);
}