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:
parent
49507b61cb
commit
d770183749
@ -13,9 +13,11 @@
|
||||
# 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
|
||||
@ -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'] = (
|
||||
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)
|
||||
|
||||
@ -1390,6 +1389,12 @@ MAP_COMMAND_TO_FAKE_RESPONSE['/HyperMetroPair/disable_hcpair/PUT'] = (
|
||||
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
|
||||
@ -1499,6 +1504,7 @@ class FakeFCStorage(huawei_driver.HuaweiFCDriver):
|
||||
self.restclient = FakeClient(configuration=self.configuration)
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class HuaweiISCSIDriverTestCase(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
@ -1516,6 +1522,7 @@ class HuaweiISCSIDriverTestCase(test.TestCase):
|
||||
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',
|
||||
@ -1526,12 +1533,9 @@ class HuaweiISCSIDriverTestCase(test.TestCase):
|
||||
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,
|
||||
@ -1567,12 +1571,10 @@ class HuaweiISCSIDriverTestCase(test.TestCase):
|
||||
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'])
|
||||
|
||||
@ -1585,35 +1587,29 @@ class HuaweiISCSIDriverTestCase(test.TestCase):
|
||||
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'])
|
||||
|
||||
@ -1623,31 +1619,26 @@ class HuaweiISCSIDriverTestCase(test.TestCase):
|
||||
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,
|
||||
@ -1664,14 +1655,12 @@ class HuaweiISCSIDriverTestCase(test.TestCase):
|
||||
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',
|
||||
@ -1679,13 +1668,11 @@ class HuaweiISCSIDriverTestCase(test.TestCase):
|
||||
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))
|
||||
@ -1694,7 +1681,6 @@ class HuaweiISCSIDriverTestCase(test.TestCase):
|
||||
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
|
||||
@ -1704,25 +1690,21 @@ class HuaweiISCSIDriverTestCase(test.TestCase):
|
||||
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'
|
||||
@ -1737,7 +1719,6 @@ class HuaweiISCSIDriverTestCase(test.TestCase):
|
||||
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'
|
||||
@ -1750,7 +1731,6 @@ class HuaweiISCSIDriverTestCase(test.TestCase):
|
||||
self.assertEqual('1', type)
|
||||
|
||||
def test_find_pool_info(self):
|
||||
self.driver.restclient.login()
|
||||
pools = {
|
||||
"error": {"code": 0},
|
||||
"data": [{
|
||||
@ -1791,7 +1771,6 @@ class HuaweiISCSIDriverTestCase(test.TestCase):
|
||||
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'])
|
||||
|
||||
@ -1799,7 +1778,6 @@ class HuaweiISCSIDriverTestCase(test.TestCase):
|
||||
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'])
|
||||
|
||||
@ -1814,12 +1792,10 @@ class HuaweiISCSIDriverTestCase(test.TestCase):
|
||||
'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": {
|
||||
@ -1890,7 +1866,6 @@ class HuaweiISCSIDriverTestCase(test.TestCase):
|
||||
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)
|
||||
@ -1927,7 +1902,6 @@ class HuaweiISCSIDriverTestCase(test.TestCase):
|
||||
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,
|
||||
@ -1956,7 +1930,6 @@ class HuaweiISCSIDriverTestCase(test.TestCase):
|
||||
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)
|
||||
@ -1981,7 +1954,6 @@ class HuaweiISCSIDriverTestCase(test.TestCase):
|
||||
mock_check_hyermetro,
|
||||
mock_lun_exit,
|
||||
mock_login_info):
|
||||
self.driver.restclient.login()
|
||||
mock_delete_hypermetro.side_effect = (
|
||||
exception.VolumeBackendAPIException(data='Delete hypermetro '
|
||||
'error.'))
|
||||
@ -1989,6 +1961,221 @@ class HuaweiISCSIDriverTestCase(test.TestCase):
|
||||
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.
|
||||
|
||||
@ -2099,23 +2286,20 @@ class HuaweiFCDriverTestCase(test.TestCase):
|
||||
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'])
|
||||
|
||||
@ -2128,35 +2312,29 @@ class HuaweiFCDriverTestCase(test.TestCase):
|
||||
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'])
|
||||
|
||||
@ -2166,31 +2344,26 @@ class HuaweiFCDriverTestCase(test.TestCase):
|
||||
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,
|
||||
@ -2206,14 +2379,12 @@ class HuaweiFCDriverTestCase(test.TestCase):
|
||||
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',
|
||||
@ -2221,7 +2392,6 @@ class HuaweiFCDriverTestCase(test.TestCase):
|
||||
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
|
||||
@ -2231,21 +2401,17 @@ class HuaweiFCDriverTestCase(test.TestCase):
|
||||
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
|
||||
@ -2278,7 +2444,6 @@ class HuaweiFCDriverTestCase(test.TestCase):
|
||||
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.
|
||||
@ -2301,7 +2466,6 @@ class HuaweiFCDriverTestCase(test.TestCase):
|
||||
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)
|
||||
@ -2359,7 +2523,6 @@ class HuaweiFCDriverTestCase(test.TestCase):
|
||||
|
||||
@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,
|
||||
@ -2370,7 +2533,6 @@ class HuaweiFCDriverTestCase(test.TestCase):
|
||||
|
||||
@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'}
|
||||
@ -2385,28 +2547,24 @@ class HuaweiFCDriverTestCase(test.TestCase):
|
||||
|
||||
@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,
|
||||
@ -2414,7 +2572,6 @@ class HuaweiFCDriverTestCase(test.TestCase):
|
||||
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)
|
||||
@ -2430,7 +2587,6 @@ class HuaweiFCDriverTestCase(test.TestCase):
|
||||
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
|
||||
@ -2446,13 +2602,11 @@ class HuaweiFCDriverTestCase(test.TestCase):
|
||||
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'
|
||||
@ -2519,7 +2673,6 @@ class HuaweiFCDriverTestCase(test.TestCase):
|
||||
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)
|
||||
|
@ -46,10 +46,13 @@ ERROR_VOLUME_NOT_EXIST = 1077939726
|
||||
RELOGIN_ERROR_PASS = [ERROR_VOLUME_NOT_EXIST]
|
||||
HYPERMETRO_RUNNSTATUS_STOP = 41
|
||||
HYPERMETRO_RUNNSTATUS_NORMAL = 1
|
||||
NO_SPLITMIRROR_LICENSE = 1077950233
|
||||
NO_MIGRATION_LICENSE = 1073806606
|
||||
|
||||
THICK_LUNTYPE = 0
|
||||
THIN_LUNTYPE = 1
|
||||
MAX_HOSTNAME_LENGTH = 31
|
||||
MAX_VOL_DESCRIPTION = 170
|
||||
|
||||
OS_TYPE = {'Linux': '0',
|
||||
'Windows': '1',
|
||||
|
@ -14,6 +14,7 @@
|
||||
# under the License.
|
||||
|
||||
import json
|
||||
import re
|
||||
import six
|
||||
import uuid
|
||||
|
||||
@ -576,7 +577,6 @@ class HuaweiBaseDriver(driver.VolumeDriver):
|
||||
'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)
|
||||
@ -668,45 +668,18 @@ class HuaweiBaseDriver(driver.VolumeDriver):
|
||||
}
|
||||
|
||||
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:
|
||||
@ -764,6 +737,40 @@ class HuaweiBaseDriver(driver.VolumeDriver):
|
||||
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})
|
||||
@ -838,6 +845,210 @@ class HuaweiBaseDriver(driver.VolumeDriver):
|
||||
|
||||
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.
|
||||
@ -853,9 +1064,10 @@ class HuaweiISCSIDriver(HuaweiBaseDriver, driver.ISCSIDriver):
|
||||
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)
|
||||
@ -1051,9 +1263,10 @@ class HuaweiFCDriver(HuaweiBaseDriver, driver.FibreChannelDriver):
|
||||
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)
|
||||
|
@ -13,6 +13,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import ast
|
||||
import json
|
||||
import six
|
||||
import socket
|
||||
@ -1653,9 +1654,11 @@ class RestClient(object):
|
||||
|
||||
return initiators
|
||||
|
||||
def rename_lun(self, lun_id, new_name):
|
||||
def rename_lun(self, lun_id, new_name, description=None):
|
||||
url = "/lun/" + lun_id
|
||||
data = json.dumps({"NAME": new_name})
|
||||
if description:
|
||||
data.update({"DESCRIPTION": description})
|
||||
result = self.call(url, data, "PUT")
|
||||
msg = _('Rename lun on array error.')
|
||||
self._assert_rest_result(result, msg)
|
||||
@ -1842,3 +1845,59 @@ class RestClient(object):
|
||||
self._assert_rest_result(result, msg)
|
||||
if 'data' in result:
|
||||
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
|
||||
|
@ -0,0 +1,2 @@
|
||||
features:
|
||||
- Add manage/unmanage volume support for Huawei drivers.
|
Loading…
Reference in New Issue
Block a user