# Copyright (c) 2014 Alex Meade. All rights reserved. # Copyright (c) 2014 Clinton Knight. All rights reserved. # Copyright (c) 2014 Andrew Kerr. All rights reserved. # 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. """ Mock unit tests for the NetApp block storage library """ import uuid import mock from cinder import exception from cinder import test from cinder.tests.volume.drivers.netapp.dataontap import fakes as fake from cinder.volume.drivers.netapp.dataontap import block_base from cinder.volume.drivers.netapp.dataontap.client import api as netapp_api from cinder.volume.drivers.netapp import utils as na_utils class NetAppBlockStorageLibraryTestCase(test.TestCase): def setUp(self): super(NetAppBlockStorageLibraryTestCase, self).setUp() kwargs = {'configuration': mock.Mock()} self.library = block_base.NetAppBlockStorageLibrary( 'driver', 'protocol', **kwargs) self.library.zapi_client = mock.Mock() self.zapi_client = self.library.zapi_client self.mock_request = mock.Mock() def tearDown(self): super(NetAppBlockStorageLibraryTestCase, self).tearDown() @mock.patch.object(block_base.NetAppBlockStorageLibrary, '_get_lun_attr', mock.Mock(return_value={'Volume': 'vol1'})) def test_get_pool(self): pool = self.library.get_pool({'name': 'volume-fake-uuid'}) self.assertEqual(pool, 'vol1') @mock.patch.object(block_base.NetAppBlockStorageLibrary, '_get_lun_attr', mock.Mock(return_value=None)) def test_get_pool_no_metadata(self): pool = self.library.get_pool({'name': 'volume-fake-uuid'}) self.assertEqual(pool, None) @mock.patch.object(block_base.NetAppBlockStorageLibrary, '_get_lun_attr', mock.Mock(return_value=dict())) def test_get_pool_volume_unknown(self): pool = self.library.get_pool({'name': 'volume-fake-uuid'}) self.assertEqual(pool, None) @mock.patch.object(block_base.NetAppBlockStorageLibrary, '_create_lun', mock.Mock()) @mock.patch.object(block_base.NetAppBlockStorageLibrary, '_create_lun_handle', mock.Mock()) @mock.patch.object(block_base.NetAppBlockStorageLibrary, '_add_lun_to_table', mock.Mock()) @mock.patch.object(na_utils, 'get_volume_extra_specs', mock.Mock(return_value=None)) @mock.patch.object(block_base, 'LOG', mock.Mock()) def test_create_volume(self): self.library.zapi_client.get_lun_by_args.return_value = ['lun'] self.library.create_volume({'name': 'lun1', 'size': 100, 'id': uuid.uuid4(), 'host': 'hostname@backend#vol1'}) self.library._create_lun.assert_called_once_with( 'vol1', 'lun1', 107374182400, mock.ANY, None) self.assertEqual(0, block_base.LOG.warning.call_count) def test_create_volume_no_pool_provided_by_scheduler(self): self.assertRaises(exception.InvalidHost, self.library.create_volume, {'name': 'lun1', 'size': 100, 'id': uuid.uuid4(), 'host': 'hostname@backend'}) # missing pool @mock.patch.object(block_base.NetAppBlockStorageLibrary, '_get_lun_attr') @mock.patch.object(block_base.NetAppBlockStorageLibrary, '_get_or_create_igroup') def test_map_lun(self, mock_get_or_create_igroup, mock_get_lun_attr): os = 'linux' protocol = 'fcp' mock_get_lun_attr.return_value = {'Path': fake.LUN1, 'OsType': os} mock_get_or_create_igroup.return_value = fake.IGROUP1_NAME self.zapi_client.map_lun.return_value = '1' lun_id = self.library._map_lun('fake_volume', fake.FC_FORMATTED_INITIATORS, protocol, None) self.assertEqual(lun_id, '1') mock_get_or_create_igroup.assert_called_once_with( fake.FC_FORMATTED_INITIATORS, protocol, os) self.zapi_client.map_lun.assert_called_once_with( fake.LUN1, fake.IGROUP1_NAME, lun_id=None) @mock.patch.object(block_base.NetAppBlockStorageLibrary, '_get_lun_attr') @mock.patch.object(block_base.NetAppBlockStorageLibrary, '_get_or_create_igroup') @mock.patch.object(block_base.NetAppBlockStorageLibrary, '_find_mapped_lun_igroup') def test_map_lun_preexisting(self, mock_find_mapped_lun_igroup, mock_get_or_create_igroup, mock_get_lun_attr): os = 'linux' protocol = 'fcp' mock_get_lun_attr.return_value = {'Path': fake.LUN1, 'OsType': os} mock_get_or_create_igroup.return_value = fake.IGROUP1_NAME mock_find_mapped_lun_igroup.return_value = (fake.IGROUP1_NAME, '2') self.zapi_client.map_lun.side_effect = netapp_api.NaApiError lun_id = self.library._map_lun( 'fake_volume', fake.FC_FORMATTED_INITIATORS, protocol, None) self.assertEqual(lun_id, '2') mock_find_mapped_lun_igroup.assert_called_once_with( fake.LUN1, fake.FC_FORMATTED_INITIATORS) @mock.patch.object(block_base.NetAppBlockStorageLibrary, '_get_lun_attr') @mock.patch.object(block_base.NetAppBlockStorageLibrary, '_get_or_create_igroup') @mock.patch.object(block_base.NetAppBlockStorageLibrary, '_find_mapped_lun_igroup') def test_map_lun_api_error(self, mock_find_mapped_lun_igroup, mock_get_or_create_igroup, mock_get_lun_attr): os = 'linux' protocol = 'fcp' mock_get_lun_attr.return_value = {'Path': fake.LUN1, 'OsType': os} mock_get_or_create_igroup.return_value = fake.IGROUP1_NAME mock_find_mapped_lun_igroup.return_value = (None, None) self.zapi_client.map_lun.side_effect = netapp_api.NaApiError self.assertRaises(netapp_api.NaApiError, self.library._map_lun, 'fake_volume', fake.FC_FORMATTED_INITIATORS, protocol, None) @mock.patch.object(block_base.NetAppBlockStorageLibrary, '_find_mapped_lun_igroup') def test_unmap_lun(self, mock_find_mapped_lun_igroup): mock_find_mapped_lun_igroup.return_value = (fake.IGROUP1_NAME, 1) self.library._unmap_lun(fake.LUN1, fake.FC_FORMATTED_INITIATORS) self.zapi_client.unmap_lun.assert_called_once_with(fake.LUN1, fake.IGROUP1_NAME) def test_find_mapped_lun_igroup(self): self.assertRaises(NotImplementedError, self.library._find_mapped_lun_igroup, fake.LUN1, fake.FC_FORMATTED_INITIATORS) def test_has_luns_mapped_to_initiators(self): self.zapi_client.has_luns_mapped_to_initiators.return_value = True self.assertTrue(self.library._has_luns_mapped_to_initiators( fake.FC_FORMATTED_INITIATORS)) self.zapi_client.has_luns_mapped_to_initiators.assert_called_once_with( fake.FC_FORMATTED_INITIATORS) def test_get_or_create_igroup_preexisting(self): self.zapi_client.get_igroup_by_initiators.return_value = [fake.IGROUP1] igroup_name = self.library._get_or_create_igroup( fake.FC_FORMATTED_INITIATORS, 'fcp', 'linux') self.assertEqual(igroup_name, fake.IGROUP1_NAME) self.zapi_client.get_igroup_by_initiators.assert_called_once_with( fake.FC_FORMATTED_INITIATORS) @mock.patch.object(uuid, 'uuid4', mock.Mock(return_value=fake.UUID1)) def test_get_or_create_igroup_none_preexisting(self): self.zapi_client.get_igroup_by_initiators.return_value = [] igroup_name = self.library._get_or_create_igroup( fake.FC_FORMATTED_INITIATORS, 'fcp', 'linux') self.assertEqual(igroup_name, 'openstack-' + fake.UUID1) self.zapi_client.create_igroup.assert_called_once_with( igroup_name, 'fcp', 'linux') self.assertEqual(len(fake.FC_FORMATTED_INITIATORS), self.zapi_client.add_igroup_initiator.call_count) def test_get_fc_target_wwpns(self): self.assertRaises(NotImplementedError, self.library._get_fc_target_wwpns) @mock.patch.object(block_base.NetAppBlockStorageLibrary, '_build_initiator_target_map') @mock.patch.object(block_base.NetAppBlockStorageLibrary, '_map_lun') def test_initialize_connection_fc(self, mock_map_lun, mock_build_initiator_target_map): self.maxDiff = None mock_map_lun.return_value = '1' mock_build_initiator_target_map.return_value = (fake.FC_TARGET_WWPNS, fake.FC_I_T_MAP, 4) target_info = self.library.initialize_connection_fc(fake.FC_VOLUME, fake.FC_CONNECTOR) self.assertDictEqual(target_info, fake.FC_TARGET_INFO) mock_map_lun.assert_called_once_with( 'fake_volume', fake.FC_FORMATTED_INITIATORS, 'fcp', None) @mock.patch.object(block_base.NetAppBlockStorageLibrary, '_build_initiator_target_map') @mock.patch.object(block_base.NetAppBlockStorageLibrary, '_map_lun') def test_initialize_connection_fc_no_wwpns( self, mock_map_lun, mock_build_initiator_target_map): mock_map_lun.return_value = '1' mock_build_initiator_target_map.return_value = (None, None, 0) self.assertRaises(exception.VolumeBackendAPIException, self.library.initialize_connection_fc, fake.FC_VOLUME, fake.FC_CONNECTOR) @mock.patch.object(block_base.NetAppBlockStorageLibrary, '_has_luns_mapped_to_initiators') @mock.patch.object(block_base.NetAppBlockStorageLibrary, '_unmap_lun') @mock.patch.object(block_base.NetAppBlockStorageLibrary, '_get_lun_attr') def test_terminate_connection_fc(self, mock_get_lun_attr, mock_unmap_lun, mock_has_luns_mapped_to_initiators): mock_get_lun_attr.return_value = {'Path': fake.LUN1} mock_unmap_lun.return_value = None mock_has_luns_mapped_to_initiators.return_value = True target_info = self.library.terminate_connection_fc(fake.FC_VOLUME, fake.FC_CONNECTOR) self.assertDictEqual(target_info, fake.FC_TARGET_INFO_EMPTY) mock_unmap_lun.assert_called_once_with(fake.LUN1, fake.FC_FORMATTED_INITIATORS) @mock.patch.object(block_base.NetAppBlockStorageLibrary, '_build_initiator_target_map') @mock.patch.object(block_base.NetAppBlockStorageLibrary, '_has_luns_mapped_to_initiators') @mock.patch.object(block_base.NetAppBlockStorageLibrary, '_unmap_lun') @mock.patch.object(block_base.NetAppBlockStorageLibrary, '_get_lun_attr') def test_terminate_connection_fc_no_more_luns( self, mock_get_lun_attr, mock_unmap_lun, mock_has_luns_mapped_to_initiators, mock_build_initiator_target_map): mock_get_lun_attr.return_value = {'Path': fake.LUN1} mock_unmap_lun.return_value = None mock_has_luns_mapped_to_initiators.return_value = False mock_build_initiator_target_map.return_value = (fake.FC_TARGET_WWPNS, fake.FC_I_T_MAP, 4) target_info = self.library.terminate_connection_fc(fake.FC_VOLUME, fake.FC_CONNECTOR) self.assertDictEqual(target_info, fake.FC_TARGET_INFO_UNMAP) @mock.patch.object(block_base.NetAppBlockStorageLibrary, '_get_fc_target_wwpns') def test_build_initiator_target_map_no_lookup_service( self, mock_get_fc_target_wwpns): self.library.lookup_service = None mock_get_fc_target_wwpns.return_value = fake.FC_FORMATTED_TARGET_WWPNS (target_wwpns, init_targ_map, num_paths) = \ self.library._build_initiator_target_map(fake.FC_CONNECTOR) self.assertSetEqual(set(fake.FC_TARGET_WWPNS), set(target_wwpns)) self.assertDictEqual(fake.FC_I_T_MAP_COMPLETE, init_targ_map) self.assertEqual(0, num_paths) @mock.patch.object(block_base.NetAppBlockStorageLibrary, '_get_fc_target_wwpns') def test_build_initiator_target_map_with_lookup_service( self, mock_get_fc_target_wwpns): self.library.lookup_service = mock.Mock() self.library.lookup_service.get_device_mapping_from_network.\ return_value = fake.FC_FABRIC_MAP mock_get_fc_target_wwpns.return_value = fake.FC_FORMATTED_TARGET_WWPNS (target_wwpns, init_targ_map, num_paths) = \ self.library._build_initiator_target_map(fake.FC_CONNECTOR) self.assertSetEqual(set(fake.FC_TARGET_WWPNS), set(target_wwpns)) self.assertDictEqual(fake.FC_I_T_MAP, init_targ_map) self.assertEqual(4, num_paths) @mock.patch.object(block_base.NetAppBlockStorageLibrary, '_create_lun', mock.Mock()) @mock.patch.object(block_base.NetAppBlockStorageLibrary, '_create_lun_handle', mock.Mock()) @mock.patch.object(block_base.NetAppBlockStorageLibrary, '_add_lun_to_table', mock.Mock()) @mock.patch.object(na_utils, 'LOG', mock.Mock()) @mock.patch.object(na_utils, 'get_volume_extra_specs', mock.Mock(return_value={'netapp:raid_type': 'raid4'})) def test_create_volume_obsolete_extra_spec(self): self.library.zapi_client.get_lun_by_args.return_value = ['lun'] self.library.create_volume({'name': 'lun1', 'size': 100, 'id': uuid.uuid4(), 'host': 'hostname@backend#vol1'}) warn_msg = 'Extra spec netapp:raid_type is obsolete. ' \ 'Use netapp_raid_type instead.' na_utils.LOG.warning.assert_called_once_with(warn_msg) @mock.patch.object(block_base.NetAppBlockStorageLibrary, '_create_lun', mock.Mock()) @mock.patch.object(block_base.NetAppBlockStorageLibrary, '_create_lun_handle', mock.Mock()) @mock.patch.object(block_base.NetAppBlockStorageLibrary, '_add_lun_to_table', mock.Mock()) @mock.patch.object(na_utils, 'LOG', mock.Mock()) @mock.patch.object(na_utils, 'get_volume_extra_specs', mock.Mock(return_value={'netapp_thick_provisioned': 'true'})) def test_create_volume_deprecated_extra_spec(self): self.library.zapi_client.get_lun_by_args.return_value = ['lun'] self.library.create_volume({'name': 'lun1', 'size': 100, 'id': uuid.uuid4(), 'host': 'hostname@backend#vol1'}) warn_msg = 'Extra spec netapp_thick_provisioned is deprecated. ' \ 'Use netapp_thin_provisioned instead.' na_utils.LOG.warning.assert_called_once_with(warn_msg) @mock.patch.object(na_utils, 'check_flags') def test_do_setup(self, mock_check_flags): self.library.do_setup(mock.Mock()) self.assertTrue(mock_check_flags.called) def test_get_existing_vol_manage_missing_id_path(self): self.assertRaises(exception.ManageExistingInvalidReference, self.library._get_existing_vol_with_manage_ref, {}) def test_get_existing_vol_manage_not_found(self): self.zapi_client.get_lun_by_args.return_value = [] self.assertRaises(exception.ManageExistingInvalidReference, self.library._get_existing_vol_with_manage_ref, {'source-id': 'src_id', 'source-name': 'lun_path'}) self.assertEqual(1, self.zapi_client.get_lun_by_args.call_count) @mock.patch.object(block_base.NetAppBlockStorageLibrary, '_extract_lun_info', mock.Mock(return_value=block_base.NetAppLun( 'lun0', 'lun0', '3', {'UUID': 'src_id'}))) def test_get_existing_vol_manage_lun(self): self.zapi_client.get_lun_by_args.return_value = ['lun0', 'lun1'] lun = self.library._get_existing_vol_with_manage_ref( {'source-id': 'src_id', 'path': 'lun_path'}) self.assertEqual(1, self.zapi_client.get_lun_by_args.call_count) self.library._extract_lun_info.assert_called_once_with('lun0') self.assertEqual('lun0', lun.name) @mock.patch.object(block_base.NetAppBlockStorageLibrary, '_get_existing_vol_with_manage_ref', mock.Mock(return_value=block_base.NetAppLun( 'handle', 'name', '1073742824', {}))) def test_manage_existing_get_size(self): size = self.library.manage_existing_get_size( {'id': 'vol_id'}, {'ref': 'ref'}) self.assertEqual(2, size) self.library._get_existing_vol_with_manage_ref.assert_called_once_with( {'ref': 'ref'}) @mock.patch.object(block_base.LOG, 'info') def test_unmanage(self, log): mock_lun = block_base.NetAppLun('handle', 'name', '1', {'Path': 'p', 'UUID': 'uuid'}) self.library._get_lun_from_table = mock.Mock(return_value=mock_lun) self.library.unmanage({'name': 'vol'}) self.library._get_lun_from_table.assert_called_once_with('vol') self.assertEqual(1, log.call_count) def test_manage_existing_lun_same_name(self): mock_lun = block_base.NetAppLun('handle', 'name', '1', {'Path': '/vol/vol1/name'}) self.library._get_existing_vol_with_manage_ref = mock.Mock( return_value=mock_lun) self.library._check_volume_type_for_lun = mock.Mock() self.library._add_lun_to_table = mock.Mock() self.zapi_client.move_lun = mock.Mock() self.library.manage_existing({'name': 'name'}, {'ref': 'ref'}) self.library._get_existing_vol_with_manage_ref.assert_called_once_with( {'ref': 'ref'}) self.assertEqual(1, self.library._check_volume_type_for_lun.call_count) self.assertEqual(1, self.library._add_lun_to_table.call_count) self.assertEqual(0, self.zapi_client.move_lun.call_count) def test_manage_existing_lun_new_path(self): mock_lun = block_base.NetAppLun( 'handle', 'name', '1', {'Path': '/vol/vol1/name'}) self.library._get_existing_vol_with_manage_ref = mock.Mock( return_value=mock_lun) self.library._check_volume_type_for_lun = mock.Mock() self.library._add_lun_to_table = mock.Mock() self.zapi_client.move_lun = mock.Mock() self.library.manage_existing({'name': 'volume'}, {'ref': 'ref'}) self.assertEqual( 2, self.library._get_existing_vol_with_manage_ref.call_count) self.assertEqual(1, self.library._check_volume_type_for_lun.call_count) self.assertEqual(1, self.library._add_lun_to_table.call_count) self.zapi_client.move_lun.assert_called_once_with( '/vol/vol1/name', '/vol/vol1/volume') def test_check_vol_type_for_lun(self): self.assertRaises(NotImplementedError, self.library._check_volume_type_for_lun, 'vol', 'lun', 'existing_ref') def test_is_lun_valid_on_storage(self): self.assertTrue(self.library._is_lun_valid_on_storage('lun'))