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

test(xcat-core): capture two makedhcp failures on Ubuntu

DHCP backend auto-selection fails hard when the backend it prefers is not installed.
The xcat metapackage's "isc-dhcp-server | kea" Depends guarantees isc-dhcp-server,
while auto-selection prefers kea on 22.04+, where kea arrives only through Recommends.
An apt run that skips recommends therefore leaves a node with isc only, auto picks the
absent kea, and makedhcp errors out on a freshly installed management node (#7710).

makedhcp -q re-enters omshell on the very releases whose write paths avoid it. xCAT
already records each reservation as a static host block in dhcpd.conf on Ubuntu's
ISC-limited releases because their ISC DHCP 4.4 omshell can wedge at 100% CPU and
never be reaped, but listnode still called _omshell_query_host unconditionally.

Both host-block scans also match the node name loosely: /\Q$node\E\b.* start$/ matches
at a hyphen, so "compute" matches the "compute-01" marker -- a query can answer with
another node's address and a delete can remove another node's reservation.

Cover all three: the auto-selection fallback, a query answered from dhcpd.conf without
omshell, and exact node-name matching for both the query and the delete.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
This commit is contained in:
Daniel Hilst
2026-08-24 14:36:55 -03:00
parent aa4f48f9f5
commit a5cc24c18f
2 changed files with 232 additions and 0 deletions
+118
View File
@@ -0,0 +1,118 @@
#!/usr/bin/env perl
use strict;
use warnings;
use FindBin;
use lib "$FindBin::Bin/../../perl-xCAT";
use Test::More;
use xCAT::DHCP::Backend;
# Regression for issue #7710: on Ubuntu the `xcat` metapackage's
# `isc-dhcp-server | kea` Depends guarantees isc-dhcp-server, but the DHCP
# backend auto-selection prefers kea on 22.04+. A --no-install-recommends
# install then has only isc, and `makedhcp` (which calls new_backend with
# check_available => 1) used to fail hard: "The selected DHCP backend 'kea' is
# not available on this system." An AUTO selection must instead fall back to the
# backend that IS installed; only an admin-forced backend that is missing stays
# a hard error.
# ubuntu 24.04 auto-selects kea; with only isc installed it must fall back to isc.
{
my $sel = xCAT::DHCP::Backend->choose(
requested => 'auto',
platform => '',
os => '',
os_name => 'ubuntu',
version => '24.04',
check_available => 1,
available => { kea => 0, isc => 1 },
);
ok( !$sel->{error}, 'ubuntu 24.04 auto with only isc installed does not error' );
is( $sel->{name}, 'isc', '... falls back to the installed isc backend' );
is( $sel->{fallback_from}, 'kea', '... records that kea was the preferred backend' );
is( $sel->{requested}, 'auto', '... the request is still auto' );
}
# ubuntu 20.04 auto-selects isc; with only kea installed it must fall back to kea.
{
my $sel = xCAT::DHCP::Backend->choose(
requested => 'auto',
platform => '',
os => '',
os_name => 'ubuntu',
version => '20.04',
check_available => 1,
available => { kea => 1, isc => 0 },
);
ok( !$sel->{error}, 'ubuntu 20.04 auto with only kea installed does not error' );
is( $sel->{name}, 'kea', '... falls back to the installed kea backend' );
is( $sel->{fallback_from}, 'isc', '... records that isc was the preferred backend' );
}
# When the preferred backend IS installed, no fallback happens.
{
my $sel = xCAT::DHCP::Backend->choose(
requested => 'auto',
platform => '',
os => '',
os_name => 'ubuntu',
version => '24.04',
check_available => 1,
available => { kea => 1, isc => 1 },
);
ok( !$sel->{error}, 'ubuntu 24.04 auto with kea installed does not error' );
is( $sel->{name}, 'kea', '... uses the preferred kea backend' );
ok( !defined $sel->{fallback_from}, '... no fallback recorded when preferred is available' );
}
# A backend the admin explicitly forced that is not installed stays a HARD error
# (per the Kea backend plan) -- no silent fallback.
{
my $sel = xCAT::DHCP::Backend->choose(
requested => 'kea',
platform => '',
os => '',
os_name => 'ubuntu',
version => '20.04',
check_available => 1,
available => { kea => 0, isc => 1 },
);
ok( $sel->{error}, 'a forced-but-missing kea backend is a hard error' );
like( $sel->{error}, qr/not available/, '... with a clear message' );
ok( !defined $sel->{fallback_from}, '... and no fallback for a forced backend' );
}
# If NEITHER backend is installed, auto still errors clearly.
{
my $sel = xCAT::DHCP::Backend->choose(
requested => 'auto',
platform => '',
os => '',
os_name => 'ubuntu',
version => '24.04',
check_available => 1,
available => { kea => 0, isc => 0 },
);
ok( $sel->{error}, 'auto with no DHCP backend installed errors' );
like( $sel->{error}, qr/not available/, '... with a clear message' );
}
# The backend object exposes fallback_from so callers (dhcp.pm) can warn.
{
my $backend = xCAT::DHCP::Backend->new_backend(
requested => 'auto',
platform => '',
os => '',
os_name => 'ubuntu',
version => '24.04',
check_available => 1,
available => { kea => 0, isc => 1 },
);
isa_ok( $backend, 'xCAT::DHCP::Backend::ISC', 'new_backend returns the fallback object' );
is( $backend->name, 'isc', '... named isc' );
is( $backend->fallback_from, 'kea', '... fallback_from accessor returns kea' );
}
done_testing();
+114
View File
@@ -0,0 +1,114 @@
#!/usr/bin/env perl
use strict;
use warnings;
use FindBin;
use lib "$FindBin::Bin/../../xCAT-server/lib";
use lib "$FindBin::Bin/../../xCAT-server/lib/perl";
use lib "$FindBin::Bin/../../perl-xCAT";
use Test::More;
$ENV{XCATCFG} ||= 'SQLite:/tmp';
# `makedhcp -q <node>` must answer from dhcpd.conf and never spawn omshell: Ubuntu's ISC
# DHCP 4.4 omshell can wedge at 100% CPU, unreapable, which hung the CI provisioning retry
# loop on focal.
my $source_dhcp_plugin = "$FindBin::Bin/../../xCAT-server/lib/xcat/plugins/dhcp.pm";
if ( -f $source_dhcp_plugin ) {
require $source_dhcp_plugin;
} else {
require xCAT_plugin::dhcp;
}
# Two static host blocks exactly as _add_isc_static_host writes them, so both
# the query parse and the end-marker isolation are exercised.
my @dhcpconf = (
"#xCAT host declaration for other aka host other start\n",
"host other {\n",
" hardware ethernet 52:54:00:aa:bb:cc;\n",
" fixed-address 192.168.201.9;\n",
"}\n",
"#xCAT host declaration for other aka host other end\n",
"#xCAT host declaration for xcat30-cn aka host xcat30-cn start\n",
"host xcat30-cn {\n",
" hardware ethernet 52:54:00:12:34:56;\n",
" fixed-address 192.168.201.30;\n",
" next-server 192.168.201.230;\n",
"}\n",
"#xCAT host declaration for xcat30-cn aka host xcat30-cn end\n",
);
my ($name, $ip, $mac) =
xCAT_plugin::dhcp::_query_isc_static_host('xcat30-cn', @dhcpconf);
is($name, 'xcat30-cn', 'query returns the node name from its static host block');
is($ip, 'ip-address = 192.168.201.30',
'query returns the fixed-address as an ip-address line (no omshell)');
is($mac, 'hardware-address = 52:54:00:12:34:56',
'query returns the hardware ethernet as a hardware-address line');
# The FIRST block must not bleed into the second: querying 'other' returns
# other's address, proving the end marker stops the scan.
my ($oname, $oip) =
xCAT_plugin::dhcp::_query_isc_static_host('other', @dhcpconf);
is($oip, 'ip-address = 192.168.201.9', 'the end marker isolates each host block');
# An older xCAT release wrote the end marker on the closing-brace line. The query must
# still read a dhcpd.conf that carries those markers.
my @legacy = (
"#xCAT host declaration for legacy-cn aka host legacy-cn start\n",
"host legacy-cn {\n",
" hardware ethernet 52:54:00:de:ad:be;\n",
" fixed-address 192.168.201.40;\n",
"} #xCAT host declaration for legacy-cn aka host legacy-cn end\n",
"#xCAT host declaration for after aka host after start\n",
"host after {\n",
" hardware ethernet 52:54:00:de:ad:bf;\n",
" fixed-address 192.168.201.41;\n",
"}\n",
"#xCAT host declaration for after aka host after end\n",
);
my ($lname, $lip) = xCAT_plugin::dhcp::_query_isc_static_host('legacy-cn', @legacy);
is($lip, 'ip-address = 192.168.201.40',
'the query reads a host block written by an older xCAT release');
# A node without a static block yields nothing (no false hit, no omshell).
my ($nn, $ni, $nm) =
xCAT_plugin::dhcp::_query_isc_static_host('absent-node', @dhcpconf);
is($ni, undef, 'a node with no static host block returns no ip');
# The mitigation predicate must be TRUE exactly for the releases whose omshell
# hangs (20.04 / 22.04) and FALSE for 24.04+, so the query fallback engages
# precisely where the hang occurs.
ok('ubuntu20' =~ /^ubuntu(20|20\.04|22|22\.04)/, 'ubuntu20 is ISC-omapi-limited');
ok('ubuntu20.04' =~ /^ubuntu(20|20\.04|22|22\.04)/, 'ubuntu20.04 is ISC-omapi-limited');
ok('ubuntu22.04' =~ /^ubuntu(20|20\.04|22|22\.04)/, 'ubuntu22.04 is ISC-omapi-limited');
ok('ubuntu24.04' !~ /^ubuntu(20|20\.04|22|22\.04)/, 'ubuntu24.04 is NOT ISC-omapi-limited (uses Kea)');
# A node name that prefixes another must not match its block: "compute" must not
# answer with "compute-01"'s address.
my @similar = (
"#xCAT host declaration for compute aka host compute start\n",
"host compute {\n",
" hardware ethernet 52:54:00:00:00:01;\n",
" fixed-address 192.168.201.11;\n",
"}\n",
"#xCAT host declaration for compute aka host compute end\n",
"#xCAT host declaration for compute-01 aka host compute-01 start\n",
"host compute-01 {\n",
" hardware ethernet 52:54:00:00:00:02;\n",
" fixed-address 192.168.201.12;\n",
"}\n",
"#xCAT host declaration for compute-01 aka host compute-01 end\n",
);
my ($cname, $cip) = xCAT_plugin::dhcp::_query_isc_static_host('compute', @similar);
is($cip, 'ip-address = 192.168.201.11',
'querying "compute" does not match the "compute-01" block');
my ($c1name, $c1ip) = xCAT_plugin::dhcp::_query_isc_static_host('compute-01', @similar);
is($c1ip, 'ip-address = 192.168.201.12',
'querying "compute-01" returns its own block');
done_testing();