mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-08-03 16:06:59 +00:00
Merge pull request #7695 from xcat2/backport-7605-to-2.18
[Backport 2.18] fix(dhcp): use Kea runtime paths
This commit is contained in:
@@ -7,6 +7,7 @@ use JSON;
|
||||
use File::Basename;
|
||||
use File::Path qw/make_path/;
|
||||
use Math::BigInt;
|
||||
use Text::ParseWords qw/shellwords/;
|
||||
use xCAT::DHCP::Range;
|
||||
use xCAT::NetworkUtils;
|
||||
|
||||
@@ -16,6 +17,7 @@ my %KEA_SERVICE_CANDIDATES = (
|
||||
'kea-dhcp-ddns' => [ 'kea-dhcp-ddns', 'kea-dhcp-ddns-server' ],
|
||||
'kea-ctrl-agent' => [ 'kea-ctrl-agent' ],
|
||||
);
|
||||
my @KEA_ACCOUNT_CANDIDATES = ( 'kea', '_kea' );
|
||||
|
||||
sub new {
|
||||
my ( $class, %args ) = @_;
|
||||
@@ -50,6 +52,25 @@ sub ddns_config_file {
|
||||
return $self->{ddns_config_file} || '/etc/kea/kea-dhcp-ddns.conf';
|
||||
}
|
||||
|
||||
sub control_socket_path {
|
||||
my ( $self, $socket_name ) = @_;
|
||||
|
||||
return $self->_kea_socket_dir() . "/$socket_name";
|
||||
}
|
||||
|
||||
sub service_account {
|
||||
foreach my $user (@KEA_ACCOUNT_CANDIDATES) {
|
||||
my @entry = getpwnam($user);
|
||||
return {
|
||||
name => $entry[0],
|
||||
uid => $entry[2],
|
||||
gid => $entry[3],
|
||||
} if @entry;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sub render_dhcp4_config {
|
||||
my ( $self, $intent ) = @_;
|
||||
|
||||
@@ -154,19 +175,19 @@ sub render_ctrl_agent_config {
|
||||
my %sockets = (
|
||||
dhcp4 => {
|
||||
'socket-type' => 'unix',
|
||||
'socket-name' => $intent->{'dhcp4-socket'} || $self->_kea_control_socket('kea4-ctrl-socket'),
|
||||
'socket-name' => $intent->{'dhcp4-socket'} || $self->control_socket_path('kea4-ctrl-socket'),
|
||||
},
|
||||
);
|
||||
if ( $intent->{dhcp6} || $intent->{'dhcp6-socket'} ) {
|
||||
$sockets{dhcp6} = {
|
||||
'socket-type' => 'unix',
|
||||
'socket-name' => $intent->{'dhcp6-socket'} || $self->_kea_control_socket('kea6-ctrl-socket'),
|
||||
'socket-name' => $intent->{'dhcp6-socket'} || $self->control_socket_path('kea6-ctrl-socket'),
|
||||
};
|
||||
}
|
||||
if ( $intent->{ddns} || $intent->{'ddns-socket'} ) {
|
||||
$sockets{d2} = {
|
||||
'socket-type' => 'unix',
|
||||
'socket-name' => $intent->{'ddns-socket'} || $self->_kea_control_socket('kea-ddns-ctrl-socket'),
|
||||
'socket-name' => $intent->{'ddns-socket'} || $self->control_socket_path('kea-ddns-ctrl-socket'),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -478,12 +499,12 @@ sub _validate_config_with {
|
||||
|
||||
my $prefix = '';
|
||||
if ( $> == 0 ) {
|
||||
my $kea_user = _kea_user();
|
||||
my $service_account = $self->service_account();
|
||||
my $runuser = _command_path('runuser');
|
||||
# Validate as the daemon user when possible so root does not hide
|
||||
# packaged Kea runtime-directory or config-readability failures.
|
||||
$prefix = _shell_quote($runuser) . ' -u ' . _shell_quote($kea_user) . ' -- '
|
||||
if $kea_user && $runuser;
|
||||
$prefix = _shell_quote($runuser) . ' -u ' . _shell_quote( $service_account->{name} ) . ' -- '
|
||||
if $service_account && $runuser;
|
||||
}
|
||||
|
||||
my $cmd = $prefix . _shell_quote($kea) . " -t " . _shell_quote($path) . " 2>&1";
|
||||
@@ -911,15 +932,7 @@ sub kea_version {
|
||||
return $self->{kea_version} if defined $self->{kea_version};
|
||||
return $self->{_detected_kea_version} if defined $self->{_detected_kea_version};
|
||||
|
||||
my $command = $self->{kea_dhcp4_command} || _command_path('kea-dhcp4');
|
||||
return unless $command;
|
||||
|
||||
my $output = '';
|
||||
if ( open( my $version_fh, '-|', $command, '-V' ) ) {
|
||||
local $/;
|
||||
$output = <$version_fh> || '';
|
||||
close($version_fh);
|
||||
}
|
||||
my $output = $self->_kea_command_output('-V') || '';
|
||||
|
||||
if ( $output =~ /(\d+(?:\.\d+){1,2})/ ) {
|
||||
$self->{_detected_kea_version} = $1;
|
||||
@@ -928,6 +941,34 @@ sub kea_version {
|
||||
return $self->{_detected_kea_version};
|
||||
}
|
||||
|
||||
sub _kea_build_report {
|
||||
my ($self) = @_;
|
||||
|
||||
return $self->{kea_build_report} if defined $self->{kea_build_report};
|
||||
return $self->{_detected_kea_build_report} if defined $self->{_detected_kea_build_report};
|
||||
|
||||
my $output = $self->_kea_command_output('-W') || '';
|
||||
|
||||
$self->{_detected_kea_build_report} = $output;
|
||||
return $output;
|
||||
}
|
||||
|
||||
sub _kea_command_output {
|
||||
my ( $self, @args ) = @_;
|
||||
|
||||
my $command = $self->{kea_dhcp4_command} || _command_path('kea-dhcp4');
|
||||
return unless $command;
|
||||
|
||||
my $output = '';
|
||||
if ( open( my $command_fh, '-|', $command, @args ) ) {
|
||||
local $/;
|
||||
$output = <$command_fh> || '';
|
||||
close($command_fh);
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
sub _first_defined {
|
||||
my @values = @_;
|
||||
foreach my $value (@values) {
|
||||
@@ -971,7 +1012,7 @@ sub _set_config_permissions {
|
||||
}
|
||||
|
||||
sub _kea_group {
|
||||
foreach my $group ( 'kea', '_kea' ) {
|
||||
foreach my $group (@KEA_ACCOUNT_CANDIDATES) {
|
||||
my @entry = getgrnam($group);
|
||||
return ( $entry[0], $entry[2] ) if @entry;
|
||||
}
|
||||
@@ -979,15 +1020,6 @@ sub _kea_group {
|
||||
return;
|
||||
}
|
||||
|
||||
sub _kea_user {
|
||||
foreach my $user ( 'kea', '_kea' ) {
|
||||
my @entry = getpwnam($user);
|
||||
return $entry[0] if @entry;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sub _kea_service {
|
||||
my ( $self, $service ) = @_;
|
||||
|
||||
@@ -1028,21 +1060,101 @@ sub _kea_socket_dir {
|
||||
|
||||
return $self->{kea_socket_dir} if defined $self->{kea_socket_dir};
|
||||
|
||||
if ( defined $self->{kea_socket_dirs} ) {
|
||||
foreach my $dir ( @{ $self->{kea_socket_dirs} || [] } ) {
|
||||
return $dir if -d $dir;
|
||||
}
|
||||
return '/var/run/kea';
|
||||
}
|
||||
|
||||
my $build_report = $self->_kea_build_report();
|
||||
if ($build_report) {
|
||||
my $prefix = _kea_build_option( $build_report, 'prefix' );
|
||||
if ( !defined($prefix) && $build_report =~ /^\s*Prefix:\s*(\/\S+)/m ) {
|
||||
$prefix = $1;
|
||||
}
|
||||
$prefix = _expand_kea_build_path( $prefix, {} );
|
||||
|
||||
# Kea's Meson build unconditionally overrides the installed runtime
|
||||
# directory for the /usr/local prefix, even when state directories
|
||||
# are supplied as build options.
|
||||
if ( defined($prefix) && $prefix eq '/usr/local' && $build_report =~ /^\s*Meson Version:/m ) {
|
||||
return '/usr/local/var/run/kea';
|
||||
}
|
||||
|
||||
my $local_state = _kea_build_option( $build_report, 'localstatedir' );
|
||||
$local_state = _expand_kea_build_path( $local_state, { prefix => $prefix }, $prefix );
|
||||
if ( !defined($local_state) && defined($prefix) ) {
|
||||
if ( $build_report =~ /^\s*Meson Version:/m ) {
|
||||
$local_state = '/var' if $prefix eq '/usr';
|
||||
}
|
||||
# Kea reverts Meson's /usr/local => /var/local default.
|
||||
$local_state = _expand_kea_build_path( 'var', {}, $prefix ) unless defined($local_state);
|
||||
}
|
||||
|
||||
my $run_state = _kea_build_option( $build_report, 'runstatedir' );
|
||||
$run_state = _expand_kea_build_path(
|
||||
$run_state,
|
||||
{
|
||||
prefix => $prefix,
|
||||
localstatedir => $local_state,
|
||||
},
|
||||
$prefix,
|
||||
);
|
||||
$run_state = "$local_state/run" if !defined($run_state) && defined($local_state);
|
||||
return $run_state eq '/' ? '/kea' : "$run_state/kea" if defined($run_state);
|
||||
}
|
||||
|
||||
# Kea validates Control Agent sockets against its packaged runtime
|
||||
# directory, and newer packages reject /var/run/kea even when it resolves
|
||||
# to /run/kea. Keep the legacy path as the unknown-state fallback for
|
||||
# older Kea builds that validate before the runtime directory exists.
|
||||
foreach my $dir ( @{ $self->{kea_socket_dirs} || [ '/run/kea', '/var/run/kea' ] } ) {
|
||||
# directory, and newer packages reject a symlink-equivalent spelling.
|
||||
# Filesystem probing is only a fallback for builds without a usable report.
|
||||
foreach my $dir ( '/run/kea', '/var/run/kea' ) {
|
||||
return $dir if -d $dir;
|
||||
}
|
||||
|
||||
return '/var/run/kea';
|
||||
}
|
||||
|
||||
sub _kea_control_socket {
|
||||
my ( $self, $socket_name ) = @_;
|
||||
sub _kea_build_option {
|
||||
my ( $build_report, $option ) = @_;
|
||||
|
||||
return $self->_kea_socket_dir() . "/$socket_name";
|
||||
return unless defined($build_report);
|
||||
my $options = $build_report;
|
||||
if ( $build_report =~ /^\s*(?:Configure arguments|Build Options):[ \t]*(.*?)(?=\n[ \t]*\n|^\s*C\+\+ Compiler:|\z)/ms ) {
|
||||
$options = $1;
|
||||
}
|
||||
|
||||
my @tokens = eval { shellwords($options) };
|
||||
return if $@;
|
||||
|
||||
my $value;
|
||||
for ( my $index = 0; $index < @tokens; $index++ ) {
|
||||
my $token = $tokens[$index];
|
||||
if ( $token =~ /^(?:--|-D)\Q$option\E=(.+)$/ ) {
|
||||
$value = $1;
|
||||
} elsif ( $token eq "--$option" && $index + 1 < @tokens ) {
|
||||
$value = $tokens[ ++$index ];
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
sub _expand_kea_build_path {
|
||||
my ( $path, $variables, $relative_base ) = @_;
|
||||
|
||||
return unless defined($path);
|
||||
foreach my $variable (qw/prefix localstatedir/) {
|
||||
next unless defined( $variables->{$variable} );
|
||||
$path =~ s/\$\{$variable\}/$variables->{$variable}/g;
|
||||
}
|
||||
if ( $path !~ m{^/} && defined($relative_base) ) {
|
||||
$path = $relative_base eq '/' ? "/$path" : "$relative_base/$path";
|
||||
}
|
||||
$path =~ s{/$}{} if length($path) > 1;
|
||||
return $path if $path =~ m{^/};
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sub _command_path {
|
||||
|
||||
@@ -2615,7 +2615,7 @@ sub kea_build_dhcp4_intent
|
||||
if (kea_control_agent_enabled()) {
|
||||
$intent->{'control-socket'} = {
|
||||
'socket-type' => 'unix',
|
||||
'socket-name' => '/var/run/kea/kea4-ctrl-socket',
|
||||
'socket-name' => $backend->control_socket_path('kea4-ctrl-socket'),
|
||||
};
|
||||
my $hook = $backend->host_cmds_hook_path();
|
||||
if ($hook) {
|
||||
@@ -2670,7 +2670,7 @@ sub kea_build_dhcp6_intent
|
||||
if (kea_control_agent_enabled()) {
|
||||
$intent->{'control-socket'} = {
|
||||
'socket-type' => 'unix',
|
||||
'socket-name' => '/var/run/kea/kea6-ctrl-socket',
|
||||
'socket-name' => $backend->control_socket_path('kea6-ctrl-socket'),
|
||||
};
|
||||
my $hook = $backend->host_cmds_hook_path();
|
||||
if ($hook) {
|
||||
|
||||
@@ -6,7 +6,10 @@ use lib "$FindBin::Bin/../../perl-xCAT";
|
||||
|
||||
use File::Path qw/make_path/;
|
||||
use File::Temp qw/tempdir/;
|
||||
use IO::Socket::INET;
|
||||
use POSIX qw/WNOHANG _exit setgid setuid/;
|
||||
use Test::More;
|
||||
use Time::HiRes qw/sleep time/;
|
||||
|
||||
use xCAT::DHCP::Backend::Kea;
|
||||
|
||||
@@ -21,99 +24,172 @@ plan skip_all => 'kea-dhcp4 and kea-ctrl-agent are required'
|
||||
|
||||
my $backend = xCAT::DHCP::Backend::Kea->new();
|
||||
my $hook = $backend->host_cmds_hook_path();
|
||||
plan skip_all => 'Kea host-commands hook is required' unless $hook;
|
||||
my $service_account = $backend->service_account();
|
||||
plan skip_all => 'Kea service account is required'
|
||||
unless ref($service_account) eq 'HASH'
|
||||
&& defined( $service_account->{name} )
|
||||
&& defined( $service_account->{uid} )
|
||||
&& defined( $service_account->{gid} );
|
||||
|
||||
my $tmp = tempdir(CLEANUP => 1);
|
||||
make_path('/var/run/kea');
|
||||
make_path('/var/lib/kea');
|
||||
chmod 0755, $tmp or die "Unable to make $tmp daemon-traversable: $!";
|
||||
|
||||
my $socket = "/var/run/kea/kea4-xcat-smoke-$$.sock";
|
||||
my $lease_file = "/var/lib/kea/kea-leases4-xcat-smoke-$$.csv";
|
||||
unlink $socket, $lease_file;
|
||||
my $runtime_dir = "$tmp/run";
|
||||
my $data_dir = "$tmp/data";
|
||||
make_path( $runtime_dir, $data_dir );
|
||||
chown( $service_account->{uid}, $service_account->{gid}, $runtime_dir, $data_dir ) == 2
|
||||
or die "Unable to set Kea fixture directory ownership: $!";
|
||||
chmod( 0750, $runtime_dir, $data_dir ) == 2
|
||||
or die "Unable to set Kea fixture directory permissions: $!";
|
||||
|
||||
local $ENV{KEA_CONTROL_SOCKET_DIR} = $runtime_dir;
|
||||
local $ENV{KEA_DHCP_DATA_DIR} = $data_dir;
|
||||
local $ENV{KEA_PIDFILE_DIR} = $runtime_dir;
|
||||
local $ENV{KEA_LOCKFILE_DIR} = $runtime_dir;
|
||||
|
||||
$backend = xCAT::DHCP::Backend::Kea->new(kea_socket_dir => $runtime_dir);
|
||||
|
||||
my $port_guard = IO::Socket::INET->new(
|
||||
LocalAddr => '127.0.0.1',
|
||||
LocalPort => 0,
|
||||
Listen => 1,
|
||||
Proto => 'tcp',
|
||||
) or die "Unable to reserve a Control Agent port: $!";
|
||||
my $control_agent_port = $port_guard->sockport();
|
||||
|
||||
my $socket = $backend->control_socket_path('kea4-ctrl-socket');
|
||||
my $lease_file = "$data_dir/kea-leases4.csv";
|
||||
|
||||
my $dhcp_config = "$tmp/kea-dhcp4.conf";
|
||||
my $ctrl_config = "$tmp/kea-ctrl-agent.conf";
|
||||
write_file(
|
||||
$dhcp_config,
|
||||
$backend->render_dhcp4_config(
|
||||
my $dhcp_settings = {
|
||||
interfaces => [],
|
||||
'lease-database' => {
|
||||
type => 'memfile',
|
||||
name => $lease_file,
|
||||
},
|
||||
'control-socket' => {
|
||||
'socket-type' => 'unix',
|
||||
'socket-name' => $socket,
|
||||
},
|
||||
subnets => [
|
||||
{
|
||||
interfaces => ['lo'],
|
||||
'lease-database' => {
|
||||
type => 'memfile',
|
||||
name => $lease_file,
|
||||
},
|
||||
'control-socket' => {
|
||||
'socket-type' => 'unix',
|
||||
'socket-name' => $socket,
|
||||
},
|
||||
'hooks-libraries' => [ { library => $hook } ],
|
||||
subnets => [
|
||||
{
|
||||
id => 1,
|
||||
subnet => '127.0.0.0/8',
|
||||
pools => [],
|
||||
},
|
||||
],
|
||||
}
|
||||
)
|
||||
);
|
||||
write_file(
|
||||
$ctrl_config,
|
||||
$backend->render_ctrl_agent_config(
|
||||
{
|
||||
'http-port' => 18000,
|
||||
'dhcp4-socket' => $socket,
|
||||
}
|
||||
)
|
||||
);
|
||||
id => 1,
|
||||
subnet => '127.0.0.0/8',
|
||||
pools => [],
|
||||
},
|
||||
],
|
||||
};
|
||||
$dhcp_settings->{'hooks-libraries'} = [ { library => $hook } ] if $hook;
|
||||
|
||||
my $dhcp_validation = $backend->validate_dhcp4_config($dhcp_config);
|
||||
ok( !$dhcp_validation->{error}, 'live smoke DHCPv4 config validates' )
|
||||
or diag $dhcp_validation->{error};
|
||||
my $ctrl_validation = $backend->validate_ctrl_agent_config($ctrl_config);
|
||||
ok( !$ctrl_validation->{error}, 'live smoke Control Agent config validates' )
|
||||
or diag $ctrl_validation->{error};
|
||||
my $dhcp_write = $backend->write_dhcp4_config(
|
||||
$dhcp_settings,
|
||||
path => $dhcp_config,
|
||||
);
|
||||
ok( !$dhcp_write->{error}, 'live smoke DHCPv4 config validates and writes' )
|
||||
or diag $dhcp_write->{error};
|
||||
|
||||
my @pids;
|
||||
END {
|
||||
kill 'TERM', @pids if @pids;
|
||||
unlink grep { defined($_) && $_ ne '' } ( $socket, $lease_file );
|
||||
my $ctrl_write = $backend->write_ctrl_agent_config(
|
||||
{
|
||||
'http-port' => $control_agent_port,
|
||||
},
|
||||
path => $ctrl_config,
|
||||
);
|
||||
ok( !$ctrl_write->{error}, 'live smoke Control Agent config validates and writes' )
|
||||
or diag $ctrl_write->{error};
|
||||
|
||||
unless ( !$dhcp_write->{error} && !$ctrl_write->{error} ) {
|
||||
done_testing();
|
||||
exit 1;
|
||||
}
|
||||
|
||||
push @pids, start_daemon($kea_dhcp4, '-c', $dhcp_config, '-d', "$tmp/kea-dhcp4.log");
|
||||
ok( wait_for_process($pids[-1]), 'kea-dhcp4 stays running for smoke test' );
|
||||
push @pids, start_daemon($kea_ctrl, '-c', $ctrl_config, '-d', "$tmp/kea-ctrl-agent.log");
|
||||
ok( wait_for_process($pids[-1]), 'kea-ctrl-agent stays running for smoke test' );
|
||||
my %children;
|
||||
END { stop_daemons(\%children); }
|
||||
|
||||
my $live_backend = xCAT::DHCP::Backend::Kea->new(control_agent_port => 18000);
|
||||
my $add = $live_backend->live_upsert_reservations(
|
||||
[
|
||||
{
|
||||
'subnet-id' => 1,
|
||||
'hw-address' => '52:54:00:12:34:56',
|
||||
'ip-address' => '127.0.0.50',
|
||||
hostname => 'node-smoke',
|
||||
},
|
||||
],
|
||||
service => ['dhcp4']
|
||||
);
|
||||
ok( !$add->{error}, 'reservation-add succeeds through Kea Control Agent' )
|
||||
or diag $add->{error};
|
||||
my $dhcp_log = "$tmp/kea-dhcp4.log";
|
||||
my $dhcp_pid = start_daemon( $service_account, $kea_dhcp4, $dhcp_log, '-c', $dhcp_config, '-d' );
|
||||
$children{$dhcp_pid} = 1;
|
||||
my $dhcp_ready = wait_for_socket( $dhcp_pid, $socket, \%children );
|
||||
ok( $dhcp_ready, 'kea-dhcp4 creates its Control Agent socket' );
|
||||
diag_file($dhcp_log) unless $dhcp_ready;
|
||||
unless ($dhcp_ready) {
|
||||
done_testing();
|
||||
exit 1;
|
||||
}
|
||||
is( ( stat $socket )[4], $service_account->{uid}, 'kea-dhcp4 socket belongs to the service account' );
|
||||
|
||||
my $delete = $live_backend->live_delete_reservations(
|
||||
[
|
||||
{
|
||||
'subnet-id' => 1,
|
||||
'hw-address' => '52:54:00:12:34:56',
|
||||
'ip-address' => '127.0.0.50',
|
||||
hostname => 'node-smoke',
|
||||
},
|
||||
],
|
||||
service => ['dhcp4']
|
||||
);
|
||||
ok( !$delete->{error}, 'reservation-del succeeds through Kea Control Agent' )
|
||||
or diag $delete->{error};
|
||||
my $lease_ready = wait_for_file( $dhcp_pid, $lease_file, \%children );
|
||||
ok( $lease_ready, 'kea-dhcp4 creates its lease file' );
|
||||
is( ( stat $lease_file )[4], $service_account->{uid}, 'kea-dhcp4 lease file belongs to the service account' )
|
||||
if $lease_ready;
|
||||
|
||||
close($port_guard) or die "Unable to release the reserved Control Agent port: $!";
|
||||
|
||||
my $ctrl_log = "$tmp/kea-ctrl-agent.log";
|
||||
my $ctrl_pid = start_daemon( $service_account, $kea_ctrl, $ctrl_log, '-c', $ctrl_config, '-d' );
|
||||
$children{$ctrl_pid} = 1;
|
||||
|
||||
my $live_backend = xCAT::DHCP::Backend::Kea->new(control_agent_port => $control_agent_port);
|
||||
my ( $ctrl_ready, $readiness ) = wait_for_control_agent( $live_backend, $ctrl_pid, \%children );
|
||||
ok( $ctrl_ready, 'Kea Control Agent forwards commands to kea-dhcp4' );
|
||||
diag( $readiness->{error} || $readiness->{text} || 'Control Agent returned no response' ) unless $ctrl_ready;
|
||||
diag_file($ctrl_log) unless $ctrl_ready;
|
||||
unless ($ctrl_ready) {
|
||||
done_testing();
|
||||
exit 1;
|
||||
}
|
||||
|
||||
SKIP: {
|
||||
skip 'Kea host-commands hook is unavailable', 4 unless $hook;
|
||||
|
||||
my $reservation = {
|
||||
'subnet-id' => 1,
|
||||
'hw-address' => '52:54:00:12:34:56',
|
||||
'ip-address' => '127.0.0.50',
|
||||
hostname => 'node-smoke',
|
||||
};
|
||||
my $add = $live_backend->live_upsert_reservations(
|
||||
[$reservation],
|
||||
service => ['dhcp4']
|
||||
);
|
||||
ok( !$add->{error}, 'reservation-add succeeds through Kea Control Agent' )
|
||||
or diag $add->{error};
|
||||
|
||||
my $lookup = {
|
||||
'subnet-id' => $reservation->{'subnet-id'},
|
||||
'identifier-type' => 'hw-address',
|
||||
identifier => $reservation->{'hw-address'},
|
||||
};
|
||||
my $found = $live_backend->control_agent_command( 'reservation-get', $lookup, service => ['dhcp4'] );
|
||||
my $stored = response_arguments($found);
|
||||
ok(
|
||||
ref($stored) eq 'HASH'
|
||||
&& defined( $stored->{'ip-address'} )
|
||||
&& $stored->{'ip-address'} eq $reservation->{'ip-address'},
|
||||
'reservation-get confirms the added reservation is stored'
|
||||
) or diag( $found->{error} || $found->{text} || 'reservation-get returned no reservation' );
|
||||
|
||||
my $delete = $live_backend->live_delete_reservations(
|
||||
[$reservation],
|
||||
service => ['dhcp4']
|
||||
);
|
||||
ok( !$delete->{error}, 'reservation-del succeeds through Kea Control Agent' )
|
||||
or diag $delete->{error};
|
||||
|
||||
my $missing = $live_backend->control_agent_command( 'reservation-get', $lookup, service => ['dhcp4'] );
|
||||
my $missing_arguments = response_arguments($missing);
|
||||
my $expected_not_found = !$missing->{error}
|
||||
|| ( defined( $missing->{result} ) && $missing->{result} == 3 )
|
||||
|| ( defined( $missing->{text} ) && $missing->{text} =~ /not\s+found/i );
|
||||
ok(
|
||||
defined( $missing->{response} )
|
||||
&& $expected_not_found
|
||||
&& ( ref($missing_arguments) ne 'HASH' || !keys %$missing_arguments ),
|
||||
'reservation-get confirms the deleted reservation is absent'
|
||||
) or diag( $missing->{error} || $missing->{text} || 'reservation-get returned an unexpected response' );
|
||||
}
|
||||
|
||||
stop_daemons(\%children);
|
||||
done_testing();
|
||||
|
||||
sub command_path {
|
||||
@@ -131,38 +207,135 @@ sub command_path {
|
||||
return;
|
||||
}
|
||||
|
||||
sub write_file {
|
||||
my ( $path, $content ) = @_;
|
||||
|
||||
open(my $fh, '>', $path) or die "Unable to write $path: $!";
|
||||
print $fh $content;
|
||||
close($fh) or die "Unable to close $path: $!";
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub start_daemon {
|
||||
my ( $command, @args ) = @_;
|
||||
my $log = pop @args;
|
||||
my ( $account, $command, $log, @args ) = @_;
|
||||
my $pid = fork();
|
||||
die "Unable to fork $command: $!" unless defined $pid;
|
||||
if ($pid == 0) {
|
||||
open(STDOUT, '>', $log) or die "Unable to write $log: $!";
|
||||
open(STDERR, '>&', \*STDOUT) or die "Unable to redirect stderr: $!";
|
||||
exec $command, @args;
|
||||
die "Unable to exec $command: $!";
|
||||
open(STDOUT, '>', $log) or child_exit("Unable to write $log: $!");
|
||||
open(STDERR, '>&', \*STDOUT) or child_exit("Unable to redirect stderr: $!");
|
||||
$) = "$account->{gid} $account->{gid}";
|
||||
defined( setgid( $account->{gid} ) )
|
||||
or child_exit("Unable to set group identity to $account->{gid}: $!");
|
||||
my @group_ids = split /\s+/, $);
|
||||
$( == $account->{gid} && @group_ids && !grep { $_ != $account->{gid} } @group_ids
|
||||
or child_exit("Kea child did not assume group identity $account->{gid}");
|
||||
defined( setuid( $account->{uid} ) )
|
||||
or child_exit("Unable to set user identity to $account->{uid}: $!");
|
||||
$> == $account->{uid} && $< == $account->{uid}
|
||||
or child_exit("Kea child did not assume user identity $account->{uid}");
|
||||
{
|
||||
no warnings 'exec';
|
||||
exec { $command } $command, @args;
|
||||
child_exit("Unable to exec $command: $!");
|
||||
}
|
||||
}
|
||||
return $pid;
|
||||
}
|
||||
|
||||
sub wait_for_process {
|
||||
my ($pid) = @_;
|
||||
sub child_exit {
|
||||
my ($message) = @_;
|
||||
|
||||
for (1 .. 10) {
|
||||
sleep 1;
|
||||
return 0 unless kill 0, $pid;
|
||||
return 1 if $_ >= 2;
|
||||
warn "$message\n";
|
||||
_exit(127);
|
||||
}
|
||||
|
||||
sub wait_for_socket {
|
||||
my ( $pid, $socket_path, $children ) = @_;
|
||||
|
||||
for (1 .. 100) {
|
||||
return 0 unless process_running( $pid, $children );
|
||||
return 1 if -S $socket_path;
|
||||
sleep 0.1;
|
||||
}
|
||||
|
||||
return kill 0, $pid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub wait_for_file {
|
||||
my ( $pid, $path, $children ) = @_;
|
||||
|
||||
for (1 .. 100) {
|
||||
return 0 unless process_running( $pid, $children );
|
||||
return 1 if -f $path;
|
||||
sleep 0.1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub wait_for_control_agent {
|
||||
my ( $backend, $pid, $children ) = @_;
|
||||
|
||||
my $last_result = {};
|
||||
my $deadline = time + 10;
|
||||
while ( time < $deadline ) {
|
||||
return ( 0, $last_result ) unless process_running( $pid, $children );
|
||||
$last_result = $backend->control_agent_command(
|
||||
'list-commands',
|
||||
{},
|
||||
service => ['dhcp4'],
|
||||
timeout => 1,
|
||||
);
|
||||
return ( 1, $last_result ) unless $last_result->{error};
|
||||
my $remaining = $deadline - time;
|
||||
sleep( $remaining < 0.1 ? $remaining : 0.1 ) if $remaining > 0;
|
||||
}
|
||||
|
||||
return ( 0, $last_result );
|
||||
}
|
||||
|
||||
sub process_running {
|
||||
my ( $pid, $children ) = @_;
|
||||
|
||||
my $waited = waitpid( $pid, WNOHANG );
|
||||
return 1 if $waited == 0;
|
||||
|
||||
delete $children->{$pid};
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub stop_daemons {
|
||||
my ($children) = @_;
|
||||
|
||||
my @pids = keys %$children;
|
||||
kill 'TERM', @pids if @pids;
|
||||
foreach my $pid (@pids) {
|
||||
for (1 .. 50) {
|
||||
last unless process_running( $pid, $children );
|
||||
sleep 0.1;
|
||||
}
|
||||
next unless exists $children->{$pid};
|
||||
|
||||
kill 'KILL', $pid;
|
||||
waitpid( $pid, 0 );
|
||||
delete $children->{$pid};
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sub response_arguments {
|
||||
my ($result) = @_;
|
||||
|
||||
return unless ref($result) eq 'HASH';
|
||||
my $response = $result->{response};
|
||||
my $item = ref($response) eq 'ARRAY' ? $response->[0] : $response;
|
||||
return unless ref($item) eq 'HASH';
|
||||
return $item->{arguments} if ref( $item->{arguments} ) eq 'HASH';
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sub diag_file {
|
||||
my ($path) = @_;
|
||||
|
||||
return unless -e $path;
|
||||
open( my $fh, '<', $path ) or return;
|
||||
local $/;
|
||||
my $content = <$fh>;
|
||||
close($fh) or diag("Unable to close $path: $!");
|
||||
diag($content) if defined($content) && $content ne '';
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
#!/usr/bin/env perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use FindBin;
|
||||
use lib "$FindBin::Bin/../../perl-xCAT";
|
||||
|
||||
use File::Temp qw/tempfile/;
|
||||
use Test::More;
|
||||
|
||||
our ( %TEST_USERS, %TEST_GROUPS, %TEST_GROUP_NAMES );
|
||||
our ( @USER_LOOKUPS, @GROUP_LOOKUPS, @GROUP_ID_LOOKUPS );
|
||||
|
||||
BEGIN {
|
||||
no warnings 'redefine';
|
||||
|
||||
*CORE::GLOBAL::getpwnam = sub {
|
||||
my ($name) = @_;
|
||||
push @USER_LOOKUPS, $name;
|
||||
my $entry = $TEST_USERS{$name};
|
||||
return unless $entry;
|
||||
return @$entry if wantarray;
|
||||
return $entry->[0];
|
||||
};
|
||||
|
||||
*CORE::GLOBAL::getgrnam = sub {
|
||||
my ($name) = @_;
|
||||
push @GROUP_LOOKUPS, $name;
|
||||
my $entry = $TEST_GROUPS{$name};
|
||||
return unless $entry;
|
||||
return @$entry if wantarray;
|
||||
return $entry->[0];
|
||||
};
|
||||
|
||||
*CORE::GLOBAL::getgrgid = sub {
|
||||
my ($gid) = @_;
|
||||
push @GROUP_ID_LOOKUPS, $gid;
|
||||
my $name = $TEST_GROUP_NAMES{$gid};
|
||||
return unless defined $name;
|
||||
return ( $name, 'x', $gid, '' ) if wantarray;
|
||||
return $name;
|
||||
};
|
||||
}
|
||||
|
||||
use xCAT::DHCP::Backend::Kea;
|
||||
|
||||
my $backend = xCAT::DHCP::Backend::Kea->new();
|
||||
|
||||
set_nss(
|
||||
users => {
|
||||
_kea => [ '_kea', 'x', 100, 300, '', '', '', '/var/empty', '/sbin/nologin' ],
|
||||
},
|
||||
groups => {
|
||||
kea => [ 'kea', 'x', 200, '' ],
|
||||
_kea => [ '_kea', 'x', 400, '' ],
|
||||
},
|
||||
group_names => { 300 => 'daemon-primary' },
|
||||
);
|
||||
|
||||
my $service_account = selected_service_account($backend);
|
||||
is_deeply(
|
||||
$service_account,
|
||||
{ name => '_kea', uid => 100, gid => 300 },
|
||||
'service account selection preserves the fallback daemon identity'
|
||||
);
|
||||
|
||||
reset_lookups();
|
||||
my ( $group, $gid ) = selected_config_group($backend);
|
||||
is( $group, 'kea', 'the preferred named Kea group owns configuration files' );
|
||||
is( $gid, 200, 'configuration ownership is independent of the daemon primary GID' );
|
||||
isnt( $gid, $service_account->{gid}, 'configuration ownership does not inherit the daemon primary GID' );
|
||||
is_deeply( \@GROUP_LOOKUPS, ['kea'], 'named configuration groups are checked in preference order' );
|
||||
is_deeply( \@USER_LOOKUPS, [], 'configuration-group selection does not inspect service users' );
|
||||
is_deeply( \@GROUP_ID_LOOKUPS, [], 'configuration-group selection does not resolve a primary GID' );
|
||||
|
||||
set_nss(
|
||||
groups => {
|
||||
_kea => [ '_kea', 'x', 400, '' ],
|
||||
},
|
||||
);
|
||||
( $group, $gid ) = selected_config_group($backend);
|
||||
is( $group, '_kea', 'the fallback named Kea group is selected when needed' );
|
||||
is( $gid, 400, 'the fallback named Kea group supplies configuration ownership' );
|
||||
is_deeply( \@GROUP_LOOKUPS, [ 'kea', '_kea' ], 'both named groups are checked before falling back' );
|
||||
is_deeply( \@USER_LOOKUPS, [], 'named-group fallback remains independent of service users' );
|
||||
is_deeply( \@GROUP_ID_LOOKUPS, [], 'named-group fallback remains independent of primary GIDs' );
|
||||
|
||||
set_nss(
|
||||
users => {
|
||||
kea => [ 'kea', 'x', 101, 300, '', '', '', '/var/empty', '/sbin/nologin' ],
|
||||
},
|
||||
group_names => { 300 => 'daemon-primary' },
|
||||
);
|
||||
( $group, $gid ) = selected_config_group($backend);
|
||||
ok( !defined($group), 'no configuration group is selected when named Kea groups are absent' );
|
||||
ok( !defined($gid), 'no configuration GID is inherited from the service account' );
|
||||
is_deeply( \@GROUP_LOOKUPS, [ 'kea', '_kea' ], 'all named groups are checked before the public-mode fallback' );
|
||||
is_deeply( \@USER_LOOKUPS, [], 'the public-mode fallback does not inspect service users' );
|
||||
is_deeply( \@GROUP_ID_LOOKUPS, [], 'the public-mode fallback does not resolve a primary GID' );
|
||||
|
||||
SKIP: {
|
||||
skip 'root privileges are required to verify file ownership and modes', 5 if $> != 0;
|
||||
|
||||
set_nss(
|
||||
users => {
|
||||
_kea => [ '_kea', 'x', 100, 300, '', '', '', '/var/empty', '/sbin/nologin' ],
|
||||
},
|
||||
groups => {
|
||||
kea => [ 'kea', 'x', 200, '' ],
|
||||
},
|
||||
group_names => { 300 => 'daemon-primary' },
|
||||
);
|
||||
my ( $named_fh, $named_path ) = tempfile(UNLINK => 1);
|
||||
close($named_fh) or die "Unable to close $named_path: $!";
|
||||
my $named_result = apply_config_permissions( $backend, $named_path );
|
||||
ok( !$named_result->{error}, 'configuration permissions are applied with a named Kea group' )
|
||||
or diag $named_result->{error};
|
||||
is( ( stat $named_path )[5], 200, 'configuration file uses the named Kea group GID' );
|
||||
is( ( stat $named_path )[2] & 07777, 0640, 'configuration file is group-readable with a named Kea group' );
|
||||
|
||||
set_nss(
|
||||
users => {
|
||||
kea => [ 'kea', 'x', 101, 300, '', '', '', '/var/empty', '/sbin/nologin' ],
|
||||
},
|
||||
group_names => { 300 => 'daemon-primary' },
|
||||
);
|
||||
my ( $public_fh, $public_path ) = tempfile(UNLINK => 1);
|
||||
close($public_fh) or die "Unable to close $public_path: $!";
|
||||
my $public_result = apply_config_permissions( $backend, $public_path );
|
||||
ok( !$public_result->{error}, 'configuration permissions fall back without a named Kea group' )
|
||||
or diag $public_result->{error};
|
||||
is( ( stat $public_path )[2] & 07777, 0644, 'missing named Kea groups preserve the public-read fallback' );
|
||||
}
|
||||
|
||||
done_testing();
|
||||
|
||||
sub set_nss {
|
||||
my (%args) = @_;
|
||||
|
||||
%TEST_USERS = %{ $args{users} || {} };
|
||||
%TEST_GROUPS = %{ $args{groups} || {} };
|
||||
%TEST_GROUP_NAMES = %{ $args{group_names} || {} };
|
||||
reset_lookups();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sub reset_lookups {
|
||||
@USER_LOOKUPS = ();
|
||||
@GROUP_LOOKUPS = ();
|
||||
@GROUP_ID_LOOKUPS = ();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sub selected_service_account {
|
||||
my ($kea_backend) = @_;
|
||||
|
||||
if ( my $resolver = $kea_backend->can('service_account') ) {
|
||||
return $resolver->($kea_backend);
|
||||
}
|
||||
|
||||
my $legacy_resolver = xCAT::DHCP::Backend::Kea->can('_kea_user');
|
||||
my $name = $legacy_resolver ? $legacy_resolver->() : undef;
|
||||
return unless defined $name;
|
||||
my @entry = getpwnam($name);
|
||||
return {
|
||||
name => $entry[0],
|
||||
uid => $entry[2],
|
||||
gid => $entry[3],
|
||||
};
|
||||
}
|
||||
|
||||
sub selected_config_group {
|
||||
my ($kea_backend) = @_;
|
||||
|
||||
if ( my $resolver = xCAT::DHCP::Backend::Kea->can('_kea_group') ) {
|
||||
return $resolver->();
|
||||
}
|
||||
|
||||
my $resolver = $kea_backend->can('_service_group');
|
||||
return $resolver ? $resolver->($kea_backend) : undef;
|
||||
}
|
||||
|
||||
sub apply_config_permissions {
|
||||
my ( $kea_backend, $path ) = @_;
|
||||
|
||||
my $permissions = xCAT::DHCP::Backend::Kea->can('_set_config_permissions');
|
||||
return { error => 'Kea configuration-permission helper is unavailable' } unless $permissions;
|
||||
return $permissions->($path) if xCAT::DHCP::Backend::Kea->can('_kea_group');
|
||||
return $permissions->( $kea_backend, $path );
|
||||
}
|
||||
@@ -55,6 +55,13 @@ if ( -f $source_dhcp_plugin ) {
|
||||
} else {
|
||||
require xCAT_plugin::dhcp;
|
||||
}
|
||||
require xCAT::DHCP::Backend::Kea;
|
||||
|
||||
{
|
||||
package DHCPKeaIntentBackend;
|
||||
our @ISA = ('xCAT::DHCP::Backend::Kea');
|
||||
sub host_cmds_hook_path { return '/test/libdhcp_host_cmds.so'; }
|
||||
}
|
||||
|
||||
{
|
||||
package DHCPKeaIntentNetTable;
|
||||
@@ -172,6 +179,42 @@ ok(!xCAT_plugin::dhcp::dhcpd_sysconfig_uses_interface_key('opensuse-tumbleweed')
|
||||
is( $intent->{subnets}[0]{subnet}, '10.0.0.0/24', 'rendered subnet comes from local route' );
|
||||
}
|
||||
|
||||
{
|
||||
no warnings 'redefine';
|
||||
local *xCAT_plugin::dhcp::kea_ipv4_routes = sub {
|
||||
return ([ '10.0.0.0', 'eth0', '255.255.255.0', '' ]);
|
||||
};
|
||||
local *xCAT_plugin::dhcp::kea_boot_client_classes = sub { return []; };
|
||||
local *xCAT_plugin::dhcp::kea_option_defs = sub { return []; };
|
||||
local *xCAT_plugin::dhcp::kea_global_option_data = sub { return []; };
|
||||
local *xCAT_plugin::dhcp::kea_dhcp_lease_time = sub { return 43200; };
|
||||
local *xCAT_plugin::dhcp::kea_control_agent_enabled = sub { return 1; };
|
||||
|
||||
my $backend = DHCPKeaIntentBackend->new(kea_socket_dir => '/run/kea-xcat-test');
|
||||
|
||||
local $xCAT::Table::networks = DHCPKeaIntentNetTable->new( \%network_entry );
|
||||
my $dhcp4_intent = xCAT_plugin::dhcp::kea_build_dhcp4_intent( $backend, { eth0 => 1 } );
|
||||
is(
|
||||
$dhcp4_intent->{'control-socket'}{'socket-name'},
|
||||
'/run/kea-xcat-test/kea4-ctrl-socket',
|
||||
'DHCPv4 intent uses the backend-selected Control Agent socket path'
|
||||
);
|
||||
|
||||
local $xCAT::Table::networks = DHCPKeaIntentNetTable->new(
|
||||
{
|
||||
%network_entry,
|
||||
net => 'fd00::/64',
|
||||
dynamicrange => undef,
|
||||
}
|
||||
);
|
||||
my $dhcp6_intent = xCAT_plugin::dhcp::kea_build_dhcp6_intent( $backend, { eth0 => 1 } );
|
||||
is(
|
||||
$dhcp6_intent->{'control-socket'}{'socket-name'},
|
||||
'/run/kea-xcat-test/kea6-ctrl-socket',
|
||||
'DHCPv6 intent uses the backend-selected Control Agent socket path'
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
no warnings 'redefine';
|
||||
local *xCAT::NetworkUtils::thishostisnot = sub { return 1; };
|
||||
|
||||
@@ -414,7 +414,8 @@ is( $ddns_config->{DhcpDdns}{port}, 53001, 'DDNS port is numeric' );
|
||||
is( $ddns_config->{DhcpDdns}{'dns-server-timeout'}, 500, 'DDNS timeout is numeric' );
|
||||
is( $ddns_config->{DhcpDdns}{'forward-ddns'}{'ddns-domains'}[0]{name}, 'cluster.example.com.', 'DDNS forward domain is rendered' );
|
||||
|
||||
my $ctrl_agent_config = decode_json($backend->render_ctrl_agent_config({ 'http-port' => '8000' }));
|
||||
my $ctrl_agent_backend = xCAT::DHCP::Backend::Kea->new( kea_socket_dirs => [] );
|
||||
my $ctrl_agent_config = decode_json($ctrl_agent_backend->render_ctrl_agent_config({ 'http-port' => '8000' }));
|
||||
is( $ctrl_agent_config->{'Control-agent'}{'http-port'}, 8000, 'Control Agent HTTP port is numeric' );
|
||||
|
||||
my $runtime_socket_dir = "$unit_dir/run/kea";
|
||||
@@ -429,6 +430,88 @@ my $legacy_socket_backend = xCAT::DHCP::Backend::Kea->new( kea_socket_dirs => []
|
||||
my $legacy_socket_config = decode_json($legacy_socket_backend->render_ctrl_agent_config({}));
|
||||
is( $legacy_socket_config->{'Control-agent'}{'control-sockets'}{dhcp4}{'socket-name'}, '/var/run/kea/kea4-ctrl-socket', 'Control Agent socket falls back to the legacy runtime path when no runtime directory exists' );
|
||||
|
||||
my $meson_socket_backend = xCAT::DHCP::Backend::Kea->new(
|
||||
kea_build_report => 'Build Options: -Dprefix=/usr -Dlocalstatedir=/var -Drunstatedir=/xcat-test-meson-run',
|
||||
);
|
||||
is( $meson_socket_backend->control_socket_path('kea4-ctrl-socket'), '/xcat-test-meson-run/kea/kea4-ctrl-socket', 'Meson runstatedir selects the socket path before the runtime directory exists' );
|
||||
|
||||
my $autoconf_socket_backend = xCAT::DHCP::Backend::Kea->new(
|
||||
kea_build_report => q{Configure arguments: '--prefix=/usr' '--localstatedir=/var' '--runstatedir=/xcat-test-autoconf-run'},
|
||||
);
|
||||
is( $autoconf_socket_backend->control_socket_path('kea4-ctrl-socket'), '/xcat-test-autoconf-run/kea/kea4-ctrl-socket', 'Autoconf runstatedir selects the socket path before the runtime directory exists' );
|
||||
|
||||
my $local_state_socket_backend = xCAT::DHCP::Backend::Kea->new(
|
||||
kea_build_report => q{Configure arguments: '--prefix=/usr' '--localstatedir=/xcat-test-local-state'},
|
||||
);
|
||||
is( $local_state_socket_backend->control_socket_path('kea4-ctrl-socket'), '/xcat-test-local-state/run/kea/kea4-ctrl-socket', 'localstatedir supplies the runtime path when runstatedir is not configured' );
|
||||
|
||||
my $variable_socket_backend = xCAT::DHCP::Backend::Kea->new(
|
||||
kea_build_report => q{Configure arguments:
|
||||
'--prefix=/usr/local' '--localstatedir=${prefix}/var' '--runstatedir=${localstatedir}/run'},
|
||||
);
|
||||
is( $variable_socket_backend->control_socket_path('kea4-ctrl-socket'), '/usr/local/var/run/kea/kea4-ctrl-socket', 'Autoconf build variables resolve through the configured prefix' );
|
||||
|
||||
my $undefined_candidates_socket_backend = xCAT::DHCP::Backend::Kea->new(
|
||||
kea_socket_dirs => undef,
|
||||
kea_build_report => 'Build Options: -Dprefix=/usr -Drunstatedir=/xcat-test-undefined-candidates',
|
||||
);
|
||||
is( $undefined_candidates_socket_backend->control_socket_path('kea4-ctrl-socket'), '/xcat-test-undefined-candidates/kea/kea4-ctrl-socket', 'undefined socket candidates preserve build-report detection' );
|
||||
|
||||
my $source_default_socket_backend = xCAT::DHCP::Backend::Kea->new(
|
||||
kea_build_report => 'Prefix: /usr/local',
|
||||
);
|
||||
is( $source_default_socket_backend->control_socket_path('kea4-ctrl-socket'), '/usr/local/var/run/kea/kea4-ctrl-socket', 'source build defaults resolve through the configured prefix' );
|
||||
|
||||
my $meson_default_socket_backend = xCAT::DHCP::Backend::Kea->new(
|
||||
kea_build_report => "Meson Version: 1.10.1\nBuild Options: -Dprefix=/usr",
|
||||
);
|
||||
is( $meson_default_socket_backend->control_socket_path('kea4-ctrl-socket'), '/var/run/kea/kea4-ctrl-socket', 'Meson system prefix defaults local state to /var' );
|
||||
|
||||
my $meson_local_default_socket_backend = xCAT::DHCP::Backend::Kea->new(
|
||||
kea_build_report => "Prefix: /usr/local\nMeson Version: 1.10.1\nBuild Options:\n\nC++ Compiler:\n",
|
||||
);
|
||||
is( $meson_local_default_socket_backend->control_socket_path('kea4-ctrl-socket'), '/usr/local/var/run/kea/kea4-ctrl-socket', 'Kea keeps local state prefix-relative for a Meson local prefix' );
|
||||
|
||||
my $meson_local_override_socket_backend = xCAT::DHCP::Backend::Kea->new(
|
||||
kea_build_report => "Prefix: /usr/local\nMeson Version: 1.10.1\nBuild Options: -Dprefix=/usr/local -Dlocalstatedir=/srv/kea -Drunstatedir=/run/kea-custom\n",
|
||||
);
|
||||
is( $meson_local_override_socket_backend->control_socket_path('kea4-ctrl-socket'), '/usr/local/var/run/kea/kea4-ctrl-socket', 'Kea overrides Meson state-directory options for a /usr/local prefix' );
|
||||
|
||||
my $split_option_socket_backend = xCAT::DHCP::Backend::Kea->new(
|
||||
kea_build_report => q{Configure arguments: '--prefix=/usr' '--localstatedir=/xcat-test-first' '--localstatedir' '/xcat-test-split-last'},
|
||||
);
|
||||
is( $split_option_socket_backend->control_socket_path('kea4-ctrl-socket'), '/xcat-test-split-last/run/kea/kea4-ctrl-socket', 'a space-separated Autoconf option can override an earlier value' );
|
||||
|
||||
my $last_option_socket_backend = xCAT::DHCP::Backend::Kea->new(
|
||||
kea_build_report => <<'BUILD_REPORT',
|
||||
Configure arguments: '--prefix=/usr' '--runstatedir=/xcat-test-first' '--runstatedir=/xcat-test-last' 'CXXFLAGS=-O2 -Drunstatedir=/xcat-test-embedded-compiler'
|
||||
|
||||
C++ Compiler:
|
||||
CXX_ARGS: -Drunstatedir=/xcat-test-compiler-only
|
||||
BUILD_REPORT
|
||||
);
|
||||
is( $last_option_socket_backend->control_socket_path('kea4-ctrl-socket'), '/xcat-test-last/kea/kea4-ctrl-socket', 'last build option wins without reading embedded or compiler-only flags' );
|
||||
|
||||
my $root_socket_backend = xCAT::DHCP::Backend::Kea->new(
|
||||
kea_build_report => 'Build Options: -Dprefix=/ -Dlocalstatedir=/ -Drunstatedir=/',
|
||||
);
|
||||
is( $root_socket_backend->control_socket_path('kea4-ctrl-socket'), '/kea/kea4-ctrl-socket', 'root runstatedir joins the socket path without a duplicate separator' );
|
||||
|
||||
my $fake_kea_dhcp4 = "$unit_dir/kea-dhcp4-build-report";
|
||||
open( my $fake_kea_fh, '>', $fake_kea_dhcp4 ) or die "Unable to write fake Kea command: $!";
|
||||
print {$fake_kea_fh} "#!$^X\n";
|
||||
print {$fake_kea_fh} <<'FAKE_KEA';
|
||||
use strict;
|
||||
use warnings;
|
||||
exit 2 unless @ARGV == 1 && $ARGV[0] eq '-W';
|
||||
print "Build Options: -Dprefix=/usr -Drunstatedir=/xcat-test-command-run\n";
|
||||
FAKE_KEA
|
||||
close($fake_kea_fh) or die "Unable to close fake Kea command: $!";
|
||||
chmod 0755, $fake_kea_dhcp4 or die "Unable to make fake Kea command executable: $!";
|
||||
|
||||
my $command_socket_backend = xCAT::DHCP::Backend::Kea->new(kea_dhcp4_command => $fake_kea_dhcp4);
|
||||
is( $command_socket_backend->control_socket_path('kea4-ctrl-socket'), '/xcat-test-command-run/kea/kea4-ctrl-socket', 'socket path comes from the Kea build-report command' );
|
||||
|
||||
my $socket_backend = xCAT::DHCP::Backend::Kea->new( kea_socket_dir => '/run/kea' );
|
||||
my $ctrl_agent_socket_config = decode_json($socket_backend->render_ctrl_agent_config({ dhcp6 => 1, ddns => 1 }));
|
||||
is( $ctrl_agent_socket_config->{'Control-agent'}{'control-sockets'}{dhcp4}{'socket-name'}, '/run/kea/kea4-ctrl-socket', 'Control Agent DHCPv4 socket uses the detected Kea socket directory' );
|
||||
|
||||
Reference in New Issue
Block a user