]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
VMware: Add volume ID in vCenter's volume config
authorVipin Balachandran <vbala@vmware.com>
Mon, 22 Jun 2015 14:26:09 +0000 (19:56 +0530)
committerVipin Balachandran <vbala@vmware.com>
Wed, 12 Aug 2015 07:54:31 +0000 (13:24 +0530)
HP data protector's backup of vCenter inventory is failing
for 'in-use' Cinder volumes because the vmdk is attached
to two VMs (Nova instance and the backing VM corresponding
to Cinder volume). The solution is to skip the backing VM
during data protector's backup.

This patch adds key 'cinder.volume.id' with value set to
volume UUID in volume's vCenter config file so that other
vCenter solutions can uniquely identify Cinder volumes.

Closes-Bug: #1484012
Change-Id: I08a5f1a39def14f164ab3ca08a480310ce0b79ca

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

index d28c5eb8bd269ccaa049a55ad2be10381d11f1b6..e05f44f587fbbfdda9763383dd0ada67d117eb06 100644 (file)
@@ -1056,7 +1056,7 @@ class VMwareEsxVmdkDriverTestCase(test.TestCase):
         fake_vm_create_spec = mock.sentinel.spec
         fake_disk_type = 'thin'
         vol_name = 'fake_volume name'
-        vol_id = '12345'
+        vol_id = 'd11a82de-ddaa-448d-b50a-a255a7e61a1e'
         fake_volume = {'name': vol_name,
                        'id': vol_id,
                        'size': fake_volume_size,
@@ -1095,12 +1095,14 @@ class VMwareEsxVmdkDriverTestCase(test.TestCase):
         image_service.show.assert_called_with(fake_context, fake_image_id)
         _select_ds_for_volume.assert_called_with(fake_volume)
         get_profile_id.assert_called_once_with(fake_volume)
+        extra_config = {vmdk.EXTRA_CONFIG_VOLUME_ID_KEY: vol_id}
         volumeops.get_create_spec.assert_called_with(fake_volume['name'],
                                                      0,
                                                      fake_disk_type,
                                                      fake_summary.name,
-                                                     profile_id,
-                                                     adapter_type)
+                                                     profileId=profile_id,
+                                                     adapter_type=adapter_type,
+                                                     extra_config=extra_config)
         self.assertTrue(download_image.called)
         download_image.assert_called_with(fake_context, timeout,
                                           image_service,
@@ -1726,15 +1728,19 @@ class VMwareEsxVmdkDriverTestCase(test.TestCase):
 
         context = mock.sentinel.context
         name = 'vm-1'
-        volume = {'name': 'vol-1', 'id': 1, 'size': 1}
+        volume = {'name': 'vol-1',
+                  'id': 'd11a82de-ddaa-448d-b50a-a255a7e61a1e',
+                  'size': 1}
         tmp_file_path = mock.sentinel.tmp_file_path
         file_size_bytes = units.Gi
         ret = self._driver._create_backing_from_stream_optimized_file(
             context, name, volume, tmp_file_path, file_size_bytes)
 
         self.assertEqual(vm_ref, ret)
+        extra_config = {vmdk.EXTRA_CONFIG_VOLUME_ID_KEY: volume['id']}
         vops.get_create_spec.assert_called_once_with(
-            name, 0, disk_type, summary.name, profile_id)
+            name, 0, disk_type, summary.name, profileId=profile_id,
+            extra_config=extra_config)
         file_open.assert_called_once_with(tmp_file_path, "rb")
         download_data.assert_called_once_with(
             context, self.IMG_TX_TIMEOUT, tmp_file, session=session,
@@ -2054,6 +2060,7 @@ class VMwareVcVmdkDriverTestCase(VMwareEsxVmdkDriverTestCase):
         """Test _clone_backing with clone type - linked."""
         fake_size = 3
         fake_volume = {'volume_type_id': None, 'name': 'fake_name',
+                       'id': '51e47214-8e3c-475d-b44b-aea6cd3eef53',
                        'size': fake_size}
         fake_snapshot = {'volume_name': 'volume_name',
                          'name': 'snapshot_name',
@@ -2063,13 +2070,15 @@ class VMwareVcVmdkDriverTestCase(VMwareEsxVmdkDriverTestCase):
         self._driver._clone_backing(fake_volume, fake_backing, fake_snapshot,
                                     volumeops.LINKED_CLONE_TYPE,
                                     fake_snapshot['volume_size'])
+        extra_config = {vmdk.EXTRA_CONFIG_VOLUME_ID_KEY: fake_volume['id']}
         volume_ops.clone_backing.assert_called_with(fake_volume['name'],
                                                     fake_backing,
                                                     fake_snapshot,
                                                     fake_type,
                                                     None,
                                                     host=None,
-                                                    resource_pool=None)
+                                                    resource_pool=None,
+                                                    extra_config=extra_config)
         # 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'],
@@ -2100,6 +2109,7 @@ class VMwareVcVmdkDriverTestCase(VMwareEsxVmdkDriverTestCase):
         fake_summary.datastore = fake_datastore
         fake_size = 3
         fake_volume = {'volume_type_id': None, 'name': 'fake_name',
+                       'id': '51e47214-8e3c-475d-b44b-aea6cd3eef53',
                        'size': fake_size}
         fake_snapshot = {'volume_name': 'volume_name', 'name': 'snapshot_name',
                          'volume_size': 2}
@@ -2110,6 +2120,7 @@ class VMwareVcVmdkDriverTestCase(VMwareEsxVmdkDriverTestCase):
                                     volumeops.FULL_CLONE_TYPE,
                                     fake_snapshot['volume_size'])
         _select_ds_for_volume.assert_called_with(fake_volume)
