use std::fmt;
use std::path::{Path, PathBuf};
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use crate::driver::{ExitStatus, RestoreSpec, VmHandle, VmRecord, VsockConn};
use crate::error::Result;
use crate::spec::{IsolationSpec, VmId, VmSpec};
#[async_trait]
pub trait Vsock: Send + Sync {
async fn dial(&self, port: u32) -> Result<VsockConn>;
}
#[async_trait]
pub trait Prepare: Send + Sync {
async fn prepare(
&self,
id: &VmId,
isolation: &IsolationSpec,
runtime_dir: &Path,
) -> Result<Box<dyn PreparedVm>>;
}
#[async_trait]
pub trait PreparedVm: Send + Sync {
fn id(&self) -> &VmId;
fn record(&self) -> VmRecord;
fn alive(&self) -> bool;
fn vsock_listener(&self) -> Option<&dyn VsockListen> {
None
}
fn staging(&self) -> Option<&dyn Staging> {
None
}
async fn boot(&self, spec: VmSpec) -> Result<Box<dyn VmHandle>>;
async fn restore(
&self,
image: &CheckpointImage,
spec: RestoreSpec,
) -> Result<Box<dyn VmHandle>>;
async fn discard(&self) -> Result<ExitStatus>;
}
#[async_trait]
pub trait Staging: Send + Sync {
async fn stage_kernel(&self, src: &Path) -> Result<PathBuf>;
async fn stage_disk(&self, id: &str, source: DiskSource<'_>) -> Result<PathBuf>;
async fn unstage_disk(&self, id: &str, dst: &Path) -> Result<bool>;
async fn stage_checkpoint(&self, image: &CheckpointImage) -> Result<CheckpointImage>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DiskSource<'a> {
Device(&'a Path),
Image(&'a Path),
Handover(&'a Path),
}
impl<'a> DiskSource<'a> {
pub fn path(&self) -> &'a Path {
match self {
Self::Device(path) | Self::Image(path) | Self::Handover(path) => path,
}
}
}
#[async_trait]
pub trait VsockListen: Send + Sync {
async fn listen(&self, port: u32) -> Result<Box<dyn VsockListener>>;
}
#[async_trait]
pub trait VsockListener: Send {
async fn accept(&mut self) -> Result<VsockConn>;
}
#[async_trait]
pub trait Checkpoint: Send + Sync {
async fn checkpoint(&self, dst: &Path, opts: CheckpointOptions) -> Result<CheckpointImage>;
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct CheckpointOptions {
pub after: AfterCheckpoint,
pub kind: CheckpointKind,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum AfterCheckpoint {
#[default]
Resume,
HoldQuiesced,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CheckpointKind {
#[default]
Full,
Diff,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CheckpointImage {
pub dir: PathBuf,
pub format: CheckpointFormat,
pub kind: CheckpointKind,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct CheckpointFormat(String);
impl CheckpointFormat {
pub fn new(name: impl Into<String>) -> Self {
Self(name.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl fmt::Display for CheckpointFormat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
impl AsRef<str> for CheckpointFormat {
fn as_ref(&self) -> &str {
&self.0
}
}
#[async_trait]
pub trait Adopt: Send + Sync {
async fn adopt(&self, record: &VmRecord) -> Result<Option<Box<dyn VmHandle>>>;
async fn discard_area(&self, record: &VmRecord, isolation: &IsolationSpec) -> Result<()>;
}
#[async_trait]
pub trait Detach: Send + Sync {
async fn detach(&self) -> Result<VmRecord>;
}
#[async_trait]
pub trait Balloon: Send + Sync {
async fn set_target(&self, bytes: u64) -> Result<()>;
async fn stats(&self) -> Result<BalloonStats>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BalloonStats {
pub target_bytes: u64,
pub current_bytes: Option<u64>,
}
#[async_trait]
pub trait Console: Send + Sync {
async fn read_output(&self, max_bytes: usize) -> Result<Vec<u8>>;
}
pub trait DebugSnapshot: Send + Sync {
fn snapshot(&self) -> serde_json::Value;
}