Huawei: Add manage/unmanage volume support

Add manage/unmanage volume support for Huawei
drivers. Also implement the required
manage_existing_get_size function.

DocImpact
Implements: blueprint huawei-support-manage-volume
Change-Id: I964d49b9979b710bca445c2d209099dcea64d3da
This commit is contained in:
Wilson Liu 2015-10-30 12:43:43 +08:00
parent 49507b61cb
commit d770183749
5 changed files with 544 additions and 114 deletions

View File

@ -13,9 +13,11 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
"""Tests for huawei drivers.""" """Tests for huawei drivers."""
import ddt
import json import json
import mock import mock
import os import os
import re
import shutil import shutil
import tempfile import tempfile
import time import time
@ -1363,9 +1365,6 @@ MAP_COMMAND_TO_FAKE_RESPONSE['/fc_initiator?range=[0-100]&PARENTID=1/GET'] = (
MAP_COMMAND_TO_FAKE_RESPONSE['/fc_initiator?PARENTTYPE=21&PARENTID=1/GET'] = ( MAP_COMMAND_TO_FAKE_RESPONSE['/fc_initiator?PARENTTYPE=21&PARENTID=1/GET'] = (
FAKE_GET_FC_PORT_RESPONSE) FAKE_GET_FC_PORT_RESPONSE)
MAP_COMMAND_TO_FAKE_RESPONSE['/system/'] = (
FAKE_SYSTEM_VERSION_RESPONSE)
MAP_COMMAND_TO_FAKE_RESPONSE['/SMARTCACHEPARTITION/0/GET'] = ( MAP_COMMAND_TO_FAKE_RESPONSE['/SMARTCACHEPARTITION/0/GET'] = (
FAKE_SMARTCACHEPARTITION_RESPONSE) FAKE_SMARTCACHEPARTITION_RESPONSE)
@ -1390,6 +1389,12 @@ MAP_COMMAND_TO_FAKE_RESPONSE['/HyperMetroPair/disable_hcpair/PUT'] = (
MAP_COMMAND_TO_FAKE_RESPONSE['/HyperMetroPair/11/DELETE'] = ( MAP_COMMAND_TO_FAKE_RESPONSE['/HyperMetroPair/11/DELETE'] = (
FAKE_COMMON_SUCCESS_RESPONSE) 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): def Fake_sleep(time):
pass pass
@ -1499,6 +1504,7 @@ class FakeFCStorage(huawei_driver.HuaweiFCDriver):
self.restclient = FakeClient(configuration=self.configuration) self.restclient = FakeClient(configuration=self.configuration)
@ddt.ddt
class HuaweiISCSIDriverTestCase(test.TestCase): class HuaweiISCSIDriverTestCase(test.TestCase):
def setUp(self): def setUp(self):
@ -1516,6 +1522,7 @@ class HuaweiISCSIDriverTestCase(test.TestCase):
driver = FakeISCSIStorage(configuration=self.configuration) driver = FakeISCSIStorage(configuration=self.configuration)
self.driver = driver self.driver = driver
self.driver.do_setup() self.driver.do_setup()
self.device_id = self.driver.restclient.login()
self.portgroup = 'portgroup-test' self.portgroup = 'portgroup-test'
self.iscsi_iqns = ['iqn.2006-08.com.huawei:oceanstor:21000022a:' self.iscsi_iqns = ['iqn.2006-08.com.huawei:oceanstor:21000022a:'
':20503:192.168.1.1', ':20503:192.168.1.1',
@ -1526,12 +1533,9 @@ class HuaweiISCSIDriverTestCase(test.TestCase):
self.portgroup_id = 11 self.portgroup_id = 11
def test_login_success(self): def test_login_success(self):
device_id = self.driver.restclient.login() self.assertEqual('210235G7J20000000000', self.device_id)
self.assertEqual('210235G7J20000000000', device_id)
def test_create_volume_success(self): def test_create_volume_success(self):
self.driver.restclient.login()
# Have pool info in the volume. # Have pool info in the volume.
test_volume = {'name': 'volume-21ec7341-9256-497b-97d9-ef48edcf0635', test_volume = {'name': 'volume-21ec7341-9256-497b-97d9-ef48edcf0635',
'size': 2, 'size': 2,
@ -1567,12 +1571,10 @@ class HuaweiISCSIDriverTestCase(test.TestCase):
self.assertEqual('1', lun_info['provider_location']) self.assertEqual('1', lun_info['provider_location'])
def test_delete_volume_success(self): def test_delete_volume_success(self):
self.driver.restclient.login()
delete_flag = self.driver.delete_volume(test_volume) delete_flag = self.driver.delete_volume(test_volume)
self.assertTrue(delete_flag) self.assertTrue(delete_flag)
def test_create_snapshot_success(self): def test_create_snapshot_success(self):
self.driver.restclient.login()
lun_info = self.driver.create_snapshot(test_snap) lun_info = self.driver.create_snapshot(test_snap)
self.assertEqual(11, lun_info['provider_location']) self.assertEqual(11, lun_info['provider_location'])
@ -1585,35 +1587,29 @@ class HuaweiISCSIDriverTestCase(test.TestCase):
self.assertEqual(11, lun_info['provider_location']) self.assertEqual(11, lun_info['provider_location'])
def test_delete_snapshot_success(self): def test_delete_snapshot_success(self):
self.driver.restclient.login()
delete_flag = self.driver.delete_snapshot(test_snap) delete_flag = self.driver.delete_snapshot(test_snap)
self.assertTrue(delete_flag) self.assertTrue(delete_flag)
def test_create_volume_from_snapsuccess(self): def test_create_volume_from_snapsuccess(self):
self.driver.restclient.login()
lun_info = self.driver.create_volume_from_snapshot(test_volume, lun_info = self.driver.create_volume_from_snapshot(test_volume,
test_volume) test_volume)
self.assertEqual('1', lun_info['ID']) self.assertEqual('1', lun_info['ID'])
def test_initialize_connection_success(self): def test_initialize_connection_success(self):
self.driver.restclient.login()
iscsi_properties = self.driver.initialize_connection(test_volume, iscsi_properties = self.driver.initialize_connection(test_volume,
FakeConnector) FakeConnector)
self.assertEqual(1, iscsi_properties['data']['target_lun']) self.assertEqual(1, iscsi_properties['data']['target_lun'])
def test_terminate_connection_success(self): def test_terminate_connection_success(self):
self.driver.restclient.login()
self.driver.restclient.terminateFlag = True self.driver.restclient.terminateFlag = True
self.driver.terminate_connection(test_volume, FakeConnector) self.driver.terminate_connection(test_volume, FakeConnector)
self.assertTrue(self.driver.restclient.terminateFlag) self.assertTrue(self.driver.restclient.terminateFlag)
def test_get_volume_status(self): def test_get_volume_status(self):
self.driver.restclient.login()
data = self.driver.get_volume_stats() 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): def test_extend_volume(self):
self.driver.restclient.login()
lun_info = self.driver.extend_volume(test_volume, 3) lun_info = self.driver.extend_volume(test_volume, 3)
self.assertEqual('1', lun_info['provider_location']) self.assertEqual('1', lun_info['provider_location'])
@ -1623,31 +1619,26 @@ class HuaweiISCSIDriverTestCase(test.TestCase):
self.driver.restclient.login) self.driver.restclient.login)
def test_create_snapshot_fail(self): def test_create_snapshot_fail(self):
self.driver.restclient.login()
self.driver.restclient.test_fail = True self.driver.restclient.test_fail = True
self.assertRaises(exception.VolumeBackendAPIException, self.assertRaises(exception.VolumeBackendAPIException,
self.driver.create_snapshot, test_snap) self.driver.create_snapshot, test_snap)
def test_create_volume_fail(self): def test_create_volume_fail(self):
self.driver.restclient.login()
self.driver.restclient.test_fail = True self.driver.restclient.test_fail = True
self.assertRaises(exception.VolumeBackendAPIException, self.assertRaises(exception.VolumeBackendAPIException,
self.driver.create_volume, test_volume) self.driver.create_volume, test_volume)
def test_delete_volume_fail(self): def test_delete_volume_fail(self):
self.driver.restclient.login()
self.driver.restclient.test_fail = True self.driver.restclient.test_fail = True
delete_flag = self.driver.delete_volume(test_volume) delete_flag = self.driver.delete_volume(test_volume)
self.assertTrue(delete_flag) self.assertTrue(delete_flag)
def test_delete_snapshot_fail(self): def test_delete_snapshot_fail(self):
self.driver.restclient.login()
self.driver.restclient.test_fail = True self.driver.restclient.test_fail = True
delete_flag = self.driver.delete_volume(test_snap) delete_flag = self.driver.delete_volume(test_snap)
self.assertTrue(delete_flag) self.assertTrue(delete_flag)
def test_initialize_connection_fail(self): def test_initialize_connection_fail(self):
self.driver.restclient.login()
self.driver.restclient.test_fail = True self.driver.restclient.test_fail = True
self.assertRaises(exception.VolumeBackendAPIException, self.assertRaises(exception.VolumeBackendAPIException,
@ -1664,14 +1655,12 @@ class HuaweiISCSIDriverTestCase(test.TestCase):
self.assertEqual(2, result) self.assertEqual(2, result)
def test_lun_is_associated_to_lungroup(self): def test_lun_is_associated_to_lungroup(self):
self.driver.restclient.login()
self.driver.restclient.associate_lun_to_lungroup('11', '11') self.driver.restclient.associate_lun_to_lungroup('11', '11')
result = self.driver.restclient._is_lun_associated_to_lungroup('11', result = self.driver.restclient._is_lun_associated_to_lungroup('11',
'11') '11')
self.assertTrue(result) self.assertTrue(result)
def test_lun_is_not_associated_to_lun_group(self): 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.associate_lun_to_lungroup('12', '12')
self.driver.restclient.remove_lun_from_lungroup('12', '12') self.driver.restclient.remove_lun_from_lungroup('12', '12')
result = self.driver.restclient._is_lun_associated_to_lungroup('12', result = self.driver.restclient._is_lun_associated_to_lungroup('12',
@ -1679,13 +1668,11 @@ class HuaweiISCSIDriverTestCase(test.TestCase):
self.assertFalse(result) self.assertFalse(result)
def test_get_tgtip(self): def test_get_tgtip(self):
self.driver.restclient.login()
portg_id = self.driver.restclient.find_tgt_port_group(self.portgroup) portg_id = self.driver.restclient.find_tgt_port_group(self.portgroup)
target_ip = self.driver.restclient._get_tgt_ip_from_portgroup(portg_id) target_ip = self.driver.restclient._get_tgt_ip_from_portgroup(portg_id)
self.assertEqual(self.target_ips, target_ip) self.assertEqual(self.target_ips, target_ip)
def test_get_iscsi_params(self): def test_get_iscsi_params(self):
self.driver.restclient.login()
(iscsi_iqns, target_ips, portgroup_id) = ( (iscsi_iqns, target_ips, portgroup_id) = (
self.driver.restclient.get_iscsi_params(self.xml_file_path, self.driver.restclient.get_iscsi_params(self.xml_file_path,
FakeConnector)) FakeConnector))
@ -1694,7 +1681,6 @@ class HuaweiISCSIDriverTestCase(test.TestCase):
self.assertEqual(self.portgroup_id, portgroup_id) self.assertEqual(self.portgroup_id, portgroup_id)
def test_get_lun_conf_params(self): def test_get_lun_conf_params(self):
self.driver.restclient.login()
luninfo = huawei_utils.get_lun_conf_params(self.xml_file_path) luninfo = huawei_utils.get_lun_conf_params(self.xml_file_path)
luninfo['pool_id'] = '0' luninfo['pool_id'] = '0'
luninfo['volume_size'] = 2 luninfo['volume_size'] = 2
@ -1704,25 +1690,21 @@ class HuaweiISCSIDriverTestCase(test.TestCase):
self.assertEqual('5mFHcBv4RkCcD+JyrWc0SA', luninfo['NAME']) self.assertEqual('5mFHcBv4RkCcD+JyrWc0SA', luninfo['NAME'])
def tset_get_iscsi_conf(self): def tset_get_iscsi_conf(self):
self.driver.restclient.login()
iscsiinfo = huawei_utils.get_iscsi_conf(self.xml_file_path) iscsiinfo = huawei_utils.get_iscsi_conf(self.xml_file_path)
self.assertEqual('iqn.1993-08.debian:01:ec2bff7ac3a3', self.assertEqual('iqn.1993-08.debian:01:ec2bff7ac3a3',
iscsiinfo['Initiator']) iscsiinfo['Initiator'])
def test_check_conf_file(self): def test_check_conf_file(self):
self.driver.restclient.login()
self.driver.restclient.checkFlag = True self.driver.restclient.checkFlag = True
huawei_utils.check_conf_file(self.xml_file_path) huawei_utils.check_conf_file(self.xml_file_path)
self.assertTrue(self.driver.restclient.checkFlag) self.assertTrue(self.driver.restclient.checkFlag)
def test_get_conf_host_os_type(self): 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', host_os = huawei_utils.get_conf_host_os_type('100.97.10.30',
self.configuration) self.configuration)
self.assertEqual('0', host_os) self.assertEqual('0', host_os)
def test_find_chap_info(self): def test_find_chap_info(self):
self.driver.restclient.login()
tmp_dict = {} tmp_dict = {}
iscsi_info = {} iscsi_info = {}
tmp_dict['Name'] = 'iqn.1993-08.debian:01:ec2bff7ac3a3' tmp_dict['Name'] = 'iqn.1993-08.debian:01:ec2bff7ac3a3'
@ -1737,7 +1719,6 @@ class HuaweiISCSIDriverTestCase(test.TestCase):
self.assertEqual('mm-user@storage', chap_password) self.assertEqual('mm-user@storage', chap_password)
def test_find_alua_info(self): def test_find_alua_info(self):
self.driver.restclient.login()
tmp_dict = {} tmp_dict = {}
iscsi_info = {} iscsi_info = {}
tmp_dict['Name'] = 'iqn.1993-08.debian:01:ec2bff7ac3a3' tmp_dict['Name'] = 'iqn.1993-08.debian:01:ec2bff7ac3a3'
@ -1750,7 +1731,6 @@ class HuaweiISCSIDriverTestCase(test.TestCase):
self.assertEqual('1', type) self.assertEqual('1', type)
def test_find_pool_info(self): def test_find_pool_info(self):
self.driver.restclient.login()
pools = { pools = {
"error": {"code": 0}, "error": {"code": 0},
"data": [{ "data": [{
@ -1791,7 +1771,6 @@ class HuaweiISCSIDriverTestCase(test.TestCase):
self.assertEqual(test_info, pool_info) self.assertEqual(test_info, pool_info)
def test_get_smartx_specs_opts(self): def test_get_smartx_specs_opts(self):
self.driver.restclient.login()
smartx_opts = smartx.SmartX().get_smartx_specs_opts(smarttier_opts) smartx_opts = smartx.SmartX().get_smartx_specs_opts(smarttier_opts)
self.assertEqual('3', smartx_opts['policy']) self.assertEqual('3', smartx_opts['policy'])
@ -1799,7 +1778,6 @@ class HuaweiISCSIDriverTestCase(test.TestCase):
return_value={'MAXIOPS': '100', return_value={'MAXIOPS': '100',
'IOType': '2'}) 'IOType': '2'})
def test_create_smartqos(self, mock_qos_value): def test_create_smartqos(self, mock_qos_value):
self.driver.restclient.login()
lun_info = self.driver.create_volume(test_volume) lun_info = self.driver.create_volume(test_volume)
self.assertEqual('1', lun_info['provider_location']) self.assertEqual('1', lun_info['provider_location'])
@ -1814,12 +1792,10 @@ class HuaweiISCSIDriverTestCase(test.TestCase):
'cachename': 'cache-test', 'cachename': 'cache-test',
'partitionname': 'partition-test'}) 'partitionname': 'partition-test'})
def test_creat_smartx(self, mock_volume_types, mock_add_lun_to_partition): 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) lun_info = self.driver.create_volume(test_volume)
self.assertEqual('1', lun_info['provider_location']) self.assertEqual('1', lun_info['provider_location'])
def test_find_available_qos(self): def test_find_available_qos(self):
self.driver.restclient.login()
qos = {'MAXIOPS': '100', 'IOType': '2'} qos = {'MAXIOPS': '100', 'IOType': '2'}
fake_qos_info_response_equal = { fake_qos_info_response_equal = {
"error": { "error": {
@ -1890,7 +1866,6 @@ class HuaweiISCSIDriverTestCase(test.TestCase):
mock_all_pool_info, mock_all_pool_info,
mock_login_return, mock_login_return,
mock_hypermetro_opts): mock_hypermetro_opts):
self.driver.restclient.login()
metadata = {"hypermetro_id": '11', metadata = {"hypermetro_id": '11',
"remote_lun_id": '1'} "remote_lun_id": '1'}
lun_info = self.driver.create_volume(hyper_volume) lun_info = self.driver.create_volume(hyper_volume)
@ -1927,7 +1902,6 @@ class HuaweiISCSIDriverTestCase(test.TestCase):
mock_all_pool_info, mock_all_pool_info,
mock_login_return, mock_login_return,
mock_hypermetro_opts): mock_hypermetro_opts):
self.driver.restclient.login()
mock_hyper_pair_info.side_effect = exception.VolumeBackendAPIException( mock_hyper_pair_info.side_effect = exception.VolumeBackendAPIException(
data='Create hypermetro error.') data='Create hypermetro error.')
self.assertRaises(exception.VolumeBackendAPIException, self.assertRaises(exception.VolumeBackendAPIException,
@ -1956,7 +1930,6 @@ class HuaweiISCSIDriverTestCase(test.TestCase):
mock_check_hyermetro, mock_check_hyermetro,
mock_lun_exit, mock_lun_exit,
mock_login_info): mock_login_info):
self.driver.restclient.login()
result = self.driver.delete_volume(hyper_volume) result = self.driver.delete_volume(hyper_volume)
mock_logout.assert_called_with() mock_logout.assert_called_with()
self.assertTrue(result) self.assertTrue(result)
@ -1981,7 +1954,6 @@ class HuaweiISCSIDriverTestCase(test.TestCase):
mock_check_hyermetro, mock_check_hyermetro,
mock_lun_exit, mock_lun_exit,
mock_login_info): mock_login_info):
self.driver.restclient.login()
mock_delete_hypermetro.side_effect = ( mock_delete_hypermetro.side_effect = (
exception.VolumeBackendAPIException(data='Delete hypermetro ' exception.VolumeBackendAPIException(data='Delete hypermetro '
'error.')) 'error.'))
@ -1989,6 +1961,221 @@ class HuaweiISCSIDriverTestCase(test.TestCase):
self.driver.delete_volume, hyper_volume) self.driver.delete_volume, hyper_volume)
mock_delete_lun.assert_called_with('11') 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): def create_fake_conf_file(self):
"""Create a fake Config file. """Create a fake Config file.
@ -2099,23 +2286,20 @@ class HuaweiFCDriverTestCase(test.TestCase):
driver = FakeFCStorage(configuration=self.configuration) driver = FakeFCStorage(configuration=self.configuration)
self.driver = driver self.driver = driver
self.driver.do_setup() self.driver.do_setup()
self.device_id = self.driver.restclient.login()
def test_login_success(self): def test_login_success(self):
device_id = self.driver.restclient.login() self.assertEqual('210235G7J20000000000', self.device_id)
self.assertEqual('210235G7J20000000000', device_id)
def test_create_volume_success(self): def test_create_volume_success(self):
self.driver.restclient.login()
lun_info = self.driver.create_volume(test_volume) lun_info = self.driver.create_volume(test_volume)
self.assertEqual('1', lun_info['provider_location']) self.assertEqual('1', lun_info['provider_location'])
def test_delete_volume_success(self): def test_delete_volume_success(self):
self.driver.restclient.login()
delete_flag = self.driver.delete_volume(test_volume) delete_flag = self.driver.delete_volume(test_volume)
self.assertTrue(delete_flag) self.assertTrue(delete_flag)
def test_create_snapshot_success(self): def test_create_snapshot_success(self):
self.driver.restclient.login()
lun_info = self.driver.create_snapshot(test_snap) lun_info = self.driver.create_snapshot(test_snap)
self.assertEqual(11, lun_info['provider_location']) self.assertEqual(11, lun_info['provider_location'])
@ -2128,35 +2312,29 @@ class HuaweiFCDriverTestCase(test.TestCase):
self.assertEqual(11, lun_info['provider_location']) self.assertEqual(11, lun_info['provider_location'])
def test_delete_snapshot_success(self): def test_delete_snapshot_success(self):
self.driver.restclient.login()
delete_flag = self.driver.delete_snapshot(test_snap) delete_flag = self.driver.delete_snapshot(test_snap)
self.assertTrue(delete_flag) self.assertTrue(delete_flag)
def test_create_volume_from_snapsuccess(self): def test_create_volume_from_snapsuccess(self):
self.driver.restclient.login()
lun_info = self.driver.create_volume_from_snapshot(test_volume, lun_info = self.driver.create_volume_from_snapshot(test_volume,
test_volume) test_volume)
self.assertEqual('1', lun_info['ID']) self.assertEqual('1', lun_info['ID'])
def test_initialize_connection_success(self): def test_initialize_connection_success(self):
self.driver.restclient.login()
iscsi_properties = self.driver.initialize_connection(test_volume, iscsi_properties = self.driver.initialize_connection(test_volume,
FakeConnector) FakeConnector)
self.assertEqual(1, iscsi_properties['data']['target_lun']) self.assertEqual(1, iscsi_properties['data']['target_lun'])
def test_terminate_connection_success(self): def test_terminate_connection_success(self):
self.driver.restclient.login()
self.driver.restclient.terminateFlag = True self.driver.restclient.terminateFlag = True
self.driver.terminate_connection(test_volume, FakeConnector) self.driver.terminate_connection(test_volume, FakeConnector)
self.assertTrue(self.driver.restclient.terminateFlag) self.assertTrue(self.driver.restclient.terminateFlag)
def test_get_volume_status(self): def test_get_volume_status(self):
self.driver.restclient.login()
data = self.driver.get_volume_stats() 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): def test_extend_volume(self):
self.driver.restclient.login()
lun_info = self.driver.extend_volume(test_volume, 3) lun_info = self.driver.extend_volume(test_volume, 3)
self.assertEqual('1', lun_info['provider_location']) self.assertEqual('1', lun_info['provider_location'])
@ -2166,31 +2344,26 @@ class HuaweiFCDriverTestCase(test.TestCase):
self.driver.restclient.login) self.driver.restclient.login)
def test_create_snapshot_fail(self): def test_create_snapshot_fail(self):
self.driver.restclient.login()
self.driver.restclient.test_fail = True self.driver.restclient.test_fail = True
self.assertRaises(exception.VolumeBackendAPIException, self.assertRaises(exception.VolumeBackendAPIException,
self.driver.create_snapshot, test_snap) self.driver.create_snapshot, test_snap)
def test_create_volume_fail(self): def test_create_volume_fail(self):
self.driver.restclient.login()
self.driver.restclient.test_fail = True self.driver.restclient.test_fail = True
self.assertRaises(exception.VolumeBackendAPIException, self.assertRaises(exception.VolumeBackendAPIException,
self.driver.create_volume, test_volume) self.driver.create_volume, test_volume)
def test_delete_volume_fail(self): def test_delete_volume_fail(self):
self.driver.restclient.login()
self.driver.restclient.test_fail = True self.driver.restclient.test_fail = True
delete_flag = self.driver.delete_volume(test_volume) delete_flag = self.driver.delete_volume(test_volume)
self.assertTrue(delete_flag) self.assertTrue(delete_flag)
def test_delete_snapshot_fail(self): def test_delete_snapshot_fail(self):
self.driver.restclient.login()
self.driver.restclient.test_fail = True self.driver.restclient.test_fail = True
delete_flag = self.driver.delete_snapshot(test_snap) delete_flag = self.driver.delete_snapshot(test_snap)
self.assertTrue(delete_flag) self.assertTrue(delete_flag)
def test_initialize_connection_fail(self): def test_initialize_connection_fail(self):
self.driver.restclient.login()
self.driver.restclient.test_fail = True self.driver.restclient.test_fail = True
self.assertRaises(exception.VolumeBackendAPIException, self.assertRaises(exception.VolumeBackendAPIException,
self.driver.initialize_connection, self.driver.initialize_connection,
@ -2206,14 +2379,12 @@ class HuaweiFCDriverTestCase(test.TestCase):
self.assertEqual(2, result) self.assertEqual(2, result)
def test_lun_is_associated_to_lungroup(self): def test_lun_is_associated_to_lungroup(self):
self.driver.restclient.login()
self.driver.restclient.associate_lun_to_lungroup('11', '11') self.driver.restclient.associate_lun_to_lungroup('11', '11')
result = self.driver.restclient._is_lun_associated_to_lungroup('11', result = self.driver.restclient._is_lun_associated_to_lungroup('11',
'11') '11')
self.assertTrue(result) self.assertTrue(result)
def test_lun_is_not_associated_to_lun_group(self): 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.associate_lun_to_lungroup('12', '12')
self.driver.restclient.remove_lun_from_lungroup('12', '12') self.driver.restclient.remove_lun_from_lungroup('12', '12')
result = self.driver.restclient._is_lun_associated_to_lungroup('12', result = self.driver.restclient._is_lun_associated_to_lungroup('12',
@ -2221,7 +2392,6 @@ class HuaweiFCDriverTestCase(test.TestCase):
self.assertFalse(result) self.assertFalse(result)
def test_get_lun_conf_params(self): def test_get_lun_conf_params(self):
self.driver.restclient.login()
luninfo = huawei_utils.get_lun_conf_params(self.xml_file_path) luninfo = huawei_utils.get_lun_conf_params(self.xml_file_path)
luninfo['pool_id'] = '0' luninfo['pool_id'] = '0'
luninfo['volume_size'] = 2 luninfo['volume_size'] = 2
@ -2231,21 +2401,17 @@ class HuaweiFCDriverTestCase(test.TestCase):
self.assertEqual('5mFHcBv4RkCcD+JyrWc0SA', luninfo['NAME']) self.assertEqual('5mFHcBv4RkCcD+JyrWc0SA', luninfo['NAME'])
def test_check_conf_file(self): def test_check_conf_file(self):
self.driver.restclient.login()
self.driver.restclient.checkFlag = True self.driver.restclient.checkFlag = True
huawei_utils.check_conf_file(self.xml_file_path) huawei_utils.check_conf_file(self.xml_file_path)
self.assertTrue(self.driver.restclient.checkFlag) self.assertTrue(self.driver.restclient.checkFlag)
def test_get_conf_host_os_type(self): 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', host_os = huawei_utils.get_conf_host_os_type('100.97.10.30',
self.configuration) self.configuration)
self.assertEqual('0', host_os) self.assertEqual('0', host_os)
@mock.patch.object(rest_client.RestClient, 'add_lun_to_partition') @mock.patch.object(rest_client.RestClient, 'add_lun_to_partition')
def test_migrate_volume_success(self, mock_add_lun_to_partition): def test_migrate_volume_success(self, mock_add_lun_to_partition):
self.driver.restclient.login()
# Migrate volume without new type. # Migrate volume without new type.
model_update = None model_update = None
moved = False moved = False
@ -2278,7 +2444,6 @@ class HuaweiFCDriverTestCase(test.TestCase):
self.assertEqual(empty_dict, model_update) self.assertEqual(empty_dict, model_update)
def test_migrate_volume_fail(self): def test_migrate_volume_fail(self):
self.driver.restclient.login()
self.driver.restclient.test_fail = True self.driver.restclient.test_fail = True
# Migrate volume without new type. # Migrate volume without new type.
@ -2301,7 +2466,6 @@ class HuaweiFCDriverTestCase(test.TestCase):
test_volume, test_host, new_type) test_volume, test_host, new_type)
def test_check_migration_valid(self): def test_check_migration_valid(self):
self.driver.restclient.login()
is_valid = self.driver._check_migration_valid(test_host, is_valid = self.driver._check_migration_valid(test_host,
test_volume) test_volume)
self.assertTrue(is_valid) self.assertTrue(is_valid)
@ -2359,7 +2523,6 @@ class HuaweiFCDriverTestCase(test.TestCase):
@mock.patch.object(rest_client.RestClient, 'rename_lun') @mock.patch.object(rest_client.RestClient, 'rename_lun')
def test_update_migrated_volume_success(self, mock_rename_lun): def test_update_migrated_volume_success(self, mock_rename_lun):
self.driver.restclient.login()
original_volume = {'id': '21ec7341-9256-497b-97d9-ef48edcf0635'} original_volume = {'id': '21ec7341-9256-497b-97d9-ef48edcf0635'}
current_volume = {'id': '21ec7341-9256-497b-97d9-ef48edcf0636'} current_volume = {'id': '21ec7341-9256-497b-97d9-ef48edcf0636'}
model_update = self.driver.update_migrated_volume(None, model_update = self.driver.update_migrated_volume(None,
@ -2370,7 +2533,6 @@ class HuaweiFCDriverTestCase(test.TestCase):
@mock.patch.object(rest_client.RestClient, 'rename_lun') @mock.patch.object(rest_client.RestClient, 'rename_lun')
def test_update_migrated_volume_fail(self, mock_rename_lun): def test_update_migrated_volume_fail(self, mock_rename_lun):
self.driver.restclient.login()
mock_rename_lun.side_effect = exception.VolumeBackendAPIException( mock_rename_lun.side_effect = exception.VolumeBackendAPIException(
data='Error occurred.') data='Error occurred.')
original_volume = {'id': '21ec7341-9256-497b-97d9-ef48edcf0635'} original_volume = {'id': '21ec7341-9256-497b-97d9-ef48edcf0635'}
@ -2385,28 +2547,24 @@ class HuaweiFCDriverTestCase(test.TestCase):
@mock.patch.object(rest_client.RestClient, 'add_lun_to_partition') @mock.patch.object(rest_client.RestClient, 'add_lun_to_partition')
def test_retype_volume_success(self, mock_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, retype = self.driver.retype(None, test_volume,
test_new_type, None, test_host) test_new_type, None, test_host)
self.assertTrue(retype) self.assertTrue(retype)
def test_retype_volume_cache_fail(self): def test_retype_volume_cache_fail(self):
self.driver.restclient.cache_not_exist = True self.driver.restclient.cache_not_exist = True
self.driver.restclient.login()
self.assertRaises(exception.VolumeBackendAPIException, self.assertRaises(exception.VolumeBackendAPIException,
self.driver.retype, None, self.driver.retype, None,
test_volume, test_new_type, None, test_host) test_volume, test_new_type, None, test_host)
def test_retype_volume_partition_fail(self): def test_retype_volume_partition_fail(self):
self.driver.restclient.partition_not_exist = True self.driver.restclient.partition_not_exist = True
self.driver.restclient.login()
self.assertRaises(exception.VolumeBackendAPIException, self.assertRaises(exception.VolumeBackendAPIException,
self.driver.retype, None, self.driver.retype, None,
test_volume, test_new_type, None, test_host) test_volume, test_new_type, None, test_host)
@mock.patch.object(rest_client.RestClient, 'add_lun_to_partition') @mock.patch.object(rest_client.RestClient, 'add_lun_to_partition')
def test_retype_volume_fail(self, mock_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 = ( mock_add_lun_to_partition.side_effect = (
exception.VolumeBackendAPIException(data='Error occurred.')) exception.VolumeBackendAPIException(data='Error occurred.'))
retype = self.driver.retype(None, test_volume, retype = self.driver.retype(None, test_volume,
@ -2414,7 +2572,6 @@ class HuaweiFCDriverTestCase(test.TestCase):
self.assertFalse(retype) self.assertFalse(retype)
def test_build_ini_targ_map(self): def test_build_ini_targ_map(self):
self.driver.restclient.login()
fake_lookup_service = FCSanLookupService() fake_lookup_service = FCSanLookupService()
fake_lookup_service.get_device_mapping_from_network = mock.Mock( fake_lookup_service.get_device_mapping_from_network = mock.Mock(
return_value=fake_fabric_mapping) return_value=fake_fabric_mapping)
@ -2430,7 +2587,6 @@ class HuaweiFCDriverTestCase(test.TestCase):
self.assertEqual(ini_target_map, init_targ_map) self.assertEqual(ini_target_map, init_targ_map)
def test_filter_port_by_contr(self): def test_filter_port_by_contr(self):
self.driver.restclient.login()
# Six ports in one fabric. # Six ports in one fabric.
ports_in_fabric = ['1', '2', '3', '4', '5', '6'] ports_in_fabric = ['1', '2', '3', '4', '5', '6']
# Ports 1,3,4,7 belonged to controller A # Ports 1,3,4,7 belonged to controller A
@ -2446,13 +2602,11 @@ class HuaweiFCDriverTestCase(test.TestCase):
self.assertEqual(expected_filtered_ports, filtered_ports) self.assertEqual(expected_filtered_ports, filtered_ports)
def test_multi_resturls_success(self): def test_multi_resturls_success(self):
self.driver.restclient.login()
self.driver.restclient.test_multi_url_flag = True self.driver.restclient.test_multi_url_flag = True
lun_info = self.driver.create_volume(test_volume) lun_info = self.driver.create_volume(test_volume)
self.assertEqual('1', lun_info['provider_location']) self.assertEqual('1', lun_info['provider_location'])
def test_get_id_from_result(self): def test_get_id_from_result(self):
self.driver.restclient.login()
result = {} result = {}
name = 'test_name' name = 'test_name'
key = 'NAME' key = 'NAME'
@ -2519,7 +2673,6 @@ class HuaweiFCDriverTestCase(test.TestCase):
mock_volume_ready, mock_volume_ready,
mock_pair_info, mock_pair_info,
mock_logout): mock_logout):
self.driver.restclient.login()
metadata = {"hypermetro_id": '11', metadata = {"hypermetro_id": '11',
"remote_lun_id": '1'} "remote_lun_id": '1'}
lun_info = self.driver.create_volume(hyper_volume) lun_info = self.driver.create_volume(hyper_volume)

View File

@ -46,10 +46,13 @@ ERROR_VOLUME_NOT_EXIST = 1077939726
RELOGIN_ERROR_PASS = [ERROR_VOLUME_NOT_EXIST] RELOGIN_ERROR_PASS = [ERROR_VOLUME_NOT_EXIST]
HYPERMETRO_RUNNSTATUS_STOP = 41 HYPERMETRO_RUNNSTATUS_STOP = 41
HYPERMETRO_RUNNSTATUS_NORMAL = 1 HYPERMETRO_RUNNSTATUS_NORMAL = 1
NO_SPLITMIRROR_LICENSE = 1077950233
NO_MIGRATION_LICENSE = 1073806606
THICK_LUNTYPE = 0 THICK_LUNTYPE = 0
THIN_LUNTYPE = 1 THIN_LUNTYPE = 1
MAX_HOSTNAME_LENGTH = 31 MAX_HOSTNAME_LENGTH = 31
MAX_VOL_DESCRIPTION = 170
OS_TYPE = {'Linux': '0', OS_TYPE = {'Linux': '0',
'Windows': '1', 'Windows': '1',

View File

@ -14,6 +14,7 @@
# under the License. # under the License.
import json import json
import re
import six import six
import uuid import uuid
@ -576,7 +577,6 @@ class HuaweiBaseDriver(driver.VolumeDriver):
'new_type': new_type, 'new_type': new_type,
'diff': diff, 'diff': diff,
'host': host}) 'host': host})
# Check what changes are needed # Check what changes are needed
migration, change_opts, lun_id = self.determine_changes_when_retype( migration, change_opts, lun_id = self.determine_changes_when_retype(
volume, new_type, host) volume, new_type, host)
@ -668,45 +668,18 @@ class HuaweiBaseDriver(driver.VolumeDriver):
} }
lun_info = self.restclient.get_lun_info(lun_id) lun_info = self.restclient.get_lun_info(lun_id)
lun_opts['LUNType'] = int(lun_info['ALLOCTYPE']) lun_opts['LUNType'] = int(lun_info.get('ALLOCTYPE'))
if lun_info['DATATRANSFERPOLICY']: if lun_info.get('DATATRANSFERPOLICY'):
lun_opts['policy'] = lun_info['DATATRANSFERPOLICY'] lun_opts['policy'] = lun_info['DATATRANSFERPOLICY']
if lun_info['SMARTCACHEPARTITIONID']: if lun_info.get('SMARTCACHEPARTITIONID'):
lun_opts['cacheid'] = lun_info['SMARTCACHEPARTITIONID'] lun_opts['cacheid'] = lun_info['SMARTCACHEPARTITIONID']
if lun_info['CACHEPARTITIONID']: if lun_info.get('CACHEPARTITIONID'):
lun_opts['partitionid'] = lun_info['CACHEPARTITIONID'] lun_opts['partitionid'] = lun_info['CACHEPARTITIONID']
return lun_opts return lun_opts
def determine_changes_when_retype(self, volume, new_type, host): def _check_needed_changes(self, lun_id, old_opts, new_opts,
migration = False change_opts, new_type):
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'])
new_cache_id = None new_cache_id = None
new_cache_name = new_opts['cachename'] new_cache_name = new_opts['cachename']
if new_cache_name: if new_cache_name:
@ -764,6 +737,40 @@ class HuaweiBaseDriver(driver.VolumeDriver):
if old_qos != new_qos: if old_qos != new_qos:
change_opts['qos'] = ([old_qos_id, 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: " LOG.debug("Determine changes when retype. Migration: "
"%(migration)s, change_opts: %(change_opts)s.", "%(migration)s, change_opts: %(change_opts)s.",
{'migration': migration, 'change_opts': change_opts}) {'migration': migration, 'change_opts': change_opts})
@ -838,6 +845,210 @@ class HuaweiBaseDriver(driver.VolumeDriver):
self.restclient.delete_luncopy(luncopy_id) 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): class HuaweiISCSIDriver(HuaweiBaseDriver, driver.ISCSIDriver):
"""ISCSI driver for Huawei storage arrays. """ISCSI driver for Huawei storage arrays.
@ -853,9 +1064,10 @@ class HuaweiISCSIDriver(HuaweiBaseDriver, driver.ISCSIDriver):
Volume migration support Volume migration support
Volume retype support Volume retype support
2.0.0 - Rename to HuaweiISCSIDriver 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): def __init__(self, *args, **kwargs):
super(HuaweiISCSIDriver, self).__init__(*args, **kwargs) super(HuaweiISCSIDriver, self).__init__(*args, **kwargs)
@ -1051,9 +1263,10 @@ class HuaweiFCDriver(HuaweiBaseDriver, driver.FibreChannelDriver):
FC zone enhancement FC zone enhancement
Volume hypermetro support Volume hypermetro support
2.0.0 - Rename to HuaweiFCDriver 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): def __init__(self, *args, **kwargs):
super(HuaweiFCDriver, self).__init__(*args, **kwargs) super(HuaweiFCDriver, self).__init__(*args, **kwargs)

