diff --git a/manila/exception.py b/manila/exception.py index d33cb97b3c..6f57f2e195 100644 --- a/manila/exception.py +++ b/manila/exception.py @@ -149,6 +149,10 @@ class InvalidContentType(Invalid): message = _("Invalid content type %(content_type)s.") +class InvalidHost(Invalid): + message = _("Invalid host: %(reason)s") + + # Cannot be templated as the error syntax varies. # msg needs to be constructed when raised. class InvalidParameterValue(Invalid): diff --git a/manila/share/drivers/netapp/dataontap/client/client_cmode.py b/manila/share/drivers/netapp/dataontap/client/client_cmode.py index 8ecb9c4e32..2c720388ac 100644 --- a/manila/share/drivers/netapp/dataontap/client/client_cmode.py +++ b/manila/share/drivers/netapp/dataontap/client/client_cmode.py @@ -127,8 +127,8 @@ class NetAppCmodeClient(client_base.NetAppBaseClient): try: root_volume_name = vserver_info.get_child_by_name( - 'attributes-list').get_child_by_name('vserver-info')\ - .get_child_content('root-volume') + 'attributes-list').get_child_by_name( + 'vserver-info').get_child_content('root-volume') except AttributeError: msg = _('Could not determine root volume name ' 'for Vserver %s.') % vserver_name @@ -264,9 +264,9 @@ class NetAppCmodeClient(client_base.NetAppBaseClient): } port_info = self.send_request('net-port-get-iter', api_args) try: - port = port_info.get_child_by_name('attributes-list')\ - .get_child_by_name('net-port-info')\ - .get_child_content('port') + port = port_info.get_child_by_name( + 'attributes-list').get_child_by_name( + 'net-port-info').get_child_content('port') except AttributeError: msg = _("Data port does not exist for node %s.") raise exception.NetAppException(msg % node) @@ -414,35 +414,48 @@ class NetAppCmodeClient(client_base.NetAppBaseClient): self.send_request('net-interface-delete', api_args) @na_utils.trace - def calculate_aggregate_capacity(self, aggregate_names): - """Calculates capacity of one or more aggregates + def get_cluster_aggregate_capacities(self, aggregate_names): + """Calculates capacity of one or more aggregates. - Returns tuple (total, free) in bytes. + Returns dictionary of aggregate capacity metrics. + 'size-used' is the actual space consumed on the aggregate. + 'size-available' is the actual space remaining. + 'size-total' is the defined total aggregate size, such that + used + available = total. """ desired_attributes = { 'aggr-attributes': { 'aggregate-name': None, 'aggr-space-attributes': { - 'size-total': None, 'size-available': None, + 'size-total': None, + 'size-used': None, }, }, } aggrs = self._get_aggregates(aggregate_names=aggregate_names, desired_attributes=desired_attributes) - aggr_space_attrs = [aggr.get_child_by_name('aggr-space-attributes') - for aggr in aggrs] - total = sum([int(aggr.get_child_content('size-total')) - for aggr in aggr_space_attrs]) if aggr_space_attrs else 0 - free = max([int(aggr.get_child_content('size-available')) - for aggr in aggr_space_attrs]) if aggr_space_attrs else 0 - return total, free + aggr_space_dict = dict() + for aggr in aggrs: + aggr_name = aggr.get_child_content('aggregate-name') + aggr_space_attrs = aggr.get_child_by_name('aggr-space-attributes') + + aggr_space_dict[aggr_name] = { + 'available': + int(aggr_space_attrs.get_child_content('size-available')), + 'total': + int(aggr_space_attrs.get_child_content('size-total')), + 'used': + int(aggr_space_attrs.get_child_content('size-used')), + } + return aggr_space_dict @na_utils.trace - def get_aggregates_for_vserver(self, vserver_name): - """Returns aggregate list and size info for a Vserver. + def get_vserver_aggregate_capacities(self, vserver_name): + """Calculates capacity of one or more aggregates for a vserver. - Must be called against a Vserver API client. + Returns dictionary of aggregate capacity metrics. This must + be called against a Vserver LIF. """ LOG.debug('Finding available aggregates for Vserver %s', vserver_name) @@ -466,19 +479,19 @@ class NetAppCmodeClient(client_base.NetAppBaseClient): vserver_aggr_info_list = vserver_aggr_info_element.get_children() if not vserver_aggr_info_list: - msg = _("No aggregates assigned to Vserver %s") - raise exception.NetAppException(msg % vserver_name) + LOG.warning(_LW('No aggregates assigned to Vserver %s.'), + vserver_name) # Return dict of key-value pair of aggr_name:aggr_size_available. - aggr_dict = {} + aggr_space_dict = {} for aggr_info in vserver_aggr_info_list: aggr_name = aggr_info.get_child_content('aggr-name') aggr_size = int(aggr_info.get_child_content('aggr-availsize')) - aggr_dict[aggr_name] = aggr_size + aggr_space_dict[aggr_name] = {'available': aggr_size} - LOG.debug('Found available aggregates: %s', aggr_dict) - return aggr_dict + LOG.debug('Found available Vserver aggregates: %s', aggr_space_dict) + return aggr_space_dict def _get_aggregates(self, aggregate_names=None, desired_attributes=None): @@ -701,6 +714,45 @@ class NetAppCmodeClient(client_base.NetAppBaseClient): result = self.send_request('volume-get-iter', api_args) return self._has_records(result) + @na_utils.trace + def get_aggregate_for_volume(self, volume_name): + """Get the name of the aggregate containing a volume.""" + + api_args = { + 'query': { + 'volume-attributes': { + 'volume-id-attributes': { + 'name': volume_name, + }, + }, + }, + 'desired-attributes': { + 'volume-attributes': { + 'volume-id-attributes': { + 'containing-aggregate-name': None, + 'name': None, + }, + }, + }, + } + result = self.send_request('volume-get-iter', api_args) + + attributes_list = result.get_child_by_name( + 'attributes-list') or netapp_api.NaElement('none') + volume_attributes = attributes_list.get_child_by_name( + 'volume-attributes') or netapp_api.NaElement('none') + volume_id_attributes = volume_attributes.get_child_by_name( + 'volume-id-attributes') or netapp_api.NaElement('none') + + aggregate = volume_id_attributes.get_child_content( + 'containing-aggregate-name') + + if not aggregate: + msg = _('Could not find aggregate for volume %s.') + raise exception.NetAppException(msg % volume_name) + + return aggregate + @na_utils.trace def create_volume_clone(self, volume_name, parent_volume_name, parent_snapshot_name=None): diff --git a/manila/share/drivers/netapp/dataontap/cluster_mode/drv_multi_svm.py b/manila/share/drivers/netapp/dataontap/cluster_mode/drv_multi_svm.py index 2c068d32b7..f8f554bef1 100644 --- a/manila/share/drivers/netapp/dataontap/cluster_mode/drv_multi_svm.py +++ b/manila/share/drivers/netapp/dataontap/cluster_mode/drv_multi_svm.py @@ -40,6 +40,9 @@ class NetAppCmodeMultiSvmShareDriver(driver.ShareDriver): def check_for_setup_error(self): self.library.check_for_setup_error() + def get_pool(self, share): + return self.library.get_pool(share) + def create_share(self, context, share, **kwargs): return self.library.create_share(context, share, **kwargs) diff --git a/manila/share/drivers/netapp/dataontap/cluster_mode/lib_base.py b/manila/share/drivers/netapp/dataontap/cluster_mode/lib_base.py index 3f46a7f696..e375b74230 100644 --- a/manila/share/drivers/netapp/dataontap/cluster_mode/lib_base.py +++ b/manila/share/drivers/netapp/dataontap/cluster_mode/lib_base.py @@ -35,6 +35,7 @@ from manila.share.drivers.netapp.dataontap.protocols import cifs_cmode from manila.share.drivers.netapp.dataontap.protocols import nfs_cmode from manila.share.drivers.netapp import options as na_opts from manila.share.drivers.netapp import utils as na_utils +from manila.share import utils as share_utils from manila import utils @@ -73,7 +74,7 @@ class NetAppCmodeFileStorageLibrary(object): AUTOSUPPORT_INTERVAL_SECONDS = 3600 # hourly def __init__(self, db, driver_name, **kwargs): - na_utils.validate_instantiation(**kwargs) + na_utils.validate_driver_instantiation(**kwargs) self.db = db self.driver_name = driver_name @@ -157,17 +158,39 @@ class NetAppCmodeFileStorageLibrary(object): @na_utils.trace def get_share_stats(self): """Retrieve stats info from Cluster Mode backend.""" - total, free = self._client.calculate_aggregate_capacity( + aggr_space = self._client.get_cluster_aggregate_capacities( self._find_matching_aggregates()) - data = dict( - share_backend_name=self.backend_name, - driver_name=self.driver_name, - vendor_name='NetApp', - driver_version='1.0', - storage_protocol='NFS_CIFS', - total_capacity_gb=(total / units.Gi), - free_capacity_gb=(free / units.Gi)) + data = { + 'share_backend_name': self.backend_name, + 'driver_name': self.driver_name, + 'vendor_name': 'NetApp', + 'driver_version': '1.0', + 'storage_protocol': 'NFS_CIFS', + 'total_capacity_gb': 0.0, + 'free_capacity_gb': 0.0, + } + + pools = [] + for aggr_name in sorted(aggr_space.keys()): + + total_capacity_gb = na_utils.round_down( + float(aggr_space[aggr_name]['total']) / units.Gi, '0.01') + free_capacity_gb = na_utils.round_down( + float(aggr_space[aggr_name]['available']) / units.Gi, '0.01') + allocated_capacity_gb = na_utils.round_down( + float(aggr_space[aggr_name]['used']) / units.Gi, '0.01') + + pools.append({ + 'pool_name': aggr_name, + 'total_capacity_gb': total_capacity_gb, + 'free_capacity_gb': free_capacity_gb, + 'allocated_capacity_gb': allocated_capacity_gb, + 'QoS_support': 'False', + 'reserved_percentage': 0, + }) + + data['pools'] = pools self._handle_ems_logging() @@ -317,6 +340,15 @@ class NetAppCmodeFileStorageLibrary(object): ip, netmask, vlan, node, port, vserver_name, allocation_id, self.configuration.netapp_lif_name_template) + @na_utils.trace + def get_pool(self, share): + pool = share_utils.extract_host(share['host'], level='pool') + if pool: + return pool + + volume_name = self._get_valid_share_name(share['id']) + return self._client.get_aggregate_for_volume(volume_name) + @ensure_vserver @na_utils.trace def create_share(self, context, share, share_server): @@ -340,12 +372,17 @@ class NetAppCmodeFileStorageLibrary(object): def _allocate_container(self, share, vserver, vserver_client): """Create new share on aggregate.""" share_name = self._get_valid_share_name(share['id']) - aggregates = vserver_client.get_aggregates_for_vserver(vserver) - aggregate = max(aggregates, key=lambda m: aggregates[m]) + + # Get Data ONTAP aggregate name as pool name. + aggregate_name = share_utils.extract_host(share['host'], level='pool') + + if aggregate_name is None: + msg = _("Pool is not available in the share host field.") + raise exception.InvalidHost(reason=msg) LOG.debug('Creating volume %(share_name)s on aggregate %(aggregate)s', - {'share_name': share_name, 'aggregate': aggregate}) - vserver_client.create_volume(aggregate, share_name, share['size']) + {'share_name': share_name, 'aggregate': aggregate_name}) + vserver_client.create_volume(aggregate_name, share_name, share['size']) @na_utils.trace def _allocate_container_from_snapshot(self, share, snapshot, diff --git a/manila/share/drivers/netapp/utils.py b/manila/share/drivers/netapp/utils.py index 3faf22b09a..8acd3236be 100644 --- a/manila/share/drivers/netapp/utils.py +++ b/manila/share/drivers/netapp/utils.py @@ -15,10 +15,12 @@ # under the License. """Utilities for NetApp drivers.""" +import decimal import platform from oslo_concurrency import processutils as putils from oslo_log import log +import six from manila import exception from manila.i18n import _, _LI, _LW @@ -32,6 +34,36 @@ TRACE_METHOD = False TRACE_API = False +def check_flags(required_flags, configuration): + """Ensure that the flags we care about are set.""" + for flag in required_flags: + if not getattr(configuration, flag, None): + msg = _('Configuration value %s is not set.') % flag + raise exception.InvalidInput(reason=msg) + + +def validate_driver_instantiation(**kwargs): + """Checks if a driver is instantiated other than by the unified driver. + + Helps check direct instantiation of netapp drivers. + Call this function in every netapp block driver constructor. + """ + if kwargs and kwargs.get('netapp_mode') == 'proxy': + return + LOG.warning(_LW('Please use NetAppDriver in the configuration file ' + 'to load the driver instead of directly specifying ' + 'the driver module name.')) + + +def round_down(value, precision): + """Round a number downward using a specified level of precision. + + Example: round_down(float(total_space_in_bytes) / units.Gi, '0.01') + """ + return float(decimal.Decimal(six.text_type(value)).quantize( + decimal.Decimal(precision), rounding=decimal.ROUND_DOWN)) + + def setup_tracing(trace_flags_string): global TRACE_METHOD global TRACE_API @@ -57,27 +89,6 @@ def trace(f): return trace_wrapper -def check_flags(required_flags, configuration): - """Ensure that the flags we care about are set.""" - for flag in required_flags: - if not getattr(configuration, flag, None): - msg = _('Configuration value %s is not set.') % flag - raise exception.InvalidInput(reason=msg) - - -def validate_instantiation(**kwargs): - """Checks if a driver is instantiated other than by the unified driver. - - Helps check direct instantiation of netapp drivers. - Call this function in every netapp block driver constructor. - """ - if kwargs and kwargs.get('netapp_mode') == 'proxy': - return - LOG.warning(_LW('Please use NetAppDriver in the configuration file ' - 'to load the driver instead of directly specifying ' - 'the driver module name.')) - - class OpenStackInfo(object): """OS/distribution, release, and version. diff --git a/manila/tests/share/drivers/netapp/dataontap/client/fakes.py b/manila/tests/share/drivers/netapp/dataontap/client/fakes.py index 19287e25a5..ac11a52f98 100644 --- a/manila/tests/share/drivers/netapp/dataontap/client/fakes.py +++ b/manila/tests/share/drivers/netapp/dataontap/client/fakes.py @@ -123,7 +123,14 @@ VSERVER_DATA_LIST_RESPONSE = etree.XML(""" """ % {'vserver': VSERVER_NAME}) -VSERVER_AGGREGATES = {'aggr0': 45678592, 'manila': 6448431104} +VSERVER_AGGREGATES = { + 'aggr0': { + 'available': 45678592, + }, + 'manila': { + 'available': 6448431104, + }, +} VSERVER_GET_RESPONSE_NO_AGGREGATES = etree.XML(""" @@ -577,8 +584,9 @@ AGGR_GET_SPACE_RESPONSE = etree.XML(""" - 45678592 + 45670400 943718400 + 898048000 aggr0 @@ -599,8 +607,9 @@ AGGR_GET_SPACE_RESPONSE = etree.XML(""" - 6448435200 + 4267659264 7549747200 + 3282087936 manila @@ -965,3 +974,21 @@ NFS_EXPORTFS_LIST_RULES_2_RESPONSE = etree.XML(""" 'host1': NFS_EXPORT_RULES[0], 'host2': NFS_EXPORT_RULES[1], }) + +GET_AGGREGATE_FOR_VOLUME_RESPONSE = etree.XML(""" + + + + + %(aggr)s + %(share)s + os_aa666789-5576-4835-87b7-868069856459 + + + + 1 + +""" % { + 'aggr': SHARE_AGGREGATE_NAME, + 'share': SHARE_NAME +}) diff --git a/manila/tests/share/drivers/netapp/dataontap/client/test_client_cmode.py b/manila/tests/share/drivers/netapp/dataontap/client/test_client_cmode.py index 02365211d6..dffdc6243b 100644 --- a/manila/tests/share/drivers/netapp/dataontap/client/test_client_cmode.py +++ b/manila/tests/share/drivers/netapp/dataontap/client/test_client_cmode.py @@ -81,11 +81,11 @@ class NetAppClientCmodeTestCase(test.TestCase): 'desired-attributes': {'vserver-info': {'vserver-name': None}} } - response = self.client.vserver_exists(fake.VSERVER_NAME) + result = self.client.vserver_exists(fake.VSERVER_NAME) self.client.send_request.assert_has_calls([ mock.call('vserver-get-iter', vserver_get_args)]) - self.assertTrue(response) + self.assertTrue(result) def test_vserver_exists_not_found(self): @@ -137,11 +137,11 @@ class NetAppClientCmodeTestCase(test.TestCase): 'desired-attributes': {'vserver-info': {'root-volume': None}} } - response = self.client.get_vserver_root_volume_name(fake.VSERVER_NAME) + result = self.client.get_vserver_root_volume_name(fake.VSERVER_NAME) self.client.send_request.assert_has_calls([ mock.call('vserver-get-iter', vserver_get_args)]) - self.assertEqual(fake.ROOT_VOLUME_NAME, response) + self.assertEqual(fake.ROOT_VOLUME_NAME, result) def test_get_vserver_root_volume_name_not_found(self): @@ -587,13 +587,13 @@ class NetAppClientCmodeTestCase(test.TestCase): } } } - response = self.client.network_interface_exists( + result = self.client.network_interface_exists( fake.VSERVER_NAME, fake.NODE_NAME, fake.PORT, fake.IP_ADDRESS, fake.NETMASK, fake.VLAN) self.client.send_request.assert_has_calls([ mock.call('net-interface-get-iter', net_interface_get_args)]) - self.assertTrue(response) + self.assertTrue(result) def test_network_interface_exists_not_found(self): @@ -602,11 +602,11 @@ class NetAppClientCmodeTestCase(test.TestCase): 'send_request', mock.Mock(return_value=api_response)) - response = self.client.network_interface_exists( + result = self.client.network_interface_exists( fake.VSERVER_NAME, fake.NODE_NAME, fake.PORT, fake.IP_ADDRESS, fake.NETMASK, fake.VLAN) - self.assertFalse(response) + self.assertFalse(result) def test_list_network_interfaces(self): @@ -669,7 +669,7 @@ class NetAppClientCmodeTestCase(test.TestCase): self.client.send_request.assert_has_calls([ mock.call('net-interface-delete', net_interface_get_args)]) - def test_calculate_aggregate_capacity(self): + def test_get_cluster_aggregate_capacities(self): api_response = netapp_api.NaElement( fake.AGGR_GET_SPACE_RESPONSE).get_child_by_name( @@ -678,14 +678,15 @@ class NetAppClientCmodeTestCase(test.TestCase): '_get_aggregates', mock.Mock(return_value=api_response)) - result = self.client.calculate_aggregate_capacity(fake.AGGR_NAMES) + result = self.client.get_cluster_aggregate_capacities(fake.AGGR_NAMES) desired_attributes = { 'aggr-attributes': { 'aggregate-name': None, 'aggr-space-attributes': { - 'size-total': None, 'size-available': None, + 'size-total': None, + 'size-used': None, } } } @@ -694,27 +695,40 @@ class NetAppClientCmodeTestCase(test.TestCase): mock.call( aggregate_names=fake.AGGR_NAMES, desired_attributes=desired_attributes)]) - self.assertEqual((8493465600, 6448435200), result) - def test_calculate_aggregate_capacity_not_found(self): + expected = { + 'manila': { + 'available': 4267659264, + 'total': 7549747200, + 'used': 3282087936, + }, + 'aggr0': { + 'available': 45670400, + 'total': 943718400, + 'used': 898048000, + } + } + self.assertDictEqual(expected, result) + + def test_get_cluster_aggregate_capacities_not_found(self): api_response = netapp_api.NaElement('none').get_children() self.mock_object(self.client, '_get_aggregates', mock.Mock(return_value=api_response)) - result = self.client.calculate_aggregate_capacity(fake.AGGR_NAMES) + result = self.client.get_cluster_aggregate_capacities(fake.AGGR_NAMES) - self.assertEqual((0, 0), result) + self.assertEqual({}, result) - def test_get_aggregates_for_vserver(self): + def test_get_vserver_aggregate_capacities(self): api_response = netapp_api.NaElement(fake.VSERVER_GET_RESPONSE) self.mock_object(self.vserver_client, 'send_request', mock.Mock(return_value=api_response)) - result = self.vserver_client.get_aggregates_for_vserver( + result = self.vserver_client.get_vserver_aggregate_capacities( fake.VSERVER_NAME) vserver_args = { @@ -734,7 +748,7 @@ class NetAppClientCmodeTestCase(test.TestCase): mock.call('vserver-get', vserver_args)]) self.assertDictEqual(fake.VSERVER_AGGREGATES, result) - def test_get_aggregates_for_vserver_not_found(self): + def test_get_vserver_aggregate_capacities_not_found(self): api_response = netapp_api.NaElement( fake.VSERVER_GET_RESPONSE_NO_AGGREGATES) @@ -742,9 +756,11 @@ class NetAppClientCmodeTestCase(test.TestCase): 'send_request', mock.Mock(return_value=api_response)) - self.assertRaises(exception.NetAppException, - self.vserver_client.get_aggregates_for_vserver, - fake.VSERVER_NAME) + result = self.vserver_client.get_vserver_aggregate_capacities( + fake.VSERVER_NAME) + + self.assertDictEqual({}, result) + self.assertEqual(1, client_cmode.LOG.warning.call_count) def test_get_aggregates(self): @@ -1181,7 +1197,7 @@ class NetAppClientCmodeTestCase(test.TestCase): 'send_request', mock.Mock(return_value=api_response)) - response = self.client.volume_exists(fake.SHARE_NAME) + result = self.client.volume_exists(fake.SHARE_NAME) volume_get_iter_args = { 'query': { @@ -1202,7 +1218,7 @@ class NetAppClientCmodeTestCase(test.TestCase): self.client.send_request.assert_has_calls([ mock.call('volume-get-iter', volume_get_iter_args)]) - self.assertTrue(response) + self.assertTrue(result) def test_volume_exists_not_found(self): @@ -1213,6 +1229,49 @@ class NetAppClientCmodeTestCase(test.TestCase): self.assertFalse(self.client.volume_exists(fake.SHARE_NAME)) + def test_get_aggregate_for_volume(self): + + api_response = netapp_api.NaElement( + fake.GET_AGGREGATE_FOR_VOLUME_RESPONSE) + self.mock_object(self.client, + 'send_request', + mock.Mock(return_value=api_response)) + + result = self.client.get_aggregate_for_volume(fake.SHARE_NAME) + + volume_get_iter_args = { + 'query': { + 'volume-attributes': { + 'volume-id-attributes': { + 'name': fake.SHARE_NAME + } + } + }, + 'desired-attributes': { + 'volume-attributes': { + 'volume-id-attributes': { + 'containing-aggregate-name': None, + 'name': None + } + } + } + } + + self.client.send_request.assert_has_calls([ + mock.call('volume-get-iter', volume_get_iter_args)]) + self.assertEqual(fake.SHARE_AGGREGATE_NAME, result) + + def test_get_aggregate_for_volume_not_found(self): + + api_response = netapp_api.NaElement(fake.NO_RECORDS_RESPONSE) + self.mock_object(self.client, + 'send_request', + mock.Mock(return_value=api_response)) + + self.assertRaises(exception.NetAppException, + self.client.get_aggregate_for_volume, + fake.SHARE_NAME) + def test_create_volume_clone(self): self.mock_object(self.client, 'send_request') diff --git a/manila/tests/share/drivers/netapp/dataontap/cluster_mode/test_lib_base.py b/manila/tests/share/drivers/netapp/dataontap/cluster_mode/test_lib_base.py index 50b711af52..9d0314ec0a 100644 --- a/manila/tests/share/drivers/netapp/dataontap/cluster_mode/test_lib_base.py +++ b/manila/tests/share/drivers/netapp/dataontap/cluster_mode/test_lib_base.py @@ -21,7 +21,6 @@ import socket import mock from oslo_utils import timeutils -from oslo_utils import units from manila import context from manila import exception @@ -99,7 +98,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase): def setUp(self): super(NetAppFileStorageLibraryTestCase, self).setUp() - self.mock_object(na_utils, 'validate_instantiation') + self.mock_object(na_utils, 'validate_driver_instantiation') self.mock_object(na_utils, 'setup_tracing') self.mock_object(lib_base, 'LOG') @@ -126,8 +125,8 @@ class NetAppFileStorageLibraryTestCase(test.TestCase): config.netapp_server_port = fake.CLIENT_KWARGS['port'] config.netapp_vserver = fake.VSERVER1 config.netapp_volume_name_template = fake.VOLUME_NAME_TEMPLATE - config.netapp_aggregate_name_search_pattern = \ - fake.AGGREGATE_NAME_SEARCH_PATTERN + config.netapp_aggregate_name_search_pattern = ( + fake.AGGREGATE_NAME_SEARCH_PATTERN) config.netapp_vserver_name_template = fake.VSERVER_NAME_TEMPLATE config.netapp_root_volume_aggregate = fake.ROOT_VOLUME_AGGREGATE config.netapp_root_volume = fake.ROOT_VOLUME @@ -137,7 +136,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase): def test_init(self): self.assertEqual(fake.DRIVER_NAME, self.library.driver_name) self.assertEqual(self.mock_db, self.library.db) - self.assertEqual(1, na_utils.validate_instantiation.call_count) + self.assertEqual(1, na_utils.validate_driver_instantiation.call_count) self.assertEqual(1, na_utils.setup_tracing.call_count) self.assertIsNone(self.library._helpers) self.assertListEqual([], self.library._licenses) @@ -263,9 +262,8 @@ class NetAppFileStorageLibraryTestCase(test.TestCase): self.mock_object(self.library, '_find_matching_aggregates') self.mock_object(self.client, - 'calculate_aggregate_capacity', - mock.Mock(return_value=(fake.TOTAL_CAPACITY, - fake.FREE_CAPACITY))) + 'get_cluster_aggregate_capacities', + mock.Mock(return_value=fake.AGGREGATE_CAPACITIES)) mock_handle_ems_logging = self.mock_object(self.library, '_handle_ems_logging') @@ -277,10 +275,27 @@ class NetAppFileStorageLibraryTestCase(test.TestCase): 'vendor_name': 'NetApp', 'driver_version': '1.0', 'storage_protocol': 'NFS_CIFS', - 'total_capacity_gb': fake.TOTAL_CAPACITY / units.Gi, - 'free_capacity_gb': fake.FREE_CAPACITY / units.Gi + 'total_capacity_gb': 0.0, + 'free_capacity_gb': 0.0, + 'pools': [ + {'pool_name': 'manila1', + 'total_capacity_gb': 3.3, + 'free_capacity_gb': 1.1, + 'allocated_capacity_gb': 2.2, + 'QoS_support': 'False', + 'reserved_percentage': 0, + }, + {'pool_name': 'manila2', + 'total_capacity_gb': 6.0, + 'free_capacity_gb': 2.0, + 'allocated_capacity_gb': 4.0, + 'QoS_support': 'False', + 'reserved_percentage': 0, + }, + ] } + self.maxDiff = None self.assertDictEqual(expected, result) self.assertTrue(mock_handle_ems_logging.called) @@ -339,13 +354,13 @@ class NetAppFileStorageLibraryTestCase(test.TestCase): 'list_aggregates', mock.Mock(return_value=fake.AGGREGATES)) - self.library.configuration.netapp_aggregate_name_search_pattern =\ - fake.AGGREGATE_NAME_SEARCH_PATTERN + self.library.configuration.netapp_aggregate_name_search_pattern = ( + fake.AGGREGATE_NAME_SEARCH_PATTERN) result = self.library._find_matching_aggregates() self.assertListEqual(result, fake.AGGREGATES) - self.library.configuration.netapp_aggregate_name_search_pattern =\ - 'aggr.*' + self.library.configuration.netapp_aggregate_name_search_pattern = ( + 'aggr.*') result = self.library._find_matching_aggregates() self.assertListEqual(result, ['aggr0']) @@ -618,6 +633,35 @@ class NetAppFileStorageLibraryTestCase(test.TestCase): self.assertFalse(self.library._client.create_network_interface.called) + def test_get_pool_has_pool(self): + result = self.library.get_pool(fake.SHARE) + self.assertEqual(fake.POOL_NAME, result) + self.assertFalse(self.client.get_aggregate_for_volume.called) + + def test_get_pool_no_pool(self): + + fake_share = copy.deepcopy(fake.SHARE) + fake_share['host'] = '%(host)s@%(backend)s' % { + 'host': fake.HOST_NAME, 'backend': fake.BACKEND_NAME} + self.client.get_aggregate_for_volume.return_value = fake.POOL_NAME + + result = self.library.get_pool(fake_share) + + self.assertEqual(fake.POOL_NAME, result) + self.assertTrue(self.client.get_aggregate_for_volume.called) + + def test_get_pool_raises(self): + + fake_share = copy.deepcopy(fake.SHARE) + fake_share['host'] = '%(host)s@%(backend)s' % { + 'host': fake.HOST_NAME, 'backend': fake.BACKEND_NAME} + self.client.get_aggregate_for_volume.side_effect = ( + exception.NetAppException) + + self.assertRaises(exception.NetAppException, + self.library.get_pool, + fake_share) + def test_create_share(self): vserver_client = mock.Mock() @@ -674,16 +718,14 @@ class NetAppFileStorageLibraryTestCase(test.TestCase): def test_allocate_container(self): - aggregates = {'aggr0': 10000000000, 'aggr1': 20000000000} vserver_client = mock.Mock() - vserver_client.get_aggregates_for_vserver.return_value = aggregates self.library._allocate_container(fake.SHARE, fake.VSERVER1, vserver_client) share_name = self.library._get_valid_share_name(fake.SHARE['id']) - vserver_client.create_volume.assert_called_with('aggr1', + vserver_client.create_volume.assert_called_with(fake.POOL_NAME, share_name, fake.SHARE['size']) @@ -938,8 +980,8 @@ class NetAppFileStorageLibraryTestCase(test.TestCase): def test_get_network_allocations_number(self): - self.library._client.list_cluster_nodes.return_value = \ - fake.CLUSTER_NODES + self.library._client.list_cluster_nodes.return_value = ( + fake.CLUSTER_NODES) result = self.library.get_network_allocations_number() diff --git a/manila/tests/share/drivers/netapp/dataontap/fakes.py b/manila/tests/share/drivers/netapp/dataontap/fakes.py index 1b7bcf0aad..cf727e0b1b 100644 --- a/manila/tests/share/drivers/netapp/dataontap/fakes.py +++ b/manila/tests/share/drivers/netapp/dataontap/fakes.py @@ -12,11 +12,11 @@ # License for the specific language governing permissions and limitations # under the License. - BACKEND_NAME = 'fake_backend_name' DRIVER_NAME = 'fake_driver_name' APP_VERSION = 'fake_app_vsersion' HOST_NAME = 'fake_host' +POOL_NAME = 'fake_pool' VSERVER1 = 'fake_vserver_1' VSERVER2 = 'fake_vserver_2' LICENSES = ['base', 'cifs', 'fcp', 'flexclone', 'iscsi', 'nfs', 'snapmirror', @@ -51,6 +51,8 @@ CLIENT_KWARGS = { SHARE = { 'id': SHARE_ID, + 'host': '%(host)s@%(backend)s#%(pool)s' % { + 'host': HOST_NAME, 'backend': BACKEND_NAME, 'pool': POOL_NAME}, 'project_id': TENANT_ID, 'name': SHARE_NAME, 'size': SHARE_SIZE, @@ -124,3 +126,16 @@ EMS_MESSAGE = { 'log-level': '6', 'auto-support': 'false' } + +AGGREGATE_CAPACITIES = { + 'manila1': { + 'available': 1181116007, # 1.1 GB + 'total': 3543348020, # 3.3 GB + 'used': 2362232013, # 2.2 GB + }, + 'manila2': { + 'available': 2147483648, # 2.0 GB + 'total': 6442450944, # 6.0 GB + 'used': 4294967296, # 4.0 GB + } +} diff --git a/manila/tests/share/drivers/netapp/dataontap/protocols/test_cifs_cmode.py b/manila/tests/share/drivers/netapp/dataontap/protocols/test_cifs_cmode.py index 51f3044d51..443024e9ab 100644 --- a/manila/tests/share/drivers/netapp/dataontap/protocols/test_cifs_cmode.py +++ b/manila/tests/share/drivers/netapp/dataontap/protocols/test_cifs_cmode.py @@ -65,8 +65,8 @@ class NetAppClusteredCIFSHelperTestCase(test.TestCase): def test_allow_access_preexisting(self): - self.mock_client.add_cifs_share_access.side_effect = \ - netapp_api.NaApiError(code=netapp_api.EDUPLICATEENTRY) + self.mock_client.add_cifs_share_access.side_effect = ( + netapp_api.NaApiError(code=netapp_api.EDUPLICATEENTRY)) self.assertRaises(exception.ShareAccessExists, self.helper.allow_access, @@ -76,8 +76,8 @@ class NetAppClusteredCIFSHelperTestCase(test.TestCase): def test_allow_access_api_error(self): - self.mock_client.add_cifs_share_access.side_effect = \ - netapp_api.NaApiError() + self.mock_client.add_cifs_share_access.side_effect = ( + netapp_api.NaApiError()) self.assertRaises(netapp_api.NaApiError, self.helper.allow_access, @@ -105,8 +105,8 @@ class NetAppClusteredCIFSHelperTestCase(test.TestCase): def test_deny_access_nonexistent_user(self): - self.mock_client.remove_cifs_share_access.side_effect = \ - netapp_api.NaApiError(code=netapp_api.EONTAPI_EINVAL) + self.mock_client.remove_cifs_share_access.side_effect = ( + netapp_api.NaApiError(code=netapp_api.EONTAPI_EINVAL)) self.helper.deny_access(self.mock_context, fake.CIFS_SHARE, fake.ACCESS) @@ -117,8 +117,8 @@ class NetAppClusteredCIFSHelperTestCase(test.TestCase): def test_deny_access_nonexistent_rule(self): - self.mock_client.remove_cifs_share_access.side_effect = \ - netapp_api.NaApiError(code=netapp_api.EOBJECTNOTFOUND) + self.mock_client.remove_cifs_share_access.side_effect = ( + netapp_api.NaApiError(code=netapp_api.EOBJECTNOTFOUND)) self.helper.deny_access(self.mock_context, fake.CIFS_SHARE, fake.ACCESS) @@ -129,8 +129,8 @@ class NetAppClusteredCIFSHelperTestCase(test.TestCase): def test_deny_access_api_error(self): - self.mock_client.remove_cifs_share_access.side_effect = \ - netapp_api.NaApiError() + self.mock_client.remove_cifs_share_access.side_effect = ( + netapp_api.NaApiError()) self.assertRaises(netapp_api.NaApiError, self.helper.deny_access, diff --git a/manila/tests/share/drivers/netapp/dataontap/protocols/test_nfs_cmode.py b/manila/tests/share/drivers/netapp/dataontap/protocols/test_nfs_cmode.py index dd6b0038ed..62a888fdf7 100644 --- a/manila/tests/share/drivers/netapp/dataontap/protocols/test_nfs_cmode.py +++ b/manila/tests/share/drivers/netapp/dataontap/protocols/test_nfs_cmode.py @@ -40,8 +40,8 @@ class NetAppClusteredNFSHelperTestCase(test.TestCase): def test_create_share(self): - self.mock_client.get_volume_junction_path.return_value = \ - fake.NFS_SHARE_PATH + self.mock_client.get_volume_junction_path.return_value = ( + fake.NFS_SHARE_PATH) result = self.helper.create_share(fake.SHARE_NAME, fake.SHARE_ADDRESS) @@ -145,8 +145,8 @@ class NetAppClusteredNFSHelperTestCase(test.TestCase): def test_get_existing_rules(self): - self.mock_client.get_nfs_export_rules.return_value = \ - fake.NFS_ACCESS_HOSTS + self.mock_client.get_nfs_export_rules.return_value = ( + fake.NFS_ACCESS_HOSTS) result = self.helper._get_existing_rules(fake.NFS_SHARE) diff --git a/manila/tests/share/drivers/netapp/test_utils.py b/manila/tests/share/drivers/netapp/test_utils.py index ae01e5d47d..fd2da38391 100644 --- a/manila/tests/share/drivers/netapp/test_utils.py +++ b/manila/tests/share/drivers/netapp/test_utils.py @@ -84,18 +84,18 @@ class NetAppDriverUtilsTestCase(test.TestCase): self.assertEqual('OK', result) self.assertEqual(2, na_utils.LOG.debug.call_count) - def test_validate_instantiation_proxy(self): + def test_validate_driver_instantiation_proxy(self): kwargs = {'netapp_mode': 'proxy'} - na_utils.validate_instantiation(**kwargs) + na_utils.validate_driver_instantiation(**kwargs) self.assertEqual(0, na_utils.LOG.warning.call_count) - def test_validate_instantiation_no_proxy(self): + def test_validate_driver_instantiation_no_proxy(self): self.mock_object(na_utils, 'LOG') kwargs = {'netapp_mode': 'asdf'} - na_utils.validate_instantiation(**kwargs) + na_utils.validate_driver_instantiation(**kwargs) self.assertEqual(1, na_utils.LOG.warning.call_count)