1ff2ebff0SFam Zheng# 2ff2ebff0SFam Zheng# VM testing base class 3ff2ebff0SFam Zheng# 48dd38334SGerd Hoffmann# Copyright 2017-2019 Red Hat Inc. 5ff2ebff0SFam Zheng# 6ff2ebff0SFam Zheng# Authors: 7ff2ebff0SFam Zheng# Fam Zheng <famz@redhat.com> 88dd38334SGerd Hoffmann# Gerd Hoffmann <kraxel@redhat.com> 9ff2ebff0SFam Zheng# 10ff2ebff0SFam Zheng# This code is licensed under the GPL version 2 or later. See 11ff2ebff0SFam Zheng# the COPYING file in the top-level directory. 12ff2ebff0SFam Zheng# 13ff2ebff0SFam Zheng 14ff2ebff0SFam Zhengimport os 158dd38334SGerd Hoffmannimport re 16ff2ebff0SFam Zhengimport sys 178dd38334SGerd Hoffmannimport socket 18ff2ebff0SFam Zhengimport logging 19ff2ebff0SFam Zhengimport time 20ff2ebff0SFam Zhengimport datetime 218f8fd9edSCleber Rosasys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) 228b272e00SWainer dos Santos Moschettafrom qemu.accel import kvm_available 23abf0bf99SJohn Snowfrom qemu.machine import QEMUMachine 24ff2ebff0SFam Zhengimport subprocess 25ff2ebff0SFam Zhengimport hashlib 26ff2ebff0SFam Zhengimport optparse 27ff2ebff0SFam Zhengimport atexit 28ff2ebff0SFam Zhengimport tempfile 29ff2ebff0SFam Zhengimport shutil 30ff2ebff0SFam Zhengimport multiprocessing 31ff2ebff0SFam Zhengimport traceback 32ff2ebff0SFam Zheng 33ff2ebff0SFam ZhengSSH_KEY = open(os.path.join(os.path.dirname(__file__), 34ff2ebff0SFam Zheng "..", "keys", "id_rsa")).read() 35ff2ebff0SFam ZhengSSH_PUB_KEY = open(os.path.join(os.path.dirname(__file__), 36ff2ebff0SFam Zheng "..", "keys", "id_rsa.pub")).read() 37ff2ebff0SFam Zheng 38ff2ebff0SFam Zhengclass BaseVM(object): 39ff2ebff0SFam Zheng GUEST_USER = "qemu" 40ff2ebff0SFam Zheng GUEST_PASS = "qemupass" 41ff2ebff0SFam Zheng ROOT_PASS = "qemupass" 42ff2ebff0SFam Zheng 43b08ba163SGerd Hoffmann envvars = [ 44b08ba163SGerd Hoffmann "https_proxy", 45b08ba163SGerd Hoffmann "http_proxy", 46b08ba163SGerd Hoffmann "ftp_proxy", 47b08ba163SGerd Hoffmann "no_proxy", 48b08ba163SGerd Hoffmann ] 49b08ba163SGerd Hoffmann 50ff2ebff0SFam Zheng # The script to run in the guest that builds QEMU 51ff2ebff0SFam Zheng BUILD_SCRIPT = "" 52ff2ebff0SFam Zheng # The guest name, to be overridden by subclasses 53ff2ebff0SFam Zheng name = "#base" 5431719c37SPhilippe Mathieu-Daudé # The guest architecture, to be overridden by subclasses 5531719c37SPhilippe Mathieu-Daudé arch = "#arch" 56b3f94b2fSGerd Hoffmann # command to halt the guest, can be overridden by subclasses 57b3f94b2fSGerd Hoffmann poweroff = "poweroff" 585b790481SEduardo Habkost # enable IPv6 networking 595b790481SEduardo Habkost ipv6 = True 60c9de3935SRobert Foley # Scale up some timeouts under TCG. 61c9de3935SRobert Foley # 4 is arbitrary, but greater than 2, 62c9de3935SRobert Foley # since we found we need to wait more than twice as long. 63c9de3935SRobert Foley tcg_ssh_timeout_multiplier = 4 64ff2ebff0SFam Zheng def __init__(self, debug=False, vcpus=None): 65ff2ebff0SFam Zheng self._guest = None 66ff2ebff0SFam Zheng self._tmpdir = os.path.realpath(tempfile.mkdtemp(prefix="vm-test-", 67ff2ebff0SFam Zheng suffix=".tmp", 68ff2ebff0SFam Zheng dir=".")) 69ff2ebff0SFam Zheng atexit.register(shutil.rmtree, self._tmpdir) 70ff2ebff0SFam Zheng 71ff2ebff0SFam Zheng self._ssh_key_file = os.path.join(self._tmpdir, "id_rsa") 72ff2ebff0SFam Zheng open(self._ssh_key_file, "w").write(SSH_KEY) 73ff2ebff0SFam Zheng subprocess.check_call(["chmod", "600", self._ssh_key_file]) 74ff2ebff0SFam Zheng 75ff2ebff0SFam Zheng self._ssh_pub_key_file = os.path.join(self._tmpdir, "id_rsa.pub") 76ff2ebff0SFam Zheng open(self._ssh_pub_key_file, "w").write(SSH_PUB_KEY) 77ff2ebff0SFam Zheng 78ff2ebff0SFam Zheng self.debug = debug 79ff2ebff0SFam Zheng self._stderr = sys.stderr 80ff2ebff0SFam Zheng self._devnull = open(os.devnull, "w") 81ff2ebff0SFam Zheng if self.debug: 82ff2ebff0SFam Zheng self._stdout = sys.stdout 83ff2ebff0SFam Zheng else: 84ff2ebff0SFam Zheng self._stdout = self._devnull 85ff2ebff0SFam Zheng self._args = [ \ 86eb2712f5SPeter Maydell "-nodefaults", "-m", "4G", 87b33bd859SPeter Maydell "-cpu", "max", 885b790481SEduardo Habkost "-netdev", "user,id=vnet,hostfwd=:127.0.0.1:0-:22" + 895b790481SEduardo Habkost (",ipv6=no" if not self.ipv6 else ""), 90ff2ebff0SFam Zheng "-device", "virtio-net-pci,netdev=vnet", 918dd38334SGerd Hoffmann "-vnc", "127.0.0.1:0,to=20"] 92071cf5a4SPhilippe Mathieu-Daudé if vcpus and vcpus > 1: 933ace9be6SGerd Hoffmann self._args += ["-smp", "%d" % vcpus] 9471531bb5SPhilippe Mathieu-Daudé if kvm_available(self.arch): 95ff2ebff0SFam Zheng self._args += ["-enable-kvm"] 96ff2ebff0SFam Zheng else: 97ff2ebff0SFam Zheng logging.info("KVM not available, not using -enable-kvm") 98ff2ebff0SFam Zheng self._data_args = [] 99ff2ebff0SFam Zheng 1005b4b4865SAlex Bennée def _download_with_cache(self, url, sha256sum=None, sha512sum=None): 101ff2ebff0SFam Zheng def check_sha256sum(fname): 102ff2ebff0SFam Zheng if not sha256sum: 103ff2ebff0SFam Zheng return True 104ff2ebff0SFam Zheng checksum = subprocess.check_output(["sha256sum", fname]).split()[0] 1053ace9be6SGerd Hoffmann return sha256sum == checksum.decode("utf-8") 106ff2ebff0SFam Zheng 1075b4b4865SAlex Bennée def check_sha512sum(fname): 1085b4b4865SAlex Bennée if not sha512sum: 1095b4b4865SAlex Bennée return True 1105b4b4865SAlex Bennée checksum = subprocess.check_output(["sha512sum", fname]).split()[0] 1115b4b4865SAlex Bennée return sha512sum == checksum.decode("utf-8") 1125b4b4865SAlex Bennée 113ff2ebff0SFam Zheng cache_dir = os.path.expanduser("~/.cache/qemu-vm/download") 114ff2ebff0SFam Zheng if not os.path.exists(cache_dir): 115ff2ebff0SFam Zheng os.makedirs(cache_dir) 1163ace9be6SGerd Hoffmann fname = os.path.join(cache_dir, 1173ace9be6SGerd Hoffmann hashlib.sha1(url.encode("utf-8")).hexdigest()) 1185b4b4865SAlex Bennée if os.path.exists(fname) and check_sha256sum(fname) and check_sha512sum(fname): 119ff2ebff0SFam Zheng return fname 120ff2ebff0SFam Zheng logging.debug("Downloading %s to %s...", url, fname) 121ff2ebff0SFam Zheng subprocess.check_call(["wget", "-c", url, "-O", fname + ".download"], 122ff2ebff0SFam Zheng stdout=self._stdout, stderr=self._stderr) 123ff2ebff0SFam Zheng os.rename(fname + ".download", fname) 124ff2ebff0SFam Zheng return fname 125ff2ebff0SFam Zheng 126796471e9SGerd Hoffmann def _ssh_do(self, user, cmd, check): 12789adc5b9SRobert Foley ssh_cmd = ["ssh", 12889adc5b9SRobert Foley "-t", 129ff2ebff0SFam Zheng "-o", "StrictHostKeyChecking=no", 130ff2ebff0SFam Zheng "-o", "UserKnownHostsFile=" + os.devnull, 131ff2ebff0SFam Zheng "-o", "ConnectTimeout=1", 132ff2ebff0SFam Zheng "-p", self.ssh_port, "-i", self._ssh_key_file] 13389adc5b9SRobert Foley # If not in debug mode, set ssh to quiet mode to 13489adc5b9SRobert Foley # avoid printing the results of commands. 13589adc5b9SRobert Foley if not self.debug: 13689adc5b9SRobert Foley ssh_cmd.append("-q") 137b08ba163SGerd Hoffmann for var in self.envvars: 138b08ba163SGerd Hoffmann ssh_cmd += ['-o', "SendEnv=%s" % var ] 139ff2ebff0SFam Zheng assert not isinstance(cmd, str) 140ff2ebff0SFam Zheng ssh_cmd += ["%s@127.0.0.1" % user] + list(cmd) 141ff2ebff0SFam Zheng logging.debug("ssh_cmd: %s", " ".join(ssh_cmd)) 142726c9a3bSFam Zheng r = subprocess.call(ssh_cmd) 143ff2ebff0SFam Zheng if check and r != 0: 144ff2ebff0SFam Zheng raise Exception("SSH command failed: %s" % cmd) 145ff2ebff0SFam Zheng return r 146ff2ebff0SFam Zheng 147ff2ebff0SFam Zheng def ssh(self, *cmd): 148ff2ebff0SFam Zheng return self._ssh_do(self.GUEST_USER, cmd, False) 149ff2ebff0SFam Zheng 150ff2ebff0SFam Zheng def ssh_root(self, *cmd): 151ff2ebff0SFam Zheng return self._ssh_do("root", cmd, False) 152ff2ebff0SFam Zheng 153ff2ebff0SFam Zheng def ssh_check(self, *cmd): 154ff2ebff0SFam Zheng self._ssh_do(self.GUEST_USER, cmd, True) 155ff2ebff0SFam Zheng 156ff2ebff0SFam Zheng def ssh_root_check(self, *cmd): 157ff2ebff0SFam Zheng self._ssh_do("root", cmd, True) 158ff2ebff0SFam Zheng 159ff2ebff0SFam Zheng def build_image(self, img): 160ff2ebff0SFam Zheng raise NotImplementedError 161ff2ebff0SFam Zheng 1621e48931cSWainer dos Santos Moschetta def exec_qemu_img(self, *args): 1631e48931cSWainer dos Santos Moschetta cmd = [os.environ.get("QEMU_IMG", "qemu-img")] 1641e48931cSWainer dos Santos Moschetta cmd.extend(list(args)) 1651e48931cSWainer dos Santos Moschetta subprocess.check_call(cmd) 1661e48931cSWainer dos Santos Moschetta 167ff2ebff0SFam Zheng def add_source_dir(self, src_dir): 1683ace9be6SGerd Hoffmann name = "data-" + hashlib.sha1(src_dir.encode("utf-8")).hexdigest()[:5] 169ff2ebff0SFam Zheng tarfile = os.path.join(self._tmpdir, name + ".tar") 170ff2ebff0SFam Zheng logging.debug("Creating archive %s for src_dir dir: %s", tarfile, src_dir) 171ff2ebff0SFam Zheng subprocess.check_call(["./scripts/archive-source.sh", tarfile], 172ff2ebff0SFam Zheng cwd=src_dir, stdin=self._devnull, 173ff2ebff0SFam Zheng stdout=self._stdout, stderr=self._stderr) 174ff2ebff0SFam Zheng self._data_args += ["-drive", 175ff2ebff0SFam Zheng "file=%s,if=none,id=%s,cache=writeback,format=raw" % \ 176ff2ebff0SFam Zheng (tarfile, name), 177ff2ebff0SFam Zheng "-device", 178ff2ebff0SFam Zheng "virtio-blk,drive=%s,serial=%s,bootindex=1" % (name, name)] 179ff2ebff0SFam Zheng 180ff2ebff0SFam Zheng def boot(self, img, extra_args=[]): 181ff2ebff0SFam Zheng args = self._args + [ 182ff2ebff0SFam Zheng "-device", "VGA", 183ff2ebff0SFam Zheng "-drive", "file=%s,if=none,id=drive0,cache=writeback" % img, 184ff2ebff0SFam Zheng "-device", "virtio-blk,drive=drive0,bootindex=0"] 185ff2ebff0SFam Zheng args += self._data_args + extra_args 186ff2ebff0SFam Zheng logging.debug("QEMU args: %s", " ".join(args)) 18731719c37SPhilippe Mathieu-Daudé qemu_bin = os.environ.get("QEMU", "qemu-system-" + self.arch) 188ff2ebff0SFam Zheng guest = QEMUMachine(binary=qemu_bin, args=args) 1898dd38334SGerd Hoffmann guest.set_machine('pc') 1908dd38334SGerd Hoffmann guest.set_console() 191ff2ebff0SFam Zheng try: 192ff2ebff0SFam Zheng guest.launch() 193ff2ebff0SFam Zheng except: 194ff2ebff0SFam Zheng logging.error("Failed to launch QEMU, command line:") 195ff2ebff0SFam Zheng logging.error(" ".join([qemu_bin] + args)) 196ff2ebff0SFam Zheng logging.error("Log:") 197ff2ebff0SFam Zheng logging.error(guest.get_log()) 198ff2ebff0SFam Zheng logging.error("QEMU version >= 2.10 is required") 199ff2ebff0SFam Zheng raise 200ff2ebff0SFam Zheng atexit.register(self.shutdown) 201ff2ebff0SFam Zheng self._guest = guest 202ff2ebff0SFam Zheng usernet_info = guest.qmp("human-monitor-command", 203ff2ebff0SFam Zheng command_line="info usernet") 204ff2ebff0SFam Zheng self.ssh_port = None 205ff2ebff0SFam Zheng for l in usernet_info["return"].splitlines(): 206ff2ebff0SFam Zheng fields = l.split() 207ff2ebff0SFam Zheng if "TCP[HOST_FORWARD]" in fields and "22" in fields: 208ff2ebff0SFam Zheng self.ssh_port = l.split()[3] 209ff2ebff0SFam Zheng if not self.ssh_port: 210ff2ebff0SFam Zheng raise Exception("Cannot find ssh port from 'info usernet':\n%s" % \ 211ff2ebff0SFam Zheng usernet_info) 212ff2ebff0SFam Zheng 2138dd38334SGerd Hoffmann def console_init(self, timeout = 120): 2148dd38334SGerd Hoffmann vm = self._guest 2158dd38334SGerd Hoffmann vm.console_socket.settimeout(timeout) 2168dd38334SGerd Hoffmann 2178dd38334SGerd Hoffmann def console_log(self, text): 2188dd38334SGerd Hoffmann for line in re.split("[\r\n]", text): 2198dd38334SGerd Hoffmann # filter out terminal escape sequences 2208dd38334SGerd Hoffmann line = re.sub("\x1b\[[0-9;?]*[a-zA-Z]", "", line) 2218dd38334SGerd Hoffmann line = re.sub("\x1b\([0-9;?]*[a-zA-Z]", "", line) 2228dd38334SGerd Hoffmann # replace unprintable chars 2238dd38334SGerd Hoffmann line = re.sub("\x1b", "<esc>", line) 2248dd38334SGerd Hoffmann line = re.sub("[\x00-\x1f]", ".", line) 2258dd38334SGerd Hoffmann line = re.sub("[\x80-\xff]", ".", line) 2268dd38334SGerd Hoffmann if line == "": 2278dd38334SGerd Hoffmann continue 2288dd38334SGerd Hoffmann # log console line 2298dd38334SGerd Hoffmann sys.stderr.write("con recv: %s\n" % line) 2308dd38334SGerd Hoffmann 23160136e06SGerd Hoffmann def console_wait(self, expect, expectalt = None): 2328dd38334SGerd Hoffmann vm = self._guest 2338dd38334SGerd Hoffmann output = "" 2348dd38334SGerd Hoffmann while True: 2358dd38334SGerd Hoffmann try: 2368dd38334SGerd Hoffmann chars = vm.console_socket.recv(1) 2378dd38334SGerd Hoffmann except socket.timeout: 2388dd38334SGerd Hoffmann sys.stderr.write("console: *** read timeout ***\n") 2398dd38334SGerd Hoffmann sys.stderr.write("console: waiting for: '%s'\n" % expect) 24060136e06SGerd Hoffmann if not expectalt is None: 24160136e06SGerd Hoffmann sys.stderr.write("console: waiting for: '%s' (alt)\n" % expectalt) 2428dd38334SGerd Hoffmann sys.stderr.write("console: line buffer:\n") 2438dd38334SGerd Hoffmann sys.stderr.write("\n") 2448dd38334SGerd Hoffmann self.console_log(output.rstrip()) 2458dd38334SGerd Hoffmann sys.stderr.write("\n") 2468dd38334SGerd Hoffmann raise 2478dd38334SGerd Hoffmann output += chars.decode("latin1") 2488dd38334SGerd Hoffmann if expect in output: 2498dd38334SGerd Hoffmann break 25060136e06SGerd Hoffmann if not expectalt is None and expectalt in output: 25160136e06SGerd Hoffmann break 2528dd38334SGerd Hoffmann if "\r" in output or "\n" in output: 2538dd38334SGerd Hoffmann lines = re.split("[\r\n]", output) 2548dd38334SGerd Hoffmann output = lines.pop() 2558dd38334SGerd Hoffmann if self.debug: 2568dd38334SGerd Hoffmann self.console_log("\n".join(lines)) 2578dd38334SGerd Hoffmann if self.debug: 2588dd38334SGerd Hoffmann self.console_log(output) 25960136e06SGerd Hoffmann if not expectalt is None and expectalt in output: 26060136e06SGerd Hoffmann return False 26160136e06SGerd Hoffmann return True 2628dd38334SGerd Hoffmann 2636c4f0416SGerd Hoffmann def console_consume(self): 2646c4f0416SGerd Hoffmann vm = self._guest 2656c4f0416SGerd Hoffmann output = "" 2666c4f0416SGerd Hoffmann vm.console_socket.setblocking(0) 2676c4f0416SGerd Hoffmann while True: 2686c4f0416SGerd Hoffmann try: 2696c4f0416SGerd Hoffmann chars = vm.console_socket.recv(1) 2706c4f0416SGerd Hoffmann except: 2716c4f0416SGerd Hoffmann break 2726c4f0416SGerd Hoffmann output += chars.decode("latin1") 2736c4f0416SGerd Hoffmann if "\r" in output or "\n" in output: 2746c4f0416SGerd Hoffmann lines = re.split("[\r\n]", output) 2756c4f0416SGerd Hoffmann output = lines.pop() 2766c4f0416SGerd Hoffmann if self.debug: 2776c4f0416SGerd Hoffmann self.console_log("\n".join(lines)) 2786c4f0416SGerd Hoffmann if self.debug: 2796c4f0416SGerd Hoffmann self.console_log(output) 2806c4f0416SGerd Hoffmann vm.console_socket.setblocking(1) 2816c4f0416SGerd Hoffmann 2828dd38334SGerd Hoffmann def console_send(self, command): 2838dd38334SGerd Hoffmann vm = self._guest 2848dd38334SGerd Hoffmann if self.debug: 2858dd38334SGerd Hoffmann logline = re.sub("\n", "<enter>", command) 2868dd38334SGerd Hoffmann logline = re.sub("[\x00-\x1f]", ".", logline) 2878dd38334SGerd Hoffmann sys.stderr.write("con send: %s\n" % logline) 2888dd38334SGerd Hoffmann for char in list(command): 2898dd38334SGerd Hoffmann vm.console_socket.send(char.encode("utf-8")) 2908dd38334SGerd Hoffmann time.sleep(0.01) 2918dd38334SGerd Hoffmann 2928dd38334SGerd Hoffmann def console_wait_send(self, wait, command): 2938dd38334SGerd Hoffmann self.console_wait(wait) 2948dd38334SGerd Hoffmann self.console_send(command) 2958dd38334SGerd Hoffmann 2968dd38334SGerd Hoffmann def console_ssh_init(self, prompt, user, pw): 2978dd38334SGerd Hoffmann sshkey_cmd = "echo '%s' > .ssh/authorized_keys\n" % SSH_PUB_KEY.rstrip() 2988dd38334SGerd Hoffmann self.console_wait_send("login:", "%s\n" % user) 2998dd38334SGerd Hoffmann self.console_wait_send("Password:", "%s\n" % pw) 3008dd38334SGerd Hoffmann self.console_wait_send(prompt, "mkdir .ssh\n") 3018dd38334SGerd Hoffmann self.console_wait_send(prompt, sshkey_cmd) 3028dd38334SGerd Hoffmann self.console_wait_send(prompt, "chmod 755 .ssh\n") 3038dd38334SGerd Hoffmann self.console_wait_send(prompt, "chmod 644 .ssh/authorized_keys\n") 3048dd38334SGerd Hoffmann 3058dd38334SGerd Hoffmann def console_sshd_config(self, prompt): 3068dd38334SGerd Hoffmann self.console_wait(prompt) 3078dd38334SGerd Hoffmann self.console_send("echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config\n") 3088dd38334SGerd Hoffmann for var in self.envvars: 3098dd38334SGerd Hoffmann self.console_wait(prompt) 3108dd38334SGerd Hoffmann self.console_send("echo 'AcceptEnv %s' >> /etc/ssh/sshd_config\n" % var) 3118dd38334SGerd Hoffmann 3128dd38334SGerd Hoffmann def print_step(self, text): 3138dd38334SGerd Hoffmann sys.stderr.write("### %s ...\n" % text) 3148dd38334SGerd Hoffmann 315fbb3aa29SRobert Foley def wait_ssh(self, wait_root=False, seconds=300): 316c9de3935SRobert Foley # Allow more time for VM to boot under TCG. 317c9de3935SRobert Foley if not kvm_available(self.arch): 318c9de3935SRobert Foley seconds *= self.tcg_ssh_timeout_multiplier 319ff2ebff0SFam Zheng starttime = datetime.datetime.now() 320f5d3d218SPhilippe Mathieu-Daudé endtime = starttime + datetime.timedelta(seconds=seconds) 321ff2ebff0SFam Zheng guest_up = False 322f5d3d218SPhilippe Mathieu-Daudé while datetime.datetime.now() < endtime: 323fbb3aa29SRobert Foley if wait_root and self.ssh_root("exit 0") == 0: 324fbb3aa29SRobert Foley guest_up = True 325fbb3aa29SRobert Foley break 326fbb3aa29SRobert Foley elif self.ssh("exit 0") == 0: 327ff2ebff0SFam Zheng guest_up = True 328ff2ebff0SFam Zheng break 329f5d3d218SPhilippe Mathieu-Daudé seconds = (endtime - datetime.datetime.now()).total_seconds() 330f5d3d218SPhilippe Mathieu-Daudé logging.debug("%ds before timeout", seconds) 331ff2ebff0SFam Zheng time.sleep(1) 332ff2ebff0SFam Zheng if not guest_up: 333ff2ebff0SFam Zheng raise Exception("Timeout while waiting for guest ssh") 334ff2ebff0SFam Zheng 335ff2ebff0SFam Zheng def shutdown(self): 336ff2ebff0SFam Zheng self._guest.shutdown() 337ff2ebff0SFam Zheng 338ff2ebff0SFam Zheng def wait(self): 339ff2ebff0SFam Zheng self._guest.wait() 340ff2ebff0SFam Zheng 341b3f94b2fSGerd Hoffmann def graceful_shutdown(self): 342b3f94b2fSGerd Hoffmann self.ssh_root(self.poweroff) 343b3f94b2fSGerd Hoffmann self._guest.wait() 344b3f94b2fSGerd Hoffmann 345ff2ebff0SFam Zheng def qmp(self, *args, **kwargs): 346ff2ebff0SFam Zheng return self._guest.qmp(*args, **kwargs) 347ff2ebff0SFam Zheng 348*b081986cSRobert Foley def gen_cloud_init_iso(self): 349*b081986cSRobert Foley cidir = self._tmpdir 350*b081986cSRobert Foley mdata = open(os.path.join(cidir, "meta-data"), "w") 351*b081986cSRobert Foley name = self.name.replace(".","-") 352*b081986cSRobert Foley mdata.writelines(["instance-id: {}-vm-0\n".format(name), 353*b081986cSRobert Foley "local-hostname: {}-guest\n".format(name)]) 354*b081986cSRobert Foley mdata.close() 355*b081986cSRobert Foley udata = open(os.path.join(cidir, "user-data"), "w") 356*b081986cSRobert Foley print("guest user:pw {}:{}".format(self._config['guest_user'], 357*b081986cSRobert Foley self._config['guest_pass'])) 358*b081986cSRobert Foley udata.writelines(["#cloud-config\n", 359*b081986cSRobert Foley "chpasswd:\n", 360*b081986cSRobert Foley " list: |\n", 361*b081986cSRobert Foley " root:%s\n" % self._config['root_pass'], 362*b081986cSRobert Foley " %s:%s\n" % (self._config['guest_user'], 363*b081986cSRobert Foley self._config['guest_pass']), 364*b081986cSRobert Foley " expire: False\n", 365*b081986cSRobert Foley "users:\n", 366*b081986cSRobert Foley " - name: %s\n" % self._config['guest_user'], 367*b081986cSRobert Foley " sudo: ALL=(ALL) NOPASSWD:ALL\n", 368*b081986cSRobert Foley " ssh-authorized-keys:\n", 369*b081986cSRobert Foley " - %s\n" % self._config['ssh_pub_key'], 370*b081986cSRobert Foley " - name: root\n", 371*b081986cSRobert Foley " ssh-authorized-keys:\n", 372*b081986cSRobert Foley " - %s\n" % self._config['ssh_pub_key'], 373*b081986cSRobert Foley "locale: en_US.UTF-8\n"]) 374*b081986cSRobert Foley proxy = os.environ.get("http_proxy") 375*b081986cSRobert Foley if not proxy is None: 376*b081986cSRobert Foley udata.writelines(["apt:\n", 377*b081986cSRobert Foley " proxy: %s" % proxy]) 378*b081986cSRobert Foley udata.close() 379*b081986cSRobert Foley subprocess.check_call(["genisoimage", "-output", "cloud-init.iso", 380*b081986cSRobert Foley "-volid", "cidata", "-joliet", "-rock", 381*b081986cSRobert Foley "user-data", "meta-data"], 382*b081986cSRobert Foley cwd=cidir, 383*b081986cSRobert Foley stdin=self._devnull, stdout=self._stdout, 384*b081986cSRobert Foley stderr=self._stdout) 385*b081986cSRobert Foley 386*b081986cSRobert Foley return os.path.join(cidir, "cloud-init.iso") 387*b081986cSRobert Foley 38863a24c5eSPhilippe Mathieu-Daudédef parse_args(vmcls): 3898a6e007eSPhilippe Mathieu-Daudé 3908a6e007eSPhilippe Mathieu-Daudé def get_default_jobs(): 39163a24c5eSPhilippe Mathieu-Daudé if kvm_available(vmcls.arch): 3923ad3e36eSWainer dos Santos Moschetta return multiprocessing.cpu_count() // 2 3938a6e007eSPhilippe Mathieu-Daudé else: 3948a6e007eSPhilippe Mathieu-Daudé return 1 3958a6e007eSPhilippe Mathieu-Daudé 396ff2ebff0SFam Zheng parser = optparse.OptionParser( 397ff2ebff0SFam Zheng description="VM test utility. Exit codes: " 398ff2ebff0SFam Zheng "0 = success, " 399ff2ebff0SFam Zheng "1 = command line error, " 400ff2ebff0SFam Zheng "2 = environment initialization failed, " 401ff2ebff0SFam Zheng "3 = test command failed") 402ff2ebff0SFam Zheng parser.add_option("--debug", "-D", action="store_true", 403ff2ebff0SFam Zheng help="enable debug output") 40463a24c5eSPhilippe Mathieu-Daudé parser.add_option("--image", "-i", default="%s.img" % vmcls.name, 405ff2ebff0SFam Zheng help="image file name") 406ff2ebff0SFam Zheng parser.add_option("--force", "-f", action="store_true", 407ff2ebff0SFam Zheng help="force build image even if image exists") 4088a6e007eSPhilippe Mathieu-Daudé parser.add_option("--jobs", type=int, default=get_default_jobs(), 409ff2ebff0SFam Zheng help="number of virtual CPUs") 41041e3340aSPeter Maydell parser.add_option("--verbose", "-V", action="store_true", 41141e3340aSPeter Maydell help="Pass V=1 to builds within the guest") 412ff2ebff0SFam Zheng parser.add_option("--build-image", "-b", action="store_true", 413ff2ebff0SFam Zheng help="build image") 414ff2ebff0SFam Zheng parser.add_option("--build-qemu", 415ff2ebff0SFam Zheng help="build QEMU from source in guest") 4165c2ec9b6SAlex Bennée parser.add_option("--build-target", 4175c2ec9b6SAlex Bennée help="QEMU build target", default="check") 418ff2ebff0SFam Zheng parser.add_option("--interactive", "-I", action="store_true", 419ff2ebff0SFam Zheng help="Interactively run command") 420983c2a77SFam Zheng parser.add_option("--snapshot", "-s", action="store_true", 421983c2a77SFam Zheng help="run tests with a snapshot") 422ff2ebff0SFam Zheng parser.disable_interspersed_args() 423ff2ebff0SFam Zheng return parser.parse_args() 424ff2ebff0SFam Zheng 425ff2ebff0SFam Zhengdef main(vmcls): 426ff2ebff0SFam Zheng try: 42763a24c5eSPhilippe Mathieu-Daudé args, argv = parse_args(vmcls) 428ff2ebff0SFam Zheng if not argv and not args.build_qemu and not args.build_image: 429f03868bdSEduardo Habkost print("Nothing to do?") 430ff2ebff0SFam Zheng return 1 431fb3b4e6dSEduardo Habkost logging.basicConfig(level=(logging.DEBUG if args.debug 432fb3b4e6dSEduardo Habkost else logging.WARN)) 433ff2ebff0SFam Zheng vm = vmcls(debug=args.debug, vcpus=args.jobs) 434ff2ebff0SFam Zheng if args.build_image: 435ff2ebff0SFam Zheng if os.path.exists(args.image) and not args.force: 436ff2ebff0SFam Zheng sys.stderr.writelines(["Image file exists: %s\n" % args.image, 437ff2ebff0SFam Zheng "Use --force option to overwrite\n"]) 438ff2ebff0SFam Zheng return 1 439ff2ebff0SFam Zheng return vm.build_image(args.image) 440ff2ebff0SFam Zheng if args.build_qemu: 441ff2ebff0SFam Zheng vm.add_source_dir(args.build_qemu) 442ff2ebff0SFam Zheng cmd = [vm.BUILD_SCRIPT.format( 443ff2ebff0SFam Zheng configure_opts = " ".join(argv), 4443ace9be6SGerd Hoffmann jobs=int(args.jobs), 4455c2ec9b6SAlex Bennée target=args.build_target, 44641e3340aSPeter Maydell verbose = "V=1" if args.verbose else "")] 447ff2ebff0SFam Zheng else: 448ff2ebff0SFam Zheng cmd = argv 449983c2a77SFam Zheng img = args.image 450983c2a77SFam Zheng if args.snapshot: 451983c2a77SFam Zheng img += ",snapshot=on" 452983c2a77SFam Zheng vm.boot(img) 453ff2ebff0SFam Zheng vm.wait_ssh() 454ff2ebff0SFam Zheng except Exception as e: 455ff2ebff0SFam Zheng if isinstance(e, SystemExit) and e.code == 0: 456ff2ebff0SFam Zheng return 0 457ff2ebff0SFam Zheng sys.stderr.write("Failed to prepare guest environment\n") 458ff2ebff0SFam Zheng traceback.print_exc() 459ff2ebff0SFam Zheng return 2 460ff2ebff0SFam Zheng 461b3f94b2fSGerd Hoffmann exitcode = 0 462ff2ebff0SFam Zheng if vm.ssh(*cmd) != 0: 463b3f94b2fSGerd Hoffmann exitcode = 3 464bcc388dfSAlex Bennée if args.interactive: 465b3f94b2fSGerd Hoffmann vm.ssh() 466b3f94b2fSGerd Hoffmann 467b3f94b2fSGerd Hoffmann if not args.snapshot: 468b3f94b2fSGerd Hoffmann vm.graceful_shutdown() 469b3f94b2fSGerd Hoffmann 470b3f94b2fSGerd Hoffmann return exitcode 471