first VM up and running! still wip
This commit is contained in:
parent
35710f6d3c
commit
0d9e299595
4 changed files with 310 additions and 118 deletions
|
|
@ -5,5 +5,5 @@ let
|
||||||
args = { inherit config pkgs lib vmixLib; };
|
args = { inherit config pkgs lib vmixLib; };
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
imports = [ (import ./network args) ];
|
imports = [ (import ./network args) (import ./vm args) ];
|
||||||
}
|
}
|
||||||
140
nixos/vm/config.nix
Normal file
140
nixos/vm/config.nix
Normal file
|
|
@ -0,0 +1,140 @@
|
||||||
|
{ config, pkgs, lib, vmixLib, ... }:
|
||||||
|
with lib;
|
||||||
|
with vmixLib.network;
|
||||||
|
let
|
||||||
|
vmixCfg = config.vmix;
|
||||||
|
|
||||||
|
mkServices4aVM = name: cfg:
|
||||||
|
let
|
||||||
|
vmCfg = cfg // { inherit name; };
|
||||||
|
netName = head (attrNames vmCfg.network.vmix);
|
||||||
|
netCfg = vmCfg.network.vmix.${netName} // { name = netName; };
|
||||||
|
|
||||||
|
mkTap4aLan = lanName: tapCfg:
|
||||||
|
let
|
||||||
|
tapInterfaceName = "vt-${vmCfg.name}-${lanName}";
|
||||||
|
lanInterfaceName = "brx-${lanName}";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
name = lanName;
|
||||||
|
iface = tapInterfaceName;
|
||||||
|
create = ''
|
||||||
|
ip tuntap add dev ${tapInterfaceName} mode tap
|
||||||
|
ip link set dev ${tapInterfaceName} up
|
||||||
|
ip link set dev ${tapInterfaceName} master ${lanInterfaceName}
|
||||||
|
'';
|
||||||
|
delete = ''
|
||||||
|
ip link del ${tapInterfaceName}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
mkMacvtap = macvtapName: macvtapCfg:
|
||||||
|
let
|
||||||
|
macvtapNetworkCfg = config.vmix.networks.${netCfg.name}.bridges.macvtaps.${macvtapName};
|
||||||
|
macvtapInterfaceName = "mt-${vmCfg.name}-${macvtapNetworkCfg.uplink.iface}";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
name = macvtapName;
|
||||||
|
iface = macvtapInterfaceName;
|
||||||
|
create = ''
|
||||||
|
ip link add link ${macvtapNetworkCfg.uplink.iface} name ${macvtapInterfaceName} type macvtap mode bridge
|
||||||
|
ip link set ${macvtapInterfaceName} netns ${netName}.vmix
|
||||||
|
'';
|
||||||
|
delete = ''
|
||||||
|
ip netns exec ${netName}.vmix ip link del ${macvtapInterfaceName}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
allTaps = (mapAttrsToList mkTap4aLan netCfg.lans);
|
||||||
|
allMacvtaps = (mapAttrsToList mkMacvtap netCfg.macvtaps);
|
||||||
|
|
||||||
|
createTapsforLansScript = pkgs.writeShellScript "${vmCfg.name}-taps-vmix" (
|
||||||
|
concatStringsSep "\n" (builtins.map (tap: tap.create) allTaps)
|
||||||
|
);
|
||||||
|
|
||||||
|
deleteTapsforLansScript = pkgs.writeShellScript "${vmCfg.name}-taps-vmix" (
|
||||||
|
concatStringsSep "\n" (builtins.map (tap: tap.delete) allTaps)
|
||||||
|
);
|
||||||
|
|
||||||
|
createMacvapsScript = pkgs.writeShellScript "${vmCfg.name}-taps-vmix" (
|
||||||
|
concatStringsSep "\n" (builtins.map (macvtap: macvtap.create) allMacvtaps)
|
||||||
|
);
|
||||||
|
|
||||||
|
deleteMacvapsScript = pkgs.writeShellScript "${vmCfg.name}-taps-vmix" (
|
||||||
|
concatStringsSep "\n" (builtins.map (macvtap: macvtap.delete) allMacvtaps)
|
||||||
|
);
|
||||||
|
|
||||||
|
osImage = vmixLib.customizeImage vmCfg.disks.os.file { name = vmCfg.name; };
|
||||||
|
|
||||||
|
qemuStartVMScript = pkgs.writeShellScript "${vmCfg.name}-qemu-vmix" ''
|
||||||
|
exec qemu-system-${vmCfg.arch} \
|
||||||
|
-nographic \
|
||||||
|
${optionalString vmCfg.kvm "-accel kvm"} \
|
||||||
|
-name ${vmCfg.name} \
|
||||||
|
-m ${toString vmCfg.mem.size} \
|
||||||
|
-smp cores=${toString vmCfg.cpu.cores} \
|
||||||
|
-cpu ${vmCfg.cpu.model} \
|
||||||
|
-machine type=${vmCfg.pc.type} \
|
||||||
|
${optionalString vmCfg.bios.efi "-bios ${pkgs.OVMF.fd}/FV/OVMF.fd"} \
|
||||||
|
${optionalString vmCfg.bios.tpm "-chardev socket,id=chrtpm,path=/tmp/mytpm-sock -tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-tis,tpmdev=tpm0"} \
|
||||||
|
-drive file=${toString osImage},format=qcow2,if=virtio${optionalString (vmCfg.disks.os.persist == false) ",snapshot=on"} \
|
||||||
|
${optionalString (vmCfg.disks.iso.file != null) "-drive file=${toString vmCfg.disks.iso.file},media=cdrom,readonly=on"} \
|
||||||
|
${concatMapStrings (diskCfg: ''
|
||||||
|
-drive file=${diskCfg.file},format=qcow2,if=${toString vmCfg.disks.bus} \
|
||||||
|
'') (attrValues vmCfg.disks.add)} \
|
||||||
|
${concatMapStrings (shareCfg: ''
|
||||||
|
-virtfs local,path=${toString shareCfg.source},security_model=passthrough,mount_tag=${shareCfg.target} \
|
||||||
|
'') (attrValues vmCfg.shares)} \
|
||||||
|
${concatMapStrings (tapCfg: ''
|
||||||
|
-device virtio-net-pci,netdev=lan-${tapCfg.name} \
|
||||||
|
-netdev tap,id=lan-${tapCfg.name},ifname=${tapCfg.iface},script=no,downscript=no \
|
||||||
|
'') allTaps} \
|
||||||
|
${optionalString cfg.network.user.enable "
|
||||||
|
-netdev user,id=user \
|
||||||
|
-device virtio-net-pci,netdev=user \
|
||||||
|
"} \
|
||||||
|
${optionalString (vmCfg.boot.menu == true) "-boot menu=on"} \
|
||||||
|
#${optionalString (length vmCfg.boot.order > 0) "-boot order=${concatStringsSep "," vmCfg.boot.order}"}
|
||||||
|
|
||||||
|
# ${concatMapStrings (macvtap: ''
|
||||||
|
# -device virtio-net-pci,netdev=macvtap-${macvtap.name} \
|
||||||
|
# -netdev tap,id=macvtap-${macvtap.name},ifname=${macvtap.iface},script=no,downscript=no \
|
||||||
|
# '') allMacvtaps} \
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
{
|
||||||
|
"vm.vmix@${vmCfg.name}" = rec {
|
||||||
|
requires = [ "net.vmix@${netCfg.name}.target" "macvtaps.vm.vmix@${vmCfg.name}.service" ];
|
||||||
|
unitConfig.JoinsNamespaceOf = "ns.net.vmix@${netCfg.name}.service";
|
||||||
|
after = requires;
|
||||||
|
path = with pkgs; [ iproute2 qemu ];
|
||||||
|
serviceConfig = {
|
||||||
|
ExecStartPre = createTapsforLansScript;
|
||||||
|
ExecStart = qemuStartVMScript;
|
||||||
|
ExecStopPost = deleteTapsforLansScript;
|
||||||
|
PrivateTmp = true;
|
||||||
|
ProtectSystem = true;
|
||||||
|
ProtectHome = true;
|
||||||
|
PrivateNetwork = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
"macvtaps.vm.vmix@${vmCfg.name}" = rec {
|
||||||
|
bindsTo = [ "net.vmix@${netCfg.name}.target" ];
|
||||||
|
after = bindsTo;
|
||||||
|
partOf = [ "vm.vmix@${vmCfg.name}.service" ];
|
||||||
|
path = with pkgs; [ iproute2 ];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
RemainAfterExit = true;
|
||||||
|
ExecStart = createMacvapsScript;
|
||||||
|
ExecStop = deleteMacvapsScript;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
vmServices = concatMapAttrs mkServices4aVM vmixCfg.vms;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
config.systemd.services = vmServices;
|
||||||
|
}
|
||||||
11
nixos/vm/default.nix
Normal file
11
nixos/vm/default.nix
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
args@{ config, pkgs, lib, vmixLib, ... }:
|
||||||
|
with lib;
|
||||||
|
{
|
||||||
|
options.vmix.vms = mkOption {
|
||||||
|
type = types.attrsOf
|
||||||
|
(types.submodule (import ./options.nix args));
|
||||||
|
default = { };
|
||||||
|
};
|
||||||
|
|
||||||
|
imports = [ (import ./config.nix args) ];
|
||||||
|
}
|
||||||
|
|
@ -1,125 +1,166 @@
|
||||||
{ config, pkgs, lib, ... }:
|
{ config, pkgs, lib, ... }:
|
||||||
with lib;
|
with lib;
|
||||||
let
|
|
||||||
networkModule = import ./network.nix { inherit config pkgs lib ;};
|
|
||||||
in
|
|
||||||
{
|
{
|
||||||
options = {
|
options = {
|
||||||
cpu =
|
cpu.cores = mkOption {
|
||||||
mkOption {
|
type = types.int;
|
||||||
|
default = 2;
|
||||||
|
description = "Number of CPU cores.";
|
||||||
|
};
|
||||||
|
cpu.model = mkOption {
|
||||||
type = types.str;
|
type = types.str;
|
||||||
default = "host";
|
default = "host";
|
||||||
description = ''
|
description = "CPU model.";
|
||||||
The CPU model to emulate.
|
|
||||||
'';
|
|
||||||
};
|
};
|
||||||
|
kvm = mkOption {
|
||||||
smp =
|
|
||||||
mkOption {
|
|
||||||
type = types.ints.positive;
|
|
||||||
default = 1;
|
|
||||||
description = ''
|
|
||||||
Specify the number of cores the guest is permitted to use.
|
|
||||||
The number can be higher than the available cores on the
|
|
||||||
host system.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
memSize =
|
|
||||||
mkOption {
|
|
||||||
type = types.ints.positive;
|
|
||||||
default = 1024;
|
|
||||||
description = ''
|
|
||||||
The memory size in megabytes of the virtual machine.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
diskSize =
|
|
||||||
mkOption {
|
|
||||||
type = types.str;
|
|
||||||
default = "";
|
|
||||||
description = ''
|
|
||||||
The disk size in qemu params.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
osImage =
|
|
||||||
mkOption {
|
|
||||||
type = types.package;
|
|
||||||
description = ''
|
|
||||||
OS image stored in the nix store, either built with vmixLib.images or fetched from the internet.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
persistantOverlayImagePath =
|
|
||||||
mkOption {
|
|
||||||
type = types.nullOr types.path;
|
|
||||||
default = null;
|
|
||||||
description = ''
|
|
||||||
Path where the persistant overlay is stored. If path is non-existant, a qcow2 image backed by the original osImage is created.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
startOnBoot =
|
|
||||||
mkOption {
|
|
||||||
type = types.bool;
|
type = types.bool;
|
||||||
description = ''
|
|
||||||
Whether to start the VM automatically on system boot.
|
|
||||||
'';
|
|
||||||
default = true;
|
default = true;
|
||||||
|
description = "Enable KVM.";
|
||||||
};
|
};
|
||||||
|
arch = mkOption {
|
||||||
sharedDirectories =
|
type = types.str;
|
||||||
lib.mkOption {
|
default = "x86_64";
|
||||||
type = types.attrsOf
|
description = "Architecture of the VM.";
|
||||||
(types.submodule {
|
};
|
||||||
|
pc.type = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "q35";
|
||||||
|
description = "PC type.";
|
||||||
|
};
|
||||||
|
bios.efi = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
description = "Enable EFI BIOS.";
|
||||||
|
};
|
||||||
|
bios.tpm = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = "Enable TPM BIOS.";
|
||||||
|
};
|
||||||
|
mem.size = mkOption {
|
||||||
|
type = types.int;
|
||||||
|
default = 1024;
|
||||||
|
description = "Memory size in MB.";
|
||||||
|
};
|
||||||
|
mem.balloon = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = "Enable memory ballooning.";
|
||||||
|
};
|
||||||
|
disks.os.file = mkOption {
|
||||||
|
type = types.path;
|
||||||
|
description = "Path to the OS disk image.";
|
||||||
|
};
|
||||||
|
disks.os.persist = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = "Persist OS disk changes.";
|
||||||
|
};
|
||||||
|
disks.iso.file = mkOption {
|
||||||
|
type = types.nullOr types.path;
|
||||||
|
description = "Path to the ISO file.";
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
disks.add = mkOption {
|
||||||
|
default = {};
|
||||||
|
type = types.attrsOf (types.submodule {
|
||||||
options = {
|
options = {
|
||||||
source = lib.mkOption {
|
file = mkOption {
|
||||||
type = types.str;
|
type = types.path;
|
||||||
description = "The path of the directory to share, can be a shell variable";
|
description = "Path to the additional disk.";
|
||||||
};
|
};
|
||||||
target = lib.mkOption {
|
mounts = mkOption {
|
||||||
|
type = types.attrsOf types.str;
|
||||||
|
description = "Mount points for the additional disk.";
|
||||||
|
};
|
||||||
|
opts = mkOption {
|
||||||
type = types.str;
|
type = types.str;
|
||||||
description = "The mount point of the directory inside the virtual machine";
|
description = "additional options in QEMU args for this disk";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
example = {
|
description = "Additional disks.";
|
||||||
my-share = { source = "/path/to/be/shared"; target = "/mnt/shared"; };
|
|
||||||
};
|
};
|
||||||
default = { };
|
shares = mkOption {
|
||||||
|
default = {};
|
||||||
|
type = types.attrsOf (types.submodule {
|
||||||
|
options = {
|
||||||
|
source = mkOption {
|
||||||
|
type = types.path;
|
||||||
|
description = "Source path for the shared directory.";
|
||||||
|
};
|
||||||
|
target = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = "Target path inside the VM for the shared directory.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
||||||
|
description = "Shared directories.";
|
||||||
};
|
};
|
||||||
|
|
||||||
networks = lib.mkOption {
|
disks.bus = mkOption {
|
||||||
type = types.attrsOf
|
type = types.str;
|
||||||
(types.submodule networkModule);
|
default = "virtio";
|
||||||
example = {
|
description = "Bus type for the disks.";
|
||||||
my-share = { source = "/path/to/be/shared"; target = "/mnt/shared"; };
|
|
||||||
};
|
};
|
||||||
default = { };
|
boot.order = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
description = "Boot order.";
|
||||||
|
default = [ "os" "iso" ];
|
||||||
};
|
};
|
||||||
|
boot.menu = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = "Enable boot menu.";
|
||||||
};
|
};
|
||||||
|
|
||||||
functions.mkVMService = name: cfg:
|
network.user.enable = mkOption {
|
||||||
let
|
type = types.bool;
|
||||||
vmCfg = cfg // { inherit name };
|
default = false;
|
||||||
mkDiskImageFrom
|
description = "enable qemu user networking";
|
||||||
diskImage =
|
};
|
||||||
qemuArgs = ''
|
|
||||||
--name ${vmCfg.name} \
|
|
||||||
--cpu ${vmCfg.cpu} \
|
|
||||||
--smp ${vmCfg.cores} \
|
|
||||||
--
|
|
||||||
'';
|
|
||||||
in
|
|
||||||
{
|
|
||||||
|
|
||||||
|
network.vmix = mkOption {
|
||||||
|
default = {};
|
||||||
|
type = types.attrsOf (types.submodule {
|
||||||
|
options = {
|
||||||
|
lans = mkOption {
|
||||||
|
default = {};
|
||||||
|
type = types.attrsOf (types.submodule {
|
||||||
|
options = {
|
||||||
|
enable = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = "Enable the LAN interface.";
|
||||||
|
};
|
||||||
|
mac = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = "MAC address for the LAN interface.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
||||||
|
description = "LAN interfaces.";
|
||||||
|
};
|
||||||
|
macvtaps = mkOption {
|
||||||
|
default = {};
|
||||||
|
type = types.attrsOf (types.submodule {
|
||||||
|
options = {
|
||||||
|
enable = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = "Enable the MACVTap interface.";
|
||||||
|
};
|
||||||
|
mac = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = "MAC address for the MACVTap interface.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
||||||
|
description = "MACVTap interfaces.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
||||||
|
description = "Network interfaces.";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
//storage
|
|
||||||
//
|
|
||||||
//networking - DNS, VPNs, Bridges, NATs, isolation, connecting with others,host
|
|
||||||
//run adhoc
|
|
||||||
//run and die
|
|
||||||
//
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue