feat(integration-vm): network + VM boot (overlay, cloud-init seed, virt-install import)
This commit is contained in:
parent
af76763c16
commit
fdd4df34b1
1 changed files with 81 additions and 0 deletions
|
|
@ -144,6 +144,87 @@ def ensure_image():
|
|||
return img
|
||||
|
||||
|
||||
def net_ensure():
|
||||
r = sh(["virsh", "net-info", NET_NAME], check=False, capture=True)
|
||||
if r.returncode != 0:
|
||||
xml = RUN_DIR / "net.xml"
|
||||
RUN_DIR.mkdir(parents=True, exist_ok=True)
|
||||
xml.write_text(NET_XML)
|
||||
sh(["virsh", "net-define", str(xml)])
|
||||
sh(["virsh", "net-autostart", NET_NAME])
|
||||
active = sh(["virsh", "net-info", NET_NAME], capture=True).stdout
|
||||
if "Active: yes" not in active:
|
||||
sh(["virsh", "net-start", NET_NAME])
|
||||
|
||||
|
||||
def _ssh_pubkey():
|
||||
for cand in ("id_ed25519.pub", "id_rsa.pub"):
|
||||
p = pathlib.Path.home() / ".ssh" / cand
|
||||
if p.exists():
|
||||
return p.read_text().strip()
|
||||
raise SystemExit("no SSH public key found in ~/.ssh")
|
||||
|
||||
|
||||
def up(host, name=None, mem_mib=DEFAULT_MEM_MIB, vcpus=DEFAULT_VCPUS):
|
||||
free = free_mib(pathlib.Path("/proc/meminfo").read_text())
|
||||
if free < MIN_FREE_MIB:
|
||||
raise SystemExit(f"refusing to start: only {free} MiB free (< {MIN_FREE_MIB})")
|
||||
running = sh(["virsh", "list", "--name"], capture=True).stdout.split()
|
||||
if any(n.startswith(NAME_PREFIX) for n in running):
|
||||
raise SystemExit("an integration VM is already running (one at a time); "
|
||||
"run `integration-vm prune` first")
|
||||
name = name or vm_name(host)
|
||||
img = ensure_image()
|
||||
net_ensure()
|
||||
RUN_DIR.mkdir(parents=True, exist_ok=True)
|
||||
overlay = RUN_DIR / f"{name}.qcow2"
|
||||
sh(["qemu-img", "create", "-f", "qcow2", "-F", "qcow2", "-b", str(img), str(overlay)])
|
||||
(RUN_DIR / "user-data").write_text(render_user_data(_ssh_pubkey(), "ansible"))
|
||||
(RUN_DIR / "meta-data").write_text(render_meta_data(f"iid-{name}", name))
|
||||
seed = RUN_DIR / f"{name}-seed.img"
|
||||
sh(["cloud-localds", str(seed), str(RUN_DIR / "user-data"), str(RUN_DIR / "meta-data")])
|
||||
DIAG_ROOT.mkdir(parents=True, exist_ok=True)
|
||||
console = DIAG_ROOT / f"{name}-console.log"
|
||||
sh(["virt-install", "--name", name, "--memory", str(mem_mib), "--vcpus", str(vcpus),
|
||||
"--import",
|
||||
"--disk", f"path={overlay},format=qcow2",
|
||||
"--disk", f"path={seed},device=cdrom",
|
||||
"--network", f"network={NET_NAME}",
|
||||
"--osinfo", "debian13",
|
||||
"--graphics", "none",
|
||||
"--serial", f"file,path={console}",
|
||||
"--noautoconsole"])
|
||||
ip = wait_for_ip(name)
|
||||
wait_for_ssh(ip, "ansible")
|
||||
(RUN_DIR / "current").write_text(f"{name}\n{ip}\n{host}\n")
|
||||
print(f"VM {name} up at {ip}")
|
||||
return name, ip
|
||||
|
||||
|
||||
def wait_for_ip(name, timeout=120):
|
||||
end = time.time() + timeout
|
||||
while time.time() < end:
|
||||
out = sh(["virsh", "domifaddr", name, "--source", "lease"],
|
||||
check=False, capture=True).stdout
|
||||
ip = parse_lease_ip(out)
|
||||
if ip:
|
||||
return ip
|
||||
time.sleep(4)
|
||||
raise SystemExit(f"timed out waiting for {name} to get a DHCP lease")
|
||||
|
||||
|
||||
def wait_for_ssh(ip, user, timeout=180):
|
||||
end = time.time() + timeout
|
||||
while time.time() < end:
|
||||
r = sh(["ssh", "-o", "StrictHostKeyChecking=no",
|
||||
"-o", "UserKnownHostsFile=/dev/null", "-o", "ConnectTimeout=5",
|
||||
f"{user}@{ip}", "true"], check=False, capture=True)
|
||||
if r.returncode == 0:
|
||||
return
|
||||
time.sleep(5)
|
||||
raise SystemExit(f"timed out waiting for SSH to {ip}")
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
p = argparse.ArgumentParser(prog="integration-vm", description=__doc__)
|
||||
sub = p.add_subparsers(dest="cmd", required=True)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue