Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions xCAT-server/share/xcat/netboot/imgutils/imgutils.pm
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,53 @@ sub mark_uftp_receiver {
return !$error;
}

# The initial ramdisk configures $prinic, and bringing it up again breaks an NFS-based boot.
# EL10 reads no ifcfg files, so its images get keyfiles.
sub write_nic_profiles {
my ($rootimg_dir, $osver, $prinic, $othernics) = @_;
my @othernics = grep { length } split(/,/, $othernics // '');
my $scripts = "$rootimg_dir/etc/sysconfig/network-scripts";
my $keyfiles = "$rootimg_dir/etc/NetworkManager/system-connections";
my $major = el_major_version($osver);
my %profiles;
if (defined($major) && $major >= 10) {
if (-d $keyfiles) {
# The keyfiles of an earlier build can name interfaces that this build no longer requests.
opendir(my $dh, $keyfiles) or die("Error: cannot read $keyfiles: $!\n");
foreach my $old (grep { /^xcat-genimage-.*\.nmconnection$/ } readdir($dh)) {
unlink("$keyfiles/$old") or die("Error: cannot remove $keyfiles/$old: $!\n");
}
closedir($dh);
$profiles{"$keyfiles/xcat-genimage-$_.nmconnection"} =
"[connection]\nid=$_\ntype=ethernet\ninterface-name=$_\n\n[ipv4]\nmethod=auto\n" for @othernics;
}
} elsif (-d $scripts) {
$profiles{"$scripts/ifcfg-$prinic"} = "ONBOOT=no\nBOOTPROTO=dhcp\nDEVICE=$prinic\n" if $prinic;
$profiles{"$scripts/ifcfg-$_"} = "ONBOOT=yes\nBOOTPROTO=dhcp\nDEVICE=$_\n" for @othernics;
}
foreach my $file (sort keys %profiles) {
# NetworkManager ignores a keyfile that other users can read.
my $mode = $file =~ /\.nmconnection$/ ? oct('600') : oct('644');
my $fh;
open($fh, '>', $file) && chmod($mode, $file) && print($fh $profiles{$file}) && close($fh)
or die("Error: cannot write $file: $!\n");
}
return;
}

# NetworkManager otherwise makes a DHCP profile for every Ethernet port without one.
sub disable_auto_profiles {
my ($rootimg_dir, $osver) = @_;
my $major = el_major_version($osver);
my $confdir = "$rootimg_dir/etc/NetworkManager/conf.d";
return unless defined($major) && $major >= 10 && -d $confdir;
my $file = "$confdir/xcat.conf";
my $fh;
open($fh, '>', $file) && print($fh "[main]\nno-auto-default=*\n") && close($fh)
or die("Error: cannot write $file: $!\n");
return;
}

sub varsubinline{
my $line=shift;
my $refvardict=shift;
Expand Down
15 changes: 2 additions & 13 deletions xCAT-server/share/xcat/netboot/rh/genimage
Original file line number Diff line number Diff line change
Expand Up @@ -2166,19 +2166,8 @@ sub generic_post { #This function is meant to leave the image in a state approxi
print $cfgfile "#Dummy resolv.conf to make boot cleaner";
close($cfgfile);

# Create the ifcfg-x file for diskless node. But keep the ONBOOT=no
# to skip the break of nfs-based boot
if ($prinic) {
open($cfgfile, ">", "$rootimg_dir/etc/sysconfig/network-scripts/ifcfg-$prinic");
print $cfgfile "ONBOOT=no\nBOOTPROTO=dhcp\nDEVICE=$prinic\n";
close($cfgfile);
}
foreach (split /,/, $othernics) {
if (/^$/) { next; }
open($cfgfile, ">", "$rootimg_dir/etc/sysconfig/network-scripts/ifcfg-$_");
print $cfgfile "ONBOOT=yes\nBOOTPROTO=dhcp\nDEVICE=$_\n";
close($cfgfile);
}
imgutils::write_nic_profiles($rootimg_dir, $osver, $prinic, $othernics);
imgutils::disable_auto_profiles($rootimg_dir, $osver);

# securetty not needed on s390x
if ($arch ne "s390x") {
Expand Down
87 changes: 87 additions & 0 deletions xCAT-test/unit/imgutils_nic_profiles.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
use strict;
use warnings;

use FindBin;
use lib "$FindBin::Bin/../lib";
use lib "$FindBin::Bin/../../perl-xCAT", "$FindBin::Bin/../../xCAT-server/lib/perl";
use Fcntl qw(S_IMODE);
use File::Path qw(make_path);
use File::Slurper qw(read_text write_text);
use File::Temp qw(tempdir);
use Test::More;
use XCAT::Test::File qw(repo_path);

$ENV{XCATROOT} = repo_path('xCAT-server');
require(repo_path('xCAT-server/share/xcat/netboot/imgutils/imgutils.pm'));

my @both = ('etc/sysconfig/network-scripts', 'etc/NetworkManager/system-connections');

sub image {
my $root = tempdir(CLEANUP => 1);
make_path(map { "$root/$_" } @_);
return $root;
}

sub mode {
my ($file) = @_;
return -f $file ? sprintf('%04o', S_IMODE((stat $file)[2])) : 'missing';
}

sub profiles {
my ($root) = @_;
return [map { substr($_, length($root) + 1) } sort glob("$root/{$both[0],$both[1]}/*")];
}

for my $case (['rhels10.1', 'etc/NetworkManager/system-connections'], ['alma10.1', @both]) {
my ($os, @dirs) = @$case;
my $root = image(@dirs);
imgutils::write_nic_profiles($root, $os, 'eth0', 'eth1,,eth2');
is_deeply(profiles($root), [map { "$both[1]/xcat-genimage-$_.nmconnection" } qw(eth1 eth2)],
"a $os image with " . join(' and ', @dirs) . ' gets keyfiles for the requested interfaces only');
for my $nic (qw(eth1 eth2)) {
my $file = "$root/$both[1]/xcat-genimage-$nic.nmconnection";
is(-f $file ? read_text($file) : '',
"[connection]\nid=$nic\ntype=ethernet\ninterface-name=$nic\n\n[ipv4]\nmethod=auto\n",
"the $os keyfile for $nic asks for DHCP");
is(mode($file), '0600', "only root can read the $os keyfile for $nic");
}
}

my $rebuilt = image($both[1]);
write_text("$rebuilt/$both[1]/site.nmconnection", "site profile\n");
imgutils::write_nic_profiles($rebuilt, 'rhels10.1', 'eth0', 'eth1,eth2');
imgutils::write_nic_profiles($rebuilt, 'rhels10.1', 'eth0', 'eth2');
is_deeply(profiles($rebuilt), ["$both[1]/site.nmconnection", "$both[1]/xcat-genimage-eth2.nmconnection"],
'a rebuilt EL10 image drops the keyfile of an interface that is no longer requested and keeps other profiles');

my $el9 = image(@both);
imgutils::write_nic_profiles($el9, 'rhels9.6', 'eth0', 'eth1');
is_deeply(profiles($el9), ["$both[0]/ifcfg-eth0", "$both[0]/ifcfg-eth1"], 'an EL9 image gets ifcfg files only');
is(read_text("$el9/$both[0]/ifcfg-eth0"), "ONBOOT=no\nBOOTPROTO=dhcp\nDEVICE=eth0\n",
'the EL9 interface of the initial ramdisk stays off at boot');
is(read_text("$el9/$both[0]/ifcfg-eth1"), "ONBOOT=yes\nBOOTPROTO=dhcp\nDEVICE=eth1\n",
'the EL9 interface eth1 gets DHCP');
is(mode("$el9/$both[0]/ifcfg-eth1"), '0644', 'the EL9 ifcfg file keeps its permissions');

for my $os (qw(rhels10.1 rhels9.6)) {
my $bare = image('etc');
imgutils::write_nic_profiles($bare, $os, 'eth0', 'eth1');
ok(!-e "$bare/etc/sysconfig" && !-e "$bare/etc/NetworkManager", "a $os image without either directory gets no profile");
}

for my $case (['rhels10.1', 1], ['rocky10.2', 1], ['rhels9.6', 0]) {
my ($os, $disabled) = @$case;
my $root = image('etc/NetworkManager/conf.d');
imgutils::disable_auto_profiles($root, $os);
my $conf = "$root/etc/NetworkManager/conf.d/xcat.conf";
if ($disabled) {
is(-f $conf ? read_text($conf) : '', "[main]\nno-auto-default=*\n", "a $os image turns off the automatic DHCP profiles");
} else {
ok(!-e $conf, "a $os image keeps the NetworkManager defaults");
}
}
my $without = image('etc');
imgutils::disable_auto_profiles($without, 'rhels10.1');
ok(!-e "$without/etc/NetworkManager", 'an image without NetworkManager gets no NetworkManager configuration');

done_testing();
Loading