NetApp cDOT driver should not report untenable pools

The NetApp cDOT driver now explicitly filters root aggregates
from the pools reported to the manila scheduler if the driver
is operating with cluster credentials.

Change-Id: I659edada559e50d2332790025c65fae265a27c3d
Closes-Bug: #1624526
This commit is contained in:
Clinton Knight 2016-09-29 19:39:36 -04:00
parent 16c522bf5e
commit eb2d9640e2
9 changed files with 234 additions and 38 deletions

View File

@ -444,7 +444,51 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
return sorted(ports, key=sort_key, reverse=True)
@na_utils.trace
def list_aggregates(self):
def list_root_aggregates(self):
"""Get names of all aggregates that contain node root volumes."""
desired_attributes = {
'aggr-attributes': {
'aggregate-name': None,
'aggr-raid-attributes': {
'has-local-root': None,
'has-partner-root': None,
},
},
}
aggrs = self._get_aggregates(desired_attributes=desired_attributes)
root_aggregates = []
for aggr in aggrs:
aggr_name = aggr.get_child_content('aggregate-name')
aggr_raid_attrs = aggr.get_child_by_name('aggr-raid-attributes')
local_root = strutils.bool_from_string(
aggr_raid_attrs.get_child_content('has-local-root'))
partner_root = strutils.bool_from_string(
aggr_raid_attrs.get_child_content('has-partner-root'))
if local_root or partner_root:
root_aggregates.append(aggr_name)
return root_aggregates
@na_utils.trace
def list_non_root_aggregates(self):
"""Get names of all aggregates that don't contain node root volumes."""
query = {
'aggr-attributes': {
'aggr-raid-attributes': {
'has-local-root': 'false',
'has-partner-root': 'false',
}
},
}
return self._list_aggregates(query=query)
@na_utils.trace
def _list_aggregates(self, query=None):
"""Get names of all aggregates."""
try:
api_args = {
@ -454,6 +498,8 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
},
},
}
if query:
api_args['query'] = query
result = self.send_iter_request('aggr-get-iter', api_args)
aggr_list = result.get_child_by_name(
'attributes-list').get_children()

View File

@ -100,7 +100,7 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
@na_utils.trace
def _find_matching_aggregates(self):
"""Find all aggregates match pattern."""
aggregate_names = self._client.list_aggregates()
aggregate_names = self._client.list_non_root_aggregates()
pattern = self.configuration.netapp_aggregate_name_search_pattern
return [aggr_name for aggr_name in aggregate_names
if re.match(pattern, aggr_name)]

View File

@ -115,9 +115,15 @@ class NetAppCmodeSingleSVMFileStorageLibrary(
"""Find all aggregates match pattern."""
vserver_client = self._get_api_client(vserver=self._vserver)
aggregate_names = vserver_client.list_vserver_aggregates()
root_aggregate_names = []
if self._have_cluster_creds:
root_aggregate_names = self._client.list_root_aggregates()
pattern = self.configuration.netapp_aggregate_name_search_pattern
return [aggr_name for aggr_name in aggregate_names
if re.match(pattern, aggr_name)]
if re.match(pattern, aggr_name) and
aggr_name not in root_aggregate_names]
@na_utils.trace
def get_network_allocations_number(self):

View File

