use std::convert::AsRef;
use std::path::{Path, PathBuf};
use std::sync::mpsc::Sender;
use anyhow::{anyhow, bail, Context, Result};
use crate::config::{Config, Target};
use crate::output::Output;
use crate::qemu::Qemu;
pub struct Vmtest {
base: PathBuf,
config: Config,
}
fn validate_config(config: &Config) -> Result<()> {
for (idx, target) in config.target.iter().enumerate() {
if target.name.is_empty() {
bail!("Target index={} name empty", idx);
}
match (&target.image, &target.kernel) {
(None, None) => bail!("Target '{}' must specify 'image' or 'kernel'", target.name),
(Some(_), Some(_)) => bail!(
"Target '{}' specified both 'image' and 'kernel'",
target.name
),
_ => (),
};
if target.uefi && target.image.is_none() {
bail!("Target '{}' must specify 'image' with 'uefi'", target.name);
}
if !target.uefi && target.vm.bios.is_some() {
bail!(
"Target '{}' cannot specify a bios without setting 'uefi'",
target.name
);
}
if target.kernel_args.is_some() && target.kernel.is_none() {
bail!(
"Target '{}' must specify 'kernel' with 'kernel_args'",
target.name
);
}
if let Some(image) = &target.image {
if image.as_os_str().is_empty() {
bail!("Target '{}' has empty image path", target.name);
}
}
if let Some(kernel) = &target.kernel {
if kernel.as_os_str().is_empty() {
bail!("Target '{}' has empty kernel path", target.name);
}
}
if target.command.is_empty() {
bail!("Target '{}' has empty command", target.name);
}
}
Ok(())
}
impl Vmtest {
pub fn new<T: AsRef<Path>>(path: T, config: Config) -> Result<Self> {
validate_config(&config).context("Invalid config")?;
Ok(Self {
base: path.as_ref().to_owned(),
config,
})
}
fn resolve_path(&self, input: &Path) -> PathBuf {
if input.is_relative() {
let mut r = self.base.clone();
r.push(input);
r
} else {
input.to_owned()
}
}
pub fn targets(&self) -> &[Target] {
&self.config.target
}
fn setup_qemu(&self, idx: usize, updates: Sender<Output>) -> Result<Qemu> {
let mut target: Target = self
.config
.target
.get(idx)
.ok_or_else(|| anyhow!("idx={} out of range", idx))?
.clone();
target.image = target.image.map(|s| self.resolve_path(s.as_path()));
target.kernel = target.kernel.map(|s| self.resolve_path(s.as_path()));
target.rootfs = self.resolve_path(target.rootfs.as_path());
target.vm.bios = target.vm.bios.map(|s| self.resolve_path(s.as_path()));
target.vm.mounts.iter_mut().for_each(|(_, m)| {
m.host_path = self.resolve_path(m.host_path.as_path());
});
Qemu::new(updates, target, &self.base).context("Failed to setup QEMU")
}
pub fn run_one(&self, idx: usize, updates: Sender<Output>) {
match self.setup_qemu(idx, updates.clone()) {
Ok(q) => q.run(),
Err(e) => {
let _ = updates.send(Output::BootEnd(Err(e)));
}
};
}
}