# Copyright 2015 IBM Corp. # 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. # """ Tests for the IBM FlashSystem iSCSI volume driver. """ import mock import six import random from cinder import context from cinder import exception from cinder import test from cinder.tests.unit.volume.drivers.ibm \ import test_ibm_flashsystem as fscommon from cinder import utils from cinder.volume import configuration as conf from cinder.volume.drivers.ibm import flashsystem_iscsi from cinder.volume import volume_types class FlashSystemManagementSimulator(fscommon.FlashSystemManagementSimulator): def __init__(self): # Default protocol is iSCSI self._protocol = 'iSCSI' self._volumes_list = {} self._hosts_list = {} self._mappings_list = {} self._next_cmd_error = { 'lsnode': '', 'lssystem': '', 'lsmdiskgrp': '' } self._errors = { # CMMVC50000 is a fake error which indicates that command has not # got expected results. This error represents kinds of CLI errors. 'CMMVC50000': ('', 'CMMVC50000 The command can not be executed ' 'successfully.') } class FlashSystemFakeISCSIDriver(flashsystem_iscsi.FlashSystemISCSIDriver): def __init__(self, *args, **kwargs): super(FlashSystemFakeISCSIDriver, self).__init__(*args, **kwargs) def set_fake_storage(self, fake): self.fake_storage = fake def _ssh(self, cmd, check_exit_code=True): utils.check_ssh_injection(cmd) ret = self.fake_storage.execute_command(cmd, check_exit_code) return ret class FlashSystemISCSIDriverTestCase(test.TestCase): def _set_flag(self, flag, value): group = self.driver.configuration.config_group self.driver.configuration.set_override(flag, value, group) def _reset_flags(self): self.driver.configuration.local_conf.reset() for k, v in self._def_flags.items(): self._set_flag(k, v) def _generate_vol_info(self, vol_name, vol_size=10, vol_status='available'): rand_id = six.text_type(random.randint(10000, 99999)) if not vol_name: vol_name = 'test_volume%s' % rand_id return {'name': vol_name, 'size': vol_size, 'id': '%s' % rand_id, 'volume_type_id': None, 'status': vol_status, 'mdisk_grp_name': 'mdiskgrp0'} def _generate_snap_info(self, vol_name, vol_id, vol_size, vol_status, snap_status='available'): rand_id = six.text_type(random.randint(10000, 99999)) return {'name': 'test_snap_%s' % rand_id, 'id': rand_id, 'volume': {'name': vol_name, 'id': vol_id, 'size': vol_size, 'status': vol_status}, 'volume_size': vol_size, 'status': snap_status, 'mdisk_grp_name': 'mdiskgrp0'} def setUp(self): super(FlashSystemISCSIDriverTestCase, self).setUp() self._def_flags = {'san_ip': 'hostname', 'san_login': 'username', 'san_password': 'password', 'flashsystem_connection_protocol': 'iSCSI', 'flashsystem_multihostmap_enabled': True, 'target_ip_address': '192.168.1.10', 'flashsystem_iscsi_portid': 1} self.connector = { 'host': 'flashsystem', 'wwnns': ['0123456789abcdef', '0123456789abcdeg'], 'wwpns': ['abcd000000000001', 'abcd000000000002'], 'initiator': 'iqn.123456'} self.sim = FlashSystemManagementSimulator() self.driver = FlashSystemFakeISCSIDriver( configuration=conf.Configuration(None)) self.driver.set_fake_storage(self.sim) self._reset_flags() self.ctxt = context.get_admin_context() self.driver.do_setup(None) self.driver.check_for_setup_error() self.sleeppatch = mock.patch('eventlet.greenthread.sleep') self.sleeppatch.start() def tearDown(self): self.sleeppatch.stop() super(FlashSystemISCSIDriverTestCase, self).tearDown() def test_flashsystem_do_setup(self): # case 1: set as iSCSI self.sim.set_protocol('iSCSI') self._set_flag('flashsystem_connection_protocol', 'iSCSI') self.driver.do_setup(None) self.assertEqual('iSCSI', self.driver._protocol) # clear environment self.sim.set_protocol('iSCSI') self._reset_flags() def test_flashsystem_validate_connector(self): conn_neither = {'host': 'host'} conn_iscsi = {'host': 'host', 'initiator': 'foo'} conn_both = {'host': 'host', 'initiator': 'foo', 'wwpns': 'bar'} protocol = self.driver._protocol # case 1: when protocol is iSCSI self.driver._protocol = 'iSCSI' self.driver.validate_connector(conn_iscsi) self.driver.validate_connector(conn_both) self.assertRaises(exception.InvalidConnectorException, self.driver.validate_connector, conn_neither) # clear environment self.driver._protocol = protocol def test_flashsystem_connection(self): # case 1: initialize_connection/terminate_connection with iSCSI self.sim.set_protocol('iSCSI') self._set_flag('flashsystem_connection_protocol', 'iSCSI') self.driver.do_setup(None) vol1 = self._generate_vol_info(None) self.driver.create_volume(vol1) self.driver.initialize_connection(vol1, self.connector) self.driver.terminate_connection(vol1, self.connector) # clear environment self.driver.delete_volume(vol1) self.sim.set_protocol('iSCSI') self._reset_flags() def test_flashsystem_create_host(self): # case 1: create host with iqn self.sim.set_protocol('iSCSI') self._set_flag('flashsystem_connection_protocol', 'iSCSI') self.driver.do_setup(None) conn = { 'host': 'flashsystem', 'wwnns': ['0123456789abcdef', '0123456789abcdeg'], 'wwpns': ['abcd000000000001', 'abcd000000000002'], 'initiator': 'iqn.123456'} host = self.driver._create_host(conn) # case 2: delete host self.driver._delete_host(host) # clear environment self.sim.set_protocol('iSCSI') self._reset_flags() def test_flashsystem_get_vdisk_params(self): # case 1: use default params self.driver._get_vdisk_params(None) # case 2: use extra params from type opts1 = {'storage_protocol': 'iSCSI'} opts2 = {'capabilities:storage_protocol': 'iSCSI'} opts3 = {'storage_protocol': 'FC'} type1 = volume_types.create(self.ctxt, 'opts1', opts1) type2 = volume_types.create(self.ctxt, 'opts2', opts2) type3 = volume_types.create(self.ctxt, 'opts3', opts3) self.assertEqual( 'iSCSI', self.driver._get_vdisk_params(type1['id'])['protocol']) self.assertEqual( 'iSCSI', self.driver._get_vdisk_params(type2['id'])['protocol']) self.assertRaises(exception.InvalidInput, self.driver._get_vdisk_params, type3['id']) # clear environment volume_types.destroy(self.ctxt, type1['id']) volume_types.destroy(self.ctxt, type2['id']) volume_types.destroy(self.ctxt, type3['id']) def test_flashsystem_map_vdisk_to_host(self): # case 1: no host found vol1 = self._generate_vol_info(None) self.driver.create_volume(vol1) self.assertEqual( # lun id shoud begin with 1 1, self.driver._map_vdisk_to_host(vol1['name'], self.connector)) # case 2: host already exists vol2 = self._generate_vol_info(None) self.driver.create_volume(vol2) self.assertEqual( # lun id shoud be sequential 2, self.driver._map_vdisk_to_host(vol2['name'], self.connector)) # case 3: test if already mapped self.assertEqual( 1, self.driver._map_vdisk_to_host(vol1['name'], self.connector)) # clean environment self.driver._unmap_vdisk_from_host(vol1['name'], self.connector) self.driver._unmap_vdisk_from_host(vol2['name'], self.connector) self.driver.delete_volume(vol1) self.driver.delete_volume(vol2) # case 4: If there is no vdisk mapped to host, host should be removed self.assertIsNone(self.driver._get_host_from_connector(self.connector)) def test_terminate_connection_with_normal_path(self): connector = {'host': 'flashsystem-host', 'wwnns': ['10000090fa17311e', '10000090fa17311f'], 'wwpns': ['20000090fa17311e', '20000090fa17311f'], 'initiator': 'iqn.1993-08.org.debian:01:89ad29bbdc43'} # create test volume volume_iscsi = self._generate_vol_info(None) self.driver.create_volume(volume_iscsi) # normal connection test self.driver.initialize_connection(volume_iscsi, connector) host = self.driver._get_host_from_connector(connector) self.assertIsNotNone(host) self.driver.terminate_connection(volume_iscsi, connector) host = self.driver._get_host_from_connector(connector) self.assertIsNone(host) # clean environment self.driver.delete_volume(volume_iscsi) def test_terminate_connection_with_resource_leak_check(self): connector = {'host': 'flashsystem-host', 'wwnns': ['10000090fa17311e', '10000090fa17311f'], 'wwpns': ['20000090fa17311e', '20000090fa17311f'], 'initiator': 'iqn.1993-08.org.debian:01:89ad29bbdc43'} # create test volume volume_iscsi = self._generate_vol_info(None) self.driver.create_volume(volume_iscsi) # volume mapping removed before terminate connection self.driver.initialize_connection(volume_iscsi, connector) host = self.driver._get_host_from_connector(connector) self.assertIsNotNone(host) rmmap_cmd = {'host': host, 'obj': volume_iscsi['name']} self.sim._cmd_rmvdiskhostmap(**rmmap_cmd) self.driver.terminate_connection(volume_iscsi, connector) host = self.driver._get_host_from_connector(connector) self.assertIsNone(host) # clean environment self.driver.delete_volume(volume_iscsi) def test_flashsystem_find_host_exhaustive(self): # case 1: create host and find it self.sim.set_protocol('iSCSI') self._set_flag('flashsystem_connection_protocol', 'iSCSI') conn1 = { 'host': 'flashsystem-01', 'wwnns': ['1111111111abcdef', '1111111111abcdeg'], 'wwpns': ['1111111111000001', '1111111111000002'], 'initiator': 'iqn.111111'} conn2 = { 'host': 'flashsystem-02', 'wwnns': ['2222222222abcdef', '2222222222abcdeg'], 'wwpns': ['2222222222000001', '2222222222000002'], 'initiator': 'iqn.222222'} conn3 = { 'host': 'flashsystem-03', 'wwnns': ['3333333333abcdef', '3333333333abcdeg'], 'wwpns': ['3333333333000001', '3333333333000002'], 'initiator': 'iqn.333333'} host1 = self.driver._create_host(conn1) host2 = self.driver._create_host(conn2) self.assertEqual( host2, self.driver._find_host_exhaustive(conn2, [host1, host2])) self.assertIsNone(self.driver._find_host_exhaustive(conn3, [host1, host2])) # case 2: hosts contains non-existent host info with mock.patch.object(FlashSystemFakeISCSIDriver, '_ssh') as mock_ssh: mock_ssh.return_value = ("pass", "") self.driver._find_host_exhaustive(conn1, [host2]) self.assertFalse(mock_ssh.called) # clear environment self.driver._delete_host(host1) self.driver._delete_host(host2) self.sim.set_protocol('iSCSI') self._reset_flags() def test_flashsystem_manage_existing(self): # case 1: manage a vdisk good path kwargs = {'name': u'unmanage-vol-01', 'size': u'1', 'unit': 'gb'} self.sim._cmd_mkvdisk(**kwargs) vol1 = self._generate_vol_info(None) existing_ref = {'source-name': u'unmanage-vol-01'} self.driver.manage_existing(vol1, existing_ref) self.driver.delete_volume(vol1) # case 2: manage a vdisk not exist vol1 = self._generate_vol_info(None) existing_ref = {'source-name': u'unmanage-vol-01'} self.assertRaises(exception.ManageExistingInvalidReference, self.driver.manage_existing, vol1, existing_ref) # case 3: manage a vdisk without name and uid kwargs = {'name': u'unmanage-vol-01', 'size': u'1', 'unit': 'gb'} self.sim._cmd_mkvdisk(**kwargs) vol1 = self._generate_vol_info(None) existing_ref = {} self.assertRaises(exception.ManageExistingInvalidReference, self.driver.manage_existing, vol1, existing_ref) vdisk1 = {'obj': u'unmanage-vol-01'} self.sim._cmd_rmvdisk(**vdisk1) @mock.patch.object(flashsystem_iscsi.FlashSystemISCSIDriver, '_get_vdiskhost_mappings') def test_flashsystem_manage_existing_get_size_mapped( self, _get_vdiskhost_mappings_mock): # case 2: manage a vdisk with mappings _get_vdiskhost_mappings_mock.return_value = {'mapped': u'yes'} kwargs = {'name': u'unmanage-vol-01', 'size': u'1', 'unit': 'gb'} self.sim._cmd_mkvdisk(**kwargs) vol1 = self._generate_vol_info(None) existing_ref = {'source-name': u'unmanage-vol-01'} self.assertRaises(exception.ManageExistingInvalidReference, self.driver.manage_existing_get_size, vol1, existing_ref) # clean environment vdisk1 = {'obj': u'unmanage-vol-01'} self.sim._cmd_rmvdisk(**vdisk1) def test_flashsystem_manage_existing_get_size_bad_ref(self): # bad existing_ref vol1 = self._generate_vol_info(None, None) existing_ref = {} self.assertRaises(exception.ManageExistingInvalidReference, self.driver.manage_existing_get_size, vol1, existing_ref) def test_flashsystem_manage_existing_get_size_vdisk_not_exist(self): # vdisk not exist vol1 = self._generate_vol_info(None) existing_ref = {'source-name': u'unmanage-vol-01'} self.assertRaises(exception.ManageExistingInvalidReference, self.driver.manage_existing_get_size, vol1, existing_ref) def test_flashsystem_manage_existing_get_size(self): # good path kwargs = {'name': u'unmanage-vol-01', 'size': u'10001', 'unit': 'gb'} self.sim._cmd_mkvdisk(**kwargs) vol1 = self._generate_vol_info(None) existing_ref = {'source-name': u'unmanage-vol-01'} vdisk_size = self.driver.manage_existing_get_size(vol1, existing_ref) self.assertEqual(10001, vdisk_size) # clean environment vdisk1 = {'obj': u'unmanage-vol-01'} self.sim._cmd_rmvdisk(**vdisk1)