From 07ad47ef80dd71e1216331b3b95f5be280455cbd Mon Sep 17 00:00:00 2001 From: Subramanian Neelakantan Date: Tue, 28 Jan 2014 11:09:25 +0530 Subject: [PATCH] vmware: default global pbm policy configuration Adding support for a global pbm policy configuration for the vmdk driver. Setting the 'pbm_default_policy' in cinder.conf will be used as the default storage profile name to be used when creating a volume without associated vmware:storage_profile extra spec. Also renaming 'vmware-pbm-wsdl' to 'pbm-wsdl-location' to use nova driver's naming convention. Implements: blueprint vmdk-storage-policy-volume-type Change-Id: I7fad167b7be6a479db88fb4d15d07f29afd023b0 --- cinder/tests/test_vmware_vmdk.py | 38 +++++++++++++++++-- cinder/volume/drivers/vmware/error_util.py | 22 +++++++++++ cinder/volume/drivers/vmware/vmdk.py | 43 +++++++++++++++++++--- etc/cinder/cinder.conf.sample | 17 ++++++--- 4 files changed, 105 insertions(+), 15 deletions(-) diff --git a/cinder/tests/test_vmware_vmdk.py b/cinder/tests/test_vmware_vmdk.py index 2083bb561..682c80a93 100644 --- a/cinder/tests/test_vmware_vmdk.py +++ b/cinder/tests/test_vmware_vmdk.py @@ -1054,12 +1054,34 @@ class VMwareVcVmdkDriverTestCase(VMwareEsxVmdkDriverTestCase): """Test class for VMwareVcVmdkDriver.""" PBM_WSDL = '/fake/wsdl/path' + DEFAULT_PROFILE = 'fakeProfile' def setUp(self): super(VMwareVcVmdkDriverTestCase, self).setUp() - self.flags(vmware_pbm_wsdl=self.PBM_WSDL) + self._config.pbm_wsdl_location = self.PBM_WSDL + self._config.pbm_default_policy = self.DEFAULT_PROFILE self._driver = vmdk.VMwareVcVmdkDriver(configuration=self._config) + @mock.patch('cinder.volume.drivers.vmware.vmdk.VMwareVcVmdkDriver.' + 'session', new_callable=mock.PropertyMock) + @mock.patch('cinder.volume.drivers.vmware.vmdk.VMwareVcVmdkDriver.' + 'volumeops', new_callable=mock.PropertyMock) + def test_do_setup(self, vol_ops, session): + """Test do_setup.""" + vol_ops = vol_ops.return_value + session = session.return_value + # pbm_wsdl_location is set and pbm_default_policy is used + self._driver.do_setup(mock.ANY) + default = self.DEFAULT_PROFILE + vol_ops.retrieve_profile_id.assert_called_once_with(default) + # pbm_wsdl_location is set and pbm_default_policy is wrong + vol_ops.retrieve_profile_id.return_value = None + self.assertRaises(error_util.PbmDefaultPolicyDoesNotExist, + self._driver.do_setup, mock.ANY) + # pbm_wsdl_location is not set + self._driver.configuration.pbm_wsdl_location = None + self._driver.do_setup(mock.ANY) + def test_init_conn_with_instance_and_backing(self): """Test initialize_connection with instance and backing.""" m = self.mox @@ -1300,20 +1322,28 @@ class VMwareVcVmdkDriverTestCase(VMwareEsxVmdkDriverTestCase): def test_get_storage_profile(self, get_volume_type_extra_specs): """Test vmdk _get_storage_profile.""" - # Test volume with no type id returns None + # volume with no type id returns None volume = FakeObject() volume['volume_type_id'] = None sp = self._driver._get_storage_profile(volume) self.assertEqual(None, sp, "Without a volume_type_id no storage " "profile should be returned.") - # Test volume with type id calls extra specs + # profile associated with the volume type should be returned fake_id = 'fake_volume_id' volume['volume_type_id'] = fake_id - self._driver._get_storage_profile(volume) + get_volume_type_extra_specs.return_value = 'fake_profile' + profile = self._driver._get_storage_profile(volume) + self.assertEqual('fake_profile', profile) spec_key = 'vmware:storage_profile' get_volume_type_extra_specs.assert_called_once_with(fake_id, spec_key) + # default profile should be returned when no storage profile is + # associated with the volume type + get_volume_type_extra_specs.return_value = False + profile = self._driver._get_storage_profile(volume) + self.assertEqual(self.DEFAULT_PROFILE, profile) + @mock.patch('cinder.volume.drivers.vmware.vim_util.' 'convert_datastores_to_hubs') @mock.patch('cinder.volume.drivers.vmware.vim_util.' diff --git a/cinder/volume/drivers/vmware/error_util.py b/cinder/volume/drivers/vmware/error_util.py index 0be99d953..a9c02bd86 100644 --- a/cinder/volume/drivers/vmware/error_util.py +++ b/cinder/volume/drivers/vmware/error_util.py @@ -45,3 +45,25 @@ class VimFaultException(exception.VolumeBackendAPIException): def __init__(self, fault_list, msg): exception.VolumeBackendAPIException.__init__(self, msg) self.fault_list = fault_list + + +class VMwareDriverException(exception.CinderException): + """Base class for all exceptions raised by the VMDK driver. + + All exceptions raised by the vmdk driver should raise an exception + descended from this class as a root. This will allow the driver to + potentially trap problems related to its own internal configuration + before halting the cinder-volume node. + """ + message = _("VMware VMDK driver exception.") + + +class VMwaredriverConfigurationException(VMwareDriverException): + """Base class for all configuration exceptions. + """ + message = _("VMware VMDK driver configuration error.") + + +class PbmDefaultPolicyDoesNotExist(VMwaredriverConfigurationException): + message = _("The configured default PBM policy is not defined on " + "vCenter Server.") diff --git a/cinder/volume/drivers/vmware/vmdk.py b/cinder/volume/drivers/vmware/vmdk.py index 176280238..44603e71c 100644 --- a/cinder/volume/drivers/vmware/vmdk.py +++ b/cinder/volume/drivers/vmware/vmdk.py @@ -80,15 +80,23 @@ vmdk_opts = [ 'Query results will be obtained in batches from the ' 'server and not in one shot. Server may still limit the ' 'count to something less than the configured value.'), - cfg.StrOpt('vmware_pbm_wsdl', +] + +spbm_opts = [ + cfg.StrOpt('pbm_wsdl_location', help='PBM service WSDL file location URL. ' 'e.g. file:///opt/SDK/spbm/wsdl/pbmService.wsdl. ' 'Not setting this will disable storage policy based ' 'placement of volumes.'), + cfg.StrOpt('pbm_default_policy', + help='The PBM default policy. If pbm_wsdl_location is set and ' + 'there is no defined storage policy for the specific ' + 'request then this policy will be used.'), ] CONF = cfg.CONF CONF.register_opts(vmdk_opts) +CONF.register_opts(spbm_opts) def _get_volume_type_extra_spec(type_id, spec_key, possible_values=None, @@ -317,12 +325,15 @@ class VMwareEsxVmdkDriver(driver.VolumeDriver): :param volume: volume whose storage profile should be queried :return: string value of storage profile if volume type is associated, + default global profile if configured in pbm_default_profile, None otherwise """ type_id = volume['volume_type_id'] if not type_id: return - return _get_volume_type_extra_spec(type_id, 'storage_profile') + default_policy = self.configuration.pbm_default_policy + return _get_volume_type_extra_spec(type_id, 'storage_profile', + default_value=default_policy) def _filter_ds_by_profile(self, datastores, storage_profile): """Filter out datastores that do not match given storage profile. @@ -951,6 +962,7 @@ class VMwareVcVmdkDriver(VMwareEsxVmdkDriver): def __init__(self, *args, **kwargs): super(VMwareVcVmdkDriver, self).__init__(*args, **kwargs) + self.configuration.append_config_values(spbm_opts) self._session = None @property @@ -962,16 +974,37 @@ class VMwareVcVmdkDriver(VMwareEsxVmdkDriver): api_retry_count = self.configuration.vmware_api_retry_count task_poll_interval = self.configuration.vmware_task_poll_interval wsdl_loc = self.configuration.safe_get('vmware_wsdl_location') - pbm_wsdl = self.configuration.vmware_pbm_wsdl + pbm_wsdl = self.configuration.pbm_wsdl_location self._session = api.VMwareAPISession(ip, username, password, api_retry_count, task_poll_interval, wsdl_loc=wsdl_loc, pbm_wsdl=pbm_wsdl) - if pbm_wsdl: - self._storage_policy_enabled = True return self._session + def do_setup(self, context): + """Any initialization the volume driver does while starting.""" + super(VMwareVcVmdkDriver, self).do_setup(context) + # VC specific setup is done here + pbm_wsdl = self.configuration.pbm_wsdl_location + default_policy = self.configuration.pbm_default_policy + if not pbm_wsdl: + if default_policy: + LOG.warn(_("Ignoring %s since pbm_wsdl_location is not " + "set."), default_policy) + return + # pbm_wsdl is set, so storage policy should be enabled + self._storage_policy_enabled = True + # now verify the default policy exists in VC + if default_policy: + if not self.volumeops.retrieve_profile_id(default_policy): + msg = _("The configured default PBM policy '%s' is not " + "defined on vCenter Server.") % default_policy + raise error_util.PbmDefaultPolicyDoesNotExist(message=msg) + else: + LOG.info(_("Successfully verified existence of " + "pbm_default_policy: %s."), default_policy) + def _get_volume_group_folder(self, datacenter): """Get volume group folder. diff --git a/etc/cinder/cinder.conf.sample b/etc/cinder/cinder.conf.sample index abf31a02e..d2f32770c 100644 --- a/etc/cinder/cinder.conf.sample +++ b/etc/cinder/cinder.conf.sample @@ -1661,6 +1661,17 @@ # Options defined in cinder.volume.drivers.vmware.vmdk # +# PBM service WSDL file location URL. e.g. +# file:///opt/SDK/spbm/wsdl/pbmService.wsdl. Not setting this +# will disable storage policy based placement of volumes. +# (string value) +#pbm_wsdl_location= + +# The PBM default policy. If pbm_wsdl_location is set and +# there is no defined storage policy for the specific request +# then this policy will be used. (string value) +#pbm_default_policy= + # IP address for connecting to VMware ESX/VC server. (string # value) #vmware_host_ip= @@ -1700,12 +1711,6 @@ # less than the configured value. (integer value) #vmware_max_objects_retrieval=100 -# PBM service WSDL file location URL. e.g. -# file:///opt/SDK/spbm/wsdl/pbmService.wsdl. Not setting this -# will disable storage policy based placement of volumes. -# (string value) -#vmware_pbm_wsdl= - # # Options defined in cinder.volume.drivers.windows.windows -- 2.45.2