sshca/server/src/machine_id.rs

109 lines
3.1 KiB
Rust

//! 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<Configuration>,
) -> Option<Uuid> {
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<Configuration>,
) -> Option<String> {
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::<Value>(&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<Configuration>,
) -> Option<String> {
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> {
Uuid::parse_str(value).map_or_else(
|e| {
error!("Invalid UUID: {}", e);
None
},
Some,
)
}