+        extra_config = {vmdk.EXTRA_CONFIG_VOLUME_ID_KEY: fake_volume['id']}
         volume_ops.clone_backing.assert_called_with(fake_volume['name'],
                                                     fake_backing,
                                                     fake_snapshot,
@@ -2117,7 +2128,8 @@ class VMwareVcVmdkDriverTestCase(VMwareEsxVmdkDriverTestCase):
                                                     fake_datastore,
                                                     host=fake_host,
                                                     resource_pool=
-                                                    fake_resource_pool)
+                                                    fake_resource_pool,
+                                                    extra_config=extra_config)
         # 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'],
@@ -2552,16 +2564,20 @@ class VMwareVcVmdkDriverTestCase(VMwareEsxVmdkDriverTestCase):
         select_ds_for_volume.return_value = (host, resource_pool, folder,
                                              summary)
 
-        volume = {'name': 'vol-1', 'volume_type_id': None, 'size': 1}
+        volume = {'name': 'vol-1', 'volume_type_id': None, 'size': 1,
+                  'id': 'd11a82de-ddaa-448d-b50a-a255a7e61a1e'}
         create_params = {vmdk.CREATE_PARAM_DISK_LESS: True}
         self._driver._create_backing(volume, host, create_params)
 
-        vops.create_backing_disk_less.assert_called_once_with('vol-1',
-                                                              folder,
-                                                              resource_pool,
-                                                              host,
-                                                              summary.name,
-                                                              None)
+        extra_config = {vmdk.EXTRA_CONFIG_VOLUME_ID_KEY: volume['id']}
+        vops.create_backing_disk_less.assert_called_once_with(
+            'vol-1',
+            folder,
+            resource_pool,
+            host,
+            summary.name,
+            profileId=None,
+            extra_config=extra_config)
 
         create_params = {vmdk.CREATE_PARAM_ADAPTER_TYPE: 'ide'}
         self._driver._create_backing(volume, host, create_params)
@@ -2573,8 +2589,9 @@ class VMwareVcVmdkDriverTestCase(VMwareEsxVmdkDriverTestCase):
                                                     resource_pool,
                                                     host,
                                                     summary.name,
-                                                    None,
-                                                    'ide')
+                                                    profileId=None,
+                                                    adapter_type='ide',
+                                                    extra_config=extra_config)
 
         vops.create_backing.reset_mock()
         backing_name = "temp-vol"
