xref: /aosp_15_r20/external/crosvm/tools/testvm (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
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