From 53c755fb28b6a9d7bca56c5ce6f22791dbc0dedc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Thu, 23 Jul 2026 01:56:46 -0300 Subject: [PATCH 1/2] refactor(network): centralize address accumulation --- perl-xCAT/xCAT/NetworkUtils.pm | 35 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/perl-xCAT/xCAT/NetworkUtils.pm b/perl-xCAT/xCAT/NetworkUtils.pm index 9a0fbef1d..4bb96106d 100644 --- a/perl-xCAT/xCAT/NetworkUtils.pm +++ b/perl-xCAT/xCAT/NetworkUtils.pm @@ -901,6 +901,17 @@ sub classful_networks_for_net_and_mask return @results; } +# Record an address while preserving the caller's undefined map until needed. +sub _record_network_address +{ + my ($mapref, $key, $address, $returnall) = @_; + + if ($returnall) { + push @{ ${$mapref}->{$key} }, $address; + } else { + ${$mapref}->{$key} = $address; + } +} #------------------------------------------------------------------------------- @@ -948,11 +959,7 @@ sub my_hexnets { my $nowhex = sprintf("%08x", $nown); my $hexnet = substr($nowhex, 0, $numchars); - if ($returnall) { - push @{ $rethash->{$hexnet} }, $curnet; - } else { - $rethash->{$hexnet} = $curnet; - } + _record_network_address(\$rethash, $hexnet, $curnet, $returnall); $nown += 1 << (32 - $maskbits - $bitstoeven); } } @@ -1131,19 +1138,11 @@ sub my_nets $nown = $nown & $curmask; my $textnet = inet_ntoa(pack("N", $nown)); $textnet .= "/$maskbits"; - if ($returnall) { - push @{ $rethash->{$textnet} }, $curnet; - } else { - $rethash->{$textnet} = $curnet; - } + _record_network_address(\$rethash, $textnet, $curnet, $returnall); } else { - if ($returnall) { - push @{ $rethash->{$v6net} }, $v6ip; - } else { - $rethash->{$v6net} = $v6ip; - } + _record_network_address(\$rethash, $v6net, $v6ip, $returnall); } } @@ -1171,11 +1170,7 @@ sub my_nets $n .= "/$nm"; #$rethash->{$n} = $if; - if ($returnall) { - push @{ $rethash->{$n} }, $master; - } else { - $rethash->{$n} = $master; - } + _record_network_address(\$rethash, $n, $master, $returnall); } } return $rethash; From dbc820780aa62f4898654390aaae83be9d980451 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Thu, 23 Jul 2026 01:58:57 -0300 Subject: [PATCH 2/2] test(network): characterize address accumulation --- xCAT-test/unit/networkutils_first_address.t | 141 +++++++++++++++++++- 1 file changed, 139 insertions(+), 2 deletions(-) diff --git a/xCAT-test/unit/networkutils_first_address.t b/xCAT-test/unit/networkutils_first_address.t index 33cd1134d..d5c29c385 100644 --- a/xCAT-test/unit/networkutils_first_address.t +++ b/xCAT-test/unit/networkutils_first_address.t @@ -9,7 +9,8 @@ use Test::More; BEGIN { package NetworkUtilsTestCommand; - our $ip_addr_output = ''; + our $ip_addr_output = ''; + our $aix_ifconfig_output = ''; package xCAT::Table; our @network_entries; @@ -19,7 +20,7 @@ BEGIN { package xCAT::TableUtils; sub get_site_attribute { - my ($attribute) = @_; + my $attribute = $_[-1]; return ('192.0.2.1') if $attribute eq 'master'; return; } @@ -30,6 +31,8 @@ BEGIN { my ($command) = @_; return $NetworkUtilsTestCommand::ip_addr_output if $command eq '/sbin/ip addr'; + return $NetworkUtilsTestCommand::aix_ifconfig_output + if $command eq '/usr/sbin/ifconfig -a'; die "Unexpected command in NetworkUtils unit test: $command"; }; } @@ -41,6 +44,17 @@ sub set_ip_addr_output { @xCAT::Table::network_entries = (); } +sub set_aix_ifconfig_output { + $NetworkUtilsTestCommand::aix_ifconfig_output = join('', @_); + @xCAT::Table::network_entries = (); +} + +sub run_as_aix { + my ($code) = @_; + local $^O = 'aix'; + return $code->(); +} + set_ip_addr_output( "2: eth0: mtu 1500\n", " inet 192.168.148.10/20 brd 192.168.159.255 scope global eth0\n", @@ -113,4 +127,127 @@ is_deeply( 'Linux IPv6 addresses remain outside the all-address result', ); +set_ip_addr_output( + "2: eth0: mtu 1500\n", + " inet 192.168.148.10/20 brd 192.168.159.255 scope global eth0\n", + " inet 192.168.148.10/20 brd 192.168.159.255 scope global eth0\n", +); + +is_deeply( + xCAT::NetworkUtils->my_nets('all'), + { + '192.168.144.0/20' => [ + '192.168.148.10', + '192.168.148.10', + ], + }, + 'my_nets all mode retains duplicate addresses', +); +is_deeply( + xCAT::NetworkUtils->my_hexnets('all'), + { + c0a89 => [ + '192.168.148.10', + '192.168.148.10', + ], + }, + 'my_hexnets all mode retains duplicate addresses', +); + +set_ip_addr_output( + "2: eth0: mtu 1500\n", + " inet 192.168.148.10/20 brd 192.168.159.255 scope global eth0\n", +); +@xCAT::Table::network_entries = ( + { + net => '192.168.144.0', + mgtifname => '!remote!', + mask => '255.255.240.0', + }, + { + net => '192.168.144.0', + mgtifname => '!remote!', + mask => '255.255.240.0', + }, + { + net => '198.51.100.0', + mgtifname => '!remote!', + mask => '255.255.255.0', + }, +); + +is_deeply( + xCAT::NetworkUtils->my_nets(), + { + '192.168.144.0/20' => '192.0.2.1', + '198.51.100.0/24' => '192.0.2.1', + }, + 'remote networks keep overwriting earlier addresses in default mode', +); +is_deeply( + xCAT::NetworkUtils->my_nets('all'), + { + '192.168.144.0/20' => [ + '192.168.148.10', + '192.0.2.1', + '192.0.2.1', + ], + '198.51.100.0/24' => ['192.0.2.1'], + }, + 'remote networks append in table order and retain duplicates in all mode', +); + +set_ip_addr_output(); +is( + xCAT::NetworkUtils->my_nets(), + undef, + 'my_nets default mode remains undefined when no addresses exist', +); +is( + xCAT::NetworkUtils->my_nets('all'), + undef, + 'my_nets all mode remains undefined when no addresses exist', +); +is( + xCAT::NetworkUtils->my_hexnets(), + undef, + 'my_hexnets default mode remains undefined when no addresses exist', +); +is( + xCAT::NetworkUtils->my_hexnets('all'), + undef, + 'my_hexnets all mode remains undefined when no addresses exist', +); + +set_aix_ifconfig_output( + "en0: flags=1e080863,480\n", + " inet 192.168.148.10 netmask 0xfffff000 broadcast 192.168.159.255\n", + " inet 192.168.149.100 netmask 0xfffff000 broadcast 192.168.159.255\n", + " inet6 2001:db8:1::10/64\n", + " inet6 2001:db8:1::10/64\n", +); + +is_deeply( + run_as_aix(sub { xCAT::NetworkUtils->my_nets() }), + { + '192.168.144.0/20' => '192.168.149.100', + '2001:db8:1::10/64' => '2001:db8:1::10', + }, + 'AIX default mode keeps scalar last-address behavior for IPv4 and IPv6', +); +is_deeply( + run_as_aix(sub { xCAT::NetworkUtils->my_nets('all') }), + { + '192.168.144.0/20' => [ + '192.168.148.10', + '192.168.149.100', + ], + '2001:db8:1::10/64' => [ + '2001:db8:1::10', + '2001:db8:1::10', + ], + }, + 'AIX all mode preserves IPv4 order and retains duplicate IPv6 addresses', +); + done_testing();