[PATCH 1/4] [kvm-autotest] client.tests.kvm.tests.cgroup: add_*_drive / rm_drive; bugfixies

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



* functions for adding and removal of drive to vm using host-file or
  host-scsi_debug device.
* move get_children_pids() into base_utils.py
* client.virt.kvm_monitor: add 'debug=True' parameter into monitor.info() cmd
* client.common_lib.base_utils: add etraceback() function for formating
  tracebacks

Signed-off-by: Lukas Doktor <ldoktor@xxxxxxxxxx>
---
 client/common_lib/base_utils.py  |   24 ++++-
 client/tests/kvm/tests/cgroup.py |  262 +++++++++++++++++++++++++-------------
 client/virt/kvm_monitor.py       |    5 +-
 3 files changed, 200 insertions(+), 91 deletions(-)

diff --git a/client/common_lib/base_utils.py b/client/common_lib/base_utils.py
index b191911..55d3314 100644
--- a/client/common_lib/base_utils.py
+++ b/client/common_lib/base_utils.py
@@ -2,7 +2,7 @@
 # Copyright 2008 Google Inc. Released under the GPL v2
 
 import os, pickle, random, re, resource, select, shutil, signal, StringIO
-import socket, struct, subprocess, sys, time, textwrap, urlparse
+import socket, struct, subprocess, sys, time, textwrap, traceback, urlparse
 import warnings, smtplib, logging, urllib2
 from threading import Thread, Event
 try:
@@ -827,6 +827,15 @@ def _wait_for_commands(bg_jobs, start_time, timeout):
     return True
 
 
+def get_children_pids(ppid):
+    """
+    Get all PIDs of children/threads of parent ppid
+    param ppid: parent PID
+    return: list of PIDs of all children/threads of ppid
+    """
+    return (system_output("ps -L --ppid=%d -o lwp" % ppid).split('\n')[1:])
+
+
 def pid_is_alive(pid):
     """
     True if process pid exists and is not yet stuck in Zombie state.
@@ -962,6 +971,19 @@ def system_output_parallel(commands, timeout=None, ignore_status=False,
     return out
 
 
+def etraceback(prep, exc_info):
+    """
+    Enhanced Traceback formats traceback into lines "prep: line\nname: line"
+    @param prep: desired line preposition
+    @param exc_info: sys.exc_info of the exception
+    @return: string which contains beautifully formatted exception
+    """
+    for line in traceback.format_exception(exc_info[0], exc_info[1],
+                                           exc_info[2]):
+        out += "%s: %s" % (prep, line)
+    return out
+
+
 def strip_unicode(input):
     if type(input) == list:
         return [strip_unicode(i) for i in input]
diff --git a/client/tests/kvm/tests/cgroup.py b/client/tests/kvm/tests/cgroup.py
index 197f364..1fa080f 100644
--- a/client/tests/kvm/tests/cgroup.py
+++ b/client/tests/kvm/tests/cgroup.py
@@ -3,7 +3,7 @@ cgroup autotest test (on KVM guest)
 @author: Lukas Doktor <ldoktor@xxxxxxxxxx>
 @copyright: 2011 Red Hat, Inc.
 """
-import logging, re, sys, tempfile, time, traceback
+import logging, re, sys, tempfile, time
 from autotest_lib.client.common_lib import error
 from autotest_lib.client.bin import utils
 from client.virt import virt_vm
@@ -17,6 +17,135 @@ def run_cgroup(test, params, env):
     vms = None
     tests = None
 
