From 14ff0cc2bd5d6cb91766f7ff6cf83f18d23ac8cd Mon Sep 17 00:00:00 2001 From: Vipin Balachandran Date: Mon, 10 Jul 2017 16:16:35 -0700 Subject: [PATCH] VMware: Add 'managedBy' info Add vCenter managedBy info to volumes in the backend so that they would be displayed as 'managed by OpenStack Cinder'. It also warns the vSphere admin if an operation is attempted using vSphere client. Change-Id: I42c00b7df6aa0576b928e49f6eb21f45b56fa647 --- .../volume/drivers/vmware/test_vmware_vmdk.py | 63 +++++++++++++++++-- .../drivers/vmware/test_vmware_volumeops.py | 14 ++++- cinder/volume/drivers/vmware/vmdk.py | 26 +++++++- cinder/volume/drivers/vmware/volumeops.py | 18 ++++-- ...ware_vmdk_managed_by-3de05504d0f9a65a.yaml | 6 ++ 5 files changed, 116 insertions(+), 11 deletions(-) create mode 100644 releasenotes/notes/vmware_vmdk_managed_by-3de05504d0f9a65a.yaml diff --git a/cinder/tests/unit/volume/drivers/vmware/test_vmware_vmdk.py b/cinder/tests/unit/volume/drivers/vmware/test_vmware_vmdk.py index 145fbeb3463..cbe3cb776b0 100644 --- a/cinder/tests/unit/volume/drivers/vmware/test_vmware_vmdk.py +++ b/cinder/tests/unit/volume/drivers/vmware/test_vmware_vmdk.py @@ -1344,18 +1344,71 @@ class VMwareVcVmdkDriverTestCase(test.TestCase): self._driver._validate_vcenter_version, '5.1') + @mock.patch('oslo_vmware.vim_util.find_extension') + @mock.patch('oslo_vmware.vim_util.register_extension') + @mock.patch.object(VMDK_DRIVER, 'session') + def _test_register_extension( + self, session, register_extension, find_extension, + ext_exists=False): + if not ext_exists: + find_extension.return_value = None + + self._driver._register_extension() + + find_extension.assert_called_once_with(session.vim, vmdk.EXTENSION_KEY) + if not ext_exists: + register_extension.assert_called_once_with( + session.vim, vmdk.EXTENSION_KEY, vmdk.EXTENSION_TYPE, + label='OpenStack Cinder') + + def test_register_extension(self): + self._test_register_extension() + + def test_register_extension_with_existing_extension(self): + self._test_register_extension(ext_exists=True) + + @mock.patch('oslo_vmware.vim_util.find_extension', return_value=None) + @mock.patch('oslo_vmware.vim_util.register_extension') + @mock.patch.object(VMDK_DRIVER, 'session') + def test_concurrent_register_extension( + self, session, register_extension, find_extension): + register_extension.side_effect = exceptions.VimFaultException( + ['InvalidArgument'], 'error') + self._driver._register_extension() + + find_extension.assert_called_once_with(session.vim, vmdk.EXTENSION_KEY) + register_extension.assert_called_once_with( + session.vim, vmdk.EXTENSION_KEY, vmdk.EXTENSION_TYPE, + label='OpenStack Cinder') + + @mock.patch('oslo_vmware.vim_util.find_extension', return_value=None) + @mock.patch('oslo_vmware.vim_util.register_extension') + @mock.patch.object(VMDK_DRIVER, 'session') + def test_register_extension_failure( + self, session, register_extension, find_extension): + register_extension.side_effect = exceptions.VimFaultException( + ['RuntimeFault'], 'error') + + self.assertRaises(exceptions.VimFaultException, + self._driver._register_extension) + find_extension.assert_called_once_with(session.vim, vmdk.EXTENSION_KEY) + register_extension.assert_called_once_with( + session.vim, vmdk.EXTENSION_KEY, vmdk.EXTENSION_TYPE, + label='OpenStack Cinder') + @mock.patch.object(VMDK_DRIVER, '_validate_params') @mock.patch.object(VMDK_DRIVER, '_get_vc_version') @mock.patch.object(VMDK_DRIVER, '_validate_vcenter_version') @mock.patch('oslo_vmware.pbm.get_pbm_wsdl_location') + @mock.patch.object(VMDK_DRIVER, '_register_extension') @mock.patch('cinder.volume.drivers.vmware.volumeops.VMwareVolumeOps') @mock.patch('cinder.volume.drivers.vmware.datastore.DatastoreSelector') @mock.patch.object(VMDK_DRIVER, 'volumeops') @mock.patch.object(VMDK_DRIVER, 'session') def _test_do_setup( - self, session, vops, ds_sel_cls, vops_cls, get_pbm_wsdl_loc, - validate_vc_version, get_vc_version, validate_params, - enable_pbm=True): + self, session, vops, ds_sel_cls, vops_cls, register_extension, + get_pbm_wsdl_loc, validate_vc_version, get_vc_version, + validate_params, enable_pbm=True): if enable_pbm: ver_str = '5.5' pbm_wsdl = mock.sentinel.pbm_wsdl @@ -1378,8 +1431,10 @@ class VMwareVcVmdkDriverTestCase(test.TestCase): get_pbm_wsdl_loc.assert_called_once_with(ver_str) self.assertEqual(pbm_wsdl, self._driver.pbm_wsdl) self.assertEqual(enable_pbm, self._driver._storage_policy_enabled) + register_extension.assert_called_once() vops_cls.assert_called_once_with( - session, self._driver.configuration.vmware_max_objects_retrieval) + session, self._driver.configuration.vmware_max_objects_retrieval, + vmdk.EXTENSION_KEY, vmdk.EXTENSION_TYPE) self.assertEqual(vops_cls.return_value, self._driver._volumeops) ds_sel_cls.assert_called_once_with( vops, diff --git a/cinder/tests/unit/volume/drivers/vmware/test_vmware_volumeops.py b/cinder/tests/unit/volume/drivers/vmware/test_vmware_volumeops.py index 59fcf8bd01a..57e347e6776 100644 --- a/cinder/tests/unit/volume/drivers/vmware/test_vmware_volumeops.py +++ b/cinder/tests/unit/volume/drivers/vmware/test_vmware_volumeops.py @@ -37,7 +37,9 @@ class VolumeOpsTestCase(test.TestCase): def setUp(self): super(VolumeOpsTestCase, self).setUp() self.session = mock.MagicMock() - self.vops = volumeops.VMwareVolumeOps(self.session, self.MAX_OBJECTS) + self.vops = volumeops.VMwareVolumeOps( + self.session, self.MAX_OBJECTS, mock.sentinel.extension_key, + mock.sentinel.extension_type) def test_split_datastore_path(self): test1 = '[datastore1] myfolder/mysubfolder/myvm.vmx' @@ -604,10 +606,14 @@ class VolumeOpsTestCase(test.TestCase): self.assertEqual(1, len(ret.extraConfig)) self.assertEqual(option_key, ret.extraConfig[0].key) self.assertEqual(option_value, ret.extraConfig[0].value) + self.assertEqual(mock.sentinel.extension_key, + ret.managedBy.extensionKey) + self.assertEqual(mock.sentinel.extension_type, ret.managedBy.type) expected = [mock.call.create('ns0:VirtualMachineFileInfo'), mock.call.create('ns0:VirtualMachineConfigSpec'), mock.call.create('ns0:VirtualMachineDefinedProfileSpec'), - mock.call.create('ns0:OptionValue')] + mock.call.create('ns0:OptionValue'), + mock.call.create('ns0:ManagedByInfo')] factory.create.assert_has_calls(expected, any_order=True) @mock.patch('cinder.volume.drivers.vmware.volumeops.VMwareVolumeOps.' @@ -1004,6 +1010,10 @@ class VolumeOpsTestCase(test.TestCase): self.assertFalse(ret.template) self.assertEqual(snapshot, ret.snapshot) self.assertEqual(mock.sentinel.uuid, ret.config.instanceUuid) + self.assertEqual(mock.sentinel.extension_key, + ret.config.managedBy.extensionKey) + self.assertEqual(mock.sentinel.extension_type, + ret.config.managedBy.type) get_relocate_spec.assert_called_once_with(datastore, rp, host, disk_move_type, disk_type, disk_device) diff --git a/cinder/volume/drivers/vmware/vmdk.py b/cinder/volume/drivers/vmware/vmdk.py index 4b67413fe1d..d1d2290c1ca 100644 --- a/cinder/volume/drivers/vmware/vmdk.py +++ b/cinder/volume/drivers/vmware/vmdk.py @@ -63,6 +63,9 @@ TMP_IMAGES_DATASTORE_FOLDER_PATH = "cinder_temp/" EXTRA_CONFIG_VOLUME_ID_KEY = "cinder.volume.id" +EXTENSION_KEY = 'org.openstack.storage' +EXTENSION_TYPE = 'volume' + vmdk_opts = [ cfg.StrOpt('vmware_host_ip', help='IP address for connecting to VMware vCenter server.'), @@ -1679,6 +1682,24 @@ class VMwareVcVmdkDriver(driver.VolumeDriver): ' to %(ver)s in a future release.', {'ver': self.NEXT_MIN_SUPPORTED_VC_VERSION}) + def _register_extension(self): + ext = vim_util.find_extension(self.session.vim, EXTENSION_KEY) + if ext: + LOG.debug('Extension %s already exists.', EXTENSION_KEY) + else: + try: + vim_util.register_extension(self.session.vim, + EXTENSION_KEY, + EXTENSION_TYPE, + label='OpenStack Cinder') + LOG.info('Registered extension %s.', EXTENSION_KEY) + except exceptions.VimFaultException as e: + if 'InvalidArgument' in e.fault_list: + LOG.debug('Extension %s is already registered.', + EXTENSION_KEY) + else: + raise + def do_setup(self, context): """Any initialization the volume driver does while starting.""" self._validate_params() @@ -1701,10 +1722,13 @@ class VMwareVcVmdkDriver(driver.VolumeDriver): # Destroy current session so that it is recreated with pbm enabled self._session = None + self._register_extension() + # recreate session and initialize volumeops and ds_sel # TODO(vbala) remove properties: session, volumeops and ds_sel max_objects = self.configuration.vmware_max_objects_retrieval - self._volumeops = volumeops.VMwareVolumeOps(self.session, max_objects) + self._volumeops = volumeops.VMwareVolumeOps( + self.session, max_objects, EXTENSION_KEY, EXTENSION_TYPE) self._ds_sel = hub.DatastoreSelector( self.volumeops, self.session, max_objects) diff --git a/cinder/volume/drivers/vmware/volumeops.py b/cinder/volume/drivers/vmware/volumeops.py index 46642dd7d6b..4436e671dc9 100644 --- a/cinder/volume/drivers/vmware/volumeops.py +++ b/cinder/volume/drivers/vmware/volumeops.py @@ -278,9 +278,11 @@ class ControllerType(object): class VMwareVolumeOps(object): """Manages volume operations.""" - def __init__(self, session, max_objects): + def __init__(self, session, max_objects, extension_key, extension_type): self._session = session self._max_objects = max_objects + self._extension_key = extension_key + self._extension_type = extension_type self._folder_cache = {} def get_backing(self, name): @@ -683,6 +685,13 @@ class VMwareVolumeOps(object): return option_values + def _create_managed_by_info(self): + managed_by = self._session.vim.client.factory.create( + 'ns0:ManagedByInfo') + managed_by.extensionKey = self._extension_key + managed_by.type = self._extension_type + return managed_by + def _get_create_spec_disk_less(self, name, ds_name, profileId=None, extra_config=None): """Return spec for creating disk-less backing. @@ -721,6 +730,7 @@ class VMwareVolumeOps(object): create_spec.extraConfig = self._get_extra_config_option_values( extra_config) + create_spec.managedBy = self._create_managed_by_info() return create_spec def get_create_spec(self, name, size_kb, disk_type, ds_name, @@ -1076,9 +1086,9 @@ class VMwareVolumeOps(object): clone_spec.template = False clone_spec.snapshot = snapshot - if extra_config or disks_to_clone: - config_spec = cf.create('ns0:VirtualMachineConfigSpec') - clone_spec.config = config_spec + config_spec = cf.create('ns0:VirtualMachineConfigSpec') + config_spec.managedBy = self._create_managed_by_info() + clone_spec.config = config_spec if extra_config: if BACKING_UUID_KEY in extra_config: diff --git a/releasenotes/notes/vmware_vmdk_managed_by-3de05504d0f9a65a.yaml b/releasenotes/notes/vmware_vmdk_managed_by-3de05504d0f9a65a.yaml new file mode 100644 index 00000000000..b832c9668d8 --- /dev/null +++ b/releasenotes/notes/vmware_vmdk_managed_by-3de05504d0f9a65a.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + The volumes created by VMware VMDK driver will be displayed as + "managed by OpenStack Cinder" in vCenter server. +