Merge "NetApp: Track SVM and Cluster scoped credentials"
This commit is contained in:
commit
474312d9fb
@ -1400,3 +1400,14 @@ VSERVER_DATA_LIST_RESPONSE = etree.XML("""
|
||||
<num-records>1</num-records>
|
||||
</results>
|
||||
""" % {'vserver': VSERVER_NAME})
|
||||
|
||||
SYSTEM_NODE_GET_ITER_RESPONSE = etree.XML("""
|
||||
<results status="passed">
|
||||
<attributes-list>
|
||||
<node-details-info>
|
||||
<node>%s</node>
|
||||
</node-details-info>
|
||||
</attributes-list>
|
||||
<num-records>1</num-records>
|
||||
</results>
|
||||
""" % NODE_NAME)
|
||||
|
@ -1814,6 +1814,19 @@ class NetAppCmodeClientTestCase(test.TestCase):
|
||||
self.assertEqual(fake_client.VOLUME_DEDUPE_INFO_SSC_NO_LOGICAL_DATA,
|
||||
result)
|
||||
|
||||
def test_get_flexvol_dedupe_info_api_insufficient_privileges(self):
|
||||
|
||||
api_error = netapp_api.NaApiError(code=netapp_api.EAPIPRIVILEGE)
|
||||
self.mock_object(self.client,
|
||||
'send_iter_request',
|
||||
side_effect=api_error)
|
||||
|
||||
result = self.client.get_flexvol_dedupe_info(
|
||||
fake_client.VOLUME_NAMES[0])
|
||||
|
||||
self.assertEqual(fake_client.VOLUME_DEDUPE_INFO_SSC_NO_LOGICAL_DATA,
|
||||
result)
|
||||
|
||||
def test_get_flexvol_dedupe_used_percent(self):
|
||||
|
||||
self.client.features.add_feature('CLONE_SPLIT_STATUS')
|
||||
@ -2199,6 +2212,70 @@ class NetAppCmodeClientTestCase(test.TestCase):
|
||||
|
||||
self.assertEqual({}, result)
|
||||
|
||||
def test_get_aggregate_api_not_found(self):
|
||||
|
||||
api_error = netapp_api.NaApiError(code=netapp_api.EAPINOTFOUND)
|
||||
self.mock_object(self.client,
|
||||
'send_iter_request',
|
||||
side_effect=api_error)
|
||||
|
||||
result = self.client.get_aggregate(fake_client.VOLUME_AGGREGATE_NAME)
|
||||
|
||||
self.assertEqual({}, result)
|
||||
|
||||
def test_list_cluster_nodes(self):
|
||||
|
||||
api_response = netapp_api.NaElement(
|
||||
fake_client.SYSTEM_NODE_GET_ITER_RESPONSE)
|
||||
self.mock_object(self.client,
|
||||
'send_request',
|
||||
mock.Mock(return_value=api_response))
|
||||
|
||||
result = self.client.list_cluster_nodes()
|
||||
|
||||
self.assertListEqual([fake_client.NODE_NAME], result)
|
||||
|
||||
def test_list_cluster_nodes_not_found(self):
|
||||
|
||||
api_response = netapp_api.NaElement(fake_client.NO_RECORDS_RESPONSE)
|
||||
self.mock_object(self.client,
|
||||
'send_request',
|
||||
mock.Mock(return_value=api_response))
|
||||
|
||||
result = self.client.list_cluster_nodes()
|
||||
|
||||
self.assertListEqual([], result)
|
||||
|
||||
def test_check_for_cluster_credentials(self):
|
||||
|
||||
self.mock_object(self.client,
|
||||
'list_cluster_nodes',
|
||||
mock.Mock(return_value=fake_client.NODE_NAMES))
|
||||
|
||||
result = self.client.check_for_cluster_credentials()
|
||||
|
||||
self.assertTrue(result)
|
||||
|
||||
def test_check_for_cluster_credentials_not_found(self):
|
||||
|
||||
api_error = netapp_api.NaApiError(code=netapp_api.EAPINOTFOUND)
|
||||
self.mock_object(self.client,
|
||||
'list_cluster_nodes',
|
||||
side_effect=api_error)
|
||||
|
||||
result = self.client.check_for_cluster_credentials()
|
||||
|
||||
self.assertFalse(result)
|
||||
|
||||
def test_check_for_cluster_credentials_api_error(self):
|
||||
|
||||
self.mock_object(self.client,
|
||||
'list_cluster_nodes',
|
||||
self._mock_api_error())
|
||||
|
||||
self.assertRaises(netapp_api.NaApiError,
|
||||
self.client.check_for_cluster_credentials)
|
||||
|
||||
@ddt.data({'types': {'FCAL'}, 'expected': ['FCAL']},
|
||||
{'types': {'SATA', 'SSD'}, 'expected': ['SATA', 'SSD']},)
|
||||
@ddt.unpack
|
||||
@ -2226,6 +2303,18 @@ class NetAppCmodeClientTestCase(test.TestCase):
|
||||
mock_get_aggregate_disk_types.assert_called_once_with(
|
||||
fake_client.VOLUME_AGGREGATE_NAME)
|
||||
|
||||
def test_get_aggregate_disk_types_api_not_found(self):
|
||||
|
||||
api_error = netapp_api.NaApiError(code=netapp_api.EAPINOTFOUND)
|
||||
self.mock_object(self.client,
|
||||
'send_iter_request',
|
||||
side_effect=api_error)
|
||||
|
||||
result = self.client.get_aggregate_disk_types(
|
||||
fake_client.VOLUME_AGGREGATE_NAME)
|
||||
|
||||
self.assertIsNone(result)
|
||||
|
||||
def test_get_aggregate_disk_types_shared(self):
|
||||
|
||||
self.client.features.add_feature('ADVANCED_DISK_PARTITIONING')
|
||||
@ -2451,6 +2540,16 @@ class NetAppCmodeClientTestCase(test.TestCase):
|
||||
|
||||
self.assertEqual({}, result)
|
||||
|
||||
def test_get_aggregate_capacity_api_not_found(self):
|
||||
|
||||
api_error = netapp_api.NaApiError(code=netapp_api.EAPINOTFOUND)
|
||||
self.mock_object(self.client, 'send_request', side_effect=api_error)
|
||||
|
||||
result = self.client.get_aggregate_capacity(
|
||||
fake_client.VOLUME_AGGREGATE_NAME)
|
||||
|
||||
self.assertEqual({}, result)
|
||||
|
||||
def test_get_performance_instance_uuids(self):
|
||||
|
||||
self.mock_send_request.return_value = netapp_api.NaElement(
|
||||
|
@ -31,6 +31,7 @@ from cinder.volume.drivers.netapp.dataontap import block_base
|
||||
from cinder.volume.drivers.netapp.dataontap import block_cmode
|
||||
from cinder.volume.drivers.netapp.dataontap.client import api as netapp_api
|
||||
from cinder.volume.drivers.netapp.dataontap.client import client_base
|
||||
from cinder.volume.drivers.netapp.dataontap.client import client_cmode
|
||||
from cinder.volume.drivers.netapp.dataontap.performance import perf_cmode
|
||||
from cinder.volume.drivers.netapp.dataontap.utils import data_motion
|
||||
from cinder.volume.drivers.netapp.dataontap.utils import loopingcalls
|
||||
@ -79,12 +80,16 @@ class NetAppBlockStorageCmodeLibraryTestCase(test.TestCase):
|
||||
config.netapp_vserver = 'openstack'
|
||||
return config
|
||||
|
||||
@mock.patch.object(client_cmode.Client, 'check_for_cluster_credentials',
|
||||
mock.MagicMock(return_value=False))
|
||||
@mock.patch.object(perf_cmode, 'PerformanceCmodeLibrary', mock.Mock())
|
||||
@mock.patch.object(client_base.Client, 'get_ontapi_version',
|
||||
mock.MagicMock(return_value=(1, 20)))
|
||||
@mock.patch.object(na_utils, 'check_flags')
|
||||
@mock.patch.object(block_base.NetAppBlockStorageLibrary, 'do_setup')
|
||||
def test_do_setup(self, super_do_setup, mock_check_flags):
|
||||
self.zapi_client.check_for_cluster_credentials = mock.MagicMock(
|
||||
return_value=True)
|
||||
self.mock_object(client_base.Client, '_init_ssh_client')
|
||||
self.mock_object(
|
||||
dot_utils, 'get_backend_configuration',
|
||||
@ -365,6 +370,7 @@ class NetAppBlockStorageCmodeLibraryTestCase(test.TestCase):
|
||||
self.mock_object(self.library, 'get_replication_backend_names',
|
||||
return_value=replication_backends)
|
||||
|
||||
self.library.using_cluster_credentials = True
|
||||
self.library.reserved_percentage = 5
|
||||
self.library.max_over_subscription_ratio = 10
|
||||
self.library.perf_library.get_node_utilization_for_pool = (
|
||||
|
@ -72,6 +72,8 @@ class NetAppBlockStorageCmodeLibrary(block_base.NetAppBlockStorageLibrary,
|
||||
self.zapi_client = dot_utils.get_client_for_backend(
|
||||
self.failed_over_backend_name or self.backend_name)
|
||||
self.vserver = self.zapi_client.vserver
|
||||
self.using_cluster_credentials = \
|
||||
self.zapi_client.check_for_cluster_credentials()
|
||||
|
||||
# Performance monitoring library
|
||||
self.perf_library = perf_cmode.PerformanceCmodeLibrary(
|
||||
@ -275,12 +277,18 @@ class NetAppBlockStorageCmodeLibrary(block_base.NetAppBlockStorageLibrary,
|
||||
if not ssc:
|
||||
return pools
|
||||
|
||||
# Get up-to-date node utilization metrics just once
|
||||
self.perf_library.update_performance_cache(ssc)
|
||||
# Utilization and performance metrics require cluster-scoped
|
||||
# credentials
|
||||
if self.using_cluster_credentials:
|
||||
# Get up-to-date node utilization metrics just once
|
||||
self.perf_library.update_performance_cache(ssc)
|
||||
|
||||
# Get up-to-date aggregate capacities just once
|
||||
aggregates = self.ssc_library.get_ssc_aggregates()
|
||||
aggr_capacities = self.zapi_client.get_aggregate_capacities(aggregates)
|
||||
# Get up-to-date aggregate capacities just once
|
||||
aggregates = self.ssc_library.get_ssc_aggregates()
|
||||
aggr_capacities = self.zapi_client.get_aggregate_capacities(
|
||||
aggregates)
|
||||
else:
|
||||
aggr_capacities = {}
|
||||
|
||||
for ssc_vol_name, ssc_vol_info in ssc.items():
|
||||
|
||||
@ -310,8 +318,11 @@ class NetAppBlockStorageCmodeLibrary(block_base.NetAppBlockStorageLibrary,
|
||||
pool['provisioned_capacity_gb'] = round(
|
||||
pool['total_capacity_gb'] - pool['free_capacity_gb'], 2)
|
||||
|
||||
dedupe_used = self.zapi_client.get_flexvol_dedupe_used_percent(
|
||||
ssc_vol_name)
|
||||
if self.using_cluster_credentials:
|
||||
dedupe_used = self.zapi_client.get_flexvol_dedupe_used_percent(
|
||||
ssc_vol_name)
|
||||
else:
|
||||
dedupe_used = 0.0
|
||||
pool['netapp_dedupe_used_percent'] = na_utils.round_down(
|
||||
dedupe_used)
|
||||
|
||||
|
@ -21,6 +21,7 @@ import math
|
||||
import re
|
||||
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import excutils
|
||||
from oslo_utils import units
|
||||
import six
|
||||
|
||||
@ -819,6 +820,38 @@ class Client(client_base.Client):
|
||||
|
||||
return True
|
||||
|
||||
def list_cluster_nodes(self):
|
||||
"""Get all available cluster nodes."""
|
||||
|
||||
api_args = {
|
||||
'desired-attributes': {
|
||||
'node-details-info': {
|
||||
'node': None,
|
||||
},
|
||||
},
|
||||
}
|
||||
result = self.send_iter_request('system-node-get-iter', api_args)
|
||||
nodes_info_list = result.get_child_by_name(
|
||||
'attributes-list') or netapp_api.NaElement('none')
|
||||
return [node_info.get_child_content('node') for node_info
|
||||
in nodes_info_list.get_children()]
|
||||
|
||||
def check_for_cluster_credentials(self):
|
||||
"""Checks whether cluster-scoped credentials are being used or not."""
|
||||
|
||||
try:
|
||||
self.list_cluster_nodes()
|
||||
# API succeeded, so definitely a cluster management LIF
|
||||
return True
|
||||
except netapp_api.NaApiError as e:
|
||||
if e.code == netapp_api.EAPINOTFOUND:
|
||||
LOG.debug('Not connected to cluster management LIF.')
|
||||
else:
|
||||
with excutils.save_and_reraise_exception():
|
||||
msg = _LE('Failed to get the list of nodes.')
|
||||
LOG.exception(msg)
|
||||
return False
|
||||
|
||||
def get_operational_lif_addresses(self):
|
||||
"""Gets the IP addresses of operational LIFs on the vserver."""
|
||||
|
||||
@ -1066,9 +1099,14 @@ class Client(client_base.Client):
|
||||
|
||||
try:
|
||||
result = self.send_iter_request('sis-get-iter', api_args)
|
||||
except netapp_api.NaApiError:
|
||||
msg = _LE('Failed to get dedupe info for volume %s.')
|
||||
LOG.exception(msg, flexvol_name)
|
||||
except netapp_api.NaApiError as e:
|
||||
if e.code == netapp_api.EAPIPRIVILEGE:
|
||||
LOG.debug('Dedup info for volume %(name)s will not be '
|
||||
'collected. This API requires cluster-scoped '
|
||||
'credentials.', {'name': flexvol_name})
|
||||
else:
|
||||
msg = _LE('Failed to get dedupe info for volume %s.')
|
||||
LOG.exception(msg, flexvol_name)
|
||||
return no_dedupe_response
|
||||
|
||||
if self._get_record_count(result) != 1:
|
||||
@ -1389,9 +1427,13 @@ class Client(client_base.Client):
|
||||
try:
|
||||
aggrs = self._get_aggregates(aggregate_names=[aggregate_name],
|
||||
desired_attributes=desired_attributes)
|
||||
except netapp_api.NaApiError:
|
||||
msg = _LE('Failed to get info for aggregate %s.')
|
||||
LOG.exception(msg, aggregate_name)
|
||||
except netapp_api.NaApiError as e:
|
||||
if e.code == netapp_api.EAPINOTFOUND:
|
||||
LOG.debug('Aggregate info can only be collected with '
|
||||
'cluster-scoped credentials.')
|
||||
else:
|
||||
msg = _LE('Failed to get info for aggregate %s.')
|
||||
LOG.exception(msg, aggregate_name)
|
||||
return {}
|
||||
|
||||
if len(aggrs) < 1:
|
||||
@ -1461,9 +1503,13 @@ class Client(client_base.Client):
|
||||
try:
|
||||
result = self.send_iter_request(
|
||||
'storage-disk-get-iter', api_args, enable_tunneling=False)
|
||||
except netapp_api.NaApiError:
|
||||
msg = _LE('Failed to get disk info for aggregate %s.')
|
||||
LOG.exception(msg, aggregate_name)
|
||||
except netapp_api.NaApiError as e:
|
||||
if e.code == netapp_api.EAPINOTFOUND:
|
||||
LOG.debug('Disk types can only be collected with '
|
||||
'cluster scoped credentials.')
|
||||
else:
|
||||
msg = _LE('Failed to get disk info for aggregate %s.')
|
||||
LOG.exception(msg, aggregate_name)
|
||||
return disk_types
|
||||
|
||||
attributes_list = result.get_child_by_name(
|
||||
@ -1509,9 +1555,13 @@ class Client(client_base.Client):
|
||||
try:
|
||||
aggrs = self._get_aggregates(aggregate_names=[aggregate_name],
|
||||
desired_attributes=desired_attributes)
|
||||
except netapp_api.NaApiError:
|
||||
msg = _LE('Failed to get info for aggregate %s.')
|
||||
LOG.exception(msg, aggregate_name)
|
||||
except netapp_api.NaApiError as e:
|
||||
if e.code == netapp_api.EAPINOTFOUND:
|
||||
LOG.debug('Aggregate capacity can only be collected with '
|
||||
'cluster scoped credentials.')
|
||||
else:
|
||||
msg = _LE('Failed to get info for aggregate %s.')
|
||||
LOG.exception(msg, aggregate_name)
|
||||
return {}
|
||||
|
||||
if len(aggrs) < 1:
|
||||
|
Loading…
x
Reference in New Issue
Block a user