]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
VMware: Relocate volume only during no disk space
authorVipin Balachandran <vbala@vmware.com>
Tue, 13 Oct 2015 06:49:08 +0000 (12:19 +0530)
committerVipin Balachandran <vbala@vmware.com>
Fri, 16 Oct 2015 06:41:26 +0000 (12:11 +0530)
During volume extend, we try to relocate the volume to a
different datastore if there is an error during vCenter
extend API call. But we need to relocate the volume to a
different datastore only if there is no space in the
current datastore to accommodate the extended volume. This
patch fixes the issue by relocating the volume only if the
exception raised is NoDiskSpaceException. It also refactors
the code to remove a redundant helper method for extending
virtual disk.

Closes-Bug: #1506422
Change-Id: Ic64eb697b49ffb3a435ffc4d969b5b7f79af91c6

cinder/tests/unit/test_vmware_vmdk.py
cinder/volume/drivers/vmware/vmdk.py
cinder/volume/drivers/vmware/volumeops.py

index d8d4f276ad5f7fb986b699cea91c5ba717fe194a..e7fba4a55d10dc03ce71f9bcdd3fb64adcce7ca0 100644 (file)
@@ -342,90 +342,6 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
         self.assertRaises(cinder_exceptions.InvalidVolume,
                           self._driver.delete_snapshot, snapshot)
 
-    @mock.patch.object(VMDK_DRIVER, '_select_ds_for_volume')
-    @mock.patch.object(VMDK_DRIVER, '_extend_vmdk_virtual_disk')
-    @mock.patch.object(VMDK_DRIVER, 'volumeops')
-    def test_extend_volume(self, volume_ops, _extend_virtual_disk,
-                           _select_ds_for_volume):
-        """Test extend_volume."""
-        self._test_extend_volume(volume_ops, _extend_virtual_disk,
-                                 _select_ds_for_volume)
-
-    def _test_extend_volume(self, volume_ops, _extend_virtual_disk,
-                            _select_ds_for_volume):
-        fake_name = u'volume-00000001'
-        new_size = '21'
-        fake_size = '20'
-        fake_vol = {'project_id': 'testprjid', 'name': fake_name,
-                    'size': fake_size,
-                    'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66'}
-        fake_host = mock.sentinel.host
-        fake_rp = mock.sentinel.rp
-        fake_folder = mock.sentinel.folder
-        fake_summary = mock.Mock(spec=object)
-        fake_summary.datastore = mock.sentinel.datastore
-        fake_summary.name = 'fake_name'
-        fake_backing = mock.sentinel.backing
-        volume_ops.get_backing.return_value = fake_backing
-
-        # If there is enough space in the datastore, where the volume is
-        # located, then the rest of this method will not be called.
-        self._driver.extend_volume(fake_vol, new_size)
-        _extend_virtual_disk.assert_called_with(fake_name, new_size)
-        self.assertFalse(_select_ds_for_volume.called)
-        self.assertFalse(volume_ops.get_backing.called)
-        self.assertFalse(volume_ops.relocate_backing.called)
-        self.assertFalse(volume_ops.move_backing_to_folder.called)
-
-        # If there is not enough space in the datastore, where the volume is
-        # located, then the rest of this method will be called. The first time
-        # _extend_virtual_disk is called, VimFaultException is raised. The
-        # second time it is called, there is no exception.
-        _extend_virtual_disk.reset_mock()
-        _extend_virtual_disk.side_effect = [exceptions.
-                                            VimFaultException([],
-                                                              'Error'), None]
-        # When _select_ds_for_volume raises no exception.
-        _select_ds_for_volume.return_value = (fake_host, fake_rp,
-                                              fake_folder, fake_summary)
-        self._driver.extend_volume(fake_vol, new_size)
-        _select_ds_for_volume.assert_called_with(new_size)
-        volume_ops.get_backing.assert_called_with(fake_name)
-        volume_ops.relocate_backing.assert_called_with(fake_backing,
-                                                       fake_summary.datastore,
-                                                       fake_rp,
-                                                       fake_host)
-        _extend_virtual_disk.assert_called_with(fake_name, new_size)
-        volume_ops.move_backing_to_folder.assert_called_with(fake_backing,
-                                                             fake_folder)
-
-        # If get_backing raises error_util.VimException,
-        # this exception will be caught for volume extend.
-        _extend_virtual_disk.reset_mock()
-        _extend_virtual_disk.side_effect = [exceptions.
-                                            VimFaultException([],
-                                                              'Error'), None]
-        volume_ops.get_backing.side_effect = exceptions.VimException('Error')
-        self.assertRaises(exceptions.VimException, self._driver.extend_volume,
-                          fake_vol, new_size)
-
-        # If _select_ds_for_volume raised an exception, the rest code will
-        # not be called.
-        _extend_virtual_disk.reset_mock()
-        volume_ops.get_backing.reset_mock()
-        volume_ops.relocate_backing.reset_mock()
-        volume_ops.move_backing_to_folder.reset_mock()
-        _extend_virtual_disk.side_effect = [exceptions.
-                                            VimFaultException([],
-                                                              'Error'), None]
-        _select_ds_for_volume.side_effect = exceptions.VimException('Error')
-        self.assertRaises(exceptions.VimException, self._driver.extend_volume,
-                          fake_vol, new_size)
-        _extend_virtual_disk.assert_called_once_with(fake_name, new_size)
-        self.assertFalse(volume_ops.get_backing.called)
-        self.assertFalse(volume_ops.relocate_backing.called)
-        self.assertFalse(volume_ops.move_backing_to_folder.called)
-
     def test_copy_image_to_volume_non_vmdk(self):
         """Test copy_image_to_volume for a non-vmdk disk format."""
         fake_context = mock.sentinel.context
