1ff2ebff0SFam Zheng#!/usr/bin/env python 2ff2ebff0SFam Zheng# 3ff2ebff0SFam Zheng# VM testing base class 4ff2ebff0SFam Zheng# 5*8dd38334SGerd Hoffmann# Copyright 2017-2019 Red Hat Inc. 6ff2ebff0SFam Zheng# 7ff2ebff0SFam Zheng# Authors: 8ff2ebff0SFam Zheng# Fam Zheng <famz@redhat.com> 9*8dd38334SGerd 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 17*8dd38334SGerd Hoffmannimport re 18ff2ebff0SFam Zhengimport sys 19*8dd38334SGerd 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", 86*8dd38334SGerd 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) 168*8dd38334SGerd Hoffmann guest.set_machine('pc') 169*8dd38334SGerd 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 192*8dd38334SGerd Hoffmann def console_init(self, timeout = 120): 193*8dd38334SGerd Hoffmann vm = self._guest 194*8dd38334SGerd Hoffmann vm.console_socket.settimeout(timeout) 195*8dd38334SGerd Hoffmann 196*8dd38334SGerd Hoffmann def console_log(self, text): 197*8dd38334SGerd Hoffmann for line in re.split("[\r\n]", text): 198*8dd38334SGerd Hoffmann # filter out terminal escape sequences 199*8dd38334SGerd Hoffmann line = re.sub("\x1b\[[0-9;?]*[a-zA-Z]", "", line) 200*8dd38334SGerd Hoffmann line = re.sub("\x1b\([0-9;?]*[a-zA-Z]", "", line) 201*8dd38334SGerd Hoffmann # replace unprintable chars 202*8dd38334SGerd Hoffmann line = re.sub("\x1b", "<esc>", line) 203*8dd38334SGerd Hoffmann line = re.sub("[\x00-\x1f]", ".", line) 204*8dd38334SGerd Hoffmann line = re.sub("[\x80-\xff]", ".", line) 205*8dd38334SGerd Hoffmann if line == "": 206*8dd38334SGerd Hoffmann continue 207*8dd38334SGerd Hoffmann # log console line 208*8dd38334SGerd Hoffmann sys.stderr.write("con recv: %s\n" % line) 209*8dd38334SGerd Hoffmann 210*8dd38334SGerd Hoffmann def console_wait(self, expect): 211*8dd38334SGerd Hoffmann vm = self._guest 212*8dd38334SGerd Hoffmann output = "" 213*8dd38334SGerd Hoffmann while True: 214*8dd38334SGerd Hoffmann try: 215*8dd38334SGerd Hoffmann chars = vm.console_socket.recv(1) 216*8dd38334SGerd Hoffmann except socket.timeout: 217*8dd38334SGerd Hoffmann sys.stderr.write("console: *** read timeout ***\n") 218*8dd38334SGerd Hoffmann sys.stderr.write("console: waiting for: '%s'\n" % expect) 219*8dd38334SGerd Hoffmann sys.stderr.write("console: line buffer:\n") 220*8dd38334SGerd Hoffmann sys.stderr.write("\n") 221*8dd38334SGerd Hoffmann self.console_log(output.rstrip()) 222*8dd38334SGerd Hoffmann sys.stderr.write("\n") 223*8dd38334SGerd Hoffmann raise 224*8dd38334SGerd Hoffmann output += chars.decode("latin1") 225*8dd38334SGerd Hoffmann if expect in output: 226*8dd38334SGerd Hoffmann break 227*8dd38334SGerd Hoffmann if "\r" in output or "\n" in output: 228*8dd38334SGerd Hoffmann lines = re.split("[\r\n]", output) 229*8dd38334SGerd Hoffmann output = lines.pop() 230*8dd38334SGerd Hoffmann if self.debug: 231*8dd38334SGerd Hoffmann self.console_log("\n".join(lines)) 232*8dd38334SGerd Hoffmann if self.debug: 233*8dd38334SGerd Hoffmann self.console_log(output) 234*8dd38334SGerd Hoffmann 235*8dd38334SGerd Hoffmann def console_send(self, command): 236*8dd38334SGerd Hoffmann vm = self._guest 237*8dd38334SGerd Hoffmann if self.debug: 238*8dd38334SGerd Hoffmann logline = re.sub("\n", "<enter>", command) 239*8dd38334SGerd Hoffmann logline = re.sub("[\x00-\x1f]", ".", logline) 240*8dd38334SGerd Hoffmann sys.stderr.write("con send: %s\n" % logline) 241*8dd38334SGerd Hoffmann for char in list(command): 242*8dd38334SGerd Hoffmann vm.console_socket.send(char.encode("utf-8")) 243*8dd38334SGerd Hoffmann time.sleep(0.01) 244*8dd38334SGerd Hoffmann 245*8dd38334SGerd Hoffmann def console_wait_send(self, wait, command): 246*8dd38334SGerd Hoffmann self.console_wait(wait) 247*8dd38334SGerd Hoffmann self.console_send(command) 248*8dd38334SGerd Hoffmann 249*8dd38334SGerd Hoffmann def console_ssh_init(self, prompt, user, pw): 250*8dd38334SGerd Hoffmann sshkey_cmd = "echo '%s' > .ssh/authorized_keys\n" % SSH_PUB_KEY.rstrip() 251*8dd38334SGerd Hoffmann self.console_wait_send("login:", "%s\n" % user) 252*8dd38334SGerd Hoffmann self.console_wait_send("Password:", "%s\n" % pw) 253*8dd38334SGerd Hoffmann self.console_wait_send(prompt, "mkdir .ssh\n") 254*8dd38334SGerd Hoffmann self.console_wait_send(prompt, sshkey_cmd) 255*8dd38334SGerd Hoffmann self.console_wait_send(prompt, "chmod 755 .ssh\n") 256*8dd38334SGerd Hoffmann self.console_wait_send(prompt, "chmod 644 .ssh/authorized_keys\n") 257*8dd38334SGerd Hoffmann 258*8dd38334SGerd Hoffmann def console_sshd_config(self, prompt): 259*8dd38334SGerd Hoffmann self.console_wait(prompt) 260*8dd38334SGerd Hoffmann self.console_send("echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config\n") 261*8dd38334SGerd Hoffmann for var in self.envvars: 262*8dd38334SGerd Hoffmann self.console_wait(prompt) 263*8dd38334SGerd Hoffmann self.console_send("echo 'AcceptEnv %s' >> /etc/ssh/sshd_config\n" % var) 264*8dd38334SGerd Hoffmann 265*8dd38334SGerd Hoffmann def print_step(self, text): 266*8dd38334SGerd Hoffmann sys.stderr.write("### %s ...\n" % text) 267*8dd38334SGerd Hoffmann 2686b699ae1SPeter Maydell def wait_ssh(self, seconds=300): 269ff2ebff0SFam Zheng starttime = datetime.datetime.now() 270f5d3d218SPhilippe Mathieu-Daudé endtime = starttime + datetime.timedelta(seconds=seconds) 271ff2ebff0SFam Zheng guest_up = False 272f5d3d218SPhilippe Mathieu-Daudé while datetime.datetime.now() < endtime: 273ff2ebff0SFam Zheng if self.ssh("exit 0") == 0: 274ff2ebff0SFam Zheng guest_up = True 275ff2ebff0SFam Zheng break 276f5d3d218SPhilippe Mathieu-Daudé seconds = (endtime - datetime.datetime.now()).total_seconds() 277f5d3d218SPhilippe Mathieu-Daudé logging.debug("%ds before timeout", seconds) 278ff2ebff0SFam Zheng time.sleep(1) 279ff2ebff0SFam Zheng if not guest_up: 280ff2ebff0SFam Zheng raise Exception("Timeout while waiting for guest ssh") 281ff2ebff0SFam Zheng 282ff2ebff0SFam Zheng def shutdown(self): 283ff2ebff0SFam Zheng self._guest.shutdown() 284ff2ebff0SFam Zheng 285ff2ebff0SFam Zheng def wait(self): 286ff2ebff0SFam Zheng self._guest.wait() 287ff2ebff0SFam Zheng 288b3f94b2fSGerd Hoffmann def graceful_shutdown(self): 289b3f94b2fSGerd Hoffmann self.ssh_root(self.poweroff) 290b3f94b2fSGerd Hoffmann self._guest.wait() 291b3f94b2fSGerd Hoffmann 292ff2ebff0SFam Zheng def qmp(self, *args, **kwargs): 293ff2ebff0SFam Zheng return self._guest.qmp(*args, **kwargs) 294ff2ebff0SFam Zheng 29563a24c5eSPhilippe Mathieu-Daudédef parse_args(vmcls): 2968a6e007eSPhilippe Mathieu-Daudé 2978a6e007eSPhilippe Mathieu-Daudé def get_default_jobs(): 29863a24c5eSPhilippe Mathieu-Daudé if kvm_available(vmcls.arch): 2993ad3e36eSWainer dos Santos Moschetta return multiprocessing.cpu_count() // 2 3008a6e007eSPhilippe Mathieu-Daudé else: 3018a6e007eSPhilippe Mathieu-Daudé return 1 3028a6e007eSPhilippe Mathieu-Daudé 303ff2ebff0SFam Zheng parser = optparse.OptionParser( 304ff2ebff0SFam Zheng description="VM test utility. Exit codes: " 305ff2ebff0SFam Zheng "0 = success, " 306ff2ebff0SFam Zheng "1 = command line error, " 307ff2ebff0SFam Zheng "2 = environment initialization failed, " 308ff2ebff0SFam Zheng "3 = test command failed") 309ff2ebff0SFam Zheng parser.add_option("--debug", "-D", action="store_true", 310ff2ebff0SFam Zheng help="enable debug output") 31163a24c5eSPhilippe Mathieu-Daudé parser.add_option("--image", "-i", default="%s.img" % vmcls.name, 312ff2ebff0SFam Zheng help="image file name") 313ff2ebff0SFam Zheng parser.add_option("--force", "-f", action="store_true", 314ff2ebff0SFam Zheng help="force build image even if image exists") 3158a6e007eSPhilippe Mathieu-Daudé parser.add_option("--jobs", type=int, default=get_default_jobs(), 316ff2ebff0SFam Zheng help="number of virtual CPUs") 31741e3340aSPeter Maydell parser.add_option("--verbose", "-V", action="store_true", 31841e3340aSPeter Maydell help="Pass V=1 to builds within the guest") 319ff2ebff0SFam Zheng parser.add_option("--build-image", "-b", action="store_true", 320ff2ebff0SFam Zheng help="build image") 321ff2ebff0SFam Zheng parser.add_option("--build-qemu", 322ff2ebff0SFam Zheng help="build QEMU from source in guest") 3235c2ec9b6SAlex Bennée parser.add_option("--build-target", 3245c2ec9b6SAlex Bennée help="QEMU build target", default="check") 325ff2ebff0SFam Zheng parser.add_option("--interactive", "-I", action="store_true", 326ff2ebff0SFam Zheng help="Interactively run command") 327983c2a77SFam Zheng parser.add_option("--snapshot", "-s", action="store_true", 328983c2a77SFam Zheng help="run tests with a snapshot") 329ff2ebff0SFam Zheng parser.disable_interspersed_args() 330ff2ebff0SFam Zheng return parser.parse_args() 331ff2ebff0SFam Zheng 332ff2ebff0SFam Zhengdef main(vmcls): 333ff2ebff0SFam Zheng try: 33463a24c5eSPhilippe Mathieu-Daudé args, argv = parse_args(vmcls) 335ff2ebff0SFam Zheng if not argv and not args.build_qemu and not args.build_image: 336f03868bdSEduardo Habkost print("Nothing to do?") 337ff2ebff0SFam Zheng return 1 338fb3b4e6dSEduardo Habkost logging.basicConfig(level=(logging.DEBUG if args.debug 339fb3b4e6dSEduardo Habkost else logging.WARN)) 340ff2ebff0SFam Zheng vm = vmcls(debug=args.debug, vcpus=args.jobs) 341ff2ebff0SFam Zheng if args.build_image: 342ff2ebff0SFam Zheng if os.path.exists(args.image) and not args.force: 343ff2ebff0SFam Zheng sys.stderr.writelines(["Image file exists: %s\n" % args.image, 344ff2ebff0SFam Zheng "Use --force option to overwrite\n"]) 345ff2ebff0SFam Zheng return 1 346ff2ebff0SFam Zheng return vm.build_image(args.image) 347ff2ebff0SFam Zheng if args.build_qemu: 348ff2ebff0SFam Zheng vm.add_source_dir(args.build_qemu) 349ff2ebff0SFam Zheng cmd = [vm.BUILD_SCRIPT.format( 350ff2ebff0SFam Zheng configure_opts = " ".join(argv), 3513ace9be6SGerd Hoffmann jobs=int(args.jobs), 3525c2ec9b6SAlex Bennée target=args.build_target, 35341e3340aSPeter Maydell verbose = "V=1" if args.verbose else "")] 354ff2ebff0SFam Zheng else: 355ff2ebff0SFam Zheng cmd = argv 356983c2a77SFam Zheng img = args.image 357983c2a77SFam Zheng if args.snapshot: 358983c2a77SFam Zheng img += ",snapshot=on" 359983c2a77SFam Zheng vm.boot(img) 360ff2ebff0SFam Zheng vm.wait_ssh() 361ff2ebff0SFam Zheng except Exception as e: 362ff2ebff0SFam Zheng if isinstance(e, SystemExit) and e.code == 0: 363ff2ebff0SFam Zheng return 0 364ff2ebff0SFam Zheng sys.stderr.write("Failed to prepare guest environment\n") 365ff2ebff0SFam Zheng traceback.print_exc() 366ff2ebff0SFam Zheng return 2 367ff2ebff0SFam Zheng 368b3f94b2fSGerd Hoffmann exitcode = 0 369ff2ebff0SFam Zheng if vm.ssh(*cmd) != 0: 370b3f94b2fSGerd Hoffmann exitcode = 3 371b3f94b2fSGerd Hoffmann if exitcode != 0 and args.interactive: 372b3f94b2fSGerd Hoffmann vm.ssh() 373b3f94b2fSGerd Hoffmann 374b3f94b2fSGerd Hoffmann if not args.snapshot: 375b3f94b2fSGerd Hoffmann vm.graceful_shutdown() 376b3f94b2fSGerd Hoffmann 377b3f94b2fSGerd Hoffmann return exitcode 378