2019-02-21 16:39:34 +00:00
|
|
|
// Copyright © 2019 Intel Corporation
|
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
//
|
|
|
|
|
2019-02-22 16:05:02 +00:00
|
|
|
extern crate vmm;
|
2019-09-25 13:01:49 +00:00
|
|
|
extern crate vmm_sys_util;
|
2019-02-22 16:05:02 +00:00
|
|
|
|
2020-02-13 05:11:03 +00:00
|
|
|
#[macro_use(crate_authors)]
|
2019-02-21 16:39:34 +00:00
|
|
|
extern crate clap;
|
|
|
|
|
2020-01-21 15:28:42 +00:00
|
|
|
use clap::{App, Arg, ArgGroup, ArgMatches};
|
2019-09-25 13:01:49 +00:00
|
|
|
use libc::EFD_NONBLOCK;
|
2019-08-07 11:19:21 +00:00
|
|
|
use log::LevelFilter;
|
2019-09-25 13:01:49 +00:00
|
|
|
use std::sync::mpsc::channel;
|
2019-09-24 14:00:00 +00:00
|
|
|
use std::sync::{Arc, Mutex};
|
2019-09-19 07:31:24 +00:00
|
|
|
use std::{env, process};
|
2020-01-21 15:35:22 +00:00
|
|
|
use vhost_user_block::start_block_backend;
|
2020-01-21 15:28:42 +00:00
|
|
|
use vhost_user_net::start_net_backend;
|
2019-05-23 19:48:05 +00:00
|
|
|
use vmm::config;
|
2019-09-25 13:01:49 +00:00
|
|
|
use vmm_sys_util::eventfd::EventFd;
|
2019-03-07 13:56:43 +00:00
|
|
|
|
2019-08-07 11:19:21 +00:00
|
|
|
struct Logger {
|
2019-08-15 15:41:40 +00:00
|
|
|
output: Mutex<Box<dyn std::io::Write + Send>>,
|
2019-08-07 11:19:21 +00:00
|
|
|
start: std::time::Instant,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl log::Log for Logger {
|
|
|
|
fn enabled(&self, _metadata: &log::Metadata) -> bool {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
|
|
|
fn log(&self, record: &log::Record) {
|
|
|
|
if !self.enabled(record.metadata()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let now = std::time::Instant::now();
|
|
|
|
let duration = now.duration_since(self.start);
|
|
|
|
|
2020-02-04 11:16:31 +00:00
|
|
|
if record.file().is_some() && record.line().is_some() {
|
2019-08-07 11:19:21 +00:00
|
|
|
writeln!(
|
|
|
|
*(*(self.output.lock().unwrap())),
|
|
|
|
"cloud-hypervisor: {:?}: {}:{}:{} -- {}",
|
|
|
|
duration,
|
|
|
|
record.level(),
|
|
|
|
record.file().unwrap(),
|
|
|
|
record.line().unwrap(),
|
|
|
|
record.args()
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
writeln!(
|
|
|
|
*(*(self.output.lock().unwrap())),
|
|
|
|
"cloud-hypervisor: {:?}: {}:{} -- {}",
|
|
|
|
duration,
|
|
|
|
record.level(),
|
|
|
|
record.target(),
|
|
|
|
record.args()
|
|
|
|
)
|
2020-02-04 14:52:25 +00:00
|
|
|
}
|
|
|
|
.ok();
|
2019-08-07 11:19:21 +00:00
|
|
|
}
|
|
|
|
fn flush(&self) {}
|
|
|
|
}
|
|
|
|
|
2019-12-13 07:34:39 +00:00
|
|
|
fn prepare_default_values() -> (String, String, String) {
|
|
|
|
let default_vcpus = format! {"boot={}", config::DEFAULT_VCPUS};
|
|
|
|
let default_memory = format! {"size={}M", config::DEFAULT_MEMORY_MB};
|
|
|
|
let default_rng = format! {"src={}", config::DEFAULT_RNG_SOURCE};
|
|
|
|
|
|
|
|
(default_vcpus, default_memory, default_rng)
|
|
|
|
}
|
|
|
|
|
2019-12-12 16:57:11 +00:00
|
|
|
fn create_app<'a, 'b>(
|
|
|
|
default_vcpus: &'a str,
|
|
|
|
default_memory: &'a str,
|
|
|
|
default_rng: &'a str,
|
|
|
|
api_server_path: &'a str,
|
|
|
|
) -> App<'a, 'b> {
|
|
|
|
App::new("cloud-hypervisor")
|
2020-02-13 05:11:03 +00:00
|
|
|
// 'BUILT_VERSION' is set by the build script 'build.rs' at
|
|
|
|
// compile time
|
|
|
|
.version(env!("BUILT_VERSION"))
|
2019-02-21 16:39:34 +00:00
|
|
|
.author(crate_authors!())
|
|
|
|
.about("Launch a cloud-hypervisor VMM.")
|
2019-09-19 06:41:00 +00:00
|
|
|
.group(ArgGroup::with_name("vm-config").multiple(true))
|
|
|
|
.group(ArgGroup::with_name("vmm-config").multiple(true))
|
2020-01-22 15:00:39 +00:00
|
|
|
.group(ArgGroup::with_name("logging").multiple(true))
|
2019-05-23 19:48:05 +00:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("cpus")
|
|
|
|
.long("cpus")
|
|
|
|
.help("Number of virtual CPUs")
|
2019-09-27 09:40:50 +00:00
|
|
|
.default_value(&default_vcpus)
|
2019-09-19 06:41:00 +00:00
|
|
|
.group("vm-config"),
|
2019-05-23 19:48:05 +00:00
|
|
|
)
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("memory")
|
|
|
|
.long("memory")
|
2019-05-24 19:21:23 +00:00
|
|
|
.help(
|
2020-03-12 16:05:28 +00:00
|
|
|
"Memory parameters \
|
|
|
|
\"size=<guest_memory_size>,file=<backing_file_path>,mergeable=on|off,\
|
2020-01-07 11:25:55 +00:00
|
|
|
hotplug_size=<hotpluggable_memory_size>\"",
|
2019-05-24 19:21:23 +00:00
|
|
|
)
|
2019-09-27 09:40:50 +00:00
|
|
|
.default_value(&default_memory)
|
2019-09-19 06:41:00 +00:00
|
|
|
.group("vm-config"),
|
2019-05-23 19:48:05 +00:00
|
|
|
)
|
2019-02-21 16:39:34 +00:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("kernel")
|
|
|
|
.long("kernel")
|
|
|
|
.help("Path to kernel image (vmlinux)")
|
2019-09-19 06:41:00 +00:00
|
|
|
.takes_value(true)
|
|
|
|
.group("vm-config"),
|
2019-02-21 16:39:34 +00:00
|
|
|
)
|
2019-05-06 20:24:57 +00:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("cmdline")
|
|
|
|
.long("cmdline")
|
|
|
|
.help("Kernel command line")
|
2019-09-19 06:41:00 +00:00
|
|
|
.takes_value(true)
|
|
|
|
.group("vm-config"),
|
2019-05-06 20:24:57 +00:00
|
|
|
)
|
2019-05-06 19:15:44 +00:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("disk")
|
|
|
|
.long("disk")
|
2019-10-02 21:01:36 +00:00
|
|
|
.help(
|
2020-03-12 16:05:28 +00:00
|
|
|
"Disk parameters \
|
|
|
|
\"path=<disk_image_path>,readonly=on|off,iommu=on|off,\
|
|
|
|
num_queues=<number_of_queues>,queue_size=<size_of_each_queue>,\
|
|
|
|
vhost_user=<vhost_user_enable>,socket=<vhost_user_socket_path>,\
|
2020-01-28 11:43:15 +00:00
|
|
|
wce=<true|false, default true>\"",
|
2019-10-02 21:01:36 +00:00
|
|
|
)
|
2019-05-22 13:56:22 +00:00
|
|
|
.takes_value(true)
|
2019-09-19 06:41:00 +00:00
|
|
|
.min_values(1)
|
|
|
|
.group("vm-config"),
|
2019-05-06 19:15:44 +00:00
|
|
|
)
|
2019-05-09 15:01:42 +00:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("net")
|
|
|
|
.long("net")
|
2019-05-23 19:48:05 +00:00
|
|
|
.help(
|
2020-03-12 16:05:28 +00:00
|
|
|
"Network parameters \
|
|
|
|
\"tap=<if_name>,ip=<ip_addr>,mask=<net_mask>,mac=<mac_addr>,iommu=on|off,\
|
|
|
|
num_queues=<number_of_queues>,queue_size=<size_of_each_queue>,\
|
2020-01-27 15:14:07 +00:00
|
|
|
vhost_user=<vhost_user_enable>,socket=<vhost_user_socket_path>\"",
|
2019-05-23 19:48:05 +00:00
|
|
|
)
|
2019-07-08 22:31:13 +00:00
|
|
|
.takes_value(true)
|
2019-09-19 06:41:00 +00:00
|
|
|
.min_values(1)
|
|
|
|
.group("vm-config"),
|
2019-03-11 16:48:35 +00:00
|
|
|
)
|
2019-05-09 05:01:48 +00:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("rng")
|
|
|
|
.long("rng")
|
2019-10-04 18:18:49 +00:00
|
|
|
.help(
|
2020-03-12 16:05:28 +00:00
|
|
|
"Random number generator parameters \"src=<entropy_source_path>,iommu=on|off\"",
|
2019-10-04 18:18:49 +00:00
|
|
|
)
|
|
|
|
.default_value(&default_rng)
|
2019-09-19 06:41:00 +00:00
|
|
|
.group("vm-config"),
|
2019-05-09 05:01:48 +00:00
|
|
|
)
|
2019-05-22 20:06:49 +00:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("fs")
|
|
|
|
.long("fs")
|
|
|
|
.help(
|
2020-03-12 16:05:28 +00:00
|
|
|
"virtio-fs parameters \
|
|
|
|
\"tag=<tag_name>,sock=<socket_path>,num_queues=<number_of_queues>,\
|
|
|
|
queue_size=<size_of_each_queue>,dax=on|off,cache_size=<DAX cache size: \
|
|
|
|
default 8Gib>\"",
|
2019-05-22 20:06:49 +00:00
|
|
|
)
|
2019-06-27 16:14:11 +00:00
|
|
|
.takes_value(true)
|
2019-09-19 06:41:00 +00:00
|
|
|
.min_values(1)
|
|
|
|
.group("vm-config"),
|
2019-05-22 20:06:49 +00:00
|
|
|
)
|
2019-06-19 20:48:37 +00:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("pmem")
|
|
|
|
.long("pmem")
|
|
|
|
.help(
|
2020-03-12 16:05:28 +00:00
|
|
|
"Persistent memory parameters \
|
|
|
|
\"file=<backing_file_path>,size=<persistent_memory_size>,iommu=on|off,\
|
2020-03-19 10:13:53 +00:00
|
|
|
mergeable=on|off,discard_writes=on|off,\"",
|
2019-06-19 20:48:37 +00:00
|
|
|
)
|
2019-06-28 08:45:58 +00:00
|
|
|
.takes_value(true)
|
2019-09-19 06:41:00 +00:00
|
|
|
.min_values(1)
|
|
|
|
.group("vm-config"),
|
2019-06-19 20:48:37 +00:00
|
|
|
)
|
2019-07-10 14:41:46 +00:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("serial")
|
|
|
|
.long("serial")
|
2019-08-09 07:56:10 +00:00
|
|
|
.help("Control serial port: off|null|tty|file=/path/to/a/file")
|
2019-09-19 06:41:00 +00:00
|
|
|
.default_value("null")
|
|
|
|
.group("vm-config"),
|
2019-07-22 19:29:02 +00:00
|
|
|
)
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("console")
|
|
|
|
.long("console")
|
2019-10-04 19:01:32 +00:00
|
|
|
.help(
|
2020-03-12 16:05:28 +00:00
|
|
|
"Control (virtio) console: \"off|null|tty|file=/path/to/a/file,iommu=on|off\"",
|
2019-10-04 19:01:32 +00:00
|
|
|
)
|
2019-09-19 06:41:00 +00:00
|
|
|
.default_value("tty")
|
|
|
|
.group("vm-config"),
|
2019-07-10 14:41:46 +00:00
|
|
|
)
|
2019-07-15 09:42:40 +00:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("device")
|
|
|
|
.long("device")
|
2020-03-12 16:20:31 +00:00
|
|
|
.help(config::DeviceConfig::SYNTAX)
|
2019-07-15 09:42:40 +00:00
|
|
|
.takes_value(true)
|
2019-09-19 06:41:00 +00:00
|
|
|
.min_values(1)
|
|
|
|
.group("vm-config"),
|
2019-07-15 09:42:40 +00:00
|
|
|
)
|
2019-09-04 17:39:17 +00:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("vsock")
|
|
|
|
.long("vsock")
|
|
|
|
.help(
|
2020-03-12 16:05:28 +00:00
|
|
|
"Virtio VSOCK parameters \"cid=<context_id>,sock=<socket_path>,iommu=on|off\"",
|
2019-09-04 17:39:17 +00:00
|
|
|
)
|
|
|
|
.takes_value(true)
|
2019-09-19 06:41:00 +00:00
|
|
|
.min_values(1)
|
|
|
|
.group("vm-config"),
|
2019-09-04 17:39:17 +00:00
|
|
|
)
|
2019-08-07 11:19:21 +00:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("v")
|
|
|
|
.short("v")
|
|
|
|
.multiple(true)
|
2019-09-19 06:41:00 +00:00
|
|
|
.help("Sets the level of debugging output")
|
2020-01-22 15:00:39 +00:00
|
|
|
.group("logging"),
|
2019-08-07 11:19:21 +00:00
|
|
|
)
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("log-file")
|
|
|
|
.long("log-file")
|
|
|
|
.help("Log file. Standard error is used if not specified")
|
|
|
|
.takes_value(true)
|
2019-09-19 06:41:00 +00:00
|
|
|
.min_values(1)
|
2020-01-22 15:00:39 +00:00
|
|
|
.group("logging"),
|
2019-08-07 11:19:21 +00:00
|
|
|
)
|
2019-09-19 07:31:24 +00:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("api-socket")
|
|
|
|
.long("api-socket")
|
|
|
|
.help("HTTP API socket path (UNIX domain socket).")
|
|
|
|
.takes_value(true)
|
|
|
|
.min_values(1)
|
|
|
|
.default_value(&api_server_path)
|
|
|
|
.group("vmm-config"),
|
|
|
|
)
|
2020-01-21 15:28:42 +00:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("net-backend")
|
|
|
|
.long("net-backend")
|
|
|
|
.help(
|
2020-03-12 16:05:28 +00:00
|
|
|
"vhost-user-net backend parameters \
|
|
|
|
\"ip=<ip_addr>,mask=<net_mask>,sock=<socket_path>,\
|
|
|
|
num_queues=<number_of_queues>,queue_size=<size_of_each_queue>,tap=<if_name>\"",
|
2020-01-21 15:28:42 +00:00
|
|
|
)
|
|
|
|
.takes_value(true)
|
2020-01-22 15:44:02 +00:00
|
|
|
.conflicts_with_all(&["block-backend", "kernel"])
|
2020-01-21 15:28:42 +00:00
|
|
|
.min_values(1),
|
|
|
|
)
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("block-backend")
|
|
|
|
.long("block-backend")
|
|
|
|
.help(
|
2020-03-12 16:05:28 +00:00
|
|
|
"vhost-user-block backend parameters \
|
|
|
|
\"image=<image_path>,sock=<socket_path>,num_queues=<number_of_queues>,\
|
vhost_user_block: Implement optional static polling
Actively polling the virtqueue significantly reduces the latency of
each I/O operation, at the expense of using more CPU time. This
features is specially useful when using low-latency devices (SSD,
NVMe) as the backend.
This change implements static polling. When a request arrives after
being idle, vhost_user_block will keep checking the virtqueue for new
requests, until POLL_QUEUE_US (50us) has passed without finding one.
POLL_QUEUE_US is defined to be 50us, based on the current latency of
enterprise SSDs (< 30us) and the overhead of the emulation.
This feature is enabled by default, and can be disabled by using the
"poll_queue" parameter of "block-backend".
This is a test using null_blk as a backend for the image, with the
following parameters:
- null_blk gb=20 nr_devices=1 irqmode=2 completion_nsec=0 no_sched=1
With "poll_queue=false":
fio --ioengine=sync --bs=4k --rw randread --name randread --direct=1
--filename=/dev/vdb --time_based --runtime=10
randread: (g=0): rw=randread, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=sync, iodepth=1
fio-3.14
Starting 1 process
Jobs: 1 (f=1): [r(1)][100.0%][r=169MiB/s][r=43.2k IOPS][eta 00m:00s]
randread: (groupid=0, jobs=1): err= 0: pid=433: Tue Feb 18 11:12:59 2020
read: IOPS=43.2k, BW=169MiB/s (177MB/s)(1688MiB/10001msec)
clat (usec): min=17, max=836, avg=21.64, stdev= 3.81
lat (usec): min=17, max=836, avg=21.77, stdev= 3.81
clat percentiles (nsec):
| 1.00th=[19328], 5.00th=[19840], 10.00th=[20352], 20.00th=[21120],
| 30.00th=[21376], 40.00th=[21376], 50.00th=[21376], 60.00th=[21632],
| 70.00th=[21632], 80.00th=[21888], 90.00th=[22144], 95.00th=[22912],
| 99.00th=[28544], 99.50th=[30336], 99.90th=[39168], 99.95th=[42752],
| 99.99th=[71168]
bw ( KiB/s): min=168440, max=188496, per=100.00%, avg=172912.00, stdev=3975.63, samples=19
iops : min=42110, max=47124, avg=43228.00, stdev=993.91, samples=19
lat (usec) : 20=5.90%, 50=94.08%, 100=0.02%, 250=0.01%, 500=0.01%
lat (usec) : 750=0.01%, 1000=0.01%
cpu : usr=10.35%, sys=25.82%, ctx=432417, majf=0, minf=10
IO depths : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0%
submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
issued rwts: total=432220,0,0,0 short=0,0,0,0 dropped=0,0,0,0
latency : target=0, window=0, percentile=100.00%, depth=1
Run status group 0 (all jobs):
READ: bw=169MiB/s (177MB/s), 169MiB/s-169MiB/s (177MB/s-177MB/s), io=1688MiB (1770MB), run=10001-10001msec
Disk stats (read/write):
vdb: ios=427867/0, merge=0/0, ticks=7346/0, in_queue=0, util=99.04%
With "poll_queue=true" (default):
fio --ioengine=sync --bs=4k --rw randread --name randread --direct=1
--filename=/dev/vdb --time_based --runtime=10
randread: (g=0): rw=randread, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=sync, iodepth=1
fio-3.14
Starting 1 process
Jobs: 1 (f=1): [r(1)][100.0%][r=260MiB/s][r=66.7k IOPS][eta 00m:00s]
randread: (groupid=0, jobs=1): err= 0: pid=422: Tue Feb 18 11:14:47 2020
read: IOPS=68.5k, BW=267MiB/s (280MB/s)(2674MiB/10001msec)
clat (usec): min=10, max=966, avg=13.60, stdev= 3.49
lat (usec): min=10, max=966, avg=13.70, stdev= 3.50
clat percentiles (nsec):
| 1.00th=[11200], 5.00th=[11968], 10.00th=[11968], 20.00th=[12224],
| 30.00th=[12992], 40.00th=[13504], 50.00th=[13760], 60.00th=[13888],
| 70.00th=[14016], 80.00th=[14144], 90.00th=[14272], 95.00th=[14656],
| 99.00th=[20352], 99.50th=[23936], 99.90th=[35072], 99.95th=[36096],
| 99.99th=[47872]
bw ( KiB/s): min=265456, max=296456, per=100.00%, avg=274229.05, stdev=13048.14, samples=19
iops : min=66364, max=74114, avg=68557.26, stdev=3262.03, samples=19
lat (usec) : 20=98.84%, 50=1.15%, 100=0.01%, 250=0.01%, 500=0.01%
lat (usec) : 750=0.01%, 1000=0.01%
cpu : usr=8.24%, sys=21.15%, ctx=684669, majf=0, minf=10
IO depths : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0%
submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
issued rwts: total=684611,0,0,0 short=0,0,0,0 dropped=0,0,0,0
latency : target=0, window=0, percentile=100.00%, depth=1
Run status group 0 (all jobs):
READ: bw=267MiB/s (280MB/s), 267MiB/s-267MiB/s (280MB/s-280MB/s), io=2674MiB (2804MB), run=10001-10001msec
Disk stats (read/write):
vdb: ios=677855/0, merge=0/0, ticks=7026/0, in_queue=0, util=99.04%
Signed-off-by: Sergio Lopez <slp@redhat.com>
2020-02-17 13:52:25 +00:00
|
|
|
readonly=true|false,direct=true|false,poll_queue=true|false\"",
|
2020-01-21 15:28:42 +00:00
|
|
|
)
|
|
|
|
.takes_value(true)
|
2020-01-22 15:44:02 +00:00
|
|
|
.conflicts_with_all(&["net-backend", "kernel"])
|
2020-01-21 15:28:42 +00:00
|
|
|
.min_values(1),
|
|
|
|
)
|
2019-12-12 16:57:11 +00:00
|
|
|
}
|
|
|
|
|
2020-01-21 15:28:42 +00:00
|
|
|
fn start_vmm(cmd_arguments: ArgMatches) {
|
2020-01-20 14:38:03 +00:00
|
|
|
let vm_params = config::VmParams::from_arg_matches(&cmd_arguments);
|
2019-12-12 17:12:42 +00:00
|
|
|
let vm_config = match config::VmConfig::parse(vm_params) {
|
2019-05-23 19:48:05 +00:00
|
|
|
Ok(config) => config,
|
|
|
|
Err(e) => {
|
|
|
|
println!("Failed parsing parameters {:?}", e);
|
|
|
|
process::exit(1);
|
|
|
|
}
|
2019-05-09 05:01:48 +00:00
|
|
|
};
|
|
|
|
|
2019-09-19 07:31:24 +00:00
|
|
|
let api_socket_path = cmd_arguments
|
|
|
|
.value_of("api-socket")
|
|
|
|
.expect("Missing argument: api-socket");
|
|
|
|
|
2019-05-10 08:02:07 +00:00
|
|
|
println!(
|
2020-03-12 16:05:28 +00:00
|
|
|
"Cloud Hypervisor Guest\n\tAPI server: {}\n\tvCPUs: {}\n\tMemory: {} MB\n\tKernel: \
|
|
|
|
{:?}\n\tKernel cmdline: {}\n\tDisk(s): {:?}",
|
2019-09-19 07:31:24 +00:00
|
|
|
api_socket_path,
|
2019-11-25 14:18:03 +00:00
|
|
|
vm_config.cpus.boot_vcpus,
|
2019-07-09 03:13:02 +00:00
|
|
|
vm_config.memory.size >> 20,
|
2019-09-19 07:52:55 +00:00
|
|
|
vm_config.kernel,
|
2019-05-23 19:48:05 +00:00
|
|
|
vm_config.cmdline.args.as_str(),
|
|
|
|
vm_config.disks,
|
2019-05-10 08:02:07 +00:00
|
|
|
);
|
2019-03-07 13:56:43 +00:00
|
|
|
|
2019-09-25 13:01:49 +00:00
|
|
|
let (api_request_sender, api_request_receiver) = channel();
|
|
|
|
let api_evt = EventFd::new(EFD_NONBLOCK).expect("Cannot create API EventFd");
|
|
|
|
|
2019-09-26 16:19:00 +00:00
|
|
|
let http_sender = api_request_sender.clone();
|
|
|
|
let vmm_thread = match vmm::start_vmm_thread(
|
2019-11-21 18:32:39 +00:00
|
|
|
env!("CARGO_PKG_VERSION").to_string(),
|
2019-09-26 16:19:00 +00:00
|
|
|
api_socket_path,
|
|
|
|
api_evt.try_clone().unwrap(),
|
|
|
|
http_sender,
|
|
|
|
api_request_receiver,
|
|
|
|
) {
|
2019-09-25 13:01:49 +00:00
|
|
|
Ok(t) => t,
|
|
|
|
Err(e) => {
|
|
|
|
println!("Failed spawning the VMM thread {:?}", e);
|
|
|
|
process::exit(1);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-09-19 07:52:55 +00:00
|
|
|
if cmd_arguments.is_present("vm-config") && vm_config.valid() {
|
2019-09-30 09:53:49 +00:00
|
|
|
// Create and boot the VM based off the VM config we just built.
|
2019-09-19 07:52:55 +00:00
|
|
|
let sender = api_request_sender.clone();
|
2019-09-26 17:00:43 +00:00
|
|
|
vmm::api::vm_create(
|
2019-09-19 07:52:55 +00:00
|
|
|
api_evt.try_clone().unwrap(),
|
|
|
|
api_request_sender,
|
2019-12-05 14:50:38 +00:00
|
|
|
Arc::new(Mutex::new(vm_config)),
|
2019-09-19 07:52:55 +00:00
|
|
|
)
|
|
|
|
.expect("Could not create the VM");
|
2019-09-30 09:53:49 +00:00
|
|
|
vmm::api::vm_boot(api_evt.try_clone().unwrap(), sender).expect("Could not boot the VM");
|
2019-09-19 07:52:55 +00:00
|
|
|
}
|
2019-09-25 13:01:49 +00:00
|
|
|
|
|
|
|
match vmm_thread.join() {
|
|
|
|
Ok(res) => match res {
|
|
|
|
Ok(_) => (),
|
|
|
|
Err(e) => {
|
|
|
|
println!("VMM thread failed {:?}", e);
|
|
|
|
process::exit(1);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
Err(e) => {
|
|
|
|
println!("Could not joing VMM thread {:?}", e);
|
|
|
|
process::exit(1);
|
|
|
|
}
|
2019-05-10 08:46:27 +00:00
|
|
|
}
|
2019-02-21 16:04:44 +00:00
|
|
|
}
|
2019-05-23 15:45:13 +00:00
|
|
|
|
2020-01-21 14:59:37 +00:00
|
|
|
fn main() {
|
|
|
|
let pid = unsafe { libc::getpid() };
|
|
|
|
let uid = unsafe { libc::getuid() };
|
|
|
|
|
|
|
|
let mut api_server_path = format! {"/run/user/{}/cloud-hypervisor.{}", uid, pid};
|
|
|
|
if uid == 0 {
|
|
|
|
// If we're running as root, we try to get the real user ID if we've been sudo'ed
|
|
|
|
// or else create our socket directly under /run.
|
|
|
|
let key = "SUDO_UID";
|
|
|
|
match env::var(key) {
|
|
|
|
Ok(sudo_uid) => {
|
|
|
|
api_server_path = format! {"/run/user/{}/cloud-hypervisor.{}", sudo_uid, pid}
|
|
|
|
}
|
|
|
|
Err(_) => api_server_path = format! {"/run/cloud-hypervisor.{}", pid},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let (default_vcpus, default_memory, default_rng) = prepare_default_values();
|
|
|
|
|
|
|
|
let cmd_arguments = create_app(
|
|
|
|
&default_vcpus,
|
|
|
|
&default_memory,
|
|
|
|
&default_rng,
|
|
|
|
&api_server_path,
|
|
|
|
)
|
|
|
|
.get_matches();
|
|
|
|
|
|
|
|
let log_level = match cmd_arguments.occurrences_of("v") {
|
|
|
|
0 => LevelFilter::Error,
|
|
|
|
1 => LevelFilter::Warn,
|
|
|
|
2 => LevelFilter::Info,
|
|
|
|
3 => LevelFilter::Debug,
|
|
|
|
_ => LevelFilter::Trace,
|
|
|
|
};
|
|
|
|
|
|
|
|
let log_file: Box<dyn std::io::Write + Send> =
|
|
|
|
if let Some(file) = cmd_arguments.value_of("log-file") {
|
|
|
|
Box::new(
|
|
|
|
std::fs::File::create(std::path::Path::new(file)).expect("Error creating log file"),
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
Box::new(std::io::stderr())
|
|
|
|
};
|
|
|
|
|
|
|
|
log::set_boxed_logger(Box::new(Logger {
|
|
|
|
output: Mutex::new(log_file),
|
|
|
|
start: std::time::Instant::now(),
|
|
|
|
}))
|
|
|
|
.map(|()| log::set_max_level(log_level))
|
|
|
|
.expect("Expected to be able to setup logger");
|
|
|
|
|
2020-01-21 15:28:42 +00:00
|
|
|
if let Some(backend_command) = cmd_arguments.value_of("net-backend") {
|
|
|
|
start_net_backend(backend_command);
|
|
|
|
} else if let Some(backend_command) = cmd_arguments.value_of("block-backend") {
|
2020-01-21 15:35:22 +00:00
|
|
|
start_block_backend(backend_command);
|
2020-01-21 15:28:42 +00:00
|
|
|
} else {
|
|
|
|
start_vmm(cmd_arguments);
|
|
|
|
}
|
2020-01-21 14:59:37 +00:00
|
|
|
}
|
|
|
|
|
2019-05-23 15:45:13 +00:00
|
|
|
#[cfg(test)]
|
2019-06-04 15:24:39 +00:00
|
|
|
#[macro_use]
|
|
|
|
extern crate credibility;
|
2019-05-23 15:45:13 +00:00
|
|
|
|
2019-12-12 17:35:33 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod unit_tests {
|
|
|
|
use crate::{create_app, prepare_default_values};
|
|
|
|
use std::path::PathBuf;
|
|
|
|
use vmm::config::{
|
|
|
|
CmdlineConfig, ConsoleConfig, ConsoleOutputMode, CpusConfig, MemoryConfig, RngConfig,
|
|
|
|
VmConfig, VmParams,
|
|
|
|
};
|
|
|
|
|
|
|
|
fn get_vm_config_from_vec(args: &[&str]) -> VmConfig {
|
|
|
|
let (default_vcpus, default_memory, default_rng) = prepare_default_values();
|
|
|
|
let api_server_path = "";
|
|
|
|
|
|
|
|
let cmd_arguments = create_app(
|
|
|
|
&default_vcpus,
|
|
|
|
&default_memory,
|
|
|
|
&default_rng,
|
|
|
|
&api_server_path,
|
|
|
|
)
|
|
|
|
.get_matches_from(args);
|
|
|
|
|
|
|
|
let vm_params = VmParams::from_arg_matches(&cmd_arguments);
|
|
|
|
|
|
|
|
VmConfig::parse(vm_params).unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn compare_vm_config_cli_vs_json(
|
|
|
|
cli: &[&str],
|
|
|
|
openapi: &str,
|
|
|
|
equal: bool,
|
|
|
|
) -> (VmConfig, VmConfig) {
|
|
|
|
let cli_vm_config = get_vm_config_from_vec(cli);
|
|
|
|
let openapi_vm_config: VmConfig = serde_json::from_str(openapi).unwrap();
|
|
|
|
|
|
|
|
test_block!(tb, "", {
|
|
|
|
if equal {
|
|
|
|
aver_eq!(tb, cli_vm_config, openapi_vm_config);
|
|
|
|
} else {
|
|
|
|
aver_ne!(tb, cli_vm_config, openapi_vm_config);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
});
|
|
|
|
|
|
|
|
(cli_vm_config, openapi_vm_config)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_valid_vm_config_default() {
|
|
|
|
let cli = vec!["cloud-hypervisor"];
|
|
|
|
let openapi = r#"{}"#;
|
|
|
|
|
|
|
|
// First we check we get identical VmConfig structures.
|
|
|
|
let (result_vm_config, _) = compare_vm_config_cli_vs_json(&cli, openapi, true);
|
|
|
|
|
|
|
|
// As a second step, we validate all the default values.
|
|
|
|
test_block!(tb, "", {
|
|
|
|
let expected_vm_config = VmConfig {
|
|
|
|
cpus: CpusConfig {
|
|
|
|
boot_vcpus: 1,
|
|
|
|
max_vcpus: 1,
|
|
|
|
},
|
|
|
|
memory: MemoryConfig {
|
|
|
|
size: 536_870_912,
|
|
|
|
file: None,
|
|
|
|
mergeable: false,
|
2020-01-07 11:25:55 +00:00
|
|
|
hotplug_size: None,
|
2019-12-12 17:35:33 +00:00
|
|
|
},
|
|
|
|
kernel: None,
|
|
|
|
cmdline: CmdlineConfig {
|
|
|
|
args: String::from(""),
|
|
|
|
},
|
|
|
|
disks: None,
|
|
|
|
net: None,
|
|
|
|
rng: RngConfig {
|
|
|
|
src: PathBuf::from("/dev/urandom"),
|
|
|
|
iommu: false,
|
|
|
|
},
|
|
|
|
fs: None,
|
|
|
|
pmem: None,
|
|
|
|
serial: ConsoleConfig {
|
|
|
|
file: None,
|
|
|
|
mode: ConsoleOutputMode::Null,
|
|
|
|
iommu: false,
|
|
|
|
},
|
|
|
|
console: ConsoleConfig {
|
|
|
|
file: None,
|
|
|
|
mode: ConsoleOutputMode::Tty,
|
|
|
|
iommu: false,
|
|
|
|
},
|
|
|
|
devices: None,
|
|
|
|
vsock: None,
|
|
|
|
iommu: false,
|
|
|
|
};
|
|
|
|
|
|
|
|
aver_eq!(tb, expected_vm_config, result_vm_config);
|
|
|
|
Ok(())
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_valid_vm_config_cpus() {
|
|
|
|
vec![
|
|
|
|
(
|
|
|
|
vec!["cloud-hypervisor", "--cpus", "boot=1"],
|
|
|
|
r#"{
|
|
|
|
"cpus": {"boot_vcpus": 1, "max_vcpus": 1}
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec!["cloud-hypervisor", "--cpus", "boot=1,max=3"],
|
|
|
|
r#"{
|
|
|
|
"cpus": {"boot_vcpus": 1, "max_vcpus": 3}
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec!["cloud-hypervisor", "--cpus", "boot=2,max=4"],
|
|
|
|
r#"{
|
|
|
|
"cpus": {"boot_vcpus": 1, "max_vcpus": 3}
|
|
|
|
}"#,
|
|
|
|
false,
|
|
|
|
),
|
|
|
|
]
|
|
|
|
.iter()
|
|
|
|
.for_each(|(cli, openapi, equal)| {
|
|
|
|
compare_vm_config_cli_vs_json(cli, openapi, *equal);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_valid_vm_config_memory() {
|
|
|
|
vec![
|
|
|
|
(
|
|
|
|
vec!["cloud-hypervisor", "--memory", "size=1073741824"],
|
|
|
|
r#"{
|
|
|
|
"memory": {"size": 1073741824}
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec!["cloud-hypervisor", "--memory", "size=1G"],
|
|
|
|
r#"{
|
|
|
|
"memory": {"size": 1073741824}
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--memory",
|
|
|
|
"size=1G,file=/path/to/shared/file",
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"memory": {"size": 1073741824, "file": "/path/to/shared/file"}
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec!["cloud-hypervisor", "--memory", "size=1G,mergeable=on"],
|
|
|
|
r#"{
|
|
|
|
"memory": {"size": 1073741824, "mergeable": true}
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec!["cloud-hypervisor", "--memory", "size=1G,mergeable=off"],
|
|
|
|
r#"{
|
|
|
|
"memory": {"size": 1073741824, "mergeable": false}
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec!["cloud-hypervisor", "--memory", "size=1G,mergeable=on"],
|
|
|
|
r#"{
|
|
|
|
"memory": {"size": 1073741824, "mergeable": false}
|
|
|
|
}"#,
|
|
|
|
false,
|
|
|
|
),
|
|
|
|
]
|
|
|
|
.iter()
|
|
|
|
.for_each(|(cli, openapi, equal)| {
|
|
|
|
compare_vm_config_cli_vs_json(cli, openapi, *equal);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_valid_vm_config_kernel() {
|
|
|
|
vec![(
|
|
|
|
vec!["cloud-hypervisor", "--kernel", "/path/to/kernel"],
|
|
|
|
r#"{
|
|
|
|
"kernel": {"path": "/path/to/kernel"}
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
)]
|
|
|
|
.iter()
|
|
|
|
.for_each(|(cli, openapi, equal)| {
|
|
|
|
compare_vm_config_cli_vs_json(cli, openapi, *equal);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_valid_vm_config_cmdline() {
|
|
|
|
vec![(
|
|
|
|
vec!["cloud-hypervisor", "--cmdline", "arg1=foo arg2=bar"],
|
|
|
|
r#"{
|
|
|
|
"cmdline": {"args": "arg1=foo arg2=bar"}
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
)]
|
|
|
|
.iter()
|
|
|
|
.for_each(|(cli, openapi, equal)| {
|
|
|
|
compare_vm_config_cli_vs_json(cli, openapi, *equal);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_valid_vm_config_disks() {
|
|
|
|
vec![
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--disk",
|
|
|
|
"path=/path/to/disk/1",
|
|
|
|
"path=/path/to/disk/2",
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"disks": [
|
|
|
|
{"path": "/path/to/disk/1"},
|
|
|
|
{"path": "/path/to/disk/2"}
|
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--disk",
|
|
|
|
"path=/path/to/disk/1",
|
|
|
|
"path=/path/to/disk/2",
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"disks": [
|
|
|
|
{"path": "/path/to/disk/1"}
|
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
false,
|
|
|
|
),
|
2020-01-28 10:38:42 +00:00
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--disk",
|
2020-03-13 10:28:39 +00:00
|
|
|
"vhost_user=true,socket=/tmp/socket1",
|
2020-01-28 10:38:42 +00:00
|
|
|
"path=/path/to/disk/2",
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"disks": [
|
2020-03-13 10:28:39 +00:00
|
|
|
{"vhost_user":true, "vhost_socket":"/tmp/socket1"},
|
2020-01-28 10:38:42 +00:00
|
|
|
{"path": "/path/to/disk/2"}
|
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--disk",
|
2020-03-13 10:28:39 +00:00
|
|
|
"vhost_user=true,socket=/tmp/socket1,wce=true",
|
2020-01-28 10:38:42 +00:00
|
|
|
"path=/path/to/disk/2",
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"disks": [
|
2020-03-13 10:28:39 +00:00
|
|
|
{"vhost_user":true, "vhost_socket":"/tmp/socket1", "wce":true},
|
2020-01-28 10:38:42 +00:00
|
|
|
{"path": "/path/to/disk/2"}
|
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
2019-12-12 17:35:33 +00:00
|
|
|
]
|
|
|
|
.iter()
|
|
|
|
.for_each(|(cli, openapi, equal)| {
|
|
|
|
compare_vm_config_cli_vs_json(cli, openapi, *equal);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_valid_vm_config_net() {
|
|
|
|
vec![
|
|
|
|
// This test is expected to fail because the default MAC address is
|
|
|
|
// randomly generated. There's no way we can have twice the same
|
|
|
|
// default value.
|
|
|
|
(
|
|
|
|
vec!["cloud-hypervisor", "--net", "mac="],
|
|
|
|
r#"{
|
|
|
|
"net": []
|
|
|
|
}"#,
|
|
|
|
false,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec!["cloud-hypervisor", "--net", "mac=12:34:56:78:90:ab"],
|
|
|
|
r#"{
|
|
|
|
"net": [
|
|
|
|
{"mac": "12:34:56:78:90:ab"}
|
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--net",
|
|
|
|
"mac=12:34:56:78:90:ab,tap=tap0",
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"net": [
|
|
|
|
{"mac": "12:34:56:78:90:ab", "tap": "tap0"}
|
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--net",
|
|
|
|
"mac=12:34:56:78:90:ab,tap=tap0,ip=1.2.3.4",
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"net": [
|
|
|
|
{"mac": "12:34:56:78:90:ab", "tap": "tap0", "ip": "1.2.3.4"}
|
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--net",
|
|
|
|
"mac=12:34:56:78:90:ab,tap=tap0,ip=1.2.3.4,mask=5.6.7.8",
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"net": [
|
|
|
|
{"mac": "12:34:56:78:90:ab", "tap": "tap0", "ip": "1.2.3.4", "mask": "5.6.7.8"}
|
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--net",
|
2020-01-09 08:32:41 +00:00
|
|
|
"mac=12:34:56:78:90:ab,tap=tap0,ip=1.2.3.4,mask=5.6.7.8,num_queues=4",
|
2019-12-12 17:35:33 +00:00
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"net": [
|
2020-01-09 08:32:41 +00:00
|
|
|
{"mac": "12:34:56:78:90:ab", "tap": "tap0", "ip": "1.2.3.4", "mask": "5.6.7.8", "num_queues": 4}
|
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--net",
|
|
|
|
"mac=12:34:56:78:90:ab,tap=tap0,ip=1.2.3.4,mask=5.6.7.8,num_queues=4,queue_size=128",
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"net": [
|
|
|
|
{"mac": "12:34:56:78:90:ab", "tap": "tap0", "ip": "1.2.3.4", "mask": "5.6.7.8", "num_queues": 4, "queue_size": 128}
|
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--net",
|
|
|
|
"mac=12:34:56:78:90:ab,tap=tap0,ip=1.2.3.4,mask=5.6.7.8,num_queues=2,queue_size=256",
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"net": [
|
|
|
|
{"mac": "12:34:56:78:90:ab", "tap": "tap0", "ip": "1.2.3.4", "mask": "5.6.7.8"}
|
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--net",
|
|
|
|
"mac=12:34:56:78:90:ab,tap=tap0,ip=1.2.3.4,mask=5.6.7.8",
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"net": [
|
|
|
|
{"mac": "12:34:56:78:90:ab", "tap": "tap0", "ip": "1.2.3.4", "mask": "5.6.7.8", "num_queues": 2, "queue_size": 256}
|
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--net",
|
|
|
|
"mac=12:34:56:78:90:ab,tap=tap0,ip=1.2.3.4,mask=5.6.7.8,num_queues=2,queue_size=256,iommu=on",
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"net": [
|
|
|
|
{"mac": "12:34:56:78:90:ab", "tap": "tap0", "ip": "1.2.3.4", "mask": "5.6.7.8", "num_queues": 2, "queue_size": 256, "iommu": true}
|
2019-12-12 17:35:33 +00:00
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
false,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--net",
|
2020-01-09 08:32:41 +00:00
|
|
|
"mac=12:34:56:78:90:ab,tap=tap0,ip=1.2.3.4,mask=5.6.7.8,num_queues=2,queue_size=256,iommu=on",
|
2019-12-12 17:35:33 +00:00
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"net": [
|
2020-01-09 08:32:41 +00:00
|
|
|
{"mac": "12:34:56:78:90:ab", "tap": "tap0", "ip": "1.2.3.4", "mask": "5.6.7.8", "num_queues": 2, "queue_size": 256, "iommu": true}
|
2019-12-12 17:35:33 +00:00
|
|
|
],
|
|
|
|
"iommu": true
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--net",
|
2020-01-09 08:32:41 +00:00
|
|
|
"mac=12:34:56:78:90:ab,tap=tap0,ip=1.2.3.4,mask=5.6.7.8,num_queues=2,queue_size=256,iommu=off",
|
2019-12-12 17:35:33 +00:00
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"net": [
|
2020-01-09 08:32:41 +00:00
|
|
|
{"mac": "12:34:56:78:90:ab", "tap": "tap0", "ip": "1.2.3.4", "mask": "5.6.7.8", "num_queues": 2, "queue_size": 256, "iommu": false}
|
2019-12-12 17:35:33 +00:00
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
2020-01-28 10:38:42 +00:00
|
|
|
(
|
|
|
|
vec!["cloud-hypervisor", "--net", "mac=12:34:56:78:90:ab,vhost_user=true,socket=/tmp/socket"],
|
|
|
|
r#"{
|
|
|
|
"net": [
|
|
|
|
{"mac": "12:34:56:78:90:ab", "vhost_user": true, "vhost_socket": "/tmp/socket"}
|
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
2019-12-12 17:35:33 +00:00
|
|
|
]
|
|
|
|
.iter()
|
|
|
|
.for_each(|(cli, openapi, equal)| {
|
|
|
|
compare_vm_config_cli_vs_json(cli, openapi, *equal);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_valid_vm_config_rng() {
|
|
|
|
vec![(
|
|
|
|
vec!["cloud-hypervisor", "--rng", "src=/path/to/entropy/source"],
|
|
|
|
r#"{
|
|
|
|
"rng": {"src": "/path/to/entropy/source"}
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
)]
|
|
|
|
.iter()
|
|
|
|
.for_each(|(cli, openapi, equal)| {
|
|
|
|
compare_vm_config_cli_vs_json(cli, openapi, *equal);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_valid_vm_config_fs() {
|
|
|
|
vec![
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--fs",
|
|
|
|
"tag=virtiofs1,sock=/path/to/sock1",
|
|
|
|
"tag=virtiofs2,sock=/path/to/sock2",
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"fs": [
|
|
|
|
{"tag": "virtiofs1", "sock": "/path/to/sock1"},
|
|
|
|
{"tag": "virtiofs2", "sock": "/path/to/sock2"}
|
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--fs",
|
|
|
|
"tag=virtiofs1,sock=/path/to/sock1",
|
|
|
|
"tag=virtiofs2,sock=/path/to/sock2",
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"fs": [
|
|
|
|
{"tag": "virtiofs1", "sock": "/path/to/sock1"}
|
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
false,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--fs",
|
|
|
|
"tag=virtiofs1,sock=/path/to/sock1,num_queues=4",
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"fs": [
|
|
|
|
{"tag": "virtiofs1", "sock": "/path/to/sock1", "num_queues": 4}
|
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--fs",
|
|
|
|
"tag=virtiofs1,sock=/path/to/sock1,num_queues=4,queue_size=128"
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"fs": [
|
|
|
|
{"tag": "virtiofs1", "sock": "/path/to/sock1", "num_queues": 4, "queue_size": 128}
|
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--fs",
|
|
|
|
"tag=virtiofs1,sock=/path/to/sock1,num_queues=4,queue_size=128,dax=on"
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"fs": [
|
|
|
|
{"tag": "virtiofs1", "sock": "/path/to/sock1", "num_queues": 4, "queue_size": 128}
|
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--fs",
|
|
|
|
"tag=virtiofs1,sock=/path/to/sock1,num_queues=4,queue_size=128,dax=on"
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"fs": [
|
|
|
|
{"tag": "virtiofs1", "sock": "/path/to/sock1", "num_queues": 4, "queue_size": 128, "dax": true}
|
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--fs",
|
|
|
|
"tag=virtiofs1,sock=/path/to/sock1,num_queues=4,queue_size=128"
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"fs": [
|
|
|
|
{"tag": "virtiofs1", "sock": "/path/to/sock1", "num_queues": 4, "queue_size": 128, "dax": true}
|
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--fs",
|
|
|
|
"tag=virtiofs1,sock=/path/to/sock1,num_queues=4,queue_size=128,cache_size=8589934592"
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"fs": [
|
|
|
|
{"tag": "virtiofs1", "sock": "/path/to/sock1", "num_queues": 4, "queue_size": 128}
|
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--fs",
|
|
|
|
"tag=virtiofs1,sock=/path/to/sock1,num_queues=4,queue_size=128"
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"fs": [
|
|
|
|
{"tag": "virtiofs1", "sock": "/path/to/sock1", "num_queues": 4, "queue_size": 128, "cache_size": 8589934592}
|
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--fs",
|
|
|
|
"tag=virtiofs1,sock=/path/to/sock1,num_queues=4,queue_size=128,cache_size=4294967296"
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"fs": [
|
|
|
|
{"tag": "virtiofs1", "sock": "/path/to/sock1", "num_queues": 4, "queue_size": 128, "cache_size": 4294967296}
|
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--fs",
|
|
|
|
"tag=virtiofs1,sock=/path/to/sock1,num_queues=4,queue_size=128,cache_size=4294967296"
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"fs": [
|
|
|
|
{"tag": "virtiofs1", "sock": "/path/to/sock1", "num_queues": 4, "queue_size": 128}
|
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
false,
|
|
|
|
),
|
|
|
|
]
|
|
|
|
.iter()
|
|
|
|
.for_each(|(cli, openapi, equal)| {
|
|
|
|
compare_vm_config_cli_vs_json(cli, openapi, *equal);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_valid_vm_config_pmem() {
|
|
|
|
vec![
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--pmem",
|
|
|
|
"file=/path/to/img/1,size=1G",
|
|
|
|
"file=/path/to/img/2,size=2G",
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"pmem": [
|
|
|
|
{"file": "/path/to/img/1", "size": 1073741824},
|
|
|
|
{"file": "/path/to/img/2", "size": 2147483648}
|
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--pmem",
|
|
|
|
"file=/path/to/img/1,size=1G,iommu=on",
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"pmem": [
|
|
|
|
{"file": "/path/to/img/1", "size": 1073741824, "iommu": true}
|
|
|
|
],
|
|
|
|
"iommu": true
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--pmem",
|
|
|
|
"file=/path/to/img/1,size=1G,iommu=on",
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"pmem": [
|
|
|
|
{"file": "/path/to/img/1", "size": 1073741824, "iommu": true}
|
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
false,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--pmem",
|
|
|
|
"file=/path/to/img/1,size=1G,mergeable=on",
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"pmem": [
|
|
|
|
{"file": "/path/to/img/1", "size": 1073741824, "mergeable": true}
|
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--pmem",
|
|
|
|
"file=/path/to/img/1,size=1G,mergeable=off",
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"pmem": [
|
|
|
|
{"file": "/path/to/img/1", "size": 1073741824, "mergeable": false}
|
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
]
|
|
|
|
.iter()
|
|
|
|
.for_each(|(cli, openapi, equal)| {
|
|
|
|
compare_vm_config_cli_vs_json(cli, openapi, *equal);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_valid_vm_config_serial_console() {
|
|
|
|
vec![
|
|
|
|
(
|
|
|
|
vec!["cloud-hypervisor"],
|
|
|
|
r#"{
|
|
|
|
"serial": {"mode": "Null"},
|
|
|
|
"console": {"mode": "Tty"}
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec!["cloud-hypervisor", "--serial", "null", "--console", "tty"],
|
|
|
|
r#"{}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec!["cloud-hypervisor", "--serial", "tty", "--console", "off"],
|
|
|
|
r#"{
|
|
|
|
"serial": {"mode": "Tty"},
|
|
|
|
"console": {"mode": "Off"}
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
]
|
|
|
|
.iter()
|
|
|
|
.for_each(|(cli, openapi, equal)| {
|
|
|
|
compare_vm_config_cli_vs_json(cli, openapi, *equal);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_valid_vm_config_devices() {
|
|
|
|
vec![
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--device",
|
|
|
|
"path=/path/to/device/1",
|
|
|
|
"path=/path/to/device/2",
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"devices": [
|
|
|
|
{"path": "/path/to/device/1"},
|
|
|
|
{"path": "/path/to/device/2"}
|
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--device",
|
|
|
|
"path=/path/to/device/1",
|
|
|
|
"path=/path/to/device/2",
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"devices": [
|
|
|
|
{"path": "/path/to/device/1"}
|
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
false,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--device",
|
|
|
|
"path=/path/to/device,iommu=on",
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"devices": [
|
|
|
|
{"path": "/path/to/device", "iommu": true}
|
|
|
|
],
|
|
|
|
"iommu": true
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--device",
|
|
|
|
"path=/path/to/device,iommu=on",
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"devices": [
|
|
|
|
{"path": "/path/to/device", "iommu": true}
|
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
false,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--device",
|
|
|
|
"path=/path/to/device,iommu=off",
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"devices": [
|
|
|
|
{"path": "/path/to/device", "iommu": false}
|
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
]
|
|
|
|
.iter()
|
|
|
|
.for_each(|(cli, openapi, equal)| {
|
|
|
|
compare_vm_config_cli_vs_json(cli, openapi, *equal);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_valid_vm_config_vsock() {
|
|
|
|
vec![
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--vsock",
|
|
|
|
"cid=123,sock=/path/to/sock/1",
|
|
|
|
"cid=456,sock=/path/to/sock/2",
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"vsock": [
|
|
|
|
{"cid": 123, "sock": "/path/to/sock/1"},
|
|
|
|
{"cid": 456, "sock": "/path/to/sock/2"}
|
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--vsock",
|
|
|
|
"cid=123,sock=/path/to/sock/1",
|
|
|
|
"cid=456,sock=/path/to/sock/2",
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"vsock": [
|
|
|
|
{"cid": 123, "sock": "/path/to/sock/1"}
|
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
false,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--vsock",
|
|
|
|
"cid=124,sock=/path/to/sock/1",
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"vsock": [
|
|
|
|
{"cid": 123, "sock": "/path/to/sock/1"}
|
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
false,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--vsock",
|
|
|
|
"cid=123,sock=/path/to/sock/1,iommu=on",
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"vsock": [
|
|
|
|
{"cid": 123, "sock": "/path/to/sock/1", "iommu": true}
|
|
|
|
],
|
|
|
|
"iommu": true
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--vsock",
|
|
|
|
"cid=123,sock=/path/to/sock/1,iommu=on",
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"vsock": [
|
|
|
|
{"cid": 123, "sock": "/path/to/sock/1", "iommu": true}
|
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
false,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
vec![
|
|
|
|
"cloud-hypervisor",
|
|
|
|
"--vsock",
|
|
|
|
"cid=123,sock=/path/to/sock/1,iommu=off",
|
|
|
|
],
|
|
|
|
r#"{
|
|
|
|
"vsock": [
|
|
|
|
{"cid": 123, "sock": "/path/to/sock/1", "iommu": false}
|
|
|
|
]
|
|
|
|
}"#,
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
]
|
|
|
|
.iter()
|
|
|
|
.for_each(|(cli, openapi, equal)| {
|
|
|
|
compare_vm_config_cli_vs_json(cli, openapi, *equal);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|