from cinder import exception
from cinder import test
from cinder.volume.drivers import nimble
+from cinder.volume import volume_types
CONF = cfg.CONF
[{'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}]}}
[{'code': 0}]},
'vol': {'target-name': 'iqn.test'}}
+FAKE_TYPE_ID = 12345
+
def create_configuration(username, password, ip_address,
pool_name=None, subnet_label=None,
'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(
'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)
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': ''})
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,
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
Version history:
1.0 - Initial driver
+ 1.1.0 - Added Extra Spec Capability
"""
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)
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):
# 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."""