1*bb4ee6a4SAndroid Build Coastguard Worker#!/usr/bin/env python3 2*bb4ee6a4SAndroid Build Coastguard Worker# Copyright 2021 The ChromiumOS Authors 3*bb4ee6a4SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be 4*bb4ee6a4SAndroid Build Coastguard Worker# found in the LICENSE file. 5*bb4ee6a4SAndroid Build Coastguard Worker 6*bb4ee6a4SAndroid Build Coastguard Workerimport shutil 7*bb4ee6a4SAndroid Build Coastguard Workerfrom typing import Iterable, Optional 8*bb4ee6a4SAndroid Build Coastguard Worker 9*bb4ee6a4SAndroid Build Coastguard Workerfrom impl.common import run_commands, argh, console, strip_ansi_escape_sequences 10*bb4ee6a4SAndroid Build Coastguard Workerfrom impl import testvm 11*bb4ee6a4SAndroid Build Coastguard Workerfrom impl.testvm import Arch, VmState 12*bb4ee6a4SAndroid Build Coastguard Worker 13*bb4ee6a4SAndroid Build Coastguard WorkerUSAGE = """Manages VMs for testing crosvm. 14*bb4ee6a4SAndroid Build Coastguard Worker 15*bb4ee6a4SAndroid Build Coastguard WorkerCan run an x86_64 and an aarch64 vm via `./tools/x86vm` and `./tools/aarch64vm`. 16*bb4ee6a4SAndroid Build Coastguard WorkerBoth are a wrapper around `./tools/testvm --arch=x86_64/aarch64`. 17*bb4ee6a4SAndroid Build Coastguard Worker 18*bb4ee6a4SAndroid Build Coastguard WorkerThe easiest way to use the VM is: 19*bb4ee6a4SAndroid Build Coastguard Worker 20*bb4ee6a4SAndroid Build Coastguard Worker $ ./tools/aarch64vm ssh 21*bb4ee6a4SAndroid Build Coastguard Worker 22*bb4ee6a4SAndroid Build Coastguard WorkerWhich will initialize and boot the VM, then wait for SSH to be available and 23*bb4ee6a4SAndroid Build Coastguard Workeropens an SSH session. The VM will stay alive between calls. 24*bb4ee6a4SAndroid Build Coastguard Worker 25*bb4ee6a4SAndroid Build Coastguard WorkerAvailable commands are: 26*bb4ee6a4SAndroid Build Coastguard Worker - up: Start the VM if it is not already running. 27*bb4ee6a4SAndroid Build Coastguard Worker - stop: Gracefully stop the VM 28*bb4ee6a4SAndroid Build Coastguard Worker - kill: Send SIGKILL to the VM 29*bb4ee6a4SAndroid Build Coastguard Worker - clean: Stop the VM and delete all images 30*bb4ee6a4SAndroid Build Coastguard Worker - logs: Print logs of the VM console 31*bb4ee6a4SAndroid Build Coastguard Worker 32*bb4ee6a4SAndroid Build Coastguard WorkerAll of these can be called on `./tools/x86vm` or `./tools/aarch64vm`, but also on 33*bb4ee6a4SAndroid Build Coastguard Worker`tools/testvm` to apply to both VMs. 34*bb4ee6a4SAndroid Build Coastguard Worker""" 35*bb4ee6a4SAndroid Build Coastguard Worker 36*bb4ee6a4SAndroid Build Coastguard Worker 37*bb4ee6a4SAndroid Build Coastguard Workerdef cli_shorthand(arch: Arch): 38*bb4ee6a4SAndroid Build Coastguard Worker if arch == "x86_64": 39*bb4ee6a4SAndroid Build Coastguard Worker return "tools/x86vm" 40*bb4ee6a4SAndroid Build Coastguard Worker elif arch == "aarch64": 41*bb4ee6a4SAndroid Build Coastguard Worker return "tools/aarch64vm" 42*bb4ee6a4SAndroid Build Coastguard Worker else: 43*bb4ee6a4SAndroid Build Coastguard Worker raise Exception(f"Unknown architecture: {arch}") 44*bb4ee6a4SAndroid Build Coastguard Worker 45*bb4ee6a4SAndroid Build Coastguard Worker 46*bb4ee6a4SAndroid Build Coastguard Workerdef arch_or_all(arch: Optional[Arch]): 47*bb4ee6a4SAndroid Build Coastguard Worker return (arch,) if arch else testvm.ARCH_OPTIONS 48*bb4ee6a4SAndroid Build Coastguard Worker 49*bb4ee6a4SAndroid Build Coastguard Worker 50*bb4ee6a4SAndroid Build Coastguard WorkerARCHS = testvm.ARCH_OPTIONS 51*bb4ee6a4SAndroid Build Coastguard Worker 52*bb4ee6a4SAndroid Build Coastguard Worker 53*bb4ee6a4SAndroid Build Coastguard Worker@argh.arg("--arch-list", "--arch", nargs="*", type=str, default=ARCHS, choices=ARCHS) 54*bb4ee6a4SAndroid Build Coastguard Workerdef up(arch_list: Iterable[Arch] = [], reset: bool = False, wait: bool = False, timeout: int = 120): 55*bb4ee6a4SAndroid Build Coastguard Worker "Start the VM if it's not already running." 56*bb4ee6a4SAndroid Build Coastguard Worker for arch in arch_list: 57*bb4ee6a4SAndroid Build Coastguard Worker testvm.up(arch, reset, wait, timeout) 58*bb4ee6a4SAndroid Build Coastguard Worker 59*bb4ee6a4SAndroid Build Coastguard Worker 60*bb4ee6a4SAndroid Build Coastguard Worker@argh.arg("--arch", required=True, choices=testvm.ARCH_OPTIONS) 61*bb4ee6a4SAndroid Build Coastguard Workerdef run(arch: Arch = "x86_64", reset: bool = False): 62*bb4ee6a4SAndroid Build Coastguard Worker "Run the VM in foreground for debugging purposes." 63*bb4ee6a4SAndroid Build Coastguard Worker if testvm.is_running(arch): 64*bb4ee6a4SAndroid Build Coastguard Worker raise Exception("VM is already running") 65*bb4ee6a4SAndroid Build Coastguard Worker testvm.build_if_needed(arch, reset) 66*bb4ee6a4SAndroid Build Coastguard Worker testvm.run_qemu( 67*bb4ee6a4SAndroid Build Coastguard Worker arch, 68*bb4ee6a4SAndroid Build Coastguard Worker testvm.rootfs_img_path(arch), 69*bb4ee6a4SAndroid Build Coastguard Worker background=False, 70*bb4ee6a4SAndroid Build Coastguard Worker ) 71*bb4ee6a4SAndroid Build Coastguard Worker 72*bb4ee6a4SAndroid Build Coastguard Worker 73*bb4ee6a4SAndroid Build Coastguard Worker@argh.arg("--arch", required=True, choices=testvm.ARCH_OPTIONS) 74*bb4ee6a4SAndroid Build Coastguard Workerdef shell(arch: Arch = "x86_64", timeout: int = 120): 75*bb4ee6a4SAndroid Build Coastguard Worker "Starts an interactive shell via SSH, will ensure the VM is running." 76*bb4ee6a4SAndroid Build Coastguard Worker testvm.up(arch, wait=True, timeout=timeout) 77*bb4ee6a4SAndroid Build Coastguard Worker testvm.ssh_exec(arch) 78*bb4ee6a4SAndroid Build Coastguard Worker 79*bb4ee6a4SAndroid Build Coastguard Worker 80*bb4ee6a4SAndroid Build Coastguard Worker@argh.arg("--arch-list", "--arch", nargs="*", type=str, default=ARCHS, choices=ARCHS) 81*bb4ee6a4SAndroid Build Coastguard Workerdef stop(arch_list: Iterable[Arch] = []): 82*bb4ee6a4SAndroid Build Coastguard Worker "Gracefully stops the running VM." 83*bb4ee6a4SAndroid Build Coastguard Worker for arch in arch_list: 84*bb4ee6a4SAndroid Build Coastguard Worker if not testvm.is_running(arch): 85*bb4ee6a4SAndroid Build Coastguard Worker print(f"{arch} VM is not running") 86*bb4ee6a4SAndroid Build Coastguard Worker break 87*bb4ee6a4SAndroid Build Coastguard Worker console.print(f"Stopping {arch} VM") 88*bb4ee6a4SAndroid Build Coastguard Worker testvm.ssh_exec(arch, "sudo poweroff") 89*bb4ee6a4SAndroid Build Coastguard Worker 90*bb4ee6a4SAndroid Build Coastguard Worker 91*bb4ee6a4SAndroid Build Coastguard Worker@argh.arg("--arch-list", "--arch", nargs="*", type=str, default=ARCHS, choices=ARCHS) 92*bb4ee6a4SAndroid Build Coastguard Workerdef kill(arch_list: Iterable[Arch] = []): 93*bb4ee6a4SAndroid Build Coastguard Worker "Kills the running VM with SIGKILL." 94*bb4ee6a4SAndroid Build Coastguard Worker for arch in arch_list: 95*bb4ee6a4SAndroid Build Coastguard Worker if not testvm.is_running(arch): 96*bb4ee6a4SAndroid Build Coastguard Worker console.print(f"{arch} VM is not running") 97*bb4ee6a4SAndroid Build Coastguard Worker break 98*bb4ee6a4SAndroid Build Coastguard Worker console.print(f"Killing {arch} VM process") 99*bb4ee6a4SAndroid Build Coastguard Worker testvm.kill_vm(arch) 100*bb4ee6a4SAndroid Build Coastguard Worker print() 101*bb4ee6a4SAndroid Build Coastguard Worker 102*bb4ee6a4SAndroid Build Coastguard Worker 103*bb4ee6a4SAndroid Build Coastguard Worker@argh.arg("--arch-list", "--arch", nargs="*", type=str, default=ARCHS, choices=ARCHS) 104*bb4ee6a4SAndroid Build Coastguard Workerdef clean(arch_list: Iterable[Arch] = []): 105*bb4ee6a4SAndroid Build Coastguard Worker "Stops the VM or VMs and deletes all data." 106*bb4ee6a4SAndroid Build Coastguard Worker for arch in arch_list: 107*bb4ee6a4SAndroid Build Coastguard Worker if testvm.is_running(arch): 108*bb4ee6a4SAndroid Build Coastguard Worker kill(arch) 109*bb4ee6a4SAndroid Build Coastguard Worker if testvm.data_dir(arch).exists(): 110*bb4ee6a4SAndroid Build Coastguard Worker console.print("Cleaning data directory", testvm.data_dir(arch)) 111*bb4ee6a4SAndroid Build Coastguard Worker shutil.rmtree(testvm.data_dir(arch)) 112*bb4ee6a4SAndroid Build Coastguard Worker print() 113*bb4ee6a4SAndroid Build Coastguard Worker 114*bb4ee6a4SAndroid Build Coastguard Worker 115*bb4ee6a4SAndroid Build Coastguard Workerdef vm_status(arch: Arch): 116*bb4ee6a4SAndroid Build Coastguard Worker def cli_tip(*args: str): 117*bb4ee6a4SAndroid Build Coastguard Worker return f"[green][bold]{cli_shorthand(arch)} {' '.join(args)}[/bold][/green]" 118*bb4ee6a4SAndroid Build Coastguard Worker 119*bb4ee6a4SAndroid Build Coastguard Worker vm = f"{arch} VM" 120*bb4ee6a4SAndroid Build Coastguard Worker port = f"[blue]{testvm.SSH_PORTS[arch]}[/blue]" 121*bb4ee6a4SAndroid Build Coastguard Worker 122*bb4ee6a4SAndroid Build Coastguard Worker state = testvm.state(arch) 123*bb4ee6a4SAndroid Build Coastguard Worker if state == VmState.REACHABLE: 124*bb4ee6a4SAndroid Build Coastguard Worker console.print(f"{vm} is [green]reachable[/green] on port {port}") 125*bb4ee6a4SAndroid Build Coastguard Worker console.print(f"Start a shell with {cli_tip('shell')}") 126*bb4ee6a4SAndroid Build Coastguard Worker elif state == VmState.STOPPED: 127*bb4ee6a4SAndroid Build Coastguard Worker console.print(f"{vm} is [red]stopped[/red]") 128*bb4ee6a4SAndroid Build Coastguard Worker console.print(f"Start the VM with {cli_tip('up')}") 129*bb4ee6a4SAndroid Build Coastguard Worker else: 130*bb4ee6a4SAndroid Build Coastguard Worker console.print(f"{vm} is running but [red]not reachable[/red] on port {port}") 131*bb4ee6a4SAndroid Build Coastguard Worker console.print(f"Recent logs:") 132*bb4ee6a4SAndroid Build Coastguard Worker logs(arch, 10, style="light_slate_grey") 133*bb4ee6a4SAndroid Build Coastguard Worker console.print(f"See all logs with {cli_tip('logs')}") 134*bb4ee6a4SAndroid Build Coastguard Worker 135*bb4ee6a4SAndroid Build Coastguard Worker 136*bb4ee6a4SAndroid Build Coastguard Worker@argh.arg("--arch-list", "--arch", nargs="*", type=str, default=ARCHS, choices=ARCHS) 137*bb4ee6a4SAndroid Build Coastguard Workerdef status(arch_list: Iterable[Arch] = []): 138*bb4ee6a4SAndroid Build Coastguard Worker for arch in arch_list: 139*bb4ee6a4SAndroid Build Coastguard Worker vm_status(arch) 140*bb4ee6a4SAndroid Build Coastguard Worker print() 141*bb4ee6a4SAndroid Build Coastguard Worker 142*bb4ee6a4SAndroid Build Coastguard Worker 143*bb4ee6a4SAndroid Build Coastguard Worker@argh.arg("--arch", required=True, choices=testvm.ARCH_OPTIONS) 144*bb4ee6a4SAndroid Build Coastguard Workerdef logs(arch: Arch = "x86_64", n: int = 0, style: Optional[str] = None): 145*bb4ee6a4SAndroid Build Coastguard Worker log_lines = testvm.log_path(arch).read_text().splitlines() 146*bb4ee6a4SAndroid Build Coastguard Worker if n > 0 and len(log_lines) > n: 147*bb4ee6a4SAndroid Build Coastguard Worker log_lines = log_lines[-n:] 148*bb4ee6a4SAndroid Build Coastguard Worker for line in log_lines: 149*bb4ee6a4SAndroid Build Coastguard Worker if style: 150*bb4ee6a4SAndroid Build Coastguard Worker console.print( 151*bb4ee6a4SAndroid Build Coastguard Worker strip_ansi_escape_sequences(line), style=style, markup=False, highlight=False 152*bb4ee6a4SAndroid Build Coastguard Worker ) 153*bb4ee6a4SAndroid Build Coastguard Worker else: 154*bb4ee6a4SAndroid Build Coastguard Worker print(line) 155*bb4ee6a4SAndroid Build Coastguard Worker 156*bb4ee6a4SAndroid Build Coastguard Worker 157*bb4ee6a4SAndroid Build Coastguard Workerif __name__ == "__main__": 158*bb4ee6a4SAndroid Build Coastguard Worker run_commands(up, run, shell, stop, kill, clean, status, logs, usage=USAGE, default_fn=status) 159