Support standard Manila capability flags in NetApp cDOT driver

With the addition of capability lists in the CapabilitiesFilter, we
can now support the standard values of compression, dedupe, and
thin_provisioning in the NetApp cDOT driver. The legacy scoped
values continue to be supported (i.e. netapp:compression, etc.).

Closes-Bug: #1493951
Change-Id: I39099b5e8bfeb03d805d566e787b6c4c5d6221e1
This commit is contained in:
Clinton Knight 2015-11-29 14:48:22 -08:00
parent e5a6f05b03
commit 261c6a4323
5 changed files with 108 additions and 1 deletions

View File

@ -63,6 +63,12 @@ class NetAppCmodeFileStorageLibrary(object):
'netapp:language': 'language',
'netapp:max_files': 'max_files',
}
# Maps standard extra spec keys to legacy NetApp keys
STANDARD_BOOLEAN_EXTRA_SPECS_MAP = {
'thin_provisioning': 'netapp:thin_provisioned',
'dedupe': 'netapp:dedup',
'compression': 'netapp:compression',
}
def __init__(self, driver_name, **kwargs):
na_utils.validate_driver_instantiation(**kwargs)
@ -256,6 +262,9 @@ class NetAppCmodeFileStorageLibrary(object):
'allocated_capacity_gb': allocated_capacity_gb,
'QoS_support': 'False',
'reserved_percentage': 0,
'dedupe': [True, False],
'compression': [True, False],
'thin_provisioning': [True, False],
}
# Add storage service catalog data.
@ -366,6 +375,7 @@ class NetAppCmodeFileStorageLibrary(object):
raise exception.InvalidHost(reason=msg)
extra_specs = share_types.get_extra_specs_from_share(share)
extra_specs = self._remap_standard_boolean_extra_specs(extra_specs)
self._check_extra_specs_validity(share, extra_specs)
provisioning_options = self._get_provisioning_options(extra_specs)
@ -377,6 +387,18 @@ class NetAppCmodeFileStorageLibrary(object):
share['size'],
**provisioning_options)
@na_utils.trace
def _remap_standard_boolean_extra_specs(self, extra_specs):
"""Replace standard boolean extra specs with NetApp-specific ones."""
specs = copy.deepcopy(extra_specs)
for (key, netapp_key) in self.STANDARD_BOOLEAN_EXTRA_SPECS_MAP.items():
if key in specs:
bool_value = share_types.parse_boolean_extra_spec(key,
specs[key])
specs[netapp_key] = 'true' if bool_value else 'false'
del specs[key]
return specs
@na_utils.trace
def _check_extra_specs_validity(self, share, extra_specs):
"""Check if the extra_specs have valid values."""

View File

@ -15,6 +15,7 @@
"""Built-in share type properties."""
import re
from oslo_config import cfg
from oslo_db import exception as db_exception
@ -318,3 +319,28 @@ def share_types_diff(context, share_type_id1, share_type_id2):
def get_extra_specs_from_share(share):
type_id = share.get('share_type_id', None)
return get_share_type_extra_specs(type_id)
def parse_boolean_extra_spec(extra_spec_key, extra_spec_value):
"""Parse extra spec values of the form '<is> True' or '<is> False'
This method returns the boolean value of an extra spec value. If
the value does not conform to the standard boolean pattern, it raises
an InvalidExtraSpec exception.
"""
try:
if not isinstance(extra_spec_value, six.string_types):
raise ValueError
match = re.match(r'^<is>\s*(?P<value>True|False)$',
extra_spec_value.strip(),
re.IGNORECASE)
if not match:
raise ValueError
else:
return strutils.bool_from_string(match.group('value'), strict=True)
except ValueError:
msg = (_('Invalid boolean extra spec %(key)s : %(value)s') %
{'key': extra_spec_key, 'value': extra_spec_value})
raise exception.InvalidExtraSpec(reason=msg)

View File

