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 87cfec1f..1c33d410 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): @@ -127,7 +128,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) @@ -247,10 +249,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) @@ -305,7 +310,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' @@ -326,6 +330,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 9b3f7d2c..8d206bec 100644 --- a/nova_powervm/virt/powervm/disk/localdisk.py +++ b/nova_powervm/virt/powervm/disk/localdisk.py @@ -34,6 +34,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 @@ -245,7 +246,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): @@ -283,6 +285,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. @@ -297,7 +310,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 @@ -306,6 +320,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