@@ -439,7 +355,7 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
                           fake_context, fake_volume,
                           image_service, fake_image_id)
 
-    @mock.patch.object(VMDK_DRIVER, '_extend_vmdk_virtual_disk')
+    @mock.patch.object(VMDK_DRIVER, '_extend_backing')
     @mock.patch('oslo_utils.uuidutils.generate_uuid')
     @mock.patch.object(VMDK_DRIVER, '_select_ds_for_volume')
     @mock.patch.object(VMDK_DRIVER, 'volumeops')
@@ -453,7 +369,7 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
     def test_copy_image_to_volume_non_stream_optimized(
             self, create_backing, get_ds_name_folder_path, get_disk_type,
             create_disk_from_sparse_image, create_disk_from_preallocated_image,
-            vops, select_ds_for_volume, generate_uuid, extend_disk):
+            vops, select_ds_for_volume, generate_uuid, extend_backing):
         self._test_copy_image_to_volume_non_stream_optimized(
             create_backing,
             get_ds_name_folder_path,
@@ -463,12 +379,12 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
             vops,
             select_ds_for_volume,
             generate_uuid,
-            extend_disk)
+            extend_backing)
 
     def _test_copy_image_to_volume_non_stream_optimized(
             self, create_backing, get_ds_name_folder_path, get_disk_type,
             create_disk_from_sparse_image, create_disk_from_preallocated_image,
-            vops, select_ds_for_volume, generate_uuid, extend_disk):
+            vops, select_ds_for_volume, generate_uuid, extend_backing):
         image_size_in_bytes = 2 * units.Gi
         adapter_type = 'lsiLogic'
         image_meta = {'disk_format': 'vmdk',
@@ -542,8 +458,9 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
         vops.delete_backing.assert_called_once_with(backing)
         vops.update_backing_disk_uuid.assert_called_once_with(clone,
                                                               volume['id'])
-        self.assertFalse(extend_disk.called)
+        self.assertFalse(extend_backing.called)
 
+        vops.get_backing.return_value = backing
         vops.get_disk_size.return_value = 1 * units.Gi
         create_backing.reset_mock()
         vops.attach_disk_to_backing.reset_mock()
@@ -565,9 +482,9 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
             adapter_type, path.get_descriptor_ds_file_path())
         vops.update_backing_disk_uuid.assert_called_once_with(backing,
                                                               volume['id'])
-        extend_disk.assert_called_once_with(volume['name'], volume['size'])
+        extend_backing.assert_called_once_with(backing, volume['size'])
 
-        extend_disk.reset_mock()
+        extend_backing.reset_mock()
         create_disk_from_preallocated_image.side_effect = (
             exceptions.VimException("Error"))
 
