Files
herolib_rust/packages/system/virt/src/rhai/cloudhv.rs
Maxime Van Hees f4512b66cf wip
2025-09-01 16:12:50 +02:00

183 lines
5.7 KiB
Rust

use crate::cloudhv;
use crate::cloudhv::{VmRecord, VmRuntime, VmSpec};
use rhai::{Array, Dynamic, Engine, EvalAltResult, Map};
// Error adapter
fn hv_to_rhai<T>(r: Result<T, cloudhv::CloudHvError>) -> Result<T, Box<EvalAltResult>> {
r.map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("cloudhv error: {}", e).into(),
rhai::Position::NONE,
))
})
}
// Map conversions
fn map_to_vmspec(spec: Map) -> Result<VmSpec, Box<EvalAltResult>> {
let id = must_get_string(&spec, "id")?;
let kernel_path = get_string(&spec, "kernel_path");
let initramfs_path = get_string(&spec, "initramfs_path");
let firmware_path = get_string(&spec, "firmware_path");
let disk_path = must_get_string(&spec, "disk_path")?;
let api_socket = get_string(&spec, "api_socket").unwrap_or_else(|| "".to_string());
let vcpus = get_int(&spec, "vcpus").unwrap_or(1) as u32;
let memory_mb = get_int(&spec, "memory_mb").unwrap_or(512) as u32;
let cmdline = get_string(&spec, "cmdline");
let extra_args = get_string_array(&spec, "extra_args");
Ok(VmSpec {
id,
kernel_path,
initramfs_path,
firmware_path,
disk_path,
api_socket,
vcpus,
memory_mb,
cmdline,
extra_args,
net_profile: None,
})
}
fn vmspec_to_map(s: &VmSpec) -> Map {
let mut m = Map::new();
m.insert("id".into(), s.id.clone().into());
if let Some(k) = &s.kernel_path {
m.insert("kernel_path".into(), k.clone().into());
} else {
m.insert("kernel_path".into(), Dynamic::UNIT);
}
if let Some(ir) = &s.initramfs_path {
m.insert("initramfs_path".into(), ir.clone().into());
} else {
m.insert("initramfs_path".into(), Dynamic::UNIT);
}
if let Some(fw) = &s.firmware_path {
m.insert("firmware_path".into(), fw.clone().into());
} else {
m.insert("firmware_path".into(), Dynamic::UNIT);
}
m.insert("disk_path".into(), s.disk_path.clone().into());
m.insert("api_socket".into(), s.api_socket.clone().into());
m.insert("vcpus".into(), (s.vcpus as i64).into());
m.insert("memory_mb".into(), (s.memory_mb as i64).into());
if let Some(c) = &s.cmdline {
m.insert("cmdline".into(), c.clone().into());
} else {
m.insert("cmdline".into(), Dynamic::UNIT);
}
if let Some(arr) = &s.extra_args {
let mut a = Array::new();
for s in arr {
a.push(s.clone().into());
}
m.insert("extra_args".into(), a.into());
} else {
m.insert("extra_args".into(), Dynamic::UNIT);
}
// net_profile not exposed in Rhai yet; return UNIT for now
m.insert("net_profile".into(), Dynamic::UNIT);
m
}
fn vmruntime_to_map(r: &VmRuntime) -> Map {
let mut m = Map::new();
match r.pid {
Some(p) => m.insert("pid".into(), (p as i64).into()),
None => m.insert("pid".into(), Dynamic::UNIT),
};
m.insert("status".into(), r.status.clone().into());
m.insert("log_file".into(), r.log_file.clone().into());
m
}
fn vmrecord_to_map(rec: &VmRecord) -> Map {
let mut m = Map::new();
m.insert("spec".into(), vmspec_to_map(&rec.spec).into());
m.insert("runtime".into(), vmruntime_to_map(&rec.runtime).into());
m
}
// Helpers for reading Rhai Map fields
fn must_get_string(m: &Map, k: &str) -> Result<String, Box<EvalAltResult>> {
match m.get(k) {
Some(v) if v.is_string() => Ok(v.clone().cast::<String>()),
_ => Err(Box::new(EvalAltResult::ErrorRuntime(
format!("missing or non-string field '{}'", k).into(),
rhai::Position::NONE,
))),
}
}
fn get_string(m: &Map, k: &str) -> Option<String> {
m.get(k).and_then(|v| if v.is_string() { Some(v.clone().cast::<String>()) } else { None })
}
fn get_int(m: &Map, k: &str) -> Option<i64> {
m.get(k).and_then(|v| v.as_int().ok())
}
fn get_string_array(m: &Map, k: &str) -> Option<Vec<String>> {
m.get(k).and_then(|v| {
if v.is_array() {
let arr = v.clone().cast::<Array>();
let mut out = vec![];
for it in arr {
if it.is_string() {
out.push(it.cast::<String>());
}
}
Some(out)
} else {
None
}
})
}
// Rhai-exposed functions
pub fn cloudhv_vm_create(spec: Map) -> Result<String, Box<EvalAltResult>> {
let s = map_to_vmspec(spec)?;
hv_to_rhai(cloudhv::vm_create(&s))
}
pub fn cloudhv_vm_start(id: &str) -> Result<(), Box<EvalAltResult>> {
hv_to_rhai(cloudhv::vm_start(id))
}
pub fn cloudhv_vm_stop(id: &str, force: bool) -> Result<(), Box<EvalAltResult>> {
hv_to_rhai(cloudhv::vm_stop(id, force))
}
pub fn cloudhv_vm_delete(id: &str, delete_disks: bool) -> Result<(), Box<EvalAltResult>> {
hv_to_rhai(cloudhv::vm_delete(id, delete_disks))
}
pub fn cloudhv_vm_list() -> Result<Array, Box<EvalAltResult>> {
let vms = hv_to_rhai(cloudhv::vm_list())?;
let mut arr = Array::new();
for rec in vms {
arr.push(vmrecord_to_map(&rec).into());
}
Ok(arr)
}
pub fn cloudhv_vm_info(id: &str) -> Result<Map, Box<EvalAltResult>> {
let rec = hv_to_rhai(cloudhv::vm_info(id))?;
Ok(vmrecord_to_map(&rec))
}
// Module registration
pub fn register_cloudhv_module(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
engine.register_fn("cloudhv_vm_create", cloudhv_vm_create);
engine.register_fn("cloudhv_vm_start", cloudhv_vm_start);
engine.register_fn("cloudhv_vm_stop", cloudhv_vm_stop);
engine.register_fn("cloudhv_vm_delete", cloudhv_vm_delete);
engine.register_fn("cloudhv_vm_list", cloudhv_vm_list);
engine.register_fn("cloudhv_vm_info", cloudhv_vm_info);
Ok(())
}