main: switch to argh

A few breaking changes:

1. `-vvv` needs to be written as `-v -v -v`.
2. `--disk D1 D2` and others need to be written as `--disk D1 --disk D2`.
3. `--option=value` needs to be written as `--option value`

Change integration tests to adapt to the breaking changes.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
This commit is contained in:
Wei Liu 2023-01-12 18:50:20 +00:00 committed by Rob Bradford
parent fe49056129
commit 111225a2a5
9 changed files with 400 additions and 649 deletions

2
Cargo.lock generated
View File

@ -178,7 +178,6 @@ dependencies = [
"anyhow", "anyhow",
"api_client", "api_client",
"argh", "argh",
"clap",
"dirs", "dirs",
"epoll", "epoll",
"event_monitor", "event_monitor",
@ -1575,7 +1574,6 @@ dependencies = [
"arch", "arch",
"bitflags", "bitflags",
"block_util", "block_util",
"clap",
"devices", "devices",
"epoll", "epoll",
"event_monitor", "event_monitor",

View File

@ -27,7 +27,6 @@ strip = true
anyhow = "1.0.68" anyhow = "1.0.68"
api_client = { path = "api_client" } api_client = { path = "api_client" }
argh = "0.1.9" argh = "0.1.9"
clap = { version = "4.0.32", features = ["wrap_help","cargo","string"] }
epoll = "4.3.1" epoll = "4.3.1"
event_monitor = { path = "event_monitor" } event_monitor = { path = "event_monitor" }
hypervisor = { path = "hypervisor" } hypervisor = { path = "hypervisor" }

218
fuzz/Cargo.lock generated
View File

@ -56,6 +56,34 @@ dependencies = [
"vmm-sys-util", "vmm-sys-util",
] ]
[[package]]
name = "argh"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab257697eb9496bf75526f0217b5ed64636a9cfafa78b8365c71bd283fcef93e"
dependencies = [
"argh_derive",
"argh_shared",
]
[[package]]
name = "argh_derive"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b382dbd3288e053331f03399e1db106c9fb0d8562ad62cb04859ae926f324fa6"
dependencies = [
"argh_shared",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "argh_shared"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64cb94155d965e3d37ffbbe7cc5b82c3dd79dd33bd48e536f73d2cfb8d85506f"
[[package]] [[package]]
name = "bincode" name = "bincode"
version = "1.3.3" version = "1.3.3"
@ -112,37 +140,13 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa91278560fc226a5d9d736cc21e485ff9aad47d26b8ffe1f54cba868b684b9f"
dependencies = [
"bitflags",
"clap_lex",
"is-terminal",
"once_cell",
"strsim",
"termcolor",
"terminal_size",
]
[[package]]
name = "clap_lex"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8"
dependencies = [
"os_str_bytes",
]
[[package]] [[package]]
name = "cloud-hypervisor" name = "cloud-hypervisor"
version = "29.0.0" version = "29.0.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"api_client", "api_client",
"clap", "argh",
"epoll", "epoll",
"event_monitor", "event_monitor",
"hypervisor", "hypervisor",
@ -269,27 +273,6 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "errno"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
dependencies = [
"errno-dragonfly",
"libc",
"winapi",
]
[[package]]
name = "errno-dragonfly"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
dependencies = [
"cc",
"libc",
]
[[package]] [[package]]
name = "event_monitor" name = "event_monitor"
version = "0.1.0" version = "0.1.0"
@ -322,15 +305,6 @@ dependencies = [
"wasi", "wasi",
] ]
[[package]]
name = "hermit-abi"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "hypervisor" name = "hypervisor"
version = "0.1.0" version = "0.1.0"
@ -365,16 +339,6 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "io-lifetimes"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c"
dependencies = [
"libc",
"windows-sys",
]
[[package]] [[package]]
name = "io-uring" name = "io-uring"
version = "0.5.11" version = "0.5.11"
@ -385,18 +349,6 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "is-terminal"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189"
dependencies = [
"hermit-abi",
"io-lifetimes",
"rustix",
"windows-sys",
]
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.5" version = "1.0.5"
@ -465,12 +417,6 @@ dependencies = [
"vm-memory", "vm-memory",
] ]
[[package]]
name = "linux-raw-sys"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.17" version = "0.4.17"
@ -527,12 +473,6 @@ checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
name = "option_parser" name = "option_parser"
version = "0.1.0" version = "0.1.0"
[[package]]
name = "os_str_bytes"
version = "6.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
[[package]] [[package]]
name = "pci" name = "pci"
version = "0.1.0" version = "0.1.0"
@ -671,20 +611,6 @@ dependencies = [
"semver", "semver",
] ]
[[package]]
name = "rustix"
version = "0.36.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3807b5d10909833d3e9acd1eb5fb988f79376ff10fce42937de71a449c4c588"
dependencies = [
"bitflags",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.12" version = "1.0.12"
@ -811,25 +737,6 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "termcolor"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
dependencies = [
"winapi-util",
]
[[package]]
name = "terminal_size"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb20089a8ba2b69debd491f8d2d023761cbf196e999218c591fa1e7e15a21907"
dependencies = [
"rustix",
"windows-sys",
]
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.38" version = "1.0.38"
@ -1102,7 +1009,6 @@ dependencies = [
"arch", "arch",
"bitflags", "bitflags",
"block_util", "block_util",
"clap",
"devices", "devices",
"epoll", "epoll",
"event_monitor", "event_monitor",
@ -1173,74 +1079,8 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]] [[package]]
name = "winapi-x86_64-pc-windows-gnu" name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
[[package]]
name = "windows_i686_gnu"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
[[package]]
name = "windows_i686_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"

View File

@ -359,11 +359,13 @@ pub fn performance_block_io(control: &PerformanceTestControl) -> f64 {
guest.disk_config.disk(DiskType::OperatingSystem).unwrap() guest.disk_config.disk(DiskType::OperatingSystem).unwrap()
) )
.as_str(), .as_str(),
"--disk",
format!( format!(
"path={}", "path={}",
guest.disk_config.disk(DiskType::CloudInit).unwrap() guest.disk_config.disk(DiskType::CloudInit).unwrap()
) )
.as_str(), .as_str(),
"--disk",
format!("path={BLK_IO_TEST_IMG}").as_str(), format!("path={BLK_IO_TEST_IMG}").as_str(),
]) ])
.default_net() .default_net()

View File

