From f8ac42460dca22f83a59097530f73b282129cc2b Mon Sep 17 00:00:00 2001 From: Eric Harney Date: Sun, 8 Sep 2013 10:18:18 -0400 Subject: [PATCH] GlusterFS: Use image_utils for qemu-img calls 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 | 381 +++++++++++++++++------------ cinder/tests/test_image_utils.py | 54 ++++ cinder/volume/drivers/glusterfs.py | 115 +++------ 3 files changed, 322 insertions(+), 228 deletions(-) diff --git a/cinder/tests/test_glusterfs.py b/cinder/tests/test_glusterfs.py index 70ed9ce8c..4f6c7aec6 100644 --- a/cinder/tests/test_glusterfs.py +++ b/cinder/tests/test_glusterfs.py @@ -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']) diff --git a/cinder/tests/test_image_utils.py b/cinder/tests/test_image_utils.py index fd1d29b9c..967aa3818 100644 --- a/cinder/tests/test_image_utils.py +++ b/cinder/tests/test_image_utils.py @@ -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"\ diff --git a/cinder/volume/drivers/glusterfs.py b/cinder/volume/drivers/glusterfs.py index 6384a25d8..b16d5384c 100644 --- a/cinder/volume/drivers/glusterfs.py +++ b/cinder/volume/drivers/glusterfs.py @@ -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. -- 2.45.2