diff options
Diffstat (limited to 'nix/modules')
-rw-r--r-- | nix/modules/goapp-frontend/default.nix | 134 | ||||
-rw-r--r-- | nix/modules/libvirtnix/a.xml | 259 | ||||
-rw-r--r-- | nix/modules/libvirtnix/b.xml | 183 | ||||
-rw-r--r-- | nix/modules/libvirtnix/config.nix | 583 | ||||
-rw-r--r-- | nix/modules/libvirtnix/default.nix | 109 | ||||
-rw-r--r-- | nix/modules/libvirtnix/domain.nix | 1443 | ||||
-rw-r--r-- | nix/modules/libvirtnix/os.nix | 119 | ||||
-rw-r--r-- | nix/modules/libvirtnix/secret.nix | 174 | ||||
-rw-r--r-- | nix/modules/libvirtnix/test.nix | 10 | ||||
-rw-r--r-- | nix/modules/libvirtnix/xml.nix | 58 | ||||
-rw-r--r-- | nix/modules/vm/default.nix | 86 | ||||
-rw-r--r-- | nix/modules/x86_64-linux.nix | 2 |
12 files changed, 3074 insertions, 86 deletions
diff --git a/nix/modules/goapp-frontend/default.nix b/nix/modules/goapp-frontend/default.nix new file mode 100644 index 0000000..c5f62aa --- /dev/null +++ b/nix/modules/goapp-frontend/default.nix @@ -0,0 +1,134 @@ +{ + config, + lib, + pkgs, + ... +}: + +let + cfg = config.services.emile.goapp-frontend; +in +with lib; +{ + options.services.emile.goapp-frontend = { + enable = mkEnableOption "Enable goapp-frontend"; + package = mkPackageOption pkgs "goapp-frontend" { }; + + # ip, port and external host to listen on + host = mkOption { + type = types.str; + default = "127.0.0.1"; + example = "0.0.0.0"; + description = "The host the service listens on"; + }; + port = mkOption { + type = types.int; + default = 8080; + example = 8080; + description = "The port the service listens on"; + }; + public-url = mkOption { + type = types.str; + default = "http://localhost:8080/"; + example = "https://goapp.emile.space/"; + description = '' + The domain that the service can be reached from externally. This is used by oidc for redirects and thus should be set, as you'll probably be running this behind some kind of reverse proxy. + ''; + }; + + # the oidc config + oidc = mkOption { + type = types.submodule { + options = { + id = mkOption { + type = types.str; + default = ""; + example = "AiliavahweiweeG5"; + description = "The oidc id"; + }; + issuer = mkOption { + type = types.str; + default = ""; + example = "https://sso.emile.space"; + description = "The oidc identity provider"; + }; + cookie-name = mkOption { + type = types.str; + default = "oidc-client"; + example = "CookieMcCookieface"; + description = "The oidc cookie name"; + }; + scopes = mkOption { + type = types.listOf types.str; + default = [ "openid" "profile" "email" "groups" ]; + example = [ "openid" "profile" "email" ]; + description = "The openid scopes to request"; + }; + secret-path = mkOption { + type = types.str; + default = ""; + example = "/run/goapp_oidc_secret"; + description = "The path to the oidc secret"; + }; + }; + }; + }; + + # paths to files + session-key-path = mkOption { + type = types.str; + default = ""; + example = "/run/sesionkey"; + description = "The path to a file containing the sessionKey"; + }; + logfile-path = mkOption { + type = types.str; + default = "/var/log/goapp-frontend.log"; + example = "/var/log/goapp-frontend.log"; + description = "The path to where the logfile should be written"; + }; + + database-path = mkOption { + type = types.str; + default = "/var/lib/goapp-frontend/main.db"; + example = "/var/lib/goapp-frontend/main.db"; + description = "The path to the main database"; + }; + sessiondb-path = mkOption { + type = types.str; + default = "/var/lib/goapp-frontend/sessions.db"; + example = "/var/lib/goapp-frontend/sessions.db"; + description = "The path to the sessions database"; + }; + }; + + config = mkIf cfg.enable { + systemd.services.goapp-frontend = { + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + RestartSec = 5; + Restart = "on-failure"; + }; + environment = { + VERSION = pkgs.goapp-frontend.version; + }; + path = [ pkgs.goapp-frontend ]; + serviceConfig.ExecStart = '' + ${pkgs.goapp-frontend}/bin/frontend \ + --host ${cfg.host} \ + --port ${toString cfg.port} \ + --public-url ${cfg.public-url} \ + --id ${cfg.oidc.id} \ + --issuer ${cfg.oidc.issuer} \ + --cookie-name ${cfg.oidc.cookie-name} \ + --scopes ${concatStringsSep "," cfg.oidc.scopes} \ + --oidc-secret-path ${cfg.oidc.secret-path} \ + --logfilepath ${cfg.logfile-path} \ + --databasepath ${cfg.database-path} \ + --sessiondbpath ${cfg.sessiondb-path} \ + --sessionkeypath ${cfg.session-key-path} \ + --templatespath ${pkgs.goapp-frontend}/templates + ''; + }; + }; +} 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 new file mode 100644 index 0000000..5272ce2 --- /dev/null +++ b/nix/modules/libvirtnix/config.nix @@ -0,0 +1,583 @@ +{ + services.emile.libvirtnix = { + enable = true; + vm = { + "alan" = { + vm_type = "kvm"; + id = "1337"; + name = "blub"; + uuid = "cafebabe-d474-452b-80f4-c951c39bcf74"; + + metadata.libosinfo = "https://libosinfo.org/xmlns/libvirt/domain/1.0"; + metadata.libosinfo_os = "https://nixos.org/nixos/unknown"; + + memory.unit = "KiB"; + memory.value = 2097152; + + currentMemory.unit = "KiB"; + currentMemory.value = 2097152; + + vcpu.placement = "static"; + vcpu.count = 3; + + resource.partition = "/machine"; + + os = { + type = { + arch = "x86_64"; + machine = "pc-q35-3.1"; + value = "hvm"; + }; + + loader = { + readonly = "yes"; + type = "pflash"; + value = "/usr/share/OVMF/OVMF_CODE.fd"; + }; + + nvram.value = "/var/lib/libvirt/qemu/nvram/fileserver2_VARS.fd"; + + boot.dev = "hd"; + }; + + features = { + acpi = true; + apic = true; + vmport = { + state = "off"; + }; + }; + + cpu = { + mode = "host-passthrough"; + check = "none"; + migratable = "on"; + }; + + 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/default.nix b/nix/modules/libvirtnix/default.nix new file mode 100644 index 0000000..a998966 --- /dev/null +++ b/nix/modules/libvirtnix/default.nix @@ -0,0 +1,109 @@ +{ + config, + lib, + pkgs, + ... +}: + +let + cfg = config.services.emile.libvirtnix; +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 = '' + { + domain = { + name = "vm"; + }; + } + ''; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.services = lib.mapAttrs' ( + name: guest: + lib.nameValuePair "libvirtd-guest-${name}" { + after = [ "libvirtd.service" ]; + requires = [ "libvirtd.service" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = "yes"; + }; + script = + let + 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}'") + + ''>'' + + (lib.optionalString (guest.domain.name != "") "<name>${guest.domain.name}</name>") + + (lib.optionalString (guest.domain.name != "") "<name>${guest.domain.name}</name>") + + (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.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> + ''; + in + '' + uuid="$(${pkgs.libvirt}/bin/virsh domuuid '${name}' || true)" + ${pkgs.libvirt}/bin/virsh define <(sed "s/UUID/$uuid/" '${xml}') + ${pkgs.libvirt}/bin/virsh start '${name}' + ''; + preStop = '' + ${pkgs.libvirt}/bin/virsh shutdown '${name}' + let "timeout = $(date +%s) + 10" + while [ "$(${pkgs.libvirt}/bin/virsh list --name | grep --count '^${name}$')" -gt 0 ]; do + if [ "$(date +%s)" -ge "$timeout" ]; then + # Meh, we warned it... + ${pkgs.libvirt}/bin/virsh destroy '${name}' + else + # The machine is still running, let's give it some time to shut down + sleep 0.5 + fi + done + ''; + } + ) config.services.emile.libvirtnix.instances; + }; +} diff --git a/nix/modules/libvirtnix/domain.nix b/nix/modules/libvirtnix/domain.nix new file mode 100644 index 0000000..d1a5b9c --- /dev/null +++ b/nix/modules/libvirtnix/domain.nix @@ -0,0 +1,1443 @@ +{ config, lib, ... }: + +let + mkTag = import ./xml.nix { inherit lib; }; + pkgs = import <nixpkgs> { }; + + stringOption = + { + default ? "", + }: + lib.mkOption { + 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 = { + mode = lib.mkOption { + type = lib.types.str; + example = "host-passthrough"; + default = ""; + }; + check = lib.mkOption { + type = lib.types.str; + example = "none"; + default = ""; + }; + migratable = lib.mkOption { + type = lib.types.str; + example = "on"; + default = ""; + }; + }; + }; + }; + + features = lib.mkOption { + type = lib.types.submodule { + options = { + acpi = lib.mkEnableOption "enable acpi"; + apic = lib.mkEnableOption "enable apic"; + + vmport = lib.mkOption { + type = lib.types.submodule { + options = { + state = lib.mkOption { + type = lib.types.str; + example = "off"; + default = ""; + }; + }; + }; + }; + }; + }; + }; + + os_boot = lib.mkOption { + type = lib.types.submodule { + options = { + dev = lib.mkOption { + type = lib.types.str; + example = "hd"; + default = ""; + }; + }; + }; + }; + + os_nvram = lib.mkOption { + type = lib.types.submodule { + options = { + value = lib.mkOption { + type = lib.types.str; + example = "/var/lib/libvirt/qemu/nvram/fileserver2_VARS.fd"; + default = ""; + }; + }; + }; + }; + + os_loader = lib.mkOption { + type = lib.types.submodule { + options = { + readonly = lib.mkOption { + type = lib.types.str; + example = "yes"; + default = ""; + }; + type = lib.mkOption { + type = lib.types.str; + example = "pflash"; + default = ""; + }; + value = lib.mkOption { + type = lib.types.str; + example = "/usr/share/OVMF/OVMF_CODE.fd"; + default = ""; + }; + }; + }; + }; + + os_type = lib.mkOption { + type = lib.types.submodule { + options = { + arch = lib.mkOption { + type = lib.types.str; + example = "x86_64"; + default = ""; + }; + machine = lib.mkOption { + type = lib.types.str; + example = "pc-q35-3.1"; + default = ""; + }; + value = lib.mkOption { + type = lib.types.str; + example = "hvm"; + default = ""; + }; + }; + }; + }; + + os = lib.mkOption { + description = "os"; + type = lib.types.submodule { + options = { + type = os_type; + loader = os_loader; + nvram = os_nvram; + boot = os_boot; + }; + }; + }; + + resource = lib.mkOption { + description = "resource"; + type = lib.types.submodule { + options = { + partition = lib.mkOption { + type = lib.types.str; + example = "/machine"; + default = ""; + }; + }; + }; + }; + + vcpu = lib.mkOption { + description = "vcpu"; + type = lib.types.submodule { + options = { + placement = lib.mkOption { + # TODO(emile): fill up + type = lib.types.enum [ "static" ]; + example = "static"; + default = "static"; + }; + count = lib.mkOption { + type = lib.types.int; + example = 4; + default = 1; + }; + }; + }; + }; + + mem = lib.mkOption { + description = "memory"; + type = lib.types.submodule { + options = { + unit = lib.mkOption { + # TODO(emile): fill up + type = lib.types.enum [ + "KiB" + "MiB" + "GiB" + "TiB" + ]; + example = "KiB"; + default = "KiB"; + }; + value = lib.mkOption { + type = lib.types.int; + example = 2097152; + default = 1000; + }; + }; + }; + }; + + memory = mem; + currentMemory = mem; + + metadata = lib.mkOption { + description = "metadata submodule"; + type = lib.types.submodule { + options = { + libosinfo = lib.mkOption { + type = lib.types.str; + example = "https://libosinfo.org/xmlns/libvirt/domain/1.0"; + default = ""; + }; + libosinfo_os = lib.mkOption { + type = lib.types.str; + example = "https://nixos.org/nixos/unknown"; + default = ""; + }; + }; + }; + }; + + vm = lib.types.submodule { + options = { + vm_type = stringOption { default = "kvm"; }; + id = stringOption { }; + name = stringOption { }; + uuid = stringOption { }; + + inherit + metadata + memory + currentMemory + vcpu + resource + os + features + cpu + 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 + output.domain = lib.mkOption { type = lib.types.path; }; + + # An attrset of VMs + vm = lib.mkOption { + description = "VMs to define"; + type = lib.types.attrsOf vm; + }; + }; + }; + + config = lib.mkIf config.services.emile.libvirtnix.enable { + services.emile.libvirtnix = + let + vm = config.services.emile.libvirtnix.vm; + + # 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 = "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 + # "" + #) + ]; + }; + + controller = + x: + mkTag { + 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 = [ + ( + if x.alias.name != null then + { + key = "name"; + val = x.alias.name; + } + else + "" + ) + ]; + closing = false; + }) + + ( + 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 + "" + ) + ]; + }; + + disk = + x: + mkTag { + name = "disk"; + args = [ + { + key = "type"; + val = x.type; + } + { + key = "device"; + val = x.device; + } + ]; + children = [ + (mkTag { + name = "driver"; + args = [ + ( + 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 + "" + ) + ]; + closing = false; + }) + (mkTag { + name = "source"; + args = [ + { + key = "dev"; + val = x.source.dev; + } + { + key = "index"; + val = "${toString x.source.index}"; + } + ]; + closing = false; + }) + ( + if x.backingStore == true then + mkTag { + name = "backingStore"; + closing = false; + } + else + "" + ) + (mkTag { + name = "target"; + args = [ + { + key = "dev"; + val = x.target.dev; + } + { + key = "bus"; + val = x.target.bus; + } + ]; + closing = false; + }) + (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 = + vm_name: + mkTag { + name = "resource"; + children = [ + (mkTag { + name = "partition"; + value = "${toString vm.${vm_name}.resource.partition}"; + }) + ]; + }; + + vcpu = + vm_name: + mkTag { + name = "vcpu"; + args = [ + { + key = "placement"; + val = vm.${vm_name}.vcpu.placement; + } + ]; + value = "${toString vm.${vm_name}.vcpu.count}"; + }; + + currentMemory = + vm_name: + mkTag { + name = "currentMemory"; + args = [ + { + key = "unit"; + val = vm.${vm_name}.currentMemory.unit; + } + ]; + value = "${toString vm.${vm_name}.currentMemory.value}"; + }; + memory = + vm_name: + mkTag { + name = "memory"; + args = [ + { + key = "unit"; + val = vm.${vm_name}.memory.unit; + } + ]; + value = "${toString vm.${vm_name}.memory.value}"; + }; + + libosinfo_os = + vm_name: + mkTag { + name = "libosinfo:os"; + args = [ + { + key = "id"; + val = vm.${vm_name}.metadata.libosinfo_os; + } + ]; + closing = false; + }; + + libosinfo_libosinfo = + vm_name: + mkTag { + name = "libosinfo:libosinfo"; + args = [ + { + key = "xmlns:libosinfo"; + val = vm.${vm_name}.metadata.libosinfo; + } + ]; + children = [ + (libosinfo_os vm_name) + ]; + }; + + metadata = + vm_name: + mkTag { + name = "metadata"; + children = [ (libosinfo_libosinfo vm_name) ]; + }; + + uuid = + vm_name: + mkTag { + name = "uuid"; + value = vm.${vm_name}.uuid; + }; + + name = + vm_name: + mkTag { + name = "name"; + value = vm.${vm_name}.name; + }; + + # the vm_name has to be passed in, as we want to accesses the value for the given vm when + # generating the config + domain = + vm_name: + mkTag { + name = "domain"; + args = [ + { + key = "type"; + val = vm.${vm_name}.vm_type; + } + { + key = "id"; + val = vm.${vm_name}.id; + } + ]; + children = [ + (name vm_name) + (uuid vm_name) + (metadata vm_name) + (memory vm_name) + (currentMemory vm_name) + (vcpu vm_name) + (resource vm_name) + (os vm_name) + (features vm_name) + (cpu vm_name) + (clock vm_name) + (on_poweroff vm_name) + (on_reboot vm_name) + (on_crash vm_name) + (pm vm_name) + (devices vm_name) + ]; + }; + in + { + output.domain = pkgs.writeText "libvirt-domain-config.xml" (domain "alan"); + }; + }; +} 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/test.nix b/nix/modules/libvirtnix/test.nix new file mode 100644 index 0000000..8542b7e --- /dev/null +++ b/nix/modules/libvirtnix/test.nix @@ -0,0 +1,10 @@ +let + pkgs = import <nixpkgs> {}; +in + pkgs.lib.evalModules { + modules = [ + # ./secret.nix + ./domain.nix + ./config.nix + ]; + } diff --git a/nix/modules/libvirtnix/xml.nix b/nix/modules/libvirtnix/xml.nix new file mode 100644 index 0000000..10eefa3 --- /dev/null +++ b/nix/modules/libvirtnix/xml.nix @@ -0,0 +1,58 @@ +{ lib, ... }: + +# takes a few args and creats a valid xml tag pair out of it +# +# testTag = mkTag { +# name = "name"; +# args = [ +# { +# key = "arg1"; +# val = "arg1val"; +# } +# { +# key = "arg2"; +# val = "arg2val"; +# } +# ]; +# value = "qwe"; +# children = [ +# (mkTag { name = "nested"; args = []; value = "qwe"; children = [];}) +# ]; +# }; +# +# <name arg1=arg1val arg2=arg2val> +# value +# {children} +# </name> +{ + name, # name of the tag to be used, such as `secret`, `description`, ... + args ? [ ], # args, [ { key="a"; val="b"; } { key="c"; val="d"; } ] + value ? "", # the value to place in the middle + children ? [ ], # the child elements + closing ? true, # add a closing tag +}: +let + + # 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}${args_str}${cond (closing == false) "/"}>${value}${child_evaled}${lib.optionalString (closing) closingTag}" diff --git a/nix/modules/vm/default.nix b/nix/modules/vm/default.nix deleted file mode 100644 index 9428c94..0000000 --- a/nix/modules/vm/default.nix +++ /dev/null @@ -1,86 +0,0 @@ -{ - config, - lib, - pkgs, - ... -}: - -let - cfg = config.services.emile.vm; -in -with lib; -{ - options.services.emile.vm = { - enable = mkEnableOption "Enable vm"; - - # ip and port to listen on - guest = mkOption { - type = types.str; - default = "vmnameone"; - example = "vmnameone"; - description = "The name of the vm"; - }; - }; - - config = mkIf cfg.enable { - systemd.services = lib.mapAttrs' ( - name: guest: - lib.nameValuePair "libvirtd-guest-${name}" { - after = [ "libvirtd.service" ]; - requires = [ "libvirtd.service" ]; - wantedBy = [ "multi-user.target" ]; - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = "yes"; - }; - script = - let - xml = pkgs.writeText "libvirt-guest-${name}.xml" '' - <domain type="kvm"> - <name>${name}</name> - <uuid>UUID</uuid> - <os> - <type>hvm</type> - </os> - <memory unit="GiB">${guest.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"/> - <interface type="direct"> - <source dev="${hostNic}" mode="bridge"/> - <mac address="${guest.mac}"/> - <model type="virtio"/> - </interface> - </devices> - <features> - <acpi/> - </features> - </domain> - ''; - in - '' - uuid="$(${pkgs.libvirt}/bin/virsh domuuid '${name}' || true)" - ${pkgs.libvirt}/bin/virsh define <(sed "s/UUID/$uuid/" '${xml}') - ${pkgs.libvirt}/bin/virsh start '${name}' - ''; - preStop = '' - ${pkgs.libvirt}/bin/virsh shutdown '${name}' - let "timeout = $(date +%s) + 10" - while [ "$(${pkgs.libvirt}/bin/virsh list --name | grep --count '^${name}$')" -gt 0 ]; do - if [ "$(date +%s)" -ge "$timeout" ]; then - # Meh, we warned it... - ${pkgs.libvirt}/bin/virsh destroy '${name}' - else - # The machine is still running, let's give it some time to shut down - sleep 0.5 - fi - done - ''; - } - ) guests; - }; -} diff --git a/nix/modules/x86_64-linux.nix b/nix/modules/x86_64-linux.nix index b913c68..62945b3 100644 --- a/nix/modules/x86_64-linux.nix +++ b/nix/modules/x86_64-linux.nix @@ -4,5 +4,7 @@ ./r2wars-web ./remarvin ./filebrowser + # ./libvirtnix + ./goapp-frontend ]; } |