mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-05-05 16:49:08 +00:00
Render Kea additional classes by version
This commit is contained in:
@@ -62,10 +62,10 @@ sub render_dhcp4_config {
|
||||
|
||||
$dhcp4{'control-socket'} = $intent->{'control-socket'} if $intent->{'control-socket'};
|
||||
$dhcp4{'hooks-libraries'} = $intent->{'hooks-libraries'} if $intent->{'hooks-libraries'};
|
||||
$dhcp4{'option-def'} = $intent->{'option-def'} if $intent->{'option-def'};
|
||||
$dhcp4{'client-classes'} = $intent->{'client-classes'} if $intent->{'client-classes'};
|
||||
$dhcp4{'option-data'} = $intent->{'option-data'} if $intent->{'option-data'};
|
||||
$dhcp4{'dhcp-ddns'} = $intent->{'dhcp-ddns'} if $intent->{'dhcp-ddns'};
|
||||
$dhcp4{'option-def'} = $intent->{'option-def'} if $intent->{'option-def'};
|
||||
$dhcp4{'client-classes'} = [ map { $self->_render_client_class($_) } @{ $intent->{'client-classes'} } ] if $intent->{'client-classes'};
|
||||
$dhcp4{'option-data'} = $intent->{'option-data'} if $intent->{'option-data'};
|
||||
$dhcp4{'dhcp-ddns'} = $intent->{'dhcp-ddns'} if $intent->{'dhcp-ddns'};
|
||||
foreach my $field (qw/ddns-send-updates ddns-override-no-update ddns-override-client-update ddns-qualifying-suffix ddns-update-on-renew/) {
|
||||
$dhcp4{$field} = $intent->{$field} if exists $intent->{$field};
|
||||
}
|
||||
@@ -659,13 +659,19 @@ sub _render_subnet4 {
|
||||
$rendered{interface} = $subnet->{interface} if defined $subnet->{interface};
|
||||
$rendered{'next-server'} = _first_defined( $subnet->{'next-server'}, $subnet->{next_server} );
|
||||
$rendered{'boot-file-name'} = _first_defined( $subnet->{'boot-file-name'}, $subnet->{boot_file_name} );
|
||||
$rendered{'option-data'} = _first_defined( $subnet->{'option-data'}, $subnet->{option_data} );
|
||||
$rendered{'require-client-classes'} = _first_defined( $subnet->{'require-client-classes'}, $subnet->{require_client_classes} );
|
||||
$rendered{reservations} = $subnet->{reservations} if $subnet->{reservations};
|
||||
$rendered{'option-data'} = _first_defined( $subnet->{'option-data'}, $subnet->{option_data} );
|
||||
my $additional_classes = _first_defined(
|
||||
$subnet->{additional_client_classes},
|
||||
$subnet->{'evaluate-additional-classes'},
|
||||
$subnet->{evaluate_additional_classes},
|
||||
$subnet->{'require-client-classes'},
|
||||
$subnet->{require_client_classes},
|
||||
);
|
||||
$rendered{ $self->_additional_class_list_field() } = $additional_classes if defined $additional_classes;
|
||||
$rendered{reservations} = $subnet->{reservations} if $subnet->{reservations};
|
||||
delete $rendered{'next-server'} unless defined $rendered{'next-server'};
|
||||
delete $rendered{'boot-file-name'} unless defined $rendered{'boot-file-name'};
|
||||
delete $rendered{'option-data'} unless defined $rendered{'option-data'};
|
||||
delete $rendered{'require-client-classes'} unless defined $rendered{'require-client-classes'};
|
||||
|
||||
if ( $subnet->{pools} ) {
|
||||
$rendered{pools} = $subnet->{pools};
|
||||
@@ -678,6 +684,26 @@ sub _render_subnet4 {
|
||||
return \%rendered;
|
||||
}
|
||||
|
||||
sub _render_client_class {
|
||||
my ( $self, $client_class ) = @_;
|
||||
|
||||
my %rendered = %$client_class;
|
||||
my $additional_only = _first_defined(
|
||||
$client_class->{additional_only},
|
||||
$client_class->{'only-in-additional-list'},
|
||||
$client_class->{only_in_additional_list},
|
||||
$client_class->{'only-if-required'},
|
||||
$client_class->{only_if_required},
|
||||
);
|
||||
|
||||
delete @rendered{qw/additional_only only_in_additional_list only_if_required/};
|
||||
delete $rendered{'only-in-additional-list'};
|
||||
delete $rendered{'only-if-required'};
|
||||
$rendered{ $self->_additional_class_flag_field() } = $additional_only if defined $additional_only;
|
||||
|
||||
return \%rendered;
|
||||
}
|
||||
|
||||
sub _render_subnet6 {
|
||||
my ( $self, $subnet ) = @_;
|
||||
|
||||
@@ -761,6 +787,51 @@ sub _control_agent_not_found {
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub _additional_class_flag_field {
|
||||
my ($self) = @_;
|
||||
|
||||
return $self->_use_modern_additional_class_syntax() ? 'only-in-additional-list' : 'only-if-required';
|
||||
}
|
||||
|
||||
sub _additional_class_list_field {
|
||||
my ($self) = @_;
|
||||
|
||||
return $self->_use_modern_additional_class_syntax() ? 'evaluate-additional-classes' : 'require-client-classes';
|
||||
}
|
||||
|
||||
sub _use_modern_additional_class_syntax {
|
||||
my ($self) = @_;
|
||||
|
||||
return 1 if $self->{additional_class_syntax} && $self->{additional_class_syntax} eq 'modern';
|
||||
return 0 if $self->{additional_class_syntax} && $self->{additional_class_syntax} eq 'legacy';
|
||||
|
||||
my $version = $self->kea_version();
|
||||
return _version_at_least( $version, '2.7.4' );
|
||||
}
|
||||
|
||||
sub kea_version {
|
||||
my ($self) = @_;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if ( $output =~ /(\d+(?:\.\d+){1,2})/ ) {
|
||||
$self->{_detected_kea_version} = $1;
|
||||
}
|
||||
|
||||
return $self->{_detected_kea_version};
|
||||
}
|
||||
|
||||
sub _first_defined {
|
||||
my @values = @_;
|
||||
foreach my $value (@values) {
|
||||
@@ -770,6 +841,25 @@ sub _first_defined {
|
||||
return;
|
||||
}
|
||||
|
||||
sub _version_at_least {
|
||||
my ( $version, $minimum ) = @_;
|
||||
|
||||
return 0 unless defined($version) && $version =~ /^\d+(?:\.\d+)*/;
|
||||
|
||||
my @version_parts = split /\./, $version;
|
||||
my @minimum_parts = split /\./, $minimum;
|
||||
my $max = @version_parts > @minimum_parts ? @version_parts : @minimum_parts;
|
||||
|
||||
for my $idx ( 0 .. $max - 1 ) {
|
||||
my $left = $version_parts[$idx] || 0;
|
||||
my $right = $minimum_parts[$idx] || 0;
|
||||
return 1 if $left > $right;
|
||||
return 0 if $left < $right;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub _integer {
|
||||
my ($value) = @_;
|
||||
|
||||
|
||||
@@ -2777,7 +2777,7 @@ sub kea_subnet4_intent
|
||||
);
|
||||
$subnet{interface} = $interface unless $remote;
|
||||
if ($opal_class) {
|
||||
$subnet{'require-client-classes'} = [ $opal_class->{name} ];
|
||||
$subnet{additional_client_classes} = [ $opal_class->{name} ];
|
||||
$subnet{client_classes} = [$opal_class];
|
||||
}
|
||||
|
||||
@@ -2794,10 +2794,10 @@ sub kea_opal_client_class
|
||||
$class_name =~ s/[^A-Za-z0-9_.-]/_/g;
|
||||
|
||||
return {
|
||||
name => $class_name,
|
||||
test => 'option[93].hex == 0x000e',
|
||||
'only-if-required' => JSON::true,
|
||||
'option-data' => [
|
||||
name => $class_name,
|
||||
test => 'option[93].hex == 0x000e',
|
||||
additional_only => JSON::true,
|
||||
'option-data' => [
|
||||
{
|
||||
name => 'conf-file',
|
||||
data => "http://$tftp:$httpport/tftpboot/pxelinux.cfg/p/" . $net . "_" . $prefix,
|
||||
|
||||
@@ -33,10 +33,10 @@ my $json = $backend->render_dhcp4_config(
|
||||
'boot-file-name' => 'http://192.168.122.1:80/tftpboot/xcat/xnba/nodes/node01',
|
||||
},
|
||||
{
|
||||
name => 'xcat-opal-v3-192.168.122.0-24',
|
||||
test => 'option[93].hex == 0x000e',
|
||||
'only-if-required' => JSON::true,
|
||||
'option-data' => [
|
||||
name => 'xcat-opal-v3-192.168.122.0-24',
|
||||
test => 'option[93].hex == 0x000e',
|
||||
additional_only => JSON::true,
|
||||
'option-data' => [
|
||||
{ name => 'conf-file', data => 'http://192.168.122.1:80/tftpboot/pxelinux.cfg/p/192.168.122.0_24' },
|
||||
],
|
||||
},
|
||||
@@ -64,7 +64,7 @@ my $json = $backend->render_dhcp4_config(
|
||||
subnet => '192.168.122.0/24',
|
||||
dynamicrange => '192.168.122.100-192.168.122.120',
|
||||
next_server => '192.168.122.1',
|
||||
'require-client-classes' => ['xcat-opal-v3-192.168.122.0-24'],
|
||||
additional_client_classes => ['xcat-opal-v3-192.168.122.0-24'],
|
||||
option_data => [
|
||||
{ name => 'routers', data => '192.168.122.1' },
|
||||
{ name => 'domain-name', data => 'cluster.test' },
|
||||
|
||||
@@ -10,7 +10,7 @@ use Test::More;
|
||||
|
||||
use xCAT::DHCP::Backend::Kea;
|
||||
|
||||
my $backend = xCAT::DHCP::Backend::Kea->new();
|
||||
my $backend = xCAT::DHCP::Backend::Kea->new( kea_version => '2.4.1' );
|
||||
my $json = $backend->render_dhcp4_config(
|
||||
{
|
||||
interfaces => ['eth0'],
|
||||
@@ -22,7 +22,7 @@ my $json = $backend->render_dhcp4_config(
|
||||
interface => 'eth0',
|
||||
dynamicrange => '10.0.0.100-10.0.0.120;10.0.0.130,10.0.0.140',
|
||||
next_server => '10.0.0.1',
|
||||
'require-client-classes' => ['xcat-opal-v3-10.0.0.0-24'],
|
||||
additional_client_classes => ['xcat-opal-v3-10.0.0.0-24'],
|
||||
option_data => [
|
||||
{ name => 'routers', data => '10.0.0.1' },
|
||||
{ name => 'domain-name-servers', data => '10.0.0.2, 10.0.0.3' },
|
||||
@@ -44,10 +44,10 @@ my $json = $backend->render_dhcp4_config(
|
||||
'boot-file-name' => 'xcat/xnba.efi',
|
||||
},
|
||||
{
|
||||
name => 'xcat-opal-v3-10.0.0.0-24',
|
||||
test => 'option[93].hex == 0x000e',
|
||||
'only-if-required' => JSON::true,
|
||||
'option-data' => [
|
||||
name => 'xcat-opal-v3-10.0.0.0-24',
|
||||
test => 'option[93].hex == 0x000e',
|
||||
additional_only => JSON::true,
|
||||
'option-data' => [
|
||||
{ name => 'conf-file', data => 'http://10.0.0.1/tftpboot/pxelinux.cfg/p/10.0.0.0_24' },
|
||||
],
|
||||
},
|
||||
@@ -123,6 +123,38 @@ is_deeply(
|
||||
'client classes are preserved, including subnet-specific OPAL conf-file class'
|
||||
);
|
||||
|
||||
my $modern_backend = xCAT::DHCP::Backend::Kea->new( kea_version => '3.0.1' );
|
||||
my $modern_json = $modern_backend->render_dhcp4_config(
|
||||
{
|
||||
subnets => [
|
||||
{
|
||||
id => 3,
|
||||
subnet => '10.0.2.0/24',
|
||||
pools => [],
|
||||
additional_client_classes => ['xcat-opal-v3-10.0.2.0-24'],
|
||||
},
|
||||
],
|
||||
'client-classes' => [
|
||||
{
|
||||
name => 'xcat-opal-v3-10.0.2.0-24',
|
||||
test => 'option[93].hex == 0x000e',
|
||||
additional_only => JSON::true,
|
||||
},
|
||||
],
|
||||
}
|
||||
);
|
||||
my $modern_config = decode_json($modern_json);
|
||||
my $modern_subnet = $modern_config->{Dhcp4}{subnet4}[0];
|
||||
my $modern_class = $modern_config->{Dhcp4}{'client-classes'}[0];
|
||||
is_deeply(
|
||||
$modern_subnet->{'evaluate-additional-classes'},
|
||||
['xcat-opal-v3-10.0.2.0-24'],
|
||||
'Kea 3.x renders modern subnet additional-class evaluation field'
|
||||
);
|
||||
ok( !exists $modern_subnet->{'require-client-classes'}, 'Kea 3.x output omits deprecated subnet additional-class field' );
|
||||
is( $modern_class->{'only-in-additional-list'}, JSON::true, 'Kea 3.x renders modern class additional-evaluation flag' );
|
||||
ok( !exists $modern_class->{'only-if-required'}, 'Kea 3.x output omits deprecated class additional-evaluation flag' );
|
||||
|
||||
my $empty_boot_json = $backend->render_dhcp4_config(
|
||||
{
|
||||
subnets => [
|
||||
|
||||
Reference in New Issue
Block a user