From 9ed17eca0698237e3873d042796b7fbeed35fb6e Mon Sep 17 00:00:00 2001 From: eater <=@eater.me> Date: Wed, 21 Apr 2021 12:16:55 +0200 Subject: [PATCH] Initial commit --- .gitignore | 1 + Cargo.lock | 297 +++++++++++++++++++++++++++++++ Cargo.toml | 2 + config/example.toml | 21 +++ resources/qemu-cmdline.txt | 146 +++++++++++++++ vore-core/Cargo.toml | 13 ++ vore-core/src/instance_config.rs | 269 ++++++++++++++++++++++++++++ vore-core/src/lib.rs | 3 + vore/Cargo.toml | 11 ++ vore/src/main.rs | 5 + vored/Cargo.toml | 11 ++ vored/src/instance.rs | 141 +++++++++++++++ vored/src/main.rs | 9 + 13 files changed, 929 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 config/example.toml create mode 100644 resources/qemu-cmdline.txt create mode 100644 vore-core/Cargo.toml create mode 100644 vore-core/src/instance_config.rs create mode 100644 vore-core/src/lib.rs create mode 100644 vore/Cargo.toml create mode 100644 vore/src/main.rs create mode 100644 vored/Cargo.toml create mode 100644 vored/src/instance.rs create mode 100644 vored/src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..564b49e --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,297 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "aho-corasick" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b" + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "config" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1b9d958c2b1368a663f05538fc1b5975adce1e19f435acceae987aceeeb369" +dependencies = [ + "lazy_static", + "nom", + "rust-ini", + "serde 1.0.125", + "serde-hjson", + "serde_json", + "toml", + "yaml-rust", +] + +[[package]] +name = "itoa" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" + +[[package]] +name = "kiam" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fec32b07a91b3ac2bca298af854b84860969f099811888fe0d9edf7bee95ce2" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lexical-core" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21f866863575d0e1d654fbeeabdc927292fdf862873dc3c96c6f753357e13374" +dependencies = [ + "arrayvec", + "bitflags", + "cfg-if", + "ryu", + "static_assertions", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" + +[[package]] +name = "memchr" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" + +[[package]] +name = "nom" +version = "5.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" +dependencies = [ + "lexical-core", + "memchr", + "version_check", +] + +[[package]] +name = "num-traits" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" +dependencies = [ + "num-traits 0.2.14", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "proc-macro2" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548" + +[[package]] +name = "rust-ini" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2" + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "serde" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8" + +[[package]] +name = "serde" +version = "1.0.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-hjson" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a3a4e0ea8a88553209f6cc6cfe8724ecad22e1acf372793c27d995290fe74f8" +dependencies = [ + "lazy_static", + "num-traits 0.1.43", + "regex", + "serde 0.8.23", +] + +[[package]] +name = "serde_derive" +version = "1.0.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" +dependencies = [ + "itoa", + "ryu", + "serde 1.0.125", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "syn" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48fe99c6bd8b1cc636890bcc071842de909d902c81ac7dab53ba33c421ab8ffb" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde 1.0.125", +] + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "version_check" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" + +[[package]] +name = "vore" +version = "0.1.0" +dependencies = [ + "anyhow", + "vore-core", +] + +[[package]] +name = "vore-core" +version = "0.1.0" +dependencies = [ + "anyhow", + "config", + "kiam", + "serde 1.0.125", +] + +[[package]] +name = "vored" +version = "0.1.0" +dependencies = [ + "anyhow", + "vore-core", +] + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..b681f02 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = ["vored", "vore", "vore-core"] diff --git a/config/example.toml b/config/example.toml new file mode 100644 index 0000000..abb5b57 --- /dev/null +++ b/config/example.toml @@ -0,0 +1,21 @@ +[machine] +name = "win10" +memory = "12G" + +[cpu] +amount = 12 + +[[disk]] +path = "/dev/disk/by-id/wwn-0x500a0751f008e09d" + +[[disk]] +path = "/dev/disk/by-id/wwn-0x5002538e4038852d" + +[uefi] +enabled = true + +[scream] +enabled = true + +[looking-glass] +enabled = true \ No newline at end of file diff --git a/resources/qemu-cmdline.txt b/resources/qemu-cmdline.txt new file mode 100644 index 0000000..f40bff6 --- /dev/null +++ b/resources/qemu-cmdline.txt @@ -0,0 +1,146 @@ +/bin/qemu-system-x86_64 +-name +guest=win10,debug-threads=on +-S +-object +secret,id=masterKey0,format=raw,file=/var/lib/libvirt/qemu/domain-1-win10/master-key.aes +-blockdev +{"driver":"file","filename":"/usr/share/OVMF/OVMF_CODE.fd","node-name":"libvirt-pflash0-storage","auto-read-only":true,"discard":"unmap"} +-blockdev +{"node-name":"libvirt-pflash0-format","read-only":true,"driver":"raw","file":"libvirt-pflash0-storage"} +-blockdev +{"driver":"file","filename":"/usr/share/OVMF/OVMF_VARS.fd","node-name":"libvirt-pflash1-storage","auto-read-only":true,"discard":"unmap"} +-blockdev +{"node-name":"libvirt-pflash1-format","read-only":false,"driver":"raw","file":"libvirt-pflash1-storage"} +-machine +pc-q35-3.0,accel=kvm,usb=off,vmport=off,dump-guest-core=off,kernel_irqchip=on,pflash0=libvirt-pflash0-format,pflash1=libvirt-pflash1-format,memory-backend=pc.ram +-cpu +host,migratable=on,hv-time,hv-relaxed,hv-vapic,hv-spinlocks=0x1fff,hv-vendor-id=whatever,kvm=off +-m +16384 +-object +memory-backend-ram,id=pc.ram,size=17179869184 +-overcommit +mem-lock=off +-smp +12,sockets=1,dies=1,cores=6,threads=2 +-object +iothread,id=iothread1 +-uuid +29d2307c-ee76-4471-85f1-ee758121a99b +-no-user-config +-nodefaults +-chardev +socket,id=charmonitor,fd=29,server=on,wait=off +-mon +chardev=charmonitor,id=monitor,mode=control +-rtc +base=localtime,driftfix=slew +-global +kvm-pit.lost_tick_policy=delay +-no-hpet +-no-shutdown +-global +ICH9-LPC.disable_s3=1 +-global +ICH9-LPC.disable_s4=1 +-boot +strict=on +-device +i82801b11-bridge,id=pci.1,bus=pcie.0,addr=0x1e +-device +pci-bridge,chassis_nr=2,id=pci.2,bus=pci.1,addr=0x0 +-device +pcie-root-port,port=0x10,chassis=3,id=pci.3,bus=pcie.0,multifunction=on,addr=0x2 +-device +pcie-root-port,port=0x11,chassis=4,id=pci.4,bus=pcie.0,addr=0x2.0x1 +-device +pcie-root-port,port=0x12,chassis=5,id=pci.5,bus=pcie.0,addr=0x2.0x2 +-device +pcie-root-port,port=0x13,chassis=6,id=pci.6,bus=pcie.0,addr=0x2.0x3 +-device +pcie-root-port,port=0x14,chassis=7,id=pci.7,bus=pcie.0,addr=0x2.0x4 +-device +pcie-root-port,port=0x15,chassis=8,id=pci.8,bus=pcie.0,addr=0x2.0x5 +-device +pcie-root-port,port=0x16,chassis=9,id=pci.9,bus=pcie.0,addr=0x2.0x6 +-device +pcie-root-port,port=0x17,chassis=10,id=pci.10,bus=pcie.0,addr=0x2.0x7 +-device +pcie-root-port,port=0x18,chassis=11,id=pci.11,bus=pcie.0,multifunction=on,addr=0x3 +-device +pcie-root-port,port=0x19,chassis=12,id=pci.12,bus=pcie.0,addr=0x3.0x1 +-device +pcie-root-port,port=0x1a,chassis=13,id=pci.13,bus=pcie.0,addr=0x3.0x2 +-device +pcie-root-port,port=0x1b,chassis=14,id=pci.14,bus=pcie.0,addr=0x3.0x3 +-device +nec-usb-xhci,id=usb,bus=pci.3,addr=0x0 +-device +virtio-scsi-pci,id=scsi0,bus=pci.9,addr=0x0 +-device +virtio-serial-pci,id=virtio-serial0,bus=pci.4,addr=0x0 +-blockdev +{"driver":"host_device","filename":"/dev/disk/by-id/wwn-0x500a0751f008e09d","aio":"threads","node-name":"libvirt-2-storage","cache":{"direct":false,"no-flush":false},"auto-read-only":true,"discard":"unmap"} +-blockdev +{"node-name":"libvirt-2-format","read-only":false,"discard":"unmap","cache":{"direct":false,"no-flush":false},"driver":"raw","file":"libvirt-2-storage"} +-device +scsi-hd,bus=scsi0.0,channel=0,scsi-id=0,lun=3,device_id=drive-scsi0-0-0-3,drive=libvirt-2-format,id=scsi0-0-0-3,bootindex=1,write-cache=on +-blockdev +{"driver":"host_device","filename":"/dev/disk/by-id/wwn-0x5002538e4038852d","aio":"threads","node-name":"libvirt-1-storage","cache":{"direct":false,"no-flush":false},"auto-read-only":true,"discard":"unmap"} +-blockdev +{"node-name":"libvirt-1-format","read-only":false,"discard":"unmap","cache":{"direct":false,"no-flush":false},"driver":"raw","file":"libvirt-1-storage"} +-device +virtio-blk-pci,bus=pci.11,addr=0x0,drive=libvirt-1-format,id=virtio-disk4,write-cache=on +-netdev +tap,fd=31,id=hostnet0,vhost=on,vhostfd=32 +-device +virtio-net-pci,netdev=hostnet0,id=net0,mac=52:54:00:9e:48:64,bus=pci.8,addr=0x0 +-netdev +tap,fd=33,id=hostnet1,vhost=on,vhostfd=34 +-device +virtio-net-pci,netdev=hostnet1,id=net1,mac=52:54:00:fa:57:7d,bus=pci.10,addr=0x0 +-chardev +pty,id=charserial0 +-device +isa-serial,chardev=charserial0,id=serial0 +-chardev +spicevmc,id=charchannel0,name=vdagent +-device +virtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel0,id=channel0,name=com.redhat.spice.0 +-device +virtio-mouse-pci,id=input0,bus=pci.13,addr=0x0 +-audiodev +id=audio1,driver=spice +-spice +unix,addr=/tmp/win.sock,disable-ticketing=on,seamless-migration=on +-device +cirrus-vga,id=video0,bus=pcie.0,addr=0x1 +-device +ich9-intel-hda,id=sound0,bus=pcie.0,addr=0x1b +-device +hda-duplex,id=sound0-codec0,bus=sound0.0,cad=0,audiodev=audio1 +-device +vfio-pci,host=0000:08:00.0,id=hostdev0,bus=pci.6,addr=0x0 +-device +vfio-pci,host=0000:08:00.1,id=hostdev1,bus=pci.7,addr=0x0 +-device +vfio-pci,host=0000:0a:00.3,id=hostdev2,bus=pci.14,addr=0x0 +-device +virtio-balloon-pci,id=balloon0,bus=pci.5,addr=0x0 +-device +usb-kbd +-audiodev +pa,id=win10,server=/run/user/1000/pulse/native +-sandbox +on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny +-object +memory-backend-file,id=shmmem-shmem0,mem-path=/dev/shm/looking-glass,size=67108864,share=on +-device +ivshmem-plain,id=shmem0,memdev=shmmem-shmem0,bus=pci.2,addr=0x1 +-object +memory-backend-file,id=shmmem-shmem1,mem-path=/dev/shm/scream,size=2097152,share=on +-device +ivshmem-plain,id=shmem1,memdev=shmmem-shmem1,bus=pci.2,addr=0x2 +-msg +timestamp=on \ No newline at end of file diff --git a/vore-core/Cargo.toml b/vore-core/Cargo.toml new file mode 100644 index 0000000..b7f3e35 --- /dev/null +++ b/vore-core/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "vore-core" +version = "0.1.0" +authors = ["eater <=@eater.me>"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +config = "0.11.0" +serde = { version = "1.0.125", features = ["serde_derive"] } +anyhow = "1.0.40" +kiam = "0.1" \ No newline at end of file diff --git a/vore-core/src/instance_config.rs b/vore-core/src/instance_config.rs new file mode 100644 index 0000000..f57ed1d --- /dev/null +++ b/vore-core/src/instance_config.rs @@ -0,0 +1,269 @@ +use anyhow::{Context, Error}; +use config::{Config, File, FileFormat, Value}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use std::str::FromStr; + +#[derive(Deserialize, Serialize, Clone, Debug)] +pub struct InstanceConfig { + pub name: String, + pub kvm: bool, + pub memory: u64, + pub cpu: CpuConfig, + pub disks: Vec, + pub uefi: UefiConfig, + pub looking_glass: LookingGlassConfig, + pub scream: ScreamConfig, +} + +impl InstanceConfig { + pub fn from_toml(toml: &str) -> Result { + let toml = Config::new().with_merged(File::from_str(toml, FileFormat::Toml))?; + Self::from_config(toml) + } + + pub fn from_config(config: Config) -> Result { + let mut instance_config = InstanceConfig::default(); + if let Ok(name) = config.get_str("machine.name") { + instance_config.name = name + } + + if let Ok(kvm) = config.get::("machine.kvm") { + instance_config.kvm = kvm.into_bool().context("machine.kvm should be a boolean")?; + } + + if let Ok(mem) = config.get::("machine.memory") { + let mem = mem + .into_str() + .context("machine.memory should be a string or number")?; + instance_config.memory = parse_size(&mem)?; + } + + if let Ok(cpu) = config.get_table("cpu") { + instance_config.cpu.apply_table(cpu)? + } + + if let Ok(disks) = config.get::("disk") { + let arr = disks.into_array().context("disk should be an array")?; + for (i, disk) in arr.into_iter().enumerate() { + let table = disk + .into_table() + .with_context(|| format!("disk[{}] should be a table", i))?; + instance_config.disks.push(DiskConfig::from_table(table)?); + } + } + + Ok(instance_config) + } +} + +impl Default for InstanceConfig { + fn default() -> Self { + InstanceConfig { + name: "vore".to_string(), + kvm: true, + // 2 GB + memory: 2 * 1024 * 1024 * 1024, + cpu: Default::default(), + disks: vec![], + uefi: Default::default(), + looking_glass: Default::default(), + scream: Default::default(), + } + } +} + +#[derive(Deserialize, Serialize, Clone, Debug)] +pub struct CpuConfig { + pub amount: u64, + pub cores: u64, + pub threads: u64, + pub dies: u64, + pub sockets: u64, +} + +impl Default for CpuConfig { + fn default() -> Self { + CpuConfig { + amount: 2, + cores: 1, + threads: 2, + dies: 1, + sockets: 1, + } + } +} + +fn get_positive_number_from_table( + table: &HashMap, + key: &str, + prefix: &str, +) -> Result, Error> { + table + .get(key) + .cloned() + .map(|x| { + x.into_int() + .with_context(|| format!("Failed to parse {}.{} as number", prefix, key)) + .and_then(|x| { + Some(x) + .filter(|x| !x.is_negative()) + .map(|x| x as u64) + .ok_or_else(|| { + anyhow::Error::msg(format!("{}.{} can't be negative", prefix, key)) + }) + }) + }) + .transpose() +} + +impl CpuConfig { + fn apply_table(&mut self, table: HashMap) -> Result<(), anyhow::Error> { + if let Some(amount) = get_positive_number_from_table(&table, "amount", "cpu")? { + self.amount = amount; + } + + if let Some(cores) = get_positive_number_from_table(&table, "cores", "cpu")? { + self.cores = cores; + } + + if let Some(threads) = get_positive_number_from_table(&table, "threads", "cpu")? { + self.threads = threads; + } + + if let Some(dies) = get_positive_number_from_table(&table, "dies", "cpu")? { + self.dies = dies; + } + + if let Some(sockets) = get_positive_number_from_table(&table, "sockets", "cpu")? { + self.sockets = sockets; + } + + if !table.contains_key("amount") { + self.amount = self.sockets * self.dies * self.cores * self.threads; + } else { + if table + .keys() + .any(|x| ["cores", "sockets", "dies", "threads"].contains(&x.as_str())) + { + let calc_amount = self.sockets * self.dies * self.cores * self.threads; + if self.amount != calc_amount { + Err(anyhow::Error::msg(format!("Amount of cpu's ({}) from sockets ({}), dies ({}), cores ({}) and threads ({}) differs from specified ({}) cpu's", calc_amount, self.sockets, self.dies, self.cores, self.threads, self.amount)))?; + } + } else { + if (self.amount % 2) == 0 { + self.cores = self.amount / 2; + } else { + self.threads = 1; + self.cores = self.amount; + } + } + } + + Ok(()) + } +} + +fn parse_size(orig_input: &str) -> Result { + let input = orig_input.to_string().to_lowercase().replace(" ", ""); + let mut input = input.strip_suffix("b").unwrap_or(&input); + let mut modifier: u64 = 1; + + if input.chars().last().unwrap_or('_').is_alphabetic() { + modifier = match input.chars().last().unwrap() { + 'k' => { + return Err(anyhow::Error::msg( + "size can only be specified in megabytes or larger", + )); + } + 'm' => 1, + 'g' => 1024, + 't' => 1024 * 1024, + _ => { + return Err(anyhow::Error::msg(format!( + "'{}' is not a valid size", + orig_input + ))); + } + }; + + input = &input[..input.len() - 1]; + } + + if input.len() == 0 { + return Err(anyhow::Error::msg(format!( + "'{}' is not a valid size", + orig_input + ))); + } + + u64::from_str(input) + .context(format!("'{}' is not a valid size", orig_input)) + .map(|x| x * modifier) +} + +#[derive(Deserialize, Serialize, Clone, Debug)] +pub struct UefiConfig { + pub enabled: bool, +} + +impl Default for UefiConfig { + fn default() -> Self { + UefiConfig { enabled: false } + } +} + +#[derive(Deserialize, Serialize, Clone, Debug)] +pub struct ScreamConfig { + pub enabled: bool, +} + +impl Default for ScreamConfig { + fn default() -> Self { + ScreamConfig { enabled: false } + } +} + +#[derive(Deserialize, Serialize, Clone, Debug)] +pub struct LookingGlassConfig { + pub enabled: bool, +} + +impl Default for LookingGlassConfig { + fn default() -> Self { + LookingGlassConfig { enabled: false } + } +} + +#[derive(Deserialize, Serialize, Clone, Debug)] +pub struct DiskConfig { + pub disk_type: String, + pub path: String, +} + +impl DiskConfig { + pub fn from_table(table: HashMap) -> Result { + let path = table + .get("path") + .cloned() + .ok_or_else(|| anyhow::Error::msg("Disk needs a path"))? + .into_str() + .context("Disk path must be a string")?; + + let disk_type = if let Some(disk_type) = table.get("type").cloned() { + disk_type.into_str()? + } else { + (kiam::when! { + path.starts_with("/dev") => "raw", + path.ends_with(".qcow2") => "qcow2", + _ => return Err(anyhow::Error::msg("Can't figure out from path what type of disk driver should be used")) + }).to_string() + }; + + let disk = DiskConfig { disk_type, path }; + + // TODO: Add blockdev details + + Ok(disk) + } +} diff --git a/vore-core/src/lib.rs b/vore-core/src/lib.rs new file mode 100644 index 0000000..292c7d2 --- /dev/null +++ b/vore-core/src/lib.rs @@ -0,0 +1,3 @@ +mod instance_config; + +pub use instance_config::*; diff --git a/vore/Cargo.toml b/vore/Cargo.toml new file mode 100644 index 0000000..8e5a950 --- /dev/null +++ b/vore/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "vore" +version = "0.1.0" +authors = ["eater <=@eater.me>"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.40" +vore-core = { path = "../vore-core" } \ No newline at end of file diff --git a/vore/src/main.rs b/vore/src/main.rs new file mode 100644 index 0000000..432de46 --- /dev/null +++ b/vore/src/main.rs @@ -0,0 +1,5 @@ +use vore_core::InstanceConfig; + +fn main() { + let cfg = InstanceConfig::from_toml(include_str!("../../config/example.toml")).unwrap(); +} diff --git a/vored/Cargo.toml b/vored/Cargo.toml new file mode 100644 index 0000000..04461ee --- /dev/null +++ b/vored/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "vored" +version = "0.1.0" +authors = ["eater <=@eater.me>"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.40" +vore-core = { path = "../vore-core" } \ No newline at end of file diff --git a/vored/src/instance.rs b/vored/src/instance.rs new file mode 100644 index 0000000..1ca6ba6 --- /dev/null +++ b/vored/src/instance.rs @@ -0,0 +1,141 @@ +use std::fmt::{Display, Formatter}; +use std::process::Child; +use vore_core::InstanceConfig; + +#[derive(Debug)] +pub struct Instance { + config: InstanceConfig, + qemu: Option, +} + +impl Instance { + pub fn from_config(config: InstanceConfig) -> Instance { + Instance { config, qemu: None } + } + + pub fn spawn_qemu(&self) -> Result<(), anyhow::Error> { + Ok(()) + } +} + +#[derive(Debug, Clone)] +pub struct ArgumentList { + items: Vec, +} + +impl Display for ArgumentList { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let item = self.items.iter().fold(String::new(), |mut x, item| { + if x.len() > 0 { + x.push_str(" ") + } + x.push_str(item.as_str()); + x + }); + f.write_str(&item) + } +} + +impl ArgumentList { + pub fn new(command: &'static str) -> ArgumentList { + ArgumentList { + items: vec![Argument::Borrowed(command)], + } + } + + pub fn pair(&mut self, key: &'static str, value: String) { + self.push(key); + self.push(value); + } +} + +pub trait PushArgument { + fn push(&mut self, argument: T); + fn push_pair(&mut self, key: T, value: T) { + self.push(key); + self.push(value); + } +} + +impl PushArgument for ArgumentList { + fn push(&mut self, argument: String) { + self.items.push(Argument::Owned(argument)) + } +} + +impl PushArgument<&'static str> for ArgumentList { + fn push(&mut self, argument: &'static str) { + self.items.push(Argument::Borrowed(argument)) + } +} + +#[derive(Debug, Clone)] +enum Argument { + Owned(String), + Borrowed(&'static str), +} + +impl Argument { + fn as_str(&self) -> &str { + match self { + Argument::Owned(owned) => &owned, + Argument::Borrowed(borrowed) => borrowed, + } + } +} + +pub fn build_qemu_command(config: &InstanceConfig) -> ArgumentList { + let mut cmd = ArgumentList::new("qemu-system-x86_64"); + cmd.pair("-name", format!("guest={},debug-threads=on", config.name)); + + cmd.push("-S"); + cmd.push("-no-user-config"); + cmd.push("-no-defaults"); + cmd.push("-no-shutdown"); + + if config.kvm { + cmd.push("-enable-kvm"); + } + + cmd.pair("-m", config.memory.to_string()); + + if config.uefi.enabled { + // OVMF will hang if S3 is not disabled + // disable S4 too, since libvirt does that 🤷 + // https://bugs.archlinux.org/task/59465#comment172528 + cmd.push_pair("-global", "ICH9-LPC.disable_s3=1"); + cmd.push_pair("-global", "ICH9-LPC.disable_s4=1"); + } + + cmd.push_pair("-rtc", "driftfix=slew"); + cmd.push_pair("-serial", "stdio"); + + #[cfg(any(target_arch = "x86_64", target_arch = "i686"))] + { + cmd.push_pair("-global", "kvm-pit.lost_tick_policy=discard") + } + + cmd.push("-no-hpet"); + cmd.push_pair("-boot", "strict=on"); + + cmd.pair( + "-smp", + format!( + "{},sockets={},dies={},cores={},threads={}", + config.cpu.amount, + config.cpu.sockets, + config.cpu.dies, + config.cpu.cores, + config.cpu.threads + ), + ); + + cmd.push_pair("-msg", "timestamp=on"); + + cmd +} + +#[derive(Debug)] +pub struct Qemu { + process: Option, +} diff --git a/vored/src/main.rs b/vored/src/main.rs new file mode 100644 index 0000000..e14facc --- /dev/null +++ b/vored/src/main.rs @@ -0,0 +1,9 @@ +use crate::instance::build_qemu_command; +use vore_core::InstanceConfig; + +mod instance; + +fn main() { + let cfg = InstanceConfig::from_toml(include_str!("../../config/example.toml")).unwrap(); + println!("Hello, world! {}", build_qemu_command(&cfg)); +}