use std::net::IpAddr;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use crate::error::Result;
use crate::spec::{MacAddr, NicSpec, VmId};
#[async_trait]
pub trait GuestNetwork: Send + Sync {
async fn reserve(&self, vm: &VmId, policy: NetworkPolicy) -> Result<NetworkLease>;
async fn activate(&self, lease: &NetworkLease, mode: AttachMode) -> Result<NicSpec>;
async fn adopt(&self, lease: &NetworkLease, mode: AttachMode) -> Result<()>;
async fn quarantine(&self, lease: NetworkLease) -> Result<()>;
async fn release(&self, lease: NetworkLease) -> Result<()>;
fn hold_address(&self, address: IpAddr);
fn identity(&self, lease: &NetworkLease, mode: AttachMode) -> NetworkIdentity;
fn host_ingress(&self, lease: &NetworkLease) -> Result<HostIngress>;
fn reconcile(&self) -> Option<&dyn NetworkReconcile> {
None
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum HostIngress {
PoolAddress,
GuestAddress {
fwmark: u32,
},
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct NetworkPolicy {
pub mode: NetworkMode,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum NetworkMode {
Isolated,
Nat,
Bridged,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AttachMode {
Invariant,
LegacySnapshot,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct NetworkLease {
pub vm: VmId,
pub ip: IpAddr,
pub prefix_len: u8,
pub gateway: IpAddr,
pub mac: MacAddr,
pub cleanup_token: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct NetworkIdentity {
pub ip: IpAddr,
pub prefix_len: u8,
pub gateway: IpAddr,
pub dns: Vec<IpAddr>,
pub mac: MacAddr,
}
#[async_trait]
pub trait NetworkReconcile: Send + Sync {
async fn pending_cleanups(&self) -> Result<Vec<(VmId, String)>>;
async fn validate_cleanup(&self, vm: &VmId, token: &str) -> Result<NetworkLease>;
async fn finalize_cleanup(&self, vm: &VmId, token: &str) -> Result<()>;
async fn startup_cleanup_token(&self) -> Option<String>;
async fn validate_startup_cleanup(&self, token: &str) -> Result<()>;
async fn finalize_startup_cleanup(&self, token: &str) -> Result<()>;
async fn wait_startup_cleanup_complete(&self);
fn replay_complete(&self);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn lease_round_trips_through_json() {
let lease = NetworkLease {
vm: VmId::new("vm-1").unwrap(),
ip: "10.200.0.2".parse().unwrap(),
prefix_len: 16,
gateway: "10.200.0.1".parse().unwrap(),
mac: "02:fa:ce:00:00:02".parse().unwrap(),
cleanup_token: "gen-1".into(),
};
let json = serde_json::to_string(&lease).unwrap();
assert_eq!(serde_json::from_str::<NetworkLease>(&json).unwrap(), lease);
assert!(json.contains("\"02:fa:ce:00:00:02\""));
assert!(json.contains("\"10.200.0.2\""));
}
}