VM works on SNP server

This commit is contained in:
ghe0 2024-12-13 03:10:03 +02:00
parent a8347f633d
commit 82273f63e5
Signed by: ghe0
GPG Key ID: 451028EE56A0FBB4
6 changed files with 57 additions and 48 deletions

2
Cargo.lock generated

@ -149,7 +149,7 @@ dependencies = [
] ]
[[package]] [[package]]
name = "daemon" name = "detee-snp-daemon"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",

@ -1,5 +1,5 @@
[package] [package]
name = "daemon" name = "detee-snp-daemon"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"

18
scripts/start_qemu_vm.sh Normal file → Executable file

@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
[[ -z "$VM_UUID" ]] || { [[ -z "$VM_UUID" ]] && {
echo "Environment variable VM_UUID is not set." echo "Environment variable VM_UUID is not set."
exit 1 exit 1
} }
@ -17,6 +17,7 @@ done
interfaces=$(env | grep -oE '^NETWORK_INTERFACE_[0-9]*') interfaces=$(env | grep -oE '^NETWORK_INTERFACE_[0-9]*')
nat_configured="false" nat_configured="false"
vtap_fd_counter=3
while read -r interface; do while read -r interface; do
interface_type="$( echo ${!interface} | cut -d '_' -f1 )" interface_type="$( echo ${!interface} | cut -d '_' -f1 )"
@ -24,13 +25,21 @@ while read -r interface; do
if [[ "$interface_type" == "macvtap" || "$interface_type" == "ipvtap" ]]; then if [[ "$interface_type" == "macvtap" || "$interface_type" == "ipvtap" ]]; then
interface_device="$( echo ${!interface} | cut -d '_' -f2 )" interface_device="$( echo ${!interface} | cut -d '_' -f2 )"
interface_name="$( echo ${!interface} | cut -d '_' -f3 )" interface_name="$( echo ${!interface} | cut -d '_' -f3 )"
if [[ "$interface_type" == "macvtap" ]]; then
ip link add link $interface_device name $interface_name type $interface_type mode bridge ip link add link $interface_device name $interface_name type $interface_type mode bridge
else
ip link add link $interface_device name $interface_name type $interface_type
fi
ip link set $interface_name up ip link set $interface_name up
ip link set $interface_name promisc on ip link set $interface_name promisc on
vtap_index="$(cat /sys/class/net/${interface_name}/ifindex)" vtap_index="$(cat /sys/class/net/${interface_name}/ifindex)"
vtap_addr="$(cat /sys/class/net/${interface_name}/address)" vtap_addr="$(cat /sys/class/net/${interface_name}/address)"
qemu_device_params="-netdev tap,id=hostnet1,fd=3 3<>/dev/tap${macvtap_index}"
qemu_device_params+=" -device virtio-net-pci,netdev=hostnet1,mac=${macvtap_addr},romfile=" fd_number=$vtap_fd_counter
exec {fd_number}<> /dev/tap${vtap_index}
qemu_device_params="-netdev tap,id=hostnet1,fd=${fd_number} -device virtio-net-pci,netdev=hostnet1,mac=${vtap_addr},romfile="
((vtap_fd_counter++))
fi fi
if [[ "$interface_type" == "NAT" && "$nat_configured" == "false" ]]; then if [[ "$interface_type" == "NAT" && "$nat_configured" == "false" ]]; then
@ -54,7 +63,8 @@ vm_disk="/root/dtrfs/arch-1-ghe0.qcow2"
qemu-system-x86_64 $qemu_device_params \ qemu-system-x86_64 $qemu_device_params \
-enable-kvm -cpu $CPU_TYPE -vga none \ -enable-kvm -cpu $CPU_TYPE -vga none \
-machine q35,confidential-guest-support=sev0,memory-backend=ram1 \ -machine q35,confidential-guest-support=sev0,memory-backend=ram1 \
-smp $VCPUS,maxcpus=$VCPUS -m $MEMORY,slots=5,maxmem=$MAX_MEMORY \ -smp $VCPUS,maxcpus=$VCPUS \
-m $MEMORY,slots=5,maxmem=$MAX_MEMORY \
-no-reboot -bios /usr/share/edk2/ovmf/OVMF.amdsev.fd \ -no-reboot -bios /usr/share/edk2/ovmf/OVMF.amdsev.fd \
-drive file=${DISK},if=none,id=disk0,format=qcow2 \ -drive file=${DISK},if=none,id=disk0,format=qcow2 \
-device virtio-blk-pci,drive=disk0 \ -device virtio-blk-pci,drive=disk0 \