@@ -575,7 +492,7 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
                           self._driver.copy_image_to_volume,
                           context, volume, image_service, image_id)
         vops.delete_backing.assert_called_once_with(backing)
-        self.assertFalse(extend_disk.called)
+        self.assertFalse(extend_backing.called)
 
     @mock.patch.object(VMDK_DRIVER, '_copy_temp_virtual_disk')
     @mock.patch.object(VMDK_DRIVER, '_get_temp_image_folder')
@@ -772,7 +689,7 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
         self.assertEqual(dest_path, ret)
 
     @mock.patch.object(image_transfer, 'download_stream_optimized_image')
-    @mock.patch.object(VMDK_DRIVER, '_extend_vmdk_virtual_disk')
+    @mock.patch.object(VMDK_DRIVER, '_extend_backing')
     @mock.patch.object(VMDK_DRIVER, '_select_ds_for_volume')
     @mock.patch.object(VMDK_DRIVER, '_get_storage_profile_id')
     @mock.patch.object(VMDK_DRIVER, 'session')
@@ -782,7 +699,7 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
                                                    session,
                                                    get_profile_id,
                                                    _select_ds_for_volume,
-                                                   _extend_virtual_disk,
+                                                   extend_backing,
                                                    download_image):
         """Test copy_image_to_volume.
 
@@ -792,14 +709,14 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
                                                          session,
                                                          get_profile_id,
                                                          _select_ds_for_volume,
-                                                         _extend_virtual_disk,
+                                                         extend_backing,
                                                          download_image)
 
     def _test_copy_image_to_volume_stream_optimized(self, volumeops,
                                                     session,
                                                     get_profile_id,
                                                     _select_ds_for_volume,
-                                                    _extend_virtual_disk,
+                                                    extend_backing,
                                                     download_image):
         fake_context = mock.Mock()
         fake_backing = mock.sentinel.backing
@@ -845,7 +762,7 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
         self.assertFalse(volumeops.get_create_spec.called)
 
         # If the volume size is greater then than the backing's disk size,
-        # _extend_vmdk_virtual_disk will be called.
+        # _extend_backing will be called.
         _select_ds_for_volume.side_effect = None
         _select_ds_for_volume.return_value = (fake_host, fake_rp,
                                               fake_folder, fake_summary)
@@ -884,18 +801,17 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
                                           image_size=size)
         volumeops.update_backing_disk_uuid.assert_called_once_with(
             backing, fake_volume['id'])
-        _extend_virtual_disk.assert_called_once_with(fake_volume['name'],
-                                                     fake_volume_size)
+        extend_backing.assert_called_once_with(backing, fake_volume_size)
 
         # If the volume size is not greater then than backing's disk size,
-        # _extend_vmdk_virtual_disk will not be called.
+        # _extend_backing will not be called.
         volumeops.get_disk_size.return_value = fake_volume_size * units.Gi
-        _extend_virtual_disk.reset_mock()
+        extend_backing.reset_mock()
 
         self._driver.copy_image_to_volume(fake_context, fake_volume,
                                           image_service, fake_image_id)
 
-        self.assertFalse(_extend_virtual_disk.called)
+        self.assertFalse(extend_backing.called)
 
         # If fetch_stream_optimized_image raises an exception,
         # get_backing and delete_backing will be called.
@@ -907,7 +823,7 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
                           image_service, fake_image_id)
         volumeops.get_backing.assert_called_with(fake_volume['name'])
         volumeops.delete_backing.assert_called_with(fake_backing)
-        self.assertFalse(_extend_virtual_disk.called)
+        self.assertFalse(extend_backing.called)
 
     def test_copy_volume_to_image_non_vmdk(self):
         """Test copy_volume_to_image for a non-vmdk disk format."""
@@ -1200,45 +1116,21 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
         self.assertFalse(vops.change_backing_profile.called)
 
     @mock.patch.object(VMDK_DRIVER, 'volumeops')
-    def test_extend_vmdk_virtual_disk(self, volume_ops):
-        """Test vmdk._extend_vmdk_virtual_disk."""
-        self._test_extend_vmdk_virtual_disk(volume_ops)
+    def test_extend_backing(self, vops):
+        vmdk_path = mock.sentinel.vmdk_path
+        vops.get_vmdk_path.return_value = vmdk_path
+        dc = mock.sentinel.datacenter
+        vops.get_dc.return_value = dc
 
-    def _test_extend_vmdk_virtual_disk(self, volume_ops):
-        fake_backing = mock.sentinel.backing
-        fake_vmdk_path = "[datastore] dest_vm/dest_vm.vmdk"
-        fake_dc = mock.sentinel.datacenter
-        fake_name = 'fake_name'
-        fake_size = 7
-
-        # If the backing is None, get_vmdk_path and get_dc
-        # will not be called
-        volume_ops.get_backing.return_value = None
-        volume_ops.get_vmdk_path.return_value = fake_vmdk_path
-        volume_ops.get_dc.return_value = fake_dc
-        self._driver._extend_vmdk_virtual_disk(fake_name, fake_size)
-        volume_ops.get_backing.assert_called_once_with(fake_name)
-        self.assertFalse(volume_ops.get_vmdk_path.called)
-        self.assertFalse(volume_ops.get_dc.called)
-        self.assertFalse(volume_ops.extend_virtual_disk.called)
-
-        # Reset the mock and set the backing with a fake,
-        # all the mocks should be called.
-        volume_ops.get_backing.reset_mock()
-        volume_ops.get_backing.return_value = fake_backing
-        self._driver._extend_vmdk_virtual_disk(fake_name, fake_size)
-        volume_ops.get_vmdk_path.assert_called_once_with(fake_backing)
-        volume_ops.get_dc.assert_called_once_with(fake_backing)
-        volume_ops.extend_virtual_disk.assert_called_once_with(fake_size,
-                                                               fake_vmdk_path,
-                                                               fake_dc)
-
-        # Test the exceptional case for extend_virtual_disk
-        volume_ops.extend_virtual_disk.side_effect = exceptions.VimException(
-            'VimException raised.')
-        self.assertRaises(exceptions.VimException,
-                          self._driver._extend_vmdk_virtual_disk,
-                          fake_name, fake_size)
+        backing = mock.sentinel.backing
+        new_size = 1
+        self._driver._extend_backing(backing, new_size)
+
+        vops.get_vmdk_path.assert_called_once_with(backing)
+        vops.get_dc.assert_called_once_with(backing)
+        vops.extend_virtual_disk.assert_called_once_with(new_size,
+                                                         vmdk_path,
+                                                         dc)
 
     @mock.patch.object(image_transfer, 'copy_stream_optimized_disk')
     @mock.patch('cinder.volume.drivers.vmware.vmdk.open', create=True)
@@ -1844,9 +1736,9 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
         vops.create_vm_inventory_folder.assert_called_once_with(
             datacenter, ['OpenStack', project_folder_name, self.VOLUME_FOLDER])
 
-    @mock.patch.object(VMDK_DRIVER, '_extend_vmdk_virtual_disk')
+    @mock.patch.object(VMDK_DRIVER, '_extend_backing')
     @mock.patch.object(VMDK_DRIVER, 'volumeops')
-    def test_clone_backing_linked(self, volume_ops, _extend_vmdk_virtual_disk):
+    def test_clone_backing_linked(self, volume_ops, extend_backing):
         """Test _clone_backing with clone type - linked."""
         clone = mock.sentinel.clone
         volume_ops.clone_backing.return_value = clone
@@ -1877,25 +1769,24 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
             clone, fake_volume['id'])
 
         # If the volume size is greater than the original snapshot size,
-        # _extend_vmdk_virtual_disk will be called.
-        _extend_vmdk_virtual_disk.assert_called_with(fake_volume['name'],
-                                                     fake_volume['size'])
+        # _extend_backing will be called.
+        extend_backing.assert_called_with(clone, fake_volume['size'])
 
         # If the volume size is not greater than the original snapshot size,
-        # _extend_vmdk_virtual_disk will not be called.
+        # _extend_backing will not be called.
         fake_size = 2
         fake_volume['size'] = fake_size
-        _extend_vmdk_virtual_disk.reset_mock()
+        extend_backing.reset_mock()
         self._driver._clone_backing(fake_volume, fake_backing, fake_snapshot,
                                     volumeops.LINKED_CLONE_TYPE,
                                     fake_snapshot['volume_size'])
-        self.assertFalse(_extend_vmdk_virtual_disk.called)
+        self.assertFalse(extend_backing.called)
 
-    @mock.patch.object(VMDK_DRIVER, '_extend_vmdk_virtual_disk')
+    @mock.patch.object(VMDK_DRIVER, '_extend_backing')
     @mock.patch.object(VMDK_DRIVER, '_select_ds_for_volume')
     @mock.patch.object(VMDK_DRIVER, 'volumeops')
     def test_clone_backing_full(self, volume_ops, _select_ds_for_volume,
-                                _extend_vmdk_virtual_disk):
+                                extend_backing):
         """Test _clone_backing with clone type - full."""
         fake_host = mock.sentinel.host
         fake_folder = mock.sentinel.folder
@@ -1936,19 +1827,18 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
             clone, fake_volume['id'])
 
         # If the volume size is greater than the original snapshot size,
-        # _extend_vmdk_virtual_disk will be called.
-        _extend_vmdk_virtual_disk.assert_called_with(fake_volume['name'],
-                                                     fake_volume['size'])
+        # _extend_backing will be called.
+        extend_backing.assert_called_with(clone, fake_volume['size'])
 
         # If the volume size is not greater than the original snapshot size,
-        # _extend_vmdk_virtual_disk will not be called.
+        # _extend_backing will not be called.
         fake_size = 2
         fake_volume['size'] = fake_size
-        _extend_vmdk_virtual_disk.reset_mock()
+        extend_backing.reset_mock()
         self._driver._clone_backing(fake_volume, fake_backing, fake_snapshot,
                                     volumeops.FULL_CLONE_TYPE,
                                     fake_snapshot['volume_size'])
-        self.assertFalse(_extend_vmdk_virtual_disk.called)
+        self.assertFalse(extend_backing.called)
 
     @mock.patch('cinder.volume.drivers.vmware.vmdk.VMwareVcVmdkDriver.'
                 'volumeops', new_callable=mock.PropertyMock)
@@ -2546,6 +2436,88 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
             cacert=self._config.vmware_ca_file,
             insecure=self._config.vmware_insecure)
 
+    @mock.patch.object(VMDK_DRIVER, 'volumeops')
+    @mock.patch.object(VMDK_DRIVER, '_extend_backing')
+    def test_extend_volume_with_no_backing(self, extend_backing, vops):
+        vops.get_backing.return_value = None
+
+        volume = {'name': 'volume-51e47214-8e3c-475d-b44b-aea6cd3eef53',
+                  'volume_type_id': None, 'size': 1,
+                  'id': '51e47214-8e3c-475d-b44b-aea6cd3eef53',
+                  'display_name': 'foo'}
+        self._driver.extend_volume(volume, 2)
+
+        self.assertFalse(extend_backing.called)
+
+    @mock.patch.object(VMDK_DRIVER, 'volumeops')
+    @mock.patch.object(VMDK_DRIVER, '_extend_backing')
+    def test_extend_volume(self, extend_backing, vops):
+        backing = mock.sentinel.backing
+        vops.get_backing.return_value = backing
+
+        volume = {'name': 'volume-51e47214-8e3c-475d-b44b-aea6cd3eef53',
+                  'volume_type_id': None, 'size': 1,
+                  'id': '51e47214-8e3c-475d-b44b-aea6cd3eef53',
+                  'display_name': 'foo'}
+        new_size = 2
+        self._driver.extend_volume(volume, new_size)
+
+        extend_backing.assert_called_once_with(backing, new_size)
+
+    @mock.patch.object(VMDK_DRIVER, 'volumeops')
+    @mock.patch.object(VMDK_DRIVER, '_extend_backing')
+    @mock.patch.object(VMDK_DRIVER, '_select_ds_for_volume')
+    def test_extend_volume_with_no_disk_space(self, select_ds_for_volume,
+                                              extend_backing, vops):
+        backing = mock.sentinel.backing
+        vops.get_backing.return_value = backing
+
+        extend_backing.side_effect = [exceptions.NoDiskSpaceException, None]
+
+        host = mock.sentinel.host
+        rp = mock.sentinel.rp
+        folder = mock.sentinel.folder
+        datastore = mock.sentinel.datastore
+        summary = mock.Mock(datastore=datastore)
+        select_ds_for_volume.return_value = (host, rp, folder, summary)
+
+        volume = {'name': 'volume-51e47214-8e3c-475d-b44b-aea6cd3eef53',
+                  'volume_type_id': None, 'size': 1,
+                  'id': '51e47214-8e3c-475d-b44b-aea6cd3eef53',
+                  'display_name': 'foo'}
+        new_size = 2
+        self._driver.extend_volume(volume, new_size)
+
+        create_params = {vmdk.CREATE_PARAM_DISK_SIZE: new_size}
+        select_ds_for_volume.assert_called_once_with(
+            volume, create_params=create_params)
+
+        vops.relocate_backing.assert_called_once_with(backing, datastore, rp,
+                                                      host)
+        vops.move_backing_to_folder(backing, folder)
+
+        extend_backing_calls = [mock.call(backing, new_size),
+                                mock.call(backing, new_size)]
+        self.assertEqual(extend_backing_calls, extend_backing.call_args_list)
+
+    @mock.patch.object(VMDK_DRIVER, 'volumeops')
+    @mock.patch.object(VMDK_DRIVER, '_extend_backing')
+    def test_extend_volume_with_extend_backing_error(
+            self, extend_backing, vops):
+        backing = mock.sentinel.backing
+        vops.get_backing.return_value = backing
+
+        extend_backing.side_effect = exceptions.VimException("Error")
+
+        volume = {'name': 'volume-51e47214-8e3c-475d-b44b-aea6cd3eef53',
+                  'volume_type_id': None, 'size': 1,
+                  'id': '51e47214-8e3c-475d-b44b-aea6cd3eef53',
+                  'display_name': 'foo'}
+        new_size = 2
+        self.assertRaises(exceptions.VimException, self._driver.extend_volume,
+                          volume, new_size)
+        extend_backing.assert_called_once_with(backing, new_size)
+
 
 class ImageDiskTypeTest(test.TestCase):
     """Unit tests for ImageDiskType."""
index e331519babd8805475ed9623d86c6ded0cfd062f..aff290608dc086a746ca9662293016e88407905f 100644 (file)
@@ -57,6 +57,7 @@ EAGER_ZEROED_THICK_VMDK_TYPE = 'eagerZeroedThick'
 CREATE_PARAM_ADAPTER_TYPE = 'adapter_type'
 CREATE_PARAM_DISK_LESS = 'disk_less'
 CREATE_PARAM_BACKING_NAME = 'name'
+CREATE_PARAM_DISK_SIZE = 'disk_size'
 
 TMP_IMAGES_DATASTORE_FOLDER_PATH = "cinder_temp/"
 
@@ -480,8 +481,11 @@ class VMwareVcVmdkDriver(driver.VolumeDriver):
         :return: (host, resource_pool, folder, summary)
         """
         # Form requirements for datastore selection.
