Fix to detect NetworkManager networking on Debian

ⓘ Prefer Netplan when Debian has Netplan YAML config, otherwise select the existing NetworkManager backend for Debian systems with saved NM connection profiles, with regression tests for backend selection.

https://github.com/webmin/webmin/issues/2559
This commit is contained in:
Ilia Ross
2026-06-02 01:33:59 +02:00
parent b33b9fb0a0
commit 6574373761
3 changed files with 106 additions and 10 deletions

32
net/net-detect.pl Normal file
View File

@@ -0,0 +1,32 @@
# net-detect.pl
# Helper functions for choosing the network config backend
sub net_has_network_manager_config
{
my ($dir) = @_;
$dir ||= "/etc/NetworkManager/system-connections";
my @files = glob("$dir/*.nmconnection");
return -d $dir && scalar(@files);
}
sub net_has_netplan_config
{
my ($dir) = @_;
$dir ||= "/etc/netplan";
return &has_command("netplan") &&
-d $dir;
}
sub net_auto_backend
{
my ($os_type, $netplan_dir, $nm_conn_dir) = @_;
return "netplan"
if ($os_type eq "debian-linux" &&
&net_has_netplan_config($netplan_dir));
return "nm"
if (($os_type eq "redhat-linux" || $os_type eq "debian-linux") &&
&net_has_network_manager_config($nm_conn_dir));
return undef;
}
1;

View File

@@ -6,6 +6,9 @@ use WebminCore;
&init_config();
%access = &get_module_acl();
$access{'ipnodes'} = $access{'hosts'};
do "net-detect.pl";
$auto_net_mode = &net_auto_backend($gconfig{'os_type'});
if (-r "$module_root_directory/$gconfig{'os_type'}-$gconfig{'os_version'}-lib.pl") {
do "$gconfig{'os_type'}-$gconfig{'os_version'}-lib.pl";
@@ -23,20 +26,16 @@ elsif ($gconfig{'os_type'} eq 'slackware-linux' &&
do "$gconfig{'os_type'}-9.1-ALL-lib.pl";
$net_mode = $gconfig{'os_type'}."/9.1";
}
elsif ($gconfig{'os_type'} eq 'redhat-linux' &&
-d "/etc/NetworkManager/system-connections" &&
glob("/etc/NetworkManager/system-connections/*.nmconnection")) {
# Special case for systems with network manager
do 'nm-lib.pl';
$net_mode = "nm";
}
elsif ($gconfig{'os_type'} eq 'debian-linux' &&
&has_command("netplan") &&
-d "/etc/netplan") {
elsif ($auto_net_mode eq "netplan") {
# Special case for newer Ubuntu versions
do "netplan-lib.pl";
$net_mode = "netplan";
}
elsif ($auto_net_mode eq "nm") {
# Special case for systems with network manager
do 'nm-lib.pl';
$net_mode = "nm";
}
else {
do "$gconfig{'os_type'}-lib.pl";
$net_mode = $gconfig{'os_type'};

View File

@@ -4,6 +4,7 @@ use warnings;
use Test::More;
use Cwd qw(abs_path);
use File::Basename qw(dirname);
use File::Path qw(make_path);
use File::Temp qw(tempdir);
my $root = abs_path(dirname(__FILE__)."/../..") or die "rootdir: $!";
@@ -58,6 +59,7 @@ close($fh) || die "close $file: $!";
sub lock_file { return 1; }
sub unlock_file { return 1; }
sub error { die join("", @_), "\n"; }
sub unflush_file_lines { delete($file_cache{$_[0]}); }
sub has_command { return $_[0] eq "netplan" ? "/usr/sbin/netplan" : undef; }
sub execute_command_logged
{
@@ -68,6 +70,13 @@ $$stdout = $out if (ref($stdout));
$$stderr = $out if (ref($stderr) && $stderr ne $stdout);
return $command_status{$cmd} || 0;
}
sub backquote_logged
{
my ($cmd) = @_;
push(@commands, $cmd);
$? = $command_status{$cmd} || 0;
return $command_output{$cmd} || "";
}
sub check_ipaddress { return $_[0] =~ /^\d+\.\d+\.\d+\.\d+$/; }
sub check_ip6address { return $_[0] =~ /:/; }
sub check_ipaddress_any { return &check_ipaddress($_[0]) || &check_ip6address($_[0]); }
@@ -83,6 +92,29 @@ return -1;
}
unshift(@INC, "$root/net", $root);
do "$root/net/net-detect.pl" || die "net-detect.pl: $@ $!";
my $detect_root = tempdir(CLEANUP => 1);
my $detect_netplan = "$detect_root/netplan";
my $detect_no_netplan = "$detect_root/no-netplan";
my $detect_nm = "$detect_root/NetworkManager/system-connections";
my $detect_nm_empty = "$detect_root/NetworkManager-empty/system-connections";
make_path($detect_netplan, $detect_nm, $detect_nm_empty);
write_text("$detect_nm/eth0.nmconnection", "");
is(main::net_auto_backend("debian-linux", $detect_netplan, $detect_nm_empty),
"netplan", "Debian uses Netplan when the config directory exists");
is(main::net_auto_backend("debian-linux", $detect_no_netplan, $detect_nm),
"nm", "Debian uses NetworkManager when only nmconnection files exist");
is(main::net_auto_backend("redhat-linux", $detect_no_netplan, $detect_nm),
"nm", "Red Hat still uses NetworkManager when nmconnection files exist");
write_text("$detect_netplan/50-cloud-init.yaml", "");
is(main::net_auto_backend("debian-linux", $detect_netplan, $detect_nm),
"netplan", "Debian prefers Netplan over NetworkManager when YAML exists");
unlink("$detect_netplan/50-cloud-init.yaml");
is(main::net_auto_backend("debian-linux", $detect_no_netplan, $detect_nm_empty),
undef, "Debian falls back when no Netplan or NetworkManager config exists");
do "$root/net/netplan-lib.pl" || die "netplan-lib.pl: $@ $!";
{
@@ -167,4 +199,37 @@ is_deeply(\@commands,
"(cd / && /usr/sbin/netplan apply)" ],
"apply_network validates before applying");
do "$root/net/nm-lib.pl" || die "nm-lib.pl: $@ $!";
my $nmfile = "$tmp/eth0.nmconnection";
write_text($nmfile, <<'NM');
[connection]
id=eth0
uuid=11111111-2222-3333-4444-555555555555
type=ethernet
interface-name=eth0
[ipv4]
method=auto
[ipv6]
method=disabled
NM
my $nmcfg = main::read_nm_config($nmfile);
my $nmiface = {
'name' => 'eth0',
'fullname' => 'eth0',
'file' => $nmfile,
'cfg' => $nmcfg,
'edit' => 1,
'up' => 1,
'dhcp' => 1,
'address6' => [ ],
'netmask6' => [ ],
'nameserver' => [ "2001:4860:4860::8888" ],
};
@commands = ( );
main::save_interface($nmiface, [ $nmiface ]);
like(join("\n", @commands), qr/ipv6\.dns/,
"NetworkManager save_interface writes IPv6 nameservers");
done_testing();