//! Look up a known machine ID use std::sync::Arc; use serde_json::Value; use tokio::task; use tracing::{debug, error}; use uuid::Uuid; use crate::config::Configuration; use crate::libvirt; /// Look up the machine ID for a host /// /// This function will return the machine ID for a known host, either from the /// static map stored in a JSON file or from a libvirt VM host. If no location /// has a record for the machine, `None` is returned. pub async fn get_machine_id( hostname: &str, config: Arc, ) -> Option { if let Some(v) = from_map(hostname, config.clone()).await { return parse_uuid(&v); } #[cfg(feature = "libvirt")] if let Some(v) = from_libvirt(hostname, config.clone()).await { return parse_uuid(&v); } None } /// Look up a machine ID in the JSON map file /// /// This function reads the machine ID map stored in the JSON file. If the /// file could not be opened, is not a valid JSON object, or does not contain /// an entry for the specified hostname, `None` is returned. async fn from_map( hostname: &str, config: Arc, ) -> Option { let data = match tokio::fs::read_to_string(&config.machine_ids).await { Ok(d) => d, Err(e) => { error!("Failed to read machine ID map file: {}", e); return None; } }; let res = task::spawn_blocking(move || serde_json::from_str::(&data)) .await; let map = match res { Ok(Ok(r)) => r, Ok(Err(e)) => { error!("Error parsing machine ID map: {}", e); return None; } Err(e) => { error!("Unexpected error while parsing JSON: {}", e); return None; } }; Some(map.as_object()?.get(hostname)?.as_str()?.to_owned()) } /// Look up a machine ID on the configured libvirt VM hosts /// /// This function iterates over the configured VM hosts and checks each one for /// a domain matching the specified hostname. If no VM host is available or /// none of the configured hosts have a matching domain, `None` is returned. #[cfg(feature = "libvirt")] async fn from_libvirt( hostname: &str, config: Arc, ) -> Option { let hostname = Arc::new(hostname.split('.').next().unwrap().to_string()); let res = task::spawn_blocking(move || { for libvirt in &config.libvirt { debug!("Checking {} for {}", libvirt.uri, hostname); match libvirt::get_machine_id(&hostname, libvirt) { Ok(v) => return Some(v), Err(e) => { debug!("libvirt error: {}", e); } }; } None }) .await; match res { Ok(v) => v, Err(e) => { error!("Unexpected error while querying libvirt: {}", e); None } } } /// Parse a UUID from a string /// /// Returns `None` if the string does not contain a valid UUID. fn parse_uuid(value: &str) -> Option { Uuid::parse_str(value).map_or_else( |e| { error!("Invalid UUID: {}", e); None }, Some, ) }