first VM up and running! still wip

This commit is contained in:
Sagar Ch 2024-06-07 02:35:33 +00:00
parent 35710f6d3c
commit 0d9e299595
4 changed files with 310 additions and 118 deletions

View file

@ -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
View 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
View 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) ];
}

View file

@ -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;
type = types.str; default = 2;
default = "host"; description = "Number of CPU cores.";
description = '' };
The CPU model to emulate. cpu.model = mkOption {
''; type = types.str;
}; default = "host";
description = "CPU model.";
smp = };
mkOption { kvm = mkOption {
type = types.ints.positive; type = types.bool;
default = 1; default = true;
description = '' description = "Enable KVM.";
Specify the number of cores the guest is permitted to use. };
The number can be higher than the available cores on the arch = mkOption {
host system. type = types.str;
''; default = "x86_64";
}; description = "Architecture of the VM.";
};
memSize = pc.type = mkOption {
mkOption { type = types.str;
type = types.ints.positive; default = "q35";
default = 1024; description = "PC type.";
description = '' };
The memory size in megabytes of the virtual machine. bios.efi = mkOption {
''; type = types.bool;
}; default = true;
description = "Enable EFI BIOS.";
diskSize = };
mkOption { bios.tpm = mkOption {
type = types.str; type = types.bool;
default = ""; default = false;
description = '' description = "Enable TPM BIOS.";
The disk size in qemu params. };
''; mem.size = mkOption {
}; type = types.int;
default = 1024;
osImage = description = "Memory size in MB.";
mkOption { };
type = types.package; mem.balloon = mkOption {
description = '' type = types.bool;
OS image stored in the nix store, either built with vmixLib.images or fetched from the internet. default = false;
''; description = "Enable memory ballooning.";
}; };
disks.os.file = mkOption {
persistantOverlayImagePath = type = types.path;
mkOption { description = "Path to the OS disk image.";
type = types.nullOr types.path; };
default = null; disks.os.persist = mkOption {
description = '' type = types.bool;
Path where the persistant overlay is stored. If path is non-existant, a qcow2 image backed by the original osImage is created. default = false;
''; description = "Persist OS disk changes.";
}; };
disks.iso.file = mkOption {
startOnBoot = type = types.nullOr types.path;
mkOption { description = "Path to the ISO file.";
type = types.bool; default = null;
description = '' };
Whether to start the VM automatically on system boot. disks.add = mkOption {
''; default = {};
default = true; type = types.attrsOf (types.submodule {
}; options = {
file = mkOption {
sharedDirectories = type = types.path;
lib.mkOption { description = "Path to the additional disk.";
type = types.attrsOf };
(types.submodule { mounts = mkOption {
options = { type = types.attrsOf types.str;
source = lib.mkOption { description = "Mount points for the additional disk.";
type = types.str; };
description = "The path of the directory to share, can be a shell variable"; opts = mkOption {
}; type = types.str;
target = lib.mkOption { description = "additional options in QEMU args for this disk";
type = types.str; };
description = "The mount point of the directory inside the virtual machine";
};
};
});
example = {
my-share = { source = "/path/to/be/shared"; target = "/mnt/shared"; };
}; };
default = { }; });
}; description = "Additional disks.";
};
networks = lib.mkOption { shares = mkOption {
type = types.attrsOf default = {};
(types.submodule networkModule); type = types.attrsOf (types.submodule {
example = { options = {
my-share = { source = "/path/to/be/shared"; target = "/mnt/shared"; }; 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.";
};
}; };
default = { }; });
}; description = "Shared directories.";
}; };
functions.mkVMService = name: cfg: disks.bus = mkOption {
let type = types.str;
vmCfg = cfg // { inherit name }; default = "virtio";
mkDiskImageFrom description = "Bus type for the disks.";
diskImage = };
qemuArgs = '' boot.order = mkOption {
--name ${vmCfg.name} \ type = types.listOf types.str;
--cpu ${vmCfg.cpu} \ description = "Boot order.";
--smp ${vmCfg.cores} \ default = [ "os" "iso" ];
-- };
''; boot.menu = mkOption {
in type = types.bool;
{ default = false;
description = "Enable boot menu.";
};
network.user.enable = mkOption {
type = types.bool;
default = false;
description = "enable qemu user networking";
};
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
//