2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-08-28 09:36:40 +00:00

Merge pull request #7727 from VersatusHPC/refactor/kea-shared-service-mapping

refactor(kea): reuse shared service mapping
This commit is contained in:
Daniel Hilst
2026-08-24 12:27:38 -03:00
committed by GitHub
3 changed files with 316 additions and 34 deletions
+27 -34
View File
@@ -11,12 +11,6 @@ use Text::ParseWords qw/shellwords/;
use xCAT::DHCP::Range;
use xCAT::NetworkUtils;
my %KEA_SERVICE_CANDIDATES = (
'kea-dhcp4' => [ 'kea-dhcp4', 'kea-dhcp4-server' ],
'kea-dhcp6' => [ 'kea-dhcp6', 'kea-dhcp6-server' ],
'kea-dhcp-ddns' => [ 'kea-dhcp-ddns', 'kea-dhcp-ddns-server' ],
'kea-ctrl-agent' => [ 'kea-ctrl-agent' ],
);
my @KEA_ACCOUNT_CANDIDATES = ( 'kea', '_kea' );
sub new {
@@ -1024,36 +1018,35 @@ sub _kea_group {
sub _kea_service {
my ( $self, $service ) = @_;
# Kea service names are package-specific, not strictly distribution-specific.
# Prefer the unit that is actually installed so derivatives, backports, and
# locally rebuilt packages do not need a distro/version decision tree here.
foreach my $candidate ( @{ $KEA_SERVICE_CANDIDATES{$service} || [$service] } ) {
return $candidate if $self->_service_available($candidate);
}
return $service
unless $service =~ /\Akea-(?:dhcp4|dhcp6|dhcp-ddns|ctrl-agent)\z/;
return $service;
}
require xCAT::Utils;
sub _service_available {
my ( $self, $service ) = @_;
my $unit = "$service.service";
foreach my $dir ( @{ $self->{service_unit_dirs} || _systemd_unit_dirs() } ) {
return 1 if -e "$dir/$unit";
}
return 1 if -x "/etc/init.d/$service";
return 0;
}
sub _systemd_unit_dirs {
return [
'/etc/systemd/system',
'/run/systemd/system',
'/usr/lib/systemd/system',
'/lib/systemd/system',
];
# Preserve the backend's established candidate subset while taking the
# candidate order and filesystem resolution from the shared service map.
my $candidate_limit = $service eq 'kea-ctrl-agent' ? 1 : 2;
return xCAT::Utils->servicemap(
$service,
{
candidate_limit => $candidate_limit,
searches => [
{
paths => $self->{service_unit_dirs} || [
'/etc/systemd/system',
'/run/systemd/system',
'/usr/lib/systemd/system',
'/lib/systemd/system',
],
suffix => '.service',
},
{
paths => $self->{service_init_dirs} || ['/etc/init.d'],
executable => 1,
},
],
},
) || $service;
}
sub _kea_socket_dir {
+21
View File
@@ -4037,6 +4037,8 @@ sub specialservicemgr {
0: SYSVinit
1: systemd
2: upstart
or a filesystem search specification for candidate-major
service resolution
Returns:
the name of service unit or service daemon
undef on fail
@@ -4088,6 +4090,25 @@ sub servicemap {
"mysql" => [ "mysqld", "mysql", "mariadb" ],
);
if (ref($svcmgrtype) eq 'HASH') {
my @candidates = $svchash{$svcname} ? @{ $svchash{$svcname} } : ($svcname);
my $candidate_limit = $svcmgrtype->{candidate_limit};
if ($candidate_limit && @candidates > $candidate_limit) {
@candidates = @candidates[ 0 .. $candidate_limit - 1 ];
}
foreach my $candidate (@candidates) {
foreach my $search (@{ $svcmgrtype->{searches} || [] }) {
foreach my $searchpath (@{ $search->{paths} || [] }) {
my $candidate_path = $searchpath . "/" . $candidate . ( $search->{suffix} || "" );
return $candidate if $search->{executable} ? -x $candidate_path : -e $candidate_path;
}
}
}
my $not_found;
return $not_found;
}
my $path = undef;
my $postfix = "";
my $retdefault = $svcname;
+268
View File
@@ -0,0 +1,268 @@
use strict;
use warnings;
use FindBin;
use lib "$FindBin::Bin/../../perl-xCAT";
use File::Path qw/make_path/;
use File::Temp qw/tempdir/;
use Test::More;
use xCAT::DHCP::Backend::Kea;
use xCAT::Utils;
my $uses_shared_service_map = !xCAT::DHCP::Backend::Kea->can('_service_available');
sub write_unit {
my ($path) = @_;
open( my $fh, '>', $path ) or die "Unable to write $path: $!";
close($fh);
}
my $fixture_root = tempdir( CLEANUP => 1 );
my $systemd_dir = "$fixture_root/systemd";
my $sysv_dir = "$fixture_root/init.d";
my $empty_dir = "$fixture_root/empty";
my $extended_dir = "$fixture_root/extended";
make_path( $systemd_dir, $sysv_dir, $empty_dir, $extended_dir );
my %packaged_units = (
'kea-dhcp4' => 'kea-dhcp4-server',
'kea-dhcp6' => 'kea-dhcp6-server',
'kea-dhcp-ddns' => 'kea-dhcp-ddns-server',
'kea-ctrl-agent' => 'kea-ctrl-agent',
);
foreach my $unit ( values %packaged_units ) {
write_unit("$systemd_dir/$unit.service");
}
{
no warnings 'redefine';
my $systemd_backend = xCAT::DHCP::Backend::Kea->new(
service_unit_dirs => [$systemd_dir],
service_init_dirs => [$empty_dir],
);
foreach my $service ( sort keys %packaged_units ) {
is(
$systemd_backend->_kea_service($service),
$packaged_units{$service},
"$service resolves to its installed systemd unit",
);
}
write_unit("$systemd_dir/kea-dhcp4.service");
is(
$systemd_backend->_kea_service('kea-dhcp4'),
'kea-dhcp4',
'the canonical systemd unit keeps candidate priority',
);
my $empty_backend = xCAT::DHCP::Backend::Kea->new(
service_unit_dirs => [$empty_dir],
service_init_dirs => [$empty_dir],
);
is(
$empty_backend->_kea_service('custom-kea'),
'custom-kea',
'an unknown service keeps its requested name',
);
write_unit("$empty_dir/dhcp3-server.service");
is(
$empty_backend->_kea_service('dhcp'),
'dhcp',
'a non-Kea service is not resolved through the shared candidate table',
);
write_unit("$extended_dir/isc-kea-dhcp4-server.service");
write_unit("$extended_dir/kea-ctrl-agent-server.service");
my $extended_backend = xCAT::DHCP::Backend::Kea->new(
service_unit_dirs => [$extended_dir],
service_init_dirs => [$empty_dir],
);
is(
$extended_backend->_kea_service('kea-dhcp4'),
'kea-dhcp4',
'the backend does not widen its established DHCP candidate subset',
);
is(
$extended_backend->_kea_service('kea-ctrl-agent'),
'kea-ctrl-agent',
'the backend does not widen its established Control Agent candidate subset',
);
SKIP: {
skip 'shared service-map filesystem search is not present before the refactor', 2
unless $uses_shared_service_map;
my $sysv_backend = xCAT::DHCP::Backend::Kea->new(
service_unit_dirs => [$systemd_dir],
service_init_dirs => [$sysv_dir],
);
unlink "$systemd_dir/kea-dhcp4.service";
write_unit("$sysv_dir/kea-dhcp4");
is(
$sysv_backend->_kea_service('kea-dhcp4'),
'kea-dhcp4-server',
'a non-executable SysV script is ignored',
);
chmod 0755, "$sysv_dir/kea-dhcp4"
or die "Unable to make the SysV fixture executable: $!";
is(
$sysv_backend->_kea_service('kea-dhcp4'),
'kea-dhcp4',
'an executable earlier SysV candidate wins over a later systemd candidate',
);
write_unit("$systemd_dir/kea-dhcp4.service");
}
if ($uses_shared_service_map) {
my $search_spec;
local *xCAT::Utils::servicemap = sub {
( undef, undef, $search_spec ) = @_;
return;
};
is(
xCAT::DHCP::Backend::Kea->new()->_kea_service('kea-dhcp4'),
'kea-dhcp4',
'an unresolved service keeps its requested name',
);
is_deeply(
$search_spec->{searches},
[
{
paths => [
'/etc/systemd/system',
'/run/systemd/system',
'/usr/lib/systemd/system',
'/lib/systemd/system',
],
suffix => '.service',
},
{
paths => ['/etc/init.d'],
executable => 1,
},
],
'the default systemd and SysV search paths remain unchanged',
);
} else {
is_deeply(
xCAT::DHCP::Backend::Kea::_systemd_unit_dirs(),
[
'/etc/systemd/system',
'/run/systemd/system',
'/usr/lib/systemd/system',
'/lib/systemd/system',
],
'the default systemd search paths match before the refactor',
);
}
my @systemd_calls;
local *xCAT::Utils::enableservice = sub {
my ( $class, $service ) = @_;
push @systemd_calls, [ enable => $service ];
return 0;
};
local *xCAT::Utils::checkservicestatus = sub {
my ( $class, $service ) = @_;
push @systemd_calls, [ status => $service ];
return 0;
};
local *xCAT::Utils::runcmd = sub {
my ( $class, $command, $output_mode ) = @_;
push @systemd_calls, [ command => $command, $output_mode ];
$::RUNCMD_RC = 0;
return '';
};
local *xCAT::Utils::restartservice = sub {
my ( $class, $service ) = @_;
push @systemd_calls, [ restart => $service ];
return 0;
};
my $restart = $systemd_backend->restart_services(
ddns => 1,
ipv6 => 1,
ctrl_agent => 1,
enable => 1,
);
is_deeply(
$restart,
{
services => [
'kea-dhcp-ddns-server',
'kea-dhcp4',
'kea-dhcp6-server',
'kea-ctrl-agent',
],
},
'restart reports the resolved systemd units in lifecycle order',
);
is_deeply(
\@systemd_calls,
[
[ enable => 'kea-dhcp-ddns-server' ],
[ status => 'kea-dhcp-ddns-server' ],
[ command => 'systemctl reload kea-dhcp-ddns-server', -1 ],
[ enable => 'kea-dhcp4' ],
[ status => 'kea-dhcp4' ],
[ command => 'systemctl reload kea-dhcp4', -1 ],
[ enable => 'kea-dhcp6-server' ],
[ status => 'kea-dhcp6-server' ],
[ command => 'systemctl reload kea-dhcp6-server', -1 ],
[ enable => 'kea-ctrl-agent' ],
[ status => 'kea-ctrl-agent' ],
[ command => 'systemctl reload kea-ctrl-agent', -1 ],
],
'systemd lifecycle calls preserve enable, status, and reload behavior',
);
{
my @sysv_calls;
local *xCAT::DHCP::Backend::Kea::_kea_service = sub { return 'kea-dhcp4-server'; };
local *xCAT::Utils::checkservicestatus = sub {
my ( $class, $service ) = @_;
push @sysv_calls, [ status => $service ];
return 0;
};
my $status = xCAT::DHCP::Backend::Kea->new()->check_services();
is_deeply(
$status,
{ services => ['kea-dhcp4-server'] },
'service checks report a resolved SysV daemon name',
);
is_deeply(
\@sysv_calls,
[ [ status => 'kea-dhcp4-server' ] ],
'service checks pass through a resolved SysV daemon name',
);
}
{
my @absent_calls;
local *xCAT::DHCP::Backend::Kea::_kea_service = sub { return $_[1]; };
local *xCAT::Utils::checkservicestatus = sub {
my ( $class, $service ) = @_;
push @absent_calls, [ status => $service ];
return 1;
};
my $absent_status = xCAT::DHCP::Backend::Kea->new()->check_services();
is_deeply(
$absent_status,
{ error => 'kea-dhcp4 is not running. Please start the Kea DHCP service.' },
'an absent unit preserves the canonical lifecycle error',
);
is_deeply(
\@absent_calls,
[ [ status => 'kea-dhcp4' ] ],
'an absent unit is checked by its requested name',
);
}
}
done_testing();