From 7093d0e290a6916db9eb244c187c56ebfa5b1990 Mon Sep 17 00:00:00 2001 From: Raunak Kumar Date: Mon, 6 Jul 2015 11:40:40 -0700 Subject: [PATCH] Add extra spec capability for Nimble Cinder Driver Supported Extra Specs are 1. Performance Policy Name nimble:perfpol-name = 2. Encryption nimble:encryption = "yes" Implements: blueprint nimble-add-extra-specs-support Change-Id: I45a18b189746b778f10eecbf669be7943828e28b --- cinder/tests/unit/test_nimble.py | 140 ++++++++++++++++++++++++++++++- cinder/volume/drivers/nimble.py | 62 +++++++++++--- 2 files changed, 188 insertions(+), 14 deletions(-) mode change 100644 => 100755 cinder/tests/unit/test_nimble.py mode change 100644 => 100755 cinder/volume/drivers/nimble.py diff --git a/cinder/tests/unit/test_nimble.py b/cinder/tests/unit/test_nimble.py old mode 100644 new mode 100755 index 4e3d02419..8bbcc28c1 --- a/cinder/tests/unit/test_nimble.py +++ b/cinder/tests/unit/test_nimble.py @@ -19,6 +19,7 @@ from oslo_config import cfg from cinder import exception from cinder import test from cinder.volume.drivers import nimble +from cinder.volume import volume_types CONF = cfg.CONF @@ -59,13 +60,33 @@ FAKE_NEGATIVE_NETCONFIG_RESPONSE = {'err-list': {'err-list': [{'code': 13}]}} FAKE_CREATE_VOLUME_POSITIVE_RESPONSE = {'err-list': {'err-list': - [{'code': 0}]}, + [{'code': 0}]}, 'name': "openstack-test11"} +FAKE_CREATE_VOLUME_POSITIVE_RESPONSE_ENCRYPTION = {'err-list': {'err-list': + [{'code': 0}]}, + 'name': + "openstack-test-encryption"} + +FAKE_CREATE_VOLUME_POSITIVE_RESPONSE_PERFPOLICY = {'err-list': {'err-list': + [{'code': 0}]}, + 'name': + "openstack-test-perfpolicy"} + FAKE_CREATE_VOLUME_NEGATIVE_RESPONSE = {'err-list': {'err-list': [{'code': 17}]}, 'name': "openstack-test11"} +FAKE_CREATE_VOLUME_NEGATIVE_RESPONSE_ENCRYPTION = {'err-list': {'err-list': + [{'code': 17}]}, + 'name': + "openstack-test-encryption"} + +FAKE_CREATE_VOLUME_NEGATIVE_RESPONSE_PERFPOLICY = {'err-list': {'err-list': + [{'code': 17}]}, + 'name': + "openstack-test-perfpolicy"} + FAKE_GENERIC_POSITIVE_RESPONSE = {'err-list': {'err-list': [{'code': 0}]}} @@ -90,6 +111,8 @@ FAKE_GET_VOL_INFO_RESPONSE = {'err-list': {'err-list': [{'code': 0}]}, 'vol': {'target-name': 'iqn.test'}} +FAKE_TYPE_ID = 12345 + def create_configuration(username, password, ip_address, pool_name=None, subnet_label=None, @@ -214,6 +237,7 @@ class NimbleDriverVolumeTestCase(NimbleDriverBaseTestCase): 'provider_auth': None}, self.driver.create_volume({'name': 'testvolume', 'size': 1, + 'volume_type_id': None, 'display_name': '', 'display_description': ''})) self.mock_client_service.service.createVol.assert_called_once_with( @@ -222,7 +246,84 @@ class NimbleDriverVolumeTestCase(NimbleDriverBaseTestCase): 'name': 'testvolume', 'reserve': 0, 'online': True, 'pool-name': 'default', 'size': 1073741824, 'quota': 1073741824, - 'perfpol-name': 'default', 'description': ''}, + 'perfpol-name': 'default', 'description': '', + 'encryptionAttr': {'cipher': 3}}, + 'sid': 'a9b9aba7'}) + + @mock.patch(NIMBLE_URLLIB2) + @mock.patch(NIMBLE_CLIENT) + @mock.patch.object(volume_types, 'get_volume_type_extra_specs', + mock.Mock(type_id=FAKE_TYPE_ID, return_value={ + 'nimble:perfpol-name': 'default', + 'nimble:encryption': 'yes'})) + @NimbleDriverBaseTestCase.client_mock_decorator(create_configuration( + 'nimble', 'nimble_pass', '10.18.108.55', 'default', '*')) + def test_create_volume_encryption_positive(self): + self.mock_client_service.service.createVol.return_value = \ + FAKE_CREATE_VOLUME_POSITIVE_RESPONSE_ENCRYPTION + self.mock_client_service.service.getVolInfo.return_value = \ + FAKE_GET_VOL_INFO_RESPONSE + self.mock_client_service.service.getNetConfig.return_value = \ + FAKE_POSITIVE_NETCONFIG_RESPONSE + + self.assertEqual({ + 'provider_location': '172.18.108.21:3260 iqn.test 0', + 'provider_auth': None}, + self.driver.create_volume({'name': 'testvolume-encryption', + 'size': 1, + 'volume_type_id': FAKE_TYPE_ID, + 'display_name': '', + 'display_description': ''})) + + mock_volume_type = volume_types.get_volume_type_extra_specs + mock_volume_type.assert_called_once_with(FAKE_TYPE_ID) + + self.mock_client_service.service.createVol.assert_called_once_with( + request={ + 'attr': {'snap-quota': 1073741824, 'warn-level': 858993459, + 'name': 'testvolume-encryption', 'reserve': 0, + 'online': True, 'pool-name': 'default', + 'size': 1073741824, 'quota': 1073741824, + 'perfpol-name': 'default', 'description': '', + 'encryptionAttr': {'cipher': 2}}, + 'sid': 'a9b9aba7'}) + + @mock.patch(NIMBLE_URLLIB2) + @mock.patch(NIMBLE_CLIENT) + @mock.patch.object(volume_types, 'get_volume_type_extra_specs', + mock.Mock(type_id=FAKE_TYPE_ID, return_value={ + 'nimble:perfpol-name': 'VMware ESX', + 'nimble:encryption': 'no'})) + @NimbleDriverBaseTestCase.client_mock_decorator(create_configuration( + 'nimble', 'nimble_pass', '10.18.108.55', 'default', '*')) + def test_create_volume_perfpolicy_positive(self): + self.mock_client_service.service.createVol.return_value = \ + FAKE_CREATE_VOLUME_POSITIVE_RESPONSE_PERFPOLICY + self.mock_client_service.service.getVolInfo.return_value = \ + FAKE_GET_VOL_INFO_RESPONSE + self.mock_client_service.service.getNetConfig.return_value = \ + FAKE_POSITIVE_NETCONFIG_RESPONSE + + self.assertEqual( + {'provider_location': '172.18.108.21:3260 iqn.test 0', + 'provider_auth': None}, + self.driver.create_volume({'name': 'testvolume-perfpolicy', + 'size': 1, + 'volume_type_id': FAKE_TYPE_ID, + 'display_name': '', + 'display_description': ''})) + + mock_volume_type = volume_types.get_volume_type_extra_specs + mock_volume_type.assert_called_once_with(FAKE_TYPE_ID) + + self.mock_client_service.service.createVol.assert_called_once_with( + request={ + 'attr': {'snap-quota': 1073741824, 'warn-level': 858993459, + 'name': 'testvolume-perfpolicy', 'reserve': 0, + 'online': True, 'pool-name': 'default', + 'size': 1073741824, 'quota': 1073741824, + 'perfpol-name': 'VMware ESX', 'description': '', + 'encryptionAttr': {'cipher': 3}}, 'sid': 'a9b9aba7'}) @mock.patch(NIMBLE_URLLIB2) @@ -237,6 +338,39 @@ class NimbleDriverVolumeTestCase(NimbleDriverBaseTestCase): self.driver.create_volume, {'name': 'testvolume', 'size': 1, + 'volume_type_id': None, + 'display_name': '', + 'display_description': ''}) + + @mock.patch(NIMBLE_URLLIB2) + @mock.patch(NIMBLE_CLIENT) + @NimbleDriverBaseTestCase.client_mock_decorator(create_configuration( + 'nimble', 'nimble_pass', '10.18.108.55', 'default', '*')) + def test_create_volume_encryption_negative(self): + self.mock_client_service.service.createVol.return_value = \ + FAKE_CREATE_VOLUME_NEGATIVE_RESPONSE_ENCRYPTION + self.assertRaises( + exception.VolumeBackendAPIException, + self.driver.create_volume, + {'name': 'testvolume-encryption', + 'size': 1, + 'volume_type_id': None, + 'display_name': '', + 'display_description': ''}) + + @mock.patch(NIMBLE_URLLIB2) + @mock.patch(NIMBLE_CLIENT) + @NimbleDriverBaseTestCase.client_mock_decorator(create_configuration( + 'nimble', 'nimble_pass', '10.18.108.55', 'default', '*')) + def test_create_volume_perfpolicy_negative(self): + self.mock_client_service.service.createVol.return_value = \ + FAKE_CREATE_VOLUME_NEGATIVE_RESPONSE_PERFPOLICY + self.assertRaises( + exception.VolumeBackendAPIException, + self.driver.create_volume, + {'name': 'testvolume-perfpolicy', + 'size': 1, + 'volume_type_id': None, 'display_name': '', 'display_description': ''}) @@ -328,7 +462,7 @@ class NimbleDriverVolumeTestCase(NimbleDriverBaseTestCase): def test_get_volume_stats(self): self.mock_client_service.service.getGroupConfig.return_value = \ FAKE_POSITIVE_GROUP_CONFIG_RESPONSE - expected_res = {'driver_version': '1.0', + expected_res = {'driver_version': '1.1.0', 'total_capacity_gb': 7466.30419921875, 'QoS_support': False, 'reserved_percentage': 0, diff --git a/cinder/volume/drivers/nimble.py b/cinder/volume/drivers/nimble.py old mode 100644 new mode 100755 index 99ff7b5c1..0bd4bb84d --- a/cinder/volume/drivers/nimble.py +++ b/cinder/volume/drivers/nimble.py @@ -33,9 +33,16 @@ from suds import client from cinder import exception from cinder.i18n import _, _LE, _LI from cinder.volume.drivers.san import san +from cinder.volume import volume_types -DRIVER_VERSION = '1.0' +DRIVER_VERSION = '1.1.0' +AES_256_XTS_CIPHER = 2 +DEFAULT_CIPHER = 3 +EXTRA_SPEC_ENCRYPTION = 'nimble:encryption' +EXTRA_SPEC_PERF_POLICY = 'nimble:perfpol-name' +DEFAULT_PERF_POLICY_SETTING = 'default' +DEFAULT_ENCRYPTION_SETTING = 'no' VOL_EDIT_MASK = 4 + 16 + 32 + 64 + 512 SOAP_PORT = 5391 SM_ACL_APPLY_TO_BOTH = 3 @@ -74,6 +81,7 @@ class NimbleISCSIDriver(san.SanISCSIDriver): Version history: 1.0 - Initial driver + 1.1.0 - Added Extra Spec Capability """ @@ -412,6 +420,7 @@ class NimbleAPIExecutor(object): self.sid = None self.username = kwargs['username'] self.password = kwargs['password'] + wsdl_url = 'https://%s/wsdl/NsGroupManagement.wsdl' % (kwargs['ip']) LOG.debug('Using Nimble wsdl_url: %s', wsdl_url) self.err_string_dict = self._create_err_code_to_str_mapper(wsdl_url) @@ -469,6 +478,23 @@ class NimbleAPIExecutor(object): response = self._execute_get_netconfig(name) return response['config'] + def _get_volumetype_extraspecs(self, volume): + specs = {} + + type_id = volume['volume_type_id'] + if type_id is not None: + specs = volume_types.get_volume_type_extra_specs(type_id) + return specs + + def _get_extra_spec_values(self, extra_specs): + """Nimble specific extra specs.""" + perf_policy_name = extra_specs.get(EXTRA_SPEC_PERF_POLICY, + DEFAULT_PERF_POLICY_SETTING) + encryption = extra_specs.get(EXTRA_SPEC_ENCRYPTION, + DEFAULT_ENCRYPTION_SETTING) + + return perf_policy_name, encryption + @_connection_checker @_response_checker def _execute_create_vol(self, volume, pool_name, reserve): @@ -482,26 +508,40 @@ class NimbleAPIExecutor(object): # Limit description size to 254 characters description = description[:254] - LOG.info(_LI('Creating a new volume=%(vol)s size=%(size)s' - ' reserve=%(reserve)s in pool=%(pool)s' - ' description=%(description)s'), - {'vol': volume['name'], - 'size': volume_size, - 'reserve': reserve, - 'pool': pool_name, - 'description': description}) + specs = self._get_volumetype_extraspecs(volume) + perf_policy_name, encrypt = self._get_extra_spec_values(specs) + # default value of cipher for encryption + cipher = DEFAULT_CIPHER + if encrypt.lower() == 'yes': + cipher = AES_256_XTS_CIPHER + + LOG.debug('Creating a new volume=%(vol)s size=%(size)s' + ' reserve=%(reserve)s in pool=%(pool)s' + ' description=%(description)s with Extra Specs' + ' perfpol-name=%(perfpol-name)s' + ' encryption=%(encryption)s cipher=%(cipher)s', + {'vol': volume['name'], + 'size': volume_size, + 'reserve': reserve, + 'pool': pool_name, + 'description': description, + 'perfpol-name': perf_policy_name, + 'encryption': encrypt, + 'cipher': cipher}) + return self.client.service.createVol( request={'sid': self.sid, 'attr': {'name': volume['name'], 'description': description, 'size': volume_size, - 'perfpol-name': 'default', 'reserve': reserve_size, 'warn-level': int(volume_size * WARN_LEVEL), 'quota': volume_size, 'snap-quota': volume_size, 'online': True, - 'pool-name': pool_name}}) + 'pool-name': pool_name, + 'perfpol-name': perf_policy_name, + 'encryptionAttr': {'cipher': cipher}}}) def create_vol(self, volume, pool_name, reserve): """Execute createVol API.""" -- 2.45.2