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

Merge pull request #7793 from VersatusHPC/refactor/nm-autoconnect

refactor(netboot): share NetworkManager autoconnect setup
This commit is contained in:
Daniel Hilst
2026-08-31 11:26:25 -03:00
committed by GitHub
4 changed files with 309 additions and 29 deletions
@@ -6,15 +6,7 @@
[ "$MASTER_IP" ] || export MASTER_IP="#ENV:MASTER_IP#"
#INCLUDE:#ENV:XCATROOT#/share/xcat/install/scripts/scriptlib#
if command -v nmcli >/dev/null 2>&1; then
nmcli -g NAME,STATE con show | \
awk -F: '$1 != "lo" && $2 == "activated" {print $1}' | \
while IFS= read -r con_name
do
[ -n "$con_name" ] || continue
nmcli con mod "$con_name" connection.autoconnect yes
done
fi
xcat_enable_active_nm_autoconnect
internet_repo_file_list="oracle-linux-ol10.repo uek-ol10.repo Rocky-AppStream.repo Rocky-BaseOS.repo Rocky-Extras.repo rocky.repo rocky-extras.repo CentOS-Base.repo centos.repo centos-addons.repo almalinux-ha.repo almalinux-nfv.repo almalinux-powertools.repo almalinux.repo almalinux-resilientstorage.repo almalinux-rt.repo"
@@ -6,22 +6,6 @@
[ "$MASTER_IP" ] || export MASTER_IP="#ENV:MASTER_IP#"
#INCLUDE:#ENV:XCATROOT#/share/xcat/install/scripts/scriptlib#
activate_nm_connections()
{
nmcli -g NAME,STATE con show | \
awk -F: '$1 != "lo" && $2 == "activated" {print $1}' | \
while IFS= read -r con_name
do
[ -n "$con_name" ] || continue
case "$XCATDEBUGMODE" in
"1"|"2")
msgutil_r "$MASTER_IP" "info" "set connection $con_name to be activated on system boot" "/var/log/xcat/xcat.log"
;;
esac
nmcli con mod "$con_name" connection.autoconnect yes
done
}
activated_ifcfg=0
if compgen -G "/etc/sysconfig/network-scripts/ifcfg-*" >/dev/null 2>&1; then
for i in /etc/sysconfig/network-scripts/ifcfg-*
@@ -40,11 +24,11 @@ if compgen -G "/etc/sysconfig/network-scripts/ifcfg-*" >/dev/null 2>&1; then
fi
done
if [ "$activated_ifcfg" -eq 0 ] && command -v nmcli >/dev/null 2>&1; then
activate_nm_connections
if [ "$activated_ifcfg" -eq 0 ]; then
xcat_enable_active_nm_autoconnect 1
fi
elif command -v nmcli >/dev/null 2>&1; then
activate_nm_connections
else
xcat_enable_active_nm_autoconnect 1
fi
# List of internal repos to be disabled
@@ -43,3 +43,23 @@ declare -F msgutil &>/dev/null || function msgutil {
msgutil_r "" "$@"
}
declare -F xcat_enable_active_nm_autoconnect &>/dev/null || function xcat_enable_active_nm_autoconnect {
local log_changes="${1:-}"
command -v nmcli >/dev/null 2>&1 || return 0
nmcli -g NAME,STATE con show | \
awk -F: '$1 != "lo" && $2 == "activated" {print $1}' | \
while IFS= read -r con_name
do
[ -n "$con_name" ] || continue
if [ -n "$log_changes" ]; then
case "$XCATDEBUGMODE" in
"1"|"2")
msgutil_r "$MASTER_IP" "info" "set connection $con_name to be activated on system boot" "/var/log/xcat/xcat.log"
;;
esac
fi
nmcli con mod "$con_name" connection.autoconnect yes
done
}
+284
View File
@@ -0,0 +1,284 @@
#!/usr/bin/env perl
use strict;
use warnings;
use File::Path qw(make_path);
use File::Spec;
use File::Temp qw(tempdir);
use FindBin;
use lib "$FindBin::Bin/../lib";
use Test::More;
use XCAT::Test::File qw(repo_path slurp_repo_file);
my $scriptlib = repo_path(
File::Spec->catfile(
'xCAT-server', 'share', 'xcat', 'install', 'scripts', 'scriptlib'
)
);
-r $scriptlib or BAIL_OUT("$scriptlib is required");
is( system( 'bash', '-n', $scriptlib ), 0,
'the install script library has valid Bash syntax' );
my $tmpdir = tempdir( CLEANUP => 1 );
my $test_bin = File::Spec->catdir( $tmpdir, 'bin' );
my $empty_bin = File::Spec->catdir( $tmpdir, 'empty-bin' );
make_path( $test_bin, $empty_bin );
my $nmcli_log = File::Spec->catfile( $tmpdir, 'nmcli.log' );
my $message_log = File::Spec->catfile( $tmpdir, 'messages.log' );
my $fake_nmcli = File::Spec->catfile( $test_bin, 'nmcli' );
write_file( $fake_nmcli, <<'SH' );
#!/bin/sh
{
printf 'nmcli'
for argument in "$@"; do
printf '\t<%s>' "$argument"
done
printf '\n'
} >>"$XCAT_TEST_NMCLI_LOG"
if [ "$#" -eq 4 ] && [ "$1" = '-g' ] && [ "$2" = 'NAME,STATE' ] &&
[ "$3" = 'con' ] && [ "$4" = 'show' ]; then
printf '%s' "$XCAT_TEST_NMCLI_OUTPUT"
fi
SH
chmod 0755, $fake_nmcli or die "Unable to make $fake_nmcli executable: $!";
my $driver = File::Spec->catfile( $tmpdir, 'run-helper' );
write_file( $driver, <<'SH' );
#!/bin/bash
msgutil_r() {
{
printf 'msgutil_r'
for argument in "$@"; do
printf '\t<%s>' "$argument"
done
printf '\n'
} >>"$XCAT_TEST_MESSAGE_LOG"
}
. "$XCAT_TEST_SCRIPTLIB"
xcat_enable_active_nm_autoconnect "$@"
SH
chmod 0755, $driver or die "Unable to make $driver executable: $!";
my $connections = join( "\n",
'primary uplink:activated', 'backup:deactivated',
'lo:activated', 'storage fabric:activated',
':activated', '' ) . "\n";
is( run_program( $driver, connections => $connections ), 0,
'the helper updates active non-loopback connections' );
is(
read_file($nmcli_log),
command_line( '-g', 'NAME,STATE', 'con', 'show' )
. command_line( 'con', 'mod', 'primary uplink',
'connection.autoconnect', 'yes' )
. command_line( 'con', 'mod', 'storage fabric',
'connection.autoconnect', 'yes' ),
'inactive, loopback, and blank connections are skipped without splitting names'
);
is( read_file($message_log), '',
'logging stays disabled when the caller does not request it' );
is(
run_program(
$driver,
arguments => ['1'],
connections => "primary uplink:activated\n",
debug => '1'
),
0,
'the helper accepts the legacy logging mode'
);
is(
read_file($message_log),
message_line(
'192.0.2.10', 'info',
'set connection primary uplink to be activated on system boot',
'/var/log/xcat/xcat.log'
),
'debug mode logs the same connection activation message'
);
is(
run_program(
$driver,
arguments => ['1'],
connections => "primary uplink:activated\n",
debug => '2'
),
0,
'the helper preserves extended debug logging'
);
is(
read_file($message_log),
message_line(
'192.0.2.10', 'info',
'set connection primary uplink to be activated on system boot',
'/var/log/xcat/xcat.log'
),
'extended debug mode logs the same connection activation message'
);
is(
run_program(
$driver,
arguments => ['1'],
connections => "primary uplink:activated\n",
debug => '0'
),
0,
'the legacy logging mode also supports non-debug operation'
);
is( read_file($message_log), '',
'non-debug operation does not emit the optional message' );
is(
run_program(
$driver,
arguments => ['1'],
connections => "primary uplink:activated\n",
debug => '1',
nmcli => 0
),
0,
'the helper is a successful no-op without nmcli'
);
is( read_file($nmcli_log), '', 'nmcli is not invoked when it is unavailable' );
is( read_file($message_log), '',
'missing nmcli does not produce a connection activation message' );
my $rhels8 = stage_rendered_postscript('post.rhels8');
my $rhels10 = stage_rendered_postscript('post.rhels10');
is(
run_program(
$rhels8,
connections => "primary uplink:activated\n",
debug => '1'
),
0,
'the EL8 and EL9 install postscript uses the shared helper'
);
is(
read_file($message_log),
message_line(
'192.0.2.10', 'info',
'set connection primary uplink to be activated on system boot',
'/var/log/xcat/xcat.log'
),
'the EL8 and EL9 caller preserves connection activation logging'
);
is(
read_file($nmcli_log),
command_line( '-g', 'NAME,STATE', 'con', 'show' )
. command_line( 'con', 'mod', 'primary uplink',
'connection.autoconnect', 'yes' ),
'the EL8 and EL9 caller passes the connection name unchanged'
);
is(
run_program(
$rhels10,
connections => "primary uplink:activated\n",
debug => '1'
),
0,
'the EL10 install postscript uses the shared helper'
);
is( read_file($message_log), '',
'the EL10 caller keeps connection activation logging disabled' );
is(
read_file($nmcli_log),
command_line( '-g', 'NAME,STATE', 'con', 'show' )
. command_line( 'con', 'mod', 'primary uplink',
'connection.autoconnect', 'yes' ),
'the EL10 caller passes the connection name unchanged'
);
done_testing();
sub command_line {
return join( '', 'nmcli', map { "\t<$_>" } @_ ) . "\n";
}
sub message_line {
return join( '', 'msgutil_r', map { "\t<$_>" } @_ ) . "\n";
}
sub run_program {
my ( $program, %options ) = @_;
my $arguments = $options{arguments} // [];
my $has_nmcli = exists( $options{nmcli} ) ? $options{nmcli} : 1;
write_file( $nmcli_log, '' );
write_file( $message_log, '' );
local %ENV = %ENV;
$ENV{PATH} = $has_nmcli ? "$test_bin:$ENV{PATH}" : $empty_bin;
$ENV{XCATDEBUGMODE} = $options{debug} // '0';
$ENV{MASTER_IP} = '192.0.2.10';
$ENV{XCAT_TEST_MESSAGE_LOG} = $message_log;
$ENV{XCAT_TEST_NMCLI_LOG} = $nmcli_log;
$ENV{XCAT_TEST_NMCLI_OUTPUT} = $options{connections} // '';
$ENV{XCAT_TEST_SCRIPTLIB} = $scriptlib;
my $status = system( '/bin/bash', $program, @{$arguments} );
return $status == -1 ? 255 : $status >> 8;
}
sub stage_rendered_postscript {
my ($name) = @_;
my $postscript = slurp_repo_file(
File::Spec->catfile(
'xCAT-server', 'share', 'xcat', 'install', 'scripts', $name
)
);
my $library = slurp_repo_file(
File::Spec->catfile(
'xCAT-server', 'share', 'xcat', 'install', 'scripts', 'scriptlib'
)
);
my $include = '#INCLUDE:#ENV:XCATROOT#/share/xcat/install/scripts/scriptlib#';
$postscript =~ s/^\Q$include\E$/$library/m
or BAIL_OUT("Unable to render the scriptlib include in $name");
my $preamble = <<'SH';
compgen() { return 1; }
sed() { :; }
msgutil_r() {
{
printf 'msgutil_r'
for argument in "$@"; do
printf '\t<%s>' "$argument"
done
printf '\n'
} >>"$XCAT_TEST_MESSAGE_LOG"
}
SH
$postscript =~ s/\A(#![^\n]*\n)/$1$preamble/
or BAIL_OUT("Unable to stage the test preamble in $name");
my $destination = File::Spec->catfile( $tmpdir, $name );
write_file( $destination, $postscript );
chmod 0755, $destination
or die "Unable to make $destination executable: $!";
return $destination;
}
sub write_file {
my ( $path, $contents ) = @_;
open( my $fh, '>:raw', $path ) or die "Unable to write $path: $!";
print {$fh} $contents;
close($fh) or die "Unable to close $path: $!";
}
sub read_file {
my ($path) = @_;
open( my $fh, '<:raw', $path ) or die "Unable to read $path: $!";
my $contents = do { local $/; <$fh> };
close($fh) or die "Unable to close $path: $!";
return $contents;
}