Improve "Libvirt.create_snapshot_qemu" function
[yardstick.git] / yardstick / benchmark / contexts / standalone / model.py
index 14738da..4d43f26 100644 (file)
@@ -12,7 +12,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from __future__ import absolute_import
 import os
 import re
 import time
@@ -25,11 +24,12 @@ from netaddr import IPNetwork
 import xml.etree.ElementTree as ET
 
 from yardstick import ssh
-from yardstick.common.constants import YARDSTICK_ROOT_PATH
+from yardstick.common import constants
+from yardstick.common import exceptions
 from yardstick.common.yaml_loader import yaml_load
 from yardstick.network_services.utils import PciAddress
 from yardstick.network_services.helpers.cpu import CpuSysCores
-from yardstick.common.utils import write_file
+
 
 LOG = logging.getLogger(__name__)
 
@@ -106,12 +106,17 @@ class Libvirt(object):
 
     @staticmethod
     def virsh_create_vm(connection, cfg):
-        err = connection.execute("virsh create %s" % cfg)[0]
-        LOG.info("VM create status: %s", err)
+        LOG.info('VM create, XML config: %s', cfg)
+        status, _, error = connection.execute('virsh create %s' % cfg)
+        if status:
+            raise exceptions.LibvirtCreateError(error=error)
 
     @staticmethod
     def virsh_destroy_vm(vm_name, connection):
-        connection.execute("virsh destroy %s" % vm_name)
+        LOG.info('VM destroy, VM name: %s', vm_name)
+        status, _, error = connection.execute('virsh destroy %s' % vm_name)
+        if status:
+            LOG.warning('Error destroying VM %s. Error: %s', vm_name, error)
 
     @staticmethod
     def _add_interface_address(interface, pci_address):
@@ -132,7 +137,7 @@ class Libvirt(object):
         return vm_pci
 
     @classmethod
-    def add_ovs_interface(cls, vpath, port_num, vpci, vports_mac, xml):
+    def add_ovs_interface(cls, vpath, port_num, vpci, vports_mac, xml_str):
         """Add a DPDK OVS 'interface' XML node in 'devices' node
 
         <devices>
@@ -156,7 +161,7 @@ class Libvirt(object):
 
         vhost_path = ('{0}/var/run/openvswitch/dpdkvhostuser{1}'.
                       format(vpath, port_num))
-        root = ET.parse(xml)
+        root = ET.fromstring(xml_str)
         pci_address = PciAddress(vpci.strip())
         device = root.find('devices')
 
@@ -181,10 +186,10 @@ class Libvirt(object):
 
         cls._add_interface_address(interface, pci_address)
 
-        root.write(xml)
+        return ET.tostring(root)
 
     @classmethod
-    def add_sriov_interfaces(cls, vm_pci, vf_pci, vf_mac, xml):
+    def add_sriov_interfaces(cls, vm_pci, vf_pci, vf_mac, xml_str):
         """Add a SR-IOV 'interface' XML node in 'devices' node
 
         <devices>
@@ -207,7 +212,7 @@ class Libvirt(object):
             -sr_iov-how_sr_iov_libvirt_works
         """
 
-        root = ET.parse(xml)
+        root = ET.fromstring(xml_str)
         device = root.find('devices')
 
         interface = ET.SubElement(device, 'interface')
@@ -224,20 +229,47 @@ class Libvirt(object):
         pci_vm_address = PciAddress(vm_pci.strip())
         cls._add_interface_address(interface, pci_vm_address)
 
-        root.write(xml)
+        return ET.tostring(root)
 
     @staticmethod
-    def create_snapshot_qemu(connection, index, vm_image):
-        # build snapshot image
-        image = "/var/lib/libvirt/images/%s.qcow2" % index
-        connection.execute("rm %s" % image)
-        qemu_template = "qemu-img create -f qcow2 -o backing_file=%s %s"
-        connection.execute(qemu_template % (vm_image, image))
+    def create_snapshot_qemu(connection, index, base_image):
+        """Create the snapshot image for a VM using a base image
 
