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:
@@ -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 ) = @_;
|
||||
|
||||
|
||||
@@ -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] });
|
||||
|
||||
@@ -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: $!";
|
||||
|
||||
Reference in New Issue
Block a user