]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
GlusterFS: Use image_utils for qemu-img calls
authorEric Harney <eharney@redhat.com>
Sun, 8 Sep 2013 14:18:18 +0000 (10:18 -0400)
committerEric Harney <eharney@redhat.com>
Fri, 20 Sep 2013 18:06:52 +0000 (14:06 -0400)
Code added for GlusterFS snapshot support in Havana included
new code to parse qemu-img output.  This removes this code
and uses image_utils qemu_img_info instead.

Add test to image_utils for parsing a different style of
qemu-img info output.

Improve GlusterFS driver test coverage with tests for cloning,
create from snapshot, and initialize_connection.

Closes-Bug: #1224030
Change-Id: I8f1811b400c06edb3cd7416c52aa297921841e54

cinder/tests/test_glusterfs.py
cinder/tests/test_image_utils.py
cinder/volume/drivers/glusterfs.py

index 70ed9ce8c9e9845ae98f9479a58d95f6a56730f5..4f6c7aec6069eb2da0182058ee4f66ac6cd84631 100644 (file)
@@ -29,6 +29,7 @@ from cinder import compute
 from cinder import context
 from cinder import db
 from cinder import exception
+from cinder.image import image_utils
 from cinder.openstack.common import processutils as putils
 from cinder import test
 from cinder.tests.compute import test_nova
@@ -448,13 +449,16 @@ class GlusterFsDriverTestCase(test.TestCase):
 
         mox.VerifyAll()
 
-    def _simple_volume(self):
+    def _simple_volume(self, id=None):
         volume = DumbVolume()
         volume['provider_location'] = self.TEST_EXPORT1
+        if id is None:
+            volume['id'] = self.VOLUME_UUID
+        else:
+            volume['id'] = id
         # volume['name'] mirrors format from db/sqlalchemy/models.py
-        volume['name'] = 'volume-%s' % self.VOLUME_UUID
+        volume['name'] = 'volume-%s' % volume['id']
         volume['size'] = 10
-        volume['id'] = self.VOLUME_UUID
         volume['status'] = 'available'
 
         return volume
@@ -572,6 +576,58 @@ class GlusterFsDriverTestCase(test.TestCase):
 
         mox.VerifyAll()
 
+    def test_create_cloned_volume(self):
+        (mox, drv) = self._mox, self._driver
+
+        mox.StubOutWithMock(drv, 'create_snapshot')
+        mox.StubOutWithMock(drv, 'delete_snapshot')
+        mox.StubOutWithMock(drv, '_read_info_file')
+        mox.StubOutWithMock(image_utils, 'convert_image')
+        mox.StubOutWithMock(drv, '_copy_volume_from_snapshot')
+
+        volume_file = 'volume-%s' % self.VOLUME_UUID
+        volume_path = '%s/%s/%s' % (self.TEST_MNT_POINT_BASE,
+                                    drv._get_hash_str(self.TEST_EXPORT1),
+                                    volume_file)
+
+        volume = self._simple_volume()
+        src_vref = self._simple_volume()
+        src_vref['id'] = '375e32b2-804a-49f2-b282-85d1d5a5b9e1'
+        src_vref['name'] = 'volume-%s' % src_vref['id']
+        volume_file = 'volume-%s' % src_vref['id']
+        volume_path = '%s/%s/%s' % (self.TEST_MNT_POINT_BASE,
+                                    drv._get_hash_str(self.TEST_EXPORT1),
+                                    volume_file)
+        src_info_path = '%s.info' % volume_path
+        volume_ref = {'id': volume['id'],
+                      'name': volume['name'] + '-clone',
+                      'status': volume['status'],
+                      'provider_location': volume['provider_location'],
+                      'size': volume['size']}
+
+        snap_ref = {'volume_name': src_vref['name'],
+                    'name': 'clone-snap-%s' % src_vref['id'],
+                    'size': src_vref['size'],
+                    'volume_size': src_vref['size'],
+                    'volume_id': src_vref['id'],
+                    'id': 'tmp-snap-%s' % src_vref['id'],
+                    'volume': src_vref}
+
+        drv.create_snapshot(snap_ref)
+
+        snap_info = {'active': volume_file,
+                     snap_ref['id']: volume_path + '-clone'}
+
+        drv._read_info_file(src_info_path).AndReturn(snap_info)
+
+        drv._copy_volume_from_snapshot(snap_ref, volume_ref, volume['size'])
+
+        drv.delete_snapshot(mox_lib.IgnoreArg())
+
+        mox.ReplayAll()
+
+        drv.create_cloned_volume(volume, src_vref)
+
     def test_delete_volume(self):
         """delete_volume simple test case."""
         mox = self._mox
@@ -719,25 +775,23 @@ class GlusterFsDriverTestCase(test.TestCase):
         snap_file_2 = '%s.%s' % (volume_filename, self.SNAP_UUID_2)
         info_path = '%s%s' % (volume_path, '.info')
 
