From 20f3a9a387d2c3b913dfa8edd429d50ac043a839 Mon Sep 17 00:00:00 2001 From: Taylor Jakobson Date: Wed, 3 May 2017 13:15:04 -0500 Subject: [PATCH] Add support for file-backed ephemeral disk Adds support for file-backed ephemeral disk as a localdisk replacement for lvm. Supports both RAW and QCOW2 files. Resizing a file-backed instance is not currently supported. partially-implements: bp file-io-driver Change-Id: I1fd499a776cc2e05246c1fc453151aa7bd712dc8 --- doc/source/devref/project_structure.rst | 5 ++- .../tests/virt/powervm/disk/test_localdisk.py | 32 ++++++++++++++++--- nova_powervm/virt/powervm/disk/localdisk.py | 19 +++++++++-- 3 files changed, 49 insertions(+), 7 deletions(-) diff --git a/doc/source/devref/project_structure.rst b/doc/source/devref/project_structure.rst index 9116e967..2d60097b 100644 --- a/doc/source/devref/project_structure.rst +++ b/doc/source/devref/project_structure.rst @@ -63,7 +63,10 @@ Two disk implementations exist currently. physical disks local to their system. Images will be cached on the same volume group as the VMs. The cached images will be periodically cleaned up by the Nova imagecache manager, at a rate determined by the ``nova.conf`` - setting: image_cache_manager_interval. + setting: image_cache_manager_interval. Also supports file-backed ephemeral + storage, which is specified by using the ``QCOW VG - default`` volume group. + Note: Resizing instances with file-backed ephemeral is not currently + supported. * Shared Storage Pool - utilizes PowerVM's distributed storage. As such this implementation allows operators to make use of live migration capabilities. diff --git a/nova_powervm/tests/virt/powervm/disk/test_localdisk.py b/nova_powervm/tests/virt/powervm/disk/test_localdisk.py index 30a49e7a..adb15af5 100644 --- a/nova_powervm/tests/virt/powervm/disk/test_localdisk.py +++ b/nova_powervm/tests/virt/powervm/disk/test_localdisk.py @@ -30,6 +30,7 @@ from nova_powervm.tests.virt.powervm import fixtures as fx from nova_powervm.virt.powervm.disk import driver as disk_dvr from nova_powervm.virt.powervm.disk import localdisk as ld from nova_powervm.virt.powervm import exception as npvmex +from nova_powervm.virt.powervm import vm class TestLocalDisk(test.TestCase): @@ -126,7 +127,8 @@ class TestLocalDisk(test.TestCase): self.apt, 'vios-uuid', self.vg_uuid, mock_it2f.return_value, 'i_3e865d14_8c1e', powervm.TEST_IMAGE1.size, d_size=powervm.TEST_IMAGE1.size, - upload_type=tsk_stor.UploadType.IO_STREAM) + upload_type=tsk_stor.UploadType.IO_STREAM, + file_format=powervm.TEST_IMAGE1.disk_format) mock_it2f.assert_called_once_with(mock_img_api.return_value) mock_img_api.assert_called_once_with('ctx', powervm.TEST_IMAGE1.id) @@ -246,10 +248,13 @@ class TestLocalDisk(test.TestCase): # to match the FeedTask. local = self.get_ls(self.apt) inst = mock.Mock(uuid=fx.FAKE_INST_UUID) - + lpar_uuid = vm.get_pvm_uuid(inst) + mock_disk = mock.Mock() # As initialized above, remove_maps returns True to trigger update. - local.connect_disk(inst, mock.Mock(), stg_ftsk=None) + local.connect_disk(inst, mock_disk, stg_ftsk=None) self.assertEqual(1, mock_add_map.call_count) + mock_build_map.assert_called_once_with( + 'host_uuid', self.vio_to_vg, lpar_uuid, mock_disk) mock_add_map.assert_called_once_with(feed[0], 'fake_map') self.assertEqual(1, self.vio_to_vg.update.call_count) @@ -304,7 +309,6 @@ class TestLocalDisk(test.TestCase): @mock.patch('pypowervm.wrappers.storage.VG', autospec=True) def test_extend_disk_not_found(self, mock_vg): local = self.get_ls(self.apt) - inst = mock.Mock() inst.name = 'Name Of Instance' inst.uuid = 'd5065c2c-ac43-3fa6-af32-ea84a3960291' @@ -325,6 +329,26 @@ class TestLocalDisk(test.TestCase): self.assertEqual(1, resp.update.call_count) self.assertEqual(vdisk.capacity, 1000) + @mock.patch('pypowervm.wrappers.storage.VG', autospec=True) + def test_extend_disk_file_format(self, mock_vg): + local = self.get_ls(self.apt) + inst = mock.Mock() + inst.name = 'Name Of Instance' + inst.uuid = 'd5065c2c-ac43-3fa6-af32-ea84a3960291' + + vdisk = mock.Mock(name='vdisk') + vdisk.configure_mock(name='/path/to/b_Name_Of__d506', + backstore_type=pvm_stor.BackStoreType.USER_QCOW, + file_format=pvm_stor.FileFormatType.QCOW2) + resp = mock.Mock(name='response') + resp.virtual_disks = [vdisk] + mock_vg.get.return_value = resp + self.assertRaises(nova_exc.ResizeError, local.extend_disk, + inst, dict(type='boot'), 10) + vdisk.file_format = pvm_stor.FileFormatType.RAW + self.assertRaises(nova_exc.ResizeError, local.extend_disk, + inst, dict(type='boot'), 10) + def _bld_mocks_for_instance_disk(self): inst = mock.Mock() inst.name = 'Name Of Instance' diff --git a/nova_powervm/virt/powervm/disk/localdisk.py b/nova_powervm/virt/powervm/disk/localdisk.py index b05e3f30..ea1b439c 100644 --- a/nova_powervm/virt/powervm/disk/localdisk.py +++ b/nova_powervm/virt/powervm/disk/localdisk.py @@ -33,6 +33,7 @@ from nova_powervm import conf as cfg from nova_powervm.virt.powervm.disk import driver as disk_dvr from nova_powervm.virt.powervm.disk import imagecache from nova_powervm.virt.powervm import exception as npvmex +from nova_powervm.virt.powervm.i18n import _ from nova_powervm.virt.powervm.i18n import _LE from nova_powervm.virt.powervm.i18n import _LI from nova_powervm.virt.powervm import vm @@ -247,7 +248,8 @@ class LocalStorage(disk_dvr.DiskAdapter): disk_dvr.IterableToFileAdapter( IMAGE_API.download(context, image_meta.id)), cache_name, image_meta.size, d_size=image_meta.size, - upload_type=tsk_stg.UploadType.IO_STREAM)[0] + upload_type=tsk_stg.UploadType.IO_STREAM, + file_format=image_meta.disk_format)[0] return image.udid def connect_disk(self, instance, disk_info, stg_ftsk=None): @@ -285,6 +287,17 @@ class LocalStorage(disk_dvr.DiskAdapter): if stg_ftsk.name == 'localdisk': stg_ftsk.execute() + def _validate_resizable(self, vdisk): + """Validates that VDisk supports resizing + + :param vdisk: The VDisk to be resized + :raise ResizeError: If resizing is not supported for the given VDisk. + """ + if (vdisk.backstore_type == pvm_stg.BackStoreType.USER_QCOW): + raise nova_exc.ResizeError( + reason=_("Resizing file-backed instances is not currently" + "supported.")) + def extend_disk(self, instance, disk_info, size): """Extends the disk. @@ -299,7 +312,8 @@ class LocalStorage(disk_dvr.DiskAdapter): vdisks = vg_wrap.virtual_disks disk_found = None for vdisk in vdisks: - if vdisk.name == vol_name: + # Vdisk name can be either disk_name or /path/to/disk_name + if vdisk.name.split('/')[-1] == vol_name.split('/')[-1]: disk_found = vdisk break @@ -308,6 +322,7 @@ class LocalStorage(disk_dvr.DiskAdapter): instance=instance) raise nova_exc.DiskNotFound( location=self.vg_name + '/' + vol_name) + self._validate_resizable(disk_found) # Set the new size disk_found.capacity = size