@ -3,7 +3,7 @@
pub(crate) const DEFAULT_OVMF: &str = "/usr/share/edk2/ovmf/OVMF.amdsev.fd"; pub(crate) const DEFAULT_OVMF: &str = "/usr/share/edk2/ovmf/OVMF.amdsev.fd";
pub(crate) const VM_BOOT_DIR: &str = "/var/lib/detee/boot/"; pub(crate) const VM_BOOT_DIR: &str = "/var/lib/detee/boot/";
pub(crate) const VM_CONFIG_DIR: &str = "/etc/detee/daemon/vms/"; pub(crate) const VM_CONFIG_DIR: &str = "/etc/detee/daemon/vms/";
pub(crate) const DAEMON_CONFIG_PATH: &str = "/etc/detee/daemon/config.json"; pub(crate) const DAEMON_CONFIG_PATH: &str = "/etc/detee/daemon/config.yaml";
pub(crate) const START_VM_SCRIPT: &str = "/usr/local/bin/detee/start_qemu_vm.sh"; pub(crate) const START_VM_SCRIPT: &str = "/usr/local/bin/detee/start_qemu_vm.sh";
// TODO: research if other CPU types provide better performance // TODO: research if other CPU types provide better performance
pub(crate) const QEMU_VM_CPU_TYPE: &str = "EPYC-v4"; pub(crate) const QEMU_VM_CPU_TYPE: &str = "EPYC-v4";

@ -7,29 +7,12 @@ use crate::config::Config;
use crate::state::NewVMRequest; use crate::state::NewVMRequest;
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = Config::from_file("prod_setting/config1.yaml")?; let args: Vec<String> = std::env::args().collect();
let config = Config::from_file(crate::constants::DAEMON_CONFIG_PATH)?;
let mut res = state::Resources::new(&config.volumes); let mut res = state::Resources::new(&config.volumes);
// println!("{:#?}", config); let new_vm_req = NewVMRequest::from_file(&args[1])?;
// let config = Config::from_file("test_data/config2.yaml")?;
// println!("{:#?}", config);
// let config = Config::from_file("test_data/config3.yaml")?;
// println!("{:#?}", config);
// let config = Config::from_file("test_data/config4.yaml")?;
// println!("{:#?}", config);
// let config = Config::from_file("test_data/config5.yaml")?;
// println!("{:#?}", config);
let new_vm_req = NewVMRequest::from_file("test_data/new_vm_req3.yaml")?;
// println!("{:#?}", new_vm_req);
// let new_vm_req = NewVMRequest::from_file("test_data/new_vm_req2.yaml")?;
// println!("{:#?}", new_vm_req);
// let new_vm_req = NewVMRequest::from_file("test_data/new_vm_req3.yaml")?;
// println!("{:#?}", new_vm_req);
// let new_vm_req = NewVMRequest::from_file("test_data/new_vm_req4.yaml")?;
// println!("{:#?}", new_vm_req);
let vm = state::VM::new(new_vm_req, &config, &mut res); let vm = state::VM::new(new_vm_req, &config, &mut res);
println!("vm: {:#?}", vm); println!("Got VM: {:#?}", vm);
println!("{:?}", vm.unwrap().start()); println!("Starting VM... \n{:?}", vm.unwrap().start());
Ok(()) Ok(())
} }