+        qemu_img_info_output = """image: volume-%s.%s
+        file format: qcow2
+        virtual size: 1.0G (1073741824 bytes)
+        disk size: 173K
+        backing file: %s
+        """ % (self.VOLUME_UUID, self.SNAP_UUID, volume_filename)
+
         mox.StubOutWithMock(drv, '_execute')
-        mox.StubOutWithMock(os.path, 'exists')
         mox.StubOutWithMock(drv, '_read_file')
         mox.StubOutWithMock(drv, '_read_info_file')
         mox.StubOutWithMock(drv, '_get_backing_chain_for_path')
         mox.StubOutWithMock(drv, '_get_matching_backing_file')
         mox.StubOutWithMock(drv, '_write_info_file')
+        mox.StubOutWithMock(image_utils, 'qemu_img_info')
 
-        os.path.exists(snap_path_2).AndReturn(True)
-
-        info_file_json = """
-        {
-             %(SNAP_UUID)s: "volume-%(VOLUME_UUID)s.%(SNAP_UUID)s",
-             %(SNAP_UUID_2)s": "volume-%(VOLUME_UUID)s.%(SNAP_UUID_2)s",
-             "active": "volume-%(VOLUME_UUID)s.%(SNAP_UUID_2)s"
-        }
-        """ % {'SNAP_UUID': self.SNAP_UUID,
-               'SNAP_UUID_2': self.SNAP_UUID_2,
-               'VOLUME_UUID': self.VOLUME_UUID}
+        img_info = image_utils.QemuImgInfo(qemu_img_info_output)
+        image_utils.qemu_img_info(snap_path_2).AndReturn(img_info)
 
         info_file_dict = {'active': snap_file_2,
                           self.SNAP_UUID_2: snap_file_2,
@@ -748,23 +802,6 @@ class GlusterFsDriverTestCase(test.TestCase):
                     'volume': self._simple_volume(),
                     'id': self.SNAP_UUID_2}
 
-        qemu_img_info_output = """image: volume-%s.%s
-        file format: qcow2
-        virtual size: 1.0G (1073741824 bytes)
-        disk size: 173K
-        backing file: %s
-        """ % (self.VOLUME_UUID, self.SNAP_UUID, volume_filename)
-
-        qemu_img_info_output_2 = """image: volume-%s
-        file format: qcow2
-        virtual size: 1.0G (1073741824 bytes)
-        disk size: 173K
-        """ % self.VOLUME_UUID
-
-        drv._execute('qemu-img', 'info', snap_path_2,
-                     run_as_root=True).\
-            AndReturn((qemu_img_info_output, ''))
-
         snap_path_2_chain = [{self.SNAP_UUID_2: snap_file_2},
                              {self.SNAP_UUID: snap_file},
                              {'active': snap_file_2}]
@@ -822,28 +859,6 @@ class GlusterFsDriverTestCase(test.TestCase):
         snap_path_2 = '%s.%s' % (volume_path, self.SNAP_UUID_2)
         snap_file_2 = 'volume-%s.%s' % (self.VOLUME_UUID, self.SNAP_UUID_2)
 
-        mox.StubOutWithMock(drv, '_execute')
-        mox.StubOutWithMock(os.path, 'exists')
-        mox.StubOutWithMock(drv, '_read_info_file')
-        mox.StubOutWithMock(drv, '_write_info_file')
-        mox.StubOutWithMock(drv, '_get_backing_chain_for_path')
-        mox.StubOutWithMock(drv, 'get_active_image_from_info')
-
-        info_file_dict = {self.SNAP_UUID_2: 'volume-%s.%s' %
-                          (self.VOLUME_UUID, self.SNAP_UUID_2),
-                          self.SNAP_UUID: 'volume-%s.%s' %
-                          (self.VOLUME_UUID, self.SNAP_UUID)}
-
-        info_path = drv._local_path_volume(volume) + '.info'
-        drv._read_info_file(info_path).AndReturn(info_file_dict)
-
-        os.path.exists(snap_path).AndReturn(True)
-
-        snap_ref = {'name': 'test snap',
-                    'volume_id': self.VOLUME_UUID,
-                    'volume': volume,
-                    'id': self.SNAP_UUID}
-
         qemu_img_info_output_snap_2 = """image: volume-%s.%s
         file format: qcow2
         virtual size: 1.0G (1073741824 bytes)
@@ -866,6 +881,29 @@ class GlusterFsDriverTestCase(test.TestCase):
         disk size: 175K
         """ % self.VOLUME_UUID
 
