Message ID | 20190520124716.30472-10-kraxel@redhat.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | tests/vm: serial console autoinstall, misc fixes. | expand |
Hi Gerd, On 5/20/19 2:47 PM, Gerd Hoffmann wrote: > Add a bunch of helpers to talk to the guest using the > serial console. > > Also drop the hard-coded -serial parameter for the vm > so QEMUMachine.set_console() actually works. > > Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> > Tested-by: Thomas Huth <thuth@redhat.com> > --- > tests/vm/basevm.py | 85 ++++++++++++++++++++++++++++++++++++++++++++-- > 1 file changed, 82 insertions(+), 3 deletions(-) > > diff --git a/tests/vm/basevm.py b/tests/vm/basevm.py > index 465c7b80d011..17281eaf99e4 100755 > --- a/tests/vm/basevm.py > +++ b/tests/vm/basevm.py > @@ -2,10 +2,11 @@ > # > # VM testing base class > # > -# Copyright 2017 Red Hat Inc. > +# Copyright 2017-2019 Red Hat Inc. > # > # Authors: > # Fam Zheng <famz@redhat.com> > +# Gerd Hoffmann <kraxel@redhat.com> > # > # This code is licensed under the GPL version 2 or later. See > # the COPYING file in the top-level directory. > @@ -13,7 +14,9 @@ > > from __future__ import print_function > import os > +import re > import sys > +import socket > import logging > import time > import datetime > @@ -79,8 +82,7 @@ class BaseVM(object): > "-cpu", "max", > "-netdev", "user,id=vnet,hostfwd=:127.0.0.1:0-:22", > "-device", "virtio-net-pci,netdev=vnet", > - "-vnc", "127.0.0.1:0,to=20", > - "-serial", "file:%s" % os.path.join(self._tmpdir, "serial.out")] > + "-vnc", "127.0.0.1:0,to=20"] > if vcpus and vcpus > 1: > self._args += ["-smp", "%d" % vcpus] > if kvm_available(self.arch): > @@ -161,6 +163,8 @@ class BaseVM(object): > logging.debug("QEMU args: %s", " ".join(args)) > qemu_bin = os.environ.get("QEMU", "qemu-system-" + self.arch) > guest = QEMUMachine(binary=qemu_bin, args=args) > + guest.set_machine('pc') This line broke another series I'm working on, I suggest amending: -- >8 -- --- a/tests/vm/basevm.py +++ b/tests/vm/basevm.py @@ -154,7 +154,7 @@ class BaseVM(object): "-device", "virtio-blk,drive=%s,serial=%s,bootindex=1" % (name, name)] - def boot(self, img, extra_args=[]): + def boot(self, img, machine='pc', extra_args=[]): args = self._args + [ "-device", "VGA", "-drive", "file=%s,if=none,id=drive0,cache=writeback" % img, @@ -163,7 +163,7 @@ class BaseVM(object): logging.debug("QEMU args: %s", " ".join(args)) qemu_bin = os.environ.get("QEMU", "qemu-system-" + self.arch) guest = QEMUMachine(binary=qemu_bin, args=args) - guest.set_machine('pc') + guest.set_machine(machine) guest.set_console() try: guest.launch() --- Anyway I can do it in a separate patch too, so: Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> Tested-by: Philippe Mathieu-Daudé <philmd@redhat.com> > + guest.set_console() > try: > guest.launch() > except: > @@ -183,6 +187,81 @@ class BaseVM(object): > raise Exception("Cannot find ssh port from 'info usernet':\n%s" % \ > usernet_info) > > + def console_init(self, timeout = 120): > + vm = self._guest > + vm.console_socket.settimeout(timeout) > + > + def console_log(self, text): > + for line in re.split("[\r\n]", text): > + # filter out terminal escape sequences > + line = re.sub("\x1b\[[0-9;?]*[a-zA-Z]", "", line) > + line = re.sub("\x1b\([0-9;?]*[a-zA-Z]", "", line) > + # replace unprintable chars > + line = re.sub("\x1b", "<esc>", line) > + line = re.sub("[\x00-\x1f]", ".", line) > + if line == "": > + continue > + # log console line > + sys.stderr.write("con recv: %s\n" % line) > + > + def console_wait(self, expect): > + vm = self._guest > + output = "" > + while True: > + try: > + chars = vm.console_socket.recv(1024) > + except socket.timeout: > + sys.stderr.write("console: *** read timeout ***\n") > + sys.stderr.write("console: waiting for: '%s'\n" % expect) > + sys.stderr.write("console: line buffer:\n") > + sys.stderr.write("\n") > + self.console_log(output.rstrip()) > + sys.stderr.write("\n") > + raise > + output += chars.decode("latin1") > + if expect in output: > + break > + if "\r" in output or "\n" in output: > + lines = re.split("[\r\n]", output) > + output = lines.pop() > + if self.debug: > + self.console_log("\n".join(lines)) > + if self.debug: > + self.console_log(output) > + > + def console_send(self, command): > + vm = self._guest > + if self.debug: > + logline = re.sub("\n", "<enter>", command) > + logline = re.sub("[\x00-\x1f]", ".", logline) > + sys.stderr.write("con send: %s\n" % logline) > + for char in list(command): > + vm.console_socket.send(char.encode("utf-8")) > + time.sleep(0.01) > + > + def console_wait_send(self, wait, command): > + self.console_wait(wait) > + self.console_send(command) > + > + def console_ssh_init(self, prompt, user, pw): > + sshkey_cmd = "echo '%s' > .ssh/authorized_keys\n" % SSH_PUB_KEY.rstrip() > + self.console_wait_send("login:", "%s\n" % user) > + self.console_wait_send("Password:", "%s\n" % pw) > + self.console_wait_send(prompt, "mkdir .ssh\n") > + self.console_wait_send(prompt, sshkey_cmd) > + self.console_wait_send(prompt, "chmod 755 .ssh\n") > + self.console_wait_send(prompt, "chmod 644 .ssh/authorized_keys\n") > + > + def console_sshd_config(self, prompt): > + self.console_wait(prompt) > + self.console_send("echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config\n") > + for var in self.envvars: > + self.console_wait(prompt) > + self.console_send("echo 'AcceptEnv %s' >> /etc/ssh/sshd_config\n" % var) > + > + def print_step(self, text): > + sys.stderr.write("### %s ...\n" % text) > + > def wait_ssh(self, seconds=300): > starttime = datetime.datetime.now() > endtime = starttime + datetime.timedelta(seconds=seconds) >
diff --git a/tests/vm/basevm.py b/tests/vm/basevm.py index 465c7b80d011..17281eaf99e4 100755 --- a/tests/vm/basevm.py +++ b/tests/vm/basevm.py @@ -2,10 +2,11 @@ # # VM testing base class # -# Copyright 2017 Red Hat Inc. +# Copyright 2017-2019 Red Hat Inc. # # Authors: # Fam Zheng <famz@redhat.com> +# Gerd Hoffmann <kraxel@redhat.com> # # This code is licensed under the GPL version 2 or later. See # the COPYING file in the top-level directory. @@ -13,7 +14,9 @@ from __future__ import print_function import os +import re import sys +import socket import logging import time import datetime @@ -79,8 +82,7 @@ class BaseVM(object): "-cpu", "max", "-netdev", "user,id=vnet,hostfwd=:127.0.0.1:0-:22", "-device", "virtio-net-pci,netdev=vnet", - "-vnc", "127.0.0.1:0,to=20", - "-serial", "file:%s" % os.path.join(self._tmpdir, "serial.out")] + "-vnc", "127.0.0.1:0,to=20"] if vcpus and vcpus > 1: self._args += ["-smp", "%d" % vcpus] if kvm_available(self.arch): @@ -161,6 +163,8 @@ class BaseVM(object): logging.debug("QEMU args: %s", " ".join(args)) qemu_bin = os.environ.get("QEMU", "qemu-system-" + self.arch) guest = QEMUMachine(binary=qemu_bin, args=args) + guest.set_machine('pc') + guest.set_console() try: guest.launch() except: @@ -183,6 +187,81 @@ class BaseVM(object): raise Exception("Cannot find ssh port from 'info usernet':\n%s" % \ usernet_info) + def console_init(self, timeout = 120): + vm = self._guest + vm.console_socket.settimeout(timeout) + + def console_log(self, text): + for line in re.split("[\r\n]", text): + # filter out terminal escape sequences + line = re.sub("\x1b\[[0-9;?]*[a-zA-Z]", "", line) + line = re.sub("\x1b\([0-9;?]*[a-zA-Z]", "", line) + # replace unprintable chars + line = re.sub("\x1b", "<esc>", line) + line = re.sub("[\x00-\x1f]", ".", line) + if line == "": + continue + # log console line + sys.stderr.write("con recv: %s\n" % line) + + def console_wait(self, expect): + vm = self._guest + output = "" + while True: + try: + chars = vm.console_socket.recv(1024) + except socket.timeout: + sys.stderr.write("console: *** read timeout ***\n") + sys.stderr.write("console: waiting for: '%s'\n" % expect) + sys.stderr.write("console: line buffer:\n") + sys.stderr.write("\n") + self.console_log(output.rstrip()) + sys.stderr.write("\n") + raise + output += chars.decode("latin1") + if expect in output: + break + if "\r" in output or "\n" in output: + lines = re.split("[\r\n]", output) + output = lines.pop() + if self.debug: + self.console_log("\n".join(lines)) + if self.debug: + self.console_log(output) + + def console_send(self, command): + vm = self._guest + if self.debug: + logline = re.sub("\n", "<enter>", command) + logline = re.sub("[\x00-\x1f]", ".", logline) + sys.stderr.write("con send: %s\n" % logline) + for char in list(command): + vm.console_socket.send(char.encode("utf-8")) + time.sleep(0.01) + + def console_wait_send(self, wait, command): + self.console_wait(wait) + self.console_send(command) + + def console_ssh_init(self, prompt, user, pw): + sshkey_cmd = "echo '%s' > .ssh/authorized_keys\n" % SSH_PUB_KEY.rstrip() + self.console_wait_send("login:", "%s\n" % user) + self.console_wait_send("Password:", "%s\n" % pw) + self.console_wait_send(prompt, "mkdir .ssh\n") + self.console_wait_send(prompt, sshkey_cmd) + self.console_wait_send(prompt, "chmod 755 .ssh\n") + self.console_wait_send(prompt, "chmod 644 .ssh/authorized_keys\n") + + def console_sshd_config(self, prompt): + self.console_wait(prompt) + self.console_send("echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config\n") + for var in self.envvars: + self.console_wait(prompt) + self.console_send("echo 'AcceptEnv %s' >> /etc/ssh/sshd_config\n" % var) + + def print_step(self, text): + sys.stderr.write("### %s ...\n" % text) + def wait_ssh(self, seconds=300): starttime = datetime.datetime.now() endtime = starttime + datetime.timedelta(seconds=seconds)