164 lines
5.1 KiB
Plaintext
164 lines
5.1 KiB
Plaintext
// Basic Cloud Hypervisor SAL smoke test (minimal)
|
||
// - Skips gracefully if dependencies or inputs are missing
|
||
// - Creates a VM spec, optionally starts/stops it if all inputs are available
|
||
|
||
print("=== Cloud Hypervisor Basic Tests ===");
|
||
|
||
// Dependency checks (static binaries only)
|
||
let chs = which("cloud-hypervisor-static");
|
||
let chrs = which("ch-remote-static");
|
||
|
||
// Normalize which() results: () or "" both mean missing (depending on SAL which variant)
|
||
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 - skipping CloudHV tests");
|
||
print("Install Cloud Hypervisor static binaries to run these tests.");
|
||
print("=== CloudHV Tests Skipped ===");
|
||
exit();
|
||
}
|
||
|
||
// Inputs (adjust these for your environment)
|
||
// Prefer firmware boot if firmware is available; otherwise fallback to direct kernel boot.
|
||
let firmware_path = "/tmp/virt_images/hypervisor-fw";
|
||
let kernel_path = "/path/to/vmlinux"; // optional when firmware_path is present
|
||
|
||
// We can reuse the base image from the QCOW2 test/builder if present.
|
||
let disk_path = "/tmp/virt_images/noble-server-cloudimg-amd64.img";
|
||
|
||
// Validate inputs
|
||
let missing = false;
|
||
let have_firmware = exist(firmware_path);
|
||
let have_kernel = exist(kernel_path);
|
||
if !have_firmware && !have_kernel {
|
||
print(`⚠️ neither firmware_path (${firmware_path}) nor kernel_path (${kernel_path}) found (start/stop will be skipped)`);
|
||
missing = true;
|
||
}
|
||
if !exist(disk_path) {
|
||
print(`⚠️ disk_path not found: ${disk_path} (start/stop will be skipped)`);
|
||
missing = true;
|
||
}
|
||
|
||
// Unique id
|
||
let rid = run_silent("date +%s%N");
|
||
let suffix = if rid.success && rid.stdout != "" { rid.stdout.trim() } else { "100000" };
|
||
let vm_id = `testvm_${suffix}`;
|
||
|
||
print("\n--- Test 1: Create VM definition ---");
|
||
let spec = #{
|
||
"id": vm_id,
|
||
"disk_path": disk_path,
|
||
"api_socket": "", // default under VM dir
|
||
"vcpus": 1,
|
||
"memory_mb": 1024,
|
||
// For firmware boot:
|
||
// Provide firmware_path only if it exists
|
||
// For kernel boot:
|
||
// Provide kernel_path and optionally a cmdline
|
||
};
|
||
if have_firmware {
|
||
spec.firmware_path = firmware_path;
|
||
} else if have_kernel {
|
||
spec.kernel_path = kernel_path;
|
||
spec.cmdline = "console=ttyS0 reboot=k panic=1";
|
||
}
|
||
// "extra_args": can be added if needed, e.g.:
|
||
// spec.extra_args = ["--rng", "src=/dev/urandom"];
|
||
|
||
try {
|
||
let created_id = cloudhv_vm_create(spec);
|
||
print(`✓ VM created: ${created_id}`);
|
||
} catch (err) {
|
||
print(`❌ VM create failed: ${err}`);
|
||
print("=== CloudHV Tests Aborted ===");
|
||
exit();
|
||
}
|
||
|
||
print("\n--- Test 2: VM info ---");
|
||
try {
|
||
let info = cloudhv_vm_info(vm_id);
|
||
print(`✓ VM info loaded: id=${info.spec.id}, status=${info.runtime.status}`);
|
||
} catch (err) {
|
||
print(`❌ VM info failed: ${err}`);
|
||
print("=== CloudHV Tests Aborted ===");
|
||
exit();
|
||
}
|
||
|
||
print("\n--- Test 3: VM list ---");
|
||
try {
|
||
let vms = cloudhv_vm_list();
|
||
print(`✓ VM list size: ${vms.len()}`);
|
||
} catch (err) {
|
||
print(`❌ VM list failed: ${err}`);
|
||
print("=== CloudHV Tests Aborted ===");
|
||
exit();
|
||
}
|
||
|
||
// Start/Stop only if inputs exist
|
||
if !missing {
|
||
print("\n--- Test 4: Start VM ---");
|
||
try {
|
||
cloudhv_vm_start(vm_id);
|
||
print("✓ VM start invoked");
|
||
} catch (err) {
|
||
print(`⚠️ VM start failed (this can happen if kernel/cmdline are incompatible): ${err}`);
|
||
}
|
||
|
||
print("\n waiting for VM to be ready...");
|
||
|
||
// Discover API socket and PID from SAL
|
||
let info1 = cloudhv_vm_info(vm_id);
|
||
let api_sock = info1.spec.api_socket;
|
||
let pid = info1.runtime.pid;
|
||
|
||
// 1) Wait for API socket to appear (up to ~50s)
|
||
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}`);
|
||
|
||
// 2) Probe ch-remote info with retries (up to ~20s)
|
||
if sock_ok {
|
||
let info_ok = false;
|
||
for x in 0..20 {
|
||
let r = run_silent(`ch-remote-static --api-socket ${api_sock} info`);
|
||
if r.success {
|
||
info_ok = true;
|
||
break;
|
||
}
|
||
sleep(1);
|
||
}
|
||
if info_ok {
|
||
print("VM API is ready (ch-remote info OK)");
|
||
} else {
|
||
print("⚠️ VM API did not become ready in time (continuing)");
|
||
}
|
||
} else {
|
||
print("⚠️ API socket not found (continuing)");
|
||
}
|
||
|
||
// print("\n--- Test 5: Stop VM (graceful) ---");
|
||
// try {
|
||
// cloudhv_vm_stop(vm_id, false);
|
||
// print("✓ VM stop invoked (graceful)");
|
||
// } catch (err) {
|
||
// print(`⚠️ VM stop failed: ${err}`);
|
||
// }
|
||
} else {
|
||
print("\n⚠️ Skipping start/stop because required inputs are missing.");
|
||
}
|
||
|
||
// print("\n--- Test 6: Delete VM definition ---");
|
||
// try {
|
||
// cloudhv_vm_delete(vm_id, false);
|
||
// print("✓ VM deleted");
|
||
// } catch (err) {
|
||
// print(`❌ VM delete failed: ${err}`);
|
||
// print("=== CloudHV Tests Aborted ===");
|
||
// exit();
|
||
// }
|
||
|
||
print("\n=== Cloud Hypervisor Basic Tests Completed ==="); |