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

fix(makehosts): index host lookups

(cherry picked from commit 009150823f)
This commit is contained in:
Vinícius Ferrão
2026-07-15 00:29:26 -03:00
parent 65b8d37532
commit 944219aa92
2 changed files with 310 additions and 43 deletions
+146 -43
View File
@@ -13,6 +13,8 @@ use Getopt::Long;
use Fcntl ':flock';
my @hosts; #Hold /etc/hosts data to be written back
my %host_indexes_by_ip;
my %host_indexes_by_node;
my $LONGNAME;
my $OTHERNAMESFIRST;
my $ADDNAMES;
@@ -27,6 +29,119 @@ sub handled_commands
return { makehosts => "hosts", };
}
sub _host_line_index_values
{
my $line = shift;
return unless defined($line);
my ($ip) = $line =~ /^(\S+)\s/;
return unless defined($ip);
my @nodes;
if ($ip =~ /^\d+\.\d+\.\d+\.\d+$/)
{
my ($primary_name) = $line =~ /^\S+\s+(\S+)/;
if (defined($primary_name))
{
# Preserve matching a short node against an FQDN-first entry.
my $offset = 0;
while ((my $dot = index($primary_name, '.', $offset)) >= 0)
{
push @nodes, substr($primary_name, 0, $dot);
$offset = $dot + 1;
}
push @nodes, $primary_name;
}
}
return ($ip, \@nodes);
}
sub _index_host_line
{
my $idx = shift;
my ($ip, $nodes) = _host_line_index_values($hosts[$idx]);
return unless defined($ip);
$host_indexes_by_ip{$ip}{$idx} = 1;
foreach my $node (@{$nodes})
{
$host_indexes_by_node{$node}{$idx} = 1;
}
}
sub _unindex_host_line
{
my $idx = shift;
my ($ip, $nodes) = _host_line_index_values($hosts[$idx]);
return unless defined($ip);
if (exists($host_indexes_by_ip{$ip}))
{
delete $host_indexes_by_ip{$ip}{$idx};
delete $host_indexes_by_ip{$ip} unless keys %{ $host_indexes_by_ip{$ip} };
}
foreach my $node (@{$nodes})
{
if (exists($host_indexes_by_node{$node}))
{
delete $host_indexes_by_node{$node}{$idx};
delete $host_indexes_by_node{$node} unless keys %{ $host_indexes_by_node{$node} };
}
}
}
sub _rebuild_host_indexes
{
%host_indexes_by_ip = ();
%host_indexes_by_node = ();
foreach my $idx (0 .. $#hosts)
{
_index_host_line($idx);
}
}
sub _set_host_lines
{
my $lines = shift;
@hosts = @{$lines};
_rebuild_host_indexes();
return \@hosts;
}
sub _set_host_line
{
my ($idx, $line) = @_;
_unindex_host_line($idx);
$hosts[$idx] = $line;
_index_host_line($idx);
}
sub _push_host_line
{
my $line = shift;
push @hosts, $line;
_index_host_line($#hosts);
}
sub _matching_host_indexes
{
my ($node, $ip) = @_;
my %matches;
if (exists($host_indexes_by_ip{$ip}))
{
$matches{$_} = 1 foreach keys %{ $host_indexes_by_ip{$ip} };
}
if (exists($host_indexes_by_node{$node}))
{
$matches{$_} = 1 foreach keys %{ $host_indexes_by_node{$node} };
}
my @indexes = sort { $a <=> $b } keys %matches;
return @indexes;
}
sub delnode
{
my $node = shift;
@@ -39,16 +154,10 @@ sub delnode
my $othernames = shift;
my $domain = shift;
my $idx = 0;
while ($idx <= $#hosts)
foreach my $idx (_matching_host_indexes($node, $ip))
{
if (($ip and $hosts[$idx] =~ /^${ip}\s/)
or $hosts[$idx] =~ /^\d+\.\d+\.\d+\.\d+\s+${node}[\s\.\r]/)
{
$hosts[$idx] = "";
}
$idx++;
_set_host_line($idx, "");
}
}
@@ -66,47 +175,40 @@ sub addnode
my $othernames = shift;
my $domain = shift;
my $nics = shift;
my $idx = 0;
my $foundone = 0;
# if this ip was already added then just update the entry
while ($idx <= $#hosts)
my @matches = _matching_host_indexes($node, $ip);
if (@matches)
{
if ($hosts[$idx] =~ /^${ip}\s/
or $hosts[$idx] =~ /^\d+\.\d+\.\d+\.\d+\s+${node}[\s\.\r]/)
my $idx = shift @matches;
my $line;
if ($nics)
{
if ($foundone)
{
$hosts[$idx] = "";
}
else
{
# we found a matching entry in the hosts list
if ($nics) {
# we're processing the nics table and we found an
# existing entry for this ip so just add this
# node name as an alias for the existing entry
my $existing_line = $hosts[$idx];
chomp($existing_line);
my ($hip, $hnode, $hdom, $hother) = split(/ /, $existing_line);
# we're processing the nics table and we found an
# existing entry for this ip so just add this
# node name as an alias for the existing entry
chomp($hosts[$idx]);
my ($hip, $hnode, $hdom, $hother) = split(/ /, $hosts[$idx]);
$hosts[$idx] = build_line($callback, $ip, $hnode, $domain, $othernames);
} else {
# otherwise just try to completely update the existing
# entry
$hosts[$idx] = build_line($callback, $ip, $node, $domain, $othernames);
}
}
$foundone = 1;
$line = build_line($callback, $ip, $hnode, $domain, $othernames);
}
$idx++;
else
{
# otherwise just try to completely update the existing entry
$line = build_line($callback, $ip, $node, $domain, $othernames);
}
_set_host_line($idx, $line);
foreach my $duplicate_idx (@matches)
{
_set_host_line($duplicate_idx, "");
}
return;
}
if ($foundone) { return; }
my $line = build_line($callback, $ip, $node, $domain, $othernames);
if ($line) {
push @hosts, $line;
_push_host_line($line);
}
}
@@ -423,17 +525,17 @@ sub process_request
my $bakname = "/etc/hosts.xcatbak";
copy("/etc/hosts", $bakname);
@hosts = ();
my @host_lines;
if ($REMOVE)
{
# add the localhost entry if trying to create the /etc/hosts from scratch
if ($^O =~ /^aix/i)
{
push @hosts, "127.0.0.1 loopback localhost\n";
push @host_lines, "127.0.0.1 loopback localhost\n";
}
else
{
push @hosts, "127.0.0.1 localhost\n";
push @host_lines, "127.0.0.1 localhost\n";
}
}
else
@@ -447,11 +549,12 @@ sub process_request
{
while (<$rconf>)
{
push @hosts, $_;
push @host_lines, $_;
}
close($rconf);
}
}
_set_host_lines(\@host_lines);
if ($req->{node})
{
+164
View File
@@ -0,0 +1,164 @@
#!/usr/bin/env perl
use strict;
use warnings;
use FindBin;
use Test::More;
BEGIN {
package xCAT::Table;
$INC{'xCAT/Table.pm'} = __FILE__;
package xCAT::TableUtils;
$INC{'xCAT/TableUtils.pm'} = __FILE__;
package xCAT::Utils;
$INC{'xCAT/Utils.pm'} = __FILE__;
package xCAT::NetworkUtils;
$INC{'xCAT/NetworkUtils.pm'} = __FILE__;
package xCAT::MsgUtils;
$INC{'xCAT/MsgUtils.pm'} = __FILE__;
}
my $source_hosts_plugin =
"$FindBin::Bin/../../xCAT-server/lib/xcat/plugins/hosts.pm";
require $source_hosts_plugin;
sub set_host_lines
{
return xCAT_plugin::hosts::_set_host_lines([@_]);
}
{
my $lines = set_host_lines(
"127.0.0.1 localhost\n",
"# retained comment\n",
);
xCAT_plugin::hosts::addnode(
undef, 'node01', '10.0.0.1', 'alias1 alias2', 'cluster.test'
);
is_deeply(
$lines,
[
"127.0.0.1 localhost\n",
"# retained comment\n",
"10.0.0.1 node01 node01.cluster.test alias1 alias2\n",
],
'new host is appended without changing unrelated lines'
);
}
{
my $lines = set_host_lines(
"10.0.0.1 node01 node01.cluster.test oldalias\n",
"10.0.0.2 other other.cluster.test\n",
"10.0.0.3 node01 node01.other.test\n",
);
xCAT_plugin::hosts::addnode(
undef, 'node01', '10.0.0.2', 'newalias', 'cluster.test'
);
is_deeply(
$lines,
[
"10.0.0.2 node01 node01.cluster.test newalias\n",
'',
'',
],
'first IP or primary-name match is updated and later duplicates are removed'
);
}
{
my $lines = set_host_lines(
"10.0.0.1 node01.cluster.test node01\n",
);
xCAT_plugin::hosts::addnode(
undef, 'node01', '10.0.0.9', '', 'cluster.test'
);
is(
$lines->[0],
"10.0.0.9 node01 node01.cluster.test \n",
'short node matches an existing FQDN-first entry'
);
}
{
my $lines = set_host_lines(
"10.0.0.1 node01 node01.cluster.test\n",
);
xCAT_plugin::hosts::addnode(
undef, 'node01-bmc', '10.0.0.1', 'bmc-alias', 'cluster.test', 1
);
is(
$lines->[0],
"10.0.0.1 node01 node01.cluster.test bmc-alias\n",
'NIC entry keeps the existing primary node when matching by IP'
);
xCAT_plugin::hosts::addnode(
undef, 'node01', '10.0.0.9', '', 'cluster.test'
);
is(
$lines->[0],
"10.0.0.9 node01 node01.cluster.test \n",
'updated line remains indexed for the next host change'
);
}
{
my $lines = set_host_lines(
"10.0.0.1 node01 node01.cluster.test\n",
"10.0.0.2 node02 node02.cluster.test\n",
"10.0.0.3 node01 node01.other.test\n",
);
xCAT_plugin::hosts::delnode('node01', '10.0.0.2', '', 'cluster.test');
is_deeply(
$lines,
['', '', ''],
'delete removes every IP or primary-name match'
);
xCAT_plugin::hosts::addnode(
undef, 'node01', '10.0.0.2', '', 'cluster.test'
);
is_deeply(
$lines,
['', '', '', "10.0.0.2 node01 node01.cluster.test \n"],
'deleted lines are removed from the indexes before a later append'
);
}
{
my $lines = set_host_lines(
"fe80::1 node01 node01.cluster.test\n",
"10.0.0.1 \n",
);
xCAT_plugin::hosts::addnode(
undef, 'node02', 'fe80::1', '', 'cluster.test'
);
xCAT_plugin::hosts::addnode(
undef, 'node03', '10.0.0.1', '', 'cluster.test'
);
is_deeply(
$lines,
[
"fe80::1 node02 node02.cluster.test \n",
"10.0.0.1 node03 node03.cluster.test \n",
],
'IP index handles IPv6 addresses and entries without a primary name'
);
}
done_testing();