Merge remote branch 'cros/upstream' into autotest-rebase

Merged to upstream trunk@5066, from trunk@4749.

There is no way I could enlist each individual CL from the upstream here since it will blow up the changelist description field.

BUG=
TEST=
Had patched this CL into a fresh cut client to avoid any side effect.
run_remote_test bvt from both emerged location and third_party/autotest/file.

Both test passed!

We should also keep any eye on this to see how it gets propagated into cautotest server.
TBR=dalecurtis

Change-Id: I72f2bc7a9de530178484aea1bfb5ace68bcad029
diff --git a/client/tests/kvm/build.cfg.sample b/client/tests/kvm/build.cfg.sample
index 860192b..a689ed4 100644
--- a/client/tests/kvm/build.cfg.sample
+++ b/client/tests/kvm/build.cfg.sample
@@ -16,30 +16,42 @@
         save_results = no
         variants:
             - release:
-                mode = release
+                install_mode = release
                 ## Install from a kvm release. You can optionally specify
                 ## a release tag. If you omit it, the build test will get
                 ## the latest release tag available at that moment.
                 # release_tag = 84
                 release_dir = http://downloads.sourceforge.net/project/kvm/
                 release_listing = http://sourceforge.net/projects/kvm/files/
+                # In some cases, you might want to provide a ROM dir, so ROM
+                # files can be copied from there to your source based install
+                # path_to_rom_images = /usr/share/kvm
             - snapshot:
-                mode = snapshot
+                install_mode = snapshot
                 ## Install from a kvm snapshot location. You can optionally
                 ## specify a snapshot date. If you omit it, the test will get
                 ## yesterday's snapshot.
                 # snapshot_date = 20090712
                 snapshot_dir = http://foo.org/kvm-snapshots/
+                # In some cases, you might want to provide a ROM dir, so ROM
+                # files can be copied from there to your source based install
+                # path_to_rom_images = /usr/share/kvm
             - localtar:
-                mode = localtar
+                install_mode = localtar
                 ## Install from tarball located on the host's filesystem.
                 tarball = /tmp/kvm-84.tar.gz
+                # In some cases, you might want to provide a ROM dir, so ROM
+                # files can be copied from there to your source based install
+                # path_to_rom_images = /usr/share/kvm
             - localsrc:
-                mode = localsrc
+                install_mode = localsrc
                 ## Install from tarball located on the host's filesystem.
                 srcdir = /tmp/kvm-84
+                # In some cases, you might want to provide a ROM dir, so ROM
+                # files can be copied from there to your source based install
+                # path_to_rom_images = /usr/share/kvm
             - git:
-                mode = git
+                install_mode = git
                 ## Install KVM from git repositories.
                 ## If you provide only "git_repo" and "user_git_repo", the
                 ## build test will assume it will perform all build from the
@@ -64,8 +76,11 @@
                 # kmod_lbranch = kmod_lbranch_name
                 # kmod_commit = kmod_commit_name
                 # kmod_patches = ['http://foo.com/patch1', 'http://foo.com/patch2']
+                # In some cases, you might want to provide a ROM dir, so ROM
+                # files can be copied from there to your source based install
+                # path_to_rom_images = /usr/share/kvm
             - yum:
-                mode = yum
+                install_mode = yum
                 src_pkg = qemu
                 ## Name of the rpms we need installed
                 pkg_list = ['qemu-kvm', 'qemu-kvm-tools', 'qemu-system-x86', 'qemu-common', 'qemu-img']
@@ -74,7 +89,7 @@
                 ## List of RPMs that will be installed
                 pkg_path_list = ['http://foo.com/rpm1', 'http://foo.com/rpm2']
             - koji:
-                mode = koji
+                install_mode = koji
                 ## Install KVM from koji (Fedora build server)
                 ## It is possible to install packages right from Koji if you
                 ## provide a release tag or a build.
diff --git a/client/tests/kvm/deps/finish.exe b/client/tests/kvm/deps/finish.exe
old mode 100755
new mode 100644
Binary files differ
diff --git a/client/tests/kvm/deps/rss.exe b/client/tests/kvm/deps/rss.exe
old mode 100755
new mode 100644
Binary files differ
diff --git a/client/tests/kvm/deps/test_clock_getres/Makefile b/client/tests/kvm/deps/test_clock_getres/Makefile
new file mode 100644
index 0000000..b4f73c7
--- /dev/null
+++ b/client/tests/kvm/deps/test_clock_getres/Makefile
@@ -0,0 +1,11 @@
+CC = gcc
+PROG = test_clock_getres
+SRC = test_clock_getres.c
+LIBS = -lrt
+
+all: $(PROG)
+
+$(PROG):
+	$(CC) $(LIBS) -o $(PROG) $(SRC)
+clean:
+	rm -f $(PROG)
diff --git a/client/tests/kvm/deps/test_clock_getres/test_clock_getres.c b/client/tests/kvm/deps/test_clock_getres/test_clock_getres.c
new file mode 100644
index 0000000..81d3b9c
--- /dev/null
+++ b/client/tests/kvm/deps/test_clock_getres/test_clock_getres.c
@@ -0,0 +1,58 @@
+/*
+ *  Test clock resolution for KVM guests that have kvm-clock as clock source
+ *
+ *  Copyright (c) 2010 Red Hat, Inc
+ *  Author: Lucas Meneghel Rodrigues <lmr@redhat.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include <stdio.h>
+#include <time.h>
+#include <stdlib.h>
+#include <string.h>
+
+int main(void) {
+	struct timespec res;
+	int clock_return = clock_getres(CLOCK_MONOTONIC, &res);
+	char clocksource[50];
+	char line[80];
+	FILE *fr;
+	if ((fr = fopen(
+			"/sys/devices/system/clocksource/clocksource0/current_clocksource",
+			"rt")) == NULL) {
+		perror("fopen");
+		return EXIT_FAILURE;
+	}
+	while (fgets(line, 80, fr) != NULL) {
+		sscanf(line, "%s", &clocksource);
+	}
+	fclose(fr);
+	if (!strncmp(clocksource, "kvm-clock", strlen("kvm-clock"))) {
+		if (clock_return == 0) {
+			if (res.tv_sec > 1 || res.tv_nsec > 100) {
+				printf("FAIL: clock_getres returned bad clock resolution\n");
+				return EXIT_FAILURE;
+			} else {
+				printf("PASS: check successful\n");
+				return EXIT_SUCCESS;
+			}
+		} else {
+			printf("FAIL: clock_getres failed\n");
+			return EXIT_FAILURE;
+		}
+	} else {
+		printf("FAIL: invalid clock source: %s\n", clocksource);
+		return EXIT_FAILURE;
+	}
+}
diff --git a/client/tests/kvm/deps/whql_delete_machine_15.exe b/client/tests/kvm/deps/whql_delete_machine_15.exe
old mode 100755
new mode 100644
Binary files differ
diff --git a/client/tests/kvm/deps/whql_submission_15.cs b/client/tests/kvm/deps/whql_submission_15.cs
index 8fa6856..2a29ac5 100644
--- a/client/tests/kvm/deps/whql_submission_15.cs
+++ b/client/tests/kvm/deps/whql_submission_15.cs
@@ -16,20 +16,140 @@
 {
     class AutoJob
     {
+        // Wait for a machine to show up in the data store
+        static void FindMachine(IResourcePool rootPool, string machineName)
+        {
+            Console.WriteLine("Looking for machine '{0}'", machineName);
+            IResource machine = null;
+            while (true)
+            {
+                try
+                {
+                    machine = rootPool.GetResourceByName(machineName);
+                }
+                catch (Exception e)
+                {
+                    Console.WriteLine("Warning: " + e.Message);
+                }
+                // Make sure the machine is valid
+                if (machine != null &&
+                    machine.OperatingSystem != null &&
+                    machine.OperatingSystem.Length > 0 &&
+                    machine.ProcessorArchitecture != null &&
+                    machine.ProcessorArchitecture.Length > 0 &&
+                    machine.GetDevices().Length > 0)
+                    break;
+                System.Threading.Thread.Sleep(1000);
+            }
+            Console.WriteLine("Client machine '{0}' found ({1}, {2})",
+                machineName, machine.OperatingSystem, machine.ProcessorArchitecture);
+        }
+
+        // Delete a machine pool if it exists
+        static void DeleteResourcePool(IDeviceScript script, string poolName)
+        {
+            while (true)
+            {
+                try
+                {
+                    IResourcePool pool = script.GetResourcePoolByName(poolName);
+                    if (pool != null)
+                        script.DeleteResourcePool(pool);
+                    break;
+                }
+                catch (Exception e)
+                {
+                    Console.WriteLine("Warning: " + e.Message);
+                    System.Threading.Thread.Sleep(1000);
+                }
+            }
+        }
+
+        // Set the machine's status to 'Reset' and optionally wait for it to become ready
+        static void ResetMachine(IResourcePool rootPool, string machineName, bool wait)
+        {
+            Console.WriteLine("Resetting machine '{0}'", machineName);
+            IResource machine;
+            while (true)
+            {
+                try
+                {
+                    machine = rootPool.GetResourceByName(machineName);
+                    machine.ChangeResourceStatus("Reset");
+                    break;
+                }
+                catch (Exception e)
+                {
+                    Console.WriteLine("Warning: " + e.Message);
+                    System.Threading.Thread.Sleep(5000);
+                }
+            }
+            if (wait)
+            {
+                Console.WriteLine("Waiting for machine '{0}' to be ready", machineName);
+                while (machine.Status != "Ready")
+                {
+                    try
+                    {
+                        machine = rootPool.GetResourceByName(machineName);
+                    }
+                    catch (Exception e)
+                    {
+                        Console.WriteLine("Warning: " + e.Message);
+                    }
+                    System.Threading.Thread.Sleep(1000);
+                }
+                Console.WriteLine("Machine '{0}' is ready", machineName);
+            }
+        }
+
+        // Look for a device in a machine, and if not found, keep trying for 3 minutes
+        static IDevice GetDevice(IResourcePool rootPool, string machineName, string regexStr)
+        {
+            Regex deviceRegex = new Regex(regexStr, RegexOptions.IgnoreCase);
+            int numAttempts = 1;
+            DateTime endTime = DateTime.Now.AddSeconds(180);
+            while (DateTime.Now < endTime)
+            {
+                IResource machine = rootPool.GetResourceByName(machineName);
+                Console.WriteLine("Looking for device '{0}' in machine '{1}' (machine has {2} devices)",
+                    regexStr, machineName, machine.GetDevices().Length);
+                foreach (IDevice d in machine.GetDevices())
+                {
+                    if (deviceRegex.IsMatch(d.FriendlyName))
+                    {
+                        Console.WriteLine("Found device '{0}'", d.FriendlyName);
+                        return d;
+                    }
+                }
+                Console.WriteLine("Device not found");
+                if (numAttempts % 5 == 0)
+                    ResetMachine(rootPool, machineName, true);
+                else
+                    System.Threading.Thread.Sleep(5000);
+                numAttempts++;
+            }
+            Console.WriteLine("Error: device '{0}' not found", deviceRegex);
+            return null;
+        }
+
         static int Main(string[] args)
         {
-            if (args.Length != 5)
+            if (args.Length < 5)
             {
                 Console.WriteLine("Error: incorrect number of command line arguments");
-                Console.WriteLine("Usage: {0} serverName clientName machinePoolName submissionName timeout",
+                Console.WriteLine("Usage: {0} serverName machinePoolName submissionName timeout machineName0 machineName1 ...",
                     System.Environment.GetCommandLineArgs()[0]);
                 return 1;
             }
             string serverName = args[0];
-            string clientName = args[1];
-            string machinePoolName = args[2];
-            string submissionName = args[3];
-            double timeout = Convert.ToDouble(args[4]);
+            string machinePoolName = args[1];
+            string submissionName = args[2];
+            double timeout = Convert.ToDouble(args[3]);
+
+            List<string> machines = new List<string>();
+            for (int i = 4; i < args.Length; i++)
+                machines.Add(args[i]);
 
             try
             {
@@ -37,37 +157,17 @@
                 Console.WriteLine("Initializing DeviceScript object");
                 DeviceScript script = new DeviceScript();
                 Console.WriteLine("Connecting to data store");
-
                 script.ConnectToNamedDataStore(serverName);
 
-                // Find client machine
+                // Wait for client machines to become available
                 IResourcePool rootPool = script.GetResourcePoolByName("$");
-                Console.WriteLine("Looking for client machine '{0}'", clientName);
-                IResource machine = null;
-                while (true)
-                {
-                    try
-                    {
-                        machine = rootPool.GetResourceByName(clientName);
-                    }
-                    catch (Exception e)
-                    {
-                        Console.WriteLine("Warning: " + e.Message);
-                    }
-                    // Make sure the machine is valid
-                    if (machine != null &&
-                        machine.OperatingSystem != null &&
-                        machine.OperatingSystem.Length > 0 &&
-                        machine.ProcessorArchitecture != null &&
-                        machine.ProcessorArchitecture.Length > 0 &&
-                        machine.GetDevices().Length > 0)
-                        break;
-                    System.Threading.Thread.Sleep(1000);
-                }
-                Console.WriteLine("Client machine '{0}' found ({1}, {2})",
-                    clientName, machine.OperatingSystem, machine.ProcessorArchitecture);
+                foreach (string machineName in machines)
+                    FindMachine(rootPool, machineName);
 
-                // Create machine pool and add client machine to it
+                // Delete the machine pool if it already exists
+                DeleteResourcePool(script, machinePoolName);
+
+                // Create the machine pool and add the client machines to it
                 // (this must be done because jobs cannot be scheduled for machines in the
                 // default pool)
                 try
@@ -79,76 +179,27 @@
                     Console.WriteLine("Warning: " + e.Message);
                 }
                 IResourcePool newPool = script.GetResourcePoolByName(machinePoolName);
-                Console.WriteLine("Moving the client machine to pool '{0}'", machinePoolName);
-                machine.ChangeResourcePool(newPool);
+                foreach (string machineName in machines)
+                {
+                    Console.WriteLine("Moving machine '{0}' to pool '{1}'", machineName, machinePoolName);
+                    rootPool.GetResourceByName(machineName).ChangeResourcePool(newPool);
+                }
 
                 // Reset client machine
-                if (machine.Status != "Ready")
-                {
-                    Console.WriteLine("Changing the client machine's status to 'Reset'");
-                    while (true)
-                    {
-                        try
-                        {
-                            machine = rootPool.GetResourceByName(clientName);
-                            machine.ChangeResourceStatus("Unsafe");
-                            System.Threading.Thread.Sleep(5000);
-                            machine.ChangeResourceStatus("Reset");
-                            break;
-                        }
-                        catch (Exception e)
-                        {
-                            Console.WriteLine("Warning: " + e.Message);
-                        }
-                        System.Threading.Thread.Sleep(5000);
-                    }
-                    Console.WriteLine("Waiting for client machine to be ready");
-                    while (machine.Status != "Ready")
-                    {
-                        try
-                        {
-                            machine = rootPool.GetResourceByName(clientName);
-                        }
-                        catch (Exception e)
-                        {
-                            Console.WriteLine("Warning: " + e.Message);
-                        }
-                        System.Threading.Thread.Sleep(1000);
-                    }
-                }
-                Console.WriteLine("Client machine is ready");
+                foreach (string machineName in machines)
+                    ResetMachine(rootPool, machineName, true);
 
-                // Get requested device regex and look for a matching device
-                Console.WriteLine("Device to test: ");
-                Regex deviceRegex = new Regex(Console.ReadLine(), RegexOptions.IgnoreCase);
-                Console.WriteLine("Looking for device '{0}'", deviceRegex);
-                IDevice device;
-                DateTime endTime = DateTime.Now.AddSeconds(120);
-                while (DateTime.Now < endTime)
-                {
-                    machine = rootPool.GetResourceByName(clientName);
-                    Console.WriteLine("(Client machine has {0} devices)", machine.GetDevices().Length);
-                    foreach (IDevice d in machine.GetDevices())
-                    {
-                        if (deviceRegex.IsMatch(d.FriendlyName))
-                        {
-                            device = d;
-                            goto deviceFound;
-                        }
-                    }
-                    System.Threading.Thread.Sleep(5000);
-                }
-                Console.WriteLine("Error: device '{0}' not found", deviceRegex);
-                return 1;
-
-            deviceFound:
-                Console.WriteLine("Found device '{0}'", device.FriendlyName);
+                // Get requested device regex and look for a matching device in the first machine
+                Console.WriteLine("Device to test:");
+                IDevice device = GetDevice(rootPool, machines[0], Console.ReadLine());
+                if (device == null)
+                    return 1;
 
                 // Get requested jobs regex
-                Console.WriteLine("Jobs to run: ");
+                Console.WriteLine("Jobs to run:");
                 Regex jobRegex = new Regex(Console.ReadLine(), RegexOptions.IgnoreCase);
 
-                // Create submission
+                // Create a submission
                 Object[] existingSubmissions = script.GetSubmissionByName(submissionName);
                 if (existingSubmissions.Length > 0)
                 {
@@ -156,83 +207,126 @@
                         submissionName);
                     script.DeleteSubmission(((ISubmission)existingSubmissions[0]).Id);
                 }
-                Console.WriteLine("Creating submission '{0}'", submissionName);
-                ISubmission submission = script.CreateHardwareSubmission(submissionName,
-                    newPool.ResourcePoolId, device.InstanceId);
+                string hardwareId = device.InstanceId.Remove(device.InstanceId.LastIndexOf("\\"));
+                Console.WriteLine("Creating submission '{0}' (hardware ID: {1})", submissionName, hardwareId);
+                ISubmission submission = script.CreateHardwareSubmission(submissionName, newPool.ResourcePoolId, hardwareId);
 
-                // Get DeviceData objects from the user
+                // Set submission DeviceData
                 List<Object> deviceDataList = new List<Object>();
                 while (true)
                 {
                     ISubmissionDeviceData dd = script.CreateNewSubmissionDeviceData();
-                    Console.WriteLine("DeviceData name: ");
+                    Console.WriteLine("DeviceData name:");
                     dd.Name = Console.ReadLine();
                     if (dd.Name.Length == 0)
                         break;
-                    Console.WriteLine("DeviceData data: ");
+                    Console.WriteLine("DeviceData data:");
                     dd.Data = Console.ReadLine();
                     deviceDataList.Add(dd);
                 }
-
-                // Set the submission's DeviceData
                 submission.SetDeviceData(deviceDataList.ToArray());
 
-                // Get descriptors from the user
+                // Set submission descriptors
                 List<Object> descriptorList = new List<Object>();
                 while (true)
                 {
-                    Console.WriteLine("Descriptor path: ");
+                    Console.WriteLine("Descriptor path:");
                     string descriptorPath = Console.ReadLine();
                     if (descriptorPath.Length == 0)
                         break;
                     descriptorList.Add(script.GetDescriptorByPath(descriptorPath));
                 }
-
-                // Set the submission's descriptors
                 submission.SetLogoDescriptors(descriptorList.ToArray());
 
-                // Create a schedule
-                ISchedule schedule = script.CreateNewSchedule();
+                // Set machine dimensions
+                foreach (string machineName in machines)
+                {
+                    IResource machine = rootPool.GetResourceByName(machineName);
+                    while (true)
+                    {
+                        Console.WriteLine("Dimension name ({0}):", machineName);
+                        string dimName = Console.ReadLine();
+                        if (dimName.Length == 0)
+                            break;
+                        Console.WriteLine("Dimension value ({0}):", machineName);
+                        machine.SetDimension(dimName, Console.ReadLine());
+                    }
+                    // Set the WDKSubmissionId dimension for all machines
+                    machine.SetDimension("WDKSubmissionId", submission.Id.ToString() + "_" + submission.Name);
+                }
+
+                // Get job parameters
+                List<string> paramNames = new List<string>();
+                List<string> paramValues = new List<string>();
+                foreach (string machineName in machines)
+                {
+                    while (true)
+                    {
+                        Console.WriteLine("Parameter name ({0}):", machineName);
+                        string paramName = Console.ReadLine();
+                        if (paramName.Length == 0)
+                            break;
+                        Console.WriteLine("Device regex ({0}):", machineName);
+                        IDevice d = GetDevice(rootPool, machineName, Console.ReadLine());
+                        if (d == null)
+                            return 1;
+                        string deviceName = d.GetAttribute("name")[0].ToString();
+                        Console.WriteLine("Setting parameter value to '{0}'", deviceName);
+                        paramNames.Add(paramName);
+                        paramValues.Add(deviceName);
+                    }
+                }
+
+                // Find jobs that match the requested pattern
                 Console.WriteLine("Scheduling jobs:");
-                int jobCount = 0;
+                List<IJob> jobs = new List<IJob>();
                 foreach (IJob j in submission.GetJobs())
                 {
                     if (jobRegex.IsMatch(j.Name))
-                     {
-                        Console.WriteLine("  " + j.Name);
-                        schedule.AddDeviceJob(device, j);
-                        jobCount++;
+                    {
+                        Console.WriteLine("    " + j.Name);
+                        // Set job parameters
+                        for (int i = 0; i < paramNames.Count; i++)
+                        {
+                            IParameter p = j.GetParameterByName(paramNames[i]);
+                            if (p != null)
+                                p.ScheduleValue = paramValues[i];
+                        }
+                        jobs.Add(j);
                     }
                 }
-                if (jobCount == 0)
+                if (jobs.Count == 0)
                 {
                     Console.WriteLine("Error: no submission jobs match pattern '{0}'", jobRegex);
                     return 1;
                 }
+
+                // Create a schedule, add jobs to it and run it
+                ISchedule schedule = script.CreateNewSchedule();
+                foreach (IScheduleItem item in submission.ProcessJobs(jobs.ToArray()))
+                {
+                    item.Device = device;
+                    schedule.AddScheduleItem(item);
+                }
                 schedule.AddSubmission(submission);
                 schedule.SetResourcePool(newPool);
                 script.RunSchedule(schedule);
 
                 // Wait for jobs to complete
-                Console.WriteLine("Waiting for all jobs to complete (timeout={0})", timeout);
-                endTime = DateTime.Now.AddSeconds(timeout);
-                int numCompleted = 0, numFailed = 0;
-                while (numCompleted < submission.GetResults().Length && DateTime.Now < endTime)
+                Console.WriteLine("Waiting for all jobs to complete (timeout={0}s)", timeout);
+                DateTime endTime = DateTime.Now.AddSeconds(timeout);
+                int numCompleted, numFailed;
+                do
                 {
-                    // Sleep for 30 seconds
                     System.Threading.Thread.Sleep(30000);
-                    // Count completed submission jobs
-                    numCompleted = 0;
-                    foreach (IResult r in submission.GetResults())
-                        if (r.ResultStatus != "InProgress")
-                            numCompleted++;
-                    // Report results in a Python readable format and count failed schedule jobs
-                    // (submission jobs are a subset of schedule jobs)
+                    // Report results in a Python readable format and count completed and failed schedule jobs
+                    numCompleted = numFailed = 0;
                     Console.WriteLine();
                     Console.WriteLine("---- [");
-                    numFailed = 0;
                     foreach (IResult r in schedule.GetResults())
                     {
+                        if (r.ResultStatus != "InProgress") numCompleted++;
+                        if (r.ResultStatus == "Investigate") numFailed++;
                         Console.WriteLine("  {");
                         Console.WriteLine("    'id': {0}, 'job': r'''{1}''',", r.Job.Id, r.Job.Name);
                         Console.WriteLine("    'logs': r'''{0}''',", r.LogLocation);
@@ -243,10 +337,10 @@
                         Console.WriteLine("    'pass': {0}, 'fail': {1}, 'notrun': {2}, 'notapplicable': {3}",
                             r.Pass, r.Fail, r.NotRun, r.NotApplicable);
                         Console.WriteLine("  },");
-                        numFailed += r.Fail;
                     }
                     Console.WriteLine("] ----");
-                }
+                } while (numCompleted < schedule.GetResults().Length && DateTime.Now < endTime);
+
                 Console.WriteLine();
 
                 // Cancel incomplete jobs
@@ -254,26 +348,16 @@
                     if (r.ResultStatus == "InProgress")
                         r.Cancel();
 
-                // Set the machine's status to Unsafe and then Reset
-                try
-                {
-                    machine = rootPool.GetResourceByName(clientName);
-                    machine.ChangeResourceStatus("Unsafe");
-                    System.Threading.Thread.Sleep(5000);
-                    machine.ChangeResourceStatus("Reset");
-                }
-                catch (Exception e)
-                {
-                    Console.WriteLine("Warning: " + e.Message);
-                }
+                // Reset the machines
+                foreach (string machineName in machines)
+                    ResetMachine(rootPool, machineName, false);
 
                 // Report failures
-                if (numCompleted < submission.GetResults().Length)
+                if (numCompleted < schedule.GetResults().Length)
                     Console.WriteLine("Some jobs did not complete on time.");
                 if (numFailed > 0)
                     Console.WriteLine("Some jobs failed.");
-
-                if (numFailed > 0 || numCompleted < submission.GetResults().Length)
+                if (numFailed > 0 || numCompleted < schedule.GetResults().Length)
                     return 1;
 
                 Console.WriteLine("All jobs completed.");
diff --git a/client/tests/kvm/deps/whql_submission_15.exe b/client/tests/kvm/deps/whql_submission_15.exe
old mode 100755
new mode 100644
index 4f30aa8..605e2e3
--- a/client/tests/kvm/deps/whql_submission_15.exe
+++ b/client/tests/kvm/deps/whql_submission_15.exe
Binary files differ
diff --git a/client/tests/kvm/get_started.py b/client/tests/kvm/get_started.py
index 6fa6b5f..126d8a7 100755
--- a/client/tests/kvm/get_started.py
+++ b/client/tests/kvm/get_started.py
@@ -82,11 +82,11 @@
     logging.info("3 - Verifying iso (make sure we have the OS ISO needed for "
                  "the default test set)")
 
-    iso_name = "Fedora-13-x86_64-DVD.iso"
-    fedora_dir = "pub/fedora/linux/releases/13/Fedora/x86_64/iso"
+    iso_name = "Fedora-14-x86_64-DVD.iso"
+    fedora_dir = "pub/fedora/linux/releases/14/Fedora/x86_64/iso"
     url = os.path.join("http://download.fedoraproject.org/", fedora_dir,
                        iso_name)
-    hash = "65c7f1aad3feb888ae3daadaf45d4a2a32b8773a"
+    hash = "38a4078011bac74493db7ecc53c9d9fbc96dbbd5"
     destination = os.path.join(base_dir, 'isos', 'linux')
     check_iso(url, destination, hash)
 
diff --git a/client/tests/kvm/installer.py b/client/tests/kvm/installer.py
new file mode 100644
index 0000000..30ee657
--- /dev/null
+++ b/client/tests/kvm/installer.py
@@ -0,0 +1,781 @@
+import time, os, sys, urllib, re, signal, logging, datetime, glob, ConfigParser
+import shutil
+from autotest_lib.client.bin import utils, test, os_dep
+from autotest_lib.client.common_lib import error
+import kvm_utils
+
+
+def check_configure_options(script_path):
+    """
+    Return the list of available options (flags) of a given kvm configure build
+    script.
+
+    @param script: Path to the configure script
+    """
+    abspath = os.path.abspath(script_path)
+    help_raw = utils.system_output('%s --help' % abspath, ignore_status=True)
+    help_output = help_raw.split("\n")
+    option_list = []
+    for line in help_output:
+        cleaned_line = line.lstrip()
+        if cleaned_line.startswith("--"):
+            option = cleaned_line.split()[0]
+            option = option.split("=")[0]
+            option_list.append(option)
+
+    return option_list
+
+
+def kill_qemu_processes():
+    """
+    Kills all qemu processes, also kills all processes holding /dev/kvm down.
+    """
+    logging.debug("Killing any qemu processes that might be left behind")
+    utils.system("pkill qemu", ignore_status=True)
+    # Let's double check to see if some other process is holding /dev/kvm
+    if os.path.isfile("/dev/kvm"):
+        utils.system("fuser -k /dev/kvm", ignore_status=True)
+
+
+def cpu_vendor():
+    vendor = "intel"
+    if os.system("grep vmx /proc/cpuinfo 1>/dev/null") != 0:
+        vendor = "amd"
+    logging.debug("Detected CPU vendor as '%s'" %(vendor))
+    return vendor
+
+
+def _unload_kvm_modules(mod_list):
+    logging.info("Unloading previously loaded KVM modules")
+    for module in reversed(mod_list):
+        utils.unload_module(module)
+
+
+def _load_kvm_modules(mod_list, module_dir=None, load_stock=False):
+    """
+    Just load the KVM modules, without killing Qemu or unloading previous
+    modules.
+
+    Load modules present on any sub directory of module_dir. Function will walk
+    through module_dir until it finds the modules.
+
+    @param module_dir: Directory where the KVM modules are located.
+    @param load_stock: Whether we are going to load system kernel modules.
+    @param extra_modules: List of extra modules to load.
+    """
+    if module_dir:
+        logging.info("Loading the built KVM modules...")
+        kvm_module_path = None
+        kvm_vendor_module_path = None
+        abort = False
+
+        list_modules = ['%s.ko' % (m) for m in mod_list]
+
+        list_module_paths = []
+        for folder, subdirs, files in os.walk(module_dir):
+            for module in list_modules:
+                if module in files:
+                    module_path = os.path.join(folder, module)
+                    list_module_paths.append(module_path)
+
+        # We might need to arrange the modules in the correct order
+        # to avoid module load problems
+        list_modules_load = []
+        for module in list_modules:
+            for module_path in list_module_paths:
+                if os.path.basename(module_path) == module:
+                    list_modules_load.append(module_path)
+
+        if len(list_module_paths) != len(list_modules):
+            logging.error("KVM modules not found. If you don't want to use the "
+                          "modules built by this test, make sure the option "
+                          "load_modules: 'no' is marked on the test control "
+                          "file.")
+            raise error.TestError("The modules %s were requested to be loaded, "
+                                  "but the only modules found were %s" %
+                                  (list_modules, list_module_paths))
+
+        for module_path in list_modules_load:
+            try:
+                utils.system("insmod %s" % module_path)
+            except Exception, e:
+                raise error.TestFail("Failed to load KVM modules: %s" % e)
+
+    if load_stock:
+        logging.info("Loading current system KVM modules...")
+        for module in mod_list:
+            utils.system("modprobe %s" % module)
+
+
+def create_symlinks(test_bindir, prefix=None, bin_list=None, unittest=None):
+    """
+    Create symbolic links for the appropriate qemu and qemu-img commands on
+    the kvm test bindir.
+
+    @param test_bindir: KVM test bindir
+    @param prefix: KVM prefix path
+    @param bin_list: List of qemu binaries to link
+    @param unittest: Path to configuration file unittests.cfg
+    """
+    qemu_path = os.path.join(test_bindir, "qemu")
+    qemu_img_path = os.path.join(test_bindir, "qemu-img")
+    qemu_unittest_path = os.path.join(test_bindir, "unittests")
+    if os.path.lexists(qemu_path):
+        os.unlink(qemu_path)
+    if os.path.lexists(qemu_img_path):
+        os.unlink(qemu_img_path)
+    if unittest and os.path.lexists(qemu_unittest_path):
+        os.unlink(qemu_unittest_path)
+
+    logging.debug("Linking qemu binaries")
+
+    if bin_list:
+        for bin in bin_list:
+            if os.path.basename(bin) == 'qemu-kvm':
+                os.symlink(bin, qemu_path)
+            elif os.path.basename(bin) == 'qemu-img':
+                os.symlink(bin, qemu_img_path)
+
+    elif prefix:
+        kvm_qemu = os.path.join(prefix, "bin", "qemu-system-x86_64")
+        if not os.path.isfile(kvm_qemu):
+            raise error.TestError('Invalid qemu path')
+        kvm_qemu_img = os.path.join(prefix, "bin", "qemu-img")
+        if not os.path.isfile(kvm_qemu_img):
+            raise error.TestError('Invalid qemu-img path')
+        os.symlink(kvm_qemu, qemu_path)
+        os.symlink(kvm_qemu_img, qemu_img_path)
+
+    if unittest:
+        logging.debug("Linking unittest dir")
+        os.symlink(unittest, qemu_unittest_path)
+
+
+def install_roms(rom_dir, prefix):
+    logging.debug("Path to roms specified. Copying roms to install prefix")
+    rom_dst_dir = os.path.join(prefix, 'share', 'qemu')
+    for rom_src in glob.glob('%s/*.bin' % rom_dir):
+        rom_dst = os.path.join(rom_dst_dir, os.path.basename(rom_src))
+        logging.debug("Copying rom file %s to %s", rom_src, rom_dst)
+        shutil.copy(rom_src, rom_dst)
+
+
+def save_build(build_dir, dest_dir):
+    logging.debug('Saving the result of the build on %s', dest_dir)
+    base_name = os.path.basename(build_dir)
+    tarball_name = base_name + '.tar.bz2'
+    os.chdir(os.path.dirname(build_dir))
+    utils.system('tar -cjf %s %s' % (tarball_name, base_name))
+    shutil.move(tarball_name, os.path.join(dest_dir, tarball_name))
+
+
+class KvmInstallException(Exception):
+    pass
+
+
+class FailedKvmInstall(KvmInstallException):
+    pass
+
+
+class KvmNotInstalled(KvmInstallException):
+    pass
+
+
+class BaseInstaller(object):
+    # default value for load_stock argument
+    load_stock_modules = True
+    def __init__(self, mode=None):
+        self.install_mode = mode
+        self._full_module_list = None
+
+    def set_install_params(self, test, params):
+        self.params = params
+
+        load_modules = params.get('load_modules', 'no')
+        if not load_modules or load_modules == 'yes':
+            self.should_load_modules = True
+        elif load_modules == 'no':
+            self.should_load_modules = False
+        default_extra_modules = str(None)
+        self.extra_modules = eval(params.get("extra_modules",
+                                             default_extra_modules))
+
+        self.cpu_vendor = cpu_vendor()
+
+        self.srcdir = test.srcdir
+        if not os.path.isdir(self.srcdir):
+            os.makedirs(self.srcdir)
+
+        self.test_bindir = test.bindir
+        self.results_dir = test.resultsdir
+
+        # KVM build prefix, for the modes that do need it
+        prefix = os.path.join(test.bindir, 'build')
+        self.prefix = os.path.abspath(prefix)
+
+        # Current host kernel directory
+        default_host_kernel_source = '/lib/modules/%s/build' % os.uname()[2]
+        self.host_kernel_srcdir = params.get('host_kernel_source',
+                                             default_host_kernel_source)
+
+        # Extra parameters that can be passed to the configure script
+        self.extra_configure_options = params.get('extra_configure_options',
+                                                  None)
+
+        # Do we want to save the result of the build on test.resultsdir?
+        self.save_results = True
+        save_results = params.get('save_results', 'no')
+        if save_results == 'no':
+            self.save_results = False
+
+        self._full_module_list = list(self._module_list())
+
+
+    def full_module_list(self):
+        """Return the module list used by the installer
+
+        Used by the module_probe test, to avoid using utils.unload_module().
+        """
+        if self._full_module_list is None:
+            raise KvmNotInstalled("KVM modules not installed yet (installer: %s)" % (type(self)))
+        return self._full_module_list
+
+
+    def _module_list(self):
+        """Generate the list of modules that need to be loaded
+        """
+        yield 'kvm'
+        yield 'kvm-%s' % (self.cpu_vendor)
+        if self.extra_modules:
+            for module in self.extra_modules:
+                yield module
+
+
+    def _load_modules(self, mod_list):
+        """
+        Load the KVM modules
+
+        May be overridden by subclasses.
+        """
+        _load_kvm_modules(mod_list, load_stock=self.load_stock_modules)
+
+
+    def load_modules(self, mod_list=None):
+        if mod_list is None:
+            mod_list = self.full_module_list()
+        self._load_modules(mod_list)
+
+
+    def _unload_modules(self, mod_list=None):
+        """
+        Just unload the KVM modules, without trying to kill Qemu
+        """
+        if mod_list is None:
+            mod_list = self.full_module_list()
+        _unload_kvm_modules(mod_list)
+
+
+    def unload_modules(self, mod_list=None):
+        """
+        Kill Qemu and unload the KVM modules
+        """
+        kill_qemu_processes()
+        self._unload_modules(mod_list)
+
+
+    def reload_modules(self):
+        """
+        Reload the KVM modules after killing Qemu and unloading the current modules
+        """
+        self.unload_modules()
+        self.load_modules()
+
+
+    def reload_modules_if_needed(self):
+        if self.should_load_modules:
+            self.reload_modules()
+
+
+class YumInstaller(BaseInstaller):
+    """
+    Class that uses yum to install and remove packages.
+    """
+    load_stock_modules = True
+    def set_install_params(self, test, params):
+        super(YumInstaller, self).set_install_params(test, params)
+        # Checking if all required dependencies are available
+        os_dep.command("rpm")
+        os_dep.command("yum")
+
+        default_pkg_list = str(['qemu-kvm', 'qemu-kvm-tools'])
+        default_qemu_bin_paths = str(['/usr/bin/qemu-kvm', '/usr/bin/qemu-img'])
+        default_pkg_path_list = str(None)
+        self.pkg_list = eval(params.get("pkg_list", default_pkg_list))
+        self.pkg_path_list = eval(params.get("pkg_path_list",
+                                             default_pkg_path_list))
+        self.qemu_bin_paths = eval(params.get("qemu_bin_paths",
+                                              default_qemu_bin_paths))
+
+
+    def _clean_previous_installs(self):
+        kill_qemu_processes()
+        removable_packages = ""
+        for pkg in self.pkg_list:
+            removable_packages += " %s" % pkg
+
+        utils.system("yum remove -y %s" % removable_packages)
+
+
+    def _get_packages(self):
+        for pkg in self.pkg_path_list:
+            utils.get_file(pkg, os.path.join(self.srcdir,
+                                             os.path.basename(pkg)))
+
+
+    def _install_packages(self):
+        """
+        Install all downloaded packages.
+        """
+        os.chdir(self.srcdir)
+        utils.system("yum install --nogpgcheck -y *.rpm")
+
+
+    def install(self):
+        self._clean_previous_installs()
+        self._get_packages()
+        self._install_packages()
+        create_symlinks(test_bindir=self.test_bindir,
+                        bin_list=self.qemu_bin_paths)
+        self.reload_modules_if_needed()
+        if self.save_results:
+            save_build(self.srcdir, self.results_dir)
+
+
+class KojiInstaller(YumInstaller):
+    """
+    Class that handles installing KVM from the fedora build service, koji.
+    It uses yum to install and remove packages.
+    """
+    load_stock_modules = True
+    def set_install_params(self, test, params):
+        """
+        Gets parameters and initializes the package downloader.
+
+        @param test: kvm test object
+        @param params: Dictionary with test arguments
+        """
+        super(KojiInstaller, self).set_install_params(test, params)
+        default_koji_cmd = '/usr/bin/koji'
+        default_src_pkg = 'qemu'
+        self.src_pkg = params.get("src_pkg", default_src_pkg)
+        self.tag = params.get("koji_tag", None)
+        self.build = params.get("koji_build", None)
+        self.koji_cmd = params.get("koji_cmd", default_koji_cmd)
+
+
+    def _get_packages(self):
+        """
+        Downloads the specific arch RPMs for the specific build name.
+        """
+        downloader = kvm_utils.KojiDownloader(cmd=self.koji_cmd)
+        downloader.get(src_package=self.src_pkg, tag=self.tag,
+                            build=self.build, dst_dir=self.srcdir)
+
+
+    def install(self):
+        super(KojiInstaller, self)._clean_previous_installs()
+        self._get_packages()
+        super(KojiInstaller, self)._install_packages()
+        create_symlinks(test_bindir=self.test_bindir,
+                        bin_list=self.qemu_bin_paths)
+        self.reload_modules_if_needed()
+        if self.save_results:
+            save_build(self.srcdir, self.results_dir)
+
+
+class SourceDirInstaller(BaseInstaller):
+    """
+    Class that handles building/installing KVM directly from a tarball or
+    a single source code dir.
+    """
+    def set_install_params(self, test, params):
+        """
+        Initializes class attributes, and retrieves KVM code.
+
+        @param test: kvm test object
+        @param params: Dictionary with test arguments
+        """
+        super(SourceDirInstaller, self).set_install_params(test, params)
+
+        self.mod_install_dir = os.path.join(self.prefix, 'modules')
+        self.installed_kmods = False  # it will be set to True in case we
+                                      # installed our own modules
+
+        srcdir = params.get("srcdir", None)
+        self.path_to_roms = params.get("path_to_rom_images", None)
+
+        if self.install_mode == 'localsrc':
+            if srcdir is None:
+                raise error.TestError("Install from source directory specified"
+                                      "but no source directory provided on the"
+                                      "control file.")
+            else:
+                shutil.copytree(srcdir, self.srcdir)
+
+        if self.install_mode == 'release':
+            release_tag = params.get("release_tag")
+            release_dir = params.get("release_dir")
+            release_listing = params.get("release_listing")
+            logging.info("Installing KVM from release tarball")
+            if not release_tag:
+                release_tag = kvm_utils.get_latest_kvm_release_tag(
+                                                                release_listing)
+            tarball = os.path.join(release_dir, 'kvm', release_tag,
+                                   "kvm-%s.tar.gz" % release_tag)
+            logging.info("Retrieving release kvm-%s" % release_tag)
+            tarball = utils.unmap_url("/", tarball, "/tmp")
+
+        elif self.install_mode == 'snapshot':
+            logging.info("Installing KVM from snapshot")
+            snapshot_dir = params.get("snapshot_dir")
+            if not snapshot_dir:
+                raise error.TestError("Snapshot dir not provided")
+            snapshot_date = params.get("snapshot_date")
+            if not snapshot_date:
+                # Take yesterday's snapshot
+                d = (datetime.date.today() -
+                     datetime.timedelta(1)).strftime("%Y%m%d")
+            else:
+                d = snapshot_date
+            tarball = os.path.join(snapshot_dir, "kvm-snapshot-%s.tar.gz" % d)
+            logging.info("Retrieving kvm-snapshot-%s" % d)
+            tarball = utils.unmap_url("/", tarball, "/tmp")
+
+        elif self.install_mode == 'localtar':
+            tarball = params.get("tarball")
+            if not tarball:
+                raise error.TestError("KVM Tarball install specified but no"
+                                      " tarball provided on control file.")
+            logging.info("Installing KVM from a local tarball")
+            logging.info("Using tarball %s")
+            tarball = utils.unmap_url("/", params.get("tarball"), "/tmp")
+
+        if self.install_mode in ['release', 'snapshot', 'localtar']:
+            utils.extract_tarball_to_dir(tarball, self.srcdir)
+
+        if self.install_mode in ['release', 'snapshot', 'localtar', 'srcdir']:
+            self.repo_type = kvm_utils.check_kvm_source_dir(self.srcdir)
+            configure_script = os.path.join(self.srcdir, 'configure')
+            self.configure_options = check_configure_options(configure_script)
+
+
+    def _build(self):
+        make_jobs = utils.count_cpus()
+        os.chdir(self.srcdir)
+        # For testing purposes, it's better to build qemu binaries with
+        # debugging symbols, so we can extract more meaningful stack traces.
+        cfg = "./configure --prefix=%s" % self.prefix
+        if "--disable-strip" in self.configure_options:
+            cfg += " --disable-strip"
+        steps = [cfg, "make clean", "make -j %s" % make_jobs]
+        logging.info("Building KVM")
+        for step in steps:
+            utils.system(step)
+
+
+    def _install_kmods_old_userspace(self, userspace_path):
+        """
+        Run the module install command.
+
+        This is for the "old userspace" code, that contained a 'kernel' subdirectory
+        with the kmod build code.
+
+        The code would be much simpler if we could specify the module install
+        path as parameter to the toplevel Makefile. As we can't do that and
+        the module install code doesn't use --prefix, we have to call
+        'make -C kernel install' directly, setting the module directory
+        parameters.
+
+        If the userspace tree doens't have a 'kernel' subdirectory, the
+        module install step will be skipped.
+
+        @param userspace_path: the path the kvm-userspace directory
+        """
+        kdir = os.path.join(userspace_path, 'kernel')
+        if os.path.isdir(kdir):
+            os.chdir(kdir)
+            # INSTALLDIR is the target dir for the modules
+            # ORIGMODDIR is the dir where the old modules will be removed. we
+            #            don't want to mess with the system modules, so set it
+            #            to a non-existing directory
+            utils.system('make install INSTALLDIR=%s ORIGMODDIR=/tmp/no-old-modules' % (self.mod_install_dir))
+            self.installed_kmods = True
+
+
+    def _install_kmods(self, kmod_path):
+        """Run the module install command for the kmod-kvm repository
+
+        @param kmod_path: the path to the kmod-kvm.git working copy
+        """
+        os.chdir(kmod_path)
+        utils.system('make modules_install DESTDIR=%s' % (self.mod_install_dir))
+        self.installed_kmods = True
+
+
+    def _install(self):
+        os.chdir(self.srcdir)
+        logging.info("Installing KVM userspace")
+        if self.repo_type == 1:
+            utils.system("make -C qemu install")
+            self._install_kmods_old_userspace(self.srcdir)
+        elif self.repo_type == 2:
+            utils.system("make install")
+        if self.path_to_roms:
+            install_roms(self.path_to_roms, self.prefix)
+        create_symlinks(self.test_bindir, self.prefix)
+
+
+    def _load_modules(self, mod_list):
+        # load the installed KVM modules in case we installed them
+        # ourselves. Otherwise, just load the system modules.
+        if self.installed_kmods:
+            logging.info("Loading installed KVM modules")
+            _load_kvm_modules(mod_list, module_dir=self.mod_install_dir)
+        else:
+            logging.info("Loading stock KVM modules")
+            _load_kvm_modules(mod_list, load_stock=True)
+
+
+    def install(self):
+        self._build()
+        self._install()
+        self.reload_modules_if_needed()
+        if self.save_results:
+            save_build(self.srcdir, self.results_dir)
+
+
+class GitInstaller(SourceDirInstaller):
+    def _pull_code(self):
+        """
+        Retrieves code from git repositories.
+        """
+        params = self.params
+
+        kernel_repo = params.get("git_repo")
+        user_repo = params.get("user_git_repo")
+        kmod_repo = params.get("kmod_repo")
+        test_repo = params.get("test_git_repo")
+
+        kernel_branch = params.get("kernel_branch", "master")
+        user_branch = params.get("user_branch", "master")
+        kmod_branch = params.get("kmod_branch", "master")
+        test_branch = params.get("test_branch", "master")
+
+        kernel_lbranch = params.get("kernel_lbranch", "master")
+        user_lbranch = params.get("user_lbranch", "master")
+        kmod_lbranch = params.get("kmod_lbranch", "master")
+        test_lbranch = params.get("test_lbranch", "master")
+
+        kernel_commit = params.get("kernel_commit", None)
+        user_commit = params.get("user_commit", None)
+        kmod_commit = params.get("kmod_commit", None)
+        test_commit = params.get("test_commit", None)
+
+        kernel_patches = eval(params.get("kernel_patches", "[]"))
+        user_patches = eval(params.get("user_patches", "[]"))
+        kmod_patches = eval(params.get("user_patches", "[]"))
+
+        if not user_repo:
+            message = "KVM user git repository path not specified"
+            logging.error(message)
+            raise error.TestError(message)
+
+        userspace_srcdir = os.path.join(self.srcdir, "kvm_userspace")
+        kvm_utils.get_git_branch(user_repo, user_branch, userspace_srcdir,
+                                 user_commit, user_lbranch)
+        self.userspace_srcdir = userspace_srcdir
+
+        if user_patches:
+            os.chdir(self.userspace_srcdir)
+            for patch in user_patches:
+                utils.get_file(patch, os.path.join(self.userspace_srcdir,
+                                                   os.path.basename(patch)))
+                utils.system('patch -p1 %s' % os.path.basename(patch))
+
+        if test_repo:
+            test_srcdir = os.path.join(self.srcdir, "kvm-unit-tests")
+            kvm_utils.get_git_branch(test_repo, test_branch, test_srcdir,
+                                     test_commit, test_lbranch)
+            unittest_cfg = os.path.join(test_srcdir, 'x86',
+                                        'unittests.cfg')
+            self.test_srcdir = test_srcdir
+        else:
+            unittest_cfg = os.path.join(userspace_srcdir, 'kvm', 'test', 'x86',
+                                        'unittests.cfg')
+
+        self.unittest_cfg = None
+        if os.path.isfile(unittest_cfg):
+            self.unittest_cfg = unittest_cfg
+
+        if kernel_repo:
+            kernel_srcdir = os.path.join(self.srcdir, "kvm")
+            kvm_utils.get_git_branch(kernel_repo, kernel_branch, kernel_srcdir,
+                                     kernel_commit, kernel_lbranch)
+            self.kernel_srcdir = kernel_srcdir
+            if kernel_patches:
+                os.chdir(self.kernel_srcdir)
+                for patch in kernel_patches:
+                    utils.get_file(patch, os.path.join(self.userspace_srcdir,
+                                                       os.path.basename(patch)))
+                    utils.system('patch -p1 %s' % os.path.basename(patch))
+        else:
+            self.kernel_srcdir = None
+
+        if kmod_repo:
+            kmod_srcdir = os.path.join (self.srcdir, "kvm_kmod")
+            kvm_utils.get_git_branch(kmod_repo, kmod_branch, kmod_srcdir,
+                                     kmod_commit, kmod_lbranch)
+            self.kmod_srcdir = kmod_srcdir
+            if kmod_patches:
+                os.chdir(self.kmod_srcdir)
+                for patch in kmod_patches:
+                    utils.get_file(patch, os.path.join(self.userspace_srcdir,
+                                                       os.path.basename(patch)))
+                    utils.system('patch -p1 %s' % os.path.basename(patch))
+        else:
+            self.kmod_srcdir = None
+
+        configure_script = os.path.join(self.userspace_srcdir, 'configure')
+        self.configure_options = check_configure_options(configure_script)
+
+
+    def _build(self):
+        make_jobs = utils.count_cpus()
+        cfg = './configure'
+        if self.kmod_srcdir:
+            logging.info('Building KVM modules')
+            os.chdir(self.kmod_srcdir)
+            module_build_steps = [cfg,
+                                  'make clean',
+                                  'make sync LINUX=%s' % self.kernel_srcdir,
+                                  'make']
+        elif self.kernel_srcdir:
+            logging.info('Building KVM modules')
+            os.chdir(self.userspace_srcdir)
+            cfg += ' --kerneldir=%s' % self.host_kernel_srcdir
+            module_build_steps = [cfg,
+                            'make clean',
+                            'make -C kernel LINUX=%s sync' % self.kernel_srcdir]
+        else:
+            module_build_steps = []
+
+        for step in module_build_steps:
+            utils.run(step)
+
+        logging.info('Building KVM userspace code')
+        os.chdir(self.userspace_srcdir)
+        cfg += ' --prefix=%s' % self.prefix
+        if "--disable-strip" in self.configure_options:
+            cfg += ' --disable-strip'
+        if self.extra_configure_options:
+            cfg += ' %s' % self.extra_configure_options
+        utils.system(cfg)
+        utils.system('make clean')
+        utils.system('make -j %s' % make_jobs)
+
+        self.unittest_prefix = None
+        if self.unittest_cfg:
+            os.chdir(os.path.dirname(os.path.dirname(self.unittest_cfg)))
+            utils.system('./configure --prefix=%s' % self.prefix)
+            utils.system('make')
+            utils.system('make install')
+            self.unittest_prefix = os.path.join(self.prefix, 'share', 'qemu',
+                                                'tests')
+
+
+    def _install(self):
+        if self.kernel_srcdir:
+            os.chdir(self.userspace_srcdir)
+            # the kernel module install with --prefix doesn't work, and DESTDIR
+            # wouldn't work for the userspace stuff, so we clear WANT_MODULE:
+            utils.system('make install WANT_MODULE=')
+            # and install the old-style-kmod modules manually:
+            self._install_kmods_old_userspace(self.userspace_srcdir)
+        elif self.kmod_srcdir:
+            # if we have a kmod repository, it is easier:
+            # 1) install userspace:
+            os.chdir(self.userspace_srcdir)
+            utils.system('make install')
+            # 2) install kmod:
+            self._install_kmods(self.kmod_srcdir)
+        else:
+            # if we don't have kmod sources, we just install
+            # userspace:
+            os.chdir(self.userspace_srcdir)
+            utils.system('make install')
+
+        if self.path_to_roms:
+            install_roms(self.path_to_roms, self.prefix)
+        create_symlinks(test_bindir=self.test_bindir, prefix=self.prefix,
+                        bin_list=None,
+                        unittest=self.unittest_prefix)
+
+
+    def install(self):
+        self._pull_code()
+        self._build()
+        self._install()
+        self.reload_modules_if_needed()
+        if self.save_results:
+            save_build(self.srcdir, self.results_dir)
+
+
+class PreInstalledKvm(BaseInstaller):
+    # load_modules() will use the stock modules:
+    load_stock_modules = True
+    def install(self):
+        logging.info("Expecting KVM to be already installed. Doing nothing")
+
+
+class FailedInstaller:
+    """
+    Class used to be returned instead of the installer if a installation fails
+
+    Useful to make sure no installer object is used if KVM installation fails.
+    """
+    def __init__(self, msg="KVM install failed"):
+        self._msg = msg
+
+
+    def load_modules(self):
+        """Will refuse to load the KVM modules as install failed"""
+        raise FailedKvmInstall("KVM modules not available. reason: %s" % (self._msg))
+
+
+installer_classes = {
+    'localsrc': SourceDirInstaller,
+    'localtar': SourceDirInstaller,
+    'release': SourceDirInstaller,
+    'snapshot': SourceDirInstaller,
+    'git': GitInstaller,
+    'yum': YumInstaller,
+    'koji': KojiInstaller,
+    'preinstalled': PreInstalledKvm,
+}
+
+
+def _installer_class(install_mode):
+    c = installer_classes.get(install_mode)
+    if c is None:
+        raise error.TestError('Invalid or unsupported'
+                              ' install mode: %s' % install_mode)
+    return c
+
+
+def make_installer(params):
+    # priority:
+    # - 'install_mode' param
+    # - 'mode' param
+    mode = params.get("install_mode", params.get("mode"))
+    klass = _installer_class(mode)
+    return klass(mode)
diff --git a/client/tests/kvm/kvm.py b/client/tests/kvm/kvm.py
index f656238..dbe29c3 100644
--- a/client/tests/kvm/kvm.py
+++ b/client/tests/kvm/kvm.py
@@ -21,9 +21,12 @@
             (Online doc - Getting started with KVM testing)
     """
     version = 1
-    env_version = 0
+    env_version = 1
 
     def run_once(self, params):
+        # Convert params to a Params object
+        params = kvm_utils.Params(params)
+
         # Report the parameters we've received and write them as keyvals
         logging.debug("Test parameters:")
         keys = params.keys()
@@ -40,8 +43,7 @@
         logging.info("Unpickling env. You may see some harmless error "
                      "messages.")
         env_filename = os.path.join(self.bindir, params.get("env", "env"))
-        env = kvm_utils.load_env(env_filename, self.env_version)
-        logging.debug("Contents of environment: %s", env)
+        env = kvm_utils.Env(env_filename, self.env_version)
 
         test_passed = False
 
@@ -66,13 +68,13 @@
                     try:
                         kvm_preprocessing.preprocess(self, params, env)
                     finally:
-                        kvm_utils.dump_env(env, env_filename)
+                        env.save()
                     # Run the test function
                     run_func = getattr(test_module, "run_%s" % t_type)
                     try:
                         run_func(self, params, env)
                     finally:
-                        kvm_utils.dump_env(env, env_filename)
+                        env.save()
                     test_passed = True
 
                 except Exception, e:
@@ -82,7 +84,7 @@
                         kvm_preprocessing.postprocess_on_error(
                             self, params, env)
                     finally:
-                        kvm_utils.dump_env(env, env_filename)
+                        env.save()
                     raise
 
             finally:
@@ -96,15 +98,14 @@
                         logging.error("Exception raised during "
                                       "postprocessing: %s", e)
                 finally:
-                    kvm_utils.dump_env(env, env_filename)
-                    logging.debug("Contents of environment: %s", env)
+                    env.save()
 
         except Exception, e:
             if params.get("abort_on_error") != "yes":
                 raise
             # Abort on error
             logging.info("Aborting job (%s)", e)
-            for vm in kvm_utils.env_get_all_vms(env):
+            for vm in env.get_all_vms():
                 if vm.is_dead():
                     continue
                 logging.info("VM '%s' is alive.", vm.name)
diff --git a/client/tests/kvm/kvm_preprocessing.py b/client/tests/kvm/kvm_preprocessing.py
index 1ddf99b..4daafec 100644
--- a/client/tests/kvm/kvm_preprocessing.py
+++ b/client/tests/kvm/kvm_preprocessing.py
@@ -50,16 +50,18 @@
     @param name: The name of the VM object.
     """
     logging.debug("Preprocessing VM '%s'..." % name)
-    vm = kvm_utils.env_get_vm(env, name)
+    vm = env.get_vm(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, test.bindir, env.get("address_cache"))
-        kvm_utils.env_register_vm(env, name, vm)
+        env.register_vm(name, vm)
 
     start_vm = False
 
+    migration_mode = params.get("migration_mode", None)
+
     if params.get("restart_vm") == "yes":
         logging.debug("'restart_vm' specified; (re)starting VM...")
         start_vm = True
@@ -72,11 +74,19 @@
             logging.debug("VM's qemu command differs from requested one; "
                           "restarting it...")
             start_vm = True
+    elif migration_mode is not None:
+        logging.debug("Starting VM on migration incoming mode...")
+        start_vm = True
 
     if start_vm:
-        # Start the VM (or restart it if it's already up)
-        if not vm.create(name, params, test.bindir):
-            raise error.TestError("Could not start VM")
+        if migration_mode is not None:
+            if not vm.create(name, params, test.bindir,
+                             migration_mode=migration_mode):
+                raise error.TestError("Could not start VM for migration")
+        else:
+            # Start the VM (or restart it if it's already up)
+            if not vm.create(name, params, test.bindir):
+                raise error.TestError("Could not start VM")
     else:
         # Don't start the VM, just update its params
         vm.params = params
@@ -112,7 +122,7 @@
     @param name: The name of the VM object.
     """
     logging.debug("Postprocessing VM '%s'..." % name)
-    vm = kvm_utils.env_get_vm(env, name)
+    vm = env.get_vm(name)
     if vm:
         logging.debug("VM object found in environment")
     else:
@@ -173,13 +183,11 @@
     @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)
+    for vm_name in params.objects("vms"):
+        vm_params = params.object_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)
+        for image_name in vm_params.objects("images"):
+            image_params = vm_params.object_params(image_name)
             # Call image_func for each image
             image_func(test, image_params)
         # Call vm_func for each vm
@@ -204,7 +212,7 @@
     if "tcpdump" not in env and params.get("run_tcpdump", "yes") == "yes":
         cmd = "%s -npvi any 'dst port 68'" % kvm_utils.find_command("tcpdump")
         logging.debug("Starting tcpdump (%s)...", cmd)
-        env["tcpdump"] = kvm_subprocess.kvm_tail(
+        env["tcpdump"] = kvm_subprocess.Tail(
             command=cmd,
             output_func=_update_address_cache,
             output_params=(env["address_cache"],))
@@ -216,7 +224,7 @@
                 env["tcpdump"].get_output()))
 
     # Destroy and remove VMs that are no longer needed in the environment
-    requested_vms = kvm_utils.get_sub_dict_names(params, "vms")
+    requested_vms = params.objects("vms")
     for key in env.keys():
         vm = env[key]
         if not kvm_utils.is_vm(vm):
@@ -330,7 +338,7 @@
     if params.get("kill_unresponsive_vms") == "yes":
         logging.debug("'kill_unresponsive_vms' specified; killing all VMs "
                       "that fail to respond to a remote login request...")
-        for vm in kvm_utils.env_get_all_vms(env):
+        for vm in env.get_all_vms():
             if vm.is_alive():
                 session = vm.remote_login()
                 if session:
@@ -342,7 +350,7 @@
     kvm_subprocess.kill_tail_threads()
 
     # Terminate tcpdump if no VMs are alive
-    living_vms = [vm for vm in kvm_utils.env_get_all_vms(env) if vm.is_alive()]
+    living_vms = [vm for vm in env.get_all_vms() if vm.is_alive()]
     if not living_vms and "tcpdump" in env:
         env["tcpdump"].close()
         del env["tcpdump"]
@@ -362,7 +370,7 @@
     @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"))
+    params.update(params.object_params("on_error"))
 
 
 def _update_address_cache(address_cache, line):
@@ -374,9 +382,11 @@
         matches = re.findall(r"\w*:\w*:\w*:\w*:\w*:\w*", line)
         if matches and address_cache.get("last_seen"):
             mac_address = matches[0].lower()
-            logging.debug("(address cache) Adding cache entry: %s ---> %s",
-                          mac_address, address_cache.get("last_seen"))
+            if time.time() - address_cache.get("time_%s" % mac_address, 0) > 5:
+                logging.debug("(address cache) Adding cache entry: %s ---> %s",
+                              mac_address, address_cache.get("last_seen"))
             address_cache[mac_address] = address_cache.get("last_seen")
+            address_cache["time_%s" % mac_address] = time.time()
             del address_cache["last_seen"]
 
 
@@ -398,7 +408,7 @@
     cache = {}
 
     while True:
-        for vm in kvm_utils.env_get_all_vms(env):
+        for vm in env.get_all_vms():
             if not vm.is_alive():
                 continue
             try:
diff --git a/client/tests/kvm/kvm_scheduler.py b/client/tests/kvm/kvm_scheduler.py
index f1adb39..95282e4 100644
--- a/client/tests/kvm/kvm_scheduler.py
+++ b/client/tests/kvm/kvm_scheduler.py
@@ -74,13 +74,13 @@
             # The scheduler wants this worker to free its used resources
             elif cmd[0] == "cleanup":
                 env_filename = os.path.join(self.bindir, self_dict["env"])
-                env = kvm_utils.load_env(env_filename, {})
+                env = kvm_utils.Env(env_filename)
                 for obj in env.values():
                     if isinstance(obj, kvm_vm.VM):
                         obj.destroy()
-                    elif isinstance(obj, kvm_subprocess.kvm_spawn):
+                    elif isinstance(obj, kvm_subprocess.Spawn):
                         obj.close()
-                kvm_utils.dump_env(env, env_filename)
+                env.save()
                 w.write("cleanup_done\n")
                 w.write("ready\n")
 
diff --git a/client/tests/kvm/kvm_subprocess.py b/client/tests/kvm/kvm_subprocess.py
index 8321bb3..c3e2dd7 100755
--- a/client/tests/kvm/kvm_subprocess.py
+++ b/client/tests/kvm/kvm_subprocess.py
@@ -189,6 +189,88 @@
 import common, kvm_utils
 
 
+class ExpectError(Exception):
+    def __init__(self, patterns, output):
+        Exception.__init__(self, patterns, output)
+        self.patterns = patterns
+        self.output = output
+
+    def _pattern_str(self):
+        if len(self.patterns) == 1:
+            return "pattern %r" % self.patterns[0]
+        else:
+            return "patterns %r" % self.patterns
+
+    def __str__(self):
+        return ("Unknown error occurred while looking for %s (output: %r)" %
+                (self._pattern_str(), self.output))
+
+
+class ExpectTimeoutError(ExpectError):
+    def __str__(self):
+        return ("Timeout expired while looking for %s (output: %r)" %
+                (self._pattern_str(), self.output))
+
+
+class ExpectProcessTerminatedError(ExpectError):
+    def __init__(self, patterns, status, output):
+        ExpectError.__init__(self, patterns, output)
+        self.status = status
+
+    def __str__(self):
+        return ("Process terminated while looking for %s (status: %s, output: "
+                "%r)" % (self._pattern_str(), self.status, self.output))
+
+
+class ShellError(Exception):
+    def __init__(self, cmd, output):
+        Exception.__init__(self, cmd, output)
+        self.cmd = cmd
+        self.output = output
+
+    def __str__(self):
+        return ("Could not execute shell command %r (output: %r)" %
+                (self.cmd, self.output))
+
+
+class ShellTimeoutError(ShellError):
+    def __str__(self):
+        return ("Timeout expired while waiting for shell command %r to "
+                "complete (output: %r)" % (self.cmd, self.output))
+
+
+class ShellProcessTerminatedError(ShellError):
+    # Raised when the shell process itself (e.g. ssh, netcat, telnet)
+    # terminates unexpectedly
+    def __init__(self, cmd, status, output):
+        ShellError.__init__(self, cmd, output)
+        self.status = status
+
+    def __str__(self):
+        return ("Shell process terminated while waiting for command %r to "
+                "complete (status: %s, output: %r)" %
+                (self.cmd, self.status, self.output))
+
+
+class ShellCmdError(ShellError):
+    # Raised when a command executed in a shell terminates with a nonzero
+    # exit code (status)
+    def __init__(self, cmd, status, output):
+        ShellError.__init__(self, cmd, output)
+        self.status = status
+
+    def __str__(self):
+        return ("Shell command %r failed with status %d (output: %r)" %
+                (self.cmd, self.status, self.output))
+
+
+class ShellStatusError(ShellError):
+    # Raised when the command's exit status cannot be obtained
+    def __str__(self):
+        return ("Could not get exit status of command %r (output: %r)" %
+                (self.cmd, self.output))
+
+
 def run_bg(command, termination_func=None, output_func=None, output_prefix="",
            timeout=1.0):
     """
@@ -210,12 +292,12 @@
     @param timeout: Time duration (in seconds) to wait for the subprocess to
             terminate before returning
 
-    @return: A kvm_tail object.
+    @return: A Tail object.
     """
-    process = kvm_tail(command=command,
-                       termination_func=termination_func,
-                       output_func=output_func,
-                       output_prefix=output_prefix)
+    process = Tail(command=command,
+                   termination_func=termination_func,
+                   output_func=output_func,
+                   output_prefix=output_prefix)
 
     end_time = time.time() + timeout
     while time.time() < end_time and process.is_alive():
@@ -256,7 +338,7 @@
     return (status, output)
 
 
-class kvm_spawn:
+class Spawn:
     """
     This class is used for spawning and controlling a child process.
 
@@ -268,7 +350,7 @@
     The text file can be accessed at any time using get_output().
     In addition, the server opens as many pipes as requested by the client and
     writes the output to them.
-    The pipes are requested and accessed by classes derived from kvm_spawn.
+    The pipes are requested and accessed by classes derived from Spawn.
     These pipes are referred to as "readers".
     The server also receives input from the client and sends it to the child
     process.
@@ -552,7 +634,7 @@
 
 def kill_tail_threads():
     """
-    Kill all kvm_tail threads.
+    Kill all Tail threads.
 
     After calling this function no new threads should be started.
     """
@@ -564,12 +646,12 @@
     _thread_kill_requested = False
 
 
-class kvm_tail(kvm_spawn):
+class Tail(Spawn):
     """
     This class runs a child process in the background and sends its output in
     real time, line-by-line, to a callback function.
 
-    See kvm_spawn's docstring.
+    See Spawn's docstring.
 
     This class uses a single pipe reader to read data in real time from the
     child process and report it to a given callback function.
@@ -610,10 +692,10 @@
         """
         # Add a reader and a close hook
         self._add_reader("tail")
-        self._add_close_hook(kvm_tail._join_thread)
+        self._add_close_hook(Tail._join_thread)
 
         # Init the superclass
-        kvm_spawn.__init__(self, command, id, auto_close, echo, linesep)
+        Spawn.__init__(self, command, id, auto_close, echo, linesep)
 
         # Remember some attributes
         self.termination_func = termination_func
@@ -629,11 +711,11 @@
 
 
     def __getinitargs__(self):
-        return kvm_spawn.__getinitargs__(self) + (self.termination_func,
-                                                  self.termination_params,
-                                                  self.output_func,
-                                                  self.output_params,
-                                                  self.output_prefix)
+        return Spawn.__getinitargs__(self) + (self.termination_func,
+                                              self.termination_params,
+                                              self.output_func,
+                                              self.output_params,
+                                              self.output_prefix)
 
 
     def set_termination_func(self, termination_func):
@@ -765,15 +847,15 @@
             t.join()
 
 
-class kvm_expect(kvm_tail):
+class Expect(Tail):
     """
     This class runs a child process in the background and provides expect-like
     services.
 
-    It also provides all of kvm_tail's functionality.
+    It also provides all of Tail's functionality.
     """
 
-    def __init__(self, command=None, id=None, auto_close=False, echo=False,
+    def __init__(self, command=None, id=None, auto_close=True, echo=False,
                  linesep="\n", termination_func=None, termination_params=(),
                  output_func=None, output_params=(), output_prefix=""):
         """
@@ -806,13 +888,13 @@
         self._add_reader("expect")
 
         # Init the superclass
-        kvm_tail.__init__(self, command, id, auto_close, echo, linesep,
-                          termination_func, termination_params,
-                          output_func, output_params, output_prefix)
+        Tail.__init__(self, command, id, auto_close, echo, linesep,
+                      termination_func, termination_params,
+                      output_func, output_params, output_prefix)
 
 
     def __getinitargs__(self):
-        return kvm_tail.__getinitargs__(self)
+        return Tail.__getinitargs__(self)
 
 
     def read_nonblocking(self, timeout=None):
@@ -858,7 +940,7 @@
 
 
     def read_until_output_matches(self, patterns, filter=lambda x: x,
-                                  timeout=30.0, internal_timeout=None,
+                                  timeout=60, internal_timeout=None,
                                   print_func=None):
         """
         Read using read_nonblocking until a match is found using match_patterns,
@@ -876,13 +958,14 @@
         @param internal_timeout: The timeout to pass to read_nonblocking
         @param print_func: A function to be used to print the data being read
                 (should take a string parameter)
-        @return: Tuple containing the match index (or None if no match was
-                found) and the data read so far.
+        @return: Tuple containing the match index and the data read so far
+        @raise ExpectTimeoutError: Raised if timeout expires
+        @raise ExpectProcessTerminatedError: Raised if the child process
+                terminates while waiting for output
+        @raise ExpectError: Raised if an unknown error occurs
         """
-        match = None
-        data = ""
-
         fd = self._get_fd("expect")
+        o = ""
         end_time = time.time() + timeout
         while True:
             try:
@@ -890,41 +973,31 @@
                                         max(0, end_time - time.time()))
             except (select.error, TypeError):
                 break
-            if fd not in r:
-                break
+            if not r:
+                raise ExpectTimeoutError(patterns, o)
             # Read data from child
-            newdata = self.read_nonblocking(internal_timeout)
+            data = self.read_nonblocking(internal_timeout)
+            if not data:
+                break
             # Print it if necessary
-            if print_func and newdata:
-                str = newdata
-                if str.endswith("\n"):
-                    str = str[:-1]
-                for line in str.split("\n"):
+            if print_func:
+                for line in data.splitlines():
                     print_func(line)
-            data += newdata
-
-            done = False
             # Look for patterns
-            match = self.match_patterns(filter(data), patterns)
+            o += data
+            match = self.match_patterns(filter(o), patterns)
             if match is not None:
-                done = True
-            # Check if child has died
-            if not self.is_alive():
-                logging.debug("Process terminated with status %s" %
-                              self.get_status())
-                done = True
-            # Are we done?
-            if done: break
+                return match, o
 
-        # Print some debugging info
-        if match is None and (self.is_alive() or self.get_status() != 0):
-            logging.debug("Timeout elapsed or process terminated. Output:" +
-                          kvm_utils.format_str_for_message(data.strip()))
-
-        return (match, data)
+        # Check if the child has terminated
+        if kvm_utils.wait_for(lambda: not self.is_alive(), 5, 0, 0.1):
+            raise ExpectProcessTerminatedError(patterns, self.get_status(), o)
+        else:
+            # This shouldn't happen
+            raise ExpectError(patterns, o)
 
 
-    def read_until_last_word_matches(self, patterns, timeout=30.0,
+    def read_until_last_word_matches(self, patterns, timeout=60,
                                      internal_timeout=None, print_func=None):
         """
         Read using read_nonblocking until the last word of the output matches
@@ -936,8 +1009,11 @@
         @param internal_timeout: The timeout to pass to read_nonblocking
         @param print_func: A function to be used to print the data being read
                 (should take a string parameter)
-        @return: A tuple containing the match index (or None if no match was
-                found) and the data read so far.
+        @return: A tuple containing the match index and the data read so far
+        @raise ExpectTimeoutError: Raised if timeout expires
+        @raise ExpectProcessTerminatedError: Raised if the child process
+                terminates while waiting for output
+        @raise ExpectError: Raised if an unknown error occurs
         """
         def get_last_word(str):
             if str:
@@ -950,7 +1026,7 @@
                                               print_func)
 
 
-    def read_until_last_line_matches(self, patterns, timeout=30.0,
+    def read_until_last_line_matches(self, patterns, timeout=60,
                                      internal_timeout=None, print_func=None):
         """
         Read using read_nonblocking until the last non-empty line of the output
@@ -967,6 +1043,11 @@
         @param internal_timeout: The timeout to pass to read_nonblocking
         @param print_func: A function to be used to print the data being read
                 (should take a string parameter)
+        @return: A tuple containing the match index and the data read so far
+        @raise ExpectTimeoutError: Raised if timeout expires
+        @raise ExpectProcessTerminatedError: Raised if the child process
+                terminates while waiting for output
+        @raise ExpectError: Raised if an unknown error occurs
         """
         def get_last_nonempty_line(str):
             nonempty_lines = [l for l in str.splitlines() if l.strip()]
@@ -980,12 +1061,12 @@
                                               print_func)
 
 
-class kvm_shell_session(kvm_expect):
+class ShellSession(Expect):
     """
     This class runs a child process in the background.  It it suited for
     processes that provide an interactive shell, such as SSH and Telnet.
 
-    It provides all services of kvm_expect and kvm_tail.  In addition, it
+    It provides all services of Expect and Tail.  In addition, it
     provides command running services, and a utility function to test the
     process for responsiveness.
     """
@@ -1022,12 +1103,12 @@
         @param prompt: Regular expression describing the shell's prompt line.
         @param status_test_command: Command to be used for getting the last
                 exit status of commands run inside the shell (used by
-                get_command_status_output() and friends).
+                cmd_status_output() and friends).
         """
         # Init the superclass
-        kvm_expect.__init__(self, command, id, auto_close, echo, linesep,
-                            termination_func, termination_params,
-                            output_func, output_params, output_prefix)
+        Expect.__init__(self, command, id, auto_close, echo, linesep,
+                        termination_func, termination_params,
+                        output_func, output_params, output_prefix)
 
         # Remember some attributes
         self.prompt = prompt
@@ -1035,8 +1116,8 @@
 
 
     def __getinitargs__(self):
-        return kvm_expect.__getinitargs__(self) + (self.prompt,
-                                                   self.status_test_command)
+        return Expect.__getinitargs__(self) + (self.prompt,
+                                               self.status_test_command)
 
 
     def set_prompt(self, prompt):
@@ -1085,7 +1166,7 @@
         return False
 
 
-    def read_up_to_prompt(self, timeout=30.0, internal_timeout=None,
+    def read_up_to_prompt(self, timeout=60, internal_timeout=None,
                           print_func=None):
         """
         Read using read_nonblocking until the last non-empty line of the output
@@ -1101,31 +1182,34 @@
         @param print_func: A function to be used to print the data being
                 read (should take a string parameter)
 
-        @return: A tuple containing True/False indicating whether the prompt
-                was found, and the data read so far.
+        @return: The data read so far
+        @raise ExpectTimeoutError: Raised if timeout expires
+        @raise ExpectProcessTerminatedError: Raised if the shell process
+                terminates while waiting for output
+        @raise ExpectError: Raised if an unknown error occurs
         """
-        (match, output) = self.read_until_last_line_matches([self.prompt],
-                                                            timeout,
-                                                            internal_timeout,
-                                                            print_func)
-        return (match is not None, output)
+        m, o = self.read_until_last_line_matches([self.prompt], timeout,
+                                                 internal_timeout, print_func)
+        return o
 
 
-    def get_command_status_output(self, command, timeout=30.0,
-                                  internal_timeout=None, print_func=None):
+    def cmd_output(self, cmd, timeout=60, internal_timeout=None,
+                   print_func=None):
         """
-        Send a command and return its exit status and output.
+        Send a command and return its output.
 
-        @param command: Command to send (must not contain newline characters)
-        @param timeout: The duration (in seconds) to wait until a match is
-                found
+        @param cmd: Command to send (must not contain newline characters)
+        @param timeout: The duration (in seconds) to wait for the prompt to
+                return
         @param internal_timeout: The timeout to pass to read_nonblocking
         @param print_func: A function to be used to print the data being read
                 (should take a string parameter)
 
-        @return: A tuple (status, output) where status is the exit status or
-                None if no exit status is available (e.g. timeout elapsed), and
-                output is the output of command.
+        @return: The output of cmd
+        @raise ShellTimeoutError: Raised if timeout expires
+        @raise ShellProcessTerminatedError: Raised if the shell process
+                terminates while waiting for output
+        @raise ShellError: Raised if an unknown error occurs
         """
         def remove_command_echo(str, cmd):
             if str and str.splitlines()[0] == cmd:
@@ -1135,79 +1219,132 @@
         def remove_last_nonempty_line(str):
             return "".join(str.rstrip().splitlines(True)[:-1])
 
-        # Print some debugging info
-        logging.debug("Sending command: %s" % command)
-
-        # Read everything that's waiting to be read
+        logging.debug("Sending command: %s" % cmd)
         self.read_nonblocking(timeout=0)
+        self.sendline(cmd)
+        try:
+            o = self.read_up_to_prompt(timeout, internal_timeout, print_func)
+        except ExpectError, e:
+            o = remove_command_echo(e.output, cmd)
+            if isinstance(e, ExpectTimeoutError):
+                raise ShellTimeoutError(cmd, o)
+            elif isinstance(e, ExpectProcessTerminatedError):
+                raise ShellProcessTerminatedError(cmd, e.status, o)
+            else:
+                raise ShellError(cmd, o)
 
-        # Send the command and get its output
-        self.sendline(command)
-        (match, output) = self.read_up_to_prompt(timeout, internal_timeout,
-                                                 print_func)
-        # Remove the echoed command from the output
-        output = remove_command_echo(output, command)
-        # If the prompt was not found, return the output so far
-        if not match:
-            return (None, output)
-        # Remove the final shell prompt from the output
-        output = remove_last_nonempty_line(output)
+        # Remove the echoed command and the final shell prompt
+        return remove_last_nonempty_line(remove_command_echo(o, cmd))
 
-        # Send the 'echo ...' command to get the last exit status
-        self.sendline(self.status_test_command)
-        (match, status) = self.read_up_to_prompt(10.0, internal_timeout)
-        if not match:
-            return (None, output)
-        status = remove_command_echo(status, self.status_test_command)
-        status = remove_last_nonempty_line(status)
+
+    def cmd_status_output(self, cmd, timeout=60, internal_timeout=None,
+                          print_func=None):
+        """
+        Send a command and return its exit status and output.
+
+        @param cmd: Command to send (must not contain newline characters)
+        @param timeout: The duration (in seconds) to wait for the prompt to
+                return
+        @param internal_timeout: The timeout to pass to read_nonblocking
+        @param print_func: A function to be used to print the data being read
+                (should take a string parameter)
+
+        @return: A tuple (status, output) where status is the exit status and
+                output is the output of cmd
+        @raise ShellTimeoutError: Raised if timeout expires
+        @raise ShellProcessTerminatedError: Raised if the shell process
+                terminates while waiting for output
+        @raise ShellStatusError: Raised if the exit status cannot be obtained
+        @raise ShellError: Raised if an unknown error occurs
+        """
+        o = self.cmd_output(cmd, timeout, internal_timeout, print_func)
+        try:
+            # Send the 'echo $?' (or equivalent) command to get the exit status
+            s = self.cmd_output(self.status_test_command, 10, internal_timeout)
+        except ShellError:
+            raise ShellStatusError(cmd, o)
+
         # Get the first line consisting of digits only
-        digit_lines = [l for l in status.splitlines() if l.strip().isdigit()]
-        if not digit_lines:
-            return (None, output)
-        status = int(digit_lines[0].strip())
-
-        # Print some debugging info
-        if status != 0:
-            logging.debug("Command failed; status: %d, output:%s", status,
-                          kvm_utils.format_str_for_message(output.strip()))
-
-        return (status, output)
+        digit_lines = [l for l in s.splitlines() if l.strip().isdigit()]
+        if digit_lines:
+            return int(digit_lines[0].strip()), o
+        else:
+            raise ShellStatusError(cmd, o)
 
 
-    def get_command_status(self, command, timeout=30.0, internal_timeout=None,
-                           print_func=None):
+    def cmd_status(self, cmd, timeout=60, internal_timeout=None,
+                   print_func=None):
         """
         Send a command and return its exit status.
 
-        @param command: Command to send
-        @param timeout: The duration (in seconds) to wait until a match is
-                found
+        @param cmd: Command to send (must not contain newline characters)
+        @param timeout: The duration (in seconds) to wait for the prompt to
+                return
         @param internal_timeout: The timeout to pass to read_nonblocking
         @param print_func: A function to be used to print the data being read
                 (should take a string parameter)
 
-        @return: Exit status or None if no exit status is available (e.g.
-                timeout elapsed).
+        @return: The exit status of cmd
+        @raise ShellTimeoutError: Raised if timeout expires
+        @raise ShellProcessTerminatedError: Raised if the shell process
+                terminates while waiting for output
+        @raise ShellStatusError: Raised if the exit status cannot be obtained
+        @raise ShellError: Raised if an unknown error occurs
         """
-        (status, output) = self.get_command_status_output(command, timeout,
-                                                          internal_timeout,
-                                                          print_func)
-        return status
+        s, o = self.cmd_status_output(cmd, timeout, internal_timeout,
+                                      print_func)
+        return s
 
 
-    def get_command_output(self, command, timeout=30.0, internal_timeout=None,
+    def cmd(self, cmd, timeout=60, internal_timeout=None, print_func=None):
+        """
+        Send a command and return its output. If the command's exit status is
+        nonzero, raise an exception.
+
+        @param cmd: Command to send (must not contain newline characters)
+        @param timeout: The duration (in seconds) to wait for the prompt to
+                return
+        @param internal_timeout: The timeout to pass to read_nonblocking
+        @param print_func: A function to be used to print the data being read
+                (should take a string parameter)
+
+        @return: The output of cmd
+        @raise ShellTimeoutError: Raised if timeout expires
+        @raise ShellProcessTerminatedError: Raised if the shell process
+                terminates while waiting for output
+        @raise ShellError: Raised if the exit status cannot be obtained or if
+                an unknown error occurs
+        @raise ShellStatusError: Raised if the exit status cannot be obtained
+        @raise ShellError: Raised if an unknown error occurs
+        @raise ShellCmdError: Raised if the exit status is nonzero
+        """
+        s, o = self.cmd_status_output(cmd, timeout, internal_timeout,
+                                      print_func)
+        if s != 0:
+            raise ShellCmdError(cmd, s, o)
+        return o
+
+
+    def get_command_output(self, cmd, timeout=60, internal_timeout=None,
                            print_func=None):
         """
-        Send a command and return its output.
-
-        @param command: Command to send
-        @param timeout: The duration (in seconds) to wait until a match is
-                found
-        @param internal_timeout: The timeout to pass to read_nonblocking
-        @param print_func: A function to be used to print the data being read
-                (should take a string parameter)
+        Alias for cmd_output() for backward compatibility.
         """
-        (status, output) = self.get_command_status_output(command, timeout,
-                                                          internal_timeout,
-                                                          print_func)
-        return output
+        return self.cmd_output(cmd, timeout, internal_timeout, print_func)
+
+
+    def get_command_status_output(self, cmd, timeout=60, internal_timeout=None,
+                                  print_func=None):
+        """
+        Alias for cmd_status_output() for backward compatibility.
+        """
+        return self.cmd_status_output(cmd, timeout, internal_timeout,
+                                      print_func)
+
+
+    def get_command_status(self, cmd, timeout=60, internal_timeout=None,
+                           print_func=None):
+        """
+        Alias for cmd_status() for backward compatibility.
+        """
+        return self.cmd_status(cmd, timeout, internal_timeout, print_func)
diff --git a/client/tests/kvm/kvm_test_utils.py b/client/tests/kvm/kvm_test_utils.py
index 014f265..7ed3330 100644
--- a/client/tests/kvm/kvm_test_utils.py
+++ b/client/tests/kvm/kvm_test_utils.py
@@ -21,7 +21,7 @@
 @copyright: 2008-2009 Red Hat Inc.
 """
 
-import time, os, logging, re, commands, signal
+import time, os, logging, re, commands, signal, threading
 from autotest_lib.client.common_lib import error
 from autotest_lib.client.bin import utils
 import kvm_utils, kvm_vm, kvm_subprocess, scan_results
@@ -35,7 +35,7 @@
     @param vm_name: Name of the desired VM object.
     @return: A VM object.
     """
-    vm = kvm_utils.env_get_vm(env, vm_name)
+    vm = env.get_vm(vm_name)
     if not vm:
         raise error.TestError("VM '%s' not found in environment" % vm_name)
     if not vm.is_alive():
@@ -44,21 +44,33 @@
     return vm
 
 
-def wait_for_login(vm, nic_index=0, timeout=240, start=0, step=2):
+def wait_for_login(vm, nic_index=0, timeout=240, start=0, step=2, serial=None):
     """
     Try logging into a VM repeatedly.  Stop on success or when timeout expires.
 
     @param vm: VM object.
     @param nic_index: Index of NIC to access in the VM.
     @param timeout: Time to wait before giving up.
+    @param serial: Whether to use a serial connection instead of a remote
+            (ssh, rss) one.
     @return: A shell session object.
     """
-    logging.info("Trying to log into guest '%s', timeout %ds", vm.name, timeout)
-    session = kvm_utils.wait_for(lambda: vm.remote_login(nic_index=nic_index),
-                                 timeout, start, step)
+    type = 'remote'
+    if serial:
+        type = 'serial'
+        logging.info("Trying to log into guest %s using serial connection,"
+                     " timeout %ds", vm.name, timeout)
+        session = kvm_utils.wait_for(lambda: vm.serial_login(), timeout,
+                                     start, step)
+    else:
+        logging.info("Trying to log into guest %s using remote connection,"
+                     " timeout %ds", vm.name, timeout)
+        session = kvm_utils.wait_for(lambda: vm.remote_login(
+                  nic_index=nic_index), timeout, start, step)
     if not session:
-        raise error.TestFail("Could not log into guest '%s'" % vm.name)
-    logging.info("Logged into guest '%s'" % vm.name)
+        raise error.TestFail("Could not log into guest %s using %s connection" %
+                             (vm.name, type))
+    logging.info("Logged into guest %s using %s connection", vm.name, type)
     return session
 
 
@@ -121,7 +133,8 @@
 
 
 def migrate(vm, env=None, mig_timeout=3600, mig_protocol="tcp",
-            mig_cancel=False):
+            mig_cancel=False, offline=False, stable_check=False,
+            clean=False, save_path=None, dest_host='localhost', mig_port=None):
     """
     Migrate a VM locally and re-register it in the environment.
 
@@ -131,7 +144,10 @@
     @param mig_timeout: timeout value for migration.
     @param mig_protocol: migration protocol
     @param mig_cancel: Test migrate_cancel or not when protocol is tcp.
-    @return: The post-migration VM.
+    @param dest_host: Destination host (defaults to 'localhost').
+    @param mig_port: Port that will be used for migration.
+    @return: The post-migration VM, in case of same host migration, True in
+            case of multi-host migration.
     """
     def mig_finished():
         o = vm.monitor.info("migrate")
@@ -169,38 +185,32 @@
             raise error.TestFail("Timeout expired while waiting for migration "
                                  "to finish")
 
-    dest_vm = vm.clone()
+    if dest_host == 'localhost':
+        dest_vm = vm.clone()
 
-    if mig_protocol == "exec":
-        # Exec is a little different from other migrate methods - first we
-        # ask the monitor the migration, then the vm state is dumped to a
-        # compressed file, then we start the dest vm with -incoming pointing
-        # to it
-        try:
-            exec_file = "/tmp/exec-%s.gz" % kvm_utils.generate_random_string(8)
-            exec_cmd = "gzip -c -d %s" % exec_file
-            uri = '"exec:gzip -c > %s"' % exec_file
-            vm.monitor.cmd("stop")
-            vm.monitor.migrate(uri)
-            wait_for_migration()
+    if (dest_host == 'localhost') and stable_check:
+        # Pause the dest vm after creation
+        dest_vm.params['extra_params'] = (dest_vm.params.get('extra_params','')
+                                          + ' -S')
 
-            if not dest_vm.create(migration_mode=mig_protocol,
-                                  migration_exec_cmd=exec_cmd, mac_source=vm):
-                raise error.TestError("Could not create dest VM")
-        finally:
-            logging.debug("Removing migration file %s", exec_file)
-            try:
-                os.remove(exec_file)
-            except OSError:
-                pass
-    else:
+    if dest_host == 'localhost':
         if not dest_vm.create(migration_mode=mig_protocol, mac_source=vm):
             raise error.TestError("Could not create dest VM")
+
+    try:
         try:
             if mig_protocol == "tcp":
-                uri = "tcp:localhost:%d" % dest_vm.migration_port
+                if dest_host == 'localhost':
+                    uri = "tcp:localhost:%d" % dest_vm.migration_port
+                else:
+                    uri = 'tcp:%s:%d' % (dest_host, mig_port)
             elif mig_protocol == "unix":
                 uri = "unix:%s" % dest_vm.migration_file
+            elif mig_protocol == "exec":
+                uri = '"exec:nc localhost %s"' % dest_vm.migration_port
+
+            if offline:
+                vm.monitor.cmd("stop")
             vm.monitor.migrate(uri)
 
             if mig_cancel:
@@ -210,14 +220,43 @@
                                           "Waiting for migration "
                                           "cancellation"):
                     raise error.TestFail("Failed to cancel migration")
-                dest_vm.destroy(gracefully=False)
+                if offline:
+                    vm.monitor.cmd("cont")
+                if dest_host == 'localhost':
+                    dest_vm.destroy(gracefully=False)
                 return vm
             else:
                 wait_for_migration()
+                if (dest_host == 'localhost') and stable_check:
+                    save_path = None or "/tmp"
+                    save1 = os.path.join(save_path, "src")
+                    save2 = os.path.join(save_path, "dst")
+
+                    vm.save_to_file(save1)
+                    dest_vm.save_to_file(save2)
+
+                    # Fail if we see deltas
+                    md5_save1 = utils.hash_file(save1)
+                    md5_save2 = utils.hash_file(save2)
+                    if md5_save1 != md5_save2:
+                        raise error.TestFail("Mismatch of VM state before "
+                                             "and after migration")
+
+                if (dest_host == 'localhost') and offline:
+                    dest_vm.monitor.cmd("cont")
         except:
-            dest_vm.destroy()
+            if dest_host == 'localhost':
+                dest_vm.destroy()
             raise
 
+    finally:
+        if (dest_host == 'localhost') and stable_check and clean:
+            logging.debug("Cleaning the state files")
+            if os.path.isfile(save1):
+                os.remove(save1)
+            if os.path.isfile(save2):
+                os.remove(save2)
+
     # Report migration status
     if mig_succeeded():
         logging.info("Migration finished successfully")
@@ -226,19 +265,23 @@
     else:
         raise error.TestFail("Migration ended with unknown status")
 
-    if "paused" in dest_vm.monitor.info("status"):
-        logging.debug("Destination VM is paused, resuming it...")
-        dest_vm.monitor.cmd("cont")
+    if dest_host == 'localhost':
+        if "paused" in dest_vm.monitor.info("status"):
+            logging.debug("Destination VM is paused, resuming it...")
+            dest_vm.monitor.cmd("cont")
 
     # Kill the source VM
     vm.destroy(gracefully=False)
 
     # Replace the source VM with the new cloned VM
-    if env is not None:
-        kvm_utils.env_register_vm(env, vm.name, dest_vm)
+    if (dest_host == 'localhost') and (env is not None):
+        env.register_vm(vm.name, dest_vm)
 
     # Return the new cloned VM
-    return dest_vm
+    if dest_host == 'localhost':
+        return dest_vm
+    else:
+        return vm
 
 
 def stop_windows_service(session, service, timeout=120):
@@ -252,7 +295,7 @@
     """
     end_time = time.time() + timeout
     while time.time() < end_time:
-        o = session.get_command_output("sc stop %s" % service, timeout=60)
+        o = session.cmd_output("sc stop %s" % service, timeout=60)
         # FAILED 1060 means the service isn't installed.
         # FAILED 1062 means the service hasn't been started.
         if re.search(r"\bFAILED (1060|1062)\b", o, re.I):
@@ -274,7 +317,7 @@
     """
     end_time = time.time() + timeout
     while time.time() < end_time:
-        o = session.get_command_output("sc start %s" % service, timeout=60)
+        o = session.cmd_output("sc start %s" % service, timeout=60)
         # FAILED 1060 means the service isn't installed.
         if re.search(r"\bFAILED 1060\b", o, re.I):
             raise error.TestError("Could not start service '%s' "
@@ -306,10 +349,7 @@
     """
     if len(re.findall("ntpdate|w32tm", time_command)) == 0:
         host_time = time.time()
-        session.sendline(time_command)
-        (match, s) = session.read_up_to_prompt()
-        if not match:
-            raise error.TestError("Could not get guest time")
+        s = session.cmd_output(time_command)
 
         try:
             s = re.findall(time_filter_re, s)[0]
@@ -323,9 +363,7 @@
 
         guest_time = time.mktime(time.strptime(s, time_format))
     else:
-        s , o = session.get_command_status_output(time_command)
-        if s != 0:
-            raise error.TestError("Could not get guest time")
+        o = session.cmd(time_command)
         if re.match('ntpdate', time_command):
             offset = re.findall('offset (.*) sec',o)[0]
             host_main, host_mantissa = re.findall(time_filter_re, o)[0]
@@ -403,7 +441,7 @@
         copy = False
         local_hash = utils.hash_file(local_path)
         basename = os.path.basename(local_path)
-        output = session.get_command_output("md5sum %s" % remote_path)
+        output = session.cmd_output("md5sum %s" % remote_path)
         if "such file" in output:
             remote_hash = "0"
         elif output:
@@ -435,10 +473,7 @@
         basename = os.path.basename(remote_path)
         logging.info("Extracting %s...", basename)
         e_cmd = "tar xjvf %s -C %s" % (remote_path, dest_dir)
-        s, o = session.get_command_status_output(e_cmd, timeout=120)
-        if s != 0:
-            logging.error("Uncompress output:\n%s", o)
-            raise error.TestFail("Failed to extract %s on guest" % basename)
+        session.cmd(e_cmd, timeout=120)
 
 
     def get_results():
@@ -459,7 +494,7 @@
         Get the status of the tests that were executed on the host and close
         the session where autotest was being executed.
         """
-        output = session.get_command_output("cat results/*/status")
+        output = session.cmd_output("cat results/*/status")
         try:
             results = scan_results.parse_results(output)
             # Report test results
@@ -508,26 +543,32 @@
     # Run the test
     logging.info("Running autotest control file %s on guest, timeout %ss",
                  os.path.basename(control_path), timeout)
-    session.get_command_output("cd %s" % autotest_path)
-    session.get_command_output("rm -f control.state")
-    session.get_command_output("rm -rf results/*")
-    logging.info("---------------- Test output ----------------")
-    status = session.get_command_status("bin/autotest control",
-                                        timeout=timeout,
-                                        print_func=logging.info)
-    logging.info("------------- End of test output ------------")
-    if status is None:
-        if not vm.is_alive():
+    session.cmd("cd %s" % autotest_path)
+    try:
+        session.cmd("rm -f control.state")
+        session.cmd("rm -rf results/*")
+    except kvm_subprocess.ShellError:
+        pass
+    try:
+        try:
+            logging.info("---------------- Test output ----------------")
+            session.cmd_output("bin/autotest control", timeout=timeout,
+                               print_func=logging.info)
+        finally:
+            logging.info("------------- End of test output ------------")
+    except kvm_subprocess.ShellTimeoutError:
+        if vm.is_alive():
+            get_results()
+            get_results_summary()
+            raise error.TestError("Timeout elapsed while waiting for job to "
+                                  "complete")
+        else:
             raise error.TestError("Autotest job on guest failed "
                                   "(VM terminated during job)")
-        if not session.is_alive():
-            get_results()
-            raise error.TestError("Autotest job on guest failed "
-                                  "(Remote session terminated during job)")
+    except kvm_subprocess.ShellProcessTerminatedError:
         get_results()
-        get_results_summary()
-        raise error.TestError("Timeout elapsed while waiting for job to "
-                              "complete")
+        raise error.TestError("Autotest job on guest failed "
+                              "(Remote session terminated during job)")
 
     results = get_results_summary()
     get_results()
@@ -589,21 +630,24 @@
         process.close()
         return status, output
     else:
-        session.sendline(command)
-        status, output = session.read_up_to_prompt(timeout=timeout,
-                                                   print_func=output_func)
-        if not status:
+        try:
+            output = session.cmd_output(command, timeout=timeout,
+                                        print_func=output_func)
+        except kvm_subprocess.ShellTimeoutError:
             # Send ctrl+c (SIGINT) through ssh session
             session.send("\003")
-            status, output2 = session.read_up_to_prompt(print_func=output_func)
-            output += output2
-            if not status:
+            try:
+                output2 = session.read_up_to_prompt(print_func=output_func)
+                output += output2
+            except kvm_subprocess.ExpectTimeoutError, e:
+                output += e.output
                 # We also need to use this session to query the return value
                 session.send("\003")
 
         session.sendline(session.status_test_command)
-        s2, o2 = session.read_up_to_prompt()
-        if not s2:
+        try:
+            o2 = session.read_up_to_prompt()
+        except kvm_subprocess.ExpectError:
             status = -1
         else:
             try:
@@ -670,7 +714,7 @@
     @mac_address: the macaddress of nic
     """
 
-    output = session.get_command_output("ifconfig -a")
+    output = session.cmd_output("ifconfig -a")
 
     try:
         ethname = re.findall("(\w+)\s+Link.*%s" % mac_address, output,
diff --git a/client/tests/kvm/kvm_utils.py b/client/tests/kvm/kvm_utils.py
index b849b37..d135979 100644
--- a/client/tests/kvm/kvm_utils.py
+++ b/client/tests/kvm/kvm_utils.py
@@ -5,7 +5,7 @@
 """
 
 import time, string, random, socket, os, signal, re, logging, commands, cPickle
-import fcntl, shelve, ConfigParser
+import fcntl, shelve, ConfigParser, rss_file_transfer, threading, sys, UserDict
 from autotest_lib.client.bin import utils, os_dep
 from autotest_lib.client.common_lib import error, logging_config
 import kvm_subprocess
@@ -27,74 +27,152 @@
     f.close()
 
 
-def dump_env(obj, filename):
+def is_vm(obj):
     """
-    Dump KVM test environment to a file.
+    Tests whether a given object is a VM object.
 
-    @param filename: Path to a file where the environment will be dumped to.
+    @param obj: Python object.
     """
-    file = open(filename, "w")
-    cPickle.dump(obj, file)
-    file.close()
+    return obj.__class__.__name__ == "VM"
 
 
-def load_env(filename, version):
+class Env(UserDict.IterableUserDict):
     """
-    Load KVM test environment from an env file.
-    If the version recorded in the file is lower than version, return an empty
-    env.  If some other error occurs during unpickling, return an empty env.
-
-    @param filename: Path to an env file.
+    A dict-like object containing global objects used by tests.
     """
-    default = {"version": version}
-    try:
-        file = open(filename, "r")
-        env = cPickle.load(file)
-        file.close()
-        if env.get("version", 0) < version:
-            logging.warn("Incompatible env file found. Not using it.")
-            return default
-        return env
-    # Almost any exception can be raised during unpickling, so let's catch
-    # them all
-    except Exception, e:
-        logging.warn(e)
-        return default
+    def __init__(self, filename=None, version=0):
+        """
+        Create an empty Env object or load an existing one from a file.
+
+        If the version recorded in the file is lower than version, or if some
+        error occurs during unpickling, or if filename is not supplied,
+        create an empty Env object.
+
+        @param filename: Path to an env file.
+        @param version: Required env version (int).
+        """
+        UserDict.IterableUserDict.__init__(self)
+        empty = {"version": version}
+        if filename:
+            self._filename = filename
+            try:
+                f = open(filename, "r")
+                env = cPickle.load(f)
+                f.close()
+                if env.get("version", 0) >= version:
+                    self.data = env
+                else:
+                    logging.warn("Incompatible env file found. Not using it.")
+                    self.data = empty
+            # Almost any exception can be raised during unpickling, so let's
+            # catch them all
+            except Exception, e:
+                logging.warn(e)
+                self.data = empty
+        else:
+            self.data = empty
 
 
-def get_sub_dict(dict, name):
+    def save(self, filename=None):
+        """
+        Pickle the contents of the Env object into a file.
+
+        @param filename: Filename to pickle the dict into.  If not supplied,
+                use the filename from which the dict was loaded.
+        """
+        filename = filename or self._filename
+        f = open(filename, "w")
+        cPickle.dump(self.data, f)
+        f.close()
+
+
+    def get_all_vms(self):
+        """
+        Return a list of all VM objects in this Env object.
+        """
+        return [o for o in self.values() if is_vm(o)]
+
+
+    def get_vm(self, name):
+        """
+        Return a VM object by its name.
+
+        @param name: VM name.
+        """
+        return self.get("vm__%s" % name)
+
+
+    def register_vm(self, name, vm):
+        """
+        Register a VM in this Env object.
+
+        @param name: VM name.
+        @param vm: VM object.
+        """
+        self["vm__%s" % name] = vm
+
+
+    def unregister_vm(self, name):
+        """
+        Remove a given VM.
+
+        @param name: VM name.
+        """
+        del self["vm__%s" % name]
+
+
+    def register_installer(self, installer):
+        """
+        Register a installer that was just run
+
+        The installer will be available for other tests, so that
+        information about the installed KVM modules and qemu-kvm can be used by
+        them.
+        """
+        self['last_installer'] = installer
+
+
+    def previous_installer(self):
+        """
+        Return the last installer that was registered
+        """
+        return self.get('last_installer')
+
+
+class Params(UserDict.IterableUserDict):
     """
-    Return a "sub-dict" corresponding to a specific object.
-
-    Operate on a copy of dict: for each key that ends with the suffix
-    "_" + name, strip the suffix from the key, and set the value of
-    the stripped key to that of the key. Return the resulting dict.
-
-    @param name: Suffix of the key we want to set the value.
+    A dict-like object passed to every test.
     """
-    suffix = "_" + name
-    new_dict = dict.copy()
-    for key in dict.keys():
-        if key.endswith(suffix):
-            new_key = key.split(suffix)[0]
-            new_dict[new_key] = dict[key]
-    return new_dict
+    def objects(self, key):
+        """
+        Return the names of objects defined using a given key.
+
+        @param key: The name of the key whose value lists the objects
+                (e.g. 'nics').
+        """
+        return self.get(key, "").split()
 
 
-def get_sub_dict_names(dict, keyword):
-    """
-    Return a list of "sub-dict" names that may be extracted with get_sub_dict.
+    def object_params(self, obj_name):
+        """
+        Return a dict-like object containing the parameters of an individual
+        object.
 
-    This function may be modified to change the behavior of all functions that
-    deal with multiple objects defined in dicts (e.g. VMs, images, NICs).
+        This method behaves as follows: the suffix '_' + obj_name is removed
+        from all key names that have it.  Other key names are left unchanged.
+        The values of keys with the suffix overwrite the values of their
+        suffixless versions.
 
-    @param keyword: A key in dict (e.g. "vms", "images", "nics").
-    """
-    names = dict.get(keyword)
-    if names:
-        return names.split()
-    else:
-        return []
+        @param obj_name: The name of the object (objects are listed by the
+                objects() method).
+        """
+        suffix = "_" + obj_name
+        new_dict = self.copy()
+        for key in self:
+            if key.endswith(suffix):
+                new_key = key.split(suffix)[0]
+                new_dict[new_key] = self[key]
+        return new_dict
 
 
 # Functions related to MAC/IP addresses
@@ -240,60 +318,6 @@
     return bool(regex.search(o))
 
 
-# Functions for working with the environment (a dict-like object)
-
-def is_vm(obj):
-    """
-    Tests whether a given object is a VM object.
-
-    @param obj: Python object (pretty much everything on python).
-    """
-    return obj.__class__.__name__ == "VM"
-
-
-def env_get_all_vms(env):
-    """
-    Return a list of all VM objects on a given environment.
-
-    @param env: Dictionary with environment items.
-    """
-    vms = []
-    for obj in env.values():
-        if is_vm(obj):
-            vms.append(obj)
-    return vms
-
-
-def env_get_vm(env, name):
-    """
-    Return a VM object by its name.
-
-    @param name: VM name.
-    """
-    return env.get("vm__%s" % name)
-
-
-def env_register_vm(env, name, vm):
-    """
-    Register a given VM in a given env.
-
-    @param env: Environment where we will register the VM.
-    @param name: VM name.
-    @param vm: VM object.
-    """
-    env["vm__%s" % name] = vm
-
-
-def env_unregister_vm(env, name):
-    """
-    Remove a given VM from a given env.
-
-    @param env: Environment where we will un-register the VM.
-    @param name: VM name.
-    """
-    del env["vm__%s" % name]
-
-
 # Utility functions for dealing with external processes
 
 def find_command(cmd):
@@ -421,7 +445,7 @@
     os.chdir(source_dir)
     has_qemu_dir = os.path.isdir('qemu')
     has_kvm_dir = os.path.isdir('kvm')
-    if has_qemu_dir and not has_kvm_dir:
+    if has_qemu_dir:
         logging.debug("qemu directory detected, source dir layout 1")
         return 1
     if has_kvm_dir and not has_qemu_dir:
@@ -442,7 +466,7 @@
 
     @brief: Log into a remote host (guest) using SSH or Telnet.
 
-    @param session: A kvm_expect or kvm_shell_session instance to operate on
+    @param session: An Expect or ShellSession instance to operate on
     @param username: The username to send in reply to a login prompt
     @param password: The password to send in reply to a password prompt
     @param prompt: The shell prompt that indicates a successful login
@@ -456,48 +480,52 @@
     login_prompt_count = 0
 
     while True:
-        (match, text) = session.read_until_last_line_matches(
+        try:
+            match, text = session.read_until_last_line_matches(
                 [r"[Aa]re you sure", r"[Pp]assword:\s*$", r"[Ll]ogin:\s*$",
                  r"[Cc]onnection.*closed", r"[Cc]onnection.*refused",
                  r"[Pp]lease wait", prompt],
-                 timeout=timeout, internal_timeout=0.5)
-        if match == 0:  # "Are you sure you want to continue connecting"
-            logging.debug("Got 'Are you sure...'; sending 'yes'")
-            session.sendline("yes")
-            continue
-        elif match == 1:  # "password:"
-            if password_prompt_count == 0:
-                logging.debug("Got password prompt; sending '%s'" % password)
-                session.sendline(password)
-                password_prompt_count += 1
+                timeout=timeout, internal_timeout=0.5)
+            if match == 0:  # "Are you sure you want to continue connecting"
+                logging.debug("Got 'Are you sure...'; sending 'yes'")
+                session.sendline("yes")
                 continue
-            else:
-                logging.debug("Got password prompt again")
+            elif match == 1:  # "password:"
+                if password_prompt_count == 0:
+                    logging.debug("Got password prompt; sending '%s'" % password)
+                    session.sendline(password)
+                    password_prompt_count += 1
+                    continue
+                else:
+                    logging.debug("Got password prompt again")
+                    return False
+            elif match == 2:  # "login:"
+                if login_prompt_count == 0:
+                    logging.debug("Got username prompt; sending '%s'" % username)
+                    session.sendline(username)
+                    login_prompt_count += 1
+                    continue
+                else:
+                    logging.debug("Got username prompt again")
+                    return False
+            elif match == 3:  # "Connection closed"
+                logging.debug("Got 'Connection closed'")
                 return False
-        elif match == 2:  # "login:"
-            if login_prompt_count == 0:
-                logging.debug("Got username prompt; sending '%s'" % username)
-                session.sendline(username)
-                login_prompt_count += 1
+            elif match == 4:  # "Connection refused"
+                logging.debug("Got 'Connection refused'")
+                return False
+            elif match == 5:  # "Please wait"
+                logging.debug("Got 'Please wait'")
+                timeout = 30
                 continue
-            else:
-                logging.debug("Got username prompt again")
-                return False
-        elif match == 3:  # "Connection closed"
-            logging.debug("Got 'Connection closed'")
+            elif match == 6:  # prompt
+                logging.debug("Got shell prompt -- logged in")
+                return True
+        except kvm_subprocess.ExpectTimeoutError, e:
+            logging.debug("Timeout elapsed (output so far: %r)" % e.output)
             return False
-        elif match == 4:  # "Connection refused"
-            logging.debug("Got 'Connection refused'")
-            return False
-        elif match == 5:  # "Please wait"
-            logging.debug("Got 'Please wait'")
-            timeout = 30
-            continue
-        elif match == 6:  # prompt
-            logging.debug("Got shell prompt -- logged in")
-            return session
-        else:  # match == None
-            logging.debug("Timeout elapsed or process terminated")
+        except kvm_subprocess.ExpectProcessTerminatedError, e:
+            logging.debug("Process terminated (output so far: %r)" % e.output)
             return False
 
 
@@ -510,7 +538,7 @@
 
     @brief: Transfer files using SCP, given a command line.
 
-    @param session: A kvm_expect or kvm_shell_session instance to operate on
+    @param session: An Expect or ShellSession instance to operate on
     @param password: The password to send in reply to a password prompt.
     @param transfer_timeout: The time duration (in seconds) to wait for the
             transfer to complete.
@@ -524,34 +552,33 @@
     timeout = login_timeout
 
     while True:
-        (match, text) = session.read_until_last_line_matches(
+        try:
+            match, text = session.read_until_last_line_matches(
                 [r"[Aa]re you sure", r"[Pp]assword:\s*$", r"lost connection"],
                 timeout=timeout, internal_timeout=0.5)
-        if match == 0:  # "Are you sure you want to continue connecting"
-            logging.debug("Got 'Are you sure...'; sending 'yes'")
-            session.sendline("yes")
-            continue
-        elif match == 1:  # "password:"
-            if password_prompt_count == 0:
-                logging.debug("Got password prompt; sending '%s'" % password)
-                session.sendline(password)
-                password_prompt_count += 1
-                timeout = transfer_timeout
+            if match == 0:  # "Are you sure you want to continue connecting"
+                logging.debug("Got 'Are you sure...'; sending 'yes'")
+                session.sendline("yes")
                 continue
-            else:
-                logging.debug("Got password prompt again")
+            elif match == 1:  # "password:"
+                if password_prompt_count == 0:
+                    logging.debug("Got password prompt; sending '%s'" % password)
+                    session.sendline(password)
+                    password_prompt_count += 1
+                    timeout = transfer_timeout
+                    continue
+                else:
+                    logging.debug("Got password prompt again")
+                    return False
+            elif match == 2:  # "lost connection"
+                logging.debug("Got 'lost connection'")
                 return False
-        elif match == 2:  # "lost connection"
-            logging.debug("Got 'lost connection'")
+        except kvm_subprocess.ExpectTimeoutError, e:
+            logging.debug("Timeout expired")
             return False
-        else:  # match == None
-            if session.is_alive():
-                logging.debug("Timeout expired")
-                return False
-            else:
-                status = session.get_status()
-                logging.debug("SCP process terminated with status %s", status)
-                return status == 0
+        except kvm_subprocess.ExpectProcessTerminatedError, e:
+            logging.debug("SCP process terminated with status %s", e.status)
+            return e.status == 0
 
 
 def remote_login(client, host, port, username, password, prompt, linesep="\n",
@@ -572,7 +599,7 @@
             each step of the login procedure (i.e. the "Are you sure" prompt
             or the password prompt)
 
-    @return: kvm_shell_session object on success and None on failure.
+    @return: ShellSession object on success and None on failure.
     """
     if client == "ssh":
         cmd = ("ssh -o UserKnownHostsFile=/dev/null "
@@ -587,8 +614,7 @@
         return
 
     logging.debug("Trying to login with command '%s'" % cmd)
-    session = kvm_subprocess.kvm_shell_session(cmd, linesep=linesep,
-                                               prompt=prompt)
+    session = kvm_subprocess.ShellSession(cmd, linesep=linesep, prompt=prompt)
     if _remote_login(session, username, password, prompt, timeout):
         if log_filename:
             session.set_output_func(log_line)
@@ -627,19 +653,83 @@
         output_func = None
         output_params = ()
 
-    session = kvm_subprocess.kvm_expect(command,
-                                        output_func=output_func,
-                                        output_params=output_params)
+    session = kvm_subprocess.Expect(command,
+                                    output_func=output_func,
+                                    output_params=output_params)
     try:
         return _remote_scp(session, password, transfer_timeout, login_timeout)
     finally:
         session.close()
 
 
+def copy_files_to(address, client, username, password, port, local_path,
+                  remote_path, log_filename=None, timeout=600):
+    """
+    Decide the transfer cleint and copy file to a remote host (guest).
+
+    @param client: Type of transfer client
+    @param username: Username (if required)
+    @param password: Password (if requried)
+    @param local_path: Path on the local machine where we are copying from
+    @param remote_path: Path on the remote machine where we are copying to
+    @param address: Address of remote host(guest)
+    @param log_filename: If specified, log all output to this file
+    @param timeout: The time duration (in seconds) to wait for the transfer to
+    complete.
+
+    @return: True on success and False on failure.
+    """
+
+    if not address or not port:
+        logging.debug("IP address or port unavailable")
+        return None
+
+    if client == "scp":
+        return scp_to_remote(address, port, username, password, local_path,
+                             remote_path, log_filename, timeout)
+    elif client == "rss":
+        c = rss_file_transfer.FileUploadClient(address, port)
+        c.upload(local_path, remote_path, timeout)
+        c.close()
+        return True
+
+
+def copy_files_from(address, client, username, password, port, local_path,
+                  remote_path, log_filename=None, timeout=600):
+    """
+    Decide the transfer cleint and copy file from a remote host (guest).
+
+    @param client: Type of transfer client
+    @param username: Username (if required)
+    @param password: Password (if requried)
+    @param local_path: Path on the local machine where we are copying from
+    @param remote_path: Path on the remote machine where we are copying to
+    @param address: Address of remote host(guest)
+    @param log_filename: If specified, log all output to this file
+    @param timeout: The time duration (in seconds) to wait for the transfer to
+    complete.
+
+    @return: True on success and False on failure.
+    """
+
+    if not address or not port:
+        logging.debug("IP address or port unavailable")
+        return None
+
+    if client == "scp":
+        return scp_from_remote(address, port, username, password, remote_path,
+                             local_path, log_filename, timeout)
+    elif client == "rss":
+        c = rss_file_transfer.FileDownloadClient(address, port)
+        c.download(remote_path, local_path, timeout)
+        c.close()
+        return True
+
+
 def scp_to_remote(host, port, username, password, local_path, remote_path,
                   log_filename=None, timeout=600):
     """
-    Copy files to a remote host (guest).
+    Copy files to a remote host (guest) through scp.
 
     @param host: Hostname or IP address
     @param username: Username (if required)
@@ -978,6 +1068,86 @@
     return re.sub(":", " ", commands.getoutput(cmd))
 
 
+class Thread(threading.Thread):
+    """
+    Run a function in a background thread.
+    """
+    def __init__(self, target, args=(), kwargs={}):
+        """
+        Initialize the instance.
+
+        @param target: Function to run in the thread.
+        @param args: Arguments to pass to target.
+        @param kwargs: Keyword arguments to pass to target.
+        """
+        threading.Thread.__init__(self)
+        self._target = target
+        self._args = args
+        self._kwargs = kwargs
+
+
+    def run(self):
+        """
+        Run target (passed to the constructor).  No point in calling this
+        function directly.  Call start() to make this function run in a new
+        thread.
+        """
+        self._e = None
+        self._retval = None
+        try:
+            try:
+                self._retval = self._target(*self._args, **self._kwargs)
+            except:
+                self._e = sys.exc_info()
+                raise
+        finally:
+            # Avoid circular references (start() may be called only once so
+            # it's OK to delete these)
+            del self._target, self._args, self._kwargs
+
+
+    def join(self, timeout=None):
+        """
+        Join the thread.  If target raised an exception, re-raise it.
+        Otherwise, return the value returned by target.
+
+        @param timeout: Timeout value to pass to threading.Thread.join().
+        """
+        threading.Thread.join(self, timeout)
+        try:
+            if self._e:
+                raise self._e[0], self._e[1], self._e[2]
+            else:
+                return self._retval
+        finally:
+            # Avoid circular references (join() may be called multiple times
+            # so we can't delete these)
+            self._e = None
+            self._retval = None
+
+
+def parallel(targets):
+    """
+    Run multiple functions in parallel.
+
+    @param targets: A sequence of tuples or functions.  If it's a sequence of
+            tuples, each tuple will be interpreted as (target, args, kwargs) or
+            (target, args) or (target,) depending on its length.  If it's a
+            sequence of functions, the functions will be called without
+            arguments.
+    @return: A list of the values returned by the functions called.
+    """
+    threads = []
+    for target in targets:
+        if isinstance(target, tuple) or isinstance(target, list):
+            t = Thread(*target)
+        else:
+            t = Thread(target)
+        threads.append(t)
+        t.start()
+    return [t.join() for t in threads]
+
+
 class KvmLoggingConfig(logging_config.LoggingConfig):
     """
     Used with the sole purpose of providing convenient logging setup
@@ -1347,7 +1517,7 @@
                              "provide an appropriate tag or build name.")
 
         if not build:
-            builds = self.session.listTagged(tag, latest=True,
+            builds = self.session.listTagged(tag, latest=True, inherit=True,
                                              package=src_package)
             if not builds:
                 raise ValueError("Tag %s has no builds of %s" % (tag,
@@ -1390,3 +1560,58 @@
                 rpm_paths.append(r)
 
         return rpm_paths
+
+
+def umount(src, mount_point, type):
+    """
+    Umount the src mounted in mount_point.
+
+    @src: mount source
+    @mount_point: mount point
+    @type: file system type
+    """
+
+    mount_string = "%s %s %s" % (src, mount_point, type)
+    if mount_string in file("/etc/mtab").read():
+        umount_cmd = "umount %s" % mount_point
+        try:
+            utils.system(umount_cmd)
+            return True
+        except error.CmdError:
+            return False
+    else:
+        logging.debug("%s is not mounted under %s" % (src, mount_point))
+        return True
+
+
+def mount(src, mount_point, type, perm="rw"):
+    """
+    Mount the src into mount_point of the host.
+
+    @src: mount source
+    @mount_point: mount point
+    @type: file system type
+    @perm: mount premission
+    """
+    umount(src, mount_point, type)
+    mount_string = "%s %s %s %s" % (src, mount_point, type, perm)
+
+    if mount_string in file("/etc/mtab").read():
+        logging.debug("%s is already mounted in %s with %s" %
+                      (src, mount_point, perm))
+        return True
+
+    mount_cmd = "mount -t %s %s %s -o %s" % (type, src, mount_point, perm)
+    try:
+        utils.system(mount_cmd)
+    except error.CmdError:
+        return False
+
+    logging.debug("Verify the mount through /etc/mtab")
+    if mount_string in file("/etc/mtab").read():
+        logging.debug("%s is successfully mounted" % src)
+        return True
+    else:
+        logging.error("Can't find mounted NFS share - /etc/mtab contents \n%s" %
+                      file("/etc/mtab").read())
+        return False
diff --git a/client/tests/kvm/kvm_vm.py b/client/tests/kvm/kvm_vm.py
index a860437..f6f1684 100755
--- a/client/tests/kvm/kvm_vm.py
+++ b/client/tests/kvm/kvm_vm.py
@@ -24,6 +24,8 @@
     """
     image_name = params.get("image_name", "image")
     image_format = params.get("image_format", "qcow2")
+    if params.get("image_raw_device") == "yes":
+        return image_name
     image_filename = "%s.%s" % (image_name, image_format)
     image_filename = kvm_utils.get_path(root_dir, image_filename)
     return image_filename
@@ -95,7 +97,7 @@
     This class handles all basic VM operations.
     """
 
-    def __init__(self, name, params, root_dir, address_cache):
+    def __init__(self, name, params, root_dir, address_cache, state=None):
         """
         Initialize the object and set a few attributes.
 
@@ -104,30 +106,35 @@
                 (see method make_qemu_command for a full description)
         @param root_dir: Base directory for relative filenames
         @param address_cache: A dict that maps MAC addresses to IP addresses
+        @param state: If provided, use this as self.__dict__
         """
-        self.process = None
-        self.serial_console = None
-        self.redirs = {}
-        self.vnc_port = 5900
-        self.monitors = []
-        self.pci_assignable = None
-        self.netdev_id = []
-        self.uuid = None
+        if state:
+            self.__dict__ = state
+        else:
+            self.process = None
+            self.serial_console = None
+            self.redirs = {}
+            self.vnc_port = 5900
+            self.monitors = []
+            self.pci_assignable = None
+            self.netdev_id = []
+            self.uuid = None
+
+            # Find a unique identifier for this VM
+            while True:
+                self.instance = (time.strftime("%Y%m%d-%H%M%S-") +
+                                 kvm_utils.generate_random_string(4))
+                if not glob.glob("/tmp/*%s" % self.instance):
+                    break
 
         self.name = name
         self.params = params
         self.root_dir = root_dir
         self.address_cache = address_cache
 
-        # Find a unique identifier for this VM
-        while True:
-            self.instance = (time.strftime("%Y%m%d-%H%M%S-") +
-                             kvm_utils.generate_random_string(4))
-            if not glob.glob("/tmp/*%s" % self.instance):
-                break
 
-
-    def clone(self, name=None, params=None, root_dir=None, address_cache=None):
+    def clone(self, name=None, params=None, root_dir=None, address_cache=None,
+              copy_state=False):
         """
         Return a clone of the VM object with optionally modified parameters.
         The clone is initially not alive and needs to be started using create().
@@ -138,6 +145,8 @@
         @param params: Optional new VM creation parameters
         @param root_dir: Optional new base directory for relative filenames
         @param address_cache: A dict that maps MAC addresses to IP addresses
+        @param copy_state: If True, copy the original VM's state to the clone.
+                Mainly useful for make_qemu_command().
         """
         if name is None:
             name = self.name
@@ -147,7 +156,11 @@
             root_dir = self.root_dir
         if address_cache is None:
             address_cache = self.address_cache
-        return VM(name, params, root_dir, address_cache)
+        if copy_state:
+            state = self.__dict__.copy()
+        else:
+            state = None
+        return VM(name, params, root_dir, address_cache, state)
 
 
     def make_qemu_command(self, name=None, params=None, root_dir=None):
@@ -236,25 +249,22 @@
 
         def add_nic(help, vlan, model=None, mac=None, netdev_id=None,
                     nic_extra_params=None):
+            if has_option(help, "netdev"):
+                netdev_vlan_str = ",netdev=%s" % netdev_id
+            else:
+                netdev_vlan_str = ",vlan=%d" % vlan
             if has_option(help, "device"):
-                if model == "virtio":
-                    model="virtio-net-pci"
                 if not model:
-                    model= "rtl8139"
-                cmd = " -device %s" % model
+                    model = "rtl8139"
+                elif model == "virtio":
+                    model = "virtio-net-pci"
+                cmd = " -device %s" % model + netdev_vlan_str
                 if mac:
-                    cmd += ",mac=%s" % mac
-                if has_option(help, "netdev"):
-                    cmd += ",netdev=%s" % netdev_id
-                else:
-                    cmd += "vlan=%d,"  % vlan
+                    cmd += ",mac='%s'" % mac
                 if nic_extra_params:
                     cmd += ",%s" % nic_extra_params
             else:
-                if has_option(help, "netdev"):
-                    cmd = " -net nic,netdev=%s" % netdev_id
-                else:
-                    cmd = " -net nic,vlan=%d" % vlan
+                cmd = " -net nic" + netdev_vlan_str
                 if model:
                     cmd += ",model=%s" % model
                 if mac:
@@ -263,11 +273,11 @@
 
         def add_net(help, vlan, mode, ifname=None, script=None,
                     downscript=None, tftp=None, bootfile=None, hostfwd=[],
-                    netdev_id=None, vhost=False):
+                    netdev_id=None, netdev_extra_params=None):
             if has_option(help, "netdev"):
                 cmd = " -netdev %s,id=%s" % (mode, netdev_id)
-                if vhost:
-                    cmd +=",vhost=on"
+                if netdev_extra_params:
+                    cmd += ",%s" % netdev_extra_params
             else:
                 cmd = " -net %s,vlan=%d" % (mode, vlan)
             if mode == "tap":
@@ -351,6 +361,9 @@
         if params is None: params = self.params
         if root_dir is None: root_dir = self.root_dir
 
+        # Clone this VM using the new params
+        vm = self.clone(name, params, root_dir, copy_state=True)
+
         qemu_binary = kvm_utils.get_path(root_dir, params.get("qemu_binary",
                                                               "qemu"))
         # Get the output of 'qemu -help' (log a message in case this call never
@@ -368,19 +381,19 @@
         # Add the VM's name
         qemu_cmd += add_name(help, 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)
+        for monitor_name in params.objects("monitors"):
+            monitor_params = params.object_params(monitor_name)
+            monitor_filename = vm.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)
 
         # Add serial console redirection
-        qemu_cmd += add_serial(help, self.get_serial_console_filename())
+        qemu_cmd += add_serial(help, vm.get_serial_console_filename())
 
-        for image_name in kvm_utils.get_sub_dict_names(params, "images"):
-            image_params = kvm_utils.get_sub_dict(params, image_name)
+        for image_name in params.objects("images"):
+            image_params = params.object_params(image_name)
             if image_params.get("boot_drive") == "no":
                 continue
             qemu_cmd += add_drive(help,
@@ -394,20 +407,23 @@
                                   image_params.get("image_boot") == "yes")
 
         redirs = []
-        for redir_name in kvm_utils.get_sub_dict_names(params, "redirs"):
-            redir_params = kvm_utils.get_sub_dict(params, redir_name)
+        for redir_name in params.objects("redirs"):
+            redir_params = params.object_params(redir_name)
             guest_port = int(redir_params.get("guest_port"))
-            host_port = self.redirs.get(guest_port)
+            host_port = vm.redirs.get(guest_port)
             redirs += [(host_port, guest_port)]
 
         vlan = 0
-        for nic_name in kvm_utils.get_sub_dict_names(params, "nics"):
-            nic_params = kvm_utils.get_sub_dict(params, nic_name)
+        for nic_name in params.objects("nics"):
+            nic_params = params.object_params(nic_name)
+            try:
+                netdev_id = vm.netdev_id[vlan]
+            except IndexError:
+                netdev_id = None
             # Handle the '-net nic' part
-            mac = self.get_mac_address(vlan)
+            mac = vm.get_mac_address(vlan)
             qemu_cmd += add_nic(help, vlan, nic_params.get("nic_model"), mac,
-                                self.netdev_id[vlan],
-                                nic_params.get("nic_extra_params"))
+                                netdev_id, nic_params.get("nic_extra_params"))
             # Handle the '-net tap' or '-net user' part
             script = nic_params.get("nic_script")
             downscript = nic_params.get("nic_downscript")
@@ -419,11 +435,10 @@
             if tftp:
                 tftp = kvm_utils.get_path(root_dir, tftp)
             qemu_cmd += add_net(help, vlan, nic_params.get("nic_mode", "user"),
-                                self.get_ifname(vlan),
+                                vm.get_ifname(vlan),
                                 script, downscript, tftp,
-                                nic_params.get("bootp"), redirs,
-                                self.netdev_id[vlan],
-                                nic_params.get("vhost")=="yes")
+                                nic_params.get("bootp"), redirs, netdev_id,
+                                nic_params.get("netdev_extra_params"))
             # Proceed to next NIC
             vlan += 1
 
@@ -435,9 +450,8 @@
         if smp:
             qemu_cmd += add_smp(help, smp)
 
-        cdroms = kvm_utils.get_sub_dict_names(params, "cdroms")
-        for cdrom in cdroms:
-            cdrom_params = kvm_utils.get_sub_dict(params, cdrom)
+        for cdrom in params.objects("cdroms"):
+            cdrom_params = params.object_params(cdrom)
             iso = cdrom_params.get("cdrom")
             if iso:
                 qemu_cmd += add_cdrom(help, kvm_utils.get_path(root_dir, iso),
@@ -477,27 +491,27 @@
             qemu_cmd += add_tcp_redir(help, host_port, guest_port)
 
         if params.get("display") == "vnc":
-            qemu_cmd += add_vnc(help, self.vnc_port)
+            qemu_cmd += add_vnc(help, vm.vnc_port)
         elif params.get("display") == "sdl":
             qemu_cmd += add_sdl(help)
         elif params.get("display") == "nographic":
             qemu_cmd += add_nographic(help)
 
         if params.get("uuid") == "random":
-            qemu_cmd += add_uuid(help, self.uuid)
+            qemu_cmd += add_uuid(help, vm.uuid)
         elif params.get("uuid"):
             qemu_cmd += add_uuid(help, params.get("uuid"))
 
         if params.get("testdev") == "yes":
-            qemu_cmd += add_testdev(help, self.get_testlog_filename())
+            qemu_cmd += add_testdev(help, vm.get_testlog_filename())
 
         if params.get("disable_hpet") == "yes":
             qemu_cmd += add_no_hpet(help)
 
         # If the PCI assignment step went OK, add each one of the PCI assigned
         # devices to the qemu command line.
-        if self.pci_assignable:
-            for pci_id in self.pa_pci_ids:
+        if vm.pci_assignable:
+            for pci_id in vm.pa_pci_ids:
                 qemu_cmd += add_pcidevice(help, pci_id)
 
         extra_params = params.get("extra_params")
@@ -508,7 +522,7 @@
 
 
     def create(self, name=None, params=None, root_dir=None, timeout=5.0,
-               migration_mode=None, migration_exec_cmd=None, mac_source=None):
+               migration_mode=None, mac_source=None):
         """
         Start the VM by running a qemu command.
         All parameters are optional. If name, params or root_dir are not
@@ -536,38 +550,40 @@
         params = self.params
         root_dir = self.root_dir
 
-        # Verify the md5sum of the ISO image
-        iso = params.get("cdrom")
-        if iso:
-            iso = kvm_utils.get_path(root_dir, iso)
-            if not os.path.exists(iso):
-                logging.error("ISO file not found: %s" % iso)
-                return False
-            compare = False
-            if params.get("md5sum_1m"):
-                logging.debug("Comparing expected MD5 sum with MD5 sum of "
-                              "first MB of ISO file...")
-                actual_hash = utils.hash_file(iso, 1048576, method="md5")
-                expected_hash = params.get("md5sum_1m")
-                compare = True
-            elif params.get("md5sum"):
-                logging.debug("Comparing expected MD5 sum with MD5 sum of ISO "
-                              "file...")
-                actual_hash = utils.hash_file(iso, method="md5")
-                expected_hash = params.get("md5sum")
-                compare = True
-            elif params.get("sha1sum"):
-                logging.debug("Comparing expected SHA1 sum with SHA1 sum of "
-                              "ISO file...")
-                actual_hash = utils.hash_file(iso, method="sha1")
-                expected_hash = params.get("sha1sum")
-                compare = True
-            if compare:
-                if actual_hash == expected_hash:
-                    logging.debug("Hashes match")
-                else:
-                    logging.error("Actual hash differs from expected one")
+        # Verify the md5sum of the ISO images
+        for cdrom in params.objects("cdroms"):
+            cdrom_params = params.object_params(cdrom)
+            iso = cdrom_params.get("cdrom")
+            if iso:
+                iso = kvm_utils.get_path(root_dir, iso)
+                if not os.path.exists(iso):
+                    logging.error("ISO file not found: %s" % iso)
                     return False
+                compare = False
+                if cdrom_params.get("md5sum_1m"):
+                    logging.debug("Comparing expected MD5 sum with MD5 sum of "
+                                  "first MB of ISO file...")
+                    actual_hash = utils.hash_file(iso, 1048576, method="md5")
+                    expected_hash = cdrom_params.get("md5sum_1m")
+                    compare = True
+                elif cdrom_params.get("md5sum"):
+                    logging.debug("Comparing expected MD5 sum with MD5 sum of "
+                                  "ISO file...")
+                    actual_hash = utils.hash_file(iso, method="md5")
+                    expected_hash = cdrom_params.get("md5sum")
+                    compare = True
+                elif cdrom_params.get("sha1sum"):
+                    logging.debug("Comparing expected SHA1 sum with SHA1 sum "
+                                  "of ISO file...")
+                    actual_hash = utils.hash_file(iso, method="sha1")
+                    expected_hash = cdrom_params.get("sha1sum")
+                    compare = True
+                if compare:
+                    if actual_hash == expected_hash:
+                        logging.debug("Hashes match")
+                    else:
+                        logging.error("Actual hash differs from expected one")
+                        return False
 
         # Make sure the following code is not executed by more than one thread
         # at the same time
@@ -576,15 +592,17 @@
 
         try:
             # Handle port redirections
-            redir_names = kvm_utils.get_sub_dict_names(params, "redirs")
+            redir_names = params.objects("redirs")
             host_ports = kvm_utils.find_free_ports(5000, 6000, len(redir_names))
             self.redirs = {}
             for i in range(len(redir_names)):
-                redir_params = kvm_utils.get_sub_dict(params, redir_names[i])
+                redir_params = params.object_params(redir_names[i])
                 guest_port = int(redir_params.get("guest_port"))
                 self.redirs[guest_port] = host_ports[i]
 
-            for nic in kvm_utils.get_sub_dict_names(params, "nics"):
+            # Generate netdev IDs for all NICs
+            self.netdev_id = []
+            for nic in params.objects("nics"):
                 self.netdev_id.append(kvm_utils.generate_random_id())
 
             # Find available VNC port, if needed
@@ -598,13 +616,19 @@
                 f.close()
 
             # Generate or copy MAC addresses for all NICs
-            num_nics = len(kvm_utils.get_sub_dict_names(params, "nics"))
+            num_nics = len(params.objects("nics"))
             for vlan in range(num_nics):
-                mac = mac_source and mac_source.get_mac_address(vlan)
-                if mac:
+                nic_name = params.objects("nics")[vlan]
+                nic_params = params.object_params(nic_name)
+                if nic_params.get("nic_mac", None):
+                    mac = nic_params.get("nic_mac")
                     kvm_utils.set_mac_address(self.instance, vlan, mac)
                 else:
-                    kvm_utils.generate_mac_address(self.instance, vlan)
+                    mac = mac_source and mac_source.get_mac_address(vlan)
+                    if mac:
+                        kvm_utils.set_mac_address(self.instance, vlan, mac)
+                    else:
+                        kvm_utils.generate_mac_address(self.instance, vlan)
 
             # Assign a PCI assignable device
             self.pci_assignable = None
@@ -660,7 +684,9 @@
                 self.migration_file = "/tmp/migration-unix-%s" % self.instance
                 qemu_command += " -incoming unix:%s" % self.migration_file
             elif migration_mode == "exec":
-                qemu_command += ' -incoming "exec:%s"' % migration_exec_cmd
+                self.migration_port = kvm_utils.find_free_port(5200, 6000)
+                qemu_command += (' -incoming "exec:nc -l %s"' %
+                                 self.migration_port)
 
             logging.debug("Running qemu command:\n%s", qemu_command)
             self.process = kvm_subprocess.run_bg(qemu_command, None,
@@ -678,9 +704,8 @@
 
             # Establish monitor connections
             self.monitors = []
-            for monitor_name in kvm_utils.get_sub_dict_names(params,
-                                                             "monitors"):
-                monitor_params = kvm_utils.get_sub_dict(params, monitor_name)
+            for monitor_name in params.objects("monitors"):
+                monitor_params = params.object_params(monitor_name)
                 # Wait for monitor connection to succeed
                 end_time = time.time() + timeout
                 while time.time() < end_time:
@@ -733,7 +758,7 @@
 
             # Establish a session with the serial console -- requires a version
             # of netcat that supports -U
-            self.serial_console = kvm_subprocess.kvm_shell_session(
+            self.serial_console = kvm_subprocess.ShellSession(
                 "nc -U %s" % self.get_serial_console_filename(),
                 auto_close=False,
                 output_func=kvm_utils.log_line,
@@ -826,7 +851,7 @@
                     os.unlink(self.migration_file)
                 except OSError:
                     pass
-            num_nics = len(kvm_utils.get_sub_dict_names(self.params, "nics"))
+            num_nics = len(self.params.objects("nics"))
             for vlan in range(num_nics):
                 self.free_mac_address(vlan)
 
@@ -885,7 +910,7 @@
         params).
         """
         return [self.get_monitor_filename(m) for m in
-                kvm_utils.get_sub_dict_names(self.params, "monitors")]
+                self.params.objects("monitors")]
 
 
     def get_serial_console_filename(self):
@@ -911,9 +936,9 @@
 
         @param index: Index of the NIC whose address is requested.
         """
-        nics = kvm_utils.get_sub_dict_names(self.params, "nics")
+        nics = self.params.objects("nics")
         nic_name = nics[index]
-        nic_params = kvm_utils.get_sub_dict(self.params, nic_name)
+        nic_params = self.params.object_params(nic_name)
         if nic_params.get("nic_mode") == "tap":
             mac = self.get_mac_address(index)
             if not mac:
@@ -946,8 +971,8 @@
         @return: If port redirection is used, return the host port redirected
                 to guest port port. Otherwise return port.
         """
-        nic_name = kvm_utils.get_sub_dict_names(self.params, "nics")[nic_index]
-        nic_params = kvm_utils.get_sub_dict(self.params, nic_name)
+        nic_name = self.params.objects("nics")[nic_index]
+        nic_params = self.params.object_params(nic_name)
         if nic_params.get("nic_mode") == "tap":
             return port
         else:
@@ -963,9 +988,9 @@
 
         @param nic_index: Index of the NIC
         """
-        nics = kvm_utils.get_sub_dict_names(self.params, "nics")
+        nics = self.params.objects("nics")
         nic_name = nics[nic_index]
-        nic_params = kvm_utils.get_sub_dict(self.params, nic_name)
+        nic_params = self.params.object_params(nic_name)
         if nic_params.get("nic_ifname"):
             return nic_params.get("nic_ifname")
         else:
@@ -1040,7 +1065,7 @@
         @param nic_index: The index of the NIC to connect to.
         @param timeout: Time (seconds) before giving up logging into the
                 guest.
-        @return: kvm_spawn object on success and None on failure.
+        @return: ShellSession object on success and None on failure.
         """
         username = self.params.get("username", "")
         password = self.params.get("password", "")
@@ -1068,7 +1093,7 @@
 
     def copy_files_to(self, local_path, remote_path, nic_index=0, timeout=600):
         """
-        Transfer files to the guest.
+        Transfer files to the remote host(guest).
 
         @param local_path: Host path
         @param remote_path: Guest path
@@ -1082,21 +1107,12 @@
         address = self.get_address(nic_index)
         port = self.get_port(int(self.params.get("file_transfer_port")))
 
-        if not address or not port:
-            logging.debug("IP address or port unavailable")
-            return None
-
-        if client == "scp":
-            log_filename = ("scp-%s-%s.log" %
-                            (self.name, kvm_utils.generate_random_string(4)))
-            return kvm_utils.scp_to_remote(address, port, username, password,
-                                           local_path, remote_path,
-                                           log_filename, timeout)
-        elif client == "rss":
-            c = rss_file_transfer.FileUploadClient(address, port)
-            c.upload(local_path, remote_path, timeout)
-            c.close()
-            return True
+        log_filename = ("transfer-%s-to-%s-%s.log" %
+                        (self.name, address,
+                        kvm_utils.generate_random_string(4)))
+        return kvm_utils.copy_files_to(address, client, username, password,
+                                       port, local_path, remote_path,
+                                       log_filename, timeout)
 
 
     def copy_files_from(self, remote_path, local_path, nic_index=0, timeout=600):
@@ -1115,21 +1131,11 @@
         address = self.get_address(nic_index)
         port = self.get_port(int(self.params.get("file_transfer_port")))
 
-        if not address or not port:
-            logging.debug("IP address or port unavailable")
-            return None
-
-        if client == "scp":
-            log_filename = ("scp-%s-%s.log" %
-                            (self.name, kvm_utils.generate_random_string(4)))
-            return kvm_utils.scp_from_remote(address, port, username, password,
-                                             remote_path, local_path,
-                                             log_filename, timeout)
-        elif client == "rss":
-            c = rss_file_transfer.FileDownloadClient(address, port)
-            c.download(remote_path, local_path, timeout)
-            c.close()
-            return True
+        log_filename = ("transfer-%s-from-%s-%s.log" %
+                        (self.name, address,
+                        kvm_utils.generate_random_string(4)))
+        return kvm_utils.copy_files_from(address, client, username, password,
+                        port, local_path, remote_path, log_filename, timeout)
 
 
     def serial_login(self, timeout=10):
@@ -1139,7 +1145,7 @@
         password prompt or a shell prompt) -- fail.
 
         @param timeout: Time (seconds) before giving up logging into the guest.
-        @return: kvm_spawn object on success and None on failure.
+        @return: ShellSession object on success and None on failure.
         """
         username = self.params.get("username", "")
         password = self.params.get("password", "")
@@ -1213,11 +1219,7 @@
         if not session:
             return None
         try:
-            cmd = self.params.get("cpu_chk_cmd")
-            s, count = session.get_command_status_output(cmd)
-            if s == 0:
-                return int(count)
-            return None
+            return int(session.cmd(self.params.get("cpu_chk_cmd")))
         finally:
             session.close()
 
@@ -1235,9 +1237,7 @@
         try:
             if not cmd:
                 cmd = self.params.get("mem_chk_cmd")
-            s, mem_str = session.get_command_status_output(cmd)
-            if s != 0:
-                return None
+            mem_str = session.cmd(cmd)
             mem = re.findall("([0-9]+)", mem_str)
             mem_size = 0
             for m in mem:
@@ -1259,3 +1259,14 @@
         """
         cmd = self.params.get("mem_chk_cur_cmd")
         return self.get_memory_size(cmd)
+
+
+    def save_to_file(self, path):
+        """
+        Save the state of virtual machine to a file through migrate to
+        exec
+        """
+        # Make sure we only get one iteration
+        self.monitor.cmd("migrate_set_speed 1000g")
+        self.monitor.cmd("migrate_set_downtime 100000000")
+        self.monitor.migrate('"exec:cat>%s"' % path)
diff --git a/client/tests/kvm/migration_control.srv b/client/tests/kvm/migration_control.srv
new file mode 100644
index 0000000..16ada36
--- /dev/null
+++ b/client/tests/kvm/migration_control.srv
@@ -0,0 +1,122 @@
+AUTHOR = "Yolkfull Chow <yzhou@redhat.com>"
+TIME = "SHORT"
+NAME = "Migration across multiple hosts"
+TEST_CATEGORY = "Functional"
+TEST_CLASS = "Virtualization"
+TEST_TYPE = "Server"
+DOC = """
+Migrate KVM guest between two hosts. It parses the base config file, restricts
+it with appropriate parameters, generates the test dicts, modify the test_dicts
+so there's a distinction between the migration roles ('dest' or 'source').
+"""
+
+import sys, os, commands, glob, shutil, logging, random
+from autotest_lib.server import utils
+
+# Specify the directory of autotest before you start this test
+AUTOTEST_DIR = '/usr/local/autotest'
+
+# Specify the root directory that on client machines
+rootdir = '/tmp/kvm_autotest_root'
+
+# Make possible to import the KVM test APIs
+KVM_DIR = os.path.join(AUTOTEST_DIR, 'client/tests/kvm')
+sys.path.append(KVM_DIR)
+
+import common, kvm_config
+
+def generate_mac_address():
+    r = random.SystemRandom()
+    mac = "9a:%02x:%02x:%02x:%02x:%02x" % (r.randint(0x00, 0xff),
+                                           r.randint(0x00, 0xff),
+                                           r.randint(0x00, 0xff),
+                                           r.randint(0x00, 0xff),
+                                           r.randint(0x00, 0xff))
+    return mac
+
+
+def run(pair):
+    logging.info("KVM migration running on source host [%s] and destination "
+                 "host [%s]\n", pair[0], pair[1])
+
+    source = hosts.create_host(pair[0])
+    dest = hosts.create_host(pair[1])
+    source_at = autotest.Autotest(source)
+    dest_at = autotest.Autotest(dest)
+
+    cfg_file = os.path.join(KVM_DIR, "tests_base.cfg")
+
+    if not os.path.exists(cfg_file):
+        raise error.JobError("Config file %s was not found", cfg_file)
+
+    # Get test set (dictionary list) from the configuration file
+    cfg = kvm_config.config()
+    test_variants = """
+image_name(_.*)? ?<= /tmp/kvm_autotest_root/images/
+cdrom(_.*)? ?<= /tmp/kvm_autotest_root/
+floppy ?<= /tmp/kvm_autotest_root/
+Linux:
+    unattended_install:
+        kernel ?<= /tmp/kvm_autotest_root/
+        initrd ?<= /tmp/kvm_autotest_root/
+qemu_binary = /usr/libexec/qemu-kvm
+qemu_img_binary = /usr/bin/qemu-img
+only qcow2
+only virtio_net
+only virtio_blk
+only smp2
+only no_pci_assignable
+only smallpages
+only Fedora.13.64
+only migrate_multi_host
+nic_mode = tap
+nic_mac_nic1 = %s
+""" % (generate_mac_address())
+    cfg.fork_and_parse(cfg_file, test_variants)
+    test_dicts = cfg.get_list()
+
+    source_control_file = dest_control_file = """
+kvm_test_dir = os.path.join(os.environ['AUTODIR'],'tests/kvm')
+sys.path.append(kvm_test_dir)\n
+"""
+    for params in test_dicts:
+        params['srchost'] = source.ip
+        params['dsthost'] = dest.ip
+        params['rootdir'] = rootdir
+
+        source_params = params.copy()
+        source_params['role'] = "source"
+
+        dest_params = params.copy()
+        dest_params['role'] = "destination"
+        dest_params['migration_mode'] = "tcp"
+
+        # Report the parameters we've received
+        print "Test parameters:"
+        keys = params.keys()
+        keys.sort()
+        for key in keys:
+            logging.debug("    %s = %s", key, params[key])
+
+        source_control_file += "job.run_test('kvm', tag='%s', params=%s)" % (source_params['shortname'], source_params)
+        dest_control_file += "job.run_test('kvm', tag='%s', params=%s)" % (dest_params['shortname'], dest_params)
+
+        logging.info('Source control file:\n%s', source_control_file)
+        logging.info('Destination control file:\n%s', dest_control_file)
+        dest_command = subcommand(dest_at.run,
+                                  [dest_control_file, dest.hostname])
+
+        source_command = subcommand(source_at.run,
+                                    [source_control_file, source.hostname])
+
+        parallel([dest_command, source_command])
+
+# Grab the pairs (and failures)
+(pairs, failures) = utils.form_ntuples_from_machines(machines, 2)
+
+# Log the failures
+for failure in failures:
+    job.record("FAIL", failure[0], "kvm", failure[1])
+
+# Now run through each pair and run
+job.parallel_simple(run, pairs, log=False)
diff --git a/client/tests/kvm/scan_results.py b/client/tests/kvm/scan_results.py
index a339a85..97105fb 100755
--- a/client/tests/kvm/scan_results.py
+++ b/client/tests/kvm/scan_results.py
@@ -38,7 +38,7 @@
             test_status = parts[0].split()[1]
             # Remove "kvm." prefix
             if test_name.startswith("kvm."):
-                test_name = test_name.split("kvm.")[1]
+                test_name = test_name[4:]
             result_list.append((test_name, test_status,
                                 int(end_time - start_time), info))
 
diff --git a/client/tests/kvm/scripts/allocator.py b/client/tests/kvm/scripts/allocator.py
index 227745a..09dc004 100755
--- a/client/tests/kvm/scripts/allocator.py
+++ b/client/tests/kvm/scripts/allocator.py
@@ -12,7 +12,7 @@
 
 PAGE_SIZE = 4096 # machine page size
 
-TMPFS_OVERHEAD = 0.0022 # overhead on 1MB of write data 
+TMPFS_OVERHEAD = 0.0022 # overhead on 1MB of write data
 
 
 class MemFill(object):
@@ -34,7 +34,7 @@
 
         self.tmpdp = tempfile.mkdtemp()
         ret_code = os.system("mount -o size=%dM tmpfs %s -t tmpfs" %
-                             ((mem+math.ceil(mem*TMPFS_OVERHEAD)), 
+                             ((mem+math.ceil(mem*TMPFS_OVERHEAD)),
                              self.tmpdp))
         if ret_code != 0:
             if os.getuid() != 0:
diff --git a/client/tests/kvm/scripts/bonding_setup.py b/client/tests/kvm/scripts/bonding_setup.py
new file mode 100644
index 0000000..f2d4be9
--- /dev/null
+++ b/client/tests/kvm/scripts/bonding_setup.py
@@ -0,0 +1,37 @@
+import os, re, commands, sys
+"""This script is used to setup bonding, macaddr of bond0 should be assigned by
+argv1"""
+
+if len(sys.argv) != 2:
+    sys.exit(1)
+mac = sys.argv[1]
+eth_nums = 0
+ifconfig_output = commands.getoutput("ifconfig")
+re_eth = "eth[0-9]*"
+for ename in re.findall(re_eth, ifconfig_output):
+    eth_config_file = "/etc/sysconfig/network-scripts/ifcfg-%s" % ename
+    eth_config = """DEVICE=%s
+USERCTL=no
+ONBOOT=yes
+MASTER=bond0
+SLAVE=yes
+BOOTPROTO=none
+""" % ename
+    f = file(eth_config_file,'w')
+    f.write(eth_config)
+    f.close()
+
+bonding_config_file = "/etc/sysconfig/network-scripts/ifcfg-bond0"
+bond_config = """DEVICE=bond0
+BOOTPROTO=dhcp
+NETWORKING_IPV6=no
+ONBOOT=yes
+USERCTL=no
+MACADDR=%s
+""" % mac
+f = file(bonding_config_file, "w")
+f.write(bond_config)
+f.close()
+os.system("modprobe bonding")
+os.system("service NetworkManager stop")
+os.system("service network restart")
diff --git a/client/tests/kvm/scripts/join_mcast.py b/client/tests/kvm/scripts/join_mcast.py
old mode 100644
new mode 100755
diff --git a/client/tests/kvm/scripts/qemu-ifup-ipv6 b/client/tests/kvm/scripts/qemu-ifup-ipv6
old mode 100644
new mode 100755
diff --git a/client/tests/kvm/scripts/unattended.py b/client/tests/kvm/scripts/unattended.py
index 1029d1e..e9e4751 100755
--- a/client/tests/kvm/scripts/unattended.py
+++ b/client/tests/kvm/scripts/unattended.py
@@ -242,9 +242,8 @@
 class UnattendedInstall(object):
     """
     Creates a floppy disk image that will contain a config file for unattended
-    OS install. Optionally, sets up a PXE install server using qemu built in
-    TFTP and DHCP servers to install a particular operating system. The
-    parameters to the script are retrieved from environment variables.
+    OS install. The parameters to the script are retrieved from environment
+    variables.
     """
     def __init__(self):
         """
@@ -256,9 +255,9 @@
 
         attributes = ['kernel_args', 'finish_program', 'cdrom_cd1',
                       'unattended_file', 'medium', 'url', 'kernel', 'initrd',
-                      'nfs_server', 'nfs_dir', 'pxe_dir', 'pxe_image',
-                      'pxe_initrd', 'install_virtio', 'tftp',
-                      'floppy', 'cdrom_unattended']
+                      'nfs_server', 'nfs_dir', 'install_virtio', 'floppy',
+                      'cdrom_unattended', 'boot_path', 'extra_params']
+
         for a in attributes:
             self._setattr(a)
 
@@ -269,13 +268,6 @@
             for va in v_attributes:
                 self._setattr(va)
 
-        # Silly attribution just to calm pylint down...
-        self.tftp = self.tftp
-        if self.tftp:
-            self.tftp = os.path.join(KVM_TEST_DIR, self.tftp)
-            if not os.path.isdir(self.tftp):
-                os.makedirs(self.tftp)
-
         if self.cdrom_cd1:
             self.cdrom_cd1 = os.path.join(KVM_TEST_DIR, self.cdrom_cd1)
         self.cdrom_cd1_mount = tempfile.mkdtemp(prefix='cdrom_cd1_', dir='/tmp')
@@ -287,9 +279,7 @@
             if not os.path.isdir(os.path.dirname(self.floppy)):
                 os.makedirs(os.path.dirname(self.floppy))
 
-        self.image_path = KVM_TEST_DIR
-        self.kernel_path = os.path.join(self.image_path, self.kernel)
-        self.initrd_path = os.path.join(self.image_path, self.initrd)
+        self.image_path = os.path.dirname(self.kernel)
 
 
     def _setattr(self, key):
@@ -408,7 +398,7 @@
             boot_disk.setup_answer_file(dest_fname, answer_contents)
 
         elif self.unattended_file.endswith('.xml'):
-            if self.tftp:
+            if "autoyast" in self.extra_params:
                 # SUSE autoyast install
                 dest_fname = "autoinst.xml"
                 if self.cdrom_unattended:
@@ -436,87 +426,44 @@
         boot_disk.close()
 
 
-    def setup_pxe_boot(self):
+    def setup_cdrom(self):
         """
-        Sets up a PXE boot environment using the built in qemu TFTP server.
-        Copies the PXE Linux bootloader pxelinux.0 from the host (needs the
-        pxelinux package or equivalent for your distro), and vmlinuz and
-        initrd.img files from the CD to a directory that qemu will serve trough
-        TFTP to the VM.
+        Mount cdrom and copy vmlinuz and initrd.img.
         """
-        print "Setting up PXE boot using TFTP root %s" % self.tftp
-
-        pxe_file = None
-        pxe_paths = ['/usr/lib/syslinux/pxelinux.0',
-                     '/usr/share/syslinux/pxelinux.0']
-        for path in pxe_paths:
-            if os.path.isfile(path):
-                pxe_file = path
-                break
-
-        if not pxe_file:
-            raise SetupError('Cannot find PXE boot loader pxelinux.0. Make '
-                             'sure pxelinux or equivalent package for your '
-                             'distro is installed.')
-
-        pxe_dest = os.path.join(self.tftp, 'pxelinux.0')
-        shutil.copyfile(pxe_file, pxe_dest)
+        print "Copying vmlinuz and initrd.img from cdrom"
+        m_cmd = ('mount -t iso9660 -v -o loop,ro %s %s' %
+                 (self.cdrom_cd1, self.cdrom_cd1_mount))
+        run(m_cmd, info='Could not mount CD image %s.' % self.cdrom_cd1)
 
         try:
-            m_cmd = ('mount -t iso9660 -v -o loop,ro %s %s' %
-                     (self.cdrom_cd1, self.cdrom_cd1_mount))
-            run(m_cmd, info='Could not mount CD image %s.' % self.cdrom_cd1)
-
-            pxe_dir = os.path.join(self.cdrom_cd1_mount, self.pxe_dir)
-            pxe_image = os.path.join(pxe_dir, self.pxe_image)
-            pxe_initrd = os.path.join(pxe_dir, self.pxe_initrd)
-
-            if not os.path.isdir(pxe_dir):
-                raise SetupError('The ISO image does not have a %s dir. The '
-                                 'script assumes that the cd has a %s dir '
-                                 'where to search for the vmlinuz image.' %
-                                 (self.pxe_dir, self.pxe_dir))
-
-            if not os.path.isfile(pxe_image) or not os.path.isfile(pxe_initrd):
-                raise SetupError('The location %s is lacking either a vmlinuz '
-                                 'or a initrd.img file. Cannot find a PXE '
-                                 'image to proceed.' % self.pxe_dir)
-
-            tftp_image = os.path.join(self.tftp, 'vmlinuz')
-            tftp_initrd = os.path.join(self.tftp, 'initrd.img')
-            shutil.copyfile(pxe_image, tftp_image)
-            shutil.copyfile(pxe_initrd, tftp_initrd)
-
+            img_path_cmd = ("mkdir -p %s" % self.image_path)
+            run(img_path_cmd, info=("Could not create image path dir %s" %
+                                    self.image_path))
+            kernel_fetch_cmd = ("cp %s/%s/%s %s" %
+                                (self.cdrom_cd1_mount, self.boot_path,
+                                 os.path.basename(self.kernel), self.kernel))
+            run(kernel_fetch_cmd, info=("Could not copy the vmlinuz from %s" %
+                                        self.cdrom_cd1_mount))
+            initrd_fetch_cmd = ("cp %s/%s/%s %s" %
+                                (self.cdrom_cd1_mount, self.boot_path,
+                                 os.path.basename(self.initrd), self.initrd))
+            run(initrd_fetch_cmd, info=("Could not copy the initrd.img from "
+                                        "%s" % self.cdrom_cd1_mount))
         finally:
             cleanup(self.cdrom_cd1_mount)
 
-        pxe_config_dir = os.path.join(self.tftp, 'pxelinux.cfg')
-        if not os.path.isdir(pxe_config_dir):
-            os.makedirs(pxe_config_dir)
-        pxe_config_path = os.path.join(pxe_config_dir, 'default')
-
-        pxe_config = open(pxe_config_path, 'w')
-        pxe_config.write('DEFAULT pxeboot\n')
-        pxe_config.write('TIMEOUT 20\n')
-        pxe_config.write('PROMPT 0\n')
-        pxe_config.write('LABEL pxeboot\n')
-        pxe_config.write('     KERNEL vmlinuz\n')
-        pxe_config.write('     APPEND initrd=initrd.img %s\n' %
-                         self.kernel_args)
-        pxe_config.close()
-
-        print "PXE boot successfuly set"
-
 
     def setup_url(self):
         """
         Download the vmlinuz and initrd.img from URL.
         """
-        print "Downloading the vmlinuz and initrd.img"
+        print "Downloading vmlinuz and initrd.img from URL"
         os.chdir(self.image_path)
 
-        kernel_fetch_cmd = "wget -q %s/isolinux/%s" % (self.url, self.kernel)
-        initrd_fetch_cmd = "wget -q %s/isolinux/%s" % (self.url, self.initrd)
+        kernel_fetch_cmd = "wget -q %s/%s/%s" % (self.url, self.boot_path,
+                                                 os.path.basename(self.kernel))
+        initrd_fetch_cmd = "wget -q %s/%s/%s" % (self.url, self.boot_path,
+                                                 os.path.basename(self.initrd))
 
         if os.path.exists(self.kernel):
             os.unlink(self.kernel)
@@ -526,7 +473,6 @@
         run(kernel_fetch_cmd, info="Could not fetch vmlinuz from %s" % self.url)
         run(initrd_fetch_cmd, info=("Could not fetch initrd.img from %s" %
                                     self.url))
-        print "Download of vmlinuz and initrd.img finished"
 
 
     def setup_nfs(self):
@@ -540,12 +486,14 @@
         run(m_cmd, info='Could not mount nfs server')
 
         try:
-            kernel_fetch_cmd = ("cp %s/isolinux/%s %s" %
-                                (self.nfs_mount, self.kernel, self.image_path))
+            kernel_fetch_cmd = ("cp %s/%s/%s %s" %
+                                (self.nfs_mount, self.boot_path,
+                                os.path.basename(self.kernel), self.image_path))
             run(kernel_fetch_cmd, info=("Could not copy the vmlinuz from %s" %
                                         self.nfs_mount))
-            initrd_fetch_cmd = ("cp %s/isolinux/%s %s" %
-                                (self.nfs_mount, self.initrd, self.image_path))
+            initrd_fetch_cmd = ("cp %s/%s/%s %s" %
+                                (self.nfs_mount, self.boot_path,
+                                os.path.basename(self.initrd), self.image_path))
             run(initrd_fetch_cmd, info=("Could not copy the initrd.img from "
                                         "%s" % self.nfs_mount))
         finally:
@@ -572,8 +520,8 @@
         if self.unattended_file and (self.floppy or self.cdrom_unattended):
             self.setup_boot_disk()
         if self.medium == "cdrom":
-            if self.tftp:
-                self.setup_pxe_boot()
+            if self.kernel and self.initrd:
+                self.setup_cdrom()
         elif self.medium == "url":
             self.setup_url()
         elif self.medium == "nfs":
diff --git a/client/tests/kvm/scripts/virtio_guest.py b/client/tests/kvm/scripts/virtio_guest.py
old mode 100644
new mode 100755
index 4862ef2..0038f48
--- a/client/tests/kvm/scripts/virtio_guest.py
+++ b/client/tests/kvm/scripts/virtio_guest.py
@@ -3,35 +3,23 @@
 """
 Auxiliary script used to send data between ports on guests.
 
-@copyright: 2008-2009 Red Hat Inc.
+@copyright: 2010 Red Hat, Inc.
 @author: Jiri Zupka (jzupka@redhat.com)
 @author: Lukas Doktor (ldoktor@redhat.com)
 """
-#from _pydev_SimpleXMLRPCServer import fcntl
-
-"""
-TODO:
-virt.init([consoles])   # sysfs, udev, OK
-virt.open(name)
-virt.close(name)
-virt.poll(name, eventmask, timeout) # poll.register(), poll.poll(),
-return event
-virt.send(name, length) # host disconnected
-virt.recv(name, length) # host disconnected
-virt.blocking(name, true)   # true = blocking, false = nonblocking
-virt.loopback(in_names, out_names, type="None")  # use select/poll
-"""
-
 import threading
 from threading import Thread
-import os, time, select, re, random, sys, array, fcntl, array, subprocess
+import os, time, select, re, random, sys, array
+import fcntl, array, subprocess, traceback, signal
 
 DEBUGPATH = "/sys/kernel/debug"
 SYSFSPATH = "/sys/class/virtio-ports/"
 
 
-class virtio_guest():
-
+class VirtioGuest:
+    """
+    Test tools of virtio_ports.
+    """
     LOOP_NONE = 0
     LOOP_POLL = 1
     LOOP_SELECT = 2
@@ -41,6 +29,9 @@
         self.exit_thread = threading.Event()
         self.threads = []
         self.ports = {}
+        self.poll_fds = {}
+        self.catch_signal = None
+        self.use_config = threading.Event()
 
 
     def _readfile(self, name):
@@ -125,7 +116,7 @@
         print "PASS: Init and check virtioconsole files in system."
 
 
-    class switch(Thread):
+    class Switch(Thread):
         """
         Thread that sends data between ports.
         """
@@ -137,7 +128,7 @@
             @param method: Method of read/write access.
             @param cachesize: Block to receive and send.
             """
-            Thread.__init__(self)
+            Thread.__init__(self, name="Switch")
 
             self.in_files = in_files
             self.out_files = out_files
@@ -211,15 +202,15 @@
 
 
         def run(self):
-            if (self.method == virtio_guest.LOOP_POLL):
+            if (self.method == VirtioGuest.LOOP_POLL):
                 self._poll_mode()
-            elif (self.method == virtio_guest.LOOP_SELECT):
+            elif (self.method == VirtioGuest.LOOP_SELECT):
                 self._select_mode()
             else:
                 self._none_mode()
 
 
-    class sender(Thread):
+    class Sender(Thread):
         """
         Creates a thread which sends random blocks of data to dst port.
         """
@@ -228,7 +219,7 @@
             @param port: Destination port
             @param length: Length of the random data block
             """
-            Thread.__init__(self)
+            Thread.__init__(self, name="Sender")
             self.port = port
             self.exit_thread = event
             self.data = array.array('L')
@@ -260,11 +251,33 @@
                         print os.system("stty -F %s raw -echo" % (name))
                         print os.system("stty -F %s -a" % (name))
                     f.append(self.files[name])
-                except Exception as inst:
+                except Exception, inst:
                     print "FAIL: Failed to open file %s" % (name)
                     raise inst
         return f
 
+    @staticmethod
+    def pollmask_to_str(mask):
+        """
+        Conver pool mast to string
+
+        @param mask: poll return mask
+        """
+        str = ""
+        if (mask & select.POLLIN):
+            str += "IN "
+        if (mask & select.POLLPRI):
+            str += "PRI IN "
+        if (mask & select.POLLOUT):
+            str += "OUT "
+        if (mask & select.POLLERR):
+            str += "ERR "
+        if (mask & select.POLLHUP):
+            str += "HUP "
+        if (mask & select.POLLMSG):
+            str += "MSG "
+        return str
+
 
     def poll(self, port, expected, timeout=500):
         """
@@ -279,24 +292,35 @@
 
         mask = p.poll(timeout)
 
-        str = ""
-        if (mask[0][1] & select.POLLIN):
-            str += "IN "
-        if (mask[0][1] & select.POLLPRI):
-            str += "PRI IN "
-        if (mask[0][1] & select.POLLOUT):
-            str += "OUT "
-        if (mask[0][1] & select.POLLERR):
-            str += "ERR "
-        if (mask[0][1] & select.POLLHUP):
-            str += "HUP "
-        if (mask[0][1] & select.POLLMSG):
-            str += "MSG "
-
+        maskstr = VirtioGuest.pollmask_to_str(mask[0][1])
         if (mask[0][1] & expected) == expected:
-            print "PASS: Events: " + str
+            print "PASS: Events: " + maskstr
         else:
-            print "FAIL: Events: " + str
+            emaskstr = VirtioGuest.pollmask_to_str(expected)
+            print "FAIL: Events: " + maskstr + "  Expected: " + emaskstr
+
+
+    def lseek(self, port, pos, how):
+        """
+        Use lseek on the device. The device is unseekable so PASS is returned
+        when lseek command fails and vice versa.
+
+        @param port: Name of the port
+        @param pos: Offset
+        @param how: Relativ offset os.SEEK_{SET,CUR,END}
+        """
+        fd = self._open([port])[0]
+
+        try:
+            os.lseek(fd, pos, how)
+        except Exception, inst:
+            if inst.errno == 29:
+                print "PASS: the lseek failed as expected"
+            else:
+                print inst
+                print "FAIL: unknown error"
+        else:
+            print "FAIL: the lseek unexpectedly passed"
 
 
     def blocking(self, port, mode=False):
@@ -306,8 +330,7 @@
         @param port: port to set mode
         @param mode: False to set nonblock mode, True for block mode
         """
-        path = self.ports[port]["path"]
-        fd = self.files[path]
+        fd = self._open([port])[0]
 
         try:
             fl = fcntl.fcntl(fd, fcntl.F_GETFL)
@@ -316,12 +339,115 @@
             else:
                 fcntl.fcntl(fd, fcntl.F_SETFL, fl & ~os.O_NONBLOCK)
 
-        except Exception as inst:
+        except Exception, inst:
             print "FAIL: Setting (non)blocking mode: " + str(inst)
             return
 
-        print ("PASS: set blocking mode to %s mode" %
-               ("blocking" if mode else "nonblocking"))
+        if mode:
+            print "PASS: set to blocking mode"
+        else:
+            print "PASS: set to nonblocking mode"
+
+
+    def __call__(self, sig, frame):
+        """
+        Call function. Used for signal handle.
+        """
+        if (sig == signal.SIGIO):
+            self.sigio_handler(sig, frame)
+
+
+    def sigio_handler(self, sig, frame):
+        """
+        Handler for sigio operation.
+
+        @param sig: signal which call handler.
+        @param frame: frame of caller
+        """
+        if self.poll_fds:
+            p = select.poll()
+            map(p.register, self.poll_fds.keys())
+
+            masks = p.poll(1)
+            print masks
+            for mask in masks:
+                self.poll_fds[mask[0]][1] |= mask[1]
+
+
+    def get_sigio_poll_return(self, port):
+        """
+        Return PASS, FAIL and poll walue in string format.
+
+        @param port: Port to check poll information.
+        """
+        fd = self._open([port])[0]
+
+        maskstr = VirtioGuest.pollmask_to_str(self.poll_fds[fd][1])
+        if (self.poll_fds[fd][0] ^ self.poll_fds[fd][1]):
+            emaskstr = VirtioGuest.pollmask_to_str(self.poll_fds[fd][0])
+            print "FAIL: Events: " + maskstr + "  Expected: " + emaskstr
+        else:
+            print "PASS: Events: " + maskstr
+        self.poll_fds[fd][1] = 0
+
+
+    def set_pool_want_return(self, port, poll_value):
+        """
+        Set value to static variable.
+
+        @param port: Port which should be set excepted mask
+        @param poll_value: Value to check sigio signal.
+        """
+        fd = self._open([port])[0]
+        self.poll_fds[fd] = [poll_value, 0]
+        print "PASS: Events: " + VirtioGuest.pollmask_to_str(poll_value)
+
+
+    def catching_signal(self):
+        """
+        return: True if should set catch signal, False if ignore signal and
+                none when configuration is not changed.
+        """
+        ret = self.catch_signal
+        self.catch_signal = None
+        return ret
+
+
+    def async(self, port, mode=True, exp_val = 0):
+        """
+        Set port function mode async/sync.
+
+        @param port: port which should be pooled.
+        @param mode: False to set sync mode, True for sync mode.
+        @param exp_val: Value which should be pooled.
+        """
+        fd = self._open([port])[0]
+
+        try:
+            fcntl.fcntl(fd, fcntl.F_SETOWN, os.getpid())
+            fl = fcntl.fcntl(fd, fcntl.F_GETFL)
+
+            self.use_config.clear()
+            if mode:
+                fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_ASYNC)
+                self.poll_fds[fd] = [exp_val, 0]
+                self.catch_signal = True
+            else:
+                del self.poll_fds[fd]
+                fcntl.fcntl(fd, fcntl.F_SETFL, fl & ~os.O_ASYNC)
+                self.catch_signal = False
+
+            os.kill(os.getpid(), signal.SIGUSR1)
+            self.use_config.wait()
+
+        except Exception, inst:
+            print "FAIL: Setting (a)sync mode: " + str(inst)
+            return
+
+        if mode:
+            print "PASS: Set to async mode"
+        else:
+            print "PASS: Set to sync mode"
 
 
     def close(self, file):
@@ -336,26 +462,29 @@
             if path in self.files.keys():
                 descriptor = self.files[path]
                 del self.files[path]
-        try:
-            os.close(descriptor)
-        except Exception as inst:
-            print "FAIL: Closing the file: " + str(inst)
-            return
+            if descriptor != None:
+                try:
+                    os.close(descriptor)
+                except Exception, inst:
+                    print "FAIL: Closing the file: " + str(inst)
+                    return
         print "PASS: Close"
 
 
-    def open(self, in_files):
+    def open(self, in_file):
         """
         Direct open devices.
 
-        @param in_files: Array of files.
+        @param in_file: Array of files.
         @return: Array of descriptors.
         """
-        name = self.ports[in_files]["path"]
+        name = self.ports[in_file]["path"]
         try:
             self.files[name] = os.open(name, os.O_RDWR)
+            if (self.ports[in_file]["is_console"] == "yes"):
+                print os.system("stty -F %s raw -echo" % (name))
             print "PASS: Open all filles correctly."
-        except Exception as inst:
+        except Exception, inst:
             print "%s\nFAIL: Failed open file %s" % (str(inst), name)
 
 
@@ -374,7 +503,7 @@
         in_f = self._open(in_files)
         out_f = self._open(out_files)
 
-        s = self.switch(in_f, out_f, self.exit_thread, cachesize, mode)
+        s = self.Switch(in_f, out_f, self.exit_thread, cachesize, mode)
         s.start()
         self.threads.append(s)
         print "PASS: Start switch"
@@ -412,7 +541,7 @@
         self.ports = self._get_port_status()
         in_f = self._open([port])
 
-        self.threads.append(self.sender(in_f[0], self.exit_thread, length))
+        self.threads.append(self.Sender(in_f[0], self.exit_thread, length))
         print "PASS: Sender prepare"
 
 
@@ -439,7 +568,7 @@
             data += "%c" % random.randrange(255)
         try:
             writes = os.write(in_f[0], data)
-        except Exception as inst:
+        except Exception, inst:
             print inst
         if not writes:
             writes = 0
@@ -447,7 +576,7 @@
             while (writes < length):
                 try:
                     writes += os.write(in_f[0], data)
-                except Exception as inst:
+                except Exception, inst:
                     print inst
         if writes >= length:
             print "PASS: Send data length %d" % writes
@@ -469,13 +598,13 @@
         recvs = ""
         try:
             recvs = os.read(in_f[0], buffer)
-        except Exception as inst:
+        except Exception, inst:
             print inst
         if mode:
             while (len(recvs) < length):
                 try:
                     recvs += os.read(in_f[0], buffer)
-                except Exception as inst:
+                except Exception, inst:
                     print inst
         if len(recvs) >= length:
             print "PASS: Recv data length %d" % len(recvs)
@@ -484,6 +613,28 @@
                    (length, len(recvs)))
 
 
+    def clean_port(self, port, buffer=1024):
+        in_f = self._open([port])
+        ret = select.select([in_f[0]], [], [], 1.0)
+        buf = ""
+        if ret[0]:
+            buf = os.read(in_f[0], buffer)
+        print ("PASS: Rest in socket: " + buf)
+
+
+def is_alive():
+    """
+    Check is only main thread is alive and if guest react.
+    """
+    if threading.activeCount() == 2:
+        print ("PASS: Guest is ok no thread alive")
+    else:
+        threads = ""
+        for thread in threading.enumerate():
+            threads += thread.name + ", "
+        print ("FAIL: On guest run thread. Active thread:" + threads)
+
+
 def compile():
     """
     Compile virtio_guest.py to speed up.
@@ -491,22 +642,52 @@
     import py_compile
     py_compile.compile(sys.path[0] + "/virtio_guest.py")
     print "PASS: compile"
-    exit(0)
+    sys.exit()
 
 
-def main():
+def worker(virt):
     """
-    Main (infinite) loop of virtio_guest.
+    Worker thread (infinite) loop of virtio_guest.
     """
-    if (len(sys.argv) > 1) and (sys.argv[1] == "-c"):
-        compile()
-
-    virt = virtio_guest()
     print "PASS: Start"
 
     while True:
         str = raw_input()
-        exec str
+        try:
+            exec str
+        except:
+            exc_type, exc_value, exc_traceback = sys.exc_info()
+            print "On Guest exception from: \n" + "".join(
+                            traceback.format_exception(exc_type,
+                                                       exc_value,
+                                                       exc_traceback))
+    sys.exit(0)
+
+
+def sigusr_handler(sig, frame):
+    pass
+
+
+def main():
+    """
+    Main function with infinite loop to catch signal from system.
+    """
+    if (len(sys.argv) > 1) and (sys.argv[1] == "-c"):
+        compile()
+
+    virt = VirtioGuest()
+    slave = Thread(target=worker, args=(virt, ))
+    slave.start()
+    signal.signal(signal.SIGUSR1, sigusr_handler)
+    while True:
+        signal.pause()
+        catch = virt.catching_signal()
+        if catch:
+            signal.signal(signal.SIGIO, virt)
+        elif catch == False:
+            signal.signal(signal.SIGIO, signal.SIG_DFL)
+        if (catch != None):
+            virt.use_config.set()
 
 
 if __name__ == "__main__":
diff --git a/client/tests/kvm/tests.cfg.sample b/client/tests/kvm/tests.cfg.sample
index ce3e307..bde7aba 100644
--- a/client/tests/kvm/tests.cfg.sample
+++ b/client/tests/kvm/tests.cfg.sample
@@ -11,10 +11,17 @@
 # * qemu and qemu-img are expected to be found under /usr/bin/qemu-kvm and
 #   /usr/bin/qemu-img respectively.
 # * All image files are expected under /tmp/kvm_autotest_root/images/
-# * All iso files are expected under /tmp/kvm_autotest_root/isos/
-qemu_img_binary = /usr/bin/qemu-img
+# * All install iso files are expected under /tmp/kvm_autotest_root/isos/
+# * The parameters cdrom_unattended, floppy, kernel and initrd are generated
+#   by KVM autotest, so remember to put them under a writable location
+#   (for example, the cdrom share can be read only)
 image_name(_.*)? ?<= /tmp/kvm_autotest_root/images/
-cdrom(_.*)? ?<= /tmp/kvm_autotest_root/isos/
+cdrom(_.*)? ?<= /tmp/kvm_autotest_root/
+floppy ?<= /tmp/kvm_autotest_root/
+Linux:
+    unattended_install:
+        kernel ?<= /tmp/kvm_autotest_root/
+        initrd ?<= /tmp/kvm_autotest_root/
 
 # Here are the test sets variants. The variant 'qemu_kvm_windows_quick' is
 # fully commented, the following ones have comments only on noteworthy points
@@ -26,6 +33,7 @@
     - @qemu_kvm_windows_quick:
         # We want qemu-kvm for this run
         qemu_binary = /usr/bin/qemu-kvm
+        qemu_img_binary = /usr/bin/qemu-img
         # Only qcow2 file format
         only qcow2
         # Only rtl8139 for nw card (default on qemu-kvm)
@@ -43,10 +51,11 @@
         # Subtest choice. You can modify that line to add more subtests
         only unattended_install.cdrom boot shutdown
 
-    # Runs qemu, f13 64 bit guest OS, install, boot, shutdown
-    - @qemu_f13_quick:
+    # Runs qemu, f14 64 bit guest OS, install, boot, shutdown
+    - @qemu_f14_quick:
         # We want qemu for this run
         qemu_binary = /usr/bin/qemu
+        qemu_img_binary = /usr/bin/qemu-img
         only qcow2
         # The default nw card for qemu is e1000
         only e1000
@@ -55,22 +64,23 @@
         only up
         only no_pci_assignable
         only smallpages
-        only Fedora.13.64
+        only Fedora.14.64
         only unattended_install.cdrom boot shutdown
         # qemu needs -enable-kvm on the cmdline
         extra_params += ' -enable-kvm'
 
-    # Runs qemu-kvm, f13 64 bit guest OS, install, boot, shutdown
-    - @qemu_kvm_f13_quick:
+    # Runs qemu-kvm, f14 64 bit guest OS, install, boot, shutdown
+    - @qemu_kvm_f14_quick:
         # We want qemu-kvm for this run
         qemu_binary = /usr/bin/qemu-kvm
+        qemu_img_binary = /usr/bin/qemu-img
         only qcow2
         only virtio_net
         only virtio_blk
         only smp2
         only no_pci_assignable
         only smallpages
-        only Fedora.13.64
+        only Fedora.14.64
         only unattended_install.cdrom boot shutdown
 
 # You may provide information about the DTM server for WHQL tests here:
@@ -87,4 +97,4 @@
 #kill_unresponsive_vms.* ?= no
 
 # Choose your test list from the testsets defined
-only qemu_kvm_f13_quick
+only qemu_kvm_f14_quick
diff --git a/client/tests/kvm/tests/build.py b/client/tests/kvm/tests/build.py
index c4f0b18..1eef7a1 100644
--- a/client/tests/kvm/tests/build.py
+++ b/client/tests/kvm/tests/build.py
@@ -1,590 +1,4 @@
-import time, os, sys, urllib, re, signal, logging, datetime, glob, ConfigParser
-import shutil
-from autotest_lib.client.bin import utils, test, os_dep
-from autotest_lib.client.common_lib import error
-import kvm_utils
-
-
-def check_configure_options(script_path):
-    """
-    Return the list of available options (flags) of a given kvm configure build
-    script.
-
-    @param script: Path to the configure script
-    """
-    abspath = os.path.abspath(script_path)
-    help_raw = utils.system_output('%s --help' % abspath, ignore_status=True)
-    help_output = help_raw.split("\n")
-    option_list = []
-    for line in help_output:
-        cleaned_line = line.lstrip()
-        if cleaned_line.startswith("--"):
-            option = cleaned_line.split()[0]
-            option = option.split("=")[0]
-            option_list.append(option)
-
-    return option_list
-
-
-def kill_qemu_processes():
-    """
-    Kills all qemu processes, also kills all processes holding /dev/kvm down.
-    """
-    logging.debug("Killing any qemu processes that might be left behind")
-    utils.system("pkill qemu", ignore_status=True)
-    # Let's double check to see if some other process is holding /dev/kvm
-    if os.path.isfile("/dev/kvm"):
-        utils.system("fuser -k /dev/kvm", ignore_status=True)
-
-
-def load_kvm_modules(module_dir=None, load_stock=False, extra_modules=None):
-    """
-    Unload previously loaded kvm modules, then load modules present on any
-    sub directory of module_dir. Function will walk through module_dir until
-    it finds the modules.
-
-    @param module_dir: Directory where the KVM modules are located.
-    @param load_stock: Whether we are going to load system kernel modules.
-    @param extra_modules: List of extra modules to load.
-    """
-    vendor = "intel"
-    if os.system("grep vmx /proc/cpuinfo 1>/dev/null") != 0:
-        vendor = "amd"
-    logging.debug("Detected CPU vendor as '%s'" %(vendor))
-
-    kill_qemu_processes()
-
-    logging.info("Unloading previously loaded KVM modules")
-    utils.unload_module("kvm")
-    if extra_modules:
-        for module in extra_modules:
-            utils.unload_module(module)
-
-    if module_dir:
-        logging.info("Loading the built KVM modules...")
-        kvm_module_path = None
-        kvm_vendor_module_path = None
-        abort = False
-
-        list_modules = ['kvm.ko', 'kvm-%s.ko' % vendor]
-        if extra_modules:
-            for extra_module in extra_modules:
-                list_modules.append('%s.ko' % extra_module)
-
-        list_module_paths = []
-        for folder, subdirs, files in os.walk(module_dir):
-            for module in list_modules:
-                if module in files:
-                    module_path = os.path.join(folder, module)
-                    list_module_paths.append(module_path)
-
-        # We might need to arrange the modules in the correct order
-        # to avoid module load problems
-        list_modules_load = []
-        for module in list_modules:
-            for module_path in list_module_paths:
-                if os.path.basename(module_path) == module:
-                    list_modules_load.append(module_path)
-
-        if len(list_module_paths) != len(list_modules):
-            logging.error("KVM modules not found. If you don't want to use the "
-                          "modules built by this test, make sure the option "
-                          "load_modules: 'no' is marked on the test control "
-                          "file.")
-            raise error.TestError("The modules %s were requested to be loaded, "
-                                  "but the only modules found were %s" %
-                                  (list_modules, list_module_paths))
-
-        for module_path in list_modules_load:
-            try:
-                utils.system("insmod %s" % module_path)
-            except Exception, e:
-                raise error.TestFail("Failed to load KVM modules: %s" % e)
-
-    if load_stock:
-        logging.info("Loading current system KVM modules...")
-        utils.system("modprobe kvm")
-        utils.system("modprobe kvm-%s" % vendor)
-        if extra_modules:
-            for module in extra_modules:
-                utils.system("modprobe %s" % module)
-
-
-def create_symlinks(test_bindir, prefix=None, bin_list=None, unittest=None):
-    """
-    Create symbolic links for the appropriate qemu and qemu-img commands on
-    the kvm test bindir.
-
-    @param test_bindir: KVM test bindir
-    @param prefix: KVM prefix path
-    @param bin_list: List of qemu binaries to link
-    @param unittest: Path to configuration file unittests.cfg
-    """
-    qemu_path = os.path.join(test_bindir, "qemu")
-    qemu_img_path = os.path.join(test_bindir, "qemu-img")
-    qemu_unittest_path = os.path.join(test_bindir, "unittests")
-    if os.path.lexists(qemu_path):
-        os.unlink(qemu_path)
-    if os.path.lexists(qemu_img_path):
-        os.unlink(qemu_img_path)
-    if unittest and os.path.lexists(qemu_unittest_path):
-        os.unlink(qemu_unittest_path)
-
-    logging.debug("Linking qemu binaries")
-
-    if bin_list:
-        for bin in bin_list:
-            if os.path.basename(bin) == 'qemu-kvm':
-                os.symlink(bin, qemu_path)
-            elif os.path.basename(bin) == 'qemu-img':
-                os.symlink(bin, qemu_img_path)
-
-    elif prefix:
-        kvm_qemu = os.path.join(prefix, "bin", "qemu-system-x86_64")
-        if not os.path.isfile(kvm_qemu):
-            raise error.TestError('Invalid qemu path')
-        kvm_qemu_img = os.path.join(prefix, "bin", "qemu-img")
-        if not os.path.isfile(kvm_qemu_img):
-            raise error.TestError('Invalid qemu-img path')
-        os.symlink(kvm_qemu, qemu_path)
-        os.symlink(kvm_qemu_img, qemu_img_path)
-
-    if unittest:
-        logging.debug("Linking unittest dir")
-        os.symlink(unittest, qemu_unittest_path)
-
-
-def save_build(build_dir, dest_dir):
-    logging.debug('Saving the result of the build on %s', dest_dir)
-    base_name = os.path.basename(build_dir)
-    tarball_name = base_name + '.tar.bz2'
-    os.chdir(os.path.dirname(build_dir))
-    utils.system('tar -cjf %s %s' % (tarball_name, base_name))
-    shutil.move(tarball_name, os.path.join(dest_dir, tarball_name))
-
-
-class BaseInstaller(object):
-    def __init__(self, test, params):
-        load_modules = params.get('load_modules', 'no')
-        if not load_modules or load_modules == 'yes':
-            self.load_modules = True
-        elif load_modules == 'no':
-            self.load_modules = False
-        default_extra_modules = str(None)
-        self.extra_modules = eval(params.get("extra_modules",
-                                             default_extra_modules))
-
-        self.srcdir = test.srcdir
-        if not os.path.isdir(self.srcdir):
-            os.makedirs(self.srcdir)
-
-        self.test_bindir = test.bindir
-        self.results_dir = test.resultsdir
-
-        # KVM build prefix, for the modes that do need it
-        prefix = os.path.join(test.bindir, 'build')
-        self.prefix = os.path.abspath(prefix)
-
-        # Current host kernel directory
-        default_host_kernel_source = '/lib/modules/%s/build' % os.uname()[2]
-        self.host_kernel_srcdir = params.get('host_kernel_source',
-                                             default_host_kernel_source)
-
-        # Extra parameters that can be passed to the configure script
-        self.extra_configure_options = params.get('extra_configure_options',
-                                                  None)
-
-        # Do we want to save the result of the build on test.resultsdir?
-        self.save_results = True
-        save_results = params.get('save_results', 'no')
-        if save_results == 'no':
-            self.save_results = False
-
-
-class YumInstaller(BaseInstaller):
-    """
-    Class that uses yum to install and remove packages.
-    """
-    def __init__(self, test, params):
-        super(YumInstaller, self).__init__(test, params)
-        # Checking if all required dependencies are available
-        os_dep.command("rpm")
-        os_dep.command("yum")
-
-        default_pkg_list = str(['qemu-kvm', 'qemu-kvm-tools'])
-        default_qemu_bin_paths = str(['/usr/bin/qemu-kvm', '/usr/bin/qemu-img'])
-        default_pkg_path_list = str(None)
-        self.pkg_list = eval(params.get("pkg_list", default_pkg_list))
-        self.pkg_path_list = eval(params.get("pkg_path_list",
-                                             default_pkg_path_list))
-        self.qemu_bin_paths = eval(params.get("qemu_bin_paths",
-                                              default_qemu_bin_paths))
-
-
-    def _clean_previous_installs(self):
-        kill_qemu_processes()
-        removable_packages = ""
-        for pkg in self.pkg_list:
-            removable_packages += " %s" % pkg
-
-        utils.system("yum remove -y %s" % removable_packages)
-
-
-    def _get_packages(self):
-        for pkg in self.pkg_path_list:
-            utils.get_file(pkg, os.path.join(self.srcdir,
-                                             os.path.basename(pkg)))
-
-
-    def _install_packages(self):
-        """
-        Install all downloaded packages.
-        """
-        os.chdir(self.srcdir)
-        utils.system("yum install --nogpgcheck -y *.rpm")
-
-
-    def install(self):
-        self._clean_previous_installs()
-        self._get_packages()
-        self._install_packages()
-        create_symlinks(test_bindir=self.test_bindir,
-                        bin_list=self.qemu_bin_paths)
-        if self.load_modules:
-            load_kvm_modules(load_stock=True, extra_modules=self.extra_modules)
-        if self.save_results:
-            save_build(self.srcdir, self.results_dir)
-
-
-class KojiInstaller(YumInstaller):
-    """
-    Class that handles installing KVM from the fedora build service, koji.
-    It uses yum to install and remove packages.
-    """
-    def __init__(self, test, params):
-        """
-        Gets parameters and initializes the package downloader.
-
-        @param test: kvm test object
-        @param params: Dictionary with test arguments
-        """
-        super(KojiInstaller, self).__init__(test, params)
-        default_koji_cmd = '/usr/bin/koji'
-        default_src_pkg = 'qemu'
-        self.src_pkg = params.get("src_pkg", default_src_pkg)
-        self.tag = params.get("koji_tag", None)
-        self.build = params.get("koji_build", None)
-        koji_cmd = params.get("koji_cmd", default_koji_cmd)
-        self.downloader = kvm_utils.KojiDownloader(cmd=koji_cmd)
-
-
-    def _get_packages(self):
-        """
-        Downloads the specific arch RPMs for the specific build name.
-        """
-        self.downloader.get(src_package=self.src_pkg, tag=self.tag,
-                            build=self.build, dst_dir=self.srcdir)
-
-
-    def install(self):
-        super(KojiInstaller, self)._clean_previous_installs()
-        self._get_packages()
-        super(KojiInstaller, self)._install_packages()
-        create_symlinks(test_bindir=self.test_bindir,
-                        bin_list=self.qemu_bin_paths)
-        if self.load_modules:
-            load_kvm_modules(load_stock=True, extra_modules=self.extra_modules)
-        if self.save_results:
-            save_build(self.srcdir, self.results_dir)
-
-
-class SourceDirInstaller(BaseInstaller):
-    """
-    Class that handles building/installing KVM directly from a tarball or
-    a single source code dir.
-    """
-    def __init__(self, test, params):
-        """
-        Initializes class attributes, and retrieves KVM code.
-
-        @param test: kvm test object
-        @param params: Dictionary with test arguments
-        """
-        super(SourceDirInstaller, self).__init__(test, params)
-
-        install_mode = params["mode"]
-        srcdir = params.get("srcdir", None)
-
-        if install_mode == 'localsrc':
-            if srcdir is None:
-                raise error.TestError("Install from source directory specified"
-                                      "but no source directory provided on the"
-                                      "control file.")
-            else:
-                shutil.copytree(srcdir, self.srcdir)
-
-        if install_mode == 'release':
-            release_tag = params.get("release_tag")
-            release_dir = params.get("release_dir")
-            release_listing = params.get("release_listing")
-            logging.info("Installing KVM from release tarball")
-            if not release_tag:
-                release_tag = kvm_utils.get_latest_kvm_release_tag(
-                                                                release_listing)
-            tarball = os.path.join(release_dir, 'kvm', release_tag,
-                                   "kvm-%s.tar.gz" % release_tag)
-            logging.info("Retrieving release kvm-%s" % release_tag)
-            tarball = utils.unmap_url("/", tarball, "/tmp")
-
-        elif install_mode == 'snapshot':
-            logging.info("Installing KVM from snapshot")
-            snapshot_dir = params.get("snapshot_dir")
-            if not snapshot_dir:
-                raise error.TestError("Snapshot dir not provided")
-            snapshot_date = params.get("snapshot_date")
-            if not snapshot_date:
-                # Take yesterday's snapshot
-                d = (datetime.date.today() -
-                     datetime.timedelta(1)).strftime("%Y%m%d")
-            else:
-                d = snapshot_date
-            tarball = os.path.join(snapshot_dir, "kvm-snapshot-%s.tar.gz" % d)
-            logging.info("Retrieving kvm-snapshot-%s" % d)
-            tarball = utils.unmap_url("/", tarball, "/tmp")
-
-        elif install_mode == 'localtar':
-            tarball = params.get("tarball")
-            if not tarball:
-                raise error.TestError("KVM Tarball install specified but no"
-                                      " tarball provided on control file.")
-            logging.info("Installing KVM from a local tarball")
-            logging.info("Using tarball %s")
-            tarball = utils.unmap_url("/", params.get("tarball"), "/tmp")
-
-        if install_mode in ['release', 'snapshot', 'localtar']:
-            utils.extract_tarball_to_dir(tarball, self.srcdir)
-
-        if install_mode in ['release', 'snapshot', 'localtar', 'srcdir']:
-            self.repo_type = kvm_utils.check_kvm_source_dir(self.srcdir)
-            configure_script = os.path.join(self.srcdir, 'configure')
-            self.configure_options = check_configure_options(configure_script)
-
-
-    def _build(self):
-        make_jobs = utils.count_cpus()
-        os.chdir(self.srcdir)
-        # For testing purposes, it's better to build qemu binaries with
-        # debugging symbols, so we can extract more meaningful stack traces.
-        cfg = "./configure --prefix=%s" % self.prefix
-        if "--disable-strip" in self.configure_options:
-            cfg += " --disable-strip"
-        steps = [cfg, "make clean", "make -j %s" % make_jobs]
-        logging.info("Building KVM")
-        for step in steps:
-            utils.system(step)
-
-
-    def _install(self):
-        os.chdir(self.srcdir)
-        logging.info("Installing KVM userspace")
-        if self.repo_type == 1:
-            utils.system("make -C qemu install")
-        elif self.repo_type == 2:
-            utils.system("make install")
-        create_symlinks(self.test_bindir, self.prefix)
-
-
-    def _load_modules(self):
-        load_kvm_modules(module_dir=self.srcdir,
-                         extra_modules=self.extra_modules)
-
-
-    def install(self):
-        self._build()
-        self._install()
-        if self.load_modules:
-            self._load_modules()
-        if self.save_results:
-            save_build(self.srcdir, self.results_dir)
-
-
-class GitInstaller(SourceDirInstaller):
-    def __init__(self, test, params):
-        """
-        Initialize class parameters and retrieves code from git repositories.
-
-        @param test: kvm test object.
-        @param params: Dictionary with test parameters.
-        """
-        super(GitInstaller, self).__init__(test, params)
-
-        kernel_repo = params.get("git_repo")
-        user_repo = params.get("user_git_repo")
-        kmod_repo = params.get("kmod_repo")
-        test_repo = params.get("test_git_repo")
-
-        kernel_branch = params.get("kernel_branch", "master")
-        user_branch = params.get("user_branch", "master")
-        kmod_branch = params.get("kmod_branch", "master")
-        test_branch = params.get("test_branch", "master")
-
-        kernel_lbranch = params.get("kernel_lbranch", "master")
-        user_lbranch = params.get("user_lbranch", "master")
-        kmod_lbranch = params.get("kmod_lbranch", "master")
-        test_lbranch = params.get("test_lbranch", "master")
-
-        kernel_commit = params.get("kernel_commit", None)
-        user_commit = params.get("user_commit", None)
-        kmod_commit = params.get("kmod_commit", None)
-        test_commit = params.get("test_commit", None)
-
-        kernel_patches = eval(params.get("kernel_patches", "[]"))
-        user_patches = eval(params.get("user_patches", "[]"))
-        kmod_patches = eval(params.get("user_patches", "[]"))
-
-        if not user_repo:
-            message = "KVM user git repository path not specified"
-            logging.error(message)
-            raise error.TestError(message)
-
-        userspace_srcdir = os.path.join(self.srcdir, "kvm_userspace")
-        kvm_utils.get_git_branch(user_repo, user_branch, userspace_srcdir,
-                                 user_commit, user_lbranch)
-        self.userspace_srcdir = userspace_srcdir
-
-        if user_patches:
-            os.chdir(self.userspace_srcdir)
-            for patch in user_patches:
-                utils.get_file(patch, os.path.join(self.userspace_srcdir,
-                                                   os.path.basename(patch)))
-                utils.system('patch -p1 %s' % os.path.basename(patch))
-
-        if test_repo:
-            test_srcdir = os.path.join(self.srcdir, "kvm-unit-tests")
-            kvm_utils.get_git_branch(test_repo, test_branch, test_srcdir,
-                                     test_commit, test_lbranch)
-            unittest_cfg = os.path.join(test_srcdir, 'x86',
-                                        'unittests.cfg')
-            self.test_srcdir = test_srcdir
-        else:
-            unittest_cfg = os.path.join(userspace_srcdir, 'kvm', 'test', 'x86',
-                                        'unittests.cfg')
-
-        self.unittest_cfg = None
-        if os.path.isfile(unittest_cfg):
-            self.unittest_cfg = unittest_cfg
-
-        if kernel_repo:
-            kernel_srcdir = os.path.join(self.srcdir, "kvm")
-            kvm_utils.get_git_branch(kernel_repo, kernel_branch, kernel_srcdir,
-                                     kernel_commit, kernel_lbranch)
-            self.kernel_srcdir = kernel_srcdir
-            if kernel_patches:
-                os.chdir(self.kernel_srcdir)
-                for patch in kernel_patches:
-                    utils.get_file(patch, os.path.join(self.userspace_srcdir,
-                                                       os.path.basename(patch)))
-                    utils.system('patch -p1 %s' % os.path.basename(patch))
-        else:
-            self.kernel_srcdir = None
-
-        if kmod_repo:
-            kmod_srcdir = os.path.join (self.srcdir, "kvm_kmod")
-            kvm_utils.get_git_branch(kmod_repo, kmod_branch, kmod_srcdir,
-                                     kmod_commit, kmod_lbranch)
-            self.kmod_srcdir = kmod_srcdir
-            if kmod_patches:
-                os.chdir(self.kmod_srcdir)
-                for patch in kmod_patches:
-                    utils.get_file(patch, os.path.join(self.userspace_srcdir,
-                                                       os.path.basename(patch)))
-                    utils.system('patch -p1 %s' % os.path.basename(patch))
-        else:
-            self.kmod_srcdir = None
-
-        configure_script = os.path.join(self.userspace_srcdir, 'configure')
-        self.configure_options = check_configure_options(configure_script)
-
-
-    def _build(self):
-        make_jobs = utils.count_cpus()
-        cfg = './configure'
-        self.modules_build_succeed = False
-        if self.kmod_srcdir:
-            logging.info('Building KVM modules')
-            os.chdir(self.kmod_srcdir)
-            module_build_steps = [cfg,
-                                  'make clean',
-                                  'make sync LINUX=%s' % self.kernel_srcdir,
-                                  'make']
-        elif self.kernel_srcdir:
-            logging.info('Building KVM modules')
-            os.chdir(self.userspace_srcdir)
-            cfg += ' --kerneldir=%s' % self.host_kernel_srcdir
-            module_build_steps = [cfg,
-                            'make clean',
-                            'make -C kernel LINUX=%s sync' % self.kernel_srcdir]
-        else:
-            module_build_steps = []
-
-        try:
-            if module_build_steps:
-                for step in module_build_steps:
-                    utils.run(step)
-                self.modules_build_succeed = True
-        except error.CmdError, e:
-            logging.error("KVM modules build failed to build: %s" % e)
-
-        logging.info('Building KVM userspace code')
-        os.chdir(self.userspace_srcdir)
-        cfg += ' --prefix=%s' % self.prefix
-        if "--disable-strip" in self.configure_options:
-            cfg += ' --disable-strip'
-        if self.extra_configure_options:
-            cfg += ' %s' % self.extra_configure_options
-        utils.system(cfg)
-        utils.system('make clean')
-        utils.system('make -j %s' % make_jobs)
-
-        self.unittest_prefix = None
-        if self.unittest_cfg:
-            os.chdir(os.path.dirname(os.path.dirname(self.unittest_cfg)))
-            utils.system('./configure --prefix=%s' % self.prefix)
-            utils.system('make')
-            utils.system('make install')
-            self.unittest_prefix = os.path.join(self.prefix, 'share', 'qemu',
-                                                'tests')
-
-
-    def _install(self):
-        os.chdir(self.userspace_srcdir)
-        utils.system('make install')
-        create_symlinks(test_bindir=self.test_bindir, prefix=self.prefix,
-                        bin_list=None,
-                        unittest=self.unittest_prefix)
-
-
-    def _load_modules(self):
-        if self.kmod_srcdir and self.modules_build_succeed:
-            load_kvm_modules(module_dir=self.kmod_srcdir,
-                             extra_modules=self.extra_modules)
-        elif self.kernel_srcdir and self.modules_build_succeed:
-            load_kvm_modules(module_dir=self.userspace_srcdir,
-                             extra_modules=self.extra_modules)
-        else:
-            logging.info("Loading stock KVM modules")
-            load_kvm_modules(load_stock=True,
-                             extra_modules=self.extra_modules)
-
-
-    def install(self):
-        self._build()
-        self._install()
-        if self.load_modules:
-            self._load_modules()
-        if self.save_results:
-            save_build(self.srcdir, self.results_dir)
-
+import installer
 
 def run_build(test, params, env):
     """
@@ -595,20 +9,17 @@
     @param params: Dictionary with test parameters.
     @param env: Test environment.
     """
-    install_mode = params.get("mode")
     srcdir = params.get("srcdir", test.srcdir)
     params["srcdir"] = srcdir
 
-    if install_mode in ['localsrc', 'localtar', 'release', 'snapshot']:
-        installer = SourceDirInstaller(test, params)
-    elif install_mode == 'git':
-        installer = GitInstaller(test, params)
-    elif install_mode == 'yum':
-        installer = YumInstaller(test, params)
-    elif install_mode == 'koji':
-        installer = KojiInstaller(test, params)
-    else:
-        raise error.TestError('Invalid or unsupported'
-                              ' install mode: %s' % install_mode)
-
-    installer.install()
+    try:
+        installer_object = installer.make_installer(params)
+        installer_object.set_install_params(test, params)
+        installer_object.install()
+        env.register_installer(installer_object)
+    except Exception,e:
+        # if the build/install fails, don't allow other tests
+        # to get a installer.
+        msg = "KVM install failed: %s" % (e)
+        env.register_installer(installer.FailedInstaller(msg))
+        raise
diff --git a/client/tests/kvm/tests/clock_getres.py b/client/tests/kvm/tests/clock_getres.py
new file mode 100644
index 0000000..f85bb26
--- /dev/null
+++ b/client/tests/kvm/tests/clock_getres.py
@@ -0,0 +1,39 @@
+import logging, time, os
+from autotest_lib.client.common_lib import error
+from autotest_lib.client.common_lib import utils
+import kvm_test_utils, kvm_utils
+
+
+def run_clock_getres(test, params, env):
+    """
+    Verify if guests using kvm-clock as the time source have a sane clock
+    resolution.
+
+    @param test: kvm test object.
+    @param params: Dictionary with test parameters.
+    @param env: Dictionary with the test environment.
+    """
+    t_name = "test_clock_getres"
+    base_dir = "/tmp"
+
+    deps_dir = os.path.join(test.bindir, "deps", t_name)
+    os.chdir(deps_dir)
+    try:
+        utils.system("make clean")
+        utils.system("make")
+    except:
+        raise error.TestError("Failed to compile %s" % t_name)
+
+    test_clock = os.path.join(deps_dir, t_name)
+    if not os.path.isfile(test_clock):
+        raise error.TestError("Could not find %s" % t_name)
+
+    vm = kvm_test_utils.get_living_vm(env, params.get("main_vm"))
+    timeout = int(params.get("login_timeout", 360))
+    session = kvm_test_utils.wait_for_login(vm, timeout=timeout)
+    if not vm.copy_files_to(test_clock, base_dir):
+        raise error.TestError("Failed to copy %s to VM" % t_name)
+    session.cmd(os.path.join(base_dir, t_name))
+    logging.info("PASS: Guest reported appropriate clock resolution")
+    logging.info("guest's dmesg:")
+    session.cmd_output("dmesg")
diff --git a/client/tests/kvm/tests/ethtool.py b/client/tests/kvm/tests/ethtool.py
index 56b1c70..8951fcb 100644
--- a/client/tests/kvm/tests/ethtool.py
+++ b/client/tests/kvm/tests/ethtool.py
@@ -1,7 +1,7 @@
 import logging, commands, re
 from autotest_lib.client.common_lib import error
 from autotest_lib.client.bin import utils
-import kvm_test_utils, kvm_utils
+import kvm_test_utils, kvm_utils, kvm_subprocess
 
 def run_ethtool(test, params, env):
     """
@@ -32,7 +32,7 @@
             'gro': 'generic.*receive.*offload',
             'lro': 'large.*receive.*offload',
             }
-        s, o = session.get_command_status_output("ethtool -k %s" % ethname)
+        o = session.cmd("ethtool -k %s" % ethname)
         try:
             return re.findall("%s: (.*)" % feature_pattern.get(type), o)[0]
         except IndexError:
@@ -51,7 +51,11 @@
             return False
         cmd = "ethtool -K %s %s %s" % (ethname, type, status)
         if ethtool_get(type) != status:
-            return session.get_command_status(cmd) == 0
+            try:
+                session.cmd(cmd)
+                return True
+            except:
+                return False
         if ethtool_get(type) != status:
             logging.error("Fail to set %s %s" % (type, status))
             return False
@@ -74,7 +78,7 @@
         logging.info("Compare md5sum of the files on guest and host")
         host_result = utils.hash_file(name, method="md5")
         try:
-            o = session.get_command_output("md5sum %s" % name)
+            o = session.cmd_output("md5sum %s" % name)
             guest_result = re.findall("\w+", o)[0]
         except IndexError:
             logging.error("Could not get file md5sum in guest")
@@ -92,13 +96,13 @@
         @param src: Source host of transfer file
         @return: Tuple (status, error msg/tcpdump result)
         """
-        session2.get_command_status("rm -rf %s" % filename)
-        dd_cmd = "dd if=/dev/urandom of=%s bs=1M count=%s" % (filename,
-                                                   params.get("filesize"))
+        session2.cmd_output("rm -rf %s" % filename)
+        dd_cmd = ("dd if=/dev/urandom of=%s bs=1M count=%s" %
+                  (filename, params.get("filesize")))
         logging.info("Creat file in source host, cmd: %s" % dd_cmd)
         tcpdump_cmd = "tcpdump -lep -s 0 tcp -vv port ssh"
         if src == "guest":
-            s = session.get_command_status(dd_cmd, timeout=360)
+            session.cmd_output(dd_cmd, timeout=360)
             tcpdump_cmd += " and src %s" % guest_ip
             copy_files_fun = vm.copy_files_from
         else:
@@ -115,18 +119,18 @@
             tcpdump_cmd += " and not port %s" % i
         logging.debug("Listen by command: %s" % tcpdump_cmd)
         session2.sendline(tcpdump_cmd)
-        if not kvm_utils.wait_for(lambda: session.get_command_status(
-                                           "pgrep tcpdump") == 0, 30):
+        if not kvm_utils.wait_for(
+                           lambda:session.cmd_status("pgrep tcpdump") == 0, 30):
             return (False, "Tcpdump process wasn't launched")
 
         logging.info("Start to transfer file")
         if not copy_files_fun(filename, filename):
             return (False, "Child process transfer file failed")
         logging.info("Transfer file completed")
-        if session.get_command_status("killall tcpdump") != 0:
-            return (False, "Could not kill all tcpdump process")
-        s, tcpdump_string = session2.read_up_to_prompt(timeout=60)
-        if not s:
+        session.cmd("killall tcpdump")
+        try:
+            tcpdump_string = session2.read_up_to_prompt(timeout=60)
+        except kvm_subprocess.ExpectError:
             return (False, "Fail to read tcpdump's output")
 
         if not compare_md5sum(filename):
@@ -173,8 +177,7 @@
     session = kvm_test_utils.wait_for_login(vm,
                   timeout=int(params.get("login_timeout", 360)))
     # Let's just error the test if we identify that there's no ethtool installed
-    if session.get_command_status("ethtool -h"):
-        raise error.TestError("Command ethtool not installed on guest")
+    session.cmd("ethtool -h")
     session2 = kvm_test_utils.wait_for_login(vm,
                   timeout=int(params.get("login_timeout", 360)))
     mtu = 1514
diff --git a/client/tests/kvm/tests/file_transfer.py b/client/tests/kvm/tests/file_transfer.py
index e872bed..f02078e 100644
--- a/client/tests/kvm/tests/file_transfer.py
+++ b/client/tests/kvm/tests/file_transfer.py
@@ -24,13 +24,17 @@
     if not session:
         raise error.TestFail("Could not log into guest '%s'" % vm.name)
 
-    dir = test.tmpdir
+    dir_name = test.tmpdir
     transfer_timeout = int(params.get("transfer_timeout"))
     transfer_type = params.get("transfer_type")
     tmp_dir = params.get("tmp_dir", "/tmp/")
     clean_cmd = params.get("clean_cmd", "rm -f")
     filesize = int(params.get("filesize", 4000))
-    cmd = "dd if=/dev/urandom of=%s/a.out bs=1M count=%d" % (dir, filesize)
+    count = int(filesize / 10)
+    if count == 0:
+        count = 1
+    cmd = "dd if=/dev/zero of=%s/a.out bs=10M count=%d" % (dir_name,
+                                                           count)
     guest_path = tmp_dir + "b.out"
 
     try:
@@ -41,7 +45,7 @@
             logging.info("Transfering file host -> guest, timeout: %ss",
                          transfer_timeout)
             t_begin = time.time()
-            success = vm.copy_files_to("%s/a.out" % dir, guest_path,
+            success = vm.copy_files_to("%s/a.out" % dir_name, guest_path,
                                        timeout=transfer_timeout)
             t_end = time.time()
             throughput = filesize / (t_end - t_begin)
@@ -53,7 +57,7 @@
             logging.info("Transfering file guest -> host, timeout: %ss",
                          transfer_timeout)
             t_begin = time.time()
-            success = vm.copy_files_from(guest_path, "%s/c.out" % dir,
+            success = vm.copy_files_from(guest_path, "%s/c.out" % dir_name,
                                          timeout=transfer_timeout)
             t_end = time.time()
             throughput = filesize / (t_end - t_begin)
@@ -66,12 +70,12 @@
                                   transfer_type)
 
         for f in ['a.out', 'c.out']:
-            p = os.path.join(dir, f)
+            p = os.path.join(dir_name, f)
             size = os.path.getsize(p)
             logging.debug('Size of %s: %sB', f, size)
 
-        md5_orig = utils.hash_file("%s/a.out" % dir, method="md5")
-        md5_new = utils.hash_file("%s/c.out" % dir, method="md5")
+        md5_orig = utils.hash_file("%s/a.out" % dir_name, method="md5")
+        md5_new = utils.hash_file("%s/c.out" % dir_name, method="md5")
 
         if md5_orig != md5_new:
             raise error.TestFail("File changed after transfer host -> guest "
@@ -80,11 +84,11 @@
     finally:
         logging.info('Cleaning temp file on guest')
         clean_cmd += " %s" % guest_path
-        s, o = session.get_command_status_output(clean_cmd)
-        if s:
-            logging.warning("Failed to clean remote file %s, output:%s",
-                            guest_path, o)
+        session.cmd(clean_cmd)
         logging.info('Cleaning temp files on host')
-        os.remove('%s/a.out' % dir)
-        os.remove('%s/c.out' % dir)
+        try:
+            os.remove('%s/a.out' % dir_name)
+            os.remove('%s/c.out' % dir_name)
+        except OSError:
+            pass
         session.close()
diff --git a/client/tests/kvm/tests/guest_s4.py b/client/tests/kvm/tests/guest_s4.py
index 2eb035b..0280f71 100644
--- a/client/tests/kvm/tests/guest_s4.py
+++ b/client/tests/kvm/tests/guest_s4.py
@@ -16,11 +16,7 @@
     session = kvm_test_utils.wait_for_login(vm, timeout=timeout)
 
     logging.info("Checking whether guest OS supports suspend to disk (S4)...")
-    s, o = session.get_command_status_output(params.get("check_s4_support_cmd"))
-    if "not enough space" in o:
-        raise error.TestError("Check S4 support failed: %s" % o)
-    elif s != 0:
-        raise error.TestNAError("Guest OS does not support S4")
+    session.cmd(params.get("check_s4_support_cmd"))
 
     logging.info("Waiting until all guest OS services are fully started...")
     time.sleep(float(params.get("services_up_timeout", 30)))
@@ -36,9 +32,7 @@
 
     # Make sure the background program is running as expected
     check_s4_cmd = params.get("check_s4_cmd")
-    if session2.get_command_status(check_s4_cmd) != 0:
-        raise error.TestError("Failed to launch '%s' as a background process" %
-                              test_s4_cmd)
+    session2.cmd(check_s4_cmd)
     logging.info("Launched background command in guest: %s" % test_s4_cmd)
 
     # Suspend to disk
@@ -68,11 +62,9 @@
 
     # Check whether the test command is still alive
     logging.info("Checking if background command is still alive...")
-    if session2.get_command_status(check_s4_cmd) != 0:
-        raise error.TestFail("Background command '%s' stopped running. S4 "
-                             "failed." % test_s4_cmd)
+    session2.cmd(check_s4_cmd)
 
     logging.info("VM resumed successfuly after suspend to disk")
-    session2.get_command_output(params.get("kill_test_s4_cmd"))
+    session2.cmd_output(params.get("kill_test_s4_cmd"))
     session.close()
     session2.close()
diff --git a/client/tests/kvm/tests/guest_test.py b/client/tests/kvm/tests/guest_test.py
index b6bebc7..b9786b5 100644
--- a/client/tests/kvm/tests/guest_test.py
+++ b/client/tests/kvm/tests/guest_test.py
@@ -20,7 +20,9 @@
     reboot = params.get("reboot", "no")
 
     vm = kvm_test_utils.get_living_vm(env, params.get("main_vm"))
-    session = kvm_test_utils.wait_for_login(vm, timeout=login_timeout)
+    serial_login = (params.get("serial_login", "no") == "yes")
+    session = kvm_test_utils.wait_for_login(vm, timeout=login_timeout,
+                                            serial=serial_login)
 
     if reboot == "yes":
         logging.debug("Rebooting guest before test ...")
@@ -48,34 +50,25 @@
             # Change dir to dst_rsc_dir, and remove the guest script dir there
             rm_cmd = "cd %s && (rmdir /s /q %s || del /s /q %s)" % \
                      (dst_rsc_dir, rsc_dir, rsc_dir)
-            if session.get_command_status(rm_cmd, timeout=test_timeout) != 0:
-                raise error.TestFail("Remove %s failed." % rsc_dir)
+            session.cmd(rm_cmd, timeout=test_timeout)
             logging.debug("Clean directory succeeded.")
 
             # then download the resource.
             rsc_cmd = "cd %s && %s %s" %(dst_rsc_dir, download_cmd, rsc_server)
-            if session.get_command_status(rsc_cmd, timeout=test_timeout) != 0:
-                raise error.TestFail("Download test resource failed.")
+            session.cmd(rsc_cmd, timeout=test_timeout)
             logging.info("Download resource finished.")
         else:
-            session.get_command_output("del %s" % dst_rsc_path,
-                                       internal_timeout=0)
+            session.cmd_output("del %s" % dst_rsc_path, internal_timeout=0)
             script_path = kvm_utils.get_path(test.bindir, script)
             vm.copy_files_to(script_path, dst_rsc_path, timeout=60)
 
-        command = "cmd /c %s %s %s" %(interpreter, dst_rsc_path, script_params)
+        cmd = "%s %s %s" % (interpreter, dst_rsc_path, script_params)
 
-        logging.info("---------------- Script output ----------------")
-        status = session.get_command_status(command,
-                                            print_func=logging.info,
-                                            timeout=test_timeout)
-        logging.info("---------------- End of script output ----------------")
-
-        if status is None:
-            raise error.TestFail("Timeout expired before script execution "
-                                 "completed (or something weird happened)")
-        if status != 0:
-            raise error.TestFail("Script execution failed")
+        try:
+            logging.info("------------ Script output ------------")
+            session.cmd(cmd, print_func=logging.info, timeout=test_timeout)
+        finally:
+            logging.info("------------ End of script output ------------")
 
         if reboot == "yes":
             logging.debug("Rebooting guest after test ...")
diff --git a/client/tests/kvm/tests/image_copy.py b/client/tests/kvm/tests/image_copy.py
new file mode 100644
index 0000000..87bafea
--- /dev/null
+++ b/client/tests/kvm/tests/image_copy.py
@@ -0,0 +1,46 @@
+import os, logging, commands
+from autotest_lib.client.common_lib import error
+from autotest_lib.client.bin import utils
+import kvm_utils
+
+
+def run_image_copy(test, params, env):
+    """
+    Copy guest images from nfs server.
+    1) Mount the NFS share directory
+    2) Check the existence of source image
+    3) If it exists, copy the image from NFS
+
+    @param test: kvm test object
+    @param params: Dictionary with the test parameters
+    @param env: Dictionary with test environment.
+    """
+    mount_dest_dir = params.get('dst_dir', '/mnt/images')
+    if not os.path.exists(mount_dest_dir):
+        try:
+            os.makedirs(mount_dest_dir)
+        except OSError, err:
+            logging.warning('mkdir %s error:\n%s', mount_dest_dir, err)
+
+    if not os.path.exists(mount_dest_dir):
+        raise error.TestError('Failed to create NFS share dir %s' %
+                              mount_dest_dir)
+
+    src = params.get('images_good')
+    mnt_cmd = 'mount %s %s -o ro' % (src, mount_dest_dir)
+    image = '%s.%s' % (os.path.split(params['image_name'])[1],
+                       params['image_format'])
+    src_path = os.path.join(mount_dest_dir, image)
+    dst_path = '%s.%s' % (params['image_name'], params['image_format'])
+    cmd = 'cp %s %s' % (src_path, dst_path)
+
+    if not kvm_utils.mount(src, mount_dest_dir, 'nfs', 'ro'):
+        raise error.TestError('Could not mount NFS share %s to %s' %
+                              (src, mount_dest_dir))
+
+    # Check the existence of source image
+    if not os.path.exists(src_path):
+        raise error.TestError('Could not find %s in NFS share' % src_path)
+
+    logging.debug('Copying image %s...' % image)
+    utils.system(cmd)
diff --git a/client/tests/kvm/tests/iofuzz.py b/client/tests/kvm/tests/iofuzz.py
index 45a0eb9..e77540e 100644
--- a/client/tests/kvm/tests/iofuzz.py
+++ b/client/tests/kvm/tests/iofuzz.py
@@ -33,11 +33,10 @@
         logging.debug("outb(0x%x, 0x%x)", port, data)
         outb_cmd = ("echo -e '\\%s' | dd of=/dev/port seek=%d bs=1 count=1" %
                     (oct(data), port))
-        s, o = session.get_command_status_output(outb_cmd)
-        if s is None:
-            logging.debug("Command did not return")
-        if s != 0:
-            logging.debug("Command returned status %s", s)
+        try:
+            session.cmd(outb_cmd)
+        except kvm_subprocess.ShellError, e:
+            logging.debug(e)
 
 
     def inb(session, port):
@@ -49,11 +48,10 @@
         """
         logging.debug("inb(0x%x)", port)
         inb_cmd = "dd if=/dev/port seek=%d of=/dev/null bs=1 count=1" % port
-        s, o = session.get_command_status_output(inb_cmd)
-        if s is None:
-            logging.debug("Command did not return")
-        if s != 0:
-            logging.debug("Command returned status %s", s)
+        try:
+            session.cmd(inb_cmd)
+        except kvm_subprocess.ShellError, e:
+            logging.debug(e)
 
 
     def fuzz(session, inst_list):
@@ -100,7 +98,7 @@
         r = random.SystemRandom()
 
         logging.info("Enumerate guest devices through /proc/ioports")
-        ioports = session.get_command_output("cat /proc/ioports")
+        ioports = session.cmd_output("cat /proc/ioports")
         logging.debug(ioports)
         devices = re.findall("(\w+)-(\w+)\ : (.*)", ioports)
 
diff --git a/client/tests/kvm/tests/ioquit.py b/client/tests/kvm/tests/ioquit.py
index 8126139..25d3ac9 100644
--- a/client/tests/kvm/tests/ioquit.py
+++ b/client/tests/kvm/tests/ioquit.py
@@ -20,17 +20,13 @@
     try:
         bg_cmd = params.get("background_cmd")
         logging.info("Add IO workload for guest OS.")
-        (s, o) = session.get_command_status_output(bg_cmd, timeout=60)
+        session.cmd_output(bg_cmd, timeout=60)
         check_cmd = params.get("check_cmd")
-        (s, o) = session2.get_command_status_output(check_cmd, timeout=60)
-        if s:
-            raise error.TestError("Fail to add IO workload for Guest OS")
+        session2.cmd(check_cmd, timeout=60)
 
         logging.info("Sleep for a while")
         time.sleep(random.randrange(30,100))
-        (s, o) = session2.get_command_status_output(check_cmd, timeout=60)
-        if s:
-            logging.info("IO workload finished before the VM was killed")
+        session2.cmd(check_cmd, timeout=60)
         logging.info("Kill the virtual machine")
         vm.process.close()
     finally:
diff --git a/client/tests/kvm/tests/iozone_windows.py b/client/tests/kvm/tests/iozone_windows.py
index a96fdfc..febf898 100644
--- a/client/tests/kvm/tests/iozone_windows.py
+++ b/client/tests/kvm/tests/iozone_windows.py
@@ -28,8 +28,8 @@
     c = params.get("iozone_cmd")
     t = int(params.get("iozone_timeout"))
     logging.info("Running IOzone command on guest, timeout %ss", t)
-    results = session.get_command_output(command=c, timeout=t,
-                                         print_func=logging.debug)
+    results = session.cmd_output(command=c, timeout=t,
+                                 print_func=logging.debug)
     utils.open_write_close(results_path, results)
 
     # Postprocess the results using the IOzone postprocessing module
diff --git a/client/tests/kvm/tests/jumbo.py b/client/tests/kvm/tests/jumbo.py
index 2c91c83..1fbce8b 100644
--- a/client/tests/kvm/tests/jumbo.py
+++ b/client/tests/kvm/tests/jumbo.py
@@ -40,11 +40,7 @@
 
         logging.info("Changing the MTU of guest ...")
         guest_mtu_cmd = "ifconfig %s mtu %s" % (ethname , mtu)
-        s, o = session.get_command_status_output(guest_mtu_cmd)
-        if s != 0:
-            logging.error(o)
-            raise error.TestError("Fail to set the MTU of guest NIC: %s" %
-                                  ethname)
+        session.cmd(guest_mtu_cmd)
 
         logging.info("Chaning the MTU of host tap ...")
         host_mtu_cmd = "ifconfig %s mtu %s" % (ifname, mtu)
diff --git a/client/tests/kvm/tests/kdump.py b/client/tests/kvm/tests/kdump.py
new file mode 100644
index 0000000..ccc4307
--- /dev/null
+++ b/client/tests/kvm/tests/kdump.py
@@ -0,0 +1,74 @@
+import logging, time
+from autotest_lib.client.common_lib import error
+import kvm_subprocess, kvm_test_utils, kvm_utils
+
+
+def run_kdump(test, params, env):
+    """
+    KVM reboot test:
+    1) Log into a guest
+    2) Check and enable the kdump
+    3) For each vcpu, trigger a crash and check the vmcore
+
+    @param test: kvm test object
+    @param params: Dictionary with the test parameters
+    @param env: Dictionary with test environment.
+    """
+    vm = kvm_test_utils.get_living_vm(env, params.get("main_vm"))
+    timeout = float(params.get("login_timeout", 240))
+    crash_timeout = float(params.get("crash_timeout", 360))
+    session = kvm_test_utils.wait_for_login(vm, 0, timeout, 0, 2)
+    def_kernel_param_cmd = ("grubby --update-kernel=`grubby --default-kernel`"
+                            " --args=crashkernel=128M")
+    kernel_param_cmd = params.get("kernel_param_cmd", def_kernel_param_cmd)
+    def_kdump_enable_cmd = "chkconfig kdump on && service kdump start"
+    kdump_enable_cmd = params.get("kdump_enable_cmd", def_kdump_enable_cmd)
+    def_crash_kernel_prob_cmd = "grep -q 1 /sys/kernel/kexec_crash_loaded"
+    crash_kernel_prob_cmd = params.get("crash_kernel_prob_cmd",
+                                       def_crash_kernel_prob_cmd)
+
+    def crash_test(vcpu):
+        """
+        Trigger a crash dump through sysrq-trigger
+
+        @param vcpu: vcpu which is used to trigger a crash
+        """
+        session = kvm_test_utils.wait_for_login(vm, 0, timeout, 0, 2)
+        session.cmd_output("rm -rf /var/crash/*")
+
+        logging.info("Triggering crash on vcpu %d ...", vcpu)
+        crash_cmd = "taskset -c %d echo c > /proc/sysrq-trigger" % vcpu
+        session.sendline(crash_cmd)
+
+        if not kvm_utils.wait_for(lambda: not session.is_responsive(), 240, 0,
+                                  1):
+            raise error.TestFail("Could not trigger crash on vcpu %d" % vcpu)
+
+        logging.info("Waiting for kernel crash dump to complete")
+        session = kvm_test_utils.wait_for_login(vm, 0, crash_timeout, 0, 2)
+
+        logging.info("Probing vmcore file...")
+        session.cmd("ls -R /var/crash | grep vmcore")
+        logging.info("Found vmcore.")
+
+        session.cmd_output("rm -rf /var/crash/*")
+
+    try:
+        logging.info("Checking the existence of crash kernel...")
+        try:
+            session.cmd(crash_kernel_prob_cmd)
+        except:
+            logging.info("Crash kernel is not loaded. Trying to load it")
+            session.cmd(kernel_param_cmd)
+            session = kvm_test_utils.reboot(vm, session, timeout=timeout)
+
+        logging.info("Enabling kdump service...")
+        # the initrd may be rebuilt here so we need to wait a little more
+        session.cmd(kdump_enable_cmd, timeout=120)
+
+        nvcpu = int(params.get("smp", 1))
+        for i in range (nvcpu):
+            crash_test(i)
+
+    finally:
+        session.close()
diff --git a/client/tests/kvm/tests/ksm_overcommit.py b/client/tests/kvm/tests/ksm_overcommit.py
index dd4a30d..c6368d3 100644
--- a/client/tests/kvm/tests/ksm_overcommit.py
+++ b/client/tests/kvm/tests/ksm_overcommit.py
@@ -27,12 +27,13 @@
         """
         logging.debug("Starting allocator.py on guest %s", vm.name)
         session.sendline("python /tmp/allocator.py")
-        (match, data) = session.read_until_last_line_matches(["PASS:", "FAIL:"],
-                                                             timeout)
-        if match == 1 or match is None:
-            raise error.TestFail("Command allocator.py on guest %s failed.\n"
-                                 "return code: %s\n output:\n%s" %
-                                 (vm.name, match, data))
+        try:
+            (match, data) = session.read_until_last_line_matches(
+                                                            ["PASS:", "FAIL:"],
+                                                            timeout)
+        except kvm_subprocess.ExpectProcessTerminatedError, e:
+            raise error.TestFail("Command allocator.py on vm '%s' failed: %s" %
+                                 (vm.name, str(e)))
 
 
     def _execute_allocator(command, vm, session, timeout):
@@ -50,12 +51,14 @@
         logging.debug("Executing '%s' on allocator.py loop, vm: %s, timeout: %s",
                       command, vm.name, timeout)
         session.sendline(command)
-        (match, data) = session.read_until_last_line_matches(["PASS:","FAIL:"],
+        try:
+            (match, data) = session.read_until_last_line_matches(
+                                                             ["PASS:","FAIL:"],
                                                              timeout)
-        if match == 1 or match is None:
-            raise error.TestFail("Failed to execute '%s' on allocator.py, "
-                                 "vm: %s, output:\n%s" %
-                                 (command, vm.name, data))
+        except kvm_subprocess.ExpectProcessTerminatedError, e:
+            e_str = ("Failed to execute command '%s' on allocator.py, "
+                     "vm '%s': %s" % (command, vm.name, str(e)))
+            raise error.TestFail(e_str)
         return (match, data)
 
 
@@ -80,9 +83,7 @@
             vm = lvms[lsessions.index(session)]
 
             logging.debug("Turning off swap on vm %s" % vm.name)
-            ret = session.get_command_status("swapoff -a", timeout=300)
-            if ret is None or ret:
-                raise error.TestFail("Failed to swapoff on VM %s" % vm.name)
+            session.cmd("swapoff -a", timeout=300)
 
             # Start the allocator
             _start_allocator(vm, session, 60 * perf_ratio)
@@ -232,7 +233,7 @@
                            (mem / 200 * 50 * perf_ratio))
         logging.debug(kvm_test_utils.get_memory_info([lvms[last_vm]]))
 
-        (status, data) = lsessions[i].get_command_status_output("die()", 20)
+        lsessions[i].cmd_output("die()", 20)
         lvms[last_vm].destroy(gracefully = False)
         logging.info("Phase 3b: PASS")
 
@@ -253,9 +254,7 @@
                 raise error.TestFail("Could not log into guest %s" %
                                      vm.name)
 
-        ret = session.get_command_status("swapoff -a", timeout=300)
-        if ret != 0:
-            raise error.TestFail("Failed to turn off swap on %s" % vm.name)
+        session.cmd("swapoff -a", timeout=300)
 
         for i in range(0, max_alloc):
             # Start the allocator
@@ -360,7 +359,7 @@
 
         logging.debug("Cleaning up...")
         for i in range(0, max_alloc):
-            lsessions[i].get_command_status_output("die()", 20)
+            lsessions[i].cmd_output("die()", 20)
         session.close()
         vm.destroy(gracefully = False)
 
@@ -545,7 +544,7 @@
 
     # Creating the first guest
     kvm_preprocessing.preprocess_vm(test, params, env, vm_name)
-    lvms.append(kvm_utils.env_get_vm(env, vm_name))
+    lvms.append(env.get_vm(vm_name))
     if not lvms[0]:
         raise error.TestError("VM object not found in environment")
     if not lvms[0].is_alive():
@@ -576,7 +575,7 @@
 
         # Last VM is later used to run more allocators simultaneously
         lvms.append(lvms[0].clone(vm_name, params))
-        kvm_utils.env_register_vm(env, vm_name, lvms[i])
+        env.register_vm(vm_name, lvms[i])
         params['vms'] += " " + vm_name
 
         logging.debug("Booting guest %s" % lvms[i].name)
diff --git a/client/tests/kvm/tests/linux_s3.py b/client/tests/kvm/tests/linux_s3.py
index 4a782b8..8a0f5eb 100644
--- a/client/tests/kvm/tests/linux_s3.py
+++ b/client/tests/kvm/tests/linux_s3.py
@@ -16,16 +16,12 @@
     session = kvm_test_utils.wait_for_login(vm, timeout=timeout)
 
     logging.info("Checking that VM supports S3")
-    status = session.get_command_status("grep -q mem /sys/power/state")
-    if status == None:
-        logging.error("Failed to check if S3 exists")
-    elif status != 0:
-        raise error.TestFail("Guest does not support S3")
+    session.cmd("grep -q mem /sys/power/state")
 
     logging.info("Waiting for a while for X to start")
     time.sleep(10)
 
-    src_tty = session.get_command_output("fgconsole").strip()
+    src_tty = session.cmd_output("fgconsole").strip()
     logging.info("Current virtual terminal is %s" % src_tty)
     if src_tty not in map(str, range(1,10)):
         raise error.TestFail("Got a strange current vt (%s)" % src_tty)
@@ -38,9 +34,7 @@
     command = "chvt %s && echo mem > /sys/power/state && chvt %s" % (dst_tty,
                                                                      src_tty)
     suspend_timeout = 120 + int(params.get("smp")) * 60
-    status = session.get_command_status(command, timeout=suspend_timeout)
-    if status != 0:
-        raise error.TestFail("Suspend to mem failed")
+    session.cmd(command, timeout=suspend_timeout)
 
     logging.info("VM resumed after S3")
 
diff --git a/client/tests/kvm/tests/mac_change.py b/client/tests/kvm/tests/mac_change.py
index c614e15..78fbab2 100644
--- a/client/tests/kvm/tests/mac_change.py
+++ b/client/tests/kvm/tests/mac_change.py
@@ -17,12 +17,10 @@
     """
     timeout = int(params.get("login_timeout", 360))
     vm = kvm_test_utils.get_living_vm(env, params.get("main_vm"))
-    logging.info("Trying to log into guest '%s' by serial", vm.name)
-    session = kvm_utils.wait_for(lambda: vm.serial_login(),
-                                  timeout, 0, step=2)
-    if not session:
-        raise error.TestFail("Could not log into guest '%s'" % vm.name)
-
+    session_serial = kvm_test_utils.wait_for_login(vm, 0, timeout, 0, 2,
+                                                   serial=True)
+    # This session will be used to assess whether the IP change worked
+    session = kvm_test_utils.wait_for_login(vm, 0, timeout, 0, 2)
     old_mac = vm.get_mac_address(0)
     while True:
         vm.free_mac_address(0)
@@ -30,23 +28,21 @@
         if old_mac != new_mac:
             break
     logging.info("The initial MAC address is %s", old_mac)
-    interface = kvm_test_utils.get_linux_ifname(session, old_mac)
+    interface = kvm_test_utils.get_linux_ifname(session_serial, old_mac)
     # Start change MAC address
     logging.info("Changing MAC address to %s", new_mac)
     change_cmd = ("ifconfig %s down && ifconfig %s hw ether %s && "
                   "ifconfig %s up" % (interface, interface, new_mac, interface))
-    if session.get_command_status(change_cmd) != 0:
-        raise error.TestFail("Fail to send mac_change command")
+    session_serial.cmd(change_cmd)
 
     # Verify whether MAC address was changed to the new one
     logging.info("Verifying the new mac address")
-    if session.get_command_status("ifconfig | grep -i %s" % new_mac) != 0:
-        raise error.TestFail("Fail to change MAC address")
+    session_serial.cmd("ifconfig | grep -i %s" % new_mac)
 
     # Restart `dhclient' to regain IP for new mac address
     logging.info("Restart the network to gain new IP")
     dhclient_cmd = "dhclient -r && dhclient %s" % interface
-    session.sendline(dhclient_cmd)
+    session_serial.sendline(dhclient_cmd)
 
     # Re-log into the guest after changing mac address
     if kvm_utils.wait_for(session.is_responsive, 120, 20, 3):
diff --git a/client/tests/kvm/tests/migration.py b/client/tests/kvm/tests/migration.py
index d6f4b11..1c9f178 100644
--- a/client/tests/kvm/tests/migration.py
+++ b/client/tests/kvm/tests/migration.py
@@ -26,10 +26,12 @@
     mig_timeout = float(params.get("mig_timeout", "3600"))
     mig_protocol = params.get("migration_protocol", "tcp")
     mig_cancel = bool(params.get("mig_cancel"))
+    offline = params.get("offline", "no") == "yes"
+    check = params.get("vmstate_check", "no") == "yes"
 
     # Get the output of migration_test_command
     test_command = params.get("migration_test_command")
-    reference_output = session.get_command_output(test_command)
+    reference_output = session.cmd_output(test_command)
 
     # Start some process in the background (and leave the session open)
     background_command = params.get("migration_bg_command", "")
@@ -42,14 +44,12 @@
 
     try:
         check_command = params.get("migration_bg_check_command", "")
-        if session2.get_command_status(check_command, timeout=30) != 0:
-            raise error.TestError("Could not start background process '%s'" %
-                                  background_command)
+        session2.cmd(check_command, timeout=30)
         session2.close()
 
         # Migrate the VM
         dest_vm = kvm_test_utils.migrate(vm, env,mig_timeout, mig_protocol,
-                                         mig_cancel)
+                                         mig_cancel, offline, check)
 
         # Log into the guest again
         logging.info("Logging into guest after migration...")
@@ -59,12 +59,10 @@
         logging.info("Logged in after migration")
 
         # Make sure the background process is still running
-        if session2.get_command_status(check_command, timeout=30) != 0:
-            raise error.TestFail("Could not find running background process "
-                                 "after migration: '%s'" % background_command)
+        session2.cmd(check_command, timeout=30)
 
         # Get the output of migration_test_command
-        output = session2.get_command_output(test_command)
+        output = session2.cmd_output(test_command)
 
         # Compare output to reference output
         if output != reference_output:
@@ -81,8 +79,7 @@
     finally:
         # Kill the background process
         if session2 and session2.is_alive():
-            session2.get_command_output(params.get("migration_bg_kill_command",
-                                                   ""))
+            session2.cmd_output(params.get("migration_bg_kill_command", ""))
 
     session2.close()
     session.close()
diff --git a/client/tests/kvm/tests/migration_multi_host.py b/client/tests/kvm/tests/migration_multi_host.py
new file mode 100644
index 0000000..15af0c8
--- /dev/null
+++ b/client/tests/kvm/tests/migration_multi_host.py
@@ -0,0 +1,108 @@
+import logging, time, socket
+from autotest_lib.client.common_lib import error
+import kvm_subprocess, kvm_test_utils, kvm_utils
+
+
+def run_migration_multi_host(test, params, env):
+    """
+    KVM multi-host migration test:
+
+    Migration execution progress:
+
+    source host                       dest host
+    ----------------------------------------------------------------------------
+    log into guest
+    ----------------------------------------------------------------------------
+    start socket server
+
+    wait 30 secs -------------------- wait login_timeout+30 secs ---------------
+
+    accept connection                 connect to socket server,send mig_port
+    ----------------------------------------------------------------------------
+    start migration
+
+    wait 30 secs -------------------- wait mig_timeout+30 secs -----------------
+
+    try to log into migrated guest    check VM's status via monitor cmd
+    ----------------------------------------------------------------------------
+
+    @param test: kvm test object.
+    @param params: Dictionary with test parameters.
+    @param env: Dictionary with the test environment.
+    """
+    def guest_active(vm):
+        o = vm.monitor.info("status")
+        if isinstance(o, str):
+            return "status: running" in o
+        else:
+            return o.get("status") == "running"
+
+    vm = kvm_test_utils.get_living_vm(env, params.get("main_vm"))
+    login_timeout = int(params.get("login_timeout", 360))
+    role = params.get("role")
+    srchost = params.get("srchost")
+    dsthost = params.get("dsthost")
+    mig_timeout = int(params.get("mig_timeout"))
+    # Port used to communicate info between source and destination
+    comm_port = int(params.get("comm_port", 12324))
+    regain_ip_cmd = params.get("regain_ip_cmd", "dhclient")
+    if role == 'source':
+        session = kvm_test_utils.wait_for_login(vm, timeout=login_timeout)
+
+        # Listen on a port to get the migration port received from
+        # dest machine
+        s_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        s_socket.bind(('', comm_port))
+        s_socket.listen(1)
+
+        # Wait 30 seconds for source and dest to reach this point
+        test.job.barrier(srchost,'socket_started', 30).rendezvous(srchost,
+                                                                  dsthost)
+
+        c_socket, addr = s_socket.accept()
+        mig_port = int(c_socket.recv(6))
+        logging.info("Received from destination the migration port %s",
+                     mig_port)
+        c_socket.close()
+
+        logging.info("Start migrating now...")
+        kvm_test_utils.migrate(vm=vm, dest_host=dsthost, mig_port=mig_port,
+                               env=env)
+
+        # Wait up to 30 seconds for dest to reach this point
+        test.job.barrier(srchost, 'mig_finished', 30).rendezvous(srchost,
+                                                                 dsthost)
+
+    elif role == 'destination':
+        # Wait up to login_timeout + 30 seconds for the source to
+        # reach this point
+        test.job.barrier(dsthost, 'socket_started',
+                         login_timeout + 30).rendezvous(srchost,
+                                                        dsthost)
+
+        c_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        c_socket.connect((srchost, comm_port))
+        logging.info("Communicating to source migration port %s",
+                     vm.migration_port)
+        c_socket.send("%d" % vm.migration_port)
+        c_socket.close()
+
+        # Wait up to mig_timeout + 30 seconds for the source to
+        # reach this point: migration finished
+        test.job.barrier(dsthost, 'mig_finished',
+                         mig_timeout + 30).rendezvous(srchost,
+                                                      dsthost)
+
+        if not guest_active(vm):
+            raise error.TestFail("Guest not active after migration")
+
+        logging.info("Migrated guest appears to be running")
+
+        # Log into the guest again
+        logging.info("Logging into migrated guest after migration...")
+        session_serial = kvm_test_utils.wait_for_login(vm, timeout=login_timeout, serial=True)
+        session_serial.cmd(regain_ip_cmd)
+        session = kvm_test_utils.wait_for_login(vm, timeout=login_timeout)
+
+    else:
+        raise error.TestError('Invalid role specified')
diff --git a/client/tests/kvm/tests/migration_with_file_transfer.py b/client/tests/kvm/tests/migration_with_file_transfer.py
new file mode 100644
index 0000000..d311350
--- /dev/null
+++ b/client/tests/kvm/tests/migration_with_file_transfer.py
@@ -0,0 +1,92 @@
+import logging, time, os
+from autotest_lib.client.common_lib import utils, error
+from autotest_lib.client.bin import utils as client_utils
+import kvm_subprocess, kvm_test_utils, kvm_utils
+
+
+def run_migration_with_file_transfer(test, params, env):
+    """
+    KVM migration test:
+    1) Get a live VM and clone it.
+    2) Verify that the source VM supports migration.  If it does, proceed with
+            the test.
+    3) Reboot the VM
+    4) Send a migration command to the source VM and wait until it's finished.
+    5) Kill off the source VM.
+    6) Log into the destination VM after the migration is finished.
+
+    @param test: kvm test object.
+    @param params: Dictionary with test parameters.
+    @param env: Dictionary with the test environment.
+    """
+    vm = kvm_test_utils.get_living_vm(env, params.get("main_vm"))
+    timeout = int(params.get("login_timeout", 360))
+    session = kvm_test_utils.wait_for_login(vm, timeout=timeout)
+
+    mig_timeout = float(params.get("mig_timeout", "3600"))
+    mig_protocol = params.get("migration_protocol", "tcp")
+
+    # params of transfer test
+    username = vm.params.get("username", "")
+    password = vm.params.get("password", "")
+    client = vm.params.get("file_transfer_client")
+    address = vm.get_address(0)
+    port = vm.get_port(int(params.get("file_transfer_port")))
+    log_filename = ("migration-transfer-%s-to-%s-%s.log" %
+                    (vm.name, address,
+                     kvm_utils.generate_random_string(4)))
+    host_path = "/tmp/file-%s" % kvm_utils.generate_random_string(6)
+    host_path_returned = "%s-returned" % host_path
+    guest_path = params.get("guest_path", "/tmp/file")
+    file_size = params.get("file_size", "500")
+    transfer_timeout = int(params.get("transfer_timeout", "240"))
+
+    try:
+        utils.run("dd if=/dev/urandom of=%s bs=1M count=%s" % (host_path,
+                                                               file_size))
+
+        logging.info("Transferring file from host to guest")
+        bg = kvm_utils.Thread(kvm_utils.copy_files_to,
+                              (address, client, username, password, port,
+                               host_path, guest_path, log_filename,
+                               transfer_timeout))
+        bg.start()
+        try:
+            while bg.is_alive():
+                logging.info("File transfer not ended, starting a round of "
+                             "migration...")
+                vm = kvm_test_utils.migrate(vm, env, mig_timeout, mig_protocol)
+        finally:
+            # bg.join() returns the value returned by copy_files_to()
+            if not bg.join():
+                raise error.TestFail("File transfer from host to guest failed")
+
+        logging.info("Transferring file back from guest to host")
+        bg = kvm_utils.Thread(kvm_utils.copy_files_from,
+                              (address, client, username, password, port,
+                               host_path_returned, guest_path, log_filename,
+                               transfer_timeout))
+        bg.start()
+        try:
+            while bg.is_alive():
+                logging.info("File transfer not ended, starting a round of "
+                             "migration...")
+                vm = kvm_test_utils.migrate(vm, env, mig_timeout, mig_protocol)
+        finally:
+            if not bg.join():
+                raise error.TestFail("File transfer from guest to host failed")
+
+        # Make sure the returned file is indentical to the original one
+        orig_hash = client_utils.hash_file(host_path)
+        returned_hash = client_utils.hash_file(host_path_returned)
+        if orig_hash != returned_hash:
+            raise error.TestFail("Returned file hash (%s) differs from "
+                                 "original one (%s)" % (returned_hash,
+                                                        orig_hash))
+
+    finally:
+        session.close()
+        if os.path.isfile(host_path):
+            os.remove(host_path)
+        if os.path.isfile(host_path_returned):
+            os.remove(host_path_returned)
diff --git a/client/tests/kvm/tests/migration_with_reboot.py b/client/tests/kvm/tests/migration_with_reboot.py
new file mode 100644
index 0000000..af5de64
--- /dev/null
+++ b/client/tests/kvm/tests/migration_with_reboot.py
@@ -0,0 +1,85 @@
+import logging, time
+import threading
+from autotest_lib.client.common_lib import error
+import kvm_subprocess, kvm_utils, kvm_test_utils
+
+
+def run_migration_with_reboot(test, params, env):
+    """
+    KVM migration test:
+    1) Get a live VM and clone it.
+    2) Verify that the source VM supports migration.  If it does, proceed with
+            the test.
+    3) Reboot the VM
+    4) Send a migration command to the source VM and wait until it's finished.
+    5) Kill off the source VM.
+    6) Log into the destination VM after the migration is finished.
+
+    @param test: kvm test object.
+    @param params: Dictionary with test parameters.
+    @param env: Dictionary with the test environment.
+    """
+    def reboot_test(client, session, address, reboot_command, port, username,
+                    password, prompt, linesep, log_filename, timeout):
+        """
+        A version of reboot test which is safe to be called in the background as
+        it doesn't need a VM object.
+        """
+        # Send a reboot command to the guest's shell
+        session.sendline(reboot_command)
+        logging.info("Reboot command sent. Waiting for guest to go down...")
+
+        # Wait for the session to become unresponsive and close it
+        if not kvm_utils.wait_for(lambda: not session.is_responsive(timeout=30),
+                                  120, 0, 1):
+            raise error.TestFail("Guest refuses to go down")
+        session.close()
+
+        # Try logging into the guest until timeout expires
+        logging.info("Guest is down. Waiting for it to go up again, timeout "
+                     "%ds", timeout)
+        session = kvm_utils.wait_for(
+            lambda: kvm_utils.remote_login(client, address, port, username,
+                                           password, prompt, linesep,
+                                           log_filename), timeout, 0, 2)
+        if not session:
+            raise error.TestFail("Could not log into guest after reboot")
+        logging.info("Guest is up again")
+        session.close()
+
+    vm = kvm_test_utils.get_living_vm(env, params.get("main_vm"))
+    timeout = int(params.get("login_timeout", 360))
+    session = kvm_test_utils.wait_for_login(vm, timeout=timeout)
+
+    # params of reboot
+    username = vm.params.get("username", "")
+    password = vm.params.get("password", "")
+    prompt = vm.params.get("shell_prompt", "[\#\$]")
+    linesep = eval("'%s'" % vm.params.get("shell_linesep", r"\n"))
+    client = vm.params.get("shell_client")
+    address = vm.get_address(0)
+    port = vm.get_port(int(params.get("shell_port")))
+    log_filename = ("migration-reboot-%s-%s.log" %
+                    (vm.name, kvm_utils.generate_random_string(4)))
+    reboot_command = vm.params.get("reboot_command")
+
+    mig_timeout = float(params.get("mig_timeout", "3600"))
+    mig_protocol = params.get("migration_protocol", "tcp")
+    mig_cancel = bool(params.get("mig_cancel"))
+
+    try:
+        # Reboot the VM in the background
+        bg = kvm_utils.Thread(reboot_test, (client, session, address,
+                                            reboot_command, port, username,
+                                            password, prompt, linesep,
+                                            log_filename, timeout))
+        bg.start()
+
+        try:
+            while bg.is_alive():
+                vm = kvm_test_utils.migrate(vm, env, mig_timeout, mig_protocol)
+        finally:
+            bg.join()
+
+    finally:
+        session.close()
diff --git a/client/tests/kvm/tests/module_probe.py b/client/tests/kvm/tests/module_probe.py
new file mode 100644
index 0000000..661ddca
--- /dev/null
+++ b/client/tests/kvm/tests/module_probe.py
@@ -0,0 +1,57 @@
+import re, commands, logging, os
+from autotest_lib.client.common_lib import error, utils
+import kvm_subprocess, kvm_test_utils, kvm_utils, installer
+
+
+def run_module_probe(test, params, env):
+    """
+    load/unload KVM modules several times.
+
+    The test can run in two modes:
+
+    - based on previous 'build' test: in case KVM modules were installed by a
+      'build' test, we used the modules installed by the previous test.
+
+    - based on own params: if no previous 'build' test was run,
+      we assume a pre-installed KVM module. Some parameters that
+      work for the 'build' can be used, then, such as 'extra_modules'.
+    """
+
+    installer_object = env.previous_installer()
+    if installer_object is None:
+        installer_object = installer.PreInstalledKvm()
+        installer_object.set_install_params(test, params)
+
+    logging.debug('installer object: %r', installer_object)
+
+    mod_str = params.get("mod_list")
+    if mod_str:
+        mod_list = re.split("[, ]", mod_str)
+        logging.debug("mod list will be: %r", mod_list)
+    else:
+        mod_list = installer_object.full_module_list()
+        logging.debug("mod list from installer: %r", mod_list)
+
+    # unload the modules before starting:
+    installer_object._unload_modules(mod_list)
+
+    load_count = int(params.get("load_count", 100))
+    try:
+        for i in range(load_count):
+            try:
+                installer_object.load_modules(mod_list)
+            except Exception,e:
+               raise error.TestFail("Failed to load modules [%r]: %s" %
+                                    (installer_object.full_module_list, e))
+
+            # unload using rmmod directly because utils.unload_module() (used by
+            # installer) does too much (runs lsmod, checks for dependencies),
+            # and we want to run the loop as fast as possible.
+            for mod in reversed(mod_list):
+                r = utils.system("rmmod %s" % (mod), ignore_status=True)
+                if r <> 0:
+                   raise error.TestFail("Failed to unload module %s. "
+                                        "exit status: %d" % (mod, r))
+    finally:
+        installer_object.load_modules()
+
diff --git a/client/tests/kvm/tests/multicast.py b/client/tests/kvm/tests/multicast.py
index a47779a..2a12b4f 100644
--- a/client/tests/kvm/tests/multicast.py
+++ b/client/tests/kvm/tests/multicast.py
@@ -1,7 +1,7 @@
 import logging, os, re
 from autotest_lib.client.common_lib import error
 from autotest_lib.client.bin import utils
-import kvm_test_utils
+import kvm_test_utils, kvm_subprocess
 
 
 def run_multicast(test, params, env):
@@ -23,10 +23,10 @@
                                   timeout=int(params.get("login_timeout", 360)))
 
     def run_guest(cmd):
-        s, o = session.get_command_status_output(cmd)
-        if s:
-            logging.warning('Command %s executed in guest returned exit code '
-                            '%s, output: %s', cmd, s, o.strip())
+        try:
+            session.cmd(cmd)
+        except kvm_subprocess.ShellError, e:
+            logging.warn(e)
 
     def run_host_guest(cmd):
         run_guest(cmd)
@@ -56,8 +56,8 @@
     mcast_path = os.path.join(test.bindir, "scripts/join_mcast.py")
     if not vm.copy_files_to(mcast_path, "/tmp"):
         raise error.TestError("Fail to copy %s to guest" % mcast_path)
-    output = session.get_command_output("python /tmp/join_mcast.py %d %s %d" %
-                                        (mgroup_count, prefix, suffix))
+    output = session.cmd_output("python /tmp/join_mcast.py %d %s %d" %
+                                (mgroup_count, prefix, suffix))
 
     # if success to join multicast, the process will be paused, and return PID.
     try:
@@ -86,6 +86,6 @@
                                      (s, o))
 
     finally:
-        logging.debug(session.get_command_output("ipmaddr show"))
-        session.get_command_output("kill -s SIGCONT %s" % pid)
+        logging.debug(session.cmd_output("ipmaddr show"))
+        session.cmd_output("kill -s SIGCONT %s" % pid)
         session.close()
diff --git a/client/tests/kvm/tests/netperf.py b/client/tests/kvm/tests/netperf.py
index dc21e0f..7c341fa 100644
--- a/client/tests/kvm/tests/netperf.py
+++ b/client/tests/kvm/tests/netperf.py
@@ -1,7 +1,7 @@
 import logging, commands, os
 from autotest_lib.client.common_lib import error
 from autotest_lib.client.bin import utils
-import kvm_test_utils
+import kvm_test_utils, kvm_subprocess
 
 def run_netperf(test, params, env):
     """
@@ -26,20 +26,19 @@
     result_file = os.path.join(test.resultsdir, "output_%s" % test.iteration)
 
     firewall_flush = "iptables -F"
-    session.get_command_output(firewall_flush)
+    session.cmd_output(firewall_flush)
 
     for i in params.get("netperf_files").split():
         if not vm.copy_files_to(os.path.join(netperf_dir, i), "/tmp"):
             raise error.TestError("Could not copy file %s to guest" % i)
 
-    if session.get_command_status(firewall_flush):
+    try:
+        session.cmd(firewall_flush)
+    except kvm_subprocess.ShellError:
         logging.warning("Could not flush firewall rules on guest")
 
-    if session.get_command_status(setup_cmd % "/tmp", timeout=200):
-        raise error.TestFail("Fail to setup netperf on guest")
-
-    if session.get_command_status(params.get("netserver_cmd") % "/tmp"):
-        raise error.TestFail("Fail to start netperf server on guest")
+    session.cmd(setup_cmd % "/tmp", timeout=200)
+    session.cmd(params.get("netserver_cmd") % "/tmp")
 
     try:
         logging.info("Setup and run netperf client on host")
@@ -49,15 +48,18 @@
         result.write("Netperf test results\n")
 
         for i in params.get("protocols").split():
-            cmd = params.get("netperf_cmd") % (netperf_dir, i, guest_ip)
-            logging.info("Netperf: protocol %s", i)
-            try:
-                netperf_output = utils.system_output(cmd,
-                                                     retain_output=True)
-                result.write("%s\n" % netperf_output)
-            except:
-                logging.error("Test of protocol %s failed", i)
-                list_fail.append(i)
+            packet_size = params.get("packet_size", "1500")
+            for size in packet_size.split():
+                cmd = params.get("netperf_cmd") % (netperf_dir, i,
+                                                   guest_ip, size)
+                logging.info("Netperf: protocol %s", i)
+                try:
+                    netperf_output = utils.system_output(cmd,
+                                                         retain_output=True)
+                    result.write("%s\n" % netperf_output)
+                except:
+                    logging.error("Test of protocol %s failed", i)
+                    list_fail.append(i)
 
         result.close()
 
@@ -66,5 +68,5 @@
                                  ", ".join(list_fail))
 
     finally:
-        session.get_command_output("killall netserver")
+        session.cmd_output("killall netserver")
         session.close()
diff --git a/client/tests/kvm/tests/nic_bonding.py b/client/tests/kvm/tests/nic_bonding.py
new file mode 100644
index 0000000..087b099
--- /dev/null
+++ b/client/tests/kvm/tests/nic_bonding.py
@@ -0,0 +1,54 @@
+import logging, time, threading
+from autotest_lib.client.common_lib import error
+from tests import file_transfer
+import kvm_test_utils, kvm_utils
+
+def run_nic_bonding(test, params, env):
+    """
+    Nic bonding test in guest.
+
+    1) Start guest with four nic models.
+    2) Setup bond0 in guest by script bonding_setup.py.
+    3) Execute file transfer test between guest and host.
+    4) Repeatedly put down/up interfaces by set_link
+    5) Execute file transfer test between guest and host.
+
+    @param test: Kvm test object.
+    @param params: Dictionary with the test parameters.
+    @param env: Dictionary with test environment.
+    """
+    def control_link_loop(vm, termination_event):
+        logging.info("Repeatedly put down/up interfaces by set_link")
+        while True:
+            for i in range(len(params.get("nics").split())):
+                linkname = "%s.%s" % (params.get("nic_model"), i)
+                cmd = "set_link %s down" % linkname
+                vm.monitor.cmd(cmd)
+                time.sleep(1)
+                cmd = "set_link %s up" % linkname
+                vm.monitor.cmd(cmd)
+            if termination_event.isSet():
+                break
+
+    timeout = int(params.get("login_timeout", 1200))
+    vm = kvm_test_utils.get_living_vm(env, params.get("main_vm"))
+    session_serial = kvm_test_utils.wait_for_login(vm, 0, timeout, 0, 2,
+                                                   serial=True)
+    script_path = kvm_utils.get_path(test.bindir, "scripts/bonding_setup.py")
+    vm.copy_files_to(script_path, "/tmp/bonding_setup.py")
+    cmd = "python /tmp/bonding_setup.py %s" % vm.get_mac_address()
+    session_serial.cmd(cmd)
+
+    termination_event = threading.Event()
+    t = threading.Thread(target=control_link_loop,
+                         args=(vm, termination_event))
+    try:
+        logging.info("Do some basic test before testing high availability")
+        file_transfer.run_file_transfer(test, params, env)
+        t.start()
+        logging.info("Do file transfer testing")
+        file_transfer.run_file_transfer(test, params, env)
+    finally:
+        termination_event.set()
+        t.join(10)
+        session_serial.close()
diff --git a/client/tests/kvm/tests/nic_hotplug.py b/client/tests/kvm/tests/nic_hotplug.py
new file mode 100644
index 0000000..edfa980
--- /dev/null
+++ b/client/tests/kvm/tests/nic_hotplug.py
@@ -0,0 +1,143 @@
+import logging, os, commands, re, time
+from autotest_lib.client.common_lib import error
+import kvm_subprocess, kvm_test_utils, kvm_utils, kvm_vm
+
+
+def run_nic_hotplug(test, params, env):
+    """
+    Test hotplug of NIC devices
+
+    1) Boot up guest with one nic
+    2) Add a host network device through monitor cmd and check if it's added
+    3) Add nic device through monitor cmd and check if it's added
+    4) Check if new interface gets ip address
+    5) Disable primary link of guest
+    6) Ping guest new ip from host
+    7) Delete nic device and netdev
+    8) Re-enable primary link of guest
+
+    @param test:   KVM test object.
+    @param params: Dictionary with the test parameters.
+    @param env:    Dictionary with test environment.
+    """
+    vm = kvm_test_utils.get_living_vm(env, params.get("main_vm"))
+    timeout = int(params.get("login_timeout", 360))
+    guest_delay = int(params.get("guest_delay", 20))
+    session_serial = kvm_test_utils.wait_for_login(vm, timeout=timeout,
+                                                   serial=True)
+
+    # Modprobe the module if specified in config file
+    module = params.get("modprobe_module")
+    if module:
+        session_serial.get_command_output("modprobe %s" % module)
+
+    def netdev_add(vm):
+        netdev_id = kvm_utils.generate_random_id()
+        attach_cmd = ("netdev_add tap,id=%s,script=%s" %
+                      (netdev_id, kvm_utils.get_path(vm.root_dir,
+                                                     params.get("nic_script"))))
+        netdev_extra_params = params.get("netdev_extra_params")
+        if netdev_extra_params:
+            attach_cmd += ",%s" % netdev_extra_params
+        logging.info("Adding netdev through %s" % attach_cmd)
+        vm.monitor.cmd(attach_cmd)
+
+        network = vm.monitor.info("network")
+        if netdev_id not in network:
+            logging.error(network)
+            raise error.TestError("Fail to add netdev: %s" % netdev_id)
+        else:
+            return netdev_id
+
+    def netdev_del(vm, id):
+        vm.monitor.cmd("netdev_del %s" % id)
+
+        network = vm.monitor.info("network")
+        if id in network:
+            logging.error(network)
+            raise error.TestError("Fail to remove netdev %s" % id)
+
+    def nic_add(vm, model, netdev_id, mac):
+        """
+        Add a nic to virtual machine
+
+        @vm: VM object
+        @model: nic model
+        @netdev_id: id of netdev
+        @mac: Mac address of new nic
+        """
+        id = kvm_utils.generate_random_id()
+        if model=="virtio": model="virtio-net-pci"
+        device_add_cmd = "device_add %s,netdev=%s,mac=%s,id=%s" % (model,
+                                                                   netdev_id,
+                                                                   mac, id)
+        logging.info("Adding nic through %s" % device_add_cmd)
+        vm.monitor.cmd(device_add_cmd)
+
+        qdev = vm.monitor.info("qtree")
+        if id not in qdev:
+            logging.error(qdev)
+            raise error.TestFail("Device %s was not plugged into qdev"
+                                 "tree" % id)
+        else:
+            return id
+
+    def nic_del(vm, id, wait=True):
+        """
+        Remove the nic from pci tree.
+
+        @vm: VM object
+        @id: the nic id
+        @wait: Whether need to wait for the guest to unplug the device
+        """
+        nic_del_cmd = "device_del %s" % id
+        vm.monitor.cmd(nic_del_cmd)
+        if wait:
+            logging.info("waiting for the guest to finish the unplug")
+            if not kvm_utils.wait_for(lambda: id not in
+                                      vm.monitor.info("qtree"),
+                                      guest_delay, 5 ,1):
+                logging.error(vm.monitor.info("qtree"))
+                raise error.TestError("Device is not unplugged by "
+                                      "guest, please check whether the "
+                                      "hotplug module was loaded in guest");
+
+    logging.info("Attach a virtio nic to vm")
+    mac = kvm_utils.generate_mac_address(vm.instance, 1)
+    if not mac:
+        mac = "00:00:02:00:00:02"
+    netdev_id = netdev_add(vm)
+    device_id = nic_add(vm, "virtio", netdev_id, mac)
+
+    if "Win" not in params.get("guest_name", ""):
+        session_serial.sendline("dhclient %s &" %
+                         kvm_test_utils.get_linux_ifname(session_serial, mac))
+
+    logging.info("Shutting down the primary link")
+    vm.monitor.cmd("set_link %s down" % vm.netdev_id[0])
+
+    try:
+        logging.info("Waiting for new nic's ip address acquisition...")
+        if not kvm_utils.wait_for(lambda: (vm.address_cache.get(mac) is
+                                           not None), 10, 1):
+            raise error.TestFail("Could not get ip address of new nic")
+        ip = vm.address_cache.get(mac)
+        if not kvm_utils.verify_ip_address_ownership(ip, mac):
+            raise error.TestFail("Could not verify the ip address of new nic")
+        else:
+            logging.info("Got the ip address of new nic: %s" % ip)
+
+        logging.info("Ping test the new nic ...")
+        s, o = kvm_test_utils.ping(ip, 100)
+        if s != 0:
+            logging.error(o)
+            raise error.TestFail("New nic failed ping test")
+
+        logging.info("Detaching a virtio nic from vm")
+        nic_del(vm, device_id)
+        netdev_del(vm,netdev_id)
+
+    finally:
+        vm.free_mac_address(1)
+        logging.info("Re-enabling the primary link")
+        vm.monitor.cmd("set_link %s up" % vm.netdev_id[0])
diff --git a/client/tests/kvm/tests/nic_promisc.py b/client/tests/kvm/tests/nic_promisc.py
index 99bbf8c..f4bf1e4 100644
--- a/client/tests/kvm/tests/nic_promisc.py
+++ b/client/tests/kvm/tests/nic_promisc.py
@@ -1,4 +1,4 @@
-import logging
+import logging, threading
 from autotest_lib.client.common_lib import error
 from autotest_lib.client.bin import utils
 import kvm_utils, kvm_test_utils
@@ -21,21 +21,12 @@
     timeout = int(params.get("login_timeout", 360))
     vm = kvm_test_utils.get_living_vm(env, params.get("main_vm"))
     session = kvm_test_utils.wait_for_login(vm, timeout=timeout)
-
-    logging.info("Trying to log into guest '%s' by serial", vm.name)
-    session2 = kvm_utils.wait_for(lambda: vm.serial_login(),
-                                  timeout, 0, step=2)
-    if not session2:
-        raise error.TestFail("Could not log into guest '%s'" % vm.name)
+    session_serial = kvm_test_utils.wait_for_login(vm, 0, timeout, 0, 2,
+                                                   serial=True)
 
     def compare(filename):
-        cmd = "md5sum %s" % filename
         md5_host = utils.hash_file(filename, method="md5")
-        rc_guest, md5_guest = session.get_command_status_output(cmd)
-        if rc_guest:
-            logging.debug("Could not get MD5 hash for file %s on guest,"
-                          "output: %s", filename, md5_guest)
-            return False
+        md5_guest = session.cmd("md5sum %s" % filename)
         md5_guest = md5_guest.split()[0]
         if md5_host != md5_guest:
             logging.error("MD5 hash mismatch between file %s "
@@ -46,11 +37,28 @@
         return True
 
     ethname = kvm_test_utils.get_linux_ifname(session, vm.get_mac_address(0))
-    set_promisc_cmd = ("ip link set %s promisc on; sleep 0.01;"
-                       "ip link set %s promisc off; sleep 0.01" %
-                       (ethname, ethname))
-    logging.info("Set promisc change repeatedly in guest")
-    session2.sendline("while true; do %s; done" % set_promisc_cmd)
+
+    class ThreadPromiscCmd(threading.Thread):
+        def __init__(self, session, termination_event):
+            self.session = session
+            self.termination_event = termination_event
+            super(ThreadPromiscCmd, self).__init__()
+
+
+        def run(self):
+            set_promisc_cmd = ("ip link set %s promisc on; sleep 0.01;"
+                               "ip link set %s promisc off; sleep 0.01" %
+                               (ethname, ethname))
+            while True:
+                self.session.cmd_output(set_promisc_cmd)
+                if self.termination_event.isSet():
+                    break
+
+
+    logging.info("Started thread to change promisc mode in guest")
+    termination_event = threading.Event()
+    promisc_thread = ThreadPromiscCmd(session_serial, termination_event)
+    promisc_thread.start()
 
     dd_cmd = "dd if=/dev/urandom of=%s bs=%d count=1"
     filename = "/tmp/nic_promisc_file"
@@ -72,10 +80,7 @@
                 success_counter += 1
 
             logging.info("Create %s bytes file on guest" % size)
-            if session.get_command_status(dd_cmd % (filename, int(size)),
-                                                    timeout=100) != 0:
-                logging.error("Create file on guest failed")
-                continue
+            session.cmd(dd_cmd % (filename, int(size)), timeout=100)
 
             logging.info("Transfer file from guest to host")
             if not vm.copy_files_from(filename, filename):
@@ -90,12 +95,14 @@
             logging.info("Clean temporary files")
             cmd = "rm -f %s" % filename
             utils.run(cmd)
-            session.get_command_status(cmd)
+            session.cmd_output(cmd)
 
     finally:
+        logging.info("Stopping the promisc thread")
+        termination_event.set()
+        promisc_thread.join(10)
         logging.info("Restore the %s to the nonpromisc mode", ethname)
-        session2.close()
-        session.get_command_status("ip link set %s promisc off" % ethname)
+        session.cmd_output("ip link set %s promisc off" % ethname)
         session.close()
 
     if success_counter != 2 * len(file_size):
diff --git a/client/tests/kvm/tests/nicdriver_unload.py b/client/tests/kvm/tests/nicdriver_unload.py
index 47318ba..a515d67 100644
--- a/client/tests/kvm/tests/nicdriver_unload.py
+++ b/client/tests/kvm/tests/nicdriver_unload.py
@@ -20,17 +20,12 @@
     timeout = int(params.get("login_timeout", 360))
     vm = kvm_test_utils.get_living_vm(env, params.get("main_vm"))
     session = kvm_test_utils.wait_for_login(vm, timeout=timeout)
-    logging.info("Trying to log into guest '%s' by serial", vm.name)
-    session2 = kvm_utils.wait_for(lambda: vm.serial_login(),
-                                  timeout, 0, step=2)
-    if not session2:
-        raise error.TestFail("Could not log into guest '%s'" % vm.name)
+    session_serial = kvm_test_utils.wait_for_login(vm, 0, timeout, 0, 2,
+                                                   serial=True)
 
     ethname = kvm_test_utils.get_linux_ifname(session, vm.get_mac_address(0))
     sys_path = "/sys/class/net/%s/device/driver" % (ethname)
-    s, o = session.get_command_status_output('readlink -e %s' % sys_path)
-    if s:
-        raise error.TestError("Could not find driver name")
+    o = session.cmd("readlink -e %s" % sys_path)
     driver = os.path.basename(o.strip())
     logging.info("driver is %s", driver)
 
@@ -45,12 +40,8 @@
                 logging.debug("Failed to transfer file %s", remote_file)
 
     def compare(origin_file, receive_file):
-        cmd = "md5sum %s"
         check_sum1 = utils.hash_file(origin_file, method="md5")
-        s, output2 = session.get_command_status_output(cmd % receive_file)
-        if s != 0:
-            logging.error("Could not get md5sum of receive_file")
-            return False
+        output2 = session.cmd("md5sum %s" % receive_file)
         check_sum2 = output2.strip().split()[0]
         logging.debug("original file md5: %s, received file md5: %s",
                       check_sum1, check_sum2)
@@ -77,9 +68,11 @@
         logging.info("Unload/load NIC driver repeatedly in guest...")
         while True:
             logging.debug("Try to unload/load nic drive once")
-            if session2.get_command_status(unload_load_cmd, timeout=120) != 0:
-                session.get_command_output("rm -rf /tmp/Thread-*")
-                raise error.TestFail("Unload/load nic driver failed")
+            try:
+                session_serial.cmd(unload_load_cmd, timeout=120)
+            except:
+                session.cmd_output("rm -rf /tmp/Thread-*")
+                raise
             pid, s = os.waitpid(pid, os.WNOHANG)
             status = os.WEXITSTATUS(s)
             if (pid, status) != (0, 0):
@@ -96,7 +89,6 @@
             t.join(timeout = scp_timeout)
         os._exit(0)
 
-    session2.close()
 
     try:
         logging.info("Check MD5 hash for received files in multi-session")
@@ -111,5 +103,5 @@
             raise error.TestFail("Test nic function after load/unload fail")
 
     finally:
-        session.get_command_output("rm -rf /tmp/Thread-*")
+        session.cmd_output("rm -rf /tmp/Thread-*")
         session.close()
diff --git a/client/tests/kvm/tests/pci_hotplug.py b/client/tests/kvm/tests/pci_hotplug.py
index 55cf666..c85ff62 100644
--- a/client/tests/kvm/tests/pci_hotplug.py
+++ b/client/tests/kvm/tests/pci_hotplug.py
@@ -26,14 +26,13 @@
     # Modprobe the module if specified in config file
     module = params.get("modprobe_module")
     if module:
-        if session.get_command_status("modprobe %s" % module):
-            raise error.TestError("Modprobe module '%s' failed" % module)
+        session.cmd("modprobe %s" % module)
 
     # Get output of command 'info pci' as reference
     info_pci_ref = vm.monitor.info("pci")
 
     # Get output of command as reference
-    reference = session.get_command_output(params.get("reference_cmd"))
+    reference = session.cmd_output(params.get("reference_cmd"))
 
     tested_model = params.get("pci_model")
     test_type = params.get("pci_type")
@@ -48,11 +47,24 @@
     else:
         raise error.TestError("Unknow version of qemu")
 
+    # Determine syntax of drive hotplug
+    # __com.redhat_drive_add == qemu-kvm-0.12 on RHEL 6
+    if len(re.findall("\n__com.redhat_drive_add", cmd_output)) > 0:
+        drive_cmd_type = "__com.redhat_drive_add"
+    # drive_add == qemu-kvm-0.13 onwards
+    elif len(re.findall("\ndrive_add", cmd_output)) > 0:
+        drive_cmd_type = "drive_add"
+    else:
+        raise error.TestError("Unknow version of qemu")
+
+    # Probe qemu for a list of supported devices
+    devices_support = vm.monitor.cmd("%s ?" % cmd_type)
+
     if cmd_type == "pci_add":
         if test_type == "nic":
             pci_add_cmd = "pci_add pci_addr=auto nic model=%s" % tested_model
         elif test_type == "block":
-            image_params = kvm_utils.get_sub_dict(params, "stg")
+            image_params = params.object_params("stg")
             image_filename = kvm_vm.get_image_filename(image_params,
                                                        test.bindir)
             pci_add_cmd = ("pci_add pci_addr=auto storage file=%s,if=%s" %
@@ -74,24 +86,40 @@
             pci_add_cmd = "device_add id=%s,driver=%s" % (id, tested_model)
 
         elif test_type == "block":
-            image_params = kvm_utils.get_sub_dict(params, "stg")
+            image_params = params.object_params("stg")
             image_filename = kvm_vm.get_image_filename(image_params,
                                                        test.bindir)
+            controller_model = None
             if tested_model == "virtio":
                 tested_model = "virtio-blk-pci"
 
             if tested_model == "scsi":
                 tested_model = "scsi-disk"
+                controller_model = "lsi53c895a"
+                if len(re.findall(controller_model, devices_support)) == 0:
+                    raise error.TestError("scsi controller device (%s) not "
+                                          "supported by qemu" %
+                                          controller_model)
 
-            driver_add_cmd = (" __com.redhat_drive_add "
-                              "file=%s,format=%s,id=%s" %
-                              (image_filename, image_format, driver_id))
+            if controller_model is not None:
+                controller_id = "controller-" + id
+                controller_add_cmd = ("device_add %s,id=%s" %
+                                      (controller_model, controller_id))
+                controller_output = vm.monitor.cmd(controller_add_cmd)
+
+            if drive_cmd_type == "drive_add":
+                driver_add_cmd = ("drive_add auto file=%s,if=none,id=%s,format=%s" %
+                                  (image_filename, driver_id, image_format))
+            elif drive_cmd_type == "__com.redhat_drive_add":
+                driver_add_cmd = ("__com.redhat_drive_add "
+                                  "file=%s,format=%s,id=%s" %
+                                  (image_filename, image_format, driver_id))
+
             pci_add_cmd = ("device_add id=%s,driver=%s,drive=%s" %
                            (id, tested_model, driver_id))
             driver_output = vm.monitor.cmd(driver_add_cmd)
 
         # Check if the device is support in qemu
-        devices_support = vm.monitor.cmd("%s ?" % cmd_type)
         if len(re.findall(tested_model, devices_support)) > 0:
             add_output = vm.monitor.cmd(pci_add_cmd)
         else:
@@ -106,8 +134,12 @@
     # Define a helper function to delete the device
     def pci_del(ignore_failure=False):
         if cmd_type == "pci_add":
-            slot_id = "0" + add_output.split(",")[2].split()[1]
-            cmd = "pci_del pci_addr=%s" % slot_id
+            result_domain, bus, slot, function = add_output.split(',')
+            domain = int(result_domain.split()[2])
+            bus = int(bus.split()[1])
+            slot = int(slot.split()[1])
+            pci_addr = "%x:%x:%x" % (domain, bus, slot)
+            cmd = "pci_del pci_addr=%s" % pci_addr
         elif cmd_type == "device_add":
             cmd = "device_del %s" % id
         # This should be replaced by a proper monitor method call
@@ -131,7 +163,7 @@
 
         # Define a helper function to compare the output
         def new_shown():
-            o = session.get_command_output(params.get("reference_cmd"))
+            o = session.cmd_output(params.get("reference_cmd"))
             return o != reference
 
         secs = int(params.get("wait_secs_for_hook_up"))
@@ -142,7 +174,7 @@
 
         # Define a helper function to catch PCI device string
         def find_pci():
-            o = session.get_command_output(params.get("find_pci_cmd"))
+            o = session.cmd_output(params.get("find_pci_cmd"))
             return params.get("match_string") in o
 
         if not kvm_utils.wait_for(find_pci, 30, 3, 3):
@@ -152,10 +184,11 @@
                                   params.get("find_pci_cmd")))
 
         # Test the newly added device
-        s, o = session.get_command_status_output(params.get("pci_test_cmd"))
-        if s != 0:
+        try:
+            session.cmd(params.get("pci_test_cmd"))
+        except kvm_subprocess.ShellError, e:
             raise error.TestFail("Check for %s device failed after PCI "
-                                 "hotplug. Output: %r" % (test_type, o))
+                                 "hotplug. Output: %r" % (test_type, e.output))
 
         session.close()
 
diff --git a/client/tests/kvm/tests/physical_resources_check.py b/client/tests/kvm/tests/physical_resources_check.py
index 682c7b2..3234da7 100644
--- a/client/tests/kvm/tests/physical_resources_check.py
+++ b/client/tests/kvm/tests/physical_resources_check.py
@@ -51,7 +51,7 @@
     # Define a function for checking number of hard drivers & NICs
     def check_num(devices, info_cmd, check_str):
         f_fail = 0
-        expected_num = kvm_utils.get_sub_dict_names(params, devices).__len__()
+        expected_num = params.objects(devices).__len__()
         try:
             o = vm.monitor.info(info_cmd)
         except kvm_monitor.MonitorError, e:
@@ -78,9 +78,9 @@
     # Define a function for checking hard drives & NICs' model
     def chk_fmt_model(device, fmt_model, info_cmd, str):
         f_fail = 0
-        devices = kvm_utils.get_sub_dict_names(params, device)
+        devices = params.objects(device)
         for chk_device in devices:
-            expected = kvm_utils.get_sub_dict(params, chk_device).get(fmt_model)
+            expected = params.object_params(chk_device).get(fmt_model)
             if not expected:
                 expected = "rtl8139"
             try:
@@ -123,7 +123,7 @@
     found_mac_addresses = re.findall("macaddr=(\S+)", o)
     logging.debug("Found MAC adresses: %s" % found_mac_addresses)
 
-    num_nics = len(kvm_utils.get_sub_dict_names(params, "nics"))
+    num_nics = len(params.objects("nics"))
     for nic_index in range(num_nics):
         mac = vm.get_mac_address(nic_index)
         if not string.lower(mac) in found_mac_addresses:
@@ -135,7 +135,7 @@
     def verify_device(expect, name, verify_cmd):
         f_fail = 0
         if verify_cmd:
-            actual = session.get_command_output(verify_cmd)
+            actual = session.cmd_output(verify_cmd)
             if not string.upper(expect) in actual:
                 f_fail += 1
                 logging.error("%s mismatch:")
diff --git a/client/tests/kvm/tests/qemu_img.py b/client/tests/kvm/tests/qemu_img.py
index d3f7ff1..6351a84 100644
--- a/client/tests/kvm/tests/qemu_img.py
+++ b/client/tests/kvm/tests/qemu_img.py
@@ -1,6 +1,6 @@
-import re, os, logging, commands
+import re, os, logging, commands, string
 from autotest_lib.client.common_lib import utils, error
-import kvm_vm, kvm_utils
+import kvm_vm, kvm_utils, kvm_test_utils, kvm_preprocessing
 
 
 def run_qemu_img(test, params, env):
@@ -243,10 +243,124 @@
     def commit_test(cmd):
         """
         Subcommand 'qemu-img commit' test.
+        1) Create a backing file of the qemu harddisk specified by image_name.
+        2) Start a VM using the backing file as its harddisk.
+        3) Touch a file "commit_testfile" in the backing_file, and shutdown the
+           VM.
+        4) Make sure touching the file does not affect the original harddisk.
+        5) Commit the change to the original harddisk by executing
+           "qemu-img commit" command.
+        6) Start the VM using the original harddisk.
+        7) Check if the file "commit_testfile" exists.
 
         @param cmd: qemu-img base command.
         """
-        pass
+        cmd += " commit"
+
+        logging.info("Commit testing started!")
+        image_name = params.get("image_name", "image")
+        image_format = params.get("image_format", "qcow2")
+        backing_file_name = "%s_bak" % (image_name)
+
+        try:
+            # Remove the existing backing file
+            backing_file = "%s.%s" % (backing_file_name, image_format)
+            if os.path.isfile(backing_file):
+                os.remove(backing_file)
+
+            # Create the new backing file
+            create_cmd = "qemu-img create -b %s.%s -f %s %s.%s" % (image_name,
+                                                                  image_format,
+                                                                  image_format,
+                                                             backing_file_name,
+                                                                  image_format)
+            try:
+                utils.system(create_cmd)
+            except error.CmdError, e:
+                raise error.TestFail("Could not create a backing file!")
+            logging.info("backing_file created!")
+
+            # Set the qemu harddisk to the backing file
+            logging.info("Original image_name is: %s", params.get('image_name'))
+            params['image_name'] = backing_file_name
+            logging.info("Param image_name changed to: %s",
+                         params.get('image_name'))
+
+            # Start a new VM, using backing file as its harddisk
+            vm_name = params.get('main_vm')
+            kvm_preprocessing.preprocess_vm(test, params, env, vm_name)
+            vm = env.get_vm(vm_name)
+            vm.create()
+            timeout = int(params.get("login_timeout", 360))
+            session = kvm_test_utils.wait_for_login(vm, timeout=timeout)
+
+            # Do some changes to the backing_file harddisk
+            try:
+                output = session.cmd("touch /commit_testfile")
+                logging.info("Output of touch /commit_testfile: %s", output)
+                output = session.cmd("ls / | grep commit_testfile")
+                logging.info("Output of ls / | grep commit_testfile: %s",
+                             output)
+            except Exception, e:
+                raise error.TestFail("Could not create commit_testfile in the "
+                                     "backing file %s", e)
+            vm.destroy()
+
+            # Make sure there is no effect on the original harddisk
+            # First, set the harddisk back to the original one
+            logging.info("Current image_name is: %s", params.get('image_name'))
+            params['image_name'] = image_name
+            logging.info("Param image_name reverted to: %s",
+                         params.get('image_name'))
+
+            # Second, Start a new VM, using image_name as its harddisk
+            # Here, the commit_testfile should not exist
+            vm_name = params.get('main_vm')
+            kvm_preprocessing.preprocess_vm(test, params, env, vm_name)
+            vm = env.get_vm(vm_name)
+            vm.create()
+            timeout = int(params.get("login_timeout", 360))
+            session = kvm_test_utils.wait_for_login(vm, timeout=timeout)
+            try:
+                output = session.cmd("[ ! -e /commit_testfile ] && echo $?")
+                logging.info("Output of [ ! -e /commit_testfile ] && echo $?: "
+                             "%s", output)
+            except:
+                output = session.cmd("rm -f /commit_testfile")
+                raise error.TestFail("The commit_testfile exists on the "
+                                     "original file")
+            vm.destroy()
+
+            # Excecute the commit command
+            logging.info("Commiting image")
+            cmitcmd = "%s -f %s %s.%s" % (cmd, image_format, backing_file_name,
+                                          image_format)
+            try:
+                utils.system(cmitcmd)
+            except error.CmdError, e:
+                raise error.TestFail("Could not commit the backing file")
+
+            # Start a new VM, using image_name as its harddisk
+            vm_name = params.get('main_vm')
+            kvm_preprocessing.preprocess_vm(test, params, env, vm_name)
+            vm = env.get_vm(vm_name)
+            vm.create()
+            timeout = int(params.get("login_timeout", 360))
+            session = kvm_test_utils.wait_for_login(vm, timeout=timeout)
+            try:
+                output = session.cmd("[ -e /commit_testfile ] && echo $?")
+                logging.info("Output of [ -e /commit_testfile ] && echo $?: %s",
+                             output)
+                session.cmd("rm -f /commit_testfile")
+            except:
+                raise error.TestFail("Could not find commit_testfile after a "
+                                     "commit")
+            vm.destroy()
+
+        finally:
+            # Remove the backing file
+            if os.path.isfile(backing_file):
+                os.remove(backing_file)
 
 
     def _rebase(cmd, img_name, base_img, backing_fmt, mode="unsafe"):
diff --git a/client/tests/kvm/tests/set_link.py b/client/tests/kvm/tests/set_link.py
new file mode 100644
index 0000000..e88a1ef
--- /dev/null
+++ b/client/tests/kvm/tests/set_link.py
@@ -0,0 +1,60 @@
+import logging
+from autotest_lib.client.common_lib import error
+from tests import file_transfer
+import kvm_test_utils
+
+
+def run_set_link(test, params, env):
+    """
+    KVM guest link test:
+    1) Boot up guest with one nic
+    2) Ping guest from host
+    3) Disable guest link and ping guest from host
+    4) Re-enable guest link and ping guest from host
+    5) Do file transfer test
+
+    @param test: kvm test object
+    @param params: Dictionary with the test parameters
+    @param env: Dictionary with test environment.
+    """
+    vm = kvm_test_utils.get_living_vm(env, params.get("main_vm"))
+    timeout = float(params.get("login_timeout", 360))
+    session = kvm_test_utils.wait_for_login(vm, 0, timeout, 0, 2)
+
+    ip = vm.get_address(0)
+    linkname = vm.netdev_id[0]
+
+    logging.info("Pinging guest from host")
+    s, o = kvm_test_utils.ping(ip, count=10, timeout=20)
+    if s != 0:
+        raise error.TestFail("Ping failed, status: %s, output: %s" % (s, o))
+    ratio = kvm_test_utils.get_loss_ratio(o)
+    if ratio != 0:
+        raise error.TestFail("Loss ratio is %s, output: %s" % (ratio, o))
+
+    logging.info("Executing 'set link %s off'" % linkname)
+    vm.monitor.cmd("set_link %s off" % linkname)
+    logging.info(vm.monitor.info("network"))
+    logging.info("Pinging guest from host")
+    s, o = kvm_test_utils.ping(ip, count=10, timeout=20)
+    if s == 0:
+        raise error.TestFail("Ping unexpectedly succeeded, status: %s,"
+                             "output: %s" % (s, o))
+    ratio = kvm_test_utils.get_loss_ratio(o)
+    if ratio != 100:
+        raise error.TestFail("Loss ratio is not 100%%,"
+                             "Loss ratio is %s" % ratio)
+
+    logging.info("Executing 'set link %s on'" % linkname)
+    vm.monitor.cmd("set_link %s on" % linkname)
+    logging.info(vm.monitor.info("network"))
+    logging.info("Pinging guest from host")
+    s, o = kvm_test_utils.ping(ip, count=10, timeout=20)
+    if s != 0:
+        raise error.TestFail("Ping failed, status: %s, output: %s" % (s, o))
+    ratio = kvm_test_utils.get_loss_ratio(o)
+    if ratio != 0:
+        raise error.TestFail("Loss ratio is %s, output: %s" % (ratio, o))
+
+    file_transfer.run_file_transfer(test, params, env)
+    session.close()
diff --git a/client/tests/kvm/tests/stepmaker.py b/client/tests/kvm/tests/stepmaker.py
index ee0ed92..9f6d9b2 100755
--- a/client/tests/kvm/tests/stepmaker.py
+++ b/client/tests/kvm/tests/stepmaker.py
@@ -337,7 +337,7 @@
 
 
 def run_stepmaker(test, params, env):
-    vm = kvm_utils.env_get_vm(env, params.get("main_vm"))
+    vm = env.get_vm(params.get("main_vm"))
     if not vm:
         raise error.TestError("VM object not found in environment")
     if not vm.is_alive():
diff --git a/client/tests/kvm/tests/steps.py b/client/tests/kvm/tests/steps.py
index 6f782f5..5d4ed25 100644
--- a/client/tests/kvm/tests/steps.py
+++ b/client/tests/kvm/tests/steps.py
@@ -181,7 +181,7 @@
 
 
 def run_steps(test, params, env):
-    vm = kvm_utils.env_get_vm(env, params.get("main_vm"))
+    vm = env.get_vm(params.get("main_vm"))
     if not vm:
         raise error.TestError("VM object not found in environment")
     if not vm.is_alive():
diff --git a/client/tests/kvm/tests/stress_boot.py b/client/tests/kvm/tests/stress_boot.py
index b7916b4..37d853b 100644
--- a/client/tests/kvm/tests/stress_boot.py
+++ b/client/tests/kvm/tests/stress_boot.py
@@ -36,7 +36,7 @@
             vm_name = "vm" + str(num)
             vm_params = vm.get_params().copy()
             curr_vm = vm.clone(vm_name, vm_params)
-            kvm_utils.env_register_vm(env, vm_name, curr_vm)
+            env.register_vm(vm_name, curr_vm)
             logging.info("Booting guest #%d" % num)
             kvm_preprocessing.preprocess_vm(tests, vm_params, env, vm_name)
             params['vms'] += " " + vm_name
@@ -51,7 +51,9 @@
 
             # check whether all previous shell sessions are responsive
             for i, se in enumerate(sessions):
-                if se.get_command_status(params.get("alive_test_cmd")) != 0:
+                try:
+                    se.cmd(params.get("alive_test_cmd"))
+                except kvm_subprocess.ShellError:
                     raise error.TestFail("Session #%d is not responsive" % i)
             num += 1
 
diff --git a/client/tests/kvm/tests/timedrift.py b/client/tests/kvm/tests/timedrift.py
index a6d3076..e5aa316 100644
--- a/client/tests/kvm/tests/timedrift.py
+++ b/client/tests/kvm/tests/timedrift.py
@@ -146,7 +146,7 @@
             restore_cpu_affinity(prev_affinity)
             # Stop the guest load
             if guest_load_stop_command:
-                session.get_command_output(guest_load_stop_command)
+                session.cmd_output(guest_load_stop_command)
             # Close all load shell sessions
             for load_session in guest_load_sessions:
                 load_session.close()
diff --git a/client/tests/kvm/tests/unittest.py b/client/tests/kvm/tests/unittest.py
index 54e5f73..c724051 100644
--- a/client/tests/kvm/tests/unittest.py
+++ b/client/tests/kvm/tests/unittest.py
@@ -36,8 +36,8 @@
                               unittest_cfg)
     logging.debug('Unit test list: %s' % test_list)
 
-    if params.get('test_list', None):
-        test_list = kvm_utils.get_sub_dict_names(params, 'test_list')
+    if params.get('test_list'):
+        test_list = params.get('test_list').split()
         logging.info('Original test list overriden by user')
         logging.info('User defined unit test list: %s' % test_list)
 
@@ -88,7 +88,7 @@
             try:
                 vm_name = params.get('main_vm')
                 kvm_preprocessing.preprocess_vm(test, params, env, vm_name)
-                vm = kvm_utils.env_get_vm(env, vm_name)
+                vm = env.get_vm(vm_name)
                 vm.create()
                 vm.monitor.cmd("cont")
                 logging.info("Waiting for unittest %s to complete, timeout %s, "
diff --git a/client/tests/kvm/tests/virtio_console.py b/client/tests/kvm/tests/virtio_console.py
index 008ec63..4b269d5 100644
--- a/client/tests/kvm/tests/virtio_console.py
+++ b/client/tests/kvm/tests/virtio_console.py
@@ -1,15 +1,16 @@
 """
 virtio_console test
 
-@copyright: Red Hat 2010
+@copyright: 2010 Red Hat, Inc.
 """
 import array, logging, os, random, re, select, shutil, socket, sys, tempfile
-import threading, time
+import threading, time, traceback
 from collections import deque
 from threading import Thread
 
 import kvm_subprocess, kvm_test_utils, kvm_utils, kvm_preprocessing
 from autotest_lib.client.common_lib import error
+from autotest_lib.client.bin import utils
 
 
 def run_virtio_console(test, params, env):
@@ -30,7 +31,224 @@
     @param params: Dictionary with the test parameters
     @param env: Dictionary with test environment
     """
-    class th_send(Thread):
+    class SubTest(object):
+        """
+        Collect result of subtest of main test.
+        """
+        def __init__(self):
+            """
+            Initialize object
+            """
+            self.result = []
+            self.passed = 0
+            self.failed = 0
+            self.cleanup_func = None
+            self.cleanup_args = None
+
+
+        def set_cleanup_func(self, func, args):
+            """
+            Set cleanup function which is called when subtest fails.
+
+            @param func: Function which should be called when test fails.
+            @param args: Arguments of cleanup function.
+            """
+            self.cleanup_func = func
+            self.cleanup_args = args
+
+
+        def do_test(self, function, args=None, fatal=False, cleanup=True):
+            """
+            Execute subtest function.
+
+            @param function: Object of function.
+            @param args: List of arguments of function.
+            @param fatal: If true exception is forwarded to main test.
+            @param cleanup: If true call cleanup function after crash of test.
+            @return: Return what returned executed subtest.
+            @raise TestError: If collapse of test is fatal raise forward
+                        exception from subtest.
+            """
+            if args == None:
+                args = []
+            res = [None, function.func_name, args]
+            try:
+                logging.debug("Start test %s." % function.func_name)
+                ret = function(*args)
+                res[0] = True
+                logging.info(self.result_to_string(res))
+                self.result.append(res)
+                self.passed += 1
+                return ret
+            except:
+                exc_type, exc_value, exc_traceback = sys.exc_info()
+                logging.error("In function (" + function.func_name + "):")
+                logging.error("Call from:\n" +
+                              traceback.format_stack()[-2][:-1])
+                logging.error("Exception from:\n" +
+                              "".join(traceback.format_exception(
+                                                        exc_type, exc_value,
+                                                        exc_traceback.tb_next)))
+                # Clean up environment after subTest crash
+                if cleanup:
+                    self.cleanup_func(*self.cleanup_args)
+                res[0] = False
+                logging.info(self.result_to_string(res))
+                self.result.append(res)
+                self.failed += 1
+                if fatal:
+                    raise
+
+
+        def is_failed(self):
+            """
+            @return: If any of subtest not pass return True.
+            """
+            if self.failed > 0:
+                return True
+            else:
+                return False
+
+
+        def get_result(self):
+            """
+            @return: Result of subtests.
+               Format:
+                 tuple(pass/fail,function_name,call_arguments)
+            """
+            return self.result
+
+
+        def result_to_string_debug(self, result):
+            """
+            @param result: Result of test.
+            """
+            sargs = ""
+            for arg in result[2]:
+                sargs += str(arg) + ","
+            sargs = sargs[:-1]
+            if result[0]:
+                status = "PASS"
+            else:
+                status = "FAIL"
+            return ("Subtest (%s(%s)): --> %s") % (result[1], sargs, status)
+
+
+        def result_to_string(self, result):
+            """
+            @param result: Result of test.
+            """
+            if result[0]:
+                status = "PASS"
+            else:
+                status = "FAIL"
+            return ("Subtest (%s): --> %s") % (result[1], status)
+
+
+        def headline(self, msg):
+            """
+            Add headline to result output.
+
+            @param msg: Test of headline
+            """
+            self.result.append([msg])
+
+
+        def _gen_res(self, format_func):
+            """
+            Format result with foramting function
+
+            @param format_func: Func for formating result.
+            """
+            result = ""
+            for res in self.result:
+                if (len(res) == 3):
+                    result += format_func(res) + "\n"
+                else:
+                    result += res[0] + "\n"
+            return result
+
+
+        def get_full_text_result(self):
+            """
+            @return string with text form of result
+            """
+            return self._gen_res(lambda str: self.result_to_string_debug(str))
+
+
+        def get_text_result(self):
+            """
+            @return string with text form of result
+            """
+            return self._gen_res(lambda str: self.result_to_string(str))
+
+
+    class Port(object):
+        """
+        Define structure to keep information about used port.
+        """
+        def __init__(self, sock, name, port_type, path):
+            """
+            @param vm: virtual machine object that port owned
+            @param sock: Socket of port if port is open.
+            @param name: Name of port for guest side.
+            @param port_type: Type of port yes = console, no= serialport.
+            @param path: Path to port on host side.
+            """
+            self.sock = sock
+            self.name = name
+            self.port_type = port_type
+            self.path = path
+            self.is_open = False
+
+
+        def for_guest(self):
+            """
+            Format data for communication with guest side.
+            """
+            return [self.name, self.port_type]
+
+
+        def open(self):
+            """
+            Open port on host side.
+            """
+            self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+            self.sock.connect(self.path)
+            self.is_open = True
+
+
+        def clean_port(self):
+            """
+            Clean all data from opened port on host side.
+            """
+            if self.is_open:
+                self.close()
+            self.open()
+            ret = select.select([self.sock], [], [], 1.0)
+            if ret[0]:
+                buf = self.sock.recv(1024)
+                logging.debug("Rest in socket: " + buf)
+
+
+        def close(self):
+            """
+            Close port.
+            """
+            self.sock.shutdown(socket.SHUT_RDWR)
+            self.sock.close()
+            self.is_open = False
+
+
+        def __str__(self):
+            """
+            Convert to text.
+            """
+            return ("%s,%s,%s,%s,%d" % ("Socket", self.name, self.port_type,
+                                        self.path, self.is_open))
+
+
+    class ThSend(Thread):
         """
         Random data sender thread.
         """
@@ -53,14 +271,14 @@
 
 
         def run(self):
-            logging.debug("th_send %s: run", self.getName())
+            logging.debug("ThSend %s: run", self.getName())
             while not self.exitevent.isSet():
                 self.idx += self.port.send(self.data)
-            logging.debug("th_send %s: exit(%d)", self.getName(),
+            logging.debug("ThSend %s: exit(%d)", self.getName(),
                           self.idx)
 
 
-    class th_send_check(Thread):
+    class ThSendCheck(Thread):
         """
         Random data sender thread.
         """
@@ -85,7 +303,7 @@
 
 
         def run(self):
-            logging.debug("th_send_check %s: run", self.getName())
+            logging.debug("ThSendCheck %s: run", self.getName())
             too_much_data = False
             while not self.exitevent.isSet():
                 # FIXME: workaround the problem with qemu-kvm stall when too
@@ -109,14 +327,14 @@
                         idx = self.port.send(buf)
                         buf = buf[idx:]
                         self.idx += idx
-            logging.debug("th_send_check %s: exit(%d)", self.getName(),
+            logging.debug("ThSendCheck %s: exit(%d)", self.getName(),
                           self.idx)
             if too_much_data:
-                logging.error("th_send_check: workaround the 'too_much_data'"
+                logging.error("ThSendCheck: workaround the 'too_much_data'"
                               "bug")
 
 
-    class th_recv(Thread):
+    class ThRecv(Thread):
         """
         Recieves data and throws it away.
         """
@@ -134,7 +352,7 @@
             self.blocklen = blocklen
             self.idx = 0
         def run(self):
-            logging.debug("th_recv %s: run", self.getName())
+            logging.debug("ThRecv %s: run", self.getName())
             while not self.exitevent.isSet():
                 # TODO: Workaround, it didn't work with select :-/
                 try:
@@ -142,10 +360,10 @@
                 except socket.timeout:
                     pass
             self.port.settimeout(self._port_timeout)
-            logging.debug("th_recv %s: exit(%d)", self.getName(), self.idx)
+            logging.debug("ThRecv %s: exit(%d)", self.getName(), self.idx)
 
 
-    class th_recv_check(Thread):
+    class ThRecvCheck(Thread):
         """
         Random data receiver/checker thread.
         """
@@ -165,10 +383,10 @@
 
 
         def run(self):
-            logging.debug("th_recv_check %s: run", self.getName())
+            logging.debug("ThRecvCheck %s: run", self.getName())
             while not self.exitevent.isSet():
                 ret = select.select([self.port], [], [], 1.0)
-                if ret and (not self.exitevent.isSet()):
+                if ret[0] and (not self.exitevent.isSet()):
                     buf = self.port.recv(self.blocklen)
                     if buf:
                         # Compare the recvd data with the control data
@@ -186,156 +404,13 @@
                                 for buf in self.buffer:
                                     ch_ += buf
                                 logging.error("Queue = %s", repr(ch_))
-                                raise error.TestFail("th_recv_check: incorrect "
+                                raise error.TestFail("ThRecvCheck: incorrect "
                                                      "data")
                         self.idx += len(buf)
-            logging.debug("th_recv_check %s: exit(%d)", self.getName(),
+            logging.debug("ThRecvCheck %s: exit(%d)", self.getName(),
                           self.idx)
 
 
-    class cpu_load():
-        """
-        Get average cpu load between start and get_load.
-        """
-        def __init__ (self):
-            self.old_load = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
-            self.startTime = 0
-            self.endTime = 0
-
-
-        def _get_cpu_load(self):
-            # Let's see if we can calc system load.
-            try:
-                f = open("/proc/stat", "r")
-                tmp = f.readlines(200)
-                f.close()
-            except:
-                logging.critical("Error reading /proc/stat")
-                error.TestFail("average_cpu_load: Error reading /proc/stat")
-
-            # 200 bytes should be enough because the information we need
-            # is typically stored in the first line
-            # Info about individual processors (not yet supported) is in
-            # the second (third, ...?) line
-            for line in tmp:
-                if line[0:4] == "cpu ":
-                    reg = re.compile('[0-9]+')
-                    load_values = reg.findall(line)
-                    # extract values from /proc/stat
-                    load = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
-                    for i in range(8):
-                        load[i] = int(load_values[i]) - self.old_load[i]
-
-                    for i in range(8):
-                        self.old_load[i] = int(load_values[i])
-                    return load
-
-
-        def start (self):
-            """
-            Start CPU usage measurement
-            """
-            self.old_load = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
-            self.startTime = time.time()
-            self._get_cpu_load()
-
-
-        def get_load(self):
-            """
-            Get and reset CPU usage
-
-            @return: return group cpu (user[%], system[%], sum[%], testTime[s])
-            """
-            self.endTime = time.time()
-            testTime = self.endTime - self.startTime
-            load = self._get_cpu_load()
-
-            user = load[0] / testTime
-            system = load[2] / testTime
-            sum = user + system
-
-            return (user, system, sum, testTime)
-
-
-    class pid_load():
-        """
-        Get average process cpu load between start and get_load
-        """
-        def __init__ (self, pid, name):
-            self.old_load = [0, 0]
-            self.startTime = 0
-            self.endTime = 0
-            self.pid = pid
-            self.name = name
-
-
-        def _get_cpu_load(self, pid):
-            # Let's see if we can calc system load.
-            try:
-                f = open("/proc/%d/stat" % (pid), "r")
-                line = f.readline()
-                f.close()
-            except:
-                logging.critical("Error reading /proc/%d/stat", pid)
-                error.TestFail("average_process_cpu_load: Error reading "
-                               "/proc/stat")
-            else:
-                reg = re.compile('[0-9]+')
-                load_values = reg.findall(line)
-                del load_values[0:11]
-                # extract values from /proc/stat
-                load = [0, 0]
-                for i in range(2):
-                    load[i] = int(load_values[i]) - self.old_load[i]
-
-                for i in range(2):
-                    self.old_load[i] = int(load_values[i])
-                return load
-
-
-        def start (self):
-            """
-            Start CPU usage measurement
-            """
-            self.old_load = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
-            self.startTime = time.time()
-            self._get_cpu_load(self.pid)
-
-
-        def get_load(self):
-            """
-            Get and reset CPU usage.
-
-            @return: Group cpu
-                    (pid, user[%], system[%], sum[%], testTime[s])
-            """
-            self.endTime = time.time()
-            testTime = self.endTime - self.startTime
-            load = self._get_cpu_load(self.pid)
-
-            user = load[0] / testTime
-            system = load[1] / testTime
-            sum = user + system
-
-            return (self.name, self.pid, user, system, sum, testTime)
-
-
-    def print_load(process, system):
-        """
-        Print load in tabular mode.
-
-        @param process: List of process statistic tuples.
-        @param system: Tuple of system cpu usage.
-        """
-
-        logging.info("%-10s %6s %5s %5s %5s %11s",
-                     "NAME", "PID", "USER", "SYS", "SUM", "TIME")
-        for pr in process:
-            logging.info("%-10s %6d %4.0f%% %4.0f%% %4.0f%% %10.3fs" % pr)
-        logging.info("TOTAL:     ------ %4.0f%% %4.0f%% %4.0f%% %10.3fs" %
-                     system)
-
-
     def process_stats(stats, scale=1.0):
         """
         Process and print the statistic.
@@ -352,7 +427,7 @@
         return stats
 
 
-    def init_guest(vm, timeout=2):
+    def _init_guest(vm, timeout=2):
         """
         Execute virtio_guest.py on guest, wait until it is initialized.
 
@@ -361,22 +436,23 @@
                 started properly.
         """
         logging.debug("compile virtio_guest.py on guest %s", vm[0].name)
-        vm[1].sendline("python -OO /tmp/virtio_guest.py -c &&"
+
+        (match, data) = _on_guest("python -OO /tmp/virtio_guest.py -c &&"
                        "echo -n 'PASS: Compile virtio_guest finished' ||"
-                       "echo -n 'FAIL: Compile virtio_guest failed'")
-        (match, data) = vm[1].read_until_last_line_matches(["PASS:", "FAIL:"],
-                                                           timeout)
-        if match == 1 or match is None:
+                       "echo -n 'FAIL: Compile virtio_guest failed'",
+                        vm, timeout)
+
+        if match != 0:
             raise error.TestFail("Command console_switch.py on guest %s failed."
                                  "\nreturn code: %s\n output:\n%s" %
                                  (vm[0].name, match, data))
         logging.debug("Starting virtio_guest.py on guest %s", vm[0].name)
-        vm[1].sendline("python /tmp/virtio_guest.pyo &&"
+        vm[1].sendline()
+        (match, data) = _on_guest("python /tmp/virtio_guest.pyo &&"
                        "echo -n 'PASS: virtio_guest finished' ||"
-                       "echo -n 'FAIL: virtio_guest failed'")
-        (match, data) = vm[1].read_until_last_line_matches(["PASS:", "FAIL:"],
-                                                           timeout)
-        if match == 1 or match is None:
+                       "echo -n 'FAIL: virtio_guest failed'",
+                       vm, timeout)
+        if match != 0:
             raise error.TestFail("Command console_switch.py on guest %s failed."
                                  "\nreturn code: %s\n output:\n%s" %
                                  (vm[0].name, match, data))
@@ -384,6 +460,21 @@
         time.sleep(2)
 
 
+    def init_guest(vm, consoles):
+        """
+        Prepares guest, executes virtio_guest.py and initialize for testing
+
+        @param vm: Informations about the guest.
+        @param consoles: Informations about consoles
+        """
+        conss = []
+        for mode in consoles:
+            for cons in mode:
+                conss.append(cons.for_guest())
+        _init_guest(vm, 10)
+        on_guest("virt.init(%s)" % (conss), vm, 10)
+
+
     def _on_guest(command, vm, timeout=2):
         """
         Execute given command inside the script's main loop, indicating the vm
@@ -398,9 +489,13 @@
         logging.debug("Executing '%s' on virtio_guest.py loop, vm: %s," +
                       "timeout: %s", command, vm[0].name, timeout)
         vm[1].sendline(command)
-        (match, data) = vm[1].read_until_last_line_matches(["PASS:", 
-                                                    "FAIL:[Failed to execute]"],
-                                                    timeout)
+        try:
+            (match, data) = vm[1].read_until_last_line_matches(["PASS:",
+                                                                "FAIL:"],
+                                                               timeout)
+        except (kvm_subprocess.ExpectError):
+            match = None
+            data = "Timeout."
         return (match, data)
 
 
@@ -425,29 +520,6 @@
         return (match, data)
 
 
-    def socket_readall(sock, read_timeout, mesagesize):
-        """
-        Read everything from the socket.
-
-        @param sock: Socket.
-        @param read_timeout: Read timeout.
-        @param mesagesize: Size of message.
-        """
-        sock_decriptor = sock.fileno()
-        sock.settimeout(read_timeout)
-        message = ""
-        try:
-            while (len(message) < mesagesize):
-                message += sock.recv(mesagesize)
-        except Exception as inst:
-            if (inst.args[0] == "timed out"):
-                logging.debug("Reading timeout")
-            else:
-                logging.debug(inst)
-        sock.setblocking(1)
-        return message
-
-
     def _guest_exit_threads(vm, send_pts, recv_pts):
         """
         Safely executes on_guest("virt.exit_threads()") using workaround of
@@ -463,7 +535,7 @@
             logging.debug("Workaround the stuck thread on guest")
             # Thread is stucked in read/write
             for send_pt in send_pts:
-                send_pt[0].sendall(".")
+                send_pt.sock.sendall(".")
         elif match != 0:
             # Something else
             raise error.TestFail("Unexpected fail\nMatch: %s\nData:\n%s"
@@ -471,8 +543,8 @@
 
         # Read-out all remaining data
         for recv_pt in recv_pts:
-            while select.select([recv_pt[0]], [], [], 0.1)[0]:
-                recv_pt[0].recv(1024)
+            while select.select([recv_pt.sock], [], [], 0.1)[0]:
+                recv_pt.sock.recv(1024)
 
         # This will cause fail in case anything went wrong.
         on_guest("print 'PASS: nothing'", vm, 10)
@@ -482,6 +554,13 @@
         """
         Creates the VM and connects the specified number of consoles and serial
         ports.
+        Ports are allocated by 2 per 1 virtio-serial-pci device starting with
+        console. (3+2 => CC|CS|S; 0+2 => SS; 3+4 => CC|CS|SS|S, ...) This way
+        it's easy to test communication on the same or different
+        virtio-serial-pci device.
+        Further in tests the consoles are being picked always from the first
+        available one (3+2: 2xC => CC|cs|s <communication on the same PCI>;
+        2xC,1xS => CC|cS|s <communication between 2 PCI devs)
 
         @param no_console: Number of desired virtconsoles.
         @param no_serialport: Number of desired virtserialports.
@@ -494,27 +573,38 @@
         tmp_dir = tempfile.mkdtemp(prefix="virtio-console-", dir="/tmp/")
         if not params.get('extra_params'):
             params['extra_params'] = ''
-        params['extra_params'] += " -device virtio-serial"
 
-        for i in  range(0, no_console):
+        for i in range(0, no_console):
+            # Spread consoles between multiple PCI devices (2 per a dev)
+            if not i % 2:
+                pci = "virtio-serial-pci%d" % (i / 2)
+                params['extra_params'] += (" -device virtio-serial-pci,id="
+                                           + pci)
+                pci += ".0"
             params['extra_params'] += (" -chardev socket,path=%s/%d,id=vc%d,"
                                        "server,nowait" % (tmp_dir, i, i))
             params['extra_params'] += (" -device virtconsole,chardev=vc%d,"
-                                      "name=console-%d,id=c%d" % (i, i, i))
+                                      "name=console-%d,id=c%d,bus=%s"
+                                      % (i, i, i, pci))
 
         for i in  range(no_console, no_console + no_serialport):
+            # Spread seroal ports between multiple PCI devices (2 per a dev)
+            if not i % 2:
+                pci = "virtio-serial-pci%d" % (i / 2)
+                params['extra_params'] += (" -device virtio-serial-pci,id="
+                                           + pci)
+                pci += ".0"
             params['extra_params'] += (" -chardev socket,path=%s/%d,id=vs%d,"
                                        "server,nowait" % (tmp_dir, i, i))
             params['extra_params'] += (" -device virtserialport,chardev=vs%d,"
-                                       "name=serialport-%d,id=p%d" % (i, i, i))
-
+                                       "name=serialport-%d,id=p%d,bus=%s"
+                                       % (i, i, i, pci))
 
         logging.debug("Booting first guest %s", params.get("main_vm"))
         kvm_preprocessing.preprocess_vm(test, params, env,
                                         params.get("main_vm"))
 
-
-        vm = kvm_utils.env_get_vm(env, params.get("main_vm"))
+        vm = env.get_vm(params.get("main_vm"))
 
         session = kvm_test_utils.wait_for_login(vm, 0,
                                          float(params.get("boot_timeout", 240)),
@@ -522,159 +612,269 @@
 
         # connect the sockets
         for i in range(0, no_console):
-            sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
-            sock.connect("%s/%d" % (tmp_dir, i))
-            consoles.append([sock, "console-%d" % i, "yes"])
+            consoles.append(Port(None ,"console-%d" % i,
+                                 "yes", "%s/%d" % (tmp_dir, i)))
         for i in range(no_console, no_console + no_serialport):
-            sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
-            sock.connect("%s/%d" % (tmp_dir, i))
-            serialports.append([sock, "serialport-%d" % i, "no"])
+            serialports.append(Port(None ,"serialport-%d" % i,
+                                    "no", "%s/%d" % (tmp_dir, i)))
 
         return [vm, session, tmp_dir], [consoles, serialports]
 
 
-    def test_smoke(vm, consoles, params):
+    def topen(vm, port):
         """
-        Virtio console smoke test.
+        Open virtioconsole port.
 
-        Tests the basic functionalities (poll, read/write with and without
-        connected host, etc.
-
-        @param vm: target virtual machine [vm, session, tmp_dir]
-        @param consoles: a field of virtio ports with the minimum of 2 items
-        @param params: test parameters '$console_type:$data;...'
+        @param vm: Target virtual machine [vm, session, tmp_dir].
+        @param port: Port identifier.
         """
-        logging.info("Smoke test: Tests the basic capabilities of "
-                     "virtio_consoles.")
-        # PREPARE
-        for param in params.split(';'):
-            if not param:
-                continue
-            logging.info("test_smoke: params: %s", param)
-            param = param.split(':')
-            if len(param) > 1:
-                data = param[1]
-            else:
-                data = "Smoke test data"
-            param = (param[0] == 'serialport')
-            send_pt = consoles[param][0]
-            recv_pt = consoles[param][1]
-
-            # TEST
-            # Poll (OUT)
-            on_guest("virt.poll('%s', %s)" % (send_pt[1], select.POLLOUT), vm,
-                     2)
-
-            # Poll (IN, OUT)
-            send_pt[0].sendall("test")
-            for test in [select.POLLIN, select.POLLOUT]:
-                on_guest("virt.poll('%s', %s)" % (send_pt[1], test), vm, 2)
-
-            # Poll (IN HUP)
-            # I store the socket informations and close the socket
-            sock = send_pt[0]
-            send_pt[0] = sock.getpeername()
-            sock.shutdown(2)
-            sock.close()
-            del sock
-            for test in [select.POLLIN, select.POLLHUP]:
-                on_guest("virt.poll('%s', %s)" % (send_pt[1], test), vm, 2)
-
-            # Poll (HUP)
-            on_guest("virt.recv('%s', 4, 1024, False)" % (send_pt[1]), vm, 2)
-            on_guest("virt.poll('%s', %s)" % (send_pt[1], select.POLLHUP), vm,
-                     2)
-
-            # Reconnect the socket
-            sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
-            sock.connect(send_pt[0])
-            send_pt[0] = sock
-            # Redefine socket in consoles
-            consoles[param][0] = send_pt
-            on_guest("virt.poll('%s', %s)" % (send_pt[1], select.POLLOUT), vm,
-                     2)
-
-            # Read/write without host connected
-            # I store the socket informations and close the socket
-            sock = send_pt[0]
-            send_pt[0] = sock.getpeername()
-            sock.shutdown(2)
-            sock.close()
-            del sock
-            # Read should pass
-            on_guest("virt.recv('%s', 0, 1024, False)" % send_pt[1], vm, 2)
-            # Write should timed-out
-            match, tmp = _on_guest("virt.send('%s', 10, False)"
-                                    % send_pt[1], vm, 2)
-            if match != None:
-                raise error.TestFail("Read on guest while host disconnected "
-                                     "didn't timed out.\nOutput:\n%s"
-                                     % tmp)
-            sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
-            sock.connect(send_pt[0])
-            send_pt[0] = sock
-
-            # Redefine socket in consoles
-            consoles[param][0] = send_pt
-            if (send_pt[0].recv(1024) < 10):
-                raise error.TestFail("Didn't received data from guest")
-            # Now the _on_guest("virt.send('%s'... command should be finished
-            on_guest("print 'PASS: nothing'", vm, 2)
-
-            # Non-blocking mode
-            on_guest("virt.blocking('%s', False)" % send_pt[1], vm, 2)
-            # Recv should return FAIL with 0 received data
-            match, tmp = _on_guest("virt.recv('%s', 10, 1024, False)" %
-                                   send_pt[1], vm, 2)
-            if match == 0:
-                raise error.TestFail("Received data even when non were sent\n"
-                                     "Data:\n%s" % tmp)
-            elif match == None:
-                raise error.TestFail("Timed out, probably in blocking mode\n"
-                                     "Data:\n%s" % tmp)
-            elif match != 1:
-                raise error.TestFail("Unexpected fail\nMatch: %s\nData:\n%s" %
-                                     (match, tmp))
-            send_pt[0].sendall("1234567890")
-            on_guest("virt.recv('%s', 10, 1024, False)" % send_pt[1], vm, 2)
-
-            # Blocking mode
-            on_guest("virt.blocking('%s', True)" % send_pt[1], vm, 2)
-            # Recv should timed out
-            match, tmp = _on_guest("virt.recv('%s', 10, 1024, False)" %
-                                   send_pt[1], vm, 2)
-            if match == 0:
-                raise error.TestFail("Received data even when non were sent\n"
-                                     "Data:\n%s" % tmp)
-            elif match != None:
-                raise error.TestFail("Unexpected fail\nMatch: %s\nData:\n%s" %
-                                     (match, tmp))
-            send_pt[0].sendall("1234567890")
-            # Now guest received the data end escaped from the recv()
-            on_guest("print 'PASS: nothing'", vm, 2)
-
-            # Basic loopback test
-            on_guest("virt.loopback(['%s'], ['%s'], 1024, virt.LOOP_NONE)" %
-                     (send_pt[1], recv_pt[1]), vm, 2)
-            send_pt[0].sendall(data)
-            tmp = ""
-            i = 0
-            while i <= 10:
-                i += 1
-                ret = select.select([recv_pt[0]], [], [], 1.0)
-                if ret:
-                    tmp += recv_pt[0].recv(1024)
-                if len(tmp) >= len(data):
-                    break
-            if tmp != data:
-                raise error.TestFail("Incorrect data: '%s' != '%s'",
-                                     data, tmp)
-            _guest_exit_threads(vm, [send_pt], [recv_pt])
-
-        return consoles
+        on_guest("virt.open('%s')" % (port.name), vm, 2)
+        port.open()
 
 
-    def test_loopback(vm, consoles, params):
+    def tmulti_open(vm, port):
+        """
+        Multiopen virtioconsole port.
+
+        @param vm: Target virtual machine [vm, session, tmp_dir].
+        @param port: Port identifier.
+        """
+        on_guest("virt.close('%s')" % (port.name), vm, 2)
+        on_guest("virt.open('%s')" % (port.name), vm, 2)
+        (match, data) = _on_guest("virt.open('%s')" % (port.name), vm, 2)
+        # Console is permitted to open the device multiple times
+        if port.port_type == "yes": #is console?
+            if match != 0: #Multiopen not pass
+                raise error.TestFail("Unexpected fail of opening the console"
+                                     " device for the 2nd time.\n%s" % data)
+        else:
+            if match != 1: #Multiopen not fail:
+                raise error.TestFail("Unexpetded pass of opening the"
+                                     " serialport device for the 2nd time.")
+            elif not "[Errno 24]" in data:
+                raise error.TestFail("Multiple opening fail but with another"
+                                     " exception %s" % data)
+        port.open()
+
+    def tclose(vm, port):
+        """
+        Close socket.
+
+        @param vm: Target virtual machine [vm, session, tmp_dir].
+        @param port: Port to open.
+        """
+        on_guest("virt.close('%s')" % (port.name), vm, 2)
+        port.close()
+
+
+    def tpooling(vm, port):
+        """
+        Test try pooling function.
+
+        @param vm: Target virtual machine [vm, session, tmp_dir].
+        @param port: Port used in test.
+        """
+        # Poll (OUT)
+        on_guest("virt.poll('%s', %s)" % (port.name, select.POLLOUT), vm,
+                 2)
+
+        # Poll (IN, OUT)
+        port.sock.sendall("test")
+        for test in [select.POLLIN, select.POLLOUT]:
+            on_guest("virt.poll('%s', %s)" % (port.name, test), vm, 2)
+
+        # Poll (IN HUP)
+        # I store the socket informations and close the socket
+        port.close()
+        for test in [select.POLLIN, select.POLLHUP]:
+            on_guest("virt.poll('%s', %s)" % (port.name, test), vm, 2)
+
+        # Poll (HUP)
+        on_guest("virt.recv('%s', 4, 1024, False)" % (port.name), vm, 2)
+        on_guest("virt.poll('%s', %s)" % (port.name, select.POLLHUP), vm,
+                 2)
+
+        # Reconnect the socket
+        port.open()
+        # Redefine socket in consoles
+        on_guest("virt.poll('%s', %s)" % (port.name, select.POLLOUT), vm,
+                 2)
+
+
+    def tsigio(vm, port):
+        """
+        Test try sigio function.
+
+        @param vm: Target virtual machine [vm, session, tmp_dir].
+        @param port: Port used in test.
+        """
+        if port.is_open:
+            port.close()
+
+        # Enable sigio on specific port
+        on_guest("virt.async('%s', True, 0)" %
+                 (port.name) , vm, 5)
+        on_guest("virt.get_sigio_poll_return('%s')" % (port.name) , vm, 2)
+
+        #Test sigio when port open
+        on_guest("virt.set_pool_want_return('%s', select.POLLOUT)" %
+                 (port.name), vm, 2)
+        port.open()
+        match = _on_guest("virt.get_sigio_poll_return('%s')" %
+                          (port.name) , vm, 2)[0]
+        if match == 1:
+            raise error.TestFail("Problem with HUP on console port.")
+
+        #Test sigio when port receive data
+        on_guest("virt.set_pool_want_return('%s', select.POLLOUT |"
+                 " select.POLLIN)" % (port.name), vm, 2)
+        port.sock.sendall("0123456789")
+        on_guest("virt.get_sigio_poll_return('%s')" % (port.name) , vm, 2)
+
+        #Test sigio port close event
+        on_guest("virt.set_pool_want_return('%s', select.POLLHUP |"
+                 " select.POLLIN)" % (port.name), vm, 2)
+        port.close()
+        on_guest("virt.get_sigio_poll_return('%s')" % (port.name) , vm, 2)
+
+        #Test sigio port open event and persistence of written data on port.
+        on_guest("virt.set_pool_want_return('%s', select.POLLOUT |"
+                 " select.POLLIN)" % (port.name), vm, 2)
+        port.open()
+        on_guest("virt.get_sigio_poll_return('%s')" % (port.name) , vm, 2)
+
+        #Test event when erase data.
+        on_guest("virt.clean_port('%s')" % (port.name), vm, 2)
+        port.close()
+        on_guest("virt.set_pool_want_return('%s', select.POLLOUT)"
+                 % (port.name), vm, 2)
+        port.open()
+        on_guest("virt.get_sigio_poll_return('%s')" % (port.name) , vm, 2)
+
+        # Disable sigio on specific port
+        on_guest("virt.async('%s', False, 0)" %
+                 (port.name) , vm, 5)
+
+
+    def tlseek(vm, port):
+        """
+        Tests the correct handling of lseek (expected fail)
+
+        @param vm: Target virtual machine [vm, session, tmp_dir].
+        @param port: Port used in test.
+        """
+        # The virt.lseek returns PASS when the seek fails
+        on_guest("virt.lseek('%s', 0, 0)" % (port.name), vm, 2)
+
+
+    def trw_host_offline(vm, port):
+        """
+        Guest read/write from host when host is disconnected.
+
+        @param vm: Target virtual machine [vm, session, tmp_dir].
+        @param port: Port used in test.
+        """
+        if port.is_open:
+            port.close()
+
+        on_guest("virt.recv('%s', 0, 1024, False)" % port.name, vm, 2)
+        match, tmp = _on_guest("virt.send('%s', 10, False)"
+                                % port.name, vm, 2)
+        if match != None:
+            raise error.TestFail("Write on guest while host disconnected "
+                                 "didn't timed out.\nOutput:\n%s"
+                                 % tmp)
+
+        port.open()
+
+        if (port.sock.recv(1024) < 10):
+            raise error.TestFail("Didn't received data from guest")
+        # Now the _on_guest("virt.send('%s'... command should be finished
+        on_guest("print 'PASS: nothing'", vm, 2)
+
+
+    def trw_blocking_mode(vm, port):
+        """
+        Guest read\write data in blocking mode.
+
+        @param vm: Target virtual machine [vm, session, tmp_dir].
+        @param port: Port used in test.
+        """
+        # Blocking mode
+        if not port.is_open:
+            port.open()
+        on_guest("virt.blocking('%s', True)" % port.name, vm, 2)
+        # Recv should timed out
+        match, tmp = _on_guest("virt.recv('%s', 10, 1024, False)" %
+                               port.name, vm, 2)
+        if match == 0:
+            raise error.TestFail("Received data even when non were sent\n"
+                                 "Data:\n%s" % tmp)
+        elif match != None:
+            raise error.TestFail("Unexpected fail\nMatch: %s\nData:\n%s" %
+                                 (match, tmp))
+        port.sock.sendall("1234567890")
+        # Now guest received the data end escaped from the recv()
+        on_guest("print 'PASS: nothing'", vm, 2)
+
+
+    def trw_nonblocking_mode(vm, port):
+        """
+        Guest read\write data in nonblocking mode.
+
+        @param vm: Target virtual machine [vm, session, tmp_dir].
+        @param port: Port used in test.
+        """
+        # Non-blocking mode
+        if not port.is_open:
+            port.open()
+        on_guest("virt.blocking('%s', False)" % port.name, vm, 2)
+        # Recv should return FAIL with 0 received data
+        match, tmp = _on_guest("virt.recv('%s', 10, 1024, False)" %
+                              port.name, vm, 2)
+        if match == 0:
+            raise error.TestFail("Received data even when non were sent\n"
+                                 "Data:\n%s" % tmp)
+        elif match == None:
+            raise error.TestFail("Timed out, probably in blocking mode\n"
+                                 "Data:\n%s" % tmp)
+        elif match != 1:
+            raise error.TestFail("Unexpected fail\nMatch: %s\nData:\n%s" %
+                                 (match, tmp))
+        port.sock.sendall("1234567890")
+        on_guest("virt.recv('%s', 10, 1024, False)" % port.name, vm, 2)
+
+
+    def tbasic_loopback(vm, send_port, recv_port, data="Smoke test data"):
+        """
+        Easy loop back test with loop over only two port.
+
+        @param vm: Target virtual machine [vm, session, tmp_dir].
+        @param port: Port used in test.
+        """
+        if not send_port.is_open:
+            send_port.open()
+        if not recv_port.is_open:
+            recv_port.open()
+        on_guest("virt.loopback(['%s'], ['%s'], 1024, virt.LOOP_NONE)" %
+                     (send_port.name, recv_port.name), vm, 2)
+        send_port.sock.sendall(data)
+        tmp = ""
+        i = 0
+        while i <= 10:
+            i += 1
+            ret = select.select([recv_port.sock], [], [], 1.0)
+            if ret:
+                tmp += recv_port.sock.recv(1024)
+            if len(tmp) >= len(data):
+                break
+        if tmp != data:
+            raise error.TestFail("Incorrect data: '%s' != '%s'",
+                                 data, tmp)
+        _guest_exit_threads(vm, [send_port], [recv_port])
+
+
+    def tloopback(vm, consoles, params):
         """
         Virtio console loopback test.
 
@@ -682,21 +882,18 @@
         ports and sends length amount of data through this connection.
         It validates the correctness of the data sent.
 
-        @param vm: target virtual machine [vm, session, tmp_dir]
-        @param consoles: a field of virtio ports with the minimum of 2 items
+        @param vm: Target virtual machine [vm, session, tmp_dir].
+        @param consoles: Field of virtio ports with the minimum of 2 items.
         @param params: test parameters, multiple recievers allowed.
             '$source_console_type@buffer_length:
              $destination_console_type1@$buffer_length:...:
              $loopback_buffer_length;...'
         """
-        logging.info("Loopback test: Creates a loopback between sender port "
-                     "and receiving port, send data through this connection, "
-                     "verify data correctness.")
         # PREPARE
         for param in params.split(';'):
             if not param:
                 continue
-            logging.info("test_loopback: params: %s", param)
+            logging.info("test_loopback: params: %s" % (param))
             param = param.split(':')
             idx_serialport = 0
             idx_console = 0
@@ -730,6 +927,13 @@
             if len(buf_len) == (idx_console + idx_serialport):
                 buf_len.append(1024)
 
+            for p in recv_pts:
+                if not p.is_open:
+                    p.open()
+
+            if not send_pt.is_open:
+                send_pt.open()
+
             if len(recv_pts) == 0:
                 raise error.TestFail("test_loopback: incorrect recv consoles"
                                      "definition")
@@ -739,21 +943,22 @@
             for i in range(0, len(recv_pts)):
                 queues.append(deque())
 
-            tmp = "'%s'" % recv_pts[0][1]
+            tmp = "'%s'" % recv_pts[0].name
             for recv_pt in recv_pts[1:]:
-                tmp += ", '%s'" % (recv_pt[1])
+                tmp += ", '%s'" % (recv_pt.name)
             on_guest("virt.loopback(['%s'], [%s], %d, virt.LOOP_POLL)"
-                     % (send_pt[1], tmp, buf_len[-1]), vm, 2)
+                     % (send_pt.name, tmp, buf_len[-1]), vm, 2)
 
             exit_event = threading.Event()
 
             # TEST
-            thread = th_send_check(send_pt[0], exit_event, queues, buf_len[0])
+            thread = ThSendCheck(send_pt.sock, exit_event, queues,
+                                   buf_len[0])
             thread.start()
             threads.append(thread)
 
             for i in range(len(recv_pts)):
-                thread = th_recv_check(recv_pts[i][0], queues[i], exit_event,
+                thread = ThRecvCheck(recv_pts[i].sock, queues[i], exit_event,
                                        buf_len[i + 1])
                 thread.start()
                 threads.append(thread)
@@ -770,8 +975,8 @@
 
             # Read-out all remaining data
             for recv_pt in recv_pts:
-                while select.select([recv_pt[0]], [], [], 0.1)[0]:
-                    recv_pt[0].recv(1024)
+                while select.select([recv_pt.sock], [], [], 0.1)[0]:
+                    recv_pt.sock.recv(1024)
 
             _guest_exit_threads(vm, [send_pt], recv_pts)
 
@@ -779,23 +984,21 @@
             del threads[:]
 
 
-    def test_perf(vm, consoles, params):
+    def tperf(vm, consoles, params):
         """
         Tests performance of the virtio_console tunel. First it sends the data
         from host to guest and than back. It provides informations about
         computer utilisation and statistic informations about the troughput.
 
-        @param vm: target virtual machine [vm, session, tmp_dir]
-        @param consoles: a field of virtio ports with the minimum of 2 items
+        @param vm: Target virtual machine [vm, session, tmp_dir].
+        @param consoles: Field of virtio ports with the minimum of 2 items.
         @param params: test parameters:
                 '$console_type@$buffer_length:$test_duration;...'
         """
-        logging.info("Performance test: Measure performance for the "
-                     "virtio console tunnel")
         for param in params.split(';'):
             if not param:
                 continue
-            logging.info("test_perf: params: %s", param)
+            logging.info("test_perf: params: %s" % (param))
             param = param.split(':')
             duration = 60.0
             if len(param) > 1:
@@ -811,39 +1014,38 @@
             param = (param[0] == 'serialport')
             port = consoles[param][0]
 
+            if not port.is_open:
+                port.open()
+
             data = ""
             for i in range(buf_len):
                 data += "%c" % random.randrange(255)
 
             exit_event = threading.Event()
-            slice = float(duration)/100
+            slice = float(duration) / 100
 
             # HOST -> GUEST
             on_guest('virt.loopback(["%s"], [], %d, virt.LOOP_NONE)' %
-                     (port[1], buf_len), vm, 2)
-            thread = th_send(port[0], data, exit_event)
+                     (port.name, buf_len), vm, 2)
+            thread = ThSend(port.sock, data, exit_event)
             stats = array.array('f', [])
-            loads = []
-            loads.append(cpu_load())
-            loads.append(pid_load(os.getpid(), 'autotest'))
-            loads.append(pid_load(vm[0].get_pid(), 'VM'))
-
-            for load in loads:
-                load.start()
+            loads = utils.SystemLoad([(os.getpid(), 'autotest'),
+                                      (vm[0].get_pid(), 'VM'), 0])
+            loads.start()
             _time = time.time()
             thread.start()
             for i in range(100):
                 stats.append(thread.idx)
                 time.sleep(slice)
             _time = time.time() - _time - duration
-            print_load([loads[1].get_load(), loads[2].get_load()],
-                       loads[0].get_load())
+            logging.info("\n" + loads.get_cpu_status_string()[:-1])
+            logging.info("\n" + loads.get_mem_status_string()[:-1])
             exit_event.set()
             thread.join()
 
             # Let the guest read-out all the remaining data
             while not _on_guest("virt.poll('%s', %s)" %
-                                (port[1], select.POLLIN), vm, 2)[0]:
+                                (port.name, select.POLLIN), vm, 2)[0]:
                 time.sleep(1)
 
             _guest_exit_threads(vm, [port], [])
@@ -853,30 +1055,29 @@
                 "Test ran %fs longer which is more than one slice", _time)
             else:
                 logging.debug("Test ran %fs longer", _time)
-            stats = process_stats(stats[1:], slice*1048576)
+            stats = process_stats(stats[1:], slice * 1048576)
             logging.debug("Stats = %s", stats)
             logging.info("Host -> Guest [MB/s] (min/med/max) = %.3f/%.3f/%.3f",
-                         stats[0], stats[len(stats)/2], stats[-1])
+                        stats[0], stats[len(stats) / 2], stats[-1])
 
             del thread
 
             # GUEST -> HOST
             exit_event.clear()
             stats = array.array('f', [])
-            on_guest("virt.send_loop_init('%s', %d)" % (port[1], buf_len),
+            on_guest("virt.send_loop_init('%s', %d)" % (port.name, buf_len),
                      vm, 30)
-            thread = th_recv(port[0], exit_event, buf_len)
+            thread = ThRecv(port.sock, exit_event, buf_len)
             thread.start()
-            for load in loads:
-                load.start()
+            loads.start()
             on_guest("virt.send_loop()", vm, 2)
             _time = time.time()
             for i in range(100):
                 stats.append(thread.idx)
                 time.sleep(slice)
             _time = time.time() - _time - duration
-            print_load([loads[1].get_load(), loads[2].get_load()],
-                       loads[0].get_load())
+            logging.info("\n" + loads.get_cpu_status_string()[:-1])
+            logging.info("\n" + loads.get_mem_status_string()[:-1])
             on_guest("virt.exit_threads()", vm, 2)
             exit_event.set()
             thread.join()
@@ -885,37 +1086,152 @@
                 "Test ran %fs longer which is more than one slice", _time)
             else:
                 logging.debug("Test ran %fs longer" % _time)
-            stats = process_stats(stats[1:], slice*1048576)
+            stats = process_stats(stats[1:], slice * 1048576)
             logging.debug("Stats = %s", stats)
             logging.info("Guest -> Host [MB/s] (min/med/max) = %.3f/%.3f/%.3f",
-                         stats[0], stats[len(stats)/2], stats[-1])
+                         stats[0], stats[len(stats) / 2], stats[-1])
 
             del thread
-
             del exit_event
-            del loads[:]
+
+
+    def clean_ports(vm, consoles):
+        """
+        Clean state of all ports and set port to default state.
+        Default state:
+           No data on port or in port buffer.
+           Read mode = blocking.
+
+        @param consoles: Consoles which should be clean.
+        """
+        # Check if python is still alive
+        print "CLEANING"
+        match, tmp = _on_guest("is_alive()", vm, 10)
+        if (match == None) or (match != 0):
+            logging.error("Python died/is stucked/have remaining threads")
+            logging.debug(tmp)
+            vm[1].close()
+            try:
+                vm[1] = kvm_test_utils.wait_for_login(vm[0], 0,
+                                        float(params.get("boot_timeout", 5)),
+                                        0, 2)
+                on_guest("killall -9 python "
+                         "&& echo -n PASS: python killed"
+                         "|| echo -n PASS: python was death",
+                         vm, 10)
+
+                init_guest(vm, consoles)
+                on_guest("virt.clean_port('%s'),1024" % consoles[0][0].name,
+                         vm, 2)
+                on_guest("virt.close('%s'),1024" %
+                         consoles[0][0].name, vm, 2)
+
+            except (error.TestFail, kvm_subprocess.ExpectError,
+                    Exception), inst:
+                logging.error(inst)
+                logging.error("Virtio-console driver is irreparably"
+                              " blocked. Every comd end with sig KILL."
+                              "Try reboot vm for continue in testing.")
+                vm[1] = kvm_test_utils.reboot(vm[0], vm[1], "system_reset")
+                init_guest(vm, consoles)
+                match = _on_guest("virt.clean_port('%s'),1024" %
+                                      consoles[0][0].name, vm, 2)[0]
+
+                if (match == None) or (match != 0):
+                    raise error.TestFail("Virtio-console driver is irrepar"
+                                         "ably blocked. Every comd end"
+                                         " with sig KILL. Neither the "
+                                         "restart did not help.")
+
+        for ctype in consoles:
+            for port in ctype:
+                openned = port.is_open
+                port.clean_port()
+                #on_guest("virt.blocking('%s', True)" % port.name, vm, 2)
+                on_guest("virt.clean_port('%s'),1024" % port.name, vm, 5)
+                if not openned:
+                    port.close()
+                    on_guest("virt.close('%s'),1024" % port.name, vm, 2)
+
+
+    def test_smoke(test, vm, consoles, params):
+        """
+        Virtio console smoke test.
+
+        Tests the basic functionalities (poll, read/write with and without
+        connected host, etc.
+
+        @param test: Main test object.
+        @param vm: Target virtual machine [vm, session, tmp_dir].
+        @param consoles: Field of virtio ports with the minimum of 2 items.
+        @param params: Test parameters '$console_type:$data;...'
+        """
+        # PREPARE
+        for param in params.split(';'):
+            if not param:
+                continue
+            headline = "test_smoke: params: %s" % (param)
+            logging.info(headline)
+            param = param.split(':')
+            if len(param) > 1:
+                data = param[1]
+            else:
+                data = "Smoke test data"
+            param = (param[0] == 'serialport')
+            send_pt = consoles[param][0]
+            recv_pt = consoles[param][1]
+            test.headline(headline)
+            test.do_test(topen, [vm, send_pt], True)
+            test.do_test(tclose, [vm, send_pt], True)
+            test.do_test(tmulti_open, [vm, send_pt])
+            test.do_test(tpooling, [vm, send_pt])
+            test.do_test(tsigio, [vm, send_pt])
+            test.do_test(tlseek, [vm, send_pt])
+            test.do_test(trw_host_offline, [vm, send_pt])
+            test.do_test(trw_nonblocking_mode, [vm, send_pt])
+            test.do_test(trw_blocking_mode, [vm, send_pt])
+            test.do_test(tbasic_loopback, [vm, send_pt, recv_pt, data], True)
+
+
+    def test_multiport(test, vm, consoles, params):
+        """
+        This is group of test which test virtio_console in maximal load and
+        with multiple ports.
+
+        @param test: Main test object.
+        @param vm: Target virtual machine [vm, session, tmp_dir].
+        @param consoles: Field of virtio ports with the minimum of 2 items.
+        @param params: Test parameters '$console_type:$data;...'
+        """
+        test.headline("test_multiport:")
+        #Test Loopback
+        test.do_test(tloopback, [vm, consoles, params[0]])
+
+        #Test Performance
+        test.do_test(tperf, [vm, consoles, params[1]])
 
 
     # INITIALIZE
-    test_smoke_params = params.get('virtio_console_smoke', '')
-    test_loopback_params = params.get('virtio_console_loopback', '')
-    test_perf_params = params.get('virtio_console_perf', '')
+
+    tsmoke_params = params.get('virtio_console_smoke', '')
+    tloopback_params = params.get('virtio_console_loopback', '')
+    tperf_params = params.get('virtio_console_perf', '')
 
     no_serialports = 0
     no_consoles = 0
     # consoles required for Smoke test
-    if (test_smoke_params.count('serialport')):
+    if (tsmoke_params.count('serialport')):
         no_serialports = max(2, no_serialports)
-    if (test_smoke_params.count('console')):
+    if (tsmoke_params.count('console')):
         no_consoles = max(2, no_consoles)
     # consoles required for Loopback test
-    for param in test_loopback_params.split(';'):
+    for param in tloopback_params.split(';'):
         no_serialports = max(no_serialports, param.count('serialport'))
         no_consoles = max(no_consoles, param.count('console'))
     # consoles required for Performance test
-    if (test_perf_params.count('serialport')):
+    if (tperf_params.count('serialport')):
         no_serialports = max(1, no_serialports)
-    if (test_perf_params.count('console')):
+    if (tperf_params.count('console')):
         no_consoles = max(1, no_consoles)
 
     if (no_serialports + no_consoles) == 0:
@@ -933,19 +1249,26 @@
 
     # ACTUAL TESTING
     # Defines all available consoles; tests udev and sysfs
-    conss = []
-    for mode in consoles:
-        for cons in mode:
-            conss.append(cons[1:3])
-    init_guest(vm, 10)
-    on_guest("virt.init(%s)" % (conss), vm, 10)
 
-    consoles = test_smoke(vm, consoles, test_smoke_params)
-    test_loopback(vm, consoles, test_loopback_params)
-    test_perf(vm, consoles, test_perf_params)
+    test = SubTest()
+    try:
+        init_guest(vm, consoles)
+
+        test.set_cleanup_func(clean_ports, [vm, consoles])
+        #Test Smoke
+        test_smoke(test, vm, consoles, tsmoke_params)
+
+        #Test multiport functionality and performance.
+        test_multiport(test, vm, consoles, [tloopback_params, tperf_params])
+    finally:
+        logging.info(("Summary: %d tests passed  %d test failed :\n" %
+                      (test.passed, test.failed)) + test.get_text_result())
+
+    if test.is_failed():
+        raise error.TestFail("Virtio_console test FAILED.")
+
 
     # CLEANUP
     vm[1].close()
     vm[0].destroy(gracefully=False)
     shutil.rmtree(vm[2])
-
diff --git a/client/tests/kvm/tests/vlan.py b/client/tests/kvm/tests/vlan.py
index f41ea6a..69a136b 100644
--- a/client/tests/kvm/tests/vlan.py
+++ b/client/tests/kvm/tests/vlan.py
@@ -1,6 +1,6 @@
 import logging, time, re
 from autotest_lib.client.common_lib import error
-import kvm_test_utils, kvm_utils
+import kvm_test_utils, kvm_utils, kvm_subprocess
 
 def run_vlan(test, params, env):
     """
@@ -35,24 +35,20 @@
     vm.append(kvm_test_utils.get_living_vm(env, "vm2"))
 
     def add_vlan(session, id, iface="eth0"):
-        if session.get_command_status("vconfig add %s %s" % (iface, id)) != 0:
-            raise error.TestError("Fail to add %s.%s" % (iface, id))
+        session.cmd("vconfig add %s %s" % (iface, id))
 
     def set_ip_vlan(session, id, ip, iface="eth0"):
         iface = "%s.%s" % (iface, id)
-        if session.get_command_status("ifconfig %s %s" % (iface, ip)) != 0:
-            raise error.TestError("Fail to configure ip for %s" % iface)
+        session.cmd("ifconfig %s %s" % (iface, ip))
 
     def set_arp_ignore(session, iface="eth0"):
         ignore_cmd = "echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore"
-        if session.get_command_status(ignore_cmd) != 0:
-            raise error.TestError("Fail to set arp_ignore of %s" % session)
+        session.cmd(ignore_cmd)
 
     def rem_vlan(session, id, iface="eth0"):
         rem_vlan_cmd = "if [[ -e /proc/net/vlan/%s ]];then vconfig rem %s;fi"
         iface = "%s.%s" % (iface, id)
-        s = session.get_command_status(rem_vlan_cmd % (iface, iface))
-        return s
+        return session.cmd_status(rem_vlan_cmd % (iface, iface))
 
     def nc_transfer(src, dst):
         nc_port = kvm_utils.find_free_port(1025, 5334, vm_ip[dst])
@@ -65,15 +61,14 @@
         time.sleep(2)
         #send file from src to dst
         send_cmd = send_cmd % (vlan_ip[dst], str(nc_port), "file")
-        if session[src].get_command_status(send_cmd, timeout = 60) != 0:
-            raise error.TestFail ("Fail to send file"
-                                    " from vm%s to vm%s" % (src+1, dst+1))
-        s, o = session[dst].read_up_to_prompt(timeout=60)
-        if s != True:
+        session[src].cmd(send_cmd, timeout=60)
+        try:
+            session[dst].read_up_to_prompt(timeout=60)
+        except kvm_subprocess.ExpectError:
             raise error.TestFail ("Fail to receive file"
                                     " from vm%s to vm%s" % (src+1, dst+1))
         #check MD5 message digest of receive file in dst
-        output = session[dst].get_command_output("md5sum receive").strip()
+        output = session[dst].cmd_output("md5sum receive").strip()
         digest_receive = re.findall(r'(\w+)', output)[0]
         if digest_receive == digest_origin[src]:
             logging.info("file succeed received in vm %s" % vlan_ip[dst])
@@ -81,7 +76,7 @@
             logging.info("digest_origin is  %s" % digest_origin[src])
             logging.info("digest_receive is %s" % digest_receive)
             raise error.TestFail("File transfered differ from origin")
-        session[dst].get_command_status("rm -f receive")
+        session[dst].cmd_output("rm -f receive")
 
     for i in range(2):
         session.append(kvm_test_utils.wait_for_login(vm[i],
@@ -97,22 +92,16 @@
 
         #produce sized file in vm
         dd_cmd = "dd if=/dev/urandom of=file bs=1024k count=%s"
-        if session[i].get_command_status(dd_cmd % file_size) != 0:
-            raise error.TestFail("File producing failed")
+        session[i].cmd(dd_cmd % file_size)
         #record MD5 message digest of file
-        s, output =session[i].get_command_status_output("md5sum file",
-                                                        timeout=60)
-        if s != 0:
-            raise error.TestFail("File MD5_checking failed" )
+        output = session[i].cmd("md5sum file", timeout=60)
         digest_origin.append(re.findall(r'(\w+)', output)[0])
 
         #stop firewall in vm
-        session[i].get_command_status("/etc/init.d/iptables stop")
+        session[i].cmd_output("/etc/init.d/iptables stop")
 
         #load 8021q module for vconfig
-        load_8021q_cmd = "modprobe 8021q"
-        if session[i].get_command_status(load_8021q_cmd) != 0:
-            raise error.TestError("Fail to load 8021q module on VM%s" % i)
+        session[i].cmd("modprobe 8021q")
 
     try:
         for i in range(2):
diff --git a/client/tests/kvm/tests/vmstop.py b/client/tests/kvm/tests/vmstop.py
new file mode 100644
index 0000000..876c3ef
--- /dev/null
+++ b/client/tests/kvm/tests/vmstop.py
@@ -0,0 +1,82 @@
+import logging, time, os
+from autotest_lib.client.common_lib import error
+from autotest_lib.client.bin import utils
+import kvm_subprocess, kvm_test_utils, kvm_utils
+
+
+def run_vmstop(test, params, env):
+    """
+    KVM guest stop test:
+    1) Log into a guest
+    2) Copy a file into guest
+    3) Stop guest
+    4) Check the status through monitor
+    5) Check the session
+    6) Migrat the vm to a file twice and compare them.
+
+    @param test: kvm test object
+    @param params: Dictionary with the test parameters
+    @param env: Dictionary with test environment.
+    """
+    vm = kvm_test_utils.get_living_vm(env, params.get("main_vm"))
+    timeout = float(params.get("login_timeout", 240))
+    session = kvm_test_utils.wait_for_login(vm, 0, timeout, 0, 2)
+
+    save_path = params.get("save_path", "/tmp")
+    clean_save = params.get("clean_save") == "yes"
+    save1 = os.path.join(save_path, "save1")
+    save2 = os.path.join(save_path, "save2")
+
+    guest_path = params.get("guest_path", "/tmp")
+    file_size = params.get("file_size", "1000")
+
+    try:
+        utils.run("dd if=/dev/zero of=/tmp/file bs=1M count=%s" % file_size)
+        # Transfer file from host to guest, we didn't expect the finish of
+        # transfer, we just let it to be a kind of stress in guest.
+        bg = kvm_utils.Thread(vm.copy_files_to, ("/tmp/file",
+                                                 guest_path, 0, 60))
+        logging.info("Start the background transfer")
+        bg.start()
+
+        try:
+            # wait for the transfer start
+            time.sleep(5)
+            logging.info("Stop the VM")
+            vm.monitor.cmd("stop")
+
+            # check with monitor
+            logging.info("Check the status through monitor")
+            if "paused" not in vm.monitor.info("status"):
+                raise error.TestFail("Guest did not pause after sending stop")
+
+            # check through session
+            logging.info("Check the session")
+            if session.is_responsive():
+                raise error.TestFail("Session still alive after sending stop")
+
+            # Check with the migration file
+            logging.info("Save and check the state files")
+            for p in [save1, save2]:
+                vm.save_to_file(p)
+                time.sleep(1)
+                if not os.path.isfile(p):
+                    raise error.TestFail("VM failed to save state file %s" % p)
+
+            # Fail if we see deltas
+            md5_save1 = utils.hash_file(save1)
+            md5_save2 = utils.hash_file(save2)
+            if md5_save1 != md5_save2:
+                raise error.TestFail("The produced state files differ")
+        finally:
+            bg.join()
+
+    finally:
+        session.close()
+        if clean_save:
+            logging.debug("Clean the state files")
+            if os.path.isfile(save1):
+                os.remove(save1)
+            if os.path.isfile(save2):
+                os.remove(save2)
+        vm.monitor.cmd("cont")
diff --git a/client/tests/kvm/tests/whql_client_install.py b/client/tests/kvm/tests/whql_client_install.py
index 84b91bc..c2616c6 100644
--- a/client/tests/kvm/tests/whql_client_install.py
+++ b/client/tests/kvm/tests/whql_client_install.py
@@ -13,6 +13,9 @@
     5) Move the client machine into the server's workgroup
     6) Reboot the client machine
     7) Install the DTM client software
+    8) Setup auto logon for the user created by the installation
+       (normally DTMLLUAdminUser)
+    9) Reboot again
 
     @param test: kvm test object
     @param params: Dictionary with the test parameters
@@ -29,6 +32,8 @@
                                     "Microsoft Driver Test Manager\\Studio")
     server_username = params.get("server_username")
     server_password = params.get("server_password")
+    client_username = params.get("client_username")
+    client_password = params.get("client_password")
     dsso_delete_machine_binary = params.get("dsso_delete_machine_binary",
                                             "deps/whql_delete_machine_15.exe")
     dsso_delete_machine_binary = kvm_utils.get_path(test.bindir,
@@ -50,27 +55,28 @@
     server_session = kvm_utils.remote_login("nc", server_address,
                                             server_shell_port, "", "",
                                             session.prompt, session.linesep)
+    server_session.set_status_test_command(session.status_test_command)
 
     # Get server and client information
     cmd = "echo %computername%"
-    server_name = server_session.get_command_output(cmd).strip()
-    client_name = session.get_command_output(cmd).strip()
+    server_name = server_session.cmd_output(cmd).strip()
+    client_name = session.cmd_output(cmd).strip()
     cmd = "wmic computersystem get domain"
-    server_workgroup = server_session.get_command_output(cmd).strip()
+    server_workgroup = server_session.cmd_output(cmd).strip()
     server_workgroup = server_workgroup.splitlines()[-1]
     regkey = r"HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters"
     cmd = "reg query %s /v Domain" % regkey
-    o = server_session.get_command_output(cmd).strip().splitlines()[-1]
+    o = server_session.cmd_output(cmd).strip().splitlines()[-1]
     try:
         server_dns_suffix = o.split(None, 2)[2]
     except IndexError:
         server_dns_suffix = ""
 
     # Delete the client machine from the server's data store (if it's there)
-    server_session.get_command_output("cd %s" % server_studio_path)
+    server_session.cmd("cd %s" % server_studio_path)
     cmd = "%s %s %s" % (os.path.basename(dsso_delete_machine_binary),
                         server_name, client_name)
-    server_session.get_command_output(cmd, print_func=logging.info)
+    server_session.cmd(cmd, print_func=logging.info)
     server_session.close()
 
     # Rename the client machine
@@ -78,21 +84,18 @@
     logging.info("Renaming client machine to '%s'" % client_name)
     cmd = ('wmic computersystem where name="%%computername%%" rename name="%s"'
            % client_name)
-    if session.get_command_status(cmd, timeout=600) != 0:
-        raise error.TestError("Could not rename the client machine")
+    session.cmd(cmd, timeout=600)
 
     # Join the server's workgroup
     logging.info("Joining workgroup '%s'" % server_workgroup)
     cmd = ('wmic computersystem where name="%%computername%%" call '
            'joindomainorworkgroup name="%s"' % server_workgroup)
-    if session.get_command_status(cmd, timeout=600) != 0:
-        raise error.TestError("Could not change the client's workgroup")
+    session.cmd(cmd, timeout=600)
 
     # Set the client machine's DNS suffix
     logging.info("Setting DNS suffix to '%s'" % server_dns_suffix)
     cmd = 'reg add %s /v Domain /d "%s" /f' % (regkey, server_dns_suffix)
-    if session.get_command_status(cmd, timeout=300) != 0:
-        raise error.TestError("Could not set the client's DNS suffix")
+    session.cmd(cmd, timeout=300)
 
     # Reboot
     session = kvm_test_utils.reboot(vm, session)
@@ -103,9 +106,11 @@
                                          server_password)
     end_time = time.time() + 120
     while time.time() < end_time:
-        s = session.get_command_status(cmd)
-        if s == 0:
+        try:
+            session.cmd(cmd)
             break
+        except:
+            pass
         time.sleep(5)
     else:
         raise error.TestError("Could not access server share from client "
@@ -114,7 +119,17 @@
     # Install
     logging.info("Installing DTM client (timeout=%ds)", install_timeout)
     install_cmd = r"cmd /c \\%s\%s" % (server_name, install_cmd.lstrip("\\"))
-    if session.get_command_status(install_cmd, timeout=install_timeout) != 0:
-        raise error.TestError("Client installation failed")
+    session.cmd(install_cmd, timeout=install_timeout)
 
+    # Setup auto logon
+    logging.info("Setting up auto logon for user '%s'", client_username)
+    cmd = ('reg add '
+           '"HKLM\\Software\\Microsoft\\Windows NT\\CurrentVersion\\winlogon" '
+           '/v "%s" /d "%s" /t REG_SZ /f')
+    session.cmd(cmd % ("AutoAdminLogon", "1"))
+    session.cmd(cmd % ("DefaultUserName", client_username))
+    session.cmd(cmd % ("DefaultPassword", client_password))
+
+    # Reboot one more time
+    session = kvm_test_utils.reboot(vm, session)
     session.close()
diff --git a/client/tests/kvm/tests/whql_submission.py b/client/tests/kvm/tests/whql_submission.py
index 1fe27c9..6fb5d83 100644
--- a/client/tests/kvm/tests/whql_submission.py
+++ b/client/tests/kvm/tests/whql_submission.py
@@ -6,20 +6,31 @@
 def run_whql_submission(test, params, env):
     """
     WHQL submission test:
-    1) Log into the guest (the client machine) and into a DTM server machine
+    1) Log into the client machines and into a DTM server machine
     2) Copy the automation program binary (dsso_test_binary) to the server machine
     3) Run the automation program
     4) Pass the program all relevant parameters (e.g. device_data)
     5) Wait for the program to terminate
     6) Parse and report job results
-    (logs and HTML reports are placed in test.bindir)
+    (logs and HTML reports are placed in test.debugdir)
 
     @param test: kvm test object
     @param params: Dictionary with the test parameters
     @param env: Dictionary with test environment.
     """
-    vm = kvm_test_utils.get_living_vm(env, params.get("main_vm"))
-    session = kvm_test_utils.wait_for_login(vm, 0, 240)
+    # Log into all client VMs
+    vms = []
+    sessions = []
+    for vm_name in params.objects("vms"):
+        vms.append(kvm_test_utils.get_living_vm(env, vm_name))
+        sessions.append(kvm_test_utils.wait_for_login(vms[-1], 0, 240))
+
+    # Make sure all NICs of all client VMs are up
+    for vm in vms:
+        nics = kvm_utils.get_sub_dict_names(vm.params, "nics")
+        for nic_index in range(len(nics)):
+            s = kvm_test_utils.wait_for_login(vm, nic_index, 600)
+            s.close()
 
     # Collect parameters
     server_address = params.get("server_address")
@@ -30,41 +41,61 @@
     dsso_test_binary = params.get("dsso_test_binary",
                                   "deps/whql_submission_15.exe")
     dsso_test_binary = kvm_utils.get_path(test.bindir, dsso_test_binary)
-    test_device = params.get("test_device")
-    job_filter = params.get("job_filter", ".*")
+    dsso_delete_machine_binary = params.get("dsso_delete_machine_binary",
+                                            "deps/whql_delete_machine_15.exe")
+    dsso_delete_machine_binary = kvm_utils.get_path(test.bindir,
+                                                    dsso_delete_machine_binary)
     test_timeout = float(params.get("test_timeout", 600))
-    wtt_services = params.get("wtt_services")
 
-    # Restart WTT service(s) on the client
-    logging.info("Restarting WTT services on client")
-    for svc in wtt_services.split():
-        kvm_test_utils.stop_windows_service(session, svc)
-    for svc in wtt_services.split():
-        kvm_test_utils.start_windows_service(session, svc)
-
-    # Copy dsso_test_binary to the server
-    rss_file_transfer.upload(server_address, server_file_transfer_port,
-                             dsso_test_binary, server_studio_path, timeout=60)
+    # Copy dsso binaries to the server
+    for filename in dsso_test_binary, dsso_delete_machine_binary:
+        rss_file_transfer.upload(server_address, server_file_transfer_port,
+                                 filename, server_studio_path, timeout=60)
 
     # Open a shell session with the server
     server_session = kvm_utils.remote_login("nc", server_address,
                                             server_shell_port, "", "",
-                                            session.prompt, session.linesep)
+                                            sessions[0].prompt,
+                                            sessions[0].linesep)
+    server_session.set_status_test_command(sessions[0].status_test_command)
 
-    # Get the computer names of the server and client
+    # Get the computer names of the server and clients
     cmd = "echo %computername%"
-    server_name = server_session.get_command_output(cmd).strip()
-    client_name = session.get_command_output(cmd).strip()
-    session.close()
+    server_name = server_session.cmd_output(cmd).strip()
+    client_names = [session.cmd_output(cmd).strip() for session in sessions]
+
+    # Delete all client machines from the server's data store
+    server_session.cmd("cd %s" % server_studio_path)
+    for client_name in client_names:
+        cmd = "%s %s %s" % (os.path.basename(dsso_delete_machine_binary),
+                            server_name, client_name)
+        server_session.cmd(cmd, print_func=logging.debug)
+
+    # Reboot the client machines
+    sessions = kvm_utils.parallel((kvm_test_utils.reboot, (vm, session))
+                                  for vm, session in zip(vms, sessions))
+
+    # Check the NICs again
+    for vm in vms:
+        nics = kvm_utils.get_sub_dict_names(vm.params, "nics")
+        for nic_index in range(len(nics)):
+            s = kvm_test_utils.wait_for_login(vm, nic_index, 600)
+            s.close()
+
+    # Run whql_pre_command and close the sessions
+    if params.get("whql_pre_command"):
+        for session in sessions:
+            session.cmd(params.get("whql_pre_command"),
+                        int(params.get("whql_pre_command_timeout", 600)))
+            session.close()
 
     # Run the automation program on the server
-    server_session.get_command_output("cd %s" % server_studio_path)
+    pool_name = "%s_pool" % client_names[0]
+    submission_name = "%s_%s" % (client_names[0],
+                                 params.get("submission_name"))
     cmd = "%s %s %s %s %s %s" % (os.path.basename(dsso_test_binary),
-                                 server_name,
-                                 client_name,
-                                 "%s_pool" % client_name,
-                                 "%s_submission" % client_name,
-                                 test_timeout)
+                                 server_name, pool_name, submission_name,
+                                 test_timeout, " ".join(client_names))
     server_session.sendline(cmd)
 
     # Helper function: wait for a given prompt and raise an exception if an
@@ -78,40 +109,69 @@
             if errors:
                 raise error.TestError(errors[0])
             else:
-                raise error.TestError("Error running automation program: could "
-                                      "not find '%s' prompt" % prompt)
+                raise error.TestError("Error running automation program: "
+                                      "could not find '%s' prompt" % prompt)
 
     # Tell the automation program which device to test
     find_prompt("Device to test:")
-    server_session.sendline(test_device)
+    server_session.sendline(params.get("test_device"))
 
     # Tell the automation program which jobs to run
     find_prompt("Jobs to run:")
-    server_session.sendline(job_filter)
+    server_session.sendline(params.get("job_filter", ".*"))
 
-    # Give the automation program all the device data supplied by the user
+    # Set submission DeviceData
     find_prompt("DeviceData name:")
-    for dd in kvm_utils.get_sub_dict_names(params, "device_data"):
-        dd_params = kvm_utils.get_sub_dict(params, dd)
+    for dd in params.objects("device_data"):
+        dd_params = params.object_params(dd)
         if dd_params.get("dd_name") and dd_params.get("dd_data"):
             server_session.sendline(dd_params.get("dd_name"))
             server_session.sendline(dd_params.get("dd_data"))
     server_session.sendline()
 
-    # Give the automation program all the descriptor information supplied by
-    # the user
+    # Set submission descriptors
     find_prompt("Descriptor path:")
-    for desc in kvm_utils.get_sub_dict_names(params, "descriptors"):
-        desc_params = kvm_utils.get_sub_dict(params, desc)
+    for desc in params.objects("descriptors"):
+        desc_params = params.object_params(desc)
         if desc_params.get("desc_path"):
             server_session.sendline(desc_params.get("desc_path"))
     server_session.sendline()
 
+    # Set machine dimensions for each client machine
+    for vm_name in params.objects("vms"):
+        vm_params = params.object_params(vm_name)
+        find_prompt(r"Dimension name\b.*:")
+        for dp in vm_params.objects("dimensions"):
+            dp_params = vm_params.object_params(dp)
+            if dp_params.get("dim_name") and dp_params.get("dim_value"):
+                server_session.sendline(dp_params.get("dim_name"))
+                server_session.sendline(dp_params.get("dim_value"))
+        server_session.sendline()
+
+    # Set extra parameters for tests that require them (e.g. NDISTest)
+    for vm_name in params.objects("vms"):
+        vm_params = params.object_params(vm_name)
+        find_prompt(r"Parameter name\b.*:")
+        for dp in vm_params.objects("device_params"):
+            dp_params = vm_params.object_params(dp)
+            if dp_params.get("dp_name") and dp_params.get("dp_regex"):
+                server_session.sendline(dp_params.get("dp_name"))
+                server_session.sendline(dp_params.get("dp_regex"))
+                # Make sure the prompt appears again (if the device isn't found
+                # the automation program will terminate)
+                find_prompt(r"Parameter name\b.*:")
+        server_session.sendline()
+
     # Wait for the automation program to terminate
-    m, o = server_session.read_up_to_prompt(print_func=logging.info,
-                                            timeout=test_timeout + 300)
-    # (test_timeout + 300 is used here because the automation program is
-    # supposed to terminate cleanly on its own when test_timeout expires)
+    try:
+        o = server_session.read_up_to_prompt(print_func=logging.info,
+                                             timeout=test_timeout + 300)
+        # (test_timeout + 300 is used here because the automation program is
+        # supposed to terminate cleanly on its own when test_timeout expires)
+        done = True
+    except kvm_subprocess.ExpectError, e:
+        o = e.output
+        done = False
     server_session.close()
 
     # Look for test results in the automation program's output
@@ -151,38 +211,63 @@
                 except (KeyError, OSError):
                     pass
 
-    # Print result summary
-    logging.info("")
-    logging.info("Result summary:")
-    name_length = max(len(r.get("job", "")) for r in results)
-    fmt = "%%-6s %%-%ds %%-15s %%-8s %%-8s %%-8s %%-15s" % name_length
-    logging.info(fmt % ("ID", "Job", "Status", "Pass", "Fail", "NotRun",
-                        "NotApplicable"))
-    logging.info(fmt % ("--", "---", "------", "----", "----", "------",
-                        "-------------"))
-    for r in results:
-        logging.info(fmt % (r.get("id"), r.get("job"), r.get("status"),
-                            r.get("pass"), r.get("fail"), r.get("notrun"),
-                            r.get("notapplicable")))
-    logging.info("(see logs and HTML reports in %s)" % test.debugdir)
+    # Print result summary (both to the regular logs and to a file named
+    # 'summary' in test.debugdir)
+    def print_summary_line(f, line):
+        logging.info(line)
+        f.write(line + "\n")
+    if results:
+        # Make sure all results have the required keys
+        for r in results:
+            r["id"] = str(r.get("id"))
+            r["job"] = str(r.get("job"))
+            r["status"] = str(r.get("status"))
+            r["pass"] = int(r.get("pass", 0))
+            r["fail"] = int(r.get("fail", 0))
+            r["notrun"] = int(r.get("notrun", 0))
+            r["notapplicable"] = int(r.get("notapplicable", 0))
+        # Sort the results by failures and total test count in descending order
+        results = [(r["fail"],
+                    r["pass"] + r["fail"] + r["notrun"] + r["notapplicable"],
+                    r) for r in results]
+        results.sort(reverse=True)
+        results = [r[-1] for r in results]
+        # Print results
+        logging.info("")
+        logging.info("Result summary:")
+        name_length = max(len(r["job"]) for r in results)
+        fmt = "%%-6s %%-%ds %%-15s %%-8s %%-8s %%-8s %%-15s" % name_length
+        f = open(os.path.join(test.debugdir, "summary"), "w")
+        print_summary_line(f, fmt % ("ID", "Job", "Status", "Pass", "Fail",
+                                     "NotRun", "NotApplicable"))
+        print_summary_line(f, fmt % ("--", "---", "------", "----", "----",
+                                     "------", "-------------"))
+        for r in results:
+            print_summary_line(f, fmt % (r["id"], r["job"], r["status"],
+                                         r["pass"], r["fail"], r["notrun"],
+                                         r["notapplicable"]))
+        f.close()
+        logging.info("(see logs and HTML reports in %s)" % test.debugdir)
 
-    # Kill the VM and fail if the automation program did not terminate on time
-    if not m:
-        vm.destroy()
+    # Kill the client VMs and fail if the automation program did not terminate
+    # on time
+    if not done:
+        kvm_utils.parallel(vm.destroy for vm in vms)
         raise error.TestFail("The automation program did not terminate "
                              "on time")
 
-    # Fail if there are failed or incomplete jobs (kill the VM if there are
-    # incomplete jobs)
-    failed_jobs = [r.get("job") for r in results
-                   if r.get("status", "").lower() == "investigate"]
-    running_jobs = [r.get("job") for r in results
-                    if r.get("status", "").lower() == "inprogress"]
+    # Fail if there are failed or incomplete jobs (kill the client VMs if there
+    # are incomplete jobs)
+    failed_jobs = [r["job"] for r in results
+                   if r["status"].lower() == "investigate"]
+    running_jobs = [r["job"] for r in results
+                    if r["status"].lower() == "inprogress"]
     errors = []
     if failed_jobs:
         errors += ["Jobs failed: %s." % failed_jobs]
     if running_jobs:
-        vm.destroy()
+        for vm in vms:
+            vm.destroy()
         errors += ["Jobs did not complete on time: %s." % running_jobs]
     if errors:
         raise error.TestFail(" ".join(errors))
diff --git a/client/tests/kvm/tests_base.cfg.sample b/client/tests/kvm/tests_base.cfg.sample
index eddc02b..6374923 100644
--- a/client/tests/kvm/tests_base.cfg.sample
+++ b/client/tests/kvm/tests_base.cfg.sample
@@ -61,7 +61,10 @@
 # Misc
 profilers = kvm_stat
 login_timeout = 360
+image_raw_device = no
 
+# NFS directory of guests' images
+images_good = 0.0.0.0:/autotest/images_good
 
 # Tests
 variants:
@@ -75,6 +78,12 @@
         kill_vm_timeout = 60
         kill_vm_timeout_on_error = 0
 
+    - image_copy:
+        type = image_copy
+        vms = ''
+        parallel = no
+        profilers =
+
     - setup:        install
         type = steps
         fail_if_stuck_for = 300
@@ -100,23 +109,19 @@
                 medium = cdrom
                 nic_mode = user
                 redirs += " unattended_install"
-                kernel =
-                initrd = 
             # Install guest from http/ftp url
             - url:
                 medium = url
-                extra_params += " --append ks=floppy"
                 url = REPLACE_THIS_WITH_TREE_URL
             # 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
             # Install guest with a remote kickstart
             - remote_ks:
                 medium = url
-                extra_params += " --append ks=REPLACE_THIS_WITH_URL_OF_KS"
+                extra_params = " --append ks=REPLACE_THIS_WITH_URL_OF_KS"
                 url = REPLACE_THIS_WITH_TREE_URL
 
     - boot:         install setup unattended_install.cdrom
@@ -141,6 +146,9 @@
         iterations = 2
         used_mem = 1024
         mig_timeout = 3600
+        # you can uncomment the following line to enable the state
+        # check
+        # vmstate_check = yes
         variants:
             - tcp:
                 migration_protocol = "tcp"
@@ -151,6 +159,27 @@
             - mig_cancel:
                 migration_protocol = "tcp"
                 mig_cancel = True
+        variants:
+            - @default:
+            - with_reboot:
+                iterations = 1
+                type = migration_with_reboot
+            - with_file_transfer:
+                iterations = 1
+                type = migration_with_file_transfer
+
+    - migrate_multi_host:      install setup unattended_install.cdrom
+        type = migration_multi_host
+        migration_test_command = help
+        migration_bg_command = "cd /tmp; nohup tcpdump -q -t ip host localhost"
+        migration_bg_check_command = pgrep tcpdump
+        migration_bg_kill_command = pkill tcpdump
+        kill_vm_on_error = yes
+        iterations = 2
+        used_mem = 1024
+        mig_timeout = 3600
+        comm_port = 13234
+        regain_ip_cmd = dhclient
 
     - boot_savevm: install setup unattended_install.cdrom
         type = boot_savevm
@@ -291,7 +320,7 @@
         reboot = yes
         variants:
             - autoit:
-                interpreter = D:\AutoIt3.exe
+                interpreter = "cmd /c D:\AutoIt3.exe"
                 variants:
                     - notepad:
                         guest_script = autoit/notepad1.au3
@@ -303,7 +332,7 @@
                         dst_rsc_dir = "C:\"
                         dst_rsc_path = "C:\autoit\stub\stub.au3"
             - powershell:
-                interpreter = "powershell.exe -File"
+                interpreter = "cmd /c powershell.exe -File"
                 variants:
                     - stub:
                         download = yes
@@ -317,7 +346,7 @@
         iozone_cmd = "D:\IOzone\iozone.exe -a"
         iozone_timeout = 3600
 
-    - @whql:         install setup unattended_install.cdrom
+    - whql:         install setup unattended_install.cdrom
         nic_mode = tap
         # Replace this with the address of an installed DTM server
         server_address = 10.20.30.40
@@ -327,11 +356,26 @@
         server_shell_port = 10022
         server_file_transfer_port = 10023
         server_studio_path = %programfiles%\Microsoft Driver Test Manager\Studio
+        dsso_test_binary = deps/whql_submission_15.exe
+        dsso_delete_machine_binary = deps/whql_delete_machine_15.exe
         wtt_services = wttsvc
         variants:
-            - whql_client_install:
+            - support_vm_install:
+                # The support VM is identical to the tested VM in every way
+                # except for the image name which ends with '-supportvm'.
+                type = unattended_install
+                pre_command += " scripts/unattended.py;"
+                extra_params += " -boot d"
+                force_create_image = yes
+                kill_vm = yes
+                nic_mode = user
+                redirs += " unattended_install"
+                guest_port_unattended_install = 12323
+                medium = cdrom
+                kernel =
+                initrd = 
+            - client_install:    support_vm_install
                 type = whql_client_install
-                dsso_delete_machine_binary = deps/whql_delete_machine_15.exe
                 # The username and password are required for accessing the DTM client
                 # installer binary shared by the server
                 server_username = administrator
@@ -340,12 +384,22 @@
                 # (the final cmd will be something like \\servername\DTMInstall\...)
                 install_cmd = \DTMInstall\Client\Setup.exe /passive
                 install_timeout = 3600
-            - whql_submission:    whql_client_install
+                # The test will setup auto logon on the client machine using the
+                # following username and password:
+                client_username = DTMLLUAdminUser
+                client_password = Testpassword,1
+                # (These are created by the DTM client installer and should probably not
+                # be changed.)
+                variants:
+                    - @original:
+                    - support_vm:
+            - submission:    client_install support_vm_install
                 type = whql_submission
                 extra_params += " -snapshot"
-                dsso_test_binary = deps/whql_submission_15.exe
+                restart_vm = yes
+                cdroms =
                 test_timeout = 3600
-                device_data = cat0 cat1 cat2 cat3 logoarch logoos whqlos whqlqual prog desc filter virt
+                device_data = cat0 cat1 cat2 cat3 prog desc virt filter logoarch logoos whqlos whqlqual
                 descriptors = desc1 desc2 desc3
                 # DeviceData names
                 dd_name_cat0     = Category
@@ -363,43 +417,105 @@
                 # Common DeviceData data
                 dd_data_filter   = FilterIfNoInf
                 dd_data_virt     = True
+                # Exclude jobs that have '(Manual)' in their names
+                job_filter = ^((?!\(Manual\)).)*$
                 variants:
-                    - keyboard:
-                        # test_device is a regular expression that should match a device's
-                        # name as it appears in device manager.  The first device that matches
-                        # is used.
-                        test_device = keyboard
-                        # Set timeout to 10 hours
-                        test_timeout = 36000
-                        dd_data_cat0 = Input\Keyboard
-                        dd_data_cat1 = Device Fundamentals
-                        dd_data_cat2 = System Fundamentals\Dynamic Partitioning
-                        dd_data_prog = InputKbd
-                        dd_data_desc = Input > Keyboard
-                    - hdd:
-                        test_device = qemu harddisk
-                        device_data += " ex0 ex1 ex2 ex3"
-                        dd_data_cat0 = Storage\Device Class\Disk\Disk
-                        dd_data_cat1 = Storage\Device Class\Disk\Fixed
-                        dd_data_cat2 = Storage\Device Class\Disk\Bus\ATA
-                        dd_data_cat3 = Device Fundamentals
-                        dd_data_prog = StorHDD
-                        dd_data_desc = Storage > Hard Disk Drive (HDD)
-                        dd_name_ex0 = Storage_bus_type
-                        dd_data_ex0 = ATA/ATAPI
-                        dd_name_ex1 = Hybrid_HDD_Support
-                        dd_data_ex1 = 0
-                        dd_name_ex2 = Non_Rotating_Media
-                        dd_data_ex2 = 0
-                        dd_name_ex3 = Secure_Storage
-                        dd_data_ex3 = 0
+                    - unclassified:
+                        dd_data_cat0 = Device Fundamentals
+                        dd_data_cat1 = System Fundamentals\Dynamic Partitioning
+                        dd_data_prog = Unclassified
+                        dd_data_desc = Unclassified
+                        dd_data_whqlqual = Unclassified Signature
                         variants:
-                            - full:
-                                # Yes, 100 hours, this is not a mistake
-                                test_timeout = 360000
-                            - syscache_test:
-                                job_filter = syscache test
-                                test_timeout = 7200
+                            - tablet:
+                                submission_name = tablet
+                                extra_params += " -usbdevice tablet"
+                                test_device = HID-compliant mouse
+                                test_timeout = 36000
+                    - device:
+                        variants:
+                            - keyboard:
+                                submission_name = keyboard
+                                # test_device is a regular expression that should match a device's
+                                # name as it appears in device manager.  The first device that matches
+                                # is used.
+                                test_device = keyboard
+                                # Set timeout to 10 hours
+                                test_timeout = 36000
+                                dd_data_cat0 = Input\Keyboard
+                                dd_data_cat1 = Device Fundamentals
+                                dd_data_cat2 = System Fundamentals\Dynamic Partitioning
+                                dd_data_prog = InputKbd
+                                dd_data_desc = Input > Keyboard
+                            - net:
+                                submission_name = net
+                                # Add a support machine and extra NICs
+                                vms += " supportvm"
+                                nics += " nic2 nic3"
+                                test_device = RTL8139.*NIC$
+                                test_timeout = 86400
+                                dd_data_cat0 = Network\LAN (Ethernet)
+                                dd_data_cat1 = Device Fundamentals
+                                dd_data_cat2 = System Fundamentals\Dynamic Partitioning
+                                dd_data_prog = NetLan
+                                dd_data_desc = Network > LAN (Ethernet)
+                                # Machine dimensions
+                                dimensions = testrole
+                                dim_name_testrole = NetDevice\TestRole
+                                dim_value_testrole_vm1 = NdistestLanClient
+                                dim_value_testrole_supportvm = NdistestLanServer
+                                # Device selection for the NDISTest client machine
+                                device_params_vm1 = testdev clientmsgdev clientsupportdev
+                                dp_name_testdev = NdistestLanClientTestDevice
+                                dp_regex_testdev = RTL8139.*NIC$
+                                dp_name_clientmsgdev = NdistestLanClientMessageDevice
+                                dp_regex_clientmsgdev = RTL8139.*NIC #2$
+                                dp_name_clientsupportdev = NdistestLanClientSupportDevice0
+                                dp_regex_clientsupportdev = RTL8139.*NIC #3$
+                                # Device selection for the NDISTest server machine
+                                device_params_supportvm = servermsgdev serversupportdev
+                                dp_name_servermsgdev = NdistestLanServerMessageDevice
+                                dp_regex_servermsgdev = RTL8139.*NIC$
+                                dp_name_serversupportdev = NdistestLanServerSupportDevice0
+                                dp_regex_serversupportdev = RTL8139.*NIC #2$
+                            - hdd:
+                                submission_name = hdd
+                                # Run the tests on a non-system drive
+                                # (match device names that contain 'QEMU HARDDISK' and do not contain '[C]')
+                                test_device = ^(?=.*?\bQEMU HARDDISK\b)((?!\[C\]).)*$
+                                device_data += " ex0 ex1 ex2 ex3"
+                                dd_data_cat0 = Storage\Device Class\Disk\Disk
+                                dd_data_cat1 = Storage\Device Class\Disk\Fixed
+                                dd_data_cat2 = Storage\Device Class\Disk\Bus\ATA
+                                dd_data_cat3 = Device Fundamentals
+                                dd_data_prog = StorHDD
+                                dd_data_desc = Storage > Hard Disk Drive (HDD)
+                                dd_name_ex0 = Storage_bus_type
+                                dd_data_ex0 = ATA/ATAPI
+                                dd_name_ex1 = Hybrid_HDD_Support
+                                dd_data_ex1 = 0
+                                dd_name_ex2 = Non_Rotating_Media
+                                dd_data_ex2 = 0
+                                dd_name_ex3 = Secure_Storage
+                                dd_data_ex3 = 0
+                                # Add a 2nd disk which will become D:
+                                images += " tmp"
+                                image_name_tmp = tmp
+                                image_size_tmp = 4G
+                                force_create_image_tmp = yes
+                                # Run diskpart to partition the 2nd disk
+                                whql_pre_command = "echo select disk=1 > dp.txt && "
+                                whql_pre_command += "echo create partition primary >> dp.txt && "
+                                whql_pre_command += "echo assign letter=d >> dp.txt && "
+                                whql_pre_command += "diskpart /s dp.txt & "
+                                whql_pre_command += "format d: /fs:ntfs /q /y"
+                                variants:
+                                    - full:
+                                        # Yes, 100 hours, this is not a mistake
+                                        test_timeout = 360000
+                                    - syscache_test:
+                                        job_filter = syscache test
+                                        test_timeout = 7200
 
     - guest_s4:     install setup unattended_install.cdrom
         type = guest_s4
@@ -412,7 +528,6 @@
         relogin_timeout = 240
 
     - nic_hotplug:  install setup unattended_install.cdrom
-        type = pci_hotplug
         pci_type = nic
         reference_cmd = lspci
         find_pci_cmd = 'lspci | tail -n1'
@@ -428,6 +543,12 @@
             - nic_e1000:
                 pci_model = e1000
                 match_string = "Gigabit Ethernet Controller"
+        variants:
+            - default:
+                type = pci_hotplug
+            - additional:
+                type = nic_hotplug
+
 
     - block_hotplug: install setup unattended_install.cdrom
         type = pci_hotplug
@@ -449,7 +570,7 @@
                 match_string = "Virtio block device"
             - block_scsi:
                 pci_model = scsi
-                match_string = "SCSI"
+                match_string = "LSI Logic"
         variants:
             - fmt_qcow2:
                 image_format_stg = qcow2
@@ -530,17 +651,41 @@
         type = netperf
         nic_mode = tap
         netperf_files = netperf-2.4.5.tar.bz2 wait_before_data.patch
+        packet_size = 1500
         setup_cmd = "cd %s && tar xvfj netperf-2.4.5.tar.bz2 && cd netperf-2.4.5 && patch -p0 < ../wait_before_data.patch && ./configure && make"
         netserver_cmd =  %s/netperf-2.4.5/src/netserver
-        # test time is 60 seconds, set the buffer size to 1 for more hardware interrupt
-        netperf_cmd = %s/netperf-2.4.5/src/netperf -t %s -H %s -l 60 -- -m 1
-        protocols = "TCP_STREAM TCP_MAERTS TCP_RR TCP_CRR UDP_RR TCP_SENDFILE UDP_STREAM"
+	variants:
+	    - stream:
+	            netperf_cmd = %s/netperf-2.4.5/src/netperf -t %s -H %s -l 60 -- -m %s
+		    protocols = "TCP_STREAM TCP_MAERTS TCP_SENDFILE UDP_STREAM"
+ 	    - rr:
+	            netperf_cmd = %s/netperf-2.4.5/src/netperf -t %s -H %s -l 60 -- -r %s
+		    protocols = "TCP_RR TCP_CRR UDP_RR"
 
     - ethtool: install setup unattended_install.cdrom
         type = ethtool
         filesize = 512
         nic_mode = tap
 
+    - nic_bonding:
+        type = nic_bonding
+        nics += ' nic2 nic3 nic4'
+        image_snapshot = yes
+        serial_login = yes
+        test_timeout = 1000
+        filesize = 4000
+        transfer_timeout = 1000
+        transfer_type = remote
+        kill_vm = yes
+
+    - set_link:
+        type = set_link
+        test_timeout = 1000
+        filesize = 4000
+        transfer_timeout = 1000
+        transfer_type = remote
+        kill_vm =yes
+
     - physical_resources_check: install setup unattended_install.cdrom
         type = physical_resources_check
         catch_uuid_cmd = dmidecode | awk -F: '/UUID/ {print $2}'
@@ -616,6 +761,15 @@
             - vmexit:
                 case = vmexit
 
+    - module_probe:
+        type = module_probe
+        # You can specify your own module list, though it is not needed usually.
+        # mod_list = kvm
+        load_count = 100
+        vms = ''
+        profilers = ''
+        take_regular_screendumps = no
+
     - ioquit:
         type = ioquit
         background_cmd = "for i in 1 2 3 4; do (nohup dd if=/dev/urandom of=/tmp/file bs=102400 count=10000000 &) done"
@@ -664,6 +818,27 @@
                 image_name_snapshot1 = sn1
                 image_name_snapshot2 = sn2
 
+    - clock_getres: install setup unattended_install.cdrom
+        type = clock_getres
+
+    - kdump: unattended_install.cdrom
+        type = kdump
+        # time waited for the completion of crash dump
+        # crash_timeout = 360
+        # command to add the crashkernel=X@Y to kernel cmd line
+        # kernel_param_cmd = "grubby --update-kernel=`grubby --default-kernel` --args=crashkernel=128M@64M"
+        # command to enable kdump service
+        # kdump_enable_cmd = chkconfig kdump on && service kdump start
+        # command to probe the crash kernel
+        # crash_kernel_prob_cmd = "grep -q 1 /sys/kernel/kexec_crash_loaded"
+
+    - vmstop:
+        type = vmstop
+        # the path used to store the saved vm state
+        # save_path = /tmp
+        # clean the state file?
+        clean_save = yes
+
     # system_powerdown, system_reset and shutdown *must* be the last ones
     # defined (in this order), since the effect of such tests can leave
     # the VM on a bad state.
@@ -704,13 +879,24 @@
         nic_model = virtio
         # You can add advanced attributes on nic_extra_params such as mrg_rxbuf
         #nic_extra_params =
-        # You can set vhost = yes to enable the vhost kernel backend
-        # (This only works if nic_mode=tap)
-        vhost = no
+        # You can add advanced attributes through netdev_extra_params
+        # such as sndbuf, as an example, you can uncomment the
+        # following lines to enable the vhost support ( only available
+        # for tap )
+        #netdev_extra_params = "vhost=on"
         jumbo:
             mtu = 65520
         ethtool:
             supported_features = "tx sg tso gso"
+        whql.submission.device.net:
+            test_device = VirtIO Ethernet Adapter$
+            # Device selection for the NDISTest client machine
+            dp_regex_testdev = VirtIO Ethernet Adapter$
+            dp_regex_clientmsgdev = VirtIO Ethernet Adapter #2$
+            dp_regex_clientsupportdev = VirtIO Ethernet Adapter #3$
+            # Device selection for the NDISTest server machine
+            dp_regex_servermsgdev = VirtIO Ethernet Adapter$
+            dp_regex_serversupportdev = VirtIO Ethernet Adapter #2$
 
 # Guests
 variants:
@@ -729,7 +915,7 @@
         mem_chk_cmd = dmidecode -t 17 | awk -F: '/Size/ {print $2}'
         mem_chk_cur_cmd = grep MemTotal /proc/meminfo
         cpu_chk_cmd = grep -c processor /proc/cpuinfo
-        unattended_install.cdrom:
+        unattended_install:
             # If you want to use floppy to hold kickstarts,
             # comment the 3 lines below
             cdroms += " unattended"
@@ -761,162 +947,222 @@
             - Fedora:
                 no setup
                 shell_prompt = "^\[.*\][\#\$]\s*$"
-                unattended_install.cdrom:
-                    pxe_dir = "images/pxeboot"
-                    pxe_image = "vmlinuz"
-                    pxe_initrd = "initrd.img"
-                    tftp = "images/tftpboot"
-                    bootp = "/pxelinux.0"
-                    extra_params += " -boot cn"
+                unattended_install:
+                    boot_path = "images/pxeboot"
                     # You have to use ks=floppy if you want to use floppies to
                     # hold your kickstart file
-                    #kernel_args = "ks=floppy nicdelay=60 console=ttyS0,115200 console=tty0"
-                    kernel_args = "ks=cdrom nicdelay=60 console=ttyS0,115200 console=tty0"
+                    #extra_params += " --append 'ks=floppy nicdelay=60 console=ttyS0,115200 console=tty0'"
+                    extra_params += " --append 'ks=cdrom nicdelay=60 console=ttyS0,115200 console=tty0'"
 
                 variants:
                     - 8.32:
                         no setup
                         image_name = fc8-32
-                        cdrom_cd1 = linux/Fedora-8-i386-DVD.iso
-                        md5sum = dd6c79fddfff36d409d02242e7b10189
-                        md5sum_1m = dabae451bb69fbbad0e505b25144b1f9
                         install:
                             steps = Fedora-8-i386.steps
-                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/Fedora-8-i386-DVD.iso
+                            md5sum_cd1 = dd6c79fddfff36d409d02242e7b10189
+                            md5sum_1m_cd1 = dabae451bb69fbbad0e505b25144b1f9
+                        unattended_install:
                             unattended_file = unattended/Fedora-8.ks
-                            tftp = images/f8-32/tftpboot
                             #floppy = images/f8-32/ks.vfd
                             cdrom_unattended = images/f8-32/ks.iso
+                            kernel = images/f8-32/vmlinuz
+                            initrd = images/f8-32/initrd.img
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/Fedora-8-i386-DVD.iso
+                            md5sum_cd1 = dd6c79fddfff36d409d02242e7b10189
+                            md5sum_1m_cd1 = dabae451bb69fbbad0e505b25144b1f9
 
                     - 8.64:
                         no setup
                         image_name = f8-64
-                        cdrom_cd1 = linux/Fedora-8-x86_64-DVD.iso
-                        md5sum = 2cb231a86709dec413425fd2f8bf5295
-                        md5sum_1m = 145f6414e19492649a56c89f0a45e719
                         install:
                             steps = Fedora-8-64.steps
-                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/Fedora-8-x86_64-DVD.iso
+                            md5sum_cd1 = 2cb231a86709dec413425fd2f8bf5295
+                            md5sum_1m_cd1 = 145f6414e19492649a56c89f0a45e719
+                        unattended_install:
                             unattended_file = unattended/Fedora-8.ks
-                            tftp = images/f8-64/tftpboot
                             #floppy = images/f8-64/ks.vfd
                             cdrom_unattended = images/f8-64/ks.iso
+                            kernel = images/f8-64/vmlinuz
+                            initrd = images/f8-64/initrd.img
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/Fedora-8-x86_64-DVD.iso
+                            md5sum_cd1 = 2cb231a86709dec413425fd2f8bf5295
+                            md5sum_1m_cd1 = 145f6414e19492649a56c89f0a45e719
 
                     - 9.32:
                         image_name = f9-32
-                        cdrom_cd1 = linux/Fedora-9-i386-DVD.iso
-                        md5sum = 72601f685ea8c808c303353d8bf4d307
-                        md5sum_1m = f24fa25689e5863f1b99984c6feb787f
                         install:
                             steps = Fedora-9-i386.steps
-                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/Fedora-9-i386-DVD.iso
+                            md5sum_cd1 = 72601f685ea8c808c303353d8bf4d307
+                            md5sum_1m_cd1 = f24fa25689e5863f1b99984c6feb787f
+                        unattended_install:
                             unattended_file = unattended/Fedora-9.ks
-                            tftp = images/f9-32/tftpboot
                             #floppy = images/f9-32/ks.vfd
                             cdrom_unattended = images/f9-32/ks.iso
+                            kernel = images/f9-32/vmlinuz
+                            initrd = images/f9-32/initrd.img
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/Fedora-9-i386-DVD.iso
+                            md5sum_cd1 = 72601f685ea8c808c303353d8bf4d307
+                            md5sum_1m_cd1 = f24fa25689e5863f1b99984c6feb787f
+
 
                     - 9.64:
                         image_name = f9-64
-                        cdrom_cd1 = linux/Fedora-9-x86_64-DVD.iso
-                        md5sum = 05b2ebeed273ec54d6f9ed3d61ea4c96
-                        md5sum_1m = 9822ab5097e37e8fe306ef2192727db4
                         install:
                             steps = Fedora-9-64.steps
-                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/Fedora-9-x86_64-DVD.iso
+                            md5sum_cd1 = 05b2ebeed273ec54d6f9ed3d61ea4c96
+                            md5sum_1m_cd1 = 9822ab5097e37e8fe306ef2192727db4
+                        unattended_install:
                             unattended_file = unattended/Fedora-9.ks
-                            tftp = images/f9-64/tftpboot
                             #floppy = images/f9-64/ks.vfd
                             cdrom_unattended = images/f9-64/ks.iso
+                            kernel = images/f9-64/vmlinuz
+                            initrd = images/f9-64/initrd.img
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/Fedora-9-x86_64-DVD.iso
+                            md5sum_cd1 = 05b2ebeed273ec54d6f9ed3d61ea4c96
+                            md5sum_1m_cd1 = 9822ab5097e37e8fe306ef2192727db4
+
 
                     - 10.32:
                         image_name = f10-32
-                        cdrom_cd1 = linux/Fedora-10-i386-DVD.iso
-                        md5sum = 27e581edb392728c4a07d00d3fc5ced0
-                        md5sum_1m = bd67c68bdf595e4ba7131ec702159181
-                        unattended_install.cdrom:
+                        unattended_install:
                             unattended_file = unattended/Fedora-10.ks
-                            tftp = images/f10-32/tftpboot
                             #floppy = images/f10-32/ks.vfd
                             cdrom_unattended = images/f10-32/ks.iso
+                            kernel = images/f10-32/vmlinuz
+                            initrd = images/f10-32/initrd.img
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/Fedora-10-i386-DVD.iso
+                            md5sum_cd1 = 27e581edb392728c4a07d00d3fc5ced0
+                            md5sum_1m_cd1 = bd67c68bdf595e4ba7131ec702159181
 
                     - 10.64:
                         image_name = f10-64
-                        cdrom_cd1 = linux/Fedora-10-x86_64-DVD.iso
-                        sha1sum = f1e5ae7db6a1ba227de7294c4112385922388648
-                        md5sum_1m = 732857cbf40c80c34683e874601d982c
-                        unattended_install.cdrom:
+                        unattended_install:
                             unattended_file = unattended/Fedora-10.ks
-                            tftp = images/f10-64/tftpboot
                             #floppy = images/f10-64/ks.vfd
                             cdrom_unattended = images/f10-64/ks.iso
+                            kernel = images/f10-64/vmlinuz
+                            initrd = images/f10-64/initrd.img
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/Fedora-10-x86_64-DVD.iso
+                            sha1sum_cd1 = f1e5ae7db6a1ba227de7294c4112385922388648
+                            md5sum_1m_cd1 = 732857cbf40c80c34683e874601d982c
 
                     - 11.32:
                         image_name = f11-32
-                        cdrom_cd1 = linux/Fedora-11-i386-DVD.iso
-                        md5sum = e3b1e2d1ba42aa4705fa5f41771b3927
-                        md5sum_1m = dc8ddf90648c247339c721395aa49714
                         install:
                             steps = Fedora-11-32.steps
-                        unattended_install.cdrom:
+                        unattended_install:
                             unattended_file = unattended/Fedora-11.ks
-                            tftp = images/f11-32/tftpboot
                             #floppy = images/f11-32/ks.vfd
                             cdrom_unattended = images/f11-32/ks.iso
+                            kernel = images/f11-32/vmlinuz
+                            initrd = images/f11-32/initrd.img
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/Fedora-11-i386-DVD.iso
+                            md5sum_cd1 = e3b1e2d1ba42aa4705fa5f41771b3927
+                            md5sum_1m_cd1 = dc8ddf90648c247339c721395aa49714
 
                     - 11.64:
                         image_name = f11-64
-                        cdrom_cd1 = linux/Fedora-11-x86_64-DVD.iso
-                        md5sum = 9d419844adeb93120215fe7505c9bce8
-                        md5sum_1m = 405ee05e2387a2e4328b008d5bcbdd1e
-                        unattended_install.cdrom:
+                        unattended_install:
                             unattended_file = unattended/Fedora-11.ks
-                            tftp = images/f11-64/tftpboot
                             #floppy = images/f11-64/ks.vfd
                             cdrom_unattended = images/f11-64/ks.iso
+                            kernel = images/f11-64/vmlinuz
+                            initrd = images/f11-64/initrd.img
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/Fedora-11-x86_64-DVD.iso
+                            md5sum_cd1 = 9d419844adeb93120215fe7505c9bce8
+                            md5sum_1m_cd1 = 405ee05e2387a2e4328b008d5bcbdd1e
 
                     - 12.32:
                         image_name = f12-32
-                        cdrom_cd1 = linux/Fedora-12-i386-DVD.iso
-                        md5sum = 2c4c1c0d09f2fbcfd8ee6a0c5542eeb2
-                        md5sum_1m = eee935d7f0cf2ef03f6ddce3a2a50050
-                        unattended_install.cdrom:
+                        unattended_install:
                             unattended_file = unattended/Fedora-12.ks
-                            tftp = images/f12-32/tftpboot
                             #floppy = images/f12-32/ks.vfd
                             cdrom_unattended = images/f12-32/ks.iso
+                            kernel = images/f12-32/vmlinuz
+                            initrd = images/f12-32/initrd.img
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/Fedora-12-i386-DVD.iso
+                            md5sum_cd1 = 2c4c1c0d09f2fbcfd8ee6a0c5542eeb2
+                            md5sum_1m_cd1 = eee935d7f0cf2ef03f6ddce3a2a50050
 
                     - 12.64:
                         image_name = f12-64
-                        cdrom_cd1 = linux/Fedora-12-x86_64-DVD.iso
-                        md5sum = 6dd31e292cc2eb1140544e9b1ba61c56
-                        md5sum_1m = 514efbd7698b55ff6768c8605438bfc5
-                        unattended_install.cdrom:
+                        unattended_install:
                             unattended_file = unattended/Fedora-12.ks
-                            tftp = images/f12-64/tftpboot
                             #floppy = images/f12-64/ks.vfd
                             cdrom_unattended = images/f12-64/ks.iso
+                            kernel = images/f12-64/vmlinuz
+                            initrd = images/f12-64/initrd.img
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/Fedora-12-x86_64-DVD.iso
+                            md5sum_cd1 = 6dd31e292cc2eb1140544e9b1ba61c56
+                            md5sum_1m_cd1 = 514efbd7698b55ff6768c8605438bfc5
 
                     - 13.32:
                         image_name = f13-32
-                        cdrom_cd1 = linux/Fedora-13-i386-DVD.iso
-                        md5sum = 212fec517c2629b4b5eaf3662ac13136
-                        md5sum_1m = 4e1578a6ed5a6e7cd03b8fb074030746
-                        unattended_install.cdrom:
+                        unattended_install:
                             unattended_file = unattended/Fedora-13.ks
-                            tftp = images/f13-32/tftpboot
                             #floppy = images/f13-32/ks.vfd
                             cdrom_unattended = images/f13-32/ks.iso
+                            kernel = images/f13-32/vmlinuz
+                            initrd = images/f13-32/initrd.img
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/Fedora-13-i386-DVD.iso
+                            md5sum_cd1 = 212fec517c2629b4b5eaf3662ac13136
+                            md5sum_1m_cd1 = 4e1578a6ed5a6e7cd03b8fb074030746
 
                     - 13.64:
                         image_name = f13-64
-                        cdrom_cd1 = linux/Fedora-13-x86_64-DVD.iso
-                        md5sum = 6fbae6379cf27f36e1f2c7827ba7dc35
-                        md5sum_1m = 68821b9de4d3b5975d6634334e7f47a6
-                        unattended_install.cdrom:
+                        unattended_install:
                             unattended_file = unattended/Fedora-13.ks
-                            tftp = images/f13-64/tftpboot
                             #floppy = images/f13-64/ks.vfd
                             cdrom_unattended = images/f13-64/ks.iso
+                            kernel = images/f13-64/vmlinuz
+                            initrd = images/f13-64/initrd.img
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/Fedora-13-x86_64-DVD.iso
+                            md5sum_cd1 = 6fbae6379cf27f36e1f2c7827ba7dc35
+                            md5sum_1m_cd1 = 68821b9de4d3b5975d6634334e7f47a6
+
+                    - 14.32:
+                        image_name = f14-32
+                        unattended_install:
+                            unattended_file = unattended/Fedora-14.ks
+                            #floppy = images/f14-32/ks.vfd
+                            cdrom_unattended = images/f14-32/ks.iso
+                            kernel = images/f14-32/vmlinuz
+                            initrd = images/f14-32/initrd.img
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/Fedora-14-i386-DVD.iso
+                            md5sum_cd1 = 1cc67641506d2f931d669b8d3528dded
+                            md5sum_1m_cd1 = d314ab126dabab686111e6a0d71d2e67
+
+                    - 14.64:
+                        image_name = f14-64
+                        unattended_install:
+                            unattended_file = unattended/Fedora-14.ks
+                            #floppy = images/f14-64/ks.vfd
+                            cdrom_unattended = images/f14-64/ks.iso
+                            kernel = images/f14-64/vmlinuz
+                            initrd = images/f14-64/initrd.img
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/Fedora-14-x86_64-DVD.iso
+                            md5sum_cd1 = f2ebf941dc45f99ee3e8a457c9544552
+                            md5sum_1m_cd1 = df029f9cffbc3517937a91124a1e0c3a
+
 
 
             - DSL-4.2.5:
@@ -924,199 +1170,232 @@
                 image_name = dsl-4.2.5
                 install:
                     steps = DSL-4.2.5.steps
-                    cdrom_cd1 = linux/dsl-4.2.5.iso
-                    md5sum = 61694888aede3e01229865b8e6acd4a1
-                    md5sum_1m = 527f2481bd25310f2e3a6e5345ff3d12
+                    cdrom_cd1 = isos/linux/dsl-4.2.5.iso
+                    md5sum_cd1 = 61694888aede3e01229865b8e6acd4a1
+                    md5sum_1m_cd1 = 527f2481bd25310f2e3a6e5345ff3d12
 
             - Mandriva-One-2007:
                 only install
                 image_name = mandriva-one-2007
                 steps = Mandriva-One-2007-32.steps
-                cdrom_cd1 = linux/mandriva-one-2007-i386.iso
-                md5sum = 7e9e183dc11b9d39f480238e4e12bb05
-                md5sum_1m = dc7865a75db665efc86d59bca7c1fe07
+                cdrom_cd1 = isos/linux/mandriva-one-2007-i386.iso
+                md5sum_cd1 = 7e9e183dc11b9d39f480238e4e12bb05
+                md5sum_1m_cd1 = dc7865a75db665efc86d59bca7c1fe07
 
             - OpenSUSE:
                 no setup
                 shell_prompt = ".*:.*\s#"
-                unattended_install.cdrom:
-                    pxe_image = "linux"
-                    pxe_initrd = "initrd"
-                    tftp = "images/tftpboot"
-                    bootp = "/pxelinux.0"
-                    extra_params += " -boot cn"
+                unattended_install:
                     # You have to use autoyast=floppy if you want to use floppies to
                     # hold your autoyast file
-                    kernel_args = "autoyast=floppy console=ttyS0,115200 console=tty0"
-                    #kernel_args = "autoyast=cdrom console=ttyS0,115200 console=tty0"
+                    extra_params += " --append 'autoyast=floppy console=ttyS0,115200 console=tty0'"
+                    #extra_params += " --append 'autoyast=cdrom console=ttyS0,115200 console=tty0'"
                     post_install_delay = 10
 
                 variants:
                     - 11.0.32:
                         image_name = openSUSE-11.0-32
-                        cdrom_cd1 = linux/openSUSE-11.0-DVD-i386.iso
-                        md5sum = ed6a5b3feb668866df812b1c2aed9d7f
-                        md5sum_1m = c720b30557af758e69de450409516369
                         install:
                             steps = openSUSE-11.0-32.steps
-                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/openSUSE-11.0-DVD-i386.iso
+                            md5sum_cd1 = ed6a5b3feb668866df812b1c2aed9d7f
+                            md5sum_1m_cd1 = c720b30557af758e69de450409516369
+                        unattended_install:
                             unattended_file = unattended/OpenSUSE-11.xml
-                            tftp = images/opensuse-11-0-32/tftpboot
                             floppy = images/opensuse-11-0-32/autoyast.vfd
                             #cdrom_unattended = images/opensuse-11-0-32/autoyast.iso
-                            pxe_dir = boot/i386/loader
+                            kernel = images/opensuse-11-0-32/linux
+                            initrd = images/opensuse-11-0-32/initrd
+                            boot_path = boot/i386/loader
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/openSUSE-11.0-DVD-i386.iso
+                            md5sum_cd1 = ed6a5b3feb668866df812b1c2aed9d7f
+                            md5sum_1m_cd1 = c720b30557af758e69de450409516369
+
 
                     - 11.0.64:
                         image_name = openSUSE-11.0-64
-                        cdrom_cd1 = linux/openSUSE-11.0-DVD-x86_64.iso
-                        md5sum = 512c8346b0f8eb35f28c4eb96454d391
-                        md5sum_1m = 661aa4cd031df2f25ea0102318a3f4d1
-                        unattended_install.cdrom:
+                        unattended_install:
                             unattended_file = unattended/OpenSUSE-11.xml
-                            tftp = images/opensuse-11-0-64/tftpboot
                             floppy = images/opensuse-11-0-64/autoyast.vfd
                             #cdrom_unattended = images/opensuse-11-0-64/autoyast.iso
-                            pxe_dir = boot/x86_64/loader
+                            kernel = images/opensuse-11-0-64/linux
+                            initrd = images/opensuse-11-0-64/initrd
+                            boot_path = boot/x86_64/loader
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/openSUSE-11.0-DVD-x86_64.iso
+                            md5sum_cd1 = 512c8346b0f8eb35f28c4eb96454d391
+                            md5sum_1m_cd1 = 661aa4cd031df2f25ea0102318a3f4d1
 
                     - 11.1.32:
                         image_name = openSUSE-11.1-32
-                        cdrom_cd1 = linux/openSUSE-11.1-DVD-i586.iso
-                        md5sum = 8f51b278c0415be28c5699e465444bd3
-                        md5sum_1m = b70217417468389083429f81ba7ce2bd
                         install:
-                            steps=openSUSE-11.1-32-and-64.steps
-                        unattended_install.cdrom:
+                            steps = openSUSE-11.1-32-and-64.steps
+                            cdrom_cd1 = isos/linux/openSUSE-11.1-DVD-i586.iso
+                            md5sum_cd1 = 8f51b278c0415be28c5699e465444bd3
+                            md5sum_1m_cd1 = b70217417468389083429f81ba7ce2bd
+                        unattended_install:
                             unattended_file = unattended/OpenSUSE-11.xml
-                            tftp = images/opensuse-11-1-32/tftpboot
                             floppy = images/opensuse-11-1-32/autoyast.vfd
                             #cdrom_unattended = images/opensuse-11-1-32/autoyast.iso
-                            pxe_dir = boot/i386/loader
+                            kernel = images/opensuse-11-1-32/linux
+                            initrd = images/opensuse-11-1-32/initrd
+                            boot_path = boot/i386/loader
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/openSUSE-11.1-DVD-i586.iso
+                            md5sum_cd1 = 8f51b278c0415be28c5699e465444bd3
+                            md5sum_1m_cd1 = b70217417468389083429f81ba7ce2bd
 
                     - 11.1.64:
                         image_name = openSUSE-11.1-64
-                        cdrom_cd1 = linux/openSUSE-11.1-DVD-x86_64.iso
-                        md5sum = 2afee1b8a87175e6dee2b8dbbd1ad8e8
-                        md5sum_1m = 768ca32503ef92c28f2d144f2a87e4d0
                         install:
                             steps=openSUSE-11.1-32-and-64.steps
-                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/openSUSE-11.1-DVD-x86_64.iso
+                            md5sum_cd1 = 2afee1b8a87175e6dee2b8dbbd1ad8e8
+                            md5sum_1m_cd1 = 768ca32503ef92c28f2d144f2a87e4d0
+                        unattended_install:
                             unattended_file = unattended/OpenSUSE-11.xml
-                            tftp = images/opensuse-11-1-64/tftpboot
                             floppy = images/opensuse-11-1-64/autoyast.vfd
                             #cdrom_unattended = images/opensuse-11-1-64/autoyast.iso
-                            pxe_dir = boot/x86_64/loader
+                            kernel = images/opensuse-11-1-64/linux
+                            initrd = images/opensuse-11-1-64/initrd
+                            boot_path = boot/x86_64/loader
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/openSUSE-11.1-DVD-x86_64.iso
+                            md5sum_cd1 = 2afee1b8a87175e6dee2b8dbbd1ad8e8
+                            md5sum_1m_cd1 = 768ca32503ef92c28f2d144f2a87e4d0
+
 
                     - 11.2.32:
                         image_name = openSUSE-11.2-32
-                        cdrom_cd1 = linux/openSUSE-11.2-DVD-i586.iso
-                        md5sum = 295d713314a30ad017948f0d542c6d92
-                        md5sum_1m = 1f8767d00acb492be5a5627c834e543f
-                        unattended_install.cdrom:
+                        unattended_install:
                             unattended_file = unattended/OpenSUSE-11.xml
-                            tftp = images/opensuse-11-2-32/tftpboot
                             floppy = images/opensuse-11-2-32/autoyast.vfd
                             #cdrom_unattended = images/opensuse-11-2-32/autoyast.iso
-                            pxe_dir = boot/i386/loader
+                            kernel = images/opensuse-11-2-32/linux
+                            initrd = images/opensuse-11-2-32/initrd
+                            boot_path = boot/i386/loader
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/openSUSE-11.2-DVD-i586.iso
+                            md5sum_cd1 = 295d713314a30ad017948f0d542c6d92
+                            md5sum_1m_cd1 = 1f8767d00acb492be5a5627c834e543f
+
 
                     - 11.2.64:
                         image_name = openSUSE-11.2-64
-                        cdrom_cd1 = linux/openSUSE-11.2-DVD-x86_64.iso
-                        md5sum = 6a09295e34dc030319d040f67f4742c6
-                        md5sum_1m = 11fd11d39744450b898f04c371dde2e7
-                        unattended_install.cdrom:
+                        unattended_install:
                             unattended_file = unattended/OpenSUSE-11.xml
-                            tftp = images/opensuse-11-2-64/tftpboot
                             floppy = images/opensuse11-2-64/autoyast.vfd
                             #cdrom_unattended = images/opensuse11-2-64/autoyast.iso
-                            pxe_dir = boot/x86_64/loader
+                            kernel = images/opensuse-11-2-64/linux
+                            initrd = images/opensuse-11-2-64/initrd
+                            boot_path = boot/x86_64/loader
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/openSUSE-11.2-DVD-x86_64.iso
+                            md5sum_cd1 = 6a09295e34dc030319d040f67f4742c6
+                            md5sum_1m_cd1 = 11fd11d39744450b898f04c371dde2e7
 
                     - 11.3.32:
                         image_name = openSUSE-11.3-32
-                        cdrom_cd1 = linux/openSUSE-11.3-DVD-i586.iso
-                        md5sum = 1a1da28c84e3cdad750d5cfa21c4fd17
-                        md5sum_1m = 4dd26906ce6cb3946519cb0b0de4b0f8
-                        unattended_install.cdrom:
+                        unattended_install:
                             unattended_file = unattended/OpenSUSE-11.xml
-                            tftp = images/opensuse-11-3-32/tftpboot
                             floppy = images/opensuse-11-3-32/autoyast.vfd
                             #cdrom_unattended = images/opensuse-11-3-32/autoyast.iso
-                            pxe_dir = boot/i386/loader
+                            kernel = images/opensuse-11-3-32/linux
+                            initrd = images/opensuse-11-3-32/initrd
+                            boot_path = boot/i386/loader
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/openSUSE-11.3-DVD-i586.iso
+                            md5sum_cd1 = 1a1da28c84e3cdad750d5cfa21c4fd17
+                            md5sum_1m_cd1 = 4dd26906ce6cb3946519cb0b0de4b0f8
 
                     - 11.3.64:
                         image_name = openSUSE-11.3-64
-                        cdrom_cd1 = linux/openSUSE-11.3-DVD-x86_64.iso
-                        md5sum = adf5d2a0a03c1e3aaf102fd6a4771b87
-                        md5sum_1m = e0dd12dac30d296417256775e1234c6e
-                        unattended_install.cdrom:
+                        unattended_install:
                             unattended_file = unattended/OpenSUSE-11.xml
-                            tftp = images/opensuse-11-3-64/tftpboot
                             floppy = images/opensuse-11-3-64/autoyast.vfd
                             #cdrom_unattended = images/opensuse-11-3-64/autoyast.iso
-                            pxe_dir = boot/x86_64/loader
+                            kernel = images/opensuse-11-3-64/linux
+                            initrd = images/opensuse-11-3-64/initrd
+                            boot_path = boot/x86_64/loader
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/openSUSE-11.3-DVD-x86_64.iso
+                            md5sum_cd1 = adf5d2a0a03c1e3aaf102fd6a4771b87
+                            md5sum_1m_cd1 = e0dd12dac30d296417256775e1234c6e
 
             - SLES:
                 shell_prompt = "^root@.*[\#\$]\s*$|#"
-                unattended_install.cdrom:
-                    pxe_image = "linux"
-                    pxe_initrd = "initrd"
-                    bootp = "/pxelinux.0"
-                    extra_params += " -boot cn"
+                unattended_install:
                     # You have to use autoyast=floppy if you want to use floppies to
                     # hold your autoyast file
-                    kernel_args = "autoyast=floppy console=ttyS0,115200 console=tty0"
-                    #kernel_args = "autoyast=cdrom console=ttyS0,115200 console=tty0"
+                    extra_params += " --append 'autoyast=floppy console=ttyS0,115200 console=tty0'"
+                    #extra_params += " --append 'autoyast=cdrom console=ttyS0,115200 console=tty0'"
                     post_install_delay = 10
+                    kernel = linux
+                    initrd = initrd
 
                 variants:
                     - 11.0.32:
                         image_name = sles11-32
-                        cdrom_cd1 = linux/SLES-11-DVD-i586-GM-DVD1.iso
-                        md5sum = 4958d4dde2575666355c8a1c5858bab0
-                        md5sum_1m = 1f19d4eff5bcead2a3e5b8b4212b6796
-                        unattended_install.cdrom:
+                        unattended_install:
                             unattended_file = unattended/SLES-11.xml
-                            tftp = images/sles-11-0-32/tftpboot
-                            #floppy = images/sles-11-0-32/autoyast.vfd
-                            cdrom_unattended = images/sles-11-0-32/autoyast.iso
                             floppy = images/sles-11-0-32/autoyast.vfd
                             #cdrom_unattended = images/sles-11-0-32/autoyast.iso
-                            pxe_dir = boot/i386/loader
+                            kernel = images/sles-11-0-32/linux
+                            initrd = images/sles-11-0-32/initrd
+                            boot_path = boot/i386/loader
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/SLES-11-DVD-i586-GM-DVD1.iso
+                            md5sum_cd1 = 4958d4dde2575666355c8a1c5858bab0
+                            md5sum_1m_cd1 = 1f19d4eff5bcead2a3e5b8b4212b6796
+
 
                     - 11.0.64:
                         image_name = sles11-64
-                        cdrom_cd1 = linux/SLES-11-DVD-x86_64-GM-DVD1.iso
-                        md5sum = 50a2bd45cd12c3808c3ee48208e2586b
-                        md5sum_1m = 00000951cab7c32e332362fc424c1054
-                        unattended_install.cdrom:
+                        cdrom_cd1 = isos/linux/SLES-11-DVD-x86_64-GM-DVD1.iso
+                        md5sum_cd1 = 50a2bd45cd12c3808c3ee48208e2586b
+                        md5sum_1m_cd1 = 00000951cab7c32e332362fc424c1054
+                        unattended_install:
                             unattended_file = unattended/SLES-11.xml
-                            tftp = images/sles-11-0-64/tftpboot
                             floppy = images/sles-11-0-64/autoyast.vfd
                             #cdrom_unattended = images/sles-11-0-64/autoyast.iso
-                            pxe_dir = boot/x86_64/loader
+                            kernel = images/sles-11-0-64/linux
+                            initrd = images/sles-11-0-64/initrd
+                            boot_path = boot/x86_64/loader
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/SLES-11-DVD-x86_64-GM-DVD1.iso
+                            md5sum_cd1 = 50a2bd45cd12c3808c3ee48208e2586b
+                            md5sum_1m_cd1 = 00000951cab7c32e332362fc424c1054
+
 
                     - 11.1.32:
                         image_name = sles11sp1-32
-                        cdrom_cd1 = linux/SLES-11-SP1-DVD-i586-GM-DVD1.iso
-                        md5sum = 0dd6886858d93501c38854552b9b1b0d
-                        md5sum_1m = a626a3d50813410e3ac42794e05773bb
                         unattended_install:
                             unattended_file = unattended/SLES-11.xml
-                            tftp = images/sles-11-1-32/tftpboot
                             floppy = images/sles-11-1-32/autoyast.vfd
                             #cdrom_unattended = images/sles-11-1-32/autoyast.iso
-                            pxe_dir = boot/i386/loader
+                            kernel = images/sles-11-1-32/linux
+                            initrd = images/sles-11-1-32/initrd
+                            boot_path = boot/i386/loader
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/SLES-11-SP1-DVD-i586-GM-DVD1.iso
+                            md5sum_cd1 = 0dd6886858d93501c38854552b9b1b0d
+                            md5sum_1m_cd1 = a626a3d50813410e3ac42794e05773bb
 
                     - 11.1.64:
                         image_name = sles11sp1-64
-                        cdrom_cd1 = linux/SLES-11-SP1-DVD-x86_64-GM-DVD1.iso
-                        md5sum = d2e10420f3689faa49a004b60fb396b7
-                        md5sum_1m = f7f67b5da46923a9f01da8a2b6909654
                         unattended_install:
                             unattended_file = unattended/SLES-11.xml
-                            tftp = images/sles-11-1-64/tftpboot
                             floppy = images/sles-11-1-64/autoyast.vfd
                             #cdrom_unattended = images/sles-11-1-64/autoyast.iso
-                            pxe_dir = boot/x86_64/loader
+                            kernel = images/sles-11-1-64/linux
+                            initrd = images/sles-11-1-64/initrd
+                            boot_path = boot/x86_64/loader
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/SLES-11-SP1-DVD-x86_64-GM-DVD1.iso
+                            md5sum_cd1 = d2e10420f3689faa49a004b60fb396b7
+                            md5sum_1m_cd1 = f7f67b5da46923a9f01da8a2b6909654
+
 
             - @Ubuntu:
                 shell_prompt = "^root@.*[\#\$]\s*$"
@@ -1126,16 +1405,16 @@
                         only install
                         image_name = ubuntu-6.10-32
                         steps = Ubuntu-6.10-32.steps
-                        cdrom_cd1 = linux/ubuntu-6.10-desktop-i386.iso
-                        md5sum = 17fb825641571ce5888a718329efd016
-                        md5sum_1m = 7531d0a84e7451d17c5d976f1c3f8509
+                        cdrom_cd1 = isos/linux/ubuntu-6.10-desktop-i386.iso
+                        md5sum_cd1 = 17fb825641571ce5888a718329efd016
+                        md5sum_1m_cd1 = 7531d0a84e7451d17c5d976f1c3f8509
 
                     - Ubuntu-8.04-32:
                         skip = yes
                         image_name = ubuntu-8.04-32
                         install:
                             steps = Ubuntu-8.04-32.steps
-                            cdrom_cd1 = linux/ubuntu-8.04.1-desktop-i386.iso
+                            cdrom_cd1 = isos/linux/ubuntu-8.04.1-desktop-i386.iso
                         setup:
                             steps = Ubuntu-8.04-32-setupssh.steps
 
@@ -1143,9 +1422,9 @@
                         image_name = ubuntu-8.10-server-32
                         install:
                             steps = Ubuntu-8.10-server-32.steps
-                            cdrom_cd1 = linux/ubuntu-8.10-server-i386.iso
-                            md5sum = a2ec9975a91e1228c8292ed9799dc302
-                            md5sum_1m = ea493eb8ef7722ead693492fd9f8a13f
+                            cdrom_cd1 = isos/linux/ubuntu-8.10-server-i386.iso
+                            md5sum_cd1 = a2ec9975a91e1228c8292ed9799dc302
+                            md5sum_1m_cd1 = ea493eb8ef7722ead693492fd9f8a13f
                         setup:
                             steps = Ubuntu-8.10-server-32-gcc.steps
 
@@ -1156,176 +1435,252 @@
                     modprobe_module = acpiphp
                 block_hotplug:
                     modprobe_module = acpiphp
-                unattended_install.cdrom:
-                    pxe_dir = "images/pxeboot"
-                    pxe_image = "vmlinuz"
-                    pxe_initrd = "initrd.img"
-                    tftp = "images/tftpboot"
-                    bootp = "/pxelinux.0"
-                    extra_params += " -boot cn"
+                unattended_install:
+                    boot_path = images/pxeboot
                     # You have to use ks=floppy if you want to use floppies to
                     # hold your kickstart file
-                    #kernel_args = "ks=floppy nicdelay=60 console=ttyS0,115200 console=tty0"
-                    kernel_args = "ks=cdrom nicdelay=60 console=ttyS0,115200 console=tty0"
+                    #extra_params += " --append 'ks=floppy nicdelay=60 console=ttyS0,115200 console=tty0'"
+                    extra_params += " --append 'ks=cdrom nicdelay=60 console=ttyS0,115200 console=tty0'"
 
                 variants:
                     - 3.9.i386:
                         no setup autotest linux_s3 guest_s4 shutdown
                         image_name = rhel3-32
                         mem_chk_cmd = dmidecode | awk -F: '/Maximum Capacity/ {print $2}'
-                        cdrom_cd1 = linux/RHEL-3.9-i386-DVD.iso
-                        md5sum = ddd11a1cb104119039b0fa05df6d52b8
-                        md5sum_1m = 5f10c9417c7b8372b3456c1b5f3f9ed0
                         install:
                             steps=RHEL-3.9-i386.steps
-                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/RHEL-3.9-i386-DVD.iso
+                            md5sum_cd1 = ddd11a1cb104119039b0fa05df6d52b8
+                            md5sum_1m_cd1 = 5f10c9417c7b8372b3456c1b5f3f9ed0
+                        unattended_install:
                             unattended_file = unattended/RHEL-3-series.ks
-                            tftp = images/rhel39-32/tftpboot
                             #floppy = images/rhel39-32/ks.vfd
                             cdrom_unattended = images/rhel39-32/ks.iso
+                            kernel = images/rhel39-32/vmlinuz
+                            initrd = images/rhel39-32/initrd.img
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/RHEL-3.9-i386-DVD.iso
+                            md5sum_cd1 = ddd11a1cb104119039b0fa05df6d52b8
+                            md5sum_1m_cd1 = 5f10c9417c7b8372b3456c1b5f3f9ed0
 
                     - 3.9.x86_64:
                         no setup autotest linux_s3 guest_s4 shutdown
                         image_name = rhel3-64
                         mem_chk_cmd = dmidecode | awk -F: '/Maximum Capacity/ {print $2}'
-                        cdrom_cd1 = linux/RHEL-3.9-x86_64-DVD.iso
-                        md5sum = bf4635e4a4bd3b43838e72bc8c329d55
-                        md5sum_1m = 18ecd37b639109f1b2af05cfb57dfeaf
                         install:
                             steps=RHEL-3.9-x86_64.steps
-                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/RHEL-3.9-x86_64-DVD.iso
+                            md5sum_cd1 = bf4635e4a4bd3b43838e72bc8c329d55
+                            md5sum_1m_cd1 = 18ecd37b639109f1b2af05cfb57dfeaf
+                        unattended_install:
                             unattended_file = unattended/RHEL-3-series.ks
-                            tftp = images/rhel39-64/tftpboot
                             #floppy = images/rhel39-64/ks.vfd
                             cdrom_unattended = images/rhel39-64/ks.iso
+                            kernel = images/rhel39-64/vmlinuz
+                            initrd = images/rhel39-64/initrd.img
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/RHEL-3.9-x86_64-DVD.iso
+                            md5sum_cd1 = bf4635e4a4bd3b43838e72bc8c329d55
+                            md5sum_1m_cd1 = 18ecd37b639109f1b2af05cfb57dfeaf
+
 
                     - 4.7.i386:
                         no setup autotest
                         image_name = rhel4-32
-                        cdrom_cd1 = linux/RHEL-4.7-i386-DVD.iso
-                        md5sum = ee5092653732a88ddbaf8eef2484c500
-                        md5sum_1m = 127081cbed825d7232331a2083975528
                         install:
                             steps = RHEL-4.7-i386.steps
-                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/RHEL-4.7-i386-DVD.iso
+                            md5sum_cd1 = ee5092653732a88ddbaf8eef2484c500
+                            md5sum_1m_cd1 = 127081cbed825d7232331a2083975528
+                        unattended_install:
                             unattended_file = unattended/RHEL-4-series.ks
-                            tftp = images/rhel47-32/tftpboot
                             #floppy = images/rhel47-32/ks.vfd
                             cdrom_unattended = images/rhel47-32/ks.iso
+                            kernel = images/rhel47-32/vmlinuz
+                            initrd = images/rhel47-32/initrd.img
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/RHEL-4.7-i386-DVD.iso
+                            md5sum_cd1 = ee5092653732a88ddbaf8eef2484c500
+                            md5sum_1m_cd1 = 127081cbed825d7232331a2083975528
 
                     - 4.7.x86_64:
                         no setup autotest
                         image_name = rhel4-64
-                        cdrom_cd1 = linux/RHEL-4.7-x86_64-DVD.iso
-                        md5sum = ea9dae16dd86f7d94092d0e672333292
-                        md5sum_1m = 58fa63eaee68e269f4cb1d2edf479792
                         install:
                             steps = RHEL-4.7-x86_64.steps
-                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/RHEL-4.7-x86_64-DVD.iso
+                            md5sum_cd1 = ea9dae16dd86f7d94092d0e672333292
+                            md5sum_1m_cd1 = 58fa63eaee68e269f4cb1d2edf479792
+                        unattended_install:
                             unattended_file = unattended/RHEL-4-series.ks
-                            tftp = images/rhel47-64/tftpboot
                             #floppy = images/rhel47-64/ks.vfd
                             cdrom_unattended = images/rhel47-64/ks.iso
+                            kernel = images/rhel47-64/vmlinuz
+                            initrd = images/rhel47-64/initrd.img
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/RHEL-4.7-x86_64-DVD.iso
+                            md5sum_cd1 = ea9dae16dd86f7d94092d0e672333292
+                            md5sum_1m_cd1 = 58fa63eaee68e269f4cb1d2edf479792
 
                     - 4.8.i386:
                         no setup autotest
                         image_name = rhel4-32
-                        cdrom_cd1 = linux/RHEL-4.8-i386-DVD.iso
-                        md5sum = b024f0af5079539d3ef51f71fed0b194
-                        md5sum_1m = 969c197402b9058f28a278c1f807d15b
-                        unattended_install.cdrom:
+                        unattended_install:
                             unattended_file = unattended/RHEL-4-series.ks
-                            tftp = images/rhel48-32/tftpboot
                             #floppy = images/rhel48-32/ks.vfd
                             cdrom_unattended = images/rhel48-32/ks.iso
+                            kernel = images/rhel48-32/vmlinuz
+                            initrd = images/rhel48-32/initrd.img
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/RHEL-4.8-i386-DVD.iso
+                            md5sum_cd1 = b024f0af5079539d3ef51f71fed0b194
+                            md5sum_1m_cd1 = 969c197402b9058f28a278c1f807d15b
+
 
                     - 4.8.x86_64:
                         no setup autotest
                         image_name = rhel4-64
-                        cdrom_cd1 = linux/RHEL-4.8-x86_64-DVD.iso
-                        md5sum = 696bc877b0200cc942626673fcc3fc09
-                        md5sum_1m = b11ac0ef7fd345ad712966972db63886
-                        unattended_install.cdrom:
+                        unattended_install:
                             unattended_file = unattended/RHEL-4-series.ks
-                            tftp = images/rhel48-64/tftpboot
                             #floppy = images/rhel48-64/ks.vfd
                             cdrom_unattended = images/rhel48-64/ks.iso
+                            kernel = images/rhel48-64/vmlinuz
+                            initrd = images/rhel48-64/initrd.img
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/RHEL-4.8-x86_64-DVD.iso
+                            md5sum_cd1 = 696bc877b0200cc942626673fcc3fc09
+                            md5sum_1m_cd1 = b11ac0ef7fd345ad712966972db63886
+
 
                     - 5.3.i386:
                         no setup
                         image_name = rhel5-32
-                        cdrom_cd1 = linux/RHEL-5.3-i386-DVD.iso
-                        md5sum = 371c62851611fd32ead440df6f24a296
-                        md5sum_1m = 242318dd44152210f6ff6cdda1bfbf51
                         install:
                             steps = RHEL-5.3-i386.steps
-                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/RHEL-5.3-i386-DVD.iso
+                            md5sum_cd1 = 371c62851611fd32ead440df6f24a296
+                            md5sum_1m_cd1 = 242318dd44152210f6ff6cdda1bfbf51
+                        unattended_install:
                             unattended_file = unattended/RHEL-5-series.ks
-                            tftp = images/rhel53-32/tftpboot
                             #floppy = images/rhel53-32/ks.vfd
                             cdrom_unattended = images/rhel53-32/ks.iso
+                            kernel = images/rhel53-32/vmlinuz
+                            initrd = images/rhel53-32/initrd.img
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/RHEL-5.3-i386-DVD.iso
+                            md5sum_cd1 = 371c62851611fd32ead440df6f24a296
+                            md5sum_1m_cd1 = 242318dd44152210f6ff6cdda1bfbf51
+
 
                     - 5.3.x86_64:
                         no setup
                         image_name = rhel5-64
-                        cdrom_cd1 = linux/RHEL-5.3-x86_64-DVD.iso
-                        md5sum = c5ed6b284410f4d8212cafc78fd7a8c5
-                        md5sum_1m = b999f437583098ea5bbd56fb1de1d011
                         install:
                             steps=RHEL-5.3-x86_64.steps
-                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/RHEL-5.3-x86_64-DVD.iso
+                            md5sum_cd1 = c5ed6b284410f4d8212cafc78fd7a8c5
+                            md5sum_1m_cd1 = b999f437583098ea5bbd56fb1de1d011
+                        unattended_install:
                             unattended_file = unattended/RHEL-5-series.ks
-                            tftp = images/rhel53-64/tftpboot
                             #floppy = images/rhel53-64/ks.vfd
                             cdrom_unattended = images/rhel53-64/ks.iso
+                            kernel = images/rhel53-64/vmlinuz
+                            initrd = images/rhel53-64/initrd.img
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/RHEL-5.3-x86_64-DVD.iso
+                            md5sum_cd1 = c5ed6b284410f4d8212cafc78fd7a8c5
+                            md5sum_1m_cd1 = b999f437583098ea5bbd56fb1de1d011
+
 
                     - 5.4.i386:
                         no setup
                         image_name = rhel5-32
-                        cdrom_cd1 = linux/RHEL-5.4-i386-DVD.iso
-                        md5sum = 7a12ec6599527e4f3d1790b51eadbfed
-                        md5sum_1m = 0dbeb8f58d213752d8c029e8601abfbb
-                        unattended_install.cdrom:
+                        unattended_install:
                             unattended_file = unattended/RHEL-5-series.ks
-                            tftp = images/rhel54-32/tftpboot
                             #floppy = images/rhel54-32/ks.vfd
                             cdrom_unattended = images/rhel54-32/ks.iso
+                            kernel = images/rhel54-32/vmlinuz
+                            initrd = images/rhel54-32/initrd.img
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/RHEL-5.4-i386-DVD.iso
+                            md5sum_cd1 = 7a12ec6599527e4f3d1790b51eadbfed
+                            md5sum_1m_cd1 = 0dbeb8f58d213752d8c029e8601abfbb
+
 
                     - 5.4.x86_64:
                         no setup
                         image_name = rhel5-64
-                        cdrom_cd1 = linux/RHEL-5.4-x86_64-DVD.iso
-                        md5sum = 04fe3c10202402d7b389528d2bad0210
-                        md5sum_1m = 3e74112003e88a966754849dbb8f5c3f
-                        unattended_install.cdrom:
+                        unattended_install:
                             unattended_file = unattended/RHEL-5-series.ks
-                            tftp = images/rhel54-64/tftpboot
                             #floppy = images/rhel54-64/ks.vfd
                             cdrom_unattended = images/rhel54-64/ks.iso
+                            kernel = images/rhel54-64/vmlinuz
+                            initrd = images/rhel54-64/initrd.img
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/RHEL-5.4-x86_64-DVD.iso
+                            md5sum_cd1 = 04fe3c10202402d7b389528d2bad0210
+                            md5sum_1m_cd1 = 3e74112003e88a966754849dbb8f5c3f
+
 
                     - 5.5.i386:
                         no setup
                         image_name = rhel5-32
-                        cdrom_cd1 = linux/RHEL-5.5-i386-DVD.iso
-                        md5sum = 148858b157f275d9153797efddfc83c3
-                        md5sum_1m = 2502cc7ddb9d0684fe08c4a83d247902
-                        unattended_install.cdrom:
+                        unattended_install:
                             unattended_file = unattended/RHEL-5-series.ks
-                            tftp = images/rhel55-32/tftpboot
                             #floppy = images/rhel55-32/ks.vfd
                             cdrom_unattended = images/rhel55-32/ks.iso
+                            kernel = images/rhel55-32/vmlinuz
+                            initrd = images/rhel55-32/initrd.img
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/RHEL-5.5-i386-DVD.iso
+                            md5sum_cd1 = 148858b157f275d9153797efddfc83c3
+                            md5sum_1m_cd1 = 2502cc7ddb9d0684fe08c4a83d247902
+
 
                     - 5.5.x86_64:
                         no setup
                         image_name = rhel5-64
-                        cdrom_cd1 = linux/RHEL-5.5-x86_64-DVD.iso
-                        md5sum = f3119f883257ef9041234feda2f1cad0
-                        md5sum_1m = a744084a03f6a08627f71527fc107a1e
-                        unattended_install.cdrom:
+                        unattended_install:
                             unattended_file = unattended/RHEL-5-series.ks
-                            tftp = images/rhel55-64/tftpboot
                             #floppy = images/rhel55-64/ks.vfd
                             cdrom_unattended = images/rhel55-64/ks.iso
+                            kernel = images/rhel55-64/vmlinuz
+                            initrd = images/rhel55-64/initrd.img
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/RHEL-5.5-x86_64-DVD.iso
+                            md5sum_cd1 = f3119f883257ef9041234feda2f1cad0
+                            md5sum_1m_cd1 = a744084a03f6a08627f71527fc107a1e
+
+
+                    - 6.0.i386:
+                        no setup
+                        image_name = rhel6-32
+                        unattended_install:
+                            unattended_file = unattended/RHEL-6-series.ks
+                            #floppy = images/rhel60-32/ks.vfd
+                            cdrom_unattended = images/rhel60-32/ks.iso
+                            kernel = images/rhel60-32/vmlinuz
+                            initrd = images/rhel60-32/initrd.img
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/RHEL-6.0-i386-DVD.iso
+                            md5sum_cd1 = 291d234c93442405972689b4b41c14bc
+                            md5sum_1m_cd1 = ee2cc3d3babe91a1d581a07099c4318b
+
+
+                    - 6.0.x86_64:
+                        no setup
+                        image_name = rhel6-64
+                        unattended_install:
+                            unattended_file = unattended/RHEL-6-series.ks
+                            #floppy = images/rhel60-64/ks.vfd
+                            cdrom_unattended = images/rhel60-64/ks.iso
+                            kernel = images/rhel60-64/vmlinuz
+                            initrd = images/rhel60-64/initrd.img
+                        unattended_install.cdrom:
+                            cdrom_cd1 = isos/linux/RHEL-6.0-x86_64-DVD.iso
+                            md5sum_cd1 = f7141396c6a19399d63e8c195354317d
+                            md5sum_1m_cd1 = b060eeef63e2c8700db54ae02056e80c
+
 
 
     # Windows section
@@ -1347,23 +1702,25 @@
         guest_port_file_transfer = 10023
 
         # This ISO will be used for all tests except install:
-        cdrom_cd1 = windows/winutils.iso
+        cdrom_cd1 = isos/windows/winutils.iso
 
         cpu_chk_cmd = echo %NUMBER_OF_PROCESSORS%
         mem_chk_cmd = wmic memphysical
         mem_chk_cur_cmd = wmic memphysical
 
-        unattended_install.cdrom:
+        unattended_install.cdrom|whql.support_vm_install:
             timeout = 7200
             finish_program = deps/finish.exe
             cdroms += " winutils"
-            cdrom_winutils = windows/winutils.iso
+            cdrom_winutils = isos/windows/winutils.iso
             drive_index_winutils = 2
+            kernel =
+            initrd =
             # Turn install_virtio = yes if you want to install the
             # Windows virtio drivers. It might be a lot of setup though :)
             #install_virtio = no
             #cdroms += " virtio"
-            #cdrom_virtio = windows/virtio-win.iso
+            #cdrom_virtio = isos/windows/virtio-win.iso
             #drive_index_virtio = 3
             #virtio_floppy = /usr/share/virtio-win/virtio-drivers.vfd
         migrate:
@@ -1371,6 +1728,10 @@
             migration_bg_command = start ping -t localhost
             migration_bg_check_command = tasklist | find /I "ping.exe"
             migration_bg_kill_command = taskkill /IM ping.exe /F
+        migrate.with_file_transfer:
+            guest_path = C:\tmpfile
+            rtl8139:
+                file_size = 10
         stress_boot:
             alive_test_cmd = systeminfo
         timedrift:
@@ -1398,7 +1759,6 @@
                 time_command = "echo TIME: %date% %time%"
                 time_filter_re = "(?<=TIME: \w\w\w ).{19}(?=\.\d\d)"
                 time_format = "%m/%d/%Y %H:%M:%S"
-
         guest_s4:
             check_s4_support_cmd = powercfg /hibernate on
             test_s4_cmd = start ping -t localhost
@@ -1420,11 +1780,13 @@
             find_pci_cmd = wmic diskdrive list brief
             pci_test_cmd = echo select disk 1 > dt && echo online >> dt && echo detail disk >> dt && echo exit >> dt && diskpart /s dt
         physical_resources_check:
-            catch_uuid_cmd = 
-
+            catch_uuid_cmd =
         file_transfer:
             tmp_dir = C:\
             clean_cmd = del
+        vmstop:
+            guest_path = C:\
+
         variants:
             - Win2000:
                 no reboot whql
@@ -1432,16 +1794,16 @@
                 kill_vm_gracefully = no
                 install:
                     steps = Win2000-32.steps
-                    cdrom_cd1 = windows/Windows2000_sp4.iso
-                    md5sum = dda6039f3a9173f0f6bfae40f5efdfea
-                    md5sum_1m = dd28fba196d366d56fe774bd93df5527
+                    cdrom_cd1 = isos/windows/Windows2000_sp4.iso
+                    md5sum_cd1 = dda6039f3a9173f0f6bfae40f5efdfea
+                    md5sum_1m_cd1 = dd28fba196d366d56fe774bd93df5527
                     user = user
                 setup:
                     steps = Win2000-32-rss.steps
                 unattended_install.cdrom:
-                    cdrom_cd1 = windows/Windows2000_sp4.iso
-                    md5sum = dda6039f3a9173f0f6bfae40f5efdfea
-                    md5sum_1m = dd28fba196d366d56fe774bd93df5527
+                    cdrom_cd1 = isos/windows/Windows2000_sp4.iso
+                    md5sum_cd1 = dda6039f3a9173f0f6bfae40f5efdfea
+                    md5sum_1m_cd1 = dd28fba196d366d56fe774bd93df5527
                     unattended_file = unattended/win2000-32.sif
                     floppy = images/win2000-32/answer.vfd
 
@@ -1451,17 +1813,17 @@
                     - 32:
                         image_name += -32
                         install:
-                            cdrom_cd1 = windows/WindowsXP-sp2-vlk.iso
-                            md5sum = 743450644b1d9fe97b3cf379e22dceb0
-                            md5sum_1m = b473bf75af2d1269fec8958cf0202bfd
+                            cdrom_cd1 = isos/windows/WindowsXP-sp2-vlk.iso
+                            md5sum_cd1 = 743450644b1d9fe97b3cf379e22dceb0
+                            md5sum_1m_cd1 = b473bf75af2d1269fec8958cf0202bfd
                             user = user
                             steps = WinXP-32.steps
                         setup:
                             steps = WinXP-32-rss.steps
-                        unattended_install.cdrom:
-                            cdrom_cd1 = windows/WindowsXP-sp2-vlk.iso
-                            md5sum = 743450644b1d9fe97b3cf379e22dceb0
-                            md5sum_1m = b473bf75af2d1269fec8958cf0202bfd
+                        unattended_install.cdrom|whql.support_vm_install:
+                            cdrom_cd1 = isos/windows/WindowsXP-sp2-vlk.iso
+                            md5sum_cd1 = 743450644b1d9fe97b3cf379e22dceb0
+                            md5sum_1m_cd1 = b473bf75af2d1269fec8958cf0202bfd
                             unattended_file = unattended/winxp32.sif
                             floppy = images/winXP-32/answer.vfd
                             # Uncomment virtio_network_installer_path line if
@@ -1470,28 +1832,31 @@
                             virtio_oemsetup_id = WXP32
                             virtio_network_path = 'F:\NetKVM\xp\x86'
                             #virtio_network_installer_path = 'F:\RHEV-Network32.msi'
-                        whql_submission:
+                        whql.submission:
                             desc_path_desc1 = $\WDK\Logo Type\Device Logo\Windows XP
                             desc_path_desc2 = $\WDK\Logo Type\Systems Logo\Windows XP
                             dd_data_logoarch = X86
                             dd_data_logoos = Windows XP
                             dd_data_whqlos = Windows XP
-                            dd_data_whqlqual = Basic
+                            device:
+                                dd_data_whqlqual = Basic
+                            device.net:
+                                image_name_supportvm = winXP-32-supportvm
 
                     - 64:
                         image_name += -64
                         install:
-                            cdrom_cd1 = windows/WindowsXP-64.iso
-                            md5sum = 8d3f007ec9c2060cec8a50ee7d7dc512
-                            md5sum_1m = e812363ff427effc512b7801ee70e513
+                            cdrom_cd1 = isos/windows/WindowsXP-64.iso
+                            md5sum_cd1 = 8d3f007ec9c2060cec8a50ee7d7dc512
+                            md5sum_1m_cd1 = e812363ff427effc512b7801ee70e513
                             user = user
                             steps = WinXP-64.steps
                         setup:
                             steps = WinXP-64-rss.steps
-                        unattended_install.cdrom:
-                            cdrom_cd1 = windows/WindowsXP-64.iso
-                            md5sum = 8d3f007ec9c2060cec8a50ee7d7dc512
-                            md5sum_1m = e812363ff427effc512b7801ee70e513
+                        unattended_install.cdrom|whql.support_vm_install:
+                            cdrom_cd1 = isos/windows/WindowsXP-64.iso
+                            md5sum_cd1 = 8d3f007ec9c2060cec8a50ee7d7dc512
+                            md5sum_1m_cd1 = e812363ff427effc512b7801ee70e513
                             unattended_file = unattended/winxp64.sif
                             floppy = images/winXP-64/answer.vfd
                             # Uncomment virtio_network_installer_path line if
@@ -1500,13 +1865,16 @@
                             virtio_oemsetup_id = WNET64
                             virtio_network_path = 'F:\NetKVM\xp\amd64'
                             #virtio_network_installer_path = 'F:\RHEV-Network64.msi'
-                        whql_submission:
+                        whql.submission:
                             desc_path_desc1 = $\WDK\Logo Type\Device Logo\Windows XP
                             desc_path_desc2 = $\WDK\Logo Type\Systems Logo\Windows XP
                             dd_data_logoarch = AMD64
                             dd_data_logoos = Windows XP 64-Bit Edition Version 2003
                             dd_data_whqlos = Windows XP x64
-                            dd_data_whqlqual = Basic
+                            device:
+                                dd_data_whqlqual = Basic
+                            device.net:
+                                image_name_supportvm = winXP-64-supportvm
 
             - Win2003:
                 image_name = win2003
@@ -1516,17 +1884,17 @@
                     - 32:
                         image_name += -32
                         install:
-                            cdrom_cd1 = windows/Windows2003_r2_VLK.iso
-                            md5sum = 03e921e9b4214773c21a39f5c3f42ef7
-                            md5sum_1m = 37c2fdec15ac4ec16aa10fdfdb338aa3
+                            cdrom_cd1 = isos/windows/Windows2003_r2_VLK.iso
+                            md5sum_cd1 = 03e921e9b4214773c21a39f5c3f42ef7
+                            md5sum_1m_cd1 = 37c2fdec15ac4ec16aa10fdfdb338aa3
                             user = user
                             steps = Win2003-32.steps
                         setup:
                             steps = Win2003-32-rss.steps
-                        unattended_install.cdrom:
-                            cdrom_cd1 = windows/Windows2003_r2_VLK.iso
-                            md5sum = 03e921e9b4214773c21a39f5c3f42ef7
-                            md5sum_1m = 37c2fdec15ac4ec16aa10fdfdb338aa3
+                        unattended_install.cdrom|whql.support_vm_install:
+                            cdrom_cd1 = isos/windows/Windows2003_r2_VLK.iso
+                            md5sum_cd1 = 03e921e9b4214773c21a39f5c3f42ef7
+                            md5sum_1m_cd1 = 37c2fdec15ac4ec16aa10fdfdb338aa3
                             unattended_file = unattended/win2003-32.sif
                             floppy = images/win2003-32/answer.vfd
                             # Uncomment virtio_network_installer_path line if
@@ -1535,27 +1903,30 @@
                             virtio_oemsetup_id = WNET32
                             virtio_network_path = 'F:\NetKVM\2k3\x86'
                             #virtio_network_installer_path = 'F:\RHEV-Network32.msi'
-                        whql_submission:
+                        whql.submission:
                             desc_path_desc1 = $\WDK\Logo Type\Device Logo\Windows Server 2003
                             dd_data_logoarch = X86
                             dd_data_logoos = Windows Server 2003
                             dd_data_whqlos = Windows Server 2003
-                            dd_data_whqlqual = Basic
+                            device:
+                                dd_data_whqlqual = Basic
+                            device.net:
+                                image_name_supportvm = win2003-32-supportvm
 
                     - 64:
                         image_name += -64
                         install:
-                            cdrom_cd1 = windows/Windows2003-x64.iso
-                            md5sum = 5703f87c9fd77d28c05ffadd3354dbbd
-                            md5sum_1m = 439393c384116aa09e08a0ad047dcea8
+                            cdrom_cd1 = isos/windows/Windows2003-x64.iso
+                            md5sum_cd1 = 5703f87c9fd77d28c05ffadd3354dbbd
+                            md5sum_1m_cd1 = 439393c384116aa09e08a0ad047dcea8
                             user = user
                             steps = Win2003-64.steps
                         setup:
                             steps = Win2003-64-rss.steps
-                        unattended_install.cdrom:
-                            cdrom_cd1 = windows/Windows2003-x64.iso
-                            md5sum = 5703f87c9fd77d28c05ffadd3354dbbd
-                            md5sum_1m = 439393c384116aa09e08a0ad047dcea8
+                        unattended_install.cdrom|whql.support_vm_install:
+                            cdrom_cd1 = isos/windows/Windows2003-x64.iso
+                            md5sum_cd1 = 5703f87c9fd77d28c05ffadd3354dbbd
+                            md5sum_1m_cd1 = 439393c384116aa09e08a0ad047dcea8
                             unattended_file = unattended/win2003-64.sif
                             floppy = images/win2003-64/answer.vfd
                             # Uncomment virtio_network_installer_path line if
@@ -1564,43 +1935,46 @@
                             virtio_oemsetup_id = WNET64
                             virtio_network_path = 'F:\NetKVM\2k3\amd64'
                             #virtio_network_installer_path = 'F:\RHEV-Network64.msi'
-
-                        whql_submission:
+                        whql.submission:
                             desc_path_desc1 = $\WDK\Logo Type\Device Logo\Windows Server 2003
                             dd_data_logoarch = AMD64
                             dd_data_logoos = Windows Server 2003
                             dd_data_whqlos = Windows Server 2003 x64
-                            dd_data_whqlqual = Basic
+                            device:
+                                dd_data_whqlqual = Basic
+                            device.net:
+                                image_name_supportvm = win2003-64-supportvm
 
             - WinVista:
                 image_name = winvista
                 image_size = 20G
-                whql_submission:
+                whql.submission:
                     desc_path_desc1 = $\WDK\Logo Type\Device Logo\Vista Client\Device Premium
                     desc_path_desc2 = $\WDK\Logo Type\Device Logo\Vista Client\Device Standard
                     desc_path_desc3 = $\WDK\Logo Type\Device Logo\Vista Client
 
                 variants:
                     - 32:
-                        whql_submission:
+                        whql.submission:
                             dd_data_logoarch = X86
                             dd_data_logoos = Windows Vista
                             dd_data_whqlos = Windows Vista Client
-                            dd_data_whqlqual = Premium
+                            device:
+                                dd_data_whqlqual = Premium
                         variants:
                             - sp1:
                                 image_name += -sp1-32
                                 install:
-                                    cdrom_cd1 = windows/WindowsVista-32.iso
-                                    md5sum = 1008f323d5170c8e614e52ccb85c0491
-                                    md5sum_1m = c724e9695da483bc0fd59e426eaefc72
+                                    cdrom_cd1 = isos/windows/WindowsVista-32.iso
+                                    md5sum_cd1 = 1008f323d5170c8e614e52ccb85c0491
+                                    md5sum_1m_cd1 = c724e9695da483bc0fd59e426eaefc72
                                     steps = Win-Vista-32.steps
                                 setup:
                                     steps = WinVista-32-rss.steps
-                                unattended_install.cdrom:
-                                    cdrom_cd1 = windows/WindowsVista-32.iso
-                                    md5sum = 1008f323d5170c8e614e52ccb85c0491
-                                    md5sum_1m = c724e9695da483bc0fd59e426eaefc72
+                                unattended_install.cdrom|whql.support_vm_install:
+                                    cdrom_cd1 = isos/windows/WindowsVista-32.iso
+                                    md5sum_cd1 = 1008f323d5170c8e614e52ccb85c0491
+                                    md5sum_1m_cd1 = c724e9695da483bc0fd59e426eaefc72
                                     unattended_file = unattended/winvista-32-autounattend.xml
                                     floppy = images/winvista-sp1-32/answer.vfd
                                     # Uncomment virtio_network_installer_path line if
@@ -1609,15 +1983,17 @@
                                     virtio_storage_path = 'F:\viostor\w7\x86'
                                     virtio_network_path = 'F:\NetKVM\w7\x86'
                                     #virtio_network_installer_path = 'F:\RHEV-Network32.msi'
+                                whql.submission.device.net:
+                                    image_name_supportvm = winvista-sp1-32-supportvm
 
                             - sp2:
                                 image_name += -sp2-32
-                                unattended_install.cdrom:
-                                    cdrom_cd1 = windows/en_windows_vista_with_sp2_x86_dvd_342266.iso
-                                    md5sum = 19ca90a425667812977bab6f4ce24175
-                                    md5sum_1m = 89c15020e0e6125be19acf7a2e5dc614
-                                    sha1sum = 25ad9a776503e6a583bec07879dbcc5dfd20cd6e
-                                    sha1sum_1m = a2afa4cffdc1c362dbf9e62942337f4f875a22cf
+                                unattended_install.cdrom|whql.support_vm_install:
+                                    cdrom_cd1 = isos/windows/en_windows_vista_with_sp2_x86_dvd_342266.iso
+                                    md5sum_cd1 = 19ca90a425667812977bab6f4ce24175
+                                    md5sum_1m_cd1 = 89c15020e0e6125be19acf7a2e5dc614
+                                    sha1sum_cd1 = 25ad9a776503e6a583bec07879dbcc5dfd20cd6e
+                                    sha1sum_1m_cd1 = a2afa4cffdc1c362dbf9e62942337f4f875a22cf
                                     unattended_file = unattended/winvista-32-autounattend.xml
                                     floppy = images/winvista-sp2-32/answer.vfd
                                     # Uncomment virtio_network_installer_path line if
@@ -1626,27 +2002,30 @@
                                     virtio_storage_path = 'F:\viostor\w7\x86'
                                     virtio_network_path = 'F:\NetKVM\w7\x86'
                                     #virtio_network_installer_path = 'F:\RHEV-Network32.msi'
+                                whql.submission.device.net:
+                                    image_name_supportvm = winvista-sp2-32-supportvm
 
                     - 64:
-                        whql_submission:
+                        whql.submission:
                             dd_data_logoarch = AMD64
                             dd_data_logoos = Windows Vista
                             dd_data_whqlos = Windows Vista Client x64
-                            dd_data_whqlqual = Premium
+                            device:
+                                dd_data_whqlqual = Premium
                         variants:
                             - sp1:
                                 image_name += -sp1-64
                                 install:
-                                    cdrom_cd1 = windows/WindowsVista-64.iso
-                                    md5sum = 11e2010d857fffc47813295e6be6d58d
-                                    md5sum_1m = 0947bcd5390546139e25f25217d6f165
+                                    cdrom_cd1 = isos/windows/WindowsVista-64.iso
+                                    md5sum_cd1 = 11e2010d857fffc47813295e6be6d58d
+                                    md5sum_1m_cd1 = 0947bcd5390546139e25f25217d6f165
                                     steps = Win-Vista-64.steps
                                 setup:
                                     steps = WinVista-64-rss.steps
-                                unattended_install.cdrom:
-                                    cdrom_cd1 = windows/WindowsVista-64.iso
-                                    md5sum = 11e2010d857fffc47813295e6be6d58d
-                                    md5sum_1m = 0947bcd5390546139e25f25217d6f165
+                                unattended_install.cdrom|whql.support_vm_install:
+                                    cdrom_cd1 = isos/windows/WindowsVista-64.iso
+                                    md5sum_cd1 = 11e2010d857fffc47813295e6be6d58d
+                                    md5sum_1m_cd1 = 0947bcd5390546139e25f25217d6f165
                                     unattended_file = unattended/winvista-64-autounattend.xml
                                     floppy = images/winvista-sp1-64/answer.vfd
                                     # Uncomment virtio_network_installer_path line if
@@ -1655,14 +2034,17 @@
                                     virtio_storage_path = 'F:\viostor\w7\amd64'
                                     virtio_network_path = 'F:\NetKVM\w7\amd64'
                                     #virtio_network_installer_path = 'F:\RHEV-Network64.msi'
+                                whql.submission.device.net:
+                                    image_name_supportvm = winvista-sp1-64-supportvm
+
                             - sp2:
                                 image_name += -sp2-64
-                                unattended_install.cdrom:
-                                    cdrom_cd1 = windows/en_windows_vista_sp2_x64_dvd_342267.iso
-                                    md5sum = a1c024d7abaf34bac3368e88efbc2574
-                                    md5sum_1m = 3d84911a80f3df71d1026f7adedc2181
-                                    sha1sum = aaee3c04533899f9f8c4ae0c4250ef5fafbe29a3
-                                    sha1sum_1m = 1fd21bd3ce2a4de8856c7b8fe6fdf80260f6d1c7
+                                unattended_install.cdrom|whql.support_vm_install:
+                                    cdrom_cd1 = isos/windows/en_windows_vista_sp2_x64_dvd_342267.iso
+                                    md5sum_cd1 = a1c024d7abaf34bac3368e88efbc2574
+                                    md5sum_1m_cd1 = 3d84911a80f3df71d1026f7adedc2181
+                                    sha1sum_cd1 = aaee3c04533899f9f8c4ae0c4250ef5fafbe29a3
+                                    sha1sum_1m_cd1 = 1fd21bd3ce2a4de8856c7b8fe6fdf80260f6d1c7
                                     unattended_file = unattended/winvista-64-autounattend.xml
                                     floppy = images/winvista-sp2-64/answer.vfd
                                     # Uncomment virtio_network_installer_path line if
@@ -1671,6 +2053,8 @@
                                     virtio_storage_path = 'F:\viostor\w7\amd64'
                                     virtio_network_path = 'F:\NetKVM\w7\amd64'
                                     #virtio_network_installer_path = 'F:\RHEV-Network64.msi'
+                                whql.submission.device.net:
+                                    image_name_supportvm = winvista-sp2-64-supportvm
 
             - Win2008:
                 no whql
@@ -1683,16 +2067,16 @@
                             - sp1:
                                 image_name += -sp1-32
                                 install:
-                                    cdrom_cd1 = windows/Windows2008-x86.iso
+                                    cdrom_cd1 = isos/windows/Windows2008-x86.iso
                                     #en_windows_server_2008_datacenter_enterprise_standard_x86_dvd_X14-26710.iso
                                     md5sum=0bfca49f0164de0a8eba236ced47007d
                                     md5sum_1m=07d7f5006393f74dc76e6e2e943e2440
-                                    sha1sum = 6ca018ff96f1e9b2b310a36546b6fded99a421e6
+                                    sha1sum_cd1 = 6ca018ff96f1e9b2b310a36546b6fded99a421e6
                                     steps = Win2008-32.steps
                                 setup:
                                     steps = Win2008-32-rss.steps
-                                unattended_install.cdrom:
-                                    cdrom_cd1 = windows/Windows2008-x86.iso
+                                unattended_install.cdrom|whql.support_vm_install:
+                                    cdrom_cd1 = isos/windows/Windows2008-x86.iso
                                     md5sum=0bfca49f0164de0a8eba236ced47007d
                                     md5sum_1m=07d7f5006393f74dc76e6e2e943e2440
                                     unattended_file = unattended/win2008-32-autounattend.xml
@@ -1706,12 +2090,12 @@
 
                             - sp2:
                                 image_name += -sp2-32
-                                unattended_install.cdrom:
-                                    cdrom_cd1 = windows/en_windows_server_2008_datacenter_enterprise_standard_sp2_x86_dvd_342333.iso
-                                    md5sum = b9201aeb6eef04a3c573d036a8780bdf
-                                    md5sum_1m = b7a9d42e55ea1e85105a3a6ad4da8e04
-                                    sha1sum = 49d0d6917c1256fe81048d414fa473bbc76a8724
-                                    sha1sum_1m = 9662ff7ed715faa00407e4befc484ea52a92a9fb
+                                unattended_install.cdrom|whql.support_vm_install:
+                                    cdrom_cd1 = isos/windows/en_windows_server_2008_datacenter_enterprise_standard_sp2_x86_dvd_342333.iso
+                                    md5sum_cd1 = b9201aeb6eef04a3c573d036a8780bdf
+                                    md5sum_1m_cd1 = b7a9d42e55ea1e85105a3a6ad4da8e04
+                                    sha1sum_cd1 = 49d0d6917c1256fe81048d414fa473bbc76a8724
+                                    sha1sum_1m_cd1 = 9662ff7ed715faa00407e4befc484ea52a92a9fb
                                     unattended_file = unattended/win2008-32-autounattend.xml
                                     floppy = images/win2008-sp2-32/answer.vfd
                                     # Uncomment virtio_network_installer_path line if
@@ -1727,16 +2111,16 @@
                                 image_name += -sp1-64
                                 install:
                                     steps = Win2008-64.steps
-                                    cdrom_cd1 = windows/Windows2008-x64.iso
+                                    cdrom_cd1 = isos/windows/Windows2008-x64.iso
                                     #en_windows_server_2008_datacenter_enterprise_standard_x64_dvd_X14-26714.iso
                                     md5sum=27c58cdb3d620f28c36333a5552f271c
                                     md5sum_1m=efdcc11d485a1ef9afa739cb8e0ca766
-                                    sha1sum = bd000374709f67e9358814db6ec8f0ddaaa16f70
+                                    sha1sum_cd1 = bd000374709f67e9358814db6ec8f0ddaaa16f70
                                     passwd = 1q2w3eP
                                 setup:
                                     steps = Win2008-64-rss.steps
-                                unattended_install.cdrom:
-                                    cdrom_cd1 = windows/Windows2008-x64.iso
+                                unattended_install.cdrom|whql.support_vm_install:
+                                    cdrom_cd1 = isos/windows/Windows2008-x64.iso
                                     md5sum=27c58cdb3d620f28c36333a5552f271c
                                     md5sum_1m=efdcc11d485a1ef9afa739cb8e0ca766
                                     unattended_file = unattended/win2008-64-autounattend.xml
@@ -1750,12 +2134,12 @@
 
                             - sp2:
                                 image_name += -sp2-64
-                                unattended_install.cdrom:
-                                    cdrom_cd1 = windows/en_windows_server_2008_datacenter_enterprise_standard_sp2_x64_dvd_342336.iso
-                                    md5sum = e94943ef484035b3288d8db69599a6b5
-                                    md5sum_1m = ee55506823d0efffb5532ddd88a8e47b
-                                    sha1sum = 34c7d726c57b0f8b19ba3b40d1b4044c15fc2029
-                                    sha1sum_1m = 8fe08b03e3531906855a60a78020ac9577dff5ba
+                                unattended_install.cdrom|whql.support_vm_install:
+                                    cdrom_cd1 = isos/windows/en_windows_server_2008_datacenter_enterprise_standard_sp2_x64_dvd_342336.iso
+                                    md5sum_cd1 = e94943ef484035b3288d8db69599a6b5
+                                    md5sum_1m_cd1 = ee55506823d0efffb5532ddd88a8e47b
+                                    sha1sum_cd1 = 34c7d726c57b0f8b19ba3b40d1b4044c15fc2029
+                                    sha1sum_1m_cd1 = 8fe08b03e3531906855a60a78020ac9577dff5ba
                                     unattended_file = unattended/win2008-64-autounattend.xml
                                     floppy = images/win2008-sp2-64/answer.vfd
                                     # Uncomment virtio_network_installer_path line if
@@ -1767,12 +2151,12 @@
 
                             - r2:
                                 image_name += -r2-64
-                                unattended_install.cdrom:
-                                    cdrom_cd1 = windows/en_windows_server_2008_r2_standard_enterprise_datacenter_and_web_x64_dvd_x15-59754.iso
-                                    md5sum = 0207ef392c60efdda92071b0559ca0f9
-                                    md5sum_1m = a5a22ce25008bd7109f6d830d627e3ed
-                                    sha1sum = ad855ea913aaec3f1d0e1833c1aef7a0de326b0a
-                                    sha1sum_1m = 9194a3aabae25b36e5f73cad001314b2c8d07d14
+                                unattended_install.cdrom|whql.support_vm_install:
+                                    cdrom_cd1 = isos/windows/en_windows_server_2008_r2_standard_enterprise_datacenter_and_web_x64_dvd_x15-59754.iso
+                                    md5sum_cd1 = 0207ef392c60efdda92071b0559ca0f9
+                                    md5sum_1m_cd1 = a5a22ce25008bd7109f6d830d627e3ed
+                                    sha1sum_cd1 = ad855ea913aaec3f1d0e1833c1aef7a0de326b0a
+                                    sha1sum_1m_cd1 = 9194a3aabae25b36e5f73cad001314b2c8d07d14
                                     unattended_file = unattended/win2008-r2-autounattend.xml
                                     floppy = images/win2008-r2-64/answer.vfd
                                     # Uncomment virtio_network_installer_path line if
@@ -1785,7 +2169,7 @@
             - Win7:
                 image_name = win7
                 image_size = 20G
-                whql_submission:
+                whql.submission:
                     desc_path_desc1 = $\WDK\Logo Type\Device Logo\Windows 7 Client\Logo
                     desc_path_desc2 = $\WDK\Logo Type\Device Logo\Windows 7 Client
                     device_data += " adq"
@@ -1795,12 +2179,12 @@
                 variants:
                     - 32:
                         image_name += -32
-                        unattended_install.cdrom:
-                            cdrom_cd1 = windows/en_windows_7_ultimate_x86_dvd_x15-65921.iso
-                            md5sum = d0b8b407e8a3d4b75ee9c10147266b89
-                            md5sum_1m = 2b0c2c22b1ae95065db08686bf83af93
-                            sha1sum = 5395dc4b38f7bdb1e005ff414deedfdb16dbf610
-                            sha1sum_1m = 9f9c3780aebeb28a9bf22188eed6bc15475dc9c5
+                        unattended_install.cdrom|whql.support_vm_install:
+                            cdrom_cd1 = isos/windows/en_windows_7_ultimate_x86_dvd_x15-65921.iso
+                            md5sum_cd1 = d0b8b407e8a3d4b75ee9c10147266b89
+                            md5sum_1m_cd1 = 2b0c2c22b1ae95065db08686bf83af93
+                            sha1sum_cd1 = 5395dc4b38f7bdb1e005ff414deedfdb16dbf610
+                            sha1sum_1m_cd1 = 9f9c3780aebeb28a9bf22188eed6bc15475dc9c5
                             unattended_file = unattended/win7-32-autounattend.xml
                             floppy = images/win7-32/answer.vfd
                             # Uncomment virtio_network_installer_path line if
@@ -1809,28 +2193,31 @@
                             virtio_storage_path = 'F:\viostor\w7\x86'
                             virtio_network_path = 'F:\NetKVM\w7\x86'
                             #virtio_network_installer_path = 'F:\RHEV-Network32.msi'
-                        whql_submission:
+                        whql.submission:
                             dd_data_logoarch = X86
                             dd_data_logoos = Windows 7
                             dd_data_whqlos = Windows 7 Client
-                            dd_data_whqlqual = Logo
+                            device:
+                                dd_data_whqlqual = Logo
+                            device.net:
+                                image_name_supportvm = win7-32-supportvm
 
                     - 64:
                         image_name += -64
                         install:
-                            cdrom_cd1 = windows/en_windows_7_ultimate_x64_dvd_x15-65922.iso
+                            cdrom_cd1 = isos/windows/en_windows_7_ultimate_x64_dvd_x15-65922.iso
                             md5sum=f43d22e4fb07bf617d573acd8785c028
                             md5sum_1m=b44d8cf99dbed2a5cb02765db8dfd48f
                             passwd = 1q2w3eP
                             steps = Win7-64.steps
                         setup:
                             steps = Win7-64-rss.steps
-                        unattended_install.cdrom:
-                            cdrom_cd1 = windows/en_windows_7_ultimate_x64_dvd_x15-65922.iso
-                            md5sum = f43d22e4fb07bf617d573acd8785c028
-                            md5sum_1m = b44d8cf99dbed2a5cb02765db8dfd48f
-                            sha1sum = 326327cc2ff9f05379f5058c41be6bc5e004baa7
-                            sha1sum_1m = 4a3903bd5157de54f0702e5263e0a683c5775515
+                        unattended_install.cdrom|whql.support_vm_install:
+                            cdrom_cd1 = isos/windows/en_windows_7_ultimate_x64_dvd_x15-65922.iso
+                            md5sum_cd1 = f43d22e4fb07bf617d573acd8785c028
+                            md5sum_1m_cd1 = b44d8cf99dbed2a5cb02765db8dfd48f
+                            sha1sum_cd1 = 326327cc2ff9f05379f5058c41be6bc5e004baa7
+                            sha1sum_1m_cd1 = 4a3903bd5157de54f0702e5263e0a683c5775515
                             unattended_file = unattended/win7-64-autounattend.xml
                             floppy = images/win7-64/answer.vfd
                             # Uncomment virtio_network_installer_path line if
@@ -1839,11 +2226,14 @@
                             virtio_storage_path = 'F:\viostor\w7\amd64'
                             virtio_network_path = 'F:\NetKVM\w7\amd64'
                             #virtio_network_installer_path = 'F:\RHEV-Network64.msi'
-                        whql_submission:
+                        whql.submission:
                             dd_data_logoarch = AMD64
                             dd_data_logoos = Windows 7
                             dd_data_whqlos = Windows 7 Client x64
-                            dd_data_whqlqual = Logo
+                            device:
+                                dd_data_whqlqual = Logo
+                            device.net:
+                                image_name_supportvm = win7-64-supportvm
 
 
     # Unix/BSD section
@@ -1858,16 +2248,16 @@
                 image_name = NetBSD-1.6.2
                 image_size = 4G
                 steps = NetBSD-1.6.2.steps
-                cdrom_cd1 = bsd/netbsd-1.6.2-i386.iso
+                cdrom_cd1 = isos/bsd/netbsd-1.6.2-i386.iso
                 md5sum=72eb680300f77d529bfbc880ba8208f3
                 md5sum_1m=f1a9e1e825c90adfb1be35c6177bd9ac
 
             - OpenBSD-4.1:
                 image_name = OpenBSD-4.1
                 steps = OpenBSD-4.1-32.steps
-                cdrom_cd1 = unix/openbsd41-i386-07-05-06.iso
-                md5sum = 984790db10ebdd6fc7a9cf97abc7c967
-                md5sum_1m = 8fc234b4b0ecfe56843a32ac1d26ed55
+                cdrom_cd1 = isos/unix/openbsd41-i386-07-05-06.iso
+                md5sum_cd1 = 984790db10ebdd6fc7a9cf97abc7c967
+                md5sum_1m_cd1 = 8fc234b4b0ecfe56843a32ac1d26ed55
 
     # Live CD section
     - @livecd:
@@ -1879,27 +2269,31 @@
         variants:
             - Belenix:
                 steps = Belenix-0.7.1.steps
-                cdrom_cd1 = unix/belenix_0.7.1.iso
-                md5sum = 29cea6160cf5250de138e2820e53e342
-                md5sum_1m = 427bbef1b85d6d051799b825d686ae94
+                cdrom_cd1 = isos/unix/belenix_0.7.1.iso
+                md5sum_cd1 = 29cea6160cf5250de138e2820e53e342
+                md5sum_1m_cd1 = 427bbef1b85d6d051799b825d686ae94
 
             - Slax:
                 steps = Slax-6.0.7.steps
-                cdrom_cd1 = linux/slax-6.0.7.iso
-                md5sum = cde0ecba3c8289d786e12c44666ded6e
-                md5sum_1m = ddf02bc7444f22d1160a6e5a8fc8723f
+                cdrom_cd1 = isos/linux/slax-6.0.7.iso
+                md5sum_cd1 = cde0ecba3c8289d786e12c44666ded6e
+                md5sum_1m_cd1 = ddf02bc7444f22d1160a6e5a8fc8723f
 
             - FreeSBIE-2.0.1:
                 steps = FreeSBIE-2.0.1.steps
-                cdrom_cd1 = unix/FreeSBIE-2.0.1-RELEASE.iso
-                md5sum = b2f680d27c21bbfaf4fb90dce090a118
-                md5sum_1m = 4d81ee7fe0101b0a14225963bfff60c1
+                cdrom_cd1 = isos/unix/FreeSBIE-2.0.1-RELEASE.iso
+                md5sum_cd1 = b2f680d27c21bbfaf4fb90dce090a118
+                md5sum_1m_cd1 = 4d81ee7fe0101b0a14225963bfff60c1
 
             - memtest:
                 mem = 128
                 steps = memtest86+.steps
-                cdrom_cd1 = misc/memtest86+-2.01.iso
-                md5sum = 9fae22f2666369968a76ef59e9a81ced
+                cdrom_cd1 = isos/misc/memtest86+-2.01.iso
+                md5sum_cd1 = 9fae22f2666369968a76ef59e9a81ced
+
+
+whql.support_vm_install|whql.client_install.support_vm:
+    image_name += -supportvm
 
 
 variants:
@@ -1923,9 +2317,11 @@
 
 
 virtio_net|virtio_blk|e1000|balloon_check:
-    only Fedora.11 Fedora.12 Fedora.13 RHEL.5 OpenSUSE.11 SLES.11 Ubuntu-8.10-server
-    # only WinXP Win2003 Win2008 WinVista Win7 Fedora.11 Fedora.12 Fedora.13 RHEL.5 OpenSUSE.11 SLES.11 Ubuntu-8.10-server
+    only Fedora.11 Fedora.12 Fedora.13 Fedora.14 RHEL.5 RHEL.6 OpenSUSE.11 SLES.11 Ubuntu-8.10-server
+    # only WinXP Win2003 Win2008 WinVista Win7 Fedora.11 Fedora.12 Fedora.13 Fedora.14 RHEL.5 RHEL.6 OpenSUSE.11 SLES.11 Ubuntu-8.10-server
 
+kdump:
+    only RHEL.5 RHEL.6
 
 variants:
     - @qcow2:
diff --git a/client/tests/kvm/unattended/Fedora-13.ks b/client/tests/kvm/unattended/Fedora-13.ks
index 0949e40..861546b 100644
--- a/client/tests/kvm/unattended/Fedora-13.ks
+++ b/client/tests/kvm/unattended/Fedora-13.ks
@@ -1,5 +1,5 @@
 install
-cdrom
+KVM_TEST_MEDIUM
 text
 reboot
 lang en_US
diff --git a/client/tests/kvm/unattended/Fedora-14.ks b/client/tests/kvm/unattended/Fedora-14.ks
new file mode 100644
index 0000000..9b99432
--- /dev/null
+++ b/client/tests/kvm/unattended/Fedora-14.ks
@@ -0,0 +1,37 @@
+install
+KVM_TEST_MEDIUM
+text
+reboot
+lang en_US
+keyboard us
+network --bootproto dhcp
+rootpw 123456
+firewall --enabled --ssh
+selinux --enforcing
+timezone --utc America/New_York
+firstboot --disable
+bootloader --location=mbr --append="rd_NO_PLYMOUTH console=tty0 console=ttyS0,115200"
+zerombr
+
+clearpart --all --initlabel
+autopart
+
+%packages
+@base
+@development-libs
+@development-tools
+%end
+
+%post --interpreter /usr/bin/python
+import socket, os
+os.system('dhclient')
+os.system('chkconfig sshd on')
+os.system('iptables -F')
+os.system('echo 0 > /selinux/enforce')
+server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+server.bind(('', 12323))
+server.listen(1)
+(client, addr) = server.accept()
+client.send("done")
+client.close()
+%end
diff --git a/client/tests/kvm/unattended/RHEL-5-series.ks b/client/tests/kvm/unattended/RHEL-5-series.ks
index 92ff727..3ee84f1 100644
--- a/client/tests/kvm/unattended/RHEL-5-series.ks
+++ b/client/tests/kvm/unattended/RHEL-5-series.ks
@@ -21,6 +21,7 @@
 @base
 @development-libs
 @development-tools
+kexec-tools
 
 %post --interpreter /usr/bin/python
 import socket, os
diff --git a/client/tests/kvm/unattended/RHEL-6-series.ks b/client/tests/kvm/unattended/RHEL-6-series.ks
new file mode 100644
index 0000000..16cd493
--- /dev/null
+++ b/client/tests/kvm/unattended/RHEL-6-series.ks
@@ -0,0 +1,40 @@
+install
+KVM_TEST_MEDIUM
+text
+reboot
+lang en_US.UTF-8
+keyboard us
+key --skip
+network --bootproto dhcp
+rootpw 123456
+firewall --enabled --ssh
+selinux --enforcing
+timezone --utc America/New_York
+firstboot --disable
+bootloader --location=mbr --append="console=tty0 console=ttyS0,115200"
+zerombr
+clearpart --all --initlabel
+autopart
+reboot
+
+%packages
+@base
+@core
+@development
+@additional-devel
+@debugging-tools
+@network-tools
+NetworkManager
+
+%post --interpreter /usr/bin/python
+import socket, os
+os.system('dhclient')
+os.system('chkconfig sshd on')
+os.system('iptables -F')
+os.system('echo 0 > /selinux/enforce')
+server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+server.bind(('', 12323))
+server.listen(1)
+(client, addr) = server.accept()
+client.send("done")
+client.close()
diff --git a/client/tests/kvm/unattended/win2003-32.sif b/client/tests/kvm/unattended/win2003-32.sif
index fab2cf5..207cd2b 100644
--- a/client/tests/kvm/unattended/win2003-32.sif
+++ b/client/tests/kvm/unattended/win2003-32.sif
@@ -63,4 +63,4 @@
     Command3="cmd /c net start telnet"
     Command4="cmd /c E:\setuprss.bat"
     Command5="cmd /c netsh interface ip set address local dhcp"
-    Command6="cmd /c ping 10.0.2.2 -n 20 && A:\finish.exe"
+    Command6="cmd /c A:\finish.exe"
diff --git a/client/tests/kvm/unattended/win2003-64.sif b/client/tests/kvm/unattended/win2003-64.sif
index fab2cf5..207cd2b 100644
--- a/client/tests/kvm/unattended/win2003-64.sif
+++ b/client/tests/kvm/unattended/win2003-64.sif
@@ -63,4 +63,4 @@
     Command3="cmd /c net start telnet"
     Command4="cmd /c E:\setuprss.bat"
     Command5="cmd /c netsh interface ip set address local dhcp"
-    Command6="cmd /c ping 10.0.2.2 -n 20 && A:\finish.exe"
+    Command6="cmd /c A:\finish.exe"
diff --git a/client/tests/kvm/unattended/win2008-32-autounattend.xml b/client/tests/kvm/unattended/win2008-32-autounattend.xml
index 352cb73..e33a36b 100644
--- a/client/tests/kvm/unattended/win2008-32-autounattend.xml
+++ b/client/tests/kvm/unattended/win2008-32-autounattend.xml
@@ -147,20 +147,6 @@
 				<SynchronousCommand wcm:action="add">
 					<CommandLine>%WINDIR%\System32\cmd /c net start telnet</CommandLine>
 					<Order>5</Order>
-<<<<<<< HEAD
-				</SynchronousCommand>
-				<SynchronousCommand wcm:action="add">
-					<CommandLine>%WINDIR%\System32\cmd /c E:\setuprss.bat</CommandLine>
-					<Order>6</Order>
-				</SynchronousCommand>
-				<SynchronousCommand wcm:action="add">
-					<CommandLine>%WINDIR%\System32\cmd /c netsh interface ip set address "Local Area Connection" dhcp</CommandLine>
-					<Order>7</Order>
-				</SynchronousCommand>
-				<SynchronousCommand wcm:action="add">
-					<CommandLine>%WINDIR%\System32\cmd /c ping 10.0.2.2 -n 20 &#38;&#38; A:\finish.exe</CommandLine>
-					<Order>8</Order>
-=======
 				</SynchronousCommand>
 				<SynchronousCommand wcm:action="add">
 					<CommandLine>%WINDIR%\System32\cmd /c bcdedit /set {current} bootstatuspolicy ignoreallfailures</CommandLine>
@@ -175,9 +161,8 @@
 					<Order>8</Order>
 				</SynchronousCommand>
 				<SynchronousCommand wcm:action="add">
-					<CommandLine>%WINDIR%\System32\cmd /c ping 10.0.2.2 -n 20 &#38;&#38; A:\finish.exe</CommandLine>
+					<CommandLine>%WINDIR%\System32\cmd /c A:\finish.exe</CommandLine>
 					<Order>9</Order>
->>>>>>> cros/upstream
 				</SynchronousCommand>
 			</FirstLogonCommands>
 			<OOBE>
diff --git a/client/tests/kvm/unattended/win2008-64-autounattend.xml b/client/tests/kvm/unattended/win2008-64-autounattend.xml
index fce6582..4ee064e 100644
--- a/client/tests/kvm/unattended/win2008-64-autounattend.xml
+++ b/client/tests/kvm/unattended/win2008-64-autounattend.xml
@@ -170,7 +170,7 @@
 					<Order>8</Order>
 				</SynchronousCommand>
 				<SynchronousCommand wcm:action="add">
-					<CommandLine>%WINDIR%\System32\cmd /c ping 10.0.2.2 -n 20 &#38;&#38; A:\finish.exe</CommandLine>
+					<CommandLine>%WINDIR%\System32\cmd /c ping A:\finish.exe</CommandLine>
 					<Order>9</Order>
 				</SynchronousCommand>
 			</FirstLogonCommands>
diff --git a/client/tests/kvm/unattended/win2008-r2-autounattend.xml b/client/tests/kvm/unattended/win2008-r2-autounattend.xml
index 7e9ab23..fce6582 100644
--- a/client/tests/kvm/unattended/win2008-r2-autounattend.xml
+++ b/client/tests/kvm/unattended/win2008-r2-autounattend.xml
@@ -114,20 +114,6 @@
 			<UILanguage>en-US</UILanguage>
 			<UserLocale>en-US</UserLocale>
 		</component>
-		<component name="Microsoft-Windows-PnpCustomizationsWinPE"
-			processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35"
-			language="neutral" versionScope="nonSxS"
-			xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
-			xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
-			<DriverPaths>
-				<PathAndCredentials wcm:keyValue="1" wcm:action="add">
-					<Path>KVM_TEST_STORAGE_DRIVER_PATH</Path>
-				</PathAndCredentials>
-				<PathAndCredentials wcm:keyValue="2" wcm:action="add">
-					<Path>KVM_TEST_NETWORK_DRIVER_PATH</Path>
-				</PathAndCredentials>
-			</DriverPaths>
-		</component>
 	</settings>
 	<settings pass="oobeSystem">
 		<component name="Microsoft-Windows-Shell-Setup"
diff --git a/client/tests/kvm/unattended/win7-32-autounattend.xml b/client/tests/kvm/unattended/win7-32-autounattend.xml
index 6904db1..f313f4a 100644
--- a/client/tests/kvm/unattended/win7-32-autounattend.xml
+++ b/client/tests/kvm/unattended/win7-32-autounattend.xml
@@ -156,19 +156,6 @@
 					<Order>5</Order>
 				</SynchronousCommand>
 				<SynchronousCommand wcm:action="add">
-<<<<<<< HEAD
-					<CommandLine>%WINDIR%\System32\cmd /c E:\setuprss.bat</CommandLine>
-					<Order>6</Order>
-				</SynchronousCommand>
-				<SynchronousCommand wcm:action="add">
-					<CommandLine>%WINDIR%\System32\cmd /c netsh interface ip set address "Local Area Connection" dhcp</CommandLine>
-					<Order>7</Order>
-				</SynchronousCommand>
-				<SynchronousCommand wcm:action="add">
-					<CommandLine>%WINDIR%\System32\cmd /c ping 10.0.2.2 -n 20 &#38;&#38; A:\finish.exe</CommandLine>
-					<Order>8</Order>
-				</SynchronousCommand>
-=======
 					<CommandLine>%WINDIR%\System32\cmd /c bcdedit /set {current} bootstatuspolicy ignoreallfailures</CommandLine>
 					<Order>6</Order>
 				</SynchronousCommand>
@@ -181,13 +168,12 @@
 					<Order>8</Order>
 				</SynchronousCommand>
 				<SynchronousCommand wcm:action="add">
-					<CommandLine>%WINDIR%\System32\cmd /c ping 10.0.2.2 -n 20 &#38;&#38; A:\finish.exe</CommandLine>
+					<CommandLine>%WINDIR%\System32\cmd /c A:\finish.exe</CommandLine>
 					<Order>9</Order>
 				</SynchronousCommand>
->>>>>>> cros/upstream
 			</FirstLogonCommands>
 		</component>
 	</settings>
 	<cpi:offlineImage cpi:source="wim:c:/install.wim#Windows Longhorn SERVERSTANDARD"
 		xmlns:cpi="urn:schemas-microsoft-com:cpi" />
-</unattend>
+</unattend>
\ No newline at end of file
diff --git a/client/tests/kvm/unattended/win7-64-autounattend.xml b/client/tests/kvm/unattended/win7-64-autounattend.xml
index e30e2c7..b42aa8f 100644
--- a/client/tests/kvm/unattended/win7-64-autounattend.xml
+++ b/client/tests/kvm/unattended/win7-64-autounattend.xml
@@ -168,7 +168,7 @@
 					<Order>8</Order>
 				</SynchronousCommand>
 				<SynchronousCommand wcm:action="add">
-					<CommandLine>%WINDIR%\System32\cmd /c ping 10.0.2.2 -n 20 &#38;&#38; A:\finish.exe</CommandLine>
+					<CommandLine>%WINDIR%\System32\cmd /c A:\finish.exe</CommandLine>
 					<Order>9</Order>
 				</SynchronousCommand>
 			</FirstLogonCommands>
@@ -176,4 +176,4 @@
 	</settings>
 	<cpi:offlineImage cpi:source="wim:c:/install.wim#Windows Longhorn SERVERSTANDARD"
 		xmlns:cpi="urn:schemas-microsoft-com:cpi" />
-</unattend>
+</unattend>
\ No newline at end of file
diff --git a/client/tests/kvm/unattended/winvista-32-autounattend.xml b/client/tests/kvm/unattended/winvista-32-autounattend.xml
index d4e8c5c..4dfe06c 100644
--- a/client/tests/kvm/unattended/winvista-32-autounattend.xml
+++ b/client/tests/kvm/unattended/winvista-32-autounattend.xml
@@ -164,7 +164,7 @@
 					<Order>7</Order>
 				</SynchronousCommand>
 				<SynchronousCommand wcm:action="add">
-					<CommandLine>%WINDIR%\System32\cmd /c ping 10.0.2.2 -n 20 &#38;&#38; A:\finish.exe</CommandLine>
+					<CommandLine>%WINDIR%\System32\cmd /c A:\finish.exe</CommandLine>
 					<Order>8</Order>
 				</SynchronousCommand>
 			</FirstLogonCommands>
diff --git a/client/tests/kvm/unattended/winvista-64-autounattend.xml b/client/tests/kvm/unattended/winvista-64-autounattend.xml
index 16d4850..5867bdb 100644
--- a/client/tests/kvm/unattended/winvista-64-autounattend.xml
+++ b/client/tests/kvm/unattended/winvista-64-autounattend.xml
@@ -165,7 +165,7 @@
 					<Order>7</Order>
 				</SynchronousCommand>
 				<SynchronousCommand wcm:action="add">
-					<CommandLine>%WINDIR%\System32\cmd /c ping 10.0.2.2 -n 20 &#38;&#38; A:\finish.exe</CommandLine>
+					<CommandLine>%WINDIR%\System32\cmd /c A:\finish.exe</CommandLine>
 					<Order>8</Order>
 				</SynchronousCommand>
 			</FirstLogonCommands>
diff --git a/client/tests/kvm/unattended/winxp32.sif b/client/tests/kvm/unattended/winxp32.sif
index b9a2ab6..e892193 100644
--- a/client/tests/kvm/unattended/winxp32.sif
+++ b/client/tests/kvm/unattended/winxp32.sif
@@ -72,4 +72,4 @@
    Command0="cmd /c KVM_TEST_VIRTIO_NETWORK_INSTALLER"
    Command1="cmd /c E:\setuprss.bat"
    Command2="cmd /c netsh interface ip set address local dhcp"
-   Command3="cmd /c ping 10.0.2.2 -n 20 && A:\finish.exe"
+   Command3="cmd /c A:\finish.exe"
diff --git a/client/tests/kvm/unattended/winxp64.sif b/client/tests/kvm/unattended/winxp64.sif
index b9a2ab6..e892193 100644
--- a/client/tests/kvm/unattended/winxp64.sif
+++ b/client/tests/kvm/unattended/winxp64.sif
@@ -72,4 +72,4 @@
    Command0="cmd /c KVM_TEST_VIRTIO_NETWORK_INSTALLER"
    Command1="cmd /c E:\setuprss.bat"
    Command2="cmd /c netsh interface ip set address local dhcp"
-   Command3="cmd /c ping 10.0.2.2 -n 20 && A:\finish.exe"
+   Command3="cmd /c A:\finish.exe"