about summary refs log tree commit diff
path: root/nix/modules/libvirtnix
diff options
context:
space:
mode:
Diffstat (limited to 'nix/modules/libvirtnix')
-rw-r--r--nix/modules/libvirtnix/a.xml259
-rw-r--r--nix/modules/libvirtnix/b.xml183
-rw-r--r--nix/modules/libvirtnix/config.nix524
-rw-r--r--nix/modules/libvirtnix/domain.nix1106
-rw-r--r--nix/modules/libvirtnix/os.nix119
-rw-r--r--nix/modules/libvirtnix/secret.nix174
-rw-r--r--nix/modules/libvirtnix/xml.nix23
7 files changed, 2310 insertions, 78 deletions
diff --git a/nix/modules/libvirtnix/a.xml b/nix/modules/libvirtnix/a.xml
new file mode 100644
index 0000000..1255ad4
--- /dev/null
+++ b/nix/modules/libvirtnix/a.xml
@@ -0,0 +1,259 @@
+<domain type='kvm' id='5'>
+  <name>fileserver2</name>
+  <uuid>d62e0276-d474-452b-80f4-c951c39bcf74</uuid>
+  <metadata>
+    <libosinfo:libosinfo xmlns:libosinfo='http://libosinfo.org/xmlns/libvirt/domain/1.0'>
+      <libosinfo:os id='http://nixos.org/nixos/unknown'/>
+    </libosinfo:libosinfo>
+  </metadata>
+  <memory unit='KiB'>2097152</memory>
+  <currentMemory unit='KiB'>2097152</currentMemory>
+  <vcpu placement='static'>3</vcpu>
+  <resource>
+    <partition>/machine</partition>
+  </resource>
+  <os>
+    <type arch='x86_64' machine='pc-q35-3.1'>hvm</type>
+    <loader readonly='yes' type='pflash'>/usr/share/OVMF/OVMF_CODE.fd</loader>
+    <nvram>/var/lib/libvirt/qemu/nvram/fileserver2_VARS.fd</nvram>
+    <boot dev='hd'/>
+  </os>
+  <features>
+    <acpi/>
+    <apic/>
+    <vmport state='off'/>
+  </features>
+  <cpu mode='host-passthrough' check='none' migratable='on'/>
+  <clock offset='utc'>
+    <timer name='rtc' tickpolicy='catchup'/>
+    <timer name='pit' tickpolicy='delay'/>
+    <timer name='hpet' present='no'/>
+  </clock>
+  <on_poweroff>destroy</on_poweroff>
+  <on_reboot>restart</on_reboot>
+  <on_crash>destroy</on_crash>
+  <pm>
+    <suspend-to-mem enabled='no'/>
+    <suspend-to-disk enabled='no'/>
+  </pm>
+  <devices>
+    <emulator>/usr/bin/qemu-system-x86_64</emulator>
+    <disk type='block' device='disk'>
+      <driver name='qemu' type='raw' cache='none' io='native' discard='unmap'/>
+      <source dev='/dev/vmstorage/fileserver2' index='3'/>
+      <backingStore/>
+      <target dev='vda' bus='virtio'/>
+      <alias name='virtio-disk0'/>
+      <address type='pci' domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
+    </disk>
+    <disk type='block' device='disk'>
+      <driver name='qemu' type='raw'/>
+      <source dev='/dev/mapper/storage1' index='2'/>
+      <backingStore/>
+      <target dev='vdb' bus='virtio'/>
+      <alias name='virtio-disk1'/>
+      <address type='pci' domain='0x0000' bus='0x08' slot='0x00' function='0x0'/>
+    </disk>
+    <disk type='block' device='disk'>
+      <driver name='qemu' type='raw'/>
+      <source dev='/dev/mapper/storage2' index='1'/>
+      <backingStore/>
+      <target dev='vdc' bus='virtio'/>
+      <alias name='virtio-disk2'/>
+      <address type='pci' domain='0x0000' bus='0x09' slot='0x00' function='0x0'/>
+    </disk>
+    <controller type='usb' index='0' model='qemu-xhci' ports='15'>
+      <alias name='usb'/>
+      <address type='pci' domain='0x0000' bus='0x02' slot='0x00' function='0x0'/>
+    </controller>
+    <controller type='pci' index='0' model='pcie-root'>
+      <alias name='pcie.0'/>
+    </controller>
+    <controller type='pci' index='1' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='1' port='0x10'/>
+      <alias name='pci.1'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0' multifunction='on'/>
+    </controller>
+    <controller type='pci' index='2' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='2' port='0x11'/>
+      <alias name='pci.2'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x1'/>
+    </controller>
+    <controller type='pci' index='3' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='3' port='0x12'/>
+      <alias name='pci.3'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x2'/>
+    </controller>
+    <controller type='pci' index='4' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='4' port='0x13'/>
+      <alias name='pci.4'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x3'/>
+    </controller>
+    <controller type='pci' index='5' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='5' port='0x14'/>
+      <alias name='pci.5'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x4'/>
+    </controller>
+    <controller type='pci' index='6' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='6' port='0x15'/>
+      <alias name='pci.6'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x5'/>
+    </controller>
+    <controller type='pci' index='7' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='7' port='0x16'/>
+      <alias name='pci.7'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x6'/>
+    </controller>
+    <controller type='pci' index='8' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='8' port='0x17'/>
+      <alias name='pci.8'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x7'/>
+    </controller>
+    <controller type='pci' index='9' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='9' port='0x18'/>
+      <alias name='pci.9'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0' multifunction='on'/>
+    </controller>
+    <controller type='pci' index='10' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='10' port='0x19'/>
+      <alias name='pci.10'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x1'/>
+    </controller>
+    <controller type='pci' index='11' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='11' port='0x1a'/>
+      <alias name='pci.11'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x2'/>
+    </controller>
+    <controller type='pci' index='12' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='12' port='0x1b'/>
+      <alias name='pci.12'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x3'/>
+    </controller>
+    <controller type='pci' index='13' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='13' port='0x1c'/>
+      <alias name='pci.13'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x4'/>
+    </controller>
+    <controller type='pci' index='14' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='14' port='0x1d'/>
+      <alias name='pci.14'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x5'/>
+    </controller>
+    <controller type='pci' index='15' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='15' port='0x1e'/>
+      <alias name='pci.15'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x6'/>
+    </controller>
+    <controller type='pci' index='16' model='pcie-to-pci-bridge'>
+      <model name='pcie-pci-bridge'/>
+      <alias name='pci.16'/>
+      <address type='pci' domain='0x0000' bus='0x07' slot='0x00' function='0x0'/>
+    </controller>
+    <controller type='sata' index='0'>
+      <alias name='ide'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x1f' function='0x2'/>
+    </controller>
+    <controller type='virtio-serial' index='0'>
+      <alias name='virtio-serial0'/>
+      <address type='pci' domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
+    </controller>
+    <controller type='scsi' index='0' model='lsilogic'>
+      <alias name='scsi0'/>
+      <address type='pci' domain='0x0000' bus='0x10' slot='0x01' function='0x0'/>
+    </controller>
+    <interface type='bridge'>
+      <mac address='52:54:00:bf:ba:88'/>
+      <source network='services' portid='6c3a3512-d823-4521-9043-3a91a65682f5' bridge='br7'/>
+      <target dev='vnet2'/>
+      <model type='virtio'/>
+      <alias name='net0'/>
+      <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
+    </interface>
+    <serial type='pty'>
+      <source path='/dev/pts/3'/>
+      <target type='isa-serial' port='0'>
+        <model name='isa-serial'/>
+      </target>
+      <alias name='serial0'/>
+    </serial>
+    <console type='pty' tty='/dev/pts/3'>
+      <source path='/dev/pts/3'/>
+      <target type='serial' port='0'/>
+      <alias name='serial0'/>
+    </console>
+    <channel type='unix'>
+      <source mode='bind' path='/var/lib/libvirt/qemu/channel/target/domain-5-fileserver2/org.qemu.guest_agent.0'/>
+      <target type='virtio' name='org.qemu.guest_agent.0' state='disconnected'/>
+      <alias name='channel0'/>
+      <address type='virtio-serial' controller='0' bus='0' port='1'/>
+    </channel>
+    <channel type='spicevmc'>
+      <target type='virtio' name='com.redhat.spice.0' state='disconnected'/>
+      <alias name='channel1'/>
+      <address type='virtio-serial' controller='0' bus='0' port='2'/>
+    </channel>
+    <input type='tablet' bus='usb'>
+      <alias name='input0'/>
+      <address type='usb' bus='0' port='1'/>
+    </input>
+    <input type='mouse' bus='ps2'>
+      <alias name='input1'/>
+    </input>
+    <input type='keyboard' bus='ps2'>
+      <alias name='input2'/>
+    </input>
+    <graphics type='spice' port='5901' autoport='yes' listen='127.0.0.1'>
+      <listen type='address' address='127.0.0.1'/>
+    </graphics>
+    <sound model='ich9'>
+      <alias name='sound0'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x1b' function='0x0'/>
+    </sound>
+    <audio id='1' type='spice'/>
+    <video>
+      <model type='qxl' ram='65536' vram='65536' vgamem='16384' heads='1' primary='yes'/>
+      <alias name='video0'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0'/>
+    </video>
+    <redirdev bus='usb' type='spicevmc'>
+      <alias name='redir0'/>
+      <address type='usb' bus='0' port='2'/>
+    </redirdev>
+    <redirdev bus='usb' type='spicevmc'>
+      <alias name='redir1'/>
+      <address type='usb' bus='0' port='3'/>
+    </redirdev>
+    <memballoon model='virtio'>
+      <alias name='balloon0'/>
+      <address type='pci' domain='0x0000' bus='0x05' slot='0x00' function='0x0'/>
+    </memballoon>
+    <rng model='virtio'>
+      <backend model='random'>/dev/urandom</backend>
+      <alias name='rng0'/>
+      <address type='pci' domain='0x0000' bus='0x06' slot='0x00' function='0x0'/>
+    </rng>
+  </devices>
+  <seclabel type='dynamic' model='apparmor' relabel='yes'>
+    <label>libvirt-d62e0276-d474-452b-80f4-c951c39bcf74</label>
+    <imagelabel>libvirt-d62e0276-d474-452b-80f4-c951c39bcf74</imagelabel>
+  </seclabel>
+  <seclabel type='dynamic' model='dac' relabel='yes'>
+    <label>+109:+115</label>
+    <imagelabel>+109:+115</imagelabel>
+  </seclabel>
+</domain>
diff --git a/nix/modules/libvirtnix/b.xml b/nix/modules/libvirtnix/b.xml
new file mode 100644
index 0000000..311e7d5
--- /dev/null
+++ b/nix/modules/libvirtnix/b.xml
@@ -0,0 +1,183 @@
+<domain type="kvm" id="1337">
+  <name>blub</name>
+  <uuid>cafebabe-d474-452b-80f4-c951c39bcf74</uuid>
+  <metadata>
+    <libosinfo:libosinfo xmlns:libosinfo="https://libosinfo.org/xmlns/libvirt/domain/1.0">
+      <libosinfo:os id="https://nixos.org/nixos/unknown"/>
+    </libosinfo:libosinfo>
+  </metadata>
+  <memory unit="KiB">2097152</memory>
+  <currentMemory unit="KiB">2097152</currentMemory>
+  <vcpu placement="static">3</vcpu>
+  <resource>
+    <partition>/machine</partition>
+  </resource>
+  <os>
+    <type arch="x86_64" machine="pc-q35-3.1">hvm</type>
+    <loader readonly="yes" pflash="pflash">/usr/share/OVMF/OVMF_CODE.fd</loader>
+    <nvram>/var/lib/libvirt/qemu/nvram/fileserver2_VARS.fd</nvram>
+    <type dev="hd"/>
+  </os>
+  <features>
+    <acpi/>
+    <apic/>
+    <vmport state="off"/>
+  </features>
+  <cpu mode="host-passthrough" check="none" migratable="on"/>
+  <clock offset="utc">
+    <timer name="rtc" tickpolicy="catchup"/>
+    <timer name="pit" tickpolicy="delay"/>
+    <timer name="hpet" present="no"/>
+  </clock>
+  <on_poweroff>destroy</on_poweroff>
+  <on_reboot>restart</on_reboot>
+  <on_crash>destroy</on_crash>
+  <pm>
+    <suspend-to-mem enabled="no"/>
+    <suspend-to-disk enabled="no"/>
+  </pm>
+  <devices>
+    <emulator>/usr/bin/qemu-system-x86_64</emulator>
+    <disk type="block" device="disk">block
+      <driver name="qemu" type="raw" cache="none" io="native" discard="unmap"/>
+      <source dev="/dev/vmstorage/fileserver2" index="3"/>
+      <backingStore/>
+      <target dev="vda" bus="virtio"/>
+      <alias name="virtio-disk0"/>
+      <address type="pci" domain="0x0000" bus="0x04" slot="0x00" function="0x0"/>
+    </disk>
+    <disk type="block" device="disk">block
+      <driver name="qemu" type="raw"/>
+      <source dev="/dev/mapper/storage1" index="2"/>
+      <backingStore/>
+      <target dev="vdb" bus="virtio"/>
+      <alias name="virtio-disk1"/>
+      <address type="pci" domain="0x0000" bus="0x08" slot="0x00" function="0x0"/>
+    </disk>
+    <disk type="block" device="disk">block
+      <driver name="qemu" type="raw"/>
+      <source dev="/dev/maper/storage2" index="1"/>
+      <backingStore/>
+      <target dev="vdc" bus="virtio"/>
+      <alias name="virtio-disk2"/>
+      <address type="pci" domain="0x0000" bus="0x09" slot="0x00" function="0x0"/>
+    </disk>
+    <controller type="usb" index="0" model="qemu-xhci" ports="15">
+      <model name="qemu-xhci"/>
+      <alias name="usb"/>
+      <address type="pci" domain="0x0000" bus="0x02" slot="0x00" function="0x0"/>
+    </controller>
+    <controller type="pci" index="0" model="pci-root">
+      <model name="pci-root"/>
+      <alias name="pcie.0"/>
+    </controller>
+    <controller type="pci" index="1" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="1" port="0x10"/>
+      <alias name="pci.1"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x0"/>
+    </controller>
+    <controller type="pci" index="2" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="2" port="0x11"/>
+      <alias name="pci.2"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x0"/>
+    </controller>
+    <controller type="pci" index="3" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="3" port="0x12"/>
+      <alias name="pci.3"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x2"/>
+    </controller>
+    <controller type="pci" index="4" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="4" port="0x13"/>
+      <alias name="pci.4"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x3"/>
+    </controller>
+    <controller type="pci" index="5" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="5" port="0x14"/>
+      <alias name="pci.5"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x4"/>
+    </controller>
+    <controller type="pci" index="6" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="6" port="0x15"/>
+      <alias name="pci.6"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x5"/>
+    </controller>
+    <controller type="pci" index="7" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="7" port="0x16"/>
+      <alias name="pci.7"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x6"/>
+    </controller>
+    <controller type="pci" index="8" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="8" port="0x17"/>
+      <alias name="pci.8"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x7"/>
+    </controller>
+    <controller type="pci" index="9" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="9" port="0x18"/>
+      <alias name="pci.9"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x03" function="0x0"/>
+    </controller>
+    <controller type="pci" index="10" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="10" port="0x19"/>
+      <alias name="pci.10"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x03" function="0x1"/>
+    </controller>
+    <controller type="pci" index="11" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="11" port="0x1a"/>
+      <alias name="pci.11"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x03" function="0x2"/>
+    </controller>
+    <controller type="pci" index="12" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="12" port="0x1b"/>
+      <alias name="pci.12"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x03" function="0x3"/>
+    </controller>
+    <controller type="pci" index="13" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="13" port="0x1c"/>
+      <alias name="pci.13"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x03" function="0x4"/>
+    </controller>
+    <controller type="pci" index="14" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="14" port="0x1d"/>
+      <alias name="pci.14"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x03" function="0x5"/>
+    </controller>
+    <controller type="pci" index="15" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="15" port="0x1e"/>
+      <alias name="pci.15"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x03" function="0x6"/>
+    </controller>
+    <controller type="pci" index="16" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <alias name="pci.16"/>
+      <address type="pci" domain="0x0000" bus="0x07" slot="0x00" function="0x0"/>
+    </controller>
+    <controller type="sata" index="0">
+      <alias name="ide"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x1f" function="0x02"/>
+    </controller>
+    <controller type="virtio-serial" index="0">
+      <alias name="virtio-serial0"/>
+      <address type="pci" domain="0x0000" bus="0x03" slot="0x00" function="0x0"/>
+    </controller>
+    <controller type="scsi" index="0" model="lsilogic">
+      <model name="lsilogic"/>
+      <alias name="scsi0"/>
+      <address type="pci" domain="0x0000" bus="0x10" slot="0x01" function="0x0"/>
+    </controller>
+  </devices>
+</domain>
diff --git a/nix/modules/libvirtnix/config.nix b/nix/modules/libvirtnix/config.nix
index e1a8d28..5272ce2 100644
--- a/nix/modules/libvirtnix/config.nix
+++ b/nix/modules/libvirtnix/config.nix
@@ -53,6 +53,530 @@
           check = "none";
           migratable = "on";
         };