@ -38,6 +38,7 @@ VSERVER_NAME_2 = 'fake_vserver_2'
ADMIN_VSERVER_NAME = 'fake_admin_vserver'
NODE_VSERVER_NAME = 'fake_node_vserver'
NFS_VERSIONS = ['nfs3', 'nfs4.0']
ROOT_AGGREGATE_NAMES = ('root_aggr1', 'root_aggr2')
ROOT_VOLUME_AGGREGATE_NAME = 'fake_root_aggr'
ROOT_VOLUME_NAME = 'fake_root_volume'
SHARE_AGGREGATE_NAME = 'fake_aggr1'
@ -791,34 +792,21 @@ AGGR_GET_NAMES_RESPONSE = etree.XML("""
<attributes-list>
<aggr-attributes>
<aggr-raid-attributes>
<plexes>
<plex-attributes>
<plex-name>/%(aggr1)s/plex0</plex-name>
<raidgroups>
<raidgroup-attributes>
<raidgroup-name>/%(aggr1)s/plex0/rg0</raidgroup-name>
</raidgroup-attributes>
</raidgroups>
</plex-attributes>
</plexes>
</aggr-raid-attributes>
<aggregate-name>%(root1)s</aggregate-name>
</aggr-attributes>
<aggr-attributes>
<aggr-raid-attributes>
</aggr-raid-attributes>
<aggregate-name>%(root2)s</aggregate-name>
</aggr-attributes>
<aggr-attributes>
<aggr-raid-attributes>
</aggr-raid-attributes>
<aggregate-name>%(aggr1)s</aggregate-name>
</aggr-attributes>
<aggr-attributes>
<aggr-raid-attributes>
<plexes>
<plex-attributes>
<plex-name>/%(aggr2)s/plex0</plex-name>
<raidgroups>
<raidgroup-attributes>
<raidgroup-name>/%(aggr2)s/plex0/rg0</raidgroup-name>
</raidgroup-attributes>
<raidgroup-attributes>
<raidgroup-name>/%(aggr2)s/plex0/rg1</raidgroup-name>
</raidgroup-attributes>
</raidgroups>
</plex-attributes>
</plexes>
</aggr-raid-attributes>
<aggregate-name>%(aggr2)s</aggregate-name>
</aggr-attributes>
@ -826,6 +814,8 @@ AGGR_GET_NAMES_RESPONSE = etree.XML("""
<num-records>2</num-records>
</results>
""" % {
'root1': ROOT_AGGREGATE_NAMES[0],
'root2': ROOT_AGGREGATE_NAMES[1],
'aggr1': SHARE_AGGREGATE_NAMES[0],
'aggr2': SHARE_AGGREGATE_NAMES[1],
})
@ -1268,6 +1258,72 @@ AGGR_GET_ITER_SSC_RESPONSE = etree.XML("""
</results>
""" % {'aggr1': SHARE_AGGREGATE_NAMES[0]})
AGGR_GET_ITER_ROOT_AGGR_RESPONSE = etree.XML("""
<results status="passed">
<attributes-list>
<aggr-attributes>
<aggr-raid-attributes>
<has-local-root>true</has-local-root>
<has-partner-root>false</has-partner-root>
</aggr-raid-attributes>
<aggregate-name>%(root1)s</aggregate-name>
</aggr-attributes>
<aggr-attributes>
<aggr-raid-attributes>
<has-local-root>true</has-local-root>
<has-partner-root>false</has-partner-root>
</aggr-raid-attributes>
<aggregate-name>%(root2)s</aggregate-name>
</aggr-attributes>
<aggr-attributes>
<aggr-raid-attributes>
<has-local-root>false</has-local-root>
<has-partner-root>false</has-partner-root>
</aggr-raid-attributes>
<aggregate-name>%(aggr1)s</aggregate-name>
</aggr-attributes>
<aggr-attributes>
<aggr-raid-attributes>
<has-local-root>false</has-local-root>
<has-partner-root>false</has-partner-root>
</aggr-raid-attributes>
<aggregate-name>%(aggr2)s</aggregate-name>
</aggr-attributes>
</attributes-list>
<num-records>6</num-records>
</results>
""" % {
'root1': ROOT_AGGREGATE_NAMES[0],
'root2': ROOT_AGGREGATE_NAMES[1],
'aggr1': SHARE_AGGREGATE_NAMES[0],
'aggr2': SHARE_AGGREGATE_NAMES[1],
})
AGGR_GET_ITER_NON_ROOT_AGGR_RESPONSE = etree.XML("""
<results status="passed">
<attributes-list>
<aggr-attributes>
<aggr-raid-attributes>
<has-local-root>false</has-local-root>
<has-partner-root>false</has-partner-root>
</aggr-raid-attributes>
<aggregate-name>%(aggr1)s</aggregate-name>
</aggr-attributes>
<aggr-attributes>
<aggr-raid-attributes>
<has-local-root>false</has-local-root>
<has-partner-root>false</has-partner-root>
</aggr-raid-attributes>
<aggregate-name>%(aggr2)s</aggregate-name>
</aggr-attributes>
</attributes-list>
<num-records>6</num-records>
</results>
""" % {
'aggr1': SHARE_AGGREGATE_NAMES[0],
'aggr2': SHARE_AGGREGATE_NAMES[1],
})
VOLUME_GET_NAME_RESPONSE = etree.XML("""
<results status="passed">
<attributes-list>

View File

@ -796,16 +796,80 @@ class NetAppClientCmodeTestCase(test.TestCase):
self.assertSequenceEqual(fake.SORTED_PORTS_ALL_SPEEDS, result)
def test_list_root_aggregates(self):
api_response = netapp_api.NaElement(
fake.AGGR_GET_ITER_ROOT_AGGR_RESPONSE)
self.mock_object(self.client,
'send_iter_request',
mock.Mock(return_value=api_response))
result = self.client.list_root_aggregates()
aggr_get_iter_args = {
'desired-attributes': {
'aggr-attributes': {
'aggregate-name': None,
'aggr-raid-attributes': {
'has-local-root': None,
'has-partner-root': None,
},
},
}
}
self.assertSequenceEqual(fake.ROOT_AGGREGATE_NAMES, result)
self.client.send_iter_request.assert_has_calls([
mock.call('aggr-get-iter', aggr_get_iter_args)])
def test_list_non_root_aggregates(self):
api_response = netapp_api.NaElement(
fake.AGGR_GET_ITER_NON_ROOT_AGGR_RESPONSE)
self.mock_object(self.client,
'send_iter_request',
mock.Mock(return_value=api_response))
result = self.client.list_non_root_aggregates()
aggr_get_iter_args = {
'query': {
'aggr-attributes': {
'aggr-raid-attributes': {
'has-local-root': 'false',
'has-partner-root': 'false',
}
},
},
'desired-attributes': {
'aggr-attributes': {
'aggregate-name': None,
},
},
}
self.assertSequenceEqual(fake.SHARE_AGGREGATE_NAMES, result)
self.client.send_iter_request.assert_has_calls([
mock.call('aggr-get-iter', aggr_get_iter_args)])
def test_list_aggregates(self):
api_response = netapp_api.NaElement(fake.AGGR_GET_NAMES_RESPONSE)
self.mock_object(self.client,
'send_request',
'send_iter_request',
mock.Mock(return_value=api_response))
result = self.client.list_aggregates()
result = self.client._list_aggregates()
self.assertSequenceEqual(fake.SHARE_AGGREGATE_NAMES, result)
aggr_get_iter_args = {
'desired-attributes': {
'aggr-attributes': {
'aggregate-name': None,
},
},
}
self.assertSequenceEqual(
fake.ROOT_AGGREGATE_NAMES + fake.SHARE_AGGREGATE_NAMES, result)
self.client.send_iter_request.assert_has_calls([
mock.call('aggr-get-iter', aggr_get_iter_args)])
def test_list_aggregates_not_found(self):
@ -815,7 +879,7 @@ class NetAppClientCmodeTestCase(test.TestCase):
mock.Mock(return_value=api_response))
self.assertRaises(exception.NetAppException,
self.client.list_aggregates)
self.client._list_aggregates)
def test_list_vserver_aggregates(self):

