WIP: automating VM deployment
This commit is contained in:
234
packages/system/virt/tests/rhai/10_vm_end_to_end.rhai
Normal file
234
packages/system/virt/tests/rhai/10_vm_end_to_end.rhai
Normal file
@@ -0,0 +1,234 @@
|
||||
// End-to-end smoke test for the new qcow2 + cloud-hypervisor refactor
|
||||
// This script executes in logical phases so we can see clearly what works.
|
||||
//
|
||||
// Phases:
|
||||
// 1) Host preflight check
|
||||
// 2) Image preparation (Ubuntu) -> raw disk
|
||||
// 3) Launch VM via builder using prepared raw disk
|
||||
// 4) Inspect VM info, list VMs
|
||||
// 5) Stop & delete VM
|
||||
// 6) Launch VM via one-shot wrapper vm_easy_launch
|
||||
// 7) Inspect VM info, list VMs
|
||||
// 8) Stop & delete VM
|
||||
//
|
||||
// Notes:
|
||||
// - Run as root on the host (required for NBD/mount/networking).
|
||||
// - Base images expected at:
|
||||
// /images/noble-server-cloudimg-amd64.img
|
||||
// /images/alpine-virt-cloudimg-amd64.qcow2 (Alpine prepare not implemented yet)
|
||||
// /images/hypervisor-fw (firmware binary used via --kernel)
|
||||
// - Network defaults: IPv4 NAT + dnsmasq DHCP; placeholder IPv6 on bridge + guest netplan.
|
||||
//
|
||||
// Conventions:
|
||||
// - Functional builder chaining: b = memory_mb(b, 4096), etc.
|
||||
// - Each phase prints a banner and either "OK" or "FAILED" with detailed error message.
|
||||
|
||||
fn banner(s) {
|
||||
print("==================================================");
|
||||
print(s);
|
||||
print("==================================================");
|
||||
}
|
||||
|
||||
fn ok(s) {
|
||||
print("[OK] " + s);
|
||||
}
|
||||
|
||||
fn fail(msg) {
|
||||
print("[FAILED] " + msg);
|
||||
}
|
||||
|
||||
fn dump_map(m) {
|
||||
// simple pretty printer for small maps
|
||||
for k in m.keys() {
|
||||
print(" " + k + ": " + m[k].to_string());
|
||||
}
|
||||
}
|
||||
|
||||
fn dump_array(a) {
|
||||
let i = 0;
|
||||
for x in a {
|
||||
print(" - " + x.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------
|
||||
// Phase 1: Host preflight check
|
||||
// ------------------------------------------------------------------------------------
|
||||
banner("PHASE 1: host_check()");
|
||||
let hc = host_check();
|
||||
if !(hc.ok == true) {
|
||||
fail("host_check indicates missing dependencies; details:");
|
||||
print("critical:");
|
||||
dump_array(hc.critical);
|
||||
print("optional:");
|
||||
dump_array(hc.optional);
|
||||
print("notes:");
|
||||
dump_array(hc.notes);
|
||||
// Short-circuit: nothing else will work without deps
|
||||
throw "Missing critical host dependencies";
|
||||
} else {
|
||||
ok("host_check passed");
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------
|
||||
// Phase 2: Image preparation for Ubuntu
|
||||
// - produces a per-VM raw disk in $HOME/hero/virt/vms/<id>/disk.raw
|
||||
// ------------------------------------------------------------------------------------
|
||||
banner("PHASE 2: image_prepare (Ubuntu) -> raw disk");
|
||||
let vmA = "vm-e2e-a";
|
||||
let prep_opts = #{
|
||||
id: vmA,
|
||||
flavor: "ubuntu",
|
||||
// source: optional override, default uses /images/noble-server-cloudimg-amd64.img
|
||||
// target_dir: optional override, default $HOME/hero/virt/vms/<id>
|
||||
net: #{
|
||||
dhcp4: true,
|
||||
dhcp6: false,
|
||||
ipv6_addr: "400::10/64",
|
||||
gw6: "400::1",
|
||||
},
|
||||
disable_cloud_init_net: true,
|
||||
};
|
||||
|
||||
let prep_res = ();
|
||||
let prep_ok = false;
|
||||
try {
|
||||
prep_res = image_prepare(prep_opts);
|
||||
ok("image_prepare returned:");
|
||||
dump_map(prep_res);
|
||||
if prep_res.raw_disk == () {
|
||||
fail("prep_res.raw_disk is UNIT; expected string path");
|
||||
} else {
|
||||
ok("raw_disk: " + prep_res.raw_disk);
|
||||
prep_ok = true;
|
||||
}
|
||||
} catch (e) {
|
||||
fail("image_prepare failed: " + e.to_string());
|
||||
}
|
||||
|
||||
if !(prep_ok) {
|
||||
throw "Stopping due to image_prepare failure";
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------
|
||||
// Phase 3: Launch VM via builder using the prepared raw disk
|
||||
// ------------------------------------------------------------------------------------
|
||||
banner("PHASE 3: Launch via cloudhv_builder (disk from Phase 2)");
|
||||
let b = cloudhv_builder(vmA);
|
||||
let b = disk(b, prep_res.raw_disk);
|
||||
let b = memory_mb(b, 4096);
|
||||
let b = vcpus(b, 2);
|
||||
// Optional extras:
|
||||
// let b = extra_arg(b, "--serial"); let b = extra_arg(b, "tty");
|
||||
// let b = no_default_net(b);
|
||||
|
||||
let vm_id_a = "";
|
||||
try {
|
||||
vm_id_a = launch(b);
|
||||
ok("builder.launch started VM id: " + vm_id_a);
|
||||
} catch (e) {
|
||||
fail("builder.launch failed: " + e.to_string());
|
||||
throw "Stopping due to launch failure for vm-e2e-a";
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------
|
||||
// Phase 4: Inspect VM info, list VMs
|
||||
// ------------------------------------------------------------------------------------
|
||||
banner("PHASE 4: cloudhv_vm_info / cloudhv_vm_list");
|
||||
try {
|
||||
let info_a = cloudhv_vm_info(vm_id_a);
|
||||
ok("cloudhv_vm_info:");
|
||||
dump_map(info_a);
|
||||
} catch (e) {
|
||||
fail("cloudhv_vm_info failed: " + e.to_string());
|
||||
}
|
||||
|
||||
try {
|
||||
let vms = cloudhv_vm_list();
|
||||
ok("cloudhv_vm_list count = " + vms.len.to_string());
|
||||
} catch (e) {
|
||||
fail("cloudhv_vm_list failed: " + e.to_string());
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------
|
||||
// Phase 5: Stop & delete VM A
|
||||
// ------------------------------------------------------------------------------------
|
||||
banner("PHASE 5: Stop & delete VM A");
|
||||
try {
|
||||
cloudhv_vm_stop(vm_id_a, false);
|
||||
ok("cloudhv_vm_stop graceful OK");
|
||||
} catch (e) {
|
||||
fail("cloudhv_vm_stop (graceful) failed: " + e.to_string() + " -> trying force");
|
||||
try {
|
||||
cloudhv_vm_stop(vm_id_a, true);
|
||||
ok("cloudhv_vm_stop force OK");
|
||||
} catch (e2) {
|
||||
fail("cloudhv_vm_stop force failed: " + e2.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
cloudhv_vm_delete(vm_id_a, true);
|
||||
ok("cloudhv_vm_delete OK (deleted disks)");
|
||||
} catch (e) {
|
||||
fail("cloudhv_vm_delete failed: " + e.to_string());
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------
|
||||
// Phase 6: Launch VM via one-shot wrapper vm_easy_launch()
|
||||
// ------------------------------------------------------------------------------------
|
||||
banner("PHASE 6: vm_easy_launch for VM B");
|
||||
let vmB = "vm-e2e-b";
|
||||
let vm_id_b = "";
|
||||
try {
|
||||
vm_id_b = vm_easy_launch("ubuntu", vmB, 4096, 2);
|
||||
ok("vm_easy_launch started VM id: " + vm_id_b);
|
||||
} catch (e) {
|
||||
fail("vm_easy_launch failed: " + e.to_string());
|
||||
throw "Stopping due to vm_easy_launch failure";
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------
|
||||
// Phase 7: Inspect VM B info, list VMs
|
||||
// ------------------------------------------------------------------------------------
|
||||
banner("PHASE 7: Inspect VM B");
|
||||
try {
|
||||
let info_b = cloudhv_vm_info(vm_id_b);
|
||||
ok("cloudhv_vm_info (B):");
|
||||
dump_map(info_b);
|
||||
} catch (e) {
|
||||
fail("cloudhv_vm_info (B) failed: " + e.to_string());
|
||||
}
|
||||
|
||||
try {
|
||||
let vms2 = cloudhv_vm_list();
|
||||
ok("cloudhv_vm_list count = " + vms2.len.to_string());
|
||||
} catch (e) {
|
||||
fail("cloudhv_vm_list failed: " + e.to_string());
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------
|
||||
// Phase 8: Stop & delete VM B
|
||||
// ------------------------------------------------------------------------------------
|
||||
banner("PHASE 8: Stop & delete VM B");
|
||||
try {
|
||||
cloudhv_vm_stop(vm_id_b, false);
|
||||
ok("cloudhv_vm_stop (B) graceful OK");
|
||||
} catch (e) {
|
||||
fail("cloudhv_vm_stop (B) graceful failed: " + e.to_string() + " -> trying force");
|
||||
try {
|
||||
cloudhv_vm_stop(vm_id_b, true);
|
||||
ok("cloudhv_vm_stop (B) force OK");
|
||||
} catch (e2) {
|
||||
fail("cloudhv_vm_stop (B) force failed: " + e2.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
cloudhv_vm_delete(vm_id_b, true);
|
||||
ok("cloudhv_vm_delete (B) OK (deleted disks)");
|
||||
} catch (e) {
|
||||
fail("cloudhv_vm_delete (B) failed: " + e.to_string());
|
||||
}
|
||||
|
||||
banner("DONE: All phases executed");
|
Reference in New Issue
Block a user