2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-09-04 20:17:55 +00:00

Merge pull request #7777 from VersatusHPC/fix/dhcp-infiniband-twin-entry

fix(dhcp): register the InfiniBand identity of a node that boots over IPoIB
This commit is contained in:
Daniel Hilst
2026-08-31 11:46:30 -03:00
committed by GitHub
3 changed files with 827 additions and 56 deletions
+300 -56
View File
@@ -232,22 +232,41 @@ sub _isc_static_host_fallback
sub _delete_isc_static_host
{
my $node = shift;
my ($node, $config, $hostname) = @_;
$config ||= \@dhcpconf;
my $start_marker = defined($hostname)
? qr/^#xCAT host declaration for \Q$node\E aka host \Q$hostname\E start$/
: qr/^#xCAT host declaration for \Q$node\E aka host .* start$/;
my $end_marker = defined($hostname)
? qr/^(?:\}\s*)?#xCAT host declaration for \Q$node\E aka host \Q$hostname\E end$/
: qr/^(?:\}\s*)?#xCAT host declaration for \Q$node\E aka host .* end$/;
my @updated;
my $skip = 0;
foreach my $line (@dhcpconf) {
if ($line =~ /^#xCAT host declaration for \Q$node\E\b.* start$/) {
foreach my $line (@{$config}) {
if ($line =~ $start_marker) {
$skip = 1;
next;
}
if ($skip && $line =~ /^#xCAT host declaration for \Q$node\E\b.* end$/) {
if ($skip && $line =~ $end_marker) {
$skip = 0;
next;
}
push @updated, $line unless $skip;
}
@dhcpconf = @updated;
@{$config} = @updated;
}
sub _begin_isc_static_host_update
{
my ($node, $enabled, $config) = @_;
return 0 unless $enabled;
$config ||= \@dhcpconf;
my $line_count = scalar(@{$config});
_delete_isc_static_host($node, $config);
return scalar(@{$config}) != $line_count;
}
sub _static_host_statements
@@ -261,21 +280,51 @@ sub _static_host_statements
return $statements;
}
sub _node_host_statements
{
my ($node, $statements) = @_;
return 'ddns-hostname \"' . $node . '\"; send host-name \"'
. $node . '\";' . ($statements || '');
}
sub _noip_hostname
{
my ($node, $mac) = @_;
my $hostname = $node . '-noip' . $mac;
$hostname =~ s/://g;
return $hostname;
}
sub _add_isc_static_host
{
my ($node, $hostname, $mac, $ip, $statements) = @_;
my ($node, $hostname, $mac, $hardwaretype, $mgtifname, $ip, $statements,
$has_infiniband_identity, $config, $update_started) = @_;
$config ||= \@dhcpconf;
return unless $ip && $ip ne 'DENIED';
_delete_isc_static_host($node);
if ($update_started && !${$update_started}) {
$restartdhcp = 1 if _begin_isc_static_host_update($node, 1, $config);
${$update_started} = 1;
}
$mac = normalize_mac($mac) || $mac;
_delete_isc_static_host($node, $config, $hostname);
my $host_statements = _static_host_statements($statements);
push @dhcpconf, "#xCAT host declaration for $node aka host $hostname start\n";
push @dhcpconf, "host $hostname {\n";
push @dhcpconf, " hardware ethernet $mac;\n";
push @dhcpconf, " fixed-address $ip;\n";
push @dhcpconf, " $host_statements\n" if $host_statements;
push @dhcpconf, "} #xCAT host declaration for $node aka host $hostname end\n";
my $hardware_kind = $hardwaretype == 32 ? 'infiniband' : 'ethernet';
push @{$config}, "#xCAT host declaration for $node aka host $hostname start\n";
push @{$config}, "host $hostname {\n";
push @{$config}, " hardware $hardware_kind $mac;\n";
push @{$config}, " fixed-address $ip;\n";
push @{$config}, " $host_statements\n" if $host_statements;
push @{$config}, "}\n";
push @{$config}, _infiniband_twin_static_lines(
$hostname, $mac, $hardwaretype, $mgtifname, $ip,
$host_statements, $has_infiniband_identity
);
push @{$config}, "#xCAT host declaration for $node aka host $hostname end\n";
$restartdhcp = 1;
}
@@ -491,6 +540,7 @@ sub delnode
if ($ent and $ent->{mac})
{
my @macs = split(/\|/, $ent->{mac});
my $has_infiniband_identity = _infiniband_identity_present(@macs);
my $mace;
my $count = 0;
foreach $mace (@macs)
@@ -505,13 +555,19 @@ sub delnode
} #Default to hostname equal to nodename
unless ($mac) { next; } #Skip corrupt format
if (!grep /:/, $mac) {
my $normalized_mac = normalize_mac($mac);
if ($normalized_mac) {
$mac = $normalized_mac;
} elsif (!grep /:/, $mac) {
$mac = lc($mac);
$mac =~ s/(\w{2})/$1:/g;
$mac =~ s/:$//;
}
my $hostname = $hname;
my %client_nethash = xCAT::DBobjUtils->getNetwkInfo([$node]);
my $hardwaretype = _hardware_type_for(
$mac, $client_nethash{$node}{mgtifname}
);
if ($client_nethash{$node}{mgtifname} =~ /hf/)
{
if (scalar(@macs) > 1) {
@@ -532,14 +588,21 @@ sub delnode
print $omshell "remove\n";
print $omshell "close\n";
if ($mac)
my ($ibnamecommands, $ibaddresscommands) =
_infiniband_twin_delete_commands(
$hostname, $mac, $hardwaretype,
$client_nethash{$node}{mgtifname},
_omapi_pre_create_cleanup_supported(),
$has_infiniband_identity
);
print $omshell $ibnamecommands if ($ibnamecommands);
my $hardwarecommands =
_hardware_address_delete_commands($mac, $hardwaretype);
if ($hardwarecommands)
{
print $omshell "new host\n";
print $omshell "set hardware-address = " . $mac
. "\n"; #find and destroy mac conflict
print $omshell "open\n";
print $omshell "remove\n";
print $omshell "close\n";
print $omshell $hardwarecommands;
print $omshell $ibaddresscommands if ($ibaddresscommands);
}
if ($inetn and _omapi_ip_lookup_supported())
{
@@ -649,6 +712,182 @@ sub addnode6 {
}
sub _infiniband_twin_mac
{
my $mac = shift;
$mac = normalize_mac($mac);
return unless defined($mac) and length($mac) == 17;
return substr($mac, 0, 8) . ":03:00" . substr($mac, 8);
}
sub _infiniband_identity_present
{
foreach my $entry (@_) {
my ($address) = split(/!/, $entry);
$address = normalize_mac($address);
return 1
if defined($address)
&& (length($address) == 23 || length($address) == 26);
}
return 0;
}
sub _hardware_type_for
{
my ($mac, $mgtifname) = @_;
return 37 if defined($mgtifname) && $mgtifname =~ /hf/;
$mac = normalize_mac($mac) || $mac || '';
return 32 if length($mac) == 23 || length($mac) == 26;
return 1;
}
sub _is_infiniband_interface
{
my $mgtifname = shift;
return defined($mgtifname) && $mgtifname =~ /(?:^|!)ib[^!]*(?:!|$)/;
}
sub _infiniband_twin_identity
{
my ($hostname, $mac, $hardwaretype, $mgtifname,
$has_infiniband_identity) = @_;
my $ibmac = _infiniband_twin_mac($mac);
return if $hardwaretype != 1
|| !$ibmac
|| !_is_infiniband_interface($mgtifname)
|| $has_infiniband_identity;
return ("$hostname-xcat-ib", $ibmac);
}
sub _infiniband_twin_create_commands
{
my ($hostname, $mac, $hardwaretype, $mgtifname, $ip, $hoststatements,
$cleanup_supported, $has_infiniband_identity) = @_;
my ($ibhostname, $ibmac) = _infiniband_twin_identity(
$hostname, $mac, $hardwaretype, $mgtifname,
$has_infiniband_identity
);
return unless $ibmac;
my $commands = '';
if ($cleanup_supported && _is_infiniband_interface($mgtifname)) {
$commands .= "new host\n"
. "set name = \"$ibhostname\"\n"
. "open\n"
. "remove\n"
. "close\n";
}
$commands .= "new host\n"
. "set name = \"$ibhostname\"\n"
. "set hardware-address = $ibmac\n"
. "set dhcp-client-identifier = $ibmac\n"
. "set hardware-type = 32\n";
if ($ip eq "DENIED") {
$commands .= "set statements = \"deny booting;\"\n";
} else {
$commands .= "set ip-address = $ip\n" if ($ip);
$commands .= "set statements = \"$hoststatements\"\n" if ($hoststatements);
}
return $commands . "create\nclose\n";
}
sub _infiniband_twin_static_lines
{
my ($hostname, $mac, $hardwaretype, $mgtifname, $ip, $hoststatements,
$has_infiniband_identity) = @_;
my ($ibhostname, $ibmac) = _infiniband_twin_identity(
$hostname, $mac, $hardwaretype, $mgtifname,
$has_infiniband_identity
);
return unless $ibmac && $ip && $ip ne 'DENIED';
my @lines = (
"host $ibhostname {\n",
" hardware infiniband $ibmac;\n",
" fixed-address $ip;\n",
);
push @lines, " $hoststatements\n" if $hoststatements;
push @lines, "}\n";
return @lines;
}
sub _infiniband_twin_delete_commands
{
my ($hostname, $mac, $hardwaretype, $mgtifname, $cleanup_supported,
$has_infiniband_identity) = @_;
my $ibmac = _infiniband_twin_mac($mac);
return unless $ibmac;
if (!$cleanup_supported) {
my (undef, $current_ibmac) = _infiniband_twin_identity(
$hostname, $mac, $hardwaretype, $mgtifname,
$has_infiniband_identity
);
return unless $current_ibmac;
}
my $ibhostname = "$hostname-xcat-ib";
my $namecommands = "new host\n"
. "set name = \"$ibhostname\"\n"
. "open\n"
. "remove\n"
. "close\n";
my $addresscommands;
if ($cleanup_supported && _is_infiniband_interface($mgtifname)) {
$addresscommands = "new host\n"
. "set hardware-address = $ibmac\n"
. "set hardware-type = 32\n"
. "open\n"
. "remove\n"
. "close\n";
}
return ($namecommands, $addresscommands);
}
sub _hardware_address_delete_commands
{
my ($mac, $hardwaretype) = @_;
return unless $mac;
my $commands = "new host\n"
. "set hardware-address = $mac\n";
$commands .= "set hardware-type = $hardwaretype\n"
if $hardwaretype != 1;
return $commands . "open\nremove\nclose\n";
}
sub _infiniband_twin_update_commands
{
my ($hostname, $mac, $hardwaretype, $mgtifname, $ip, $hoststatements,
$cleanup_supported, $has_infiniband_identity) = @_;
my ($namecommands, $addresscommands);
if ($cleanup_supported) {
($namecommands, $addresscommands) =
_infiniband_twin_delete_commands(
$hostname, $mac, $hardwaretype, $mgtifname,
$cleanup_supported, $has_infiniband_identity
);
undef $addresscommands if $has_infiniband_identity;
}
my $createcommands = _infiniband_twin_create_commands(
$hostname, $mac, $hardwaretype, $mgtifname, $ip,
$hoststatements, 0, $has_infiniband_identity
);
return ($namecommands, $addresscommands, $createcommands);
}
sub addnode
{
@@ -772,6 +1011,9 @@ sub addnode
}
my @macs = split(/\|/, $ent->{mac});
my $has_infiniband_identity = _infiniband_identity_present(@macs);
my $static_host_fallback = _isc_static_host_fallback();
my $static_host_update_started = 0;
my $mace;
my $deflstaments = $lstatements;
my $count = 0;
@@ -797,10 +1039,11 @@ sub addnode
);
next;
}
my $noip_mac = $mac;
$mac = normalize_mac($mac);
my $ip = getipaddr($hname, OnlyV4 => 1);
if ($hname eq '*NOIP*') {
$hname = $node . "-noip" . $mac;
$hname =~ s/://g;
$hname = _noip_hostname($node, $noip_mac);
$ip = 'DENIED';
# } #if 'guess_next_server', inherit from the network provided value... see how this pans out
@@ -928,17 +1171,13 @@ sub addnode
}
else
{
if (!grep /:/, $mac) {
$mac = lc($mac);
$mac =~ s/(\w{2})/$1:/g;
$mac =~ s/:$//;
}
my $hostname = $hname;
my $hardwaretype = 1;
my %client_nethash = xCAT::DBobjUtils->getNetwkInfo([$node]);
if ($client_nethash{$node}{mgtifname} =~ /hf/)
my $hardwaretype = _hardware_type_for(
$mac, $client_nethash{$node}{mgtifname}
);
if ($hardwaretype == 37)
{
$hardwaretype = 37;
if (scalar(@macs) > 1) {
if ($hname !~ /^(.*)-hf(.*)$/) {
$hostname = $hname . "-hf" . $count;
@@ -946,27 +1185,35 @@ sub addnode
$hostname = $1 . "-hf" . $count;
}
}
} elsif (length($mac) == 23 || length($mac) == 26) { # 8 or 9 bytes of mac address
# Currently the only thing that has 8 or 9 bytes is an infiniband
# or infiniband like device, which is type 32 (0x20).
$hardwaretype = 32;
}
if (_isc_static_host_fallback()) {
if ($static_host_fallback) {
if ($ip ne "DENIED") {
if ($lstatements) {
$lstatements = 'ddns-hostname \"' . $node . '\"; send host-name \"' . $node . '\";' . $lstatements;
} else {
$lstatements = 'ddns-hostname \"' . $node . '\"; send host-name \"' . $node . '\";';
}
$lstatements = _node_host_statements($node, $lstatements);
} else {
$lstatements = "deny booting;";
}
_add_isc_static_host($node, $hostname, $mac, $ip, $lstatements);
_add_isc_static_host(
$node, $hostname, $mac, $hardwaretype,
$client_nethash{$node}{mgtifname}, $ip, $lstatements,
$has_infiniband_identity, undef,
\$static_host_update_started
);
$count = $count + 2;
next;
}
if ($ip ne "DENIED") {
$lstatements = _node_host_statements($node, $lstatements);
}
my ($ibnamecommands, $ibaddresscommands, $ibcreatecommands) =
_infiniband_twin_update_commands(
$hostname, $mac, $hardwaretype,
$client_nethash{$node}{mgtifname}, $ip, $lstatements,
_omapi_pre_create_cleanup_supported(),
$has_infiniband_identity
);
#syslog("local4|err", "Setting $node ($hname|$ip) to " . $mac);
if (_omapi_pre_create_cleanup_supported()) {
print $omshell "new host\n";
@@ -975,6 +1222,7 @@ sub addnode
print $omshell "open\n";
print $omshell "remove\n";
print $omshell "close\n";
print $omshell $ibnamecommands if ($ibnamecommands);
}
if ($ip and $ip ne 'DENIED' and _omapi_ip_lookup_supported()) {
print $omshell "new host\n";
@@ -984,12 +1232,9 @@ sub addnode
print $omshell "close\n";
}
if (_omapi_pre_create_cleanup_supported()) {
print $omshell "new host\n";
print $omshell "set hardware-address = " . $mac
. "\n"; #find and destroy mac conflict
print $omshell "open\n";
print $omshell "remove\n";
print $omshell "close\n";
print $omshell
_hardware_address_delete_commands($mac, $hardwaretype);
print $omshell $ibaddresscommands if ($ibaddresscommands);
}
print $omshell "new host\n";
print $omshell "set name = \"$hostname\"\n";
@@ -1006,18 +1251,12 @@ sub addnode
if ($ip) {
print $omshell "set ip-address = $ip\n";
}
if ($lstatements)
{
$lstatements = 'ddns-hostname \"' . $node . '\"; send host-name \"' . $node . '\";' . $lstatements;
} else {
$lstatements = 'ddns-hostname \"' . $node . '\"; send host-name \"' . $node . '\";';
}
print $omshell "set statements = \"$lstatements\"\n";
}
print $omshell "create\n";
print $omshell "close\n";
print $omshell $ibcreatecommands if ($ibcreatecommands);
unless ($::XCATSITEVALS{externaldhcpservers}) {
unless (grep /#definition for host $node aka host $hostname/, @dhcpconf)
{
@@ -3319,7 +3558,7 @@ sub kea_xnba_client_classes_for_nodes
);
}
sub kea_normalize_mac
sub normalize_mac
{
my ($mac) = @_;
@@ -3329,6 +3568,11 @@ sub kea_normalize_mac
return lc($mac);
}
sub kea_normalize_mac
{
return normalize_mac(shift);
}
sub kea_node_reservations6
{
my ( $backend, $config, $node ) = @_;
+343
View File
@@ -0,0 +1,343 @@
#!/usr/bin/env perl
use strict;
use warnings;
use FindBin;
use lib "$FindBin::Bin/../../xCAT-server/lib";
use lib "$FindBin::Bin/../../xCAT-server/lib/perl";
use lib "$FindBin::Bin/../../perl-xCAT";
use Test::More;
$ENV{XCATCFG} ||= 'SQLite:/tmp';
my $source_dhcp_plugin = "$FindBin::Bin/../../xCAT-server/lib/xcat/plugins/dhcp.pm";
if ( -f $source_dhcp_plugin ) {
require $source_dhcp_plugin;
} else {
require xCAT_plugin::dhcp;
}
sub twin { return xCAT_plugin::dhcp::_infiniband_twin_mac( $_[0] ); }
is(
xCAT_plugin::dhcp::normalize_mac('B8-3F-D2-4A-68-AA'),
'b8:3f:d2:4a:68:aa',
'the shared MAC normalizer canonicalizes case and separators',
);
is( twin('b8:3f:d2:4a:68:aa'), 'b8:3f:d2:03:00:4a:68:aa',
'the port GUID of the first adapter is derived from its mac' );
is( twin('b8:3f:d2:4a:68:b2'), 'b8:3f:d2:03:00:4a:68:b2',
'the port GUID of the second adapter is derived from its mac' );
is( twin('b8-3f-d2-4a-68-aa'), 'b8:3f:d2:03:00:4a:68:aa',
'a dash-separated mac produces a canonical InfiniBand identity' );
is( length( twin('b8:3f:d2:4a:68:aa') ), 23,
'the derived address is eight bytes' );
foreach my $other ( 'b8:3f:d2:03:00:4a:68:aa', '00:11:22:33:44', '', 'notamac' ) {
is( twin($other), undef, "'$other' gives no derived address" );
}
is( twin(undef), undef, 'no mac gives no derived address' );
ok(
xCAT_plugin::dhcp::_infiniband_identity_present(
'b8:3f:d2:4a:68:aa!node01',
'b8:3f:d2:03:00:4a:68:aa!node01-ib',
),
'an explicit InfiniBand identity is detected in a node mac list',
);
ok(
!xCAT_plugin::dhcp::_infiniband_identity_present(
'b83fd203004a68aa!node01-ib',
),
'a colonless address rejected by addnode is not treated as an identity',
);
ok(
xCAT_plugin::dhcp::_infiniband_identity_present(
'b8-3f-d2-03-00-4a-68-aa!node01-ib',
),
'a valid dashed InfiniBand identity is detected in a node mac list',
);
ok(
!xCAT_plugin::dhcp::_infiniband_identity_present(
'b8:3f:d2:4a:68:aa!node01',
),
'an Ethernet-only mac list needs the derived identity',
);
is(
xCAT_plugin::dhcp::_hardware_type_for('b8:3f:d2:4a:68:aa', 'eth0'),
1,
'an Ethernet interface uses the Ethernet hardware type',
);
is(
xCAT_plugin::dhcp::_hardware_type_for(
'b8:3f:d2:03:00:4a:68:aa', 'ib0'
),
32,
'an eight-byte fabric address uses the InfiniBand hardware type',
);
is(
xCAT_plugin::dhcp::_hardware_type_for('b8:3f:d2:4a:68:aa', 'hf0'),
37,
'an HFI interface uses the HFI hardware type',
);
is(
xCAT_plugin::dhcp::_node_host_statements('node01', ''),
'ddns-hostname \"node01\"; send host-name \"node01\";',
'the default host statements identify the node',
);
is(
xCAT_plugin::dhcp::_node_host_statements(
'node01', 'filename = \"bootfile\";'
),
'ddns-hostname \"node01\"; send host-name \"node01\";filename = \"bootfile\";',
'node identity is prepended to existing host statements',
);
is(
xCAT_plugin::dhcp::_noip_hostname('node01', 'B8:3F:D2:4A:68:AA'),
'node01-noipB83FD24A68AA',
'the denied-host name preserves the legacy MAC case',
);
is(
xCAT_plugin::dhcp::_noip_hostname('node01', 'B8-3F-D2-4A-68-AA'),
'node01-noipB8-3F-D2-4A-68-AA',
'the denied-host name preserves legacy dash separators',
);
my $create_commands = <<'OMAPI';
new host
set name = "node01-xcat-ib"
open
remove
close
new host
set name = "node01-xcat-ib"
set hardware-address = b8:3f:d2:03:00:4a:68:aa
set dhcp-client-identifier = b8:3f:d2:03:00:4a:68:aa
set hardware-type = 32
set ip-address = 192.0.2.10
set statements = "ddns-hostname \"node01\"; send host-name \"node01\";"
create
close
OMAPI
is(
xCAT_plugin::dhcp::_infiniband_twin_create_commands(
'node01', 'b8:3f:d2:4a:68:aa', 1, 'ib0', '192.0.2.10',
'ddns-hostname \"node01\"; send host-name \"node01\";', 1, 0
),
$create_commands,
'an Ethernet identity on an IPoIB network creates the InfiniBand twin',
);
is(
xCAT_plugin::dhcp::_infiniband_twin_create_commands(
'node01', 'b8:3f:d2:4a:68:aa', 1, '!service!ib0', '192.0.2.10',
'ddns-hostname \"node01\"; send host-name \"node01\";', 1, 0
),
$create_commands,
'a relayed IPoIB interface creates the InfiniBand twin',
);
is(
xCAT_plugin::dhcp::_infiniband_twin_create_commands(
'node01', 'b8:3f:d2:4a:68:aa', 1, 'ib0', 'DENIED', '', 1, 0
),
join( '', ( split( /^/m, $create_commands ) )[ 0 .. 9 ] )
. "set statements = \"deny booting;\"\ncreate\nclose\n",
'a denied Ethernet identity creates a denied InfiniBand twin',
);
is(
xCAT_plugin::dhcp::_infiniband_twin_create_commands(
'node01', 'b8:3f:d2:4a:68:aa', 32, 'ib0', '192.0.2.10', '', 1, 1
),
undef,
'an existing InfiniBand identity does not create another twin',
);
is(
xCAT_plugin::dhcp::_infiniband_twin_create_commands(
'node01', 'b8:3f:d2:4a:68:aa', 1, 'eth0', '192.0.2.10', '', 1, 0
),
undef,
'an Ethernet network does not create an InfiniBand twin',
);
my $create_without_cleanup = <<'OMAPI';
new host
set name = "node01-xcat-ib"
set hardware-address = b8:3f:d2:03:00:4a:68:aa
set dhcp-client-identifier = b8:3f:d2:03:00:4a:68:aa
set hardware-type = 32
set ip-address = 192.0.2.10
set statements = "ddns-hostname \"node01\"; send host-name \"node01\";"
create
close
OMAPI
is(
xCAT_plugin::dhcp::_infiniband_twin_create_commands(
'node01', 'b8:3f:d2:4a:68:aa', 1, 'ib0', '192.0.2.10',
'ddns-hostname \"node01\"; send host-name \"node01\";', 0, 0
),
$create_without_cleanup,
'the Ubuntu-limited OMAPI path creates the twin without a failed-open cleanup',
);
my $create_without_address = $create_without_cleanup;
$create_without_address =~ s/^set ip-address = .*\n//m;
is(
xCAT_plugin::dhcp::_infiniband_twin_create_commands(
'node01', 'b8:3f:d2:4a:68:aa', 1, 'ib0', undef,
'ddns-hostname \"node01\"; send host-name \"node01\";', 0, 0
),
$create_without_address,
'an unresolved IP omits only the twin address',
);
is(
xCAT_plugin::dhcp::_infiniband_twin_create_commands(
'node01', 'b8:3f:d2:4a:68:aa', 1, 'ib0', '192.0.2.10', '', 1, 1
),
undef,
'an explicit InfiniBand identity suppresses the derived twin',
);
is_deeply(
[ xCAT_plugin::dhcp::_infiniband_twin_static_lines(
'node01', 'b8:3f:d2:4a:68:aa', 1, 'ib0', '192.0.2.10',
'option host-name "node01";', 0
) ],
[
"host node01-xcat-ib {\n",
" hardware infiniband b8:3f:d2:03:00:4a:68:aa;\n",
" fixed-address 192.0.2.10;\n",
" option host-name \"node01\";\n",
"}\n",
],
'the static-host fallback declares the derived InfiniBand identity',
);
my $delete_name_commands = <<'OMAPI';
new host
set name = "node01-xcat-ib"
open
remove
close
OMAPI
my $delete_address_commands = <<'OMAPI';
new host
set hardware-address = b8:3f:d2:03:00:4a:68:aa
set hardware-type = 32
open
remove
close
OMAPI
is(
xCAT_plugin::dhcp::_hardware_address_delete_commands(
'b8:3f:d2:4a:68:aa', 1
),
"new host\nset hardware-address = b8:3f:d2:4a:68:aa\nopen\nremove\nclose\n",
'Ethernet cleanup keeps the legacy hardware-address lookup',
);
is(
xCAT_plugin::dhcp::_hardware_address_delete_commands(
'b8:3f:d2:03:00:4a:68:aa', 32
),
$delete_address_commands,
'InfiniBand cleanup includes its OMAPI hardware type',
);
my @delete_commands = xCAT_plugin::dhcp::_infiniband_twin_delete_commands(
'node01', 'b8:3f:d2:4a:68:aa', 1, 'ib0', 1, 0
);
is_deeply(
\@delete_commands,
[ $delete_name_commands, $delete_address_commands ],
'removing a node produces separate name and address cleanup commands',
);
my @moved_network_delete_commands =
xCAT_plugin::dhcp::_infiniband_twin_delete_commands(
'node01', 'b8-3f-d2-4a-68-aa', 1, 'eth0', 1, 1
);
is_deeply(
\@moved_network_delete_commands,
[ $delete_name_commands, undef ],
'removing a moved node cleans up its old twin by name only',
);
my @limited_delete_commands =
xCAT_plugin::dhcp::_infiniband_twin_delete_commands(
'node01', 'b8:3f:d2:4a:68:aa', 1, 'ib0', 0, 0
);
is_deeply(
\@limited_delete_commands,
[ $delete_name_commands, undef ],
'the Ubuntu-limited OMAPI path still removes the twin by name',
);
my @limited_ethernet_delete_commands =
xCAT_plugin::dhcp::_infiniband_twin_delete_commands(
'node01', 'b8:3f:d2:4a:68:aa', 1, 'eth0', 0, 0
);
is_deeply(
\@limited_ethernet_delete_commands,
[],
'the Ubuntu-limited OMAPI path avoids a failed-open cleanup on Ethernet',
);
my @moved_network_update_commands =
xCAT_plugin::dhcp::_infiniband_twin_update_commands(
'node01', 'b8:3f:d2:4a:68:aa', 1, 'eth0', '192.0.2.10',
'ddns-hostname \"node01\"; send host-name \"node01\";', 1, 0
);
is_deeply(
\@moved_network_update_commands,
[ $delete_name_commands, undef, undef ],
're-registering a node on Ethernet removes its former twin by name only',
);
my @infiniband_update_commands =
xCAT_plugin::dhcp::_infiniband_twin_update_commands(
'node01', 'b8:3f:d2:4a:68:aa', 1, 'ib0', '192.0.2.10',
'ddns-hostname \"node01\"; send host-name \"node01\";', 1, 0
);
is_deeply(
\@infiniband_update_commands,
[ $delete_name_commands, $delete_address_commands, $create_without_cleanup ],
're-registering an IPoIB node replaces its generated twin',
);
my @limited_update_commands =
xCAT_plugin::dhcp::_infiniband_twin_update_commands(
'node01', 'b8:3f:d2:4a:68:aa', 1, 'ib0', '192.0.2.10',
'ddns-hostname \"node01\"; send host-name \"node01\";', 0, 0
);
is_deeply(
\@limited_update_commands,
[ undef, undef, $create_without_cleanup ],
'the limited OMAPI path does not attempt a failed-open cleanup',
);
my @explicit_identity_update_commands =
xCAT_plugin::dhcp::_infiniband_twin_update_commands(
'node01', 'b8:3f:d2:4a:68:aa', 1, 'ib0', '192.0.2.10',
'ddns-hostname \"node01\"; send host-name \"node01\";', 1, 1
);
is_deeply(
\@explicit_identity_update_commands,
[ $delete_name_commands, undef, undef ],
'updating an explicit InfiniBand identity does not remove it by address',
);
# The mgtifname of a network can name more than one interface, separated by !.
# The InfiniBand interface is not always the last one, so a test that only
# matches the last name leaves a node on ib0!eth0 without its second entry.
foreach my $ifname (qw(ib0 ib0.8001 eth0!ib0 ib0!eth0 ib0!ib1 bond0!ib2)) {
ok( xCAT_plugin::dhcp::_is_infiniband_interface($ifname),
"'$ifname' is served by an InfiniBand interface" );
}
foreach my $ifname (qw(eth0 eth0!eth1 bond0 hf0)) {
ok( !xCAT_plugin::dhcp::_is_infiniband_interface($ifname),
"'$ifname' is not served by an InfiniBand interface" );
}
ok( !xCAT_plugin::dhcp::_is_infiniband_interface(undef),
'a network with no interface name is not served by InfiniBand' );
done_testing();
+184
View File
@@ -0,0 +1,184 @@
#!/usr/bin/env perl
use strict;
use warnings;
use FindBin;
use lib "$FindBin::Bin/../../xCAT-server/lib";
use lib "$FindBin::Bin/../../xCAT-server/lib/perl";
use lib "$FindBin::Bin/../../perl-xCAT";
use Test::More;
$ENV{XCATCFG} ||= 'SQLite:/tmp';
my $source_dhcp_plugin = "$FindBin::Bin/../../xCAT-server/lib/xcat/plugins/dhcp.pm";
if ( -f $source_dhcp_plugin ) {
require $source_dhcp_plugin;
} else {
require xCAT_plugin::dhcp;
}
my @config;
my @infiniband_config;
xCAT_plugin::dhcp::_add_isc_static_host(
'node00', 'node00', 'b8:3f:d2:03:00:4a:68:aa', 32,
'ib0', '192.0.2.10', '', 1, \@infiniband_config,
);
like(
join( '', @infiniband_config ),
qr/^\s*hardware infiniband b8:3f:d2:03:00:4a:68:aa;$/m,
'the static-host fallback keeps an explicit InfiniBand hardware type',
);
unlike(
join( '', @infiniband_config ),
qr/^\s*hardware ethernet b8:3f:d2:03:00:4a:68:aa;$/m,
'the static-host fallback does not label an InfiniBand identity as Ethernet',
);
my @dashed_mac_config;
xCAT_plugin::dhcp::_add_isc_static_host(
'node00', 'node00', 'B8-3F-D2-4A-68-AA', 1,
'eth0', '192.0.2.11', '', 0, \@dashed_mac_config,
);
like(
join( '', @dashed_mac_config ),
qr/^\s*hardware ethernet b8:3f:d2:4a:68:aa;$/m,
'the static-host fallback emits a valid canonical dashed MAC',
);
xCAT_plugin::dhcp::_add_isc_static_host(
'node01', 'node01', '00:11:22:33:44:55', 1,
'eth0', '192.0.2.1', '', 0, \@config,
);
xCAT_plugin::dhcp::_add_isc_static_host(
'node02', 'node02', '00:11:22:33:44:66', 1,
'eth0', '192.0.2.2', '', 0, \@config,
);
like(
join( '', @config ),
qr/^#xCAT host declaration for node01 aka host node01 end$/m,
'the static host end marker occupies its own line',
);
xCAT_plugin::dhcp::_delete_isc_static_host('node01', \@config);
my $remaining = join( '', @config );
unlike($remaining, qr/\bnode01\b/, 'the selected static host is removed');
like($remaining, qr/\bnode02\b/, 'the following static host is preserved');
my @legacy_config = (
"#xCAT host declaration for node01 aka host node01 start\n",
"host node01 {\n",
" hardware ethernet 00:11:22:33:44:55;\n",
" fixed-address 192.0.2.1;\n",
"} #xCAT host declaration for node01 aka host node01 end\n",
"#xCAT host declaration for node02 aka host node02 start\n",
"host node02 {\n",
" hardware ethernet 00:11:22:33:44:66;\n",
" fixed-address 192.0.2.2;\n",
"} #xCAT host declaration for node02 aka host node02 end\n",
);
xCAT_plugin::dhcp::_delete_isc_static_host('node01', \@legacy_config);
my $legacy_remaining = join( '', @legacy_config );
unlike($legacy_remaining, qr/\bnode01\b/,
'a static host written by an older xCAT release is removed');
like($legacy_remaining, qr/\bnode02\b/,
'deleting an old-format host preserves the following host');
my @multi_host_config;
xCAT_plugin::dhcp::_add_isc_static_host(
'node03', 'node03', '00:11:22:33:44:77', 1,
'eth0', '192.0.2.3', '', 0, \@multi_host_config,
);
xCAT_plugin::dhcp::_add_isc_static_host(
'node03', 'node03-ib', '00:11:22:33:44:88', 32,
'ib0', '192.0.2.3', '', 1, \@multi_host_config,
);
my $multi_host = join( '', @multi_host_config );
like($multi_host, qr/^host node03 \{$/m,
'adding a second identity preserves the first host for a node');
like($multi_host, qr/^host node03-ib \{$/m,
'adding a second identity records its own host for the node');
my @prefix_config;
xCAT_plugin::dhcp::_add_isc_static_host(
'node01', 'node01', '00:11:22:33:44:99', 1,
'eth0', '192.0.2.1', '', 0, \@prefix_config,
);
xCAT_plugin::dhcp::_add_isc_static_host(
'node01-ib', 'node01-ib', '00:11:22:33:44:aa', 1,
'eth0', '192.0.2.4', '', 0, \@prefix_config,
);
xCAT_plugin::dhcp::_delete_isc_static_host('node01', \@prefix_config);
my $prefix_remaining = join( '', @prefix_config );
unlike($prefix_remaining, qr/^host node01 \{$/m,
'node-wide deletion removes the exact node');
like($prefix_remaining, qr/^host node01-ib \{$/m,
'node-wide deletion preserves a node with the same prefix');
my @replacement_config;
xCAT_plugin::dhcp::_add_isc_static_host(
'node04', 'node04-old', '00:11:22:33:44:bb', 1,
'eth0', '192.0.2.5', '', 0, \@replacement_config,
);
xCAT_plugin::dhcp::_add_isc_static_host(
'node04', 'node04-old-ib', '00:11:22:33:44:cc', 32,
'ib0', '192.0.2.5', '', 1, \@replacement_config,
);
xCAT_plugin::dhcp::_add_isc_static_host(
'node05', 'node05', '00:11:22:33:44:dd', 1,
'eth0', '192.0.2.6', '', 0, \@replacement_config,
);
ok(
xCAT_plugin::dhcp::_begin_isc_static_host_update(
'node04', 1, \@replacement_config,
),
'removing stale identities marks the static configuration as changed',
);
xCAT_plugin::dhcp::_add_isc_static_host(
'node04', 'node04-current', '00:11:22:33:44:ee', 1,
'eth0', '192.0.2.5', '', 0, \@replacement_config,
);
my $replacement = join( '', @replacement_config );
unlike($replacement, qr/^host node04-old(?:-ib)? \{$/m,
're-registering a node removes identities that are no longer present');
like($replacement, qr/^host node04-current \{$/m,
're-registering a node writes its current identity');
like($replacement, qr/^host node05 \{$/m,
're-registering a node preserves other nodes');
ok(
!xCAT_plugin::dhcp::_begin_isc_static_host_update(
'node04', 0, \@replacement_config,
),
'a disabled static update leaves the configuration unchanged',
);
my @lazy_config;
xCAT_plugin::dhcp::_add_isc_static_host(
'node06', 'node06-old', '00:11:22:33:44:11', 1,
'eth0', '192.0.2.7', '', 0, \@lazy_config,
);
my $before_failed_update = join( '', @lazy_config );
my $update_started = 0;
xCAT_plugin::dhcp::_add_isc_static_host(
'node06', 'node06-current', '00:11:22:33:44:22', 1,
'eth0', undef, '', 0, \@lazy_config, \$update_started,
);
is(join( '', @lazy_config ), $before_failed_update,
'an unresolved replacement preserves the existing static host');
is($update_started, 0,
'an unresolved replacement does not begin the static update');
xCAT_plugin::dhcp::_add_isc_static_host(
'node06', 'node06-current', '00:11:22:33:44:22', 1,
'eth0', '192.0.2.8', '', 0, \@lazy_config, \$update_started,
);
my $lazy_replacement = join( '', @lazy_config );
unlike($lazy_replacement, qr/^host node06-old \{$/m,
'the first valid replacement removes stale node identities');
like($lazy_replacement, qr/^host node06-current \{$/m,
'the first valid replacement writes the current identity');
is($update_started, 1,
'the successful replacement records that cleanup has started');
done_testing();