@ -6,7 +6,7 @@
#[macro_use] #[macro_use]
extern crate event_monitor; extern crate event_monitor;
use clap::{Arg, ArgAction, ArgGroup, ArgMatches, Command}; use argh::FromArgs;
use libc::EFD_NONBLOCK; use libc::EFD_NONBLOCK;
use log::LevelFilter; use log::LevelFilter;
use option_parser::OptionParser; use option_parser::OptionParser;
@ -113,10 +113,6 @@ impl log::Log for Logger {
fn flush(&self) {} fn flush(&self) {}
} }
fn prepare_default_values() -> (String, String, String) {
(default_vcpus(), default_memory(), default_rng())
}
fn default_vcpus() -> String { fn default_vcpus() -> String {
format!( format!(
"boot={},max_phys_bits={}", "boot={},max_phys_bits={}",
@ -133,287 +129,257 @@ fn default_rng() -> String {
format!("src={}", config::DEFAULT_RNG_SOURCE) format!("src={}", config::DEFAULT_RNG_SOURCE)
} }
fn create_app(default_vcpus: String, default_memory: String, default_rng: String) -> Command { #[derive(FromArgs)]
#[allow(clippy::let_and_return)] /// Launch a cloud-hypervisor VMM.
let app = Command::new("cloud-hypervisor") pub struct TopLevel {
// 'BUILT_VERSION' is set by the build script 'build.rs' at #[argh(option, long = "cpus", default = "default_vcpus()")]
// compile time /// boot=<boot_vcpus>,max=<max_vcpus>,topology=<threads_per_core>:<cores_per_die>:<dies_per_package>:<packages>,kvm_hyperv=on|off,max_phys_bits=<maximum_number_of_physical_bits>,affinity=<list_of_vcpus_with_their_associated_cpuset>,features=<list_of_features_to_enable>
.version(env!("BUILT_VERSION")) cpus: String,
.author(env!("CARGO_PKG_AUTHORS"))
.about("Launch a cloud-hypervisor VMM.")
.group(ArgGroup::new("vm-config").multiple(true))
.group(ArgGroup::new("vmm-config").multiple(true))
.group(ArgGroup::new("logging").multiple(true))
.arg(
Arg::new("cpus")
.long("cpus")
.help(
"boot=<boot_vcpus>,max=<max_vcpus>,\
topology=<threads_per_core>:<cores_per_die>:<dies_per_package>:<packages>,\
kvm_hyperv=on|off,max_phys_bits=<maximum_number_of_physical_bits>,\
affinity=<list_of_vcpus_with_their_associated_cpuset>,\
features=<list_of_features_to_enable>",
)
.default_value(default_vcpus)
.group("vm-config"),
)
.arg(
Arg::new("platform")
.long("platform")
.help(
"num_pci_segments=<num_pci_segments>,iommu_segments=<list_of_segments>,serial_number=<dmi_device_serial_number>,uuid=<dmi_device_uuid>,oem_strings=<list_of_strings>",
)
.num_args(1)
.group("vm-config"),
)
.arg(
Arg::new("memory")
.long("memory")
.help(
"Memory parameters \
\"size=<guest_memory_size>,mergeable=on|off,shared=on|off,\
hugepages=on|off,hugepage_size=<hugepage_size>,\
hotplug_method=acpi|virtio-mem,\
hotplug_size=<hotpluggable_memory_size>,\
hotplugged_size=<hotplugged_memory_size>,\
prefault=on|off,thp=on|off\"",
)
.default_value(default_memory)
.group("vm-config"),
)
.arg(
Arg::new("memory-zone")
.long("memory-zone")
.help(
"User defined memory zone parameters \
\"size=<guest_memory_region_size>,file=<backing_file>,\
shared=on|off,\
hugepages=on|off,hugepage_size=<hugepage_size>,\
host_numa_node=<node_id>,\
id=<zone_identifier>,hotplug_size=<hotpluggable_memory_size>,\
hotplugged_size=<hotplugged_memory_size>,\
prefault=on|off\"",
)
.num_args(1..)
.group("vm-config"),
)
.arg(
Arg::new("firmware")
.long("firmware")
.help("Path to firmware that is loaded in an architectural specific way")
.num_args(1)
.group("vm-config"),
)
.arg(
Arg::new("kernel")
.long("kernel")
.help(
"Path to kernel to load. This may be a kernel or firmware that supports a PVH \
entry point (e.g. vmlinux) or architecture equivalent",
)
.num_args(1)
.group("vm-config"),
)
.arg(
Arg::new("initramfs")
.long("initramfs")
.help("Path to initramfs image")
.num_args(1)
.group("vm-config"),
)
.arg(
Arg::new("cmdline")
.long("cmdline")
.help("Kernel command line")
.num_args(1)
.group("vm-config"),
)
.arg(
Arg::new("disk")
.long("disk")
.help(config::DiskConfig::SYNTAX)
.num_args(1..)
.group("vm-config"),
)
.arg(
Arg::new("net")
.long("net")
.help(config::NetConfig::SYNTAX)
.num_args(1..)
.group("vm-config"),
)
.arg(
Arg::new("rng")
.long("rng")
.help(
"Random number generator parameters \"src=<entropy_source_path>,iommu=on|off\"",
)
.default_value(default_rng)
.group("vm-config"),
)
.arg(
Arg::new("balloon")
.long("balloon")
.help(config::BalloonConfig::SYNTAX)
.num_args(1)
.group("vm-config"),
)
.arg(
Arg::new("fs")
.long("fs")
.help(config::FsConfig::SYNTAX)
.num_args(1..)
.group("vm-config"),
)
.arg(
Arg::new("pmem")
.long("pmem")
.help(config::PmemConfig::SYNTAX)
.num_args(1..)
.group("vm-config"),
)
.arg(
Arg::new("serial")
.long("serial")
.help("Control serial port: off|null|pty|tty|file=/path/to/a/file")
.default_value("null")
.group("vm-config"),
)
.arg(
Arg::new("console")
.long("console")
.help(
"Control (virtio) console: \"off|null|pty|tty|file=/path/to/a/file,iommu=on|off\"",
)
.default_value("tty")
.group("vm-config"),
)
.arg(
Arg::new("device")
.long("device")
.help(config::DeviceConfig::SYNTAX)
.num_args(1..)
.group("vm-config"),
)
.arg(
Arg::new("user-device")
.long("user-device")
.help(config::UserDeviceConfig::SYNTAX)
.num_args(1..)
.group("vm-config"),
)
.arg(
Arg::new("vdpa")
.long("vdpa")
.help(config::VdpaConfig::SYNTAX)
.num_args(1..)
.group("vm-config"),
)
.arg(
Arg::new("vsock")
.long("vsock")
.help(config::VsockConfig::SYNTAX)
.num_args(1)
.group("vm-config"),
)
.arg(
Arg::new("numa")
.long("numa")
.help(config::NumaConfig::SYNTAX)
.num_args(1..)
.group("vm-config"),
)
.arg(
Arg::new("watchdog")
.long("watchdog")
.help("Enable virtio-watchdog")
.num_args(0)
.action(ArgAction::SetTrue)
.group("vm-config"),
)
.arg(
Arg::new("v")
.short('v')
.action(ArgAction::Count)
.help("Sets the level of debugging output")
.group("logging"),
)
.arg(
Arg::new("log-file")
.long("log-file")
.help("Log file. Standard error is used if not specified")
.num_args(1)
.group("logging"),
)
.arg(
Arg::new("api-socket")
.long("api-socket")
.help("HTTP API socket (UNIX domain socket): path=</path/to/a/file> or fd=<fd>.")
.num_args(1)
.group("vmm-config"),
)
.arg(
Arg::new("event-monitor")
.long("event-monitor")
.help("File to report events on: path=</path/to/a/file> or fd=<fd>")
.num_args(1)
.group("vmm-config"),
)
.arg(
Arg::new("restore")
.long("restore")
.help(config::RestoreConfig::SYNTAX)
.num_args(1)
.group("vmm-config"),
)
.arg(
Arg::new("seccomp")
.long("seccomp")
.num_args(1)
.value_parser(["true", "false", "log"])
.default_value("true"),
)
.arg(
Arg::new("tpm")
.long("tpm")
.num_args(1)
.help(config::TpmConfig::SYNTAX)
.group("vmm-config"),
); #[argh(option, long = "platform")]
/// num_pci_segments=<num_pci_segments>,iommu_segments=<list_of_segments>,serial_number=<dmi_device_serial_number>,uuid=<dmi_device_uuid>,oem_strings=<list_of_strings>
platform: Option<String>,
#[argh(option, long = "memory", default = "default_memory()")]
/// size=<guest_memory_size>,mergeable=on|off,shared=on|off,hugepages=on|off,hugepage_size=<hugepage_size>,hotplug_method=acpi|virtio-mem,hotplug_size=<hotpluggable_memory_size>,hotplugged_size=<hotplugged_memory_size>,prefault=on|off,thp=on|off
memory: String,
#[argh(option, long = "memory-zone")]
/// size=<guest_memory_region_size>,file=<backing_file>,shared=on|off,hugepages=on|off,hugepage_size=<hugepage_size>,host_numa_node=<node_id>,id=<zone_identifier>,hotplug_size=<hotpluggable_memory_size>,hotplugged_size=<hotplugged_memory_size>,prefault=on|off
memory_zone: Vec<String>,
#[argh(option, long = "firmware")]
/// path to firmware that is loaded in an architectural specific way
firmware: Option<String>,
#[argh(option, long = "kernel")]
/// path to kernel or firmware that supports a PVH entry point or architecture equivalent
kernel: Option<String>,
#[argh(option, long = "initramfs")]
/// path to initramfs image
initramfs: Option<String>,
#[argh(option, long = "cmdline")]
/// kernel command line
cmdline: Option<String>,
#[argh(option, long = "disk")]
/// path=<disk_image_path>,readonly=on|off,direct=on|off,iommu=on|off,num_queues=<number_of_queues>,queue_size=<size_of_each_queue>,vhost_user=on|off,socket=<vhost_user_socket_path>,bw_size=<bytes>,bw_one_time_burst=<bytes>,bw_refill_time=<ms>,ops_size=<io_ops>,ops_one_time_burst=<io_ops>,ops_refill_time=<ms>,id=<device_id>,pci_segment=<segment_id>
disk: Vec<String>,
#[argh(option, long = "net")]
/// tap=<if_name>,ip=<ip_addr>,mask=<net_mask>,mac=<mac_addr>,fd=<fd1,fd2...>,iommu=on|off,num_queues=<number_of_queues>,queue_size=<size_of_each_queue>,id=<device_id>,vhost_user=<vhost_user_enable>,socket=<vhost_user_socket_path>,vhost_mode=client|server,bw_size=<bytes>,bw_one_time_burst=<bytes>,bw_refill_time=<ms>,ops_size=<io_ops>,ops_one_time_burst=<io_ops>,ops_refill_time=<ms>,pci_segment=<segment_id>offload_tso=on|off,offload_ufo=on|off,offload_csum=on|off
net: Vec<String>,
#[argh(option, long = "rng", default = "default_rng()")]
/// src=<entropy_source_path>,iommu=on|off
rng: String,
#[argh(option, long = "balloon")]
/// size=<balloon_size>,deflate_on_oom=on|off,free_page_reporting=on|off
balloon: Option<String>,
#[argh(option, long = "fs")]
/// tag=<tag_name>,socket=<socket_path>,num_queues=<number_of_queues>,queue_size=<size_of_each_queue>,id=<device_id>,pci_segment=<segment_id>
fs: Vec<String>,
#[argh(option, long = "pmem")]
/// file=<backing_file_path>,size=<persistent_memory_size>,iommu=on|off,discard_writes=on|off,id=<device_id>,pci_segment=<segment_id>
pmem: Vec<String>,
#[argh(option, long = "serial", default = "String::from(\"null\")")]
/// off|null|pty|tty|file=/path/to/a/file
serial: String,
#[argh(option, long = "console", default = "String::from(\"tty\")")]
/// off|null|pty|tty|file=/path/to/a/file,iommu=on|off
console: String,
#[argh(option, long = "device")]
/// path=<device_path>,iommu=on|off,id=<device_id>,pci_segment=<segment_id>
device: Vec<String>,
#[argh(option, long = "user-device")]
/// socket=<socket_path>,id=<device_id>,pci_segment=<segment_id>
user_device: Vec<String>,
#[argh(option, long = "vdpa")]
/// path=<device_path>,num_queues=<number_of_queues>,iommu=on|off,id=<device_id>,pci_segment=<segment_id>
vdpa: Vec<String>,
#[argh(option, long = "vsock")]
/// cid=<context_id>,socket=<socket_path>,iommu=on|off,id=<device_id>,pci_segment=<segment_id>
vsock: Option<String>,
#[argh(option, long = "numa")]
/// guest_numa_id=<node_id>,cpus=<cpus_id>,distances=<list_of_distances_to_destination_nodes>,memory_zones=<list_of_memory_zones>,sgx_epc_sections=<list_of_sgx_epc_sections>
numa: Vec<String>,
#[argh(switch, long = "watchdog")]
/// enable virtio-watchdog
watchdog: bool,
#[argh(switch, short = 'v')]
/// set the level of debugging output
verbosity: u8,
#[argh(option, long = "log-file")]
/// path to log file
log_file: Option<String>,
#[argh(option, long = "api-socket")]
/// path=<path/to/a/file>|fd=<fd>
api_socket: Option<String>,
#[argh(option, long = "event-monitor")]
/// path=<path/to/a/file>|fd=<fd>
event_monitor: Option<String>,
#[argh(option, long = "restore")]
/// source_url=<source_url>,prefault=on|off
restore: Option<String>,
#[argh(option, long = "seccomp", default = "String::from(\"true\")")]
/// seccomp configuration (true, false or log)
seccomp: String,
#[argh(option, long = "tpm")]
/// socket=<path/to/a/socket>
tpm: Option<String>,
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
let app = app.arg( #[argh(option, long = "sgx-epc")]
Arg::new("sgx-epc") /// id=<epc_section_identifier>,size=<epc_section_size>,prefault=on|off
.long("sgx-epc") sgx_epc: Vec<String>,
.help(config::SgxEpcConfig::SYNTAX)
.num_args(1..)
.group("vm-config"),
);
#[cfg(feature = "guest_debug")] #[cfg(feature = "guest_debug")]
let app = app.arg( #[argh(option, long = "gdb")]
Arg::new("gdb") /// path=<path/to/a/file>
.long("gdb") gdb: Option<String>,
.help("GDB socket (UNIX domain socket): path=</path/to/a/file>")
.num_args(1)
.group("vmm-config"),
);
app #[argh(switch, short = 'V', long = "version")]
/// print version information
version: bool,
} }
fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> { impl TopLevel {
let log_level = match cmd_arguments.get_count("v") { fn to_vm_params(&self) -> config::VmParams<'_> {
let cpus = &self.cpus;
let memory = &self.memory;
let memory_zones = if !self.memory_zone.is_empty() {
Some(self.memory_zone.iter().map(|x| x.as_str()).collect())
} else {
None
};
let rng = &self.rng;
let serial = &self.serial;
let firmware = self.firmware.as_deref();
let kernel = self.kernel.as_deref();
let initramfs = self.initramfs.as_deref();
let cmdline = self.cmdline.as_deref();
let disks = if !self.disk.is_empty() {
Some(self.disk.iter().map(|x| x.as_str()).collect())
} else {
None
};
let net = if !self.net.is_empty() {
Some(self.net.iter().map(|x| x.as_str()).collect())
} else {
None
};
let console = &self.console;
let balloon = self.balloon.as_deref();
let fs = if !self.fs.is_empty() {
Some(self.fs.iter().map(|x| x.as_str()).collect())
} else {
None
};
let pmem = if !self.pmem.is_empty() {
Some(self.pmem.iter().map(|x| x.as_str()).collect())
} else {
None
};
let devices = if !self.device.is_empty() {
Some(self.device.iter().map(|x| x.as_str()).collect())
} else {
None
};
let user_devices = if !self.user_device.is_empty() {
Some(self.user_device.iter().map(|x| x.as_str()).collect())
} else {
None
};
let vdpa = if !self.vdpa.is_empty() {
Some(self.vdpa.iter().map(|x| x.as_str()).collect())
} else {
None
};
let vsock = self.vsock.as_deref();
#[cfg(target_arch = "x86_64")]
let sgx_epc = if !self.sgx_epc.is_empty() {
Some(self.sgx_epc.iter().map(|x| x.as_str()).collect())
} else {
None
};
let numa = if !self.numa.is_empty() {
Some(self.numa.iter().map(|x| x.as_str()).collect())
} else {
None
};
let watchdog = self.watchdog;
let platform = self.platform.as_deref();
#[cfg(feature = "guest_debug")]
let gdb = self.gdb.is_some();
let tpm = self.tpm.as_deref();
config::VmParams {
cpus,
memory,
memory_zones,
firmware,
kernel,
initramfs,
cmdline,
disks,
net,
rng,
balloon,
fs,
pmem,
serial,
console,
devices,
user_devices,
vdpa,
vsock,
#[cfg(target_arch = "x86_64")]
sgx_epc,
numa,
watchdog,
#[cfg(feature = "guest_debug")]
gdb,
platform,
tpm,
}
}
}
fn start_vmm(toplevel: TopLevel) -> Result<Option<String>, Error> {
let log_level = match toplevel.verbosity {
0 => LevelFilter::Warn, 0 => LevelFilter::Warn,
1 => LevelFilter::Info, 1 => LevelFilter::Info,
2 => LevelFilter::Debug, 2 => LevelFilter::Debug,
_ => LevelFilter::Trace, _ => LevelFilter::Trace,
}; };
let log_file: Box<dyn std::io::Write + Send> = if let Some(file) = let log_file: Box<dyn std::io::Write + Send> = if let Some(ref file) = toplevel.log_file {
cmd_arguments.get_one::<String>("log-file")
{
Box::new(std::fs::File::create(std::path::Path::new(file)).map_err(Error::LogFileCreation)?) Box::new(std::fs::File::create(std::path::Path::new(file)).map_err(Error::LogFileCreation)?)
} else { } else {
Box::new(std::io::stderr()) Box::new(std::io::stderr())
@ -426,8 +392,7 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
.map(|()| log::set_max_level(log_level)) .map(|()| log::set_max_level(log_level))
.map_err(Error::LoggerSetup)?; .map_err(Error::LoggerSetup)?;
let (api_socket_path, api_socket_fd) = let (api_socket_path, api_socket_fd) = if let Some(ref socket_config) = toplevel.api_socket {
if let Some(socket_config) = cmd_arguments.get_one::<String>("api-socket") {
let mut parser = OptionParser::new(); let mut parser = OptionParser::new();
parser.add("path").add("fd"); parser.add("path").add("fd");
parser.parse(socket_config).unwrap_or_default(); parser.parse(socket_config).unwrap_or_default();
@ -440,18 +405,13 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
} else if let Some(path) = parser.get("path") { } else if let Some(path) = parser.get("path") {
(Some(path), None) (Some(path), None)
} else { } else {
( (toplevel.api_socket.as_ref().map(|s| s.to_string()), None)
cmd_arguments
.get_one::<String>("api-socket")
.map(|s| s.to_string()),
None,
)
} }
} else { } else {
(None, None) (None, None)
}; };
if let Some(monitor_config) = cmd_arguments.get_one::<String>("event-monitor") { if let Some(ref monitor_config) = toplevel.event_monitor {
let mut parser = OptionParser::new(); let mut parser = OptionParser::new();
parser.add("path").add("fd"); parser.add("path").add("fd");
parser parser
@ -481,18 +441,14 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
let api_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::CreateApiEventFd)?; let api_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::CreateApiEventFd)?;
let http_sender = api_request_sender.clone(); let http_sender = api_request_sender.clone();
let seccomp_action = if let Some(seccomp_value) = cmd_arguments.get_one::<String>("seccomp") { let seccomp_action = match &toplevel.seccomp as &str {
match seccomp_value as &str {
"true" => SeccompAction::Trap, "true" => SeccompAction::Trap,
"false" => SeccompAction::Allow, "false" => SeccompAction::Allow,
"log" => SeccompAction::Log, "log" => SeccompAction::Log,
_ => { val => {
// The user providing an invalid value will be rejected by clap // The user providing an invalid value will be rejected
panic!("Invalid parameter {seccomp_value} for \"--seccomp\" flag"); panic!("Invalid parameter {val} for \"--seccomp\" flag");
} }
}
} else {
SeccompAction::Trap
}; };
if seccomp_action == SeccompAction::Trap { if seccomp_action == SeccompAction::Trap {
@ -532,7 +488,7 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
let hypervisor = hypervisor::new().map_err(Error::CreateHypervisor)?; let hypervisor = hypervisor::new().map_err(Error::CreateHypervisor)?;
#[cfg(feature = "guest_debug")] #[cfg(feature = "guest_debug")]
let gdb_socket_path = if let Some(gdb_config) = cmd_arguments.get_one::<String>("gdb") { let gdb_socket_path = if let Some(ref gdb_config) = toplevel.gdb {
let mut parser = OptionParser::new(); let mut parser = OptionParser::new();
parser.add("path"); parser.add("path");
parser.parse(gdb_config).map_err(Error::ParsingGdb)?; parser.parse(gdb_config).map_err(Error::ParsingGdb)?;
@ -568,11 +524,10 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
) )
.map_err(Error::StartVmmThread)?; .map_err(Error::StartVmmThread)?;
let payload_present = let payload_present = toplevel.kernel.is_some() || toplevel.firmware.is_some();
cmd_arguments.contains_id("kernel") || cmd_arguments.contains_id("firmware");
if payload_present { if payload_present {
let vm_params = config::VmParams::from_arg_matches(&cmd_arguments); let vm_params = toplevel.to_vm_params();
let vm_config = config::VmConfig::parse(vm_params).map_err(Error::ParsingConfig)?; let vm_config = config::VmConfig::parse(vm_params).map_err(Error::ParsingConfig)?;
// Create and boot the VM based off the VM config we just built. // Create and boot the VM based off the VM config we just built.
@ -584,11 +539,11 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
) )
.map_err(Error::VmCreate)?; .map_err(Error::VmCreate)?;
vmm::api::vm_boot(api_evt.try_clone().unwrap(), sender).map_err(Error::VmBoot)?; vmm::api::vm_boot(api_evt.try_clone().unwrap(), sender).map_err(Error::VmBoot)?;
} else if let Some(restore_params) = cmd_arguments.get_one::<String>("restore") { } else if let Some(restore_params) = toplevel.restore {
vmm::api::vm_restore( vmm::api::vm_restore(
api_evt.try_clone().unwrap(), api_evt.try_clone().unwrap(),
api_request_sender, api_request_sender,
Arc::new(config::RestoreConfig::parse(restore_params).map_err(Error::ParsingRestore)?), Arc::new(config::RestoreConfig::parse(&restore_params).map_err(Error::ParsingRestore)?),
) )
.map_err(Error::VmRestore)?; .map_err(Error::VmRestore)?;
} }
@ -606,9 +561,14 @@ fn main() {
// SAFETY: trivially safe // SAFETY: trivially safe
let _ = unsafe { libc::umask(0o077) }; let _ = unsafe { libc::umask(0o077) };
let (default_vcpus, default_memory, default_rng) = prepare_default_values(); let toplevel: TopLevel = argh::from_env();
let cmd_arguments = create_app(default_vcpus, default_memory, default_rng).get_matches();
let exit_code = match start_vmm(cmd_arguments) { if toplevel.version {
println!("{} {}", env!("CARGO_BIN_NAME"), env!("BUILT_VERSION"));
return;
}
let exit_code = match start_vmm(toplevel) {
Ok(path) => { Ok(path) => {
path.map(|s| std::fs::remove_file(s).ok()); path.map(|s| std::fs::remove_file(s).ok());
0 0
@ -633,19 +593,45 @@ fn main() {
#[cfg(test)] #[cfg(test)]
mod unit_tests { mod unit_tests {
use crate::config::HotplugMethod; use crate::config::HotplugMethod;
use crate::{create_app, prepare_default_values}; use crate::TopLevel;
use std::path::PathBuf; use std::path::PathBuf;
use vmm::config::{ use vmm::config::{
ConsoleConfig, ConsoleOutputMode, CpuFeatures, CpusConfig, MemoryConfig, PayloadConfig, ConsoleConfig, ConsoleOutputMode, CpuFeatures, CpusConfig, MemoryConfig, PayloadConfig,
RngConfig, VmConfig, VmParams, RngConfig, VmConfig,
}; };
fn get_vm_config_from_vec(args: &[&str]) -> VmConfig { // Taken from argh
let (default_vcpus, default_memory, default_rng) = prepare_default_values(); fn cmd<'a>(default: &'a str, path: &'a str) -> &'a str {
let cmd_arguments = std::path::Path::new(path)
create_app(default_vcpus, default_memory, default_rng).get_matches_from(args); .file_name()
.and_then(|s| s.to_str())
.unwrap_or(default)
}
let vm_params = VmParams::from_arg_matches(&cmd_arguments); // Some code taken from argh since it does not provide a helper to parse arbitrary strings
fn get_vm_config_from_vec(args: &[&str]) -> VmConfig {
let strings: Vec<String> = args.iter().map(|x| x.to_string()).collect();
let cmd = cmd(&strings[0], &strings[0]);
let strs: Vec<&str> = strings.iter().map(|s| s.as_str()).collect();
let toplevel = <TopLevel as argh::FromArgs>::from_args(&[cmd], &strs[1..]).unwrap_or_else(
|early_exit| {
std::process::exit(match early_exit.status {
Ok(()) => {
println!("{}", early_exit.output);
0
}
Err(()) => {
eprintln!(
"{}\nRun {} --help for more information.",
early_exit.output, cmd
);
1
}
})
},
);
let vm_params = toplevel.to_vm_params();
VmConfig::parse(vm_params).unwrap() VmConfig::parse(vm_params).unwrap()
} }
@ -904,6 +890,7 @@ mod unit_tests {
"/path/to/kernel", "/path/to/kernel",
"--disk", "--disk",
"path=/path/to/disk/1", "path=/path/to/disk/1",
"--disk",
"path=/path/to/disk/2", "path=/path/to/disk/2",
], ],
r#"{ r#"{
@ -922,6 +909,7 @@ mod unit_tests {
"/path/to/kernel", "/path/to/kernel",
"--disk", "--disk",
"path=/path/to/disk/1", "path=/path/to/disk/1",
"--disk",
"path=/path/to/disk/2", "path=/path/to/disk/2",
], ],
r#"{ r#"{
@ -1197,6 +1185,7 @@ mod unit_tests {
"--memory", "shared=true", "--memory", "shared=true",
"--fs", "--fs",
"tag=virtiofs1,socket=/path/to/sock1", "tag=virtiofs1,socket=/path/to/sock1",
"--fs",
"tag=virtiofs2,socket=/path/to/sock2", "tag=virtiofs2,socket=/path/to/sock2",
], ],
r#"{ r#"{
@ -1215,6 +1204,7 @@ mod unit_tests {
"--memory", "shared=true", "--memory", "shared=true",
"--fs", "--fs",
"tag=virtiofs1,socket=/path/to/sock1", "tag=virtiofs1,socket=/path/to/sock1",
"--fs",
"tag=virtiofs2,socket=/path/to/sock2", "tag=virtiofs2,socket=/path/to/sock2",
], ],
r#"{ r#"{
@ -1277,6 +1267,7 @@ mod unit_tests {
"/path/to/kernel", "/path/to/kernel",
"--pmem", "--pmem",
"file=/path/to/img/1,size=1G", "file=/path/to/img/1,size=1G",
"--pmem",
"file=/path/to/img/2,size=2G", "file=/path/to/img/2,size=2G",
], ],
r#"{ r#"{
@ -1443,6 +1434,7 @@ mod unit_tests {
"/path/to/kernel", "/path/to/kernel",
"--device", "--device",
"path=/path/to/device/1", "path=/path/to/device/1",
"--device",
"path=/path/to/device/2", "path=/path/to/device/2",
], ],
r#"{ r#"{
@ -1461,6 +1453,7 @@ mod unit_tests {
"/path/to/kernel", "/path/to/kernel",
"--device", "--device",
"path=/path/to/device/1", "path=/path/to/device/1",
"--device",
"path=/path/to/device/2", "path=/path/to/device/2",
], ],
r#"{ r#"{
@ -1537,6 +1530,7 @@ mod unit_tests {
"/path/to/kernel", "/path/to/kernel",
"--vdpa", "--vdpa",
"path=/path/to/device/1", "path=/path/to/device/1",
"--vdpa",
"path=/path/to/device/2,num_queues=2", "path=/path/to/device/2,num_queues=2",
], ],
r#"{ r#"{
@ -1555,6 +1549,7 @@ mod unit_tests {
"/path/to/kernel", "/path/to/kernel",
"--vdpa", "--vdpa",
"path=/path/to/device/1", "path=/path/to/device/1",
"--vdpa",
"path=/path/to/device/2", "path=/path/to/device/2",
], ],
r#"{ r#"{

View File

@ -1159,7 +1159,7 @@ impl ToString for VerbosityLevel {
match self { match self {
Warn => "".to_string(), Warn => "".to_string(),
Info => "-v".to_string(), Info => "-v".to_string(),
Debug => "-vv".to_string(), Debug => "-v -v".to_string(),
} }
} }
} }
@ -1210,7 +1210,7 @@ impl<'a> GuestCommand<'a> {
self.command.arg("-v"); self.command.arg("-v");
} }
Debug => { Debug => {
self.command.arg("-vv"); self.command.args(["-v", "-v"]);
} }
}; };
@ -1278,6 +1278,7 @@ impl<'a> GuestCommand<'a> {
.unwrap() .unwrap()
) )
.as_str(), .as_str(),
"--disk",
format!( format!(
"path={}", "path={}",
self.guest.disk_config.disk(DiskType::CloudInit).unwrap() self.guest.disk_config.disk(DiskType::CloudInit).unwrap()

View File

@ -378,7 +378,7 @@ fn setup_ovs_dpdk_guests(
.args(["--kernel", direct_kernel_boot_path().to_str().unwrap()]) .args(["--kernel", direct_kernel_boot_path().to_str().unwrap()])
.args(["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE]) .args(["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
.default_disks() .default_disks()
.args(["--net", guest1.default_net_string().as_str(), "vhost_user=true,socket=/tmp/dpdkvhostclient1,num_queues=2,queue_size=256,vhost_mode=server"]) .args(["--net", guest1.default_net_string().as_str(), "--net", "vhost_user=true,socket=/tmp/dpdkvhostclient1,num_queues=2,queue_size=256,vhost_mode=server"])
.capture_output() .capture_output()
.spawn() .spawn()
.unwrap(); .unwrap();
@ -428,7 +428,7 @@ fn setup_ovs_dpdk_guests(
.args(["--kernel", direct_kernel_boot_path().to_str().unwrap()]) .args(["--kernel", direct_kernel_boot_path().to_str().unwrap()])
.args(["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE]) .args(["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
.default_disks() .default_disks()
.args(["--net", guest2.default_net_string().as_str(), "vhost_user=true,socket=/tmp/dpdkvhostclient2,num_queues=2,queue_size=256,vhost_mode=server"]) .args(["--net", guest2.default_net_string().as_str(), "--net", "vhost_user=true,socket=/tmp/dpdkvhostclient2,num_queues=2,queue_size=256,vhost_mode=server"])
.capture_output() .capture_output()
.spawn() .spawn()
.unwrap(); .unwrap();
@ -661,13 +661,17 @@ fn _test_guest_numa_nodes(acpi: bool) {
.args([ .args([
"--memory-zone", "--memory-zone",
"id=mem0,size=1G,hotplug_size=3G", "id=mem0,size=1G,hotplug_size=3G",
"--memory-zone",
"id=mem1,size=2G,hotplug_size=3G", "id=mem1,size=2G,hotplug_size=3G",
"--memory-zone",
"id=mem2,size=3G,hotplug_size=3G", "id=mem2,size=3G,hotplug_size=3G",
]) ])
.args([ .args([
"--numa", "--numa",
"guest_numa_id=0,cpus=[0-2,9],distances=[1@15,2@20],memory_zones=mem0", "guest_numa_id=0,cpus=[0-2,9],distances=[1@15,2@20],memory_zones=mem0",
"--numa",
"guest_numa_id=1,cpus=[3-4,6-8],distances=[0@20,2@25],memory_zones=mem1", "guest_numa_id=1,cpus=[3-4,6-8],distances=[0@20,2@25],memory_zones=mem1",
"--numa",
"guest_numa_id=2,cpus=[5,10-11],distances=[0@25,1@30],memory_zones=mem2", "guest_numa_id=2,cpus=[5,10-11],distances=[0@25,1@30],memory_zones=mem2",
]) ])
.args(["--kernel", kernel_path.to_str().unwrap()]) .args(["--kernel", kernel_path.to_str().unwrap()])
@ -970,11 +974,13 @@ fn test_vhost_user_blk(
guest.disk_config.disk(DiskType::OperatingSystem).unwrap() guest.disk_config.disk(DiskType::OperatingSystem).unwrap()
) )
.as_str(), .as_str(),
"--disk",
format!( format!(
"path={}", "path={}",
guest.disk_config.disk(DiskType::CloudInit).unwrap() guest.disk_config.disk(DiskType::CloudInit).unwrap()
) )
.as_str(), .as_str(),
"--disk",
blk_params.as_str(), blk_params.as_str(),
]) ])
.default_net() .default_net()
@ -1114,6 +1120,7 @@ fn test_boot_from_vhost_user_blk(
.args([ .args([
"--disk", "--disk",
blk_boot_params.as_str(), blk_boot_params.as_str(),
"--disk",
format!( format!(
"path={}", "path={}",
guest.disk_config.disk(DiskType::CloudInit).unwrap() guest.disk_config.disk(DiskType::CloudInit).unwrap()
@ -1796,6 +1803,7 @@ fn _test_virtio_iommu(acpi: bool) {
guest.disk_config.disk(DiskType::OperatingSystem).unwrap() guest.disk_config.disk(DiskType::OperatingSystem).unwrap()
) )
.as_str(), .as_str(),
"--disk",
format!( format!(
"path={},iommu=on", "path={},iommu=on",
guest.disk_config.disk(DiskType::CloudInit).unwrap() guest.disk_config.disk(DiskType::CloudInit).unwrap()
@ -2211,7 +2219,9 @@ mod common_parallel {
.args([ .args([
"--memory-zone", "--memory-zone",
"id=mem0,size=1G,hotplug_size=2G", "id=mem0,size=1G,hotplug_size=2G",
"--memory-zone",
"id=mem1,size=1G,file=/dev/shm", "id=mem1,size=1G,file=/dev/shm",
"--memory-zone",
"id=mem2,size=1G,host_numa_node=0,hotplug_size=2G", "id=mem2,size=1G,host_numa_node=0,hotplug_size=2G",
]) ])
.args(["--kernel", kernel_path.to_str().unwrap()]) .args(["--kernel", kernel_path.to_str().unwrap()])
@ -2463,11 +2473,13 @@ mod common_parallel {
guest.disk_config.disk(DiskType::OperatingSystem).unwrap() guest.disk_config.disk(DiskType::OperatingSystem).unwrap()
) )
.as_str(), .as_str(),
"--disk",
format!( format!(
"path={}", "path={}",
guest.disk_config.disk(DiskType::CloudInit).unwrap() guest.disk_config.disk(DiskType::CloudInit).unwrap()
) )
.as_str(), .as_str(),
"--disk",
format!("path={test_disk_path},pci_segment=15").as_str(), format!("path={test_disk_path},pci_segment=15").as_str(),
]) ])
.capture_output() .capture_output()
@ -2602,11 +2614,13 @@ mod common_parallel {
guest.disk_config.disk(DiskType::OperatingSystem).unwrap() guest.disk_config.disk(DiskType::OperatingSystem).unwrap()
) )
.as_str(), .as_str(),
"--disk",
format!( format!(
"path={}", "path={}",
guest.disk_config.disk(DiskType::CloudInit).unwrap() guest.disk_config.disk(DiskType::CloudInit).unwrap()
) )
.as_str(), .as_str(),
"--disk",
format!( format!(
"path={},readonly=on,direct=on,num_queues=4,_disable_io_uring={}", "path={},readonly=on,direct=on,num_queues=4,_disable_io_uring={}",
blk_file_path.to_str().unwrap(), blk_file_path.to_str().unwrap(),
@ -2768,11 +2782,13 @@ mod common_parallel {
guest.disk_config.disk(DiskType::OperatingSystem).unwrap() guest.disk_config.disk(DiskType::OperatingSystem).unwrap()
) )
.as_str(), .as_str(),
"--disk",
format!( format!(
"path={}", "path={}",
guest.disk_config.disk(DiskType::CloudInit).unwrap() guest.disk_config.disk(DiskType::CloudInit).unwrap()
) )
.as_str(), .as_str(),
"--disk",
format!("path={vhdx_path}").as_str(), format!("path={vhdx_path}").as_str(),
]) ])
.default_net() .default_net()
@ -2842,6 +2858,7 @@ mod common_parallel {
.args([ .args([
"--disk", "--disk",
format!("path={},direct=on", os_path.as_path().to_str().unwrap()).as_str(), format!("path={},direct=on", os_path.as_path().to_str().unwrap()).as_str(),
"--disk",
format!( format!(
"path={}", "path={}",
guest.disk_config.disk(DiskType::CloudInit).unwrap() guest.disk_config.disk(DiskType::CloudInit).unwrap()
@ -3219,7 +3236,9 @@ mod common_parallel {
.args([ .args([
"--net", "--net",
guest.default_net_string().as_str(), guest.default_net_string().as_str(),
"--net",
"tap=,mac=8a:6b:6f:5a:de:ac,ip=192.168.3.1,mask=255.255.255.0", "tap=,mac=8a:6b:6f:5a:de:ac,ip=192.168.3.1,mask=255.255.255.0",
"--net",
"tap=mytap1,mac=fe:1f:9e:e1:60:f2,ip=192.168.4.1,mask=255.255.255.0", "tap=mytap1,mac=fe:1f:9e:e1:60:f2,ip=192.168.4.1,mask=255.255.255.0",
]) ])
.capture_output() .capture_output()
@ -3759,12 +3778,15 @@ mod common_parallel {
guest.disk_config.disk(DiskType::OperatingSystem).unwrap() guest.disk_config.disk(DiskType::OperatingSystem).unwrap()
) )
.as_str(), .as_str(),
"--disk",
format!( format!(
"path={}", "path={}",
guest.disk_config.disk(DiskType::CloudInit).unwrap() guest.disk_config.disk(DiskType::CloudInit).unwrap()
) )
.as_str(), .as_str(),
"--disk",
format!("path={}", vfio_disk_path.to_str().unwrap()).as_str(), format!("path={}", vfio_disk_path.to_str().unwrap()).as_str(),
"--disk",
format!("path={},iommu=on", blk_file_path.to_str().unwrap()).as_str(), format!("path={},iommu=on", blk_file_path.to_str().unwrap()).as_str(),
]) ])
.args([ .args([
@ -3777,16 +3799,19 @@ mod common_parallel {
.args([ .args([
"--net", "--net",
format!("tap={},mac={}", vfio_tap0, guest.network.guest_mac).as_str(), format!("tap={},mac={}", vfio_tap0, guest.network.guest_mac).as_str(),
"--net",
format!( format!(
"tap={},mac={},iommu=on", "tap={},mac={},iommu=on",
vfio_tap1, guest.network.l2_guest_mac1 vfio_tap1, guest.network.l2_guest_mac1
) )
.as_str(), .as_str(),
"--net",
format!( format!(
"tap={},mac={},iommu=on", "tap={},mac={},iommu=on",
vfio_tap2, guest.network.l2_guest_mac2 vfio_tap2, guest.network.l2_guest_mac2
) )
.as_str(), .as_str(),
"--net",
format!( format!(
"tap={},mac={},iommu=on", "tap={},mac={},iommu=on",
vfio_tap3, guest.network.l2_guest_mac3 vfio_tap3, guest.network.l2_guest_mac3
@ -4323,6 +4348,7 @@ mod common_parallel {
.args([ .args([
"--net", "--net",
guest.default_net_string().as_str(), guest.default_net_string().as_str(),
"--net",
"tap=,mac=8a:6b:6f:5a:de:ac,ip=192.168.3.1,mask=255.255.255.0", "tap=,mac=8a:6b:6f:5a:de:ac,ip=192.168.3.1,mask=255.255.255.0",
]) ])
.capture_output() .capture_output()
@ -5064,11 +5090,13 @@ mod common_parallel {
guest.disk_config.disk(DiskType::OperatingSystem).unwrap() guest.disk_config.disk(DiskType::OperatingSystem).unwrap()
) )
.as_str(), .as_str(),
"--disk",
format!( format!(
"path={}", "path={}",
guest.disk_config.disk(DiskType::CloudInit).unwrap() guest.disk_config.disk(DiskType::CloudInit).unwrap()
) )
.as_str(), .as_str(),
"--disk",
format!("path={}", &loop_dev).as_str(), format!("path={}", &loop_dev).as_str(),
]) ])
.default_net() .default_net()
@ -5633,6 +5661,7 @@ mod common_parallel {
guest.disk_config.disk(DiskType::OperatingSystem).unwrap() guest.disk_config.disk(DiskType::OperatingSystem).unwrap()
) )
.as_str(), .as_str(),
"--disk",
cloudinit_params.as_str(), cloudinit_params.as_str(),
]) ])
.args(["--net", net_params.as_str()]) .args(["--net", net_params.as_str()])
@ -7672,7 +7701,9 @@ mod windows {
.args([ .args([
"--net", "--net",
windows_guest.guest().default_net_string().as_str(), windows_guest.guest().default_net_string().as_str(),
"--net",
"tap=,mac=8a:6b:6f:5a:de:ac,ip=192.168.3.1,mask=255.255.255.0", "tap=,mac=8a:6b:6f:5a:de:ac,ip=192.168.3.1,mask=255.255.255.0",
"--net",
"tap=mytap42,mac=fe:1f:9e:e1:60:f2,ip=192.168.4.1,mask=255.255.255.0", "tap=mytap42,mac=fe:1f:9e:e1:60:f2,ip=192.168.4.1,mask=255.255.255.0",
]) ])
.capture_output() .capture_output()
@ -7825,12 +7856,15 @@ mod vfio {
guest.disk_config.disk(DiskType::OperatingSystem).unwrap() guest.disk_config.disk(DiskType::OperatingSystem).unwrap()
) )
.as_str(), .as_str(),
"--disk",
format!( format!(
"path={}", "path={}",
guest.disk_config.disk(DiskType::CloudInit).unwrap() guest.disk_config.disk(DiskType::CloudInit).unwrap()
) )
.as_str(), .as_str(),
"--disk",
format!("path={}", vfio_disk_path.to_str().unwrap()).as_str(), format!("path={}", vfio_disk_path.to_str().unwrap()).as_str(),
"--disk",
format!("path={},iommu=on", blk_file_path.to_str().unwrap()).as_str(), format!("path={},iommu=on", blk_file_path.to_str().unwrap()).as_str(),
]) ])
.args([ .args([
@ -7843,16 +7877,19 @@ mod vfio {
.args([ .args([
"--net", "--net",
format!("tap={},mac={}", vfio_tap0, guest.network.guest_mac).as_str(), format!("tap={},mac={}", vfio_tap0, guest.network.guest_mac).as_str(),
"--net",
format!( format!(
"tap={},mac={},iommu=on", "tap={},mac={},iommu=on",
vfio_tap1, guest.network.l2_guest_mac1 vfio_tap1, guest.network.l2_guest_mac1
) )
.as_str(), .as_str(),
"--net",
format!( format!(
"tap={},mac={},iommu=on", "tap={},mac={},iommu=on",
vfio_tap2, guest.network.l2_guest_mac2 vfio_tap2, guest.network.l2_guest_mac2
) )
.as_str(), .as_str(),
"--net",
format!( format!(
"tap={},mac={},iommu=on", "tap={},mac={},iommu=on",
vfio_tap3, guest.network.l2_guest_mac3 vfio_tap3, guest.network.l2_guest_mac3
@ -8641,11 +8678,15 @@ mod live_migration {
"size=0,hotplug_method=virtio-mem,shared=on", "size=0,hotplug_method=virtio-mem,shared=on",
"--memory-zone", "--memory-zone",
"id=mem0,size=1G,hotplug_size=4G,shared=on", "id=mem0,size=1G,hotplug_size=4G,shared=on",
"--memory-zone",
"id=mem1,size=1G,hotplug_size=4G,shared=on", "id=mem1,size=1G,hotplug_size=4G,shared=on",
"--memory-zone",
"id=mem2,size=2G,hotplug_size=4G,shared=on", "id=mem2,size=2G,hotplug_size=4G,shared=on",
"--numa", "--numa",
"guest_numa_id=0,cpus=[0-2,9],distances=[1@15,2@20],memory_zones=mem0", "guest_numa_id=0,cpus=[0-2,9],distances=[1@15,2@20],memory_zones=mem0",
"--numa",
"guest_numa_id=1,cpus=[3-4,6-8],distances=[0@20,2@25],memory_zones=mem1", "guest_numa_id=1,cpus=[3-4,6-8],distances=[0@20,2@25],memory_zones=mem1",
"--numa",
"guest_numa_id=2,cpus=[5,10-11],distances=[0@25,1@30],memory_zones=mem2", "guest_numa_id=2,cpus=[5,10-11],distances=[0@25,1@30],memory_zones=mem2",
] ]
} else { } else {
@ -8654,11 +8695,15 @@ mod live_migration {
"size=0,hotplug_method=virtio-mem", "size=0,hotplug_method=virtio-mem",
"--memory-zone", "--memory-zone",
"id=mem0,size=1G,hotplug_size=4G", "id=mem0,size=1G,hotplug_size=4G",
"--memory-zone",
"id=mem1,size=1G,hotplug_size=4G", "id=mem1,size=1G,hotplug_size=4G",
"--memory-zone",
"id=mem2,size=2G,hotplug_size=4G", "id=mem2,size=2G,hotplug_size=4G",
"--numa", "--numa",
"guest_numa_id=0,cpus=[0-2,9],distances=[1@15,2@20],memory_zones=mem0", "guest_numa_id=0,cpus=[0-2,9],distances=[1@15,2@20],memory_zones=mem0",
"--numa",
"guest_numa_id=1,cpus=[3-4,6-8],distances=[0@20,2@25],memory_zones=mem1", "guest_numa_id=1,cpus=[3-4,6-8],distances=[0@20,2@25],memory_zones=mem1",
"--numa",
"guest_numa_id=2,cpus=[5,10-11],distances=[0@25,1@30],memory_zones=mem2", "guest_numa_id=2,cpus=[5,10-11],distances=[0@25,1@30],memory_zones=mem2",
] ]
}; };
@ -9496,11 +9541,13 @@ mod rate_limiter {
guest.disk_config.disk(DiskType::OperatingSystem).unwrap() guest.disk_config.disk(DiskType::OperatingSystem).unwrap()
) )
.as_str(), .as_str(),
"--disk",
format!( format!(
"path={}", "path={}",
guest.disk_config.disk(DiskType::CloudInit).unwrap() guest.disk_config.disk(DiskType::CloudInit).unwrap()
) )
.as_str(), .as_str(),
"--disk",
test_blk_params.as_str(), test_blk_params.as_str(),
]) ])
.default_net() .default_net()

View File

@ -19,7 +19,6 @@ arc-swap = "1.5.1"
arch = { path = "../arch" } arch = { path = "../arch" }
bitflags = "1.3.2" bitflags = "1.3.2"
block_util = { path = "../block_util" } block_util = { path = "../block_util" }
clap = "4.0.32"
devices = { path = "../devices" } devices = { path = "../devices" }
epoll = "4.3.1" epoll = "4.3.1"
event_monitor = { path = "../event_monitor" } event_monitor = { path = "../event_monitor" }

View File

@ -4,7 +4,6 @@
// //
pub use crate::vm_config::*; pub use crate::vm_config::*;
use clap::ArgMatches;
use option_parser::{ use option_parser::{
ByteSized, IntegerList, OptionParser, OptionParserError, StringList, Toggle, Tuple, ByteSized, IntegerList, OptionParser, OptionParserError, StringList, Toggle, Tuple,
}; };
@ -381,88 +380,6 @@ pub struct VmParams<'a> {
pub tpm: Option<&'a str>, pub tpm: Option<&'a str>,
} }
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.get_one::<String>("cpus").unwrap();
let memory = args.get_one::<String>("memory").unwrap();
let memory_zones: Option<Vec<&str>> = args
.get_many::<String>("memory-zone")
.map(|x| x.map(|y| y as &str).collect());
let rng = args.get_one::<String>("rng").unwrap();
let serial = args.get_one::<String>("serial").unwrap();
let firmware = args.get_one::<String>("firmware").map(|x| x as &str);
let kernel = args.get_one::<String>("kernel").map(|x| x as &str);
let initramfs = args.get_one::<String>("initramfs").map(|x| x as &str);
let cmdline = args.get_one::<String>("cmdline").map(|x| x as &str);
let disks: Option<Vec<&str>> = args
.get_many::<String>("disk")
.map(|x| x.map(|y| y as &str).collect());
let net: Option<Vec<&str>> = args
.get_many::<String>("net")
.map(|x| x.map(|y| y as &str).collect());
let console = args.get_one::<String>("console").unwrap();
let balloon = args.get_one::<String>("balloon").map(|x| x as &str);
let fs: Option<Vec<&str>> = args
.get_many::<String>("fs")
.map(|x| x.map(|y| y as &str).collect());
let pmem: Option<Vec<&str>> = args
.get_many::<String>("pmem")
.map(|x| x.map(|y| y as &str).collect());
let devices: Option<Vec<&str>> = args
.get_many::<String>("device")
.map(|x| x.map(|y| y as &str).collect());
let user_devices: Option<Vec<&str>> = args
.get_many::<String>("user-device")
.map(|x| x.map(|y| y as &str).collect());
let vdpa: Option<Vec<&str>> = args
.get_many::<String>("vdpa")
.map(|x| x.map(|y| y as &str).collect());
let vsock: Option<&str> = args.get_one::<String>("vsock").map(|x| x as &str);
#[cfg(target_arch = "x86_64")]
let sgx_epc: Option<Vec<&str>> = args
.get_many::<String>("sgx-epc")
.map(|x| x.map(|y| y as &str).collect());
let numa: Option<Vec<&str>> = args
.get_many::<String>("numa")
.map(|x| x.map(|y| y as &str).collect());
let watchdog = args.get_flag("watchdog");
let platform = args.get_one::<String>("platform").map(|x| x as &str);
#[cfg(feature = "guest_debug")]
let gdb = args.contains_id("gdb");
let tpm: Option<&str> = args.get_one::<String>("tpm").map(|x| x as &str);
VmParams {
cpus,
memory,
memory_zones,
firmware,
kernel,
initramfs,
cmdline,
disks,
net,
rng,
balloon,
fs,
pmem,
serial,
console,
devices,
user_devices,
vdpa,
vsock,
#[cfg(target_arch = "x86_64")]
sgx_epc,
numa,
watchdog,
#[cfg(feature = "guest_debug")]
gdb,
platform,
tpm,
}
}
}
#[derive(Debug)] #[derive(Debug)]
pub enum ParseHotplugMethodError { pub enum ParseHotplugMethodError {
InvalidValue(String), InvalidValue(String),
@ -827,14 +744,6 @@ impl MemoryConfig {
} }
impl DiskConfig { impl DiskConfig {
pub const SYNTAX: &'static str = "Disk parameters \
\"path=<disk_image_path>,readonly=on|off,direct=on|off,iommu=on|off,\
num_queues=<number_of_queues>,queue_size=<size_of_each_queue>,\
vhost_user=on|off,socket=<vhost_user_socket_path>,\
bw_size=<bytes>,bw_one_time_burst=<bytes>,bw_refill_time=<ms>,\
ops_size=<io_ops>,ops_one_time_burst=<io_ops>,ops_refill_time=<ms>,\
id=<device_id>,pci_segment=<segment_id>\"";
pub fn parse(disk: &str) -> Result<Self> { pub fn parse(disk: &str) -> Result<Self> {
let mut parser = OptionParser::new(); let mut parser = OptionParser::new();
parser parser
@ -1007,14 +916,6 @@ impl FromStr for VhostMode {
} }
impl NetConfig { impl NetConfig {
pub const SYNTAX: &'static str = "Network parameters \
\"tap=<if_name>,ip=<ip_addr>,mask=<net_mask>,mac=<mac_addr>,fd=<fd1,fd2...>,iommu=on|off,\
num_queues=<number_of_queues>,queue_size=<size_of_each_queue>,id=<device_id>,\
vhost_user=<vhost_user_enable>,socket=<vhost_user_socket_path>,vhost_mode=client|server,\
bw_size=<bytes>,bw_one_time_burst=<bytes>,bw_refill_time=<ms>,\
ops_size=<io_ops>,ops_one_time_burst=<io_ops>,ops_refill_time=<ms>,pci_segment=<segment_id>\
offload_tso=on|off,offload_ufo=on|off,offload_csum=on|off\"";
pub fn parse(net: &str) -> Result<Self> { pub fn parse(net: &str) -> Result<Self> {
let mut parser = OptionParser::new(); let mut parser = OptionParser::new();
@ -1255,10 +1156,6 @@ impl RngConfig {
} }
impl BalloonConfig { impl BalloonConfig {
pub const SYNTAX: &'static str =
"Balloon parameters \"size=<balloon_size>,deflate_on_oom=on|off,\
free_page_reporting=on|off\"";
pub fn parse(balloon: &str) -> Result<Self> { pub fn parse(balloon: &str) -> Result<Self> {
let mut parser = OptionParser::new(); let mut parser = OptionParser::new();
parser.add("size"); parser.add("size");
@ -1293,10 +1190,6 @@ impl BalloonConfig {
} }
impl FsConfig { impl FsConfig {
pub const SYNTAX: &'static str = "virtio-fs parameters \
\"tag=<tag_name>,socket=<socket_path>,num_queues=<number_of_queues>,\
queue_size=<size_of_each_queue>,id=<device_id>,pci_segment=<segment_id>\"";
pub fn parse(fs: &str) -> Result<Self> { pub fn parse(fs: &str) -> Result<Self> {
let mut parser = OptionParser::new(); let mut parser = OptionParser::new();
parser parser
@ -1361,9 +1254,6 @@ impl FsConfig {
} }
impl PmemConfig { impl PmemConfig {
pub const SYNTAX: &'static str = "Persistent memory parameters \
\"file=<backing_file_path>,size=<persistent_memory_size>,iommu=on|off,\
discard_writes=on|off,id=<device_id>,pci_segment=<segment_id>\"";
pub fn parse(pmem: &str) -> Result<Self> { pub fn parse(pmem: &str) -> Result<Self> {
let mut parser = OptionParser::new(); let mut parser = OptionParser::new();
parser parser
@ -1465,8 +1355,6 @@ impl ConsoleConfig {
} }
impl DeviceConfig { impl DeviceConfig {
pub const SYNTAX: &'static str =
"Direct device assignment parameters \"path=<device_path>,iommu=on|off,id=<device_id>,pci_segment=<segment_id>\"";
pub fn parse(device: &str) -> Result<Self> { pub fn parse(device: &str) -> Result<Self> {
let mut parser = OptionParser::new(); let mut parser = OptionParser::new();
parser.add("path").add("id").add("iommu").add("pci_segment"); parser.add("path").add("id").add("iommu").add("pci_segment");
@ -1513,8 +1401,6 @@ impl DeviceConfig {
} }
impl UserDeviceConfig { impl UserDeviceConfig {
pub const SYNTAX: &'static str =
"Userspace device socket=<socket_path>,id=<device_id>,pci_segment=<segment_id>\"";
pub fn parse(user_device: &str) -> Result<Self> { pub fn parse(user_device: &str) -> Result<Self> {
let mut parser = OptionParser::new(); let mut parser = OptionParser::new();
parser.add("socket").add("id").add("pci_segment"); parser.add("socket").add("id").add("pci_segment");
@ -1557,9 +1443,6 @@ impl UserDeviceConfig {
} }
impl VdpaConfig { impl VdpaConfig {
pub const SYNTAX: &'static str = "vDPA device \
\"path=<device_path>,num_queues=<number_of_queues>,iommu=on|off,\
id=<device_id>,pci_segment=<segment_id>\"";
pub fn parse(vdpa: &str) -> Result<Self> { pub fn parse(vdpa: &str) -> Result<Self> {
let mut parser = OptionParser::new(); let mut parser = OptionParser::new();
parser parser
@ -1616,8 +1499,6 @@ impl VdpaConfig {
} }
impl VsockConfig { impl VsockConfig {
pub const SYNTAX: &'static str = "Virtio VSOCK parameters \
\"cid=<context_id>,socket=<socket_path>,iommu=on|off,id=<device_id>,pci_segment=<segment_id>\"";
pub fn parse(vsock: &str) -> Result<Self> { pub fn parse(vsock: &str) -> Result<Self> {
let mut parser = OptionParser::new(); let mut parser = OptionParser::new();
parser parser
@ -1675,8 +1556,6 @@ impl VsockConfig {
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
impl SgxEpcConfig { impl SgxEpcConfig {
pub const SYNTAX: &'static str = "SGX EPC parameters \
\"id=<epc_section_identifier>,size=<epc_section_size>,prefault=on|off\"";
pub fn parse(sgx_epc: &str) -> Result<Self> { pub fn parse(sgx_epc: &str) -> Result<Self> {
let mut parser = OptionParser::new(); let mut parser = OptionParser::new();
parser.add("id").add("size").add("prefault"); parser.add("id").add("size").add("prefault");
@ -1699,9 +1578,6 @@ impl SgxEpcConfig {
} }
impl NumaConfig { impl NumaConfig {
pub const SYNTAX: &'static str = "Settings related to a given NUMA node \
\"guest_numa_id=<node_id>,cpus=<cpus_id>,distances=<list_of_distances_to_destination_nodes>,\
memory_zones=<list_of_memory_zones>,sgx_epc_sections=<list_of_sgx_epc_sections>\"";
pub fn parse(numa: &str) -> Result<Self> { pub fn parse(numa: &str) -> Result<Self> {
let mut parser = OptionParser::new(); let mut parser = OptionParser::new();
parser parser
@ -1760,10 +1636,6 @@ pub struct RestoreConfig {
} }
impl RestoreConfig { impl RestoreConfig {
pub const SYNTAX: &'static str = "Restore from a VM snapshot. \
\nRestore parameters \"source_url=<source_url>,prefault=on|off\" \
\n`source_url` should be a valid URL (e.g file:///foo/bar or tcp://192.168.1.10/foo) \
\n`prefault` brings memory pages in when enabled (disabled by default)";
pub fn parse(restore: &str) -> Result<Self> { pub fn parse(restore: &str) -> Result<Self> {
let mut parser = OptionParser::new(); let mut parser = OptionParser::new();
parser.add("source_url").add("prefault"); parser.add("source_url").add("prefault");
@ -1787,8 +1659,6 @@ impl RestoreConfig {
} }
impl TpmConfig { impl TpmConfig {
pub const SYNTAX: &'static str = "TPM device \
\"(UNIX Domain Socket from swtpm) socket=</path/to/a/socket>\"";
pub fn parse(tpm: &str) -> Result<Self> { pub fn parse(tpm: &str) -> Result<Self> {
let mut parser = OptionParser::new(); let mut parser = OptionParser::new();
parser.add("socket"); parser.add("socket");