+        create_params = create_params or {}
+        size = create_params.get(CREATE_PARAM_DISK_SIZE, volume['size'])
+
         req = {}
-        req[hub.DatastoreSelector.SIZE_BYTES] = (volume['size'] * units.Gi)
+        req[hub.DatastoreSelector.SIZE_BYTES] = size * units.Gi
         req[hub.DatastoreSelector.PROFILE_NAME] = self._get_storage_profile(
             volume)
 
@@ -1043,39 +1047,16 @@ class VMwareVcVmdkDriver(driver.VolumeDriver):
         LOG.info(_LI("Done copying image: %(id)s to volume: %(vol)s."),
                  {'id': image_id, 'vol': volume['name']})
 
-    def _extend_vmdk_virtual_disk(self, name, new_size_in_gb):
-        """Extend the size of the vmdk virtual disk to the new size.
+    def _extend_backing(self, backing, new_size_in_gb):
+        """Extend volume backing's virtual disk.
 
-        :param name: the name of the volume
-        :param new_size_in_gb: the new size the vmdk virtual disk extends to
-        """
-        backing = self.volumeops.get_backing(name)
-        if not backing:
-            LOG.info(_LI("The backing is not found, so there is no need "
-                         "to extend the vmdk virtual disk for the volume "
-                         "%s."), name)
-        else:
-            root_vmdk_path = self.volumeops.get_vmdk_path(backing)
-            datacenter = self.volumeops.get_dc(backing)
-            self._extend_volumeops_virtual_disk(new_size_in_gb, root_vmdk_path,
-                                                datacenter)
-
-    def _extend_volumeops_virtual_disk(self, new_size_in_gb, root_vmdk_path,
-                                       datacenter):
-        """Call the ExtendVirtualDisk_Task.
-
-        :param new_size_in_gb: the new size the vmdk virtual disk extends to
-        :param root_vmdk_path: the path for the vmdk file
-        :param datacenter: reference to the datacenter
+        :param backing: volume backing
+        :param new_size_in_gb: new size of virtual disk
         """
-        try:
-            self.volumeops.extend_virtual_disk(new_size_in_gb,
-                                               root_vmdk_path, datacenter)
-        except exceptions.VimException:
-            with excutils.save_and_reraise_exception():
-                LOG.exception(_LE("Unable to extend the size of the "
-                                  "vmdk virtual disk at the path %s."),
-                              root_vmdk_path)
+        root_vmdk_path = self.volumeops.get_vmdk_path(backing)
+        datacenter = self.volumeops.get_dc(backing)
+        self.volumeops.extend_virtual_disk(new_size_in_gb, root_vmdk_path,
+                                           datacenter)
 
     def copy_image_to_volume(self, context, volume, image_service, image_id):
         """Creates volume from image.
