1pub mod to_command;
2
3use std::fmt::{Display, Formatter};
4use std::net::IpAddr;
5use std::path::PathBuf;
6
7use bytesize::ByteSize;
8use derive_builder::Builder;
9use serde::{Deserialize, Serialize};
10
11use crate::to_command::ToCommand;
12
13#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
14pub enum OnOff {
15 On,
16 Off,
17}
18
19impl Display for OnOff {
20 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
21 match self {
22 OnOff::On => {
23 write!(f, "on")
24 }
25 OnOff::Off => {
26 write!(f, "off")
27 }
28 }
29 }
30}
31
32#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
33pub enum PathOrFileDescriptorOption {
34 Path(PathBuf),
35 Fd(usize),
36}
37
38#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
39pub struct CloudHypervisorInstance {
40 bin_path: PathBuf,
41 cpus: Option<Cpus>,
42 platform: Option<Platform>,
43 memory: Option<Memory>,
44 memory_zone: Option<MemoryZone>,
45 firmware: Option<PathBuf>,
46 kernel: Option<PathBuf>,
47 initramfs: Option<PathBuf>,
48 cmdline: Option<String>,
49 rate_limit_group: Option<Vec<RateLimitGroup>>,
50 disk: Option<Vec<Disk>>,
51 net: Option<Vec<Net>>,
52 rng: Option<Rng>,
53 balloon: Option<Balloon>,
54 fs: Option<Vec<Fs>>,
55 pmem: Option<Vec<Pmem>>,
56 serial: Option<Serial>,
57 console: Option<Console>,
58 device: Option<Vec<Device>>,
59 user_device: Option<Vec<UserDevice>>,
60 vdpa: Option<Vec<Vdpa>>,
61 vsock: Option<Vsock>,
62 pvpanic: Option<bool>,
63 numa: Option<Vec<Numa>>,
64 watchdog: Option<bool>,
65 log_file: Option<PathBuf>,
66 api_socket: Option<PathOrFileDescriptorOption>,
67 event_monitor: Option<PathOrFileDescriptorOption>,
68 restore: Option<Restore>,
69 seccomp: Option<SecComp>,
70 tpm: Option<PathBuf>,
71 sgx_epc: Option<Vec<SgxEpc>>,
72 debug_console: Option<DebugConsole>,
73 v: Option<u8>,
74}
75
76impl CloudHypervisorInstance {
77 pub fn new(bin_path: PathBuf) -> Self {
78 CloudHypervisorInstance {
79 bin_path,
80 ..Self::default()
81 }
82 }
83
84 pub fn cpus(&mut self, cpus: Cpus) -> &mut Self {
85 self.cpus = Some(cpus);
86 self
87 }
88 pub fn platform(&mut self, platform: Platform) -> &mut Self {
89 self.platform = Some(platform);
90 self
91 }
92 pub fn memory(&mut self, memory: Memory) -> &mut Self {
93 self.memory = Some(memory);
94 self
95 }
96 pub fn memory_zone(&mut self, memory_zone: MemoryZone) -> &mut Self {
97 self.memory_zone = Some(memory_zone);
98 self
99 }
100 pub fn firmware(&mut self, firmware: PathBuf) -> &mut Self {
101 self.firmware = Some(firmware);
102 self
103 }
104 pub fn kernel(&mut self, kernel: PathBuf) -> &mut Self {
105 self.kernel = Some(kernel);
106 self
107 }
108 pub fn initramfs(&mut self, initramfs: PathBuf) -> &mut Self {
109 self.initramfs = Some(initramfs);
110 self
111 }
112 pub fn cmdline(&mut self, cmdline: String) -> &mut Self {
113 self.cmdline = Some(cmdline);
114 self
115 }
116 pub fn rate_limit_group(&mut self, rate_limit_group: RateLimitGroup) -> &mut Self {
117 match &mut self.rate_limit_group {
118 None => {
119 self.rate_limit_group = Some(vec![rate_limit_group]);
120 }
121 Some(rate_limit_groups) => {
122 rate_limit_groups.push(rate_limit_group);
123 }
124 }
125 self
126 }
127 pub fn disk(&mut self, disk: Disk) -> &mut Self {
128 match &mut self.disk {
129 None => {
130 self.disk = Some(vec![disk]);
131 }
132 Some(disks) => {
133 disks.push(disk);
134 }
135 }
136 self
137 }
138 pub fn net(&mut self, net: Net) -> &mut Self {
139 match &mut self.net {
140 None => {
141 self.net = Some(vec![net]);
142 }
143 Some(nets) => {
144 nets.push(net);
145 }
146 }
147 self
148 }
149 pub fn rng(&mut self, rng: Rng) -> &mut Self {
150 self.rng = Some(rng);
151 self
152 }
153 pub fn balloon(&mut self, balloon: Balloon) -> &mut Self {
154 self.balloon = Some(balloon);
155 self
156 }
157 pub fn fs(&mut self, fs: Fs) -> &mut Self {
158 match &mut self.fs {
159 None => {
160 self.fs = Some(vec![fs]);
161 }
162 Some(fss) => {
163 fss.push(fs);
164 }
165 }
166 self
167 }
168 pub fn pmem(&mut self, pmem: Pmem) -> &mut Self {
169 match &mut self.pmem {
170 None => {
171 self.pmem = Some(vec![pmem]);
172 }
173 Some(pmems) => {
174 pmems.push(pmem);
175 }
176 }
177 self
178 }
179 pub fn serial(&mut self, serial: Serial) -> &mut Self {
180 self.serial = Some(serial);
181 self
182 }
183 pub fn console(&mut self, console: Console) -> &mut Self {
184 self.console = Some(console);
185 self
186 }
187 pub fn device(&mut self, device: Device) -> &mut Self {
188 match &mut self.device {
189 None => {
190 self.device = Some(vec![device]);
191 }
192 Some(devices) => {
193 devices.push(device);
194 }
195 }
196
197 self
198 }
199 pub fn user_device(&mut self, user_device: UserDevice) -> &mut Self {
200 match &mut self.user_device {
201 None => {
202 self.user_device = Some(vec![user_device]);
203 }
204 Some(user_devices) => {
205 user_devices.push(user_device);
206 }
207 }
208 self
209 }
210 pub fn vdpa(&mut self, vdpa: Vdpa) -> &mut Self {
211 match &mut self.vdpa {
212 None => {
213 self.vdpa = Some(vec![vdpa]);
214 }
215 Some(vdpas) => {
216 vdpas.push(vdpa);
217 }
218 }
219 self
220 }
221 pub fn vsock(&mut self, vsock: Vsock) -> &mut Self {
222 self.vsock = Some(vsock);
223 self
224 }
225 pub fn pvpanic(&mut self, pvpanic: bool) -> &mut Self {
226 self.pvpanic = Some(pvpanic);
227 self
228 }
229 pub fn numa(&mut self, numa: Numa) -> &mut Self {
230 match &mut self.numa {
231 None => {
232 self.numa = Some(vec![numa]);
233 }
234 Some(numas) => {
235 numas.push(numa);
236 }
237 }
238 self
239 }
240 pub fn watchdog(&mut self, watchdog: bool) -> &mut Self {
241 self.watchdog = Some(watchdog);
242 self
243 }
244 pub fn log_file(&mut self, log_file: PathBuf) -> &mut Self {
245 self.log_file = Some(log_file);
246 self
247 }
248 pub fn api_socket(&mut self, api_socket: PathOrFileDescriptorOption) -> &mut Self {
249 self.api_socket = Some(api_socket);
250 self
251 }
252 pub fn event_monitor(&mut self, event_monitor: PathOrFileDescriptorOption) -> &mut Self {
253 self.event_monitor = Some(event_monitor);
254 self
255 }
256 pub fn restore(&mut self, restore: Restore) -> &mut Self {
257 self.restore = Some(restore);
258 self
259 }
260 pub fn seccomp(&mut self, seccomp: SecComp) -> &mut Self {
261 self.seccomp = Some(seccomp);
262 self
263 }
264 pub fn tpm(&mut self, tpm: PathBuf) -> &mut Self {
265 self.tpm = Some(tpm);
266 self
267 }
268 pub fn sgx_epc(&mut self, sgx_epc: SgxEpc) -> &mut Self {
269 match &mut self.sgx_epc {
270 None => {
271 self.sgx_epc = Some(vec![sgx_epc]);
272 }
273 Some(sgx_epcs) => {
274 sgx_epcs.push(sgx_epc);
275 }
276 }
277
278 self
279 }
280 pub fn debug_console(&mut self, debug_console: DebugConsole) -> &mut Self {
281 self.debug_console = Some(debug_console);
282 self
283 }
284 pub fn v(&mut self) -> &mut Self {
285 match &mut self.v {
286 None => self.v = Some(1),
287 Some(v) => {
288 *v += 1;
289 }
290 }
291 self
292 }
293}
294
295impl ToCommand for CloudHypervisorInstance {
296 fn to_command(&self) -> Vec<String> {
297 let mut cmd = vec![self.bin_path.display().to_string()];
298
299 if let Some(cpus) = &self.cpus {
300 cmd.append(cpus.to_command().as_mut());
301 }
302 if let Some(platform) = &self.platform {
303 cmd.append(platform.to_command().as_mut());
304 }
305 if let Some(memory) = &self.memory {
306 cmd.append(memory.to_command().as_mut());
307 }
308 if let Some(memory_zone) = &self.memory_zone {
309 cmd.append(memory_zone.to_command().as_mut());
310 }
311 if let Some(firmware) = &self.firmware {
312 cmd.push("--firmware".to_string());
313 cmd.push(firmware.display().to_string());
314 }
315 if let Some(kernel) = &self.kernel {
316 cmd.push("--kernel".to_string());
317 cmd.push(kernel.display().to_string());
318 }
319 if let Some(initramfs) = &self.initramfs {
320 cmd.push("--initramfs".to_string());
321 cmd.push(initramfs.display().to_string());
322 }
323 if let Some(cmdline) = &self.cmdline {
324 cmd.push("--cmdline".to_string());
325 cmd.push(cmdline.to_string());
326 }
327 if let Some(rate_limit_groups) = &self.rate_limit_group {
328 let mut arg = vec![];
329 for rate_limit_group in rate_limit_groups {
330 if let Some(bw_size) = &rate_limit_group.bw_size {
331 arg.push(format!("bw_size={}", bw_size.0));
332 }
333 if let Some(bw_one_time_burst) = &rate_limit_group.bw_one_time_burst {
334 arg.push(format!("bw_one_time_burst={}", bw_one_time_burst));
335 }
336 if let Some(bw_refill_time) = &rate_limit_group.bw_refill_time {
337 arg.push(format!("bw_refill_time={}", bw_refill_time));
338 }
339 if let Some(ops_size) = &rate_limit_group.ops_size {
340 arg.push(format!("ops_size={}", ops_size));
341 }
342 if let Some(ops_one_time_burst) = &rate_limit_group.ops_one_time_burst {
343 arg.push(format!("ops_one_time_burst={}", ops_one_time_burst));
344 }
345 if let Some(ops_refill_time) = &rate_limit_group.ops_refill_time {
346 arg.push(format!("ops_refill_time={}", ops_refill_time));
347 }
348 if let Some(id) = &rate_limit_group.id {
349 arg.push(format!("id={}", id));
350 }
351 if !arg.is_empty() {
352 cmd.push("--rate_limit_group".to_string());
353 cmd.push(arg.join(","));
354 }
355 }
356 }
357 if let Some(disks) = &self.disk {
358 if !disks.is_empty() {
359 let mut added = false;
360
361 for disk in disks {
362 let mut arg = vec![];
363 if let Some(path) = &disk.path {
364 arg.push(format!("path={}", path.display()));
365 }
366 if let Some(readonly) = &disk.readonly {
367 arg.push(format!("readonly={}", readonly));
368 }
369 if let Some(direct) = &disk.direct {
370 arg.push(format!("direct={}", direct));
371 }
372 if let Some(iommu) = &disk.iommu {
373 arg.push(format!("iommu={}", iommu));
374 }
375 if let Some(num_queues) = &disk.num_queues {
376 arg.push(format!("num_queues={}", num_queues));
377 }
378 if let Some(queue_size) = &disk.queue_size {
379 arg.push(format!("queue_size={}", queue_size));
380 }
381 if let Some(vhost_user) = &disk.vhost_user {
382 arg.push(format!("vhost_user={}", vhost_user));
383 }
384 if let Some(socket) = &disk.socket {
385 arg.push(format!("socket={}", socket.display()));
386 }
387 if let Some(bw_size) = &disk.bw_size {
388 arg.push(format!("bw_size={}", bw_size.0));
389 }
390 if let Some(bw_one_time_burst) = &disk.bw_one_time_burst {
391 arg.push(format!("bw_one_time_burst={}", bw_one_time_burst));
392 }
393 if let Some(bw_refill_time) = &disk.bw_refill_time {
394 arg.push(format!("bw_refill_time={}", bw_refill_time));
395 }
396 if let Some(ops_size) = &disk.ops_size {
397 arg.push(format!("ops_size={}", ops_size));
398 }
399 if let Some(ops_one_time_burst) = &disk.ops_one_time_burst {
400 arg.push(format!("ops_one_time_burst={}", ops_one_time_burst));
401 }
402 if let Some(ops_refill_time) = &disk.ops_refill_time {
403 arg.push(format!("ops_refill_time={}", ops_refill_time));
404 }
405 if let Some(id) = &disk.id {
406 arg.push(format!("id={}", id));
407 }
408 if let Some(pci_segment) = &disk.pci_segment {
409 arg.push(format!("pci_segment={}", pci_segment));
410 }
411 if let Some(rate_limit_group) = &disk.rate_limit_group {
412 arg.push(format!("rate_limit_group={}", rate_limit_group));
413 }
414 if let Some(queue_affinity) = &disk.queue_affinity {
415 arg.push(format!("queue_affinity={}", queue_affinity));
416 }
417 if !arg.is_empty() {
418 if !added {
419 cmd.push("--disk".to_string());
420 added = true;
421 }
422 cmd.push(arg.join(","));
423 }
424 }
425 }
426 }
427 if let Some(nets) = &self.net {
428 if !nets.is_empty() {
429 let mut added = false;
430
431 for net in nets {
432 let mut arg = vec![];
433
434 if let Some(tap) = &net.tap {
435 arg.push(format!("tap={}", tap));
436 }
437 if let Some(ip) = &net.ip {
438 arg.push(format!("ip={}", ip));
439 }
440 if let Some(mask) = &net.mask {
441 arg.push(format!("mask={}", mask));
442 }
443 if let Some(mac) = &net.mac {
444 arg.push(format!("mac={}", mac));
445 }
446 if let Some(fd) = &net.fd {
447 arg.push(format!(
448 "fd={}",
449 fd.iter()
450 .map(|v| v.to_string())
451 .collect::<Vec<String>>()
452 .join(",")
453 ));
454 }
455 if let Some(iommu) = &net.iommu {
456 arg.push(format!("iommu={}", iommu));
457 }
458 if let Some(num_queues) = &net.num_queues {
459 arg.push(format!("num_queues={}", num_queues));
460 }
461 if let Some(queue_size) = &net.queue_size {
462 arg.push(format!("queue_size={}", queue_size));
463 }
464 if let Some(id) = &net.id {
465 arg.push(format!("id={}", id));
466 }
467 if let Some(vhost_user) = &net.vhost_user {
468 arg.push(format!("vhost_user={}", vhost_user));
469 }
470 if let Some(socket) = &net.socket {
471 arg.push(format!("socket={}", socket.display()));
472 }
473 if let Some(vhost_mode) = &net.vhost_mode {
474 arg.push(format!("vhost_mode={}", vhost_mode));
475 }
476 if let Some(bw_size) = &net.bw_size {
477 arg.push(format!("bw_size={}", bw_size.0));
478 }
479 if let Some(bw_one_time_burst) = &net.bw_one_time_burst {
480 arg.push(format!("bw_one_time_burst={}", bw_one_time_burst));
481 }
482 if let Some(bw_refill_time) = &net.bw_refill_time {
483 arg.push(format!("bw_refill_time={}", bw_refill_time));
484 }
485 if let Some(ops_size) = &net.ops_size {
486 arg.push(format!("ops_size={}", ops_size));
487 }
488 if let Some(ops_one_time_burst) = &net.ops_one_time_burst {
489 arg.push(format!("ops_one_time_burst={}", ops_one_time_burst));
490 }
491 if let Some(ops_refill_time) = &net.ops_refill_time {
492 arg.push(format!("ops_refill_time={}", ops_refill_time));
493 }
494 if let Some(pci_segment) = &net.pci_segment {
495 arg.push(format!("pci_segment={}", pci_segment));
496 }
497 if let Some(offload_tso) = &net.offload_tso {
498 arg.push(format!("offload_tso={}", offload_tso));
499 }
500 if let Some(offload_ufo) = &net.offload_ufo {
501 arg.push(format!("offload_ufo={}", offload_ufo));
502 }
503 if let Some(offload_csum) = &net.offload_csum {
504 arg.push(format!("offload_csum={}", offload_csum));
505 }
506 if !arg.is_empty() {
507 if !added {
508 cmd.push("--net".to_string());
509 added = true;
510 }
511 cmd.push(arg.join(","));
512 }
513 }
514 }
515 }
516 if let Some(rng) = &self.rng {
517 cmd.push("--rng".to_string());
518 match rng {
519 Rng::Src(path) => {
520 cmd.push(format!("src={}", path.display()));
521 }
522 Rng::Iommu(state) => {
523 cmd.push(format!("iommu={}", state));
524 }
525 }
526 }
527 if let Some(balloon) = &self.balloon {
528 let mut arg = vec![];
529 if let Some(size) = &balloon.size {
530 arg.push(format!("size={}", size.0));
531 }
532 if let Some(deflate_on_oom) = &balloon.deflate_on_oom {
533 arg.push(format!("deflate_on_oom={}", deflate_on_oom));
534 }
535 if let Some(free_page_reporting) = &balloon.free_page_reporting {
536 arg.push(format!("free_page_reporting={}", free_page_reporting));
537 }
538 if !arg.is_empty() {
539 cmd.push("--balloon".to_string());
540 cmd.push(arg.join(","));
541 }
542 }
543 if let Some(fss) = &self.fs {
544 if !fss.is_empty() {
545 let mut added = false;
546
547 for fs in fss {
548 let mut arg = vec![];
549 if let Some(tag) = &fs.tag {
550 arg.push(format!("tag={}", tag));
551 }
552 if let Some(socket) = &fs.socket {
553 arg.push(format!("socket={}", socket.display()));
554 }
555 if let Some(num_queues) = &fs.num_queues {
556 arg.push(format!("num_queues={}", num_queues));
557 }
558 if let Some(queue_size) = &fs.queue_size {
559 arg.push(format!("queue_size={}", queue_size));
560 }
561 if let Some(id) = &fs.id {
562 arg.push(format!("id={}", id));
563 }
564 if let Some(pci_segment) = &fs.pci_segment {
565 arg.push(format!("pci_segment={}", pci_segment));
566 }
567 if !arg.is_empty() {
568 if !added {
569 cmd.push("--fs".to_string());
570 added = true;
571 }
572 cmd.push(arg.join(","));
573 }
574 }
575 }
576 }
577 if let Some(pmems) = &self.pmem {
578 if !pmems.is_empty() {
579 let mut added = false;
580
581 for pmem in pmems {
582 let mut arg = vec![];
583
584 if let Some(path) = &pmem.file {
585 arg.push(format!("file={}", path.display()));
586 }
587 if let Some(size) = &pmem.size {
588 arg.push(format!("size={}", size));
589 }
590 if let Some(iommu) = &pmem.iommu {
591 arg.push(format!("iommu={}", iommu));
592 }
593 if let Some(discard_writes) = &pmem.discard_writes {
594 arg.push(format!("discard_writes={}", discard_writes));
595 }
596 if let Some(id) = &pmem.id {
597 arg.push(format!("id={}", id));
598 }
599 if let Some(pci_segment) = &pmem.pci_segment {
600 arg.push(format!("pci_segment={}", pci_segment));
601 }
602 if !arg.is_empty() {
603 if !added {
604 cmd.push("--pmem".to_string());
605 added = true;
606 }
607 cmd.push(arg.join(","));
608 }
609 }
610 }
611 }
612 if let Some(serial) = &self.serial {
613 cmd.push("--serial".to_string());
614 match serial {
615 Serial::Off => {
616 cmd.push("off".to_string());
617 }
618 Serial::Null => {
619 cmd.push("null".to_string());
620 }
621 Serial::Pty => {
622 cmd.push("pty".to_string());
623 }
624 Serial::Tty => {
625 cmd.push("tty".to_string());
626 }
627 Serial::File(path) => cmd.push(format!("file={}", path.display())),
628 Serial::Socket(path) => cmd.push(format!("socket={}", path.display())),
629 }
630 }
631 if let Some(console) = &self.console {
632 cmd.push("--console".to_string());
633 match console {
634 Console::Off => {
635 cmd.push("off".to_string());
636 }
637 Console::Null => {
638 cmd.push("null".to_string());
639 }
640 Console::Pty => {
641 cmd.push("pty".to_string());
642 }
643 Console::Tty => {
644 cmd.push("tty".to_string());
645 }
646 Console::File(path) => cmd.push(format!("file={}", path.display())),
647 Console::Iommu(state) => {
648 cmd.push(format!("iommu={}", state));
649 }
650 }
651 }
652 if let Some(devices) = &self.device {
653 if !devices.is_empty() {
654 let mut added = false;
655
656 for device in devices {
657 let mut arg = vec![];
658
659 if let Some(path) = &device.path {
660 arg.push(format!("path={}", path.display()));
661 }
662 if let Some(iommu) = &device.iommu {
663 arg.push(format!("iommu={}", iommu));
664 }
665 if let Some(id) = &device.id {
666 arg.push(format!("id={}", id));
667 }
668 if let Some(pci_segment) = &device.pci_segment {
669 arg.push(format!("pci_segment={}", pci_segment));
670 }
671 if !arg.is_empty() {
672 if !added {
673 cmd.push("--device".to_string());
674 added = true;
675 }
676 cmd.push(arg.join(","));
677 }
678 }
679 }
680 }
681 if let Some(user_devices) = &self.user_device {
682 if !user_devices.is_empty() {
683 let mut added = false;
684
685 for user_device in user_devices {
686 let mut arg = vec![];
687
688 if let Some(path) = &user_device.socket {
689 arg.push(format!("socket={}", path.display()));
690 }
691 if let Some(id) = &user_device.id {
692 arg.push(format!("id={}", id));
693 }
694 if let Some(pci_segment) = &user_device.pci_segment {
695 arg.push(format!("pci_segment={}", pci_segment));
696 }
697 if !arg.is_empty() {
698 if !added {
699 cmd.push("--user-device".to_string());
700 added = true;
701 }
702 cmd.push(arg.join(","));
703 }
704 }
705 }
706 }
707 if let Some(vsock) = &self.vsock {
708 let mut arg = vec![];
709 if let Some(cid) = &vsock.cid {
710 arg.push(format!("cid={}", cid));
711 }
712 if let Some(path) = &vsock.socket {
713 arg.push(format!("socket={}", path.display()));
714 }
715 if let Some(iommu) = &vsock.iommu {
716 arg.push(format!("iommu={}", iommu));
717 }
718 if let Some(id) = &vsock.id {
719 arg.push(format!("id={}", id));
720 }
721 if let Some(pci_segment) = &vsock.pci_segment {
722 arg.push(format!("pci_segment={}", pci_segment));
723 }
724 if !arg.is_empty() {
725 cmd.push("--vsock".to_string());
726 cmd.push(arg.join(","));
727 }
728 }
729 if let Some(vdpas) = &self.vdpa {
730 if !vdpas.is_empty() {
731 let mut added = false;
732
733 for vdpa in vdpas {
734 let mut arg = vec![];
735 if let Some(path) = &vdpa.path {
736 arg.push(format!("path={}", path.display()));
737 }
738 if let Some(num_queues) = &vdpa.num_queues {
739 arg.push(format!("num_queues={}", num_queues));
740 }
741 if let Some(iommu) = &vdpa.iommu {
742 arg.push(format!("iommu={}", iommu));
743 }
744 if let Some(id) = &vdpa.id {
745 arg.push(format!("id={}", id));
746 }
747 if let Some(pci_segment) = &vdpa.pci_segment {
748 arg.push(format!("pci_segment={}", pci_segment));
749 }
750 if !arg.is_empty() {
751 if !added {
752 cmd.push("--vdpa".to_string());
753 added = true;
754 }
755 cmd.push(arg.join(","));
756 }
757 }
758 }
759 }
760 if let Some(pvpanic) = self.pvpanic {
761 if pvpanic {
762 cmd.push("--pvpanic".to_string());
763 }
764 }
765 if let Some(numas) = &self.numa {
766 if !numas.is_empty() {
767 let mut added = false;
768
769 for numa in numas {
770 let mut arg = vec![];
771 if let Some(guest_numa_id) = &numa.guest_numa_id {
772 arg.push(format!("guest_numa_id={}", guest_numa_id));
773 }
774 if let Some(cpus) = &numa.cpus {
775 arg.push(format!(
776 "cpus={}",
777 cpus.iter()
778 .map(|v| v.to_string())
779 .collect::<Vec<String>>()
780 .join(",")
781 ));
782 }
783 if let Some(distances) = &numa.distances {
784 arg.push(format!(
785 "distances={}",
786 distances
787 .iter()
788 .map(|v| v.to_string())
789 .collect::<Vec<String>>()
790 .join(",")
791 ));
792 }
793 if let Some(memory_zones) = &numa.memory_zones {
794 arg.push(format!(
795 "memory_zones={}",
796 memory_zones
797 .iter()
798 .map(|v| v.to_string())
799 .collect::<Vec<String>>()
800 .join(",")
801 ));
802 }
803 if let Some(sgx_epc_sections) = &numa.sgx_epc_sections {
804 arg.push(format!(
805 "sgx_epc_sections={}",
806 sgx_epc_sections
807 .iter()
808 .map(|v| v.to_string())
809 .collect::<Vec<String>>()
810 .join(",")
811 ));
812 }
813 if let Some(pci_segments) = &numa.pci_segments {
814 arg.push(format!(
815 "pci_segments={}",
816 pci_segments
817 .iter()
818 .map(|v| v.to_string())
819 .collect::<Vec<String>>()
820 .join(",")
821 ));
822 }
823 if !arg.is_empty() {
824 if !added {
825 cmd.push("--numa".to_string());
826 added = true;
827 }
828 cmd.push(arg.join(","));
829 }
830 }
831 }
832 }
833 if let Some(watchdog) = self.watchdog {
834 if watchdog {
835 cmd.push("--watchdog".to_string());
836 }
837 }
838 if let Some(log_file) = &self.log_file {
839 cmd.push("--log-file".to_string());
840 cmd.push(log_file.display().to_string());
841 }
842 if let Some(api_socket) = &self.api_socket {
843 cmd.push("--api-socket".to_string());
844 match api_socket {
845 PathOrFileDescriptorOption::Path(path) => {
846 cmd.push(format!("path={}", path.display()));
847 }
848 PathOrFileDescriptorOption::Fd(fd) => {
849 cmd.push(format!("fd={}", fd));
850 }
851 }
852 }
853 if let Some(event_monitor) = &self.event_monitor {
854 cmd.push("--event-monitor".to_string());
855 match event_monitor {
856 PathOrFileDescriptorOption::Path(path) => {
857 cmd.push(format!("path={}", path.display()));
858 }
859 PathOrFileDescriptorOption::Fd(fd) => {
860 cmd.push(format!("fd={}", fd));
861 }
862 }
863 }
864 if let Some(restore) = &self.restore {
865 let mut arg = vec![];
866 if let Some(source_url) = &restore.source_url {
867 arg.push(format!("source_url={}", source_url));
868 }
869 if let Some(prefault) = &restore.prefault {
870 arg.push(format!("prefault={}", prefault));
871 }
872 if !arg.is_empty() {
873 cmd.push("--restore".to_string());
874 cmd.push(arg.join(","));
875 }
876 }
877 if let Some(seccomp) = &self.seccomp {
878 cmd.push("--seccomp".to_string());
879 match seccomp {
880 SecComp::True => {
881 cmd.push("true".to_string());
882 }
883 SecComp::False => {
884 cmd.push("false".to_string());
885 }
886 SecComp::Log => {
887 cmd.push("log".to_string());
888 }
889 }
890 }
891 if let Some(tpm) = &self.tpm {
892 cmd.push("--tpm".to_string());
893 cmd.push(tpm.display().to_string());
894 }
895 if let Some(sgx_epcs) = &self.sgx_epc {
896 if !sgx_epcs.is_empty() {
897 let mut added = false;
898
899 for sgx_epc in sgx_epcs {
900 let mut arg = vec![];
901 if let Some(id) = &sgx_epc.id {
902 arg.push(format!("id={}", id));
903 }
904 if let Some(size) = &sgx_epc.size {
905 arg.push(format!("size={}", size));
906 }
907 if let Some(prefault) = &sgx_epc.prefault {
908 arg.push(format!("prefault={}", prefault));
909 }
910 if !arg.is_empty() {
911 if !added {
912 cmd.push("--sgx-epc".to_string());
913 added = true;
914 }
915 cmd.push(arg.join(","));
916 }
917 }
918 }
919 }
920 if let Some(debug_console) = &self.debug_console {
921 let mut arg = vec![];
922 if let Some(ctype) = &debug_console.console_type {
923 match ctype {
924 DebugConsoleType::Off => {
925 arg.push("off".to_string());
926 }
927 DebugConsoleType::Pty => arg.push("pty".to_string()),
928 DebugConsoleType::Tty => arg.push("tty".to_string()),
929 DebugConsoleType::File(path) => {
930 arg.push(format!("file={}", path.display()));
931 }
932 }
933 }
934 if let Some(iobase) = &debug_console.iobase {
935 arg.push(format!("iobase={}", iobase));
936 }
937 if !arg.is_empty() {
938 cmd.push("--debug-console".to_string());
939 cmd.push(arg.join(","));
940 }
941 }
942 if let Some(v) = self.v {
943 for _ in 0..v {
944 cmd.push("-v".to_string());
945 }
946 }
947 cmd
948 }
949}
950
951#[derive(Builder, Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
952#[builder(setter(strip_option), default)]
953pub struct CpuAffinity {
954 pub vcpu: u8,
955 pub host_cpus: Vec<usize>,
956}
957
958#[derive(Builder, Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
959#[builder(setter(strip_option), default)]
960pub struct CpuFeatures {
961 pub amx: Option<bool>,
962}
963
964#[derive(Builder, Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
965#[builder(setter(strip_option), default)]
966pub struct CpuTopology {
967 pub threads_per_core: u8,
968 pub cores_per_die: u8,
969 pub dies_per_package: u8,
970 pub packages: u8,
971}
972
973#[derive(Builder, Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
974#[builder(setter(strip_option), default)]
975pub struct Cpus {
976 pub boot: Option<u8>,
977 pub max: Option<u8>,
978 pub topology: Option<CpuTopology>,
979 pub kvm_hyperv: Option<OnOff>,
980 pub max_phys_bits: Option<u8>,
981 pub affinity: Option<Vec<CpuAffinity>>,
982 pub features: Option<CpuFeatures>,
983}
984
985impl ToCommand for Cpus {
986 fn to_command(&self) -> Vec<String> {
987 let mut cmd = vec![];
988
989 let mut arg: Vec<String> = vec![];
990 if let Some(boot) = self.boot {
991 arg.push(format!("boot={}", boot));
992 }
993 if let Some(max) = self.max {
994 arg.push(format!("max={}", max));
995 }
996 if let Some(topology) = &self.topology {
997 arg.push(format!(
998 "topology={}:{}:{}:{}",
999 topology.threads_per_core,
1000 topology.cores_per_die,
1001 topology.dies_per_package,
1002 topology.packages
1003 ));
1004 }
1005 if let Some(kvm_hyperv) = &self.kvm_hyperv {
1006 arg.push(format!("kvm_hyperv={}", kvm_hyperv));
1007 }
1008 if let Some(max_phys_bits) = self.max_phys_bits {
1009 arg.push(format!("max_phys_bits={}", max_phys_bits));
1010 }
1011 if let Some(affinity) = &self.affinity {
1012 if !affinity.is_empty() {
1013 let mut aarg: Vec<String> = vec![];
1014 for vcpu in affinity {
1015 aarg.push(format!(
1016 "{}@[{}]",
1017 vcpu.vcpu,
1018 vcpu.host_cpus
1019 .iter()
1020 .map(|v| v.to_string())
1021 .collect::<Vec<String>>()
1022 .join(",")
1023 ));
1024 }
1025 arg.push(format!("affinity=[{}]", aarg.join(",")));
1026 }
1027 }
1028 if let Some(features) = &self.features {
1029 let mut farg = vec![];
1030
1031 if let Some(amx) = features.amx {
1032 if amx {
1033 farg.push("amx");
1034 }
1035 }
1036 if !farg.is_empty() {
1037 arg.push(format!("features={}", farg.join(",")));
1038 }
1039 }
1040 if !arg.is_empty() {
1041 cmd.push("--cpus".to_string());
1042 cmd.push(arg.join(","));
1043 }
1044
1045 cmd
1046 }
1047}
1048
1049#[derive(Builder, Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
1050#[builder(setter(strip_option, into), default)]
1051pub struct Platform {
1052 pub num_pci_segments: Option<u8>,
1053 pub iommu_segments: Option<u8>,
1054 pub serial_number: Option<String>,
1055 pub uuid: Option<String>,
1056 pub oem_strings: Option<Vec<String>>,
1057}
1058impl ToCommand for Platform {
1059 fn to_command(&self) -> Vec<String> {
1060 let mut cmd = vec![];
1061 let mut arg: Vec<String> = vec![];
1062 if let Some(num_pci_segments) = &self.num_pci_segments {
1063 arg.push(format!("num_pci_segments={}", num_pci_segments));
1064 }
1065
1066 if let Some(iommu_segments) = &self.iommu_segments {
1067 arg.push(format!("iommu_segments={}", iommu_segments));
1068 }
1069 if let Some(serial_number) = &self.serial_number {
1070 arg.push(format!("serial_number={}", serial_number));
1071 }
1072 if let Some(uuid) = &self.uuid {
1073 arg.push(format!("uuid={}", uuid));
1074 }
1075 if let Some(oem_strings) = &self.oem_strings {
1076 if !oem_strings.is_empty() {
1077 arg.push(format!("oem_strings=[{}]", oem_strings.join(",")));
1078 }
1079 }
1080
1081 if !arg.is_empty() {
1082 cmd.push("--platform".to_string());
1083 cmd.push(arg.join(","));
1084 }
1085
1086 cmd
1087 }
1088}
1089
1090#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
1091pub enum MemoryHotplugMethod {
1092 Acpi,
1093 VirtioMem,
1094}
1095
1096impl Display for MemoryHotplugMethod {
1097 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1098 match self {
1099 MemoryHotplugMethod::Acpi => {
1100 write!(f, "acpi")
1101 }
1102 MemoryHotplugMethod::VirtioMem => {
1103 write!(f, "virtio-mem")
1104 }
1105 }
1106 }
1107}
1108#[derive(Builder, Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
1109#[builder(setter(strip_option, into), default)]
1110pub struct Memory {
1111 pub size: Option<ByteSize>,
1112 pub mergeable: Option<OnOff>,
1113 pub shared: Option<OnOff>,
1114 pub hugepages: Option<OnOff>,
1115 pub hugepage_size: Option<ByteSize>,
1116 pub hotplug_method: Option<MemoryHotplugMethod>,
1117 pub hotplug_size: Option<ByteSize>,
1118 pub hotplugged_size: Option<ByteSize>,
1119 pub prefault: Option<OnOff>,
1120 pub thp: Option<OnOff>,
1121}
1122
1123impl ToCommand for Memory {
1124 fn to_command(&self) -> Vec<String> {
1125 let mut cmd = vec![];
1126 let mut arg: Vec<String> = vec![];
1127
1128 if let Some(size) = &self.size {
1129 arg.push(format!("size={}", size.0));
1130 }
1131
1132 if let Some(mergeable) = &self.mergeable {
1133 arg.push(format!("mergeable={}", mergeable));
1134 }
1135
1136 if let Some(shared) = &self.shared {
1137 arg.push(format!("shared={}", shared));
1138 }
1139
1140 if let Some(hugepages) = &self.hugepages {
1141 arg.push(format!("hugepages={}", hugepages));
1142 }
1143
1144 if let Some(hugepage_size) = &self.hugepage_size {
1145 arg.push(format!("hugepage_size={}", hugepage_size.0));
1146 }
1147
1148 if let Some(hotplug_method) = &self.hotplug_method {
1149 arg.push(format!("hotplug_method={}", hotplug_method));
1150 }
1151
1152 if let Some(hotplug_size) = &self.hotplug_size {
1153 arg.push(format!("hotplug_size={}", hotplug_size.0));
1154 }
1155
1156 if let Some(hotplugged_size) = &self.hotplugged_size {
1157 arg.push(format!("hotplugged_size={}", hotplugged_size.0));
1158 }
1159
1160 if let Some(prefault) = &self.prefault {
1161 arg.push(format!("prefault={}", prefault));
1162 }
1163
1164 if let Some(thp) = &self.thp {
1165 arg.push(format!("thp={}", thp));
1166 }
1167
1168 if !arg.is_empty() {
1169 cmd.push("--memory".to_string());
1170 cmd.push(arg.join(","));
1171 }
1172
1173 cmd
1174 }
1175}
1176
1177#[derive(Builder, Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
1178#[builder(setter(strip_option, into), default)]
1179pub struct MemoryZone {
1180 pub size: Option<ByteSize>,
1181 pub file: Option<PathBuf>,
1182 pub shared: Option<OnOff>,
1183 pub hugepages: Option<OnOff>,
1184 pub hugepage_size: Option<ByteSize>,
1185 pub host_numa_node: Option<usize>,
1186 pub id: Option<String>,
1187 pub hotplug_size: Option<ByteSize>,
1188 pub hotplugged_size: Option<ByteSize>,
1189 pub prefault: Option<OnOff>,
1190}
1191
1192impl ToCommand for MemoryZone {
1193 fn to_command(&self) -> Vec<String> {
1194 let mut cmd = vec![];
1195 let mut arg: Vec<String> = vec![];
1196
1197 if let Some(size) = &self.size {
1198 arg.push(format!("size={}", size.0));
1199 }
1200
1201 if let Some(path) = &self.file {
1202 arg.push(format!("file={}", path.display()));
1203 }
1204
1205 if let Some(shared) = &self.shared {
1206 arg.push(format!("shared={}", shared));
1207 }
1208
1209 if let Some(hugepages) = &self.hugepages {
1210 arg.push(format!("hugepages={}", hugepages));
1211 }
1212
1213 if let Some(hugepage_size) = &self.hugepage_size {
1214 arg.push(format!("hugepage_size={}", hugepage_size.0));
1215 }
1216
1217 if let Some(host_numa_node) = &self.host_numa_node {
1218 arg.push(format!("host_numa_node={}", host_numa_node));
1219 }
1220
1221 if let Some(id) = &self.id {
1222 arg.push(format!("id={}", id));
1223 }
1224
1225 if let Some(hotplug_size) = &self.hotplug_size {
1226 arg.push(format!("hotplug_size={}", hotplug_size.0));
1227 }
1228
1229 if let Some(hotplugged_size) = &self.hotplugged_size {
1230 arg.push(format!("hotplugged_size={}", hotplugged_size.0));
1231 }
1232
1233 if let Some(prefault) = &self.prefault {
1234 arg.push(format!("prefault={}", prefault));
1235 }
1236
1237 if !arg.is_empty() {
1238 cmd.push("--memory-zone".to_string());
1239 cmd.push(arg.join(","));
1240 }
1241
1242 cmd
1243 }
1244}
1245
1246#[derive(Builder, Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
1247#[builder(setter(strip_option, into), default)]
1248pub struct RateLimitGroup {
1249 pub bw_size: Option<ByteSize>,
1250 pub bw_one_time_burst: Option<ByteSize>,
1251 pub bw_refill_time: Option<usize>,
1252 pub ops_size: Option<usize>,
1253 pub ops_one_time_burst: Option<usize>,
1254 pub ops_refill_time: Option<usize>,
1255 pub id: Option<String>,
1256}
1257
1258#[derive(Builder, Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
1259#[builder(setter(strip_option, into), default)]
1260pub struct Disk {
1261 pub path: Option<PathBuf>,
1262 pub readonly: Option<OnOff>,
1263 pub direct: Option<OnOff>,
1264 pub iommu: Option<OnOff>,
1265 pub num_queues: Option<usize>,
1266 pub queue_size: Option<usize>,
1267 pub vhost_user: Option<OnOff>,
1268 pub socket: Option<PathBuf>,
1269 pub bw_size: Option<ByteSize>,
1270 pub bw_one_time_burst: Option<ByteSize>,
1271 pub bw_refill_time: Option<usize>,
1272 pub ops_size: Option<usize>,
1273 pub ops_one_time_burst: Option<usize>,
1274 pub ops_refill_time: Option<usize>,
1275 pub id: Option<String>,
1276 pub pci_segment: Option<String>,
1277 pub rate_limit_group: Option<String>,
1278 pub queue_affinity: Option<String>,
1279}
1280
1281#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
1282pub enum VhostMode {
1283 Client,
1284 Server,
1285}
1286
1287impl Display for VhostMode {
1288 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1289 match self {
1290 VhostMode::Client => {
1291 write!(f, "client")
1292 }
1293 VhostMode::Server => {
1294 write!(f, "server")
1295 }
1296 }
1297 }
1298}
1299#[derive(Builder, Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
1300#[builder(setter(strip_option, into), default)]
1301pub struct Net {
1302 pub tap: Option<String>,
1303 pub ip: Option<IpAddr>,
1304 pub mask: Option<u8>,
1305 pub mac: Option<String>,
1306 pub fd: Option<Vec<usize>>,
1307 pub iommu: Option<OnOff>,
1308 pub num_queues: Option<usize>,
1309 pub queue_size: Option<usize>,
1310 pub id: Option<String>,
1311 pub vhost_user: Option<OnOff>,
1312 pub socket: Option<PathBuf>,
1313 pub vhost_mode: Option<VhostMode>,
1314 pub bw_size: Option<ByteSize>,
1315 pub bw_one_time_burst: Option<ByteSize>,
1316 pub bw_refill_time: Option<usize>,
1317 pub ops_size: Option<usize>,
1318 pub ops_one_time_burst: Option<usize>,
1319 pub ops_refill_time: Option<usize>,
1320 pub pci_segment: Option<String>,
1321 pub offload_tso: Option<OnOff>,
1322 pub offload_ufo: Option<OnOff>,
1323 pub offload_csum: Option<OnOff>,
1324}
1325#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
1326pub enum Rng {
1327 Src(PathBuf),
1328 Iommu(OnOff),
1329}
1330
1331#[derive(Builder, Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
1332#[builder(setter(strip_option, into), default)]
1333pub struct Balloon {
1334 pub size: Option<ByteSize>,
1335 pub deflate_on_oom: Option<OnOff>,
1336 pub free_page_reporting: Option<OnOff>,
1337}
1338
1339#[derive(Builder, Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
1340#[builder(setter(strip_option, into), default)]
1341pub struct Fs {
1342 pub tag: Option<String>,
1343 pub socket: Option<PathBuf>,
1344 pub num_queues: Option<usize>,
1345 pub queue_size: Option<usize>,
1346 pub id: Option<String>,
1347 pub pci_segment: Option<String>,
1348}
1349
1350#[derive(Builder, Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
1351#[builder(setter(strip_option, into), default)]
1352pub struct Pmem {
1353 pub file: Option<PathBuf>,
1354 pub size: Option<usize>,
1355 pub iommu: Option<OnOff>,
1356 pub discard_writes: Option<OnOff>,
1357 pub id: Option<String>,
1358 pub pci_segment: Option<String>,
1359}
1360
1361#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
1362pub enum Serial {
1363 Off,
1364 Null,
1365 Pty,
1366 Tty,
1367 File(PathBuf),
1368 Socket(PathBuf),
1369}
1370#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
1371pub enum Console {
1372 Off,
1373 Null,
1374 Pty,
1375 Tty,
1376 File(PathBuf),
1377 Iommu(OnOff),
1378}
1379
1380#[derive(Builder, Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
1381#[builder(setter(strip_option, into), default)]
1382pub struct Device {
1383 pub path: Option<PathBuf>,
1384 pub iommu: Option<OnOff>,
1385 pub id: Option<String>,
1386 pub pci_segment: Option<String>,
1387}
1388#[derive(Builder, Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
1389#[builder(setter(strip_option, into), default)]
1390pub struct UserDevice {
1391 pub socket: Option<PathBuf>,
1392 pub id: Option<String>,
1393 pub pci_segment: Option<String>,
1394}
1395
1396#[derive(Builder, Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
1397#[builder(setter(strip_option, into), default)]
1398pub struct Vdpa {
1399 pub path: Option<PathBuf>,
1400 pub num_queues: Option<usize>,
1401 pub iommu: Option<OnOff>,
1402 pub id: Option<String>,
1403 pub pci_segment: Option<String>,
1404}
1405#[derive(Builder, Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
1406#[builder(setter(strip_option, into), default)]
1407pub struct Vsock {
1408 pub cid: Option<String>,
1409 pub socket: Option<PathBuf>,
1410 pub iommu: Option<OnOff>,
1411 pub id: Option<String>,
1412 pub pci_segment: Option<String>,
1413}
1414
1415#[derive(Builder, Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
1416#[builder(setter(strip_option, into), default)]
1417pub struct Numa {
1418 pub guest_numa_id: Option<String>,
1419 pub cpus: Option<Vec<usize>>,
1420 pub distances: Option<Vec<usize>>,
1421 pub memory_zones: Option<Vec<String>>,
1422 pub sgx_epc_sections: Option<Vec<String>>,
1423 pub pci_segments: Option<Vec<String>>,
1424}
1425
1426#[derive(Builder, Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
1427#[builder(setter(strip_option, into), default)]
1428pub struct Restore {
1429 pub source_url: Option<String>,
1430 pub prefault: Option<OnOff>,
1431}
1432#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
1433pub enum SecComp {
1434 True,
1435 False,
1436 Log,
1437}
1438
1439#[derive(Builder, Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
1440#[builder(setter(strip_option, into), default)]
1441pub struct SgxEpc {
1442 pub id: Option<String>,
1443 pub size: Option<String>,
1444 pub prefault: Option<OnOff>,
1445}
1446
1447#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
1448pub enum DebugConsoleType {
1449 Off,
1450 Pty,
1451 Tty,
1452 File(PathBuf),
1453}
1454
1455#[derive(Builder, Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
1456#[builder(setter(strip_option, into), default)]
1457pub struct DebugConsole {
1458 pub console_type: Option<DebugConsoleType>,
1459 pub iobase: Option<String>,
1460}