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')) 248b272e00SWainer dos Santos Moschettafrom qemu.accel 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 985b4b4865SAlex Bennée def _download_with_cache(self, url, sha256sum=None, sha512sum=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 1055b4b4865SAlex Bennée def check_sha512sum(fname): 1065b4b4865SAlex Bennée if not sha512sum: 1075b4b4865SAlex Bennée return True 1085b4b4865SAlex Bennée checksum = subprocess.check_output(["sha512sum", fname]).split()[0] 1095b4b4865SAlex Bennée return sha512sum == checksum.decode("utf-8") 1105b4b4865SAlex Bennée 111ff2ebff0SFam Zheng cache_dir = os.path.expanduser("~/.cache/qemu-vm/download") 112ff2ebff0SFam Zheng if not os.path.exists(cache_dir): 113ff2ebff0SFam Zheng os.makedirs(cache_dir) 1143ace9be6SGerd Hoffmann fname = os.path.join(cache_dir, 1153ace9be6SGerd Hoffmann hashlib.sha1(url.encode("utf-8")).hexdigest()) 1165b4b4865SAlex Bennée if os.path.exists(fname) and check_sha256sum(fname) and check_sha512sum(fname): 117ff2ebff0SFam Zheng return fname 118ff2ebff0SFam Zheng logging.debug("Downloading %s to %s...", url, fname) 119ff2ebff0SFam Zheng subprocess.check_call(["wget", "-c", url, "-O", fname + ".download"], 120ff2ebff0SFam Zheng stdout=self._stdout, stderr=self._stderr) 121ff2ebff0SFam Zheng os.rename(fname + ".download", fname) 122ff2ebff0SFam Zheng return fname 123ff2ebff0SFam Zheng 124796471e9SGerd Hoffmann def _ssh_do(self, user, cmd, check): 125796471e9SGerd Hoffmann ssh_cmd = ["ssh", "-q", "-t", 126ff2ebff0SFam Zheng "-o", "StrictHostKeyChecking=no", 127ff2ebff0SFam Zheng "-o", "UserKnownHostsFile=" + os.devnull, 128ff2ebff0SFam Zheng "-o", "ConnectTimeout=1", 129ff2ebff0SFam Zheng "-p", self.ssh_port, "-i", self._ssh_key_file] 130b08ba163SGerd Hoffmann for var in self.envvars: 131b08ba163SGerd Hoffmann ssh_cmd += ['-o', "SendEnv=%s" % var ] 132ff2ebff0SFam Zheng assert not isinstance(cmd, str) 133ff2ebff0SFam Zheng ssh_cmd += ["%s@127.0.0.1" % user] + list(cmd) 134ff2ebff0SFam Zheng logging.debug("ssh_cmd: %s", " ".join(ssh_cmd)) 135726c9a3bSFam Zheng r = subprocess.call(ssh_cmd) 136ff2ebff0SFam Zheng if check and r != 0: 137ff2ebff0SFam Zheng raise Exception("SSH command failed: %s" % cmd) 138ff2ebff0SFam Zheng return r 139ff2ebff0SFam Zheng 140ff2ebff0SFam Zheng def ssh(self, *cmd): 141ff2ebff0SFam Zheng return self._ssh_do(self.GUEST_USER, cmd, False) 142ff2ebff0SFam Zheng 143ff2ebff0SFam Zheng def ssh_root(self, *cmd): 144ff2ebff0SFam Zheng return self._ssh_do("root", cmd, False) 145ff2ebff0SFam Zheng 146ff2ebff0SFam Zheng def ssh_check(self, *cmd): 147ff2ebff0SFam Zheng self._ssh_do(self.GUEST_USER, cmd, True) 148ff2ebff0SFam Zheng 149ff2ebff0SFam Zheng def ssh_root_check(self, *cmd): 150ff2ebff0SFam Zheng self._ssh_do("root", cmd, True) 151ff2ebff0SFam Zheng 152ff2ebff0SFam Zheng def build_image(self, img): 153ff2ebff0SFam Zheng raise NotImplementedError 154ff2ebff0SFam Zheng 155*1e48931cSWainer dos Santos Moschetta def exec_qemu_img(self, *args): 156*1e48931cSWainer dos Santos Moschetta cmd = [os.environ.get("QEMU_IMG", "qemu-img")] 157*1e48931cSWainer dos Santos Moschetta cmd.extend(list(args)) 158*1e48931cSWainer dos Santos Moschetta subprocess.check_call(cmd) 159*1e48931cSWainer dos Santos Moschetta 160ff2ebff0SFam Zheng def add_source_dir(self, src_dir): 1613ace9be6SGerd Hoffmann name = "data-" + hashlib.sha1(src_dir.encode("utf-8")).hexdigest()[:5] 162ff2ebff0SFam Zheng tarfile = os.path.join(self._tmpdir, name + ".tar") 163ff2ebff0SFam Zheng logging.debug("Creating archive %s for src_dir dir: %s", tarfile, src_dir) 164ff2ebff0SFam Zheng subprocess.check_call(["./scripts/archive-source.sh", tarfile], 165ff2ebff0SFam Zheng cwd=src_dir, stdin=self._devnull, 166ff2ebff0SFam Zheng stdout=self._stdout, stderr=self._stderr) 167ff2ebff0SFam Zheng self._data_args += ["-drive", 168ff2ebff0SFam Zheng "file=%s,if=none,id=%s,cache=writeback,format=raw" % \ 169ff2ebff0SFam Zheng (tarfile, name), 170ff2ebff0SFam Zheng "-device", 171ff2ebff0SFam Zheng "virtio-blk,drive=%s,serial=%s,bootindex=1" % (name, name)] 172ff2ebff0SFam Zheng 173ff2ebff0SFam Zheng def boot(self, img, extra_args=[]): 174ff2ebff0SFam Zheng args = self._args + [ 175ff2ebff0SFam Zheng "-device", "VGA", 176ff2ebff0SFam Zheng "-drive", "file=%s,if=none,id=drive0,cache=writeback" % img, 177ff2ebff0SFam Zheng "-device", "virtio-blk,drive=drive0,bootindex=0"] 178ff2ebff0SFam Zheng args += self._data_args + extra_args 179ff2ebff0SFam Zheng logging.debug("QEMU args: %s", " ".join(args)) 18031719c37SPhilippe Mathieu-Daudé qemu_bin = os.environ.get("QEMU", "qemu-system-" + self.arch) 181ff2ebff0SFam Zheng guest = QEMUMachine(binary=qemu_bin, args=args) 1828dd38334SGerd Hoffmann guest.set_machine('pc') 1838dd38334SGerd Hoffmann guest.set_console() 184ff2ebff0SFam Zheng try: 185ff2ebff0SFam Zheng guest.launch() 186ff2ebff0SFam Zheng except: 187ff2ebff0SFam Zheng logging.error("Failed to launch QEMU, command line:") 188ff2ebff0SFam Zheng logging.error(" ".join([qemu_bin] + args)) 189ff2ebff0SFam Zheng logging.error("Log:") 190ff2ebff0SFam Zheng logging.error(guest.get_log()) 191ff2ebff0SFam Zheng logging.error("QEMU version >= 2.10 is required") 192ff2ebff0SFam Zheng raise 193ff2ebff0SFam Zheng atexit.register(self.shutdown) 194ff2ebff0SFam Zheng self._guest = guest 195ff2ebff0SFam Zheng usernet_info = guest.qmp("human-monitor-command", 196ff2ebff0SFam Zheng command_line="info usernet") 197ff2ebff0SFam Zheng self.ssh_port = None 198ff2ebff0SFam Zheng for l in usernet_info["return"].splitlines(): 199ff2ebff0SFam Zheng fields = l.split() 200ff2ebff0SFam Zheng if "TCP[HOST_FORWARD]" in fields and "22" in fields: 201ff2ebff0SFam Zheng self.ssh_port = l.split()[3] 202ff2ebff0SFam Zheng if not self.ssh_port: 203ff2ebff0SFam Zheng raise Exception("Cannot find ssh port from 'info usernet':\n%s" % \ 204ff2ebff0SFam Zheng usernet_info) 205ff2ebff0SFam Zheng 2068dd38334SGerd Hoffmann def console_init(self, timeout = 120): 2078dd38334SGerd Hoffmann vm = self._guest 2088dd38334SGerd Hoffmann vm.console_socket.settimeout(timeout) 2098dd38334SGerd Hoffmann 2108dd38334SGerd Hoffmann def console_log(self, text): 2118dd38334SGerd Hoffmann for line in re.split("[\r\n]", text): 2128dd38334SGerd Hoffmann # filter out terminal escape sequences 2138dd38334SGerd Hoffmann line = re.sub("\x1b\[[0-9;?]*[a-zA-Z]", "", line) 2148dd38334SGerd Hoffmann line = re.sub("\x1b\([0-9;?]*[a-zA-Z]", "", line) 2158dd38334SGerd Hoffmann # replace unprintable chars 2168dd38334SGerd Hoffmann line = re.sub("\x1b", "<esc>", line) 2178dd38334SGerd Hoffmann line = re.sub("[\x00-\x1f]", ".", line) 2188dd38334SGerd Hoffmann line = re.sub("[\x80-\xff]", ".", line) 2198dd38334SGerd Hoffmann if line == "": 2208dd38334SGerd Hoffmann continue 2218dd38334SGerd Hoffmann # log console line 2228dd38334SGerd Hoffmann sys.stderr.write("con recv: %s\n" % line) 2238dd38334SGerd Hoffmann 22460136e06SGerd Hoffmann def console_wait(self, expect, expectalt = None): 2258dd38334SGerd Hoffmann vm = self._guest 2268dd38334SGerd Hoffmann output = "" 2278dd38334SGerd Hoffmann while True: 2288dd38334SGerd Hoffmann try: 2298dd38334SGerd Hoffmann chars = vm.console_socket.recv(1) 2308dd38334SGerd Hoffmann except socket.timeout: 2318dd38334SGerd Hoffmann sys.stderr.write("console: *** read timeout ***\n") 2328dd38334SGerd Hoffmann sys.stderr.write("console: waiting for: '%s'\n" % expect) 23360136e06SGerd Hoffmann if not expectalt is None: 23460136e06SGerd Hoffmann sys.stderr.write("console: waiting for: '%s' (alt)\n" % expectalt) 2358dd38334SGerd Hoffmann sys.stderr.write("console: line buffer:\n") 2368dd38334SGerd Hoffmann sys.stderr.write("\n") 2378dd38334SGerd Hoffmann self.console_log(output.rstrip()) 2388dd38334SGerd Hoffmann sys.stderr.write("\n") 2398dd38334SGerd Hoffmann raise 2408dd38334SGerd Hoffmann output += chars.decode("latin1") 2418dd38334SGerd Hoffmann if expect in output: 2428dd38334SGerd Hoffmann break 24360136e06SGerd Hoffmann if not expectalt is None and expectalt in output: 24460136e06SGerd Hoffmann break 2458dd38334SGerd Hoffmann if "\r" in output or "\n" in output: 2468dd38334SGerd Hoffmann lines = re.split("[\r\n]", output) 2478dd38334SGerd Hoffmann output = lines.pop() 2488dd38334SGerd Hoffmann if self.debug: 2498dd38334SGerd Hoffmann self.console_log("\n".join(lines)) 2508dd38334SGerd Hoffmann if self.debug: 2518dd38334SGerd Hoffmann self.console_log(output) 25260136e06SGerd Hoffmann if not expectalt is None and expectalt in output: 25360136e06SGerd Hoffmann return False 25460136e06SGerd Hoffmann return True 2558dd38334SGerd Hoffmann 2566c4f0416SGerd Hoffmann def console_consume(self): 2576c4f0416SGerd Hoffmann vm = self._guest 2586c4f0416SGerd Hoffmann output = "" 2596c4f0416SGerd Hoffmann vm.console_socket.setblocking(0) 2606c4f0416SGerd Hoffmann while True: 2616c4f0416SGerd Hoffmann try: 2626c4f0416SGerd Hoffmann chars = vm.console_socket.recv(1) 2636c4f0416SGerd Hoffmann except: 2646c4f0416SGerd Hoffmann break 2656c4f0416SGerd Hoffmann output += chars.decode("latin1") 2666c4f0416SGerd Hoffmann if "\r" in output or "\n" in output: 2676c4f0416SGerd Hoffmann lines = re.split("[\r\n]", output) 2686c4f0416SGerd Hoffmann output = lines.pop() 2696c4f0416SGerd Hoffmann if self.debug: 2706c4f0416SGerd Hoffmann self.console_log("\n".join(lines)) 2716c4f0416SGerd Hoffmann if self.debug: 2726c4f0416SGerd Hoffmann self.console_log(output) 2736c4f0416SGerd Hoffmann vm.console_socket.setblocking(1) 2746c4f0416SGerd Hoffmann 2758dd38334SGerd Hoffmann def console_send(self, command): 2768dd38334SGerd Hoffmann vm = self._guest 2778dd38334SGerd Hoffmann if self.debug: 2788dd38334SGerd Hoffmann logline = re.sub("\n", "<enter>", command) 2798dd38334SGerd Hoffmann logline = re.sub("[\x00-\x1f]", ".", logline) 2808dd38334SGerd Hoffmann sys.stderr.write("con send: %s\n" % logline) 2818dd38334SGerd Hoffmann for char in list(command): 2828dd38334SGerd Hoffmann vm.console_socket.send(char.encode("utf-8")) 2838dd38334SGerd Hoffmann time.sleep(0.01) 2848dd38334SGerd Hoffmann 2858dd38334SGerd Hoffmann def console_wait_send(self, wait, command): 2868dd38334SGerd Hoffmann self.console_wait(wait) 2878dd38334SGerd Hoffmann self.console_send(command) 2888dd38334SGerd Hoffmann 2898dd38334SGerd Hoffmann def console_ssh_init(self, prompt, user, pw): 2908dd38334SGerd Hoffmann sshkey_cmd = "echo '%s' > .ssh/authorized_keys\n" % SSH_PUB_KEY.rstrip() 2918dd38334SGerd Hoffmann self.console_wait_send("login:", "%s\n" % user) 2928dd38334SGerd Hoffmann self.console_wait_send("Password:", "%s\n" % pw) 2938dd38334SGerd Hoffmann self.console_wait_send(prompt, "mkdir .ssh\n") 2948dd38334SGerd Hoffmann self.console_wait_send(prompt, sshkey_cmd) 2958dd38334SGerd Hoffmann self.console_wait_send(prompt, "chmod 755 .ssh\n") 2968dd38334SGerd Hoffmann self.console_wait_send(prompt, "chmod 644 .ssh/authorized_keys\n") 2978dd38334SGerd Hoffmann 2988dd38334SGerd Hoffmann def console_sshd_config(self, prompt): 2998dd38334SGerd Hoffmann self.console_wait(prompt) 3008dd38334SGerd Hoffmann self.console_send("echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config\n") 3018dd38334SGerd Hoffmann for var in self.envvars: 3028dd38334SGerd Hoffmann self.console_wait(prompt) 3038dd38334SGerd Hoffmann self.console_send("echo 'AcceptEnv %s' >> /etc/ssh/sshd_config\n" % var) 3048dd38334SGerd Hoffmann 3058dd38334SGerd Hoffmann def print_step(self, text): 3068dd38334SGerd Hoffmann sys.stderr.write("### %s ...\n" % text) 3078dd38334SGerd Hoffmann 3086b699ae1SPeter Maydell def wait_ssh(self, seconds=300): 309ff2ebff0SFam Zheng starttime = datetime.datetime.now() 310f5d3d218SPhilippe Mathieu-Daudé endtime = starttime + datetime.timedelta(seconds=seconds) 311ff2ebff0SFam Zheng guest_up = False 312f5d3d218SPhilippe Mathieu-Daudé while datetime.datetime.now() < endtime: 313ff2ebff0SFam Zheng if self.ssh("exit 0") == 0: 314ff2ebff0SFam Zheng guest_up = True 315ff2ebff0SFam Zheng break 316f5d3d218SPhilippe Mathieu-Daudé seconds = (endtime - datetime.datetime.now()).total_seconds() 317f5d3d218SPhilippe Mathieu-Daudé logging.debug("%ds before timeout", seconds) 318ff2ebff0SFam Zheng time.sleep(1) 319ff2ebff0SFam Zheng if not guest_up: 320ff2ebff0SFam Zheng raise Exception("Timeout while waiting for guest ssh") 321ff2ebff0SFam Zheng 322ff2ebff0SFam Zheng def shutdown(self): 323ff2ebff0SFam Zheng self._guest.shutdown() 324ff2ebff0SFam Zheng 325ff2ebff0SFam Zheng def wait(self): 326ff2ebff0SFam Zheng self._guest.wait() 327ff2ebff0SFam Zheng 328b3f94b2fSGerd Hoffmann def graceful_shutdown(self): 329b3f94b2fSGerd Hoffmann self.ssh_root(self.poweroff) 330b3f94b2fSGerd Hoffmann self._guest.wait() 331b3f94b2fSGerd Hoffmann 332ff2ebff0SFam Zheng def qmp(self, *args, **kwargs): 333ff2ebff0SFam Zheng return self._guest.qmp(*args, **kwargs) 334ff2ebff0SFam Zheng 33563a24c5eSPhilippe Mathieu-Daudédef parse_args(vmcls): 3368a6e007eSPhilippe Mathieu-Daudé 3378a6e007eSPhilippe Mathieu-Daudé def get_default_jobs(): 33863a24c5eSPhilippe Mathieu-Daudé if kvm_available(vmcls.arch): 3393ad3e36eSWainer dos Santos Moschetta return multiprocessing.cpu_count() // 2 3408a6e007eSPhilippe Mathieu-Daudé else: 3418a6e007eSPhilippe Mathieu-Daudé return 1 3428a6e007eSPhilippe Mathieu-Daudé 343ff2ebff0SFam Zheng parser = optparse.OptionParser( 344ff2ebff0SFam Zheng description="VM test utility. Exit codes: " 345ff2ebff0SFam Zheng "0 = success, " 346ff2ebff0SFam Zheng "1 = command line error, " 347ff2ebff0SFam Zheng "2 = environment initialization failed, " 348ff2ebff0SFam Zheng "3 = test command failed") 349ff2ebff0SFam Zheng parser.add_option("--debug", "-D", action="store_true", 350ff2ebff0SFam Zheng help="enable debug output") 35163a24c5eSPhilippe Mathieu-Daudé parser.add_option("--image", "-i", default="%s.img" % vmcls.name, 352ff2ebff0SFam Zheng help="image file name") 353ff2ebff0SFam Zheng parser.add_option("--force", "-f", action="store_true", 354ff2ebff0SFam Zheng help="force build image even if image exists") 3558a6e007eSPhilippe Mathieu-Daudé parser.add_option("--jobs", type=int, default=get_default_jobs(), 356ff2ebff0SFam Zheng help="number of virtual CPUs") 35741e3340aSPeter Maydell parser.add_option("--verbose", "-V", action="store_true", 35841e3340aSPeter Maydell help="Pass V=1 to builds within the guest") 359ff2ebff0SFam Zheng parser.add_option("--build-image", "-b", action="store_true", 360ff2ebff0SFam Zheng help="build image") 361ff2ebff0SFam Zheng parser.add_option("--build-qemu", 362ff2ebff0SFam Zheng help="build QEMU from source in guest") 3635c2ec9b6SAlex Bennée parser.add_option("--build-target", 3645c2ec9b6SAlex Bennée help="QEMU build target", default="check") 365ff2ebff0SFam Zheng parser.add_option("--interactive", "-I", action="store_true", 366ff2ebff0SFam Zheng help="Interactively run command") 367983c2a77SFam Zheng parser.add_option("--snapshot", "-s", action="store_true", 368983c2a77SFam Zheng help="run tests with a snapshot") 369ff2ebff0SFam Zheng parser.disable_interspersed_args() 370ff2ebff0SFam Zheng return parser.parse_args() 371ff2ebff0SFam Zheng 372ff2ebff0SFam Zhengdef main(vmcls): 373ff2ebff0SFam Zheng try: 37463a24c5eSPhilippe Mathieu-Daudé args, argv = parse_args(vmcls) 375ff2ebff0SFam Zheng if not argv and not args.build_qemu and not args.build_image: 376f03868bdSEduardo Habkost print("Nothing to do?") 377ff2ebff0SFam Zheng return 1 378fb3b4e6dSEduardo Habkost logging.basicConfig(level=(logging.DEBUG if args.debug 379fb3b4e6dSEduardo Habkost else logging.WARN)) 380ff2ebff0SFam Zheng vm = vmcls(debug=args.debug, vcpus=args.jobs) 381ff2ebff0SFam Zheng if args.build_image: 382ff2ebff0SFam Zheng if os.path.exists(args.image) and not args.force: 383ff2ebff0SFam Zheng sys.stderr.writelines(["Image file exists: %s\n" % args.image, 384ff2ebff0SFam Zheng "Use --force option to overwrite\n"]) 385ff2ebff0SFam Zheng return 1 386ff2ebff0SFam Zheng return vm.build_image(args.image) 387ff2ebff0SFam Zheng if args.build_qemu: 388ff2ebff0SFam Zheng vm.add_source_dir(args.build_qemu) 389ff2ebff0SFam Zheng cmd = [vm.BUILD_SCRIPT.format( 390ff2ebff0SFam Zheng configure_opts = " ".join(argv), 3913ace9be6SGerd Hoffmann jobs=int(args.jobs), 3925c2ec9b6SAlex Bennée target=args.build_target, 39341e3340aSPeter Maydell verbose = "V=1" if args.verbose else "")] 394ff2ebff0SFam Zheng else: 395ff2ebff0SFam Zheng cmd = argv 396983c2a77SFam Zheng img = args.image 397983c2a77SFam Zheng if args.snapshot: 398983c2a77SFam Zheng img += ",snapshot=on" 399983c2a77SFam Zheng vm.boot(img) 400ff2ebff0SFam Zheng vm.wait_ssh() 401ff2ebff0SFam Zheng except Exception as e: 402ff2ebff0SFam Zheng if isinstance(e, SystemExit) and e.code == 0: 403ff2ebff0SFam Zheng return 0 404ff2ebff0SFam Zheng sys.stderr.write("Failed to prepare guest environment\n") 405ff2ebff0SFam Zheng traceback.print_exc() 406ff2ebff0SFam Zheng return 2 407ff2ebff0SFam Zheng 408b3f94b2fSGerd Hoffmann exitcode = 0 409ff2ebff0SFam Zheng if vm.ssh(*cmd) != 0: 410b3f94b2fSGerd Hoffmann exitcode = 3 411bcc388dfSAlex Bennée if args.interactive: 412b3f94b2fSGerd Hoffmann vm.ssh() 413b3f94b2fSGerd Hoffmann 414b3f94b2fSGerd Hoffmann if not args.snapshot: 415b3f94b2fSGerd Hoffmann vm.graceful_shutdown() 416b3f94b2fSGerd Hoffmann 417b3f94b2fSGerd Hoffmann return exitcode 418