diff --git a/net/net-detect.pl b/net/net-detect.pl new file mode 100644 index 000000000..c87ded6d6 --- /dev/null +++ b/net/net-detect.pl @@ -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; diff --git a/net/net-lib.pl b/net/net-lib.pl index 9023a2d3d..318dcfd4d 100755 --- a/net/net-lib.pl +++ b/net/net-lib.pl @@ -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'}; diff --git a/net/t/run-tests.t b/net/t/run-tests.t index 8012538ac..21f936e3e 100644 --- a/net/t/run-tests.t +++ b/net/t/run-tests.t @@ -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();