From 111225a2a562a465838219a82611b82393308ae7 Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Thu, 12 Jan 2023 18:50:20 +0000 Subject: [PATCH] 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 --- Cargo.lock | 2 - Cargo.toml | 1 - fuzz/Cargo.lock | 218 +------ performance-metrics/src/performance_tests.rs | 2 + src/main.rs | 639 +++++++++---------- test_infra/src/lib.rs | 5 +- tests/integration.rs | 51 +- vmm/Cargo.toml | 1 - vmm/src/config.rs | 130 ---- 9 files changed, 400 insertions(+), 649 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ac877789d..612d9f938 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -178,7 +178,6 @@ dependencies = [ "anyhow", "api_client", "argh", - "clap", "dirs", "epoll", "event_monitor", @@ -1575,7 +1574,6 @@ dependencies = [ "arch", "bitflags", "block_util", - "clap", "devices", "epoll", "event_monitor", diff --git a/Cargo.toml b/Cargo.toml index 86775d841..8d7d4d98c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,6 @@ strip = true anyhow = "1.0.68" api_client = { path = "api_client" } argh = "0.1.9" -clap = { version = "4.0.32", features = ["wrap_help","cargo","string"] } epoll = "4.3.1" event_monitor = { path = "event_monitor" } hypervisor = { path = "hypervisor" } diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index e31ed5423..9d686d774 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -56,6 +56,34 @@ dependencies = [ "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]] name = "bincode" version = "1.3.3" @@ -112,37 +140,13 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "cloud-hypervisor" version = "29.0.0" dependencies = [ "anyhow", "api_client", - "clap", + "argh", "epoll", "event_monitor", "hypervisor", @@ -269,27 +273,6 @@ dependencies = [ "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]] name = "event_monitor" version = "0.1.0" @@ -322,15 +305,6 @@ dependencies = [ "wasi", ] -[[package]] -name = "hermit-abi" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - [[package]] name = "hypervisor" version = "0.1.0" @@ -365,16 +339,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "io-uring" version = "0.5.11" @@ -385,18 +349,6 @@ dependencies = [ "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]] name = "itoa" version = "1.0.5" @@ -465,12 +417,6 @@ dependencies = [ "vm-memory", ] -[[package]] -name = "linux-raw-sys" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" - [[package]] name = "log" version = "0.4.17" @@ -527,12 +473,6 @@ checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" name = "option_parser" 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]] name = "pci" version = "0.1.0" @@ -671,20 +611,6 @@ dependencies = [ "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]] name = "ryu" version = "1.0.12" @@ -811,25 +737,6 @@ dependencies = [ "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]] name = "thiserror" version = "1.0.38" @@ -1102,7 +1009,6 @@ dependencies = [ "arch", "bitflags", "block_util", - "clap", "devices", "epoll", "event_monitor", @@ -1173,74 +1079,8 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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" diff --git a/performance-metrics/src/performance_tests.rs b/performance-metrics/src/performance_tests.rs index a2d1542b5..65587f78d 100644 --- a/performance-metrics/src/performance_tests.rs +++ b/performance-metrics/src/performance_tests.rs @@ -359,11 +359,13 @@ pub fn performance_block_io(control: &PerformanceTestControl) -> f64 { guest.disk_config.disk(DiskType::OperatingSystem).unwrap() ) .as_str(), + "--disk", format!( "path={}", guest.disk_config.disk(DiskType::CloudInit).unwrap() ) .as_str(), + "--disk", format!("path={BLK_IO_TEST_IMG}").as_str(), ]) .default_net() diff --git a/src/main.rs b/src/main.rs index c1a4646f9..9d993a8db 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,7 +6,7 @@ #[macro_use] extern crate event_monitor; -use clap::{Arg, ArgAction, ArgGroup, ArgMatches, Command}; +use argh::FromArgs; use libc::EFD_NONBLOCK; use log::LevelFilter; use option_parser::OptionParser; @@ -113,10 +113,6 @@ impl log::Log for Logger { fn flush(&self) {} } -fn prepare_default_values() -> (String, String, String) { - (default_vcpus(), default_memory(), default_rng()) -} - fn default_vcpus() -> String { format!( "boot={},max_phys_bits={}", @@ -133,287 +129,257 @@ fn default_rng() -> String { format!("src={}", config::DEFAULT_RNG_SOURCE) } -fn create_app(default_vcpus: String, default_memory: String, default_rng: String) -> Command { - #[allow(clippy::let_and_return)] - let app = Command::new("cloud-hypervisor") - // 'BUILT_VERSION' is set by the build script 'build.rs' at - // compile time - .version(env!("BUILT_VERSION")) - .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=,max=,\ - topology=:::,\ - kvm_hyperv=on|off,max_phys_bits=,\ - affinity=,\ - features=", - ) - .default_value(default_vcpus) - .group("vm-config"), - ) - .arg( - Arg::new("platform") - .long("platform") - .help( - "num_pci_segments=,iommu_segments=,serial_number=,uuid=,oem_strings=", - ) - .num_args(1) - .group("vm-config"), - ) - .arg( - Arg::new("memory") - .long("memory") - .help( - "Memory parameters \ - \"size=,mergeable=on|off,shared=on|off,\ - hugepages=on|off,hugepage_size=,\ - hotplug_method=acpi|virtio-mem,\ - hotplug_size=,\ - hotplugged_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=,file=,\ - shared=on|off,\ - hugepages=on|off,hugepage_size=,\ - host_numa_node=,\ - id=,hotplug_size=,\ - hotplugged_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=,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= or fd=.") - .num_args(1) - .group("vmm-config"), - ) - .arg( - Arg::new("event-monitor") - .long("event-monitor") - .help("File to report events on: path= or 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"), +#[derive(FromArgs)] +/// Launch a cloud-hypervisor VMM. +pub struct TopLevel { + #[argh(option, long = "cpus", default = "default_vcpus()")] + /// boot=,max=,topology=:::,kvm_hyperv=on|off,max_phys_bits=,affinity=,features= + cpus: String, - ); + #[argh(option, long = "platform")] + /// num_pci_segments=,iommu_segments=,serial_number=,uuid=,oem_strings= + platform: Option, + + #[argh(option, long = "memory", default = "default_memory()")] + /// size=,mergeable=on|off,shared=on|off,hugepages=on|off,hugepage_size=,hotplug_method=acpi|virtio-mem,hotplug_size=,hotplugged_size=,prefault=on|off,thp=on|off + memory: String, + + #[argh(option, long = "memory-zone")] + /// size=,file=,shared=on|off,hugepages=on|off,hugepage_size=,host_numa_node=,id=,hotplug_size=,hotplugged_size=,prefault=on|off + memory_zone: Vec, + + #[argh(option, long = "firmware")] + /// path to firmware that is loaded in an architectural specific way + firmware: Option, + + #[argh(option, long = "kernel")] + /// path to kernel or firmware that supports a PVH entry point or architecture equivalent + kernel: Option, + + #[argh(option, long = "initramfs")] + /// path to initramfs image + initramfs: Option, + + #[argh(option, long = "cmdline")] + /// kernel command line + cmdline: Option, + + #[argh(option, long = "disk")] + /// path=,readonly=on|off,direct=on|off,iommu=on|off,num_queues=,queue_size=,vhost_user=on|off,socket=,bw_size=,bw_one_time_burst=,bw_refill_time=,ops_size=,ops_one_time_burst=,ops_refill_time=,id=,pci_segment= + disk: Vec, + + #[argh(option, long = "net")] + /// tap=,ip=,mask=,mac=,fd=,iommu=on|off,num_queues=,queue_size=,id=,vhost_user=,socket=,vhost_mode=client|server,bw_size=,bw_one_time_burst=,bw_refill_time=,ops_size=,ops_one_time_burst=,ops_refill_time=,pci_segment=offload_tso=on|off,offload_ufo=on|off,offload_csum=on|off + net: Vec, + + #[argh(option, long = "rng", default = "default_rng()")] + /// src=,iommu=on|off + rng: String, + + #[argh(option, long = "balloon")] + /// size=,deflate_on_oom=on|off,free_page_reporting=on|off + balloon: Option, + + #[argh(option, long = "fs")] + /// tag=,socket=,num_queues=,queue_size=,id=,pci_segment= + fs: Vec, + + #[argh(option, long = "pmem")] + /// file=,size=,iommu=on|off,discard_writes=on|off,id=,pci_segment= + pmem: Vec, + + #[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=,iommu=on|off,id=,pci_segment= + device: Vec, + + #[argh(option, long = "user-device")] + /// socket=,id=,pci_segment= + user_device: Vec, + + #[argh(option, long = "vdpa")] + /// path=,num_queues=,iommu=on|off,id=,pci_segment= + vdpa: Vec, + + #[argh(option, long = "vsock")] + /// cid=,socket=,iommu=on|off,id=,pci_segment= + vsock: Option, + + #[argh(option, long = "numa")] + /// guest_numa_id=,cpus=,distances=,memory_zones=,sgx_epc_sections= + numa: Vec, + + #[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, + + #[argh(option, long = "api-socket")] + /// path=|fd= + api_socket: Option, + + #[argh(option, long = "event-monitor")] + /// path=|fd= + event_monitor: Option, + + #[argh(option, long = "restore")] + /// source_url=,prefault=on|off + restore: Option, + + #[argh(option, long = "seccomp", default = "String::from(\"true\")")] + /// seccomp configuration (true, false or log) + seccomp: String, + + #[argh(option, long = "tpm")] + /// socket= + tpm: Option, #[cfg(target_arch = "x86_64")] - let app = app.arg( - Arg::new("sgx-epc") - .long("sgx-epc") - .help(config::SgxEpcConfig::SYNTAX) - .num_args(1..) - .group("vm-config"), - ); + #[argh(option, long = "sgx-epc")] + /// id=,size=,prefault=on|off + sgx_epc: Vec, #[cfg(feature = "guest_debug")] - let app = app.arg( - Arg::new("gdb") - .long("gdb") - .help("GDB socket (UNIX domain socket): path=") - .num_args(1) - .group("vmm-config"), - ); + #[argh(option, long = "gdb")] + /// path= + gdb: Option, - app + #[argh(switch, short = 'V', long = "version")] + /// print version information + version: bool, } -fn start_vmm(cmd_arguments: ArgMatches) -> Result, Error> { - let log_level = match cmd_arguments.get_count("v") { +impl TopLevel { + 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, Error> { + let log_level = match toplevel.verbosity { 0 => LevelFilter::Warn, 1 => LevelFilter::Info, 2 => LevelFilter::Debug, _ => LevelFilter::Trace, }; - let log_file: Box = if let Some(file) = - cmd_arguments.get_one::("log-file") - { + let log_file: Box = if let Some(ref file) = toplevel.log_file { Box::new(std::fs::File::create(std::path::Path::new(file)).map_err(Error::LogFileCreation)?) } else { Box::new(std::io::stderr()) @@ -426,32 +392,26 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result, Error> { .map(|()| log::set_max_level(log_level)) .map_err(Error::LoggerSetup)?; - let (api_socket_path, api_socket_fd) = - if let Some(socket_config) = cmd_arguments.get_one::("api-socket") { - let mut parser = OptionParser::new(); - parser.add("path").add("fd"); - parser.parse(socket_config).unwrap_or_default(); + let (api_socket_path, api_socket_fd) = if let Some(ref socket_config) = toplevel.api_socket { + let mut parser = OptionParser::new(); + parser.add("path").add("fd"); + parser.parse(socket_config).unwrap_or_default(); - if let Some(fd) = parser.get("fd") { - ( - None, - Some(fd.parse::().map_err(Error::ParsingApiSocket)?), - ) - } else if let Some(path) = parser.get("path") { - (Some(path), None) - } else { - ( - cmd_arguments - .get_one::("api-socket") - .map(|s| s.to_string()), - None, - ) - } + if let Some(fd) = parser.get("fd") { + ( + None, + Some(fd.parse::().map_err(Error::ParsingApiSocket)?), + ) + } else if let Some(path) = parser.get("path") { + (Some(path), None) } else { - (None, None) - }; + (toplevel.api_socket.as_ref().map(|s| s.to_string()), None) + } + } else { + (None, None) + }; - if let Some(monitor_config) = cmd_arguments.get_one::("event-monitor") { + if let Some(ref monitor_config) = toplevel.event_monitor { let mut parser = OptionParser::new(); parser.add("path").add("fd"); parser @@ -481,18 +441,14 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result, Error> { let api_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::CreateApiEventFd)?; let http_sender = api_request_sender.clone(); - let seccomp_action = if let Some(seccomp_value) = cmd_arguments.get_one::("seccomp") { - match seccomp_value as &str { - "true" => SeccompAction::Trap, - "false" => SeccompAction::Allow, - "log" => SeccompAction::Log, - _ => { - // The user providing an invalid value will be rejected by clap - panic!("Invalid parameter {seccomp_value} for \"--seccomp\" flag"); - } + let seccomp_action = match &toplevel.seccomp as &str { + "true" => SeccompAction::Trap, + "false" => SeccompAction::Allow, + "log" => SeccompAction::Log, + val => { + // The user providing an invalid value will be rejected + panic!("Invalid parameter {val} for \"--seccomp\" flag"); } - } else { - SeccompAction::Trap }; if seccomp_action == SeccompAction::Trap { @@ -532,7 +488,7 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result, Error> { let hypervisor = hypervisor::new().map_err(Error::CreateHypervisor)?; #[cfg(feature = "guest_debug")] - let gdb_socket_path = if let Some(gdb_config) = cmd_arguments.get_one::("gdb") { + let gdb_socket_path = if let Some(ref gdb_config) = toplevel.gdb { let mut parser = OptionParser::new(); parser.add("path"); parser.parse(gdb_config).map_err(Error::ParsingGdb)?; @@ -568,11 +524,10 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result, Error> { ) .map_err(Error::StartVmmThread)?; - let payload_present = - cmd_arguments.contains_id("kernel") || cmd_arguments.contains_id("firmware"); + let payload_present = toplevel.kernel.is_some() || toplevel.firmware.is_some(); 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)?; // Create and boot the VM based off the VM config we just built. @@ -584,11 +539,11 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result, Error> { ) .map_err(Error::VmCreate)?; vmm::api::vm_boot(api_evt.try_clone().unwrap(), sender).map_err(Error::VmBoot)?; - } else if let Some(restore_params) = cmd_arguments.get_one::("restore") { + } else if let Some(restore_params) = toplevel.restore { vmm::api::vm_restore( api_evt.try_clone().unwrap(), 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)?; } @@ -606,9 +561,14 @@ fn main() { // SAFETY: trivially safe let _ = unsafe { libc::umask(0o077) }; - let (default_vcpus, default_memory, default_rng) = prepare_default_values(); - let cmd_arguments = create_app(default_vcpus, default_memory, default_rng).get_matches(); - let exit_code = match start_vmm(cmd_arguments) { + let toplevel: TopLevel = argh::from_env(); + + if toplevel.version { + println!("{} {}", env!("CARGO_BIN_NAME"), env!("BUILT_VERSION")); + return; + } + + let exit_code = match start_vmm(toplevel) { Ok(path) => { path.map(|s| std::fs::remove_file(s).ok()); 0 @@ -633,19 +593,45 @@ fn main() { #[cfg(test)] mod unit_tests { use crate::config::HotplugMethod; - use crate::{create_app, prepare_default_values}; + use crate::TopLevel; use std::path::PathBuf; use vmm::config::{ ConsoleConfig, ConsoleOutputMode, CpuFeatures, CpusConfig, MemoryConfig, PayloadConfig, - RngConfig, VmConfig, VmParams, + RngConfig, VmConfig, }; - fn get_vm_config_from_vec(args: &[&str]) -> VmConfig { - let (default_vcpus, default_memory, default_rng) = prepare_default_values(); - let cmd_arguments = - create_app(default_vcpus, default_memory, default_rng).get_matches_from(args); + // Taken from argh + fn cmd<'a>(default: &'a str, path: &'a str) -> &'a str { + std::path::Path::new(path) + .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 = 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 = ::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() } @@ -904,6 +890,7 @@ mod unit_tests { "/path/to/kernel", "--disk", "path=/path/to/disk/1", + "--disk", "path=/path/to/disk/2", ], r#"{ @@ -922,6 +909,7 @@ mod unit_tests { "/path/to/kernel", "--disk", "path=/path/to/disk/1", + "--disk", "path=/path/to/disk/2", ], r#"{ @@ -1197,6 +1185,7 @@ mod unit_tests { "--memory", "shared=true", "--fs", "tag=virtiofs1,socket=/path/to/sock1", + "--fs", "tag=virtiofs2,socket=/path/to/sock2", ], r#"{ @@ -1215,6 +1204,7 @@ mod unit_tests { "--memory", "shared=true", "--fs", "tag=virtiofs1,socket=/path/to/sock1", + "--fs", "tag=virtiofs2,socket=/path/to/sock2", ], r#"{ @@ -1277,6 +1267,7 @@ mod unit_tests { "/path/to/kernel", "--pmem", "file=/path/to/img/1,size=1G", + "--pmem", "file=/path/to/img/2,size=2G", ], r#"{ @@ -1443,6 +1434,7 @@ mod unit_tests { "/path/to/kernel", "--device", "path=/path/to/device/1", + "--device", "path=/path/to/device/2", ], r#"{ @@ -1461,6 +1453,7 @@ mod unit_tests { "/path/to/kernel", "--device", "path=/path/to/device/1", + "--device", "path=/path/to/device/2", ], r#"{ @@ -1537,6 +1530,7 @@ mod unit_tests { "/path/to/kernel", "--vdpa", "path=/path/to/device/1", + "--vdpa", "path=/path/to/device/2,num_queues=2", ], r#"{ @@ -1555,6 +1549,7 @@ mod unit_tests { "/path/to/kernel", "--vdpa", "path=/path/to/device/1", + "--vdpa", "path=/path/to/device/2", ], r#"{ diff --git a/test_infra/src/lib.rs b/test_infra/src/lib.rs index fa7137c16..0074e6bfd 100644 --- a/test_infra/src/lib.rs +++ b/test_infra/src/lib.rs @@ -1159,7 +1159,7 @@ impl ToString for VerbosityLevel { match self { Warn => "".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"); } Debug => { - self.command.arg("-vv"); + self.command.args(["-v", "-v"]); } }; @@ -1278,6 +1278,7 @@ impl<'a> GuestCommand<'a> { .unwrap() ) .as_str(), + "--disk", format!( "path={}", self.guest.disk_config.disk(DiskType::CloudInit).unwrap() diff --git a/tests/integration.rs b/tests/integration.rs index 9c8789b9b..02e899a02 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -378,7 +378,7 @@ fn setup_ovs_dpdk_guests( .args(["--kernel", direct_kernel_boot_path().to_str().unwrap()]) .args(["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE]) .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() .spawn() .unwrap(); @@ -428,7 +428,7 @@ fn setup_ovs_dpdk_guests( .args(["--kernel", direct_kernel_boot_path().to_str().unwrap()]) .args(["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE]) .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() .spawn() .unwrap(); @@ -661,13 +661,17 @@ fn _test_guest_numa_nodes(acpi: bool) { .args([ "--memory-zone", "id=mem0,size=1G,hotplug_size=3G", + "--memory-zone", "id=mem1,size=2G,hotplug_size=3G", + "--memory-zone", "id=mem2,size=3G,hotplug_size=3G", ]) .args([ "--numa", "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", + "--numa", "guest_numa_id=2,cpus=[5,10-11],distances=[0@25,1@30],memory_zones=mem2", ]) .args(["--kernel", kernel_path.to_str().unwrap()]) @@ -970,11 +974,13 @@ fn test_vhost_user_blk( guest.disk_config.disk(DiskType::OperatingSystem).unwrap() ) .as_str(), + "--disk", format!( "path={}", guest.disk_config.disk(DiskType::CloudInit).unwrap() ) .as_str(), + "--disk", blk_params.as_str(), ]) .default_net() @@ -1114,6 +1120,7 @@ fn test_boot_from_vhost_user_blk( .args([ "--disk", blk_boot_params.as_str(), + "--disk", format!( "path={}", guest.disk_config.disk(DiskType::CloudInit).unwrap() @@ -1796,6 +1803,7 @@ fn _test_virtio_iommu(acpi: bool) { guest.disk_config.disk(DiskType::OperatingSystem).unwrap() ) .as_str(), + "--disk", format!( "path={},iommu=on", guest.disk_config.disk(DiskType::CloudInit).unwrap() @@ -2211,7 +2219,9 @@ mod common_parallel { .args([ "--memory-zone", "id=mem0,size=1G,hotplug_size=2G", + "--memory-zone", "id=mem1,size=1G,file=/dev/shm", + "--memory-zone", "id=mem2,size=1G,host_numa_node=0,hotplug_size=2G", ]) .args(["--kernel", kernel_path.to_str().unwrap()]) @@ -2463,11 +2473,13 @@ mod common_parallel { guest.disk_config.disk(DiskType::OperatingSystem).unwrap() ) .as_str(), + "--disk", format!( "path={}", guest.disk_config.disk(DiskType::CloudInit).unwrap() ) .as_str(), + "--disk", format!("path={test_disk_path},pci_segment=15").as_str(), ]) .capture_output() @@ -2602,11 +2614,13 @@ mod common_parallel { guest.disk_config.disk(DiskType::OperatingSystem).unwrap() ) .as_str(), + "--disk", format!( "path={}", guest.disk_config.disk(DiskType::CloudInit).unwrap() ) .as_str(), + "--disk", format!( "path={},readonly=on,direct=on,num_queues=4,_disable_io_uring={}", blk_file_path.to_str().unwrap(), @@ -2768,11 +2782,13 @@ mod common_parallel { guest.disk_config.disk(DiskType::OperatingSystem).unwrap() ) .as_str(), + "--disk", format!( "path={}", guest.disk_config.disk(DiskType::CloudInit).unwrap() ) .as_str(), + "--disk", format!("path={vhdx_path}").as_str(), ]) .default_net() @@ -2842,6 +2858,7 @@ mod common_parallel { .args([ "--disk", format!("path={},direct=on", os_path.as_path().to_str().unwrap()).as_str(), + "--disk", format!( "path={}", guest.disk_config.disk(DiskType::CloudInit).unwrap() @@ -3219,7 +3236,9 @@ mod common_parallel { .args([ "--net", 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", + "--net", "tap=mytap1,mac=fe:1f:9e:e1:60:f2,ip=192.168.4.1,mask=255.255.255.0", ]) .capture_output() @@ -3759,12 +3778,15 @@ mod common_parallel { guest.disk_config.disk(DiskType::OperatingSystem).unwrap() ) .as_str(), + "--disk", format!( "path={}", guest.disk_config.disk(DiskType::CloudInit).unwrap() ) .as_str(), + "--disk", format!("path={}", vfio_disk_path.to_str().unwrap()).as_str(), + "--disk", format!("path={},iommu=on", blk_file_path.to_str().unwrap()).as_str(), ]) .args([ @@ -3777,16 +3799,19 @@ mod common_parallel { .args([ "--net", format!("tap={},mac={}", vfio_tap0, guest.network.guest_mac).as_str(), + "--net", format!( "tap={},mac={},iommu=on", vfio_tap1, guest.network.l2_guest_mac1 ) .as_str(), + "--net", format!( "tap={},mac={},iommu=on", vfio_tap2, guest.network.l2_guest_mac2 ) .as_str(), + "--net", format!( "tap={},mac={},iommu=on", vfio_tap3, guest.network.l2_guest_mac3 @@ -4323,6 +4348,7 @@ mod common_parallel { .args([ "--net", 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", ]) .capture_output() @@ -5064,11 +5090,13 @@ mod common_parallel { guest.disk_config.disk(DiskType::OperatingSystem).unwrap() ) .as_str(), + "--disk", format!( "path={}", guest.disk_config.disk(DiskType::CloudInit).unwrap() ) .as_str(), + "--disk", format!("path={}", &loop_dev).as_str(), ]) .default_net() @@ -5633,6 +5661,7 @@ mod common_parallel { guest.disk_config.disk(DiskType::OperatingSystem).unwrap() ) .as_str(), + "--disk", cloudinit_params.as_str(), ]) .args(["--net", net_params.as_str()]) @@ -7672,7 +7701,9 @@ mod windows { .args([ "--net", 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", + "--net", "tap=mytap42,mac=fe:1f:9e:e1:60:f2,ip=192.168.4.1,mask=255.255.255.0", ]) .capture_output() @@ -7825,12 +7856,15 @@ mod vfio { guest.disk_config.disk(DiskType::OperatingSystem).unwrap() ) .as_str(), + "--disk", format!( "path={}", guest.disk_config.disk(DiskType::CloudInit).unwrap() ) .as_str(), + "--disk", format!("path={}", vfio_disk_path.to_str().unwrap()).as_str(), + "--disk", format!("path={},iommu=on", blk_file_path.to_str().unwrap()).as_str(), ]) .args([ @@ -7843,16 +7877,19 @@ mod vfio { .args([ "--net", format!("tap={},mac={}", vfio_tap0, guest.network.guest_mac).as_str(), + "--net", format!( "tap={},mac={},iommu=on", vfio_tap1, guest.network.l2_guest_mac1 ) .as_str(), + "--net", format!( "tap={},mac={},iommu=on", vfio_tap2, guest.network.l2_guest_mac2 ) .as_str(), + "--net", format!( "tap={},mac={},iommu=on", vfio_tap3, guest.network.l2_guest_mac3 @@ -8641,11 +8678,15 @@ mod live_migration { "size=0,hotplug_method=virtio-mem,shared=on", "--memory-zone", "id=mem0,size=1G,hotplug_size=4G,shared=on", + "--memory-zone", "id=mem1,size=1G,hotplug_size=4G,shared=on", + "--memory-zone", "id=mem2,size=2G,hotplug_size=4G,shared=on", "--numa", "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", + "--numa", "guest_numa_id=2,cpus=[5,10-11],distances=[0@25,1@30],memory_zones=mem2", ] } else { @@ -8654,11 +8695,15 @@ mod live_migration { "size=0,hotplug_method=virtio-mem", "--memory-zone", "id=mem0,size=1G,hotplug_size=4G", + "--memory-zone", "id=mem1,size=1G,hotplug_size=4G", + "--memory-zone", "id=mem2,size=2G,hotplug_size=4G", "--numa", "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", + "--numa", "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() ) .as_str(), + "--disk", format!( "path={}", guest.disk_config.disk(DiskType::CloudInit).unwrap() ) .as_str(), + "--disk", test_blk_params.as_str(), ]) .default_net() diff --git a/vmm/Cargo.toml b/vmm/Cargo.toml index 07ec7e046..ea2c52781 100644 --- a/vmm/Cargo.toml +++ b/vmm/Cargo.toml @@ -19,7 +19,6 @@ arc-swap = "1.5.1" arch = { path = "../arch" } bitflags = "1.3.2" block_util = { path = "../block_util" } -clap = "4.0.32" devices = { path = "../devices" } epoll = "4.3.1" event_monitor = { path = "../event_monitor" } diff --git a/vmm/src/config.rs b/vmm/src/config.rs index 4e43abce6..980bd5242 100644 --- a/vmm/src/config.rs +++ b/vmm/src/config.rs @@ -4,7 +4,6 @@ // pub use crate::vm_config::*; -use clap::ArgMatches; use option_parser::{ ByteSized, IntegerList, OptionParser, OptionParserError, StringList, Toggle, Tuple, }; @@ -381,88 +380,6 @@ pub struct VmParams<'a> { 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::("cpus").unwrap(); - let memory = args.get_one::("memory").unwrap(); - let memory_zones: Option> = args - .get_many::("memory-zone") - .map(|x| x.map(|y| y as &str).collect()); - let rng = args.get_one::("rng").unwrap(); - let serial = args.get_one::("serial").unwrap(); - let firmware = args.get_one::("firmware").map(|x| x as &str); - let kernel = args.get_one::("kernel").map(|x| x as &str); - let initramfs = args.get_one::("initramfs").map(|x| x as &str); - let cmdline = args.get_one::("cmdline").map(|x| x as &str); - let disks: Option> = args - .get_many::("disk") - .map(|x| x.map(|y| y as &str).collect()); - let net: Option> = args - .get_many::("net") - .map(|x| x.map(|y| y as &str).collect()); - let console = args.get_one::("console").unwrap(); - let balloon = args.get_one::("balloon").map(|x| x as &str); - let fs: Option> = args - .get_many::("fs") - .map(|x| x.map(|y| y as &str).collect()); - let pmem: Option> = args - .get_many::("pmem") - .map(|x| x.map(|y| y as &str).collect()); - let devices: Option> = args - .get_many::("device") - .map(|x| x.map(|y| y as &str).collect()); - let user_devices: Option> = args - .get_many::("user-device") - .map(|x| x.map(|y| y as &str).collect()); - let vdpa: Option> = args - .get_many::("vdpa") - .map(|x| x.map(|y| y as &str).collect()); - let vsock: Option<&str> = args.get_one::("vsock").map(|x| x as &str); - #[cfg(target_arch = "x86_64")] - let sgx_epc: Option> = args - .get_many::("sgx-epc") - .map(|x| x.map(|y| y as &str).collect()); - let numa: Option> = args - .get_many::("numa") - .map(|x| x.map(|y| y as &str).collect()); - let watchdog = args.get_flag("watchdog"); - let platform = args.get_one::("platform").map(|x| x as &str); - #[cfg(feature = "guest_debug")] - let gdb = args.contains_id("gdb"); - let tpm: Option<&str> = args.get_one::("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)] pub enum ParseHotplugMethodError { InvalidValue(String), @@ -827,14 +744,6 @@ impl MemoryConfig { } impl DiskConfig { - pub const SYNTAX: &'static str = "Disk parameters \ - \"path=,readonly=on|off,direct=on|off,iommu=on|off,\ - num_queues=,queue_size=,\ - vhost_user=on|off,socket=,\ - bw_size=,bw_one_time_burst=,bw_refill_time=,\ - ops_size=,ops_one_time_burst=,ops_refill_time=,\ - id=,pci_segment=\""; - pub fn parse(disk: &str) -> Result { let mut parser = OptionParser::new(); parser @@ -1007,14 +916,6 @@ impl FromStr for VhostMode { } impl NetConfig { - pub const SYNTAX: &'static str = "Network parameters \ - \"tap=,ip=,mask=,mac=,fd=,iommu=on|off,\ - num_queues=,queue_size=,id=,\ - vhost_user=,socket=,vhost_mode=client|server,\ - bw_size=,bw_one_time_burst=,bw_refill_time=,\ - ops_size=,ops_one_time_burst=,ops_refill_time=,pci_segment=\ - offload_tso=on|off,offload_ufo=on|off,offload_csum=on|off\""; - pub fn parse(net: &str) -> Result { let mut parser = OptionParser::new(); @@ -1255,10 +1156,6 @@ impl RngConfig { } impl BalloonConfig { - pub const SYNTAX: &'static str = - "Balloon parameters \"size=,deflate_on_oom=on|off,\ - free_page_reporting=on|off\""; - pub fn parse(balloon: &str) -> Result { let mut parser = OptionParser::new(); parser.add("size"); @@ -1293,10 +1190,6 @@ impl BalloonConfig { } impl FsConfig { - pub const SYNTAX: &'static str = "virtio-fs parameters \ - \"tag=,socket=,num_queues=,\ - queue_size=,id=,pci_segment=\""; - pub fn parse(fs: &str) -> Result { let mut parser = OptionParser::new(); parser @@ -1361,9 +1254,6 @@ impl FsConfig { } impl PmemConfig { - pub const SYNTAX: &'static str = "Persistent memory parameters \ - \"file=,size=,iommu=on|off,\ - discard_writes=on|off,id=,pci_segment=\""; pub fn parse(pmem: &str) -> Result { let mut parser = OptionParser::new(); parser @@ -1465,8 +1355,6 @@ impl ConsoleConfig { } impl DeviceConfig { - pub const SYNTAX: &'static str = - "Direct device assignment parameters \"path=,iommu=on|off,id=,pci_segment=\""; pub fn parse(device: &str) -> Result { let mut parser = OptionParser::new(); parser.add("path").add("id").add("iommu").add("pci_segment"); @@ -1513,8 +1401,6 @@ impl DeviceConfig { } impl UserDeviceConfig { - pub const SYNTAX: &'static str = - "Userspace device socket=,id=,pci_segment=\""; pub fn parse(user_device: &str) -> Result { let mut parser = OptionParser::new(); parser.add("socket").add("id").add("pci_segment"); @@ -1557,9 +1443,6 @@ impl UserDeviceConfig { } impl VdpaConfig { - pub const SYNTAX: &'static str = "vDPA device \ - \"path=,num_queues=,iommu=on|off,\ - id=,pci_segment=\""; pub fn parse(vdpa: &str) -> Result { let mut parser = OptionParser::new(); parser @@ -1616,8 +1499,6 @@ impl VdpaConfig { } impl VsockConfig { - pub const SYNTAX: &'static str = "Virtio VSOCK parameters \ - \"cid=,socket=,iommu=on|off,id=,pci_segment=\""; pub fn parse(vsock: &str) -> Result { let mut parser = OptionParser::new(); parser @@ -1675,8 +1556,6 @@ impl VsockConfig { #[cfg(target_arch = "x86_64")] impl SgxEpcConfig { - pub const SYNTAX: &'static str = "SGX EPC parameters \ - \"id=,size=,prefault=on|off\""; pub fn parse(sgx_epc: &str) -> Result { let mut parser = OptionParser::new(); parser.add("id").add("size").add("prefault"); @@ -1699,9 +1578,6 @@ impl SgxEpcConfig { } impl NumaConfig { - pub const SYNTAX: &'static str = "Settings related to a given NUMA node \ - \"guest_numa_id=,cpus=,distances=,\ - memory_zones=,sgx_epc_sections=\""; pub fn parse(numa: &str) -> Result { let mut parser = OptionParser::new(); parser @@ -1760,10 +1636,6 @@ pub struct RestoreConfig { } impl RestoreConfig { - pub const SYNTAX: &'static str = "Restore from a VM snapshot. \ - \nRestore parameters \"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 { let mut parser = OptionParser::new(); parser.add("source_url").add("prefault"); @@ -1787,8 +1659,6 @@ impl RestoreConfig { } impl TpmConfig { - pub const SYNTAX: &'static str = "TPM device \ - \"(UNIX Domain Socket from swtpm) socket=\""; pub fn parse(tpm: &str) -> Result { let mut parser = OptionParser::new(); parser.add("socket");