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

fix(xcat-core): make makedhcp work on a stock Ubuntu management node

Fall back to an available DHCP backend on auto-selection. When the request is "auto"
and the backend chosen for this OS is not installed, use the other one if it is,
recording fallback_from so process_request can tell the operator which preferred
backend is missing. A backend the admin forced through site.dhcpbackend still fails
hard when absent, and "neither installed" still errors clearly. Fixes #7710.

Answer makedhcp -q from the static host block on Ubuntu's ISC-limited releases.
listnode now branches on _isc_static_host_fallback() before any omapi work and reads
the node's fixed-address and hardware ethernet straight out of dhcpd.conf, so the
query path never spawns the omshell its own write paths already avoid. A node with no
reservation is now reported rather than answered with silence.

Match the host-block markers exactly. _add_isc_static_host writes a fully determined
pair -- "#xCAT host declaration for <node> aka host <hostname> start" and the "}"
line carrying the matching end -- so both scans anchor on that whole shape through
shared _isc_host_start_re/_isc_host_end_re helpers. The previous /\Q$node\E\b.*/ also
matched at a hyphen, letting node "compute" act on "compute-01"'s block: the query
could return another node's address and the delete could remove another node's
reservation. _delete_isc_static_host also accepts an explicit line list now, so the
scan is unit testable without file-scoped state.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
This commit is contained in:
Daniel Hilst
2026-08-24 14:39:04 -03:00
parent a5cc24c18f
commit a11bd9e43d
4 changed files with 111 additions and 6 deletions
+14
View File
@@ -33,6 +33,20 @@ sub choose {
my $selected = $normalized eq 'auto' ? $class->default_backend(%args) : $normalized;
if ( $args{check_available} && !$class->available( $selected, %args ) ) {
# Ubuntu's metapackage guarantees isc-dhcp-server but only Recommends kea, so a
# --no-install-recommends install has auto preferring a kea that is not there. Fall
# back to whatever is installed; an explicitly forced backend still fails hard. #7710
if ( $normalized eq 'auto' ) {
for my $alt (qw(kea isc)) {
next if $alt eq $selected;
next unless $class->available( $alt, %args );
return {
requested => $normalized,
name => $alt,
fallback_from => $selected,
};
}
}
return {
requested => $normalized,
name => $selected,
+6
View File
@@ -12,6 +12,12 @@ sub name {
return 'isc';
}
# The backend auto-selection preferred, and undef when it did not fall back. #7710
sub fallback_from {
my $self = shift;
return ref($self) ? $self->{selection}{fallback_from} : undef;
}
sub implemented {
return 1;
}
+6
View File
@@ -22,6 +22,12 @@ sub name {
return 'kea';
}
# The backend auto-selection preferred, and undef when it did not fall back. #7710
sub fallback_from {
my $self = shift;
return ref($self) ? $self->{selection}{fallback_from} : undef;
}
sub implemented {
return 1;
}
+85 -6
View File
@@ -230,17 +230,34 @@ sub _isc_static_host_fallback
return _ubuntu_isc_omapi_limited() && !$::XCATSITEVALS{externaldhcpservers};
}
# Match the whole marker _add_isc_static_host writes: a looser /\Q$node\E\b/ ends at a
# hyphen, so node "compute" would match "compute-01" and act on the wrong block. An older
# xCAT wrote the end marker on the closing-brace line, so that brace stays optional.
sub _isc_host_start_re
{
my ($node, $hostname) = @_;
return defined($hostname)
? qr/^#xCAT host declaration for \Q$node\E aka host \Q$hostname\E start$/
: qr/^#xCAT host declaration for \Q$node\E aka host .* start$/;
}
sub _isc_host_end_re
{
my ($node, $hostname) = @_;
return defined($hostname)
? qr/^(?:\}\s*)?#xCAT host declaration for \Q$node\E aka host \Q$hostname\E end$/
: qr/^(?:\}\s*)?#xCAT host declaration for \Q$node\E aka host .* end$/;
}
sub _delete_isc_static_host
{
my ($node, $config, $hostname) = @_;
$config ||= \@dhcpconf;
my $start_marker = defined($hostname)
? qr/^#xCAT host declaration for \Q$node\E aka host \Q$hostname\E start$/
: qr/^#xCAT host declaration for \Q$node\E aka host .* start$/;
my $end_marker = defined($hostname)
? qr/^(?:\}\s*)?#xCAT host declaration for \Q$node\E aka host \Q$hostname\E end$/
: qr/^(?:\}\s*)?#xCAT host declaration for \Q$node\E aka host .* end$/;
my $start_marker = _isc_host_start_re($node, $hostname);
my $end_marker = _isc_host_end_re($node, $hostname);
my @updated;
my $skip = 0;
@@ -329,6 +346,45 @@ sub _add_isc_static_host
$restartdhcp = 1;
}
# Answer `makedhcp -q <node>` from dhcpd.conf rather than omshell, which on Ubuntu's ISC
# 4.4 can wedge at 100% CPU and never be reaped -- the same reason the write paths avoid it.
# Returns the shape _parse_omshell_host_output does, so listnode prints it unchanged.
sub _query_isc_static_host
{
my $node = shift;
my @lines = @_ ? @_ : @dhcpconf;
# Only the reconfigure paths populate @dhcpconf, so a bare query reads the file itself.
if (!@lines && $dhcpconffile && -r $dhcpconffile) {
if (open(my $dhfh, '<', $dhcpconffile)) {
@lines = <$dhfh>;
close($dhfh);
}
}
my $start_re = _isc_host_start_re($node);
my $end_re = _isc_host_end_re($node);
my ($nname, $ipaddr, $hwaddr);
my $skip = 0;
foreach my $line (@lines) {
if ($line =~ $start_re) {
$skip = 1;
$nname = $node;
next;
}
last if $skip && $line =~ $end_re;
next unless $skip;
if ($line =~ /^\s*hardware\s+ethernet\s+(.+?)\s*;/) {
$hwaddr = "hardware-address = $1";
} elsif ($line =~ /^\s*fixed-address\s+(.+?)\s*;/) {
$ipaddr = "ip-address = $1";
}
}
return ($nname, $ipaddr, $hwaddr);
}
sub _open_omshell_writer
{
my $settings = shift;
@@ -489,6 +545,21 @@ sub listnode
my $callback = shift;
my $rsp;
# On Ubuntu's ISC-limited releases the omshell host query can wedge at 100% CPU and never
# be reaped, so answer from the static host block xCAT already wrote into dhcpd.conf and
# never spawn omshell. This runs before the omapi key lookup below, which is moot here.
if (_isc_static_host_fallback()) {
my ($sname, $sip, $shw) = _query_isc_static_host($node);
if ($sip) {
push @{ $rsp->{data} }, "$sname: $sip, $shw";
xCAT::MsgUtils->message("I", $rsp, $callback);
} else {
$rsp->{data}->[0] = "$node: no DHCP reservation found in $dhcpconffile";
xCAT::MsgUtils->message("I", $rsp, $callback);
}
return;
}
my $settings = _omapi_settings($callback);
return unless $settings;
@@ -1803,6 +1874,14 @@ sub process_request
xCAT::MsgUtils->message("E", $rsp, $callback, 1);
return;
}
if ( $backend->can('fallback_from') && ( my $from = $backend->fallback_from ) ) {
my $rsp = {};
$rsp->{data}->[0] =
"DHCP backend '$from' auto-selected for this OS is not installed; "
. "falling back to the available '" . $backend->name . "' backend. "
. "Install '$from' or set site.dhcpbackend to silence this.";
xCAT::MsgUtils->message("W", $rsp, $callback);
}
if ( $backend->name eq 'kea' && $statements ) {
my $rsp = {};
$rsp->{data}->[0] = "The -s option contains ISC DHCP statement text and is not supported with the Kea DHCP backend.";