-        return image
+        :param connection: SSH connection to the remote host
+        :param index: index of the VM to be spawn
+        :param base_image: path of the VM base image in the remote host
+        :return: snapshot image path
+        """
+        vm_image = '/var/lib/libvirt/images/%s.qcow2' % index
+        connection.execute('rm -- "%s"' % vm_image)
+        status, _, _ = connection.execute('test -r %s' % base_image)
+        if status:
+            if not os.access(base_image, os.R_OK):
+                raise exceptions.LibvirtQemuImageBaseImageNotPresent(
+                    vm_image=vm_image, base_image=base_image)
+            # NOTE(ralonsoh): done in two steps to avoid root permission
+            # issues.
+            LOG.info('Copy %s from execution host to remote host', base_image)
+            file_name = os.path.basename(os.path.normpath(base_image))
+            connection.put_file(base_image, '/tmp/%s' % file_name)
+            status, _, error = connection.execute(
+                'mv -- "/tmp/%s" "%s"' % (file_name, base_image))
+            if status:
+                raise exceptions.LibvirtQemuImageCreateError(
+                    vm_image=vm_image, base_image=base_image, error=error)
+
+        LOG.info('Convert image %s to %s', base_image, vm_image)
+        qemu_cmd = ('qemu-img create -f qcow2 -o backing_file=%s %s' %
+                    (base_image, vm_image))
+        status, _, error = connection.execute(qemu_cmd)
+        if status:
+            raise exceptions.LibvirtQemuImageCreateError(
+                vm_image=vm_image, base_image=base_image, error=error)
+        return vm_image
 
     @classmethod
-    def build_vm_xml(cls, connection, flavor, cfg, vm_name, index):
+    def build_vm_xml(cls, connection, flavor, vm_name, index):
+        """Build the XML from the configuration parameters"""
         memory = flavor.get('ram', '4096')
         extra_spec = flavor.get('extra_specs', {})
         cpu = extra_spec.get('hw:cpu_cores', '2')
@@ -261,9 +293,7 @@ class Libvirt(object):
             socket=socket, threads=threads,
             vm_image=image, cpuset=cpuset, cputune=cputune)
 
-        write_file(cfg, vm_xml)
-
-        return [vcpu, mac]
+        return vm_xml, mac
 
     @staticmethod
     def update_interrupts_hugepages_perf(connection):
@@ -283,6 +313,13 @@ class Libvirt(object):
         cpuset = "%s,%s" % (cores, threads)
         return cpuset
 
+    @classmethod
+    def write_file(cls, file_name, xml_str):
+        """Dump a XML string to a file"""
+        root = ET.fromstring(xml_str)
+        et = ET.ElementTree(element=root)
+        et.write(file_name, encoding='utf-8', method='xml')
+
 
 class StandaloneContextHelper(object):
     """ This class handles all the common code for standalone
@@ -374,7 +411,8 @@ class StandaloneContextHelper(object):
         except IOError as io_error:
             if io_error.errno != errno.ENOENT:
                 raise
-            self.file_path = os.path.join(YARDSTICK_ROOT_PATH, file_path)
+            self.file_path = os.path.join(constants.YARDSTICK_ROOT_PATH,
+                                          file_path)
             cfg = self.read_config_file()
 
         nodes.extend([node for node in cfg["nodes"] if str(node["role"]) != nfvi_role])
@@ -506,7 +544,7 @@ class OvsDeploy(object):
         StandaloneContextHelper.install_req_libs(self.connection, pkgs)
 
     def ovs_deploy(self):
-        ovs_deploy = os.path.join(YARDSTICK_ROOT_PATH,
+        ovs_deploy = os.path.join(constants.YARDSTICK_ROOT_PATH,
                                   "yardstick/resources/scripts/install/",
                                   self.OVS_DEPLOY_SCRIPT)
         if os.path.isfile(ovs_deploy):
@@ -522,4 +560,6 @@ class OvsDeploy(object):
 
             cmd = "sudo -E %s --ovs='%s' --dpdk='%s' -p='%s'" % (remote_ovs_deploy,
                                                                  ovs, dpdk, http_proxy)
-            self.connection.execute(cmd)
+            exit_status, _, stderr = self.connection.execute(cmd)
+            if exit_status:
+                raise exceptions.OVSDeployError(stderr=stderr)