@@ -2588,8 +2605,9 @@ class VMwareVcVmdkDriverTestCase(VMwareEsxVmdkDriverTestCase):
                                                     resource_pool,
                                                     host,
                                                     summary.name,
-                                                    None,
-                                                    'lsiLogic')
+                                                    profileId=None,
+                                                    adapter_type='lsiLogic',
+                                                    extra_config=extra_config)
 
     @mock.patch('cinder.openstack.common.fileutils.ensure_tree')
     @mock.patch('cinder.openstack.common.fileutils.delete_if_exists')
index 1b871cfc5b1d52e77314966465a1ed26a191bf18..d2510ad1c01cddf89d9e94d63acacd954aa683e1 100644 (file)
@@ -661,18 +661,48 @@ class VolumeOpsTestCase(test.TestCase):
         name = mock.sentinel.name
         ds_name = mock.sentinel.ds_name
         profile_id = mock.sentinel.profile_id
-        ret = self.vops._get_create_spec_disk_less(name, ds_name, profile_id)
+        option_key = mock.sentinel.key
+        option_value = mock.sentinel.value
+        extra_config = {option_key: option_value}
+        ret = self.vops._get_create_spec_disk_less(name, ds_name, profile_id,
+                                                   extra_config)
 
         factory.create.side_effect = None
         self.assertEqual(name, ret.name)
         self.assertEqual('[%s]' % ds_name, ret.files.vmPathName)
         self.assertEqual("vmx-08", ret.version)
         self.assertEqual(profile_id, ret.vmProfile[0].profileId)
+        self.assertEqual(1, len(ret.extraConfig))
+        self.assertEqual(option_key, ret.extraConfig[0].key)
+        self.assertEqual(option_value, ret.extraConfig[0].value)
         expected = [mock.call.create('ns0:VirtualMachineFileInfo'),
                     mock.call.create('ns0:VirtualMachineConfigSpec'),
-                    mock.call.create('ns0:VirtualMachineDefinedProfileSpec')]
+                    mock.call.create('ns0:VirtualMachineDefinedProfileSpec'),
+                    mock.call.create('ns0:OptionValue')]
         factory.create.assert_has_calls(expected, any_order=True)
 
+    @mock.patch('cinder.volume.drivers.vmware.volumeops.VMwareVolumeOps.'
+                '_get_create_spec_disk_less')
+    @mock.patch('cinder.volume.drivers.vmware.volumeops.VMwareVolumeOps.'
+                '_create_specs_for_disk_add')
+    def test_get_create_spec(self, create_specs_for_disk_add,
+                             get_create_spec_disk_less):
+        name = 'vol-1'
+        size_kb = 1024
+        disk_type = 'thin'
+        ds_name = 'nfs-1'
+        profileId = mock.sentinel.profile_id
+        adapter_type = 'busLogic'
+        extra_config = mock.sentinel.extra_config
+
+        self.vops.get_create_spec(name, size_kb, disk_type, ds_name,
+                                  profileId, adapter_type, extra_config)
+
+        get_create_spec_disk_less.assert_called_once_with(
+            name, ds_name, profileId=profileId, extra_config=extra_config)
+        create_specs_for_disk_add.assert_called_once_with(
+            size_kb, disk_type, adapter_type)
+
     @mock.patch('cinder.volume.drivers.vmware.volumeops.VMwareVolumeOps.'
                 'get_create_spec')
     def test_create_backing(self, get_create_spec):
@@ -692,13 +722,14 @@ class VolumeOpsTestCase(test.TestCase):
         host = mock.sentinel.host
         ds_name = mock.sentinel.ds_name
         profile_id = mock.sentinel.profile_id
+        extra_config = mock.sentinel.extra_config
         ret = self.vops.create_backing(name, size_kb, disk_type, folder,
                                        resource_pool, host, ds_name,
-                                       profile_id, adapter_type)
+                                       profile_id, adapter_type, extra_config)
         self.assertEqual(mock.sentinel.result, ret)
