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

test(configeth): nothing catches the NIC being left down on netplan

On a netplan-rendered node the reconfigure path takes the interface down
unconditionally, while the `netplan apply` that brings it back is gated on
reboot_nic_bool. In the diskful provision postscripts stage reboot_nic_bool is
0, so configeth downs the NIC and nothing brings it back until the node
reboots -- over the very interface the postscripts are talking on.

The other two arms pair their halves: the ifupdown arm answers `ifdown` with an
unconditional `ifup`, and the redhat arm gates both the down and the up. Only
the netplan arm takes one half.

Nothing in the suite covers it. This drives the two blocks configeth actually
executes -- the down-selection inside the modify branch and the restart block
underneath it -- with `ip`, `ifdown`, `ifup` and `netplan` shadowed by shell
functions, which bash resolves ahead of $PATH, so every call is recorded rather
than run and the host's network is never touched.

Six of the eight assertions pass already and are there to pin the arms that are
correct: redhat gating both halves, ifupdown pairing its own, and the
reboot_nic_bool=1 path doing the down before the apply. The two that fail are
the bug.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
This commit is contained in:
Daniel Hilst
2026-09-01 18:45:36 -03:00
parent 9585205ea8
commit 2a5463659b
@@ -0,0 +1,123 @@
#!/usr/bin/env perl
use strict;
use warnings;
use File::Spec;
use File::Temp qw(tempdir);
use FindBin;
use Test::More;
# Regression: on a netplan-rendered node configeth took the NIC down on every reconfigure,
# while the `netplan apply` that brings it back is gated on reboot_nic_bool. In the diskful
# provision postscripts stage reboot_nic_bool is 0, so the interface went down and nothing
# brought it back until the node rebooted -- over the very NIC the postscripts are talking on.
#
# The other two arms already pair the two halves: the ifupdown arm answers `ifdown` with an
# unconditional `ifup`, and the redhat arm gates BOTH the down and the up on reboot_nic_bool.
# Only the netplan arm took one half of the pair.
#
# This drives the two blocks configeth actually executes rather than matching its text: the
# down-selection inside the modify branch, and the restart block underneath it. `ip` and
# `netplan` are shadowed by shell functions, which bash resolves ahead of $PATH, so the host's
# network is never touched -- every call is recorded to a file instead.
my $repo_root = File::Spec->rel2abs(
File::Spec->catdir( $FindBin::Bin, '..', '..' )
);
my $configeth = File::Spec->catfile( $repo_root, 'xCAT', 'postscripts', 'configeth' );
plan skip_all => "configeth not found" unless -f $configeth;
my $src = do { local $/; open my $fh, '<', $configeth or die $!; <$fh> };
# BAIL_OUT rather than skip: a rename that stops these matching must fail loudly instead of
# silently covering nothing.
my ($down_block) = $src =~ /\n( if \[ "\$str_nic_status" = "up" \];then\n.*?\n fi\n)/ms;
BAIL_OUT('could not extract the nic-down block from configeth')
unless defined $down_block;
my ($restart_block) = $src =~ /\n( #restart the nic\n if \[ \$bool_restart_flag -eq 1 \];then\n.*?\n fi\n)/ms;
BAIL_OUT('could not extract the restart block from configeth')
unless defined $restart_block;
my $dir = tempdir( CLEANUP => 1 );
my $run_no = 0;
# Run both blocks back to back for one (reboot_nic_bool, arm) combination and return what the
# script asked the system to do, in order.
sub drive {
my (%opt) = @_;
$run_no++;
my $root = File::Spec->catdir( $dir, "run$run_no" );
mkdir $root;
my $calls = File::Spec->catfile( $root, 'calls' );
my $harness = File::Spec->catfile( $root, 'harness.sh' );
open my $fh, '>', $harness or die $!;
print $fh <<"PRE";
#!/bin/bash
NODE=cn1
str_nic_name=eth0
str_nic_status=up
str_os_type='$opt{os_type}'
netplan_active='$opt{netplan_active}'
networkmanager_active=0
reboot_nic_bool=$opt{reboot_nic_bool}
bool_modify_flag=1
bool_restart_flag=1
error_code=0
array_ip_old_temp=()
log_info(){ :; }
log_warn(){ :; }
log_error(){ echo "log_error \$*" >> '$calls'; }
ip(){ echo "ip \$*" >> '$calls'; }
ifdown(){ echo "ifdown \$*" >> '$calls'; }
ifup(){ echo "ifup \$*" >> '$calls'; }
nmcli(){ echo "nmcli \$*" >> '$calls'; }
netplan(){ echo "netplan \$*" >> '$calls'; }
networkctl(){ echo "networkctl \$*" >> '$calls'; }
netplan_apply(){ echo "netplan_apply \$*" >> '$calls'; return 0; }
wait_for_ifstate(){ echo 0; return 0; }
PRE
print $fh $down_block, "\n", $restart_block, "\n";
close $fh;
system( '/bin/bash', $harness );
return '' unless -f $calls;
my $out = do { local $/; open my $c, '<', $calls or die $!; <$c> };
return $out;
}
# The bug, stated as behaviour: during the install postscripts stage the NIC must not be left
# down. Either the link is not touched, or something brings it back.
my $install = drive( os_type => 'debian', netplan_active => 1, reboot_nic_bool => 0 );
my $took_down = $install =~ /^ip link set dev eth0 down/m;
my $brought_up = $install =~ /^netplan_apply/m;
ok( !$took_down || $brought_up,
'netplan: with reboot_nic_bool=0 the NIC is not left down' );
unlike( $install, qr/^ip link set dev eth0 down/m,
'netplan: with reboot_nic_bool=0 the link is not taken down at all' );
# The redhat arm is the reference: it has always gated both halves.
my $rh_install = drive( os_type => 'rhel', netplan_active => 0, reboot_nic_bool => 0 );
unlike( $rh_install, qr/^ip link set dev eth0 down/m,
'redhat: with reboot_nic_bool=0 the link is not taken down (reference behaviour)' );
# ifupdown pairs its own halves unconditionally, so it may take the link down.
my $deb_install = drive( os_type => 'debian', netplan_active => 0, reboot_nic_bool => 0 );
like( $deb_install, qr/^ifdown --force eth0/m,
'ifupdown: takes the link down' );
like( $deb_install, qr/^ifup -a -i /m,
'ifupdown: and brings it back unconditionally, so it is never left down' );
# reboot_nic_bool=1 is the normal reconfigure: both halves must run.
my $reboot = drive( os_type => 'debian', netplan_active => 1, reboot_nic_bool => 1 );
like( $reboot, qr/^ip link set dev eth0 down/m,
'netplan: with reboot_nic_bool=1 the link is taken down' );
like( $reboot, qr/^netplan_apply eth0/m,
'netplan: with reboot_nic_bool=1 the apply brings it back' );
my ($down_at) = $reboot =~ /\A(.*?)^netplan_apply/ms;
ok( defined $down_at && $down_at =~ /ip link set dev eth0 down/,
'netplan: the down happens before the apply, not after it' );
done_testing();