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" 60*5b790481SEduardo Habkost # enable IPv6 networking 61*5b790481SEduardo 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", 86*5b790481SEduardo Habkost "-netdev", "user,id=vnet,hostfwd=:127.0.0.1:0-:22" + 87*5b790481SEduardo 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 2458dd38334SGerd Hoffmann def console_send(self, command): 2468dd38334SGerd Hoffmann vm = self._guest 2478dd38334SGerd Hoffmann if self.debug: 2488dd38334SGerd Hoffmann logline = re.sub("\n", "<enter>", command) 2498dd38334SGerd Hoffmann logline = re.sub("[\x00-\x1f]", ".", logline) 2508dd38334SGerd Hoffmann sys.stderr.write("con send: %s\n" % logline) 2518dd38334SGerd Hoffmann for char in list(command): 2528dd38334SGerd Hoffmann vm.console_socket.send(char.encode("utf-8")) 2538dd38334SGerd Hoffmann time.sleep(0.01) 2548dd38334SGerd Hoffmann 2558dd38334SGerd Hoffmann def console_wait_send(self, wait, command): 2568dd38334SGerd Hoffmann self.console_wait(wait) 2578dd38334SGerd Hoffmann self.console_send(command) 2588dd38334SGerd Hoffmann 2598dd38334SGerd Hoffmann def console_ssh_init(self, prompt, user, pw): 2608dd38334SGerd Hoffmann sshkey_cmd = "echo '%s' > .ssh/authorized_keys\n" % SSH_PUB_KEY.rstrip() 2618dd38334SGerd Hoffmann self.console_wait_send("login:", "%s\n" % user) 2628dd38334SGerd Hoffmann self.console_wait_send("Password:", "%s\n" % pw) 2638dd38334SGerd Hoffmann self.console_wait_send(prompt, "mkdir .ssh\n") 2648dd38334SGerd Hoffmann self.console_wait_send(prompt, sshkey_cmd) 2658dd38334SGerd Hoffmann self.console_wait_send(prompt, "chmod 755 .ssh\n") 2668dd38334SGerd Hoffmann self.console_wait_send(prompt, "chmod 644 .ssh/authorized_keys\n") 2678dd38334SGerd Hoffmann 2688dd38334SGerd Hoffmann def console_sshd_config(self, prompt): 2698dd38334SGerd Hoffmann self.console_wait(prompt) 2708dd38334SGerd Hoffmann self.console_send("echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config\n") 2718dd38334SGerd Hoffmann for var in self.envvars: 2728dd38334SGerd Hoffmann self.console_wait(prompt) 2738dd38334SGerd Hoffmann self.console_send("echo 'AcceptEnv %s' >> /etc/ssh/sshd_config\n" % var) 2748dd38334SGerd Hoffmann 2758dd38334SGerd Hoffmann def print_step(self, text): 2768dd38334SGerd Hoffmann sys.stderr.write("### %s ...\n" % text) 2778dd38334SGerd Hoffmann 2786b699ae1SPeter Maydell def wait_ssh(self, seconds=300): 279ff2ebff0SFam Zheng starttime = datetime.datetime.now() 280f5d3d218SPhilippe Mathieu-Daudé endtime = starttime + datetime.timedelta(seconds=seconds) 281ff2ebff0SFam Zheng guest_up = False 282f5d3d218SPhilippe Mathieu-Daudé while datetime.datetime.now() < endtime: 283ff2ebff0SFam Zheng if self.ssh("exit 0") == 0: 284ff2ebff0SFam Zheng guest_up = True 285ff2ebff0SFam Zheng break 286f5d3d218SPhilippe Mathieu-Daudé seconds = (endtime - datetime.datetime.now()).total_seconds() 287f5d3d218SPhilippe Mathieu-Daudé logging.debug("%ds before timeout", seconds) 288ff2ebff0SFam Zheng time.sleep(1) 289ff2ebff0SFam Zheng if not guest_up: 290ff2ebff0SFam Zheng raise Exception("Timeout while waiting for guest ssh") 291ff2ebff0SFam Zheng 292ff2ebff0SFam Zheng def shutdown(self): 293ff2ebff0SFam Zheng self._guest.shutdown() 294ff2ebff0SFam Zheng 295ff2ebff0SFam Zheng def wait(self): 296ff2ebff0SFam Zheng self._guest.wait() 297ff2ebff0SFam Zheng 298b3f94b2fSGerd Hoffmann def graceful_shutdown(self): 299b3f94b2fSGerd Hoffmann self.ssh_root(self.poweroff) 300b3f94b2fSGerd Hoffmann self._guest.wait() 301b3f94b2fSGerd Hoffmann 302ff2ebff0SFam Zheng def qmp(self, *args, **kwargs): 303ff2ebff0SFam Zheng return self._guest.qmp(*args, **kwargs) 304ff2ebff0SFam Zheng 30563a24c5eSPhilippe Mathieu-Daudédef parse_args(vmcls): 3068a6e007eSPhilippe Mathieu-Daudé 3078a6e007eSPhilippe Mathieu-Daudé def get_default_jobs(): 30863a24c5eSPhilippe Mathieu-Daudé if kvm_available(vmcls.arch): 3093ad3e36eSWainer dos Santos Moschetta return multiprocessing.cpu_count() // 2 3108a6e007eSPhilippe Mathieu-Daudé else: 3118a6e007eSPhilippe Mathieu-Daudé return 1 3128a6e007eSPhilippe Mathieu-Daudé 313ff2ebff0SFam Zheng parser = optparse.OptionParser( 314ff2ebff0SFam Zheng description="VM test utility. Exit codes: " 315ff2ebff0SFam Zheng "0 = success, " 316ff2ebff0SFam Zheng "1 = command line error, " 317ff2ebff0SFam Zheng "2 = environment initialization failed, " 318ff2ebff0SFam Zheng "3 = test command failed") 319ff2ebff0SFam Zheng parser.add_option("--debug", "-D", action="store_true", 320ff2ebff0SFam Zheng help="enable debug output") 32163a24c5eSPhilippe Mathieu-Daudé parser.add_option("--image", "-i", default="%s.img" % vmcls.name, 322ff2ebff0SFam Zheng help="image file name") 323ff2ebff0SFam Zheng parser.add_option("--force", "-f", action="store_true", 324ff2ebff0SFam Zheng help="force build image even if image exists") 3258a6e007eSPhilippe Mathieu-Daudé parser.add_option("--jobs", type=int, default=get_default_jobs(), 326ff2ebff0SFam Zheng help="number of virtual CPUs") 32741e3340aSPeter Maydell parser.add_option("--verbose", "-V", action="store_true", 32841e3340aSPeter Maydell help="Pass V=1 to builds within the guest") 329ff2ebff0SFam Zheng parser.add_option("--build-image", "-b", action="store_true", 330ff2ebff0SFam Zheng help="build image") 331ff2ebff0SFam Zheng parser.add_option("--build-qemu", 332ff2ebff0SFam Zheng help="build QEMU from source in guest") 3335c2ec9b6SAlex Bennée parser.add_option("--build-target", 3345c2ec9b6SAlex Bennée help="QEMU build target", default="check") 335ff2ebff0SFam Zheng parser.add_option("--interactive", "-I", action="store_true", 336ff2ebff0SFam Zheng help="Interactively run command") 337983c2a77SFam Zheng parser.add_option("--snapshot", "-s", action="store_true", 338983c2a77SFam Zheng help="run tests with a snapshot") 339ff2ebff0SFam Zheng parser.disable_interspersed_args() 340ff2ebff0SFam Zheng return parser.parse_args() 341ff2ebff0SFam Zheng 342ff2ebff0SFam Zhengdef main(vmcls): 343ff2ebff0SFam Zheng try: 34463a24c5eSPhilippe Mathieu-Daudé args, argv = parse_args(vmcls) 345ff2ebff0SFam Zheng if not argv and not args.build_qemu and not args.build_image: 346f03868bdSEduardo Habkost print("Nothing to do?") 347ff2ebff0SFam Zheng return 1 348fb3b4e6dSEduardo Habkost logging.basicConfig(level=(logging.DEBUG if args.debug 349fb3b4e6dSEduardo Habkost else logging.WARN)) 350ff2ebff0SFam Zheng vm = vmcls(debug=args.debug, vcpus=args.jobs) 351ff2ebff0SFam Zheng if args.build_image: 352ff2ebff0SFam Zheng if os.path.exists(args.image) and not args.force: 353ff2ebff0SFam Zheng sys.stderr.writelines(["Image file exists: %s\n" % args.image, 354ff2ebff0SFam Zheng "Use --force option to overwrite\n"]) 355ff2ebff0SFam Zheng return 1 356ff2ebff0SFam Zheng return vm.build_image(args.image) 357ff2ebff0SFam Zheng if args.build_qemu: 358ff2ebff0SFam Zheng vm.add_source_dir(args.build_qemu) 359ff2ebff0SFam Zheng cmd = [vm.BUILD_SCRIPT.format( 360ff2ebff0SFam Zheng configure_opts = " ".join(argv), 3613ace9be6SGerd Hoffmann jobs=int(args.jobs), 3625c2ec9b6SAlex Bennée target=args.build_target, 36341e3340aSPeter Maydell verbose = "V=1" if args.verbose else "")] 364ff2ebff0SFam Zheng else: 365ff2ebff0SFam Zheng cmd = argv 366983c2a77SFam Zheng img = args.image 367983c2a77SFam Zheng if args.snapshot: 368983c2a77SFam Zheng img += ",snapshot=on" 369983c2a77SFam Zheng vm.boot(img) 370ff2ebff0SFam Zheng vm.wait_ssh() 371ff2ebff0SFam Zheng except Exception as e: 372ff2ebff0SFam Zheng if isinstance(e, SystemExit) and e.code == 0: 373ff2ebff0SFam Zheng return 0 374ff2ebff0SFam Zheng sys.stderr.write("Failed to prepare guest environment\n") 375ff2ebff0SFam Zheng traceback.print_exc() 376ff2ebff0SFam Zheng return 2 377ff2ebff0SFam Zheng 378b3f94b2fSGerd Hoffmann exitcode = 0 379ff2ebff0SFam Zheng if vm.ssh(*cmd) != 0: 380b3f94b2fSGerd Hoffmann exitcode = 3 381b3f94b2fSGerd Hoffmann if exitcode != 0 and args.interactive: 382b3f94b2fSGerd Hoffmann vm.ssh() 383b3f94b2fSGerd Hoffmann 384b3f94b2fSGerd Hoffmann if not args.snapshot: 385b3f94b2fSGerd Hoffmann vm.graceful_shutdown() 386b3f94b2fSGerd Hoffmann 387b3f94b2fSGerd Hoffmann return exitcode 388