Commit 6ae3e769 authored by Andrew Hoff's avatar Andrew Hoff
Browse files

nixos/virtualisation: add option for explicitly 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` is still supported for cases where the name of
the network interface is irrelevant.
parent 1c18d9a5
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -687,6 +687,15 @@
          been fixed to allow more than one plugin in the path.
        </para>
      </listitem>
      <listitem>
        <para>
          A new option was added to the virtualisation module that
          enables specifying explicitly named network interfaces in QEMU
          VMs. The existing <literal>virtualisation.vlans</literal> is
          still supported for cases where the name of the network
          interface is irrelevant.
        </para>
      </listitem>
    </itemizedlist>
  </section>
</section>
+2 −0
Original line number Diff line number Diff line
@@ -170,3 +170,5 @@ In addition to numerous new and upgraded packages, this release has the followin
- `nixos-version` now accepts `--configuration-revision` to display more information about the current generation revision

- The option `services.nomad.extraSettingsPlugins` has been fixed to allow more than one plugin in the path.

- 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.
+3 −1
Original line number Diff line number Diff line
@@ -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);

  nodeHostNames =
+27 −14
Original line number Diff line number Diff line
@@ -18,24 +18,40 @@ let

  networkModule = { config, nodes, pkgs, ... }:
    let
      interfacesNumbered = zipLists config.virtualisation.vlans (range 1 255);
      interfaces = forEach interfacesNumbered ({ fst, snd }:
        nameValuePair "eth${toString snd}" {
          ipv4.addresses =
            [{
              address = "192.168.${toString fst}.${toString config.virtualisation.test.nodeNumber}";
      qemu-common = import ../qemu-common.nix { inherit lib pkgs; };

      # Convert legacy VLANs to named interfaces and merge with explicit interfaces.
      vlansNumbered = forEach (zipLists config.virtualisation.vlans (range 1 255)) (v: {
        name = "eth${toString v.snd}";
        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;
            }];
        });

      qemuOptions = lib.flatten (forEach interfacesNumbered ({ fst, snd }:
        qemu-common.qemuNICFlags snd fst.vlan config.virtualisation.test.nodeNumber));
      udevRules = forEach interfacesNumbered ({ fst, snd }:
        "SUBSYSTEM==\"net\",ACTION==\"add\",ATTR{address}==\"${qemu-common.qemuNicMac fst.vlan config.virtualisation.test.nodeNumber}\",NAME=\"${fst.name}\"");

      networkConfig =
        {
          networking.hostName = mkDefault config.virtualisation.test.nodeName;

          networking.interfaces = listToAttrs interfaces;
          networking.interfaces = listToAttrs ipInterfaces;

          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
          # /etc/hosts file.  If a machine has multiple
@@ -51,16 +67,13 @@ let
                    "${config.networking.hostName}.${config.networking.domain} " +
                  "${config.networking.hostName}\n"));

          virtualisation.qemu.options =
            let qemu-common = import ../qemu-common.nix { inherit lib pkgs; };
            in
            flip concatMap interfacesNumbered
              ({ fst, snd }: qemu-common.qemuNICFlags snd fst config.virtualisation.test.nodeNumber);
          virtualisation.qemu.options = qemuOptions;
          boot.initrd.services.udev.rules = concatMapStrings (x: x + "\n") udevRules;
        };

    in
    {
      key = "ip-address";
      key = "network-interfaces";
      config = networkConfig // {
        # Expose the networkConfig items for tests like nixops
        # that need to recreate the network config.
+31 −1
Original line number Diff line number Diff line
@@ -545,7 +545,8 @@ in
    virtualisation.vlans =
      mkOption {
        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 ];
        description =
          lib.mdDoc ''
@@ -560,6 +561,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 =
      mkOption {
        type = types.bool;
Loading