Improve "Libvirt.create_snapshot_qemu" function 11/50711/24
authorRodolfo Alonso Hernandez <rodolfo.alonso.hernandez@intel.com>
Tue, 16 Jan 2018 15:55:02 +0000 (15:55 +0000)
committerRodolfo Alonso Hernandez <rodolfo.alonso.hernandez@intel.com>
Mon, 16 Apr 2018 11:24:27 +0000 (11:24 +0000)
Improved "Libvirt.create_snapshot_qemu" function:
- Check if the base image is present in the remote host.
  If not, try to copy from the execution host.
- Check the execution status. In case the command fails, raise an
  exception.

JIRA: YARDSTICK-944

Change-Id: I78bd0d3ed6a1f35ed772c0d192bb240009a580ed
Signed-off-by: Rodolfo Alonso Hernandez <rodolfo.alonso.hernandez@intel.com>
yardstick/benchmark/contexts/standalone/model.py
yardstick/common/exceptions.py
yardstick/tests/unit/benchmark/contexts/standalone/test_model.py

index f18d090..4d43f26 100644 (file)
@@ -232,14 +232,40 @@ class Libvirt(object):
         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))
-
-        return image
+    def create_snapshot_qemu(connection, index, base_image):
+        """Create the snapshot image for a VM using a base 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, vm_name, index):
index 65e4440..597eaf4 100644 (file)
@@ -117,6 +117,17 @@ class LibvirtCreateError(YardstickException):
     message = 'Error creating the virtual machine. Error: %(error)s.'
 
 
+class LibvirtQemuImageBaseImageNotPresent(YardstickException):
+    message = ('Error creating the qemu image for %(vm_image)s. Base image: '
+               '%(base_image)s. Base image not present in execution host or '
+               'remote host.')
+
+
+class LibvirtQemuImageCreateError(YardstickException):
+    message = ('Error creating the qemu image for %(vm_image)s. Base image: '
+               '%(base_image)s. Error: %(error)s.')
+
+
 class ScenarioConfigContextNameNotFound(YardstickException):
     message = 'Context name "%(context_name)s" not found'
 
index b1dcee2..7078c89 100644 (file)
 # limitations under the License.
 
 import copy
-import mock
 import os
-import unittest
 import uuid
 
+import mock
+import unittest
 from xml.etree import ElementTree
 
 from yardstick import ssh
@@ -172,14 +172,70 @@ class ModelLibvirtTestCase(unittest.TestCase):
                          interface_address.get('function'))
 
     def test_create_snapshot_qemu(self):
-        result = "/var/lib/libvirt/images/0.qcow2"
-        with mock.patch("yardstick.ssh.SSH") as ssh:
-            ssh_mock = mock.Mock(autospec=ssh.SSH)
-            ssh_mock.execute = \
-                mock.Mock(return_value=(0, "a", ""))
-            ssh.return_value = ssh_mock
-        image = model.Libvirt.create_snapshot_qemu(ssh_mock, "0", "ubuntu.img")
-        self.assertEqual(image, result)
+        self.mock_ssh.execute = mock.Mock(return_value=(0, 0, 0))
+        index = 1
+        vm_image = '/var/lib/libvirt/images/%s.qcow2' % index
+        base_image = '/tmp/base_image'
+
+        model.Libvirt.create_snapshot_qemu(self.mock_ssh, index, base_image)
+        self.mock_ssh.execute.assert_has_calls([
+            mock.call('rm -- "%s"' % vm_image),
+            mock.call('test -r %s' % base_image),
+            mock.call('qemu-img create -f qcow2 -o backing_file=%s %s' %
+                      (base_image, vm_image))
+        ])
+
+    @mock.patch.object(os.path, 'basename', return_value='base_image')
+    @mock.patch.object(os.path, 'normpath')
+    @mock.patch.object(os, 'access', return_value=True)
+    def test_create_snapshot_qemu_no_image_remote(self,
+            mock_os_access, mock_normpath, mock_basename):
+        self.mock_ssh.execute = mock.Mock(
+            side_effect=[(0, 0, 0), (1, 0, 0), (0, 0, 0), (0, 0, 0)])
+        index = 1
+        vm_image = '/var/lib/libvirt/images/%s.qcow2' % index
+        base_image = '/tmp/base_image'
+        mock_normpath.return_value = base_image
+
+        model.Libvirt.create_snapshot_qemu(self.mock_ssh, index, base_image)
+        self.mock_ssh.execute.assert_has_calls([
+            mock.call('rm -- "%s"' % vm_image),
+            mock.call('test -r %s' % base_image),
+            mock.call('mv -- "/tmp/%s" "%s"' % ('base_image', base_image)),
+            mock.call('qemu-img create -f qcow2 -o backing_file=%s %s' %
+                      (base_image, vm_image))
+        ])
+        mock_os_access.assert_called_once_with(base_image, os.R_OK)
+        mock_normpath.assert_called_once_with(base_image)
+        mock_basename.assert_has_calls([mock.call(base_image)])
+        self.mock_ssh.put_file.assert_called_once_with(base_image,
+                                                       '/tmp/base_image')
+
+    @mock.patch.object(os, 'access', return_value=False)
+    def test_create_snapshot_qemu_no_image_local(self, mock_os_access):
+        self.mock_ssh.execute = mock.Mock(side_effect=[(0, 0, 0), (1, 0, 0)])
+        base_image = '/tmp/base_image'
+
+        with self.assertRaises(exceptions.LibvirtQemuImageBaseImageNotPresent):
+            model.Libvirt.create_snapshot_qemu(self.mock_ssh, 3, base_image)
+        mock_os_access.assert_called_once_with(base_image, os.R_OK)
+
+    def test_create_snapshot_qemu_error_qemuimg_command(self):
+        self.mock_ssh.execute = mock.Mock(
+            side_effect=[(0, 0, 0), (0, 0, 0), (1, 0, 0)])
+        index = 1
+        vm_image = '/var/lib/libvirt/images/%s.qcow2' % index
+        base_image = '/tmp/base_image'
+
+        with self.assertRaises(exceptions.LibvirtQemuImageCreateError):
+            model.Libvirt.create_snapshot_qemu(self.mock_ssh, index,
+                                               base_image)
+        self.mock_ssh.execute.assert_has_calls([
+            mock.call('rm -- "%s"' % vm_image),
+            mock.call('test -r %s' % base_image),
+            mock.call('qemu-img create -f qcow2 -o backing_file=%s %s' %
+                      (base_image, vm_image))
+        ])
 
     @mock.patch.object(model.Libvirt, 'pin_vcpu_for_perf', return_value='4,5')
     @mock.patch.object(model.Libvirt, 'create_snapshot_qemu',