nixos/qemu-vm: add option for named network interfaces

Adds a new option to the virtualisation modules that enables specifying explicitly named network interfaces in QEMU VMs.
The existing `virtualisation.vlans` option is still supported for cases where the name of the network interface is irrelevant.
This commit is contained in:
Graham Dennis 2023-05-22 13:38:52 +10:00
parent e6e049b7a2
commit 93502aa3b1
5 changed files with 140 additions and 96 deletions

View file

@ -16,4 +16,4 @@
## Other Notable Changes {#sec-release-23.11-notable-changes} ## Other Notable Changes {#sec-release-23.11-notable-changes}
- Create the first release note entry in this section! - A new option was added to the virtualisation module that enables specifying explicitly named network interfaces in QEMU VMs. The existing `virtualisation.vlans` is still supported for cases where the name of the network interface is irrelevant.

View file

@ -12,7 +12,9 @@ let
}; };
vlans = map (m: m.virtualisation.vlans) (lib.attrValues config.nodes); vlans = map (m: (
m.virtualisation.vlans ++
(lib.mapAttrsToList (_: v: v.vlan) m.virtualisation.interfaces))) (lib.attrValues config.nodes);
vms = map (m: m.system.build.vm) (lib.attrValues config.nodes); vms = map (m: m.system.build.vm) (lib.attrValues config.nodes);
nodeHostNames = nodeHostNames =

View file

