# Copyright (c) 2015 Clinton Knight. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Unit tests for the NetApp Data ONTAP cDOT multi-SVM storage driver library. """ import copy import ddt import mock from oslo_log import log from manila import context from manila import exception from manila.share.drivers.netapp.dataontap.client import api as netapp_api from manila.share.drivers.netapp.dataontap.cluster_mode import lib_base from manila.share.drivers.netapp.dataontap.cluster_mode import lib_multi_svm from manila.share.drivers.netapp import utils as na_utils from manila import test from manila.tests.share.drivers.netapp.dataontap import fakes as fake @ddt.ddt class NetAppFileStorageLibraryTestCase(test.TestCase): def setUp(self): super(NetAppFileStorageLibraryTestCase, self).setUp() self.mock_object(na_utils, 'validate_driver_instantiation') # Mock loggers as themselves to allow logger arg validation mock_logger = log.getLogger('mock_logger') self.mock_object(lib_multi_svm.LOG, 'warning', mock.Mock(side_effect=mock_logger.warning)) self.mock_object(lib_multi_svm.LOG, 'error', mock.Mock(side_effect=mock_logger.error)) kwargs = { 'configuration': fake.get_config_cmode(), 'private_storage': mock.Mock(), 'app_version': fake.APP_VERSION } self.library = lib_multi_svm.NetAppCmodeMultiSVMFileStorageLibrary( fake.DRIVER_NAME, **kwargs) self.library._client = mock.Mock() self.library._client.get_ontapi_version.return_value = (1, 21) self.client = self.library._client self.context = mock.Mock() def test_check_for_setup_error_cluster_creds_no_vserver(self): self.library._have_cluster_creds = True self.mock_object(self.library, '_find_matching_aggregates', mock.Mock(return_value=fake.AGGREGATES)) mock_super = self.mock_object(lib_base.NetAppCmodeFileStorageLibrary, 'check_for_setup_error') self.library.check_for_setup_error() self.assertTrue(self.library._find_matching_aggregates.called) mock_super.assert_called_once_with() def test_check_for_setup_error_cluster_creds_with_vserver(self): self.library._have_cluster_creds = True self.library.configuration.netapp_vserver = fake.VSERVER1 self.mock_object(self.library, '_find_matching_aggregates', mock.Mock(return_value=fake.AGGREGATES)) mock_super = self.mock_object(lib_base.NetAppCmodeFileStorageLibrary, 'check_for_setup_error') self.library.check_for_setup_error() mock_super.assert_called_once_with() self.assertTrue(self.library._find_matching_aggregates.called) self.assertTrue(lib_multi_svm.LOG.warning.called) def test_check_for_setup_error_vserver_creds(self): self.library._have_cluster_creds = False self.assertRaises(exception.InvalidInput, self.library.check_for_setup_error) def test_check_for_setup_error_no_aggregates(self): self.library._have_cluster_creds = True self.mock_object(self.library, '_find_matching_aggregates', mock.Mock(return_value=[])) self.assertRaises(exception.NetAppException, self.library.check_for_setup_error) self.assertTrue(self.library._find_matching_aggregates.called) def test_get_vserver_no_share_server(self): self.assertRaises(exception.InvalidInput, self.library._get_vserver) def test_get_vserver_no_backend_details(self): fake_share_server = copy.deepcopy(fake.SHARE_SERVER) fake_share_server.pop('backend_details') kwargs = {'share_server': fake_share_server} self.assertRaises(exception.VserverNotSpecified, self.library._get_vserver, **kwargs) def test_get_vserver_none_backend_details(self): fake_share_server = copy.deepcopy(fake.SHARE_SERVER) fake_share_server['backend_details'] = None kwargs = {'share_server': fake_share_server} self.assertRaises(exception.VserverNotSpecified, self.library._get_vserver, **kwargs) def test_get_vserver_no_vserver(self): fake_share_server = copy.deepcopy(fake.SHARE_SERVER) fake_share_server['backend_details'].pop('vserver_name') kwargs = {'share_server': fake_share_server} self.assertRaises(exception.VserverNotSpecified, self.library._get_vserver, **kwargs) def test_get_vserver_none_vserver(self): fake_share_server = copy.deepcopy(fake.SHARE_SERVER) fake_share_server['backend_details']['vserver_name'] = None kwargs = {'share_server': fake_share_server} self.assertRaises(exception.VserverNotSpecified, self.library._get_vserver, **kwargs) def test_get_vserver_not_found(self): self.library._client.vserver_exists.return_value = False kwargs = {'share_server': fake.SHARE_SERVER} self.assertRaises(exception.VserverNotFound, self.library._get_vserver, **kwargs) def test_get_vserver(self): self.library._client.vserver_exists.return_value = True self.mock_object(self.library, '_get_api_client', mock.Mock(return_value='fake_client')) result = self.library._get_vserver(share_server=fake.SHARE_SERVER) self.assertTupleEqual((fake.VSERVER1, 'fake_client'), result) def test_handle_housekeeping_tasks(self): self.mock_object(self.client, 'prune_deleted_nfs_export_policies') mock_super = self.mock_object(lib_base.NetAppCmodeFileStorageLibrary, '_handle_housekeeping_tasks') self.library._handle_housekeeping_tasks() self.assertTrue(self.client.prune_deleted_nfs_export_policies.called) self.assertTrue(mock_super.called) def test_find_matching_aggregates(self): self.mock_object(self.client, 'list_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) def test_setup_server(self): mock_get_vserver_name = self.mock_object( self.library, '_get_vserver_name', mock.Mock(return_value=fake.VSERVER1)) mock_create_vserver = self.mock_object( self.library, '_create_vserver_if_nonexistent') mock_validate_network_type = self.mock_object( self.library, '_validate_network_type') result = self.library.setup_server(fake.NETWORK_INFO) self.assertTrue(mock_validate_network_type.called) self.assertTrue(mock_get_vserver_name.called) self.assertTrue(mock_create_vserver.called) self.assertDictEqual({'vserver_name': fake.VSERVER1}, result) def test_setup_server_with_error(self): mock_get_vserver_name = self.mock_object( self.library, '_get_vserver_name', mock.Mock(return_value=fake.VSERVER1)) fake_exception = exception.ManilaException("fake") mock_create_vserver = self.mock_object( self.library, '_create_vserver_if_nonexistent', mock.Mock(side_effect=fake_exception)) mock_validate_network_type = self.mock_object( self.library, '_validate_network_type') self.assertRaises( exception.ManilaException, self.library.setup_server, fake.NETWORK_INFO) self.assertTrue(mock_validate_network_type.called) self.assertTrue(mock_get_vserver_name.called) self.assertTrue(mock_create_vserver.called) self.assertDictEqual( {'server_details': {'vserver_name': fake.VSERVER1}}, fake_exception.detail_data) @ddt.data( {'network_info': {'network_type': 'vlan', 'segmentation_id': 1000}}, {'network_info': {'network_type': None, 'segmentation_id': None}}, {'network_info': {'network_type': 'flat', 'segmentation_id': None}}) @ddt.unpack def test_validate_network_type_with_valid_network_types(self, network_info): self.library._validate_network_type(network_info) @ddt.data( {'network_info': {'network_type': 'vxlan', 'segmentation_id': 1000}}, {'network_info': {'network_type': 'gre', 'segmentation_id': 100}}) @ddt.unpack def test_validate_network_type_with_invalid_network_types(self, network_info): self.assertRaises(exception.NetworkBadConfigurationException, self.library._validate_network_type, network_info) def test_get_vserver_name(self): vserver_id = fake.NETWORK_INFO['server_id'] vserver_name = fake.VSERVER_NAME_TEMPLATE % vserver_id actual_result = self.library._get_vserver_name(vserver_id) self.assertEqual(vserver_name, actual_result) def test_create_vserver_if_nonexistent(self): vserver_id = fake.NETWORK_INFO['server_id'] vserver_name = fake.VSERVER_NAME_TEMPLATE % vserver_id vserver_client = mock.Mock() self.mock_object(context, 'get_admin_context', mock.Mock(return_value='fake_admin_context')) self.mock_object(self.library, '_get_api_client', mock.Mock(return_value=vserver_client)) self.mock_object(self.library._client, 'vserver_exists', mock.Mock(return_value=False)) self.mock_object(self.library, '_find_matching_aggregates', mock.Mock(return_value=fake.AGGREGATES)) self.mock_object(self.library, '_create_vserver_lifs') self.library._create_vserver_if_nonexistent(vserver_name, fake.NETWORK_INFO) self.library._get_api_client.assert_called_with(vserver=vserver_name) self.library._client.create_vserver.assert_called_with( vserver_name, fake.ROOT_VOLUME_AGGREGATE, fake.ROOT_VOLUME, fake.AGGREGATES) self.library._create_vserver_lifs.assert_called_with( vserver_name, vserver_client, fake.NETWORK_INFO) self.assertTrue(vserver_client.enable_nfs.called) self.library._client.setup_security_services.assert_called_with( fake.NETWORK_INFO['security_services'], vserver_client, vserver_name) def test_create_vserver_if_nonexistent_already_present(self): vserver_id = fake.NETWORK_INFO['server_id'] vserver_name = fake.VSERVER_NAME_TEMPLATE % vserver_id self.mock_object(context, 'get_admin_context', mock.Mock(return_value='fake_admin_context')) self.mock_object(self.library._client, 'vserver_exists', mock.Mock(return_value=True)) self.assertRaises(exception.NetAppException, self.library._create_vserver_if_nonexistent, vserver_name, fake.NETWORK_INFO) @ddt.data(netapp_api.NaApiError, exception.NetAppException) def test_create_vserver_if_nonexistent_lif_creation_failure(self, lif_exception): vserver_id = fake.NETWORK_INFO['server_id'] vserver_name = fake.VSERVER_NAME_TEMPLATE % vserver_id vserver_client = mock.Mock() self.mock_object(context, 'get_admin_context', mock.Mock(return_value='fake_admin_context')) self.mock_object(self.library, '_get_api_client', mock.Mock(return_value=vserver_client)) self.mock_object(self.library._client, 'vserver_exists', mock.Mock(return_value=False)) self.mock_object(self.library, '_find_matching_aggregates', mock.Mock(return_value=fake.AGGREGATES)) self.mock_object(self.library, '_create_vserver_lifs', mock.Mock(side_effect=lif_exception)) self.assertRaises(lif_exception, self.library._create_vserver_if_nonexistent, vserver_name, fake.NETWORK_INFO) self.library._get_api_client.assert_called_with(vserver=vserver_name) self.assertTrue(self.library._client.create_vserver.called) self.library._create_vserver_lifs.assert_called_with( vserver_name, vserver_client, fake.NETWORK_INFO) self.library._client.delete_vserver.assert_called_once_with( vserver_name, vserver_client) self.assertFalse(vserver_client.enable_nfs.called) self.assertEqual(1, lib_multi_svm.LOG.error.call_count) def test_create_vserver_lifs(self): self.mock_object(self.library._client, 'list_cluster_nodes', mock.Mock(return_value=fake.CLUSTER_NODES)) self.mock_object(self.library, '_get_node_data_port', mock.Mock(return_value=fake.NODE_DATA_PORT)) self.mock_object(self.library, '_create_lif_if_nonexistent') self.library._create_vserver_lifs(fake.VSERVER1, 'fake_vserver_client', fake.NETWORK_INFO) self.library._create_lif_if_nonexistent.assert_has_calls([ mock.call( fake.VSERVER1, fake.NETWORK_INFO['network_allocations'][0]['id'], fake.NETWORK_INFO['segmentation_id'], fake.CLUSTER_NODES[0], fake.NODE_DATA_PORT, fake.NETWORK_INFO['network_allocations'][0]['ip_address'], fake.NETWORK_INFO_NETMASK, 'fake_vserver_client'), mock.call( fake.VSERVER1, fake.NETWORK_INFO['network_allocations'][1]['id'], fake.NETWORK_INFO['segmentation_id'], fake.CLUSTER_NODES[1], fake.NODE_DATA_PORT, fake.NETWORK_INFO['network_allocations'][1]['ip_address'], fake.NETWORK_INFO_NETMASK, 'fake_vserver_client')]) def test_get_node_data_port(self): self.mock_object(self.client, 'list_node_data_ports', mock.Mock(return_value=fake.NODE_DATA_PORTS)) self.library.configuration.netapp_port_name_search_pattern = 'e0c' result = self.library._get_node_data_port(fake.CLUSTER_NODE) self.assertEqual('e0c', result) self.library._client.list_node_data_ports.assert_has_calls([ mock.call(fake.CLUSTER_NODE)]) def test_get_node_data_port_no_match(self): self.mock_object(self.client, 'list_node_data_ports', mock.Mock(return_value=fake.NODE_DATA_PORTS)) self.library.configuration.netapp_port_name_search_pattern = 'ifgroup1' self.assertRaises(exception.NetAppException, self.library._get_node_data_port, fake.CLUSTER_NODE) def test_create_lif_if_nonexistent(self): vserver_client = mock.Mock() vserver_client.network_interface_exists = mock.Mock( return_value=False) self.library._create_lif_if_nonexistent('fake_vserver', 'fake_allocation_id', 'fake_vlan', 'fake_node', 'fake_port', 'fake_ip', 'fake_netmask', vserver_client) self.library._client.create_network_interface.assert_has_calls([ mock.call( 'fake_ip', 'fake_netmask', 'fake_vlan', 'fake_node', 'fake_port', 'fake_vserver', 'fake_allocation_id', fake.LIF_NAME_TEMPLATE)]) def test_create_lif_if_nonexistent_already_present(self): vserver_client = mock.Mock() vserver_client.network_interface_exists = mock.Mock( return_value=True) self.library._create_lif_if_nonexistent('fake_vserver', 'fake_allocation_id', 'fake_vlan', 'fake_node', 'fake_port', 'fake_ip', 'fake_netmask', vserver_client) self.assertFalse(self.library._client.create_network_interface.called) def test_get_network_allocations_number(self): self.library._client.list_cluster_nodes.return_value = ( fake.CLUSTER_NODES) result = self.library.get_network_allocations_number() self.assertEqual(len(fake.CLUSTER_NODES), result) def test_teardown_server(self): vserver_client = mock.Mock() self.mock_object(self.library, '_get_api_client', mock.Mock(return_value=vserver_client)) self.library._client.vserver_exists.return_value = True self.library.teardown_server( fake.SHARE_SERVER['backend_details'], security_services=fake.NETWORK_INFO['security_services']) self.library._client.vserver_exists.assert_called_once_with( fake.VSERVER1) self.library._client.delete_vserver.assert_called_once_with( fake.VSERVER1, vserver_client, security_services=fake.NETWORK_INFO['security_services']) @ddt.data(None, {}, {'vserver_name': None}) def test_teardown_server_no_share_server(self, server_details): self.library.teardown_server(server_details) self.assertFalse(self.library._client.delete_vserver.called) self.assertTrue(lib_multi_svm.LOG.warning.called) def test_teardown_server_no_vserver(self): self.library._client.vserver_exists.return_value = False self.library.teardown_server( fake.SHARE_SERVER['backend_details'], security_services=fake.NETWORK_INFO['security_services']) self.library._client.vserver_exists.assert_called_once_with( fake.VSERVER1) self.assertFalse(self.library._client.delete_vserver.called) self.assertTrue(lib_multi_svm.LOG.warning.called)