2019-05-23 19:48:05 +00:00
|
|
|
// Copyright © 2019 Intel Corporation
|
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
//
|
|
|
|
|
2019-08-28 10:01:01 +00:00
|
|
|
extern crate vm_virtio;
|
|
|
|
|
2019-12-12 17:12:42 +00:00
|
|
|
use clap::ArgMatches;
|
2019-05-23 19:48:05 +00:00
|
|
|
use net_util::MacAddr;
|
2020-03-27 11:15:55 +00:00
|
|
|
use std::collections::HashMap;
|
2019-05-23 19:48:05 +00:00
|
|
|
use std::convert::From;
|
|
|
|
use std::net::Ipv4Addr;
|
2019-09-23 17:46:35 +00:00
|
|
|
use std::path::PathBuf;
|
2019-05-23 19:48:05 +00:00
|
|
|
use std::result;
|
2020-03-27 11:15:55 +00:00
|
|
|
use std::str::FromStr;
|
2019-05-23 19:48:05 +00:00
|
|
|
|
2019-09-27 09:40:50 +00:00
|
|
|
pub const DEFAULT_VCPUS: u8 = 1;
|
|
|
|
pub const DEFAULT_MEMORY_MB: u64 = 512;
|
2019-05-23 19:48:05 +00:00
|
|
|
pub const DEFAULT_RNG_SOURCE: &str = "/dev/urandom";
|
2019-12-10 16:09:28 +00:00
|
|
|
pub const DEFAULT_NUM_QUEUES_VUNET: usize = 2;
|
|
|
|
pub const DEFAULT_QUEUE_SIZE_VUNET: u16 = 256;
|
2019-12-10 16:15:43 +00:00
|
|
|
pub const DEFAULT_NUM_QUEUES_VUBLK: usize = 1;
|
|
|
|
pub const DEFAULT_QUEUE_SIZE_VUBLK: u16 = 128;
|
2019-05-23 19:48:05 +00:00
|
|
|
|
|
|
|
/// Errors associated with VM configuration parameters.
|
|
|
|
#[derive(Debug)]
|
2020-01-24 07:30:50 +00:00
|
|
|
pub enum Error {
|
2019-11-25 14:18:03 +00:00
|
|
|
/// Max is less than boot
|
|
|
|
ParseCpusMaxLowerThanBoot,
|
2019-05-23 19:48:05 +00:00
|
|
|
/// Failed parsing kernel parameters.
|
|
|
|
ParseKernelParams,
|
|
|
|
/// Failed parsing kernel command line parameters.
|
|
|
|
ParseCmdlineParams,
|
2020-03-13 10:28:39 +00:00
|
|
|
/// Both socket and path specified
|
|
|
|
ParseDiskSocketAndPath,
|
2020-04-02 10:00:45 +00:00
|
|
|
/// Filesystem tag is missing
|
|
|
|
ParseFsTagMissing,
|
|
|
|
/// Filesystem socket is missing
|
|
|
|
ParseFsSockMissing,
|
2019-08-05 19:45:58 +00:00
|
|
|
/// Cannot have dax=off along with cache_size parameter.
|
|
|
|
InvalidCacheSizeWithDaxOff,
|
2020-04-02 15:00:52 +00:00
|
|
|
/// Missing persistant memory file parameter.
|
|
|
|
ParsePmemFileMissing,
|
|
|
|
/// Missing persistant memory size parameter.
|
|
|
|
ParsePmemSizeMissing,
|
2019-07-09 03:13:02 +00:00
|
|
|
/// Failed parsing size parameter.
|
|
|
|
ParseSizeParam(std::num::ParseIntError),
|
2019-07-22 19:29:02 +00:00
|
|
|
/// Both console and serial are tty.
|
|
|
|
ParseTTYParam,
|
2019-09-04 17:39:17 +00:00
|
|
|
/// Failed parsing vsock context ID parameter.
|
|
|
|
ParseVsockCidParam(std::num::ParseIntError),
|
|
|
|
/// Failed parsing vsock socket path parameter.
|
|
|
|
ParseVsockSockParam,
|
2019-09-19 07:52:55 +00:00
|
|
|
/// Missing kernel configuration
|
|
|
|
ValidateMissingKernelConfig,
|
2019-11-19 23:40:57 +00:00
|
|
|
/// Failed parsing generic on|off parameter.
|
|
|
|
ParseOnOff,
|
2020-03-27 11:51:09 +00:00
|
|
|
/// Error parsing CPU options
|
|
|
|
ParseCpus(OptionParserError),
|
2020-03-30 17:50:46 +00:00
|
|
|
/// Error parsing memory options
|
|
|
|
ParseMemory(OptionParserError),
|
2020-03-31 10:45:43 +00:00
|
|
|
/// Error parsing disk options
|
|
|
|
ParseDisk(OptionParserError),
|
2020-03-31 15:54:42 +00:00
|
|
|
/// Error parsing network options
|
|
|
|
ParseNetwork(OptionParserError),
|
2020-04-02 09:17:10 +00:00
|
|
|
/// Error parsing RNG options
|
|
|
|
ParseRNG(OptionParserError),
|
2020-04-02 10:00:45 +00:00
|
|
|
/// Error parsing filesystem parameters
|
|
|
|
ParseFileSystem(OptionParserError),
|
2020-04-02 15:00:52 +00:00
|
|
|
/// Error parsing persistent memorry parameters
|
|
|
|
ParsePersistentMemory(OptionParserError),
|
2020-04-02 15:52:30 +00:00
|
|
|
/// Failed parsing console
|
|
|
|
ParseConsole(OptionParserError),
|
|
|
|
/// Missing file value for console
|
|
|
|
ParseConsoleFileMissing,
|
|
|
|
/// No mode given for console
|
|
|
|
ParseConsoleInvalidModeGiven,
|
2019-05-23 19:48:05 +00:00
|
|
|
}
|
2020-01-24 07:30:50 +00:00
|
|
|
pub type Result<T> = result::Result<T, Error>;
|
2019-05-23 19:48:05 +00:00
|
|
|
|
2020-03-27 11:15:55 +00:00
|
|
|
#[derive(Default)]
|
|
|
|
pub struct OptionParser {
|
|
|
|
options: HashMap<String, OptionParserValue>,
|
|
|
|
}
|
|
|
|
|
|
|
|
struct OptionParserValue {
|
|
|
|
value: Option<String>,
|
2020-04-02 15:50:37 +00:00
|
|
|
requires_value: bool,
|
2020-03-27 11:15:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum OptionParserError {
|
|
|
|
UnknownOption(String),
|
|
|
|
InvalidSyntax(String),
|
|
|
|
Conversion(String, String),
|
|
|
|
}
|
|
|
|
|
|
|
|
type OptionParserResult<T> = std::result::Result<T, OptionParserError>;
|
|
|
|
|
|
|
|
impl OptionParser {
|
|
|
|
pub fn new() -> Self {
|
|
|
|
Self {
|
|
|
|
options: HashMap::new(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn parse(&mut self, input: &str) -> OptionParserResult<()> {
|
|
|
|
if input.trim().is_empty() {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
|
|
|
|
let options_list: Vec<&str> = input.trim().split(',').collect();
|
|
|
|
|
|
|
|
for option in options_list.iter() {
|
|
|
|
let parts: Vec<&str> = option.split('=').collect();
|
|
|
|
|
|
|
|
match self.options.get_mut(parts[0]) {
|
|
|
|
None => return Err(OptionParserError::UnknownOption(parts[0].to_owned())),
|
|
|
|
Some(value) => {
|
2020-04-02 15:50:37 +00:00
|
|
|
if value.requires_value {
|
|
|
|
if parts.len() != 2 {
|
|
|
|
return Err(OptionParserError::InvalidSyntax((*option).to_owned()));
|
|
|
|
}
|
|
|
|
value.value = Some(parts[1].trim().to_owned());
|
|
|
|
} else {
|
|
|
|
value.value = Some(String::new());
|
|
|
|
}
|
2020-03-27 11:15:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn add(&mut self, option: &str) -> &mut Self {
|
2020-04-02 15:50:37 +00:00
|
|
|
self.options.insert(
|
|
|
|
option.to_owned(),
|
|
|
|
OptionParserValue {
|
|
|
|
value: None,
|
|
|
|
requires_value: true,
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn add_valueless(&mut self, option: &str) -> &mut Self {
|
|
|
|
self.options.insert(
|
|
|
|
option.to_owned(),
|
|
|
|
OptionParserValue {
|
|
|
|
value: None,
|
|
|
|
requires_value: false,
|
|
|
|
},
|
|
|
|
);
|
2020-03-27 11:15:55 +00:00
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get(&self, option: &str) -> Option<String> {
|
2020-04-01 08:46:16 +00:00
|
|
|
self.options
|
|
|
|
.get(option)
|
|
|
|
.and_then(|v| v.value.clone())
|
|
|
|
.and_then(|s| if s.is_empty() { None } else { Some(s) })
|
2020-03-27 11:15:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_set(&self, option: &str) -> bool {
|
|
|
|
self.options
|
|
|
|
.get(option)
|
|
|
|
.and_then(|v| v.value.as_ref())
|
|
|
|
.is_some()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn convert<T: FromStr>(&self, option: &str) -> OptionParserResult<Option<T>> {
|
2020-04-01 08:46:16 +00:00
|
|
|
match self.get(option) {
|
2020-03-27 11:15:55 +00:00
|
|
|
None => Ok(None),
|
|
|
|
Some(v) => Ok(Some(v.parse().map_err(|_| {
|
|
|
|
OptionParserError::Conversion(option.to_owned(), v.to_owned())
|
|
|
|
})?)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-23 19:48:05 +00:00
|
|
|
pub struct VmParams<'a> {
|
|
|
|
pub cpus: &'a str,
|
|
|
|
pub memory: &'a str,
|
2019-09-19 07:52:55 +00:00
|
|
|
pub kernel: Option<&'a str>,
|
2020-03-15 17:51:17 +00:00
|
|
|
pub initramfs: Option<&'a str>,
|
2019-05-23 19:48:05 +00:00
|
|
|
pub cmdline: Option<&'a str>,
|
2019-07-08 22:48:39 +00:00
|
|
|
pub disks: Option<Vec<&'a str>>,
|
2019-07-08 22:31:13 +00:00
|
|
|
pub net: Option<Vec<&'a str>>,
|
2019-05-22 20:06:49 +00:00
|
|
|
pub rng: &'a str,
|
2019-06-27 16:14:11 +00:00
|
|
|
pub fs: Option<Vec<&'a str>>,
|
2019-06-28 08:45:58 +00:00
|
|
|
pub pmem: Option<Vec<&'a str>>,
|
2019-07-10 14:41:46 +00:00
|
|
|
pub serial: &'a str,
|
2019-07-22 19:29:02 +00:00
|
|
|
pub console: &'a str,
|
2019-07-15 09:42:40 +00:00
|
|
|
pub devices: Option<Vec<&'a str>>,
|
2019-09-04 17:39:17 +00:00
|
|
|
pub vsock: Option<Vec<&'a str>>,
|
2019-05-23 19:48:05 +00:00
|
|
|
}
|
|
|
|
|
2019-12-12 17:12:42 +00:00
|
|
|
impl<'a> VmParams<'a> {
|
|
|
|
pub fn from_arg_matches(args: &'a ArgMatches) -> Self {
|
|
|
|
// These .unwrap()s cannot fail as there is a default value defined
|
|
|
|
let cpus = args.value_of("cpus").unwrap();
|
|
|
|
let memory = args.value_of("memory").unwrap();
|
|
|
|
let rng = args.value_of("rng").unwrap();
|
|
|
|
let serial = args.value_of("serial").unwrap();
|
|
|
|
|
|
|
|
let kernel = args.value_of("kernel");
|
2020-03-15 17:51:17 +00:00
|
|
|
let initramfs = args.value_of("initramfs");
|
2019-12-12 17:12:42 +00:00
|
|
|
let cmdline = args.value_of("cmdline");
|
|
|
|
|
|
|
|
let disks: Option<Vec<&str>> = args.values_of("disk").map(|x| x.collect());
|
|
|
|
let net: Option<Vec<&str>> = args.values_of("net").map(|x| x.collect());
|
|
|
|
let console = args.value_of("console").unwrap();
|
|
|
|
let fs: Option<Vec<&str>> = args.values_of("fs").map(|x| x.collect());
|
|
|
|
let pmem: Option<Vec<&str>> = args.values_of("pmem").map(|x| x.collect());
|
|
|
|
let devices: Option<Vec<&str>> = args.values_of("device").map(|x| x.collect());
|
|
|
|
let vsock: Option<Vec<&str>> = args.values_of("vsock").map(|x| x.collect());
|
|
|
|
|
|
|
|
VmParams {
|
|
|
|
cpus,
|
|
|
|
memory,
|
|
|
|
kernel,
|
2020-03-15 17:51:17 +00:00
|
|
|
initramfs,
|
2019-12-12 17:12:42 +00:00
|
|
|
cmdline,
|
|
|
|
disks,
|
|
|
|
net,
|
|
|
|
rng,
|
|
|
|
fs,
|
|
|
|
pmem,
|
|
|
|
serial,
|
|
|
|
console,
|
|
|
|
devices,
|
|
|
|
vsock,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-27 12:33:32 +00:00
|
|
|
struct Toggle(bool);
|
|
|
|
|
|
|
|
enum ToggleParseError {
|
|
|
|
InvalidValue(String),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FromStr for Toggle {
|
|
|
|
type Err = ToggleParseError;
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
|
|
|
Ok(Toggle(parse_on_off(s).map_err(|_| {
|
|
|
|
ToggleParseError::InvalidValue(s.to_owned())
|
|
|
|
})?))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-27 12:34:05 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
|
|
|
pub enum HotplugMethod {
|
|
|
|
Acpi,
|
|
|
|
VirtioMem,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for HotplugMethod {
|
|
|
|
fn default() -> Self {
|
|
|
|
HotplugMethod::Acpi
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum ParseHotplugMethodError {
|
|
|
|
InvalidValue(String),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FromStr for HotplugMethod {
|
|
|
|
type Err = ParseHotplugMethodError;
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
|
|
|
match s.to_lowercase().as_str() {
|
|
|
|
"acpi" => Ok(HotplugMethod::Acpi),
|
|
|
|
"virtio-mem" => Ok(HotplugMethod::VirtioMem),
|
|
|
|
_ => Err(ParseHotplugMethodError::InvalidValue(s.to_owned())),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-30 17:27:35 +00:00
|
|
|
struct ByteSized(u64);
|
|
|
|
|
|
|
|
enum ByteSizedParseError {
|
|
|
|
InvalidValue(String),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FromStr for ByteSized {
|
|
|
|
type Err = ByteSizedParseError;
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
|
|
|
Ok(ByteSized(parse_size(s).map_err(|_| {
|
|
|
|
ByteSizedParseError::InvalidValue(s.to_owned())
|
|
|
|
})?))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-09 03:13:02 +00:00
|
|
|
fn parse_size(size: &str) -> Result<u64> {
|
|
|
|
let s = size.trim();
|
|
|
|
|
|
|
|
let shift = if s.ends_with('K') {
|
|
|
|
10
|
|
|
|
} else if s.ends_with('M') {
|
|
|
|
20
|
|
|
|
} else if s.ends_with('G') {
|
|
|
|
30
|
|
|
|
} else {
|
|
|
|
0
|
|
|
|
};
|
|
|
|
|
|
|
|
let s = s.trim_end_matches(|c| c == 'K' || c == 'M' || c == 'G');
|
|
|
|
let res = s.parse::<u64>().map_err(Error::ParseSizeParam)?;
|
|
|
|
Ok(res << shift)
|
|
|
|
}
|
|
|
|
|
2019-11-19 23:40:57 +00:00
|
|
|
fn parse_on_off(param: &str) -> Result<bool> {
|
|
|
|
if !param.is_empty() {
|
|
|
|
let res = match param {
|
2019-10-02 21:01:36 +00:00
|
|
|
"on" => true,
|
|
|
|
"off" => false,
|
2019-11-19 23:40:57 +00:00
|
|
|
_ => return Err(Error::ParseOnOff),
|
2019-10-02 21:01:36 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Ok(res)
|
|
|
|
} else {
|
|
|
|
Ok(false)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-13 10:56:37 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
2019-10-16 05:38:42 +00:00
|
|
|
pub struct CpusConfig {
|
2019-11-25 14:18:03 +00:00
|
|
|
pub boot_vcpus: u8,
|
|
|
|
pub max_vcpus: u8,
|
2019-10-16 05:38:42 +00:00
|
|
|
}
|
2019-05-23 19:48:05 +00:00
|
|
|
|
|
|
|
impl CpusConfig {
|
|
|
|
pub fn parse(cpus: &str) -> Result<Self> {
|
2020-03-27 11:51:09 +00:00
|
|
|
let mut parser = OptionParser::new();
|
|
|
|
parser.add("boot").add("max");
|
|
|
|
parser.parse(cpus).map_err(Error::ParseCpus)?;
|
|
|
|
|
|
|
|
let boot_vcpus: u8 = parser
|
|
|
|
.convert("boot")
|
|
|
|
.map_err(Error::ParseCpus)?
|
|
|
|
.unwrap_or(DEFAULT_VCPUS);
|
|
|
|
let max_vcpus: u8 = parser
|
|
|
|
.convert("max")
|
|
|
|
.map_err(Error::ParseCpus)?
|
|
|
|
.unwrap_or(boot_vcpus);
|
2020-02-21 16:08:25 +00:00
|
|
|
|
|
|
|
if max_vcpus < boot_vcpus {
|
|
|
|
return Err(Error::ParseCpusMaxLowerThanBoot);
|
2019-11-25 14:18:03 +00:00
|
|
|
}
|
2020-02-21 16:08:25 +00:00
|
|
|
|
|
|
|
Ok(CpusConfig {
|
|
|
|
boot_vcpus,
|
|
|
|
max_vcpus,
|
|
|
|
})
|
2019-05-23 19:48:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-27 09:40:50 +00:00
|
|
|
impl Default for CpusConfig {
|
|
|
|
fn default() -> Self {
|
2019-10-16 05:38:42 +00:00
|
|
|
CpusConfig {
|
2019-11-25 14:18:03 +00:00
|
|
|
boot_vcpus: DEFAULT_VCPUS,
|
|
|
|
max_vcpus: DEFAULT_VCPUS,
|
2019-10-16 05:38:42 +00:00
|
|
|
}
|
2019-09-27 09:40:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-13 10:56:37 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
2019-09-23 16:51:36 +00:00
|
|
|
pub struct MemoryConfig {
|
2019-05-24 19:21:23 +00:00
|
|
|
pub size: u64,
|
2019-12-10 13:43:26 +00:00
|
|
|
#[serde(default)]
|
2019-09-23 16:51:36 +00:00
|
|
|
pub file: Option<PathBuf>,
|
2019-11-19 23:40:57 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub mergeable: bool,
|
2020-01-07 11:25:55 +00:00
|
|
|
#[serde(default)]
|
2020-03-04 02:16:07 +00:00
|
|
|
pub hotplug_method: HotplugMethod,
|
|
|
|
#[serde(default)]
|
2020-01-07 11:25:55 +00:00
|
|
|
pub hotplug_size: Option<u64>,
|
2019-05-23 19:48:05 +00:00
|
|
|
}
|
|
|
|
|
2019-09-23 16:51:36 +00:00
|
|
|
impl MemoryConfig {
|
|
|
|
pub fn parse(memory: &str) -> Result<Self> {
|
2020-03-30 17:50:46 +00:00
|
|
|
let mut parser = OptionParser::new();
|
|
|
|
parser
|
|
|
|
.add("size")
|
|
|
|
.add("file")
|
|
|
|
.add("mergeable")
|
|
|
|
.add("hotplug_method")
|
|
|
|
.add("hotplug_size");
|
|
|
|
parser.parse(memory).map_err(Error::ParseMemory)?;
|
|
|
|
|
|
|
|
let size = parser
|
|
|
|
.convert::<ByteSized>("size")
|
|
|
|
.map_err(Error::ParseMemory)?
|
|
|
|
.unwrap_or(ByteSized(DEFAULT_MEMORY_MB << 20))
|
|
|
|
.0;
|
|
|
|
let file = parser.get("file").map(PathBuf::from);
|
|
|
|
let mergeable = parser
|
|
|
|
.convert::<Toggle>("mergeable")
|
|
|
|
.map_err(Error::ParseMemory)?
|
|
|
|
.unwrap_or(Toggle(false))
|
|
|
|
.0;
|
|
|
|
let hotplug_method = parser
|
|
|
|
.convert("hotplug_method")
|
|
|
|
.map_err(Error::ParseMemory)?
|
|
|
|
.unwrap_or_default();
|
|
|
|
let hotplug_size = parser
|
|
|
|
.convert::<ByteSized>("hotplug_size")
|
|
|
|
.map_err(Error::ParseMemory)?
|
|
|
|
.map(|v| v.0);
|
2020-03-04 02:16:07 +00:00
|
|
|
|
2019-05-24 19:21:23 +00:00
|
|
|
Ok(MemoryConfig {
|
2020-03-30 17:50:46 +00:00
|
|
|
size,
|
2019-05-24 19:21:23 +00:00
|
|
|
file,
|
2020-03-30 17:50:46 +00:00
|
|
|
mergeable,
|
2020-03-04 02:16:07 +00:00
|
|
|
hotplug_method,
|
2020-03-30 17:50:46 +00:00
|
|
|
hotplug_size,
|
2019-05-24 19:21:23 +00:00
|
|
|
})
|
2019-05-23 19:48:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-27 09:40:50 +00:00
|
|
|
impl Default for MemoryConfig {
|
|
|
|
fn default() -> Self {
|
|
|
|
MemoryConfig {
|
|
|
|
size: DEFAULT_MEMORY_MB << 20,
|
|
|
|
file: None,
|
2019-11-19 23:40:57 +00:00
|
|
|
mergeable: false,
|
2020-03-04 02:16:07 +00:00
|
|
|
hotplug_method: HotplugMethod::Acpi,
|
2020-01-07 11:25:55 +00:00
|
|
|
hotplug_size: None,
|
2019-09-27 09:40:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-13 10:56:37 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
2019-09-23 16:54:00 +00:00
|
|
|
pub struct KernelConfig {
|
|
|
|
pub path: PathBuf,
|
2019-05-23 19:48:05 +00:00
|
|
|
}
|
|
|
|
|
2020-03-15 17:51:17 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
|
|
|
pub struct InitramfsConfig {
|
|
|
|
pub path: PathBuf,
|
|
|
|
}
|
|
|
|
|
2019-12-13 10:56:37 +00:00
|
|
|
#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)]
|
2019-05-23 19:48:05 +00:00
|
|
|
pub struct CmdlineConfig {
|
2019-09-27 08:39:56 +00:00
|
|
|
pub args: String,
|
2019-05-23 19:48:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl CmdlineConfig {
|
|
|
|
pub fn parse(cmdline: Option<&str>) -> Result<Self> {
|
2019-09-27 08:39:56 +00:00
|
|
|
let args = cmdline
|
2019-05-23 19:48:05 +00:00
|
|
|
.map(std::string::ToString::to_string)
|
|
|
|
.unwrap_or_else(String::new);
|
|
|
|
|
2019-09-27 16:06:53 +00:00
|
|
|
Ok(CmdlineConfig { args })
|
2019-05-23 19:48:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-13 10:56:37 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
2019-09-23 17:12:19 +00:00
|
|
|
pub struct DiskConfig {
|
2020-03-13 10:25:17 +00:00
|
|
|
pub path: Option<PathBuf>,
|
2019-10-02 21:01:36 +00:00
|
|
|
#[serde(default)]
|
2020-01-21 11:22:09 +00:00
|
|
|
pub readonly: bool,
|
|
|
|
#[serde(default)]
|
2020-01-21 11:36:27 +00:00
|
|
|
pub direct: bool,
|
|
|
|
#[serde(default)]
|
2019-10-02 21:01:36 +00:00
|
|
|
pub iommu: bool,
|
2020-01-24 15:31:13 +00:00
|
|
|
#[serde(default = "default_diskconfig_num_queues")]
|
|
|
|
pub num_queues: usize,
|
|
|
|
#[serde(default = "default_diskconfig_queue_size")]
|
|
|
|
pub queue_size: u16,
|
2020-01-28 11:43:15 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub vhost_user: bool,
|
|
|
|
pub vhost_socket: Option<String>,
|
|
|
|
#[serde(default = "default_diskconfig_wce")]
|
|
|
|
pub wce: bool,
|
2020-02-20 15:49:16 +00:00
|
|
|
#[serde(default = "default_diskconfig_poll_queue")]
|
|
|
|
pub poll_queue: bool,
|
2020-01-24 15:31:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn default_diskconfig_num_queues() -> usize {
|
|
|
|
DEFAULT_NUM_QUEUES_VUBLK
|
|
|
|
}
|
|
|
|
|
|
|
|
fn default_diskconfig_queue_size() -> u16 {
|
|
|
|
DEFAULT_QUEUE_SIZE_VUBLK
|
2019-05-23 19:48:05 +00:00
|
|
|
}
|
|
|
|
|
2020-01-28 11:43:15 +00:00
|
|
|
fn default_diskconfig_wce() -> bool {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
2020-02-20 15:49:16 +00:00
|
|
|
fn default_diskconfig_poll_queue() -> bool {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
2020-03-31 10:46:11 +00:00
|
|
|
impl Default for DiskConfig {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
path: None,
|
|
|
|
readonly: false,
|
|
|
|
direct: false,
|
|
|
|
iommu: false,
|
|
|
|
num_queues: default_diskconfig_num_queues(),
|
|
|
|
queue_size: default_diskconfig_queue_size(),
|
|
|
|
vhost_user: false,
|
|
|
|
vhost_socket: None,
|
|
|
|
wce: default_diskconfig_wce(),
|
|
|
|
poll_queue: default_diskconfig_poll_queue(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-23 17:12:19 +00:00
|
|
|
impl DiskConfig {
|
2020-03-12 16:31:26 +00:00
|
|
|
pub const SYNTAX: &'static str = "Disk parameters \
|
|
|
|
\"path=<disk_image_path>,readonly=on|off,iommu=on|off,num_queues=<number_of_queues>,\
|
|
|
|
queue_size=<size_of_each_queue>,vhost_user=<vhost_user_enable>,\
|
|
|
|
socket=<vhost_user_socket_path>,wce=<true|false, default true>\"";
|
|
|
|
|
2019-09-23 17:12:19 +00:00
|
|
|
pub fn parse(disk: &str) -> Result<Self> {
|
2020-03-31 10:45:43 +00:00
|
|
|
let mut parser = OptionParser::new();
|
|
|
|
parser
|
|
|
|
.add("path")
|
|
|
|
.add("readonly")
|
|
|
|
.add("direct")
|
|
|
|
.add("iommu")
|
|
|
|
.add("queue_size")
|
|
|
|
.add("num_queues")
|
|
|
|
.add("vhost_user")
|
|
|
|
.add("socket")
|
|
|
|
.add("wce")
|
|
|
|
.add("poll_queue");
|
|
|
|
parser.parse(disk).map_err(Error::ParseDisk)?;
|
|
|
|
|
|
|
|
let path = parser.get("path").map(PathBuf::from);
|
|
|
|
let readonly = parser
|
|
|
|
.convert::<Toggle>("readonly")
|
|
|
|
.map_err(Error::ParseDisk)?
|
|
|
|
.unwrap_or(Toggle(false))
|
|
|
|
.0;
|
|
|
|
let direct = parser
|
|
|
|
.convert::<Toggle>("direct")
|
|
|
|
.map_err(Error::ParseDisk)?
|
|
|
|
.unwrap_or(Toggle(false))
|
|
|
|
.0;
|
|
|
|
let iommu = parser
|
|
|
|
.convert::<Toggle>("iommu")
|
|
|
|
.map_err(Error::ParseDisk)?
|
|
|
|
.unwrap_or(Toggle(false))
|
|
|
|
.0;
|
|
|
|
let queue_size = parser
|
|
|
|
.convert("queue_size")
|
|
|
|
.map_err(Error::ParseDisk)?
|
|
|
|
.unwrap_or_else(default_diskconfig_queue_size);
|
|
|
|
let num_queues = parser
|
|
|
|
.convert("num_queues")
|
|
|
|
.map_err(Error::ParseDisk)?
|
|
|
|
.unwrap_or_else(default_diskconfig_num_queues);
|
|
|
|
let vhost_user = parser
|
|
|
|
.convert("vhost_user")
|
|
|
|
.map_err(Error::ParseDisk)?
|
|
|
|
.unwrap_or(false);
|
|
|
|
let vhost_socket = parser.get("socket");
|
|
|
|
let wce = parser
|
|
|
|
.convert("wce")
|
|
|
|
.map_err(Error::ParseDisk)?
|
|
|
|
.unwrap_or_else(default_diskconfig_wce);
|
|
|
|
let poll_queue = parser
|
|
|
|
.convert("poll_queue")
|
|
|
|
.map_err(Error::ParseDisk)?
|
|
|
|
.unwrap_or_else(default_diskconfig_poll_queue);
|
|
|
|
|
|
|
|
if parser.is_set("wce") && !vhost_user {
|
|
|
|
warn!("wce parameter currently only has effect when used vhost_user=true");
|
2019-10-02 21:01:36 +00:00
|
|
|
}
|
|
|
|
|
2020-03-31 10:45:43 +00:00
|
|
|
if parser.is_set("poll_queue") && !vhost_user {
|
|
|
|
warn!("poll_queue parameter currently only has effect when used vhost_user=true");
|
2020-03-13 10:25:17 +00:00
|
|
|
}
|
2020-01-28 11:43:15 +00:00
|
|
|
|
2020-03-13 10:28:39 +00:00
|
|
|
if vhost_socket.as_ref().and(path.as_ref()).is_some() {
|
|
|
|
return Err(Error::ParseDiskSocketAndPath);
|
|
|
|
}
|
|
|
|
|
2019-05-23 19:48:05 +00:00
|
|
|
Ok(DiskConfig {
|
2020-03-13 10:25:17 +00:00
|
|
|
path,
|
2020-03-31 10:45:43 +00:00
|
|
|
readonly,
|
|
|
|
direct,
|
|
|
|
iommu,
|
2020-01-24 15:31:13 +00:00
|
|
|
num_queues,
|
|
|
|
queue_size,
|
2020-01-28 11:43:15 +00:00
|
|
|
vhost_socket,
|
|
|
|
vhost_user,
|
|
|
|
wce,
|
2020-02-20 15:49:16 +00:00
|
|
|
poll_queue,
|
2019-05-23 19:48:05 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-13 10:56:37 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
2019-09-23 17:14:35 +00:00
|
|
|
pub struct NetConfig {
|
2019-12-09 17:45:00 +00:00
|
|
|
#[serde(default = "default_netconfig_tap")]
|
2019-09-23 17:14:35 +00:00
|
|
|
pub tap: Option<String>,
|
2019-12-09 17:45:00 +00:00
|
|
|
#[serde(default = "default_netconfig_ip")]
|
2019-05-23 19:48:05 +00:00
|
|
|
pub ip: Ipv4Addr,
|
2019-12-09 17:45:00 +00:00
|
|
|
#[serde(default = "default_netconfig_mask")]
|
2019-05-23 19:48:05 +00:00
|
|
|
pub mask: Ipv4Addr,
|
2019-12-09 17:45:00 +00:00
|
|
|
#[serde(default = "default_netconfig_mac")]
|
2019-05-23 19:48:05 +00:00
|
|
|
pub mac: MacAddr,
|
2019-10-02 21:13:44 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub iommu: bool,
|
2019-11-12 09:14:54 +00:00
|
|
|
#[serde(default = "default_netconfig_num_queues")]
|
|
|
|
pub num_queues: usize,
|
|
|
|
#[serde(default = "default_netconfig_queue_size")]
|
|
|
|
pub queue_size: u16,
|
2020-01-27 15:14:07 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub vhost_user: bool,
|
|
|
|
pub vhost_socket: Option<String>,
|
2019-05-23 19:48:05 +00:00
|
|
|
}
|
|
|
|
|
2019-12-09 17:45:00 +00:00
|
|
|
fn default_netconfig_tap() -> Option<String> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
fn default_netconfig_ip() -> Ipv4Addr {
|
|
|
|
Ipv4Addr::new(192, 168, 249, 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn default_netconfig_mask() -> Ipv4Addr {
|
|
|
|
Ipv4Addr::new(255, 255, 255, 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn default_netconfig_mac() -> MacAddr {
|
|
|
|
MacAddr::local_random()
|
|
|
|
}
|
|
|
|
|
2019-11-12 09:14:54 +00:00
|
|
|
fn default_netconfig_num_queues() -> usize {
|
|
|
|
DEFAULT_NUM_QUEUES_VUNET
|
|
|
|
}
|
|
|
|
|
|
|
|
fn default_netconfig_queue_size() -> u16 {
|
|
|
|
DEFAULT_QUEUE_SIZE_VUNET
|
|
|
|
}
|
|
|
|
|
2020-03-31 15:27:13 +00:00
|
|
|
impl Default for NetConfig {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
tap: default_netconfig_tap(),
|
|
|
|
ip: default_netconfig_ip(),
|
|
|
|
mask: default_netconfig_mask(),
|
|
|
|
mac: default_netconfig_mac(),
|
|
|
|
iommu: false,
|
|
|
|
num_queues: default_netconfig_num_queues(),
|
|
|
|
queue_size: default_netconfig_queue_size(),
|
|
|
|
vhost_user: false,
|
|
|
|
vhost_socket: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-23 17:14:35 +00:00
|
|
|
impl NetConfig {
|
2020-03-12 16:31:26 +00:00
|
|
|
pub const SYNTAX: &'static str = "Network parameters \
|
|
|
|
\"tap=<if_name>,ip=<ip_addr>,mask=<net_mask>,mac=<mac_addr>,iommu=on|off,\
|
|
|
|
num_queues=<number_of_queues>,queue_size=<size_of_each_queue>,\
|
|
|
|
vhost_user=<vhost_user_enable>,socket=<vhost_user_socket_path>\"";
|
|
|
|
|
2019-09-23 17:14:35 +00:00
|
|
|
pub fn parse(net: &str) -> Result<Self> {
|
2020-03-31 15:54:42 +00:00
|
|
|
let mut parser = OptionParser::new();
|
2019-05-23 19:48:05 +00:00
|
|
|
|
2020-03-31 15:54:42 +00:00
|
|
|
parser
|
|
|
|
.add("tap")
|
|
|
|
.add("ip")
|
|
|
|
.add("mask")
|
|
|
|
.add("mac")
|
|
|
|
.add("iommu")
|
|
|
|
.add("queue_size")
|
|
|
|
.add("num_queues")
|
|
|
|
.add("vhost_user")
|
|
|
|
.add("socket");
|
|
|
|
parser.parse(net).map_err(Error::ParseNetwork)?;
|
|
|
|
|
|
|
|
let tap = parser.get("tap");
|
|
|
|
let ip = parser
|
|
|
|
.convert("ip")
|
|
|
|
.map_err(Error::ParseNetwork)?
|
|
|
|
.unwrap_or_else(default_netconfig_ip);
|
|
|
|
let mask = parser
|
|
|
|
.convert("mask")
|
|
|
|
.map_err(Error::ParseNetwork)?
|
|
|
|
.unwrap_or_else(default_netconfig_mask);
|
|
|
|
let mac = parser
|
|
|
|
.convert("mac")
|
|
|
|
.map_err(Error::ParseNetwork)?
|
|
|
|
.unwrap_or_else(default_netconfig_mac);
|
|
|
|
let iommu = parser
|
|
|
|
.convert::<Toggle>("iommu")
|
|
|
|
.map_err(Error::ParseNetwork)?
|
|
|
|
.unwrap_or(Toggle(false))
|
|
|
|
.0;
|
|
|
|
let queue_size = parser
|
|
|
|
.convert("queue_size")
|
|
|
|
.map_err(Error::ParseNetwork)?
|
|
|
|
.unwrap_or_else(default_netconfig_queue_size);
|
|
|
|
let num_queues = parser
|
|
|
|
.convert("num_queues")
|
|
|
|
.map_err(Error::ParseNetwork)?
|
|
|
|
.unwrap_or_else(default_netconfig_num_queues);
|
|
|
|
let vhost_user = parser
|
|
|
|
.convert("vhost_user")
|
|
|
|
.map_err(Error::ParseNetwork)?
|
|
|
|
.unwrap_or(false);
|
|
|
|
let vhost_socket = parser.get("socket");
|
2020-01-27 15:14:07 +00:00
|
|
|
|
2019-10-02 21:13:44 +00:00
|
|
|
Ok(NetConfig {
|
|
|
|
tap,
|
|
|
|
ip,
|
|
|
|
mask,
|
|
|
|
mac,
|
|
|
|
iommu,
|
2019-11-12 09:14:54 +00:00
|
|
|
num_queues,
|
|
|
|
queue_size,
|
2020-01-27 15:14:07 +00:00
|
|
|
vhost_user,
|
|
|
|
vhost_socket,
|
2019-10-02 21:13:44 +00:00
|
|
|
})
|
2019-05-23 19:48:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-13 10:56:37 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
2019-09-23 17:15:50 +00:00
|
|
|
pub struct RngConfig {
|
|
|
|
pub src: PathBuf,
|
2019-10-04 18:18:49 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub iommu: bool,
|
2019-05-22 20:06:49 +00:00
|
|
|
}
|
|
|
|
|
2019-09-23 17:15:50 +00:00
|
|
|
impl RngConfig {
|
|
|
|
pub fn parse(rng: &str) -> Result<Self> {
|
2020-04-02 09:17:10 +00:00
|
|
|
let mut parser = OptionParser::new();
|
|
|
|
parser.add("src").add("iommu");
|
|
|
|
parser.parse(rng).map_err(Error::ParseRNG)?;
|
|
|
|
|
|
|
|
let src = PathBuf::from(
|
|
|
|
parser
|
|
|
|
.get("src")
|
|
|
|
.unwrap_or_else(|| DEFAULT_RNG_SOURCE.to_owned()),
|
|
|
|
);
|
|
|
|
let iommu = parser
|
|
|
|
.convert::<Toggle>("iommu")
|
|
|
|
.map_err(Error::ParseRNG)?
|
|
|
|
.unwrap_or(Toggle(false))
|
|
|
|
.0;
|
2019-10-04 18:18:49 +00:00
|
|
|
|
2020-04-02 09:17:10 +00:00
|
|
|
Ok(RngConfig { src, iommu })
|
2019-05-22 20:06:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-27 09:40:50 +00:00
|
|
|
impl Default for RngConfig {
|
|
|
|
fn default() -> Self {
|
|
|
|
RngConfig {
|
|
|
|
src: PathBuf::from(DEFAULT_RNG_SOURCE),
|
2019-10-04 18:18:49 +00:00
|
|
|
iommu: false,
|
2019-09-27 09:40:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-13 10:56:37 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
2019-09-23 17:23:17 +00:00
|
|
|
pub struct FsConfig {
|
|
|
|
pub tag: String,
|
|
|
|
pub sock: PathBuf,
|
2019-12-09 17:22:51 +00:00
|
|
|
#[serde(default = "default_fsconfig_num_queues")]
|
2019-05-22 20:06:49 +00:00
|
|
|
pub num_queues: usize,
|
2019-12-09 17:22:51 +00:00
|
|
|
#[serde(default = "default_fsconfig_queue_size")]
|
2019-05-22 20:06:49 +00:00
|
|
|
pub queue_size: u16,
|
vmm: api: Adjust FsConfig for OpenAPI
The FsConfig structure has been recently adjusted so that the default
value matches between OpenAPI and CLI. Unfortunately, with the current
description, there is no way from the OpenAPI to describe a cache_size
value "None", so that DAX does not get enabled. Usually, using a Rust
"Option" works because the default value is None. But in this case, the
default value is Some(8G), which means we cannot describe a None.
This commit tackles the problem, introducing an explicit parameter
"dax", and leaving "cache_size" as a simple u64 integer.
This way, the default value is dax=true and cache_size=8G, but it lets
the opportunity to disable DAX entirely with dax=false, which will
simply ignore the cache_size value.
Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2019-12-11 09:44:26 +00:00
|
|
|
#[serde(default = "default_fsconfig_dax")]
|
|
|
|
pub dax: bool,
|
2019-12-09 17:22:51 +00:00
|
|
|
#[serde(default = "default_fsconfig_cache_size")]
|
vmm: api: Adjust FsConfig for OpenAPI
The FsConfig structure has been recently adjusted so that the default
value matches between OpenAPI and CLI. Unfortunately, with the current
description, there is no way from the OpenAPI to describe a cache_size
value "None", so that DAX does not get enabled. Usually, using a Rust
"Option" works because the default value is None. But in this case, the
default value is Some(8G), which means we cannot describe a None.
This commit tackles the problem, introducing an explicit parameter
"dax", and leaving "cache_size" as a simple u64 integer.
This way, the default value is dax=true and cache_size=8G, but it lets
the opportunity to disable DAX entirely with dax=false, which will
simply ignore the cache_size value.
Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2019-12-11 09:44:26 +00:00
|
|
|
pub cache_size: u64,
|
2019-05-22 20:06:49 +00:00
|
|
|
}
|
|
|
|
|
2019-12-09 17:22:51 +00:00
|
|
|
fn default_fsconfig_num_queues() -> usize {
|
|
|
|
1
|
|
|
|
}
|
|
|
|
|
|
|
|
fn default_fsconfig_queue_size() -> u16 {
|
|
|
|
1024
|
|
|
|
}
|
|
|
|
|
vmm: api: Adjust FsConfig for OpenAPI
The FsConfig structure has been recently adjusted so that the default
value matches between OpenAPI and CLI. Unfortunately, with the current
description, there is no way from the OpenAPI to describe a cache_size
value "None", so that DAX does not get enabled. Usually, using a Rust
"Option" works because the default value is None. But in this case, the
default value is Some(8G), which means we cannot describe a None.
This commit tackles the problem, introducing an explicit parameter
"dax", and leaving "cache_size" as a simple u64 integer.
This way, the default value is dax=true and cache_size=8G, but it lets
the opportunity to disable DAX entirely with dax=false, which will
simply ignore the cache_size value.
Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2019-12-11 09:44:26 +00:00
|
|
|
fn default_fsconfig_dax() -> bool {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
|
|
|
fn default_fsconfig_cache_size() -> u64 {
|
|
|
|
0x0002_0000_0000
|
2019-12-09 17:22:51 +00:00
|
|
|
}
|
|
|
|
|
2020-04-02 09:41:35 +00:00
|
|
|
impl Default for FsConfig {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
tag: "".to_owned(),
|
|
|
|
sock: PathBuf::new(),
|
|
|
|
num_queues: default_fsconfig_num_queues(),
|
|
|
|
queue_size: default_fsconfig_queue_size(),
|
|
|
|
dax: default_fsconfig_dax(),
|
|
|
|
cache_size: default_fsconfig_cache_size(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-23 17:23:17 +00:00
|
|
|
impl FsConfig {
|
|
|
|
pub fn parse(fs: &str) -> Result<Self> {
|
2020-04-02 10:00:45 +00:00
|
|
|
let mut parser = OptionParser::new();
|
|
|
|
parser
|
|
|
|
.add("tag")
|
|
|
|
.add("dax")
|
|
|
|
.add("cache_size")
|
|
|
|
.add("queue_size")
|
|
|
|
.add("num_queues")
|
|
|
|
.add("sock");
|
|
|
|
parser.parse(fs).map_err(Error::ParseFileSystem)?;
|
2019-05-22 20:06:49 +00:00
|
|
|
|
2020-04-02 10:00:45 +00:00
|
|
|
let tag = parser.get("tag").ok_or(Error::ParseFsTagMissing)?;
|
|
|
|
let sock = PathBuf::from(parser.get("sock").ok_or(Error::ParseFsSockMissing)?);
|
2019-05-22 20:06:49 +00:00
|
|
|
|
2020-04-02 10:00:45 +00:00
|
|
|
let queue_size = parser
|
|
|
|
.convert("queue_size")
|
|
|
|
.map_err(Error::ParseFileSystem)?
|
|
|
|
.unwrap_or_else(default_fsconfig_queue_size);
|
|
|
|
let num_queues = parser
|
|
|
|
.convert("num_queues")
|
|
|
|
.map_err(Error::ParseFileSystem)?
|
|
|
|
.unwrap_or_else(default_fsconfig_num_queues);
|
2019-08-05 19:45:58 +00:00
|
|
|
|
2020-04-02 10:00:45 +00:00
|
|
|
let dax = parser
|
|
|
|
.convert::<Toggle>("dax")
|
|
|
|
.map_err(Error::ParseFileSystem)?
|
|
|
|
.unwrap_or_else(|| Toggle(default_fsconfig_dax()))
|
|
|
|
.0;
|
|
|
|
|
|
|
|
if parser.is_set("cache_size") && !dax {
|
|
|
|
return Err(Error::InvalidCacheSizeWithDaxOff);
|
2019-08-05 19:45:58 +00:00
|
|
|
}
|
2019-05-22 20:06:49 +00:00
|
|
|
|
2020-04-02 10:00:45 +00:00
|
|
|
let cache_size = parser
|
|
|
|
.convert::<ByteSized>("cache_size")
|
|
|
|
.map_err(Error::ParseFileSystem)?
|
|
|
|
.unwrap_or_else(|| ByteSized(default_fsconfig_cache_size()))
|
|
|
|
.0;
|
|
|
|
|
2019-06-27 16:14:11 +00:00
|
|
|
Ok(FsConfig {
|
2020-04-02 10:00:45 +00:00
|
|
|
tag,
|
|
|
|
sock,
|
2019-05-22 20:06:49 +00:00
|
|
|
num_queues,
|
|
|
|
queue_size,
|
vmm: api: Adjust FsConfig for OpenAPI
The FsConfig structure has been recently adjusted so that the default
value matches between OpenAPI and CLI. Unfortunately, with the current
description, there is no way from the OpenAPI to describe a cache_size
value "None", so that DAX does not get enabled. Usually, using a Rust
"Option" works because the default value is None. But in this case, the
default value is Some(8G), which means we cannot describe a None.
This commit tackles the problem, introducing an explicit parameter
"dax", and leaving "cache_size" as a simple u64 integer.
This way, the default value is dax=true and cache_size=8G, but it lets
the opportunity to disable DAX entirely with dax=false, which will
simply ignore the cache_size value.
Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2019-12-11 09:44:26 +00:00
|
|
|
dax,
|
2019-08-05 19:45:58 +00:00
|
|
|
cache_size,
|
2019-06-27 16:14:11 +00:00
|
|
|
})
|
2019-05-22 20:06:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-02 14:52:33 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Default)]
|
2019-09-23 17:27:56 +00:00
|
|
|
pub struct PmemConfig {
|
|
|
|
pub file: PathBuf,
|
2019-06-19 20:48:37 +00:00
|
|
|
pub size: u64,
|
2019-10-04 18:27:22 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub iommu: bool,
|
2019-11-19 23:47:31 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub mergeable: bool,
|
2020-03-19 10:13:53 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub discard_writes: bool,
|
2019-06-19 20:48:37 +00:00
|
|
|
}
|
|
|
|
|
2019-09-23 17:27:56 +00:00
|
|
|
impl PmemConfig {
|
2020-03-12 16:31:26 +00:00
|
|
|
pub const SYNTAX: &'static str = "Persistent memory parameters \
|
|
|
|
\"file=<backing_file_path>,size=<persistent_memory_size>,iommu=on|off,\
|
|
|
|
mergeable=on|off,discard_writes=on|off,\"";
|
2019-09-23 17:27:56 +00:00
|
|
|
pub fn parse(pmem: &str) -> Result<Self> {
|
2020-04-02 15:00:52 +00:00
|
|
|
let mut parser = OptionParser::new();
|
|
|
|
parser
|
|
|
|
.add("size")
|
|
|
|
.add("file")
|
|
|
|
.add("mergeable")
|
|
|
|
.add("iommu")
|
|
|
|
.add("discard_writes");
|
|
|
|
parser.parse(pmem).map_err(Error::ParsePersistentMemory)?;
|
2019-06-19 20:48:37 +00:00
|
|
|
|
2020-04-02 15:00:52 +00:00
|
|
|
let file = PathBuf::from(parser.get("file").ok_or(Error::ParsePmemFileMissing)?);
|
|
|
|
let size = parser
|
|
|
|
.convert::<ByteSized>("size")
|
|
|
|
.map_err(Error::ParseMemory)?
|
|
|
|
.ok_or(Error::ParsePmemSizeMissing)?
|
|
|
|
.0;
|
|
|
|
let mergeable = parser
|
|
|
|
.convert::<Toggle>("mergeable")
|
|
|
|
.map_err(Error::ParsePersistentMemory)?
|
|
|
|
.unwrap_or(Toggle(false))
|
|
|
|
.0;
|
|
|
|
let iommu = parser
|
|
|
|
.convert::<Toggle>("iommu")
|
|
|
|
.map_err(Error::ParsePersistentMemory)?
|
|
|
|
.unwrap_or(Toggle(false))
|
|
|
|
.0;
|
|
|
|
let discard_writes = parser
|
|
|
|
.convert::<Toggle>("discard_writes")
|
|
|
|
.map_err(Error::ParsePersistentMemory)?
|
|
|
|
.unwrap_or(Toggle(false))
|
|
|
|
.0;
|
2019-06-19 20:48:37 +00:00
|
|
|
|
2019-06-28 08:45:58 +00:00
|
|
|
Ok(PmemConfig {
|
2020-04-02 15:00:52 +00:00
|
|
|
file,
|
|
|
|
size,
|
|
|
|
iommu,
|
|
|
|
mergeable,
|
|
|
|
discard_writes,
|
2019-06-28 08:45:58 +00:00
|
|
|
})
|
2019-06-19 20:48:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-13 10:56:37 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
2019-07-22 19:29:02 +00:00
|
|
|
pub enum ConsoleOutputMode {
|
2019-07-10 14:41:46 +00:00
|
|
|
Off,
|
|
|
|
Tty,
|
|
|
|
File,
|
2019-08-09 07:56:10 +00:00
|
|
|
Null,
|
2019-07-10 14:41:46 +00:00
|
|
|
}
|
|
|
|
|
2019-08-09 10:48:03 +00:00
|
|
|
impl ConsoleOutputMode {
|
|
|
|
pub fn input_enabled(&self) -> bool {
|
|
|
|
match self {
|
|
|
|
ConsoleOutputMode::Tty => true,
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-13 10:56:37 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
2019-09-23 17:30:22 +00:00
|
|
|
pub struct ConsoleConfig {
|
2019-12-10 14:40:47 +00:00
|
|
|
#[serde(default = "default_consoleconfig_file")]
|
2019-09-23 17:30:22 +00:00
|
|
|
pub file: Option<PathBuf>,
|
2019-07-22 19:29:02 +00:00
|
|
|
pub mode: ConsoleOutputMode,
|
2019-10-04 19:01:32 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub iommu: bool,
|
2019-07-10 14:41:46 +00:00
|
|
|
}
|
|
|
|
|
2019-12-10 14:40:47 +00:00
|
|
|
fn default_consoleconfig_file() -> Option<PathBuf> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
2019-09-23 17:30:22 +00:00
|
|
|
impl ConsoleConfig {
|
2019-10-04 19:01:32 +00:00
|
|
|
pub fn parse(console: &str) -> Result<Self> {
|
2020-04-02 15:52:30 +00:00
|
|
|
let mut parser = OptionParser::new();
|
|
|
|
parser
|
|
|
|
.add_valueless("off")
|
|
|
|
.add_valueless("tty")
|
|
|
|
.add_valueless("null")
|
|
|
|
.add("file")
|
|
|
|
.add("iommu");
|
|
|
|
parser.parse(console).map_err(Error::ParseConsole)?;
|
2019-10-04 19:01:32 +00:00
|
|
|
|
2019-12-10 14:40:47 +00:00
|
|
|
let mut file: Option<PathBuf> = default_consoleconfig_file();
|
2019-10-04 19:01:32 +00:00
|
|
|
let mut mode: ConsoleOutputMode = ConsoleOutputMode::Off;
|
|
|
|
|
2020-04-02 15:52:30 +00:00
|
|
|
if parser.is_set("off") {
|
|
|
|
} else if parser.is_set("tty") {
|
|
|
|
mode = ConsoleOutputMode::Tty
|
|
|
|
} else if parser.is_set("null") {
|
|
|
|
mode = ConsoleOutputMode::Null
|
|
|
|
} else if parser.is_set("file") {
|
|
|
|
mode = ConsoleOutputMode::File;
|
|
|
|
file = Some(PathBuf::from(
|
|
|
|
parser.get("file").ok_or(Error::ParseConsoleFileMissing)?,
|
|
|
|
));
|
|
|
|
} else {
|
|
|
|
return Err(Error::ParseConsoleInvalidModeGiven);
|
2019-10-04 19:01:32 +00:00
|
|
|
}
|
2020-04-02 15:52:30 +00:00
|
|
|
let iommu = parser
|
|
|
|
.convert::<Toggle>("iommu")
|
|
|
|
.map_err(Error::ParseConsole)?
|
|
|
|
.unwrap_or(Toggle(false))
|
|
|
|
.0;
|
2019-10-04 19:01:32 +00:00
|
|
|
|
2020-04-02 15:52:30 +00:00
|
|
|
Ok(Self { mode, file, iommu })
|
2019-07-10 14:41:46 +00:00
|
|
|
}
|
2019-09-27 09:40:50 +00:00
|
|
|
|
|
|
|
pub fn default_serial() -> Self {
|
|
|
|
ConsoleConfig {
|
|
|
|
file: None,
|
|
|
|
mode: ConsoleOutputMode::Null,
|
2019-10-04 19:01:32 +00:00
|
|
|
iommu: false,
|
2019-09-27 09:40:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn default_console() -> Self {
|
|
|
|
ConsoleConfig {
|
|
|
|
file: None,
|
|
|
|
mode: ConsoleOutputMode::Tty,
|
2019-10-04 19:01:32 +00:00
|
|
|
iommu: false,
|
2019-09-27 09:40:50 +00:00
|
|
|
}
|
|
|
|
}
|
2019-07-10 14:41:46 +00:00
|
|
|
}
|
|
|
|
|
2019-12-13 10:56:37 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
2019-09-23 17:31:41 +00:00
|
|
|
pub struct DeviceConfig {
|
|
|
|
pub path: PathBuf,
|
2019-10-07 16:03:58 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub iommu: bool,
|
2020-03-09 13:08:59 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub id: Option<String>,
|
2019-07-15 09:42:40 +00:00
|
|
|
}
|
|
|
|
|
2019-09-23 17:31:41 +00:00
|
|
|
impl DeviceConfig {
|
2020-03-12 16:20:31 +00:00
|
|
|
pub const SYNTAX: &'static str =
|
|
|
|
"Direct device assignment parameters \"path=<device_path>,iommu=on|off,id=<device_id>\"";
|
2019-09-23 17:31:41 +00:00
|
|
|
pub fn parse(device: &str) -> Result<Self> {
|
2019-10-07 16:03:58 +00:00
|
|
|
// Split the parameters based on the comma delimiter
|
|
|
|
let params_list: Vec<&str> = device.split(',').collect();
|
|
|
|
|
|
|
|
let mut path_str: &str = "";
|
|
|
|
let mut iommu_str: &str = "";
|
2020-03-11 10:23:42 +00:00
|
|
|
let mut id_str: &str = "";
|
2019-10-07 16:03:58 +00:00
|
|
|
|
|
|
|
for param in params_list.iter() {
|
|
|
|
if param.starts_with("path=") {
|
|
|
|
path_str = ¶m[5..];
|
|
|
|
} else if param.starts_with("iommu=") {
|
|
|
|
iommu_str = ¶m[6..];
|
2020-03-11 10:23:42 +00:00
|
|
|
} else if param.starts_with("id=") {
|
|
|
|
id_str = ¶m[3..];
|
2019-10-07 16:03:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-11 10:23:42 +00:00
|
|
|
let id = if !id_str.is_empty() {
|
|
|
|
Some(String::from(id_str))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
|
2019-07-15 09:42:40 +00:00
|
|
|
Ok(DeviceConfig {
|
2019-10-07 16:03:58 +00:00
|
|
|
path: PathBuf::from(path_str),
|
2019-11-19 23:40:57 +00:00
|
|
|
iommu: parse_on_off(iommu_str)?,
|
2020-03-11 10:23:42 +00:00
|
|
|
id,
|
2019-07-15 09:42:40 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-13 10:56:37 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
2019-09-23 17:46:35 +00:00
|
|
|
pub struct VsockConfig {
|
2019-09-04 17:39:17 +00:00
|
|
|
pub cid: u64,
|
2019-09-23 17:46:35 +00:00
|
|
|
pub sock: PathBuf,
|
2019-10-04 19:06:34 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub iommu: bool,
|
2019-09-04 17:39:17 +00:00
|
|
|
}
|
|
|
|
|
2019-09-23 17:46:35 +00:00
|
|
|
impl VsockConfig {
|
|
|
|
pub fn parse(vsock: &str) -> Result<Self> {
|
2019-09-04 17:39:17 +00:00
|
|
|
// Split the parameters based on the comma delimiter
|
|
|
|
let params_list: Vec<&str> = vsock.split(',').collect();
|
|
|
|
|
|
|
|
let mut cid_str: &str = "";
|
|
|
|
let mut sock_str: &str = "";
|
2019-10-04 19:06:34 +00:00
|
|
|
let mut iommu_str: &str = "";
|
2019-09-04 17:39:17 +00:00
|
|
|
|
|
|
|
for param in params_list.iter() {
|
|
|
|
if param.starts_with("cid=") {
|
|
|
|
cid_str = ¶m[4..];
|
|
|
|
} else if param.starts_with("sock=") {
|
|
|
|
sock_str = ¶m[5..];
|
2019-10-04 19:06:34 +00:00
|
|
|
} else if param.starts_with("iommu=") {
|
|
|
|
iommu_str = ¶m[6..];
|
2019-09-04 17:39:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if sock_str.is_empty() {
|
|
|
|
return Err(Error::ParseVsockSockParam);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(VsockConfig {
|
|
|
|
cid: cid_str.parse::<u64>().map_err(Error::ParseVsockCidParam)?,
|
2019-09-23 17:46:35 +00:00
|
|
|
sock: PathBuf::from(sock_str),
|
2019-11-19 23:40:57 +00:00
|
|
|
iommu: parse_on_off(iommu_str)?,
|
2019-09-04 17:39:17 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-13 10:56:37 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
2019-09-23 17:46:35 +00:00
|
|
|
pub struct VmConfig {
|
2019-09-27 09:40:50 +00:00
|
|
|
#[serde(default)]
|
2019-05-23 19:48:05 +00:00
|
|
|
pub cpus: CpusConfig,
|
2019-09-27 09:40:50 +00:00
|
|
|
#[serde(default)]
|
2019-09-23 16:51:36 +00:00
|
|
|
pub memory: MemoryConfig,
|
2019-09-19 07:52:55 +00:00
|
|
|
pub kernel: Option<KernelConfig>,
|
2020-03-15 17:51:17 +00:00
|
|
|
pub initramfs: Option<InitramfsConfig>,
|
2019-12-13 11:10:13 +00:00
|
|
|
#[serde(default)]
|
2019-05-23 19:48:05 +00:00
|
|
|
pub cmdline: CmdlineConfig,
|
2019-09-23 17:12:19 +00:00
|
|
|
pub disks: Option<Vec<DiskConfig>>,
|
2019-09-23 17:14:35 +00:00
|
|
|
pub net: Option<Vec<NetConfig>>,
|
2019-09-27 09:40:50 +00:00
|
|
|
#[serde(default)]
|
2019-09-23 17:15:50 +00:00
|
|
|
pub rng: RngConfig,
|
2019-09-23 17:23:17 +00:00
|
|
|
pub fs: Option<Vec<FsConfig>>,
|
2019-09-23 17:27:56 +00:00
|
|
|
pub pmem: Option<Vec<PmemConfig>>,
|
2019-09-27 09:40:50 +00:00
|
|
|
#[serde(default = "ConsoleConfig::default_serial")]
|
2019-09-23 17:30:22 +00:00
|
|
|
pub serial: ConsoleConfig,
|
2019-09-27 09:40:50 +00:00
|
|
|
#[serde(default = "ConsoleConfig::default_console")]
|
2019-09-23 17:30:22 +00:00
|
|
|
pub console: ConsoleConfig,
|
2019-09-23 17:31:41 +00:00
|
|
|
pub devices: Option<Vec<DeviceConfig>>,
|
2019-09-23 17:46:35 +00:00
|
|
|
pub vsock: Option<Vec<VsockConfig>>,
|
2019-09-18 14:13:56 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub iommu: bool,
|
2019-05-23 19:48:05 +00:00
|
|
|
}
|
|
|
|
|
2019-09-23 17:46:35 +00:00
|
|
|
impl VmConfig {
|
2019-09-19 07:52:55 +00:00
|
|
|
pub fn valid(&self) -> bool {
|
|
|
|
self.kernel.is_some()
|
|
|
|
}
|
|
|
|
|
2019-09-23 17:46:35 +00:00
|
|
|
pub fn parse(vm_params: VmParams) -> Result<Self> {
|
2019-10-02 21:01:36 +00:00
|
|
|
let mut iommu = false;
|
2019-09-18 14:13:56 +00:00
|
|
|
|
2019-07-08 22:48:39 +00:00
|
|
|
let mut disks: Option<Vec<DiskConfig>> = None;
|
|
|
|
if let Some(disk_list) = &vm_params.disks {
|
|
|
|
let mut disk_config_list = Vec::new();
|
|
|
|
for item in disk_list.iter() {
|
2019-10-02 21:01:36 +00:00
|
|
|
let disk_config = DiskConfig::parse(item)?;
|
|
|
|
if disk_config.iommu {
|
|
|
|
iommu = true;
|
|
|
|
}
|
|
|
|
disk_config_list.push(disk_config);
|
2019-07-08 22:48:39 +00:00
|
|
|
}
|
|
|
|
disks = Some(disk_config_list);
|
2019-05-23 19:48:05 +00:00
|
|
|
}
|
|
|
|
|
2019-07-08 22:31:13 +00:00
|
|
|
let mut net: Option<Vec<NetConfig>> = None;
|
|
|
|
if let Some(net_list) = &vm_params.net {
|
|
|
|
let mut net_config_list = Vec::new();
|
|
|
|
for item in net_list.iter() {
|
2019-10-02 21:13:44 +00:00
|
|
|
let net_config = NetConfig::parse(item)?;
|
|
|
|
if net_config.iommu {
|
|
|
|
iommu = true;
|
|
|
|
}
|
|
|
|
net_config_list.push(net_config);
|
2019-07-08 22:31:13 +00:00
|
|
|
}
|
|
|
|
net = Some(net_config_list);
|
|
|
|
}
|
|
|
|
|
2019-10-04 18:18:49 +00:00
|
|
|
let rng = RngConfig::parse(vm_params.rng)?;
|
|
|
|
if rng.iommu {
|
|
|
|
iommu = true;
|
|
|
|
}
|
|
|
|
|
2019-06-27 16:14:11 +00:00
|
|
|
let mut fs: Option<Vec<FsConfig>> = None;
|
|
|
|
if let Some(fs_list) = &vm_params.fs {
|
|
|
|
let mut fs_config_list = Vec::new();
|
|
|
|
for item in fs_list.iter() {
|
|
|
|
fs_config_list.push(FsConfig::parse(item)?);
|
|
|
|
}
|
|
|
|
fs = Some(fs_config_list);
|
|
|
|
}
|
|
|
|
|
2019-06-28 08:45:58 +00:00
|
|
|
let mut pmem: Option<Vec<PmemConfig>> = None;
|
|
|
|
if let Some(pmem_list) = &vm_params.pmem {
|
|
|
|
let mut pmem_config_list = Vec::new();
|
|
|
|
for item in pmem_list.iter() {
|
2019-10-04 18:27:22 +00:00
|
|
|
let pmem_config = PmemConfig::parse(item)?;
|
|
|
|
if pmem_config.iommu {
|
|
|
|
iommu = true;
|
|
|
|
}
|
|
|
|
pmem_config_list.push(pmem_config);
|
2019-06-28 08:45:58 +00:00
|
|
|
}
|
|
|
|
pmem = Some(pmem_config_list);
|
|
|
|
}
|
|
|
|
|
2019-07-22 19:29:02 +00:00
|
|
|
let console = ConsoleConfig::parse(vm_params.console)?;
|
2019-10-04 19:01:32 +00:00
|
|
|
if console.iommu {
|
|
|
|
iommu = true;
|
|
|
|
}
|
2019-07-22 19:29:02 +00:00
|
|
|
let serial = ConsoleConfig::parse(vm_params.serial)?;
|
|
|
|
if console.mode == ConsoleOutputMode::Tty && serial.mode == ConsoleOutputMode::Tty {
|
|
|
|
return Err(Error::ParseTTYParam);
|
|
|
|
}
|
|
|
|
|
2019-07-15 09:42:40 +00:00
|
|
|
let mut devices: Option<Vec<DeviceConfig>> = None;
|
|
|
|
if let Some(device_list) = &vm_params.devices {
|
|
|
|
let mut device_config_list = Vec::new();
|
|
|
|
for item in device_list.iter() {
|
2019-10-07 16:03:58 +00:00
|
|
|
let device_config = DeviceConfig::parse(item)?;
|
|
|
|
if device_config.iommu {
|
|
|
|
iommu = true;
|
|
|
|
}
|
|
|
|
device_config_list.push(device_config);
|
2019-07-15 09:42:40 +00:00
|
|
|
}
|
|
|
|
devices = Some(device_config_list);
|
|
|
|
}
|
|
|
|
|
2019-09-04 17:39:17 +00:00
|
|
|
let mut vsock: Option<Vec<VsockConfig>> = None;
|
|
|
|
if let Some(vsock_list) = &vm_params.vsock {
|
|
|
|
let mut vsock_config_list = Vec::new();
|
|
|
|
for item in vsock_list.iter() {
|
2019-10-04 19:06:34 +00:00
|
|
|
let vsock_config = VsockConfig::parse(item)?;
|
|
|
|
if vsock_config.iommu {
|
|
|
|
iommu = true;
|
|
|
|
}
|
|
|
|
vsock_config_list.push(vsock_config);
|
2019-09-04 17:39:17 +00:00
|
|
|
}
|
|
|
|
vsock = Some(vsock_config_list);
|
|
|
|
}
|
|
|
|
|
2019-09-19 07:52:55 +00:00
|
|
|
let mut kernel: Option<KernelConfig> = None;
|
|
|
|
if let Some(k) = vm_params.kernel {
|
|
|
|
kernel = Some(KernelConfig {
|
|
|
|
path: PathBuf::from(k),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-03-15 17:51:17 +00:00
|
|
|
let mut initramfs: Option<InitramfsConfig> = None;
|
|
|
|
if let Some(k) = vm_params.initramfs {
|
|
|
|
initramfs = Some(InitramfsConfig {
|
|
|
|
path: PathBuf::from(k),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-05-23 19:48:05 +00:00
|
|
|
Ok(VmConfig {
|
|
|
|
cpus: CpusConfig::parse(vm_params.cpus)?,
|
|
|
|
memory: MemoryConfig::parse(vm_params.memory)?,
|
2019-09-19 07:52:55 +00:00
|
|
|
kernel,
|
2020-03-15 17:51:17 +00:00
|
|
|
initramfs,
|
2019-05-23 19:48:05 +00:00
|
|
|
cmdline: CmdlineConfig::parse(vm_params.cmdline)?,
|
|
|
|
disks,
|
2019-07-08 22:31:13 +00:00
|
|
|
net,
|
2019-10-04 18:18:49 +00:00
|
|
|
rng,
|
2019-06-27 16:14:11 +00:00
|
|
|
fs,
|
2019-06-28 08:45:58 +00:00
|
|
|
pmem,
|
2019-07-22 19:29:02 +00:00
|
|
|
serial,
|
|
|
|
console,
|
2019-07-15 09:42:40 +00:00
|
|
|
devices,
|
2019-09-04 17:39:17 +00:00
|
|
|
vsock,
|
2019-09-18 14:13:56 +00:00
|
|
|
iommu,
|
2019-05-23 19:48:05 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2020-03-27 11:15:55 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_option_parser() -> std::result::Result<(), OptionParserError> {
|
|
|
|
let mut parser = OptionParser::new();
|
|
|
|
parser
|
|
|
|
.add("size")
|
|
|
|
.add("file")
|
|
|
|
.add("mergeable")
|
|
|
|
.add("hotplug_method")
|
|
|
|
.add("hotplug_size");
|
|
|
|
|
|
|
|
assert!(parser
|
|
|
|
.parse("size=128M,file=/dev/shm,hanging_param")
|
|
|
|
.is_err());
|
|
|
|
assert!(parser
|
|
|
|
.parse("size=128M,file=/dev/shm,too_many_equals=foo=bar")
|
|
|
|
.is_err());
|
|
|
|
assert!(parser.parse("size=128M,file=/dev/shm").is_ok());
|
|
|
|
|
|
|
|
assert_eq!(parser.get("size"), Some("128M".to_owned()));
|
|
|
|
assert_eq!(parser.get("file"), Some("/dev/shm".to_owned()));
|
|
|
|
assert!(!parser.is_set("mergeable"));
|
|
|
|
assert!(parser.is_set("size"));
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-03-27 12:30:13 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cpu_parsing() -> Result<()> {
|
|
|
|
assert_eq!(CpusConfig::parse("")?, CpusConfig::default());
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
CpusConfig::parse("boot=1")?,
|
|
|
|
CpusConfig {
|
|
|
|
boot_vcpus: 1,
|
|
|
|
max_vcpus: 1
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
CpusConfig::parse("boot=1,max=2")?,
|
|
|
|
CpusConfig {
|
|
|
|
boot_vcpus: 1,
|
|
|
|
max_vcpus: 2,
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert!(CpusConfig::parse("boot=2,max=1").is_err());
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-03-27 12:54:48 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_mem_parsing() -> Result<()> {
|
|
|
|
assert_eq!(MemoryConfig::parse("")?, MemoryConfig::default());
|
|
|
|
// Default string
|
|
|
|
assert_eq!(MemoryConfig::parse("size=512M")?, MemoryConfig::default());
|
|
|
|
assert_eq!(
|
|
|
|
MemoryConfig::parse("size=512M,file=/some/file")?,
|
|
|
|
MemoryConfig {
|
|
|
|
size: 512 << 20,
|
|
|
|
file: Some(PathBuf::from("/some/file")),
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
MemoryConfig::parse("size=512M,mergeable=on")?,
|
|
|
|
MemoryConfig {
|
|
|
|
size: 512 << 20,
|
|
|
|
mergeable: true,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
MemoryConfig::parse("mergeable=on")?,
|
|
|
|
MemoryConfig {
|
|
|
|
mergeable: true,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
MemoryConfig::parse("size=1G,mergeable=off")?,
|
|
|
|
MemoryConfig {
|
|
|
|
size: 1 << 30,
|
|
|
|
mergeable: false,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
MemoryConfig::parse("hotplug_method=acpi")?,
|
|
|
|
MemoryConfig {
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
MemoryConfig::parse("hotplug_method=acpi,hotplug_size=512M")?,
|
|
|
|
MemoryConfig {
|
|
|
|
hotplug_size: Some(512 << 20),
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
MemoryConfig::parse("hotplug_method=virtio-mem,hotplug_size=512M")?,
|
|
|
|
MemoryConfig {
|
|
|
|
hotplug_size: Some(512 << 20),
|
|
|
|
hotplug_method: HotplugMethod::VirtioMem,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-03-31 10:46:11 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_disk_parsing() -> Result<()> {
|
|
|
|
assert_eq!(
|
|
|
|
DiskConfig::parse("path=/path/to_file")?,
|
|
|
|
DiskConfig {
|
|
|
|
path: Some(PathBuf::from("/path/to_file")),
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
DiskConfig::parse("path=/path/to_file,vhost_user=true")?,
|
|
|
|
DiskConfig {
|
|
|
|
path: Some(PathBuf::from("/path/to_file")),
|
|
|
|
vhost_user: true,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
DiskConfig::parse("path=/path/to_file,iommu=on")?,
|
|
|
|
DiskConfig {
|
|
|
|
path: Some(PathBuf::from("/path/to_file")),
|
|
|
|
iommu: true,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
DiskConfig::parse("path=/path/to_file,iommu=on,queue_size=256")?,
|
|
|
|
DiskConfig {
|
|
|
|
path: Some(PathBuf::from("/path/to_file")),
|
|
|
|
iommu: true,
|
|
|
|
queue_size: 256,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
DiskConfig::parse("path=/path/to_file,iommu=on,queue_size=256,num_queues=4")?,
|
|
|
|
DiskConfig {
|
|
|
|
path: Some(PathBuf::from("/path/to_file")),
|
|
|
|
iommu: true,
|
|
|
|
queue_size: 256,
|
|
|
|
num_queues: 4,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
DiskConfig::parse("path=/path/to_file,direct=on")?,
|
|
|
|
DiskConfig {
|
|
|
|
path: Some(PathBuf::from("/path/to_file")),
|
|
|
|
direct: true,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
DiskConfig::parse("path=/path/to_file,wce=true")?,
|
|
|
|
DiskConfig {
|
|
|
|
path: Some(PathBuf::from("/path/to_file")),
|
|
|
|
wce: true,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
DiskConfig::parse("path=/path/to_file,wce=false")?,
|
|
|
|
DiskConfig {
|
|
|
|
path: Some(PathBuf::from("/path/to_file")),
|
|
|
|
wce: false,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
DiskConfig::parse("path=/path/to_file,poll_queue=false")?,
|
|
|
|
DiskConfig {
|
|
|
|
path: Some(PathBuf::from("/path/to_file")),
|
|
|
|
poll_queue: false,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
DiskConfig::parse("path=/path/to_file,poll_queue=true")?,
|
|
|
|
DiskConfig {
|
|
|
|
path: Some(PathBuf::from("/path/to_file")),
|
|
|
|
poll_queue: true,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert!(DiskConfig::parse("path=/path/to_file,socket=/path/to_socket").is_err());
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-03-31 15:27:13 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_net_parsing() -> Result<()> {
|
|
|
|
// mac address is random
|
|
|
|
assert_eq!(
|
|
|
|
NetConfig::parse("mac=de:ad:be:ef:12:34")?,
|
|
|
|
NetConfig {
|
|
|
|
mac: MacAddr::parse_str("de:ad:be:ef:12:34").unwrap(),
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
NetConfig::parse(
|
|
|
|
"mac=de:ad:be:ef:12:34,tap=tap0,ip=192.168.100.1,mask=255.255.255.128"
|
|
|
|
)?,
|
|
|
|
NetConfig {
|
|
|
|
mac: MacAddr::parse_str("de:ad:be:ef:12:34").unwrap(),
|
|
|
|
tap: Some("tap0".to_owned()),
|
|
|
|
ip: "192.168.100.1".parse().unwrap(),
|
|
|
|
mask: "255.255.255.128".parse().unwrap(),
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
NetConfig::parse("mac=de:ad:be:ef:12:34,vhost_user=true,socket=/tmp/socket")?,
|
|
|
|
NetConfig {
|
|
|
|
mac: MacAddr::parse_str("de:ad:be:ef:12:34").unwrap(),
|
|
|
|
vhost_user: true,
|
|
|
|
vhost_socket: Some("/tmp/socket".to_owned()),
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
NetConfig::parse("mac=de:ad:be:ef:12:34,num_queues=4,queue_size=1024,iommu=on")?,
|
|
|
|
NetConfig {
|
|
|
|
mac: MacAddr::parse_str("de:ad:be:ef:12:34").unwrap(),
|
|
|
|
num_queues: 4,
|
|
|
|
queue_size: 1024,
|
|
|
|
iommu: true,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-04-02 09:08:02 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_parse_rng() -> Result<()> {
|
|
|
|
assert_eq!(RngConfig::parse("")?, RngConfig::default());
|
|
|
|
assert_eq!(
|
|
|
|
RngConfig::parse("src=/dev/random")?,
|
|
|
|
RngConfig {
|
|
|
|
src: PathBuf::from("/dev/random"),
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
RngConfig::parse("src=/dev/random,iommu=on")?,
|
|
|
|
RngConfig {
|
|
|
|
src: PathBuf::from("/dev/random"),
|
|
|
|
iommu: true,
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
RngConfig::parse("iommu=on")?,
|
|
|
|
RngConfig {
|
|
|
|
iommu: true,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-04-02 09:41:35 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_parse_fs() -> Result<()> {
|
|
|
|
// "tag" and "socket" must be supplied
|
|
|
|
assert!(FsConfig::parse("").is_err());
|
|
|
|
assert!(FsConfig::parse("tag=mytag").is_err());
|
|
|
|
assert!(FsConfig::parse("sock=/tmp/sock").is_err());
|
|
|
|
assert_eq!(
|
|
|
|
FsConfig::parse("tag=mytag,sock=/tmp/sock")?,
|
|
|
|
FsConfig {
|
|
|
|
sock: PathBuf::from("/tmp/sock"),
|
|
|
|
tag: "mytag".to_owned(),
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
FsConfig::parse("tag=mytag,sock=/tmp/sock")?,
|
|
|
|
FsConfig {
|
|
|
|
sock: PathBuf::from("/tmp/sock"),
|
|
|
|
tag: "mytag".to_owned(),
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
FsConfig::parse("tag=mytag,sock=/tmp/sock,num_queues=4,queue_size=1024")?,
|
|
|
|
FsConfig {
|
|
|
|
sock: PathBuf::from("/tmp/sock"),
|
|
|
|
tag: "mytag".to_owned(),
|
|
|
|
num_queues: 4,
|
|
|
|
queue_size: 1024,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
// DAX on -> default cache size
|
|
|
|
assert_eq!(
|
|
|
|
FsConfig::parse("tag=mytag,sock=/tmp/sock,dax=on")?,
|
|
|
|
FsConfig {
|
|
|
|
sock: PathBuf::from("/tmp/sock"),
|
|
|
|
tag: "mytag".to_owned(),
|
|
|
|
dax: true,
|
|
|
|
cache_size: default_fsconfig_cache_size(),
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
FsConfig::parse("tag=mytag,sock=/tmp/sock,dax=on,cache_size=4G")?,
|
|
|
|
FsConfig {
|
|
|
|
sock: PathBuf::from("/tmp/sock"),
|
|
|
|
tag: "mytag".to_owned(),
|
|
|
|
dax: true,
|
|
|
|
cache_size: 4 << 30,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
// Cache size without DAX is an error
|
|
|
|
assert!(FsConfig::parse("tag=mytag,sock=/tmp/sock,dax=off,cache_size=4G").is_err());
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-04-02 14:52:33 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_pmem_parsing() -> Result<()> {
|
|
|
|
// Must always give a file and size
|
|
|
|
assert!(PmemConfig::parse("").is_err());
|
|
|
|
assert!(PmemConfig::parse("file=/tmp/pmem").is_err());
|
|
|
|
assert!(PmemConfig::parse("size=128M").is_err());
|
|
|
|
assert_eq!(
|
|
|
|
PmemConfig::parse("file=/tmp/pmem,size=128M")?,
|
|
|
|
PmemConfig {
|
|
|
|
file: PathBuf::from("/tmp/pmem"),
|
|
|
|
size: 128 << 20,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
PmemConfig::parse("file=/tmp/pmem,size=128M,iommu=on,mergeable=on,discard_writes=on")?,
|
|
|
|
PmemConfig {
|
|
|
|
file: PathBuf::from("/tmp/pmem"),
|
|
|
|
size: 128 << 20,
|
|
|
|
mergeable: true,
|
|
|
|
discard_writes: true,
|
|
|
|
iommu: true,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-04-02 15:52:58 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_console_parsing() -> Result<()> {
|
|
|
|
assert!(ConsoleConfig::parse("").is_err());
|
|
|
|
assert!(ConsoleConfig::parse("badmode").is_err());
|
|
|
|
assert_eq!(
|
|
|
|
ConsoleConfig::parse("off")?,
|
|
|
|
ConsoleConfig {
|
|
|
|
mode: ConsoleOutputMode::Off,
|
|
|
|
iommu: false,
|
|
|
|
file: None,
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
ConsoleConfig::parse("tty")?,
|
|
|
|
ConsoleConfig {
|
|
|
|
mode: ConsoleOutputMode::Tty,
|
|
|
|
iommu: false,
|
|
|
|
file: None,
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
ConsoleConfig::parse("null")?,
|
|
|
|
ConsoleConfig {
|
|
|
|
mode: ConsoleOutputMode::Null,
|
|
|
|
iommu: false,
|
|
|
|
file: None,
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
ConsoleConfig::parse("file=/tmp/console")?,
|
|
|
|
ConsoleConfig {
|
|
|
|
mode: ConsoleOutputMode::File,
|
|
|
|
iommu: false,
|
|
|
|
file: Some(PathBuf::from("/tmp/console"))
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
ConsoleConfig::parse("null,iommu=on")?,
|
|
|
|
ConsoleConfig {
|
|
|
|
mode: ConsoleOutputMode::Null,
|
|
|
|
iommu: true,
|
|
|
|
file: None,
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
ConsoleConfig::parse("file=/tmp/console,iommu=on")?,
|
|
|
|
ConsoleConfig {
|
|
|
|
mode: ConsoleOutputMode::File,
|
|
|
|
iommu: true,
|
|
|
|
file: Some(PathBuf::from("/tmp/console"))
|
|
|
|
}
|
|
|
|
);
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-04-02 17:16:18 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_device_parsing() -> Result<()> {
|
|
|
|
assert_eq!(
|
|
|
|
DeviceConfig::parse("path=/path/to/device")?,
|
|
|
|
DeviceConfig {
|
|
|
|
path: PathBuf::from("/path/to/device"),
|
|
|
|
id: None,
|
|
|
|
iommu: false
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
DeviceConfig::parse("path=/path/to/device,iommu=on")?,
|
|
|
|
DeviceConfig {
|
|
|
|
path: PathBuf::from("/path/to/device"),
|
|
|
|
id: None,
|
|
|
|
iommu: true
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
DeviceConfig::parse("path=/path/to/device,iommu=on,id=mydevice0")?,
|
|
|
|
DeviceConfig {
|
|
|
|
path: PathBuf::from("/path/to/device"),
|
|
|
|
id: Some("mydevice0".to_owned()),
|
|
|
|
iommu: true
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-03-27 11:15:55 +00:00
|
|
|
}
|