+
+    # Func
+    def _check_vms(vms):
+        """
+        Checks if the VM is alive.
+        @param vms: list of vm's
+        """
+        err = ""
+        for i in range(len(vms)):
+            try:
+                vms[i].verify_alive()
+                vms[i].verify_kernel_crash()
+                vms[i].wait_for_login(timeout=30).close()
+            except virt_vm.VMDeadKernelCrashError, failure_detail:
+                logging.error("_check_vms: %s", failure_detail)
+                logging.warn("recreate VM(%s)", i)
+                # The vm has to be recreated to reset the qemu PCI state
+                vms[i].create()
+                err += "%s, " % vms[i].name
+        if err:
+            raise error.TestFail("WM [%s] had to be recreated" % err[:-2])
+
+
+    def get_device_driver():
+        """
+        Discovers the used block device driver {ide, scsi, virtio_blk}
+        @return: Used block device driver {ide, scsi, virtio}
+        """
+        return params.get('drive_format', 'virtio')
+
+
+    def add_file_drive(vm, driver=get_device_driver(), host_file=None):
+        """
+        Hot-add a drive based on file to a vm
+        @param vm: Desired VM
+        @param driver: which driver should be used (default: same as in test)
+        @param host_file: Which file on host is the image (default: create new)
+        @return: Tupple(ret_file, device)
+                    ret_file: created file handler (None if not created)
+                    device: PCI id of the virtual disk
+        """
+        if not host_file:
+            host_file = tempfile.NamedTemporaryFile(prefix="cgroup-disk-",
+                                               suffix=".iso")
+            utils.system("dd if=/dev/zero of=%s bs=1M count=8 &>/dev/null"
+                         % (host_file.name))
+            ret_file = host_file
+        else:
+            ret_file = None
+
+        out = vm.monitor.cmd("pci_add auto storage file=%s,if=%s,snapshot=off,"
+                             "cache=off" % (host_file.name, driver))
+        dev = re.search(r'OK domain (\d+), bus (\d+), slot (\d+), function \d+',
+                        out)
+        if not dev:
+            raise error.TestFail("Can't add device(%s, %s, %s): %s" % (vm.name,
+                                                host_file.name, driver, out))
+        device = "%02x:%02x" % (int(dev.group(2)), int(dev.group(3)))
+        time.sleep(3)
+        out = vm.monitor.info('qtree', debug=False)
+        if out.count('addr %s.0' % device) != 1:
+            raise error.TestFail("Can't add device(%s, %s, %s): device in qtree"
+                            ":\n%s" % (vm.name, host_file.name, driver, out))
+        return (ret_file, device)
+
+
+    def add_scsi_drive(vm, driver=get_device_driver(), host_file=None):
+        """
+        Hot-add a drive based on scsi_debug device to a vm
+        @param vm: Desired VM
+        @param driver: which driver should be used (default: same as in test)
+        @param host_file: Which dev on host is the image (default: create new)
+        @return: Tupple(ret_file, device)
+                    ret_file: string of the created dev (None if not created)
+                    device: PCI id of the virtual disk
+        """
+        if not host_file:
+            if utils.system_output("lsmod | grep scsi_debug -c") == 0:
+                utils.system("modprobe scsi_debug dev_size_mb=8 add_host=0")
+            utils.system("echo 1 > /sys/bus/pseudo/drivers/scsi_debug/add_host")
+            host_file = utils.system_output("ls /dev/sd* | tail -n 1")
+            # Enable idling in scsi_debug drive
+            utils.system("echo 1 > /sys/block/%s/queue/rotational" % host_file)
+            ret_file = host_file
+        else:
+            # Don't remove this device during cleanup
+            # Reenable idling in scsi_debug drive (in case it's not)
+            utils.system("echo 1 > /sys/block/%s/queue/rotational" % host_file)
+            ret_file = None
+
+        out = vm.monitor.cmd("pci_add auto storage file=%s,if=%s,snapshot=off,"
+                             "cache=off" % (host_file, driver))
+        dev = re.search(r'OK domain (\d+), bus (\d+), slot (\d+), function \d+',
+                        out)
+        if not dev:
+            raise error.TestFail("Can't add device(%s, %s, %s): %s" % (vm.name,
+                                                        host_file, driver, out))
+        device = "%02x:%02x" % (int(dev.group(2)), int(dev.group(3)))
+        time.sleep(3)
+        out = vm.monitor.info('qtree', debug=False)
+        if out.count('addr %s.0' % device) != 1:
+            raise error.TestFail("Can't add device(%s, %s, %s): device in qtree"
+                            ":\n%s" % (vm.name, host_file.name, driver, out))
+        return (ret_file, device)
+
+
+    def rm_drive(vm, host_file, device):
+        """
+        Remove drive from vm and device on disk
+        ! beware to remove scsi devices in reverse order !
+        """
+        err = False
+        vm.monitor.cmd("pci_del %s" % device)
+        time.sleep(3)
+        qtree = vm.monitor.info('qtree', debug=False)
+        if qtree.count('addr %s.0' % device) != 0:
+            err = True
+            vm.destroy()
+
+        if isinstance(host_file, str):    # scsi device
+            utils.system("echo -1> /sys/bus/pseudo/drivers/scsi_debug/add_host")
+        else:     # file
+            host_file.close()
+
+        if err:
+            logging.error("Cant del device(%s, %s, %s):\n%s", vm.name,
+                                                    host_file, device, qtree)
+
+
     # Tests
     class _TestBlkioBandwidth:
         """
@@ -46,9 +175,8 @@ def run_cgroup(test, params, env):
             """
             err = ""
             try:
