mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-10-21 23:28:19 +00:00
bf5e2bdb20
The packet test runs dhcpd and kea-dhcp4 in a network namespace with the rules that BootPolicy renders, and sends relayed DHCPDISCOVER packets from a second namespace. It checks the boot file of firmware PXE, xNBA and iPXE clients, with each iPXE feature present or missing, for an xnba node and an ipxe node that install, for both methods with a SAN disk, and for an unknown node. On ISC it also covers an iSCSI node of each method in the boot state, and repeats the node rows after a restart, when dhcpd reads the host statements back from dhcpd.leases. On Kea it repeats the rows of the ipxe nodes with a configuration that makedhcp upgraded, which keeps the xNBA global classes of the older makedhcp -n. It also checks that the UEFI clients of an xnba node get xNBA when the server lacks the xNBA UEFI file. The Kea validation test checks the x86 classes of xnba and ipxe nodes with kea-dhcp4 -t. XCAT::Test::DHCP builds the packets, and the integration case expects seven files.
313 lines
12 KiB
Perl
313 lines
12 KiB
Perl
use strict;
|
|
use warnings;
|
|
|
|
use FindBin;
|
|
use lib "$FindBin::Bin/../../perl-xCAT";
|
|
|
|
use File::Temp qw/tempfile/;
|
|
use JSON ();
|
|
use Test::More;
|
|
|
|
use xCAT::CommandUtils;
|
|
use xCAT::DHCP::Backend::Kea;
|
|
use xCAT::DHCP::BootPolicy;
|
|
|
|
my $kea_dhcp4 = xCAT::CommandUtils::find_executable('kea-dhcp4');
|
|
plan skip_all => 'kea-dhcp4 is not installed' unless $kea_dhcp4;
|
|
|
|
my $validation_dir = validation_temp_dir($kea_dhcp4);
|
|
plan skip_all => 'kea-dhcp4 cannot read temporary config files; run as root to validate from /etc/kea'
|
|
unless defined $validation_dir;
|
|
|
|
my $backend = xCAT::DHCP::Backend::Kea->new();
|
|
my $json = $backend->render_dhcp4_config(
|
|
{
|
|
interfaces => ['*'],
|
|
'option-def' => [
|
|
{ name => 'conf-file', code => 209, type => 'string', space => 'dhcp4' },
|
|
{ name => 'iscsi-initiator-iqn', code => 203, type => 'string', space => 'dhcp4' },
|
|
{ name => 'cumulus-provision-url', code => 239, type => 'string', space => 'dhcp4' },
|
|
],
|
|
'client-classes' => [
|
|
{
|
|
name => 'xcat-xnba-node01-525400123456-bios',
|
|
test => "(option[77].exists and (option[77].text == 'xNBA' or option[77].hex == 0x784e4241 or substring(option[77].hex,1,4) == 'xNBA')) and option[93].hex == 0x0000 and pkt4.mac == 0x525400123456",
|
|
'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',
|
|
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' },
|
|
],
|
|
},
|
|
{
|
|
name => 'xcat-s390x-qemu-192.168.122.0_24',
|
|
test => 'option[93].hex == 0x001f',
|
|
additional_only => JSON::true,
|
|
'option-data' => [
|
|
{ name => 'conf-file', data => 's390x/192.168.122.0_24' },
|
|
],
|
|
},
|
|
{
|
|
name => 'xcat-uefi-x64',
|
|
test => "(option[93].hex == 0x0007 or option[93].hex == 0x0009 or option[93].hex == 0x0010) and not ((option[77].exists and (option[77].text == 'xNBA' or option[77].hex == 0x784e4241 or substring(option[77].hex,1,4) == 'xNBA')))",
|
|
'boot-file-name' => 'xcat/xnba.efi',
|
|
},
|
|
],
|
|
'dhcp-ddns' => {
|
|
'enable-updates' => JSON::true,
|
|
'server-ip' => '127.0.0.1',
|
|
'server-port' => 53001,
|
|
'ncr-protocol' => 'UDP',
|
|
'ncr-format' => 'JSON',
|
|
},
|
|
'ddns-send-updates' => JSON::true,
|
|
'ddns-override-no-update' => JSON::true,
|
|
'ddns-override-client-update' => JSON::true,
|
|
'ddns-qualifying-suffix' => 'cluster.test.',
|
|
'ddns-update-on-renew' => JSON::true,
|
|
subnets => [
|
|
{
|
|
id => 1,
|
|
subnet => '192.168.122.0/24',
|
|
dynamicrange => '192.168.122.100-192.168.122.120',
|
|
next_server => '192.168.122.1',
|
|
additional_client_classes => [
|
|
'xcat-opal-v3-192.168.122.0-24',
|
|
'xcat-s390x-qemu-192.168.122.0_24',
|
|
],
|
|
option_data => [
|
|
{ name => 'routers', data => '192.168.122.1' },
|
|
{ name => 'domain-name', data => 'cluster.test' },
|
|
{ name => 'domain-name-servers', data => '192.168.122.1' },
|
|
{ name => 'cumulus-provision-url', data => 'http://192.168.122.1:80/install/postscripts/cumulusztp' },
|
|
],
|
|
reservations => [
|
|
{
|
|
'hw-address' => '52:54:00:12:34:56',
|
|
'ip-address' => '192.168.122.50',
|
|
hostname => 'node01',
|
|
'next-server' => '192.168.122.1',
|
|
'boot-file-name' => 'pxelinux.0',
|
|
'option-data' => [ { name => 'host-name', data => 'node01' } ],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id => 2,
|
|
subnet => '192.168.123.0/24',
|
|
pools => [],
|
|
reservations => [
|
|
{
|
|
'hw-address' => '52:54:00:65:43:21',
|
|
'ip-address' => '192.168.123.50',
|
|
hostname => 'node02',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
}
|
|
);
|
|
|
|
my $path = write_validation_file( $json, 'xcat-test-kea-dhcp4' );
|
|
|
|
my $result = $backend->validate_dhcp4_config($path);
|
|
ok( !$result->{error}, 'generated Kea DHCPv4 config validates with kea-dhcp4 -t' )
|
|
or diag $result->{error};
|
|
|
|
# makedhcp without -n adds the iPXE feature options to the configuration of an older makedhcp -n, and
|
|
# writes it back. The next makedhcp reads the upgraded configuration.
|
|
{
|
|
my $source = $path;
|
|
my @written;
|
|
for my $label ( 'an upgraded', 'a current' ) {
|
|
my $loaded = $backend->load_dhcp4_config($source);
|
|
xCAT::DHCP::BootPolicy->kea_declare_ipxe_features( $loaded->{Dhcp4} );
|
|
$source = write_validation_file( $backend->encode_config($loaded), 'xcat-test-kea-dhcp4-upgraded' );
|
|
push @written, $source;
|
|
my $check = $backend->validate_dhcp4_config($source);
|
|
ok( !$check->{error}, "makedhcp writes $label Kea DHCPv4 config that validates with kea-dhcp4 -t" )
|
|
or diag $check->{error};
|
|
}
|
|
unlink @written;
|
|
}
|
|
|
|
unlink $path;
|
|
# The x86 boot classes as makedhcp renders them, with an xnba node, an ipxe node and an ipxe node that
|
|
# boots from SAN, and the option 175 definitions that the upstream loader classes test.
|
|
{
|
|
my $boot_json = $backend->render_dhcp4_config(
|
|
{
|
|
interfaces => ['*'],
|
|
'option-def' => [
|
|
{ name => 'conf-file', code => 209, type => 'string', space => 'dhcp4' },
|
|
@{ xCAT::DHCP::BootPolicy->kea_ipxe_option_defs() },
|
|
],
|
|
'client-classes' => [
|
|
@{ xCAT::DHCP::BootPolicy->kea_xnba_node_classes(
|
|
xnba_kpxe => 1,
|
|
xnba_efi => 1,
|
|
nodes => [
|
|
{ node => 'node01', mac => '52:54:00:12:34:56', next_server => '192.168.122.1', httpport => 80, netboot => 'xnba' },
|
|
{ node => 'node02', mac => '52:54:00:12:34:57', next_server => '192.168.122.1', httpport => 80, netboot => 'ipxe' },
|
|
{ node => 'node03', mac => '52:54:00:12:34:58', next_server => '192.168.122.1', httpport => 80, netboot => 'ipxe', iscsi => 1 },
|
|
],
|
|
) },
|
|
@{ xCAT::DHCP::BootPolicy->kea_client_classes() },
|
|
@{ xCAT::DHCP::BootPolicy->kea_xnba_network_classes(
|
|
net => '192.168.122.0',
|
|
prefix => 24,
|
|
next_server => '192.168.122.1',
|
|
httpport => 80,
|
|
) },
|
|
],
|
|
subnets => [
|
|
{
|
|
id => 1,
|
|
subnet => '192.168.122.0/24',
|
|
dynamicrange => '192.168.122.100-192.168.122.120',
|
|
next_server => '192.168.122.1',
|
|
},
|
|
],
|
|
}
|
|
);
|
|
my $boot_path = write_validation_file( $boot_json, 'xcat-test-kea-dhcp4-boot' );
|
|
my $boot_result = $backend->validate_dhcp4_config($boot_path);
|
|
ok( !$boot_result->{error}, 'the x86 boot classes of xnba and ipxe nodes validate with kea-dhcp4 -t' )
|
|
or diag $boot_result->{error};
|
|
unlink $boot_path;
|
|
}
|
|
|
|
SKIP: {
|
|
skip 'kea-dhcp6 is not installed', 1 unless xCAT::CommandUtils::find_executable('kea-dhcp6');
|
|
my $dhcp6_json = $backend->render_dhcp6_config(
|
|
{
|
|
interfaces => ['*'],
|
|
'dhcp-ddns' => {
|
|
'enable-updates' => JSON::true,
|
|
'server-ip' => '127.0.0.1',
|
|
'server-port' => 53001,
|
|
'ncr-protocol' => 'UDP',
|
|
'ncr-format' => 'JSON',
|
|
},
|
|
'ddns-send-updates' => JSON::true,
|
|
'ddns-override-no-update' => JSON::true,
|
|
'ddns-override-client-update' => JSON::true,
|
|
'ddns-qualifying-suffix' => 'cluster.test.',
|
|
'ddns-update-on-renew' => JSON::true,
|
|
subnets => [
|
|
{
|
|
id => 6,
|
|
subnet => '2001:db8:1::/64',
|
|
dynamicrange => '2001:db8:1::100/120',
|
|
option_data => [
|
|
{ name => 'dns-servers', data => '2001:db8:1::1' },
|
|
{ name => 'domain-search', data => 'cluster.test' },
|
|
],
|
|
reservations => [
|
|
{
|
|
duid => '00:04:52:54:00:12:34:56',
|
|
'ip-addresses' => ['2001:db8:1::50'],
|
|
hostname => 'nodev6',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
}
|
|
);
|
|
my $dhcp6_path = write_validation_file( $dhcp6_json, 'xcat-test-kea-dhcp6' );
|
|
my $dhcp6_result = $backend->validate_dhcp6_config($dhcp6_path);
|
|
ok( !$dhcp6_result->{error}, 'generated Kea DHCPv6 config validates with kea-dhcp6 -t' )
|
|
or diag $dhcp6_result->{error};
|
|
unlink $dhcp6_path;
|
|
}
|
|
|
|
SKIP: {
|
|
skip 'kea-dhcp-ddns is not installed', 1 unless xCAT::CommandUtils::find_executable('kea-dhcp-ddns');
|
|
my $ddns_json = $backend->render_ddns_config(
|
|
{
|
|
'tsig-keys' => [
|
|
{ name => 'xcat_key', algorithm => 'HMAC-SHA256', secret => 'YWJjMTIz' },
|
|
],
|
|
forward_domains => [
|
|
{
|
|
name => 'cluster.test.',
|
|
'key-name' => 'xcat_key',
|
|
'dns-servers' => [ { 'ip-address' => '127.0.0.1', port => 53 } ],
|
|
},
|
|
],
|
|
reverse_domains => [
|
|
{
|
|
name => '122.168.192.in-addr.arpa.',
|
|
'key-name' => 'xcat_key',
|
|
'dns-servers' => [ { 'ip-address' => '127.0.0.1', port => 53 } ],
|
|
},
|
|
],
|
|
}
|
|
);
|
|
my $ddns_path = write_validation_file( $ddns_json, 'xcat-test-kea-ddns' );
|
|
my $ddns_result = $backend->validate_ddns_config($ddns_path);
|
|
ok( !$ddns_result->{error}, 'generated Kea DHCP-DDNS config validates with kea-dhcp-ddns -t' )
|
|
or diag $ddns_result->{error};
|
|
unlink $ddns_path;
|
|
}
|
|
|
|
SKIP: {
|
|
skip 'kea-ctrl-agent is not installed', 1 unless xCAT::CommandUtils::find_executable('kea-ctrl-agent');
|
|
my $ctrl_agent_json = $backend->render_ctrl_agent_config(
|
|
{
|
|
dhcp6 => 1,
|
|
ddns => 1,
|
|
}
|
|
);
|
|
my $ctrl_path = write_validation_file( $ctrl_agent_json, 'xcat-test-kea-ctrl-agent' );
|
|
my $ctrl_result = $backend->validate_ctrl_agent_config($ctrl_path);
|
|
ok( !$ctrl_result->{error}, 'generated Kea Control Agent config validates with kea-ctrl-agent -t' )
|
|
or diag $ctrl_result->{error};
|
|
unlink $ctrl_path;
|
|
}
|
|
done_testing();
|
|
|
|
sub validation_temp_dir {
|
|
my ($kea_dhcp4) = @_;
|
|
|
|
return '/etc/kea' if -d '/etc/kea' && -w '/etc/kea';
|
|
|
|
my ( $fh, $path ) = tempfile();
|
|
print $fh '{"Dhcp4":{"interfaces-config":{"interfaces":[]},"lease-database":{"type":"memfile","name":"/tmp/xcat-test-kea-leases4.csv"},"valid-lifetime":600,"subnet4":[]}}';
|
|
close($fh);
|
|
chmod 0644, $path or die "Unable to set $path permissions: $!";
|
|
|
|
my $command = shell_quote($kea_dhcp4) . ' -t ' . shell_quote($path) . ' 2>&1';
|
|
my $output = `$command`;
|
|
unlink $path;
|
|
if ( $output =~ /Unable to open file/ ) {
|
|
return;
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
sub write_validation_file {
|
|
my ( $content, $template ) = @_;
|
|
|
|
my %opts = ( TEMPLATE => "$template-XXXXXX", UNLINK => 0 );
|
|
$opts{DIR} = $validation_dir if defined $validation_dir;
|
|
|
|
my ( $fh, $path ) = tempfile(%opts);
|
|
print $fh $content;
|
|
close($fh);
|
|
chmod 0644, $path or die "Unable to set $path permissions: $!";
|
|
|
|
return $path;
|
|
}
|
|
|
|
sub shell_quote {
|
|
my ($value) = @_;
|
|
|
|
$value =~ s/'/'\\''/g;
|
|
return "'$value'";
|
|
}
|