diff options
author | Emile <git@emile.space> | 2025-05-07 21:42:04 +0200 |
---|---|---|
committer | Emile <git@emile.space> | 2025-05-07 21:42:04 +0200 |
commit | d72fee53142fe38b2ddd0a73c76bc80c81951710 (patch) | |
tree | aac9fc9236f64ad042dd4c7e189ed605a2f653a3 | |
parent | 3b9564639c26be02aa8e60fcc5bd05dcafda18f9 (diff) |
(corrino) basic libvirtnix setup
Build using ; nix-build test.nix -A config.services.emile.libvirtnix.output.domain --show-trace && cat result | xq Generates a result symlink to an xml file that is generated from config.nix
-rw-r--r-- | nix/modules/libvirtnix/config.nix | 59 | ||||
-rw-r--r-- | nix/modules/libvirtnix/domain.nix | 1126 | ||||
-rw-r--r-- | nix/modules/libvirtnix/test.nix | 10 | ||||
-rw-r--r-- | nix/modules/libvirtnix/xml.nix | 45 |
4 files changed, 568 insertions, 672 deletions
diff --git a/nix/modules/libvirtnix/config.nix b/nix/modules/libvirtnix/config.nix new file mode 100644 index 0000000..e1a8d28 --- /dev/null +++ b/nix/modules/libvirtnix/config.nix @@ -0,0 +1,59 @@ +{ + services.emile.libvirtnix = { + enable = true; + vm = { + "alan" = { + vm_type = "kvm"; + id = "1337"; + name = "blub"; + uuid = "cafebabe-d474-452b-80f4-c951c39bcf74"; + + metadata.libosinfo = "https://libosinfo.org/xmlns/libvirt/domain/1.0"; + metadata.libosinfo_os = "https://nixos.org/nixos/unknown"; + + memory.unit = "KiB"; + memory.value = 2097152; + + currentMemory.unit = "KiB"; + currentMemory.value = 2097152; + + vcpu.placement = "static"; + vcpu.count = 3; + + resource.partition = "/machine"; + + os = { + type = { + arch = "x86_64"; + machine = "pc-q35-3.1"; + value = "hvm"; + }; + + loader = { + readonly = "yes"; + type = "pflash"; + value = "/usr/share/OVMF/OVMF_CODE.fd"; + }; + + nvram.value = "/var/lib/libvirt/qemu/nvram/fileserver2_VARS.fd"; + + boot.dev = "hd"; + }; + + features = { + acpi = true; + apic = true; + vmport = { + state = "off"; + }; + }; + + cpu = { + mode = "host-passthrough"; + check = "none"; + migratable = "on"; + }; + }; + }; + }; +} diff --git a/nix/modules/libvirtnix/domain.nix b/nix/modules/libvirtnix/domain.nix index a028c7d..f7596e3 100644 --- a/nix/modules/libvirtnix/domain.nix +++ b/nix/modules/libvirtnix/domain.nix @@ -1,701 +1,483 @@ -{ pkgs, lib, ... }: - +{ config, lib, ... }: + +let + mkTag = import ./xml.nix { inherit lib; }; + pkgs = import <nixpkgs> { }; + + stringOption = + { + default ? "", + }: + lib.mkOption { + type = lib.types.str; + default = "${default}"; + }; +in { - options = with lib; { - - # meta, this allows defining the package used for each vm individually, defaults should be sane - packages = mkOption { - type = types.submodule { - options = { - libvirt = mkPackageOption pkgs "libvirt" { }; - qemu = mkPackageOption pkgs "qemu" { }; + options = { + services.emile.libvirtnix = + let + cpu = lib.mkOption { + type = lib.types.submodule { + options = { + mode = lib.mkOption { + type = lib.types.string; + example = "host-passthrough"; + default = ""; + }; + check = lib.mkOption { + type = lib.types.string; + example = "none"; + default = ""; + }; + migratable = lib.mkOption { + type = lib.types.string; + example = "on"; + default = ""; + }; + }; + }; }; - }; - }; - - # attributs for the root `<domain ...>` node - type = mkOption { - type = types.enum [ - "xen" - "hvm" - "kvm" - "qemu" - "lxc" - ]; - default = "kvm"; - example = "qemu"; - description = '' - hypervisor used for running the domain - allowed values are driver specific, if missing, plz send PR - - hvm since since 8.1.0 and QEMU 2.12 - ''; - }; - - id = mkOption { - type = types.int; - default = 0; - example = 42; - }; + features = lib.mkOption { + type = lib.types.submodule { + options = { + acpi = lib.mkEnableOption "enable acpi"; + apic = lib.mkEnableOption "enable apic"; + + vmport = lib.mkOption { + type = lib.types.submodule { + options = { + state = lib.mkOption { + type = lib.types.string; + example = "off"; + default = ""; + }; + }; + }; + }; + }; + }; + }; + + os_boot = lib.mkOption { + type = lib.types.submodule { + options = { + dev = lib.mkOption { + type = lib.types.str; + example = "hd"; + default = ""; + }; + }; + }; + }; + + os_nvram = lib.mkOption { + type = lib.types.submodule { + options = { + value = lib.mkOption { + type = lib.types.str; + example = "/var/lib/libvirt/qemu/nvram/fileserver2_VARS.fd"; + default = ""; + }; + }; + }; + }; + + os_loader = lib.mkOption { + type = lib.types.submodule { + options = { + readonly = lib.mkOption { + type = lib.types.str; + example = "yes"; + default = ""; + }; + type = lib.mkOption { + type = lib.types.str; + example = "pflash"; + default = ""; + }; + value = lib.mkOption { + type = lib.types.str; + example = "/usr/share/OVMF/OVMF_CODE.fd"; + default = ""; + }; + }; + }; + }; + + os_type = lib.mkOption { + type = lib.types.submodule { + options = { + arch = lib.mkOption { + type = lib.types.str; + example = "x86_64"; + default = ""; + }; + machine = lib.mkOption { + type = lib.types.str; + example = "pc-q35-3.1"; + default = ""; + }; + value = lib.mkOption { + type = lib.types.str; + example = "hvm"; + default = ""; + }; + }; + }; + }; - # General metadata - name = mkOption { - type = types.str; - example = "MyGuest"; - }; + os = lib.mkOption { + description = "os"; + type = lib.types.submodule { + options = { + type = os_type; + loader = os_loader; + nvram = os_nvram; + boot = os_boot; + }; + }; + }; - uuid = mkOption { - type = types.str; - example = "3e3fce45-4f53-4fa7-bb32-11f34168b82b"; - }; + resource = lib.mkOption { + description = "resource"; + type = lib.types.submodule { + options = { + partition = lib.mkOption { + type = lib.types.str; + example = "/machine"; + default = ""; + }; + }; + }; + }; - genid = mkOption { - type = types.str; - example = "43dc0cf8-809b-4adb-9bea-a9abb5f3d90e"; - }; + vcpu = lib.mkOption { + description = "vcpu"; + type = lib.types.submodule { + options = { + placement = lib.mkOption { + # TODO(emile): fill up + type = lib.types.enum [ "static" ]; + example = "static"; + default = "static"; + }; + count = lib.mkOption { + type = lib.types.int; + example = 4; + default = 1; + }; + }; + }; + }; - title = mkOption { - type = types.str; - example = "A short description - title - of the domain"; - }; + mem = lib.mkOption { + description = "memory"; + type = lib.types.submodule { + options = { + unit = lib.mkOption { + # TODO(emile): fill up + type = lib.types.enum [ + "KiB" + "MiB" + "GiB" + "TiB" + ]; + example = "KiB"; + default = "KiB"; + }; + value = lib.mkOption { + type = lib.types.int; + example = 2097152; + default = 1000; + }; + }; + }; + }; - description = mkOption { - type = types.str; - example = "Some human readable description"; - }; + memory = mem; + currentMemory = mem; + + metadata = lib.mkOption { + description = "metadata submodule"; + type = lib.types.submodule { + options = { + libosinfo = lib.mkOption { + type = lib.types.str; + example = "https://libosinfo.org/xmlns/libvirt/domain/1.0"; + default = ""; + }; + libosinfo_os = lib.mkOption { + type = lib.types.str; + example = "https://nixos.org/nixos/unknown"; + default = ""; + }; + }; + }; + }; - metadata = mkOption { - type = types.str; - default = ""; - example = '' - <metadata> - <app1:foo xmlns:app1="http://app1.org/app1/">..</app1:foo> - <app2:bar xmlns:app2="http://app1.org/app2/">..</app2:bar> - </metadata> - ''; - }; + vm = lib.types.submodule { + options = { + vm_type = stringOption { default = "kvm"; }; + id = stringOption { }; + name = stringOption { }; + uuid = stringOption { }; + + inherit + metadata + memory + currentMemory + vcpu + resource + os + features + cpu + ; + }; + }; + in + { + enable = lib.mkEnableOption "Enable r2wars-web"; + + + # Temporary output we write the domain to, in order to inspect the correctness of + # the generated xml + output.domain = lib.mkOption { type = lib.types.path; }; + + # An attrset of VMs + vm = lib.mkOption { + description = "VMs to define"; + type = lib.types.attrsOf vm; + }; + }; + }; - os = mkOption { - type = types.submodule { - options = { - firmware = mkOption { - type = types.enum [ - "bios" - "efi" + config = lib.mkIf config.services.emile.libvirtnix.enable { + services.emile.libvirtnix = + let + vm = config.services.emile.libvirtnix.vm; + + cpu = + vm_name: + mkTag { + name = "cpu"; + args = let + mode = vm.${vm_name}.cpu.mode; + check = vm.${vm_name}.cpu.check; + migratable= vm.${vm_name}.cpu.migratable; + in [ + (if (mode != null) + then {key = "mode"; val = mode;} + else "") + (if (check != null) + then {key = "check"; val = check;} + else "") + (if (migratable != null) + then {key = "migratable"; val = migratable;} + else "") ]; - default = "efi"; - example = "bios"; + closing = false; }; - type = mkOption { - type = types.enum [ - "hvm" - "linux" + + features = + vm_name: + mkTag { + name = "features"; + children = let + acpi = vm_name: mkTag { + name = "acpi"; + closing = false; + }; + apic = vm_name: mkTag { + name = "apic"; + closing = false; + }; + vmport = vm_name: mkTag { + name = "vmport"; + args = [ + { key = "state"; val = vm.${vm_name}.features.vmport.state; } + ]; + closing = false; + }; + in [ + (if (vm.${vm_name}.features.acpi != false) then (acpi vm_name) else "") + (if (vm.${vm_name}.features.apic != false) then (apic vm_name) else "") + (if (vm.${vm_name}.features.vmport != null) then (vmport vm_name) else "") ]; - default = "hvm"; - example = "linux"; }; - arch = mkOption { type = types.str; }; - machine = mkOption { type = types.str; }; - - # TODO(emile): features - # Search for the following... - # > When using firmware auto-selection there are different features enabled in the firmwares - # ... here: https://libvirt.org/formatdomain.html - # can't bother to implement this now - # features = mkOption { - # type = types.submodule { - # options = { - # enabled = { - # type = types.enum [ "yes" "no" ]; - # }; - # name = {}; - # }; - # }; - # }; - - # such as: - # <loader readonly='yes' secure='yes' type='pflash'>/usr/share/OVMF/OVMF_CODE.fd</loader> - loader = mkOption { - type = types.submodule { - options = { - readonly = mkOption { - type = types.enum [ - "yes" - "no" - ]; - }; - type = mkOption { - type = types.enum [ - "rom" - "pflash" - ]; - }; - secure = mkOption { - type = types.enum [ - "yes" - "no" - ]; - }; - stateless = mkOption { - type = types.enum [ - "yes" - "no" - ]; - }; - format = mkOption { - type = types.enum [ - "raw" - "qcow2" - ]; - }; - value = mkOption { - type = types.str; - example = "/usr/share/OVMF/OVMF_CODE.fd"; - }; + os = + vm_name: + mkTag { + name = "os"; + children = let + os_type = vm_name: mkTag { + name = "type"; + args = [ + { key = "arch"; val = vm.${vm_name}.os.type.arch; } + { key = "machine"; val = vm.${vm_name}.os.type.machine; } + ]; + value = vm.${vm_name}.os.type.value; }; - }; + os_loader = vm_name: mkTag { + name = "loader"; + args = [ + { key = "readonly"; val = vm.${vm_name}.os.loader.readonly; } + { key = "pflash"; val = vm.${vm_name}.os.loader.type; } + ]; + value = vm.${vm_name}.os.loader.value; + }; + os_nvram = vm_name: mkTag { + name = "nvram"; + value = vm.${vm_name}.os.nvram.value; + }; + os_boot = vm_name: mkTag { + name = "type"; + args = [ + { key = "dev"; val = vm.${vm_name}.os.boot.dev; } + ]; + closing = false; + }; + in [ + (os_type vm_name) + (os_loader vm_name) + (os_nvram vm_name) + (os_boot vm_name) + ]; }; - # <nvram type='network'> - # <source protocol='iscsi' name='iqn.2013-07.com.example:iscsi-nopool/0'> - # <host name='example.com' port='6000'/> - # <auth username='myname'> - # <secret type='iscsi' usage='mycluster_myname'/> - # </auth> - # </source> - # </nvram> - - # nvram = { - # type = "network"; - # source = { - # protocol = "iscsi"; - # name = "iqn.2013-07.com.example:iscsi-nopool/0"; - # }; - # }; - - nvram = mkOption { - type = types.submodule { - options = { - type = mkOption { - type = types.enum [ - "file" - "block" - "dir" - "network" - "volume" - "nvme" - "vhostuser" - "vhostvdpa" - ]; - default = ""; - }; - - source = mkOption { - type = types.submodule { - options = { - # TODO(emile): figure out how to conditionally allow setting the options below - # if the type defined above has been set - - # if type == file - file = mkOption { type = types.str; }; - fdgroup = mkOption { type = types.str; }; - - # if type == block - dev = mkOption { type = types.str; }; - - # if type == dir - dir = mkOption { type = types.str; }; - - # if type == network - protocol = mkOption { - type = types.enum [ - "nbd" - "iscsi" - "rbd" - "sheepdog" - "gluster" - "vxhs" - "nfs" - "http" - "https" - "ftp" - "ftps" - "tftp" - "ssh" - ]; - }; - name = mkOption { type = types.str; }; - tls = mkOption { - type = types.enum [ - "yes" - "no" - ]; - }; - tlsHostname = mkOption { type = types.str; }; - query = mkOption { type = types.str; }; - - # if type == volume - pool = mkOption { type = types.str; }; - volume = mkOption { type = types.str; }; - mode = mkOption { - type = types.enum [ - "direct" - "host" - ]; - default = "host"; - }; - - # if type == nvme - type = mkOption { - type = types.enum [ "pci" ]; - default = "pci"; - description = '' - When the type is `nvme`, only `pci` is supported - When the type is `vhostuser`, only `unix` is supported - - (I've got not clue how to model this is nix, it's essentially an attribute that is overloaded based on another value) - ''; - }; - managed = mkOption { - type = types.enum [ - "yes" - "no" - ]; - }; - namespace = mkOption { - type = types.int; - default = 0; - }; - - # if type == vhostuser - # type = see the type in the nvme section above - path = mkOption { type = types.str; }; - - # if type == vhostvdpa - # dev = (defined above in the "type == block" section) - - # if type == file - # if type == block - # if type == volume - seclabel = mkOption { type = types.str; }; - - index = mkOption { - type = types.int; - default = 0; - }; - - # TODO(emile): implement checks here - # start here and scroll down a bit to the table - # https://libvirt.org/formatdomain.html#hard-drives-floppy-disks-cdroms - # if type == network - host = mkOption { - type = types.submodule { - options = { - name = mkOption { type = types.str; }; - port = mkOption { - type = types.int; - default = 0; - }; - transport = mkOption { type = types.str; }; - socket = mkOption { type = types.str; }; - - }; - }; - }; - - snapshot = mkOption { - type = types.submodule { - options = { - name = mkOption { type = types.str; }; - }; - }; - }; - - config = mkOption { - type = types.submodule { - options = { - file = mkOption { type = types.str; }; - }; - }; - }; - - # Since 3.9.0, the auth element is supported for a disk type "network" that is using a source element with the protocol attributes "rbd", "iscsi", or "ssh". - auth = mkOption { - type = types.submodule { - options = { - type = mkOption { - # type = types.enum [ "chap" ]; - # freeform, yet "chap" is one of the allowed options, I don't know others (yet) - type = types.str; - }; - username = mkOption { type = types.str; }; - - # https://libvirt.org/formatsecret.html - secret = mkOption { - type = types.submodule { - options = { - ephemeral = mkOption { - type = types.enum [ - "yes" - "no" - ]; - default = "no"; - }; - private = mkOption { - type = types.enum [ - "yes" - "no" - ]; - default = "no"; - }; - - uuid = types.submodule { - options = { - value = mkOption { - type = types.str; - default = ""; - }; - }; - }; - description = types.submodule { - options = { - value = mkOption { - type = types.str; - default = ""; - }; - }; - }; - - usage = types.submodule { - options = { - type = mkOption { - type = types.enum [ - "volume" - "ceph" - "iscsi" - "tls" - "vtpm" - ]; - default = ""; - }; - value = mkOption { - type = types.str; - default = ""; - }; - - name = mkOption { - type = types.submodule { - options = { - value = mkOption { type = types.str; }; - }; - }; - }; - - volume = mkOption { - type = types.submodule { - options = { - value = mkOption { type = types.str; }; - }; - }; - }; - - # when using the "iscsi" type - target = mkOption { - type = types.submodule { - options = { - value = mkOption { type = types.str; }; - }; - }; - }; - - }; - }; - - }; - }; - }; # end of secret - - }; - }; - }; # end of auth - - # https://libvirt.org/formatstorageencryption.html - encryption = mkOption { - type = types.submodule { - options = { - type = mkOption { type = types.str; }; - - # mandatory - format = mkOption { - type = types.enum [ - "default" - "qcow" - "luks" - "luks2" - "luks-any" - ]; - }; - - engine = mkOption { - type = types.enum [ - "qemu" - "librbd" - ]; - }; - - secrets = mkOption { - type = types.listOf (mkOption { - type = types.submodule { - options = { - - # mandatory - type = mkOption { type = types.enum [ "volume" ]; }; - - # uuid or usage - uuid = mkOption { type = types.str; }; - usage = mkOption { type = types.str; }; - - }; - }; - }); - }; # end of secrets - - cipher = mkOption { - type = types.submodule { - options = { - name = mkOption { - type = types.str; - example = "'aes', 'des', 'cast5', 'serpent', 'twofish', etc."; - }; - size = mkOption { - type = types.str; - example = "'256', '192', '128', etc."; - }; - mode = mkOption { - type = types.str; - example = "'cbc', 'xts', 'ecb', etc."; - }; - hash = mkOption { - type = types.str; - example = "'md5', 'sha1', 'sha256', etc."; - }; - }; - }; - }; # end of cipher - - ivgen = mkOption { - type = types.submodule { - options = { - name = mkOption { - type = types.str; - example = "'plain', 'plain64', 'essiv', etc."; - }; - hash = mkOption { - type = types.str; - example = "'md5', 'sha1', 'sha256'"; - }; - }; - }; - }; # end of ivygen - }; - }; - }; # end of encryption - - # TODO(emile): reservations - # Looking at the following, it seems like this can use the source element recursively - # Haven't looked into how to define recursive nix options yet... - # https://github.com/virt-manager/virt-manager/blob/5ddd3456a0ca9836a98fc6ca4f0b2eaab268bf47/tests/data/cli/compare/virt-install-many-devices.xml#L398-L400 - - # TODO(emile): initiator - - # Based on this here: - # https://github.com/virt-manager/virt-manager/blob/5ddd3456a0ca9836a98fc6ca4f0b2eaab268bf47/tests/data/cli/compare/virt-install-many-devices.xml#L440 - address = mkOption { - type = types.submodule { - options = { - domain = mkOption { type = types.int; }; - bus = mkOption { type = types.int; }; - slot = mkOption { type = types.int; }; - function = mkOption { type = types.int; }; - }; - }; - }; # end of address - - # TODO(emile): slices - # Didn't find any usage of it, why is there documentation for it then? - - ssl = mkOption { - type = types.submodule { - options = { - verify = mkOption { - type = types.enum [ - "yes" - "no" - ]; - }; - }; - }; - }; # end of ssl - - # TODO(emile): cookies for http and https - - readahead = mkOption { - type = types.submodule { - options = { - size = mkOption { type = types.int; }; - }; - }; - }; # end of readahead - - timeout = mkOption { - type = types.submodule { - options = { - seconds = mkOption { type = types.int; }; - }; - }; - }; # end of timeout - - identity = mkOption { - type = types.submodule { - options = { - user = mkOption { type = types.str; }; - group = mkOption { type = types.str; }; - - # if ssh - # required - username = mkOption { type = types.str; }; - - # one of these: - agentsock = mkOption { type = types.str; }; - keyfile = mkOption { type = types.str; }; - }; - }; - }; # end of identity - - # disk type "vhostuser" - reconnect = mkOption { - type = types.submodule { - options = { - # mandatory - enabled = mkOption { - type = types.enum [ - "yes" - "no" - ]; - }; - timeout = mkOption { - type = types.int; - description = "seconds"; - }; - - # optional for disk type network and protocol nbd - delay = mkOption { - type = types.int; - default = 0; - description = "seconds"; - }; - }; - }; - }; # end of reconnect - - # disk type "ssh" - knownHosts = mkOption { - type = types.submodule { - options = { - path = mkOption { type = types.str; }; - }; - }; - }; # end of knownHosts - - dataStore = mkOption { - type = types.submodule { - options = { - # TODO(emile): can accept the same types as a `source` element - type = mkOption { type = types.str; }; - - # TODO(emile): can accept format and source subelements - # this is (once again) recursive for the source, stil need to figure - # out how to handle this, just not now - }; - }; - }; # end of dataStore + resource = + vm_name: + mkTag { + name = "resource"; + children = [ + (mkTag { + name = "partition"; + value = "${toString vm.${vm_name}.resource.partition}"; + }) + ]; + }; - startupPolicy = mkOption { - type = types.enum [ - "mandatory" - "requisite" - "optional" - ]; - default = "mandatory"; - }; # end of startupPolicy + vcpu = + vm_name: + mkTag { + name = "vcpu"; + args = [ + { + key = "placement"; + val = vm.${vm_name}.vcpu.placement; + } + ]; + value = "${toString vm.${vm_name}.vcpu.count}"; + }; - backingStore = mkOption { - type = types.submodule { - options = { - type = mkOption { - # TODO(emile): can accept the same types as a `source` element - type = types.str; - }; + currentMemory = + vm_name: + mkTag { + name = "currentMemory"; + args = [ + { + key = "unit"; + val = vm.${vm_name}.currentMemory.unit; + } + ]; + value = "${toString vm.${vm_name}.currentMemory.value}"; + }; + memory = + vm_name: + mkTag { + name = "memory"; + args = [ + { + key = "unit"; + val = vm.${vm_name}.memory.unit; + } + ]; + value = "${toString vm.${vm_name}.memory.value}"; + }; - index = mkOption { - # TODO(emile): figure out if this can just be an int - type = types.str; - }; + libosinfo_os = + vm_name: + mkTag { + name = "libosinfo:os"; + args = [ + { + key = "id"; + val = vm.${vm_name}.metadata.libosinfo_os; + } + ]; + closing = false; + }; - # TODO(emile): can use the following sub elements: - # - format - # - source - # - backingStore - }; - }; - }; # end of backingStore + libosinfo_libosinfo = + vm_name: + mkTag { + name = "libosinfo:libosinfo"; + args = [ + { + key = "xmlns:libosinfo"; + val = vm.${vm_name}.metadata.libosinfo; + } + ]; + children = [ + (libosinfo_os vm_name) + ]; + }; - mirror = mkOption { - type = types.submodule { - options = { - api = mkOption { - type = types.enum [ - "copy" - "active-commit" - ]; - }; - ready = mkOption { - type = types.enum [ - "yes" - "abort" - "pivot" - ]; - }; - # TODO(emile): can use the following sub elements: - # - type (disk types) - # - format - # - source - # - file - }; - }; - }; # end of mirror + metadata = + vm_name: + mkTag { + name = "metadata"; + children = [ (libosinfo_libosinfo vm_name) ]; + }; - }; - }; - }; # end of source + uuid = + vm_name: + mkTag { + name = "uuid"; + value = vm.${vm_name}.uuid; + }; - }; - }; - }; # end of nvram + name = + vm_name: + mkTag { + name = "name"; + value = vm.${vm_name}.name; + }; - }; + # the vm_name has to be passed in, as we want to accesses the value for the given vm when + # generating the config + domain = + vm_name: + mkTag { + name = "domain"; + args = [ + { + key = "type"; + val = vm.${vm_name}.vm_type; + } + { + key = "id"; + val = vm.${vm_name}.id; + } + ]; + children = [ + (name vm_name) + (uuid vm_name) + (metadata vm_name) + (memory vm_name) + (currentMemory vm_name) + (vcpu vm_name) + (resource vm_name) + (os vm_name) + (features vm_name) + (cpu vm_name) + ]; + }; + in + { + output.domain = pkgs.writeText "libvirt-domain-config.xml" (domain "alan"); }; - }; - - memory = lib.mkOption { - type = lib.types.int; - default = 1024; - example = 2048; - description = '' - The amount of memory to provide to the VM - ''; - }; }; } diff --git a/nix/modules/libvirtnix/test.nix b/nix/modules/libvirtnix/test.nix new file mode 100644 index 0000000..8542b7e --- /dev/null +++ b/nix/modules/libvirtnix/test.nix @@ -0,0 +1,10 @@ +let + pkgs = import <nixpkgs> {}; +in + pkgs.lib.evalModules { + modules = [ + # ./secret.nix + ./domain.nix + ./config.nix + ]; + } diff --git a/nix/modules/libvirtnix/xml.nix b/nix/modules/libvirtnix/xml.nix new file mode 100644 index 0000000..134a878 --- /dev/null +++ b/nix/modules/libvirtnix/xml.nix @@ -0,0 +1,45 @@ +{ lib, ... }: + +# takes a few args and creats a valid xml tag pair out of it +# +# testTag = mkTag { +# name = "name"; +# args = [ +# { +# key = "arg1"; +# val = "arg1val"; +# } +# { +# key = "arg2"; +# val = "arg2val"; +# } +# ]; +# value = "qwe"; +# children = [ +# (mkTag { name = "nested"; args = []; value = "qwe"; children = [];}) +# ]; +# }; +# +# <name arg1=arg1val arg2=arg2val> +# value +# {children} +# </name> +{ + name, # name of the tag to be used, such as `secret`, `description`, ... + args ? [ ], # args, [ { key="a"; val="b"; } { key="c"; val="d"; } ] + value ? "", # the value to place in the middle + children ? [ ], # the child elements + closing ? true, # add a closing tag +}: +let + args_str = + " " + lib.strings.concatStrings (lib.strings.intersperse " " (map (x: "${x.key}='${x.val}'") args)); + child_evaled = lib.strings.concatStrings children; + + cond = condition: value: if condition then value else ""; + + closingTag = if closing == true then "</${name}>" else ""; +in +"<${name}${ + lib.optionalString (args != [ ]) args_str +}${cond (closing == false) "/"}>${value}${child_evaled}${lib.optionalString (closing) closingTag}" |