Adding kvm test
The KVM team has been working on a set of tests for our virtualization
stack based on autotest. The project is known as kvm-autotest:
http://www.linux-kvm.org/page/KVM-Autotest
git://git.kernel.org/pub/scm/virt/kvm/kvm-autotest.git
In order to accomodate the needs of KVM Quality Engineering, quite a
substantial amount of code was written. A very brief overview of how the
tests are structured follows:
* kvm_runtest_2: Entry point, that defines hooks to all the KVM tests,
documented under:
http://www.linux-kvm.org/page/KVM-Autotest/Tests
* kvm_config: Module that handles the KVM configuration file format
http://www.linux-kvm.org/page/KVM-Autotest/Test_Config_File
It parses the configuration file and generates a list of dictionaries
that will be used by the other tests.
* step editor and stepmaker: A method to automate installation of guests
was devised, the idea is to send input to qemu through *step files*.
StepEditor and StepMaker are pygtk applications that allow to create and
edit step files.
From:
uril@redhat.com (Uri Lublin)
mgoldish@redhat.com (Michael Goldish)
dhuff@redhat.com (David Huff)
aeromenk@redhat.com (Alexey Eromenko)
mburns@redhat.com (Mike Burns)
Signed-off-by: Lucas Meneghel Rodrigues <lmr@redhat.com>
git-svn-id: http://test.kernel.org/svn/autotest/trunk@3187 592f7852-d20e-0410-864c-8624ca9c26a4
diff --git a/client/tests/kvm/kvm_preprocessing.py b/client/tests/kvm/kvm_preprocessing.py
new file mode 100644
index 0000000..e395d98
--- /dev/null
+++ b/client/tests/kvm/kvm_preprocessing.py
@@ -0,0 +1,255 @@
+import sys, os, time, commands, re, logging
+from autotest_lib.client.bin import test
+from autotest_lib.client.common_lib import error
+import kvm_vm, kvm_utils
+
+
+def preprocess_image(test, params):
+ """
+ Preprocess a single QEMU image according to the instructions in params.
+
+ @param test: Autotest test object.
+ @param params: A dict containing image preprocessing parameters.
+ @note: Currently this function just creates an image if requested.
+ """
+ qemu_img_path = os.path.join(test.bindir, "qemu-img")
+ image_dir = os.path.join(test.bindir, "images")
+ image_filename = kvm_vm.get_image_filename(params, image_dir)
+
+ create_image = False
+
+ if params.get("force_create_image") == "yes":
+ logging.debug("'force_create_image' specified; creating image...")
+ create_image = True
+ elif params.get("create_image") == "yes" and not \
+ os.path.exists(image_filename):
+ logging.debug("Creating image...")
+ create_image = True
+
+ if create_image:
+ if not kvm_vm.create_image(params, qemu_img_path, image_dir):
+ message = "Could not create image"
+ logging.error(message)
+ raise error.TestError(message)
+
+
+def preprocess_vm(test, params, env, name):
+ """
+ Preprocess a single VM object according to the instructions in params.
+ Start the VM if requested and get a screendump.
+
+ @param test: An Autotest test object.
+ @param params: A dict containing VM preprocessing parameters.
+ @param env: The environment (a dict-like object).
+ @param name: The name of the VM object.
+ """
+ qemu_path = os.path.join(test.bindir, "qemu")
+ image_dir = os.path.join(test.bindir, "images")
+ iso_dir = os.path.join(test.bindir, "isos")
+
+ logging.debug("Preprocessing VM '%s'..." % name)
+ vm = kvm_utils.env_get_vm(env, name)
+ if vm:
+ logging.debug("VM object found in environment")
+ else:
+ logging.debug("VM object does not exist; creating it")
+ vm = kvm_vm.VM(name, params, qemu_path, image_dir, iso_dir)
+ kvm_utils.env_register_vm(env, name, vm)
+
+ start_vm = False
+ for_migration = False
+
+ if params.get("start_vm_for_migration") == "yes":
+ logging.debug("'start_vm_for_migration' specified; (re)starting VM with"
+ " -incoming option...")
+ start_vm = True
+ for_migration = True
+ elif params.get("restart_vm") == "yes":
+ logging.debug("'restart_vm' specified; (re)starting VM...")
+ start_vm = True
+ elif params.get("start_vm") == "yes":
+ if not vm.is_alive():
+ logging.debug("VM is not alive; starting it...")
+ start_vm = True
+ elif vm.make_qemu_command() != vm.make_qemu_command(name, params,
+ qemu_path,
+ image_dir,
+ iso_dir):
+ logging.debug("VM's qemu command differs from requested one;"
+ "restarting it...")
+ start_vm = True
+
+ if start_vm:
+ vm.destroy()
+ if not vm.create(name, params, qemu_path, image_dir, iso_dir,
+ for_migration):
+ message = "Could not start VM"
+ logging.error(message)
+ raise error.TestError(message)
+
+ scrdump_filename = os.path.join(test.debugdir, "pre_%s.ppm" % name)
+ vm.send_monitor_cmd("screendump %s" % scrdump_filename)
+
+
+def postprocess_image(test, params):
+ """
+ Postprocess a single QEMU image according to the instructions in params.
+ Currently this function just removes an image if requested.
+
+ @param test: An Autotest test object.
+ @param params: A dict containing image postprocessing parameters.
+ """
+ image_dir = os.path.join(test.bindir, "images")
+
+ if params.get("remove_image") == "yes":
+ kvm_vm.remove_image(params, image_dir)
+
+
+def postprocess_vm(test, params, env, name):
+ """
+ Postprocess a single VM object according to the instructions in params.
+ Kill the VM if requested and get a screendump.
+
+ @param test: An Autotest test object.
+ @param params: A dict containing VM postprocessing parameters.
+ @param env: The environment (a dict-like object).
+ @param name: The name of the VM object.
+ """
+ logging.debug("Postprocessing VM '%s'..." % name)
+ vm = kvm_utils.env_get_vm(env, name)
+ if vm:
+ logging.debug("VM object found in environment")
+ else:
+ logging.debug("VM object does not exist in environment")
+ return
+
+ scrdump_filename = os.path.join(test.debugdir, "post_%s.ppm" % name)
+ vm.send_monitor_cmd("screendump %s" % scrdump_filename)
+
+ if params.get("kill_vm") == "yes":
+ if not kvm_utils.wait_for(vm.is_dead,
+ float(params.get("kill_vm_timeout", 0)), 0.0, 1.0,
+ "Waiting for VM to kill itself..."):
+ logging.debug("'kill_vm' specified; killing VM...")
+ vm.destroy(gracefully = params.get("kill_vm_gracefully") == "yes")
+
+
+def process(test, params, env, image_func, vm_func):
+ """
+ Pre- or post-process VMs and images according to the instructions in params.
+ Call image_func for each image listed in params and vm_func for each VM.
+
+ @param test: An Autotest test object.
+ @param params: A dict containing all VM and image parameters.
+ @param env: The environment (a dict-like object).
+ @param image_func: A function to call for each image.
+ @param vm_func: A function to call for each VM.
+ """
+ # Get list of VMs specified for this test
+ vm_names = kvm_utils.get_sub_dict_names(params, "vms")
+ for vm_name in vm_names:
+ vm_params = kvm_utils.get_sub_dict(params, vm_name)
+ # Get list of images specified for this VM
+ image_names = kvm_utils.get_sub_dict_names(vm_params, "images")
+ for image_name in image_names:
+ image_params = kvm_utils.get_sub_dict(vm_params, image_name)
+ # Call image_func for each image
+ image_func(test, image_params)
+ # Call vm_func for each vm
+ vm_func(test, vm_params, env, vm_name)
+
+
+def preprocess(test, params, env):
+ """
+ Preprocess all VMs and images according to the instructions in params.
+ Also, collect some host information, such as the KVM version.
+
+ @param test: An Autotest test object.
+ @param params: A dict containing all VM and image parameters.
+ @param env: The environment (a dict-like object).
+ """
+ # Verify the identities of all living VMs
+ for vm in env.values():
+ if not kvm_utils.is_vm(vm):
+ continue
+ if vm.is_dead():
+ continue
+ if not vm.verify_process_identity():
+ logging.debug("VM '%s' seems to have been replaced by another"
+ " process" % vm.name)
+ vm.pid = None
+
+ # Destroy and remove VMs that are no longer needed in the environment
+ requested_vms = kvm_utils.get_sub_dict_names(params, "vms")
+ for key in env.keys():
+ vm = env[key]
+ if not kvm_utils.is_vm(vm):
+ continue
+ if not vm.name in requested_vms:
+ logging.debug("VM '%s' found in environment but not required for"
+ " test; removing it..." % vm.name)
+ vm.destroy()
+ del env[key]
+
+ # Preprocess all VMs and images
+ process(test, params, env, preprocess_image, preprocess_vm)
+
+ # Get the KVM kernel module version and write it as a keyval
+ logging.debug("Fetching KVM module version...")
+ if os.path.exists("/dev/kvm"):
+ kvm_version = os.uname()[2]
+ try:
+ file = open("/sys/module/kvm/version", "r")
+ kvm_version = file.read().strip()
+ file.close()
+ except:
+ pass
+ else:
+ kvm_version = "Unknown"
+ logging.debug("KVM module not loaded")
+ logging.debug("KVM version: %s" % kvm_version)
+ test.write_test_keyval({"kvm_version": kvm_version})
+
+ # Get the KVM userspace version and write it as a keyval
+ logging.debug("Fetching KVM userspace version...")
+ qemu_path = os.path.join(test.bindir, "qemu")
+ version_line = commands.getoutput("%s -help | head -n 1" % qemu_path)
+ exp = re.compile("[Vv]ersion .*?,")
+ match = exp.search(version_line)
+ if match:
+ kvm_userspace_version = " ".join(match.group().split()[1:]).strip(",")
+ else:
+ kvm_userspace_version = "Unknown"
+ logging.debug("Could not fetch KVM userspace version")
+ logging.debug("KVM userspace version: %s" % kvm_userspace_version)
+ test.write_test_keyval({"kvm_userspace_version": kvm_userspace_version})
+
+
+def postprocess(test, params, env):
+ """
+ Postprocess all VMs and images according to the instructions in params.
+
+ @param test: An Autotest test object.
+ @param params: Dict containing all VM and image parameters.
+ @param env: The environment (a dict-like object).
+ """
+ process(test, params, env, postprocess_image, postprocess_vm)
+
+ # See if we should get rid of all PPM files
+ if not params.get("keep_ppm_files") == "yes":
+ # Remove them all
+ logging.debug("'keep_ppm_files' not specified; removing all PPM files"
+ " from results dir...")
+ kvm_utils.run_bg("rm -vf %s" % os.path.join(test.debugdir, "*.ppm"),
+ None, logging.debug, "(rm) ", timeout=5.0)
+
+
+def postprocess_on_error(test, params, env):
+ """
+ Perform postprocessing operations required only if the test failed.
+
+ @param test: An Autotest test object.
+ @param params: A dict containing all VM and image parameters.
+ @param env: The environment (a dict-like object).
+ """
+ params.update(kvm_utils.get_sub_dict(params, "on_error"))