+
+        clock = {
+          offset = "utc";
+          timer = [
+            {
+              name = "rtc";
+              tickpolicy = "catchup";
+            }
+            {
+              name = "pit";
+              tickpolicy = "delay";
+            }
+            {
+              name = "hpet";
+              present = "no";
+            }
+          ];
+        };
+
+        on_poweroff = "destroy";
+        on_reboot = "restart";
+        on_crash = "destroy";
+
+        pm = {
+          suspend-to-mem = "no";
+          suspend-to-disk = "no";
+        };
+
+        devices = {
+          emulators = [
+            {
+              value = "/usr/bin/qemu-system-x86_64";
+            }
+          ];
+
+          disks = [
+            {
+              type = "block";
+              device = "disk";
+              driver = {
+                name = "qemu";
+                type = "raw";
+                cache = "none";
+                io = "native";
+                discard = "unmap";
+              };
+              source = {
+                dev = "/dev/vmstorage/fileserver2";
+                index = 3;
+              };
+              backingStore = true;
+              target = {
+                dev = "vda";
+                bus = "virtio";
+              };
+              alias = {
+                name = "virtio-disk0";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x04";
+                slot = "0x00";
+                function = "0x0";
+              };
+            }
+            {
+              type = "block";
+              device = "disk";
+              driver = {
+                name = "qemu";
+                type = "raw";
+              };
+              source = {
+                dev = "/dev/mapper/storage1";
+                index = 2;
+              };
+              backingStore = true;
+              target = {
+                dev = "vdb";
+                bus = "virtio";
+              };
+              alias = {
+                name = "virtio-disk1";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x08";
+                slot = "0x00";
+                function = "0x0";
+              };
+            }
+            {
+              type = "block";
+              device = "disk";
+              driver = {
+                name = "qemu";
+                type = "raw";
+              };
+              source = {
+                dev = "/dev/maper/storage2";
+                index = 1;
+              };
+              backingStore = true;
+              target = {
+                dev = "vdc";
+                bus = "virtio";
+              };
+              alias = {
+                name = "virtio-disk2";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x09";
+                slot = "0x00";
+                function = "0x0";
+              };
+            }
+          ];
+
+          controllers = [
+            {
+              type = "usb";
+              index = 0;
+              model = "qemu-xhci";
+              ports = 15;
+              alias = {
+                name = "usb";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x02";
+                slot = "0x00";
+                function = "0x0";
+              };
+            }
+            {
+              type = "pci";
+              index = 0;
+              model = "pci-root";
+              alias = {
+                name = "pcie.0";
+              };
+            }
+            {
+              type = "pci";
+              index = 1;
+              model = "pcie-root-port";
+              target = {
+                chassis = 1;
+                port = "0x10";
+              };
+              alias = {
+                name = "pci.1";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x02";
+                function = "0x0";
+                multifunction = "on";
+              };
+            }
+            {
+              type = "pci";
+              index = 2;
+              model = "pcie-root-port";
+              target = {
+                chassis = 2;
+                port = "0x11";
+              };
+              alias = {
+                name = "pci.2";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x02";
+                function = "0x0";
+                multifunction = "on";
+              };
+            }
+            {
+              type = "pci";
+              index = 3;
+              model = "pcie-root-port";
+              target = {
+                chassis = 3;
+                port = "0x12";
+              };
+              alias = {
+                name = "pci.3";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x02";
+                function = "0x2";
+              };
+            }
+            {
+              type = "pci";
+              index = 4;
+              model = "pcie-root-port";
+              target = {
+                chassis = 4;
+                port = "0x13";
+              };
+              alias = {
+                name = "pci.4";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x02";
+                function = "0x3";
+              };
+            }
+            {
+              type = "pci";
+              index = 5;
+              model = "pcie-root-port";
+              target = {
+                chassis = 5;
+                port = "0x14";
+              };
+              alias = {
+                name = "pci.5";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x02";
+                function = "0x4";
+              };
+            }
+            {
+              type = "pci";
+              index = 6;
+              model = "pcie-root-port";
+              target = {
+                chassis = 6;
+                port = "0x15";
+              };
+              alias = {
+                name = "pci.6";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x02";
+                function = "0x5";
+              };
+            }
+            {
+              type = "pci";
+              index = 7;
+              model = "pcie-root-port";
+              target = {
+                chassis = 7;
+                port = "0x16";
+              };
+              alias = {
+                name = "pci.7";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x02";
+                function = "0x6";
+              };
+            }
+            {
+              type = "pci";
+              index = 8;
+              model = "pcie-root-port";
+              target = {
+                chassis = 8;
+                port = "0x17";
+              };
+              alias = {
+                name = "pci.8";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x02";
+                function = "0x7";
+              };
+            }
+            {
+              type = "pci";
+              index = 9;
+              model = "pcie-root-port";
+              target = {
+                chassis = 9;
+                port = "0x18";
+              };
+              alias = {
+                name = "pci.9";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x03";
+                function = "0x0";
+                multifunction = "on";
+              };
+            }
+            {
+              type = "pci";
+              index = 10;
+              model = "pcie-root-port";
+              target = {
+                chassis = 10;
+                port = "0x19";
+              };
+              alias = {
+                name = "pci.10";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x03";
+                function = "0x1";
+              };
+            }
+            {
+              type = "pci";
+              index = 11;
+              model = "pcie-root-port";
+              target = {
+                chassis = 11;
+                port = "0x1a";
+              };
+              alias = {
+                name = "pci.11";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x03";
+                function = "0x2";
+              };
+            }
+            {
+              type = "pci";
+              index = 12;
+              model = "pcie-root-port";
+              target = {
+                chassis = 12;
+                port = "0x1b";
+              };
+              alias = {
+                name = "pci.12";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x03";
+                function = "0x3";
+              };
+            }
+            {
+              type = "pci";
+              index = 13;
+              model = "pcie-root-port";
+              target = {
+                chassis = 13;
+                port = "0x1c";
+              };
+              alias = {
+                name = "pci.13";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x03";
+                function = "0x4";
+              };
+            }
+            {
+              type = "pci";
+              index = 14;
+              model = "pcie-root-port";
+              target = {
+                chassis = 14;
+                port = "0x1d";
+              };
+              alias = {
+                name = "pci.14";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x03";
+                function = "0x5";
+              };
+            }
+            {
+              type = "pci";
+              index = 15;
+              model = "pcie-root-port";
+              target = {
+                chassis = 15;
+                port = "0x1e";
+              };
+              alias = {
+                name = "pci.15";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x03";
+                function = "0x6";
+              };
+            }
+            {
+              type = "pci";
+              index = 16;
+              model = "pcie-root-port";
+              alias = {
+                name = "pci.16";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x07";
+                slot = "0x00";
+                function = "0x0";
+              };
+            }
+            {
+              type = "sata";
+              index = 0;
+              alias = {
+                name = "ide";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x1f";
+                function = "0x02";
+              };
+            }
+            {
+              type = "virtio-serial";
+              index = 0;
+              alias = {
+                name = "virtio-serial0";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x03";
+                slot = "0x00";
+                function = "0x0";
+              };
+            }
+            {
+              type = "scsi";
+              index = 0;
+              model = "lsilogic";
+              alias = {
+                name = "scsi0";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x10";
+                slot = "0x01";
+                function = "0x0";
+              };
+            }
+          ];
+          interfaces = [
+            {
+              type = "bridge";
+              mac = {
+                address = "52:54:00:bf:ba:88";
+              };
+              source = {
+                network = "services";
+                portid = "6c3a3512-d823-4521-9043-3a91a65682f5";
+                bridge = "br7";
+              };
+              target = {
+                dev = "vnet2";
+              };
+              model = {
+                type = "virtio";
+              };
+              alias = {
+                name = "net0";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x01";
+                slot = "0x00";
+                function = "0x0";
+              };
+            }
+          ];
+        };
       };
     };
   };