@@ -1152,7 +1133,7 @@ class VMwareVcVmdkDriver(driver.VolumeDriver):
                       {'name': volume['name'],
                        'vol_size': volume_size,
                        'disk_size': disk_size})
-            self._extend_vmdk_virtual_disk(volume['name'], volume['size'])
+            self._extend_backing(backing, volume['size'])
         # TODO(vbala): handle volume_size < disk_size case.
 
     def copy_volume_to_image(self, context, volume, image_service, image_meta):
@@ -1383,50 +1364,58 @@ class VMwareVcVmdkDriver(driver.VolumeDriver):
         return True
 
     def extend_volume(self, volume, new_size):
-        """Extend vmdk to new_size.
+        """Extend volume to new size.
 
-        Extends the vmdk backing to new volume size. First try to extend in
-        place on the same datastore. If that fails, try to relocate the volume
-        to a different datastore that can accommodate the new_size'd volume.
+        Extends the volume backing's virtual disk to new size. First, try to
+        extend in place on the same datastore. If that fails due to
+        insufficient disk space, then try to relocate the volume to a different
+        datastore that can accommodate the backing with new size and retry
+        extend.
 
         :param volume: dictionary describing the existing 'available' volume
         :param new_size: new size in GB to extend this volume to
         """
         vol_name = volume['name']
+        backing = self.volumeops.get_backing(vol_name)
+        if not backing:
+            LOG.info(_LI("There is no backing for volume: %s; no need to "
+                         "extend the virtual disk."), vol_name)
+            return
+
         # try extending vmdk in place
         try:
-            self._extend_vmdk_virtual_disk(vol_name, new_size)
-            LOG.info(_LI("Done extending volume %(vol)s "
-                         "to size %(size)s GB."),
+            self._extend_backing(backing, new_size)
+            LOG.info(_LI("Successfully extended volume: %(vol)s to size: "
+                         "%(size)s GB."),
                      {'vol': vol_name, 'size': new_size})
             return
-        except exceptions.VimFaultException:
-            LOG.info(_LI("Relocating volume %s vmdk to a different "
-                         "datastore since trying to extend vmdk file "
-                         "in place failed."), vol_name)
-        # If in place extend fails, then try to relocate the volume
+        except exceptions.NoDiskSpaceException:
+            LOG.warning(_LW("Unable to extend volume: %(vol)s to size: "
+                            "%(size)s on current datastore due to insufficient"
+                            " space."),
+                        {'vol': vol_name, 'size': new_size})
+
+        # Insufficient disk space; relocate the volume to a different datastore
+        # and retry extend.
+        LOG.info(_LI("Relocating volume: %s to a different datastore due to "
+                     "insufficient disk space on current datastore."),
+                 vol_name)
         try:
-            (host, rp, folder, summary) = self._select_ds_for_volume(new_size)
-        except exceptions.VimException:
-            with excutils.save_and_reraise_exception():
-                LOG.exception(_LE("Not able to find a different datastore to "
-                                  "place the extended volume %s."), vol_name)
-
-        LOG.info(_LI("Selected datastore %(ds)s to place extended volume of "
-                     "size %(size)s GB."), {'ds': summary.name,
-                                            'size': new_size})
-
-        try:
-            backing = self.volumeops.get_backing(vol_name)
+            create_params = {CREATE_PARAM_DISK_SIZE: new_size}
+            (host, rp, folder, summary) = self._select_ds_for_volume(
+                volume, create_params=create_params)
             self.volumeops.relocate_backing(backing, summary.datastore, rp,
                                             host)
-            self._extend_vmdk_virtual_disk(vol_name, new_size)
             self.volumeops.move_backing_to_folder(backing, folder)
-        except exceptions.VimException:
+            self._extend_backing(backing, new_size)
+        except exceptions.VMwareDriverException:
             with excutils.save_and_reraise_exception():
-                LOG.exception(_LE("Not able to relocate volume %s for "
-                                  "extending."), vol_name)
-        LOG.info(_LI("Done extending volume %(vol)s to size %(size)s GB."),
+                LOG.error(_LE("Failed to extend volume: %(vol)s to size: "
+                              "%(size)s GB."),
+                          {'vol': vol_name, 'size': new_size})
+
+        LOG.info(_LI("Successfully extended volume: %(vol)s to size: "
+                     "%(size)s GB."),
                  {'vol': vol_name, 'size': new_size})
 
     @contextlib.contextmanager
@@ -1869,7 +1858,7 @@ class VMwareVcVmdkDriver(driver.VolumeDriver):
         # we need to extend/resize the capacity of the vmdk virtual disk from
         # the size of the source volume to the volume size.
         if volume['size'] > src_vsize:
-            self._extend_vmdk_virtual_disk(volume['name'], volume['size'])
+            self._extend_backing(clone, volume['size'])
         LOG.info(_LI("Successfully created clone: %s."), clone)
 
     def _create_volume_from_snapshot(self, volume, snapshot):
index 9e8fd80dd9b03cd69c5e58767bfcc1397ccecec9..c238e32c62f59a176e26b7588eab50c2efcf1b3c 100644 (file)
@@ -617,18 +617,18 @@ class VMwareVolumeOps(object):
                    'folder': folder})
         return folder
 
-    def extend_virtual_disk(self, requested_size_in_gb, name, dc_ref,
+    def extend_virtual_disk(self, requested_size_in_gb, path, dc_ref,
                             eager_zero=False):
         """Extend the virtual disk to the requested size.
 
         :param requested_size_in_gb: Size of the volume in GB
