123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 |
- #!/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
- from typing import Iterable, Optional
- from impl.common import run_commands, argh, console, strip_ansi_escape_sequences
- from impl import testvm
- from impl.testvm import Arch, VmState
- USAGE = """Manages VMs for testing crosvm.
- Can run an x86_64 and an aarch64 vm via `./tools/x86vm` and `./tools/aarch64vm`.
- Both are a wrapper around `./tools/testvm --arch=x86_64/aarch64`.
- 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.
- 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
- All of these can be called on `./tools/x86vm` or `./tools/aarch64vm`, but also on
- `tools/testvm` to apply to both VMs.
- """
- 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):
- "Start the VM if it's not already running."
- for arch in arch_list:
- testvm.up(arch, reset, wait, timeout)
- @argh.arg("--arch", required=True, choices=testvm.ARCH_OPTIONS)
- def run(arch: Arch = "x86_64", reset: bool = False):
- "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)
- def shell(arch: Arch = "x86_64", timeout: int = 120):
- "Starts an interactive shell via SSH, will ensure the VM is running."
- testvm.up(arch, wait=True, timeout=timeout)
- testvm.ssh_exec(arch)
- @argh.arg("--arch-list", "--arch", nargs="*", type=str, default=ARCHS, choices=ARCHS)
- def stop(arch_list: Iterable[Arch] = []):
- "Gracefully stops the running VM."
- 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")
- @argh.arg("--arch-list", "--arch", nargs="*", type=str, default=ARCHS, choices=ARCHS)
- def kill(arch_list: Iterable[Arch] = []):
- "Kills the running VM with SIGKILL."
- 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()
- @argh.arg("--arch", required=True, choices=testvm.ARCH_OPTIONS)
- 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)
- if __name__ == "__main__":
- run_commands(up, run, shell, stop, kill, clean, status, logs, usage=USAGE, default_fn=status)
|