about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEmile <git@emile.space>2025-02-11 00:01:35 +0100
committerEmile <git@emile.space>2025-02-11 00:01:35 +0100
commit951726c6ee34d3b9733d739dba3c4ee3c873bc7b (patch)
treed833bcde3dbb04041d7645750f9d009774e5add4
parent906026e924e3aac0f0ed32ec8ab2f7468e8e1c0a (diff)
corrino: libvirtnix, formatted
-rw-r--r--nix/hosts/corrino/vm.nix77
-rw-r--r--nix/modules/libvirtnix/default.nix87
-rw-r--r--nix/modules/libvirtnix/domain.nix1211
3 files changed, 751 insertions, 624 deletions
diff --git a/nix/hosts/corrino/vm.nix b/nix/hosts/corrino/vm.nix
index 7fcf4af..37d1356 100644
--- a/nix/hosts/corrino/vm.nix
+++ b/nix/hosts/corrino/vm.nix
@@ -1,43 +1,42 @@
 { pkgs, ... }:
 
 {
-	services.emile.libvirtnix = {
-		enable = true;
-		instances = {
-
-			vm1 = {
-				domain = {
-					name = "VM1";
-					title = "vm one";
-					description = "The first VM";
-					id = 1;
-
-					uuid = "E34DE478-1402-45BB-B3FD-FC960549258E";
-					genid = "CA1E2462-1E9D-404C-8DDB-19EEF9D9651B";
-
-					packages = {
-						libvirt = pkgs.libvirt;
-						qemu = pkgs.qemu;
-					};
-					memory = 1024;
-				};
-			};
-
-			vm2 = {
-				domain = {
-					name = "VM2";
-					title = "vm one";
-					description = "The second VM";
-					id = 2;
-
-					uuid = "E34DE478-1402-45BB-B3FD-FC960549258E";
-					genid = "002D0D8F-B21A-4001-92BF-2313707EED9D";
-
-					memory = 2048;
-				};
-			};
-
-		};
-	};
+  services.emile.libvirtnix = {
+    enable = true;
+    instances = {
+
+      vm1 = {
+        domain = {
+          name = "VM1";
+          title = "vm one";
+          description = "The first VM";
+          id = 1;
+
+          uuid = "E34DE478-1402-45BB-B3FD-FC960549258E";
+          genid = "CA1E2462-1E9D-404C-8DDB-19EEF9D9651B";
+
+          packages = {
+            libvirt = pkgs.libvirt;
+            qemu = pkgs.qemu;
+          };
+          memory = 1024;
+        };
+      };
+
+      vm2 = {
+        domain = {
+          name = "VM2";
+          title = "vm one";
+          description = "The second VM";
+          id = 2;
+
+          uuid = "E34DE478-1402-45BB-B3FD-FC960549258E";
+          genid = "002D0D8F-B21A-4001-92BF-2313707EED9D";
+
+          memory = 2048;
+        };
+      };
+
+    };
+  };
 }
-
diff --git a/nix/modules/libvirtnix/default.nix b/nix/modules/libvirtnix/default.nix
index 4689979..a998966 100644
--- a/nix/modules/libvirtnix/default.nix
+++ b/nix/modules/libvirtnix/default.nix
@@ -7,31 +7,36 @@
 
 let
   cfg = config.services.emile.libvirtnix;