@ -4,7 +4,7 @@ let
inherit (lib) inherit (lib)
attrNames concatMap concatMapStrings flip forEach head attrNames concatMap concatMapStrings flip forEach head
listToAttrs mkDefault mkOption nameValuePair optionalString listToAttrs mkDefault mkOption nameValuePair optionalString
range types zipListsWith zipLists range toLower types zipListsWith zipLists
mdDoc mdDoc
; ;
@ -18,24 +18,41 @@ let
networkModule = { config, nodes, pkgs, ... }: networkModule = { config, nodes, pkgs, ... }:
let let
interfacesNumbered = zipLists config.virtualisation.vlans (range 1 255); qemu-common = import ../qemu-common.nix { inherit lib pkgs; };
interfaces = forEach interfacesNumbered ({ fst, snd }:
nameValuePair "eth${toString snd}" { # Convert legacy VLANs to named interfaces and merge with explicit interfaces.
ipv4.addresses = vlansNumbered = forEach (zipLists config.virtualisation.vlans (range 1 255)) (v: {
[{ name = "eth${toString v.snd}";
address = "192.168.${toString fst}.${toString config.virtualisation.test.nodeNumber}"; vlan = v.fst;
assignIP = true;
});
explicitInterfaces = lib.mapAttrsToList (n: v: v // { name = n; }) config.virtualisation.interfaces;
interfaces = vlansNumbered ++ explicitInterfaces;
interfacesNumbered = zipLists interfaces (range 1 255);
# Automatically assign IP addresses to requested interfaces.
assignIPs = lib.filter (i: i.assignIP) interfaces;
ipInterfaces = forEach assignIPs (i:
nameValuePair i.name { ipv4.addresses =
[ { address = "192.168.${toString i.vlan}.${toString config.virtualisation.test.nodeNumber}";
prefixLength = 24; prefixLength = 24;
}]; }];
}); });
qemuOptions = lib.flatten (forEach interfacesNumbered ({ fst, snd }:
qemu-common.qemuNICFlags snd fst.vlan config.virtualisation.test.nodeNumber));
udevRules = forEach interfacesNumbered ({ fst, snd }:
# MAC Addresses for QEMU network devices are lowercase, and udev string comparison is case-sensitive.
"SUBSYSTEM==\"net\",ACTION==\"add\",ATTR{address}==\"${toLower(qemu-common.qemuNicMac fst.vlan config.virtualisation.test.nodeNumber)}\",NAME=\"${fst.name}\"");
networkConfig = networkConfig =
{ {
networking.hostName = mkDefault config.virtualisation.test.nodeName; networking.hostName = mkDefault config.virtualisation.test.nodeName;
networking.interfaces = listToAttrs interfaces; networking.interfaces = listToAttrs ipInterfaces;
networking.primaryIPAddress = networking.primaryIPAddress =
optionalString (interfaces != [ ]) (head (head interfaces).value.ipv4.addresses).address; optionalString (ipInterfaces != [ ]) (head (head ipInterfaces).value.ipv4.addresses).address;
# Put the IP addresses of all VMs in this machine's # Put the IP addresses of all VMs in this machine's
# /etc/hosts file. If a machine has multiple # /etc/hosts file. If a machine has multiple
@ -51,16 +68,13 @@ let
"${config.networking.hostName}.${config.networking.domain} " + "${config.networking.hostName}.${config.networking.domain} " +
"${config.networking.hostName}\n")); "${config.networking.hostName}\n"));
virtualisation.qemu.options = virtualisation.qemu.options = qemuOptions;
let qemu-common = import ../qemu-common.nix { inherit lib pkgs; }; boot.initrd.services.udev.rules = concatMapStrings (x: x + "\n") udevRules;
in
flip concatMap interfacesNumbered
({ fst, snd }: qemu-common.qemuNICFlags snd fst config.virtualisation.test.nodeNumber);
}; };
in in
{ {
key = "ip-address"; key = "network-interfaces";
config = networkConfig // { config = networkConfig // {
# Expose the networkConfig items for tests like nixops # Expose the networkConfig items for tests like nixops
# that need to recreate the network config. # that need to recreate the network config.

View file

@ -564,7 +564,8 @@ in
virtualisation.vlans = virtualisation.vlans =
mkOption { mkOption {
type = types.listOf types.ints.unsigned; type = types.listOf types.ints.unsigned;
default = [ 1 ]; default = if config.virtualisation.interfaces == {} then [ 1 ] else [ ];
defaultText = lib.literalExpression ''if config.virtualisation.interfaces == {} then [ 1 ] else [ ]'';
example = [ 1 2 ]; example = [ 1 2 ];
description = description =
lib.mdDoc '' lib.mdDoc ''
@ -579,6 +580,35 @@ in
''; '';
}; };
virtualisation.interfaces = mkOption {
default = {};
example = {
enp1s0.vlan = 1;
};
description = lib.mdDoc ''
Network interfaces to add to the VM.
'';
type = with types; attrsOf (submodule {
options = {
vlan = mkOption {
type = types.ints.unsigned;
description = lib.mdDoc ''
VLAN to which the network interface is connected.
'';
};
assignIP = mkOption {
type = types.bool;
default = false;
description = lib.mdDoc ''
Automatically assign an IP address to the network interface using the same scheme as
virtualisation.vlans.
'';
};
};
});
};
virtualisation.writableStore = virtualisation.writableStore =
mkOption { mkOption {
type = types.bool; type = types.bool;

View file

@ -93,18 +93,19 @@ let
name = "Static"; name = "Static";
nodes.router = router; nodes.router = router;
nodes.client = { pkgs, ... }: with pkgs.lib; { nodes.client = { pkgs, ... }: with pkgs.lib; {
virtualisation.vlans = [ 1 2 ]; virtualisation.interfaces.enp1s0.vlan = 1;
virtualisation.interfaces.enp2s0.vlan = 2;
networking = { networking = {
useNetworkd = networkd; useNetworkd = networkd;
useDHCP = false; useDHCP = false;
defaultGateway = "192.168.1.1"; defaultGateway = "192.168.1.1";
defaultGateway6 = "fd00:1234:5678:1::1"; defaultGateway6 = "fd00:1234:5678:1::1";
interfaces.eth1.ipv4.addresses = mkOverride 0 [ interfaces.enp1s0.ipv4.addresses = [
{ address = "192.168.1.2"; prefixLength = 24; } { address = "192.168.1.2"; prefixLength = 24; }
{ address = "192.168.1.3"; prefixLength = 32; } { address = "192.168.1.3"; prefixLength = 32; }
{ address = "192.168.1.10"; prefixLength = 32; } { address = "192.168.1.10"; prefixLength = 32; }
]; ];
interfaces.eth2.ipv4.addresses = mkOverride 0 [ interfaces.enp2s0.ipv4.addresses = [
{ address = "192.168.2.2"; prefixLength = 24; } { address = "192.168.2.2"; prefixLength = 24; }
]; ];
}; };
@ -170,12 +171,12 @@ let
# Disable test driver default config # Disable test driver default config
networking.interfaces = lib.mkForce {}; networking.interfaces = lib.mkForce {};
networking.useNetworkd = networkd; networking.useNetworkd = networkd;
virtualisation.vlans = [ 1 ]; virtualisation.interfaces.enp1s0.vlan = 1;
}; };
testScript = '' testScript = ''
start_all() start_all()
client.wait_for_unit("multi-user.target") client.wait_for_unit("multi-user.target")
client.wait_until_succeeds("ip addr show dev eth1 | grep '192.168.1'") client.wait_until_succeeds("ip addr show dev enp1s0 | grep '192.168.1'")
client.shell_interact() client.shell_interact()
client.succeed("ping -c 1 192.168.1.1") client.succeed("ping -c 1 192.168.1.1")
router.succeed("ping -c 1 192.168.1.1") router.succeed("ping -c 1 192.168.1.1")
@ -187,20 +188,13 @@ let
name = "SimpleDHCP"; name = "SimpleDHCP";
nodes.router = router; nodes.router = router;
nodes.client = { pkgs, ... }: with pkgs.lib; { nodes.client = { pkgs, ... }: with pkgs.lib; {
virtualisation.vlans = [ 1 2 ]; virtualisation.interfaces.enp1s0.vlan = 1;
virtualisation.interfaces.enp2s0.vlan = 2;
networking = { networking = {
useNetworkd = networkd; useNetworkd = networkd;
useDHCP = false; useDHCP = false;
interfaces.eth1 = { interfaces.enp1s0.useDHCP = true;
ipv4.addresses = mkOverride 0 [ ]; interfaces.enp2s0.useDHCP = true;
ipv6.addresses = mkOverride 0 [ ];
useDHCP = true;
};
interfaces.eth2 = {
ipv4.addresses = mkOverride 0 [ ];
ipv6.addresses = mkOverride 0 [ ];
useDHCP = true;
};
}; };
}; };
testScript = { ... }: testScript = { ... }:
@ -211,10 +205,10 @@ let
router.wait_for_unit("network-online.target") router.wait_for_unit("network-online.target")
with subtest("Wait until we have an ip address on each interface"): 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 enp1s0 | grep -q '192.168.1'")
client.wait_until_succeeds("ip addr show dev eth1 | grep -q 'fd00:1234:5678:1:'") client.wait_until_succeeds("ip addr show dev enp1s0 | grep -q 'fd00:1234:5678:1:'")
client.wait_until_succeeds("ip addr show dev eth2 | grep -q '192.168.2'") client.wait_until_succeeds("ip addr show dev enp2s0 | grep -q '192.168.2'")
client.wait_until_succeeds("ip addr show dev eth2 | grep -q 'fd00:1234:5678:2:'") client.wait_until_succeeds("ip addr show dev enp2s0 | grep -q 'fd00:1234:5678:2:'")
with subtest("Test vlan 1"): with subtest("Test vlan 1"):
client.wait_until_succeeds("ping -c 1 192.168.1.1") client.wait_until_succeeds("ping -c 1 192.168.1.1")
@ -243,16 +237,15 @@ let
name = "OneInterfaceDHCP"; name = "OneInterfaceDHCP";
nodes.router = router; nodes.router = router;
nodes.client = { pkgs, ... }: with pkgs.lib; { nodes.client = { pkgs, ... }: with pkgs.lib; {
virtualisation.vlans = [ 1 2 ]; virtualisation.interfaces.enp1s0.vlan = 1;
virtualisation.interfaces.enp2s0.vlan = 2;
networking = { networking = {
useNetworkd = networkd; useNetworkd = networkd;
useDHCP = false; useDHCP = false;
interfaces.eth1 = { interfaces.enp1s0 = {
ipv4.addresses = mkOverride 0 [ ];
mtu = 1343; mtu = 1343;
useDHCP = true; useDHCP = true;
}; };
interfaces.eth2.ipv4.addresses = mkOverride 0 [ ];
}; };
}; };
testScript = { ... }: testScript = { ... }:
@ -264,10 +257,10 @@ let
router.wait_for_unit("network.target") router.wait_for_unit("network.target")
with subtest("Wait until we have an ip address on each interface"): 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 enp1s0 | grep -q '192.168.1'")
with subtest("ensure MTU is set"): with subtest("ensure MTU is set"):
assert "mtu 1343" in client.succeed("ip link show dev eth1") assert "mtu 1343" in client.succeed("ip link show dev enp1s0")
with subtest("Test vlan 1"): with subtest("Test vlan 1"):
client.wait_until_succeeds("ping -c 1 192.168.1.1") client.wait_until_succeeds("ping -c 1 192.168.1.1")
@ -286,16 +279,15 @@ let
}; };
bond = let bond = let
node = address: { pkgs, ... }: with pkgs.lib; { node = address: { pkgs, ... }: with pkgs.lib; {
virtualisation.vlans = [ 1 2 ]; virtualisation.interfaces.enp1s0.vlan = 1;
virtualisation.interfaces.enp2s0.vlan = 2;
networking = { networking = {
useNetworkd = networkd; useNetworkd = networkd;
useDHCP = false; useDHCP = false;
bonds.bond0 = { bonds.bond0 = {
interfaces = [ "eth1" "eth2" ]; interfaces = [ "enp1s0" "enp2s0" ];
driverOptions.mode = "802.3ad"; driverOptions.mode = "802.3ad";
}; };
interfaces.eth1.ipv4.addresses = mkOverride 0 [ ];
interfaces.eth2.ipv4.addresses = mkOverride 0 [ ];
interfaces.bond0.ipv4.addresses = mkOverride 0 interfaces.bond0.ipv4.addresses = mkOverride 0
[ { inherit address; prefixLength = 30; } ]; [ { inherit address; prefixLength = 30; } ];
}; };
@ -326,12 +318,11 @@ let
}; };
bridge = let bridge = let
node = { address, vlan }: { pkgs, ... }: with pkgs.lib; { node = { address, vlan }: { pkgs, ... }: with pkgs.lib; {
virtualisation.vlans = [ vlan ]; virtualisation.interfaces.enp1s0.vlan = vlan;
networking = { networking = {
useNetworkd = networkd; useNetworkd = networkd;
useDHCP = false; useDHCP = false;
interfaces.eth1.ipv4.addresses = mkOverride 0 interfaces.enp1s0.ipv4.addresses = [ { inherit address; prefixLength = 24; } ];
[ { inherit address; prefixLength = 24; } ];
}; };
}; };
in { in {
@ -339,11 +330,12 @@ let
nodes.client1 = node { address = "192.168.1.2"; vlan = 1; }; nodes.client1 = node { address = "192.168.1.2"; vlan = 1; };
nodes.client2 = node { address = "192.168.1.3"; vlan = 2; }; nodes.client2 = node { address = "192.168.1.3"; vlan = 2; };
nodes.router = { pkgs, ... }: with pkgs.lib; { nodes.router = { pkgs, ... }: with pkgs.lib; {
virtualisation.vlans = [ 1 2 ]; virtualisation.interfaces.enp1s0.vlan = 1;
virtualisation.interfaces.enp2s0.vlan = 2;
networking = { networking = {
useNetworkd = networkd; useNetworkd = networkd;
useDHCP = false; useDHCP = false;
bridges.bridge.interfaces = [ "eth1" "eth2" ]; bridges.bridge.interfaces = [ "enp1s0" "enp2s0" ];
interfaces.eth1.ipv4.addresses = mkOverride 0 [ ]; interfaces.eth1.ipv4.addresses = mkOverride 0 [ ];
interfaces.eth2.ipv4.addresses = mkOverride 0 [ ]; interfaces.eth2.ipv4.addresses = mkOverride 0 [ ];
interfaces.bridge.ipv4.addresses = mkOverride 0 interfaces.bridge.ipv4.addresses = mkOverride 0
@ -377,7 +369,7 @@ let
nodes.router = router; nodes.router = router;
nodes.client = { pkgs, ... }: with pkgs.lib; { nodes.client = { pkgs, ... }: with pkgs.lib; {
environment.systemPackages = [ pkgs.iptables ]; # to debug firewall rules environment.systemPackages = [ pkgs.iptables ]; # to debug firewall rules
virtualisation.vlans = [ 1 ]; virtualisation.interfaces.enp1s0.vlan = 1;
networking = { networking = {
useNetworkd = networkd; useNetworkd = networkd;
useDHCP = false; useDHCP = false;
@ -385,14 +377,9 @@ let
# reverse path filtering rules for the macvlan interface seem # reverse path filtering rules for the macvlan interface seem
# to be incorrect, causing the test to fail. Disable temporarily. # to be incorrect, causing the test to fail. Disable temporarily.
firewall.checkReversePath = false; firewall.checkReversePath = false;
macvlans.macvlan.interface = "eth1"; macvlans.macvlan.interface = "enp1s0";
interfaces.eth1 = { interfaces.enp1s0.useDHCP = true;
ipv4.addresses = mkOverride 0 [ ]; interfaces.macvlan.useDHCP = true;
useDHCP = true;
};
interfaces.macvlan = {
useDHCP = true;
};
}; };
}; };
testScript = { ... }: testScript = { ... }:
@ -404,7 +391,7 @@ let
router.wait_for_unit("network.target") router.wait_for_unit("network.target")
with subtest("Wait until we have an ip address on each interface"): 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 enp1s0 | grep -q '192.168.1'")
client.wait_until_succeeds("ip addr show dev macvlan | grep -q '192.168.1'") client.wait_until_succeeds("ip addr show dev macvlan | grep -q '192.168.1'")
with subtest("Print lots of diagnostic information"): with subtest("Print lots of diagnostic information"):
@ -431,23 +418,22 @@ let
fou = { fou = {
name = "foo-over-udp"; name = "foo-over-udp";
nodes.machine = { ... }: { nodes.machine = { ... }: {
virtualisation.vlans = [ 1 ]; virtualisation.interfaces.enp1s0.vlan = 1;
networking = { networking = {
useNetworkd = networkd; useNetworkd = networkd;
useDHCP = false; useDHCP = false;
interfaces.eth1.ipv4.addresses = mkOverride 0 interfaces.enp1s0.ipv4.addresses = [ { address = "192.168.1.1"; prefixLength = 24; } ];
[ { address = "192.168.1.1"; prefixLength = 24; } ];
fooOverUDP = { fooOverUDP = {
fou1 = { port = 9001; }; fou1 = { port = 9001; };
fou2 = { port = 9002; protocol = 41; }; fou2 = { port = 9002; protocol = 41; };
fou3 = mkIf (!networkd) fou3 = mkIf (!networkd)
{ port = 9003; local.address = "192.168.1.1"; }; { port = 9003; local.address = "192.168.1.1"; };
fou4 = mkIf (!networkd) fou4 = mkIf (!networkd)
{ port = 9004; local = { address = "192.168.1.1"; dev = "eth1"; }; }; { port = 9004; local = { address = "192.168.1.1"; dev = "enp1s0"; }; };
}; };
}; };
systemd.services = { systemd.services = {
fou3-fou-encap.after = optional (!networkd) "network-addresses-eth1.service"; fou3-fou-encap.after = optional (!networkd) "network-addresses-enp1s0.service";
}; };
}; };
testScript = { ... }: testScript = { ... }:
@ -470,22 +456,22 @@ let
"gue": None, "gue": None,
"family": "inet", "family": "inet",
"local": "192.168.1.1", "local": "192.168.1.1",
"dev": "eth1", "dev": "enp1s0",
} in fous, "fou4 exists" } in fous, "fou4 exists"
''; '';
}; };
sit = let sit = let
node = { address4, remote, address6 }: { pkgs, ... }: with pkgs.lib; { node = { address4, remote, address6 }: { pkgs, ... }: with pkgs.lib; {
virtualisation.vlans = [ 1 ]; virtualisation.interfaces.enp1s0.vlan = 1;
networking = { networking = {
useNetworkd = networkd; useNetworkd = networkd;
useDHCP = false; useDHCP = false;
sits.sit = { sits.sit = {
inherit remote; inherit remote;
local = address4; local = address4;
dev = "eth1"; dev = "enp1s0";
}; };
interfaces.eth1.ipv4.addresses = mkOverride 0 interfaces.enp1s0.ipv4.addresses = mkOverride 0
[ { address = address4; prefixLength = 24; } ]; [ { address = address4; prefixLength = 24; } ];
interfaces.sit.ipv6.addresses = mkOverride 0 interfaces.sit.ipv6.addresses = mkOverride 0
[ { address = address6; prefixLength = 64; } ]; [ { address = address6; prefixLength = 64; } ];
@ -685,10 +671,10 @@ let
vlan-ping = let vlan-ping = let
baseIP = number: "10.10.10.${number}"; baseIP = number: "10.10.10.${number}";
vlanIP = number: "10.1.1.${number}"; vlanIP = number: "10.1.1.${number}";
baseInterface = "eth1"; baseInterface = "enp1s0";
vlanInterface = "vlan42"; vlanInterface = "vlan42";
node = number: {pkgs, ... }: with pkgs.lib; { node = number: {pkgs, ... }: with pkgs.lib; {
virtualisation.vlans = [ 1 ]; virtualisation.interfaces.enp1s0.vlan = 1;
networking = { networking = {
#useNetworkd = networkd; #useNetworkd = networkd;
useDHCP = false; useDHCP = false;
@ -785,12 +771,12 @@ let
privacy = { privacy = {
name = "Privacy"; name = "Privacy";
nodes.router = { ... }: { nodes.router = { ... }: {
virtualisation.vlans = [ 1 ]; virtualisation.interfaces.enp1s0.vlan = 1;
boot.kernel.sysctl."net.ipv6.conf.all.forwarding" = true; boot.kernel.sysctl."net.ipv6.conf.all.forwarding" = true;
networking = { networking = {
useNetworkd = networkd; useNetworkd = networkd;
useDHCP = false; useDHCP = false;
interfaces.eth1.ipv6.addresses = singleton { interfaces.enp1s0.ipv6.addresses = singleton {
address = "fd00:1234:5678:1::1"; address = "fd00:1234:5678:1::1";
prefixLength = 64; prefixLength = 64;
}; };
@ -798,7 +784,7 @@ let
services.radvd = { services.radvd = {
enable = true; enable = true;
config = '' config = ''
interface eth1 { interface enp1s0 {
AdvSendAdvert on; AdvSendAdvert on;
AdvManagedFlag on; AdvManagedFlag on;
AdvOtherConfigFlag on; AdvOtherConfigFlag on;
@ -812,11 +798,11 @@ let
}; };
}; };
nodes.client_with_privacy = { pkgs, ... }: with pkgs.lib; { nodes.client_with_privacy = { pkgs, ... }: with pkgs.lib; {
virtualisation.vlans = [ 1 ]; virtualisation.interfaces.enp1s0.vlan = 1;
networking = { networking = {
useNetworkd = networkd; useNetworkd = networkd;
useDHCP = false; useDHCP = false;
interfaces.eth1 = { interfaces.enp1s0 = {
tempAddress = "default"; tempAddress = "default";
ipv4.addresses = mkOverride 0 [ ]; ipv4.addresses = mkOverride 0 [ ];
ipv6.addresses = mkOverride 0 [ ]; ipv6.addresses = mkOverride 0 [ ];
@ -825,11 +811,11 @@ let
}; };
}; };
nodes.client = { pkgs, ... }: with pkgs.lib; { nodes.client = { pkgs, ... }: with pkgs.lib; {
virtualisation.vlans = [ 1 ]; virtualisation.interfaces.enp1s0.vlan = 1;
networking = { networking = {
useNetworkd = networkd; useNetworkd = networkd;
useDHCP = false; useDHCP = false;
interfaces.eth1 = { interfaces.enp1s0 = {
tempAddress = "enabled"; tempAddress = "enabled";
ipv4.addresses = mkOverride 0 [ ]; ipv4.addresses = mkOverride 0 [ ];
ipv6.addresses = mkOverride 0 [ ]; ipv6.addresses = mkOverride 0 [ ];
@ -847,9 +833,9 @@ let
with subtest("Wait until we have an ip address"): with subtest("Wait until we have an ip address"):
client_with_privacy.wait_until_succeeds( client_with_privacy.wait_until_succeeds(
"ip addr show dev eth1 | grep -q 'fd00:1234:5678:1:'" "ip addr show dev enp1s0 | grep -q 'fd00:1234:5678:1:'"
) )
client.wait_until_succeeds("ip addr show dev eth1 | grep -q 'fd00:1234:5678:1:'") client.wait_until_succeeds("ip addr show dev enp1s0 | grep -q 'fd00:1234:5678:1:'")
with subtest("Test vlan 1"): with subtest("Test vlan 1"):
client_with_privacy.wait_until_succeeds("ping -c 1 fd00:1234:5678:1::1") client_with_privacy.wait_until_succeeds("ping -c 1 fd00:1234:5678:1::1")
@ -947,7 +933,7 @@ let
), "The IPv6 routing table has not been properly cleaned:\n{}".format(ipv6Residue) ), "The IPv6 routing table has not been properly cleaned:\n{}".format(ipv6Residue)
''; '';
}; };
rename = { rename = if networkd then {
name = "RenameInterface"; name = "RenameInterface";
nodes.machine = { pkgs, ... }: { nodes.machine = { pkgs, ... }: {
virtualisation.vlans = [ 1 ]; virtualisation.vlans = [ 1 ];
@ -955,23 +941,20 @@ let
useNetworkd = networkd; useNetworkd = networkd;
useDHCP = false; useDHCP = false;
}; };
} // systemd.network.links."10-custom_name" = {
(if networkd matchConfig.MACAddress = "52:54:00:12:01:01";
then { systemd.network.links."10-custom_name" = { linkConfig.Name = "custom_name";
matchConfig.MACAddress = "52:54:00:12:01:01"; };
linkConfig.Name = "custom_name"; };
};
}
else { boot.initrd.services.udev.rules = ''
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="52:54:00:12:01:01", KERNEL=="eth*", NAME="custom_name"
'';
});
testScript = '' testScript = ''
machine.succeed("udevadm settle") machine.succeed("udevadm settle")
print(machine.succeed("ip link show dev custom_name")) print(machine.succeed("ip link show dev custom_name"))
''; '';
}; } else {
name = "RenameInterface";
nodes = { }; nodes = { };
testScript = "";
};
# even with disabled networkd, systemd.network.links should work # even with disabled networkd, systemd.network.links should work
# (as it's handled by udev, not networkd) # (as it's handled by udev, not networkd)
link = { link = {
@ -1015,6 +998,21 @@ let
machine.fail("ip address show wlan0 | grep -q ${testMac}") machine.fail("ip address show wlan0 | grep -q ${testMac}")
''; '';
}; };
caseSensitiveRenaming = {
name = "CaseSensitiveRenaming";
nodes.machine = { pkgs, ... }: {
virtualisation.interfaces.enCustom.vlan = 11;
networking = {
useNetworkd = networkd;
useDHCP = false;
};
};
testScript = ''
machine.succeed("udevadm settle")
print(machine.succeed("ip link show dev enCustom"))
machine.wait_until_succeeds("ip link show dev enCustom | grep -q '52:54:00:12:0b:01")
'';
};
}; };
in mapAttrs (const (attrs: makeTest (attrs // { in mapAttrs (const (attrs: makeTest (attrs // {