-                for i in range (2):
-                    vms[i].monitor.cmd("pci_del %s" % self.devices[i])
-                    self.files[i].close()
+                for i in range(1, -1, -1):
+                    rm_drive(vms[i], self.files[i], self.devices[i])
             except Exception, failure_detail:
                 err += "\nCan't remove PCI drive: %s" % failure_detail
             try:
@@ -67,7 +195,7 @@ def run_cgroup(test, params, env):
              * assigns vm1 and vm2 into cgroups and sets the properties
              * creates a new virtio device and adds it into vms
             """
-            if test.tagged_testname.find('virtio_blk') == -1:
+            if get_device_driver() != 'virtio':
                 logging.warn("The main disk for this VM is non-virtio, keep in "
                              "mind that this particular subtest will add a new "
                              "virtio_blk disk to it")
@@ -89,8 +217,7 @@ def run_cgroup(test, params, env):
                 if blkio.set_cgroup(self.vms[i].get_shell_pid(), pwd[i]):
                     raise error.TestError("Could not set cgroup")
                 # Move all existing threads into cgroup
-                for tmp in utils.system_output("ps -L --ppid=%d -o lwp"
-                                % self.vms[i].get_shell_pid()).split('\n')[1:]:
+                for tmp in utils.get_children_pids(self.vms[i].get_shell_pid()):
                     if blkio.set_cgroup(int(tmp), pwd[i]):
                         raise error.TestError("Could not set cgroup")
             if self.blkio.set_property("blkio.weight", 100, pwd[0]):
@@ -101,18 +228,9 @@ def run_cgroup(test, params, env):
             # Add dummy drives
             # TODO: implement also using QMP.
             for i in range(2):
-                self.files.append(tempfile.NamedTemporaryFile(
-                                        prefix="cgroup-disk-",
-                                        suffix=".iso"))
-                utils.system("dd if=/dev/zero of=%s bs=1M count=10 &>/dev/null"
-                             % (self.files[i].name))
-                out = vms[i].monitor.cmd("pci_add auto storage file=%s,"
-                                "if=virtio,snapshot=off,cache=off"
-                                % (self.files[i].name))
-                out = re.search(r'OK domain (\d+), bus (\d+), slot (\d+), '
-                                 'function \d+', out).groups()
-                self.devices.append("%s:%s:%s" % out)
-
+                (host_file, device) = add_file_drive(vms[i], "virtio")
+                self.files.append(host_file)
+                self.devices.append(device)
 
         def run(self):
             """
@@ -155,13 +273,16 @@ def run_cgroup(test, params, env):
             out2 = out[1][1]
             # Cgroup are limitting weights of guests 100:1000. On bare mettal it
             # works in virtio_blk we are satisfied with the ratio 1:3.
