Merge "NetApp ONTAP: Add core functions on REST client"

This commit is contained in:
Zuul 2022-09-09 03:34:19 +00:00 committed by Gerrit Code Review
commit 48a36c3800
14 changed files with 5304 additions and 176 deletions

View File

@ -1441,6 +1441,7 @@ class NetAppCmodeClientTestCase(test.TestCase):
<num-records>1</num-records>
<attributes-list>
<net-interface-info>
<vserver>fake_vserver</vserver>
</net-interface-info>
</attributes-list>
</results>"""))
@ -4494,3 +4495,46 @@ class NetAppCmodeClientTestCase(test.TestCase):
}
self.client.connection.send_request.assert_called_once_with(
'file-rename-file', api_args)
def test_check_api_permissions(self):
mock_log = self.mock_object(client_cmode.LOG, 'warning')
self.mock_object(self.client, 'check_cluster_api', return_value=True)
self.client.check_api_permissions()
self.client.check_cluster_api.assert_has_calls(
[mock.call(*key) for key in client_cmode.SSC_API_MAP.keys()])
self.assertEqual(0, mock_log.call_count)
def test_check_api_permissions_failed_ssc_apis(self):
def check_cluster_api(object_name, operation_name, api):
if api != 'volume-get-iter':
return False
return True
self.mock_object(self.client, 'check_cluster_api',
side_effect=check_cluster_api)
mock_log = self.mock_object(client_cmode.LOG, 'warning')
self.client.check_api_permissions()
self.assertEqual(1, mock_log.call_count)
def test_check_api_permissions_failed_volume_api(self):
def check_cluster_api(object_name, operation_name, api):
if api == 'volume-get-iter':
return False
return True
self.mock_object(self.client, 'check_cluster_api',
side_effect=check_cluster_api)
mock_log = self.mock_object(client_cmode.LOG, 'warning')
self.assertRaises(exception.VolumeBackendAPIException,
self.client.check_api_permissions)
self.assertEqual(0, mock_log.call_count)

View File

@ -275,8 +275,9 @@ IGROUP1 = {'initiator-group-os-type': 'linux',
QOS_SPECS = {}
EXTRA_SPECS = {}
MAX_THROUGHPUT = '21734278B/s'
MIN_IOPS = '256IOPS'
MAX_IOPS = '512IOPS'
MIN_IOPS = '256iops'
MAX_IOPS = '512iops'
MAX_BPS = '1000000B/s'
QOS_POLICY_GROUP_NAME = 'fake_qos_policy_group_name'
QOS_POLICY_GROUP_INFO_LEGACY = {
@ -290,6 +291,11 @@ QOS_POLICY_GROUP_SPEC = {
'policy_name': QOS_POLICY_GROUP_NAME,
}
QOS_POLICY_GROUP_SPEC_BPS = {
'max_throughput': MAX_BPS,
'policy_name': QOS_POLICY_GROUP_NAME,
}
QOS_POLICY_GROUP_SPEC_MAX = {
'max_throughput': MAX_THROUGHPUT,
'policy_name': QOS_POLICY_GROUP_NAME,
@ -417,6 +423,19 @@ FAKE_LUN = netapp_api.NaElement.create_node_with_children(
'volume': 'fakeLUN',
'vserver': 'fake_vserver'})
FAKE_LUN_GET_ITER_RESULT = [
{
'Vserver': 'fake_vserver',
'Volume': 'fake_volume',
'Size': 123,
'Qtree': 'fake_qtree',
'Path': 'fake_path',
'OsType': 'fake_os',
'SpaceReserved': 'true',
'UUID': 'fake-uuid',
},
]
CG_VOLUME_NAME = 'fake_cg_volume'
CG_GROUP_NAME = 'fake_consistency_group'
CG_POOL_NAME = 'cdot'
@ -740,12 +759,219 @@ def get_fake_net_interface_get_iter_response():
def get_fake_ifs():
list_of_ifs = [
etree.XML("""<net-interface-info>
<address>FAKE_IP</address></net-interface-info>"""),
etree.XML("""<net-interface-info>
<address>FAKE_IP2</address></net-interface-info>"""),
etree.XML("""<net-interface-info>
<address>FAKE_IP3</address></net-interface-info>"""),
]
return [netapp_api.NaElement(el) for el in list_of_ifs]
return [{'vserver': VSERVER_NAME}]
AFF_SYSTEM_NODE_GET_ITER_RESPONSE_REST = {
"records": [
{
"uuid": "9eff6c76-fc13-11ea-8799-525400",
"name": "aff-node1",
"model": "AFFA400",
"is_all_flash_optimized": True,
"is_all_flash_select_optimized": False,
"_links": {
"self": {
"href": "/api/cluster/nodes/9eff6c76-fc13-11ea-8799-525400"
}
}
},
{
"uuid": "9eff6c76-fc13-11ea-8799-52540006bba9",
"name": "aff-node2",
"model": "AFFA400",
"is_all_flash_optimized": True,
"is_all_flash_select_optimized": False,
"_links": {
"self": {
"href": "/api/cluster/nodes/9eff6c76-fc13-11ea-8799-525400"
}
}
}
],
"num_records": 2,
"_links": {
"self": {
"href": "/api/cluster/nodes?fields=model,name,"
"is_all_flash_optimized,is_all_flash_select_optimized"
}
}
}
FAS_SYSTEM_NODE_GET_ITER_RESPONSE_REST = {
"records": [
{
"uuid": "9eff6c76-fc13-11ea-8799-52540006bba9",
"name": "fas-node1",
"model": "FAS2554",
"is_all_flash_optimized": False,
"is_all_flash_select_optimized": False,
"_links": {
"self": {
"href": "/api/cluster/nodes/9eff6c76-fc13-11ea-8799-525400"
}
}
},
{
"uuid": "9eff6c76-fc13-11ea-8799-52540006bba9",
"name": "fas-node2",
"model": "FAS2554",
"is_all_flash_optimized": False,
"is_all_flash_select_optimized": False,
"_links": {
"self": {
"href": "/api/cluster/nodes/9eff6c76-fc13-11ea-8799-525400"
}
}
}
],
"num_records": 2,
"_links": {
"self": {
"href": "/api/cluster/nodes?fields=model,name,"
"is_all_flash_optimized,is_all_flash_select_optimized"
}
}
}
HYBRID_SYSTEM_NODE_GET_ITER_RESPONSE_REST = {
"records": [
{
"uuid": "9eff6c76-fc13-11ea-8799-52540006bba9",
"name": "select-node",
"model": "FDvM300",
"is_all_flash_optimized": False,
"is_all_flash_select_optimized": True,
"_links": {
"self": {
"href": "/api/cluster/nodes/9eff6c76-fc13-11ea-8799-525400"
}
}
},
{
"uuid": "9eff6c76-fc13-11ea-8799-52540006bba9",
"name": "c190-node",
"model": "AFF-C190",
"is_all_flash_optimized": True,
"is_all_flash_select_optimized": False,
"_links": {
"self": {
"href": "/api/cluster/nodes/9eff6c76-fc13-11ea-8799-525400"
}
}
}
],
"num_records": 2,
"_links": {
"self": {
"href": "/api/cluster/nodes?fields=model,name,"
"is_all_flash_optimized,is_all_flash_select_optimized"
}
}
}
QOS_POLICY_BY_NAME_RESPONSE_REST = {
"records": [
{
"uuid": "9eff6c76-fc13-11ea-8799-52540006bba9",
"name": "openstack-cd-uuid",
"_links": {
"self": {
"href": "/api/storage/qos/policies/"
"9eff6c76-fc13-11ea-8799-52540006bba9"
}
}
}
],
"num_records": 1,
"_links": {
"self": {
"href": "/api/storage/qos/policies?fields=name"
}
}
}
QOS_SPECS_REST = {}
MAX_THROUGHPUT_REST = '21734278'
MIN_IOPS_REST = '256'
MAX_IOPS_REST = '512'
MAX_BPS_REST = '1'
QOS_POLICY_GROUP_INFO_LEGACY_REST = {
'legacy': 'legacy-' + QOS_POLICY_GROUP_NAME,
'spec': None,
}
QOS_POLICY_GROUP_SPEC_REST = {
'min_throughput': MIN_IOPS_REST,
'max_throughput': MAX_IOPS_REST,
'policy_name': QOS_POLICY_GROUP_NAME,
}
QOS_POLICY_GROUP_API_ARGS_REST = {
'name': QOS_POLICY_GROUP_NAME,
'svm': {
'name': VSERVER_NAME
},
'fixed': {
'max_throughput_iops': int(MAX_IOPS_REST),
'min_throughput_iops': int(MIN_IOPS_REST)
}
}
QOS_POLICY_GROUP_API_ARGS_REST_BPS = {
'name': QOS_POLICY_GROUP_NAME,
'svm': {
'name': VSERVER_NAME
},
'fixed': {
'max_throughput_mbps': int(MAX_BPS_REST),
}
}
QOS_POLICY_GROUP_SPEC_MAX_REST = {
'max_throughput': MAX_THROUGHPUT_REST,
'policy_name': QOS_POLICY_GROUP_NAME,
}
EXPECTED_IOPS_PER_GB_REST = '128'
PEAK_IOPS_PER_GB_REST = '512'
PEAK_IOPS_ALLOCATION_REST = 'used-space'
EXPECTED_IOPS_ALLOCATION_REST = 'used-space'
ABSOLUTE_MIN_IOPS_REST = '75'
BLOCK_SIZE_REST = 'ANY'
ADAPTIVE_QOS_SPEC_REST = {
'policy_name': QOS_POLICY_GROUP_NAME,
'expected_iops': EXPECTED_IOPS_PER_GB_REST,
'expected_iops_allocation': EXPECTED_IOPS_ALLOCATION_REST,
'peak_iops': PEAK_IOPS_PER_GB_REST,
'peak_iops_allocation': PEAK_IOPS_ALLOCATION_REST,
'absolute_min_iops': ABSOLUTE_MIN_IOPS_REST,
'block_size': BLOCK_SIZE_REST,
}
ADAPTIVE_QOS_API_ARGS_REST = {
'name': QOS_POLICY_GROUP_NAME,
'svm': {
'name': VSERVER_NAME
},
'adaptive': {
'absolute_min_iops': int(ABSOLUTE_MIN_IOPS_REST),
'expected_iops': int(EXPECTED_IOPS_PER_GB_REST),
'expected_iops_allocation': EXPECTED_IOPS_ALLOCATION_REST,
'peak_iops': int(PEAK_IOPS_PER_GB_REST),
'peak_iops_allocation': PEAK_IOPS_ALLOCATION_REST,
'block_size': BLOCK_SIZE_REST,
}
}
QOS_POLICY_GROUP_INFO_REST = {
'legacy': None, 'spec': QOS_POLICY_GROUP_SPEC_REST}
QOS_POLICY_GROUP_INFO_MAX_REST = {
'legacy': None, 'spec': QOS_POLICY_GROUP_SPEC_MAX_REST}
ADAPTIVE_QOS_POLICY_GROUP_INFO_REST = {
'legacy': None,
'spec': ADAPTIVE_QOS_SPEC_REST,
}
REST_FIELDS = 'uuid,name,style'

View File

@ -19,7 +19,6 @@
from unittest import mock
import ddt
import six
from cinder import exception
from cinder.objects import fields
@ -280,10 +279,8 @@ class NetAppBlockStorageCmodeLibraryTestCase(test.TestCase):
self.library._get_lun_attr = mock.Mock(return_value={'Volume':
'fakeLUN'})
self.library.zapi_client = mock.Mock()
self.library.zapi_client.get_lun_by_args.return_value = [
mock.Mock(spec=netapp_api.NaElement)]
lun = fake.FAKE_LUN
self.library._get_lun_by_args = mock.Mock(return_value=[lun])
lun = fake.FAKE_LUN_GET_ITER_RESULT
self.library.zapi_client.get_lun_by_args.return_value = lun
self.library._add_lun_to_table = mock.Mock()
self.library._clone_lun('fakeLUN', 'newFakeLUN', 'false')
@ -303,10 +300,8 @@ class NetAppBlockStorageCmodeLibraryTestCase(test.TestCase):
self.library._get_lun_attr = mock.Mock(return_value={'Volume':
'fakeLUN'})
self.library.zapi_client = mock.Mock()
self.library.zapi_client.get_lun_by_args.return_value = [
mock.Mock(spec=netapp_api.NaElement)]
lun = fake.FAKE_LUN
self.library._get_lun_by_args = mock.Mock(return_value=[lun])
lun = fake.FAKE_LUN_GET_ITER_RESULT
self.library.zapi_client.get_lun_by_args.return_value = lun
self.library._add_lun_to_table = mock.Mock()
self.library._clone_lun('fakeLUN', 'newFakeLUN', 'false',
@ -327,10 +322,8 @@ class NetAppBlockStorageCmodeLibraryTestCase(test.TestCase):
'fakeLUN'})
self.library.zapi_client = mock.Mock()
self.library.lun_space_reservation = 'false'
self.library.zapi_client.get_lun_by_args.return_value = [
mock.Mock(spec=netapp_api.NaElement)]
lun = fake.FAKE_LUN
self.library._get_lun_by_args = mock.Mock(return_value=[lun])
lun = fake.FAKE_LUN_GET_ITER_RESULT
self.library.zapi_client.get_lun_by_args.return_value = lun
self.library._add_lun_to_table = mock.Mock()
self.library._clone_lun('fakeLUN', 'newFakeLUN', is_snapshot=True)
@ -1542,27 +1535,22 @@ class NetAppBlockStorageCmodeLibraryTestCase(test.TestCase):
fake.LUN_WITH_METADATA['metadata'])
new_snap_name = 'new-%s' % fake.SNAPSHOT['name']
snapshot_path = lun_obj.metadata['Path']
flexvol_name = lun_obj.metadata['Volume']
block_count = 40960
mock__get_lun_from_table = self.mock_object(
self.library, '_get_lun_from_table', return_value=lun_obj)
mock__get_lun_block_count = self.mock_object(
self.library, '_get_lun_block_count', return_value=block_count)
mock_create_lun = self.mock_object(self.library.zapi_client,
'create_lun')
mock__clone_lun = self.mock_object(self.library, '_clone_lun')
self.library._clone_snapshot(fake.SNAPSHOT['name'])
mock__get_lun_from_table.assert_called_once_with(fake.SNAPSHOT['name'])
mock__get_lun_block_count.assert_called_once_with(snapshot_path)
mock_create_lun.assert_called_once_with(flexvol_name, new_snap_name,
six.text_type(lun_obj.size),
lun_obj.metadata)
mock__clone_lun.assert_called_once_with(fake.SNAPSHOT['name'],
new_snap_name,
block_count=block_count)
space_reserved='false',
is_snapshot=True)
def test__clone_snapshot_invalid_block_count(self):
lun_obj = block_base.NetAppLun(fake.LUN_WITH_METADATA['handle'],
@ -1594,8 +1582,6 @@ class NetAppBlockStorageCmodeLibraryTestCase(test.TestCase):
self.library, '_get_lun_from_table', return_value=lun_obj)
mock__get_lun_block_count = self.mock_object(
self.library, '_get_lun_block_count', return_value=block_count)
mock_create_lun = self.mock_object(self.library.zapi_client,
'create_lun')
side_effect = exception.VolumeBackendAPIException(data='data')
mock__clone_lun = self.mock_object(self.library, '_clone_lun',
side_effect=side_effect)
@ -1608,12 +1594,10 @@ class NetAppBlockStorageCmodeLibraryTestCase(test.TestCase):
mock__get_lun_from_table.assert_called_once_with(fake.SNAPSHOT['name'])
mock__get_lun_block_count.assert_called_once_with(snapshot_path)
mock_create_lun.assert_called_once_with(flexvol_name, new_snap_name,
six.text_type(lun_obj.size),
lun_obj.metadata)
mock__clone_lun.assert_called_once_with(fake.SNAPSHOT['name'],
new_snap_name,
block_count=block_count)
space_reserved='false',
is_snapshot=True)
mock_destroy_lun.assert_called_once_with(new_lun_path)
def test__swap_luns(self):

View File

@ -453,7 +453,7 @@ class NetAppCmodeNfsDriverTestCase(test.TestCase):
vserver = self.driver._get_vserver_for_ip('FAKE_IP')
self.assertIsNone(vserver)
self.assertEqual(fake.VSERVER_NAME, vserver)
def test_check_for_setup_error(self):
mock_add_looping_tasks = self.mock_object(
@ -892,9 +892,8 @@ class NetAppCmodeNfsDriverTestCase(test.TestCase):
is_snapshot=is_snapshot)
def test__clone_backing_file_for_volume(self):
body = fake.get_fake_net_interface_get_iter_response()
self.driver.zapi_client.get_if_info_by_ip = mock.Mock(
return_value=[netapp_api.NaElement(body)])
return_value=[{'ip': 'fake_ip'}])
self.driver.zapi_client.get_vol_by_junc_vserver = mock.Mock(
return_value='nfsvol')
self.mock_object(self.driver, '_get_export_ip_path',

View File

@ -20,7 +20,6 @@ from unittest import mock
import ddt
import six
from cinder import exception
from cinder.tests.unit import test
from cinder.tests.unit.volume.drivers.netapp.dataontap.client import (
fakes as fake_client)
@ -46,45 +45,6 @@ class CapabilitiesLibraryTestCase(test.TestCase):
config.volume_backend_name = 'fake_backend'
return config
def test_check_api_permissions(self):
mock_log = self.mock_object(capabilities.LOG, 'warning')
self.ssc_library.check_api_permissions()
self.zapi_client.check_cluster_api.assert_has_calls(
[mock.call(*key) for key in capabilities.SSC_API_MAP.keys()])
self.assertEqual(0, mock_log.call_count)
def test_check_api_permissions_failed_ssc_apis(self):
def check_cluster_api(object_name, operation_name, api):
if api != 'volume-get-iter':
return False
return True
self.zapi_client.check_cluster_api.side_effect = check_cluster_api
mock_log = self.mock_object(capabilities.LOG, 'warning')
self.ssc_library.check_api_permissions()
self.assertEqual(1, mock_log.call_count)
def test_check_api_permissions_failed_volume_api(self):
def check_cluster_api(object_name, operation_name, api):
if api == 'volume-get-iter':
return False
return True
self.zapi_client.check_cluster_api.side_effect = check_cluster_api
mock_log = self.mock_object(capabilities.LOG, 'warning')
self.assertRaises(exception.VolumeBackendAPIException,
self.ssc_library.check_api_permissions)
self.assertEqual(0, mock_log.call_count)
def test_get_ssc(self):
result = self.ssc_library.get_ssc()

View File

@ -410,12 +410,11 @@ class NetAppBlockStorageLibrary(object):
def _extract_lun_info(self, lun):
"""Extracts the LUNs from API and populates the LUN table."""
meta_dict = self._create_lun_meta(lun)
path = lun.get_child_content('path')
path = lun['Path']
(_rest, _splitter, name) = path.rpartition('/')
handle = self._create_lun_handle(meta_dict)
size = lun.get_child_content('size')
return NetAppLun(handle, name, size, meta_dict)
handle = self._create_lun_handle(lun)
size = lun['Size']
return NetAppLun(handle, name, size, lun)
def _extract_and_populate_luns(self, api_luns):
"""Extracts the LUNs from API and populates the LUN table."""
@ -547,9 +546,6 @@ class NetAppBlockStorageLibrary(object):
LOG.error("Error getting LUN attribute. Exception: %s", e)
return None
def _create_lun_meta(self, lun):
raise NotImplementedError()
def _get_fc_target_wwpns(self, include_partner=True):
raise NotImplementedError()
@ -725,8 +721,8 @@ class NetAppBlockStorageLibrary(object):
msg = _('Failure getting LUN info for %s.')
raise exception.VolumeBackendAPIException(data=msg % seg[-1])
lun_info = lun_infos[-1]
bs = int(lun_info.get_child_content('block-size'))
ls = int(lun_info.get_child_content('size'))
bs = int(lun_info['BlockSize'])
ls = int(lun_info['Size'])
block_count = ls / bs
return block_count

View File

@ -236,27 +236,14 @@ class NetAppBlockStorageCmodeLibrary(block_base.NetAppBlockStorageLibrary,
if len(lun) == 0:
msg = _("No cloned LUN named %s found on the filer")
raise exception.VolumeBackendAPIException(data=msg % new_name)
clone_meta = self._create_lun_meta(lun[0])
self._add_lun_to_table(
block_base.NetAppLun('%s:%s' % (clone_meta['Vserver'],
clone_meta['Path']),
new_name,
lun[0].get_child_content('size'),
clone_meta))
def _create_lun_meta(self, lun):
"""Creates LUN metadata dictionary."""
self.zapi_client.check_is_naelement(lun)
meta_dict = {}
meta_dict['Vserver'] = lun.get_child_content('vserver')
meta_dict['Volume'] = lun.get_child_content('volume')
meta_dict['Qtree'] = lun.get_child_content('qtree')
meta_dict['Path'] = lun.get_child_content('path')
meta_dict['OsType'] = lun.get_child_content('multiprotocol-type')
meta_dict['SpaceReserved'] = \
lun.get_child_content('is-space-reservation-enabled')
meta_dict['UUID'] = lun.get_child_content('uuid')
return meta_dict
clone_lun = lun[0]
self._add_lun_to_table(
block_base.NetAppLun('%s:%s' % (clone_lun['Vserver'],
clone_lun['Path']),
new_name,
clone_lun['Size'],
clone_lun))
def _get_fc_target_wwpns(self, include_partner=True):
return self.zapi_client.get_fc_target_wwpns()
@ -879,8 +866,6 @@ class NetAppBlockStorageCmodeLibrary(block_base.NetAppBlockStorageLibrary,
LOG.info("Cloning LUN %s from snapshot %s in volume %s.", lun_name,
snapshot_name, flexvol_name)
metadata = snapshot_lun.metadata
block_count = self._get_lun_block_count(snapshot_path)
if block_count == 0:
msg = _("%s cannot be reverted using clone operation"
@ -889,12 +874,9 @@ class NetAppBlockStorageCmodeLibrary(block_base.NetAppBlockStorageLibrary,
new_snap_name = "new-%s" % snapshot_name
self.zapi_client.create_lun(
flexvol_name, new_snap_name,
six.text_type(snapshot_lun.size), metadata)
try:
self._clone_lun(snapshot_name, new_snap_name,
block_count=block_count)
space_reserved='false', is_snapshot=True)
return new_snap_name
except Exception:
with excutils.save_and_reraise_exception():

View File

@ -644,6 +644,13 @@ class SSHUtil(object):
# REST API error codes.
REST_UNAUTHORIZED = '6'
REST_API_NOT_FOUND = '3'
REST_UPDATE_SNAPMIRROR_FAILED = '13303844'
REST_ERELATION_EXISTS = '6619637'
REST_SNAPMIRROR_IN_PROGRESS = '13303810'
REST_UPDATE_SNAPMIRROR_FAILED = '13303844'
REST_NO_SUCH_LUN_MAP = '5374922'
REST_NO_SUCH_FILE = '6684674'
class RestNaServer(object):

View File

@ -37,6 +37,28 @@ DEFAULT_MAX_PAGE_LENGTH = 50
ONTAP_SELECT_MODEL = 'FDvM300'
ONTAP_C190 = 'C190'
# NOTE(cknight): The keys in this map are tuples that contain arguments needed
# for efficient use of the system-user-capability-get-iter cDOT API. The
# values are SSC extra specs associated with the APIs listed in the keys.
SSC_API_MAP = {
('storage.aggregate', 'show', 'aggr-options-list-info'): [
'netapp_raid_type',
],
('storage.disk', 'show', 'storage-disk-get-iter'): [
'netapp_disk_type',
],
('snapmirror', 'show', 'snapmirror-get-iter'): [
'netapp_mirrored',
],
('volume.efficiency', 'show', 'sis-get-iter'): [
'netapp_dedup',
'netapp_compression',
],
('volume', '*show', 'volume-get-iter'): [
'netapp_flexvol_encryption',
],
}
@six.add_metaclass(volume_utils.TraceWrapperMetaclass)
class Client(client_base.Client):
@ -182,6 +204,32 @@ class Client(client_base.Client):
result.get_child_by_name('next-tag').set_content('')
return result
def check_api_permissions(self):
"""Check which APIs that support SSC functionality are available."""
inaccessible_apis = []
invalid_extra_specs = []
for api_tuple, extra_specs in SSC_API_MAP.items():
object_name, operation_name, api = api_tuple
if not self.check_cluster_api(object_name,
operation_name,
api):
inaccessible_apis.append(api)
invalid_extra_specs.extend(extra_specs)
if inaccessible_apis:
if 'volume-get-iter' in inaccessible_apis:
msg = _('User not permitted to query Data ONTAP volumes.')
raise exception.VolumeBackendAPIException(data=msg)
else:
LOG.warning('The configured user account does not have '
'sufficient privileges to use all needed '
'APIs. The following extra specs will fail '
'or be ignored: %s.', invalid_extra_specs)
return invalid_extra_specs
def _get_cluster_nodes_info(self):
"""Return a list of models of the nodes in the cluster"""
api_args = {
@ -481,7 +529,25 @@ class Client(client_base.Client):
tag = result.get_child_content('next-tag')
if tag is None:
break
return luns
lun_list = [self._create_lun_meta(lun) for lun in luns]
return lun_list
def _create_lun_meta(self, lun):
"""Creates LUN metadata dictionary."""
self.check_is_naelement(lun)
meta_dict = {}
meta_dict['Vserver'] = lun.get_child_content('vserver')
meta_dict['Volume'] = lun.get_child_content('volume')
meta_dict['Size'] = lun.get_child_content('size')
meta_dict['Qtree'] = lun.get_child_content('qtree')
meta_dict['Path'] = lun.get_child_content('path')
meta_dict['OsType'] = lun.get_child_content('multiprotocol-type')
meta_dict['SpaceReserved'] = \
lun.get_child_content('is-space-reservation-enabled')
meta_dict['UUID'] = lun.get_child_content('uuid')
meta_dict['BlockSize'] = lun.get_child_content('block-size')
return meta_dict
def get_lun_map(self, path):
"""Gets the LUN map by LUN path."""
@ -853,7 +919,10 @@ class Client(client_base.Client):
attr_list = luns.get_child_by_name('attributes-list')
if not attr_list:
return []
return attr_list.get_children()
lun_list = [self._create_lun_meta(lun)
for lun in attr_list.get_children()]
return lun_list
def file_assign_qos(self, flex_vol, qos_policy_group_name,
qos_policy_group_is_adaptive, file_path):
@ -1061,7 +1130,8 @@ class Client(client_base.Client):
num_records = result.get_child_content('num-records')
if num_records and int(num_records) >= 1:
attr_list = result.get_child_by_name('attributes-list')
return attr_list.get_children()
return [{'vserver': attr.get_child_content('vserver')}
for attr in attr_list.get_children()]
raise exception.NotFound(
_('No interface found on cluster for ip %s') % ip)

View File

@ -314,7 +314,7 @@ class NetAppCmodeNfsDriver(nfs_base.NetAppNfsDriver,
"""Gets the vserver and export volume for share."""
(host_ip, export_path) = self._get_export_ip_path(volume_id, share)
ifs = self.zapi_client.get_if_info_by_ip(host_ip)
vserver = ifs[0].get_child_content('vserver')
vserver = ifs[0].get('vserver')
exp_volume = self.zapi_client.get_vol_by_junc_vserver(vserver,
export_path)
return vserver, exp_volume
@ -512,7 +512,7 @@ class NetAppCmodeNfsDriver(nfs_base.NetAppNfsDriver,
"""Get vserver for the mentioned ip."""
try:
ifs = self.zapi_client.get_if_info_by_ip(ip)
vserver = ifs[0].get_child_content('vserver')
vserver = ifs[0].get('vserver')
return vserver
except Exception:
return None

View File

@ -22,34 +22,9 @@ import re
from oslo_log import log as logging
import six
from cinder import exception
from cinder.i18n import _
LOG = logging.getLogger(__name__)
# NOTE(cknight): The keys in this map are tuples that contain arguments needed
# for efficient use of the system-user-capability-get-iter cDOT API. The
# values are SSC extra specs associated with the APIs listed in the keys.
SSC_API_MAP = {
('storage.aggregate', 'show', 'aggr-options-list-info'): [
'netapp_raid_type',
],
('storage.disk', 'show', 'storage-disk-get-iter'): [
'netapp_disk_type',
],
('snapmirror', 'show', 'snapmirror-get-iter'): [
'netapp_mirrored',
],
('volume.efficiency', 'show', 'sis-get-iter'): [
'netapp_dedup',
'netapp_compression',
],
('volume', '*show', 'volume-get-iter'): [
'netapp_flexvol_encryption',
],
}
class CapabilitiesLibrary(object):
@ -64,30 +39,7 @@ class CapabilitiesLibrary(object):
self.invalid_extra_specs = []
def check_api_permissions(self):
"""Check which APIs that support SSC functionality are available."""
inaccessible_apis = []
invalid_extra_specs = []
for api_tuple, extra_specs in SSC_API_MAP.items():
object_name, operation_name, api = api_tuple
if not self.zapi_client.check_cluster_api(object_name,
operation_name,
api):
inaccessible_apis.append(api)
invalid_extra_specs.extend(extra_specs)
if inaccessible_apis:
if 'volume-get-iter' in inaccessible_apis:
msg = _('User not permitted to query Data ONTAP volumes.')
raise exception.VolumeBackendAPIException(data=msg)
else:
LOG.warning('The configured user account does not have '
'sufficient privileges to use all needed '
'APIs. The following extra specs will fail '
'or be ignored: %s.', invalid_extra_specs)
self.invalid_extra_specs = invalid_extra_specs
self.invalid_extra_specs = self.zapi_client.check_api_permissions()
def cluster_user_supported(self):
return not self.invalid_extra_specs