2023-03-30 18:26:45 +00:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
# Copyright 2021 The ChromiumOS Authors
|
|
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
|
|
# found in the LICENSE file.
|
|
|
|
|
|
|
|
import shutil
|
2023-03-31 23:00:39 +00:00
|
|
|
from typing import Iterable, Optional
|
2023-03-30 18:26:45 +00:00
|
|
|
|
2023-03-31 23:00:39 +00:00
|
|
|
from impl.common import run_commands, argh, console, strip_ansi_escape_sequences
|
2023-03-30 18:26:45 +00:00
|
|
|
from impl import testvm
|
2023-03-31 23:00:39 +00:00
|
|
|
from impl.testvm import Arch, VmState
|
2023-03-30 18:26:45 +00:00
|
|
|
|
|
|
|
USAGE = """Manages VMs for testing crosvm.
|
|
|
|
|
|
|
|
Can run an x86_64 and an aarch64 vm via `./tools/x86vm` and `./tools/aarch64vm`.
|
2023-03-31 23:00:39 +00:00
|
|
|
Both are a wrapper around `./tools/testvm --arch=x86_64/aarch64`.
|
2023-03-30 18:26:45 +00:00
|
|
|
|
|
|
|
The easiest way to use the VM is:
|
|
|
|
|
|
|
|
$ ./tools/aarch64vm ssh
|
|
|
|
|
|
|
|
Which will initialize and boot the VM, then wait for SSH to be available and
|
|
|
|
opens an SSH session. The VM will stay alive between calls.
|
|
|
|
|
2023-03-31 23:00:39 +00:00
|
|
|
Available commands are:
|
|
|
|
- up: Start the VM if it is not already running.
|
|
|
|
- stop: Gracefully stop the VM
|
|
|
|
- kill: Send SIGKILL to the VM
|
|
|
|
- clean: Stop the VM and delete all images
|
|
|
|
- logs: Print logs of the VM console
|
2023-03-30 18:26:45 +00:00
|
|
|
|
2023-03-31 23:00:39 +00:00
|
|
|
All of these can be called on `./tools/x86vm` or `./tools/aarch64vm`, but also on
|
|
|
|
`tools/testvm` to apply to both VMs.
|
2023-03-30 18:26:45 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
2023-03-31 23:00:39 +00:00
|
|
|
def cli_shorthand(arch: Arch):
|
|
|
|
if arch == "x86_64":
|
|
|
|
return "tools/x86vm"
|
|
|
|
elif arch == "aarch64":
|
|
|
|
return "tools/aarch64vm"
|
|
|
|
else:
|
|
|
|
raise Exception(f"Unknown architecture: {arch}")
|
|
|
|
|
|
|
|
|
|
|
|
def arch_or_all(arch: Optional[Arch]):
|
|
|
|
return (arch,) if arch else testvm.ARCH_OPTIONS
|
|
|
|
|
|
|
|
|
|
|
|
ARCHS = testvm.ARCH_OPTIONS
|
|
|
|
|
|
|
|
|
|
|
|
@argh.arg("--arch-list", "--arch", nargs="*", type=str, default=ARCHS, choices=ARCHS)
|
|
|
|
def up(arch_list: Iterable[Arch] = [], reset: bool = False, wait: bool = False, timeout: int = 120):
|
2023-03-30 18:26:45 +00:00
|
|
|
"Start the VM if it's not already running."
|
2023-03-31 23:00:39 +00:00
|
|
|
for arch in arch_list:
|
|
|
|
testvm.up(arch, reset, wait, timeout)
|
2023-03-30 18:26:45 +00:00
|
|
|
|
|
|
|
|
|
|
|
@argh.arg("--arch", required=True, choices=testvm.ARCH_OPTIONS)
|
2023-03-31 23:00:39 +00:00
|
|
|
def run(arch: Arch = "x86_64", reset: bool = False):
|
2023-03-30 18:26:45 +00:00
|
|
|
"Run the VM in foreground for debugging purposes."
|
|
|
|
if testvm.is_running(arch):
|
|
|
|
raise Exception("VM is already running")
|
|
|
|
testvm.build_if_needed(arch, reset)
|
|
|
|
testvm.run_qemu(
|
|
|
|
arch,
|
|
|
|
testvm.rootfs_img_path(arch),
|
|
|
|
background=False,
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
@argh.arg("--arch", required=True, choices=testvm.ARCH_OPTIONS)
|
2023-03-31 23:00:39 +00:00
|
|
|
def shell(arch: Arch = "x86_64", timeout: int = 120):
|
2023-03-30 18:26:45 +00:00
|
|
|
"Starts an interactive shell via SSH, will ensure the VM is running."
|
2023-03-31 23:00:39 +00:00
|
|
|
testvm.up(arch, wait=True, timeout=timeout)
|
2023-03-30 18:26:45 +00:00
|
|
|
testvm.ssh_exec(arch)
|
|
|
|
|
|
|
|
|
2023-03-31 23:00:39 +00:00
|
|
|
@argh.arg("--arch-list", "--arch", nargs="*", type=str, default=ARCHS, choices=ARCHS)
|
|
|
|
def stop(arch_list: Iterable[Arch] = []):
|
2023-03-30 18:26:45 +00:00
|
|
|
"Gracefully stops the running VM."
|
2023-03-31 23:00:39 +00:00
|
|
|
for arch in arch_list:
|
|
|
|
if not testvm.is_running(arch):
|
|
|
|
print(f"{arch} VM is not running")
|
|
|
|
break
|
|
|
|
console.print(f"Stopping {arch} VM")
|
|
|
|
testvm.ssh_exec(arch, "sudo poweroff")
|
2023-03-30 18:26:45 +00:00
|
|
|
|
|
|
|
|
2023-03-31 23:00:39 +00:00
|
|
|
@argh.arg("--arch-list", "--arch", nargs="*", type=str, default=ARCHS, choices=ARCHS)
|
|
|
|
def kill(arch_list: Iterable[Arch] = []):
|
2023-03-30 18:26:45 +00:00
|
|
|
"Kills the running VM with SIGKILL."
|
2023-03-31 23:00:39 +00:00
|
|
|
for arch in arch_list:
|
|
|
|
if not testvm.is_running(arch):
|
|
|
|
console.print(f"{arch} VM is not running")
|
|
|
|
break
|
|
|
|
console.print(f"Killing {arch} VM process")
|
|
|
|
testvm.kill_vm(arch)
|
|
|
|
print()
|
|
|
|
|
|
|
|
|
|
|
|
@argh.arg("--arch-list", "--arch", nargs="*", type=str, default=ARCHS, choices=ARCHS)
|
|
|
|
def clean(arch_list: Iterable[Arch] = []):
|
|
|
|
"Stops the VM or VMs and deletes all data."
|
|
|
|
for arch in arch_list:
|
|
|
|
if testvm.is_running(arch):
|
|
|
|
kill(arch)
|
|
|
|
if testvm.data_dir(arch).exists():
|
|
|
|
console.print("Cleaning data directory", testvm.data_dir(arch))
|
|
|
|
shutil.rmtree(testvm.data_dir(arch))
|
|
|
|
print()
|
|
|
|
|
|
|
|
|
|
|
|
def vm_status(arch: Arch):
|
|
|
|
def cli_tip(*args: str):
|
|
|
|
return f"[green][bold]{cli_shorthand(arch)} {' '.join(args)}[/bold][/green]"
|
|
|
|
|
|
|
|
vm = f"{arch} VM"
|
|
|
|
port = f"[blue]{testvm.SSH_PORTS[arch]}[/blue]"
|
|
|
|
|
|
|
|
state = testvm.state(arch)
|
|
|
|
if state == VmState.REACHABLE:
|
|
|
|
console.print(f"{vm} is [green]reachable[/green] on port {port}")
|
|
|
|
console.print(f"Start a shell with {cli_tip('shell')}")
|
|
|
|
elif state == VmState.STOPPED:
|
|
|
|
console.print(f"{vm} is [red]stopped[/red]")
|
|
|
|
console.print(f"Start the VM with {cli_tip('up')}")
|
|
|
|
else:
|
|
|
|
console.print(f"{vm} is running but [red]not reachable[/red] on port {port}")
|
|
|
|
console.print(f"Recent logs:")
|
|
|
|
logs(arch, 10, style="light_slate_grey")
|
|
|
|
console.print(f"See all logs with {cli_tip('logs')}")
|
|
|
|
|
|
|
|
|
|
|
|
@argh.arg("--arch-list", "--arch", nargs="*", type=str, default=ARCHS, choices=ARCHS)
|
|
|
|
def status(arch_list: Iterable[Arch] = []):
|
|
|
|
for arch in arch_list:
|
|
|
|
vm_status(arch)
|
|
|
|
print()
|
2023-03-30 18:26:45 +00:00
|
|
|
|
|
|
|
|
|
|
|
@argh.arg("--arch", required=True, choices=testvm.ARCH_OPTIONS)
|
2023-03-31 23:00:39 +00:00
|
|
|
def logs(arch: Arch = "x86_64", n: int = 0, style: Optional[str] = None):
|
|
|
|
log_lines = testvm.log_path(arch).read_text().splitlines()
|
|
|
|
if n > 0 and len(log_lines) > n:
|
|
|
|
log_lines = log_lines[-n:]
|
|
|
|
for line in log_lines:
|
|
|
|
if style:
|
|
|
|
console.print(
|
|
|
|
strip_ansi_escape_sequences(line), style=style, markup=False, highlight=False
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
print(line)
|
2023-03-30 18:26:45 +00:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
2023-03-31 23:00:39 +00:00
|
|
|
run_commands(up, run, shell, stop, kill, clean, status, logs, usage=USAGE, default_fn=status)
|