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" 60ff2ebff0SFam Zheng def __init__(self, debug=False, vcpus=None): 61ff2ebff0SFam Zheng self._guest = None 62ff2ebff0SFam Zheng self._tmpdir = os.path.realpath(tempfile.mkdtemp(prefix="vm-test-", 63ff2ebff0SFam Zheng suffix=".tmp", 64ff2ebff0SFam Zheng dir=".")) 65ff2ebff0SFam Zheng atexit.register(shutil.rmtree, self._tmpdir) 66ff2ebff0SFam Zheng 67ff2ebff0SFam Zheng self._ssh_key_file = os.path.join(self._tmpdir, "id_rsa") 68ff2ebff0SFam Zheng open(self._ssh_key_file, "w").write(SSH_KEY) 69ff2ebff0SFam Zheng subprocess.check_call(["chmod", "600", self._ssh_key_file]) 70ff2ebff0SFam Zheng 71ff2ebff0SFam Zheng self._ssh_pub_key_file = os.path.join(self._tmpdir, "id_rsa.pub") 72ff2ebff0SFam Zheng open(self._ssh_pub_key_file, "w").write(SSH_PUB_KEY) 73ff2ebff0SFam Zheng 74ff2ebff0SFam Zheng self.debug = debug 75ff2ebff0SFam Zheng self._stderr = sys.stderr 76ff2ebff0SFam Zheng self._devnull = open(os.devnull, "w") 77ff2ebff0SFam Zheng if self.debug: 78ff2ebff0SFam Zheng self._stdout = sys.stdout 79ff2ebff0SFam Zheng else: 80ff2ebff0SFam Zheng self._stdout = self._devnull 81ff2ebff0SFam Zheng self._args = [ \ 82eb2712f5SPeter Maydell "-nodefaults", "-m", "4G", 83b33bd859SPeter Maydell "-cpu", "max", 84ff2ebff0SFam Zheng "-netdev", "user,id=vnet,hostfwd=:127.0.0.1:0-:22", 85ff2ebff0SFam Zheng "-device", "virtio-net-pci,netdev=vnet", 868dd38334SGerd Hoffmann "-vnc", "127.0.0.1:0,to=20"] 87071cf5a4SPhilippe Mathieu-Daudé if vcpus and vcpus > 1: 883ace9be6SGerd Hoffmann self._args += ["-smp", "%d" % vcpus] 8971531bb5SPhilippe Mathieu-Daudé if kvm_available(self.arch): 90ff2ebff0SFam Zheng self._args += ["-enable-kvm"] 91ff2ebff0SFam Zheng else: 92ff2ebff0SFam Zheng logging.info("KVM not available, not using -enable-kvm") 93ff2ebff0SFam Zheng self._data_args = [] 94ff2ebff0SFam Zheng 95ff2ebff0SFam Zheng def _download_with_cache(self, url, sha256sum=None): 96ff2ebff0SFam Zheng def check_sha256sum(fname): 97ff2ebff0SFam Zheng if not sha256sum: 98ff2ebff0SFam Zheng return True 99ff2ebff0SFam Zheng checksum = subprocess.check_output(["sha256sum", fname]).split()[0] 1003ace9be6SGerd Hoffmann return sha256sum == checksum.decode("utf-8") 101ff2ebff0SFam Zheng 102ff2ebff0SFam Zheng cache_dir = os.path.expanduser("~/.cache/qemu-vm/download") 103ff2ebff0SFam Zheng if not os.path.exists(cache_dir): 104ff2ebff0SFam Zheng os.makedirs(cache_dir) 1053ace9be6SGerd Hoffmann fname = os.path.join(cache_dir, 1063ace9be6SGerd Hoffmann hashlib.sha1(url.encode("utf-8")).hexdigest()) 107ff2ebff0SFam Zheng if os.path.exists(fname) and check_sha256sum(fname): 108ff2ebff0SFam Zheng return fname 109ff2ebff0SFam Zheng logging.debug("Downloading %s to %s...", url, fname) 110ff2ebff0SFam Zheng subprocess.check_call(["wget", "-c", url, "-O", fname + ".download"], 111ff2ebff0SFam Zheng stdout=self._stdout, stderr=self._stderr) 112ff2ebff0SFam Zheng os.rename(fname + ".download", fname) 113ff2ebff0SFam Zheng return fname 114ff2ebff0SFam Zheng 115796471e9SGerd Hoffmann def _ssh_do(self, user, cmd, check): 116796471e9SGerd Hoffmann ssh_cmd = ["ssh", "-q", "-t", 117ff2ebff0SFam Zheng "-o", "StrictHostKeyChecking=no", 118ff2ebff0SFam Zheng "-o", "UserKnownHostsFile=" + os.devnull, 119ff2ebff0SFam Zheng "-o", "ConnectTimeout=1", 120ff2ebff0SFam Zheng "-p", self.ssh_port, "-i", self._ssh_key_file] 121b08ba163SGerd Hoffmann for var in self.envvars: 122b08ba163SGerd Hoffmann ssh_cmd += ['-o', "SendEnv=%s" % var ] 123ff2ebff0SFam Zheng assert not isinstance(cmd, str) 124ff2ebff0SFam Zheng ssh_cmd += ["%s@127.0.0.1" % user] + list(cmd) 125ff2ebff0SFam Zheng logging.debug("ssh_cmd: %s", " ".join(ssh_cmd)) 126726c9a3bSFam Zheng r = subprocess.call(ssh_cmd) 127ff2ebff0SFam Zheng if check and r != 0: 128ff2ebff0SFam Zheng raise Exception("SSH command failed: %s" % cmd) 129ff2ebff0SFam Zheng return r 130ff2ebff0SFam Zheng 131ff2ebff0SFam Zheng def ssh(self, *cmd): 132ff2ebff0SFam Zheng return self._ssh_do(self.GUEST_USER, cmd, False) 133ff2ebff0SFam Zheng 134ff2ebff0SFam Zheng def ssh_root(self, *cmd): 135ff2ebff0SFam Zheng return self._ssh_do("root", cmd, False) 136ff2ebff0SFam Zheng 137ff2ebff0SFam Zheng def ssh_check(self, *cmd): 138ff2ebff0SFam Zheng self._ssh_do(self.GUEST_USER, cmd, True) 139ff2ebff0SFam Zheng 140ff2ebff0SFam Zheng def ssh_root_check(self, *cmd): 141ff2ebff0SFam Zheng self._ssh_do("root", cmd, True) 142ff2ebff0SFam Zheng 143ff2ebff0SFam Zheng def build_image(self, img): 144ff2ebff0SFam Zheng raise NotImplementedError 145ff2ebff0SFam Zheng 146ff2ebff0SFam Zheng def add_source_dir(self, src_dir): 1473ace9be6SGerd Hoffmann name = "data-" + hashlib.sha1(src_dir.encode("utf-8")).hexdigest()[:5] 148ff2ebff0SFam Zheng tarfile = os.path.join(self._tmpdir, name + ".tar") 149ff2ebff0SFam Zheng logging.debug("Creating archive %s for src_dir dir: %s", tarfile, src_dir) 150ff2ebff0SFam Zheng subprocess.check_call(["./scripts/archive-source.sh", tarfile], 151ff2ebff0SFam Zheng cwd=src_dir, stdin=self._devnull, 152ff2ebff0SFam Zheng stdout=self._stdout, stderr=self._stderr) 153ff2ebff0SFam Zheng self._data_args += ["-drive", 154ff2ebff0SFam Zheng "file=%s,if=none,id=%s,cache=writeback,format=raw" % \ 155ff2ebff0SFam Zheng (tarfile, name), 156ff2ebff0SFam Zheng "-device", 157ff2ebff0SFam Zheng "virtio-blk,drive=%s,serial=%s,bootindex=1" % (name, name)] 158ff2ebff0SFam Zheng 159ff2ebff0SFam Zheng def boot(self, img, extra_args=[]): 160ff2ebff0SFam Zheng args = self._args + [ 161ff2ebff0SFam Zheng "-device", "VGA", 162ff2ebff0SFam Zheng "-drive", "file=%s,if=none,id=drive0,cache=writeback" % img, 163ff2ebff0SFam Zheng "-device", "virtio-blk,drive=drive0,bootindex=0"] 164ff2ebff0SFam Zheng args += self._data_args + extra_args 165ff2ebff0SFam Zheng logging.debug("QEMU args: %s", " ".join(args)) 16631719c37SPhilippe Mathieu-Daudé qemu_bin = os.environ.get("QEMU", "qemu-system-" + self.arch) 167ff2ebff0SFam Zheng guest = QEMUMachine(binary=qemu_bin, args=args) 1688dd38334SGerd Hoffmann guest.set_machine('pc') 1698dd38334SGerd Hoffmann guest.set_console() 170ff2ebff0SFam Zheng try: 171ff2ebff0SFam Zheng guest.launch() 172ff2ebff0SFam Zheng except: 173ff2ebff0SFam Zheng logging.error("Failed to launch QEMU, command line:") 174ff2ebff0SFam Zheng logging.error(" ".join([qemu_bin] + args)) 175ff2ebff0SFam Zheng logging.error("Log:") 176ff2ebff0SFam Zheng logging.error(guest.get_log()) 177ff2ebff0SFam Zheng logging.error("QEMU version >= 2.10 is required") 178ff2ebff0SFam Zheng raise 179ff2ebff0SFam Zheng atexit.register(self.shutdown) 180ff2ebff0SFam Zheng self._guest = guest 181ff2ebff0SFam Zheng usernet_info = guest.qmp("human-monitor-command", 182ff2ebff0SFam Zheng command_line="info usernet") 183ff2ebff0SFam Zheng self.ssh_port = None 184ff2ebff0SFam Zheng for l in usernet_info["return"].splitlines(): 185ff2ebff0SFam Zheng fields = l.split() 186ff2ebff0SFam Zheng if "TCP[HOST_FORWARD]" in fields and "22" in fields: 187ff2ebff0SFam Zheng self.ssh_port = l.split()[3] 188ff2ebff0SFam Zheng if not self.ssh_port: 189ff2ebff0SFam Zheng raise Exception("Cannot find ssh port from 'info usernet':\n%s" % \ 190ff2ebff0SFam Zheng usernet_info) 191ff2ebff0SFam Zheng 1928dd38334SGerd Hoffmann def console_init(self, timeout = 120): 1938dd38334SGerd Hoffmann vm = self._guest 1948dd38334SGerd Hoffmann vm.console_socket.settimeout(timeout) 1958dd38334SGerd Hoffmann 1968dd38334SGerd Hoffmann def console_log(self, text): 1978dd38334SGerd Hoffmann for line in re.split("[\r\n]", text): 1988dd38334SGerd Hoffmann # filter out terminal escape sequences 1998dd38334SGerd Hoffmann line = re.sub("\x1b\[[0-9;?]*[a-zA-Z]", "", line) 2008dd38334SGerd Hoffmann line = re.sub("\x1b\([0-9;?]*[a-zA-Z]", "", line) 2018dd38334SGerd Hoffmann # replace unprintable chars 2028dd38334SGerd Hoffmann line = re.sub("\x1b", "<esc>", line) 2038dd38334SGerd Hoffmann line = re.sub("[\x00-\x1f]", ".", line) 2048dd38334SGerd Hoffmann line = re.sub("[\x80-\xff]", ".", line) 2058dd38334SGerd Hoffmann if line == "": 2068dd38334SGerd Hoffmann continue 2078dd38334SGerd Hoffmann # log console line 2088dd38334SGerd Hoffmann sys.stderr.write("con recv: %s\n" % line) 2098dd38334SGerd Hoffmann 210*60136e06SGerd Hoffmann def console_wait(self, expect, expectalt = None): 2118dd38334SGerd Hoffmann vm = self._guest 2128dd38334SGerd Hoffmann output = "" 2138dd38334SGerd Hoffmann while True: 2148dd38334SGerd Hoffmann try: 2158dd38334SGerd Hoffmann chars = vm.console_socket.recv(1) 2168dd38334SGerd Hoffmann except socket.timeout: 2178dd38334SGerd Hoffmann sys.stderr.write("console: *** read timeout ***\n") 2188dd38334SGerd Hoffmann sys.stderr.write("console: waiting for: '%s'\n" % expect) 219*60136e06SGerd Hoffmann if not expectalt is None: 220*60136e06SGerd Hoffmann sys.stderr.write("console: waiting for: '%s' (alt)\n" % expectalt) 2218dd38334SGerd Hoffmann sys.stderr.write("console: line buffer:\n") 2228dd38334SGerd Hoffmann sys.stderr.write("\n") 2238dd38334SGerd Hoffmann self.console_log(output.rstrip()) 2248dd38334SGerd Hoffmann sys.stderr.write("\n") 2258dd38334SGerd Hoffmann raise 2268dd38334SGerd Hoffmann output += chars.decode("latin1") 2278dd38334SGerd Hoffmann if expect in output: 2288dd38334SGerd Hoffmann break 229*60136e06SGerd Hoffmann if not expectalt is None and expectalt in output: 230*60136e06SGerd Hoffmann break 2318dd38334SGerd Hoffmann if "\r" in output or "\n" in output: 2328dd38334SGerd Hoffmann lines = re.split("[\r\n]", output) 2338dd38334SGerd Hoffmann output = lines.pop() 2348dd38334SGerd Hoffmann if self.debug: 2358dd38334SGerd Hoffmann self.console_log("\n".join(lines)) 2368dd38334SGerd Hoffmann if self.debug: 2378dd38334SGerd Hoffmann self.console_log(output) 238*60136e06SGerd Hoffmann if not expectalt is None and expectalt in output: 239*60136e06SGerd Hoffmann return False 240*60136e06SGerd Hoffmann return True 2418dd38334SGerd Hoffmann 2428dd38334SGerd Hoffmann def console_send(self, command): 2438dd38334SGerd Hoffmann vm = self._guest 2448dd38334SGerd Hoffmann if self.debug: 2458dd38334SGerd Hoffmann logline = re.sub("\n", "<enter>", command) 2468dd38334SGerd Hoffmann logline = re.sub("[\x00-\x1f]", ".", logline) 2478dd38334SGerd Hoffmann sys.stderr.write("con send: %s\n" % logline) 2488dd38334SGerd Hoffmann for char in list(command): 2498dd38334SGerd Hoffmann vm.console_socket.send(char.encode("utf-8")) 2508dd38334SGerd Hoffmann time.sleep(0.01) 2518dd38334SGerd Hoffmann 2528dd38334SGerd Hoffmann def console_wait_send(self, wait, command): 2538dd38334SGerd Hoffmann self.console_wait(wait) 2548dd38334SGerd Hoffmann self.console_send(command) 2558dd38334SGerd Hoffmann 2568dd38334SGerd Hoffmann def console_ssh_init(self, prompt, user, pw): 2578dd38334SGerd Hoffmann sshkey_cmd = "echo '%s' > .ssh/authorized_keys\n" % SSH_PUB_KEY.rstrip() 2588dd38334SGerd Hoffmann self.console_wait_send("login:", "%s\n" % user) 2598dd38334SGerd Hoffmann self.console_wait_send("Password:", "%s\n" % pw) 2608dd38334SGerd Hoffmann self.console_wait_send(prompt, "mkdir .ssh\n") 2618dd38334SGerd Hoffmann self.console_wait_send(prompt, sshkey_cmd) 2628dd38334SGerd Hoffmann self.console_wait_send(prompt, "chmod 755 .ssh\n") 2638dd38334SGerd Hoffmann self.console_wait_send(prompt, "chmod 644 .ssh/authorized_keys\n") 2648dd38334SGerd Hoffmann 2658dd38334SGerd Hoffmann def console_sshd_config(self, prompt): 2668dd38334SGerd Hoffmann self.console_wait(prompt) 2678dd38334SGerd Hoffmann self.console_send("echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config\n") 2688dd38334SGerd Hoffmann for var in self.envvars: 2698dd38334SGerd Hoffmann self.console_wait(prompt) 2708dd38334SGerd Hoffmann self.console_send("echo 'AcceptEnv %s' >> /etc/ssh/sshd_config\n" % var) 2718dd38334SGerd Hoffmann 2728dd38334SGerd Hoffmann def print_step(self, text): 2738dd38334SGerd Hoffmann sys.stderr.write("### %s ...\n" % text) 2748dd38334SGerd Hoffmann 2756b699ae1SPeter Maydell def wait_ssh(self, seconds=300): 276ff2ebff0SFam Zheng starttime = datetime.datetime.now() 277f5d3d218SPhilippe Mathieu-Daudé endtime = starttime + datetime.timedelta(seconds=seconds) 278ff2ebff0SFam Zheng guest_up = False 279f5d3d218SPhilippe Mathieu-Daudé while datetime.datetime.now() < endtime: 280ff2ebff0SFam Zheng if self.ssh("exit 0") == 0: 281ff2ebff0SFam Zheng guest_up = True 282ff2ebff0SFam Zheng break 283f5d3d218SPhilippe Mathieu-Daudé seconds = (endtime - datetime.datetime.now()).total_seconds() 284f5d3d218SPhilippe Mathieu-Daudé logging.debug("%ds before timeout", seconds) 285ff2ebff0SFam Zheng time.sleep(1) 286ff2ebff0SFam Zheng if not guest_up: 287ff2ebff0SFam Zheng raise Exception("Timeout while waiting for guest ssh") 288ff2ebff0SFam Zheng 289ff2ebff0SFam Zheng def shutdown(self): 290ff2ebff0SFam Zheng self._guest.shutdown() 291ff2ebff0SFam Zheng 292ff2ebff0SFam Zheng def wait(self): 293ff2ebff0SFam Zheng self._guest.wait() 294ff2ebff0SFam Zheng 295b3f94b2fSGerd Hoffmann def graceful_shutdown(self): 296b3f94b2fSGerd Hoffmann self.ssh_root(self.poweroff) 297b3f94b2fSGerd Hoffmann self._guest.wait() 298b3f94b2fSGerd Hoffmann 299ff2ebff0SFam Zheng def qmp(self, *args, **kwargs): 300ff2ebff0SFam Zheng return self._guest.qmp(*args, **kwargs) 301ff2ebff0SFam Zheng 30263a24c5eSPhilippe Mathieu-Daudédef parse_args(vmcls): 3038a6e007eSPhilippe Mathieu-Daudé 3048a6e007eSPhilippe Mathieu-Daudé def get_default_jobs(): 30563a24c5eSPhilippe Mathieu-Daudé if kvm_available(vmcls.arch): 3063ad3e36eSWainer dos Santos Moschetta return multiprocessing.cpu_count() // 2 3078a6e007eSPhilippe Mathieu-Daudé else: 3088a6e007eSPhilippe Mathieu-Daudé return 1 3098a6e007eSPhilippe Mathieu-Daudé 310ff2ebff0SFam Zheng parser = optparse.OptionParser( 311ff2ebff0SFam Zheng description="VM test utility. Exit codes: " 312ff2ebff0SFam Zheng "0 = success, " 313ff2ebff0SFam Zheng "1 = command line error, " 314ff2ebff0SFam Zheng "2 = environment initialization failed, " 315ff2ebff0SFam Zheng "3 = test command failed") 316ff2ebff0SFam Zheng parser.add_option("--debug", "-D", action="store_true", 317ff2ebff0SFam Zheng help="enable debug output") 31863a24c5eSPhilippe Mathieu-Daudé parser.add_option("--image", "-i", default="%s.img" % vmcls.name, 319ff2ebff0SFam Zheng help="image file name") 320ff2ebff0SFam Zheng parser.add_option("--force", "-f", action="store_true", 321ff2ebff0SFam Zheng help="force build image even if image exists") 3228a6e007eSPhilippe Mathieu-Daudé parser.add_option("--jobs", type=int, default=get_default_jobs(), 323ff2ebff0SFam Zheng help="number of virtual CPUs") 32441e3340aSPeter Maydell parser.add_option("--verbose", "-V", action="store_true", 32541e3340aSPeter Maydell help="Pass V=1 to builds within the guest") 326ff2ebff0SFam Zheng parser.add_option("--build-image", "-b", action="store_true", 327ff2ebff0SFam Zheng help="build image") 328ff2ebff0SFam Zheng parser.add_option("--build-qemu", 329ff2ebff0SFam Zheng help="build QEMU from source in guest") 3305c2ec9b6SAlex Bennée parser.add_option("--build-target", 3315c2ec9b6SAlex Bennée help="QEMU build target", default="check") 332ff2ebff0SFam Zheng parser.add_option("--interactive", "-I", action="store_true", 333ff2ebff0SFam Zheng help="Interactively run command") 334983c2a77SFam Zheng parser.add_option("--snapshot", "-s", action="store_true", 335983c2a77SFam Zheng help="run tests with a snapshot") 336ff2ebff0SFam Zheng parser.disable_interspersed_args() 337ff2ebff0SFam Zheng return parser.parse_args() 338ff2ebff0SFam Zheng 339ff2ebff0SFam Zhengdef main(vmcls): 340ff2ebff0SFam Zheng try: 34163a24c5eSPhilippe Mathieu-Daudé args, argv = parse_args(vmcls) 342ff2ebff0SFam Zheng if not argv and not args.build_qemu and not args.build_image: 343f03868bdSEduardo Habkost print("Nothing to do?") 344ff2ebff0SFam Zheng return 1 345fb3b4e6dSEduardo Habkost logging.basicConfig(level=(logging.DEBUG if args.debug 346fb3b4e6dSEduardo Habkost else logging.WARN)) 347ff2ebff0SFam Zheng vm = vmcls(debug=args.debug, vcpus=args.jobs) 348ff2ebff0SFam Zheng if args.build_image: 349ff2ebff0SFam Zheng if os.path.exists(args.image) and not args.force: 350ff2ebff0SFam Zheng sys.stderr.writelines(["Image file exists: %s\n" % args.image, 351ff2ebff0SFam Zheng "Use --force option to overwrite\n"]) 352ff2ebff0SFam Zheng return 1 353ff2ebff0SFam Zheng return vm.build_image(args.image) 354ff2ebff0SFam Zheng if args.build_qemu: 355ff2ebff0SFam Zheng vm.add_source_dir(args.build_qemu) 356ff2ebff0SFam Zheng cmd = [vm.BUILD_SCRIPT.format( 357ff2ebff0SFam Zheng configure_opts = " ".join(argv), 3583ace9be6SGerd Hoffmann jobs=int(args.jobs), 3595c2ec9b6SAlex Bennée target=args.build_target, 36041e3340aSPeter Maydell verbose = "V=1" if args.verbose else "")] 361ff2ebff0SFam Zheng else: 362ff2ebff0SFam Zheng cmd = argv 363983c2a77SFam Zheng img = args.image 364983c2a77SFam Zheng if args.snapshot: 365983c2a77SFam Zheng img += ",snapshot=on" 366983c2a77SFam Zheng vm.boot(img) 367ff2ebff0SFam Zheng vm.wait_ssh() 368ff2ebff0SFam Zheng except Exception as e: 369ff2ebff0SFam Zheng if isinstance(e, SystemExit) and e.code == 0: 370ff2ebff0SFam Zheng return 0 371ff2ebff0SFam Zheng sys.stderr.write("Failed to prepare guest environment\n") 372ff2ebff0SFam Zheng traceback.print_exc() 373ff2ebff0SFam Zheng return 2 374ff2ebff0SFam Zheng 375b3f94b2fSGerd Hoffmann exitcode = 0 376ff2ebff0SFam Zheng if vm.ssh(*cmd) != 0: 377b3f94b2fSGerd Hoffmann exitcode = 3 378b3f94b2fSGerd Hoffmann if exitcode != 0 and args.interactive: 379b3f94b2fSGerd Hoffmann vm.ssh() 380b3f94b2fSGerd Hoffmann 381b3f94b2fSGerd Hoffmann if not args.snapshot: 382b3f94b2fSGerd Hoffmann vm.graceful_shutdown() 383b3f94b2fSGerd Hoffmann 384b3f94b2fSGerd Hoffmann return exitcode 385