pub mod actions;
pub mod balloon;
pub mod boot_source;
pub mod common;
pub mod config_file;
pub mod cpu_config;
pub mod drive;
pub mod entropy;
pub mod hotplug_memory;
pub mod logger;
pub mod machine_config;
pub mod metrics;
pub mod mmds;
pub mod network;
pub mod pmem;
pub mod serial;
pub mod snapshot;
pub mod vm;
pub mod vsock;
pub use actions::{InstanceAction, InstanceActionInfo};
pub use balloon::{BalloonConfig, BalloonHintingOp, BalloonStatsUpdate, BalloonUpdate};
pub use boot_source::BootSourceConfig;
pub use common::{
DriveId, IfaceId, InstanceId, MAX_DRIVES, MAX_NICS, MAX_PMEM, MAX_VIRTIO_MEM, MacAddr,
MemSizeMib, SafePath, UdsPath, VsockId,
};
pub use config_file::ConfigFile;
pub use cpu_config::CpuConfig;
pub use drive::{DriveConfig, DrivePatch};
pub use entropy::EntropyConfig;
pub use hotplug_memory::{HotplugMemoryConfig, HotplugMemoryUpdate};
pub use logger::LoggerConfig;
pub use machine_config::{MachineConfig, MachineConfigPatch};
pub use metrics::MetricsConfig;
pub use mmds::{MmdsConfig, MmdsContents};
pub use network::{NetworkInterfaceConfig, NetworkPatch};
pub use pmem::{PmemConfig, PmemPatch};
use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
pub use serial::SerialConfig;
pub use snapshot::{SnapshotCreateConfig, SnapshotLoadConfig};
use squib_core::WireVmState;
pub use vm::VmStateChange;
pub use vsock::VsockConfig;
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct VersionResponse {
pub firecracker_version: String,
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct InstanceInfo {
pub id: String,
pub state: VmState,
pub vmm_version: String,
pub app_name: String,
}
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
pub enum VmState {
#[default]
NotStarted,
Running,
Paused,
}
impl VmState {
#[must_use]
pub const fn as_wire_str(self) -> &'static str {
match self {
Self::NotStarted => "Not started",
Self::Running => "Running",
Self::Paused => "Paused",
}
}
}
impl From<WireVmState> for VmState {
fn from(s: WireVmState) -> Self {
match s {
WireVmState::NotStarted => Self::NotStarted,
WireVmState::Running => Self::Running,
WireVmState::Paused => Self::Paused,
}
}
}
impl Serialize for VmState {
fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
ser.serialize_str(self.as_wire_str())
}
}
impl<'de> Deserialize<'de> for VmState {
fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
let s = <&str>::deserialize(de)?;
match s {
"Not started" => Ok(Self::NotStarted),
"Running" => Ok(Self::Running),
"Paused" => Ok(Self::Paused),
other => Err(de::Error::unknown_variant(
other,
&["Not started", "Running", "Paused"],
)),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_should_serialize_version_response_in_snake_case() {
let v = VersionResponse {
firecracker_version: "1.16.0".into(),
};
let json = serde_json::to_string(&v).unwrap();
assert_eq!(json, r#"{"firecracker_version":"1.16.0"}"#);
}
#[test]
fn test_should_round_trip_instance_info() {
let original = InstanceInfo {
id: "anonymous".into(),
state: VmState::NotStarted,
vmm_version: "1.16.0 (squib 0.1.0)".into(),
app_name: "Firecracker".into(),
};
let json = serde_json::to_string(&original).unwrap();
let back: InstanceInfo = serde_json::from_str(&json).unwrap();
assert_eq!(original, back);
}
#[test]
fn test_should_serialize_vm_state_to_upstream_strings_verbatim() {
assert_eq!(
serde_json::to_string(&VmState::NotStarted).unwrap(),
r#""Not started""#
);
assert_eq!(
serde_json::to_string(&VmState::Running).unwrap(),
r#""Running""#
);
assert_eq!(
serde_json::to_string(&VmState::Paused).unwrap(),
r#""Paused""#
);
}
#[test]
fn test_should_reject_pascalcase_vm_state_on_deserialize() {
let s: VmState = serde_json::from_str(r#""Not started""#).unwrap();
assert_eq!(s, VmState::NotStarted);
assert!(serde_json::from_str::<VmState>(r#""NotStarted""#).is_err());
}
#[test]
fn test_should_collapse_lifecycle_phase_to_vm_state() {
use squib_core::LifecyclePhase;
let cases = [
(LifecyclePhase::Uninitialized, VmState::NotStarted),
(LifecyclePhase::NotStarted, VmState::NotStarted),
(LifecyclePhase::Starting, VmState::NotStarted),
(LifecyclePhase::Shutdown, VmState::NotStarted),
(LifecyclePhase::Running, VmState::Running),
(LifecyclePhase::Paused, VmState::Paused),
];
for (phase, expected) in cases {
assert_eq!(VmState::from(phase.wire_state()), expected, "{phase:?}");
}
}
}