+            if out1 == 0:
+                raise error.TestFail("No data transfered: %d:%d (1:10)" %
+                                      (out1, out2))
             if out1*3  > out2:
-                raise error.TestFail("dd values: %s:%s (1:%f), limit 1:2.5"
+                raise error.TestFail("dd values: %d:%d (1:%.2f), limit 1:3"
                                      ", theoretical: 1:10"
                                      % (out1, out2, out2/out1))
             else:
-                logging.info("dd values: %s:%s (1:%s)", out1, out2, out2/out1)
-            return "dd values: %s:%s (1:%s)" % (out1, out2, out2/out1)
+                logging.info("dd values: %d:%d (1:%.2f)", out1, out2, out2/out1)
+            return "dd values: %d:%d (1:%.2f)" % (out1, out2, out2/out1)
 
 
 
@@ -178,7 +299,7 @@ def run_cgroup(test, params, env):
             _TestBlkioBandwidth.__init__(self, vms, modules)
             # Read from the last vd* in a loop until test removes the
             # /tmp/cgroup_lock file (and kills us)
-            self.dd_cmd = ("export FILE=$(ls /dev/vd* | tail -n 1); touch "
+            self.dd_cmd = ("export FILE=$(ls /dev/vd? | tail -n 1); touch "
                            "/tmp/cgroup_lock ; while [ -e /tmp/cgroup_lock ];"
                            "do dd if=$FILE of=/dev/null iflag=direct bs=100K;"
                            "done")
@@ -197,41 +318,12 @@ def run_cgroup(test, params, env):
             # Write on the last vd* in a loop until test removes the
             # /tmp/cgroup_lock file (and kills us)
             _TestBlkioBandwidth.__init__(self, vms, modules)
-            self.dd_cmd = ('export FILE=$(ls /dev/vd* | tail -n 1); touch '
+            self.dd_cmd = ('export FILE=$(ls /dev/vd? | tail -n 1); touch '
                            '/tmp/cgroup_lock ; while [ -e /tmp/cgroup_lock ];'
                            'do dd if=/dev/zero of=$FILE oflag=direct bs=100K;'
                            'done')
 
 
-    def _check_vms(vms):
-        """
-        Checks if the VM is alive.
-        @param vms: list of vm's
-        """
-        for i in range(len(vms)):
-            vms[i].verify_alive()
-            try:
-                vms[i].verify_kernel_crash()
-            except virt_vm.VMDeadKernelCrashError, failure_detail:
-                logging.error("_check_vms: %s", failure_detail)
-                logging.warn("recreate VM(%s)", i)
-                # The vm has to be recreated to reset the qemu PCI state
-                vms[i].create()
-
-    def _traceback(name, exc_info):
-        """
-        Formats traceback into lines "name: line\nname: line"
-        @param name: desired line preposition
-        @param exc_info: sys.exc_info of the exception
-        @return: string which contains beautifully formatted exception
-        """
-        out = "\n"
-        for line in traceback.format_exception(exc_info[0], exc_info[1],
-                                               exc_info[2]):
-            out += "%s: %s" % (name, line)
-        return out
-
-
     # Setup
     # TODO: Add all new tests here
     tests = {"blkio_bandwidth_weigth_read"  : TestBlkioBandwidthWeigthRead,
@@ -266,55 +358,49 @@ def run_cgroup(test, params, env):
             # cg_test is the subtest name from regular expression
             for cg_test in [_ for _ in tests.keys() if re.match(rexpr, _)]:
                 logging.info("%s: Entering the test", cg_test)
+                err = ""
                 try:
-                    _check_vms(vms)
                     tst = tests[cg_test](vms, modules)
                     tst.init()
                     out = tst.run()
                 except error.TestFail, failure_detail:
                     logging.error("%s: Leaving, test FAILED (TestFail): %s",
                                   cg_test, failure_detail)
-                    results += "\n * %s: Test FAILED (TestFail): %s" % (cg_test,
-                                                                failure_detail)
-                    try:
-                        tst.cleanup()
-                    except Exception, failure_detail:
-                        tb = _traceback("%s cleanup:" % cg_test, sys.exc_info())
-                        logging.info("%s: cleanup also failed\n%s", cg_test, tb)
+                    err += "test, "
+                    out = failure_detail
                 except error.TestError, failure_detail:
-                    tb = _traceback(cg_test, sys.exc_info())
+                    tb = utils.etraceback(cg_test, sys.exc_info())
                     logging.error("%s: Leaving, test FAILED (TestError): %s",
                                   cg_test, tb)
-                    results += "\n * %s: Test FAILED (TestError): %s"% (cg_test,
-                                                                failure_detail)
-                    try:
-                        tst.cleanup()
-                    except Exception, failure_detail:
-                        logging.warn("%s: cleanup also failed: %s\n", cg_test,
-                                                                failure_detail)
+                    err += "testErr, "
+                    out = failure_detail
                 except Exception, failure_detail:
-                    tb = _traceback(cg_test, sys.exc_info())
+                    tb = utils.etraceback(cg_test, sys.exc_info())
                     logging.error("%s: Leaving, test FAILED (Exception): %s",
                                   cg_test, tb)
-                    results += "\n * %s: Test FAILED (Exception): %s"% (cg_test,
-                                                                failure_detail)
-                    try:
-                        tst.cleanup()
-                    except Exception, failure_detail:
-                        logging.warn("%s: cleanup also failed: %s\n", cg_test,
-                                                                failure_detail)
+                    err += "testUnknownErr, "
+                    out = failure_detail
+
+                try:
+                    tst.cleanup()
+                except Exception, failure_detail:
+                    logging.warn("%s: cleanup failed: %s\n", failure_detail)
+                    err += "cleanup, "
+
+                try:
+                    _check_vms(vms)
+                except Exception, failure_detail:
+                    logging.warn("%s: _check_vms failed: %s\n", failure_detail)
+                    err += "VM check, "
+
+                if err.startswith("test"):
+                    results += ("\n [F] %s: {%s} FAILED: %s" %
+                                 (cg_test, err[:-2], out))
+                elif err:
+                    results += ("\n [C] %s: Test passed but {%s} FAILED: %s" %
+                                 (cg_test, err[:-2], out))
                 else:
-                    try:
-                        tst.cleanup()
-                    except Exception, failure_detail:
-                        tb = _traceback("%s cleanup:" % cg_test, sys.exc_info())
-                        logging.info("%s: Leaving, test passed but cleanup "
-                                     "FAILED\n%s", cg_test, tb)
-                        results += ("\n * %s: Test passed but cleanup FAILED"
-                                    % (cg_test))
-                    else:
-                        logging.info("%s: Leaving, test PASSED", cg_test)
-                        results += "\n * %s: Test PASSED: %s" % (cg_test, out)
+                    results += ("\n [P] %s: PASSED: %s" % (cg_test, out))
 
     out = ("SUM: All tests finished (%d PASS / %d FAIL = %d TOTAL)%s" %
            (results.count("PASSED"), results.count("FAILED"),
diff --git a/client/virt/kvm_monitor.py b/client/virt/kvm_monitor.py
index 888cefd..6b00e31 100644
--- a/client/virt/kvm_monitor.py
+++ b/client/virt/kvm_monitor.py
@@ -322,11 +322,12 @@ class HumanMonitor(Monitor):
         self._send("quit")
 
 
-    def info(self, what):
+    def info(self, what, debug=True):
         """
         Request info about something and return the output.
+        @param debug: Whether to print the commands being sent and responses
         """
-        return self.cmd("info %s" % what)
+        return self.cmd("info %s" % what, debug=debug)
 
 
     def query(self, what):
-- 
1.7.6.2

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux