Merge "NetApp: Track SVM and Cluster scoped credentials" into stable/newton
This commit is contained in:
commit
6c2575dc25
|
@ -1301,3 +1301,14 @@ SNAPMIRROR_INITIALIZE_RESULT = etree.XML("""
|
|||
<result-status>succeeded</result-status>
|
||||
</results>
|
||||
""")
|
||||
|
||||
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)
|
||||
|
|
|
@ -1673,6 +1673,18 @@ class NetAppCmodeClientTestCase(test.TestCase):
|
|||
expected = {'compression': False, 'dedupe': False}
|
||||
self.assertEqual(expected, 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({'compression': False, 'dedupe': False}, result)
|
||||
|
||||
def test_is_flexvol_mirrored(self):
|
||||
|
||||
api_response = netapp_api.NaElement(
|
||||
|
@ -1920,6 +1932,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
|
||||
|
@ -1949,6 +2025,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')
|
||||
|
@ -2175,6 +2263,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
|
||||
|
@ -82,12 +83,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(
|
||||
config_utils, 'get_backend_configuration',
|
||||
|
@ -353,6 +358,7 @@ class NetAppBlockStorageCmodeLibraryTestCase(test.TestCase):
|
|||
self.mock_object(self.library, 'get_replication_backend_names',
|
||||
mock.Mock(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 = cmode_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(
|
||||
|
@ -264,12 +266,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():
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ import math
|
|||
import re
|
||||
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import excutils
|
||||
from oslo_utils import units
|
||||
import six
|
||||
|
||||
|
@ -752,6 +753,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."""
|
||||
|
||||
|
@ -990,9 +1023,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('Dedupe 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 {'compression': False, 'dedupe': False}
|
||||
|
||||
if self._get_record_count(result) != 1:
|
||||
|
@ -1230,9 +1268,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:
|
||||
|
@ -1302,9 +1344,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(
|
||||
|
@ -1350,9 +1396,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…
Reference in New Issue