+        mox.StubOutWithMock(drv, '_execute')
+        mox.StubOutWithMock(drv, '_read_info_file')
+        mox.StubOutWithMock(drv, '_write_info_file')
+        mox.StubOutWithMock(drv, '_get_backing_chain_for_path')
+        mox.StubOutWithMock(drv, 'get_active_image_from_info')
+        mox.StubOutWithMock(image_utils, 'qemu_img_info')
+
+        info_file_dict = {self.SNAP_UUID_2: 'volume-%s.%s' %
+                          (self.VOLUME_UUID, self.SNAP_UUID_2),
+                          self.SNAP_UUID: 'volume-%s.%s' %
+                          (self.VOLUME_UUID, self.SNAP_UUID)}
+
+        info_path = drv._local_path_volume(volume) + '.info'
+        drv._read_info_file(info_path).AndReturn(info_file_dict)
+
+        img_info = image_utils.QemuImgInfo(qemu_img_info_output_snap_1)
+        image_utils.qemu_img_info(snap_path).AndReturn(img_info)
+
+        snap_ref = {'name': 'test snap',
+                    'volume_id': self.VOLUME_UUID,
+                    'volume': volume,
+                    'id': self.SNAP_UUID}
+
         snap_path_chain = [{'filename': snap_file_2,
                             'backing-filename': snap_file},
                            {'filename': snap_file,
@@ -891,63 +929,6 @@ class GlusterFsDriverTestCase(test.TestCase):
 
         mox.VerifyAll()
 
-    def test_get_backing_file(self, path='', actual_path=''):
-        (mox, drv) = self._mox, self._driver
-
-        qemu_img_info_output = """image: volume-%s
-        file format: qcow2
-        virtual size: 1.0G (1073741824 bytes)
-        disk size: 152K
-        backing file: %svolume-%s.%s%s
-        """ % (self.VOLUME_UUID,
-               path, self.VOLUME_UUID, self.SNAP_UUID, actual_path)
-
-        mox.ReplayAll()
-
-        expected_file = 'volume-%s.%s' % (self.VOLUME_UUID, self.SNAP_UUID)
-        self.assertEqual(drv._get_backing_file(qemu_img_info_output),
-                         expected_file)
-
-        mox.VerifyAll()
-
-    def test_get_backing_file_with_path(self):
-        self.test_get_backing_file(path='/mnt/asdf/')
-
-    def test_get_backing_file_other_cwd(self):
-        ap = ' (actual path: /mnt/asdf/volume-%s.%s)' % \
-             (self.VOLUME_UUID, self.SNAP_UUID)
-        self.test_get_backing_file(actual_path=ap)
-
-    def test_get_backing_file_none(self):
-        (mox, drv) = self._mox, self._driver
-
-        qemu_img_info_output = """image: volume-%s
-        file format: raw
-        virtual size: 1.0G (1073741824 bytes)
-        disk size: 152K
-        """ % self.VOLUME_UUID
-
-        mox.ReplayAll()
-
-        self.assertIsNone(drv._get_backing_file(qemu_img_info_output))
-
-        mox.VerifyAll()
-
-    def test_get_file_format(self):
-        (mox, drv) = self._mox, self._driver
-
-        qemu_img_info_output = """image: volume-%s
-        file format: qcow2
-        virtual size: 1.0G (1073741824 bytes)
-        disk size: 152K
-        """ % self.VOLUME_UUID
-
-        mox.ReplayAll()
-
-        self.assertEqual(drv._get_file_format(qemu_img_info_output), 'qcow2')
-
-        mox.VerifyAll()
-
     def test_read_info_file(self):
         (mox, drv) = self._mox, self._driver
 
@@ -979,10 +960,10 @@ class GlusterFsDriverTestCase(test.TestCase):
 
         volume = self._simple_volume()
 
-        mox.StubOutWithMock(drv, '_execute')
-        mox.StubOutWithMock(drv, 'get_active_image_from_info')
-
-        drv.get_active_image_from_info(volume).AndReturn(volume['name'])
+        volume_path = '%s/%s/volume-%s' % (self.TEST_MNT_POINT_BASE,
+                                           drv._get_hash_str(
+                                               self.TEST_EXPORT1),
+                                           self.VOLUME_UUID)
 
         qemu_img_info_output = """image: volume-%s
         file format: qcow2
@@ -990,14 +971,18 @@ class GlusterFsDriverTestCase(test.TestCase):
         disk size: 473K
         """ % self.VOLUME_UUID
 
-        volume_path = '%s/%s/volume-%s' % (self.TEST_MNT_POINT_BASE,
-                                           drv._get_hash_str(
-                                               self.TEST_EXPORT1),
-                                           self.VOLUME_UUID)
-        drv._execute('qemu-img', 'info', volume_path).\
-            AndReturn((qemu_img_info_output, ''))
+        img_info = image_utils.QemuImgInfo(qemu_img_info_output)
 
-        drv._execute('qemu-img', 'resize', volume_path, '3G', run_as_root=True)
+        mox.StubOutWithMock(drv, '_execute')
+        mox.StubOutWithMock(drv, 'get_active_image_from_info')
+        mox.StubOutWithMock(image_utils, 'qemu_img_info')
+        mox.StubOutWithMock(image_utils, 'resize_image')
+
+        drv.get_active_image_from_info(volume).AndReturn(volume['name'])
+
+        image_utils.qemu_img_info(volume_path).AndReturn(img_info)
+
+        image_utils.resize_image(volume_path, 3)
 
         mox.ReplayAll()
 
@@ -1155,8 +1140,8 @@ class GlusterFsDriverTestCase(test.TestCase):
         mox.StubOutWithMock(drv, '_read_info_file')
         mox.StubOutWithMock(drv, '_write_info_file')
         mox.StubOutWithMock(os.path, 'exists')
-        mox.StubOutWithMock(drv, '_get_backing_file_for_path')
         mox.StubOutWithMock(db, 'snapshot_get')
+        mox.StubOutWithMock(image_utils, 'qemu_img_info')
 
         snap_info = {'active': snap_file,
                      self.SNAP_UUID: snap_file}
@@ -1165,15 +1150,17 @@ class GlusterFsDriverTestCase(test.TestCase):
 
         os.path.exists(snap_path).AndReturn(True)
 
-        drv._read_info_file(info_path, empty_if_missing=True).\
-            AndReturn(snap_info)
-
-        asdfqemu_img_info_output = """image: %s
+        qemu_img_info_output = """image: %s
         file format: qcow2
         virtual size: 1.0G (1073741824 bytes)
         disk size: 173K
         backing file: %s
         """ % (snap_file, volume_file)
+        img_info = image_utils.QemuImgInfo(qemu_img_info_output)
+        image_utils.qemu_img_info(snap_path).AndReturn(img_info)
+
+        drv._read_info_file(info_path, empty_if_missing=True).\
+            AndReturn(snap_info)
 
         delete_info = {
             'type': 'qcow2',
@@ -1184,8 +1171,6 @@ class GlusterFsDriverTestCase(test.TestCase):
 
         drv._nova.delete_volume_snapshot(ctxt, self.SNAP_UUID, delete_info)
 
-        drv._get_backing_file_for_path(snap_path).AndReturn(volume_file)
-
         drv._read_info_file(info_path).AndReturn(snap_info)
 
         drv._read_info_file(info_path).AndReturn(snap_info)
@@ -1242,8 +1227,8 @@ class GlusterFsDriverTestCase(test.TestCase):
         mox.StubOutWithMock(drv, '_read_info_file')
         mox.StubOutWithMock(drv, '_write_info_file')
         mox.StubOutWithMock(os.path, 'exists')
-        mox.StubOutWithMock(drv, '_get_backing_file_for_path')
         mox.StubOutWithMock(db, 'snapshot_get')
+        mox.StubOutWithMock(image_utils, 'qemu_img_info')
 
         snap_info = {'active': snap_file_2,
                      self.SNAP_UUID: snap_file,
@@ -1253,11 +1238,19 @@ class GlusterFsDriverTestCase(test.TestCase):
 
         os.path.exists(snap_path).AndReturn(True)
 
+        qemu_img_info_output = """image: %s
+        file format: qcow2
+        virtual size: 1.0G (1073741824 bytes)
+        disk size: 173K
+        backing file: %s
+        """ % (snap_file, volume_file)
+        img_info = image_utils.QemuImgInfo(qemu_img_info_output)
+
+        image_utils.qemu_img_info(snap_path).AndReturn(img_info)
+
         drv._read_info_file(info_path, empty_if_missing=True).\
             AndReturn(snap_info)
 
-        drv._get_backing_file_for_path(snap_path).AndReturn(volume_file)
-
         delete_info = {'type': 'qcow2',
                        'merge_target_file': volume_file,
                        'file_to_merge': snap_file,
@@ -1318,8 +1311,8 @@ class GlusterFsDriverTestCase(test.TestCase):
         mox.StubOutWithMock(drv, '_read_info_file')
         mox.StubOutWithMock(drv, '_write_info_file')
         mox.StubOutWithMock(os.path, 'exists')
-        mox.StubOutWithMock(drv, '_get_backing_file_for_path')
         mox.StubOutWithMock(db, 'snapshot_get')
+        mox.StubOutWithMock(image_utils, 'qemu_img_info')
 
         snap_info = {'active': snap_file,
                      self.SNAP_UUID: snap_file}
@@ -1328,15 +1321,18 @@ class GlusterFsDriverTestCase(test.TestCase):
 
         os.path.exists(snap_path).AndReturn(True)
 
-        drv._read_info_file(info_path, empty_if_missing=True).\
-            AndReturn(snap_info)
-
-        asdfqemu_img_info_output = """image: %s
+        qemu_img_info_output = """image: %s
         file format: qcow2
         virtual size: 1.0G (1073741824 bytes)
         disk size: 173K
         backing file: %s
         """ % (snap_file, volume_file)
+        img_info = image_utils.QemuImgInfo(qemu_img_info_output)
+
+        image_utils.qemu_img_info(snap_path).AndReturn(img_info)
+
+        drv._read_info_file(info_path, empty_if_missing=True).\
+            AndReturn(snap_info)
 
         delete_info = {
             'type': 'qcow2',
@@ -1347,8 +1343,6 @@ class GlusterFsDriverTestCase(test.TestCase):
 
         drv._nova.delete_volume_snapshot(ctxt, self.SNAP_UUID, delete_info)
 
-        drv._get_backing_file_for_path(snap_path).AndReturn(volume_file)
-
         drv._read_info_file(info_path).AndReturn(snap_info)
 
         drv._read_info_file(info_path).AndReturn(snap_info)
@@ -1393,28 +1387,113 @@ class GlusterFsDriverTestCase(test.TestCase):
 
         mox.StubOutWithMock(drv, '_execute')
         mox.StubOutWithMock(drv, '_local_volume_dir')
+        mox.StubOutWithMock(image_utils, 'qemu_img_info')
 
-        qemu_img_output_base = """image: %s
+        qemu_img_output_base = """image: %(image_name)s
         file format: qcow2
         virtual size: 1.0G (1073741824 bytes)
         disk size: 173K
         """
-        qemu_img_output = qemu_img_output_base + 'backing file: %s\n'
+        qemu_img_output = """image: %(image_name)s
+        file format: qcow2
+        virtual size: 1.0G (1073741824 bytes)
+        disk size: 173K
+        backing file: %(backing_file)s
+        """
+
+        qemu_img_output_1 = qemu_img_output_base % {'image_name': vol_filename}
+        qemu_img_output_2 = qemu_img_output % {'image_name': vol_filename_2,
+                                               'backing_file': vol_filename}
+        qemu_img_output_3 = qemu_img_output % {'image_name': vol_filename_3,
+                                               'backing_file': vol_filename_2}
 
-        qemu_img_output_1 = qemu_img_output % (vol_filename, vol_filename_2)
-        qemu_img_output_2 = qemu_img_output % (vol_filename_2, vol_filename_3)
-        qemu_img_output_3 = qemu_img_output_base % (vol_filename_3)
+        info_1 = image_utils.QemuImgInfo(qemu_img_output_1)
+        info_2 = image_utils.QemuImgInfo(qemu_img_output_2)
+        info_3 = image_utils.QemuImgInfo(qemu_img_output_3)
 
         drv._local_volume_dir(volume).AndReturn(vol_dir)
-        drv._execute('qemu-img', 'info', vol_path).\
-            AndReturn((qemu_img_output_1, None))
+        image_utils.qemu_img_info(vol_path_3).\
+            AndReturn(info_3)
         drv._local_volume_dir(volume).AndReturn(vol_dir)
-        drv._execute('qemu-img', 'info', vol_path_2).\
-            AndReturn((qemu_img_output_2, None))
+        image_utils.qemu_img_info(vol_path_2).\
+            AndReturn(info_2)
         drv._local_volume_dir(volume).AndReturn(vol_dir)
-        drv._execute('qemu-img', 'info', vol_path_3).\
-            AndReturn((qemu_img_output_3, None))
+        image_utils.qemu_img_info(vol_path).\
+            AndReturn(info_1)
 
         mox.ReplayAll()
 
-        drv._get_backing_chain_for_path(volume, vol_path)
+        chain = drv._get_backing_chain_for_path(volume, vol_path_3)
+
+        # Verify chain contains all expected data
+        item_1 = drv._get_matching_backing_file(chain, vol_filename)
+        self.assertEqual(item_1['filename'], vol_filename_2)
+        chain.remove(item_1)
+        item_2 = drv._get_matching_backing_file(chain, vol_filename_2)
+        self.assertEqual(item_2['filename'], vol_filename_3)
+        chain.remove(item_2)
+        self.assertEqual(len(chain), 1)
+        self.assertEqual(chain[0]['filename'], vol_filename)
+
+    def test_create_volume_from_snapshot(self):
+        (mox, drv) = self._mox, self._driver
+
+        volume = self._simple_volume('c1073000-0000-0000-0000-0000000c1073')
+        src_volume = self._simple_volume()
+
+        mox.StubOutWithMock(drv, 'create_snapshot')
+        mox.StubOutWithMock(drv, '_copy_volume_from_snapshot')
+        mox.StubOutWithMock(drv, 'delete_snapshot')
+
+        snap_ref = {'volume_name': src_volume['name'],
+                    'name': 'clone-snap-%s' % src_volume['id'],
+                    'size': src_volume['size'],
+                    'volume_size': src_volume['size'],
+                    'volume_id': src_volume['id'],
+                    'id': 'tmp-snap-%s' % src_volume['id'],
+                    'volume': src_volume}
+
+        volume_ref = {'id': volume['id'],
+                      'size': volume['size'],
+                      'status': volume['status'],
+                      'provider_location': volume['provider_location'],
+                      'name': 'volume-' + volume['id'] + '-clone'}
+
+        drv.create_snapshot(snap_ref)
+        drv._copy_volume_from_snapshot(snap_ref,
+                                       volume_ref,
+                                       src_volume['size'])
+        drv.delete_snapshot(snap_ref)
+
+        mox.ReplayAll()
+
+        drv.create_cloned_volume(volume, src_volume)
+
+    def test_initialize_connection(self):
+        (mox, drv) = self._mox, self._driver
+
+        volume = self._simple_volume()
+        vol_dir = os.path.join(self.TEST_MNT_POINT_BASE,
+                               drv._get_hash_str(self.TEST_EXPORT1))
+        vol_path = os.path.join(vol_dir, volume['name'])
+
+        qemu_img_output = """image: %s
+        file format: raw
+        virtual size: 1.0G (1073741824 bytes)
+        disk size: 173K
+        """ % volume['name']
+        img_info = image_utils.QemuImgInfo(qemu_img_output)
+
+        mox.StubOutWithMock(drv, 'get_active_image_from_info')
+        mox.StubOutWithMock(image_utils, 'qemu_img_info')
+
+        drv.get_active_image_from_info(volume).AndReturn(volume['name'])
+        image_utils.qemu_img_info(vol_path).AndReturn(img_info)
+
+        mox.ReplayAll()
+
+        conn_info = drv.initialize_connection(volume, None)
+
+        self.assertEqual(conn_info['data']['format'], 'raw')
+        self.assertEqual(conn_info['driver_volume_type'], 'glusterfs')
+        self.assertEqual(conn_info['data']['name'], volume['name'])
index fd1d29b9c4c9e7df3776565fc97fd3d5789628c1..967aa38182049e09dcfc8e7389edc3f9a572ac48 100644 (file)
@@ -136,6 +136,60 @@ class TestUtils(test.TestCase):
 
         self.assertEqual(str(inf), TEST_STR)
 
+    def test_qemu_img_info_alt(self):
+        """Test a slightly different variation of qemu-img output.
+
+           (Based on Fedora 19's qemu-img 1.4.2.)
+        """
+
+        TEST_PATH = "img/qemu.qcow2"
+        TEST_RETURN = "image: qemu.qcow2\n"\
+                      "backing file: qemu.qcow2 (actual path: qemu.qcow2)\n"\
+                      "file format: qcow2\n"\
+                      "virtual size: 50M (52428800 bytes)\n"\
+                      "cluster_size: 65536\n"\
+                      "disk size: 196K (200704 bytes)\n"\
+                      "Snapshot list:\n"\
+                      "ID TAG  VM SIZE DATE VM CLOCK\n"\
+                      "1  snap1 1.7G 2011-10-04 19:04:00 32:06:34.974"
+        TEST_STR = "image: qemu.qcow2\n"\
+                   "file_format: qcow2\n"\
+                   "virtual_size: 52428800\n"\
+                   "disk_size: 200704\n"\
+                   "cluster_size: 65536\n"\
+                   "backing_file: qemu.qcow2\n"\
+                   "snapshots: [{'date': '2011-10-04', "\
+                   "'vm_clock': '19:04:00 32:06:34.974', "\
+                   "'vm_size': '1.7G', 'tag': 'snap1', 'id': '1'}]"
+
+        mox = self._mox
+        mox.StubOutWithMock(utils, 'execute')
+
+        cmd = ['env', 'LC_ALL=C', 'LANG=C',
+               'qemu-img', 'info', TEST_PATH]
+        utils.execute(*cmd, run_as_root=True).AndReturn(
+            (TEST_RETURN, 'ignored'))
+
+        mox.ReplayAll()
+
+        inf = image_utils.qemu_img_info(TEST_PATH)
+
+        self.assertEquals(inf.image, 'qemu.qcow2')
+        self.assertEquals(inf.backing_file, 'qemu.qcow2')
+        self.assertEquals(inf.file_format, 'qcow2')
+        self.assertEquals(inf.virtual_size, 52428800)
+        self.assertEquals(inf.cluster_size, 65536)
+        self.assertEquals(inf.disk_size, 200704)
+
+        self.assertEquals(inf.snapshots[0]['id'], '1')
+        self.assertEquals(inf.snapshots[0]['tag'], 'snap1')
+        self.assertEquals(inf.snapshots[0]['vm_size'], '1.7G')
+        self.assertEquals(inf.snapshots[0]['date'], '2011-10-04')
+        self.assertEquals(inf.snapshots[0]['vm_clock'],
+                          '19:04:00 32:06:34.974')
+
+        self.assertEquals(str(inf), TEST_STR)
+
     def test_fetch_to_raw(self):
         TEST_RET = "image: qemu.qcow2\n"\
                    "file_format: qcow2 \n"\
index 6384a25d8cfdf6a43cbb01611ac5e0de7fd7a36f..b16d5384c4912df28b4975b1cb371626feb3a2aa 100644 (file)
@@ -121,6 +121,20 @@ class GlusterfsDriver(nfs.RemoteFsDriver):
     def _local_path_volume_info(self, volume):
         return '%s%s' % (self._local_path_volume(volume), '.info')
 
+    def _qemu_img_info(self, path):
+        """Sanitize image_utils' qemu_img_info.
+
+        This code expects to deal only with relative filenames.
+        """
+
+        info = image_utils.qemu_img_info(path)
+        if info.image:
+            info.image = os.path.basename(info.image)
+        if info.backing_file:
+            info.backing_file = os.path.basename(info.backing_file)
+
+        return info
+
     def get_active_image_from_info(self, volume):
         """Returns filename of the active image from the info file."""
 
@@ -230,12 +244,7 @@ class GlusterfsDriver(nfs.RemoteFsDriver):
         else:
             out_format = 'raw'
 
-        command = ['qemu-img', 'convert',
-                   '-O', out_format,
-                   path_to_disk,
-                   path_to_new_vol]
-
-        self._execute(*command, run_as_root=True)
+        image_utils.convert_image(path_to_disk, path_to_new_vol, out_format)
 
     def delete_volume(self, volume):
         """Deletes a logical volume."""
@@ -437,9 +446,8 @@ class GlusterfsDriver(nfs.RemoteFsDriver):
                    'backing_file=%s' % backing_path_full_path, new_snap_path]
         self._execute(*command, run_as_root=True)
 
-        command = ['qemu-img', 'info', backing_path_full_path]
-        (out, err) = self._execute(*command, run_as_root=True)
-        backing_fmt = self._get_file_format(out)
+        info = self._qemu_img_info(backing_path_full_path)
+        backing_fmt = info.file_format
 
         command = ['qemu-img', 'rebase', '-u',
                    '-b', backing_filename,
@@ -529,9 +537,7 @@ class GlusterfsDriver(nfs.RemoteFsDriver):
         snapshot_path = '%s/%s' % (self._local_volume_dir(snapshot['volume']),
                                    snapshot_file)
 
-        if not os.path.exists(snapshot_path):
-            msg = _('Snapshot file at %s does not exist.') % snapshot_path
-            raise exception.InvalidSnapshot(msg)
+        snapshot_path_img_info = self._qemu_img_info(snapshot_path)
 
         vol_path = self._local_volume_dir(snapshot['volume'])
 
@@ -543,7 +549,7 @@ class GlusterfsDriver(nfs.RemoteFsDriver):
             # Online delete
             context = snapshot['context']
 
-            base_file = self._get_backing_file_for_path(snapshot_path)
+            base_file = snapshot_path_img_info.backing_file
             if base_file is None:
                 # There should always be at least the original volume
                 # file as base.
@@ -582,11 +588,10 @@ class GlusterfsDriver(nfs.RemoteFsDriver):
             # (guaranteed to|  (being deleted)  |
             #    exist)     |                   |
 
-            base_file = self._get_backing_file_for_path(snapshot_path)
-            snapshot_file_path = '%s/%s' % (vol_path, snapshot_file)
+            base_file = snapshot_path_img_info.backing_file
 
-            self._qemu_img_commit(snapshot_file_path)
-            self._execute('rm', '-f', snapshot_file_path, run_as_root=True)
+            self._qemu_img_commit(snapshot_path)
+            self._execute('rm', '-f', snapshot_path, run_as_root=True)
 
             # Remove snapshot_file from info
             info_path = self._local_path_volume(snapshot['volume']) + '.info'
@@ -643,8 +648,8 @@ class GlusterfsDriver(nfs.RemoteFsDriver):
             self._qemu_img_commit(higher_file_path)
             if highest_file is not None:
                 highest_file_path = '%s/%s' % (vol_path, highest_file)
-                snapshot_file_fmt = self._get_file_format_for_path(
-                    '%s/%s' % (vol_path, snapshot_file))
+                info = self._qemu_img_info(snapshot_path)
+                snapshot_file_fmt = info.file_format
 
                 backing_fmt = ('-F', snapshot_file_fmt)
                 self._execute('qemu-img', 'rebase', '-u',
@@ -754,34 +759,6 @@ class GlusterfsDriver(nfs.RemoteFsDriver):
             self._local_volume_dir(snapshot['volume']), file_to_delete)
         self._execute('rm', '-f', path_to_delete, run_as_root=True)
 
-    def _get_backing_file(self, output):
-        for line in output.split('\n'):
-            backing_file = None
-
-            m = re.search(r'(?<=backing\ file: )(.*)', line)
-            if m:
-                backing_file = m.group(0)
-
-            if backing_file is None:
-                continue
-
-            # Remove "(actual path: /mnt/asdf/a.img)" suffix added when
-            #  running from a different directory
-            backing_file = re.sub(r' \(actual path: .*$', '',
-                                  backing_file, count=1)
-
-            return os.path.basename(backing_file)
-
-    def _get_backing_file_for_path(self, path):
-        (out, err) = self._execute('qemu-img', 'info', path,
-                                   run_as_root=True)
-        return self._get_backing_file(out)
-
-    def _get_file_format_for_path(self, path):
-        (out, err) = self._execute('qemu-img', 'info', path,
-                                   run_as_root=True)
-        return self._get_file_format(out)
-
     def _get_backing_chain_for_path(self, volume, path):
         """Returns list of dicts containing backing-chain information.
 
@@ -798,20 +775,18 @@ class GlusterfsDriver(nfs.RemoteFsDriver):
 
         output = []
 
-        (out, _err) = self._execute('qemu-img', 'info', path)
+        info = self._qemu_img_info(path)
         new_info = {}
         new_info['filename'] = os.path.basename(path)
-        new_info['backing-filename'] = self._get_backing_file(out)
+        new_info['backing-filename'] = info.backing_file
 
         output.append(new_info)
 
-        while True:
+        while new_info['backing-filename']:
             filename = new_info['backing-filename']
             path = os.path.join(self._local_volume_dir(volume), filename)
-            (out, _err) = self._execute('qemu-img', 'info', path)
-            backing_filename = self._get_backing_file(out)
-            if backing_filename is None:
-                break
+            info = self._qemu_img_info(path)
+            backing_filename = info.backing_file
             new_info = {}
             new_info['filename'] = filename
             new_info['backing-filename'] = backing_filename
@@ -820,18 +795,6 @@ class GlusterfsDriver(nfs.RemoteFsDriver):
 
         return output
 
-    def _get_file_format(self, output):
-        for line in output.split('\n'):
-            m = re.search(r'(?<=file\ format: )(.*)', line)
-            if m:
-                return m.group(0)
-
-    def _get_backing_file_format(self, output):
-        for line in output.split('\n'):
-            m = re.search(r'(?<=backing\ file\ format: )(.*)', line)
-            if m:
-                return m.group(0)
-
     def _qemu_img_commit(self, path):
         return self._execute('qemu-img', 'commit', path, run_as_root=True)
 
@@ -868,9 +831,8 @@ class GlusterfsDriver(nfs.RemoteFsDriver):
             data['options'] = self.shares[volume['provider_location']]
 
         # Test file for raw vs. qcow2 format
-        (out, err) = self._execute('qemu-img', 'info', path,
-                                   run_as_root=True)
-        data['format'] = self._get_file_format(out)
+        info = self._qemu_img_info(path)
+        data['format'] = info.file_format
         if data['format'] not in ['raw', 'qcow2']:
             msg = _('%s must be a valid raw or qcow2 image.') % path
             raise exception.InvalidVolume(msg)
@@ -892,14 +854,14 @@ class GlusterfsDriver(nfs.RemoteFsDriver):
         active_file = self.get_active_image_from_info(volume)
         active_file_path = '%s/%s' % (self._local_volume_dir(volume),
                                       active_file)
-        backing_file = self._get_backing_file_for_path(active_file_path)
-        if backing_file is not None:
+        info = self._qemu_img_info(active_file_path)
+        backing_file = info.backing_file
+        if backing_file:
             snapshots_exist = True
         else:
             snapshots_exist = False
 
-        root_file_fmt = self._get_file_format_for_path(
-            self._local_path_volume(volume))
+        root_file_fmt = info.file_format
 
         temp_path = None
 
@@ -937,16 +899,15 @@ class GlusterfsDriver(nfs.RemoteFsDriver):
                     ' driver when no snapshots exist.')
             raise exception.InvalidVolume(msg)
 
-        (out, err) = self._execute('qemu-img', 'info', volume_path)
-        backing_fmt = self._get_file_format(out)
+        info = self._qemu_img_info(volume_path)
+        backing_fmt = info.file_format
 
         if backing_fmt not in ['raw', 'qcow2']:
             msg = _('Unrecognized backing format: %s')
             raise exception.InvalidVolume(msg % backing_fmt)
 
         # qemu-img can resize both raw and qcow2 files
-        cmd = ['qemu-img', 'resize', volume_path, '%sG' % size_gb]
-        self._execute(*cmd, run_as_root=True)
+        image_utils.resize_image(volume_path, size_gb)
 
     def _do_create_volume(self, volume):
         """Create a volume on given glusterfs_share.