added logic to reserve ips
This commit is contained in:
parent
edbaebfc56
commit
b905c96261
96
Cargo.lock
generated
96
Cargo.lock
generated
@ -8,6 +8,18 @@ version = "1.0.94"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "cidr"
|
||||
version = "0.3.0"
|
||||
@ -23,9 +35,36 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cidr",
|
||||
"rand",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.167"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.92"
|
||||
@ -44,6 +83,36 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.215"
|
||||
@ -80,3 +149,30 @@ name = "unicode-ident"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
@ -6,4 +6,5 @@ edition = "2021"
|
||||
[dependencies]
|
||||
anyhow = "1.0.94"
|
||||
cidr = { version = "0.3.0", features = ["serde"] }
|
||||
rand = "0.8.5"
|
||||
serde = { version = "1.0.215", features = ["derive"] }
|
||||
|
@ -4,35 +4,47 @@ use cidr::Ipv6Cidr;
|
||||
use core::net::Ipv4Addr;
|
||||
use core::net::Ipv6Addr;
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::ops::Range;
|
||||
|
||||
struct Volume {
|
||||
pub struct Volume {
|
||||
path: String,
|
||||
// maximum allowed storage in GB
|
||||
max_reservation: u64,
|
||||
}
|
||||
|
||||
struct Interface {
|
||||
driver: InterfaceType,
|
||||
name: String,
|
||||
ipv4_ranges: Vec<Ipv4Cidr>,
|
||||
reserved_v4_addrs: Vec<Ipv4Addr>,
|
||||
ipv6_ranges: Vec<Ipv6Cidr>,
|
||||
reserved_v6_addrs: Vec<Ipv6Addr>,
|
||||
pub struct IPv4Range {
|
||||
pub subnet: Ipv4Cidr,
|
||||
pub gateway: Ipv4Addr,
|
||||
pub reserved_addrs: HashSet<Ipv4Addr>,
|
||||
}
|
||||
|
||||
pub struct IPv6Range {
|
||||
pub subnet: Ipv6Cidr,
|
||||
pub gateway: Ipv6Addr,
|
||||
pub reserved_addrs: HashSet<Ipv6Addr>,
|
||||
}
|
||||
|
||||
pub struct Interface {
|
||||
pub driver: InterfaceType,
|
||||
pub device: String,
|
||||
pub ipv4: Vec<IPv4Range>,
|
||||
pub ipv6: Vec<IPv6Range>,
|
||||
}
|
||||
|
||||
// TODO: create mechanic to autodetect interface type
|
||||
enum InterfaceType {
|
||||
pub enum InterfaceType {
|
||||
MACVTAP,
|
||||
IPVTAP,
|
||||
Bridge,
|
||||
}
|
||||
|
||||
struct Config {
|
||||
max_cores_per_vm: u64,
|
||||
max_vcpu_reservation: u64,
|
||||
max_mem_reservation: u64,
|
||||
network_interfaces: Vec<Interface>,
|
||||
volumes: Vec<Volume>,
|
||||
public_port_range: Range<u16>,
|
||||
pub struct Config {
|
||||
pub max_cores_per_vm: usize,
|
||||
pub max_vcpu_reservation: usize,
|
||||
pub max_mem_reservation: usize,
|
||||
pub network_interfaces: Vec<Interface>,
|
||||
pub volumes: Vec<Volume>,
|
||||
pub public_port_range: Range<u16>,
|
||||
pub max_ports_per_vm: u16,
|
||||
}
|
||||
|
329
src/state.rs
329
src/state.rs
@ -1,18 +1,15 @@
|
||||
#![allow(dead_code)]
|
||||
use crate::config::Config;
|
||||
use crate::constants::*;
|
||||
use anyhow::anyhow;
|
||||
use anyhow::Result;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::fs::remove_file;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::ops::Range;
|
||||
use std::process::Command;
|
||||
|
||||
type VMUUID = String;
|
||||
|
||||
pub enum NIC {
|
||||
pub enum InterfaceConfig {
|
||||
// TODO: instead of QEMU userspace NAT, use iptables kernelspace NAT
|
||||
// in case of QEMU-base NAT, device name is not needed
|
||||
NAT { device: String },
|
||||
@ -22,89 +19,154 @@ pub enum NIC {
|
||||
Bridge { device: String },
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum IPStatus {
|
||||
Available,
|
||||
Reserved(VMUUID),
|
||||
Blacklisted,
|
||||
}
|
||||
|
||||
struct IPData {
|
||||
interface: NIC,
|
||||
status: IPStatus,
|
||||
}
|
||||
|
||||
pub struct StoragePool {
|
||||
path: String,
|
||||
max_reservation: u64,
|
||||
current_reservation: u64,
|
||||
// add mechanic to detect storage tier
|
||||
// tier: StorageTier,
|
||||
}
|
||||
|
||||
pub struct PortPool {
|
||||
port_range: Range<u16>,
|
||||
used_ports: HashSet<u16>,
|
||||
}
|
||||
|
||||
pub struct Resources {
|
||||
// QEMU does not support MHz limiation
|
||||
mac_vcpus_reservation: u64,
|
||||
available_vcpus_reservation: u64,
|
||||
total_memory_reservation: u64,
|
||||
available_memory_reservation: u64,
|
||||
// will be only one StoragePool for now and multiple later
|
||||
reserved_vcpus: usize,
|
||||
reserved_memory: usize,
|
||||
used_ports: HashSet<u16>,
|
||||
storage_pools: Vec<StoragePool>,
|
||||
ipv4_pool: HashMap<String, IPData>,
|
||||
ipv6_pool: HashMap<String, IPData>,
|
||||
reserved_ipv4: HashSet<String>,
|
||||
reserved_ipv6: HashSet<String>,
|
||||
reserved_if_names: HashSet<String>,
|
||||
}
|
||||
|
||||
impl Resources {
|
||||
fn get_available_ipv4(&self) -> usize {
|
||||
self.ipv4_pool
|
||||
.values()
|
||||
.filter(|ip_data| ip_data.status == IPStatus::Available)
|
||||
.count()
|
||||
fn reserve_vm_if(&mut self) -> String {
|
||||
use rand::{distributions::Alphanumeric, Rng};
|
||||
loop {
|
||||
let mut interface_name: String = rand::thread_rng()
|
||||
.sample_iter(&Alphanumeric)
|
||||
.take(9)
|
||||
.map(char::from)
|
||||
.collect();
|
||||
interface_name = "detee".to_string() + &interface_name;
|
||||
if !self.reserved_if_names.contains(&interface_name) {
|
||||
return interface_name;
|
||||
}
|
||||
fn get_available_ipv6(&self) -> usize {
|
||||
self.ipv6_pool
|
||||
.values()
|
||||
.filter(|ip_data| ip_data.status == IPStatus::Available)
|
||||
.count()
|
||||
}
|
||||
}
|
||||
|
||||
fn reserve_public_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() {
|
||||
if !range.reserved_addrs.contains(&ip.address())
|
||||
&& !self.reserved_ipv4.contains(&ip.to_string())
|
||||
{
|
||||
let if_config = match nic.driver {
|
||||
crate::config::InterfaceType::MACVTAP => InterfaceConfig::MACVTAP {
|
||||
name: self.reserve_vm_if(),
|
||||
device: nic.device.clone(),
|
||||
},
|
||||
crate::config::InterfaceType::IPVTAP => InterfaceConfig::IPVTAP {
|
||||
name: self.reserve_vm_if(),
|
||||
device: nic.device.clone(),
|
||||
},
|
||||
crate::config::InterfaceType::Bridge => InterfaceConfig::Bridge {
|
||||
device: nic.device.clone(),
|
||||
},
|
||||
};
|
||||
let mut ips = Vec::new();
|
||||
let mask = range
|
||||
.subnet
|
||||
.to_string()
|
||||
.split('/')
|
||||
.next()
|
||||
.unwrap()
|
||||
.to_string();
|
||||
ips.push(IPConfig {
|
||||
address: ip.address().to_string(),
|
||||
subnet: mask,
|
||||
gateway: range.gateway.to_string(),
|
||||
});
|
||||
return Some(VMNIC { if_config, ips });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn reserve_public_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() {
|
||||
if !range.reserved_addrs.contains(&ip.address())
|
||||
&& !self.reserved_ipv6.contains(&ip.to_string())
|
||||
{
|
||||
let if_config = match nic.driver {
|
||||
crate::config::InterfaceType::MACVTAP => InterfaceConfig::MACVTAP {
|
||||
name: self.reserve_vm_if(),
|
||||
device: nic.device.clone(),
|
||||
},
|
||||
crate::config::InterfaceType::IPVTAP => InterfaceConfig::IPVTAP {
|
||||
name: self.reserve_vm_if(),
|
||||
device: nic.device.clone(),
|
||||
},
|
||||
crate::config::InterfaceType::Bridge => InterfaceConfig::Bridge {
|
||||
device: nic.device.clone(),
|
||||
},
|
||||
};
|
||||
let mut ips = Vec::new();
|
||||
let mask = range
|
||||
.subnet
|
||||
.to_string()
|
||||
.split('/')
|
||||
.next()
|
||||
.unwrap()
|
||||
.to_string();
|
||||
ips.push(IPConfig {
|
||||
address: ip.address().to_string(),
|
||||
subnet: mask,
|
||||
gateway: range.gateway.to_string(),
|
||||
});
|
||||
return Some(VMNIC { if_config, ips });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl NIC {
|
||||
impl InterfaceConfig {
|
||||
fn if_type(&self) -> String {
|
||||
match self {
|
||||
NIC::IPVTAP { .. } => format!("ipvtap"),
|
||||
NIC::MACVTAP { .. } => format!("macvtap"),
|
||||
NIC::NAT { .. } => format!("nat"),
|
||||
NIC::Bridge { .. } => format!("bridge"),
|
||||
InterfaceConfig::IPVTAP { .. } => format!("ipvtap"),
|
||||
InterfaceConfig::MACVTAP { .. } => format!("macvtap"),
|
||||
InterfaceConfig::NAT { .. } => format!("nat"),
|
||||
InterfaceConfig::Bridge { .. } => format!("bridge"),
|
||||
}
|
||||
}
|
||||
|
||||
fn device_name(&self) -> String {
|
||||
match self {
|
||||
NIC::IPVTAP { device, .. } => device.clone(),
|
||||
NIC::MACVTAP { device, .. } => device.clone(),
|
||||
NIC::NAT { device, .. } => device.clone(),
|
||||
NIC::Bridge { device, .. } => device.clone(),
|
||||
InterfaceConfig::IPVTAP { device, .. } => device.clone(),
|
||||
InterfaceConfig::MACVTAP { device, .. } => device.clone(),
|
||||
InterfaceConfig::NAT { device, .. } => device.clone(),
|
||||
InterfaceConfig::Bridge { device, .. } => device.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn vtap_name(&self) -> Option<String> {
|
||||
match self {
|
||||
NIC::IPVTAP { name, .. } => Some(name.clone()),
|
||||
NIC::MACVTAP { name, .. } => Some(name.clone()),
|
||||
NIC::NAT { .. } | NIC::Bridge { .. } => None,
|
||||
InterfaceConfig::IPVTAP { name, .. } => Some(name.clone()),
|
||||
InterfaceConfig::MACVTAP { name, .. } => Some(name.clone()),
|
||||
InterfaceConfig::NAT { .. } | InterfaceConfig::Bridge { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_vtap(&self) -> bool {
|
||||
match self {
|
||||
NIC::IPVTAP { .. } | NIC::MACVTAP { .. } => true,
|
||||
NIC::NAT { .. } | NIC::Bridge { .. } => false,
|
||||
InterfaceConfig::IPVTAP { .. } | InterfaceConfig::MACVTAP { .. } => true,
|
||||
InterfaceConfig::NAT { .. } | InterfaceConfig::Bridge { .. } => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -114,42 +176,152 @@ struct IPConfig {
|
||||
// requires short format (example: 24)
|
||||
subnet: String,
|
||||
gateway: String,
|
||||
nameserver: String,
|
||||
nic: NIC,
|
||||
}
|
||||
|
||||
pub struct VMNIC {
|
||||
if_config: InterfaceConfig,
|
||||
ips: Vec<IPConfig>,
|
||||
}
|
||||
|
||||
pub struct VM {
|
||||
uuid: VMUUID,
|
||||
uuid: String,
|
||||
hostname: String,
|
||||
admin_key: String,
|
||||
ips: Vec<IPConfig>,
|
||||
fw_ports: Vec<u16>,
|
||||
nics: Vec<VMNIC>,
|
||||
// currently hardcoded to EPYC-v4
|
||||
// cpu_type: String,
|
||||
vcpus: u32,
|
||||
vcpus: usize,
|
||||
// memory in MB
|
||||
memory: u32,
|
||||
memory: usize,
|
||||
// disk size in GB
|
||||
disk_size: u32,
|
||||
disk_size: usize,
|
||||
kernel_path: String,
|
||||
kernel_sha: String,
|
||||
initrd_path: String,
|
||||
ovmf_path: String,
|
||||
initrd_sha: String,
|
||||
}
|
||||
|
||||
pub struct NewVMRequest {
|
||||
uuid: String,
|
||||
hostname: String,
|
||||
admin_key: String,
|
||||
forwarded_ports: Vec<u16>,
|
||||
public_ipv4: bool,
|
||||
public_ipv6: bool,
|
||||
disk_size: usize,
|
||||
vcpus: usize,
|
||||
memory: usize,
|
||||
kernel_path: String,
|
||||
kernel_sha: String,
|
||||
initrd_path: String,
|
||||
initrd_sha: String,
|
||||
}
|
||||
|
||||
pub enum VMCreationErrors {
|
||||
NATandIPv4Conflict,
|
||||
TooManyCores,
|
||||
NotEnoughCPU,
|
||||
NotEnoughMemory,
|
||||
NotEnoughStorage,
|
||||
IPv4NotAvailable,
|
||||
IPv6NotAvailable,
|
||||
}
|
||||
|
||||
impl VM {
|
||||
pub fn new(
|
||||
req: NewVMRequest,
|
||||
config: &Config,
|
||||
res: &mut Resources,
|
||||
) -> Result<Self, VMCreationErrors> {
|
||||
if req.forwarded_ports.len() > 0 && req.public_ipv4 {
|
||||
return Err(VMCreationErrors::NATandIPv4Conflict);
|
||||
}
|
||||
if config.max_cores_per_vm < req.vcpus {
|
||||
return Err(VMCreationErrors::TooManyCores);
|
||||
}
|
||||
if config.max_vcpu_reservation < res.reserved_vcpus.saturating_add(req.vcpus) {
|
||||
return Err(VMCreationErrors::NotEnoughCPU);
|
||||
}
|
||||
if config.max_mem_reservation < res.reserved_memory.saturating_add(req.memory) {
|
||||
return Err(VMCreationErrors::NotEnoughMemory);
|
||||
}
|
||||
let mut vm_nics = Vec::new();
|
||||
if req.public_ipv4 {
|
||||
match res.reserve_public_ipv4(config) {
|
||||
Some(vmnic) => vm_nics.push(vmnic),
|
||||
None => return Err(VMCreationErrors::IPv4NotAvailable),
|
||||
}
|
||||
}
|
||||
if req.public_ipv6 {
|
||||
match res.reserve_public_ipv4(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() {
|
||||
vmnic.ips.append(&mut existing_vmnic.ips);
|
||||
vm_nics.push(vmnic);
|
||||
} else {
|
||||
vm_nics.push(existing_vmnic);
|
||||
vm_nics.push(vmnic);
|
||||
}
|
||||
} else {
|
||||
vm_nics.push(vmnic);
|
||||
}
|
||||
},
|
||||
None => return Err(VMCreationErrors::IPv4NotAvailable),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(VM {
|
||||
uuid: req.uuid,
|
||||
hostname: req.hostname,
|
||||
admin_key: req.admin_key,
|
||||
nics: vm_nics,
|
||||
vcpus: req.vcpus,
|
||||
memory: req.memory,
|
||||
disk_size: req.disk_size,
|
||||
kernel_sha: req.kernel_sha,
|
||||
initrd_sha: req.initrd_sha,
|
||||
fw_ports: todo!(),
|
||||
kernel_path: todo!(),
|
||||
initrd_path: todo!(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn deploy(&self) -> Result<()> {
|
||||
self.create_disk()?;
|
||||
self.write_vm_config()?;
|
||||
self.write_systemd_unit_file()?;
|
||||
// TODO: daemon-reload, systemctl enable, systemctl start
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn delete(&self) -> Result<()> {
|
||||
// TODO: systemctl disable, systemctl stop
|
||||
self.delete_disk()?;
|
||||
self.delete_vtap_interfaces()?;
|
||||
self.delete_systemd_unit_file()?;
|
||||
// TODO: systemctl daemon-reload
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// For the MVP, the T-Contract offers VM+IP+Disk as a bundle.
|
||||
// This means we can enforce the path to the disk.
|
||||
// This may change in the future as the VM is allowed to have multiple disks.
|
||||
pub fn disk_path(&self) -> String {
|
||||
VM_DISK_DIR.to_string() + "/" + &self.uuid + ".qcow2"
|
||||
}
|
||||
|
||||
pub fn kernel_params(&self) -> String {
|
||||
let mut ip_string = String::new();
|
||||
let mut i = 0;
|
||||
for ip in self.ips.iter() {
|
||||
for nic in self.nics.iter() {
|
||||
for ip in nic.ips.iter() {
|
||||
ip_string += &format!(
|
||||
"detee_net_eth{}={}_{}_{}_{}",
|
||||
i, ip.address, ip.subnet, ip.gateway, ip.nameserver
|
||||
"detee_net_eth{}={}_{}_{}",
|
||||
i, ip.address, ip.subnet, ip.gateway
|
||||
);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
let admin_key = format!("detee_admin={}", self.admin_key);
|
||||
@ -157,16 +329,16 @@ impl VM {
|
||||
format!("{} {} {}", ip_string, admin_key, hostname)
|
||||
}
|
||||
|
||||
pub fn export_vm_env(&self) -> String {
|
||||
pub fn write_vm_config(&self) -> Result<()> {
|
||||
let mut vars = String::new();
|
||||
|
||||
let mut i = 0;
|
||||
for ip in self.ips.iter() {
|
||||
for nic in self.nics.iter() {
|
||||
let mut interface = String::new();
|
||||
interface += &format!("NETWORK_INTERFACE_{}={}", i, ip.nic.if_type());
|
||||
interface += &format!("NETWORK_INTERFACE_{}={}", i, nic.if_config.if_type());
|
||||
// device is currently ignored in case of NAT cause we assume QEMU userspace NAT
|
||||
if let Some(vtap_name) = ip.nic.vtap_name() {
|
||||
interface += &format!("_{}_{}", ip.nic.device_name(), vtap_name);
|
||||
if let Some(vtap_name) = nic.if_config.vtap_name() {
|
||||
interface += &format!("_{}_{}", nic.if_config.device_name(), vtap_name);
|
||||
}
|
||||
vars += &format!("{}\n", interface);
|
||||
i += 1;
|
||||
@ -182,12 +354,19 @@ impl VM {
|
||||
vars += &format!("DISK={}\n", self.disk_path());
|
||||
vars += &format!("DISK_SIZE={}GB\n", self.disk_size);
|
||||
|
||||
todo!();
|
||||
let mut file = File::create(VM_CONFIG_DIR.to_string() + "/" + &self.uuid)?;
|
||||
file.write_all(vars.as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn delete_vm_config(&self) -> Result<()> {
|
||||
remove_file(VM_CONFIG_DIR.to_string() + "/" + &self.uuid)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn delete_vtap_interfaces(&self) -> Result<()> {
|
||||
for ip in self.ips.iter() {
|
||||
if let Some(name) = ip.nic.vtap_name() {
|
||||
for nic in self.nics.iter() {
|
||||
if let Some(name) = nic.if_config.vtap_name() {
|
||||
let result = Command::new("ip")
|
||||
.arg("link")
|
||||
.arg("del")
|
||||
@ -222,13 +401,13 @@ impl VM {
|
||||
contents += &format!("[Install]");
|
||||
contents += &format!("WantedBy=multi-user.target");
|
||||
|
||||
let mut file = File::create(VM_CONFIG_DIR.to_string() + "/" + &self.uuid)?;
|
||||
let mut file = File::create("/etc/systemd/system/".to_string() + &self.uuid + ".service")?;
|
||||
file.write_all(contents.as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn delete_systemd_unit_file(&self) -> Result<()> {
|
||||
remove_file(VM_CONFIG_DIR.to_string() + "/" + &self.uuid)?;
|
||||
remove_file("/etc/systemd/system/".to_string() + &self.uuid + ".service")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,10 @@ pub struct FinalizedTContract {
|
||||
pub owner: String,
|
||||
pub user: String,
|
||||
pub alloc: ResourceAllocation,
|
||||
pub kernel_uri: String,
|
||||
pub kernel_sha: String,
|
||||
pub initrd_uri: String,
|
||||
pub initrd_sha: String,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
Loading…
Reference in New Issue
Block a user