View File

@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import ast
import json import json
import six import six
import socket import socket
@ -1653,9 +1654,11 @@ class RestClient(object):
return initiators return initiators
def rename_lun(self, lun_id, new_name): def rename_lun(self, lun_id, new_name, description=None):
url = "/lun/" + lun_id url = "/lun/" + lun_id
data = json.dumps({"NAME": new_name}) data = json.dumps({"NAME": new_name})
if description:
data.update({"DESCRIPTION": description})
result = self.call(url, data, "PUT") result = self.call(url, data, "PUT")
msg = _('Rename lun on array error.') msg = _('Rename lun on array error.')
self._assert_rest_result(result, msg) self._assert_rest_result(result, msg)
@ -1842,3 +1845,59 @@ class RestClient(object):
self._assert_rest_result(result, msg) self._assert_rest_result(result, msg)
if 'data' in result: if 'data' in result:
return result["data"]["AVAILABLEHOSTLUNIDLIST"] return result["data"]["AVAILABLEHOSTLUNIDLIST"]
def get_hypermetro_pairs(self):
url = "/HyperMetroPair?range=[0-100]"
result = self.call(url, None, "GET")
msg = _('Get HyperMetroPair error.')
self._assert_rest_result(result, msg)
return result.get('data', [])
def get_split_mirrors(self):
url = "/splitmirror?range=[0-100]"
result = self.call(url, None, "GET")
if result['error']['code'] == constants.NO_SPLITMIRROR_LICENSE:
msg = _('License is unavailable.')
raise exception.VolumeBackendAPIException(data=msg)
msg = _('Get SplitMirror error.')
self._assert_rest_result(result, msg)
return result.get('data', [])
def get_target_luns(self, id):
url = ("/SPLITMIRRORTARGETLUN/targetLUN?TYPE=228&PARENTID=%s&"
"PARENTTYPE=220") % id
result = self.call(url, None, "GET")
msg = _('Get target LUN of SplitMirror error.')
self._assert_rest_result(result, msg)
target_luns = []
for item in result.get('data', []):
target_luns.append(item.get('ID'))
return target_luns
def get_migration_task(self):
url = "/LUN_MIGRATION?range=[0-100]"
result = self.call(url, None, "GET")
if result['error']['code'] == constants.NO_MIGRATION_LICENSE:
msg = _('License is unavailable.')
raise exception.VolumeBackendAPIException(data=msg)
msg = _('Get migration task error.')
self._assert_rest_result(result, msg)
return result.get('data', [])
def is_lun_in_mirror(self, lun_id):
url = "/lun?range=[0-65535]"
result = self.call(url, None, "GET")
self._assert_rest_result(result, _('Get volume by name error.'))
if 'data' in result:
for item in result['data']:
rss_obj = item.get('HASRSSOBJECT')
if rss_obj:
rss_obj = ast.literal_eval(rss_obj)
if (item.get('ID') == lun_id and
rss_obj.get('LUNMirror') == 'TRUE'):
return True
return False

View File

@ -0,0 +1,2 @@
features:
- Add manage/unmanage volume support for Huawei drivers.