View File

@ -186,14 +186,16 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
def test_find_matching_aggregates(self):
self.mock_object(self.client,
'list_aggregates',
mock.Mock(return_value=fake.AGGREGATES))
mock_list_non_root_aggregates = self.mock_object(
self.client, 'list_non_root_aggregates',
mock.Mock(return_value=fake.AGGREGATES))
self.library.configuration.netapp_aggregate_name_search_pattern = (
'.*_aggr_1')
result = self.library._find_matching_aggregates()
self.assertListEqual([fake.AGGREGATES[0]], result)
mock_list_non_root_aggregates.assert_called_once_with()
def test_setup_server(self):

View File

@ -15,6 +15,7 @@
Unit tests for the NetApp Data ONTAP cDOT single-SVM storage driver library.
"""
import ddt
import mock
from oslo_log import log
@ -26,6 +27,7 @@ from manila import test
import manila.tests.share.drivers.netapp.dataontap.fakes as fake
@ddt.ddt
class NetAppFileStorageLibraryTestCase(test.TestCase):
def setUp(self):
@ -164,19 +166,32 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.assertTrue(mock_vserver_client.prune_deleted_snapshots.called)
self.assertTrue(mock_super.called)
def test_find_matching_aggregates(self):
@ddt.data(True, False)
def test_find_matching_aggregates(self, have_cluster_creds):
self.library._have_cluster_creds = have_cluster_creds
aggregates = fake.AGGREGATES + fake.ROOT_AGGREGATES
mock_vserver_client = mock.Mock()
mock_vserver_client.list_vserver_aggregates.return_value = (
fake.AGGREGATES)
mock_vserver_client.list_vserver_aggregates.return_value = aggregates
self.mock_object(self.library,
'_get_api_client',
mock.Mock(return_value=mock_vserver_client))
mock_client = mock.Mock()
mock_client.list_root_aggregates.return_value = fake.ROOT_AGGREGATES
self.library._client = mock_client
self.library.configuration.netapp_aggregate_name_search_pattern = (
'.*_aggr_1')
result = self.library._find_matching_aggregates()
self.assertListEqual([fake.AGGREGATES[0]], result)
if have_cluster_creds:
self.assertListEqual([fake.AGGREGATES[0]], result)
mock_client.list_root_aggregates.assert_called_once_with()
else:
self.assertListEqual([fake.AGGREGATES[0], fake.ROOT_AGGREGATES[0]],
result)
self.assertFalse(mock_client.list_root_aggregates.called)
def test_get_network_allocations_number(self):
self.assertEqual(0, self.library.get_network_allocations_number())

View File

@ -54,6 +54,7 @@ FREE_CAPACITY = 10000000000
TOTAL_CAPACITY = 20000000000
AGGREGATE = 'manila_aggr_1'
AGGREGATES = ('manila_aggr_1', 'manila_aggr_2')
ROOT_AGGREGATES = ('root_aggr_1', 'root_aggr_2')
ROOT_VOLUME_AGGREGATE = 'manila1'
ROOT_VOLUME = 'root'
CLUSTER_NODE = 'cluster1_01'

View File

@ -0,0 +1,6 @@
---
fixes:
- The NetApp cDOT driver now explicitly filters root aggregates
from the pools reported to the manila scheduler if the driver
is operating with cluster credentials.