-in {
+in
+{
   options.services.emile.libvirtnix = with lib; {
     enable = mkEnableOption "enable libvirtnix";
-    instances = 
-      mkOption {
-        type = types.attrsOf (types.submodule ({ name, ... }: {
-          options = {
-            enable = mkEnableOption "enable this instance";
-            domain = mkOption {
-              type = types.submodule (import ./domain.nix);
-            };
-          };
-        }));
-        default = {};
-        description = ''
-          A full libvirt config, statically defined using nix.
-        '';
-        example = ''
+    instances = mkOption {
+      type = types.attrsOf (
+        types.submodule (
+          { name, ... }:
           {
-            domain = {
-              name = "vm";
+            options = {
+              enable = mkEnableOption "enable this instance";
+              domain = mkOption {
+                type = types.submodule (import ./domain.nix);
+              };
             };
           }
-        '';
-      };
+        )
+      );
+      default = { };
+      description = ''
+        A full libvirt config, statically defined using nix.
+      '';
+      example = ''
+        {
+          domain = {
+            name = "vm";
+          };
+        }
+      '';
+    };
   };
 
   config = lib.mkIf cfg.enable {
@@ -47,8 +52,8 @@ in {
         };
         script =
           let
-            xml = pkgs.writeText "libvirt-guest-${name}.xml" ''
-              <domain ''
+            xml =
+              pkgs.writeText "libvirt-guest-${name}.xml" ''<domain ''
               + (lib.optionalString (guest.domain.type != "") "type='${guest.domain.type}' ")
               + (lib.optionalString (guest.domain.id != 0) "id='${guest.domain.type}'")
               + ''>''
@@ -57,26 +62,28 @@ in {
               + (lib.optionalString (guest.domain.uuid != "") "<uuid>${guest.domain.uuid}</uuid>")
               + (lib.optionalString (guest.domain.genid != "") "<genid>${guest.domain.genid}</genid>")
               + (lib.optionalString (guest.domain.title != "") "<title>${guest.domain.title}</title>")
-              + (lib.optionalString (guest.domain.description != "") "<description>${guest.domain.description}</description>")
+              + (lib.optionalString (
+                guest.domain.description != ""
+              ) "<description>${guest.domain.description}</description>")
               + (lib.optionalString (guest.domain.metadata != "") "${guest.domain.metadata}")
               + ''
-                <os>
-                  <type>hvm</type>
-                </os>
-                <memory unit="GiB">${toString guest.domain.memory}</memory>
-                <devices>
-                  <disk type="volume">
-                    <source volume="guest-${name}"/>
-                    <target dev="vda" bus="virtio"/>
-                  </disk>
-                  <graphics type="spice" autoport="yes"/>
-                  <input type="keyboard" bus="usb"/>
-                </devices>
-                <features>
-                  <acpi/>
-                </features>
-              </domain>
-            '';
+                  <os>
+                    <type>hvm</type>
+                  </os>
+                  <memory unit="GiB">${toString guest.domain.memory}</memory>
+                  <devices>
+                    <disk type="volume">
+                      <source volume="guest-${name}"/>
+                      <target dev="vda" bus="virtio"/>
+                    </disk>
+                    <graphics type="spice" autoport="yes"/>
+                    <input type="keyboard" bus="usb"/>
+                  </devices>
+                  <features>
+                    <acpi/>
+                  </features>
+                </domain>
+              '';
           in
           ''
             uuid="$(${pkgs.libvirt}/bin/virsh domuuid '${name}' || true)"
diff --git a/nix/modules/libvirtnix/domain.nix b/nix/modules/libvirtnix/domain.nix
index 027edda..a028c7d 100644
--- a/nix/modules/libvirtnix/domain.nix
+++ b/nix/modules/libvirtnix/domain.nix
@@ -1,31 +1,37 @@
 { pkgs, lib, ... }:
 
 {
-	options = with lib; {
+  options = with lib; {
 
-		# meta, this allows defining the package used for each vm individually, defaults should be sane
+    # 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 = {
+          libvirt = mkPackageOption pkgs "libvirt" { };
+          qemu = mkPackageOption pkgs "qemu" { };
+        };
+      };
     };
 
-		# 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
+    # 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
+        				allowed values are driver specific, if missing, plz send PR
 
-				hvm since since 8.1.0 and QEMU 2.12
-			'';
-		};
+        				hvm since since 8.1.0 and QEMU 2.12
+        			'';
+    };
 
     id = mkOption {
       type = types.int;
@@ -33,540 +39,655 @@
       example = 42;
     };
 
-		# General metadata
+    # General metadata
     name = mkOption {
       type = types.str;
       example = "MyGuest";
     };
 
-		uuid = mkOption {
-			type = types.str;
-			example = "3e3fce45-4f53-4fa7-bb32-11f34168b82b";
-		};
-
-		genid = mkOption {
-			type = types.str;
-			example = "43dc0cf8-809b-4adb-9bea-a9abb5f3d90e";
-		};
-
-		title = mkOption {
-			type = types.str;
-			example = "A short description - title - of the domain";
-		};
-
-		description = mkOption {
-			type = types.str;
-			example = "Some human readable description";
-		};
-
-		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>
-			'';
-		};
+    uuid = mkOption {
+      type = types.str;
+      example = "3e3fce45-4f53-4fa7-bb32-11f34168b82b";
+    };
+
+    genid = mkOption {
+      type = types.str;
+      example = "43dc0cf8-809b-4adb-9bea-a9abb5f3d90e";
+    };
+
+    title = mkOption {
+      type = types.str;
+      example = "A short description - title - of the domain";
+    };
+
+    description = mkOption {
+      type = types.str;
+      example = "Some human readable description";
+    };
+
+    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>
+        			'';
+    };
 
     os = mkOption {
-			type = types.submodule {
-				options = {
-					firmware = mkOption {
-						type = types.enum [ "bios" "efi" ];
-						default = "efi";
-						example = "bios";
-					};
-					type = mkOption {
-						type = types.enum [ "hvm" "linux" ];
-						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";
-								};
-							};
-						};
-					};
-
-				  # <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
-
-											startupPolicy = mkOption {
-												type = types.enum [ "mandatory" "requisite" "optional" ];
-												default = "mandatory";
-											}; # end of startupPolicy
-											
-											backingStore = mkOption {
-												type = types.submodule {
-													options = {
-														type = mkOption {
-															# TODO(emile): can accept the same types as a `source` element
-															type = types.str;
-														};
-
-														index = mkOption {
-															# TODO(emile): figure out if this can just be an int
-															type = types.str;
-														};
-
-														# TODO(emile): can use the following sub elements:
-														# - format
-														# - source
-														# - backingStore
-													};
-												};
-											}; # end of backingStore
-
-											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
-
-
-										};
-									};
-								}; # end of source
-
-							};
-						};
-					}; # end of nvram
-
-				};
-			};
-		};
+      type = types.submodule {
+        options = {
+          firmware = mkOption {
+            type = types.enum [
+              "bios"
+              "efi"
+            ];
+            default = "efi";
+            example = "bios";
+          };
+          type = mkOption {
+            type = types.enum [
+              "hvm"
+              "linux"
+            ];
+            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";
+                };
+              };
+            };
+          };
+
+          # <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
+
+                      startupPolicy = mkOption {
+                        type = types.enum [
+                          "mandatory"
+                          "requisite"
+                          "optional"
+                        ];
+                        default = "mandatory";
+                      }; # end of startupPolicy
+
+                      backingStore = mkOption {
+                        type = types.submodule {
+                          options = {
+                            type = mkOption {
+                              # TODO(emile): can accept the same types as a `source` element
+                              type = types.str;
+                            };
+
+                            index = mkOption {
+                              # TODO(emile): figure out if this can just be an int
+                              type = types.str;
+                            };
+
+                            # TODO(emile): can use the following sub elements:
+                            # - format
+                            # - source
+                            # - backingStore
+                          };
+                        };
+                      }; # end of backingStore
+
+                      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
+
+                    };
+                  };
+                }; # end of source
+
+              };
+            };
+          }; # end of nvram
+
+        };
+      };
+    };
 
     memory = lib.mkOption {
       type = lib.types.int;