use crate::machine::{MachineInfo, MachineState};
use arcbox_constants::container_network::ContainerNetwork;
use std::path::PathBuf;
use std::time::Duration;
use super::{
DEFAULT_HEALTH_CHECK_INTERVAL_SECS, DEFAULT_IDLE_TIMEOUT_SECS, DEFAULT_MAX_RETRIES,
DEFAULT_STARTUP_TIMEOUT_SECS,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VmLifecycleState {
NotExist,
Creating,
Created,
Starting,
Running,
Idle,
Stopping,
Stopped,
Failed,
}
impl VmLifecycleState {
#[must_use]
pub const fn is_ready(&self) -> bool {
matches!(self, Self::Running | Self::Idle)
}
#[must_use]
pub const fn needs_start(&self) -> bool {
matches!(
self,
Self::NotExist | Self::Created | Self::Stopped | Self::Failed
)
}
#[must_use]
pub const fn as_str(&self) -> &'static str {
match self {
Self::NotExist => "not_exist",
Self::Creating => "creating",
Self::Created => "created",
Self::Starting => "starting",
Self::Running => "running",
Self::Idle => "idle",
Self::Stopping => "stopping",
Self::Stopped => "stopped",
Self::Failed => "failed",
}
}
}
impl From<MachineState> for VmLifecycleState {
fn from(state: MachineState) -> Self {
match state {
MachineState::Created => Self::Created,
MachineState::Starting => Self::Starting,
MachineState::Running => Self::Running,
MachineState::Stopping => Self::Stopping,
MachineState::Stopped => Self::Stopped,
}
}
}
#[derive(Debug, Clone)]
pub struct VmLifecycleConfig {
pub auto_stop: bool,
pub idle_timeout: Duration,
pub startup_timeout: Duration,
pub health_check_interval: Duration,
pub max_retries: u32,
pub default_vm: DefaultVmConfig,
pub skip_vm_check: bool,
pub guest_docker_vsock_port: Option<u32>,
pub container_network: ContainerNetwork,
pub allow_unpinned_boot_manifest: bool,
pub backend: arcbox_vmm::VmBackend,
pub route_hook: Option<RouteHook>,
}
#[derive(Clone)]
pub struct RouteHook(std::sync::Arc<dyn Fn() + Send + Sync>);
impl RouteHook {
#[must_use]
pub fn new(hook: std::sync::Arc<dyn Fn() + Send + Sync>) -> Self {
Self(hook)
}
pub fn call(&self) {
(self.0)();
}
}
impl std::fmt::Debug for RouteHook {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("RouteHook")
}
}
impl Default for VmLifecycleConfig {
fn default() -> Self {
Self {
auto_stop: true,
idle_timeout: Duration::from_secs(DEFAULT_IDLE_TIMEOUT_SECS),
startup_timeout: Duration::from_secs(DEFAULT_STARTUP_TIMEOUT_SECS),
health_check_interval: Duration::from_secs(DEFAULT_HEALTH_CHECK_INTERVAL_SECS),
max_retries: DEFAULT_MAX_RETRIES,
default_vm: DefaultVmConfig::default(),
skip_vm_check: false,
guest_docker_vsock_port: None,
container_network: ContainerNetwork::default(),
allow_unpinned_boot_manifest: false,
backend: arcbox_vmm::VmBackend::default(),
route_hook: None,
}
}
}
#[derive(Debug, Clone)]
pub struct DefaultVmConfig {
pub cpus: u32,
pub memory_mb: u64,
pub disk_gb: u64,
pub kernel: Option<PathBuf>,
pub cmdline: Option<String>,
pub rosetta: bool,
}
impl Default for DefaultVmConfig {
fn default() -> Self {
Self {
cpus: arcbox_hypervisor::default_vm_cpu_count(),
memory_mb: arcbox_hypervisor::default_vm_memory_size() / (1024 * 1024),
disk_gb: 50,
kernel: None,
cmdline: None,
rosetta: cfg!(target_arch = "aarch64"),
}
}
}
pub(super) struct DesiredBoot {
pub(super) kernel: String,
pub(super) cmdline: String,
pub(super) rootfs_image: PathBuf,
}
pub(super) fn machine_drift_reason(
persisted: &MachineInfo,
want: &DefaultVmConfig,
boot: Option<&DesiredBoot>,
) -> Option<&'static str> {
if persisted.cpus != want.cpus {
Some("cpus")
} else if persisted.memory_mb != want.memory_mb {
Some("memory_mb")
} else if persisted.block_devices.len() != BASE_MACHINE_DISK_COUNT {
Some("block_devices")
} else if boot.is_some_and(|boot| persisted.kernel.as_deref() != Some(boot.kernel.as_str())) {
Some("kernel")
} else if boot.is_some_and(|boot| persisted.cmdline.as_deref() != Some(boot.cmdline.as_str())) {
Some("cmdline")
} else {
None
}
}
const BASE_MACHINE_DISK_COUNT: usize = 3;
pub(super) fn metadata_image_filename(data_image_filename: &str) -> String {
let stem = data_image_filename
.strip_suffix(".img")
.unwrap_or(data_image_filename);
format!("{stem}-meta.img")
}