diff --git a/nix/modules/libvirtnix/domain.nix b/nix/modules/libvirtnix/domain.nix
index 22cd891..d1a5b9c 100644
--- a/nix/modules/libvirtnix/domain.nix
+++ b/nix/modules/libvirtnix/domain.nix
@@ -12,11 +12,363 @@ let
       type = lib.types.str;
       default = "${default}";
     };
+
+  # shorthand for an option with an enum, no default value
+  enumOpt =
+    options:
+    lib.mkOption {
+      type = lib.types.nullOr (lib.types.enum options);
+      default = null;
+    };
 in
 {
   options = {
     services.emile.libvirtnix =
       let
+
+        address = lib.mkOption {
+          default = null;
+          type = lib.types.nullOr (
+            lib.types.submodule {
+              options = {
+                type = enumOpt [ "pci" ];
+                domain = lib.mkOption {
+                  type = lib.types.str;
+                  example = "0x0000";
+                };
+                bus = lib.mkOption {
+                  type = lib.types.str;
+                  example = "0x4";
+                };
+                slot = lib.mkOption {
+                  type = lib.types.str;
+                  example = "0x00";
+                };
+                function = lib.mkOption {
+                  type = lib.types.str;
+                  example = "0x00";
+                };
+                multifunction = lib.mkOption {
+                  type = lib.types.enum [
+                    "on"
+                    "off"
+                  ];
+                  example = "on";
+                  default = "off";
+                };
+              };
+            }
+          );
+        };
+
+        interface = lib.mkOption {
+          type = lib.types.submodule {
+            options = {
+              type = enumOpt [ "bridge" ];
+              mac = lib.mkOption {
+                type = lib.types.submodule {
+                  options = {
+                    address = lib.types.mkOption {
+                      type = lib.types.str;
+                    };
+                  };
+                };
+              };
+              source = lib.mkOption {
+                type = lib.types.submodule {
+                  options = {
+                    network = lib.types.mkOption {
+                      type = lib.types.str;
+                    };
+                    portid = lib.types.mkOption {
+                      type = lib.types.str;
+                    };
+                    bridge = lib.types.mkOption {
+                      type = lib.types.str;
+                    };
+                  };
+                };
+              };
+              target = lib.mkOption {
+                type = lib.types.submodule {
+                  options = {
+                    dev = lib.types.mkOption {
+                      type = lib.types.str;
+                    };
+                  };
+                };
+              };
+              model = lib.mkOption {
+                type = lib.types.submodule {
+                  options = {
+                    type = lib.types.mkOption {
+                      type = enumOpt [ "virtio" ];
+                    };
+                  };
+                };
+              };
+              alias = lib.mkOption {
+                type = lib.types.submodule {
+                  options = {
+                    dev = lib.types.mkOption {
+                      name = lib.types.str;
+                    };
+                  };
+                };
+              };
+              inherit address;
+            };
+          };
+        };
+
+
+        controller = lib.types.submodule {
+          options = {
+            type = enumOpt [
+              "usb"
+              "pci"
+              "sata"
+              "virtio-serial"
+              "scsi"
+            ];
+            index = lib.mkOption { type = lib.types.int; };
+            model = lib.mkOption {
+              type = lib.types.nullOr lib.types.str;
+              default = null;
+            };
+            ports = lib.mkOption {
+              type = lib.types.nullOr lib.types.int;
+              default = null;
+            };
+            alias = lib.mkOption {
+              type = lib.types.submodule {
+                options = {
+                  name = lib.mkOption { type = lib.types.str; };
+                };
+              };
+            };
+            target = lib.mkOption {
+              type = lib.types.nullOr (
+                lib.types.submodule {
+                  options = {
+                    chassis = lib.mkOption { type = lib.types.int; };
+                    port = lib.mkOption { type = lib.types.str; };
+                  };
+                }
+              );
+              default = null;
+            };
+            inherit address;
+          };
+        };
+
+        emulator = lib.types.submodule {
+          options = {
+            value = lib.mkOption { type = lib.types.str; };
+          };
+        };
+
+        disk = lib.types.submodule {
+          options = {
+            type = enumOpt [ "block" ];
+            device = enumOpt [ "disk" ];
+            driver = lib.mkOption {
+              type = lib.types.submodule {
+                options = {
+                  name = lib.mkOption { type = lib.types.str; };
+                  type = enumOpt [ "raw" ];
+                  cache = enumOpt [ "none" ];
+                  io = enumOpt [ "native" ];
+                  discard = enumOpt [ "unmap" ];
+                };
+              };
+            };
+            source = lib.mkOption {
+              type = lib.types.submodule {
+                options = {
+                  dev = lib.mkOption { type = lib.types.str; };
+                  index = lib.mkOption { type = lib.types.int; };
+                };
+              };
+            };
+            backingStore = lib.mkOption { type = lib.types.bool; };
+            target = lib.mkOption {
+              type = lib.types.submodule {
+                options = {
+                  dev = lib.mkOption { type = lib.types.str; };
+                  bus = enumOpt [ "virtio" ];
+                };
+              };
+            };
+            alias = lib.mkOption {
+              type = lib.types.submodule {
+                options = {
+                  name = lib.mkOption { type = lib.types.str; };
+                };
+              };
+            };
+            inherit address;
+          };
+        };
+
+        devices = lib.mkOption {
+          type = lib.types.submodule {
+            options = {
+              emulators = lib.mkOption {
+                type = lib.types.listOf emulator;
+              };
+              disks = lib.mkOption {
+                type = lib.types.listOf disk;
+              };
+              controllers = lib.mkOption {
+                type = lib.types.listOf controller;
+              };
+              interfaces = lib.mkOption {
+                type = lib.types.listOf interface;
+              };
+            };
+          };
+        };
+
+        pm = lib.mkOption {
+          type = lib.types.submodule {
+            options = {
+              suspend-to-mem = lib.mkOption {
+                type = lib.types.nullOr (
+                  lib.types.enum [
+                    "yes"
+                    "no"
+                  ]
+                );
+                default = null;
+                example = "yes";
+              };
+              suspend-to-disk = lib.mkOption {
+                type = lib.types.nullOr (
+                  lib.types.enum [
+                    "yes"
+                    "no"
+                  ]
+                );
+                default = null;
+                example = "yes";
+              };
+            };
+          };
+        };
+
+        on_handle = lib.mkOption {
+          type = lib.types.enum [
+            "destroy"
+            "restart"
+            "preserve"
+            "rename-restart"
+          ];
+        };
+
+        on_poweroff = on_handle;
+        on_reboot = on_handle;
+        on_crash = on_handle;
+
+        clock = lib.mkOption {
+          type = lib.types.submodule {
+            options = {
+              offset = lib.mkOption {
+                type = lib.types.str;
+                example = "utc";
+                default = "utc";
+              };
+              timer = lib.mkOption {
+                type = lib.types.listOf (
+                  lib.types.submodule {
+                    options = {
+                      name = lib.mkOption {
+                        type = lib.types.enum [
+                          # "platform" (currently unsupported)
+                          "hpet" # xen, qemu, lxc
+                          "kvmclock" # qemu
+                          "pit" # qemu
+                          "rtc" # qemu, lxc
+                          "tsc" # xen, qemu - since 3.2.0
+
+                          # The hypervclock timer adds support for the reference time counter and the reference page for iTSC feature for guests running the Microsoft Windows operating system.
+                          "hypervclock" # qemu - since 1.2.2
+
+                          "armvtimer" # qemu - since 6.1.0
+                        ];
+                        default = "";
+                        example = "";
+                      };
+                      track = lib.mkOption {
+                        # TODO(emile): Only valid for name="rtc" or name="platform".
+                        type = lib.types.enum [
+                          "boot"
+                          "guest"
+                          "wall"
+                          "realtime"
+                        ];
+                        default = "";
+                        example = "";
+                      };
+                      tickpolicy = lib.mkOption {
+                        type = lib.types.nullOr (
+                          lib.types.enum [
+                            "catchup"
+                            "delay"
+                            "merge"
+                            "discard"
+                            "catchup"
+                          ]
+                        );
+                        default = null;
+                        example = "";
+                      };
+                      present = lib.mkOption {
+                        type = lib.types.nullOr (
+                          lib.types.enum [
+                            "yes"
+                            "no"
+                          ]
+                        );
+                        default = null;
+                        example = "";
+                      };
+                    };
+                  }
+                );
+                example = [
+                  {
+                    name = "rtc";
+                    tickpolicy = "catchup";
+                  }
+                  {
+                    name = "pit";
+                    tickpolicy = "delay";
+                  }
+                  {
+                    name = "hpet";
+                    present = "no";
+                  }
+                ];
+                default = [
+                  {
+                    name = "rtc";
+                    tickpolicy = "catchup";
+                  }
+                  {
+                    name = "pit";
+                    tickpolicy = "delay";
+                  }
+                  {
+                    name = "hpet";
+                    present = "no";
+                  }
+                ];
+              };
+            };
+          };
+        };
+
         cpu = lib.mkOption {
           type = lib.types.submodule {
             options = {
@@ -59,7 +411,7 @@ in
             };
           };
         };
-      
+
         os_boot = lib.mkOption {
           type = lib.types.submodule {
             options = {
@@ -71,7 +423,7 @@ in
             };
           };
         };
-      
+
         os_nvram = lib.mkOption {
           type = lib.types.submodule {
             options = {
@@ -83,7 +435,7 @@ in
             };
           };
         };
-      
+
         os_loader = lib.mkOption {
           type = lib.types.submodule {
             options = {
@@ -105,7 +457,7 @@ in
             };
           };
         };
-      
+
         os_type = lib.mkOption {
           type = lib.types.submodule {
             options = {
@@ -233,13 +585,18 @@ in
               os
               features
               cpu
+              clock
+              on_poweroff
+              on_reboot
+              on_crash
+              pm
+              devices
               ;
           };
         };
       in
       {
         enable = lib.mkEnableOption "Enable r2wars-web";
-      
 
         # Temporary output we write the domain to, in order to inspect the correctness of
         # the generated xml
@@ -258,93 +615,690 @@ in
       let
         vm = config.services.emile.libvirtnix.vm;
 
-        cpu =
-          vm_name:
+        # try this in a nix repl
+        # nix-repl> mkTag = import ./xml.nix { lib = (import <nixpkgs> {}).lib; }
+        # nix-repl> :p let func = (x: mkTag {name=x.variant;}); case = {"emulator" = func; "disk" = func;}; in map (x: case."${x.variant}" x) [ {variant = "emulator"; value = "asd";} {variant = "disk"; type = "block"; } ]
+
+        emulator =
+          x:
           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 "")
+            name = "emulator";
+            value = x.value;
+          };
+
+        interface =
+          x:
+          mkTag {
+            name = "interface";
+            args = [
+              (
+                if x.type != null then
+                  {
+                    key = "type";
+                    val = x.type;
+                  }
+                else
+                  ""
+              )
+            ];
+            children = [
+              # (
+              #   if x.mac != null then
+              #     (mkTag {
+              #       name = "mac";
+              #       args = [
+              #         {
+              #           key = "address";
+              #           val = x.mac.address;
+              #         }
+              #       ];
+              #       closing = false;
+              #     })
+              #   else
+              #     ""
+              # )
+              #(
+              #  if x.source != null then
+              #    (mkTag {
+              #      name = "source";
+              #      args = [
+              #        {
+              #          key = "network";
+              #          val = x.source.network;
+              #        }
+              #        {
+              #          key = "portid";
+              #          val = x.source.portid;
+              #        }
+              #        {
+              #          key = "bridge";
+              #          val = x.source.bridge;
+              #        }
+              #      ];
+              #      closing = false;
+              #    })
+              #  else
+              #    ""
+              #)
+              #(
+              #  if x.target != null then
+              #    (mkTag {
+              #      name = "target";
+              #      args = [
+              #        {
+              #          key = "dev";
+              #          val = x.target.dev;
+              #        }
+              #      ];
+              #    })
+              #  else
+              #    ""
+              #)
+              #(
+              #  if x.model != null then
+              #    (mkTag {
+              #      name = "model";
+              #      args = [
+              #        {
+              #          key = "type";
+              #          val = x.model.type;
+              #        }
+              #      ];
+              #    })
+              #  else
+              #    ""
+              #)
+              #(
+              #  if x.alias!= null then
+              #    (mkTag {
+              #      name = "alias";
+              #      args = [
+              #        {
+              #          key = "name";
+              #          val = x.alias.name;
+              #        }
+              #      ];
+              #    })
+              #  else
+              #    ""
+              #)
+              #(
+              #  if x.address != null then
+              #    mkTag {
+              #      name = "address";
+              #      args = [
+              #        (
+              #          if x.address.type != null then
+              #            {
+              #              key = "type";
+              #              val = x.address.type;
+              #            }
+              #          else
+              #            ""
+              #        )
+              #        {
+              #          key = "domain";
+              #          val = x.address.domain;
+              #        }
+              #        {
+              #          key = "bus";
+              #          val = x.address.bus;
+              #        }
+              #        {
+              #          key = "slot";
+              #          val = x.address.slot;
+              #        }
+              #        {
+              #          key = "function";
+              #          val = x.address.function;
+              #        }
+              #      ];
+              #      closing = false;
+              #    }
+              #  else
+              #    ""
+              #)
             ];
-            closing = false;
           };
-        
-        features =
-          vm_name:
+
+        controller =
+          x:
           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";
+            name = "controller";
+            args = [
+              (
+                if x.type != null then
+                  {
+                    key = "type";
+                    val = x.type;
+                  }
+                else
+                  ""
+              )
+              (
+                if x.index != null then
+                  {
+                    key = "index";
+                    val = "${toString x.index}";
+                  }
+                else
+                  ""
+              )
+              (
+                if x.model != null then
+                  {
+                    key = "model";
+                    val = x.model;
+                  }
+                else
+                  ""
+              )
+              (
+                if x.ports != null then
+                  {
+                    key = "ports";
+                    val = "${toString x.ports}";
+                  }
+                else
+                  ""
+              )
+            ];
+            children = [
+              # TODO(emile): figure out if the arg in controller is really the same as the
+              #              one given in model. The configs I've got let it seem so
+              # <controller type="pci" index="1" model="pcie-root-port">
+              #   <model name="pcie-root-port"/>
+              (
+                if x.model != null then
+                  (mkTag {
+                    name = "model";
+                    args = [
+                      {
+                        key = "name";
+                        val = x.model;
+                      }
+                    ];
+                    closing = false;
+                  })
+                else
+                  ""
+              )
+              (
+                if x.target != null then
+                  (mkTag {
+                    name = "target";
+                    args = [
+                      {
+                        key = "chassis";
+                        val = "${toString x.target.chassis}";
+                      }
+                      {
+                        key = "port";
+                        val = x.target.port;
+                      }
+                    ];
+                  })
+                else
+                  ""
+              )
+              (mkTag {
+                name = "alias";
                 args = [
-                  { key = "state"; val = vm.${vm_name}.features.vmport.state; }
+                  (
+                    if x.alias.name != null then
+                      {
+                        key = "name";
+                        val = x.alias.name;
+                      }
+                    else
+                      ""
+                  )
                 ];
                 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 "")
+              })
+
+              (
+                if x.address != null then
+                  mkTag {
+                    name = "address";
+                    args = [
+                      (
+                        if x.address.type != null then
+                          {
+                            key = "type";
+                            val = x.address.type;
+                          }
+                        else
+                          ""
+                      )
+                      {
+                        key = "domain";
+                        val = x.address.domain;
+                      }
+                      {
+                        key = "bus";
+                        val = x.address.bus;
+                      }
+                      {
+                        key = "slot";
+                        val = x.address.slot;
+                      }
+                      {
+                        key = "function";
+                        val = x.address.function;
+                      }
+                    ];
+                    closing = false;
+                  }
+                else
+                  ""
+              )
             ];
           };
 
