Files
herolib_rust/packages/system/virt/tests/rhai/05_cloudhv_diag.rhai
2025-08-21 18:57:20 +02:00

148 lines
3.8 KiB
Plaintext

// Cloud Hypervisor diagnostic script
// Creates a VM, starts CH, verifies PID, API socket, ch-remote info, and tails logs.
print("=== CloudHV Diagnostic ===");
// Dependency check
let chs = which("cloud-hypervisor-static");
let chrs = which("ch-remote-static");
let ch_missing = (chs == () || chs == "");
let chr_missing = (chrs == () || chrs == "");
if ch_missing || chr_missing {
print("cloud-hypervisor-static and/or ch-remote-static not available - aborting.");
exit();
}
// Inputs
let firmware_path = "/tmp/virt_images/hypervisor-fw";
let disk_path = "/tmp/virt_images/noble-server-cloudimg-amd64.img";
if !exist(firmware_path) {
print(`Firmware not found: ${firmware_path}`);
exit();
}
if !exist(disk_path) {
print(`Disk image not found: ${disk_path}`);
exit();
}
// Unique ID
let rid = run_silent("date +%s%N");
let suffix = if rid.success && rid.stdout != "" { rid.stdout.trim() } else { "100000" };
let vm_id = `diagvm_${suffix}`;
// Socket path will be obtained from VM info (SAL populates spec.api_socket after start)
// Build minimal spec; let SAL decide the api_socket under the VM dir
let spec = #{
"id": vm_id,
"disk_path": disk_path,
"vcpus": 1,
"memory_mb": 512
};
spec.firmware_path = firmware_path;
fn pid_alive(p) {
if p == () { return false; }
// Use /proc to avoid noisy "kill: No such process" messages from kill -0
return exist(`/proc/${p}`);
}
fn tail_log(p, n) {
if exist(p) {
let r = run_silent(`tail -n ${n} ${p}`);
if r.success { print(r.stdout); } else { print(r.stderr); }
} else {
print(`Log file not found: ${p}`);
}
}
try {
print("--- Create VM spec ---");
let created = cloudhv_vm_create(spec);
print(`created: ${created}`);
} catch (err) {
print(`create failed: ${err}`);
exit();
}
// Read back info to get SAL-resolved log_file path
let info0 = cloudhv_vm_info(vm_id);
let log_file = info0.runtime.log_file;
// Rely on SAL to handle socket directory creation and stale-socket cleanup
print("--- Start VM ---");
try {
cloudhv_vm_start(vm_id);
print("start invoked");
} catch (err) {
print(`start failed: ${err}`);
tail_log(log_file, 200);
exit();
}
// Fetch PID and discover API socket path from updated spec
let info1 = cloudhv_vm_info(vm_id);
let pid = info1.runtime.pid;
let api_sock = info1.spec.api_socket;
print(`pid=${pid}`);
print(`api_sock_from_sal=${api_sock}`);
// Wait for socket file
let sock_ok = false;
for x in 0..50 {
if exist(api_sock) { sock_ok = true; break; }
sleep(1);
}
print(`api_sock_exists=${sock_ok} path=${api_sock}`);
// Probe ch-remote info
let info_ok = false;
let last_err = "";
if sock_ok {
for x in 0..20 {
let r = run_silent(`ch-remote-static --api-socket ${api_sock} info`);
if r.success {
info_ok = true;
print("ch-remote info OK");
break;
} else {
last_err = if r.stderr != "" { r.stderr } else { r.stdout };
sleep(1);
}
}
}
if !info_ok {
print("ch-remote info FAILED");
if last_err != "" { print(last_err); }
let alive = pid_alive(pid);
print(`pid_alive=${alive}`);
print("--- Last 200 lines of CH log ---");
tail_log(log_file, 200);
print("--- End of log ---");
} else {
print("--- Stop via SAL (force) ---");
try {
cloudhv_vm_stop(vm_id, true);
print("SAL stop invoked (force)");
} catch (err) {
print(`stop failed: ${err}`);
}
// wait for exit (check original PID)
for x in 0..30 {
if !pid_alive(pid) { break; }
sleep(1);
}
print(`pid_alive_after_stop=${pid_alive(pid)}`);
}
print("--- Cleanup ---");
try {
cloudhv_vm_delete(vm_id, false);
print("vm deleted");
} catch (err) {
print(`delete failed: ${err}`);
}
print("=== Diagnostic done ===");