use std::{fs::copy, path::Path};
use tracing::{debug, info, instrument};
use crate::{
builder::Configuration,
executor::{Action, Executor},
};
use firepilot_models::models::vm::{State, Vm};
#[derive(Debug)]
pub enum FirepilotError {
Setup(String),
Configure(String),
Execute(String),
}
#[derive(Debug)]
pub struct Machine {
executor: Executor,
}
impl Machine {
pub fn new() -> Self {
Machine {
executor: Executor::new(),
}
}
fn copy<P, Q>(from: P, to: Q) -> Result<(), FirepilotError>
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
copy(&from, &to).map_err(|e| {
let msg = format!(
"Failed to copy {:?} to {:?}: {}",
from.as_ref(),
to.as_ref(),
e
);
FirepilotError::Setup(msg)
})?;
Ok(())
}
#[instrument(skip(self, config), fields(id = %config.vm_id))]
pub async fn create(&mut self, mut config: Configuration) -> Result<(), FirepilotError> {
self.executor = match config.executor {
Some(executor) => Ok(executor),
None => Err(FirepilotError::Setup(
"No executor was provided in the configuration".to_string(),
)),
}?;
self.executor.create_workspace()?;
let kernel = config.kernel.unwrap();
for drive in config.storage.iter_mut() {
let new_drive_path = self.executor.chroot().join(&drive.drive_id);
info!("Copy drive {} in the workspace", drive.drive_id);
debug!(
"Drive from {:?} to {:?}",
drive.path_on_host, new_drive_path
);
Machine::copy(&drive.path_on_host, &new_drive_path)?;
drive.path_on_host = new_drive_path.into_os_string().into_string().unwrap();
}
let kernel_path = self.executor.chroot().join("vmlinux");
info!("Copy kernel in the workspace");
debug!(
"Kernel from {:?} to {:?}",
kernel.kernel_image_path, kernel_path
);
Machine::copy(kernel.kernel_image_path.clone(), kernel_path)?;
if let Some(initrd) = kernel.initrd_path.clone() {
Machine::copy(initrd, self.executor.chroot().join("initrd"))?;
}
self.executor.run_socket()?;
info!("Configure microVM");
self.executor.configure_drives(config.storage).await?;
self.executor.configure_boot_source(kernel).await?;
self.executor.configure_network(config.interfaces).await?;
Ok(())
}
pub async fn kill(&mut self) -> Result<(), FirepilotError> {
self.executor.destroy_socket().await?;
Ok(())
}
pub async fn start(&self) -> Result<(), FirepilotError> {
self.executor.send_action(Action::InstanceStart).await?;
Ok(())
}
pub async fn stop(&self) -> Result<(), FirepilotError> {
self.executor.send_action(Action::SendCtrlAltDel).await?;
Ok(())
}
pub async fn pause(&self) -> Result<(), FirepilotError> {
self.executor.set_vm_state(Vm::new(State::Paused)).await?;
Ok(())
}
pub async fn resume(&self) -> Result<(), FirepilotError> {
self.executor.set_vm_state(Vm::new(State::Resumed)).await?;
Ok(())
}
}