-        os =
-          vm_name:
+        disk =
+          x:
           mkTag {
-            name = "os";
-            children = let
-              os_type = vm_name: mkTag {
-                name = "type";
+            name = "disk";
+            args = [
+              {
+                key = "type";
+                val = x.type;
+              }
+              {
+                key = "device";
+                val = x.device;
+              }
+            ];
+            children = [
+              (mkTag {
+                name = "driver";
                 args = [
-                  { key = "arch"; val = vm.${vm_name}.os.type.arch; }
-                  { key = "machine"; val = vm.${vm_name}.os.type.machine; }
+                  (
+                    if x.driver.name != null then
+                      {
+                        key = "name";
+                        val = x.driver.name;
+                      }
+                    else
+                      ""
+                  )
+                  (
+                    if x.driver.type != null then
+                      {
+                        key = "type";
+                        val = x.driver.type;
+                      }
+                    else
+                      ""
+                  )
+                  (
+                    if x.driver.cache != null then
+                      {
+                        key = "cache";
+                        val = x.driver.cache;
+                      }
+                    else
+                      ""
+                  )
+                  (
+                    if x.driver.io != null then
+                      {
+                        key = "io";
+                        val = x.driver.io;
+                      }
+                    else
+                      ""
+                  )
+                  (
+                    if x.driver.discard != null then
+                      {
+                        key = "discard";
+                        val = x.driver.discard;
+                      }
+                    else
+                      ""
+                  )
                 ];
-                value = vm.${vm_name}.os.type.value;
-              };
-              os_loader = vm_name: mkTag {
-                name = "loader";
+                closing = false;
+              })
+              (mkTag {
+                name = "source";
                 args = [
-                  { key = "readonly"; val = vm.${vm_name}.os.loader.readonly; }
-                  { key = "pflash"; val = vm.${vm_name}.os.loader.type; }
+                  {
+                    key = "dev";
+                    val = x.source.dev;
+                  }
+                  {
+                    key = "index";
+                    val = "${toString x.source.index}";
+                  }
                 ];
-                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";
+                closing = false;
+              })
+              (
+                if x.backingStore == true then
+                  mkTag {
+                    name = "backingStore";
+                    closing = false;
+                  }
+                else
+                  ""
+              )
+              (mkTag {
+                name = "target";
                 args = [
-                  { key = "dev"; val = vm.${vm_name}.os.boot.dev; }
+                  {
+                    key = "dev";
+                    val = x.target.dev;
+                  }
+                  {
+                    key = "bus";
+                    val = x.target.bus;
+                  }
                 ];
                 closing = false;
-              };
-            in [
-              (os_type vm_name)
-              (os_loader vm_name)
-              (os_nvram vm_name)
-              (os_boot vm_name)
+              })
+              (mkTag {
+                name = "alias";
+                args = [
+                  {
+                    key = "name";
+                    val = x.alias.name;
+                  }
+                ];
+                closing = false;
+              })
+              (mkTag {
+                name = "address";
+                args = [
+                  {
+                    key = "type";
+                    val = x.address.type;
+                  }
+                  {
+                    key = "domain";
+                    val = x.address.domain;
+                  }
+                  {
+                    key = "bus";
+                    val = x.address.bus;
+                  }
+                  {
+                    key = "slot";
+                    val = x.address.slot;
+                  }
+                  {
+                    key = "function";
+                    val = x.address.function;
+                  }
+                ];
+                closing = false;
+              })
             ];
+            value = x.type;
+          };
+
+        devices =
+          vm_name:
+          mkTag {
+            name = "devices";
+            children =
+              map (x: emulator x) vm.${vm_name}.devices.emulators
+              ++ map (x: disk x) vm.${vm_name}.devices.disks
+              ++ map (x: controller x) vm.${vm_name}.devices.controllers
+              ++ map (x: interface x) vm.${vm_name}.devices.interfaces;
+          };
+
+        pm =
+          vm_name:
+          mkTag {
+            name = "pm";
+            children =
+              let
+                suspend-to-mem = mkTag {
+                  name = "suspend-to-mem";
+                  args = [
+                    {
+                      key = "enabled";
+                      val = vm.${vm_name}.pm.suspend-to-mem;
+                    }
+                  ];
+                  closing = false;
+                };
+
+                suspend-to-disk = mkTag {
+                  name = "suspend-to-disk";
+                  args = [
+                    {
+                      key = "enabled";
+                      val = vm.${vm_name}.pm.suspend-to-disk;
+                    }
+                  ];
+                  closing = false;
+                };
+              in
+              [
+                suspend-to-mem
+                suspend-to-disk
+              ];
+          };
+
+        on_handler =
+          { vm_name, tag }:
+          mkTag {
+            name = "${tag}";
+            value = vm.${vm_name}.${tag};
+          };
+
+        on_poweroff =
+          vm_name:
+          on_handler {
+            inherit vm_name;
+            tag = "on_poweroff";
+          };
+        on_reboot =
+          vm_name:
+          on_handler {
+            inherit vm_name;
+            tag = "on_reboot";
+          };
+        on_crash =
+          vm_name:
+          on_handler {
+            inherit vm_name;
+            tag = "on_crash";
+          };
+
+        clock =
+          vm_name:
+          mkTag {
+            name = "clock";
+            args = [
+              {
+                key = "offset";
+                val = vm.${vm_name}.clock.offset;
+              }
+            ];
+            children = map (
+              x:
+              mkTag {
+                name = "timer";
+                args =
+                  let
+                    tickpolicy =
+                      if x.tickpolicy != null then
+                        {
+                          key = "tickpolicy";
+                          val = x.tickpolicy;
+                        }
+                      else
+                        { };
+
+                    present =
+                      if x.present != null then
+                        {
+                          key = "present";
+                          val = x.present;
+                        }
+                      else
+                        { };
+                  in
+                  [
+                    {
+                      key = "name";
+                      val = x.name;
+                    }
+                    tickpolicy
+                    present
+                  ];
+              }
+            ) vm.${vm_name}.clock.timer;
+          };
+
+        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
+                    ""
+                )
+              ];
+            closing = false;
+          };
+
+        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 "")
+              ];
+          };
+
+        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)
+              ];
           };
 
         resource =
