correctly assigning and storing IPs and interfaces
This commit is contained in:
		
							parent
							
								
									64a84c48be
								
							
						
					
					
						commit
						d4c5cc2634
					
				| @ -2,7 +2,7 @@ | ||||
| 
 | ||||
| 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_DISK_DIR: &str = "/var/lib/detee/boot/"; | ||||
| pub(crate) const VM_DISK_DIR: &str = "/var/lib/detee/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 START_VM_SCRIPT: &str = "/usr/local/bin/detee/start_qemu_vm.sh"; | ||||
|  | ||||
							
								
								
									
										26
									
								
								src/main.rs
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										26
									
								
								src/main.rs
									
									
									
									
									
								
							| @ -7,8 +7,10 @@ use crate::config::Config; | ||||
| use crate::state::NewVMRequest; | ||||
| 
 | ||||
| fn main() -> Result<(), Box<dyn std::error::Error>> { | ||||
|     // let config = Config::from_file("test_data/config1.yaml")?;
 | ||||
|     let mut res = state::Resources::new(); | ||||
|     let config = Config::from_file("test_data/config2.yaml")?; | ||||
|     // println!("{:#?}", config);
 | ||||
| 
 | ||||
|     // let config = Config::from_file("test_data/config2.yaml")?;
 | ||||
|     // println!("{:#?}", config);
 | ||||
|     // let config = Config::from_file("test_data/config3.yaml")?;
 | ||||
| @ -17,13 +19,21 @@ fn main() -> Result<(), Box<dyn std::error::Error>> { | ||||
|     // println!("{:#?}", config);
 | ||||
|     // let config = Config::from_file("test_data/config5.yaml")?;
 | ||||
|     // println!("{:#?}", config);
 | ||||
|     let new_vm_req = NewVMRequest::from_file("test_data/new_vm_req1.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); | ||||
|     // 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 new_vm_req = NewVMRequest::from_file("test_data/new_vm_req4.yaml")?; | ||||
|     let vm = state::VM::new(new_vm_req, &config, &mut res); | ||||
|     let new_vm_req = NewVMRequest::from_file("test_data/new_vm_req4.yaml")?; | ||||
|     let vm = state::VM::new(new_vm_req, &config, &mut res); | ||||
|     println!("vm: {:#?}", vm); | ||||
|     println!("res: {:#?}", res); | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
							
								
								
									
										100
									
								
								src/state.rs
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										100
									
								
								src/state.rs
									
									
									
									
									
								
							| @ -3,6 +3,7 @@ use crate::config::Config; | ||||
| use crate::constants::*; | ||||
| use anyhow::anyhow; | ||||
| use anyhow::Result; | ||||
| use serde::Deserialize; | ||||
| use sha2::{Digest, Sha256}; | ||||
| use std::collections::HashSet; | ||||
| use std::fs; | ||||
| @ -12,8 +13,8 @@ use std::io::Read; | ||||
| use std::io::Write; | ||||
| use std::path::Path; | ||||
| use std::process::Command; | ||||
| use serde::Deserialize; | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub struct Resources { | ||||
|     // QEMU does not support MHz limiation
 | ||||
|     reserved_vcpus: usize, | ||||
| @ -27,7 +28,18 @@ pub struct Resources { | ||||
| } | ||||
| 
 | ||||
| impl Resources { | ||||
|     fn reserve_ports(&mut self, extra_ports: usize, config: &Config) -> Vec<u16> { | ||||
|     pub fn new() -> Self { | ||||
|         Resources { | ||||
|             reserved_vcpus: 0, | ||||
|             reserved_memory: 0, | ||||
|             reserved_ports: HashSet::new(), | ||||
|             storage_pools: Vec::new(), | ||||
|             reserved_ips: HashSet::new(), | ||||
|             reserved_if_names: HashSet::new(), | ||||
|             boot_files: HashSet::new(), | ||||
|         } | ||||
|     } | ||||
|     fn get_free_ports(&mut self, extra_ports: usize, config: &Config) -> Vec<u16> { | ||||
|         use rand::Rng; | ||||
|         let total_ports = extra_ports + 1; | ||||
|         if config.public_port_range.len() < self.reserved_ports.len() + total_ports as usize { | ||||
| @ -40,7 +52,7 @@ impl Resources { | ||||
|         for _ in 0..total_ports { | ||||
|             for _ in 0..5 { | ||||
|                 let port = rand::thread_rng().gen_range(config.public_port_range.clone()); | ||||
|                 if self.reserved_ports.insert(port) { | ||||
|                 if self.reserved_ports.get(&port).is_none() { | ||||
|                     published_ports.push(port); | ||||
|                 } | ||||
|                 break; | ||||
| @ -50,7 +62,7 @@ impl Resources { | ||||
|         published_ports | ||||
|     } | ||||
| 
 | ||||
|     fn reserve_vm_if(&mut self) -> String { | ||||
|     fn get_free_vm_name(&mut self) -> String { | ||||
|         use rand::{distributions::Alphanumeric, Rng}; | ||||
|         loop { | ||||
|             let mut interface_name: String = rand::thread_rng() | ||||
| @ -65,20 +77,21 @@ impl Resources { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn reserve_public_ipv4(&mut self, config: &Config) -> Option<VMNIC> { | ||||
|     fn get_free_ipv4(&mut self, config: &Config) -> Option<VMNIC> { | ||||
|         for nic in config.network_interfaces.iter() { | ||||
|             for range in nic.ipv4.iter() { | ||||
|                 for ip in range.subnet.iter() { | ||||
|                 for ip in range.subnet.iter().skip(1) { | ||||
|                     if !range.reserved_addrs.contains(&ip.address()) | ||||
|                         && !self.reserved_ips.contains(&ip.to_string()) | ||||
|                         && !self.reserved_ips.contains(&ip.address().to_string()) | ||||
|                         && ip.address() != range.gateway | ||||
|                     { | ||||
|                         let if_config = match nic.driver { | ||||
|                             crate::config::InterfaceType::MACVTAP => InterfaceConfig::MACVTAP { | ||||
|                                 name: self.reserve_vm_if(), | ||||
|                                 name: self.get_free_vm_name(), | ||||
|                                 device: nic.device.clone(), | ||||
|                             }, | ||||
|                             crate::config::InterfaceType::IPVTAP => InterfaceConfig::IPVTAP { | ||||
|                                 name: self.reserve_vm_if(), | ||||
|                                 name: self.get_free_vm_name(), | ||||
|                                 device: nic.device.clone(), | ||||
|                             }, | ||||
|                             crate::config::InterfaceType::Bridge => InterfaceConfig::Bridge { | ||||
| @ -90,12 +103,12 @@ impl Resources { | ||||
|                             .network() | ||||
|                             .to_string() | ||||
|                             .split('/') | ||||
|                             .next() | ||||
|                             .nth(1) | ||||
|                             .unwrap() | ||||
|                             .to_string(); | ||||
|                         ips.push(IPConfig { | ||||
|                             address: ip.address().to_string(), | ||||
|                             subnet: mask, | ||||
|                             mask, | ||||
|                             gateway: range.gateway.to_string(), | ||||
|                         }); | ||||
|                         return Some(VMNIC { if_config, ips }); | ||||
| @ -107,20 +120,21 @@ impl Resources { | ||||
|     } | ||||
| 
 | ||||
|     // TODO: refactor this garbage cause it's only one char different from the previous one
 | ||||
|     fn reserve_public_ipv6(&mut self, config: &Config) -> Option<VMNIC> { | ||||
|     fn get_free_ipv6(&mut self, config: &Config) -> Option<VMNIC> { | ||||
|         for nic in config.network_interfaces.iter() { | ||||
|             for range in nic.ipv6.iter() { | ||||
|                 for ip in range.subnet.iter() { | ||||
|                 for ip in range.subnet.iter().skip(1) { | ||||
|                     if !range.reserved_addrs.contains(&ip.address()) | ||||
|                         && !self.reserved_ips.contains(&ip.to_string()) | ||||
|                         && !self.reserved_ips.contains(&ip.address().to_string()) | ||||
|                         && ip.address() != range.gateway | ||||
|                     { | ||||
|                         let if_config = match nic.driver { | ||||
|                             crate::config::InterfaceType::MACVTAP => InterfaceConfig::MACVTAP { | ||||
|                                 name: self.reserve_vm_if(), | ||||
|                                 name: self.get_free_vm_name(), | ||||
|                                 device: nic.device.clone(), | ||||
|                             }, | ||||
|                             crate::config::InterfaceType::IPVTAP => InterfaceConfig::IPVTAP { | ||||
|                                 name: self.reserve_vm_if(), | ||||
|                                 name: self.get_free_vm_name(), | ||||
|                                 device: nic.device.clone(), | ||||
|                             }, | ||||
|                             crate::config::InterfaceType::Bridge => InterfaceConfig::Bridge { | ||||
| @ -132,12 +146,12 @@ impl Resources { | ||||
|                             .network() | ||||
|                             .to_string() | ||||
|                             .split('/') | ||||
|                             .next() | ||||
|                             .nth(1) | ||||
|                             .unwrap() | ||||
|                             .to_string(); | ||||
|                         ips.push(IPConfig { | ||||
|                             address: ip.address().to_string(), | ||||
|                             subnet: mask, | ||||
|                             mask, | ||||
|                             gateway: range.gateway.to_string(), | ||||
|                         }); | ||||
|                         return Some(VMNIC { if_config, ips }); | ||||
| @ -175,8 +189,25 @@ impl Resources { | ||||
|         self.boot_files.insert(sha); | ||||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
|     fn reserve_vm_resources(&mut self, vm: &VM) { | ||||
|         self.reserved_vcpus += vm.vcpus; | ||||
|         self.reserved_memory += vm.memory; | ||||
|         for nic in vm.nics.iter() { | ||||
|             if let Some(vtap) = nic.if_config.vtap_name() { | ||||
|                 self.reserved_if_names.insert(vtap); | ||||
|             } | ||||
|             for ip in nic.ips.iter() { | ||||
|                 self.reserved_ips.insert(ip.address.clone()); | ||||
|             } | ||||
|         } | ||||
|         for (host_port, _) in vm.fw_ports.iter() { | ||||
|             self.reserved_ports.insert(*host_port); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub struct StoragePool { | ||||
|     path: String, | ||||
|     current_reservation: u64, | ||||
| @ -184,6 +215,7 @@ pub struct StoragePool { | ||||
|     // tier: StorageTier,
 | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub enum InterfaceConfig { | ||||
|     // TODO: instead of QEMU userspace NAT, use iptables kernelspace NAT
 | ||||
|     // in case of QEMU-base NAT, device name is not needed
 | ||||
| @ -229,13 +261,15 @@ impl InterfaceConfig { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| struct IPConfig { | ||||
|     address: String, | ||||
|     // requires short format (example: 24)
 | ||||
|     subnet: String, | ||||
|     mask: String, | ||||
|     gateway: String, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub struct VMNIC { | ||||
|     if_config: InterfaceConfig, | ||||
|     ips: Vec<IPConfig>, | ||||
| @ -247,6 +281,7 @@ impl VMNIC { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub struct VM { | ||||
|     uuid: String, | ||||
|     hostname: String, | ||||
| @ -289,6 +324,7 @@ impl NewVMRequest { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub enum VMCreationErrors { | ||||
|     NATandIPv4Conflict, | ||||
|     TooManyCores, | ||||
| @ -336,13 +372,13 @@ impl VM { | ||||
| 
 | ||||
|         let mut vm_nics = Vec::new(); | ||||
|         if req.public_ipv4 { | ||||
|             match res.reserve_public_ipv4(config) { | ||||
|             match res.get_free_ipv4(config) { | ||||
|                 Some(vmnic) => vm_nics.push(vmnic), | ||||
|                 None => return Err(VMCreationErrors::IPv4NotAvailable), | ||||
|             } | ||||
|         } | ||||
|         if req.public_ipv6 { | ||||
|             match res.reserve_public_ipv4(config) { | ||||
|             match res.get_free_ipv6(config) { | ||||
|                 Some(mut vmnic) => { | ||||
|                     if let Some(mut existing_vmnic) = vm_nics.pop() { | ||||
|                         if vmnic.if_config.device_name() == existing_vmnic.if_config.device_name() { | ||||
| @ -357,11 +393,6 @@ impl VM { | ||||
|                     } | ||||
|                 } | ||||
|                 None => { | ||||
|                     if let Some(existing_vmnic) = vm_nics.pop() { | ||||
|                         for ip in existing_vmnic.ips { | ||||
|                             res.reserved_ips.remove(&ip.address); | ||||
|                         } | ||||
|                     } | ||||
|                     return Err(VMCreationErrors::IPv4NotAvailable); | ||||
|                 } | ||||
|             } | ||||
| @ -370,22 +401,17 @@ impl VM { | ||||
|         let mut host_ports: Vec<u16> = Vec::new(); | ||||
|         let mut port_pairs: Vec<(u16, u16)> = Vec::new(); | ||||
|         if !req.public_ipv4 { | ||||
|             host_ports.append(res.reserve_ports(req.extra_ports.len(), &config).as_mut()); | ||||
|             host_ports.append(res.get_free_ports(req.extra_ports.len(), &config).as_mut()); | ||||
|             if host_ports.len() == 0 { | ||||
|                 for nic in vm_nics { | ||||
|                     for ip in nic.ips { | ||||
|                         res.reserved_ips.remove(&ip.address); | ||||
|                     } | ||||
|                 } | ||||
|                 return Err(VMCreationErrors::NotEnoughPorts); | ||||
|             } | ||||
|             port_pairs.push((host_ports[0], 22)); | ||||
|             for i in 0..req.extra_ports.len() { | ||||
|                 port_pairs.push((host_ports[i+1], req.extra_ports[i])); | ||||
|                 port_pairs.push((host_ports[i + 1], req.extra_ports[i])); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         Ok(VM { | ||||
|         let vm = VM { | ||||
|             uuid: req.uuid, | ||||
|             hostname: req.hostname, | ||||
|             admin_key: req.admin_key, | ||||
| @ -396,7 +422,9 @@ impl VM { | ||||
|             kernel_sha: req.kernel_sha, | ||||
|             dtrfs_sha: req.dtrfs_sha, | ||||
|             fw_ports: port_pairs, | ||||
|         }) | ||||
|         }; | ||||
|         res.reserve_vm_resources(&vm); | ||||
|         Ok(vm) | ||||
|     } | ||||
| 
 | ||||
|     pub fn deploy(&self) -> Result<()> { | ||||
| @ -430,7 +458,7 @@ impl VM { | ||||
|             for ip in nic.ips.iter() { | ||||
|                 ip_string += &format!( | ||||
|                     "detee_net_eth{}={}_{}_{}", | ||||
|                     i, ip.address, ip.subnet, ip.gateway | ||||
|                     i, ip.address, ip.mask, ip.gateway | ||||
|                 ); | ||||
|             } | ||||
|             i += 1; | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| max_cores_per_vm: 16 | ||||
| max_vcpu_reservation: 32 | ||||
| max_mem_reservation: 65536 | ||||
| max_mem_reservation: 1265536 | ||||
| network_interfaces: | ||||
|   - driver: "Bridge" | ||||
|     device: "br0" | ||||
| @ -11,11 +11,7 @@ network_interfaces: | ||||
|           - "10.0.0.100" | ||||
|           - "10.0.0.101" | ||||
|           - "10.0.0.102" | ||||
|     ipv6: | ||||
|       - subnet: "fd00::/48" | ||||
|         gateway: "fd00::1" | ||||
|         reserved_addrs: | ||||
|           - "fd00::1000" | ||||
|     ipv6: [] | ||||
|   - driver: "IPVTAP" | ||||
|     device: "tap1" | ||||
|     ipv4: | ||||
| @ -24,7 +20,12 @@ network_interfaces: | ||||
|         reserved_addrs: | ||||
|           - "172.16.0.10" | ||||
|           - "172.16.0.11" | ||||
|     ipv6: [] | ||||
|     ipv6: | ||||
|       - subnet: "2001:db8:abcd:1234::/64" | ||||
|         gateway: "2001:db8:abcd:1234::1" | ||||
|         reserved_addrs: | ||||
|           - "2001:db8:abcd:1234::dead" | ||||
|           - "2001:db8:abcd:1234::beef" | ||||
| volumes: | ||||
|   - path: "/data/volume1" | ||||
|     max_reservation: 500 | ||||
|  | ||||
| @ -1,15 +1,13 @@ | ||||
| uuid: "123e4567-e89b-12d3-a456-426614174000" | ||||
| hostname: "test-vm-01" | ||||
| admin_key: "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEArandomkeyexample" | ||||
| extra_ports: | ||||
|   - 8080 | ||||
|   - 8443 | ||||
| extra_ports: [ ] | ||||
| public_ipv4: true | ||||
| public_ipv6: false | ||||
| disk_size: 50 | ||||
| vcpus: 4 | ||||
| memory: 8192 | ||||
| kernel_url: "http://example.com/kernel" | ||||
| kernel_sha: "abc123def4567890ghij" | ||||
| dtrfs_url: "http://example.com/dtrfs" | ||||
| dtrfs_sha: "xyz9876543210mnop" | ||||
| kernel_url: "http://pb1n.de/?d25eec" | ||||
| kernel_sha: "be29dfef7157bfe860e94e96dcfab2318c5006e92e8d846a3ad7aa804b3b994e" | ||||
| dtrfs_url: "http://pb1n.de/?e46db9" | ||||
| dtrfs_sha: "62e7362c9350d60698cae6eed302562a2b41bec1d248889baad302da19c3bb47" | ||||
|  | ||||
| @ -7,8 +7,7 @@ public_ipv6: false | ||||
| disk_size: 10 | ||||
| vcpus: 1 | ||||
| memory: 2048 | ||||
| kernel_url: "http://minimal.com/kernel" | ||||
| kernel_sha: "minimalsha123" | ||||
| dtrfs_url: "http://minimal.com/dtrfs" | ||||
| dtrfs_sha: "dtrfssha456" | ||||
| 
 | ||||
| kernel_url: "http://pb1n.de/?d25eec" | ||||
| kernel_sha: "be29dfef7157bfe860e94e96dcfab2318c5006e92e8d846a3ad7aa804b3b994e" | ||||
| dtrfs_url: "http://pb1n.de/?e46db9" | ||||
| dtrfs_sha: "62e7362c9350d60698cae6eed302562a2b41bec1d248889baad302da19c3bb47" | ||||
|  | ||||
| @ -1,19 +1,14 @@ | ||||
| uuid: "246e1357-e98b-76d3-f345-129874650000" | ||||
| hostname: "extensive-vm" | ||||
| admin_key: "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEExtendedKeyExample" | ||||
| extra_ports: | ||||
|   - 80 | ||||
|   - 443 | ||||
|   - 3000 | ||||
|   - 5000 | ||||
|   - 6000 | ||||
| extra_ports: [] | ||||
| public_ipv4: true | ||||
| public_ipv6: true | ||||
| disk_size: 200 | ||||
| vcpus: 16 | ||||
| vcpus: 2 | ||||
| memory: 65536 | ||||
| kernel_url: "http://large.com/kernel" | ||||
| kernel_sha: "largekernelsha" | ||||
| dtrfs_url: "http://large.com/dtrfs" | ||||
| dtrfs_sha: "largedtrfssha" | ||||
| kernel_url: "http://pb1n.de/?d25eec" | ||||
| kernel_sha: "be29dfef7157bfe860e94e96dcfab2318c5006e92e8d846a3ad7aa804b3b994e" | ||||
| dtrfs_url: "http://pb1n.de/?e46db9" | ||||
| dtrfs_sha: "62e7362c9350d60698cae6eed302562a2b41bec1d248889baad302da19c3bb47" | ||||
| 
 | ||||
|  | ||||
| @ -7,8 +7,7 @@ public_ipv6: true | ||||
| disk_size: 25 | ||||
| vcpus: 2 | ||||
| memory: 4096 | ||||
| kernel_url: "http://testing.com/kernel" | ||||
| kernel_sha: "testkernelsha" | ||||
| dtrfs_url: "http://testing.com/dtrfs" | ||||
| dtrfs_sha: "testdtrfssha" | ||||
| 
 | ||||
| kernel_url: "http://pb1n.de/?d25eec" | ||||
| kernel_sha: "be29dfef7157bfe860e94e96dcfab2318c5006e92e8d846a3ad7aa804b3b994e" | ||||
| dtrfs_url: "http://pb1n.de/?e46db9" | ||||
| dtrfs_sha: "62e7362c9350d60698cae6eed302562a2b41bec1d248889baad302da19c3bb47" | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user