use cmdstruct::Arg;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use system_harness_macros::{Backend, PropertyList, PropertyValue};
#[derive(Copy, Clone, Serialize, Deserialize, PropertyValue)]
#[serde(rename_all = "kebab-case")]
pub enum SecurityModel {
MappedXattr,
MappedFile,
Passthrough,
None
}
#[derive(Clone, Serialize, Deserialize, Backend)]
#[serde(rename_all = "kebab-case", tag = "type")]
pub enum FsDev {
Local {
path: String,
security_model: SecurityModel
}
}
#[derive(Clone, Serialize, Deserialize, PropertyList)]
#[serde(rename_all = "kebab-case")]
pub struct Boot {
menu: Option<OnOff>,
strict: Option<OnOff>,
#[serde(rename = "reboot-time")]
reboot_time: Option<String>,
#[serde(rename = "splash-time")]
splash_time: Option<String>,
splash: Option<String>,
once: Option<String>,
order: Option<String>
}
#[derive(Copy, Clone, Serialize, Deserialize, PropertyValue)]
#[serde(rename_all = "kebab-case")]
pub enum Discard {
Ignore,
Unmap,
}
#[derive(Clone, Serialize, Deserialize, PropertyList)]
#[serde(rename_all = "kebab-case")]
pub struct BlockDev {
driver: String,
#[serde(rename = "node-name")]
node_name: String,
discard: Option<Discard>,
#[serde(flatten)]
properties: BTreeMap<String, String>,
}
#[derive(Serialize, Deserialize)]
pub struct Backend<T> {
#[serde(flatten)]
backend: T,
id: String,
}
impl<T> Arg for Backend<T>
where
T: super::args::Backend,
{
fn append_arg(&self, command: &mut std::process::Command) {
command.arg(format!(
"{},id={},{}",
self.backend.name(),
self.id,
self.backend.properties()
));
}
}
impl<T: Clone> Clone for Backend<T> {
fn clone(&self) -> Self {
Self {
backend: self.backend.clone(),
id: self.id.clone()
}
}
}
#[derive(Clone, Serialize, Deserialize, Backend)]
#[serde(rename_all = "kebab-case", tag = "type")]
pub enum CharDev {
Stdio,
Socket { path: String },
}
#[derive(Copy, Clone, Serialize, Deserialize, PropertyValue)]
#[serde(rename_all = "kebab-case")]
pub enum OnOff {
On,
Off
}
#[derive(Clone, Serialize, Deserialize, Backend)]
#[serde(rename_all = "kebab-case", tag = "type")]
pub enum NetDev {
User {
ipv4: Option<OnOff>,
net: Option<String>,
host: Option<String>,
smb: Option<String>,
hostfwd: Vec<String>
},
}
#[derive(Clone, Serialize, Deserialize, PropertyList)]
pub struct Device {
driver: String,
#[serde(flatten)]
properties: BTreeMap<String, String>,
}
#[derive(Clone, Serialize, Deserialize, PropertyList)]
pub struct Smp {
cpus: Option<usize>,
maxcpus: Option<usize>,
dies: Option<usize>,
sockets: Option<usize>,
clusters: Option<usize>,
cores: Option<usize>,
threads: Option<usize>,
}
#[derive(Clone, Serialize, Deserialize, PropertyList)]
pub struct Machine {
#[serde(rename = "type")]
r#type: Option<String>,
#[serde(flatten)]
properties: BTreeMap<String, String>,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::qemu::args::Backend as _;
use cmdstruct::Arg;
macro_rules! property_value_test {
($test_name: ident, $enum: expr, $json_string: expr, $string: expr) => {
#[test]
fn $test_name() {
assert_eq!($json_string, serde_json::to_string(&$enum).unwrap());
assert_eq!(crate::qemu::args::PropertyValue::value(&$enum).unwrap(), $string.to_string());
}
}
}
property_value_test!(on, OnOff::On, r#""on""#, "on");
property_value_test!(off, OnOff::Off, r#""off""#, "off");
property_value_test!(security_model_passthrough, SecurityModel::Passthrough, r#""passthrough""#, "passthrough");
property_value_test!(security_model_none, SecurityModel::None, r#""none""#, "none");
property_value_test!(security_model_mapped_xattr, SecurityModel::MappedXattr, r#""mapped-xattr""#, "mapped-xattr");
property_value_test!(security_model_mapped_file, SecurityModel::MappedFile, r#""mapped-file""#, "mapped-file");
#[test]
fn chardev() {
const EXPECTED: &'static str = r#"{"type":"socket","path":"test.sock","id":"abc"}"#;
let chardev = Backend::<CharDev> {
id: "abc".to_string(),
backend: CharDev::Socket {
path: "test.sock".to_string(),
},
};
assert_eq!("socket", chardev.backend.name());
assert_eq!(
"path=test.sock",
format!("{}", chardev.backend.properties())
);
assert_eq!(EXPECTED, &serde_json::to_string(&chardev).unwrap());
}
#[test]
fn device_arg() {
let mut properties = BTreeMap::new();
properties.insert("a".to_string(), "abc".to_string());
let device = Device {
driver: "test".to_string(),
properties,
};
let mut command = std::process::Command::new("test");
device.append_arg(&mut command);
assert_eq!(
vec!["driver=test,a=abc"],
command.get_args().collect::<Vec<_>>()
);
}
}