# License for the specific language governing permissions and limitations
# under the License.
"""Tests for huawei drivers."""
+import ddt
import json
import mock
import os
+import re
import shutil
import tempfile
import time
MAP_COMMAND_TO_FAKE_RESPONSE['/fc_initiator?PARENTTYPE=21&PARENTID=1/GET'] = (
FAKE_GET_FC_PORT_RESPONSE)
-MAP_COMMAND_TO_FAKE_RESPONSE['/system/'] = (
- FAKE_SYSTEM_VERSION_RESPONSE)
-
MAP_COMMAND_TO_FAKE_RESPONSE['/SMARTCACHEPARTITION/0/GET'] = (
FAKE_SMARTCACHEPARTITION_RESPONSE)
MAP_COMMAND_TO_FAKE_RESPONSE['/HyperMetroPair/11/DELETE'] = (
FAKE_COMMON_SUCCESS_RESPONSE)
+MAP_COMMAND_TO_FAKE_RESPONSE['/HyperMetroPair?range=[0-100]/GET'] = (
+ FAKE_COMMON_SUCCESS_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['/splitmirror?range=[0-100]/GET'] = (
+ FAKE_COMMON_SUCCESS_RESPONSE)
+
def Fake_sleep(time):
pass
self.restclient = FakeClient(configuration=self.configuration)
+@ddt.ddt
class HuaweiISCSIDriverTestCase(test.TestCase):
def setUp(self):
driver = FakeISCSIStorage(configuration=self.configuration)
self.driver = driver
self.driver.do_setup()
+ self.device_id = self.driver.restclient.login()
self.portgroup = 'portgroup-test'
self.iscsi_iqns = ['iqn.2006-08.com.huawei:oceanstor:21000022a:'
':20503:192.168.1.1',
self.portgroup_id = 11
def test_login_success(self):
- device_id = self.driver.restclient.login()
- self.assertEqual('210235G7J20000000000', device_id)
+ self.assertEqual('210235G7J20000000000', self.device_id)
def test_create_volume_success(self):
- self.driver.restclient.login()
-
# Have pool info in the volume.
test_volume = {'name': 'volume-21ec7341-9256-497b-97d9-ef48edcf0635',
'size': 2,
self.assertEqual('1', lun_info['provider_location'])
def test_delete_volume_success(self):
- self.driver.restclient.login()
delete_flag = self.driver.delete_volume(test_volume)
self.assertTrue(delete_flag)
def test_create_snapshot_success(self):
- self.driver.restclient.login()
lun_info = self.driver.create_snapshot(test_snap)
self.assertEqual(11, lun_info['provider_location'])
self.assertEqual(11, lun_info['provider_location'])
def test_delete_snapshot_success(self):
- self.driver.restclient.login()
delete_flag = self.driver.delete_snapshot(test_snap)
self.assertTrue(delete_flag)
def test_create_volume_from_snapsuccess(self):
- self.driver.restclient.login()
lun_info = self.driver.create_volume_from_snapshot(test_volume,
test_volume)
self.assertEqual('1', lun_info['ID'])
def test_initialize_connection_success(self):
- self.driver.restclient.login()
iscsi_properties = self.driver.initialize_connection(test_volume,
FakeConnector)
self.assertEqual(1, iscsi_properties['data']['target_lun'])
def test_terminate_connection_success(self):
- self.driver.restclient.login()
self.driver.restclient.terminateFlag = True
self.driver.terminate_connection(test_volume, FakeConnector)
self.assertTrue(self.driver.restclient.terminateFlag)
def test_get_volume_status(self):
- self.driver.restclient.login()
data = self.driver.get_volume_stats()
- self.assertEqual('2.0.0', data['driver_version'])
+ self.assertEqual('2.0.1', data['driver_version'])
def test_extend_volume(self):
- self.driver.restclient.login()
lun_info = self.driver.extend_volume(test_volume, 3)
self.assertEqual('1', lun_info['provider_location'])
self.driver.restclient.login)
def test_create_snapshot_fail(self):
- self.driver.restclient.login()
self.driver.restclient.test_fail = True
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.create_snapshot, test_snap)
def test_create_volume_fail(self):
- self.driver.restclient.login()
self.driver.restclient.test_fail = True
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.create_volume, test_volume)
def test_delete_volume_fail(self):
- self.driver.restclient.login()
self.driver.restclient.test_fail = True
delete_flag = self.driver.delete_volume(test_volume)
self.assertTrue(delete_flag)
def test_delete_snapshot_fail(self):
- self.driver.restclient.login()
self.driver.restclient.test_fail = True
delete_flag = self.driver.delete_volume(test_snap)
self.assertTrue(delete_flag)
def test_initialize_connection_fail(self):
- self.driver.restclient.login()
self.driver.restclient.test_fail = True
self.assertRaises(exception.VolumeBackendAPIException,
self.assertEqual(2, result)
def test_lun_is_associated_to_lungroup(self):
- self.driver.restclient.login()
self.driver.restclient.associate_lun_to_lungroup('11', '11')
result = self.driver.restclient._is_lun_associated_to_lungroup('11',
'11')
self.assertTrue(result)
def test_lun_is_not_associated_to_lun_group(self):
- self.driver.restclient.login()
self.driver.restclient.associate_lun_to_lungroup('12', '12')
self.driver.restclient.remove_lun_from_lungroup('12', '12')
result = self.driver.restclient._is_lun_associated_to_lungroup('12',
self.assertFalse(result)
def test_get_tgtip(self):
- self.driver.restclient.login()
portg_id = self.driver.restclient.find_tgt_port_group(self.portgroup)
target_ip = self.driver.restclient._get_tgt_ip_from_portgroup(portg_id)
self.assertEqual(self.target_ips, target_ip)
def test_get_iscsi_params(self):
- self.driver.restclient.login()
(iscsi_iqns, target_ips, portgroup_id) = (
self.driver.restclient.get_iscsi_params(self.xml_file_path,
FakeConnector))
self.assertEqual(self.portgroup_id, portgroup_id)
def test_get_lun_conf_params(self):
- self.driver.restclient.login()
luninfo = huawei_utils.get_lun_conf_params(self.xml_file_path)
luninfo['pool_id'] = '0'
luninfo['volume_size'] = 2
self.assertEqual('5mFHcBv4RkCcD+JyrWc0SA', luninfo['NAME'])
def tset_get_iscsi_conf(self):
- self.driver.restclient.login()
iscsiinfo = huawei_utils.get_iscsi_conf(self.xml_file_path)
self.assertEqual('iqn.1993-08.debian:01:ec2bff7ac3a3',
iscsiinfo['Initiator'])
def test_check_conf_file(self):
- self.driver.restclient.login()
self.driver.restclient.checkFlag = True
huawei_utils.check_conf_file(self.xml_file_path)
self.assertTrue(self.driver.restclient.checkFlag)
def test_get_conf_host_os_type(self):
- self.driver.restclient.login()
host_os = huawei_utils.get_conf_host_os_type('100.97.10.30',
self.configuration)
self.assertEqual('0', host_os)
def test_find_chap_info(self):
- self.driver.restclient.login()
tmp_dict = {}
iscsi_info = {}
tmp_dict['Name'] = 'iqn.1993-08.debian:01:ec2bff7ac3a3'
self.assertEqual('mm-user@storage', chap_password)
def test_find_alua_info(self):
- self.driver.restclient.login()
tmp_dict = {}
iscsi_info = {}
tmp_dict['Name'] = 'iqn.1993-08.debian:01:ec2bff7ac3a3'
self.assertEqual('1', type)
def test_find_pool_info(self):
- self.driver.restclient.login()
pools = {
"error": {"code": 0},
"data": [{
self.assertEqual(test_info, pool_info)
def test_get_smartx_specs_opts(self):
- self.driver.restclient.login()
smartx_opts = smartx.SmartX().get_smartx_specs_opts(smarttier_opts)
self.assertEqual('3', smartx_opts['policy'])
return_value={'MAXIOPS': '100',
'IOType': '2'})
def test_create_smartqos(self, mock_qos_value):
- self.driver.restclient.login()
lun_info = self.driver.create_volume(test_volume)
self.assertEqual('1', lun_info['provider_location'])
'cachename': 'cache-test',
'partitionname': 'partition-test'})
def test_creat_smartx(self, mock_volume_types, mock_add_lun_to_partition):
- self.driver.restclient.login()
lun_info = self.driver.create_volume(test_volume)
self.assertEqual('1', lun_info['provider_location'])
def test_find_available_qos(self):
- self.driver.restclient.login()
qos = {'MAXIOPS': '100', 'IOType': '2'}
fake_qos_info_response_equal = {
"error": {
mock_all_pool_info,
mock_login_return,
mock_hypermetro_opts):
- self.driver.restclient.login()
metadata = {"hypermetro_id": '11',
"remote_lun_id": '1'}
lun_info = self.driver.create_volume(hyper_volume)
mock_all_pool_info,
mock_login_return,
mock_hypermetro_opts):
- self.driver.restclient.login()
mock_hyper_pair_info.side_effect = exception.VolumeBackendAPIException(
data='Create hypermetro error.')
self.assertRaises(exception.VolumeBackendAPIException,
mock_check_hyermetro,
mock_lun_exit,
mock_login_info):
- self.driver.restclient.login()
result = self.driver.delete_volume(hyper_volume)
mock_logout.assert_called_with()
self.assertTrue(result)
mock_check_hyermetro,
mock_lun_exit,
mock_login_info):
- self.driver.restclient.login()
mock_delete_hypermetro.side_effect = (
exception.VolumeBackendAPIException(data='Delete hypermetro '
'error.'))
self.driver.delete_volume, hyper_volume)
mock_delete_lun.assert_called_with('11')
+ def test_manage_existing_get_size_invalid_reference(self):
+ # Can't find LUN by source-name.
+ external_ref = {'source-name': 'LUN1'}
+ with mock.patch.object(rest_client.RestClient, 'get_volume_by_name',
+ return_value=None):
+ ex = self.assertRaises(exception.ManageExistingInvalidReference,
+ self.driver.manage_existing_get_size,
+ test_volume, external_ref)
+ self.assertIsNotNone(re.search('please check the source-name '
+ 'or source-id', ex.msg))
+
+ # Can't find LUN by source-id.
+ external_ref = {'source-id': 'ID1'}
+ with mock.patch.object(rest_client.RestClient, 'get_lun_info') as m_gt:
+ m_gt.side_effect = exception.VolumeBackendAPIException(
+ data='Error')
+ self.assertRaises(exception.VolumeBackendAPIException,
+ self.driver.manage_existing_get_size,
+ test_volume, external_ref)
+ self.assertIsNotNone(re.search('please check the source-name '
+ 'or source-id', ex.msg))
+
+ def test_manage_existing_get_size_improper_lunsize(self):
+ # LUN size is not multiple of 1 GB.
+ external_ref = {'source-id': 'ID1'}
+ with mock.patch.object(rest_client.RestClient, 'get_lun_info',
+ return_value={'CAPACITY': 2097150}):
+ ex = self.assertRaises(exception.VolumeBackendAPIException,
+ self.driver.manage_existing_get_size,
+ test_volume, external_ref)
+ self.assertIsNotNone(
+ re.search('Volume size must be multiple of 1 GB', ex.msg))
+
+ @ddt.data({'source-id': 'ID1'}, {'source-name': 'LUN1'},
+ {'source-name': 'LUN1', 'source-id': 'ID1'})
+ @mock.patch.object(rest_client.RestClient, 'get_lun_info',
+ return_value={'CAPACITY': 2097152})
+ @mock.patch.object(rest_client.RestClient, 'get_volume_by_name',
+ return_value='ID1')
+ def test_manage_existing_get_size_success(self, mock_get_volume_by_name,
+ mock_get_lun_info, external_ref):
+ size = self.driver.manage_existing_get_size(test_volume,
+ external_ref)
+ self.assertEqual(1, size)
+
+ @mock.patch.object(rest_client.RestClient, 'get_lun_info',
+ return_value={'CAPACITY': 2097152,
+ 'ID': 'ID1',
+ 'PARENTNAME': 'StoragePool001'})
+ @mock.patch.object(rest_client.RestClient, 'get_volume_by_name',
+ return_value='ID1')
+ def test_manage_existing_pool_mismatch(self, mock_get_by_name,
+ mock_get_info):
+ # LUN does not belong to the specified pool.
+ with mock.patch.object(huawei_driver.HuaweiBaseDriver,
+ '_get_lun_by_ref',
+ return_value={'PARENTNAME': 'StoragePool001'}):
+ test_volume = {'host': 'ubuntu-204@v3r3#StoragePool002',
+ 'name': 'volume-21ec7341-9256-497b-97d9-ef48edcf'}
+ external_ref = {'source-name': 'LUN1'}
+ ex = self.assertRaises(exception.ManageExistingInvalidReference,
+ self.driver.manage_existing,
+ test_volume, external_ref)
+ self.assertIsNotNone(re.search('The specified LUN does not belong'
+ ' to the given pool', ex.msg))
+
+ @mock.patch.object(rest_client.RestClient, 'get_lun_info',
+ return_value={'CAPACITY': 2097152,
+ 'ID': 'ID1',
+ 'PARENTNAME': 'StoragePool001'})
+ @mock.patch.object(rest_client.RestClient, 'get_volume_by_name',
+ return_value='ID1')
+ def test_manage_existing_lun_abnormal(self, mock_get_by_name,
+ mock_get_info):
+ # Has snapshot.
+ ret = {'PARENTNAME': "StoragePool001",
+ 'HEALTHSTATUS': '2'}
+ with mock.patch.object(huawei_driver.HuaweiBaseDriver,
+ '_get_lun_by_ref',
+ return_value=ret):
+ test_volume = {'host': 'ubuntu-204@v3r3#StoragePool001',
+ 'name': 'volume-21ec7341-9256-497b-97d9-ef48edcf'}
+ external_ref = {'source-name': 'LUN1'}
+ ex = self.assertRaises(exception.ManageExistingInvalidReference,
+ self.driver.manage_existing,
+ test_volume, external_ref)
+ self.assertIsNotNone(re.search('LUN status is not normal', ex.msg))
+
+ @mock.patch.object(rest_client.RestClient, 'get_hypermetro_pairs',
+ return_value=[{'LOCALOBJID': 'ID1'}])
+ @mock.patch.object(rest_client.RestClient, 'get_lun_info',
+ return_value={'CAPACITY': 2097152,
+ 'ID': 'ID1',
+ 'PARENTNAME': 'StoragePool001',
+ 'HEALTHSTATUS': constants.STATUS_HEALTH})
+ @mock.patch.object(rest_client.RestClient, 'get_volume_by_name',
+ return_value='ID1')
+ def test_manage_existing_with_hypermetro(self, mock_get_by_name,
+ mock_get_info,
+ mock_get_hyper_pairs):
+ test_volume = {'host': 'ubuntu-204@v3r3#StoragePool001',
+ 'name': 'volume-21ec7341-9256-497b-97d9-ef48edcf'}
+ # Exists in a HyperMetroPair.
+ with mock.patch.object(rest_client.RestClient,
+ 'get_hypermetro_pairs',
+ return_value=[{'LOCALOBJID': 'ID1'}]):
+ external_ref = {'source-name': 'LUN1'}
+ ex = self.assertRaises(exception.ManageExistingInvalidReference,
+ self.driver.manage_existing,
+ test_volume, external_ref)
+ self.assertIsNotNone(re.search('HyperMetroPair', ex.msg))
+
+ @ddt.data([[{'PRILUNID': 'ID1'}], []],
+ [[{'PRILUNID': 'ID2'}], ['ID1', 'ID2']])
+ @mock.patch.object(rest_client.RestClient, 'get_lun_info',
+ return_value={'CAPACITY': 2097152,
+ 'ID': 'ID1',
+ 'PARENTNAME': 'StoragePool001',
+ 'HEALTHSTATUS': constants.STATUS_HEALTH})
+ @mock.patch.object(rest_client.RestClient, 'get_volume_by_name',
+ return_value='ID1')
+ def test_manage_existing_with_splitmirror(self, ddt_data, mock_get_by_name,
+ mock_get_info):
+ test_volume = {'host': 'ubuntu-204@v3r3#StoragePool001',
+ 'name': 'volume-21ec7341-9256-497b-97d9-ef48edcf',
+ 'id': '21ec7341-9256-497b-97d9-ef48edcf'}
+ # Exists in a SplitMirror.
+ with mock.patch.object(rest_client.RestClient, 'get_split_mirrors',
+ return_value=ddt_data[0]), \
+ mock.patch.object(rest_client.RestClient, 'get_target_luns',
+ return_value=ddt_data[1]):
+ external_ref = {'source-name': 'LUN1'}
+ ex = self.assertRaises(exception.ManageExistingInvalidReference,
+ self.driver.manage_existing,
+ test_volume, external_ref)
+ self.assertIsNotNone(re.search('SplitMirror', ex.msg))
+
+ @ddt.data([{'PARENTID': 'ID1'}], [{'TARGETLUNID': 'ID1'}])
+ @mock.patch.object(rest_client.RestClient, 'get_lun_info',
+ return_value={'CAPACITY': 2097152,
+ 'ID': 'ID1',
+ 'PARENTNAME': 'StoragePool001',
+ 'HEALTHSTATUS': constants.STATUS_HEALTH})
+ @mock.patch.object(rest_client.RestClient, 'get_volume_by_name',
+ return_value='ID1')
+ def test_manage_existing_under_migration(self, ddt_data, mock_get_by_name,
+ mock_get_info):
+ test_volume = {'host': 'ubuntu-204@v3r3#StoragePool001',
+ 'name': 'volume-21ec7341-9256-497b-97d9-ef48edcf',
+ 'id': '21ec7341-9256-497b-97d9-ef48edcf'}
+ # Exists in a migration task.
+ with mock.patch.object(rest_client.RestClient, 'get_migration_task',
+ return_value=ddt_data):
+ external_ref = {'source-name': 'LUN1'}
+ ex = self.assertRaises(exception.ManageExistingInvalidReference,
+ self.driver.manage_existing,
+ test_volume, external_ref)
+ self.assertIsNotNone(re.search('migration', ex.msg))
+
+ @mock.patch.object(rest_client.RestClient, 'get_lun_info',
+ return_value={'CAPACITY': 2097152,
+ 'ID': 'ID1',
+ 'PARENTNAME': 'StoragePool001',
+ 'SNAPSHOTIDS': [],
+ 'EXPOSEDTOINITIATOR': 'false',
+ 'ISADD2LUNGROUP': 'true',
+ 'HEALTHSTATUS': constants.STATUS_HEALTH})
+ @mock.patch.object(rest_client.RestClient, 'get_volume_by_name',
+ return_value='ID1')
+ def test_manage_existing_with_lungroup(self, mock_get_by_name,
+ mock_get_info):
+ # Already in LUN group.
+ test_volume = {'host': 'ubuntu-204@v3r3#StoragePool001',
+ 'name': 'volume-21ec7341-9256-497b-97d9-ef48edcf'}
+ external_ref = {'source-name': 'LUN1'}
+ ex = self.assertRaises(exception.ManageExistingInvalidReference,
+ self.driver.manage_existing,
+ test_volume, external_ref)
+ self.assertIsNotNone(re.search('Already exists in a LUN group',
+ ex.msg))
+
+ @ddt.data({'source-name': 'LUN1'}, {'source-id': 'ID1'})
+ @mock.patch.object(rest_client.RestClient, 'rename_lun')
+ @mock.patch.object(huawei_driver.HuaweiBaseDriver,
+ '_get_lun_by_ref',
+ return_value={'PARENTNAME': 'StoragePool001',
+ 'SNAPSHOTIDS': [],
+ 'EXPOSEDTOINITIATOR': 'false',
+ 'ID': 'ID1',
+ 'HEALTHSTATUS': constants.STATUS_HEALTH})
+ @mock.patch.object(rest_client.RestClient, 'get_lun_info',
+ return_value={'CAPACITY': 2097152,
+ 'ALLOCTYPE': 1})
+ @mock.patch.object(rest_client.RestClient, 'get_volume_by_name',
+ return_value='ID1')
+ def test_manage_existing_success(self, mock_get_by_name, mock_get_info,
+ mock_check_lun, mock_rename,
+ external_ref):
+ test_volume = {'host': 'ubuntu-204@v3r3#StoragePool001',
+ 'id': '21ec7341-9256-497b-97d9-ef48edcf0635',
+ 'name': 'volume-21ec7341-9256-497b-97d9-ef48edcf'}
+ model_update = self.driver.manage_existing(test_volume,
+ external_ref)
+ self.assertEqual({'provider_location': 'ID1'}, model_update)
+
+ @ddt.data([None, 0], ['ID1', 1])
+ @mock.patch.object(rest_client.RestClient, 'rename_lun')
+ def test_unmanage(self, ddt_data, mock_rename):
+ test_volume = {'host': 'ubuntu-204@v3r3#StoragePool001',
+ 'id': '21ec7341-9256-497b-97d9-ef48edcf0635'}
+ with mock.patch.object(rest_client.RestClient, 'get_volume_by_name',
+ return_value=ddt_data[0]):
+ self.driver.unmanage(test_volume)
+ self.assertEqual(ddt_data[1], mock_rename.call_count)
+
def create_fake_conf_file(self):
"""Create a fake Config file.
driver = FakeFCStorage(configuration=self.configuration)
self.driver = driver
self.driver.do_setup()
+ self.device_id = self.driver.restclient.login()
def test_login_success(self):
- device_id = self.driver.restclient.login()
- self.assertEqual('210235G7J20000000000', device_id)
+ self.assertEqual('210235G7J20000000000', self.device_id)
def test_create_volume_success(self):
- self.driver.restclient.login()
lun_info = self.driver.create_volume(test_volume)
self.assertEqual('1', lun_info['provider_location'])
def test_delete_volume_success(self):
- self.driver.restclient.login()
delete_flag = self.driver.delete_volume(test_volume)
self.assertTrue(delete_flag)
def test_create_snapshot_success(self):
- self.driver.restclient.login()
lun_info = self.driver.create_snapshot(test_snap)
self.assertEqual(11, lun_info['provider_location'])
self.assertEqual(11, lun_info['provider_location'])
def test_delete_snapshot_success(self):
- self.driver.restclient.login()
delete_flag = self.driver.delete_snapshot(test_snap)
self.assertTrue(delete_flag)
def test_create_volume_from_snapsuccess(self):
- self.driver.restclient.login()
lun_info = self.driver.create_volume_from_snapshot(test_volume,
test_volume)
self.assertEqual('1', lun_info['ID'])
def test_initialize_connection_success(self):
- self.driver.restclient.login()
iscsi_properties = self.driver.initialize_connection(test_volume,
FakeConnector)
self.assertEqual(1, iscsi_properties['data']['target_lun'])
def test_terminate_connection_success(self):
- self.driver.restclient.login()
self.driver.restclient.terminateFlag = True
self.driver.terminate_connection(test_volume, FakeConnector)
self.assertTrue(self.driver.restclient.terminateFlag)
def test_get_volume_status(self):
- self.driver.restclient.login()
data = self.driver.get_volume_stats()
- self.assertEqual('2.0.0', data['driver_version'])
+ self.assertEqual('2.0.1', data['driver_version'])
def test_extend_volume(self):
- self.driver.restclient.login()
lun_info = self.driver.extend_volume(test_volume, 3)
self.assertEqual('1', lun_info['provider_location'])
self.driver.restclient.login)
def test_create_snapshot_fail(self):
- self.driver.restclient.login()
self.driver.restclient.test_fail = True
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.create_snapshot, test_snap)
def test_create_volume_fail(self):
- self.driver.restclient.login()
self.driver.restclient.test_fail = True
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.create_volume, test_volume)
def test_delete_volume_fail(self):
- self.driver.restclient.login()
self.driver.restclient.test_fail = True
delete_flag = self.driver.delete_volume(test_volume)
self.assertTrue(delete_flag)
def test_delete_snapshot_fail(self):
- self.driver.restclient.login()
self.driver.restclient.test_fail = True
delete_flag = self.driver.delete_snapshot(test_snap)
self.assertTrue(delete_flag)
def test_initialize_connection_fail(self):
- self.driver.restclient.login()
self.driver.restclient.test_fail = True
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.initialize_connection,
self.assertEqual(2, result)
def test_lun_is_associated_to_lungroup(self):
- self.driver.restclient.login()
self.driver.restclient.associate_lun_to_lungroup('11', '11')
result = self.driver.restclient._is_lun_associated_to_lungroup('11',
'11')
self.assertTrue(result)
def test_lun_is_not_associated_to_lun_group(self):
- self.driver.restclient.login()
self.driver.restclient.associate_lun_to_lungroup('12', '12')
self.driver.restclient.remove_lun_from_lungroup('12', '12')
result = self.driver.restclient._is_lun_associated_to_lungroup('12',
self.assertFalse(result)
def test_get_lun_conf_params(self):
- self.driver.restclient.login()
luninfo = huawei_utils.get_lun_conf_params(self.xml_file_path)
luninfo['pool_id'] = '0'
luninfo['volume_size'] = 2
self.assertEqual('5mFHcBv4RkCcD+JyrWc0SA', luninfo['NAME'])
def test_check_conf_file(self):
- self.driver.restclient.login()
self.driver.restclient.checkFlag = True
huawei_utils.check_conf_file(self.xml_file_path)
self.assertTrue(self.driver.restclient.checkFlag)
def test_get_conf_host_os_type(self):
- self.driver.restclient.login()
host_os = huawei_utils.get_conf_host_os_type('100.97.10.30',
self.configuration)
self.assertEqual('0', host_os)
@mock.patch.object(rest_client.RestClient, 'add_lun_to_partition')
def test_migrate_volume_success(self, mock_add_lun_to_partition):
- self.driver.restclient.login()
-
# Migrate volume without new type.
model_update = None
moved = False
self.assertEqual(empty_dict, model_update)
def test_migrate_volume_fail(self):
- self.driver.restclient.login()
self.driver.restclient.test_fail = True
# Migrate volume without new type.
test_volume, test_host, new_type)
def test_check_migration_valid(self):
- self.driver.restclient.login()
is_valid = self.driver._check_migration_valid(test_host,
test_volume)
self.assertTrue(is_valid)
@mock.patch.object(rest_client.RestClient, 'rename_lun')
def test_update_migrated_volume_success(self, mock_rename_lun):
- self.driver.restclient.login()
original_volume = {'id': '21ec7341-9256-497b-97d9-ef48edcf0635'}
current_volume = {'id': '21ec7341-9256-497b-97d9-ef48edcf0636'}
model_update = self.driver.update_migrated_volume(None,
@mock.patch.object(rest_client.RestClient, 'rename_lun')
def test_update_migrated_volume_fail(self, mock_rename_lun):
- self.driver.restclient.login()
mock_rename_lun.side_effect = exception.VolumeBackendAPIException(
data='Error occurred.')
original_volume = {'id': '21ec7341-9256-497b-97d9-ef48edcf0635'}
@mock.patch.object(rest_client.RestClient, 'add_lun_to_partition')
def test_retype_volume_success(self, mock_add_lun_to_partition):
- self.driver.restclient.login()
retype = self.driver.retype(None, test_volume,
test_new_type, None, test_host)
self.assertTrue(retype)
def test_retype_volume_cache_fail(self):
self.driver.restclient.cache_not_exist = True
- self.driver.restclient.login()
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.retype, None,
test_volume, test_new_type, None, test_host)
def test_retype_volume_partition_fail(self):
self.driver.restclient.partition_not_exist = True
- self.driver.restclient.login()
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.retype, None,
test_volume, test_new_type, None, test_host)
@mock.patch.object(rest_client.RestClient, 'add_lun_to_partition')
def test_retype_volume_fail(self, mock_add_lun_to_partition):
- self.driver.restclient.login()
mock_add_lun_to_partition.side_effect = (
exception.VolumeBackendAPIException(data='Error occurred.'))
retype = self.driver.retype(None, test_volume,
self.assertFalse(retype)
def test_build_ini_targ_map(self):
- self.driver.restclient.login()
fake_lookup_service = FCSanLookupService()
fake_lookup_service.get_device_mapping_from_network = mock.Mock(
return_value=fake_fabric_mapping)
self.assertEqual(ini_target_map, init_targ_map)
def test_filter_port_by_contr(self):
- self.driver.restclient.login()
# Six ports in one fabric.
ports_in_fabric = ['1', '2', '3', '4', '5', '6']
# Ports 1,3,4,7 belonged to controller A
self.assertEqual(expected_filtered_ports, filtered_ports)
def test_multi_resturls_success(self):
- self.driver.restclient.login()
self.driver.restclient.test_multi_url_flag = True
lun_info = self.driver.create_volume(test_volume)
self.assertEqual('1', lun_info['provider_location'])
def test_get_id_from_result(self):
- self.driver.restclient.login()
result = {}
name = 'test_name'
key = 'NAME'
mock_volume_ready,
mock_pair_info,
mock_logout):
- self.driver.restclient.login()
metadata = {"hypermetro_id": '11',
"remote_lun_id": '1'}
lun_info = self.driver.create_volume(hyper_volume)
# under the License.
import json
+import re
import six
import uuid
'new_type': new_type,
'diff': diff,
'host': host})
-
# Check what changes are needed
migration, change_opts, lun_id = self.determine_changes_when_retype(
volume, new_type, host)
}
lun_info = self.restclient.get_lun_info(lun_id)
- lun_opts['LUNType'] = int(lun_info['ALLOCTYPE'])
- if lun_info['DATATRANSFERPOLICY']:
+ lun_opts['LUNType'] = int(lun_info.get('ALLOCTYPE'))
+ if lun_info.get('DATATRANSFERPOLICY'):
lun_opts['policy'] = lun_info['DATATRANSFERPOLICY']
- if lun_info['SMARTCACHEPARTITIONID']:
+ if lun_info.get('SMARTCACHEPARTITIONID'):
lun_opts['cacheid'] = lun_info['SMARTCACHEPARTITIONID']
- if lun_info['CACHEPARTITIONID']:
+ if lun_info.get('CACHEPARTITIONID'):
lun_opts['partitionid'] = lun_info['CACHEPARTITIONID']
return lun_opts
- def determine_changes_when_retype(self, volume, new_type, host):
- migration = False
- change_opts = {
- 'policy': None,
- 'partitionid': None,
- 'cacheid': None,
- 'qos': None,
- 'host': None,
- 'LUNType': None,
- }
-
- lun_id = volume.get('provider_location')
- old_opts = self.get_lun_specs(lun_id)
-
- new_specs = new_type['extra_specs']
- new_opts = huawei_utils._get_extra_spec_value(new_specs)
- new_opts = smartx.SmartX().get_smartx_specs_opts(new_opts)
-
- if 'LUNType' not in new_opts:
- new_opts['LUNType'] = huawei_utils.find_luntype_in_xml(
- self.xml_file_path)
-
- if volume['host'] != host['host']:
- migration = True
- change_opts['host'] = (volume['host'], host['host'])
- if old_opts['LUNType'] != new_opts['LUNType']:
- migration = True
- change_opts['LUNType'] = (old_opts['LUNType'], new_opts['LUNType'])
-
+ def _check_needed_changes(self, lun_id, old_opts, new_opts,
+ change_opts, new_type):
new_cache_id = None
new_cache_name = new_opts['cachename']
if new_cache_name:
if old_qos != new_qos:
change_opts['qos'] = ([old_qos_id, old_qos], new_qos)
+ return change_opts
+
+ def determine_changes_when_retype(self, volume, new_type, host):
+ migration = False
+ change_opts = {
+ 'policy': None,
+ 'partitionid': None,
+ 'cacheid': None,
+ 'qos': None,
+ 'host': None,
+ 'LUNType': None,
+ }
+
+ lun_id = volume.get('provider_location')
+ old_opts = self.get_lun_specs(lun_id)
+
+ new_specs = new_type['extra_specs']
+ new_opts = huawei_utils._get_extra_spec_value(new_specs)
+ new_opts = smartx.SmartX().get_smartx_specs_opts(new_opts)
+
+ if 'LUNType' not in new_opts:
+ new_opts['LUNType'] = huawei_utils.find_luntype_in_xml(
+ self.xml_file_path)
+
+ if volume['host'] != host['host']:
+ migration = True
+ change_opts['host'] = (volume['host'], host['host'])
+ if old_opts['LUNType'] != new_opts['LUNType']:
+ migration = True
+ change_opts['LUNType'] = (old_opts['LUNType'], new_opts['LUNType'])
+
+ change_opts = self._check_needed_changes(lun_id, old_opts, new_opts,
+ change_opts, new_type)
+
LOG.debug("Determine changes when retype. Migration: "
"%(migration)s, change_opts: %(change_opts)s.",
{'migration': migration, 'change_opts': change_opts})
self.restclient.delete_luncopy(luncopy_id)
+ def _check_lun_valid_for_manage(self, lun_info, external_ref):
+ lun_id = lun_info.get('ID')
+ # Check whether the LUN is Normal.
+ if lun_info.get('HEALTHSTATUS') != constants.STATUS_HEALTH:
+ msg = _("Can't import LUN %s to Cinder. LUN status is not "
+ "normal.") % lun_id
+ raise exception.ManageExistingInvalidReference(
+ existing_ref=external_ref, reason=msg)
+
+ # Check whether the LUN exists in a HyperMetroPair.
+ try:
+ hypermetro_pairs = self.restclient.get_hypermetro_pairs()
+ except exception.VolumeBackendAPIException:
+ msg = _("Failed to get HyperMetroPair.")
+ raise exception.ManageExistingInvalidReference(
+ existing_ref=external_ref, reason=msg)
+
+ for pair in hypermetro_pairs:
+ if pair.get('LOCALOBJID') == lun_id:
+ msg = (_("Can't import LUN %s to Cinder. Already exists in a "
+ "HyperMetroPair.") % lun_id)
+ raise exception.ManageExistingInvalidReference(
+ existing_ref=external_ref, reason=msg)
+
+ # Check whether the LUN exists in a SplitMirror.
+ try:
+ split_mirrors = self.restclient.get_split_mirrors()
+ except exception.VolumeBackendAPIException as ex:
+ if re.search('License is unavailable', ex.msg):
+ # Can't check whether the LUN has SplitMirror with it,
+ # just pass the check and log it.
+ split_mirrors = []
+ LOG.warning(_LW('No license for SplitMirror.'))
+ else:
+ msg = _("Failed to get SplitMirror.")
+ raise exception.VolumeBackendAPIException(data=msg)
+
+ for mirror in split_mirrors:
+ try:
+ target_luns = self.restclient.get_target_luns(mirror.get('ID'))
+ except exception.VolumeBackendAPIException:
+ msg = _("Failed to get target LUN of SplitMirror.")
+ raise exception.VolumeBackendAPIException(data=msg)
+
+ if (mirror.get('PRILUNID') == lun_id) or (lun_id in target_luns):
+ msg = (_("Can't import LUN %s to Cinder. Already exists in a "
+ "SplitMirror.") % lun_id)
+ raise exception.ManageExistingInvalidReference(
+ existing_ref=external_ref, reason=msg)
+
+ # Check whether the LUN exists in a migration task.
+ try:
+ migration_tasks = self.restclient.get_migration_task()
+ except exception.VolumeBackendAPIException as ex:
+ if re.search('License is unavailable', ex.msg):
+ # Can't check whether the LUN has migration task with it,
+ # just pass the check and log it.
+ migration_tasks = []
+ LOG.warning(_LW('No license for migration.'))
+ else:
+ msg = _("Failed to get migration task.")
+ raise exception.VolumeBackendAPIException(data=msg)
+
+ for migration in migration_tasks:
+ if lun_id in (migration.get('PARENTID'),
+ migration.get('TARGETLUNID')):
+ msg = (_("Can't import LUN %s to Cinder. Already exists in a "
+ "migration task.") % lun_id)
+ raise exception.ManageExistingInvalidReference(
+ existing_ref=external_ref, reason=msg)
+
+ # Check whether the LUN exists in a LUN copy task.
+ lun_copy = lun_info.get('LUNCOPYIDS')
+ if lun_copy and lun_copy[1:-1]:
+ msg = (_("Can't import LUN %s to Cinder. Already exists in "
+ "a LUN copy task.") % lun_id)
+ raise exception.ManageExistingInvalidReference(
+ existing_ref=external_ref, reason=msg)
+
+ # Check whether the LUN exists in a remote replication task.
+ rmt_replication = lun_info.get('REMOTEREPLICATIONIDS')
+ if rmt_replication and rmt_replication[1:-1]:
+ msg = (_("Can't import LUN %s to Cinder. Already exists in "
+ "a remote replication task.") % lun_id)
+ raise exception.ManageExistingInvalidReference(
+ existing_ref=external_ref, reason=msg)
+
+ # Check whether the LUN exists in a LUN mirror.
+ if self.restclient.is_lun_in_mirror(lun_id):
+ msg = (_("Can't import LUN %s to Cinder. Already exists in "
+ "a LUN mirror.") % lun_id)
+ raise exception.ManageExistingInvalidReference(
+ existing_ref=external_ref, reason=msg)
+
+ # Check whether the LUN has already in LUN group.
+ if lun_info.get('ISADD2LUNGROUP') == 'true':
+ msg = (_("Can't import LUN %s to Cinder. Already exists in a LUN "
+ "group.") % lun_id)
+ raise exception.ManageExistingInvalidReference(
+ existing_ref=external_ref, reason=msg)
+
+ def manage_existing(self, volume, external_ref):
+ """Manage an existing volume on the backend storage."""
+ # Check whether the LUN is belonged to the specified pool.
+ pool = volume_utils.extract_host(volume['host'], 'pool')
+ LOG.debug("Pool specified is: %s.", pool)
+ lun_info = self._get_lun_by_ref(external_ref)
+ lun_id = lun_info.get('ID')
+ description = lun_info.get('DESCRIPTION', '')
+ if len(description) <= (
+ constants.MAX_VOL_DESCRIPTION - len(volume['name']) - 1):
+ description = volume['name'] + ' ' + description
+
+ lun_pool = lun_info.get('PARENTNAME')
+ LOG.debug("Storage pool of existing LUN %(lun)s is %(pool)s.",
+ {"lun": lun_id, "pool": lun_pool})
+ if pool != lun_pool:
+ msg = (_("The specified LUN does not belong to the given "
+ "pool: %s.") % pool)
+ raise exception.ManageExistingInvalidReference(
+ existing_ref=external_ref, reason=msg)
+
+ # Check other stuffs to determine whether this LUN can be imported.
+ self._check_lun_valid_for_manage(lun_info, external_ref)
+ type_id = volume.get('volume_type_id')
+ if type_id:
+ # Handle volume type if specified.
+ old_opts = self.get_lun_specs(lun_id)
+ volume_type = volume_types.get_volume_type(None, type_id)
+ new_specs = volume_type.get('extra_specs')
+ new_opts = huawei_utils._get_extra_spec_value(new_specs)
+ new_opts = smartx.SmartX().get_smartx_specs_opts(new_opts)
+ if ('LUNType' in new_opts and
+ old_opts['LUNType'] != new_opts['LUNType']):
+ msg = (_("Can't import LUN %(lun_id)s to Cinder. "
+ "LUN type mismatched.") % lun_id)
+ raise exception.ManageExistingVolumeTypeMismatch(reason=msg)
+ if volume_type:
+ change_opts = {'policy': None, 'partitionid': None,
+ 'cacheid': None, 'qos': None}
+ change_opts = self._check_needed_changes(lun_id, old_opts,
+ new_opts, change_opts,
+ volume_type)
+ self.modify_lun(lun_id, change_opts)
+
+ # Rename the LUN to make it manageable for Cinder.
+ new_name = huawei_utils.encode_name(volume['id'])
+ LOG.debug("Rename LUN %(old_name)s to %(new_name)s.",
+ {'old_name': lun_info.get('NAME'),
+ 'new_name': new_name})
+ self.restclient.rename_lun(lun_id, new_name, description)
+
+ return {'provider_location': lun_id}
+
+ def _get_lun_by_ref(self, external_ref):
+ LOG.debug("Get external_ref: %s", external_ref)
+ name = external_ref.get('source-name')
+ id = external_ref.get('source-id')
+ if not (name or id):
+ msg = _('Must specify source-name or source-id.')
+ raise exception.ManageExistingInvalidReference(
+ existing_ref=external_ref, reason=msg)
+
+ lun_id = id or self.restclient.get_volume_by_name(name)
+ if not lun_id:
+ msg = _("Can't find LUN on the array, please check the "
+ "source-name or source-id.")
+ raise exception.ManageExistingInvalidReference(
+ existing_ref=external_ref, reason=msg)
+
+ lun_info = self.restclient.get_lun_info(lun_id)
+ return lun_info
+
+ def unmanage(self, volume):
+ """Export Huawei volume from Cinder."""
+ volume_id = volume['id']
+ LOG.debug("Unmanage volume: %s.", volume_id)
+ lun_name = huawei_utils.encode_name(volume_id)
+ lun_id = self.restclient.get_volume_by_name(lun_name)
+ if not lun_id:
+ LOG.error(_LE("Can't find LUN on the array for volume: %s."),
+ volume_id)
+ return
+ new_name = 'unmged_' + lun_name
+ LOG.debug("Rename LUN %(lun_name)s to %(new_name)s.",
+ {'lun_name': lun_name,
+ 'new_name': new_name})
+ try:
+ self.restclient.rename_lun(lun_id, new_name)
+ except Exception:
+ LOG.warning(_LW("Rename lun %(lun_id)s fails when "
+ "unmanaging volume %(volume)s."),
+ {"lun_id": lun_id, "volume": volume['id']})
+
+ def manage_existing_get_size(self, volume, external_ref):
+ """Get the size of the existing volume."""
+ lun_info = self._get_lun_by_ref(external_ref)
+ size = float(lun_info.get('CAPACITY')) // constants.CAPACITY_UNIT
+ remainder = float(lun_info.get('CAPACITY')) % constants.CAPACITY_UNIT
+ if int(remainder) > 0:
+ msg = _("Volume size must be multiple of 1 GB.")
+ raise exception.VolumeBackendAPIException(data=msg)
+ return int(size)
+
class HuaweiISCSIDriver(HuaweiBaseDriver, driver.ISCSIDriver):
"""ISCSI driver for Huawei storage arrays.
Volume migration support
Volume retype support
2.0.0 - Rename to HuaweiISCSIDriver
+ 2.0.1 - Manage/unmanage volume support
"""
- VERSION = "2.0.0"
+ VERSION = "2.0.1"
def __init__(self, *args, **kwargs):
super(HuaweiISCSIDriver, self).__init__(*args, **kwargs)
FC zone enhancement
Volume hypermetro support
2.0.0 - Rename to HuaweiFCDriver
+ 2.0.1 - Manage/unmanage volume support
"""
- VERSION = "2.0.0"
+ VERSION = "2.0.1"
def __init__(self, *args, **kwargs):
super(HuaweiFCDriver, self).__init__(*args, **kwargs)