-        :param name: Name of the backing
+        :param path: Datastore path of the virtual disk to extend
         :param dc_ref: Reference to datacenter
         :param eager_zero: Boolean determining if the free space
         is zeroed out
         """
-        LOG.debug("Extending the volume %(name)s to %(size)s GB.",
-                  {'name': name, 'size': requested_size_in_gb})
+        LOG.debug("Extending virtual disk: %(path)s to %(size)s GB.",
+                  {'path': path, 'size': requested_size_in_gb})
         diskMgr = self._session.vim.service_content.virtualDiskManager
 
         # VMWare API needs the capacity unit to be in KB, so convert the
@@ -637,14 +637,14 @@ class VMwareVolumeOps(object):
         task = self._session.invoke_api(self._session.vim,
                                         "ExtendVirtualDisk_Task",
                                         diskMgr,
-                                        name=name,
+                                        name=path,
                                         datacenter=dc_ref,
                                         newCapacityKb=size_in_kb,
                                         eagerZero=eager_zero)
         self._session.wait_for_task(task)
-        LOG.info(_LI("Successfully extended the volume %(name)s to "
+        LOG.info(_LI("Successfully extended virtual disk: %(path)s to "
                      "%(size)s GB."),
-                 {'name': name, 'size': requested_size_in_gb})
+                 {'path': path, 'size': requested_size_in_gb})
 
     def _create_controller_config_spec(self, adapter_type):
         """Returns config spec for adding a disk controller."""