* [KVM-AUTOTEST PATCH 01/14] KVM test: tests_base.cfg.sample: remove inline comments @ 2010-06-13 14:33 Michael Goldish 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 02/14] KVM test: tests_base.cfg.sample: style modifications Michael Goldish 0 siblings, 1 reply; 18+ messages in thread From: Michael Goldish @ 2010-06-13 14:33 UTC (permalink / raw) To: autotest, kvm Inline comments are not supported (yet?) and break the parsing of tests_base.cfg.sample. Signed-off-by: Michael Goldish <mgoldish@redhat.com> --- client/tests/kvm/tests_base.cfg.sample | 12 ++++++++---- 1 files changed, 8 insertions(+), 4 deletions(-) diff --git a/client/tests/kvm/tests_base.cfg.sample b/client/tests/kvm/tests_base.cfg.sample index ab8922b..84a903b 100644 --- a/client/tests/kvm/tests_base.cfg.sample +++ b/client/tests/kvm/tests_base.cfg.sample @@ -86,22 +86,26 @@ variants: initrd = initrd.img nic_mode = tap variants: - - cdrom: # install guest from cdrom + # Install guest from cdrom + - cdrom: medium = cdrom nic_mode = user redirs += " unattended_install" kernel = initrd = - - url: # install guest from http/ftp url + # Install guest from http/ftp url + - url: medium = url extra_params += " --append ks=floppy" url = REPLACE_THIS_WITH_TREE_URL - - nfs: # install guest from nfs nfs_server:nfs_dir + # Install guest from nfs nfs_server:nfs_dir + - nfs: medium = nfs extra_params += " --append ks=floppy" nfs_server = REPLACE_THIS_WITH_NFS_SERVER nfs_dir = REPLACE_THIS_WITH_NFS_DIRECTORY - - remote_ks: # install guest with a remote kickstart + # Install guest with a remote kickstart + - remote_ks: medium = url extra_params += " --append ks=REPLACE_THIS_WITH_URL_OF_KS" url = REPLACE_THIS_WITH_TREE_URL -- 1.5.4.1 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* [KVM-AUTOTEST PATCH 02/14] KVM test: tests_base.cfg.sample: style modifications 2010-06-13 14:33 [KVM-AUTOTEST PATCH 01/14] KVM test: tests_base.cfg.sample: remove inline comments Michael Goldish @ 2010-06-13 14:33 ` Michael Goldish 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 03/14] KVM test: kvm_utils.py: warn about exceptions raised during unpickling of env Michael Goldish 0 siblings, 1 reply; 18+ messages in thread From: Michael Goldish @ 2010-06-13 14:33 UTC (permalink / raw) To: autotest, kvm Try to exclude tests as soon as possible. (Also remove broken linux_s3 exception at the same time.) Signed-off-by: Michael Goldish <mgoldish@redhat.com> --- client/tests/kvm/tests_base.cfg.sample | 22 ++++++---------------- 1 files changed, 6 insertions(+), 16 deletions(-) diff --git a/client/tests/kvm/tests_base.cfg.sample b/client/tests/kvm/tests_base.cfg.sample index 84a903b..b302557 100644 --- a/client/tests/kvm/tests_base.cfg.sample +++ b/client/tests/kvm/tests_base.cfg.sample @@ -982,7 +982,7 @@ variants: # Windows section - @Windows: - no autotest linux_s3 vlan_tag + no autotest linux_s3 vlan_tag ioquit unattended_install.(url|nfs|remote_ks) shutdown_command = shutdown /s /f /t 0 reboot_command = shutdown /r /f /t 0 status_test_command = echo %errorlevel% @@ -1368,13 +1368,6 @@ variants: md5sum = 9fae22f2666369968a76ef59e9a81ced -linux_s3 - only Linux - -unattended_install.url|unattended_install.nfs|unattended_install.remote_ks: - only Linux - - variants: - @up: no autotest.npb autotest.tsc @@ -1414,17 +1407,20 @@ virtio|virtio_blk|e1000|balloon_check: variants: - @qcow2: image_format = qcow2 - post_command = " python scripts/check_image.py;" - remove_image = no + post_command += " python scripts/check_image.py;" post_command_timeout = 600 post_command_noncritical = yes + ioquit: + post_command_noncritical = no - vmdk: + no ioquit only Fedora Ubuntu Windows only smp2 only rtl8139 only acpi image_format = vmdk - raw: + no ioquit only Fedora Ubuntu Windows only smp2 only rtl8139 @@ -1439,12 +1435,6 @@ variants: extra_params += " -mem-path /mnt/kvm_hugepage" -ioquit: - post_command_noncritical = no - only qcow2 - only Linux - - variants: - @no_pci_assignable: pci_assignable = no -- 1.5.4.1 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* [KVM-AUTOTEST PATCH 03/14] KVM test: kvm_utils.py: warn about exceptions raised during unpickling of env 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 02/14] KVM test: tests_base.cfg.sample: style modifications Michael Goldish @ 2010-06-13 14:33 ` Michael Goldish 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 04/14] KVM test: kvm_utils.py: remove unnecessary imports Michael Goldish 0 siblings, 1 reply; 18+ messages in thread From: Michael Goldish @ 2010-06-13 14:33 UTC (permalink / raw) To: autotest, kvm Signed-off-by: Michael Goldish <mgoldish@redhat.com> --- client/tests/kvm/kvm_utils.py | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/client/tests/kvm/kvm_utils.py b/client/tests/kvm/kvm_utils.py index 0da7015..82ecb77 100644 --- a/client/tests/kvm/kvm_utils.py +++ b/client/tests/kvm/kvm_utils.py @@ -35,7 +35,8 @@ def load_env(filename, default={}): return obj # Almost any exception can be raised during unpickling, so let's catch # them all - except: + except Exception, e: + logging.warn(e) return default -- 1.5.4.1 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* [KVM-AUTOTEST PATCH 04/14] KVM test: kvm_utils.py: remove unnecessary imports 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 03/14] KVM test: kvm_utils.py: warn about exceptions raised during unpickling of env Michael Goldish @ 2010-06-13 14:33 ` Michael Goldish 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 05/14] KVM test: kvm_vm.py: make get_pid() return the pid of the VM itself Michael Goldish 0 siblings, 1 reply; 18+ messages in thread From: Michael Goldish @ 2010-06-13 14:33 UTC (permalink / raw) To: autotest, kvm Signed-off-by: Michael Goldish <mgoldish@redhat.com> --- client/tests/kvm/kvm_utils.py | 3 +-- 1 files changed, 1 insertions(+), 2 deletions(-) diff --git a/client/tests/kvm/kvm_utils.py b/client/tests/kvm/kvm_utils.py index 82ecb77..0ea5a8a 100644 --- a/client/tests/kvm/kvm_utils.py +++ b/client/tests/kvm/kvm_utils.py @@ -4,8 +4,7 @@ KVM test utility functions. @copyright: 2008-2009 Red Hat Inc. """ -import thread, subprocess, time, string, random, socket, os, signal -import select, re, logging, commands, cPickle, pty +import time, string, random, socket, os, signal, re, logging, commands, cPickle from autotest_lib.client.bin import utils from autotest_lib.client.common_lib import error, logging_config import kvm_subprocess -- 1.5.4.1 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* [KVM-AUTOTEST PATCH 05/14] KVM test: kvm_vm.py: make get_pid() return the pid of the VM itself 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 04/14] KVM test: kvm_utils.py: remove unnecessary imports Michael Goldish @ 2010-06-13 14:33 ` Michael Goldish 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 06/14] KVM test: kvm_vm.py: correct get_shared_meminfo() Michael Goldish 0 siblings, 1 reply; 18+ messages in thread From: Michael Goldish @ 2010-06-13 14:33 UTC (permalink / raw) To: autotest, kvm Currently get_pid() returns the pid of the parent shell process. Make it return the pid of the VM itself, and use vm.get_shell_pid() when the pid of the parent shell process is required. Signed-off-by: Michael Goldish <mgoldish@redhat.com> --- client/tests/kvm/kvm_vm.py | 26 +++++++++++++++++++++----- client/tests/kvm/tests/timedrift.py | 2 +- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/client/tests/kvm/kvm_vm.py b/client/tests/kvm/kvm_vm.py index 67a13d7..9d3539f 100755 --- a/client/tests/kvm/kvm_vm.py +++ b/client/tests/kvm/kvm_vm.py @@ -594,8 +594,7 @@ class VM: self.destroy() return False - logging.debug("VM appears to be alive with PID %d", - self.process.get_pid()) + logging.debug("VM appears to be alive with PID %s", self.get_pid()) return True finally: @@ -704,8 +703,7 @@ class VM: logging.debug("VM is already down") return - logging.debug("Destroying VM with PID %d..." % - self.process.get_pid()) + logging.debug("Destroying VM with PID %s...", self.get_pid()) if gracefully and self.params.get("shutdown_command"): # Try to destroy with shell command @@ -853,7 +851,25 @@ class VM: def get_pid(self): """ - Return the VM's PID. + Return the VM's PID. If the VM is dead return None. + + @note: This works under the assumption that self.process.get_pid() + returns the PID of the parent shell process. + """ + try: + children = commands.getoutput("ps --ppid=%d -o pid=" % + self.process.get_pid()).split() + return int(children[0]) + except (TypeError, IndexError, ValueError): + return None + + + def get_shell_pid(self): + """ + Return the PID of the parent shell process. + + @note: This works under the assumption that self.process.get_pid() + returns the PID of the parent shell process. """ return self.process.get_pid() diff --git a/client/tests/kvm/tests/timedrift.py b/client/tests/kvm/tests/timedrift.py index 9cb7489..bb23830 100644 --- a/client/tests/kvm/tests/timedrift.py +++ b/client/tests/kvm/tests/timedrift.py @@ -80,7 +80,7 @@ def run_timedrift(test, params, env): try: # Set the VM's CPU affinity - prev_affinity = set_cpu_affinity(vm.get_pid(), cpu_mask) + prev_affinity = set_cpu_affinity(vm.get_shell_pid(), cpu_mask) try: # Open shell sessions with the guest -- 1.5.4.1 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* [KVM-AUTOTEST PATCH 06/14] KVM test: kvm_vm.py: correct get_shared_meminfo() 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 05/14] KVM test: kvm_vm.py: make get_pid() return the pid of the VM itself Michael Goldish @ 2010-06-13 14:33 ` Michael Goldish 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 07/14] KVM test: kvm_vm.py: correct add_smp() mistake in make_qemu_command() Michael Goldish 0 siblings, 1 reply; 18+ messages in thread From: Michael Goldish @ 2010-06-13 14:33 UTC (permalink / raw) To: autotest, kvm - Use open("filename") instead of os.popen("cat filename") - Use self.get_pid() to obtain the PID - Return the result as a float Signed-off-by: Michael Goldish <mgoldish@redhat.com> --- client/tests/kvm/kvm_vm.py | 7 +++---- 1 files changed, 3 insertions(+), 4 deletions(-) diff --git a/client/tests/kvm/kvm_vm.py b/client/tests/kvm/kvm_vm.py index 9d3539f..f65d967 100755 --- a/client/tests/kvm/kvm_vm.py +++ b/client/tests/kvm/kvm_vm.py @@ -884,11 +884,10 @@ class VM: logging.error("Could not get shared memory info from dead VM.") return None - cmd = "cat /proc/%d/statm" % self.params.get('pid_' + self.name) - shm = int(os.popen(cmd).readline().split()[2]) + filename = "/proc/%d/statm" % self.get_pid() + shm = int(open(filename).read().split()[2]) # statm stores informations in pages, translate it to MB - shm = shm * 4 / 1024 - return shm + return shm * 4.0 / 1024 def remote_login(self, nic_index=0, timeout=10): -- 1.5.4.1 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* [KVM-AUTOTEST PATCH 07/14] KVM test: kvm_vm.py: correct add_smp() mistake in make_qemu_command() 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 06/14] KVM test: kvm_vm.py: correct get_shared_meminfo() Michael Goldish @ 2010-06-13 14:33 ` Michael Goldish 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 08/14] KVM test: kvm_vm.py: use shell quoting in make_qemu_command() where appropriate Michael Goldish 0 siblings, 1 reply; 18+ messages in thread From: Michael Goldish @ 2010-06-13 14:33 UTC (permalink / raw) To: autotest, kvm add_smp() is defined but not used. Signed-off-by: Michael Goldish <mgoldish@redhat.com> --- client/tests/kvm/kvm_vm.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/client/tests/kvm/kvm_vm.py b/client/tests/kvm/kvm_vm.py index f65d967..039fbff 100755 --- a/client/tests/kvm/kvm_vm.py +++ b/client/tests/kvm/kvm_vm.py @@ -350,7 +350,7 @@ class VM: smp = params.get("smp") if smp: - qemu_cmd += " -smp %s" % smp + qemu_cmd += add_smp(help, smp) iso = params.get("cdrom") if iso: -- 1.5.4.1 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* [KVM-AUTOTEST PATCH 08/14] KVM test: kvm_vm.py: use shell quoting in make_qemu_command() where appropriate 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 07/14] KVM test: kvm_vm.py: correct add_smp() mistake in make_qemu_command() Michael Goldish @ 2010-06-13 14:33 ` Michael Goldish 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 09/14] KVM test: remove reference to _screendump_thread at postprocessing Michael Goldish 0 siblings, 1 reply; 18+ messages in thread From: Michael Goldish @ 2010-06-13 14:33 UTC (permalink / raw) To: autotest, kvm Signed-off-by: Michael Goldish <mgoldish@redhat.com> --- client/tests/kvm/kvm_vm.py | 36 ++++++++++++++++++------------------ 1 files changed, 18 insertions(+), 18 deletions(-) diff --git a/client/tests/kvm/kvm_vm.py b/client/tests/kvm/kvm_vm.py index 039fbff..e40abb4 100755 --- a/client/tests/kvm/kvm_vm.py +++ b/client/tests/kvm/kvm_vm.py @@ -204,7 +204,7 @@ class VM: return " -name '%s'" % name def add_unix_socket_monitor(help, filename): - return " -monitor unix:%s,server,nowait" % filename + return " -monitor unix:'%s',server,nowait" % filename def add_mem(help, mem): return " -m %s" % mem @@ -214,18 +214,18 @@ class VM: def add_cdrom(help, filename, index=2): if has_option(help, "drive"): - return " -drive file=%s,index=%d,media=cdrom" % (filename, - index) + return " -drive file='%s',index=%d,media=cdrom" % (filename, + index) else: - return " -cdrom %s" % filename + return " -cdrom '%s'" % filename def add_drive(help, filename, format=None, cache=None, werror=None, serial=None, snapshot=False, boot=False): - cmd = " -drive file=%s" % filename + cmd = " -drive file='%s'" % filename if format: cmd += ",if=%s" % format if cache: cmd += ",cache=%s" % cache if werror: cmd += ",werror=%s" % werror - if serial: cmd += ",serial=%s" % serial + if serial: cmd += ",serial='%s'" % serial if snapshot: cmd += ",snapshot=on" if boot: cmd += ",boot=on" return cmd @@ -233,23 +233,23 @@ class VM: def add_nic(help, vlan, model=None, mac=None): cmd = " -net nic,vlan=%d" % vlan if model: cmd += ",model=%s" % model - if mac: cmd += ",macaddr=%s" % mac + if mac: cmd += ",macaddr='%s'" % mac return cmd def add_net(help, vlan, mode, ifname=None, script=None, downscript=None): cmd = " -net %s,vlan=%d" % (mode, vlan) if mode == "tap": - if ifname: cmd += ",ifname=%s" % ifname - if script: cmd += ",script=%s" % script - cmd += ",downscript=%s" % (downscript or "no") + if ifname: cmd += ",ifname='%s'" % ifname + if script: cmd += ",script='%s'" % script + cmd += ",downscript='%s'" % (downscript or "no") return cmd def add_floppy(help, filename): - return " -fda %s" % filename + return " -fda '%s'" % filename def add_tftp(help, filename): - return " -tftp %s" % filename + return " -tftp '%s'" % filename def add_tcp_redir(help, host_port, guest_port): return " -redir tcp:%s::%s" % (host_port, guest_port) @@ -267,22 +267,22 @@ class VM: return " -nographic" def add_uuid(help, uuid): - return " -uuid %s" % uuid + return " -uuid '%s'" % uuid def add_pcidevice(help, host): - return " -pcidevice host=%s" % host + return " -pcidevice host='%s'" % host def add_initrd(help, filename): - return " -initrd %s" % filename + return " -initrd '%s'" % filename def add_kernel(help, filename): - return " -kernel %s" % filename + return " -kernel '%s'" % filename def add_kernel_cmdline(help, cmdline): - return " -append %s" % cmdline + return " -append '%s'" % cmdline def add_testdev(help, filename): - return (" -chardev file,id=testlog,path=%s" + return (" -chardev file,id=testlog,path='%s'" " -device testdev,chardev=testlog" % filename) # End of command line option wrappers -- 1.5.4.1 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* [KVM-AUTOTEST PATCH 09/14] KVM test: remove reference to _screendump_thread at postprocessing 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 08/14] KVM test: kvm_vm.py: use shell quoting in make_qemu_command() where appropriate Michael Goldish @ 2010-06-13 14:33 ` Michael Goldish 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 10/14] KVM test: kvm_subprocess.py: use sys.executable instead of hardcoded 'python' Michael Goldish 0 siblings, 1 reply; 18+ messages in thread From: Michael Goldish @ 2010-06-13 14:33 UTC (permalink / raw) To: autotest, kvm _screendump_thread contains a reference to 'env' which prevents VMs from being garbage collected. This makes a difference for multi-iteration tests where several tests run consecutively in the same process. Removing the reference to _screendump_thread also removes a reference to VMs, thus allowing them to be garbage collected. This is mainly important for the new monitor classes. Signed-off-by: Michael Goldish <mgoldish@redhat.com> --- client/tests/kvm/kvm_preprocessing.py | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) diff --git a/client/tests/kvm/kvm_preprocessing.py b/client/tests/kvm/kvm_preprocessing.py index 318bf3f..76c8268 100644 --- a/client/tests/kvm/kvm_preprocessing.py +++ b/client/tests/kvm/kvm_preprocessing.py @@ -285,6 +285,8 @@ def postprocess(test, params, env): logging.debug("Terminating screendump thread...") _screendump_thread_termination_event.set() _screendump_thread.join(10) + _screendump_thread = None + _screendump_thread_termination_event = None # Warn about corrupt PPM files for f in glob.glob(os.path.join(test.debugdir, "*.ppm")): -- 1.5.4.1 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* [KVM-AUTOTEST PATCH 10/14] KVM test: kvm_subprocess.py: use sys.executable instead of hardcoded 'python' 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 09/14] KVM test: remove reference to _screendump_thread at postprocessing Michael Goldish @ 2010-06-13 14:33 ` Michael Goldish 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 11/14] KVM test: add kvm_monitor.py, an interface to QEMU monitors Michael Goldish 0 siblings, 1 reply; 18+ messages in thread From: Michael Goldish @ 2010-06-13 14:33 UTC (permalink / raw) To: autotest, kvm When running the server part of kvm_subprocess, use sys.executable instead of 'python'. 'python' might refer to a different Python version from the currently running one, e.g. if autotest is executed using 'python26' instead of the default python interpreter. Signed-off-by: Michael Goldish <mgoldish@redhat.com> --- client/tests/kvm/kvm_subprocess.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/client/tests/kvm/kvm_subprocess.py b/client/tests/kvm/kvm_subprocess.py index 9222d33..2d70146 100755 --- a/client/tests/kvm/kvm_subprocess.py +++ b/client/tests/kvm/kvm_subprocess.py @@ -337,7 +337,7 @@ class kvm_spawn: # Start the server (which runs the command) if command: - sub = subprocess.Popen("python %s" % __file__, + sub = subprocess.Popen("%s %s" % (sys.executable, __file__), shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, -- 1.5.4.1 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* [KVM-AUTOTEST PATCH 11/14] KVM test: add kvm_monitor.py, an interface to QEMU monitors 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 10/14] KVM test: kvm_subprocess.py: use sys.executable instead of hardcoded 'python' Michael Goldish @ 2010-06-13 14:33 ` Michael Goldish 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 12/14] KVM test: use new monitor interface Michael Goldish 2010-06-14 1:32 ` [Autotest] [KVM-AUTOTEST PATCH 11/14] KVM test: add kvm_monitor.py, an interface to QEMU monitors Amos Kong 0 siblings, 2 replies; 18+ messages in thread From: Michael Goldish @ 2010-06-13 14:33 UTC (permalink / raw) To: autotest, kvm This module should replace vm.send_monitor_cmd(). Instead of connecting to the monitor each time a command is issued, this module maintains a continuous connection to the monitor. It disconnects when a test terminates and reconnects as soon as the next test begins (upon unpickling). It currently contains only an interface to the human monitor. A QMP interface will be introduced in a future patch. Signed-off-by: Michael Goldish <mgoldish@redhat.com> --- client/tests/kvm/kvm_monitor.py | 356 +++++++++++++++++++++++++++++++++++++++ 1 files changed, 356 insertions(+), 0 deletions(-) create mode 100644 client/tests/kvm/kvm_monitor.py diff --git a/client/tests/kvm/kvm_monitor.py b/client/tests/kvm/kvm_monitor.py new file mode 100644 index 0000000..c5cf9c3 --- /dev/null +++ b/client/tests/kvm/kvm_monitor.py @@ -0,0 +1,356 @@ +""" +Interfaces to the QEMU monitor. + +@copyright: 2008-2010 Red Hat Inc. +""" + +import socket, time, threading, logging +import kvm_utils + + +class MonitorError(Exception): + pass + + +class MonitorConnectError(MonitorError): + pass + + +class MonitorSendError(MonitorError): + pass + + +class MonitorLockError(MonitorError): + pass + + +class MonitorProtocolError(MonitorError): + pass + + +class Monitor: + """ + Common code for monitor classes. + """ + + def __init__(self, filename): + """ + Initialize the instance. + + @param filename: Monitor socket filename. + @raise MonitorConnectError: Raised if the connection fails + """ + self.filename = filename + self.lock = threading.RLock() + self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + self.socket.setblocking(False) + + try: + self.socket.connect(filename) + except socket.error: + raise MonitorConnectError("Could not connect to monitor socket") + + + def __del__(self): + # Automatically close the connection when the instance is garbage + # collected + try: + self.socket.shutdown(socket.SHUT_RDWR) + except socket.error: + pass + self.socket.close() + + + # The following two functions are defined to make sure the state is set + # exclusively by the constructor call as specified in __getinitargs__(). + + def __getstate__(self): + pass + + + def __setstate__(self, state): + pass + + + def __getinitargs__(self): + # Save some information when pickling -- will be passed to the + # constructor upon unpickling + return self.filename, True + + + def _acquire_lock(self, timeout=20): + end_time = time.time() + timeout + while time.time() < end_time: + if self.lock.acquire(False): + return True + time.sleep(0.05) + return False + + + def _recvall(self): + s = "" + while True: + try: + data = self.socket.recv(1024) + except socket.error: + break + if not data: + break + s += data + return s + + +class HumanMonitor(Monitor): + """ + Wraps "human monitor" commands. + """ + + def __init__(self, filename, suppress_exceptions=False): + """ + Connect to the monitor socket and find the (qemu) prompt. + + @param filename: Monitor socket filename + @raise MonitorConnectError: Raised if the connection fails and + suppress_exceptions is False + @raise MonitorProtocolError: Raised if the initial (qemu) prompt isn't + found and suppress_exceptions is False + """ + try: + Monitor.__init__(self, filename) + + self.protocol = "human" + + # Find the initial (qemu) prompt + s, o = self._read_up_to_qemu_prompt(20) + if not s: + raise MonitorProtocolError("Could not find (qemu) prompt " + "after connecting to monitor. " + "Output so far: %r" % o) + + # Save the output of 'help' for future use + self.help = self._get_command_output("help") + + except MonitorError, e: + if suppress_exceptions: + logging.warn(e) + else: + raise + + + # Private methods + + def _read_up_to_qemu_prompt(self, timeout=20): + o = "" + end_time = time.time() + timeout + while time.time() < end_time: + try: + data = self.socket.recv(1024) + if not data: + break + o += data + if o.splitlines()[-1].split()[-1] == "(qemu)": + return True, "\n".join(o.splitlines()[:-1]) + except (socket.error, IndexError): + time.sleep(0.01) + return False, "\n".join(o.splitlines()) + + + def _send_command(self, command): + """ + Send a command without waiting for output. + + @param command: Command to send + @return: True if successful, False otherwise + @raise MonitorLockError: Raised if the lock cannot be acquired + @raise MonitorSendError: Raised if the command cannot be sent + """ + if not self._acquire_lock(20): + raise MonitorLockError("Could not acquire exclusive lock to send " + "monitor command '%s'" % command) + + try: + try: + self.socket.sendall(command + "\n") + except socket.error: + raise MonitorSendError("Could not send monitor command '%s'" % + command) + + finally: + self.lock.release() + + + def _get_command_output(self, command, timeout=20): + """ + Send command to the monitor. + + @param command: Command to send to the monitor + @param timeout: Time duration to wait for the (qemu) prompt to return + @return: Output received from the monitor + @raise MonitorLockError: Raised if the lock cannot be acquired + @raise MonitorSendError: Raised if the command cannot be sent + @raise MonitorProtocolError: Raised if the (qemu) prompt cannot be + found after sending the command + """ + if not self._acquire_lock(20): + raise MonitorLockError("Could not acquire exclusive lock to send " + "monitor command '%s'" % command) + + try: + # Read any data that might be available + self._recvall() + # Send command + self._send_command(command) + # Read output + s, o = self._read_up_to_qemu_prompt(timeout) + # Remove command echo from output + o = "\n".join(o.splitlines()[1:]) + # Report success/failure + if s: + return o + else: + msg = ("Could not find (qemu) prompt after command '%s'. " + "Output so far: %r" % (command, o)) + raise MonitorProtocolError(msg) + + finally: + self.lock.release() + + + # Public methods + + def is_responsive(self): + """ + Make sure the monitor is responsive by sending a command. + + @return: True if responsive, False otherwise + """ + try: + self._get_command_output("help") + return True + except MonitorError: + return False + + + # Command wrappers + # Notes: + # - All of the following commands raise exceptions in a similar manner to + # cmd() and _get_command_output(). + # - A command wrapper should use self.help if it requires information about + # the monitor's capabilities. + + def cmd(self, command, timeout=20): + """ + Send a simple command with no parameters and return its output. + Should only be used for commands that take no parameters and are + implemented under the same name for both the human and QMP monitors. + + @param command: Command to send + @param timeout: Time duration to wait for (qemu) prompt after command + @return: The output of the command + @raise MonitorLockError: Raised if the lock cannot be acquired + @raise MonitorSendError: Raised if the command cannot be sent + @raise MonitorProtocolError: Raised if the (qemu) prompt cannot be + found after sending the command + """ + return self._get_command_output(command, timeout) + + + def help(self): + """ + Send "help" and return the output. + """ + return self._get_command_output("help") + + + def quit(self): + """ + Send "quit" without waiting for output. + """ + self._send_command("quit") + + + def info(self, what): + """ + Request info about something and return the output. + """ + return self._get_command_output("info %s" % what) + + + def query(self, what): + """ + Alias for info. + """ + return self.info(what) + + + def screendump(self, filename): + """ + Request a screendump. + + @param filename: Location for the screendump + @return: The command's output + """ + return self._get_command_output("screendump %s" % filename) + + + def migrate(self, uri, full_copy=False, incremental_copy=False, wait=False): + """ + Migrate. + + @param uri: destination URI + @param full_copy: If true, migrate with full disk copy + @param incremental_copy: If true, migrate with incremental disk copy + @param wait: If true, wait for completion + @return: The command's output + """ + cmd = "migrate" + if not wait: + cmd += " -d" + if full_copy: + cmd += " -b" + if incremental_copy: + cmd += " -i" + cmd += " %s" % uri + return self._get_command_output(cmd) + + + def migrate_set_speed(self, value): + """ + Set maximum speed (in bytes/sec) for migrations. + + @param value: Speed in bytes/sec + @return: The command's output + """ + return self._get_command_output("migrate_set_speed %s" % value) + + + def sendkey(self, keystr, hold_time=1): + """ + Send key combination to VM. + + @param keystr: Key combination string + @param hold_time: Hold time in ms (should normally stay 1 ms) + @return: The command's output + """ + return self._get_command_output("sendkey %s %s" % (keystr, hold_time)) + + + def mouse_move(self, dx, dy): + """ + Move mouse. + + @param dx: X amount + @param dy: Y amount + @return: The command's output + """ + return self._get_command_output("mouse_move %d %d" % (dx, dy)) + + + def mouse_button(self, state): + """ + Set mouse button state. + + @param state: Button state (1=L, 2=M, 4=R) + @return: The command's output + """ + return self._get_command_output("mouse_button %d" % state) -- 1.5.4.1 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* [KVM-AUTOTEST PATCH 12/14] KVM test: use new monitor interface 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 11/14] KVM test: add kvm_monitor.py, an interface to QEMU monitors Michael Goldish @ 2010-06-13 14:33 ` Michael Goldish 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 13/14] KVM test: kvm_monitor.py: add QMP interface Michael Goldish 2010-06-14 1:32 ` [Autotest] [KVM-AUTOTEST PATCH 11/14] KVM test: add kvm_monitor.py, an interface to QEMU monitors Amos Kong 1 sibling, 1 reply; 18+ messages in thread From: Michael Goldish @ 2010-06-13 14:33 UTC (permalink / raw) To: autotest, kvm - Add new monitor definition syntax that allows definition of multiple monitors. Monitors are now defined like other objects in the config file: monitors = MyMonitor SomeOtherMonitor YetAnotherMonitor # defines 3 monitors monitor_type = human # default for all monitors monitor_type_SomeOtherMonitor = qmp # applies only to SomeOtherMonitor monitor_type_YetAnotherMonitor = qmp # applies only to YetAnotherMonitor main_monitor = MyMonitor # defines the main monitor to use # in the test - Use the new syntax in tests_base.cfg.sample. - Establish monitor connections using kvm_monitor in VM.create(). Store all monitors in self.monitors. Store main monitor in self.monitor. - Replace calls to send_monitor_cmd() with appropriate calls to methods of self.monitor (the main monitor). - For now, ignore the parameter screendump_verbose because currently monitor commands are always silent (when successful). Signed-off-by: Michael Goldish <mgoldish@redhat.com> --- client/tests/kvm/kvm_preprocessing.py | 33 ++-- client/tests/kvm/kvm_test_utils.py | 35 +-- client/tests/kvm/kvm_vm.py | 226 +++++++++----------- client/tests/kvm/tests/balloon_check.py | 12 +- client/tests/kvm/tests/boot_savevm.py | 41 ++-- client/tests/kvm/tests/ksm_overcommit.py | 8 +- client/tests/kvm/tests/pci_hotplug.py | 13 +- client/tests/kvm/tests/physical_resources_check.py | 40 ++-- client/tests/kvm/tests/shutdown.py | 2 +- client/tests/kvm/tests/stepmaker.py | 44 ++-- client/tests/kvm/tests/steps.py | 12 +- client/tests/kvm/tests_base.cfg.sample | 7 +- 12 files changed, 232 insertions(+), 241 deletions(-) diff --git a/client/tests/kvm/kvm_preprocessing.py b/client/tests/kvm/kvm_preprocessing.py index 76c8268..ee3e9b2 100644 --- a/client/tests/kvm/kvm_preprocessing.py +++ b/client/tests/kvm/kvm_preprocessing.py @@ -1,7 +1,7 @@ import sys, os, time, commands, re, logging, signal, glob, threading, shutil from autotest_lib.client.bin import test, utils from autotest_lib.client.common_lib import error -import kvm_vm, kvm_utils, kvm_subprocess, ppm_utils +import kvm_vm, kvm_utils, kvm_subprocess, kvm_monitor, ppm_utils try: import PIL.Image except ImportError: @@ -83,7 +83,11 @@ def preprocess_vm(test, params, env, name): raise error.TestError("Could not start VM") scrdump_filename = os.path.join(test.debugdir, "pre_%s.ppm" % name) - vm.send_monitor_cmd("screendump %s" % scrdump_filename) + try: + if vm.monitor: + vm.monitor.screendump(scrdump_filename) + except kvm_monitor.MonitorError, e: + logging.warn(e) def postprocess_image(test, params): @@ -117,7 +121,11 @@ def postprocess_vm(test, params, env, name): return scrdump_filename = os.path.join(test.debugdir, "post_%s.ppm" % name) - vm.send_monitor_cmd("screendump %s" % scrdump_filename) + try: + if vm.monitor: + vm.monitor.screendump(scrdump_filename) + except kvm_monitor.MonitorError, e: + logging.warn(e) if params.get("kill_vm") == "yes": kill_vm_timeout = float(params.get("kill_vm_timeout", 0)) @@ -356,8 +364,9 @@ def postprocess(test, params, env): for vm in kvm_utils.env_get_all_vms(env): if not vm.is_dead(): logging.info("VM '%s' is alive.", vm.name) - logging.info("The monitor unix socket of '%s' is: %s", - vm.name, vm.monitor_file_name) + for m in vm.monitors: + logging.info("'%s' has a %s monitor unix socket at: %s", + vm.name, m.protocol, m.filename) logging.info("The command line used to start '%s' was:\n%s", vm.name, vm.make_qemu_command()) raise error.JobError("Abort requested (%s)" % exc_string) @@ -403,10 +412,6 @@ def _take_screendumps(test, params, env): kvm_utils.generate_random_string(6)) delay = float(params.get("screendump_delay", 5)) quality = int(params.get("screendump_quality", 30)) - if params.get("screendump_verbose") == 'yes': - screendump_verbose = True - else: - screendump_verbose = False cache = {} @@ -414,11 +419,11 @@ def _take_screendumps(test, params, env): for vm in kvm_utils.env_get_all_vms(env): if vm.is_dead(): continue - if screendump_verbose: - vm.send_monitor_cmd("screendump %s" % temp_filename) - else: - vm.send_monitor_cmd("screendump %s" % temp_filename, - verbose=False) + try: + vm.monitor.screendump(temp_filename) + except kvm_monitor.MonitorError, e: + logging.warn(e) + continue if not os.path.exists(temp_filename): logging.warn("VM '%s' failed to produce a screendump", vm.name) continue diff --git a/client/tests/kvm/kvm_test_utils.py b/client/tests/kvm/kvm_test_utils.py index 24e2bf5..c3b6b8a 100644 --- a/client/tests/kvm/kvm_test_utils.py +++ b/client/tests/kvm/kvm_test_utils.py @@ -85,9 +85,9 @@ def reboot(vm, session, method="shell", sleep_before_reset=10, nic_index=0, # Sleep for a while before sending the command time.sleep(sleep_before_reset) # Send a system_reset monitor command - vm.send_monitor_cmd("system_reset") + vm.monitor.cmd("system_reset") logging.info("Monitor command system_reset sent. Waiting for guest to " - "go down") + "go down...") else: logging.error("Unknown reboot method: %s", method) @@ -119,21 +119,16 @@ def migrate(vm, env=None): """ # Helper functions def mig_finished(): - s, o = vm.send_monitor_cmd("info migrate") - return s == 0 and not "Migration status: active" in o + o = vm.monitor.info("migrate") + return "status: active" not in o def mig_succeeded(): - s, o = vm.send_monitor_cmd("info migrate") - return s == 0 and "Migration status: completed" in o + o = vm.monitor.info("migrate") + return "status: completed" in o def mig_failed(): - s, o = vm.send_monitor_cmd("info migrate") - return s == 0 and "Migration status: failed" in o - - # See if migration is supported - s, o = vm.send_monitor_cmd("help info") - if not "info migrate" in o: - raise error.TestError("Migration is not supported") + o = vm.monitor.info("migrate") + return "status: failed" in o # Clone the source VM and ask the clone to wait for incoming migration dest_vm = vm.clone() @@ -141,21 +136,15 @@ def migrate(vm, env=None): raise error.TestError("Could not create dest VM") try: - # Define the migration command - cmd = "migrate -d tcp:localhost:%d" % dest_vm.migration_port - logging.debug("Migrating with command: %s" % cmd) - # Migrate - s, o = vm.send_monitor_cmd(cmd) - if s: - logging.error("Migration command failed (command: %r, output: %r)" - % (cmd, o)) - raise error.TestFail("Migration command failed") + uri = "tcp:localhost:%d" % dest_vm.migration_port + logging.debug("Migrating to: %s" % uri) + o = vm.monitor.migrate(uri) # Wait for migration to finish if not kvm_utils.wait_for(mig_finished, 90, 2, 2, "Waiting for migration to finish..."): - raise error.TestFail("Timeout elapsed while waiting for migration " + raise error.TestFail("Timeout expired while waiting for migration " "to finish") # Report migration status diff --git a/client/tests/kvm/kvm_vm.py b/client/tests/kvm/kvm_vm.py index e40abb4..6aae053 100755 --- a/client/tests/kvm/kvm_vm.py +++ b/client/tests/kvm/kvm_vm.py @@ -5,8 +5,8 @@ Utility classes and functions to handle Virtual Machine creation using qemu. @copyright: 2008-2009 Red Hat Inc. """ -import time, socket, os, logging, fcntl, re, commands -import kvm_utils, kvm_subprocess +import time, socket, os, logging, fcntl, re, commands, glob +import kvm_utils, kvm_subprocess, kvm_monitor from autotest_lib.client.common_lib import error from autotest_lib.client.bin import utils @@ -109,25 +109,20 @@ class VM: self.redirs = {} self.vnc_port = 5900 self.uuid = None + self.monitors = [] + self.monitor = None + self.pci_assignable = None self.name = name self.params = params self.root_dir = root_dir self.address_cache = address_cache - self.pci_assignable = None - # Find available monitor filename + # Find a unique identifier for this VM while True: - # A unique identifier for this VM self.instance = (time.strftime("%Y%m%d-%H%M%S-") + kvm_utils.generate_random_string(4)) - # Monitor - self.monitor_file_name = "/tmp/monitor-" + self.instance - # Test log for unit tests - self.testlog_file_name = "/tmp/testlog-" + self.instance - # Verify uniqueness - if True not in map(os.path.exists, [self.monitor_file_name, - self.testlog_file_name]): + if not glob.glob("/tmp/*%s" % self.instance): break @@ -203,9 +198,12 @@ class VM: def add_name(help, name): return " -name '%s'" % name - def add_unix_socket_monitor(help, filename): + def add_human_monitor(help, filename): return " -monitor unix:'%s',server,nowait" % filename + def add_qmp_monitor(help, filename): + return " -qmp unix:'%s',server,nowait" % filename + def add_mem(help, mem): return " -m %s" % mem @@ -307,8 +305,14 @@ class VM: qemu_cmd += qemu_binary # Add the VM's name qemu_cmd += add_name(help, name) - # Add the monitor socket parameter - qemu_cmd += add_unix_socket_monitor(help, self.monitor_file_name) + # Add monitors + for monitor_name in kvm_utils.get_sub_dict_names(params, "monitors"): + monitor_params = kvm_utils.get_sub_dict(params, monitor_name) + monitor_filename = self.get_monitor_filename(monitor_name) + if monitor_params.get("monitor_type") == "qmp": + qemu_cmd += add_qmp_monitor(help, monitor_filename) + else: + qemu_cmd += add_human_monitor(help, monitor_filename) for image_name in kvm_utils.get_sub_dict_names(params, "images"): image_params = kvm_utils.get_sub_dict(params, image_name) @@ -410,7 +414,7 @@ class VM: qemu_cmd += add_uuid(help, params.get("uuid")) if params.get("testdev") == "yes": - qemu_cmd += add_testdev(help, self.testlog_file_name) + qemu_cmd += add_testdev(help, self.get_testlog_filename()) # If the PCI assignment step went OK, add each one of the PCI assigned # devices to the qemu command line. @@ -567,6 +571,7 @@ class VM: self.process = kvm_subprocess.run_bg(qemu_command, None, logging.debug, "(qemu) ") + # Make sure the process was started successfully if not self.process.is_alive(): logging.error("VM could not be created; " "qemu command failed:\n%s" % qemu_command) @@ -576,11 +581,41 @@ class VM: self.destroy() return False - if not kvm_utils.wait_for(self.is_alive, timeout, 0, 1): - logging.error("VM is not alive for some reason; " - "qemu command:\n%s" % qemu_command) - self.destroy() - return False + # Establish monitor connections + self.monitors = [] + self.monitor = None + for monitor_name in kvm_utils.get_sub_dict_names(params, "monitors"): + monitor_params = kvm_utils.get_sub_dict(params, monitor_name) + # Wait for monitor connection to succeed + end_time = time.time() + timeout + while time.time() < end_time: + try: + if monitor_params.get("monitor_type") == "qmp": + # Add a QMP monitor: not implemented yet + monitor = None + else: + # Add a "human" monitor + monitor = kvm_monitor.HumanMonitor( + self.get_monitor_filename(monitor_name)) + except kvm_monitor.MonitorError, e: + logging.warn(e) + else: + if monitor and monitor.is_responsive(): + break + time.sleep(1) + else: + logging.error("Could not connect to monitor '%s'" % + monitor_name) + self.destroy() + return False + # Add this monitor to the list + self.monitors += [monitor] + # Define the main monitor + if params.get("main_monitor") == monitor_name: + self.monitor = monitor + # If the main monitor hasn't been defined yet, use the first one + if self.monitors and not self.monitor: + self.monitor = self.monitors[0] # Get the output so far, to see if we have any problems with # hugepage setup. @@ -602,89 +637,6 @@ class VM: lockfile.close() - def send_monitor_cmd(self, command, block=True, timeout=20.0, verbose=True): - """ - Send command to the QEMU monitor. - - Connect to the VM's monitor socket and wait for the (qemu) prompt. - If block is True, read output from the socket until the (qemu) prompt - is found again, or until timeout expires. - - Return a tuple containing an integer indicating success or failure, - and the data read so far. The integer is 0 on success and 1 on failure. - A failure is any of the following cases: connection to the socket - failed, or the first (qemu) prompt could not be found, or block is - True and the second prompt could not be found. - - @param command: Command that will be sent to the monitor - @param block: Whether the output from the socket will be read until - the timeout expires - @param timeout: Timeout (seconds) before giving up on reading from - socket - """ - def read_up_to_qemu_prompt(s, timeout): - """ - Read data from socket s until the (qemu) prompt is found. - - If the prompt is found before timeout expires, return a tuple - containing True and the data read. Otherwise return a tuple - containing False and the data read so far. - - @param s: Socket object - @param timeout: Time (seconds) before giving up trying to get the - qemu prompt. - """ - o = "" - end_time = time.time() + timeout - while time.time() < end_time: - try: - o += s.recv(1024) - if o.splitlines()[-1].split()[-1] == "(qemu)": - return (True, o) - except: - time.sleep(0.01) - return (False, o) - - # In certain conditions printing this debug output might be too much - # Just print it if verbose is enabled (True by default) - if verbose: - logging.debug("Sending monitor command: %s" % command) - # Connect to monitor - try: - s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - s.setblocking(False) - s.connect(self.monitor_file_name) - except: - logging.debug("Could not connect to monitor socket") - return (1, "") - - # Send the command and get the resulting output - try: - status, data = read_up_to_qemu_prompt(s, timeout) - if not status: - logging.debug("Could not find (qemu) prompt; output so far:" + - kvm_utils.format_str_for_message(data)) - return (1, "") - # Send command - s.sendall(command + "\n") - # Receive command output - data = "" - if block: - status, data = read_up_to_qemu_prompt(s, timeout) - data = "\n".join(data.splitlines()[1:]) - if not status: - logging.debug("Could not find (qemu) prompt after command; " - "output so far:" + - kvm_utils.format_str_for_message(data)) - return (1, data) - return (0, data) - - # Clean up before exiting - finally: - s.shutdown(socket.SHUT_RDWR) - s.close() - - def destroy(self, gracefully=True): """ Destroy the VM. @@ -721,15 +673,18 @@ class VM: finally: session.close() - # Try to destroy with a monitor command - logging.debug("Trying to kill VM with monitor command...") - status, output = self.send_monitor_cmd("quit", block=False) - # Was the command sent successfully? - if status == 0: - # Wait for the VM to be really dead - if kvm_utils.wait_for(self.is_dead, 5, 0.5, 0.5): - logging.debug("VM is down") - return + if self.monitor: + # Try to destroy with a monitor command + logging.debug("Trying to kill VM with monitor command...") + try: + self.monitor.quit() + except kvm_monitor.MonitorError, e: + logging.warn(e) + else: + # Wait for the VM to be really dead + if kvm_utils.wait_for(self.is_dead, 5, 0.5, 0.5): + logging.debug("VM is down") + return # If the VM isn't dead yet... logging.debug("Cannot quit normally; sending a kill to close the " @@ -747,7 +702,10 @@ class VM: self.pci_assignable.release_devs() if self.process: self.process.close() - for f in [self.monitor_file_name, self.testlog_file_name]: + self.monitors = [] + self.monitor = None + for f in ([self.get_testlog_filename()] + + self.get_monitor_filenames()): try: os.unlink(f) except OSError: @@ -762,10 +720,7 @@ class VM: if self.is_dead(): return False # Try sending a monitor command - (status, output) = self.send_monitor_cmd("help") - if status: - return False - return True + return bool(self.monitor) and self.monitor.is_responsive() def is_dead(self): @@ -791,6 +746,29 @@ class VM: return self.params + def get_monitor_filename(self, monitor_name): + """ + Return the filename corresponding to a given monitor name. + """ + return "/tmp/monitor-%s-%s" % (monitor_name, self.instance) + + + def get_monitor_filenames(self): + """ + Return a list of all monitor filenames (as specified in the VM's + params). + """ + return [self.get_monitor_filename(m) for m in + kvm_utils.get_sub_dict_names(self.params, "monitors")] + + + def get_testlog_filename(self): + """ + Return the testlog filename. + """ + return "/tmp/testlog-%s" % self.instance + + def get_address(self, index=0): """ Return the address of a NIC of the guest, in host space. @@ -988,12 +966,12 @@ class VM: # For compatibility with versions of QEMU that do not recognize all # key names: replace keyname with the hex value from the dict, which # QEMU will definitely accept - dict = { "comma": "0x33", - "dot": "0x34", - "slash": "0x35" } - for key in dict.keys(): - keystr = keystr.replace(key, dict[key]) - self.send_monitor_cmd("sendkey %s 1" % keystr) + dict = {"comma": "0x33", + "dot": "0x34", + "slash": "0x35"} + for key, value in dict.items(): + keystr = keystr.replace(key, value) + self.monitor.sendkey(keystr) time.sleep(0.2) diff --git a/client/tests/kvm/tests/balloon_check.py b/client/tests/kvm/tests/balloon_check.py index 2d483c6..bbab95f 100644 --- a/client/tests/kvm/tests/balloon_check.py +++ b/client/tests/kvm/tests/balloon_check.py @@ -1,6 +1,6 @@ import re, string, logging, random, time from autotest_lib.client.common_lib import error -import kvm_test_utils, kvm_utils +import kvm_test_utils, kvm_utils, kvm_monitor def run_balloon_check(test, params, env): """ @@ -21,9 +21,10 @@ def run_balloon_check(test, params, env): @return: Number of failures occurred during operation. """ fail = 0 - status, output = vm.send_monitor_cmd("info balloon") - if status != 0: - logging.error("qemu monitor command failed: info balloon") + try: + vm.monitor.info("balloon") + except kvm_monitor.MonitorError, e: + logging.error(e) fail += 1 return 0 return int(re.findall("\d+", output)[0]), fail @@ -39,7 +40,8 @@ def run_balloon_check(test, params, env): """ fail = 0 logging.info("Changing VM memory to %s", new_mem) - vm.send_monitor_cmd("balloon %s" % new_mem) + # This should be replaced by proper monitor method call + vm.monitor.cmd("balloon %s" % new_mem) time.sleep(20) ballooned_mem, cfail = check_ballooned_memory() diff --git a/client/tests/kvm/tests/boot_savevm.py b/client/tests/kvm/tests/boot_savevm.py index e8ea724..3305695 100644 --- a/client/tests/kvm/tests/boot_savevm.py +++ b/client/tests/kvm/tests/boot_savevm.py @@ -1,6 +1,6 @@ import logging, time from autotest_lib.client.common_lib import error -import kvm_subprocess, kvm_test_utils, kvm_utils +import kvm_test_utils, kvm_utils, kvm_monitor def run_boot_savevm(test, params, env): """ @@ -23,23 +23,30 @@ def run_boot_savevm(test, params, env): while time.time() < end_time: time.sleep(savevm_delay) - s, o = vm.send_monitor_cmd("stop") - if s: - logging.error("stop failed: %r" % o) - s, o = vm.send_monitor_cmd("savevm 1") - if s: - logging.error("savevm failed: %r" % o) - s, o = vm.send_monitor_cmd("system_reset") - if s: - logging.error("system_reset: %r" % o) - s, o = vm.send_monitor_cmd("loadvm 1") - if s: - logging.error("loadvm failed: %r" % o) - s, o = vm.send_monitor_cmd("cont") - if s: - logging.error("cont failed: %r" % o) + try: + vm.monitor.cmd("stop") + except kvm_monitor.MonitorError, e: + logging.error(e) + try: + # This should be replaced by a proper monitor method call + vm.monitor.cmd("savevm 1") + except kvm_monitor.MonitorError, e: + logging.error(e) + try: + vm.monitor.cmd("system_reset") + except kvm_monitor.MonitorError, e: + logging.error(e) + try: + # This should be replaced by a proper monitor method call + vm.monitor.cmd("loadvm 1") + except kvm_monitor.MonitorError, e: + logging.error(e) + try: + vm.monitor.cmd("cont") + except kvm_monitor.MonitorError, e: + logging.error(e) - # Log in + # Log in if (time.time() > login_expire): login_expire = time.time() + savevm_login_delay logging.info("Logging in after loadvm...") diff --git a/client/tests/kvm/tests/ksm_overcommit.py b/client/tests/kvm/tests/ksm_overcommit.py index 2b791f1..ddf1670 100644 --- a/client/tests/kvm/tests/ksm_overcommit.py +++ b/client/tests/kvm/tests/ksm_overcommit.py @@ -173,11 +173,11 @@ def run_ksm_overcommit(test, params, env): # We need to keep some memory for python to run. if (free_mem < 64000) or (ksm_swap and free_mem < (450000 * perf_ratio)): - vm.send_monitor_cmd('stop') + vm.monitor.cmd("stop") for j in range(0, i): lvms[j].destroy(gracefully = False) time.sleep(20) - vm.send_monitor_cmd('c') + vm.monitor.cmd("c") logging.debug("Only %s free memory, killing %d guests" % (free_mem, (i-1))) last_vm = i @@ -188,12 +188,12 @@ def run_ksm_overcommit(test, params, env): logging.debug("Only %s host free memory, killing %d guests" % (free_mem, (i - 1))) logging.debug("Stopping %s", vm.name) - vm.send_monitor_cmd('stop') + vm.monitor.cmd("stop") for j in range(0, i): logging.debug("Destroying %s", lvms[j].name) lvms[j].destroy(gracefully = False) time.sleep(20) - vm.send_monitor_cmd('c') + vm.monitor.cmd("c") last_vm = i if last_vm != 0: diff --git a/client/tests/kvm/tests/pci_hotplug.py b/client/tests/kvm/tests/pci_hotplug.py index d8f34f8..f50a67e 100644 --- a/client/tests/kvm/tests/pci_hotplug.py +++ b/client/tests/kvm/tests/pci_hotplug.py @@ -27,7 +27,7 @@ def run_pci_hotplug(test, params, env): raise error.TestError("Modprobe module '%s' failed" % module) # Get output of command 'info pci' as reference - s, info_pci_ref = vm.send_monitor_cmd("info pci") + info_pci_ref = vm.monitor.info("pci") # Get output of command as reference reference = session.get_command_output(params.get("reference_cmd")) @@ -43,21 +43,22 @@ def run_pci_hotplug(test, params, env): pci_add_cmd = ("pci_add pci_addr=auto storage file=%s,if=%s" % (image_filename, tested_model)) - # Implement pci_add - s, add_output = vm.send_monitor_cmd(pci_add_cmd) + # Execute pci_add (should be replaced by a proper monitor method call) + add_output = vm.monitor.cmd(pci_add_cmd) if not "OK domain" in add_output: raise error.TestFail("Add device failed. Hypervisor command is: %s. " "Output: %r" % (pci_add_cmd, add_output)) - s, after_add = vm.send_monitor_cmd("info pci") + after_add = vm.monitor.info("pci") # Define a helper function to delete the device def pci_del(ignore_failure=False): slot_id = "0" + add_output.split(",")[2].split()[1] cmd = "pci_del pci_addr=%s" % slot_id - vm.send_monitor_cmd(cmd) + # This should be replaced by a proper monitor method call + vm.monitor.cmd(cmd) def device_removed(): - s, after_del = vm.send_monitor_cmd("info pci") + after_del = vm.monitor.info("pci") return after_del != after_add if (not kvm_utils.wait_for(device_removed, 10, 0, 1) diff --git a/client/tests/kvm/tests/physical_resources_check.py b/client/tests/kvm/tests/physical_resources_check.py index af9613e..946760f 100644 --- a/client/tests/kvm/tests/physical_resources_check.py +++ b/client/tests/kvm/tests/physical_resources_check.py @@ -1,6 +1,6 @@ import re, string, logging from autotest_lib.client.common_lib import error -import kvm_test_utils, kvm_utils +import kvm_test_utils, kvm_utils, kvm_monitor def run_physical_resources_check(test, params, env): @@ -48,13 +48,15 @@ def run_physical_resources_check(test, params, env): logging.error(" Reported by OS: %s" % actual_mem) # Define a function for checking number of hard drivers & NICs - def check_num(devices, cmd, check_str): + def check_num(devices, info_cmd, check_str): f_fail = 0 expected_num = kvm_utils.get_sub_dict_names(params, devices).__len__() - s, o = vm.send_monitor_cmd(cmd) - if s != 0: + try: + o = vm.monitor.info(info_cmd) + except kvm_monitor.MonitorError, e: f_fail += 1 - logging.error("qemu monitor command failed: %s" % cmd) + logging.error(e) + logging.error("info/query monitor command failed (%s)", info_cmd) actual_num = string.count(o, check_str) if expected_num != actual_num: @@ -65,25 +67,28 @@ def run_physical_resources_check(test, params, env): return expected_num, f_fail logging.info("Hard drive count check") - drives_num, f_fail = check_num("images", "info block", "type=hd") + drives_num, f_fail = check_num("images", "block", "type=hd") n_fail += f_fail logging.info("NIC count check") - nics_num, f_fail = check_num("nics", "info network", "model=") + nics_num, f_fail = check_num("nics", "network", "model=") n_fail += f_fail # Define a function for checking hard drives & NICs' model - def chk_fmt_model(device, fmt_model, cmd, str): + def chk_fmt_model(device, fmt_model, info_cmd, str): f_fail = 0 devices = kvm_utils.get_sub_dict_names(params, device) for chk_device in devices: expected = kvm_utils.get_sub_dict(params, chk_device).get(fmt_model) if not expected: expected = "rtl8139" - s, o = vm.send_monitor_cmd(cmd) - if s != 0: + try: + o = vm.monitor.info(info_cmd) + except kvm_monitor.MonitorError, e: f_fail += 1 - logging.error("qemu monitor command failed: %s" % cmd) + logging.error(e) + logging.error("info/query monitor command failed (%s)", + info_cmd) device_found = re.findall(str, o) logging.debug("Found devices: %s" % device_found) @@ -100,19 +105,20 @@ def run_physical_resources_check(test, params, env): return f_fail logging.info("NICs model check") - f_fail = chk_fmt_model("nics", "nic_model", "info network", "model=(.*),") + f_fail = chk_fmt_model("nics", "nic_model", "network", "model=(.*),") n_fail += f_fail logging.info("Drive format check") - f_fail = chk_fmt_model("images", "drive_format", "info block", - "(.*)\: type=hd") + f_fail = chk_fmt_model("images", "drive_format", "block", "(.*)\: type=hd") n_fail += f_fail logging.info("Network card MAC check") - s, o = vm.send_monitor_cmd("info network") - if s != 0: + try: + o = vm.monitor.info("network") + except kvm_monitor.MonitorError, e: n_fail += 1 - logging.error("qemu monitor command failed: info network") + logging.error(e) + logging.error("info/query monitor command failed (network)") found_mac_addresses = re.findall("macaddr=(.*)", o) logging.debug("Found MAC adresses: %s" % found_mac_addresses) diff --git a/client/tests/kvm/tests/shutdown.py b/client/tests/kvm/tests/shutdown.py index 8a252d9..3cbdd79 100644 --- a/client/tests/kvm/tests/shutdown.py +++ b/client/tests/kvm/tests/shutdown.py @@ -28,7 +28,7 @@ def run_shutdown(test, params, env): # Sleep for a while -- give the guest a chance to finish booting time.sleep(float(params.get("sleep_before_powerdown", 10))) # Send a system_powerdown monitor command - vm.send_monitor_cmd("system_powerdown") + vm.monitor.cmd("system_powerdown") logging.info("system_powerdown monitor command sent; waiting for " "guest to go down...") diff --git a/client/tests/kvm/tests/stepmaker.py b/client/tests/kvm/tests/stepmaker.py index 24060db..ee0ed92 100755 --- a/client/tests/kvm/tests/stepmaker.py +++ b/client/tests/kvm/tests/stepmaker.py @@ -10,7 +10,7 @@ Step file creator/editor. import pygtk, gtk, gobject, time, os, commands import common from autotest_lib.client.common_lib import error -import kvm_utils, logging, ppm_utils, stepeditor +import kvm_utils, logging, ppm_utils, stepeditor, kvm_monitor pygtk.require('2.0') @@ -84,7 +84,7 @@ class StepMaker(stepeditor.StepMakerWindow): def destroy(self, widget): - self.vm.send_monitor_cmd("cont") + self.vm.monitor.cmd("cont") self.steps_file.close() self.vars_file.close() stepeditor.StepMakerWindow.destroy(self, widget) @@ -112,7 +112,7 @@ class StepMaker(stepeditor.StepMakerWindow): # Start the screendump timer self.redirect_timer(100, self.update) # Resume the VM - self.vm.send_monitor_cmd("cont") + self.vm.monitor.cmd("cont") def switch_to_step_mode(self): @@ -127,7 +127,7 @@ class StepMaker(stepeditor.StepMakerWindow): # Start the screendump timer self.redirect_timer() # Stop the VM - self.vm.send_monitor_cmd("stop") + self.vm.monitor.cmd("stop") # Events in step mode @@ -137,10 +137,10 @@ class StepMaker(stepeditor.StepMakerWindow): if os.path.exists(self.screendump_filename): os.unlink(self.screendump_filename) - (status, output) = self.vm.send_monitor_cmd("screendump " + - self.screendump_filename) - if status: # Failure - logging.info("Could not fetch screendump") + try: + self.vm.monitor.screendump(self.screendump_filename) + except kvm_monitor.MonitorError, e: + logging.warn(e) else: self.set_image_from_file(self.screendump_filename) @@ -228,15 +228,14 @@ class StepMaker(stepeditor.StepMakerWindow): continue self.vm.send_string(val) elif words[0] == "mousemove": - self.vm.send_monitor_cmd("mouse_move %d %d" % (-8000,-8000)) + self.vm.monitor.mouse_move(-8000, -8000) time.sleep(0.5) - self.vm.send_monitor_cmd("mouse_move %s %s" % (words[1], - words[2])) + self.vm.monitor.mouse_move(words[1], words[2]) time.sleep(0.5) elif words[0] == "mouseclick": - self.vm.send_monitor_cmd("mouse_button %s" % words[1]) + self.vm.monitor.mouse_button(words[1]) time.sleep(0.1) - self.vm.send_monitor_cmd("mouse_button 0") + self.vm.monitor.mouse_button(0) # Remember the current time self.time_when_actions_completed = time.time() @@ -267,7 +266,7 @@ class StepMaker(stepeditor.StepMakerWindow): self.event_capture_button_release, self.event_capture_scroll) self.redirect_timer(10, self.update_capture) - self.vm.send_monitor_cmd("cont") + self.vm.monitor.cmd("cont") # Events in mouse capture mode @@ -280,11 +279,10 @@ class StepMaker(stepeditor.StepMakerWindow): delay = self.spin_latency.get_value() / 1000 if (x, y) != (self.prev_x, self.prev_y): - self.vm.send_monitor_cmd("mouse_move %d %d" % (-8000, -8000)) + self.vm.monitor.mouse_move(-8000, -8000) time.sleep(delay) - self.vm.send_monitor_cmd("mouse_move %d %d" % - (self.mouse_click_coords[0], - self.mouse_click_coords[1])) + self.vm.monitor.mouse_move(self.mouse_click_coords[0], + self.mouse_click_coords[1]) time.sleep(delay) self.prev_x = x @@ -293,10 +291,10 @@ class StepMaker(stepeditor.StepMakerWindow): if os.path.exists(self.screendump_filename): os.unlink(self.screendump_filename) - (status, output) = self.vm.send_monitor_cmd("screendump " + - self.screendump_filename) - if status: # Failure - logging.info("Could not fetch screendump") + try: + self.vm.monitor.screendump(self.screendump_filename) + except kvm_monitor.MonitorError, e: + logging.warn(e) else: self.set_image_from_file(self.screendump_filename) @@ -317,7 +315,7 @@ class StepMaker(stepeditor.StepMakerWindow): None, self.event_expose) self.redirect_timer() - self.vm.send_monitor_cmd("stop") + self.vm.monitor.cmd("stop") self.mouse_click_captured = True self.mouse_click_button = event.button self.set_image(self.image_width_backup, self.image_height_backup, diff --git a/client/tests/kvm/tests/steps.py b/client/tests/kvm/tests/steps.py index 8ebe7c1..6f782f5 100644 --- a/client/tests/kvm/tests/steps.py +++ b/client/tests/kvm/tests/steps.py @@ -6,7 +6,7 @@ Utilities to perform automatic guest installation using step files. import os, time, re, shutil, logging from autotest_lib.client.common_lib import utils, error -import kvm_utils, ppm_utils, kvm_subprocess +import kvm_utils, ppm_utils, kvm_subprocess, kvm_monitor try: import PIL.Image except ImportError: @@ -85,10 +85,10 @@ def barrier_2(vm, words, params, debug_dir, data_scrdump_filename, break # Request screendump - (status, output) = vm.send_monitor_cmd("screendump %s" % - scrdump_filename) - if status: - logging.error("Could not fetch screendump") + try: + vm.monitor.screendump(scrdump_filename) + except kvm_monitor.MonitorError, e: + logging.warn(e) continue # Read image file @@ -199,7 +199,7 @@ def run_steps(test, params, env): lines = sf.readlines() sf.close() - vm.send_monitor_cmd("cont") + vm.monitor.cmd("cont") current_step_num = 0 current_screendump = None diff --git a/client/tests/kvm/tests_base.cfg.sample b/client/tests/kvm/tests_base.cfg.sample index b302557..cabeb4a 100644 --- a/client/tests/kvm/tests_base.cfg.sample +++ b/client/tests/kvm/tests_base.cfg.sample @@ -4,9 +4,11 @@ vms = vm1 images = image1 nics = nic1 +monitors = humanmonitor1 -# Choose the main VM +# Choose the main VM and monitor main_vm = vm1 +main_monitor = humanmonitor1 # Some preprocessor/postprocessor params start_vm = yes @@ -34,6 +36,9 @@ image_size = 10G shell_port = 22 display = vnc +# Monitor params +monitor_type = human + # Default scheduler params used_cpus = 1 used_mem = 512 -- 1.5.4.1 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* [KVM-AUTOTEST PATCH 13/14] KVM test: kvm_monitor.py: add QMP interface 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 12/14] KVM test: use new monitor interface Michael Goldish @ 2010-06-13 14:33 ` Michael Goldish 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 14/14] KVM test: migration: support QMP Michael Goldish 2010-06-14 2:39 ` [Autotest] [KVM-AUTOTEST PATCH 13/14] KVM test: kvm_monitor.py: add QMP interface Amos Kong 0 siblings, 2 replies; 18+ messages in thread From: Michael Goldish @ 2010-06-13 14:33 UTC (permalink / raw) To: autotest, kvm An initial QMP client implementation. Should be fully functional and supports asynchronous events. However, most tests must be modified to support it, because it returns output in a different format from the human monitor (the human monitor returns strings and the QMP one returns dicts or lists). To enable QMP, set main_monitor to a monitor whose monitor_type is "qmp". For example (a single QMP monitor): monitors = monitor1 monitor_type_monitor1 = qmp main_monitor = monitor1 Another example (multiple monitors, both human and QMP): monitors = MyMonitor SomeOtherMonitor YetAnotherMonitor # defines 3 monitors monitor_type = human # default for all monitors monitor_type_SomeOtherMonitor = qmp # applies only to SomeOtherMonitor monitor_type_YetAnotherMonitor = qmp # applies only to YetAnotherMonitor main_monitor = SomeOtherMonitor # the main monitor is a QMP one, so # the test will use QMP Note: Monitor methods now raise exceptions such as MonitorLockError and QMPCmdError. If this turns out to be a bad idea, it shouldn't be hard to revert to the old convention of returning a (status, output) tuple. Signed-off-by: Michael Goldish <mgoldish@redhat.com> --- client/tests/kvm/kvm_monitor.py | 275 +++++++++++++++++++++++++++++++++++++++ client/tests/kvm/kvm_vm.py | 6 +- 2 files changed, 279 insertions(+), 2 deletions(-) diff --git a/client/tests/kvm/kvm_monitor.py b/client/tests/kvm/kvm_monitor.py index c5cf9c3..76a1a83 100644 --- a/client/tests/kvm/kvm_monitor.py +++ b/client/tests/kvm/kvm_monitor.py @@ -6,6 +6,11 @@ Interfaces to the QEMU monitor. import socket, time, threading, logging import kvm_utils +try: + import json +except ImportError: + logging.warning("Could not import json module. " + "QMP monitor functionality disabled.") class MonitorError(Exception): @@ -28,6 +33,10 @@ class MonitorProtocolError(MonitorError): pass +class QMPCmdError(MonitorError): + pass + + class Monitor: """ Common code for monitor classes. @@ -114,6 +123,8 @@ class HumanMonitor(Monitor): suppress_exceptions is False @raise MonitorProtocolError: Raised if the initial (qemu) prompt isn't found and suppress_exceptions is False + @note: Other exceptions may be raised. See _get_command_output's + docstring. """ try: Monitor.__init__(self, filename) @@ -354,3 +365,267 @@ class HumanMonitor(Monitor): @return: The command's output """ return self._get_command_output("mouse_button %d" % state) + + +class QMPMonitor(Monitor): + """ + Wraps QMP monitor commands. + """ + + def __init__(self, filename, suppress_exceptions=False): + """ + Connect to the monitor socket and issue the qmp_capabilities command + + @param filename: Monitor socket filename + @raise MonitorConnectError: Raised if the connection fails and + suppress_exceptions is False + @note: Other exceptions may be raised if the qmp_capabilities command + fails. See _get_command_output's docstring. + """ + try: + Monitor.__init__(self, filename) + + self.protocol = "qmp" + self.events = [] + + # Issue qmp_capabilities + self._get_command_output("qmp_capabilities") + + except MonitorError, e: + if suppress_exceptions: + logging.warn(e) + else: + raise + + + # Private methods + + def _build_cmd(self, cmd, args=None): + obj = {"execute": cmd} + if args: + obj["arguments"] = args + return obj + + + def _read_objects(self, timeout=5): + """ + Read lines from monitor and try to decode them. + Stop when all available lines have been successfully decoded, or when + timeout expires. If any decoded objects are asynchronous events, store + them in self.events. Return all decoded objects. + + @param timeout: Time to wait for all lines to decode successfully + @return: A list of objects + """ + s = "" + objs = [] + end_time = time.time() + timeout + while time.time() < end_time: + s += self._recvall() + for line in s.splitlines(): + if not line: + continue + try: + obj = json.loads(line) + except: + # Found an incomplete or broken line -- keep reading + break + objs += [obj] + else: + # All lines are OK -- stop reading + break + time.sleep(0.1) + # Keep track of asynchronous events + self.events += [obj for obj in objs if "event" in obj] + return objs + + + def _send_command(self, cmd, args=None): + """ + Send command without waiting for response. + + @param cmd: Command to send + @param args: A dict containing command arguments, or None + @raise MonitorLockError: Raised if the lock cannot be acquired + @raise MonitorSendError: Raised if the command cannot be sent + """ + if not self._acquire_lock(20): + raise MonitorLockError("Could not acquire exclusive lock to send " + "QMP command '%s'" % cmd) + + try: + cmdobj = self._build_cmd(cmd, args) + try: + self.socket.sendall(json.dumps(cmdobj) + "\n") + except socket.error: + raise MonitorSendError("Could not send QMP command '%s'" % cmd) + + finally: + self.lock.release() + + + def _get_command_output(self, cmd, args=None, timeout=20): + """ + Send monitor command and wait for response. + + @param cmd: Command to send + @param args: A dict containing command arguments, or None + @param timeout: Time duration to wait for response + @return: The response received + @raise MonitorLockError: Raised if the lock cannot be acquired + @raise MonitorSendError: Raised if the command cannot be sent + @raise MonitorProtocolError: Raised if no response is received + @raise QMPCmdError: Raised if the response is an error message + (the exception's args are (msg, data) where msg is a string and + data is the error data) + """ + if not self._acquire_lock(20): + raise MonitorLockError("Could not acquire exclusive lock to send " + "QMP command '%s'" % cmd) + + try: + # Read any data that might be available + self._read_objects() + # Send command + self._send_command(cmd, args) + # Read response + end_time = time.time() + timeout + while time.time() < end_time: + for obj in self._read_objects(): + if "return" in obj: + return obj["return"] + elif "error" in obj: + raise QMPCmdError("QMP command '%s' failed" % cmd, + obj["error"]) + time.sleep(0.1) + # No response found + raise MonitorProtocolError("Received no response to QMP command " + "'%s'" % cmd) + + finally: + self.lock.release() + + + # Public methods + + def is_responsive(self): + """ + Make sure the monitor is responsive by sending a command. + + @return: True if responsive, False otherwise + """ + try: + self._get_command_output("query-version") + return True + except MonitorError: + return False + + + def get_events(self): + """ + Return a list of the asynchronous events received since the last + clear_events() call. + + @return: A list of events (the objects returned have an "event" key) + @raise MonitorLockError: Raised if the lock cannot be acquired + """ + if not self._acquire_lock(20): + raise MonitorLockError("Could not acquire exclusive lock to read " + "events from monitor") + try: + self._read_objects() + return self.events[:] + finally: + self.lock.release() + + + def clear_events(self): + """ + Clear the list of asynchronous events. + + @raise MonitorLockError: Raised if the lock cannot be acquired + """ + if not self._acquire_lock(20): + raise MonitorLockError("Could not acquire exclusive lock to clear " + "event list") + self.events = [] + self.lock.release() + + + # Command wrappers + # Note: all of the following functions raise exceptions in a similar manner + # to cmd() and _get_command_output(). + + def cmd(self, command, timeout=20): + """ + Send a simple command with no parameters and return its output. + Should only be used for commands that take no parameters and are + implemented under the same name for both the human and QMP monitors. + + @param command: Command to send + @param timeout: Time duration to wait for response + @return: The response to the command + @raise MonitorLockError: Raised if the lock cannot be acquired + @raise MonitorSendError: Raised if the command cannot be sent + @raise MonitorProtocolError: Raised if no response is received + """ + return self._get_command_output(command, timeout=timeout) + + + def quit(self): + """ + Send "quit" and return the response. + """ + return self._get_command_output("quit") + + + def info(self, what): + """ + Request info about something and return the response. + """ + return self._get_command_output("query-%s" % what) + + + def query(self, what): + """ + Alias for info. + """ + return self.info(what) + + + def screendump(self, filename): + """ + Request a screendump. + + @param filename: Location for the screendump + @return: The response to the command + """ + args = {"filename": filename} + return self._get_command_output("screendump", args) + + + def migrate(self, uri, full_copy=False, incremental_copy=False, wait=False): + """ + Migrate. + + @param uri: destination URI + @param full_copy: If true, migrate with full disk copy + @param incremental_copy: If true, migrate with incremental disk copy + @param wait: If true, wait for completion + @return: The response to the command + """ + args = {"uri": uri, + "blk": full_copy, + "inc": incremental_copy} + return self._get_command_output("migrate", args) + + + def migrate_set_speed(self, value): + """ + Set maximum speed (in bytes/sec) for migrations. + + @param value: Speed in bytes/sec + @return: The response to the command + """ + args = {"value": value} + return self._get_command_output("migrate_set_speed", args) diff --git a/client/tests/kvm/kvm_vm.py b/client/tests/kvm/kvm_vm.py index 6aae053..a77cb9c 100755 --- a/client/tests/kvm/kvm_vm.py +++ b/client/tests/kvm/kvm_vm.py @@ -584,7 +584,8 @@ class VM: # Establish monitor connections self.monitors = [] self.monitor = None - for monitor_name in kvm_utils.get_sub_dict_names(params, "monitors"): + for monitor_name in kvm_utils.get_sub_dict_names(params, + "monitors"): monitor_params = kvm_utils.get_sub_dict(params, monitor_name) # Wait for monitor connection to succeed end_time = time.time() + timeout @@ -592,7 +593,8 @@ class VM: try: if monitor_params.get("monitor_type") == "qmp": # Add a QMP monitor: not implemented yet - monitor = None + monitor = kvm_monitor.QMPMonitor( + self.get_monitor_filename(monitor_name)) else: # Add a "human" monitor monitor = kvm_monitor.HumanMonitor( -- 1.5.4.1 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* [KVM-AUTOTEST PATCH 14/14] KVM test: migration: support QMP 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 13/14] KVM test: kvm_monitor.py: add QMP interface Michael Goldish @ 2010-06-13 14:33 ` Michael Goldish 2010-06-14 2:39 ` [Autotest] [KVM-AUTOTEST PATCH 13/14] KVM test: kvm_monitor.py: add QMP interface Amos Kong 1 sibling, 0 replies; 18+ messages in thread From: Michael Goldish @ 2010-06-13 14:33 UTC (permalink / raw) To: autotest, kvm If the value returned from a monitor method call is a string, treat it as human monitor output. Otherwise treat it as QMP output. Signed-off-by: Michael Goldish <mgoldish@redhat.com> --- client/tests/kvm/kvm_test_utils.py | 15 ++++++++++++--- 1 files changed, 12 insertions(+), 3 deletions(-) diff --git a/client/tests/kvm/kvm_test_utils.py b/client/tests/kvm/kvm_test_utils.py index c3b6b8a..9fdea87 100644 --- a/client/tests/kvm/kvm_test_utils.py +++ b/client/tests/kvm/kvm_test_utils.py @@ -120,15 +120,24 @@ def migrate(vm, env=None): # Helper functions def mig_finished(): o = vm.monitor.info("migrate") - return "status: active" not in o + if isinstance(o, str): + return "status: active" not in o + else: + return o.get("status") != "active" def mig_succeeded(): o = vm.monitor.info("migrate") - return "status: completed" in o + if isinstance(o, str): + return "status: completed" in o + else: + return o.get("status") == "completed" def mig_failed(): o = vm.monitor.info("migrate") - return "status: failed" in o + if isinstance(o, str): + return "status: failed" in o + else: + return o.get("status") == "failed" # Clone the source VM and ask the clone to wait for incoming migration dest_vm = vm.clone() -- 1.5.4.1 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* Re: [Autotest] [KVM-AUTOTEST PATCH 13/14] KVM test: kvm_monitor.py: add QMP interface 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 13/14] KVM test: kvm_monitor.py: add QMP interface Michael Goldish 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 14/14] KVM test: migration: support QMP Michael Goldish @ 2010-06-14 2:39 ` Amos Kong 2010-06-14 6:34 ` Michael Goldish 1 sibling, 1 reply; 18+ messages in thread From: Amos Kong @ 2010-06-14 2:39 UTC (permalink / raw) To: Michael Goldish; +Cc: autotest, kvm On Sun, Jun 13, 2010 at 05:33:44PM +0300, Michael Goldish wrote: > An initial QMP client implementation. > Should be fully functional and supports asynchronous events. > However, most tests must be modified to support it, because it returns output > in a different format from the human monitor (the human monitor returns strings > and the QMP one returns dicts or lists). > > To enable QMP, set main_monitor to a monitor whose monitor_type is "qmp". > > For example (a single QMP monitor): > > monitors = monitor1 > monitor_type_monitor1 = qmp > main_monitor = monitor1 > > Another example (multiple monitors, both human and QMP): > > monitors = MyMonitor SomeOtherMonitor YetAnotherMonitor # defines 3 monitors > monitor_type = human # default for all monitors > monitor_type_SomeOtherMonitor = qmp # applies only to SomeOtherMonitor > monitor_type_YetAnotherMonitor = qmp # applies only to YetAnotherMonitor > main_monitor = SomeOtherMonitor # the main monitor is a QMP one, so > # the test will use QMP > > Note: > Monitor methods now raise exceptions such as MonitorLockError and QMPCmdError. > If this turns out to be a bad idea, it shouldn't be hard to revert to the old > convention of returning a (status, output) tuple. > > Signed-off-by: Michael Goldish <mgoldish@redhat.com> > --- > client/tests/kvm/kvm_monitor.py | 275 +++++++++++++++++++++++++++++++++++++++ > client/tests/kvm/kvm_vm.py | 6 +- > 2 files changed, 279 insertions(+), 2 deletions(-) > > diff --git a/client/tests/kvm/kvm_monitor.py b/client/tests/kvm/kvm_monitor.py > index c5cf9c3..76a1a83 100644 > --- a/client/tests/kvm/kvm_monitor.py > +++ b/client/tests/kvm/kvm_monitor.py > @@ -6,6 +6,11 @@ Interfaces to the QEMU monitor. > > import socket, time, threading, logging > import kvm_utils > +try: > + import json > +except ImportError: > + logging.warning("Could not import json module. " > + "QMP monitor functionality disabled.") > > > class MonitorError(Exception): > @@ -28,6 +33,10 @@ class MonitorProtocolError(MonitorError): > pass > > > +class QMPCmdError(MonitorError): > + pass > + > + > class Monitor: > """ > Common code for monitor classes. > @@ -114,6 +123,8 @@ class HumanMonitor(Monitor): > suppress_exceptions is False > @raise MonitorProtocolError: Raised if the initial (qemu) prompt isn't > found and suppress_exceptions is False > + @note: Other exceptions may be raised. See _get_command_output's > + docstring. > """ > try: > Monitor.__init__(self, filename) > @@ -354,3 +365,267 @@ class HumanMonitor(Monitor): > @return: The command's output > """ > return self._get_command_output("mouse_button %d" % state) > + > + > +class QMPMonitor(Monitor): > + """ > + Wraps QMP monitor commands. > + """ > + > + def __init__(self, filename, suppress_exceptions=False): > + """ > + Connect to the monitor socket and issue the qmp_capabilities command > + > + @param filename: Monitor socket filename > + @raise MonitorConnectError: Raised if the connection fails and > + suppress_exceptions is False > + @note: Other exceptions may be raised if the qmp_capabilities command > + fails. See _get_command_output's docstring. > + """ > + try: > + Monitor.__init__(self, filename) > + > + self.protocol = "qmp" > + self.events = [] > + > + # Issue qmp_capabilities > + self._get_command_output("qmp_capabilities") > + > + except MonitorError, e: > + if suppress_exceptions: > + logging.warn(e) > + else: > + raise > + > + > + # Private methods > + > + def _build_cmd(self, cmd, args=None): > + obj = {"execute": cmd} > + if args: > + obj["arguments"] = args > + return obj > + > + > + def _read_objects(self, timeout=5): > + """ > + Read lines from monitor and try to decode them. > + Stop when all available lines have been successfully decoded, or when > + timeout expires. If any decoded objects are asynchronous events, store > + them in self.events. Return all decoded objects. > + > + @param timeout: Time to wait for all lines to decode successfully > + @return: A list of objects > + """ > + s = "" > + objs = [] > + end_time = time.time() + timeout > + while time.time() < end_time: > + s += self._recvall() > + for line in s.splitlines(): > + if not line: > + continue > + try: > + obj = json.loads(line) > + except: > + # Found an incomplete or broken line -- keep reading > + break > + objs += [obj] > + else: > + # All lines are OK -- stop reading > + break > + time.sleep(0.1) > + # Keep track of asynchronous events > + self.events += [obj for obj in objs if "event" in obj] > + return objs > + > + > + def _send_command(self, cmd, args=None): > + """ > + Send command without waiting for response. > + > + @param cmd: Command to send > + @param args: A dict containing command arguments, or None > + @raise MonitorLockError: Raised if the lock cannot be acquired > + @raise MonitorSendError: Raised if the command cannot be sent > + """ > + if not self._acquire_lock(20): > + raise MonitorLockError("Could not acquire exclusive lock to send " > + "QMP command '%s'" % cmd) > + > + try: > + cmdobj = self._build_cmd(cmd, args) > + try: > + self.socket.sendall(json.dumps(cmdobj) + "\n") > + except socket.error: > + raise MonitorSendError("Could not send QMP command '%s'" % cmd) > + > + finally: > + self.lock.release() > + > + > + def _get_command_output(self, cmd, args=None, timeout=20): > + """ > + Send monitor command and wait for response. > + > + @param cmd: Command to send > + @param args: A dict containing command arguments, or None > + @param timeout: Time duration to wait for response > + @return: The response received > + @raise MonitorLockError: Raised if the lock cannot be acquired > + @raise MonitorSendError: Raised if the command cannot be sent > + @raise MonitorProtocolError: Raised if no response is received > + @raise QMPCmdError: Raised if the response is an error message > + (the exception's args are (msg, data) where msg is a string and > + data is the error data) > + """ > + if not self._acquire_lock(20): > + raise MonitorLockError("Could not acquire exclusive lock to send " > + "QMP command '%s'" % cmd) > + > + try: > + # Read any data that might be available > + self._read_objects() > + # Send command > + self._send_command(cmd, args) > + # Read response > + end_time = time.time() + timeout > + while time.time() < end_time: > + for obj in self._read_objects(): > + if "return" in obj: > + return obj["return"] > + elif "error" in obj: > + raise QMPCmdError("QMP command '%s' failed" % cmd, > + obj["error"]) > + time.sleep(0.1) > + # No response found > + raise MonitorProtocolError("Received no response to QMP command " > + "'%s'" % cmd) > + > + finally: > + self.lock.release() > + > + > + # Public methods > + > + def is_responsive(self): > + """ > + Make sure the monitor is responsive by sending a command. > + > + @return: True if responsive, False otherwise > + """ > + try: > + self._get_command_output("query-version") > + return True > + except MonitorError: > + return False > + > + > + def get_events(self): > + """ > + Return a list of the asynchronous events received since the last > + clear_events() call. > + > + @return: A list of events (the objects returned have an "event" key) > + @raise MonitorLockError: Raised if the lock cannot be acquired > + """ > + if not self._acquire_lock(20): > + raise MonitorLockError("Could not acquire exclusive lock to read " > + "events from monitor") > + try: > + self._read_objects() > + return self.events[:] > + finally: > + self.lock.release() > + > + > + def clear_events(self): > + """ > + Clear the list of asynchronous events. > + > + @raise MonitorLockError: Raised if the lock cannot be acquired > + """ > + if not self._acquire_lock(20): > + raise MonitorLockError("Could not acquire exclusive lock to clear " > + "event list") > + self.events = [] > + self.lock.release() > + > + > + # Command wrappers > + # Note: all of the following functions raise exceptions in a similar manner > + # to cmd() and _get_command_output(). > + > + def cmd(self, command, timeout=20): > + """ > + Send a simple command with no parameters and return its output. > + Should only be used for commands that take no parameters and are > + implemented under the same name for both the human and QMP monitors. > + > + @param command: Command to send > + @param timeout: Time duration to wait for response > + @return: The response to the command > + @raise MonitorLockError: Raised if the lock cannot be acquired > + @raise MonitorSendError: Raised if the command cannot be sent > + @raise MonitorProtocolError: Raised if no response is received > + """ > + return self._get_command_output(command, timeout=timeout) > + > + > + def quit(self): > + """ > + Send "quit" and return the response. > + """ > + return self._get_command_output("quit") > + > + > + def info(self, what): > + """ > + Request info about something and return the response. > + """ > + return self._get_command_output("query-%s" % what) > + > + > + def query(self, what): > + """ > + Alias for info. > + """ > + return self.info(what) > + > + > + def screendump(self, filename): > + """ > + Request a screendump. > + > + @param filename: Location for the screendump > + @return: The response to the command > + """ > + args = {"filename": filename} > + return self._get_command_output("screendump", args) Fail to execute get a screendump. qmp hasn't support this cmd ? 09:55:14 WARNI| ("QMP command 'screendump' failed", {u'data': {u'name': u'screendump'}, u'class': u'CommandNotFound', u'desc': u'The command screendump has not been found'}) > + > + def migrate(self, uri, full_copy=False, incremental_copy=False, wait=False): > + """ > + Migrate. > + > + @param uri: destination URI > + @param full_copy: If true, migrate with full disk copy ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [Autotest] [KVM-AUTOTEST PATCH 13/14] KVM test: kvm_monitor.py: add QMP interface 2010-06-14 2:39 ` [Autotest] [KVM-AUTOTEST PATCH 13/14] KVM test: kvm_monitor.py: add QMP interface Amos Kong @ 2010-06-14 6:34 ` Michael Goldish 0 siblings, 0 replies; 18+ messages in thread From: Michael Goldish @ 2010-06-14 6:34 UTC (permalink / raw) To: Amos Kong; +Cc: autotest, kvm On 06/14/2010 05:39 AM, Amos Kong wrote: > On Sun, Jun 13, 2010 at 05:33:44PM +0300, Michael Goldish wrote: >> An initial QMP client implementation. >> Should be fully functional and supports asynchronous events. >> However, most tests must be modified to support it, because it returns output >> in a different format from the human monitor (the human monitor returns strings >> and the QMP one returns dicts or lists). >> >> To enable QMP, set main_monitor to a monitor whose monitor_type is "qmp". >> >> For example (a single QMP monitor): >> >> monitors = monitor1 >> monitor_type_monitor1 = qmp >> main_monitor = monitor1 >> >> Another example (multiple monitors, both human and QMP): >> >> monitors = MyMonitor SomeOtherMonitor YetAnotherMonitor # defines 3 monitors >> monitor_type = human # default for all monitors >> monitor_type_SomeOtherMonitor = qmp # applies only to SomeOtherMonitor >> monitor_type_YetAnotherMonitor = qmp # applies only to YetAnotherMonitor >> main_monitor = SomeOtherMonitor # the main monitor is a QMP one, so >> # the test will use QMP >> >> Note: >> Monitor methods now raise exceptions such as MonitorLockError and QMPCmdError. >> If this turns out to be a bad idea, it shouldn't be hard to revert to the old >> convention of returning a (status, output) tuple. >> >> Signed-off-by: Michael Goldish <mgoldish@redhat.com> >> --- >> client/tests/kvm/kvm_monitor.py | 275 +++++++++++++++++++++++++++++++++++++++ >> client/tests/kvm/kvm_vm.py | 6 +- >> 2 files changed, 279 insertions(+), 2 deletions(-) >> >> diff --git a/client/tests/kvm/kvm_monitor.py b/client/tests/kvm/kvm_monitor.py >> index c5cf9c3..76a1a83 100644 >> --- a/client/tests/kvm/kvm_monitor.py >> +++ b/client/tests/kvm/kvm_monitor.py >> @@ -6,6 +6,11 @@ Interfaces to the QEMU monitor. >> >> import socket, time, threading, logging >> import kvm_utils >> +try: >> + import json >> +except ImportError: >> + logging.warning("Could not import json module. " >> + "QMP monitor functionality disabled.") >> >> >> class MonitorError(Exception): >> @@ -28,6 +33,10 @@ class MonitorProtocolError(MonitorError): >> pass >> >> >> +class QMPCmdError(MonitorError): >> + pass >> + >> + >> class Monitor: >> """ >> Common code for monitor classes. >> @@ -114,6 +123,8 @@ class HumanMonitor(Monitor): >> suppress_exceptions is False >> @raise MonitorProtocolError: Raised if the initial (qemu) prompt isn't >> found and suppress_exceptions is False >> + @note: Other exceptions may be raised. See _get_command_output's >> + docstring. >> """ >> try: >> Monitor.__init__(self, filename) >> @@ -354,3 +365,267 @@ class HumanMonitor(Monitor): >> @return: The command's output >> """ >> return self._get_command_output("mouse_button %d" % state) >> + >> + >> +class QMPMonitor(Monitor): >> + """ >> + Wraps QMP monitor commands. >> + """ >> + >> + def __init__(self, filename, suppress_exceptions=False): >> + """ >> + Connect to the monitor socket and issue the qmp_capabilities command >> + >> + @param filename: Monitor socket filename >> + @raise MonitorConnectError: Raised if the connection fails and >> + suppress_exceptions is False >> + @note: Other exceptions may be raised if the qmp_capabilities command >> + fails. See _get_command_output's docstring. >> + """ >> + try: >> + Monitor.__init__(self, filename) >> + >> + self.protocol = "qmp" >> + self.events = [] >> + >> + # Issue qmp_capabilities >> + self._get_command_output("qmp_capabilities") >> + >> + except MonitorError, e: >> + if suppress_exceptions: >> + logging.warn(e) >> + else: >> + raise >> + >> + >> + # Private methods >> + >> + def _build_cmd(self, cmd, args=None): >> + obj = {"execute": cmd} >> + if args: >> + obj["arguments"] = args >> + return obj >> + >> + >> + def _read_objects(self, timeout=5): >> + """ >> + Read lines from monitor and try to decode them. >> + Stop when all available lines have been successfully decoded, or when >> + timeout expires. If any decoded objects are asynchronous events, store >> + them in self.events. Return all decoded objects. >> + >> + @param timeout: Time to wait for all lines to decode successfully >> + @return: A list of objects >> + """ >> + s = "" >> + objs = [] >> + end_time = time.time() + timeout >> + while time.time() < end_time: >> + s += self._recvall() >> + for line in s.splitlines(): >> + if not line: >> + continue >> + try: >> + obj = json.loads(line) >> + except: >> + # Found an incomplete or broken line -- keep reading >> + break >> + objs += [obj] >> + else: >> + # All lines are OK -- stop reading >> + break >> + time.sleep(0.1) >> + # Keep track of asynchronous events >> + self.events += [obj for obj in objs if "event" in obj] >> + return objs >> + >> + >> + def _send_command(self, cmd, args=None): >> + """ >> + Send command without waiting for response. >> + >> + @param cmd: Command to send >> + @param args: A dict containing command arguments, or None >> + @raise MonitorLockError: Raised if the lock cannot be acquired >> + @raise MonitorSendError: Raised if the command cannot be sent >> + """ >> + if not self._acquire_lock(20): >> + raise MonitorLockError("Could not acquire exclusive lock to send " >> + "QMP command '%s'" % cmd) >> + >> + try: >> + cmdobj = self._build_cmd(cmd, args) >> + try: >> + self.socket.sendall(json.dumps(cmdobj) + "\n") >> + except socket.error: >> + raise MonitorSendError("Could not send QMP command '%s'" % cmd) >> + >> + finally: >> + self.lock.release() >> + >> + >> + def _get_command_output(self, cmd, args=None, timeout=20): >> + """ >> + Send monitor command and wait for response. >> + >> + @param cmd: Command to send >> + @param args: A dict containing command arguments, or None >> + @param timeout: Time duration to wait for response >> + @return: The response received >> + @raise MonitorLockError: Raised if the lock cannot be acquired >> + @raise MonitorSendError: Raised if the command cannot be sent >> + @raise MonitorProtocolError: Raised if no response is received >> + @raise QMPCmdError: Raised if the response is an error message >> + (the exception's args are (msg, data) where msg is a string and >> + data is the error data) >> + """ >> + if not self._acquire_lock(20): >> + raise MonitorLockError("Could not acquire exclusive lock to send " >> + "QMP command '%s'" % cmd) >> + >> + try: >> + # Read any data that might be available >> + self._read_objects() >> + # Send command >> + self._send_command(cmd, args) >> + # Read response >> + end_time = time.time() + timeout >> + while time.time() < end_time: >> + for obj in self._read_objects(): >> + if "return" in obj: >> + return obj["return"] >> + elif "error" in obj: >> + raise QMPCmdError("QMP command '%s' failed" % cmd, >> + obj["error"]) >> + time.sleep(0.1) >> + # No response found >> + raise MonitorProtocolError("Received no response to QMP command " >> + "'%s'" % cmd) >> + >> + finally: >> + self.lock.release() >> + >> + >> + # Public methods >> + >> + def is_responsive(self): >> + """ >> + Make sure the monitor is responsive by sending a command. >> + >> + @return: True if responsive, False otherwise >> + """ >> + try: >> + self._get_command_output("query-version") >> + return True >> + except MonitorError: >> + return False >> + >> + >> + def get_events(self): >> + """ >> + Return a list of the asynchronous events received since the last >> + clear_events() call. >> + >> + @return: A list of events (the objects returned have an "event" key) >> + @raise MonitorLockError: Raised if the lock cannot be acquired >> + """ >> + if not self._acquire_lock(20): >> + raise MonitorLockError("Could not acquire exclusive lock to read " >> + "events from monitor") >> + try: >> + self._read_objects() >> + return self.events[:] >> + finally: >> + self.lock.release() >> + >> + >> + def clear_events(self): >> + """ >> + Clear the list of asynchronous events. >> + >> + @raise MonitorLockError: Raised if the lock cannot be acquired >> + """ >> + if not self._acquire_lock(20): >> + raise MonitorLockError("Could not acquire exclusive lock to clear " >> + "event list") >> + self.events = [] >> + self.lock.release() >> + >> + >> + # Command wrappers >> + # Note: all of the following functions raise exceptions in a similar manner >> + # to cmd() and _get_command_output(). >> + >> + def cmd(self, command, timeout=20): >> + """ >> + Send a simple command with no parameters and return its output. >> + Should only be used for commands that take no parameters and are >> + implemented under the same name for both the human and QMP monitors. >> + >> + @param command: Command to send >> + @param timeout: Time duration to wait for response >> + @return: The response to the command >> + @raise MonitorLockError: Raised if the lock cannot be acquired >> + @raise MonitorSendError: Raised if the command cannot be sent >> + @raise MonitorProtocolError: Raised if no response is received >> + """ >> + return self._get_command_output(command, timeout=timeout) >> + >> + >> + def quit(self): >> + """ >> + Send "quit" and return the response. >> + """ >> + return self._get_command_output("quit") >> + >> + >> + def info(self, what): >> + """ >> + Request info about something and return the response. >> + """ >> + return self._get_command_output("query-%s" % what) >> + >> + >> + def query(self, what): >> + """ >> + Alias for info. >> + """ >> + return self.info(what) >> + >> + >> + def screendump(self, filename): >> + """ >> + Request a screendump. >> + >> + @param filename: Location for the screendump >> + @return: The response to the command >> + """ >> + args = {"filename": filename} >> + return self._get_command_output("screendump", args) > > Fail to execute get a screendump. qmp hasn't support this cmd ? > > 09:55:14 WARNI| ("QMP command 'screendump' failed", {u'data': {u'name': u'screendump'}, u'class': u'CommandNotFound', u'desc': u'The command screendump has not been found'}) The latest git has screendump support. BTW, if you're interested in the error data, you can use this syntax: try: ... except QMPCmdError, (msg, error): # here msg is "QMP command ... failed" # and error is a dict with keys 'data', 'class' and 'desc' >> + >> + def migrate(self, uri, full_copy=False, incremental_copy=False, wait=False): >> + """ >> + Migrate. >> + >> + @param uri: destination URI >> + @param full_copy: If true, migrate with full disk copy > -- > To unsubscribe from this list: send the line "unsubscribe kvm" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [Autotest] [KVM-AUTOTEST PATCH 11/14] KVM test: add kvm_monitor.py, an interface to QEMU monitors 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 11/14] KVM test: add kvm_monitor.py, an interface to QEMU monitors Michael Goldish 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 12/14] KVM test: use new monitor interface Michael Goldish @ 2010-06-14 1:32 ` Amos Kong 2010-06-14 5:59 ` Michael Goldish 1 sibling, 1 reply; 18+ messages in thread From: Amos Kong @ 2010-06-14 1:32 UTC (permalink / raw) To: Michael Goldish; +Cc: autotest, kvm On Sun, Jun 13, 2010 at 05:33:42PM +0300, Michael Goldish wrote: > This module should replace vm.send_monitor_cmd(). Instead of connecting to the > monitor each time a command is issued, this module maintains a continuous > connection to the monitor. It disconnects when a test terminates and > reconnects as soon as the next test begins (upon unpickling). > > It currently contains only an interface to the human monitor. A QMP interface > will be introduced in a future patch. > > Signed-off-by: Michael Goldish <mgoldish@redhat.com> > --- > client/tests/kvm/kvm_monitor.py | 356 +++++++++++++++++++++++++++++++++++++++ > 1 files changed, 356 insertions(+), 0 deletions(-) > create mode 100644 client/tests/kvm/kvm_monitor.py > > diff --git a/client/tests/kvm/kvm_monitor.py b/client/tests/kvm/kvm_monitor.py > new file mode 100644 > index 0000000..c5cf9c3 > --- /dev/null > +++ b/client/tests/kvm/kvm_monitor.py > @@ -0,0 +1,356 @@ > +""" > +Interfaces to the QEMU monitor. > + > +@copyright: 2008-2010 Red Hat Inc. > +""" > + ... > +class HumanMonitor(Monitor): > + """ > + Wraps "human monitor" commands. > + """ > + > + def __init__(self, filename, suppress_exceptions=False): > + """ > + Connect to the monitor socket and find the (qemu) prompt. > + > + @param filename: Monitor socket filename > + @raise MonitorConnectError: Raised if the connection fails and > + suppress_exceptions is False > + @raise MonitorProtocolError: Raised if the initial (qemu) prompt isn't > + found and suppress_exceptions is False > + """ > + try: > + Monitor.__init__(self, filename) > + > + self.protocol = "human" > + > + # Find the initial (qemu) prompt > + s, o = self._read_up_to_qemu_prompt(20) > + if not s: > + raise MonitorProtocolError("Could not find (qemu) prompt " > + "after connecting to monitor. " > + "Output so far: %r" % o) > + > + # Save the output of 'help' for future use > + self.help = self._get_command_output("help") Hi Michael, Here, self.help is a string type. But you repeatedly define a sub function self.help() below. If I call vm.monitor.help() in testcase, will touch this err: TypeError: 'str' object is not callable How about using self.help_str = self._get_command_output("help")? and remove help sub function, Not only repeated name, but repeated function. Amos > + > + except MonitorError, e: > + if suppress_exceptions: > + logging.warn(e) > + else: > + raise > + ... > + > + def help(self): > + """ > + Send "help" and return the output. > + """ > + return self._get_command_output("help") ... ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [Autotest] [KVM-AUTOTEST PATCH 11/14] KVM test: add kvm_monitor.py, an interface to QEMU monitors 2010-06-14 1:32 ` [Autotest] [KVM-AUTOTEST PATCH 11/14] KVM test: add kvm_monitor.py, an interface to QEMU monitors Amos Kong @ 2010-06-14 5:59 ` Michael Goldish 0 siblings, 0 replies; 18+ messages in thread From: Michael Goldish @ 2010-06-14 5:59 UTC (permalink / raw) To: Amos Kong; +Cc: autotest, kvm On 06/14/2010 04:32 AM, Amos Kong wrote: > On Sun, Jun 13, 2010 at 05:33:42PM +0300, Michael Goldish wrote: >> This module should replace vm.send_monitor_cmd(). Instead of connecting to the >> monitor each time a command is issued, this module maintains a continuous >> connection to the monitor. It disconnects when a test terminates and >> reconnects as soon as the next test begins (upon unpickling). >> >> It currently contains only an interface to the human monitor. A QMP interface >> will be introduced in a future patch. >> >> Signed-off-by: Michael Goldish <mgoldish@redhat.com> >> --- >> client/tests/kvm/kvm_monitor.py | 356 +++++++++++++++++++++++++++++++++++++++ >> 1 files changed, 356 insertions(+), 0 deletions(-) >> create mode 100644 client/tests/kvm/kvm_monitor.py >> >> diff --git a/client/tests/kvm/kvm_monitor.py b/client/tests/kvm/kvm_monitor.py >> new file mode 100644 >> index 0000000..c5cf9c3 >> --- /dev/null >> +++ b/client/tests/kvm/kvm_monitor.py >> @@ -0,0 +1,356 @@ >> +""" >> +Interfaces to the QEMU monitor. >> + >> +@copyright: 2008-2010 Red Hat Inc. >> +""" >> + > > ... > >> +class HumanMonitor(Monitor): >> + """ >> + Wraps "human monitor" commands. >> + """ >> + >> + def __init__(self, filename, suppress_exceptions=False): >> + """ >> + Connect to the monitor socket and find the (qemu) prompt. >> + >> + @param filename: Monitor socket filename >> + @raise MonitorConnectError: Raised if the connection fails and >> + suppress_exceptions is False >> + @raise MonitorProtocolError: Raised if the initial (qemu) prompt isn't >> + found and suppress_exceptions is False >> + """ >> + try: >> + Monitor.__init__(self, filename) >> + >> + self.protocol = "human" >> + >> + # Find the initial (qemu) prompt >> + s, o = self._read_up_to_qemu_prompt(20) >> + if not s: >> + raise MonitorProtocolError("Could not find (qemu) prompt " >> + "after connecting to monitor. " >> + "Output so far: %r" % o) >> + >> + # Save the output of 'help' for future use >> + self.help = self._get_command_output("help") > > Hi Michael, > > Here, self.help is a string type. > But you repeatedly define a sub function self.help() below. > > If I call vm.monitor.help() in testcase, will touch this err: > TypeError: 'str' object is not callable Oops. I'll fix that. > How about using self.help_str = self._get_command_output("help")? > and remove help sub function, Not only repeated name, but repeated function. > > > Amos OK, it might be a good idea to remove help() altogether, especially since QMP doesn't support it AFAIK. BTW, testcases are supposed to use vm.monitor.cmd(), not vm.monitor._get_command_output(). >> + >> + except MonitorError, e: >> + if suppress_exceptions: >> + logging.warn(e) >> + else: >> + raise >> + > > ... > >> + >> + def help(self): >> + """ >> + Send "help" and return the output. >> + """ >> + return self._get_command_output("help") > > ... > -- > To unsubscribe from this list: send the line "unsubscribe kvm" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 18+ messages in thread
end of thread, other threads:[~2010-06-14 6:35 UTC | newest] Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2010-06-13 14:33 [KVM-AUTOTEST PATCH 01/14] KVM test: tests_base.cfg.sample: remove inline comments Michael Goldish 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 02/14] KVM test: tests_base.cfg.sample: style modifications Michael Goldish 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 03/14] KVM test: kvm_utils.py: warn about exceptions raised during unpickling of env Michael Goldish 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 04/14] KVM test: kvm_utils.py: remove unnecessary imports Michael Goldish 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 05/14] KVM test: kvm_vm.py: make get_pid() return the pid of the VM itself Michael Goldish 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 06/14] KVM test: kvm_vm.py: correct get_shared_meminfo() Michael Goldish 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 07/14] KVM test: kvm_vm.py: correct add_smp() mistake in make_qemu_command() Michael Goldish 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 08/14] KVM test: kvm_vm.py: use shell quoting in make_qemu_command() where appropriate Michael Goldish 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 09/14] KVM test: remove reference to _screendump_thread at postprocessing Michael Goldish 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 10/14] KVM test: kvm_subprocess.py: use sys.executable instead of hardcoded 'python' Michael Goldish 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 11/14] KVM test: add kvm_monitor.py, an interface to QEMU monitors Michael Goldish 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 12/14] KVM test: use new monitor interface Michael Goldish 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 13/14] KVM test: kvm_monitor.py: add QMP interface Michael Goldish 2010-06-13 14:33 ` [KVM-AUTOTEST PATCH 14/14] KVM test: migration: support QMP Michael Goldish 2010-06-14 2:39 ` [Autotest] [KVM-AUTOTEST PATCH 13/14] KVM test: kvm_monitor.py: add QMP interface Amos Kong 2010-06-14 6:34 ` Michael Goldish 2010-06-14 1:32 ` [Autotest] [KVM-AUTOTEST PATCH 11/14] KVM test: add kvm_monitor.py, an interface to QEMU monitors Amos Kong 2010-06-14 5:59 ` Michael Goldish
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.