-        get_create_spec.assert_called_once_with(name, size_kb, disk_type,
-                                                ds_name, profile_id,
-                                                adapter_type)
+        get_create_spec.assert_called_once_with(
+            name, size_kb, disk_type, ds_name, profileId=profile_id,
+            adapter_type=adapter_type, extra_config=extra_config)
         self.session.invoke_api.assert_called_once_with(self.session.vim,
                                                         'CreateVM_Task',
                                                         folder,
@@ -723,12 +754,14 @@ class VolumeOpsTestCase(test.TestCase):
         host = mock.sentinel.host
         ds_name = mock.sentinel.ds_name
         profile_id = mock.sentinel.profile_id
+        extra_config = mock.sentinel.extra_config
         ret = self.vops.create_backing_disk_less(name, folder, resource_pool,
-                                                 host, ds_name, profile_id)
+                                                 host, ds_name, profile_id,
+                                                 extra_config)
 
         self.assertEqual(mock.sentinel.result, ret)
-        get_create_spec_disk_less.assert_called_once_with(name, ds_name,
-                                                          profile_id)
+        get_create_spec_disk_less.assert_called_once_with(
+            name, ds_name, profileId=profile_id, extra_config=extra_config)
         self.session.invoke_api.assert_called_once_with(self.session.vim,
                                                         'CreateVM_Task',
                                                         folder,
@@ -988,6 +1021,11 @@ class VolumeOpsTestCase(test.TestCase):
             self.assertEqual(folder, ret)
             get_parent.assert_called_once_with(backing, 'Folder')
 
+    def _verify_extra_config(self, option_values, key, value):
+        self.assertEqual(1, len(option_values))
+        self.assertEqual(key, option_values[0].key)
+        self.assertEqual(value, option_values[0].value)
+
     @mock.patch('cinder.volume.drivers.vmware.volumeops.VMwareVolumeOps.'
                 '_get_relocate_spec')
     @mock.patch('cinder.volume.drivers.vmware.volumeops.VMwareVolumeOps.'
@@ -998,28 +1036,38 @@ class VolumeOpsTestCase(test.TestCase):
         relocate_spec = mock.sentinel.relocate_spec
         get_relocate_spec.return_value = relocate_spec
 
+        # Test with empty disk type.
         datastore = mock.sentinel.datastore
         disk_move_type = mock.sentinel.disk_move_type
         snapshot = mock.sentinel.snapshot
         disk_type = None
         backing = mock.sentinel.backing
+        host = mock.sentinel.host
+        rp = mock.sentinel.rp
+        key = mock.sentinel.key
+        value = mock.sentinel.value
+        extra_config = {key: value}
         ret = self.vops._get_clone_spec(datastore, disk_move_type, snapshot,
-                                        backing, disk_type)
+                                        backing, disk_type, host, rp,
+                                        extra_config)
 
         self.assertEqual(relocate_spec, ret.location)
         self.assertFalse(ret.powerOn)
         self.assertFalse(ret.template)
         self.assertEqual(snapshot, ret.snapshot)
-        get_relocate_spec.assert_called_once_with(datastore, None, None,
+        get_relocate_spec.assert_called_once_with(datastore, rp, host,
                                                   disk_move_type, disk_type,
                                                   None)
+        self._verify_extra_config(ret.config.extraConfig, key, value)
 
+        # Test with non-empty disk type.
         disk_device = mock.sentinel.disk_device
         get_disk_device.return_value = disk_device
 
         disk_type = 'thin'
         ret = self.vops._get_clone_spec(datastore, disk_move_type, snapshot,
-                                        backing, disk_type)
+                                        backing, disk_type, host, rp,
+                                        extra_config)
 
         factory.create.side_effect = None
         self.assertEqual(relocate_spec, ret.location)
@@ -1027,9 +1075,10 @@ class VolumeOpsTestCase(test.TestCase):
         self.assertFalse(ret.template)
         self.assertEqual(snapshot, ret.snapshot)
         get_disk_device.assert_called_once_with(backing)
-        get_relocate_spec.assert_called_with(datastore, None, None,
+        get_relocate_spec.assert_called_with(datastore, rp, host,
                                              disk_move_type, disk_type,
                                              disk_device)
+        self._verify_extra_config(ret.config.extraConfig, key, value)
 
     @mock.patch('cinder.volume.drivers.vmware.volumeops.VMwareVolumeOps.'
                 '_get_clone_spec')
@@ -1056,8 +1105,9 @@ class VolumeOpsTestCase(test.TestCase):
         # verify calls
         self.assertEqual(mock.sentinel.new_backing, ret)
         disk_move_type = 'moveAllDiskBackingsAndDisallowSharing'
-        get_clone_spec.assert_called_with(datastore, disk_move_type, snapshot,
-                                          backing, None, None, None)
+        get_clone_spec.assert_called_with(
+            datastore, disk_move_type, snapshot, backing, None, host=None,
+            resource_pool=None, extra_config=None)
         expected = [mock.call(vim_util, 'get_object_property',
                               self.session.vim, backing, 'parent'),
                     mock.call(self.session.vim, 'CloneVM_Task', backing,
@@ -1072,27 +1122,32 @@ class VolumeOpsTestCase(test.TestCase):
         # verify calls
         self.assertEqual(mock.sentinel.new_backing, ret)
         disk_move_type = 'createNewChildDiskBacking'
-        get_clone_spec.assert_called_with(datastore, disk_move_type, snapshot,
-                                          backing, None, None, None)
+        get_clone_spec.assert_called_with(
+            datastore, disk_move_type, snapshot, backing, None, host=None,
+            resource_pool=None, extra_config=None)
         expected = [mock.call(vim_util, 'get_object_property',
                               self.session.vim, backing, 'parent'),
                     mock.call(self.session.vim, 'CloneVM_Task', backing,
                               folder=folder, name=name, spec=clone_spec)]
         self.assertEqual(expected, self.session.invoke_api.mock_calls)
 
-        # Test disk type conversion and target host.
+        # Test with optional params (disk_type, host, resource_pool and
+        # extra_config).
         clone_type = None
         disk_type = 'thin'
         host = mock.sentinel.host
         rp = mock.sentinel.rp
+        extra_config = mock.sentinel.extra_config
         self.session.invoke_api.reset_mock()
         ret = self.vops.clone_backing(name, backing, snapshot, clone_type,
-                                      datastore, disk_type, host, rp)
+                                      datastore, disk_type, host, rp,
+                                      extra_config)
 
         self.assertEqual(mock.sentinel.new_backing, ret)
         disk_move_type = 'moveAllDiskBackingsAndDisallowSharing'
-        get_clone_spec.assert_called_with(datastore, disk_move_type, snapshot,
-                                          backing, disk_type, host, rp)
+        get_clone_spec.assert_called_with(
+            datastore, disk_move_type, snapshot, backing, disk_type, host=host,
+            resource_pool=rp, extra_config=extra_config)
         expected = [mock.call(vim_util, 'get_object_property',
                               self.session.vim, backing, 'parent'),
                     mock.call(self.session.vim, 'CloneVM_Task', backing,
index 79edb36f011a2faca4c0e3aeea19a371968fd8bf..d29a056d8410e8590d24aeb9e04658c0365f7460 100644 (file)
@@ -60,6 +60,8 @@ CREATE_PARAM_BACKING_NAME = 'name'
 
 TMP_IMAGES_DATASTORE_FOLDER_PATH = "cinder_temp/"
 
+EXTRA_CONFIG_VOLUME_ID_KEY = "cinder.volume.id"
+
 vmdk_opts = [
     cfg.StrOpt('vmware_host_ip',
                default=None,
@@ -414,6 +416,9 @@ class VMwareEsxVmdkDriver(driver.VolumeDriver):
                 profile_id = profile.uniqueId
         return profile_id
 
+    def _get_extra_config(self, volume):
+        return {EXTRA_CONFIG_VOLUME_ID_KEY: volume['id']}
+
     def _create_backing(self, volume, host=None, create_params=None):
         """Create volume backing under the given host.
 
@@ -436,17 +441,21 @@ class VMwareEsxVmdkDriver(driver.VolumeDriver):
         backing_name = create_params.get(CREATE_PARAM_BACKING_NAME,
                                          volume['name'])
 
+        extra_config = self._get_extra_config(volume)
+
         # default is a backing with single disk
         disk_less = create_params.get(CREATE_PARAM_DISK_LESS, False)
         if disk_less:
             # create a disk-less backing-- disk can be added later; for e.g.,
             # by copying an image
-            return self.volumeops.create_backing_disk_less(backing_name,
-                                                           folder,
-                                                           resource_pool,
-                                                           host_ref,
-                                                           summary.name,
-                                                           profile_id)
+            return self.volumeops.create_backing_disk_less(
+                backing_name,
+                folder,
+                resource_pool,
+                host_ref,
+                summary.name,
+                profileId=profile_id,
+                extra_config=extra_config)
 
         # create a backing with single disk
         disk_type = VMwareEsxVmdkDriver._get_disk_type(volume)
@@ -460,8 +469,9 @@ class VMwareEsxVmdkDriver(driver.VolumeDriver):
                                              resource_pool,
                                              host_ref,
                                              summary.name,
-                                             profile_id,
-                                             adapter_type)
+                                             profileId=profile_id,
+                                             adapter_type=adapter_type,
+                                             extra_config=extra_config)
 
     def _relocate_backing(self, volume, backing, host):
         pass
@@ -1113,12 +1123,15 @@ class VMwareEsxVmdkDriver(driver.VolumeDriver):
         # The size of stream optimized glance image is often suspect,
         # so better let VC figure out the disk capacity during import.
         dummy_disk_size = 0
-        vm_create_spec = self.volumeops.get_create_spec(volume['name'],
-                                                        dummy_disk_size,
-                                                        disk_type,
-                                                        summary.name,
-                                                        profile_id,
-                                                        adapter_type)
+        extra_config = self._get_extra_config(volume)
+        vm_create_spec = self.volumeops.get_create_spec(
+            volume['name'],
+            dummy_disk_size,
+            disk_type,
+            summary.name,
+            profileId=profile_id,
+            adapter_type=adapter_type,
+            extra_config=extra_config)
         # convert vm_create_spec to vm_import_spec
         cf = self.session.vim.client.factory
         vm_import_spec = cf.create('ns0:VirtualMachineImportSpec')
@@ -1619,11 +1632,13 @@ class VMwareEsxVmdkDriver(driver.VolumeDriver):
 
         profile_id = self._get_storage_profile_id(volume)
         disk_type = VMwareEsxVmdkDriver._get_disk_type(volume)
-        vm_create_spec = self.volumeops.get_create_spec(name,
-                                                        0,
-                                                        disk_type,
-                                                        summary.name,
-                                                        profile_id)
+        extra_config = self._get_extra_config(volume)
+        # We cannot determine the size of a virtual disk created from
+        # streamOptimized disk image. Set size to 0 and let vCenter
+        # figure out the size after virtual disk creation.
+        vm_create_spec = self.volumeops.get_create_spec(
+            name, 0, disk_type, summary.name, profileId=profile_id,
+            extra_config=extra_config)
         vm_import_spec.configSpec = vm_create_spec
 
         timeout = self.configuration.vmware_image_transfer_timeout_secs
@@ -1954,9 +1969,11 @@ class VMwareVcVmdkDriver(VMwareEsxVmdkDriver):
             # Pick a datastore where to create the full clone under any host
             (host, rp, _folder, summary) = self._select_ds_for_volume(volume)
             datastore = summary.datastore
+        extra_config = self._get_extra_config(volume)
         clone = self.volumeops.clone_backing(volume['name'], backing,
                                              snapshot, clone_type, datastore,
-                                             host=host, resource_pool=rp)
+                                             host=host, resource_pool=rp,
+                                             extra_config=extra_config)
         # If the volume size specified by the user is greater than
         # the size of the source volume, the newly created volume will
         # allocate the capacity to the size of the source volume in the backend
index 4e4e61e0bb8bf6d06530a5ff3b5f8e649bb657a6..0f0b30bd83d676bb30030a091460c4636c0fe416 100644 (file)
@@ -23,6 +23,7 @@ from oslo_utils import units
 from oslo_vmware import exceptions
 from oslo_vmware import pbm
 from oslo_vmware import vim_util
+import six
 from six.moves import urllib
 
 from cinder.i18n import _, _LE, _LI
@@ -682,12 +683,28 @@ class VMwareVolumeOps(object):
             specs.append(controller_spec)
         return specs
 
-    def _get_create_spec_disk_less(self, name, ds_name, profileId=None):
+    def _get_extra_config_option_values(self, extra_config):
+
+        cf = self._session.vim.client.factory
+        option_values = []
+
+        for key, value in six.iteritems(extra_config):
+            opt = cf.create('ns0:OptionValue')
+            opt.key = key
+            opt.value = value
+            option_values.append(opt)
+
+        return option_values
+
+    def _get_create_spec_disk_less(self, name, ds_name, profileId=None,
+                                   extra_config=None):
         """Return spec for creating disk-less backing.
 
         :param name: Name of the backing
         :param ds_name: Datastore name where the disk is to be provisioned
-        :param profileId: storage profile ID for the backing
+        :param profileId: Storage profile ID for the backing
+        :param extra_config: Key-value pairs to be written to backing's
+                             extra-config
         :return: Spec for creation
         """
         cf = self._session.vim.client.factory
@@ -711,10 +728,15 @@ class VMwareVolumeOps(object):
             vmProfile.profileId = profileId
             create_spec.vmProfile = [vmProfile]
 
+        if extra_config:
+            create_spec.extraConfig = self._get_extra_config_option_values(
+                extra_config)
+
         return create_spec
 
     def get_create_spec(self, name, size_kb, disk_type, ds_name,
-                        profileId=None, adapter_type='lsiLogic'):
+                        profileId=None, adapter_type='lsiLogic',
+                        extra_config=None):
         """Return spec for creating backing with a single disk.
 
         :param name: name of the backing
@@ -723,9 +745,12 @@ class VMwareVolumeOps(object):
         :param ds_name: datastore name where the disk is to be provisioned
         :param profileId: storage profile ID for the backing
         :param adapter_type: disk adapter type
+        :param extra_config: key-value pairs to be written to backing's
+                             extra-config
         :return: spec for creation
         """
-        create_spec = self._get_create_spec_disk_less(name, ds_name, profileId)
+        create_spec = self._get_create_spec_disk_less(
+            name, ds_name, profileId=profileId, extra_config=extra_config)
         create_spec.deviceChange = self._create_specs_for_disk_add(
             size_kb, disk_type, adapter_type)
         return create_spec
@@ -742,7 +767,8 @@ class VMwareVolumeOps(object):
         return backing
 
     def create_backing(self, name, size_kb, disk_type, folder, resource_pool,
-                       host, ds_name, profileId=None, adapter_type='lsiLogic'):
+                       host, ds_name, profileId=None, adapter_type='lsiLogic',
+                       extra_config=None):
         """Create backing for the volume.
 
         Creates a VM with one VMDK based on the given inputs.
@@ -754,8 +780,10 @@ class VMwareVolumeOps(object):
         :param resource_pool: Resource pool reference
         :param host: Host reference
         :param ds_name: Datastore name where the disk is to be provisioned
-        :param profileId: storage profile ID to be associated with backing
+        :param profileId: Storage profile ID to be associated with backing
         :param adapter_type: Disk adapter type
+        :param extra_config: Key-value pairs to be written to backing's
+                             extra-config
         :return: Reference to the created backing entity
         """
         LOG.debug("Creating volume backing with name: %(name)s "
@@ -768,13 +796,15 @@ class VMwareVolumeOps(object):
                    'ds_name': ds_name, 'profile': profileId, 'host': host,
                    'adapter_type': adapter_type})
 
-        create_spec = self.get_create_spec(name, size_kb, disk_type, ds_name,
-                                           profileId, adapter_type)
+        create_spec = self.get_create_spec(
+            name, size_kb, disk_type, ds_name, profileId=profileId,
+            adapter_type=adapter_type, extra_config=extra_config)
         return self._create_backing_int(folder, resource_pool, host,
                                         create_spec)
 
     def create_backing_disk_less(self, name, folder, resource_pool,
-                                 host, ds_name, profileId=None):
+                                 host, ds_name, profileId=None,
+                                 extra_config=None):
         """Create disk-less volume backing.
 
         This type of backing is useful for creating volume from image. The
@@ -787,6 +817,8 @@ class VMwareVolumeOps(object):
         :param host: Host reference
         :param ds_name: Name of the datastore used for VM storage
         :param profileId: Storage profile ID to be associated with backing
+        :param extra_config: Key-value pairs to be written to backing's
+                             extra-config
         :return: Reference to the created backing entity
         """
         LOG.debug("Creating disk-less volume backing with name: %(name)s "
@@ -797,7 +829,8 @@ class VMwareVolumeOps(object):
                    'resource_pool': resource_pool, 'host': host,
                    'ds_name': ds_name})
 
-        create_spec = self._get_create_spec_disk_less(name, ds_name, profileId)
+        create_spec = self._get_create_spec_disk_less(
+            name, ds_name, profileId=profileId, extra_config=extra_config)
         return self._create_backing_int(folder, resource_pool, host,
                                         create_spec)
 
@@ -1023,7 +1056,8 @@ class VMwareVolumeOps(object):
         return self._get_parent(backing, 'Folder')
 
     def _get_clone_spec(self, datastore, disk_move_type, snapshot, backing,
-                        disk_type, host=None, resource_pool=None):
+                        disk_type, host=None, resource_pool=None,
+                        extra_config=None):
         """Get the clone spec.
 
         :param datastore: Reference to datastore
@@ -1033,6 +1067,8 @@ class VMwareVolumeOps(object):
         :param disk_type: Disk type of clone
         :param host: Target host
         :param resource_pool: Target resource pool
+        :param extra_config: Key-value pairs to be written to backing's
+                             extra-config
         :return: Clone spec
         """
         if disk_type is not None:
@@ -1050,11 +1086,18 @@ class VMwareVolumeOps(object):
         clone_spec.template = False
         clone_spec.snapshot = snapshot
 
+        if extra_config:
+            config_spec = cf.create('ns0:VirtualMachineConfigSpec')
+            config_spec.extraConfig = self._get_extra_config_option_values(
+                extra_config)
+            clone_spec.config = config_spec
+
         LOG.debug("Spec for cloning the backing: %s.", clone_spec)
         return clone_spec
 
     def clone_backing(self, name, backing, snapshot, clone_type, datastore,
-                      disk_type=None, host=None, resource_pool=None):
+                      disk_type=None, host=None, resource_pool=None,
+                      extra_config=None):
         """Clone backing.
 
         If the clone_type is 'full', then a full clone of the source volume
@@ -1069,6 +1112,8 @@ class VMwareVolumeOps(object):
         :param disk_type: Disk type of the clone
         :param host: Target host
         :param resource_pool: Target resource pool
+        :param extra_config: Key-value pairs to be written to backing's
+                             extra-config
         """
         LOG.debug("Creating a clone of backing: %(back)s, named: %(name)s, "
                   "clone type: %(type)s from snapshot: %(snap)s on "
@@ -1082,9 +1127,9 @@ class VMwareVolumeOps(object):
             disk_move_type = 'createNewChildDiskBacking'
         else:
             disk_move_type = 'moveAllDiskBackingsAndDisallowSharing'
-        clone_spec = self._get_clone_spec(datastore, disk_move_type, snapshot,
-                                          backing, disk_type, host,
-                                          resource_pool)
+        clone_spec = self._get_clone_spec(
+            datastore, disk_move_type, snapshot, backing, disk_type, host=host,
+            resource_pool=resource_pool, extra_config=extra_config)
         task = self._session.invoke_api(self._session.vim, 'CloneVM_Task',
                                         backing, folder=folder, name=name,
                                         spec=clone_spec)