Code change:
<ins> { 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 </ins>

Related discussion:
text: Describe the bug NetworkManager is a particularly important component and we don't have any tests for it that could block a channel advance. In particular, a change can happen on nixos-unstable leaving users without functioning networking, and while rollbacks are possible it isn't an excuse to allow that to reach production systems. NixOS doesn't produce either. Manual testing is also cumbersome if we want to make a new stable release. Upstream NetworkManager does have some integration testing but obviously not useful to us. I think to start, if we at least had a test that verifies the services start without issues would be great.
Agreed. While developing tests for the multipath TCP module I was surprised to see no networkmanager module. I had some difficulties with the nixos test infrastructure at first because the test infra will by default install a slirp interface () and it messed up the interface naming. Like I would clear but interfaces would still be named starting from instead of . Just thought I would mention it in case the brave soul writing the test is interested in multihoming. (but that's more a concern I have with the test infra).
Thank you for your contributions. This has been automatically marked as stale because it has had no activity for 180 days. If this is still important to you, we ask that you leave a comment below. Your comment can be as simple as "still important to me". This lets people see that at least one person still cares about this. Someone will have to do this at most twice a year if there is no other activity. Here are suggestions that might help resolve this more quickly: for maintainers and people that previously touched the related code and @ mention them in a comment. on the . 3. Ask on the on .
Bleh.
I marked this as stale due to inactivity. &rarr;
consequences of our inactions