From 8253a4882e36807591429bd8efdf1d1d4d949d9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Sun, 27 Sep 2026 13:50:18 -0300 Subject: [PATCH] feat(dhcp): check an external ISC server for the iPXE feature options makedhcp does not write the configuration of a DHCP server that site.externaldhcpservers names, so an upgrade leaves it without the iPXE feature options. OMAPI removes a host before it adds it again, so a host statement that such a server rejects loses the reservation of the node. Before it updates netboot=ipxe nodes on that server, makedhcp creates a host of its own whose statement tests every iPXE feature option, reads it back in a second omshell run, and removes it. Only the server can return the address of that host, so the nodes are updated only when it does. Otherwise makedhcp reports the server and leaves the reservations of those nodes unchanged. Other nodes and a local server get no check. Each run uses its own host name and address, as other xCAT servers can check the same server at the same time. The check opens no host before the create, because the omshell of Ubuntu 20.04 and 22.04 can hang on a failed open. --- xCAT-server/lib/xcat/plugins/dhcp.pm | 43 ++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/xCAT-server/lib/xcat/plugins/dhcp.pm b/xCAT-server/lib/xcat/plugins/dhcp.pm index e266bf536..37e9b7395 100644 --- a/xCAT-server/lib/xcat/plugins/dhcp.pm +++ b/xCAT-server/lib/xcat/plugins/dhcp.pm @@ -254,6 +254,41 @@ sub _isc_declare_ipxe_features return; } +# An external dhcpd keeps a configuration that makedhcp does not write, and OMAPI removes a host before +# it adds it again. The netboot=ipxe nodes stay unchanged unless that dhcpd keeps a host that tests the +# iPXE feature options: a second omshell reads it back, as only the dhcpd can return its address. +sub _isc_external_ipxe_check +{ + my ($nodes, $nrhash, $settings, $secret) = @_; + + my $server = $::XCATSITEVALS{externaldhcpservers}; + return ({}) unless $server; + my @ipxe = grep { ((($nrhash->{$_} || [])->[0] || {})->{netboot} // '') eq 'ipxe' } @$nodes; + return ({}) unless @ipxe; + + # A host of its own for each run, as other xCAT servers can check the same dhcpd at the same time. + # No cleanup open before the create: the omshell of Ubuntu 20.04 and 22.04 can hang on a failed open. + my $id = sprintf('%04x%06x', $$ & 0xffff, int(rand(0x1000000))); + my $name = "xcat-ipxe-check-$id"; + my $mac = join ':', '02', unpack('(A2)5', substr($id, 0, 10)); + my $test = join ' and ', map { /^option (\S+) code / ? "exists $1" : () } + @{ xCAT::DHCP::BootPolicy->isc_ipxe_feature_option_lines() }; + my $connect = xCAT::DHCP::OmapiPolicy->omshell_preamble($settings, secret => $secret, server => $server) . "connect\n"; + my @created = _run_omshell( + $connect . "new host\nset name = \"$name\"\nset hardware-address = $mac\nset hardware-type = 1\n" + . "set statements = \"if $test { filename = \\\"\\\"; }\"\ncreate\nclose\n", + $settings); + my @kept = _run_omshell($connect . "new host\nset name = \"$name\"\nopen\nremove\nclose\n", $settings); + return ({}) if grep { /^hardware-address = \Q$mac\E$/ } @kept; + + my $why = (grep { /parse error/i } @created) + ? "does not declare the iPXE feature options that the host statements of netboot=ipxe nodes test" + : "did not confirm a check of the iPXE feature options"; + return ({ map { $_ => 1 } @ipxe }, + "The DHCP server $server $why, so makedhcp leaves the reservations of these nodes unchanged: @ipxe. " + . "Declare the gpxe options of a makedhcp -n configuration on that server."); +} + sub _isc_static_host_fallback { return _ubuntu_isc_omapi_limited() && !$::XCATSITEVALS{externaldhcpservers}; @@ -2479,6 +2514,7 @@ sub process_request } } + my ($omapi_settings, $omapi_secret); if ($^O ne 'aix' and !_isc_static_host_fallback()) { my $settings = _omapi_settings(); @@ -2491,6 +2527,7 @@ sub process_request syslog("local4|err", "Unable to access omapi key from passwd table, unable to update DHCP configuration"); return; } # TODO sane err + ($omapi_settings, $omapi_secret) = ($settings, $ent->{password}); #Have nodes to update #open2($omshellout,$omshell,"/usr/bin/omshell"); @@ -2551,6 +2588,11 @@ sub process_request } my $vpdtab = xCAT::Table->new('vpd'); $vpdhash = $vpdtab->getNodesAttribs($req->{node}, ['uuid']); + my ($unchanged, $external_error) = ({}); + if ($omapi_settings and not $opt{d}) { + ($unchanged, $external_error) = _isc_external_ipxe_check($req->{node}, $nrhash, $omapi_settings, $omapi_secret); + $callback->({ error => [$external_error], errorcode => [1] }) if $external_error; + } foreach (@{ $req->{node} }) { if ($opt{d}) @@ -2570,6 +2612,7 @@ sub process_request { next; } + next if $unchanged->{$_}; addnode $_; if ($usingipv6) { addnode6 $_;