@ -554,7 +554,9 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
return_value=fake.POOL_NAME))
self.mock_object(share_types, 'get_extra_specs_from_share',
mock.Mock(return_value=fake.EXTRA_SPEC))
mock_remap_standard_boolean_extra_specs = self.mock_object(
self.library, '_remap_standard_boolean_extra_specs',
mock.Mock(return_value=fake.EXTRA_SPEC))
self.mock_object(self.library, '_check_boolean_extra_specs_validity')
self.mock_object(self.library, '_get_boolean_provisioning_options',
mock.Mock(return_value=fake.PROVISIONING_OPTIONS))
@ -568,6 +570,16 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
thin_provisioned=True, snapshot_policy='default',
language='en-US', dedup_enabled=True,
compression_enabled=False, max_files=5000)
mock_remap_standard_boolean_extra_specs.assert_called_once_with(
fake.EXTRA_SPEC)
def test_remap_standard_boolean_extra_specs(self):
extra_specs = copy.deepcopy(fake.OVERLAPPING_EXTRA_SPEC)
result = self.library._remap_standard_boolean_extra_specs(extra_specs)
self.assertDictEqual(fake.REMAPPED_OVERLAPPING_EXTRA_SPEC, result)
def test_allocate_container_no_pool_name(self):
self.mock_object(self.library, '_get_valid_share_name', mock.Mock(

View File

@ -191,6 +191,21 @@ SHARE_TYPE = {
'extra_specs': EXTRA_SPEC
}
OVERLAPPING_EXTRA_SPEC = {
'compression': '<is> True',
'netapp:compression': 'true',
'dedupe': '<is> True',
'netapp:dedup': 'false',
'thin_provisioning': '<is> False',
'netapp:thin_provisioned': 'true',
}
REMAPPED_OVERLAPPING_EXTRA_SPEC = {
'netapp:compression': 'true',
'netapp:dedup': 'true',
'netapp:thin_provisioned': 'false',
}
EXTRA_SPEC_SHARE = copy.deepcopy(SHARE)
EXTRA_SPEC_SHARE['share_type_id'] = SHARE_TYPE_ID
@ -424,6 +439,9 @@ POOLS = [
'allocated_capacity_gb': 2.2,
'QoS_support': 'False',
'reserved_percentage': 0,
'dedupe': [True, False],
'compression': [True, False],
'thin_provisioning': [True, False],
'netapp_raid_type': 'raid4',
'netapp_disk_type': 'FCAL'
},
@ -433,6 +451,9 @@ POOLS = [
'allocated_capacity_gb': 4.0,
'QoS_support': 'False',
'reserved_percentage': 0,
'dedupe': [True, False],
'compression': [True, False],
'thin_provisioning': [True, False],
'netapp_raid_type': 'raid_dp',
'netapp_disk_type': 'SSD'
},
@ -445,6 +466,9 @@ POOLS_VSERVER_CREDS = [
'allocated_capacity_gb': 0.0,
'QoS_support': 'False',
'reserved_percentage': 0,
'dedupe': [True, False],
'compression': [True, False],
'thin_provisioning': [True, False],
'netapp_raid_type': 'raid4',
'netapp_disk_type': 'FCAL'
},
@ -454,6 +478,9 @@ POOLS_VSERVER_CREDS = [
'allocated_capacity_gb': 0.0,
'QoS_support': 'False',
'reserved_percentage': 0,
'dedupe': [True, False],
'compression': [True, False],
'thin_provisioning': [True, False],
'netapp_raid_type': 'raid_dp',
'netapp_disk_type': 'SSD'
},

View File

@ -256,3 +256,23 @@ class ShareTypesTestCase(test.TestCase):
self.assertRaises(exception.InvalidShareType,
share_types.remove_share_type_access,
'fake', None, 'fake')
@ddt.data({'spec_value': '<is> True', 'expected': True},
{'spec_value': '<is>true', 'expected': True},
{'spec_value': '<is> False', 'expected': False},
{'spec_value': '<is>false', 'expected': False},
{'spec_value': u' <is> FaLsE ', 'expected': False})
@ddt.unpack
def test_parse_boolean_extra_spec(self, spec_value, expected):
result = share_types.parse_boolean_extra_spec('fake_key', spec_value)
self.assertEqual(expected, result)
@ddt.data('True', 'False', '<isnt> True', '<is> Wrong', None, 5)
def test_parse_boolean_extra_spec_invalid(self, spec_value):
self.assertRaises(exception.InvalidExtraSpec,
share_types.parse_boolean_extra_spec,
'fake_key',
spec_value)