Merge "NetApp: Add aggregate capacity info to scheduler"

This commit is contained in:
Jenkins
2016-07-14 09:58:58 +00:00
committed by Gerrit Code Review
9 changed files with 265 additions and 9 deletions

View File

@@ -652,6 +652,30 @@ AGGR_INFO_SSC = {
'raid-type': AGGR_RAID_TYPE,
}
AGGR_SIZE_TOTAL = 107374182400
AGGR_SIZE_AVAILABLE = 59055800320
AGGR_USED_PERCENT = 45
AGGR_GET_ITER_CAPACITY_RESPONSE = etree.XML("""
<results status="passed">
<attributes-list>
<aggr-attributes>
<aggr-space-attributes>
<percent-used-capacity>%(used)s</percent-used-capacity>
<size-total>%(total_size)s</size-total>
<size-available>%(available_size)s</size-available>
</aggr-space-attributes>
<aggregate-name>%(aggr)s</aggregate-name>
</aggr-attributes>
</attributes-list>
<num-records>1</num-records>
</results>
""" % {
'aggr': VOLUME_AGGREGATE_NAME,
'used': AGGR_USED_PERCENT,
'available_size': AGGR_SIZE_AVAILABLE,
'total_size': AGGR_SIZE_TOTAL,
})
VOLUME_SIZE_TOTAL = 19922944
VOLUME_SIZE_AVAILABLE = 19791872
VOLUME_GET_ITER_CAPACITY_RESPONSE = etree.XML("""

View File

@@ -1725,6 +1725,114 @@ class NetAppCmodeClientTestCase(test.TestCase):
self.assertEqual('unknown', result)
def test_get_aggregate_capacities(self):
aggr1_capacities = {
'percent-used': 50,
'size-available': 100.0,
'size-total': 200.0,
}
aggr2_capacities = {
'percent-used': 75,
'size-available': 125.0,
'size-total': 500.0,
}
mock_get_aggregate_capacity = self.mock_object(
self.client, 'get_aggregate_capacity',
mock.Mock(side_effect=[aggr1_capacities, aggr2_capacities]))
result = self.client.get_aggregate_capacities(['aggr1', 'aggr2'])
expected = {
'aggr1': aggr1_capacities,
'aggr2': aggr2_capacities,
}
self.assertEqual(expected, result)
mock_get_aggregate_capacity.assert_has_calls([
mock.call('aggr1'),
mock.call('aggr2'),
])
def test_get_aggregate_capacities_not_found(self):
mock_get_aggregate_capacity = self.mock_object(
self.client, 'get_aggregate_capacity',
mock.Mock(side_effect=[{}, {}]))
result = self.client.get_aggregate_capacities(['aggr1', 'aggr2'])
expected = {
'aggr1': {},
'aggr2': {},
}
self.assertEqual(expected, result)
mock_get_aggregate_capacity.assert_has_calls([
mock.call('aggr1'),
mock.call('aggr2'),
])
def test_get_aggregate_capacities_not_list(self):
result = self.client.get_aggregate_capacities('aggr1')
self.assertEqual({}, result)
def test_get_aggregate_capacity(self):
api_response = netapp_api.NaElement(
fake_client.AGGR_GET_ITER_CAPACITY_RESPONSE).get_child_by_name(
'attributes-list').get_children()
self.mock_object(self.client,
'_get_aggregates',
mock.Mock(return_value=api_response))
result = self.client.get_aggregate_capacity(
fake_client.VOLUME_AGGREGATE_NAME)
desired_attributes = {
'aggr-attributes': {
'aggr-space-attributes': {
'percent-used-capacity': None,
'size-available': None,
'size-total': None,
},
},
}
self.client._get_aggregates.assert_has_calls([
mock.call(
aggregate_names=[fake_client.VOLUME_AGGREGATE_NAME],
desired_attributes=desired_attributes)])
expected = {
'percent-used': float(fake_client.AGGR_USED_PERCENT),
'size-available': float(fake_client.AGGR_SIZE_AVAILABLE),
'size-total': float(fake_client.AGGR_SIZE_TOTAL),
}
self.assertEqual(expected, result)
def test_get_aggregate_capacity_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.get_aggregate_capacity(
fake_client.VOLUME_AGGREGATE_NAME)
self.assertEqual({}, result)
def test_get_aggregate_capacity_api_error(self):
self.mock_object(self.client,
'send_request',
mock.Mock(side_effect=self._mock_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(

View File

@@ -310,6 +310,9 @@ class NetAppBlockStorageCmodeLibraryTestCase(test.TestCase):
mock_get_ssc = self.mock_object(self.library.ssc_library,
'get_ssc',
mock.Mock(return_value=ssc))
mock_get_aggrs = self.mock_object(self.library.ssc_library,
'get_ssc_aggregates',
mock.Mock(return_value=['aggr1']))
self.library.reserved_percentage = 5
self.library.max_over_subscription_ratio = 10
@@ -323,6 +326,17 @@ class NetAppBlockStorageCmodeLibraryTestCase(test.TestCase):
self.zapi_client, 'get_flexvol_capacity',
mock.Mock(return_value=mock_capacities))
aggr_capacities = {
'aggr1': {
'percent-used': 45,
'size-available': 59055800320.0,
'size-total': 107374182400.0,
},
}
mock_get_aggr_capacities = self.mock_object(
self.zapi_client, 'get_aggregate_capacities',
mock.Mock(return_value=aggr_capacities))
result = self.library._get_pool_stats(filter_function='filter',
goodness_function='goodness')
@@ -335,6 +349,7 @@ class NetAppBlockStorageCmodeLibraryTestCase(test.TestCase):
'total_capacity_gb': 10.0,
'free_capacity_gb': 2.0,
'provisioned_capacity_gb': 8.0,
'aggregate_used_percent': 45,
'utilization': 30.0,
'filter_function': 'filter',
'goodness_function': 'goodness',
@@ -351,6 +366,8 @@ class NetAppBlockStorageCmodeLibraryTestCase(test.TestCase):
self.assertEqual(expected, result)
mock_get_ssc.assert_called_once_with()
mock_get_aggrs.assert_called_once_with()
mock_get_aggr_capacities.assert_called_once_with(['aggr1'])
@ddt.data({}, None)
def test_get_pool_stats_no_ssc_vols(self, ssc):

View File

@@ -83,6 +83,7 @@ class NetAppCmodeNfsDriverTestCase(test.TestCase):
def test_get_pool_stats(self):
self.driver.zapi_client = mock.Mock()
ssc = {
'vola': {
'pool_name': '10.10.10.10:/vola',
@@ -100,6 +101,9 @@ class NetAppCmodeNfsDriverTestCase(test.TestCase):
mock_get_ssc = self.mock_object(self.driver.ssc_library,
'get_ssc',
mock.Mock(return_value=ssc))
mock_get_aggrs = self.mock_object(self.driver.ssc_library,
'get_ssc_aggregates',
mock.Mock(return_value=['aggr1']))
total_capacity_gb = na_utils.round_down(
fake.TOTAL_BYTES // units.Gi, '0.01')
@@ -117,6 +121,17 @@ class NetAppCmodeNfsDriverTestCase(test.TestCase):
'_get_share_capacity_info',
mock.Mock(return_value=capacity))
aggr_capacities = {
'aggr1': {
'percent-used': 45,
'size-available': 59055800320.0,
'size-total': 107374182400.0,
},
}
mock_get_aggr_capacities = self.mock_object(
self.driver.zapi_client, 'get_aggregate_capacities',
mock.Mock(return_value=aggr_capacities))
self.driver.perf_library.get_node_utilization_for_pool = (
mock.Mock(return_value=30.0))
@@ -131,6 +146,7 @@ class NetAppCmodeNfsDriverTestCase(test.TestCase):
'total_capacity_gb': total_capacity_gb,
'free_capacity_gb': free_capacity_gb,
'provisioned_capacity_gb': provisioned_capacity_gb,
'aggregate_used_percent': 45,
'utilization': 30.0,
'filter_function': 'filter',
'goodness_function': 'goodness',
@@ -147,6 +163,8 @@ class NetAppCmodeNfsDriverTestCase(test.TestCase):
self.assertEqual(expected, result)
mock_get_ssc.assert_called_once_with()
mock_get_aggrs.assert_called_once_with()
mock_get_aggr_capacities.assert_called_once_with(['aggr1'])
@ddt.data({}, None)
def test_get_pool_stats_no_ssc_vols(self, ssc):

View File

@@ -104,6 +104,12 @@ class CapabilitiesLibraryTestCase(test.TestCase):
self.assertEqual({}, result)
def test_get_ssc_aggregates(self):
result = self.ssc_library.get_ssc_aggregates()
self.assertEqual(list(fake.SSC_AGGREGATES), result)
def test_update_ssc(self):
mock_get_ssc_flexvol_info = self.mock_object(

View File

@@ -216,8 +216,13 @@ 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)
# Get up-to-date aggregate capacities just once
aggregates = self.ssc_library.get_ssc_aggregates()
aggr_capacities = self.zapi_client.get_aggregate_capacities(aggregates)
for ssc_vol_name, ssc_vol_info in ssc.items():
pool = dict()
@@ -245,6 +250,11 @@ class NetAppBlockStorageCmodeLibrary(block_base.NetAppBlockStorageLibrary):
pool['provisioned_capacity_gb'] = round(
pool['total_capacity_gb'] - pool['free_capacity_gb'], 2)
aggregate_name = ssc_vol_info.get('aggregate')
aggr_capacity = aggr_capacities.get(aggregate_name, {})
pool['aggregate_used_percent'] = aggr_capacity.get(
'percent-used', 0)
# Add utilization data
utilization = self.perf_library.get_node_utilization_for_pool(
ssc_vol_name)

View File

@@ -23,7 +23,7 @@ from oslo_log import log as logging
import six
from cinder import exception
from cinder.i18n import _, _LW
from cinder.i18n import _, _LW, _LE
from cinder import utils
from cinder.volume.drivers.netapp.dataontap.client import api as netapp_api
from cinder.volume.drivers.netapp.dataontap.client import client_base
@@ -951,8 +951,8 @@ class Client(client_base.Client):
try:
result = self.send_iter_request('sis-get-iter', api_args)
except netapp_api.NaApiError:
msg = _('Failed to get dedupe info for volume %s.')
LOG.exception(msg % flexvol_name)
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:
@@ -993,8 +993,8 @@ class Client(client_base.Client):
try:
result = self.send_iter_request('snapmirror-get-iter', api_args)
except netapp_api.NaApiError:
msg = _('Failed to get SnapMirror info for volume %s.')
LOG.exception(msg % flexvol_name)
msg = _LE('Failed to get SnapMirror info for volume %s.')
LOG.exception(msg, flexvol_name)
return False
if not self._has_records(result):
@@ -1090,8 +1090,8 @@ class Client(client_base.Client):
aggrs = self._get_aggregates(aggregate_names=[aggregate_name],
desired_attributes=desired_attributes)
except netapp_api.NaApiError:
msg = _('Failed to get info for aggregate %s.')
LOG.exception(msg % aggregate_name)
msg = _LE('Failed to get info for aggregate %s.')
LOG.exception(msg, aggregate_name)
return {}
if len(aggrs) < 1:
@@ -1136,8 +1136,8 @@ class Client(client_base.Client):
result = self.send_request('storage-disk-get-iter', api_args,
enable_tunneling=False)
except netapp_api.NaApiError:
msg = _('Failed to get disk info for aggregate %s.')
LOG.exception(msg % aggregate_name)
msg = _LE('Failed to get disk info for aggregate %s.')
LOG.exception(msg, aggregate_name)
return 'unknown'
if self._get_record_count(result) != 1:
@@ -1156,6 +1156,60 @@ class Client(client_base.Client):
return 'unknown'
def get_aggregate_capacities(self, aggregate_names):
"""Gets capacity info for multiple aggregates."""
if not isinstance(aggregate_names, list):
return {}
aggregates = {}
for aggregate_name in aggregate_names:
aggregates[aggregate_name] = self.get_aggregate_capacity(
aggregate_name)
return aggregates
def get_aggregate_capacity(self, aggregate_name):
"""Gets capacity info for an aggregate."""
desired_attributes = {
'aggr-attributes': {
'aggr-space-attributes': {
'percent-used-capacity': None,
'size-available': None,
'size-total': None,
},
},
}
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)
return {}
if len(aggrs) < 1:
return {}
aggr_attributes = aggrs[0]
aggr_space_attributes = aggr_attributes.get_child_by_name(
'aggr-space-attributes') or netapp_api.NaElement('none')
percent_used = int(aggr_space_attributes.get_child_content(
'percent-used-capacity'))
size_available = float(aggr_space_attributes.get_child_content(
'size-available'))
size_total = float(
aggr_space_attributes.get_child_content('size-total'))
return {
'percent-used': percent_used,
'size-available': size_available,
'size-total': size_total,
}
def get_performance_instance_uuids(self, object_name, node_name):
"""Get UUIDs of performance instances for a cluster node."""

View File

@@ -185,8 +185,13 @@ class NetAppCmodeNfsDriver(nfs_base.NetAppNfsDriver):
if not ssc:
return pools
# 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)
for ssc_vol_name, ssc_vol_info in ssc.items():
pool = dict()
@@ -202,6 +207,11 @@ class NetAppCmodeNfsDriver(nfs_base.NetAppNfsDriver):
capacity = self._get_share_capacity_info(nfs_share)
pool.update(capacity)
aggregate_name = ssc_vol_info.get('aggregate')
aggr_capacity = aggr_capacities.get(aggregate_name, {})
pool['aggregate_used_percent'] = aggr_capacity.get(
'percent-used', 0)
# Add utilization data
utilization = self.perf_library.get_node_utilization_for_pool(
ssc_vol_name)

View File

@@ -93,6 +93,15 @@ class CapabilitiesLibrary(object):
return copy.deepcopy(self.ssc.get(flexvol_name, {}))
def get_ssc_aggregates(self):
"""Get a list of aggregates for all SSC flexvols."""
aggregates = set()
for __, flexvol_info in self.ssc.items():
if 'aggregate' in flexvol_info:
aggregates.add(flexvol_info['aggregate'])
return list(aggregates)
def update_ssc(self, flexvol_map):
"""Periodically runs to update Storage Service Catalog data.