Merge pull request #292472 from Janik-Haag/networkmanager-tests

nixosTests.networking: refactor and add NetworkManager support

Changed files
+224 -41
.github
nixos
doc
manual
release-notes
modules
services
networking
tests
pkgs
tools
networking
networkmanager
+2
.github/CODEOWNERS
···
/nixos/modules/services/networking/babeld.nix @mweinelt
/nixos/modules/services/networking/kea.nix @mweinelt
/nixos/modules/services/networking/knot.nix @mweinelt
+
nixos/modules/services/networking/networkmanager.nix @Janik-Haag
/nixos/modules/services/monitoring/prometheus/exporters/kea.nix @mweinelt
/nixos/tests/babeld.nix @mweinelt
/nixos/tests/kea.nix @mweinelt
/nixos/tests/knot.nix @mweinelt
+
/nixos/tests/networking/* @Janik-Haag
# Web servers
/doc/packages/nginx.section.md @raitobezarius
+2
nixos/doc/manual/release-notes/rl-2405.section.md
···
- The `hardware.pulseaudio` module now sets permission of pulse user home directory to 755 when running in "systemWide" mode. It fixes [issue 114399](https://github.com/NixOS/nixpkgs/issues/114399).
+
- The `services.networkmanager.extraConfig` was renamed to `services.networkmanager.settings` and was changed to use the ini type instead of using a multiline string.
+
- The module `services.github-runner` has been removed. To configure a single GitHub Actions Runner refer to `services.github-runners.*`. Note that this will trigger a new runner registration.
- The `services.slskd` has been refactored to include more configuation options in
+43 -41
nixos/modules/services/networking/networkmanager.nix
···
enableIwd = cfg.wifi.backend == "iwd";
-
mkValue = v:
-
if v == true then "yes"
-
else if v == false then "no"
-
else if lib.isInt v then toString v
-
else v;
-
-
mkSection = name: attrs: ''
-
[${name}]
-
${
-
lib.concatStringsSep "\n"
-
(lib.mapAttrsToList
-
(k: v: "${k}=${mkValue v}")
-
(lib.filterAttrs
-
(k: v: v != null)
-
attrs))
-
}
-
'';
-
-
configFile = pkgs.writeText "NetworkManager.conf" (lib.concatStringsSep "\n" [
-
(mkSection "main" {
+
configAttrs = lib.recursiveUpdate {
+
main = {
plugins = "keyfile";
inherit (cfg) dhcp dns;
# If resolvconf is disabled that means that resolv.conf is managed by some other module.
rc-manager =
if config.networking.resolvconf.enable then "resolvconf"
else "unmanaged";
-
})
-
(mkSection "keyfile" {
+
};
+
keyfile = {
unmanaged-devices =
-
if cfg.unmanaged == [ ] then null
-
else lib.concatStringsSep ";" cfg.unmanaged;
-
})
-
(mkSection "logging" {
+
if cfg.unmanaged == [ ] then null
+
else lib.concatStringsSep ";" cfg.unmanaged;
+
};
+
logging = {
audit = config.security.audit.enable;
level = cfg.logLevel;
-
})
-
(mkSection "connection" cfg.connectionConfig)
-
(mkSection "device" {
-
"wifi.scan-rand-mac-address" = cfg.wifi.scanRandMacAddress;
-
"wifi.backend" = cfg.wifi.backend;
-
})
-
cfg.extraConfig
-
]);
+
};
+
connection = cfg.connectionConfig;
+
device = {
+
"wifi.scan-rand-mac-address" = cfg.wifi.scanRandMacAddress;
+
"wifi.backend" = cfg.wifi.backend;
+
};
+
} cfg.settings;
+
configFile = ini.generate "NetworkManager.conf" configAttrs;
/*
[network-manager]
···
{
meta = {
-
maintainers = teams.freedesktop.members;
+
maintainers = teams.freedesktop.members ++ [ lib.maintainers.janik ];
};
###### interface
···
'';
};
-
extraConfig = mkOption {
-
type = types.lines;
-
default = "";
+
settings = mkOption {
+
type = ini.type;
+
default = {};
description = ''
-
Configuration appended to the generated NetworkManager.conf.
+
Configuration added to the generated NetworkManager.conf, note that you can overwrite settings with this.
Refer to
[
https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html
···
imports = [
(mkRenamedOptionModule
[ "networking" "networkmanager" "packages" ]
-
[ "networking" "networkmanager" "plugins" ])
-
(mkRenamedOptionModule [ "networking" "networkmanager" "useDnsmasq" ] [ "networking" "networkmanager" "dns" ])
+
[ "networking" "networkmanager" "plugins" ]
+
)
+
(mkRenamedOptionModule
+
[ "networking" "networkmanager" "useDnsmasq" ]
+
[ "networking" "networkmanager" "dns" ]
+
)
+
(mkRemovedOptionModule [ "networking" "networkmanager" "extraConfig" ] ''
+
This option was removed in favour of `networking.networkmanager.settings`,
+
which accepts structured nix-code equivalent to the ini
+
and allows for overriding settings.
+
Example patch:
+
```patch
+
networking.networkmanager = {
+
- extraConfig = '''
+
- [main]
+
- no-auto-default=*
+
- '''
+
+ extraConfig.main.no-auto-default = "*";
+
};
+
```
+
''
+
)
(mkRemovedOptionModule [ "networking" "networkmanager" "enableFccUnlock" ] ''
This option was removed, because using bundled FCC unlock scripts is risky,
might conflict with vendor-provided unlock scripts, and should
+1
nixos/tests/all-tests.nix
···
netdata = handleTest ./netdata.nix {};
networking.scripted = handleTest ./networking/networkd-and-scripted.nix { networkd = false; };
networking.networkd = handleTest ./networking/networkd-and-scripted.nix { networkd = true; };
+
networking.networkmanager = handleTest ./networking/networkmanager.nix {};
netbox_3_6 = handleTest ./web-apps/netbox.nix { netbox = pkgs.netbox_3_6; };
netbox_3_7 = handleTest ./web-apps/netbox.nix { netbox = pkgs.netbox_3_7; };
netbox-upgrade = handleTest ./web-apps/netbox-upgrade.nix {};
+172
nixos/tests/networking/networkmanager.nix
···
+
{ system ? builtins.currentSystem
+
, config ? {}
+
, pkgs ? import ../.. { inherit system config; }
+
}:
+
+
with import ../../lib/testing-python.nix { inherit system pkgs; };
+
+
let
+
lib = pkgs.lib;
+
# this is intended as a client test since you shouldn't use NetworkManager for a router or server
+
# so using systemd-networkd for the router vm is fine in these tests.
+
router = import ./router.nix { networkd = true; };
+
qemu-common = import ../../lib/qemu-common.nix { inherit (pkgs) lib pkgs; };
+
clientConfig = extraConfig: lib.recursiveUpdate {
+
networking.useDHCP = false;
+
+
# Make sure that only NetworkManager configures the interface
+
networking.interfaces = lib.mkForce {
+
eth1 = {};
+
};
+
networking.networkmanager = {
+
enable = true;
+
# this is needed so NM doesn't generate 'Wired Connection' profiles and instead uses the default one
+
settings.main.no-auto-default = "*";
+
ensureProfiles.profiles.default = {
+
connection = {
+
id = "default";
+
type = "ethernet";
+
interface-name = "eth1";
+
autoconnect = true;
+
};
+
};
+
};
+
} extraConfig;
+
testCases = {
+
static = {
+
name = "static";
+
nodes = {
+
inherit router;
+
client = clientConfig {
+
networking.networkmanager.ensureProfiles.profiles.default = {
+
ipv4.method = "manual";
+
ipv4.addresses = "192.168.1.42/24";
+
ipv4.gateway = "192.168.1.1";
+
ipv6.method = "manual";
+
ipv6.addresses = "fd00:1234:5678:1::42/64";
+
ipv6.gateway = "fd00:1234:5678:1::1";
+
};
+
};
+
};
+
testScript = ''
+
start_all()
+
router.systemctl("start network-online.target")
+
router.wait_for_unit("network-online.target")
+
client.wait_for_unit("NetworkManager.service")
+
+
with subtest("Wait until we have an ip address on each interface"):
+
client.wait_until_succeeds("ip addr show dev eth1 | grep -q '192.168.1'")
+
client.wait_until_succeeds("ip addr show dev eth1 | grep -q 'fd00:1234:5678:1:'")
+
+
with subtest("Test if icmp echo works"):
+
client.wait_until_succeeds("ping -c 1 192.168.3.1")
+
client.wait_until_succeeds("ping -c 1 fd00:1234:5678:3::1")
+
router.wait_until_succeeds("ping -c 1 192.168.1.42")
+
router.wait_until_succeeds("ping -c 1 fd00:1234:5678:1::42")
+
'';
+
};
+
auto = {
+
name = "auto";
+
nodes = {
+
inherit router;
+
client = clientConfig {
+
networking.networkmanager.ensureProfiles.profiles.default = {
+
ipv4.method = "auto";
+
ipv6.method = "auto";
+
};
+
};
+
};
+
testScript = ''
+
start_all()
+
router.systemctl("start network-online.target")
+
router.wait_for_unit("network-online.target")
+
client.wait_for_unit("NetworkManager.service")
+
+
with subtest("Wait until we have an ip address on each interface"):
+
client.wait_until_succeeds("ip addr show dev eth1 | grep -q '192.168.1'")
+
client.wait_until_succeeds("ip addr show dev eth1 | grep -q 'fd00:1234:5678:1:'")
+
+
with subtest("Test if icmp echo works"):
+
client.wait_until_succeeds("ping -c 1 192.168.1.1")
+
client.wait_until_succeeds("ping -c 1 fd00:1234:5678:1::1")
+
router.wait_until_succeeds("ping -c 1 192.168.1.2")
+
router.wait_until_succeeds("ping -c 1 fd00:1234:5678:1::2")
+
'';
+
};
+
dns = {
+
name = "dns";
+
nodes = {
+
inherit router;
+
dynamic = clientConfig {
+
networking.networkmanager.ensureProfiles.profiles.default = {
+
ipv4.method = "auto";
+
};
+
};
+
static = clientConfig {
+
networking.networkmanager.ensureProfiles.profiles.default = {
+
ipv4 = {
+
method = "auto";
+
ignore-auto-dns = "true";
+
dns = "10.10.10.10";
+
dns-search = "";
+
};
+
};
+
};
+
};
+
testScript = ''
+
start_all()
+
router.systemctl("start network-online.target")
+
router.wait_for_unit("network-online.target")
+
dynamic.wait_for_unit("NetworkManager.service")
+
static.wait_for_unit("NetworkManager.service")
+
+
dynamic.wait_until_succeeds("cat /etc/resolv.conf | grep -q '192.168.1.1'")
+
static.wait_until_succeeds("cat /etc/resolv.conf | grep -q '10.10.10.10'")
+
static.wait_until_fails("cat /etc/resolv.conf | grep -q '192.168.1.1'")
+
'';
+
};
+
dispatcherScripts = {
+
name = "dispatcherScripts";
+
nodes.client = clientConfig {
+
networking.networkmanager.dispatcherScripts = [{
+
type = "pre-up";
+
source = pkgs.writeText "testHook" ''
+
touch /tmp/dispatcher-scripts-are-working
+
'';
+
}];
+
};
+
testScript = ''
+
start_all()
+
client.wait_for_unit("NetworkManager.service")
+
client.wait_until_succeeds("stat /tmp/dispatcher-scripts-are-working")
+
'';
+
};
+
envsubst = {
+
name = "envsubst";
+
nodes.client = let
+
# you should never write secrets in to your nixos configuration, please use tools like sops-nix or agenix
+
secretFile = pkgs.writeText "my-secret.env" ''
+
MY_SECRET_IP=fd00:1234:5678:1::23/64
+
'';
+
in clientConfig {
+
networking.networkmanager.ensureProfiles.environmentFiles = [ secretFile ];
+
networking.networkmanager.ensureProfiles.profiles.default = {
+
ipv6.method = "manual";
+
ipv6.addresses = "$MY_SECRET_IP";
+
};
+
};
+
testScript = ''
+
start_all()
+
client.wait_for_unit("NetworkManager.service")
+
client.wait_until_succeeds("ip addr show dev eth1 | grep -q 'fd00:1234:5678:1:'")
+
client.wait_until_succeeds("ping -c 1 fd00:1234:5678:1::23")
+
'';
+
};
+
};
+
in lib.mapAttrs (lib.const (attrs: makeTest (attrs // {
+
name = "${attrs.name}-Networking-NetworkManager";
+
meta = {
+
maintainers = with lib.maintainers; [ janik ];
+
};
+
+
}))) testCases
+4
pkgs/tools/networking/networkmanager/default.nix
···
, mobile-broadband-provider-info
, runtimeShell
, buildPackages
+
, nixosTests
}:
let
···
packageName = "NetworkManager";
attrPath = "networkmanager";
versionPolicy = "odd-unstable";
+
};
+
tests = {
+
inherit (nixosTests.networking) networkmanager;
};
};