From f0492bd53c3e78afc429f3b2f3792f13d6352cdb Mon Sep 17 00:00:00 2001 From: Arnold Krille Date: Fri, 30 Sep 2016 10:45:48 +0200 Subject: [PATCH 1/2] containers: Do not wait for udev for network devices Test that adding physical devices to containers works, find that network setup then doesn't work because there is no udev in the container to tell systemd that the device is present. Fixed by not depending on the device in the container. Activate the new container test for release Bonds, bridges and other network devices need the underlying not as dependency when used inside the container. Because the device is already there. But the address configuration needs the aggregated device itself. --- .../tasks/network-interfaces-scripted.nix | 33 +++++++--- nixos/modules/tasks/network-interfaces.nix | 13 ++-- nixos/release.nix | 1 + .../tests/containers-physical_interfaces.nix | 66 +++++++++++++++++++ 4 files changed, 101 insertions(+), 12 deletions(-) create mode 100644 nixos/tests/containers-physical_interfaces.nix diff --git a/nixos/modules/tasks/network-interfaces-scripted.nix b/nixos/modules/tasks/network-interfaces-scripted.nix index 6a8f20bab5b..4bbb5a3a166 100644 --- a/nixos/modules/tasks/network-interfaces-scripted.nix +++ b/nixos/modules/tasks/network-interfaces-scripted.nix @@ -46,6 +46,23 @@ in systemd.services = let + deviceDependency = dev: + if (config.boot.isContainer == false) + then + # Trust udev when not in the container + [ (subsystemDevice dev) ] + else + # When in the container, check whether the interface is built from other definitions + if (hasAttr dev cfg.bridges) || + (hasAttr dev cfg.bonds) || + (hasAttr dev cfg.macvlans) || + (hasAttr dev cfg.sits) || + (hasAttr dev cfg.vlans) || + (hasAttr dev cfg.vswitches) || + (hasAttr dev cfg.wlanInterfaces) + then [ "${dev}-netdev.service" ] + else []; + networkLocalCommands = { after = [ "network-setup.service" ]; bindsTo = [ "network-setup.service" ]; @@ -120,8 +137,8 @@ in # order before network-setup because the routes that are configured # there may need ip addresses configured before = [ "network-setup.service" ]; - bindsTo = [ (subsystemDevice i.name) ]; - after = [ (subsystemDevice i.name) "network-pre.target" ]; + bindsTo = deviceDependency i.name; + after = [ "network-pre.target" ] ++ (deviceDependency i.name); serviceConfig.Type = "oneshot"; serviceConfig.RemainAfterExit = true; path = [ pkgs.iproute ]; @@ -179,7 +196,7 @@ in createBridgeDevice = n: v: nameValuePair "${n}-netdev" (let - deps = map subsystemDevice v.interfaces; + deps = if config.boot.isContainer then [] else map subsystemDevice v.interfaces; in { description = "Bridge Interface ${n}"; wantedBy = [ "network-setup.service" (subsystemDevice n) ]; @@ -220,7 +237,7 @@ in createVswitchDevice = n: v: nameValuePair "${n}-netdev" (let - deps = map subsystemDevice v.interfaces; + deps = if config.boot.isContainer then [] else map subsystemDevice v.interfaces; ofRules = pkgs.writeText "vswitch-${n}-openFlowRules" v.openFlowRules; in { description = "Open vSwitch Interface ${n}"; @@ -253,7 +270,7 @@ in createBondDevice = n: v: nameValuePair "${n}-netdev" (let - deps = map subsystemDevice v.interfaces; + deps = if config.boot.isContainer then [] else map subsystemDevice v.interfaces; in { description = "Bond Interface ${n}"; wantedBy = [ "network-setup.service" (subsystemDevice n) ]; @@ -291,7 +308,7 @@ in createMacvlanDevice = n: v: nameValuePair "${n}-netdev" (let - deps = [ (subsystemDevice v.interface) ]; + deps = deviceDependency v.interface; in { description = "Vlan Interface ${n}"; wantedBy = [ "network-setup.service" (subsystemDevice n) ]; @@ -316,7 +333,7 @@ in createSitDevice = n: v: nameValuePair "${n}-netdev" (let - deps = optional (v.dev != null) (subsystemDevice v.dev); + deps = optional (v.dev != null) (deviceDependency v.dev); in { description = "6-to-4 Tunnel Interface ${n}"; wantedBy = [ "network-setup.service" (subsystemDevice n) ]; @@ -344,7 +361,7 @@ in createVlanDevice = n: v: nameValuePair "${n}-netdev" (let - deps = [ (subsystemDevice v.interface) ]; + deps = deviceDependency v.interface; in { description = "Vlan Interface ${n}"; wantedBy = [ "network-setup.service" (subsystemDevice n) ]; diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix index cac7e6b02eb..aae4dc5fdad 100644 --- a/nixos/modules/tasks/network-interfaces.nix +++ b/nixos/modules/tasks/network-interfaces.nix @@ -310,9 +310,9 @@ in generate a random 32-bit ID using the following commands: cksum /etc/machine-id | while read c rest; do printf "%x" $c; done - + (this derives it from the machine-id that systemd generates) or - + head -c4 /dev/urandom | od -A none -t x4 ''; }; @@ -972,12 +972,17 @@ in ''; }; } // (listToAttrs (flip map interfaces (i: + let + deviceDependency = if config.boot.isContainer + then [] + else [ (subsystemDevice i.name) ]; + in nameValuePair "network-link-${i.name}" { description = "Link configuration of ${i.name}"; wantedBy = [ "network-interfaces.target" ]; before = [ "network-interfaces.target" ]; - bindsTo = [ (subsystemDevice i.name) ]; - after = [ (subsystemDevice i.name) "network-pre.target" ]; + bindsTo = deviceDependency; + after = [ "network-pre.target" ] ++ deviceDependency; path = [ pkgs.iproute ]; serviceConfig = { Type = "oneshot"; diff --git a/nixos/release.nix b/nixos/release.nix index bff17da607f..10c624afebc 100644 --- a/nixos/release.nix +++ b/nixos/release.nix @@ -227,6 +227,7 @@ in rec { tests.containers-bridge = callTest tests/containers-bridge.nix {}; tests.containers-imperative = callTest tests/containers-imperative.nix {}; tests.containers-extra_veth = callTest tests/containers-extra_veth.nix {}; + tests.containers-physical_interfaces = callTest tests/containers-physical_interfaces.nix {}; tests.docker = hydraJob (import tests/docker.nix { system = "x86_64-linux"; }); tests.dnscrypt-proxy = callTest tests/dnscrypt-proxy.nix { system = "x86_64-linux"; }; tests.ecryptfs = callTest tests/ecryptfs.nix {}; diff --git a/nixos/tests/containers-physical_interfaces.nix b/nixos/tests/containers-physical_interfaces.nix new file mode 100644 index 00000000000..4a53f8c824f --- /dev/null +++ b/nixos/tests/containers-physical_interfaces.nix @@ -0,0 +1,66 @@ + +import ./make-test.nix ({ pkgs, ...} : { + name = "containers-physical_interfaces"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ kampfschlaefer ]; + }; + + nodes = { + server = { config, pkgs, ... }: + { + virtualisation.memorySize = 256; + virtualisation.vlans = [ 1 ]; + + containers.server = { + privateNetwork = true; + interfaces = [ "eth1" ]; + + config = { + networking.interfaces.eth1 = { + ip4 = [ { address = "10.10.0.1"; prefixLength = 24; } ]; + }; + networking.firewall.enable = false; + }; + }; + }; + client = { config, pkgs, ... }: { + virtualisation.memorySize = 256; + virtualisation.vlans = [ 1 ]; + + containers.client = { + privateNetwork = true; + interfaces = [ "eth1" ]; + + config = { + networking.bridges.br0.interfaces = [ "eth1" ]; + networking.interfaces.br0 = { + ip4 = [ { address = "10.10.0.2"; prefixLength = 24; } ]; + }; + networking.firewall.enable = false; + }; + }; + }; + }; + + testScript = '' + startAll; + + $server->waitForUnit("default.target"); + $server->execute("ip link >&2"); + + $server->succeed("ip link show dev eth1 >&2"); + + $server->succeed("nixos-container start server"); + $server->waitForUnit("container\@server"); + $server->succeed("systemctl -M server list-dependencies network-addresses-eth1.service >&2"); + + $server->succeed("nixos-container run server -- ip a show dev eth1 >&2"); + + $client->waitForUnit("default.target"); + $client->succeed("nixos-container start client"); + $client->waitForUnit("container\@client"); + $client->succeed("systemctl -M client list-dependencies network-addresses-br0.service >&2"); + $client->succeed("systemctl -M client status -n 30 -l network-addresses-br0.service"); + $client->succeed("nixos-container run client -- ping -w 10 -c 1 -n 10.10.0.1"); + ''; +}) From cc91f274e0098a6642e88bf26c2c1a3eefdb806e Mon Sep 17 00:00:00 2001 From: Arnold Krille Date: Fri, 30 Sep 2016 14:52:46 +0200 Subject: [PATCH 2/2] containers: Improve device dependency tracking Now the tracking works with aggregated devices on aggregated devices. So container with physical device where the device is put in a bond which is the basis for a bridge is now handled correctly. --- .../tasks/network-interfaces-scripted.nix | 6 +- .../tests/containers-physical_interfaces.nix | 99 ++++++++++++++++--- 2 files changed, 86 insertions(+), 19 deletions(-) diff --git a/nixos/modules/tasks/network-interfaces-scripted.nix b/nixos/modules/tasks/network-interfaces-scripted.nix index 4bbb5a3a166..89ee13d5cd9 100644 --- a/nixos/modules/tasks/network-interfaces-scripted.nix +++ b/nixos/modules/tasks/network-interfaces-scripted.nix @@ -196,7 +196,7 @@ in createBridgeDevice = n: v: nameValuePair "${n}-netdev" (let - deps = if config.boot.isContainer then [] else map subsystemDevice v.interfaces; + deps = concatLists (map deviceDependency v.interfaces); in { description = "Bridge Interface ${n}"; wantedBy = [ "network-setup.service" (subsystemDevice n) ]; @@ -237,7 +237,7 @@ in createVswitchDevice = n: v: nameValuePair "${n}-netdev" (let - deps = if config.boot.isContainer then [] else map subsystemDevice v.interfaces; + deps = concatLists (map deviceDependency v.interfaces); ofRules = pkgs.writeText "vswitch-${n}-openFlowRules" v.openFlowRules; in { description = "Open vSwitch Interface ${n}"; @@ -270,7 +270,7 @@ in createBondDevice = n: v: nameValuePair "${n}-netdev" (let - deps = if config.boot.isContainer then [] else map subsystemDevice v.interfaces; + deps = concatLists (map deviceDependency v.interfaces); in { description = "Bond Interface ${n}"; wantedBy = [ "network-setup.service" (subsystemDevice n) ]; diff --git a/nixos/tests/containers-physical_interfaces.nix b/nixos/tests/containers-physical_interfaces.nix index 4a53f8c824f..a3b0b29951b 100644 --- a/nixos/tests/containers-physical_interfaces.nix +++ b/nixos/tests/containers-physical_interfaces.nix @@ -23,11 +23,11 @@ import ./make-test.nix ({ pkgs, ...} : { }; }; }; - client = { config, pkgs, ... }: { - virtualisation.memorySize = 256; + bridged = { config, pkgs, ... }: { + virtualisation.memorySize = 128; virtualisation.vlans = [ 1 ]; - containers.client = { + containers.bridged = { privateNetwork = true; interfaces = [ "eth1" ]; @@ -40,27 +40,94 @@ import ./make-test.nix ({ pkgs, ...} : { }; }; }; + + bonded = { config, pkgs, ... }: { + virtualisation.memorySize = 128; + virtualisation.vlans = [ 1 ]; + + containers.bonded = { + privateNetwork = true; + interfaces = [ "eth1" ]; + + config = { + networking.bonds.bond0 = { + interfaces = [ "eth1" ]; + mode = "active-backup"; + }; + networking.interfaces.bond0 = { + ip4 = [ { address = "10.10.0.3"; prefixLength = 24; } ]; + }; + networking.firewall.enable = false; + }; + }; + }; + + bridgedbond = { config, pkgs, ... }: { + virtualisation.memorySize = 128; + virtualisation.vlans = [ 1 ]; + + containers.bridgedbond = { + privateNetwork = true; + interfaces = [ "eth1" ]; + + config = { + networking.bonds.bond0 = { + interfaces = [ "eth1" ]; + mode = "active-backup"; + }; + networking.bridges.br0.interfaces = [ "bond0" ]; + networking.interfaces.br0 = { + ip4 = [ { address = "10.10.0.4"; prefixLength = 24; } ]; + }; + networking.firewall.enable = false; + }; + }; + }; }; testScript = '' startAll; - $server->waitForUnit("default.target"); - $server->execute("ip link >&2"); + subtest "prepare server", sub { + $server->waitForUnit("default.target"); + $server->succeed("ip link show dev eth1 >&2"); + }; - $server->succeed("ip link show dev eth1 >&2"); + subtest "simple physical interface", sub { + $server->succeed("nixos-container start server"); + $server->waitForUnit("container\@server"); + $server->succeed("systemctl -M server list-dependencies network-addresses-eth1.service >&2"); - $server->succeed("nixos-container start server"); - $server->waitForUnit("container\@server"); - $server->succeed("systemctl -M server list-dependencies network-addresses-eth1.service >&2"); + # The other tests will ping this container on its ip. Here we just check + # that the device is present in the container. + $server->succeed("nixos-container run server -- ip a show dev eth1 >&2"); + }; - $server->succeed("nixos-container run server -- ip a show dev eth1 >&2"); + subtest "physical device in bridge in container", sub { + $bridged->waitForUnit("default.target"); + $bridged->succeed("nixos-container start bridged"); + $bridged->waitForUnit("container\@bridged"); + $bridged->succeed("systemctl -M bridged list-dependencies network-addresses-br0.service >&2"); + $bridged->succeed("systemctl -M bridged status -n 30 -l network-addresses-br0.service"); + $bridged->succeed("nixos-container run bridged -- ping -w 10 -c 1 -n 10.10.0.1"); + }; - $client->waitForUnit("default.target"); - $client->succeed("nixos-container start client"); - $client->waitForUnit("container\@client"); - $client->succeed("systemctl -M client list-dependencies network-addresses-br0.service >&2"); - $client->succeed("systemctl -M client status -n 30 -l network-addresses-br0.service"); - $client->succeed("nixos-container run client -- ping -w 10 -c 1 -n 10.10.0.1"); + subtest "physical device in bond in container", sub { + $bonded->waitForUnit("default.target"); + $bonded->succeed("nixos-container start bonded"); + $bonded->waitForUnit("container\@bonded"); + $bonded->succeed("systemctl -M bonded list-dependencies network-addresses-bond0 >&2"); + $bonded->succeed("systemctl -M bonded status -n 30 -l network-addresses-bond0 >&2"); + $bonded->succeed("nixos-container run bonded -- ping -w 10 -c 1 -n 10.10.0.1"); + }; + + subtest "physical device in bond in bridge in container", sub { + $bridgedbond->waitForUnit("default.target"); + $bridgedbond->succeed("nixos-container start bridgedbond"); + $bridgedbond->waitForUnit("container\@bridgedbond"); + $bridgedbond->succeed("systemctl -M bridgedbond list-dependencies network-addresses-br0.service >&2"); + $bridgedbond->succeed("systemctl -M bridgedbond status -n 30 -l network-addresses-br0.service"); + $bridgedbond->succeed("nixos-container run bridgedbond -- ping -w 10 -c 1 -n 10.10.0.1"); + }; ''; })