use serde::Serialize;
use crate::schemas::{
BalloonConfig, BalloonHintingOp, BalloonStatsUpdate, BalloonUpdate, BootSourceConfig,
CpuConfig, DriveConfig, DriveId, DrivePatch, EntropyConfig, HotplugMemoryConfig,
HotplugMemoryUpdate, IfaceId, InstanceAction, LoggerConfig, MachineConfig, MachineConfigPatch,
MetricsConfig, MmdsConfig, MmdsContents, NetworkInterfaceConfig, NetworkPatch, PmemConfig,
PmemPatch, SerialConfig, SnapshotCreateConfig, SnapshotLoadConfig, VmStateChange, VsockConfig,
};
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum ActionClass {
PreBootConfig,
InstanceStart,
SnapshotCreate,
SnapshotLoad,
VmStateChange,
BalloonResize,
Other,
}
impl ActionClass {
#[must_use]
pub const fn label(self) -> &'static str {
match self {
Self::PreBootConfig => "pre-boot config",
Self::InstanceStart => "InstanceStart",
Self::SnapshotCreate => "PUT /snapshot/create",
Self::SnapshotLoad => "PUT /snapshot/load",
Self::VmStateChange => "PATCH /vm",
Self::BalloonResize => "PATCH /balloon",
Self::Other => "VMM action",
}
}
}
#[derive(Debug)]
pub enum ApiAction {
PutBootSource(BootSourceConfig),
PutDrive(DriveConfig),
PatchDrive(DrivePatch),
DeleteDrive {
drive_id: DriveId,
},
PutNetwork(NetworkInterfaceConfig),
PatchNetwork(NetworkPatch),
DeleteNetwork {
iface_id: IfaceId,
},
PutVsock(VsockConfig),
PutMmds(MmdsContents),
PatchMmds(MmdsContents),
PutMmdsConfig(MmdsConfig),
PutBalloon(BalloonConfig),
PatchBalloon(BalloonUpdate),
PatchBalloonStats(BalloonStatsUpdate),
PatchBalloonHinting {
op: BalloonHintingOp,
},
PutEntropy(EntropyConfig),
PutSerial(SerialConfig),
PutPmem(PmemConfig),
PatchPmem(PmemPatch),
DeletePmem {
pmem_id: DriveId,
},
PutHotplugMemory(HotplugMemoryConfig),
PatchHotplugMemory(HotplugMemoryUpdate),
PutCpuConfig(CpuConfig),
PutMachineConfig(MachineConfig),
PatchMachineConfig(MachineConfigPatch),
PutLogger(LoggerConfig),
PutMetrics(MetricsConfig),
Action(InstanceAction),
PatchVm(VmStateChange),
SnapshotCreate(SnapshotCreateConfig),
SnapshotLoad(SnapshotLoadConfig),
Shutdown,
}
impl ApiAction {
#[must_use]
pub const fn class(&self) -> ActionClass {
match self {
Self::Action(InstanceAction::InstanceStart) => ActionClass::InstanceStart,
Self::Action(_) | Self::Shutdown => ActionClass::Other,
Self::PatchVm(_) => ActionClass::VmStateChange,
Self::PatchBalloon(_) => ActionClass::BalloonResize,
Self::SnapshotCreate(_) => ActionClass::SnapshotCreate,
Self::SnapshotLoad(_) => ActionClass::SnapshotLoad,
_ => ActionClass::PreBootConfig,
}
}
#[must_use]
pub const fn is_pre_boot(&self) -> bool {
matches!(
self,
Self::PutBootSource(_)
| Self::PutDrive(_)
| Self::PutNetwork(_)
| Self::PutVsock(_)
| Self::PutMmds(_)
| Self::PatchMmds(_)
| Self::PutMmdsConfig(_)
| Self::PutBalloon(_)
| Self::PutEntropy(_)
| Self::PutSerial(_)
| Self::PutPmem(_)
| Self::PutHotplugMemory(_)
| Self::PutCpuConfig(_)
| Self::PutMachineConfig(_)
| Self::PatchMachineConfig(_)
| Self::PutLogger(_)
| Self::PutMetrics(_)
| Self::PatchDrive(_)
| Self::PatchNetwork(_)
| Self::Action(InstanceAction::InstanceStart)
| Self::SnapshotLoad(_)
| Self::Shutdown
)
}
#[must_use]
pub const fn is_post_boot(&self) -> bool {
matches!(
self,
Self::PatchVm(_)
| Self::PatchBalloon(_)
| Self::PatchBalloonStats(_)
| Self::PatchBalloonHinting { .. }
| Self::PatchHotplugMemory(_)
| Self::PatchMmds(_)
| Self::PutMmds(_)
| Self::PatchDrive(_)
| Self::PatchNetwork(_)
| Self::PatchPmem(_)
| Self::DeleteDrive { .. }
| Self::DeleteNetwork { .. }
| Self::DeletePmem { .. }
| Self::SnapshotCreate(_)
| Self::Action(InstanceAction::FlushMetrics)
| Self::Shutdown
)
}
#[must_use]
pub const fn label(&self) -> &'static str {
match self {
Self::PutBootSource(_) => "PUT /boot-source",
Self::PutDrive(_) => "PUT /drives/{id}",
Self::PatchDrive(_) => "PATCH /drives/{id}",
Self::DeleteDrive { .. } => "DELETE /drives/{id}",
Self::PutNetwork(_) => "PUT /network-interfaces/{id}",
Self::PatchNetwork(_) => "PATCH /network-interfaces/{id}",
Self::DeleteNetwork { .. } => "DELETE /network-interfaces/{id}",
Self::PutVsock(_) => "PUT /vsock",
Self::PutMmds(_) => "PUT /mmds",
Self::PatchMmds(_) => "PATCH /mmds",
Self::PutMmdsConfig(_) => "PUT /mmds/config",
Self::PutBalloon(_) => "PUT /balloon",
Self::PatchBalloon(_) => "PATCH /balloon",
Self::PatchBalloonStats(_) => "PATCH /balloon/statistics",
Self::PatchBalloonHinting { .. } => "PATCH /balloon/hinting/{op}",
Self::PutEntropy(_) => "PUT /entropy",
Self::PutSerial(_) => "PUT /serial",
Self::PutPmem(_) => "PUT /pmem/{id}",
Self::PatchPmem(_) => "PATCH /pmem/{id}",
Self::DeletePmem { .. } => "DELETE /pmem/{id}",
Self::PutHotplugMemory(_) => "PUT /hotplug/memory",
Self::PatchHotplugMemory(_) => "PATCH /hotplug/memory",
Self::PutCpuConfig(_) => "PUT /cpu-config",
Self::PutMachineConfig(_) => "PUT /machine-config",
Self::PatchMachineConfig(_) => "PATCH /machine-config",
Self::PutLogger(_) => "PUT /logger",
Self::PutMetrics(_) => "PUT /metrics",
Self::Action(_) => "PUT /actions",
Self::PatchVm(_) => "PATCH /vm",
Self::SnapshotCreate(_) => "PUT /snapshot/create",
Self::SnapshotLoad(_) => "PUT /snapshot/load",
Self::Shutdown => "Shutdown",
}
}
}
#[derive(Debug, Clone, Serialize)]
#[serde(untagged)]
pub enum ApiResponse {
NoContent,
Json(serde_json::Value),
Fault {
status: u16,
fault_message: String,
},
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_should_classify_instance_start_for_30s_timeout() {
let action = ApiAction::Action(InstanceAction::InstanceStart);
assert_eq!(action.class(), ActionClass::InstanceStart);
}
#[test]
fn test_should_classify_snapshot_actions_for_5min_timeout() {
let create_label = ActionClass::SnapshotCreate.label();
assert_eq!(create_label, "PUT /snapshot/create");
}
#[test]
fn test_should_classify_pre_boot_config_default() {
let action = ApiAction::PutEntropy(EntropyConfig::default());
assert_eq!(action.class(), ActionClass::PreBootConfig);
}
#[test]
fn test_should_label_actions_human_readable() {
let action = ApiAction::PatchVm(VmStateChange::Paused);
assert_eq!(action.label(), "PATCH /vm");
}
}