use std::{collections::BTreeMap, net::Ipv4Addr};
use jsonrpsee_types::{DeserializeOwned, JsonValue};
use crate::{declare_id_type, impl_xo_object, types::XoObject};
#[derive(serde::Deserialize, Debug)]
pub struct Vm<O> {
pub id: VmId,
pub name_label: String,
pub name_description: String,
pub power_state: PowerState,
#[serde(rename = "$pool")]
pub pool: String,
pub tags: Vec<String>,
#[serde(default)]
pub(crate) addresses: BTreeMap<String, String>,
#[serde(deserialize_with = "map_from_optional_map", default)]
pub os_version: BTreeMap<String, String>,
pub other: O,
}
impl<O: DeserializeOwned> XoObject for Vm<O> {
const OBJECT_TYPE: &'static str = "VM";
type IdType = VmId;
}
#[derive(Debug, Clone, Copy, serde::Deserialize, PartialEq, Eq)]
pub enum PowerState {
Running,
Halted,
Suspended,
Paused,
}
impl<'a, O: serde::de::DeserializeOwned> Vm<O> {
pub fn is_running(&self) -> bool {
matches!(self.power_state, PowerState::Running)
}
pub fn distro(&self) -> Option<&str> {
match &self.os_version.get("distro") {
Some(distro) => Some(distro),
None if self.os_version.contains_key("spmajor") => Some("windows"),
None => None,
}
}
pub fn ipv4_addresses(&self) -> impl Iterator<Item = Ipv4Addr> + '_ {
self.addresses
.iter()
.filter(|(tag, _ip)| tag.contains("ipv4"))
.flat_map(|(_tag, ip)| ip.split(' '))
.filter_map(move |ip| match ip.parse() {
Ok(ip) => Some(ip),
Err(e) => {
log::warn!("Invalid IP found for VM: {}, {:?}", self.name_label, e);
None
}
})
}
}
fn map_from_optional_map<'de, D>(des: D) -> Result<BTreeMap<String, String>, D::Error>
where
D: serde::de::Deserializer<'de>,
{
let option: Option<_> = serde::de::Deserialize::deserialize(des)?;
Ok(option.unwrap_or_default())
}
pub trait OtherInfo: serde::de::DeserializeOwned {}
impl OtherInfo for BTreeMap<String, String> {}
impl OtherInfo for std::collections::HashMap<String, String> {}
declare_id_type! {
pub struct VmId;
}
declare_id_type! {
pub struct SnapshotId;
}
#[derive(Debug, PartialEq, Clone, Eq, PartialOrd, Ord)]
pub struct VmOrSnapshotId(pub(crate) String);
impl From<SnapshotId> for VmOrSnapshotId {
fn from(id: SnapshotId) -> Self {
VmOrSnapshotId(id.0)
}
}
impl From<VmOrSnapshotId> for JsonValue {
fn from(id: VmOrSnapshotId) -> Self {
JsonValue::String(id.0)
}
}
impl From<VmId> for VmOrSnapshotId {
fn from(id: VmId) -> Self {
VmOrSnapshotId(id.0)
}
}
#[derive(serde::Deserialize, Debug)]
pub struct Snapshot {
pub id: SnapshotId,
pub name_label: String,
pub name_description: String,
}
impl_xo_object!(Snapshot => "VM-snapshot", SnapshotId);