//! The `new` sub-command. use crate::cloudinit::{CloudInitConfig, CloudInitError}; use crate::image::{ImageError, VirtualMachineImage}; use crate::install::{virt_install, VirtInstallArgs, VirtInstallError}; use crate::libvirt::{Libvirt, VirtError}; use crate::spec::Specification; use crate::util::wait_for_ssh; use bytesize::GIB; use log::info; use tempfile::tempdir; /// Errors returned by this module. #[derive(Debug, thiserror::Error)] pub enum NewError { /// Problem with cloud-init configuration. #[error(transparent)] CloudInitError(#[from] CloudInitError), /// Problem creating VM image. #[error(transparent)] ImageError(#[from] ImageError), /// Problem with libvirt. #[error(transparent)] VirtInstallError(#[from] VirtInstallError), /// Problem with virsh. #[error(transparent)] VirshError(#[from] std::io::Error), /// Problem from libvirt server. #[error(transparent)] VirtError(#[from] VirtError), } /// The `new` sub-command. /// /// Create all the new virtual machines specified by the caller. Wait /// until each VM's SSH port listens for connections. pub fn new(specs: &[Specification]) -> Result<(), NewError> { let libvirt = Libvirt::connect("qemu:///system")?; for spec in specs { info!("creating new VM {}", spec.name); info!("creating cloud-init config"); let init = CloudInitConfig::from(&spec)?; info!( "creating VM image {} from {}", spec.image.display(), spec.base.display() ); let image = VirtualMachineImage::new_from_base(&spec.base, &spec.image)?; info!("resizing image to {} GiB", spec.image_size_gib); image.resize(spec.image_size_gib * GIB)?; info!("creating VM"); let dir = tempdir()?; let iso = dir.path().join("cloudinit.iso"); let mut args = VirtInstallArgs::new(&spec.name, &image, &init); args.set_memory(spec.memory_mib); args.set_vcpus(spec.cpus); virt_install(&args, &iso)?; info!("waiting for {} to open its SSH port", spec.name); wait_for_ssh(&spec.name); libvirt.detach_cloud_init_iso(&spec.name)?; } Ok(()) }