* [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 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 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 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
* 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
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.