1ff2ebff0SFam Zheng#!/usr/bin/env python 2ff2ebff0SFam Zheng# 3ff2ebff0SFam Zheng# VM testing base class 4ff2ebff0SFam Zheng# 58dd38334SGerd Hoffmann# Copyright 2017-2019 Red Hat Inc. 6ff2ebff0SFam Zheng# 7ff2ebff0SFam Zheng# Authors: 8ff2ebff0SFam Zheng# Fam Zheng <famz@redhat.com> 98dd38334SGerd Hoffmann# Gerd Hoffmann <kraxel@redhat.com> 10ff2ebff0SFam Zheng# 11ff2ebff0SFam Zheng# This code is licensed under the GPL version 2 or later. See 12ff2ebff0SFam Zheng# the COPYING file in the top-level directory. 13ff2ebff0SFam Zheng# 14ff2ebff0SFam Zheng 15f03868bdSEduardo Habkostfrom __future__ import print_function 16ff2ebff0SFam Zhengimport os 178dd38334SGerd Hoffmannimport re 18ff2ebff0SFam Zhengimport sys 198dd38334SGerd Hoffmannimport socket 20ff2ebff0SFam Zhengimport logging 21ff2ebff0SFam Zhengimport time 22ff2ebff0SFam Zhengimport datetime 238f8fd9edSCleber Rosasys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) 24abf0bf99SJohn Snowfrom qemu import kvm_available 25abf0bf99SJohn Snowfrom qemu.machine import QEMUMachine 26ff2ebff0SFam Zhengimport subprocess 27ff2ebff0SFam Zhengimport hashlib 28ff2ebff0SFam Zhengimport optparse 29ff2ebff0SFam Zhengimport atexit 30ff2ebff0SFam Zhengimport tempfile 31ff2ebff0SFam Zhengimport shutil 32ff2ebff0SFam Zhengimport multiprocessing 33ff2ebff0SFam Zhengimport traceback 34ff2ebff0SFam Zheng 35ff2ebff0SFam ZhengSSH_KEY = open(os.path.join(os.path.dirname(__file__), 36ff2ebff0SFam Zheng "..", "keys", "id_rsa")).read() 37ff2ebff0SFam ZhengSSH_PUB_KEY = open(os.path.join(os.path.dirname(__file__), 38ff2ebff0SFam Zheng "..", "keys", "id_rsa.pub")).read() 39ff2ebff0SFam Zheng 40ff2ebff0SFam Zhengclass BaseVM(object): 41ff2ebff0SFam Zheng GUEST_USER = "qemu" 42ff2ebff0SFam Zheng GUEST_PASS = "qemupass" 43ff2ebff0SFam Zheng ROOT_PASS = "qemupass" 44ff2ebff0SFam Zheng 45b08ba163SGerd Hoffmann envvars = [ 46b08ba163SGerd Hoffmann "https_proxy", 47b08ba163SGerd Hoffmann "http_proxy", 48b08ba163SGerd Hoffmann "ftp_proxy", 49b08ba163SGerd Hoffmann "no_proxy", 50b08ba163SGerd Hoffmann ] 51b08ba163SGerd Hoffmann 52ff2ebff0SFam Zheng # The script to run in the guest that builds QEMU 53ff2ebff0SFam Zheng BUILD_SCRIPT = "" 54ff2ebff0SFam Zheng # The guest name, to be overridden by subclasses 55ff2ebff0SFam Zheng name = "#base" 5631719c37SPhilippe Mathieu-Daudé # The guest architecture, to be overridden by subclasses 5731719c37SPhilippe Mathieu-Daudé arch = "#arch" 58b3f94b2fSGerd Hoffmann # command to halt the guest, can be overridden by subclasses 59b3f94b2fSGerd Hoffmann poweroff = "poweroff" 605b790481SEduardo Habkost # enable IPv6 networking 615b790481SEduardo Habkost ipv6 = True 62ff2ebff0SFam Zheng def __init__(self, debug=False, vcpus=None): 63ff2ebff0SFam Zheng self._guest = None 64ff2ebff0SFam Zheng self._tmpdir = os.path.realpath(tempfile.mkdtemp(prefix="vm-test-", 65ff2ebff0SFam Zheng suffix=".tmp", 66ff2ebff0SFam Zheng dir=".")) 67ff2ebff0SFam Zheng atexit.register(shutil.rmtree, self._tmpdir) 68ff2ebff0SFam Zheng 69ff2ebff0SFam Zheng self._ssh_key_file = os.path.join(self._tmpdir, "id_rsa") 70ff2ebff0SFam Zheng open(self._ssh_key_file, "w").write(SSH_KEY) 71ff2ebff0SFam Zheng subprocess.check_call(["chmod", "600", self._ssh_key_file]) 72ff2ebff0SFam Zheng 73ff2ebff0SFam Zheng self._ssh_pub_key_file = os.path.join(self._tmpdir, "id_rsa.pub") 74ff2ebff0SFam Zheng open(self._ssh_pub_key_file, "w").write(SSH_PUB_KEY) 75ff2ebff0SFam Zheng 76ff2ebff0SFam Zheng self.debug = debug 77ff2ebff0SFam Zheng self._stderr = sys.stderr 78ff2ebff0SFam Zheng self._devnull = open(os.devnull, "w") 79ff2ebff0SFam Zheng if self.debug: 80ff2ebff0SFam Zheng self._stdout = sys.stdout 81ff2ebff0SFam Zheng else: 82ff2ebff0SFam Zheng self._stdout = self._devnull 83ff2ebff0SFam Zheng self._args = [ \ 84eb2712f5SPeter Maydell "-nodefaults", "-m", "4G", 85b33bd859SPeter Maydell "-cpu", "max", 865b790481SEduardo Habkost "-netdev", "user,id=vnet,hostfwd=:127.0.0.1:0-:22" + 875b790481SEduardo Habkost (",ipv6=no" if not self.ipv6 else ""), 88ff2ebff0SFam Zheng "-device", "virtio-net-pci,netdev=vnet", 898dd38334SGerd Hoffmann "-vnc", "127.0.0.1:0,to=20"] 90071cf5a4SPhilippe Mathieu-Daudé if vcpus and vcpus > 1: 913ace9be6SGerd Hoffmann self._args += ["-smp", "%d" % vcpus] 9271531bb5SPhilippe Mathieu-Daudé if kvm_available(self.arch): 93ff2ebff0SFam Zheng self._args += ["-enable-kvm"] 94ff2ebff0SFam Zheng else: 95ff2ebff0SFam Zheng logging.info("KVM not available, not using -enable-kvm") 96ff2ebff0SFam Zheng self._data_args = [] 97ff2ebff0SFam Zheng 98ff2ebff0SFam Zheng def _download_with_cache(self, url, sha256sum=None): 99ff2ebff0SFam Zheng def check_sha256sum(fname): 100ff2ebff0SFam Zheng if not sha256sum: 101ff2ebff0SFam Zheng return True 102ff2ebff0SFam Zheng checksum = subprocess.check_output(["sha256sum", fname]).split()[0] 1033ace9be6SGerd Hoffmann return sha256sum == checksum.decode("utf-8") 104ff2ebff0SFam Zheng 105ff2ebff0SFam Zheng cache_dir = os.path.expanduser("~/.cache/qemu-vm/download") 106ff2ebff0SFam Zheng if not os.path.exists(cache_dir): 107ff2ebff0SFam Zheng os.makedirs(cache_dir) 1083ace9be6SGerd Hoffmann fname = os.path.join(cache_dir, 1093ace9be6SGerd Hoffmann hashlib.sha1(url.encode("utf-8")).hexdigest()) 110ff2ebff0SFam Zheng if os.path.exists(fname) and check_sha256sum(fname): 111ff2ebff0SFam Zheng return fname 112ff2ebff0SFam Zheng logging.debug("Downloading %s to %s...", url, fname) 113ff2ebff0SFam Zheng subprocess.check_call(["wget", "-c", url, "-O", fname + ".download"], 114ff2ebff0SFam Zheng stdout=self._stdout, stderr=self._stderr) 115ff2ebff0SFam Zheng os.rename(fname + ".download", fname) 116ff2ebff0SFam Zheng return fname 117ff2ebff0SFam Zheng 118796471e9SGerd Hoffmann def _ssh_do(self, user, cmd, check): 119796471e9SGerd Hoffmann ssh_cmd = ["ssh", "-q", "-t", 120ff2ebff0SFam Zheng "-o", "StrictHostKeyChecking=no", 121ff2ebff0SFam Zheng "-o", "UserKnownHostsFile=" + os.devnull, 122ff2ebff0SFam Zheng "-o", "ConnectTimeout=1", 123ff2ebff0SFam Zheng "-p", self.ssh_port, "-i", self._ssh_key_file] 124b08ba163SGerd Hoffmann for var in self.envvars: 125b08ba163SGerd Hoffmann ssh_cmd += ['-o', "SendEnv=%s" % var ] 126ff2ebff0SFam Zheng assert not isinstance(cmd, str) 127ff2ebff0SFam Zheng ssh_cmd += ["%s@127.0.0.1" % user] + list(cmd) 128ff2ebff0SFam Zheng logging.debug("ssh_cmd: %s", " ".join(ssh_cmd)) 129726c9a3bSFam Zheng r = subprocess.call(ssh_cmd) 130ff2ebff0SFam Zheng if check and r != 0: 131ff2ebff0SFam Zheng raise Exception("SSH command failed: %s" % cmd) 132ff2ebff0SFam Zheng return r 133ff2ebff0SFam Zheng 134ff2ebff0SFam Zheng def ssh(self, *cmd): 135ff2ebff0SFam Zheng return self._ssh_do(self.GUEST_USER, cmd, False) 136ff2ebff0SFam Zheng 137ff2ebff0SFam Zheng def ssh_root(self, *cmd): 138ff2ebff0SFam Zheng return self._ssh_do("root", cmd, False) 139ff2ebff0SFam Zheng 140ff2ebff0SFam Zheng def ssh_check(self, *cmd): 141ff2ebff0SFam Zheng self._ssh_do(self.GUEST_USER, cmd, True) 142ff2ebff0SFam Zheng 143ff2ebff0SFam Zheng def ssh_root_check(self, *cmd): 144ff2ebff0SFam Zheng self._ssh_do("root", cmd, True) 145ff2ebff0SFam Zheng 146ff2ebff0SFam Zheng def build_image(self, img): 147ff2ebff0SFam Zheng raise NotImplementedError 148ff2ebff0SFam Zheng 149ff2ebff0SFam Zheng def add_source_dir(self, src_dir): 1503ace9be6SGerd Hoffmann name = "data-" + hashlib.sha1(src_dir.encode("utf-8")).hexdigest()[:5] 151ff2ebff0SFam Zheng tarfile = os.path.join(self._tmpdir, name + ".tar") 152ff2ebff0SFam Zheng logging.debug("Creating archive %s for src_dir dir: %s", tarfile, src_dir) 153ff2ebff0SFam Zheng subprocess.check_call(["./scripts/archive-source.sh", tarfile], 154ff2ebff0SFam Zheng cwd=src_dir, stdin=self._devnull, 155ff2ebff0SFam Zheng stdout=self._stdout, stderr=self._stderr) 156ff2ebff0SFam Zheng self._data_args += ["-drive", 157ff2ebff0SFam Zheng "file=%s,if=none,id=%s,cache=writeback,format=raw" % \ 158ff2ebff0SFam Zheng (tarfile, name), 159ff2ebff0SFam Zheng "-device", 160ff2ebff0SFam Zheng "virtio-blk,drive=%s,serial=%s,bootindex=1" % (name, name)] 161ff2ebff0SFam Zheng 162ff2ebff0SFam Zheng def boot(self, img, extra_args=[]): 163ff2ebff0SFam Zheng args = self._args + [ 164ff2ebff0SFam Zheng "-device", "VGA", 165ff2ebff0SFam Zheng "-drive", "file=%s,if=none,id=drive0,cache=writeback" % img, 166ff2ebff0SFam Zheng "-device", "virtio-blk,drive=drive0,bootindex=0"] 167ff2ebff0SFam Zheng args += self._data_args + extra_args 168ff2ebff0SFam Zheng logging.debug("QEMU args: %s", " ".join(args)) 16931719c37SPhilippe Mathieu-Daudé qemu_bin = os.environ.get("QEMU", "qemu-system-" + self.arch) 170ff2ebff0SFam Zheng guest = QEMUMachine(binary=qemu_bin, args=args) 1718dd38334SGerd Hoffmann guest.set_machine('pc') 1728dd38334SGerd Hoffmann guest.set_console() 173ff2ebff0SFam Zheng try: 174ff2ebff0SFam Zheng guest.launch() 175ff2ebff0SFam Zheng except: 176ff2ebff0SFam Zheng logging.error("Failed to launch QEMU, command line:") 177ff2ebff0SFam Zheng logging.error(" ".join([qemu_bin] + args)) 178ff2ebff0SFam Zheng logging.error("Log:") 179ff2ebff0SFam Zheng logging.error(guest.get_log()) 180ff2ebff0SFam Zheng logging.error("QEMU version >= 2.10 is required") 181ff2ebff0SFam Zheng raise 182ff2ebff0SFam Zheng atexit.register(self.shutdown) 183ff2ebff0SFam Zheng self._guest = guest 184ff2ebff0SFam Zheng usernet_info = guest.qmp("human-monitor-command", 185ff2ebff0SFam Zheng command_line="info usernet") 186ff2ebff0SFam Zheng self.ssh_port = None 187ff2ebff0SFam Zheng for l in usernet_info["return"].splitlines(): 188ff2ebff0SFam Zheng fields = l.split() 189ff2ebff0SFam Zheng if "TCP[HOST_FORWARD]" in fields and "22" in fields: 190ff2ebff0SFam Zheng self.ssh_port = l.split()[3] 191ff2ebff0SFam Zheng if not self.ssh_port: 192ff2ebff0SFam Zheng raise Exception("Cannot find ssh port from 'info usernet':\n%s" % \ 193ff2ebff0SFam Zheng usernet_info) 194ff2ebff0SFam Zheng 1958dd38334SGerd Hoffmann def console_init(self, timeout = 120): 1968dd38334SGerd Hoffmann vm = self._guest 1978dd38334SGerd Hoffmann vm.console_socket.settimeout(timeout) 1988dd38334SGerd Hoffmann 1998dd38334SGerd Hoffmann def console_log(self, text): 2008dd38334SGerd Hoffmann for line in re.split("[\r\n]", text): 2018dd38334SGerd Hoffmann # filter out terminal escape sequences 2028dd38334SGerd Hoffmann line = re.sub("\x1b\[[0-9;?]*[a-zA-Z]", "", line) 2038dd38334SGerd Hoffmann line = re.sub("\x1b\([0-9;?]*[a-zA-Z]", "", line) 2048dd38334SGerd Hoffmann # replace unprintable chars 2058dd38334SGerd Hoffmann line = re.sub("\x1b", "<esc>", line) 2068dd38334SGerd Hoffmann line = re.sub("[\x00-\x1f]", ".", line) 2078dd38334SGerd Hoffmann line = re.sub("[\x80-\xff]", ".", line) 2088dd38334SGerd Hoffmann if line == "": 2098dd38334SGerd Hoffmann continue 2108dd38334SGerd Hoffmann # log console line 2118dd38334SGerd Hoffmann sys.stderr.write("con recv: %s\n" % line) 2128dd38334SGerd Hoffmann 21360136e06SGerd Hoffmann def console_wait(self, expect, expectalt = None): 2148dd38334SGerd Hoffmann vm = self._guest 2158dd38334SGerd Hoffmann output = "" 2168dd38334SGerd Hoffmann while True: 2178dd38334SGerd Hoffmann try: 2188dd38334SGerd Hoffmann chars = vm.console_socket.recv(1) 2198dd38334SGerd Hoffmann except socket.timeout: 2208dd38334SGerd Hoffmann sys.stderr.write("console: *** read timeout ***\n") 2218dd38334SGerd Hoffmann sys.stderr.write("console: waiting for: '%s'\n" % expect) 22260136e06SGerd Hoffmann if not expectalt is None: 22360136e06SGerd Hoffmann sys.stderr.write("console: waiting for: '%s' (alt)\n" % expectalt) 2248dd38334SGerd Hoffmann sys.stderr.write("console: line buffer:\n") 2258dd38334SGerd Hoffmann sys.stderr.write("\n") 2268dd38334SGerd Hoffmann self.console_log(output.rstrip()) 2278dd38334SGerd Hoffmann sys.stderr.write("\n") 2288dd38334SGerd Hoffmann raise 2298dd38334SGerd Hoffmann output += chars.decode("latin1") 2308dd38334SGerd Hoffmann if expect in output: 2318dd38334SGerd Hoffmann break 23260136e06SGerd Hoffmann if not expectalt is None and expectalt in output: 23360136e06SGerd Hoffmann break 2348dd38334SGerd Hoffmann if "\r" in output or "\n" in output: 2358dd38334SGerd Hoffmann lines = re.split("[\r\n]", output) 2368dd38334SGerd Hoffmann output = lines.pop() 2378dd38334SGerd Hoffmann if self.debug: 2388dd38334SGerd Hoffmann self.console_log("\n".join(lines)) 2398dd38334SGerd Hoffmann if self.debug: 2408dd38334SGerd Hoffmann self.console_log(output) 24160136e06SGerd Hoffmann if not expectalt is None and expectalt in output: 24260136e06SGerd Hoffmann return False 24360136e06SGerd Hoffmann return True 2448dd38334SGerd Hoffmann 245*6c4f0416SGerd Hoffmann def console_consume(self): 246*6c4f0416SGerd Hoffmann vm = self._guest 247*6c4f0416SGerd Hoffmann output = "" 248*6c4f0416SGerd Hoffmann vm.console_socket.setblocking(0) 249*6c4f0416SGerd Hoffmann while True: 250*6c4f0416SGerd Hoffmann try: 251*6c4f0416SGerd Hoffmann chars = vm.console_socket.recv(1) 252*6c4f0416SGerd Hoffmann except: 253*6c4f0416SGerd Hoffmann break 254*6c4f0416SGerd Hoffmann output += chars.decode("latin1") 255*6c4f0416SGerd Hoffmann if "\r" in output or "\n" in output: 256*6c4f0416SGerd Hoffmann lines = re.split("[\r\n]", output) 257*6c4f0416SGerd Hoffmann output = lines.pop() 258*6c4f0416SGerd Hoffmann if self.debug: 259*6c4f0416SGerd Hoffmann self.console_log("\n".join(lines)) 260*6c4f0416SGerd Hoffmann if self.debug: 261*6c4f0416SGerd Hoffmann self.console_log(output) 262*6c4f0416SGerd Hoffmann vm.console_socket.setblocking(1) 263*6c4f0416SGerd Hoffmann 2648dd38334SGerd Hoffmann def console_send(self, command): 2658dd38334SGerd Hoffmann vm = self._guest 2668dd38334SGerd Hoffmann if self.debug: 2678dd38334SGerd Hoffmann logline = re.sub("\n", "<enter>", command) 2688dd38334SGerd Hoffmann logline = re.sub("[\x00-\x1f]", ".", logline) 2698dd38334SGerd Hoffmann sys.stderr.write("con send: %s\n" % logline) 2708dd38334SGerd Hoffmann for char in list(command): 2718dd38334SGerd Hoffmann vm.console_socket.send(char.encode("utf-8")) 2728dd38334SGerd Hoffmann time.sleep(0.01) 2738dd38334SGerd Hoffmann 2748dd38334SGerd Hoffmann def console_wait_send(self, wait, command): 2758dd38334SGerd Hoffmann self.console_wait(wait) 2768dd38334SGerd Hoffmann self.console_send(command) 2778dd38334SGerd Hoffmann 2788dd38334SGerd Hoffmann def console_ssh_init(self, prompt, user, pw): 2798dd38334SGerd Hoffmann sshkey_cmd = "echo '%s' > .ssh/authorized_keys\n" % SSH_PUB_KEY.rstrip() 2808dd38334SGerd Hoffmann self.console_wait_send("login:", "%s\n" % user) 2818dd38334SGerd Hoffmann self.console_wait_send("Password:", "%s\n" % pw) 2828dd38334SGerd Hoffmann self.console_wait_send(prompt, "mkdir .ssh\n") 2838dd38334SGerd Hoffmann self.console_wait_send(prompt, sshkey_cmd) 2848dd38334SGerd Hoffmann self.console_wait_send(prompt, "chmod 755 .ssh\n") 2858dd38334SGerd Hoffmann self.console_wait_send(prompt, "chmod 644 .ssh/authorized_keys\n") 2868dd38334SGerd Hoffmann 2878dd38334SGerd Hoffmann def console_sshd_config(self, prompt): 2888dd38334SGerd Hoffmann self.console_wait(prompt) 2898dd38334SGerd Hoffmann self.console_send("echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config\n") 2908dd38334SGerd Hoffmann for var in self.envvars: 2918dd38334SGerd Hoffmann self.console_wait(prompt) 2928dd38334SGerd Hoffmann self.console_send("echo 'AcceptEnv %s' >> /etc/ssh/sshd_config\n" % var) 2938dd38334SGerd Hoffmann 2948dd38334SGerd Hoffmann def print_step(self, text): 2958dd38334SGerd Hoffmann sys.stderr.write("### %s ...\n" % text) 2968dd38334SGerd Hoffmann 2976b699ae1SPeter Maydell def wait_ssh(self, seconds=300): 298ff2ebff0SFam Zheng starttime = datetime.datetime.now() 299f5d3d218SPhilippe Mathieu-Daudé endtime = starttime + datetime.timedelta(seconds=seconds) 300ff2ebff0SFam Zheng guest_up = False 301f5d3d218SPhilippe Mathieu-Daudé while datetime.datetime.now() < endtime: 302ff2ebff0SFam Zheng if self.ssh("exit 0") == 0: 303ff2ebff0SFam Zheng guest_up = True 304ff2ebff0SFam Zheng break 305f5d3d218SPhilippe Mathieu-Daudé seconds = (endtime - datetime.datetime.now()).total_seconds() 306f5d3d218SPhilippe Mathieu-Daudé logging.debug("%ds before timeout", seconds) 307ff2ebff0SFam Zheng time.sleep(1) 308ff2ebff0SFam Zheng if not guest_up: 309ff2ebff0SFam Zheng raise Exception("Timeout while waiting for guest ssh") 310ff2ebff0SFam Zheng 311ff2ebff0SFam Zheng def shutdown(self): 312ff2ebff0SFam Zheng self._guest.shutdown() 313ff2ebff0SFam Zheng 314ff2ebff0SFam Zheng def wait(self): 315ff2ebff0SFam Zheng self._guest.wait() 316ff2ebff0SFam Zheng 317b3f94b2fSGerd Hoffmann def graceful_shutdown(self): 318b3f94b2fSGerd Hoffmann self.ssh_root(self.poweroff) 319b3f94b2fSGerd Hoffmann self._guest.wait() 320b3f94b2fSGerd Hoffmann 321ff2ebff0SFam Zheng def qmp(self, *args, **kwargs): 322ff2ebff0SFam Zheng return self._guest.qmp(*args, **kwargs) 323ff2ebff0SFam Zheng 32463a24c5eSPhilippe Mathieu-Daudédef parse_args(vmcls): 3258a6e007eSPhilippe Mathieu-Daudé 3268a6e007eSPhilippe Mathieu-Daudé def get_default_jobs(): 32763a24c5eSPhilippe Mathieu-Daudé if kvm_available(vmcls.arch): 3283ad3e36eSWainer dos Santos Moschetta return multiprocessing.cpu_count() // 2 3298a6e007eSPhilippe Mathieu-Daudé else: 3308a6e007eSPhilippe Mathieu-Daudé return 1 3318a6e007eSPhilippe Mathieu-Daudé 332ff2ebff0SFam Zheng parser = optparse.OptionParser( 333ff2ebff0SFam Zheng description="VM test utility. Exit codes: " 334ff2ebff0SFam Zheng "0 = success, " 335ff2ebff0SFam Zheng "1 = command line error, " 336ff2ebff0SFam Zheng "2 = environment initialization failed, " 337ff2ebff0SFam Zheng "3 = test command failed") 338ff2ebff0SFam Zheng parser.add_option("--debug", "-D", action="store_true", 339ff2ebff0SFam Zheng help="enable debug output") 34063a24c5eSPhilippe Mathieu-Daudé parser.add_option("--image", "-i", default="%s.img" % vmcls.name, 341ff2ebff0SFam Zheng help="image file name") 342ff2ebff0SFam Zheng parser.add_option("--force", "-f", action="store_true", 343ff2ebff0SFam Zheng help="force build image even if image exists") 3448a6e007eSPhilippe Mathieu-Daudé parser.add_option("--jobs", type=int, default=get_default_jobs(), 345ff2ebff0SFam Zheng help="number of virtual CPUs") 34641e3340aSPeter Maydell parser.add_option("--verbose", "-V", action="store_true", 34741e3340aSPeter Maydell help="Pass V=1 to builds within the guest") 348ff2ebff0SFam Zheng parser.add_option("--build-image", "-b", action="store_true", 349ff2ebff0SFam Zheng help="build image") 350ff2ebff0SFam Zheng parser.add_option("--build-qemu", 351ff2ebff0SFam Zheng help="build QEMU from source in guest") 3525c2ec9b6SAlex Bennée parser.add_option("--build-target", 3535c2ec9b6SAlex Bennée help="QEMU build target", default="check") 354ff2ebff0SFam Zheng parser.add_option("--interactive", "-I", action="store_true", 355ff2ebff0SFam Zheng help="Interactively run command") 356983c2a77SFam Zheng parser.add_option("--snapshot", "-s", action="store_true", 357983c2a77SFam Zheng help="run tests with a snapshot") 358ff2ebff0SFam Zheng parser.disable_interspersed_args() 359ff2ebff0SFam Zheng return parser.parse_args() 360ff2ebff0SFam Zheng 361ff2ebff0SFam Zhengdef main(vmcls): 362ff2ebff0SFam Zheng try: 36363a24c5eSPhilippe Mathieu-Daudé args, argv = parse_args(vmcls) 364ff2ebff0SFam Zheng if not argv and not args.build_qemu and not args.build_image: 365f03868bdSEduardo Habkost print("Nothing to do?") 366ff2ebff0SFam Zheng return 1 367fb3b4e6dSEduardo Habkost logging.basicConfig(level=(logging.DEBUG if args.debug 368fb3b4e6dSEduardo Habkost else logging.WARN)) 369ff2ebff0SFam Zheng vm = vmcls(debug=args.debug, vcpus=args.jobs) 370ff2ebff0SFam Zheng if args.build_image: 371ff2ebff0SFam Zheng if os.path.exists(args.image) and not args.force: 372ff2ebff0SFam Zheng sys.stderr.writelines(["Image file exists: %s\n" % args.image, 373ff2ebff0SFam Zheng "Use --force option to overwrite\n"]) 374ff2ebff0SFam Zheng return 1 375ff2ebff0SFam Zheng return vm.build_image(args.image) 376ff2ebff0SFam Zheng if args.build_qemu: 377ff2ebff0SFam Zheng vm.add_source_dir(args.build_qemu) 378ff2ebff0SFam Zheng cmd = [vm.BUILD_SCRIPT.format( 379ff2ebff0SFam Zheng configure_opts = " ".join(argv), 3803ace9be6SGerd Hoffmann jobs=int(args.jobs), 3815c2ec9b6SAlex Bennée target=args.build_target, 38241e3340aSPeter Maydell verbose = "V=1" if args.verbose else "")] 383ff2ebff0SFam Zheng else: 384ff2ebff0SFam Zheng cmd = argv 385983c2a77SFam Zheng img = args.image 386983c2a77SFam Zheng if args.snapshot: 387983c2a77SFam Zheng img += ",snapshot=on" 388983c2a77SFam Zheng vm.boot(img) 389ff2ebff0SFam Zheng vm.wait_ssh() 390ff2ebff0SFam Zheng except Exception as e: 391ff2ebff0SFam Zheng if isinstance(e, SystemExit) and e.code == 0: 392ff2ebff0SFam Zheng return 0 393ff2ebff0SFam Zheng sys.stderr.write("Failed to prepare guest environment\n") 394ff2ebff0SFam Zheng traceback.print_exc() 395ff2ebff0SFam Zheng return 2 396ff2ebff0SFam Zheng 397b3f94b2fSGerd Hoffmann exitcode = 0 398ff2ebff0SFam Zheng if vm.ssh(*cmd) != 0: 399b3f94b2fSGerd Hoffmann exitcode = 3 400b3f94b2fSGerd Hoffmann if exitcode != 0 and args.interactive: 401b3f94b2fSGerd Hoffmann vm.ssh() 402b3f94b2fSGerd Hoffmann 403b3f94b2fSGerd Hoffmann if not args.snapshot: 404b3f94b2fSGerd Hoffmann vm.graceful_shutdown() 405b3f94b2fSGerd Hoffmann 406b3f94b2fSGerd Hoffmann return exitcode 407