@ -38,7 +38,7 @@ impl Resources {
available_gb: config_vol.max_reservation_gb, available_gb: config_vol.max_reservation_gb,
}); });
} }
Resources { let mut res = Resources {
reserved_vcpus: 0, reserved_vcpus: 0,
reserved_memory: 0, reserved_memory: 0,
reserved_ports: HashSet::new(), reserved_ports: HashSet::new(),
@ -46,7 +46,9 @@ impl Resources {
reserved_ips: HashSet::new(), reserved_ips: HashSet::new(),
reserved_if_names: HashSet::new(), reserved_if_names: HashSet::new(),
boot_files: HashSet::new(), boot_files: HashSet::new(),
} };
res.scan_boot_files().unwrap();
res
} }
fn available_storage_pool(&mut self, required_gb: usize) -> Option<String> { fn available_storage_pool(&mut self, required_gb: usize) -> Option<String> {
@ -319,9 +321,7 @@ pub struct VM {
// currently hardcoded to EPYC-v4 // currently hardcoded to EPYC-v4
// cpu_type: String, // cpu_type: String,
vcpus: usize, vcpus: usize,
// memory in MB
memory_mb: usize, memory_mb: usize,
// disk size in GB
disk_size_gb: usize, disk_size_gb: usize,
kernel_sha: String, kernel_sha: String,
dtrfs_sha: String, dtrfs_sha: String,
@ -510,7 +510,7 @@ impl VM {
for nic in self.nics.iter() { for nic in self.nics.iter() {
for ip in nic.ips.iter() { for ip in nic.ips.iter() {
ip_string += &format!( ip_string += &format!(
"detee_net_eth{}={}_{}_{}", "detee_net_eth{}={}_{}_{} ",
i, ip.address, ip.mask, ip.gateway i, ip.address, ip.mask, ip.gateway
); );
} }
@ -527,12 +527,14 @@ impl VM {
let mut i = 0; let mut i = 0;
for nic in self.nics.iter() { for nic in self.nics.iter() {
let mut interface = String::new(); let mut interface = String::new();
interface += &format!("NETWORK_INTERFACE_{}={}", i, nic.if_config.if_type()); interface += &format!(r#"export NETWORK_INTERFACE_{}="{}"#, i, nic.if_config.if_type());
// device is currently ignored in case of NAT cause we assume QEMU userspace NAT // device is currently ignored in case of NAT cause we assume QEMU userspace NAT
if let Some(vtap_name) = nic.if_config.vtap_name() { if let Some(vtap_name) = nic.if_config.vtap_name() {
interface += &format!("_{}_{}", nic.if_config.device_name(), vtap_name); interface += &format!("_{}_{}", nic.if_config.device_name(), vtap_name);
} }
vars += &format!("{}\n", interface); interface += r#"""#;
vars += &format!(r#"{}"#, interface);
vars += "\n";
i += 1; i += 1;
} }
@ -541,17 +543,32 @@ impl VM {
ports += &format!("{}:{} ", port.0, port.1); ports += &format!("{}:{} ", port.0, port.1);
} }
if ports != "" { if ports != "" {
vars += &format!("NAT_PORT_FW={}", ports.trim_end()); vars += &format!(r#"NAT_PORT_FW="{}""#, ports.trim_end());
vars += "\n";
} }
vars += &format!("KERNEL={}\n", VM_BOOT_DIR.to_string() + &self.kernel_sha); vars += &format!(
vars += &format!("INITRD={}\n", VM_BOOT_DIR.to_string() + &self.dtrfs_sha); r#"export KERNEL="{}""#,
vars += &format!("PARAMS={}\n", self.kernel_params()); VM_BOOT_DIR.to_string() + &self.kernel_sha
vars += &format!("CPU_TYPE={}\n", QEMU_VM_CPU_TYPE); );
vars += &format!("VCPUS={}\n", self.vcpus); vars += "\n";
vars += &format!("MEMORY={}MB\n", self.memory_mb); vars += &format!(
vars += &format!("MAX_MEMORY={}MB\n", self.memory_mb + 256); r#"export INITRD="{}""#,
vars += &format!("DISK={}\n", self.disk_path()); VM_BOOT_DIR.to_string() + &self.dtrfs_sha
);
vars += "\n";
vars += &format!(r#"export PARAMS="{}""#, self.kernel_params());
vars += "\n";
vars += &format!(r#"export CPU_TYPE="{}""#, QEMU_VM_CPU_TYPE);
vars += "\n";
vars += &format!(r#"export VCPUS="{}""#, self.vcpus);
vars += "\n";
vars += &format!(r#"export MEMORY="{}M""#, self.memory_mb);
vars += "\n";
vars += &format!(r#"export MAX_MEMORY="{}M""#, self.memory_mb + 256);
vars += "\n";
vars += &format!(r#"export DISK="{}""#, self.disk_path());
vars += "\n";
let mut file = File::create(VM_CONFIG_DIR.to_string() + &self.uuid)?; let mut file = File::create(VM_CONFIG_DIR.to_string() + &self.uuid)?;
file.write_all(vars.as_bytes())?; file.write_all(vars.as_bytes())?;
@ -600,8 +617,7 @@ impl VM {
contents += &format!("[Install]\n"); contents += &format!("[Install]\n");
contents += &format!("WantedBy=multi-user.target\n"); contents += &format!("WantedBy=multi-user.target\n");
let mut file = let mut file = File::create("/etc/systemd/system/".to_string() + &self.uuid + ".service")?;
File::create("/tmp/etc/systemd/system/".to_string() + &self.uuid + ".service")?;
file.write_all(contents.as_bytes())?; file.write_all(contents.as_bytes())?;
Ok(()) Ok(())
} }