nix-config/modules/microvm.nix

171 lines
4.9 KiB
Nix

{ zentralwerk, config, lib, pkgs, ... }:
let
defaultGateways = {
serv = "serv-gw";
c3d2 = "c3d2-gw3";
pub = "pub-gw";
flpk = "flpk-gw";
};
inherit (config.networking) hostName;
inherit (config.c3d2.deployment) server;
serverFQDN = "${server}.cluster.zentralwerk.org";
generateMacAddress = net:
let
hash = builtins.hashString "md5" "1-${net}-${hostName}";
c = off: builtins.substring off 2 hash;
in
"${builtins.substring 0 1 hash}2:${c 2}:${c 4}:${c 6}:${c 8}:${c 10}";
nets = builtins.attrNames (
lib.filterAttrs (_: { hosts4, hosts6, ... }:
hosts4 ? ${hostName} ||
lib.filterAttrs (_: hosts6:
hosts6 ? ${hostName}
) hosts6 != {}
) zentralwerk.lib.config.site.net
);
arch-to-host = rec {
server9 = "westmere";
server10 = "ivybridge";
nomad = server9;
};
in
{
options.c3d2.deployment = with lib; {
server = mkOption {
type = types.enum [ "server9" "server10" ];
default = null;
description = ''
Server that is supposed to host this MicroVM.
'';
};
autoNetSetup = mkOption {
type = types.bool;
default = true;
description = ''
Automatically configure MicroVM network interfaces and
systemd-networkd according to Zentralwerk network data.
'';
};
mounts = mkOption {
description = "Persistent filesystems to create, without leading /.";
type = with types; listOf str;
};
mountBase = mkOption {
description = "Location (ZFS dataset, ...) where all the shares live.";
type = types.path;
default = "/var/lib/microvms/${hostName}";
};
};
config = {
c3d2.deployment.mounts = [ "etc" "home" "var" ];
# make mounts like /etc /home /var available early so that they can be used in system.activationScripts
fileSystems = lib.genAttrs (map (x: "/" + x) config.c3d2.deployment.mounts)
(_: { neededForBoot = true; });
microvm = {
hypervisor = lib.mkDefault "cloud-hypervisor";
mem = lib.mkDefault 512;
vcpu = lib.mkDefault 4;
interfaces = lib.mkIf config.c3d2.deployment.autoNetSetup (
map (net: {
type = "tap";
id = builtins.substring 0 15 "${net}-${hostName}";
mac = generateMacAddress net;
}) nets
);
shares = [ {
source = "/nix/store";
mountPoint = "/nix/.ro-store";
tag = "store";
proto = "virtiofs";
socket = "store.socket";
} ]
++ map (dir:
if lib.hasPrefix "/" dir
then throw "${dir} starts with a leading /. Just don't!"
else let
tag = builtins.replaceStrings ["/"] ["_"] dir;
in {
source = "${config.c3d2.deployment.mountBase}/${dir}";
mountPoint = "/${dir}";
inherit tag;
proto = "virtiofs";
socket = "${tag}.socket";
}) config.c3d2.deployment.mounts;
};
networking = lib.mkIf config.c3d2.deployment.autoNetSetup {
useDHCP = false;
dhcpcd.enable = false;
useNetworkd = true;
};
systemd.network = lib.mkIf config.c3d2.deployment.autoNetSetup {
links = builtins.foldl' (links: net: links // {
"30-${net}" = {
# enable = true;
matchConfig.MACAddress = generateMacAddress net;
# rename interface to net name
linkConfig.Name = net;
};
}) {} nets;
networks = builtins.foldl' (networks: net: networks // {
"30-${net}" =
let
zwNet = zentralwerk.lib.config.site.net.${net};
addresses =
lib.optional (zwNet.hosts4 ? ${hostName}) "${zwNet.hosts4.${hostName}}/${toString zwNet.subnet4Len}"
++
map (hosts6: "${hosts6.${hostName}}/64") (
builtins.filter (hosts6: hosts6 ? ${hostName}) (
builtins.attrValues zwNet.hosts6
)
);
in {
matchConfig.MACAddress = generateMacAddress net;
addresses = map (Address: {
addressConfig = { inherit Address; };
}) addresses;
gateway = lib.mkIf (defaultGateways ? ${net}) (
let
gw = defaultGateways.${net};
in
[ zwNet.hosts4.${gw} ]
++ map (hosts6: hosts6.${gw}) (
builtins.filter (hosts6: hosts6 ? ${gw}) (
builtins.attrValues zwNet.hosts6
)
)
);
};
}) {} nets;
};
simd.arch = arch-to-host.${config.c3d2.deployment.server};
system.build = {
copyToServer = pkgs.writeShellScript "copy-to-${server}" ''
nix copy --no-check-sigs --to ssh-ng://root@${serverFQDN} $@
'';
runOnServer = pkgs.writeShellScript "run-on-${server}" ''
# we cannot execute any other commands here because it grabs away $@
ssh root@${serverFQDN} -- $@
'';
};
};
}