@@ -473,6 +1427,12 @@ in
               (os vm_name)
               (features vm_name)
               (cpu vm_name)
+              (clock vm_name)
+              (on_poweroff vm_name)
+              (on_reboot vm_name)
+              (on_crash vm_name)
+              (pm vm_name)
+              (devices vm_name)
             ];
           };
       in
diff --git a/nix/modules/libvirtnix/os.nix b/nix/modules/libvirtnix/os.nix
new file mode 100644
index 0000000..41f36f3
--- /dev/null
+++ b/nix/modules/libvirtnix/os.nix
@@ -0,0 +1,119 @@
+{ lib, ... }:
+
+let
+  mkOption = lib.mkOption;
+  submodule = lib.types.submodule;
+  types = lib.types;
+
+  os = {
+    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";
+    # 	};
+    # };
+    
+  };
+in
+{
+  options = {
+    os = mkOption {
+      type = submodule {
+        options = {
+          inherit (os) firmware type arch machine loader;
+        };
+      };
+    };
+  };
+}
diff --git a/nix/modules/libvirtnix/secret.nix b/nix/modules/libvirtnix/secret.nix
new file mode 100644
index 0000000..b597174
--- /dev/null
+++ b/nix/modules/libvirtnix/secret.nix
@@ -0,0 +1,174 @@
+{ config, lib, ... }:
+
+# https://libvirt.org/formatsecret.html
+
+let
+  pkgs = import <nixpkgs> { };
+
+  mkOption = lib.mkOption;
+  submodule = lib.types.submodule;
+  types = lib.types;
+  enum = types.enum;
+  str = types.str;
+
+  yesNoOption = mkOption {
+    type = enum [
+      "yes"
+      "no"
+    ];
+    default = "no";
+  };
+  # YesnoOption = mkOption { type = enum [ "yes" "no" ]; default = "yes"; };
+
+  # 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>
+  mkTag =
+    {
+      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
+    }:
+    let
+      args_str =
+        " " + lib.strings.concatStrings (lib.strings.intersperse " " (map (x: "${x.key}='${x.val}'") args));
+      child_evaled = lib.strings.concatStrings children;
+    in
+    "<${name}${lib.optionalString (args != [ ]) args_str}>${value}${child_evaled}</${name}>";
+
+  strOption =
+    {
+      default ? "",
+    }:
+    mkOption {
+      type = str;
+      default = "${default}";
+    };
+
+  usage = mkOption {
+    type = submodule {
+      options = {
+        type = mkOption {
+          type = enum [
+            "volume"
+            "ceph"
+            "iscsi"
+            "tls"
+            "vtpm"
+          ];
+          default = "";
+        };
+
+        value = strOption { };
+
+        name = strOption { };
+        volume = strOption { };
+        target = strOption { };
+      };
+    };
+  };
+
+  secret = {
+    inherit usage;
+
+    ephemeral = yesNoOption;
+    private = yesNoOption;
+
+    uuid = strOption { };
+    description = strOption { };
+  };
+
+in
+{
+  options = {
+    services.emile.libvirtnix = {
+      enable = lib.mkEnableOption "Enable r2wars-web";
+
+      secret = mkOption {
+        type = submodule {
+          options = {
+            inherit (secret)
+              ephemeral
+              private
+              uuid
+              description
+              usage
+              ;
+          };
+        };
+      };
+
+      # output = mkOption { type = types.path; };
+    };
+  };
+
+  config = lib.mkIf config.services.emile.libvirtnix.enable {
+    services.emile.libvirtnix =
+      let
+        secret = mkTag {
+          name = "secret";
+          args = [
+            {
+              key = "ephemeral";
+              val = config.services.emile.libvirtnix.secret.ephemeral;
+            }
+            {
+              key = "private";
+              val = config.services.emile.libvirtnix.secret.private;
+            }
+          ];
+          children = [
+            (mkTag {
+              name = "description";
+              value = "Super secret description";
+            })
+            (mkTag {
+              name = "uuid";
+              value = "0a81f5b2-8403-7b23-c8d6-21ccc2f80d6f";
+            })
+            (mkTag {
+              name = "usage";
+              args = [
+                {
+                  key = "type";
+                  val = "volume";
+                }
+              ];
+              children = [
+                (mkTag {
+                  name = "volume";
+                  value = "/var/lib/libvirt/images/kernel.img";
+                })
+              ];
+            })
+          ];
+        };
+
+      in
+      {
+        # output = pkgs.writeText "libvirt-secret-config.xml" secret;
+      };
+  };
+}
diff --git a/nix/modules/libvirtnix/xml.nix b/nix/modules/libvirtnix/xml.nix
index 134a878..10eefa3 100644
--- a/nix/modules/libvirtnix/xml.nix
+++ b/nix/modules/libvirtnix/xml.nix
@@ -32,14 +32,27 @@
   closing ? true, # add a closing tag
 }:
 let
-  args_str =
-    " " + lib.strings.concatStrings (lib.strings.intersperse " " (map (x: "${x.key}='${x.val}'") args));
+
+  # filter out all values missing a key
+  # this allows passing empty args such as `{}` which then get discarded
+  filteredArgs = (builtins.filter (x: x ? key) args);
+
+  # convert the list of attr sets into a list of strings
+  mappedArgs = (map (x: "${x.key}='${x.val}'") filteredArgs);
+
+  # [ "A" "B" "C" ] => [ "A" " " "B" " " "C" ]
+  spacedArgs = lib.strings.intersperse "" mappedArgs;
+
+  # [ "A" " " "B" " " "C" ] => "A B C"
+  joinedArgs = lib.strings.concatStrings spacedArgs;
+
+  # prefix the args with a space when inserting
+  args_str = lib.optionalString (filteredArgs != [ ]) (" " + joinedArgs);
+
   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}"
+"<${name}${args_str}${cond (closing == false) "/"}>${value}${child_evaled}${lib.optionalString (closing) closingTag}"