2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-08-03 16:06:59 +00:00

Merge pull request #7705 from xcat2/backport-7703-to-2.18

[Backport 2.18] fix(dhcp): preserve Kea reservations on makedhcp -n
This commit is contained in:
xcat2-backport-automation[bot]
2026-07-31 20:52:10 +00:00
committed by GitHub
3 changed files with 144 additions and 0 deletions
+36
View File
@@ -630,6 +630,42 @@ sub query_reservations {
return \@found;
}
sub preserve_reservations {
my ( $self, $config, $intent ) = @_;
my $target_subnets = $intent->{subnets} || [];
return $intent unless @$target_subnets;
my $target_config = $config->{Dhcp6}
? { Dhcp6 => { subnet6 => $target_subnets } }
: { Dhcp4 => { subnet4 => $target_subnets } };
foreach my $source_subnet ( _subnets_for_config($config) ) {
foreach my $reservation ( @{ $source_subnet->{reservations} || [] } ) {
my $ip = $reservation->{'ip-address'};
$ip = $reservation->{'ip-addresses'}[0]
if !$ip && ref( $reservation->{'ip-addresses'} ) eq 'ARRAY';
my $target_subnet;
if ($ip) {
my $subnet_id = $self->subnet_id_for_ip( $target_config, $ip );
$target_subnet = _find_subnet_by_id( $target_config, $subnet_id ) if defined($subnet_id);
}
if ( !$target_subnet && $source_subnet->{subnet} ) {
($target_subnet) = grep {
( $_->{subnet} || '' ) eq $source_subnet->{subnet}
} @$target_subnets;
}
next unless $target_subnet;
$target_subnet->{reservations} ||= [];
push @{ $target_subnet->{reservations} }, { %$reservation };
}
}
return $intent;
}
sub subnet_id_for_ip {
my ( $self, $config, $ip ) = @_;
+18
View File
@@ -2380,6 +2380,24 @@ sub kea_process_request
}
if ($opt->{n}) {
my $loaded4 = $backend->load_dhcp4_config();
if ($loaded4->{error}) {
$callback->({ error => [ $loaded4->{error} ], errorcode => [1] });
flock($dhcplockfd, LOCK_UN);
return;
}
$backend->preserve_reservations($loaded4, $intent4);
if ($using_dhcp6) {
my $loaded6 = $backend->load_dhcp6_config();
if ($loaded6->{error}) {
$callback->({ error => [ $loaded6->{error} ], errorcode => [1] });
flock($dhcplockfd, LOCK_UN);
return;
}
$backend->preserve_reservations($loaded6, $intent6);
}
my $result = $backend->write_dhcp4_config($intent4, backup_existing => 1);
if ($result->{error}) {
$callback->({ error => [ $result->{error} ], errorcode => [1] });
+90
View File
@@ -329,6 +329,96 @@ my $deleted = $backend->delete_reservations( $reservation_config, { 'hw-address'
is( scalar @$deleted, 1, 'reservation delete returns deleted reservation' );
is( scalar @{ $reservation_config->{Dhcp4}{subnet4}[0]{reservations} }, 0, 'reservation is removed from config' );
my $existing_dhcp4 = {
Dhcp4 => {
subnet4 => [
{
id => 1,
subnet => '10.20.0.0/24',
reservations => [
{
'hw-address' => '00:11:22:33:44:66',
'ip-address' => '10.20.0.12',
hostname => 'node20',
},
],
},
{
id => 2,
subnet => '10.30.0.0/24',
reservations => [
{
'hw-address' => '00:11:22:33:44:77',
'ip-address' => '10.30.0.12',
hostname => 'removed-node',
},
],
},
],
},
};
my $regenerated_dhcp4 = {
subnets => [
{
id => 9,
subnet => '10.20.0.0/24',
},
],
};
$backend->preserve_reservations( $existing_dhcp4, $regenerated_dhcp4 );
is_deeply(
$regenerated_dhcp4->{subnets}[0]{reservations},
$existing_dhcp4->{Dhcp4}{subnet4}[0]{reservations},
'regeneration preserves IPv4 reservations in the matching subnet'
);
is(
scalar @{ $regenerated_dhcp4->{subnets}[0]{reservations} },
1,
'regeneration drops reservations for removed IPv4 subnets'
);
is(
$regenerated_dhcp4->{subnets}[0]{id},
9,
'regeneration does not copy the old IPv4 subnet id'
);
my $existing_dhcp6 = {
Dhcp6 => {
subnet6 => [
{
id => 10001,
subnet => '2001:db8:20::/64',
reservations => [
{
duid => '00:04:00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff',
'ip-addresses' => ['2001:db8:20::12'],
hostname => 'node20-v6',
},
],
},
],
},
};
my $regenerated_dhcp6 = {
subnets => [
{
id => 10009,
subnet => '2001:db8:20::/64',
},
],
};
$backend->preserve_reservations( $existing_dhcp6, $regenerated_dhcp6 );
is_deeply(
$regenerated_dhcp6->{subnets}[0]{reservations},
$existing_dhcp6->{Dhcp6}{subnet6}[0]{reservations},
'regeneration preserves IPv6 reservations in the matching subnet'
);
is(
$regenerated_dhcp6->{subnets}[0]{id},
10009,
'regeneration does not copy the old IPv6 subnet id'
);
my $hookdir = tempdir(CLEANUP => 1);
my $hook = "$hookdir/libdhcp_host_cmds.so";
open(my $hookfh, '>', $hook) or die "Unable to create fake hook: $!";