-# Copyright (c) 2015 Huawei Technologies Co., Ltd.
+# Copyright (c) 2016 Huawei Technologies Co., Ltd.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
import ddt
import json
import mock
-import os
import re
-import shutil
import tempfile
import time
from xml.dom import minidom
from cinder.volume import configuration as conf
from cinder.volume.drivers.huawei import constants
from cinder.volume.drivers.huawei import fc_zone_helper
+from cinder.volume.drivers.huawei import huawei_conf
from cinder.volume.drivers.huawei import huawei_driver
-from cinder.volume.drivers.huawei import huawei_utils
from cinder.volume.drivers.huawei import hypermetro
from cinder.volume.drivers.huawei import rest_client
from cinder.volume.drivers.huawei import smartx
'display_description': 'test volume',
'volume_type_id': None,
'provider_location': '11',
+ 'volume': {"volume_id": '21ec7341-9256-497b-97d9-ef48edcf0635'},
'volume': {'provider_location': '12'},
}
}
}
-FAKE_CREATE_VOLUME_RESPONSE = {"ID": "1",
- "NAME": "5mFHcBv4RkCcD+JyrWc0SA"}
-
CHANGE_OPTS = {'policy': ('1', '2'),
'partitionid': (['1', 'partition001'], ['2', 'partition002']),
'cacheid': (['1', 'cache001'], ['2', 'cache002']),
"ID": "11",
"IOCLASSID": "11",
"NAME": "5mFHcBv4RkCcD+JyrWc0SA",
+ "DESCRIPTION": "21ec7341-9256-497b-97d9-ef48edcf0635",
"RUNNINGSTATUS": "2",
"HEALTHSTATUS": "1",
"RUNNINGSTATUS": "27",
FAKE_PORT_GROUP_RESPONSE)
# mock system info map
-MAP_COMMAND_TO_FAKE_RESPONSE['/system/'] = (
+MAP_COMMAND_TO_FAKE_RESPONSE['/system//GET'] = (
FAKE_SYSTEM_VERSION_RESPONSE)
MAP_COMMAND_TO_FAKE_RESPONSE['/fc_initiator?range=[0-256]/GET'] = (
pass
+class FakeHuaweiConf(object):
+ def __init__(self, conf, protocol):
+ self.conf = conf
+ self.protocol = protocol
+
+ def update_config_value(self):
+ setattr(self.conf, 'san_address',
+ ['http://100.115.10.69:8082/deviceManager/rest/'])
+ setattr(self.conf, 'san_user', 'admin')
+ setattr(self.conf, 'san_password', 'Admin@storage')
+ setattr(self.conf, 'san_product', 'V3')
+ setattr(self.conf, 'san_protocol', self.protocol)
+ setattr(self.conf, 'lun_type', constants.THICK_LUNTYPE)
+ setattr(self.conf, 'lun_ready_wait_interval', 2)
+ setattr(self.conf, 'lun_copy_wait_interval', 2)
+ setattr(self.conf, 'lun_timeout', 43200)
+ setattr(self.conf, 'lun_write_type', '1')
+ setattr(self.conf, 'lun_mirror_switch', '1')
+ setattr(self.conf, 'lun_prefetch_type', '1')
+ setattr(self.conf, 'lun_prefetch_value', '0')
+ setattr(self.conf, 'lun_policy', '0')
+ setattr(self.conf, 'lun_read_cache_policy', '2')
+ setattr(self.conf, 'lun_write_cache_policy', '5')
+ setattr(self.conf, 'storage_pools', ['OpenStack_Pool'])
+ setattr(self.conf, 'iscsi_default_target_ip', ['100.115.10.68'])
+ setattr(self.conf, 'metro_san_address',
+ ['https://100.97.50.240:8088/deviceManager/rest/'])
+ setattr(self.conf, 'metro_storage_pools', 'StoragePool001')
+ setattr(self.conf, 'metro_san_user', 'admin')
+ setattr(self.conf, 'metro_san_password', 'Admin@storage1')
+ setattr(self.conf, 'metro_domain_name', 'hypermetro_test')
+
+ iscsi_info = {'Name': 'iqn.1993-08.debian:01:ec2bff7ac3a3',
+ 'TargetIP': '192.168.100.2',
+ 'CHAPinfo': 'mm-user;mm-user@storage',
+ 'ALUA': '1',
+ 'TargetPortGroup': 'portgroup-test', }
+ setattr(self.conf, 'iscsi_info', [iscsi_info])
+
+
class FakeClient(rest_client.RestClient):
def __init__(self, configuration):
- rest_client.RestClient.__init__(self, configuration)
- self.delete_flag = False
- self.terminateFlag = False
- self.device_id = None
+ san_address = configuration.san_address
+ san_user = configuration.san_user
+ san_password = configuration.san_password
+ rest_client.RestClient.__init__(self, configuration,
+ san_address,
+ san_user,
+ san_password)
self.test_fail = False
self.test_multi_url_flag = False
- self.checkFlag = False
- self.remove_chap_flag = False
self.cache_not_exist = False
self.partition_not_exist = False
- def _change_file_mode(self, filepath):
- pass
-
- def _parse_volume_type(self, volume):
- poolinfo = self._find_pool_info()
- volume_size = self._get_volume_size(poolinfo, volume)
-
- params = {'LUNType': 0,
- 'WriteType': '1',
- 'PrefetchType': '3',
- 'qos_level': 'Qos-high',
- 'StripUnitSize': '64',
- 'PrefetchValue': '0',
- 'PrefetchTimes': '0',
- 'qos': 'OpenStack_Qos_High',
- 'MirrorSwitch': '1',
- 'tier': 'Tier_high',
- }
-
- params['volume_size'] = volume_size
- params['pool_id'] = poolinfo['ID']
- return params
-
def _get_snapshotid_by_name(self, snapshot_name):
return "11"
def do_call(self, url=False, data=None, method=None, calltimeout=4):
url = url.replace('http://100.115.10.69:8082/deviceManager/rest', '')
command = url.replace('/210235G7J20000000000/', '')
- data = None
+ data = json.dumps(data) if data else None
if method:
command = command + "/" + method
return json.loads(data)
+class FakeDB(object):
+
+ def volume_update(self, context, volume_id, model_update):
+ pass
+
+ def volume_get_all_by_group(self, context, group_id):
+ volumes = []
+ volumes.append(test_volume)
+ return volumes
+
+
class FakeISCSIStorage(huawei_driver.HuaweiISCSIDriver):
"""Fake Huawei Storage, Rewrite some methods of HuaweiISCSIDriver."""
def __init__(self, configuration):
self.configuration = configuration
- self.xml_file_path = self.configuration.cinder_huawei_conf_file
+ self.db = FakeDB()
+ self.huawei_conf = FakeHuaweiConf(self.configuration, 'iSCSI')
def do_setup(self):
- self.restclient = FakeClient(configuration=self.configuration)
+ self.metro_flag = True
+ self.huawei_conf.update_config_value()
+ self.client = FakeClient(configuration=self.configuration)
+ self.rmt_client = FakeClient(configuration=self.configuration)
+ self.metro = hypermetro.HuaweiHyperMetro(self.client,
+ self.rmt_client,
+ self.configuration,
+ self.db)
class FakeFCStorage(huawei_driver.HuaweiFCDriver):
def __init__(self, configuration):
self.configuration = configuration
- self.xml_file_path = self.configuration.cinder_huawei_conf_file
self.fcsan_lookup_service = None
+ self.db = FakeDB()
+ self.huawei_conf = FakeHuaweiConf(self.configuration, 'iSCSI')
def do_setup(self):
- self.restclient = FakeClient(configuration=self.configuration)
+ self.metro_flag = True
+ self.huawei_conf.update_config_value()
+ self.client = FakeClient(configuration=self.configuration)
+ self.rmt_client = FakeClient(configuration=self.configuration)
+ self.metro = hypermetro.HuaweiHyperMetro(self.client,
+ self.rmt_client,
+ self.configuration,
+ self.db)
@ddt.ddt
def setUp(self):
super(HuaweiISCSIDriverTestCase, self).setUp()
- self.tmp_dir = tempfile.mkdtemp()
- self.fake_conf_file = self.tmp_dir + '/cinder_huawei_conf.xml'
- self.addCleanup(shutil.rmtree, self.tmp_dir)
- self.create_fake_conf_file()
- self.addCleanup(os.remove, self.fake_conf_file)
self.configuration = mock.Mock(spec=conf.Configuration)
- self.configuration.cinder_huawei_conf_file = self.fake_conf_file
- self.xml_file_path = self.configuration.cinder_huawei_conf_file
self.configuration.hypermetro_devices = hypermetro_devices
self.stubs.Set(time, 'sleep', Fake_sleep)
- driver = FakeISCSIStorage(configuration=self.configuration)
- self.driver = driver
+ self.driver = FakeISCSIStorage(configuration=self.configuration)
self.driver.do_setup()
- self.device_id = self.driver.restclient.login()
self.portgroup = 'portgroup-test'
self.iscsi_iqns = ['iqn.2006-08.com.huawei:oceanstor:21000022a:'
':20503:192.168.1.1',
self.target_ips = ['192.168.1.1',
'192.168.1.2']
self.portgroup_id = 11
+ self.driver.client.login()
def test_login_success(self):
- self.assertEqual('210235G7J20000000000', self.device_id)
+ device_id = self.driver.client.login()
+ self.assertEqual('210235G7J20000000000', device_id)
def test_create_volume_success(self):
+
# Have pool info in the volume.
test_volume = {'name': 'volume-21ec7341-9256-497b-97d9-ef48edcf0635',
'size': 2,
def test_create_volume_from_snapsuccess(self):
lun_info = self.driver.create_volume_from_snapshot(test_volume,
test_volume)
- self.assertEqual('1', lun_info['ID'])
+ self.assertEqual('1', lun_info['provider_location'])
def test_initialize_connection_success(self):
iscsi_properties = self.driver.initialize_connection(test_volume,
self.assertEqual(1, iscsi_properties['data']['target_lun'])
def test_terminate_connection_success(self):
- self.driver.restclient.terminateFlag = True
self.driver.terminate_connection(test_volume, FakeConnector)
- self.assertTrue(self.driver.restclient.terminateFlag)
def test_get_volume_status(self):
data = self.driver.get_volume_stats()
- self.assertEqual('2.0.1', data['driver_version'])
+ self.assertEqual('2.0.2', data['driver_version'])
def test_extend_volume(self):
lun_info = self.driver.extend_volume(test_volume, 3)
self.assertEqual('1', lun_info['provider_location'])
def test_login_fail(self):
- self.driver.restclient.test_fail = True
+ self.driver.client.test_fail = True
self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.restclient.login)
+ self.driver.client.login)
def test_create_snapshot_fail(self):
- self.driver.restclient.test_fail = True
+
+ self.driver.client.test_fail = True
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.create_snapshot, test_snap)
def test_create_volume_fail(self):
- self.driver.restclient.test_fail = True
+
+ self.driver.client.test_fail = True
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.create_volume, test_volume)
def test_delete_volume_fail(self):
- self.driver.restclient.test_fail = True
- delete_flag = self.driver.delete_volume(test_volume)
- self.assertTrue(delete_flag)
+
+ self.driver.client.test_fail = True
+ self.driver.delete_volume(test_volume)
def test_delete_snapshot_fail(self):
- self.driver.restclient.test_fail = True
- delete_flag = self.driver.delete_volume(test_snap)
- self.assertTrue(delete_flag)
+
+ self.driver.client.test_fail = True
+ self.driver.delete_volume(test_snap)
def test_initialize_connection_fail(self):
- self.driver.restclient.test_fail = True
+
+ self.driver.client.test_fail = True
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.initialize_connection,
test_volume, FakeConnector)
- def test_get_default_timeout(self):
- result = huawei_utils.get_default_timeout(self.xml_file_path)
- self.assertEqual('43200', result)
-
- def test_get_wait_interval(self):
- result = huawei_utils.get_wait_interval(self.xml_file_path,
- 'LUNReadyWaitInterval')
- self.assertEqual(2, result)
-
def test_lun_is_associated_to_lungroup(self):
- self.driver.restclient.associate_lun_to_lungroup('11', '11')
- result = self.driver.restclient._is_lun_associated_to_lungroup('11',
- '11')
+
+ self.driver.client.associate_lun_to_lungroup('11', '11')
+ result = self.driver.client._is_lun_associated_to_lungroup('11',
+ '11')
self.assertTrue(result)
def test_lun_is_not_associated_to_lun_group(self):
- 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',
- '12')
+
+ self.driver.client.associate_lun_to_lungroup('12', '12')
+ self.driver.client.remove_lun_from_lungroup('12', '12')
+ result = self.driver.client._is_lun_associated_to_lungroup('12', '12')
self.assertFalse(result)
def test_get_tgtip(self):
- 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):
- (iscsi_iqns, target_ips, portgroup_id) = (
- self.driver.restclient.get_iscsi_params(self.xml_file_path,
- FakeConnector))
- self.assertEqual(self.iscsi_iqns, iscsi_iqns)
- self.assertEqual(self.target_ips, target_ips)
- self.assertEqual(self.portgroup_id, portgroup_id)
-
- def test_get_lun_conf_params(self):
- luninfo = huawei_utils.get_lun_conf_params(self.xml_file_path)
- luninfo['pool_id'] = '0'
- luninfo['volume_size'] = 2
- luninfo['volume_description'] = 'test volume'
- luninfo = huawei_utils.init_lun_parameters('5mFHcBv4RkCcD+JyrWc0SA',
- luninfo)
- self.assertEqual('5mFHcBv4RkCcD+JyrWc0SA', luninfo['NAME'])
-
- def tset_get_iscsi_conf(self):
- 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.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):
- host_os = huawei_utils.get_conf_host_os_type('100.97.10.30',
- self.configuration)
- self.assertEqual('0', host_os)
+ portg_id = self.driver.client.get_tgt_port_group(self.portgroup)
+ target_ip = self.driver.client._get_tgt_ip_from_portgroup(portg_id)
+ self.assertEqual(self.target_ips, target_ip)
def test_find_chap_info(self):
+
tmp_dict = {}
iscsi_info = {}
tmp_dict['Name'] = 'iqn.1993-08.debian:01:ec2bff7ac3a3'
tmp_dict['CHAPinfo'] = 'mm-user;mm-user@storage'
- ini_list = [tmp_dict]
- iscsi_info['Initiator'] = ini_list
+ iscsi_info = [tmp_dict]
initiator_name = FakeConnector['initiator']
- chapinfo = self.driver.restclient.find_chap_info(iscsi_info,
- initiator_name)
+ chapinfo = self.driver.client.find_chap_info(iscsi_info,
+ initiator_name)
chap_username, chap_password = chapinfo.split(';')
self.assertEqual('mm-user', chap_username)
self.assertEqual('mm-user@storage', chap_password)
def test_find_alua_info(self):
+
tmp_dict = {}
iscsi_info = {}
tmp_dict['Name'] = 'iqn.1993-08.debian:01:ec2bff7ac3a3'
tmp_dict['ALUA'] = '1'
- ini_list = [tmp_dict]
- iscsi_info['Initiator'] = ini_list
+ iscsi_info = [tmp_dict]
initiator_name = FakeConnector['initiator']
- type = self.driver.restclient._find_alua_info(iscsi_info,
- initiator_name)
+ type = self.driver.client._find_alua_info(iscsi_info,
+ initiator_name)
self.assertEqual('1', type)
- def test_find_pool_info(self):
- pools = {
- "error": {"code": 0},
- "data": [{
- "NAME": "test001",
- "ID": "0",
- "USERFREECAPACITY": "36",
- "USERTOTALCAPACITY": "48",
- "USAGETYPE": constants.BLOCK_STORAGE_POOL_TYPE},
- {"NAME": "test002",
- "ID": "1",
- "USERFREECAPACITY": "37",
- "USERTOTALCAPACITY": "49",
- "USAGETYPE": constants.FILE_SYSTEM_POOL_TYPE},
- {"NAME": "test003",
- "ID": "0",
- "USERFREECAPACITY": "36",
- "DATASPACE": "35",
- "USERTOTALCAPACITY": "48",
- "USAGETYPE": constants.BLOCK_STORAGE_POOL_TYPE}]}
+ def test_get_pool_info(self):
+
+ pools = [{"NAME": "test001",
+ "ID": "0",
+ "USERFREECAPACITY": "36",
+ "USERTOTALCAPACITY": "48",
+ "USAGETYPE": constants.BLOCK_STORAGE_POOL_TYPE},
+ {"NAME": "test002",
+ "ID": "1",
+ "USERFREECAPACITY": "37",
+ "USERTOTALCAPACITY": "49",
+ "USAGETYPE": constants.FILE_SYSTEM_POOL_TYPE},
+ {"NAME": "test003",
+ "ID": "0",
+ "USERFREECAPACITY": "36",
+ "DATASPACE": "35",
+ "USERTOTALCAPACITY": "48",
+ "USAGETYPE": constants.BLOCK_STORAGE_POOL_TYPE}]
pool_name = 'test001'
test_info = {'CAPACITY': '36', 'ID': '0', 'TOTALCAPACITY': '48'}
- pool_info = self.driver.restclient.find_pool_info(pool_name, pools)
+ pool_info = self.driver.client.get_pool_info(pool_name, pools)
self.assertEqual(test_info, pool_info)
pool_name = 'test002'
test_info = {}
- pool_info = self.driver.restclient.find_pool_info(pool_name, pools)
+ pool_info = self.driver.client.get_pool_info(pool_name, pools)
self.assertEqual(test_info, pool_info)
pool_name = 'test000'
test_info = {}
- pool_info = self.driver.restclient.find_pool_info(pool_name, pools)
+ pool_info = self.driver.client.get_pool_info(pool_name, pools)
self.assertEqual(test_info, pool_info)
pool_name = 'test003'
test_info = {'CAPACITY': '35', 'ID': '0', 'TOTALCAPACITY': '48'}
- pool_info = self.driver.restclient.find_pool_info(pool_name, pools)
+ pool_info = self.driver.client.get_pool_info(pool_name, pools)
self.assertEqual(test_info, pool_info)
def test_get_smartx_specs_opts(self):
+
smartx_opts = smartx.SmartX().get_smartx_specs_opts(smarttier_opts)
self.assertEqual('3', smartx_opts['policy'])
- @mock.patch.object(huawei_utils, 'get_volume_qos',
+ @mock.patch.object(smartx.SmartQos, 'get_qos_by_volume_type',
return_value={'MAXIOPS': '100',
'IOType': '2'})
def test_create_smartqos(self, mock_qos_value):
+
lun_info = self.driver.create_volume(test_volume)
self.assertEqual('1', lun_info['provider_location'])
@mock.patch.object(rest_client.RestClient, 'add_lun_to_partition')
- @mock.patch.object(huawei_utils, 'get_volume_params',
+ @mock.patch.object(huawei_driver.HuaweiBaseDriver, '_get_volume_params',
return_value={'smarttier': 'true',
'smartcache': 'true',
'smartpartition': 'true',
'cachename': 'cache-test',
'partitionname': 'partition-test'})
def test_creat_smartx(self, mock_volume_types, mock_add_lun_to_partition):
+
lun_info = self.driver.create_volume(test_volume)
self.assertEqual('1', lun_info['provider_location'])
def test_find_available_qos(self):
+
qos = {'MAXIOPS': '100', 'IOType': '2'}
fake_qos_info_response_equal = {
"error": {
# Number of LUNs in QoS is equal to 64
with mock.patch.object(rest_client.RestClient, 'get_qos',
return_value=fake_qos_info_response_equal):
- (qos_id, lun_list) = self.driver.restclient.find_available_qos(qos)
+ (qos_id, lun_list) = self.driver.client.find_available_qos(qos)
self.assertEqual((None, []), (qos_id, lun_list))
# Number of LUNs in QoS is less than 64
}
with mock.patch.object(rest_client.RestClient, 'get_qos',
return_value=fake_qos_info_response_less):
- (qos_id, lun_list) = self.driver.restclient.find_available_qos(qos)
+ (qos_id, lun_list) = self.driver.client.find_available_qos(qos)
self.assertEqual(("11", u'["0", "1", "2"]'), (qos_id, lun_list))
- @mock.patch.object(huawei_utils, 'get_volume_params',
+ @mock.patch.object(huawei_driver.HuaweiBaseDriver, '_get_volume_params',
return_value=fake_hypermetro_opts)
- @mock.patch.object(rest_client.RestClient, 'login_with_ip',
- return_value='123456789')
- @mock.patch.object(rest_client.RestClient, 'find_all_pools',
+ @mock.patch.object(rest_client.RestClient, 'get_all_pools',
return_value=FAKE_STORAGE_POOL_RESPONSE)
- @mock.patch.object(rest_client.RestClient, 'find_pool_info',
+ @mock.patch.object(rest_client.RestClient, 'get_pool_info',
return_value=FAKE_FIND_POOL_RESPONSE)
- @mock.patch.object(rest_client.RestClient, 'create_volume',
+ @mock.patch.object(rest_client.RestClient, 'create_lun',
return_value=FAKE_CREATE_VOLUME_RESPONSE)
@mock.patch.object(rest_client.RestClient, 'get_hyper_domain_id',
return_value='11')
mock_create_volume,
mock_pool_info,
mock_all_pool_info,
- mock_login_return,
mock_hypermetro_opts):
metadata = {"hypermetro_id": '11',
"remote_lun_id": '1'}
lun_info = self.driver.create_volume(hyper_volume)
- mock_logout.assert_called_with()
self.assertEqual(metadata, lun_info['metadata'])
- @mock.patch.object(huawei_utils, 'get_volume_params',
+ @mock.patch.object(huawei_driver.HuaweiBaseDriver, '_get_volume_params',
return_value=fake_hypermetro_opts)
- @mock.patch.object(rest_client.RestClient, 'login_with_ip',
- return_value='123456789')
- @mock.patch.object(rest_client.RestClient, 'find_all_pools',
+ @mock.patch.object(rest_client.RestClient, 'get_all_pools',
return_value=FAKE_STORAGE_POOL_RESPONSE)
- @mock.patch.object(rest_client.RestClient, 'find_pool_info',
+ @mock.patch.object(rest_client.RestClient, 'get_pool_info',
return_value=FAKE_FIND_POOL_RESPONSE)
- @mock.patch.object(rest_client.RestClient, 'create_volume',
+ @mock.patch.object(rest_client.RestClient, 'create_lun',
return_value=FAKE_CREATE_VOLUME_RESPONSE)
@mock.patch.object(rest_client.RestClient, 'get_hyper_domain_id',
return_value='11')
mock_create_volume,
mock_pool_info,
mock_all_pool_info,
- mock_login_return,
mock_hypermetro_opts):
+ self.driver.client.login()
mock_hyper_pair_info.side_effect = exception.VolumeBackendAPIException(
data='Create hypermetro error.')
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.create_volume, hyper_volume)
mock_delete_lun.assert_called_with('1')
- mock_logout.assert_called_with()
- @mock.patch.object(rest_client.RestClient, 'login_with_ip',
- return_value='123456789')
@mock.patch.object(rest_client.RestClient, 'check_lun_exist',
return_value=True)
@mock.patch.object(rest_client.RestClient, 'check_hypermetro_exist',
mock_delete_hypermetro,
mock_metro_info,
mock_check_hyermetro,
- mock_lun_exit,
- mock_login_info):
+ mock_lun_exit):
result = self.driver.delete_volume(hyper_volume)
- mock_logout.assert_called_with()
self.assertTrue(result)
- @mock.patch.object(rest_client.RestClient, 'login_with_ip',
- return_value='123456789')
@mock.patch.object(rest_client.RestClient, 'check_lun_exist',
return_value=True)
@mock.patch.object(rest_client.RestClient, 'check_hypermetro_exist',
mock_delete_hypermetro,
mock_metro_info,
mock_check_hyermetro,
- mock_lun_exit,
- mock_login_info):
+ mock_lun_exit):
+
mock_delete_hypermetro.side_effect = (
exception.VolumeBackendAPIException(data='Delete hypermetro '
'error.'))
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.
-
- Huawei storage customize a XML configuration file, the configuration
- file is used to set the Huawei storage custom parameters, therefore,
- in the UT test we need to simulate such a configuration file.
- """
- doc = minidom.Document()
-
- config = doc.createElement('config')
- doc.appendChild(config)
-
- storage = doc.createElement('Storage')
- config.appendChild(storage)
- controllerip0 = doc.createElement('ControllerIP0')
- controllerip0_text = doc.createTextNode('10.10.10.1')
- controllerip0.appendChild(controllerip0_text)
- storage.appendChild(controllerip0)
- controllerip1 = doc.createElement('ControllerIP1')
- controllerip1_text = doc.createTextNode('10.10.10.2')
- controllerip1.appendChild(controllerip1_text)
- storage.appendChild(controllerip1)
- username = doc.createElement('UserName')
- username_text = doc.createTextNode('admin')
- username.appendChild(username_text)
- storage.appendChild(username)
- userpassword = doc.createElement('UserPassword')
- userpassword_text = doc.createTextNode('Admin@storage')
- userpassword.appendChild(userpassword_text)
- storage.appendChild(userpassword)
- url = doc.createElement('RestURL')
- url_text = doc.createTextNode('http://100.115.10.69:8082/'
- 'deviceManager/rest/')
- url.appendChild(url_text)
- storage.appendChild(url)
-
- storagepool = doc.createElement('StoragePool')
- pool_text = doc.createTextNode('OpenStack_Pool')
- storagepool.appendChild(pool_text)
- storage.appendChild(storagepool)
-
- lun = doc.createElement('LUN')
- config.appendChild(lun)
- storagepool = doc.createElement('StoragePool')
- pool_text = doc.createTextNode('OpenStack_Pool;OpenStack_Pool2')
- storagepool.appendChild(pool_text)
- lun.appendChild(storagepool)
-
- timeout = doc.createElement('Timeout')
- timeout_text = doc.createTextNode('43200')
- timeout.appendChild(timeout_text)
- lun.appendChild(timeout)
-
- lun_ready_wait_interval = doc.createElement('LUNReadyWaitInterval')
- lun_ready_wait_interval_text = doc.createTextNode('2')
- lun_ready_wait_interval.appendChild(lun_ready_wait_interval_text)
- lun.appendChild(lun_ready_wait_interval)
-
- prefetch = doc.createElement('Prefetch')
- prefetch.setAttribute('Type', '1')
- prefetch.setAttribute('Value', '0')
- lun.appendChild(prefetch)
-
- iscsi = doc.createElement('iSCSI')
- config.appendChild(iscsi)
- defaulttargetip = doc.createElement('DefaultTargetIP')
- defaulttargetip_text = doc.createTextNode('100.115.10.68')
- defaulttargetip.appendChild(defaulttargetip_text)
- iscsi.appendChild(defaulttargetip)
- initiator = doc.createElement('Initiator')
- initiator.setAttribute('Name', 'iqn.1993-08.debian:01:ec2bff7ac3a3')
- initiator.setAttribute('TargetIP', '192.168.1.2')
- initiator.setAttribute('CHAPinfo', 'mm-user;mm-user@storage')
- initiator.setAttribute('ALUA', '1')
- initiator.setAttribute('TargetPortGroup', 'portgroup-test')
- iscsi.appendChild(initiator)
-
- host = doc.createElement('Host')
- host.setAttribute('HostIP', '100.97.10.30')
- host.setAttribute('OSType', 'Linux')
- config.appendChild(host)
-
- fakefile = open(self.fake_conf_file, 'w')
- fakefile.write(doc.toprettyxml(indent=''))
- fakefile.close()
-
class FCSanLookupService(object):
+
def get_device_mapping_from_network(self, initiator_list,
target_list):
return fake_fabric_mapping
def setUp(self):
super(HuaweiFCDriverTestCase, self).setUp()
- self.tmp_dir = tempfile.mkdtemp()
- self.fake_conf_file = self.tmp_dir + '/cinder_huawei_conf.xml'
- self.addCleanup(shutil.rmtree, self.tmp_dir)
- self.create_fake_conf_file()
- self.addCleanup(os.remove, self.fake_conf_file)
self.configuration = mock.Mock(spec=conf.Configuration)
- self.configuration.cinder_huawei_conf_file = self.fake_conf_file
- self.xml_file_path = self.configuration.cinder_huawei_conf_file
+ self.huawei_conf = FakeHuaweiConf(self.configuration, 'FC')
self.configuration.hypermetro_devices = hypermetro_devices
self.stubs.Set(time, 'sleep', Fake_sleep)
driver = FakeFCStorage(configuration=self.configuration)
self.driver = driver
self.driver.do_setup()
- self.device_id = self.driver.restclient.login()
+ self.driver.client.login()
def test_login_success(self):
- self.assertEqual('210235G7J20000000000', self.device_id)
+ device_id = self.driver.client.login()
+ self.assertEqual('210235G7J20000000000', device_id)
def test_create_volume_success(self):
+
lun_info = self.driver.create_volume(test_volume)
self.assertEqual('1', lun_info['provider_location'])
def test_delete_volume_success(self):
+
delete_flag = self.driver.delete_volume(test_volume)
self.assertTrue(delete_flag)
def test_create_snapshot_success(self):
+
lun_info = self.driver.create_snapshot(test_snap)
self.assertEqual(11, lun_info['provider_location'])
self.assertEqual(11, lun_info['provider_location'])
def test_delete_snapshot_success(self):
+
delete_flag = self.driver.delete_snapshot(test_snap)
self.assertTrue(delete_flag)
def test_create_volume_from_snapsuccess(self):
+
lun_info = self.driver.create_volume_from_snapshot(test_volume,
test_volume)
- self.assertEqual('1', lun_info['ID'])
+ self.assertEqual('1', lun_info['provider_location'])
def test_initialize_connection_success(self):
+
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.terminateFlag = True
+
+ self.driver.client.terminateFlag = True
self.driver.terminate_connection(test_volume, FakeConnector)
- self.assertTrue(self.driver.restclient.terminateFlag)
+ self.assertTrue(self.driver.client.terminateFlag)
def test_get_volume_status(self):
+
data = self.driver.get_volume_stats()
- self.assertEqual('2.0.1', data['driver_version'])
+ self.assertEqual('2.0.2', data['driver_version'])
def test_extend_volume(self):
+
lun_info = self.driver.extend_volume(test_volume, 3)
self.assertEqual('1', lun_info['provider_location'])
def test_login_fail(self):
- self.driver.restclient.test_fail = True
+ self.driver.client.test_fail = True
self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.restclient.login)
+ self.driver.client.login)
def test_create_snapshot_fail(self):
- self.driver.restclient.test_fail = True
+
+ self.driver.client.test_fail = True
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.create_snapshot, test_snap)
def test_create_volume_fail(self):
- self.driver.restclient.test_fail = True
+
+ self.driver.client.test_fail = True
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.create_volume, test_volume)
def test_delete_volume_fail(self):
- self.driver.restclient.test_fail = True
- delete_flag = self.driver.delete_volume(test_volume)
- self.assertTrue(delete_flag)
+ self.driver.client.test_fail = True
+ self.driver.delete_volume(test_volume)
def test_delete_snapshot_fail(self):
- self.driver.restclient.test_fail = True
+ self.driver.client.test_fail = True
delete_flag = self.driver.delete_snapshot(test_snap)
self.assertTrue(delete_flag)
def test_initialize_connection_fail(self):
- self.driver.restclient.test_fail = True
+
+ self.driver.client.test_fail = True
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.initialize_connection,
test_volume, FakeConnector)
- def test_get_default_timeout(self):
- result = huawei_utils.get_default_timeout(self.xml_file_path)
- self.assertEqual('43200', result)
-
- def test_get_wait_interval(self):
- result = huawei_utils.get_wait_interval(self.xml_file_path,
- 'LUNReadyWaitInterval')
- self.assertEqual(2, result)
-
def test_lun_is_associated_to_lungroup(self):
- self.driver.restclient.associate_lun_to_lungroup('11', '11')
- result = self.driver.restclient._is_lun_associated_to_lungroup('11',
- '11')
+
+ self.driver.client.associate_lun_to_lungroup('11', '11')
+ result = self.driver.client._is_lun_associated_to_lungroup('11',
+ '11')
self.assertTrue(result)
def test_lun_is_not_associated_to_lun_group(self):
- 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',
- '12')
- self.assertFalse(result)
- def test_get_lun_conf_params(self):
- luninfo = huawei_utils.get_lun_conf_params(self.xml_file_path)
- luninfo['pool_id'] = '0'
- luninfo['volume_size'] = 2
- luninfo['volume_description'] = 'test volume'
- luninfo = huawei_utils.init_lun_parameters('5mFHcBv4RkCcD+JyrWc0SA',
- luninfo)
- self.assertEqual('5mFHcBv4RkCcD+JyrWc0SA', luninfo['NAME'])
-
- def test_check_conf_file(self):
- 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):
- host_os = huawei_utils.get_conf_host_os_type('100.97.10.30',
- self.configuration)
- self.assertEqual('0', host_os)
+ self.driver.client.associate_lun_to_lungroup('12', '12')
+ self.driver.client.remove_lun_from_lungroup('12', '12')
+ result = self.driver.client._is_lun_associated_to_lungroup('12',
+ '12')
+ self.assertFalse(result)
@mock.patch.object(rest_client.RestClient, 'add_lun_to_partition')
def test_migrate_volume_success(self, mock_add_lun_to_partition):
+
# Migrate volume without new type.
model_update = None
moved = False
self.assertEqual(empty_dict, model_update)
def test_migrate_volume_fail(self):
- self.driver.restclient.test_fail = True
+
+ self.driver.client.test_fail = True
# Migrate volume without new type.
self.assertRaises(exception.VolumeBackendAPIException,
'policy': '2',
'smartcache:cachename': 'cache-test',
'partitionname': 'partition-test'}}
- self.driver.restclient.test_fail = True
+ self.driver.client.test_fail = True
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.migrate_volume, None,
test_volume, test_host, new_type)
def test_check_migration_valid(self):
+
is_valid = self.driver._check_migration_valid(test_host,
test_volume)
self.assertTrue(is_valid)
@mock.patch.object(rest_client.RestClient, 'rename_lun')
def test_update_migrated_volume_success(self, mock_rename_lun):
+
original_volume = {'id': '21ec7341-9256-497b-97d9-ef48edcf0635'}
current_volume = {'id': '21ec7341-9256-497b-97d9-ef48edcf0636'}
model_update = self.driver.update_migrated_volume(None,
@mock.patch.object(rest_client.RestClient, 'rename_lun')
def test_update_migrated_volume_fail(self, mock_rename_lun):
+
mock_rename_lun.side_effect = exception.VolumeBackendAPIException(
data='Error occurred.')
original_volume = {'id': '21ec7341-9256-497b-97d9-ef48edcf0635'}
@mock.patch.object(rest_client.RestClient, 'add_lun_to_partition')
def test_retype_volume_success(self, mock_add_lun_to_partition):
+
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.client.cache_not_exist = True
+
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.client.partition_not_exist = True
+
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):
+
mock_add_lun_to_partition.side_effect = (
exception.VolumeBackendAPIException(data='Error occurred.'))
retype = self.driver.retype(None, test_volume,
self.assertFalse(retype)
def test_build_ini_targ_map(self):
+
fake_lookup_service = FCSanLookupService()
fake_lookup_service.get_device_mapping_from_network = mock.Mock(
return_value=fake_fabric_mapping)
zone_helper = fc_zone_helper.FCZoneHelper(
- fake_lookup_service, self.driver.restclient)
+ fake_lookup_service, self.driver.client)
(tgt_port_wwns,
init_targ_map) = (zone_helper.build_ini_targ_map(
['10000090fa0d6754']))
self.assertEqual(ini_target_map, init_targ_map)
def test_filter_port_by_contr(self):
+
# Six ports in one fabric.
ports_in_fabric = ['1', '2', '3', '4', '5', '6']
# Ports 1,3,4,7 belonged to controller A
self.assertEqual(expected_filtered_ports, filtered_ports)
def test_multi_resturls_success(self):
- self.driver.restclient.test_multi_url_flag = True
+
+ self.driver.client.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):
+
result = {}
name = 'test_name'
key = 'NAME'
- re = self.driver.restclient._get_id_from_result(result, name, key)
+ re = self.driver.client._get_id_from_result(result, name, key)
self.assertIsNone(re)
result = {'data': {}}
- re = self.driver.restclient._get_id_from_result(result, name, key)
+ re = self.driver.client._get_id_from_result(result, name, key)
self.assertIsNone(re)
result = {'data': [{'COUNT': 1, 'ID': '1'},
{'COUNT': 2, 'ID': '2'}]}
- re = self.driver.restclient._get_id_from_result(result, name, key)
+ re = self.driver.client._get_id_from_result(result, name, key)
self.assertIsNone(re)
result = {'data': [{'NAME': 'test_name1', 'ID': '1'},
{'NAME': 'test_name2', 'ID': '2'}]}
- re = self.driver.restclient._get_id_from_result(result, name, key)
+ re = self.driver.client._get_id_from_result(result, name, key)
self.assertIsNone(re)
result = {'data': [{'NAME': 'test_name', 'ID': '1'},
{'NAME': 'test_name2', 'ID': '2'}]}
- re = self.driver.restclient._get_id_from_result(result, name, key)
+ re = self.driver.client._get_id_from_result(result, name, key)
self.assertEqual('1', re)
- @mock.patch.object(rest_client.RestClient, 'find_pool_info',
+ @mock.patch.object(rest_client.RestClient, 'get_pool_info',
return_value={'ID': 1,
'CAPACITY': 110362624,
'TOTALCAPACITY': 209715200})
- def test_get_capacity(self, mock_find_pool_info):
+ def test_get_capacity(self, mock_get_pool_info):
expected_pool_capacity = {'total_capacity': 100.0,
'free_capacity': 52.625}
- pool_capacity = self.driver.restclient._get_capacity(None,
- None)
+ pool_capacity = self.driver.client._get_capacity(None,
+ None)
self.assertEqual(expected_pool_capacity, pool_capacity)
- @mock.patch.object(huawei_utils, 'get_volume_params',
+ @mock.patch.object(huawei_driver.HuaweiBaseDriver, '_get_volume_params',
return_value=fake_hypermetro_opts)
- @mock.patch.object(rest_client.RestClient, 'login_with_ip',
- return_value='123456789')
- @mock.patch.object(rest_client.RestClient, 'find_all_pools',
+ @mock.patch.object(rest_client.RestClient, 'get_all_pools',
return_value=FAKE_STORAGE_POOL_RESPONSE)
- @mock.patch.object(rest_client.RestClient, 'find_pool_info',
+ @mock.patch.object(rest_client.RestClient, 'get_pool_info',
return_value=FAKE_FIND_POOL_RESPONSE)
- @mock.patch.object(rest_client.RestClient, 'create_volume',
+ @mock.patch.object(rest_client.RestClient, 'create_lun',
return_value=FAKE_CREATE_VOLUME_RESPONSE)
@mock.patch.object(rest_client.RestClient, 'get_hyper_domain_id',
return_value='11')
mock_create_volume,
mock_hyper_domain,
mock_volume_ready,
- mock_pair_info,
mock_logout):
+
metadata = {"hypermetro_id": '11',
"remote_lun_id": '1'}
lun_info = self.driver.create_volume(hyper_volume)
self.assertEqual(metadata, lun_info['metadata'])
- def create_fake_conf_file(self):
- """Create a fake Config file
- Huawei storage customize a XML configuration file,
- the configuration file is used to set the Huawei storage custom
- parameters, therefore, in the UT test we need to simulate such a
- configuration file
+class HuaweiConfTestCase(test.TestCase):
+ def setUp(self):
+ super(HuaweiConfTestCase, self).setUp()
+
+ self.tmp_dir = tempfile.mkdtemp()
+ self.fake_xml_file = self.tmp_dir + '/cinder_huawei_conf.xml'
+
+ self.conf = mock.Mock()
+ self.conf.cinder_huawei_conf_file = self.fake_xml_file
+ self.huawei_conf = huawei_conf.HuaweiConf(self.conf)
+
+ def _create_fake_conf_file(self):
+ """Create a fake Config file.
+
+ Huawei storage customize a XML configuration file, the configuration
+ file is used to set the Huawei storage custom parameters, therefore,
+ in the UT test we need to simulate such a configuration file.
"""
doc = minidom.Document()
storage = doc.createElement('Storage')
config.appendChild(storage)
- controllerip0 = doc.createElement('ControllerIP0')
- controllerip0_text = doc.createTextNode('10.10.10.1')
- controllerip0.appendChild(controllerip0_text)
- storage.appendChild(controllerip0)
- controllerip1 = doc.createElement('ControllerIP1')
- controllerip1_text = doc.createTextNode('10.10.10.2')
- controllerip1.appendChild(controllerip1_text)
- storage.appendChild(controllerip1)
+ url = doc.createElement('RestURL')
+ url_text = doc.createTextNode('http://100.115.10.69:8082/'
+ 'deviceManager/rest/')
+ url.appendChild(url_text)
+ storage.appendChild(url)
username = doc.createElement('UserName')
username_text = doc.createTextNode('admin')
username.appendChild(username_text)
storage.appendChild(username)
- userpassword = doc.createElement('UserPassword')
- userpassword_text = doc.createTextNode('Admin@storage')
- userpassword.appendChild(userpassword_text)
- storage.appendChild(userpassword)
-
+ password = doc.createElement('UserPassword')
+ password_text = doc.createTextNode('Admin@storage')
+ password.appendChild(password_text)
+ storage.appendChild(password)
+ product = doc.createElement('Product')
+ product_text = doc.createTextNode('V3')
+ product.appendChild(product_text)
+ storage.appendChild(product)
protocol = doc.createElement('Protocol')
- protocol_text = doc.createTextNode('FC')
+ protocol_text = doc.createTextNode('iSCSI')
protocol.appendChild(protocol_text)
storage.appendChild(protocol)
- url = doc.createElement('RestURL')
- url_text = doc.createTextNode('http://100.115.10.69:8082/'
- 'deviceManager/rest/')
- url.appendChild(url_text)
- storage.appendChild(url)
-
- storagepool = doc.createElement('StoragePool')
- pool_text = doc.createTextNode('OpenStack_Pool')
- storagepool.appendChild(pool_text)
- storage.appendChild(storagepool)
-
lun = doc.createElement('LUN')
config.appendChild(lun)
- storagepool = doc.createElement('StoragePool')
- pool_text = doc.createTextNode('OpenStack_Pool;OpenStack_Pool2')
- storagepool.appendChild(pool_text)
- lun.appendChild(storagepool)
-
- lun_type = doc.createElement('LUNType')
- lun_type_text = doc.createTextNode('Thick')
- lun_type.appendChild(lun_type_text)
- lun.appendChild(lun_type)
-
- timeout = doc.createElement('Timeout')
- timeout_text = doc.createTextNode('43200')
- timeout.appendChild(timeout_text)
- lun.appendChild(timeout)
-
+ luntype = doc.createElement('LUNType')
+ luntype_text = doc.createTextNode('Thick')
+ luntype.appendChild(luntype_text)
+ lun.appendChild(luntype)
lun_ready_wait_interval = doc.createElement('LUNReadyWaitInterval')
lun_ready_wait_interval_text = doc.createTextNode('2')
lun_ready_wait_interval.appendChild(lun_ready_wait_interval_text)
lun.appendChild(lun_ready_wait_interval)
+ lun_copy_wait_interval = doc.createElement('LUNcopyWaitInterval')
+ lun_copy_wait_interval_text = doc.createTextNode('2')
+ lun_copy_wait_interval.appendChild(lun_copy_wait_interval_text)
+ lun.appendChild(lun_copy_wait_interval)
+ timeout = doc.createElement('Timeout')
+ timeout_text = doc.createTextNode('43200')
+ timeout.appendChild(timeout_text)
+ lun.appendChild(timeout)
+ write_type = doc.createElement('WriteType')
+ write_type_text = doc.createTextNode('1')
+ write_type.appendChild(write_type_text)
+ lun.appendChild(write_type)
+ mirror_switch = doc.createElement('MirrorSwitch')
+ mirror_switch_text = doc.createTextNode('1')
+ mirror_switch.appendChild(mirror_switch_text)
+ lun.appendChild(mirror_switch)
+ prefetch = doc.createElement('Prefetch')
+ prefetch.setAttribute('Type', '1')
+ prefetch.setAttribute('Value', '0')
+ lun.appendChild(prefetch)
+ pool = doc.createElement('StoragePool')
+ pool_text = doc.createTextNode('OpenStack_Pool')
+ pool.appendChild(pool_text)
+ lun.appendChild(pool)
iscsi = doc.createElement('iSCSI')
config.appendChild(iscsi)
iscsi.appendChild(defaulttargetip)
initiator = doc.createElement('Initiator')
initiator.setAttribute('Name', 'iqn.1993-08.debian:01:ec2bff7ac3a3')
- initiator.setAttribute('TargetIP', '192.168.1.2')
+ initiator.setAttribute('TargetIP', '192.168.100.2')
+ initiator.setAttribute('CHAPinfo', 'mm-user;mm-user@storage')
+ initiator.setAttribute('ALUA', '1')
+ initiator.setAttribute('TargetPortGroup', 'PortGroup001')
iscsi.appendChild(initiator)
- prefetch = doc.createElement('Prefetch')
- prefetch.setAttribute('Type', '1')
- prefetch.setAttribute('Value', '0')
- lun.appendChild(prefetch)
-
- host = doc.createElement('Host')
- host.setAttribute('HostIP', '100.97.10.30')
- host.setAttribute('OSType', 'Linux')
- config.appendChild(host)
-
- fakefile = open(self.fake_conf_file, 'w')
+ fakefile = open(self.conf.cinder_huawei_conf_file, 'w')
fakefile.write(doc.toprettyxml(indent=''))
fakefile.close()
-# Copyright (c) 2015 Huawei Technologies Co., Ltd.
+# Copyright (c) 2016 Huawei Technologies Co., Ltd.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# Copyright (c) 2015 Huawei Technologies Co., Ltd.
+# Copyright (c) 2016 Huawei Technologies Co., Ltd.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
class FCZoneHelper(object):
"""FC zone helper for Huawei driver."""
- def __init__(self, fcsan_lookup_service, restclient):
+ def __init__(self, fcsan_lookup_service, client):
self.fcsan_lookup_service = fcsan_lookup_service
- self.restclient = restclient
+ self.client = client
def _get_fc_port_contr_map(self):
port_list = []
port_contr_map = {}
- data = self.restclient.get_fc_ports_on_array()
+ data = self.client.get_fc_ports_on_array()
for item in data:
if item['RUNNINGSTATUS'] == constants.FC_PORT_CONNECTED:
port_list.append(item['WWN'])
--- /dev/null
+# Copyright (c) 2016 Huawei Technologies Co., Ltd.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""
+Set Huawei private configuration into Configuration object.
+
+For conveniently get private configuration. We parse Huawei config file
+and set every property into Configuration object as an attribute.
+"""
+
+import base64
+import six
+from xml.etree import ElementTree as ET
+
+from oslo_log import log as logging
+
+from cinder import exception
+from cinder.i18n import _
+from cinder import utils
+from cinder.volume.drivers.huawei import constants
+
+LOG = logging.getLogger(__name__)
+
+
+class HuaweiConf(object):
+ def __init__(self, conf):
+ self.conf = conf
+
+ def _encode_authentication(self):
+ need_encode = False
+ tree = ET.parse(self.conf.cinder_huawei_conf_file)
+ xml_root = tree.getroot()
+ name_node = xml_root.find('Storage/UserName')
+ pwd_node = xml_root.find('Storage/UserPassword')
+ if (name_node is not None
+ and not name_node.text.startswith('!$$$')):
+ name_node.text = '!$$$' + base64.b64encode(name_node.text)
+ need_encode = True
+ if (pwd_node is not None
+ and not pwd_node.text.startswith('!$$$')):
+ pwd_node.text = '!$$$' + base64.b64encode(pwd_node.text)
+ need_encode = True
+
+ if need_encode:
+ utils.execute('chmod',
+ '600',
+ self.conf.cinder_huawei_conf_file,
+ run_as_root=True)
+ tree.write(self.conf.cinder_huawei_conf_file, 'UTF-8')
+
+ def update_config_value(self):
+ self._encode_authentication()
+
+ set_attr_funcs = (self._san_address,
+ self._san_user,
+ self._san_password,
+ self._san_product,
+ self._san_protocol,
+ self._lun_type,
+ self._lun_ready_wait_interval,
+ self._lun_copy_wait_interval,
+ self._lun_timeout,
+ self._lun_write_type,
+ self._lun_mirror_switch,
+ self._lun_prefetch,
+ self._lun_policy,
+ self._lun_read_cache_policy,
+ self._lun_write_cache_policy,
+ self._storage_pools,
+ self._iscsi_default_target_ip,
+ self._iscsi_info,)
+
+ tree = ET.parse(self.conf.cinder_huawei_conf_file)
+ xml_root = tree.getroot()
+ for f in set_attr_funcs:
+ f(xml_root)
+
+ def _san_address(self, xml_root):
+ text = xml_root.findtext('Storage/RestURL')
+ if not text:
+ msg = _("RestURL is not configured.")
+ LOG.error(msg)
+ raise exception.InvalidInput(reason=msg)
+
+ addrs = text.split(';')
+ addrs = list(set([x.strip() for x in addrs if x.strip()]))
+ setattr(self.conf, 'san_address', addrs)
+
+ def _san_user(self, xml_root):
+ text = xml_root.findtext('Storage/UserName')
+ if not text:
+ msg = _("UserName is not configured.")
+ LOG.error(msg)
+ raise exception.InvalidInput(reason=msg)
+
+ user = base64.b64decode(text[4:])
+ setattr(self.conf, 'san_user', user)
+
+ def _san_password(self, xml_root):
+ text = xml_root.findtext('Storage/UserPassword')
+ if not text:
+ msg = _("UserPassword is not configured.")
+ LOG.error(msg)
+ raise exception.InvalidInput(reason=msg)
+
+ pwd = base64.b64decode(text[4:])
+ setattr(self.conf, 'san_password', pwd)
+
+ def _san_product(self, xml_root):
+ text = xml_root.findtext('Storage/Product')
+ if not text:
+ msg = _("SAN product is not configured.")
+ LOG.error(msg)
+ raise exception.InvalidInput(reason=msg)
+
+ product = text.strip()
+ setattr(self.conf, 'san_product', product)
+
+ def _san_protocol(self, xml_root):
+ text = xml_root.findtext('Storage/Protocol')
+ if not text:
+ msg = _("SAN protocol is not configured.")
+ LOG.error(msg)
+ raise exception.InvalidInput(reason=msg)
+
+ protocol = text.strip()
+ setattr(self.conf, 'san_protocol', protocol)
+
+ def _lun_type(self, xml_root):
+ lun_type = constants.THICK_LUNTYPE
+
+ text = xml_root.findtext('LUN/LUNType')
+ if text:
+ lun_type = text.strip()
+ if lun_type == 'Thick':
+ lun_type = constants.THICK_LUNTYPE
+ elif lun_type == 'Thin':
+ lun_type = constants.THIN_LUNTYPE
+ else:
+ msg = (_("Invalid lun type %s is configured.") % lun_type)
+ LOG.exception(msg)
+
+ raise exception.InvalidInput(reason=msg)
+
+ setattr(self.conf, 'lun_type', lun_type)
+
+ def _lun_ready_wait_interval(self, xml_root):
+ text = xml_root.findtext('LUN/LUNReadyWaitInterval')
+ interval = text.strip() if text else constants.DEFAULT_WAIT_INTERVAL
+ setattr(self.conf, 'lun_ready_wait_interval', int(interval))
+
+ def _lun_copy_wait_interval(self, xml_root):
+ text = xml_root.findtext('LUN/LUNcopyWaitInterval')
+ interval = text.strip() if text else constants.DEFAULT_WAIT_INTERVAL
+ setattr(self.conf, 'lun_copy_wait_interval', int(interval))
+
+ def _lun_timeout(self, xml_root):
+ text = xml_root.findtext('LUN/Timeout')
+ interval = text.strip() if text else constants.DEFAULT_WAIT_TIMEOUT
+ setattr(self.conf, 'lun_timeout', int(interval))
+
+ def _lun_write_type(self, xml_root):
+ text = xml_root.findtext('LUN/WriteType')
+ write_type = text.strip() if text else '1'
+ setattr(self.conf, 'lun_write_type', write_type)
+
+ def _lun_mirror_switch(self, xml_root):
+ text = xml_root.findtext('LUN/MirrorSwitch')
+ mirror_switch = text.strip() if text else '1'
+ setattr(self.conf, 'lun_mirror_switch', mirror_switch)
+
+ def _lun_prefetch(self, xml_root):
+ prefetch_type = '3'
+ prefetch_value = '0'
+
+ node = xml_root.find('LUN/Prefetch')
+ if (node is not None
+ and node.attrib['Type']
+ and node.attrib['Value']):
+ prefetch_type = node.attrib['Type'].strip()
+ if prefetch_type not in ['0', '1', '2', '3']:
+ msg = (_(
+ "Invalid prefetch type '%s' is configured. "
+ "PrefetchType must be in 0,1,2,3.") % prefetch_type)
+ LOG.error(msg)
+ raise exception.InvalidInput(reason=msg)
+
+ prefetch_value = node.attrib['Value'].strip()
+ factor = {'1': 2, '2': 2}
+ factor = int(factor.get('prefetch_type', '1'))
+ prefetch_value = int(prefetch_value) * factor
+ prefetch_value = six.text_type(prefetch_value)
+
+ setattr(self.conf, 'lun_prefetch_type', prefetch_type)
+ setattr(self.conf, 'lun_prefetch_value', prefetch_value)
+
+ def _lun_policy(self, xml_root):
+ setattr(self.conf, 'lun_policy', '0')
+
+ def _lun_read_cache_policy(self, xml_root):
+ setattr(self.conf, 'lun_read_cache_policy', '2')
+
+ def _lun_write_cache_policy(self, xml_root):
+ setattr(self.conf, 'lun_write_cache_policy', '5')
+
+ def _storage_pools(self, xml_root):
+ nodes = xml_root.findall('LUN/StoragePool')
+ if not nodes:
+ msg = _('Storage pool is not configured.')
+ LOG.error(msg)
+ raise exception.InvalidInput(reason=msg)
+
+ texts = [x.text for x in nodes]
+ merged_text = ';'.join(texts)
+ pools = set(x.strip() for x in merged_text.split(';') if x.strip())
+ if not pools:
+ msg = _('Invalid storage pool is configured.')
+ LOG.error(msg)
+ raise exception.InvalidInput(msg)
+
+ setattr(self.conf, 'storage_pools', list(pools))
+
+ def _iscsi_default_target_ip(self, xml_root):
+ text = xml_root.findtext('iSCSI/DefaultTargetIP')
+ target_ip = text.split() if text else []
+ setattr(self.conf, 'iscsi_default_target_ip', target_ip)
+
+ def _iscsi_info(self, xml_root):
+ nodes = xml_root.findall('iSCSI/Initiator')
+ if nodes is None:
+ setattr(self.conf, 'iscsi_info', [])
+ return
+
+ iscsi_info = []
+ for node in nodes:
+ props = {}
+ for item in node.items():
+ props[item[0].strip()] = item[1].strip()
+
+ iscsi_info.append(props)
+
+ setattr(self.conf, 'iscsi_info', iscsi_info)
-# Copyright (c) 2015 Huawei Technologies Co., Ltd.
+# Copyright (c) 2016 Huawei Technologies Co., Ltd.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
from oslo_utils import excutils
from oslo_utils import units
+from cinder import context
from cinder import exception
from cinder.i18n import _, _LE, _LI, _LW
from cinder import utils
from cinder.volume import driver
from cinder.volume.drivers.huawei import constants
from cinder.volume.drivers.huawei import fc_zone_helper
+from cinder.volume.drivers.huawei import huawei_conf
from cinder.volume.drivers.huawei import huawei_utils
from cinder.volume.drivers.huawei import hypermetro
from cinder.volume.drivers.huawei import rest_client
default='/etc/cinder/cinder_huawei_conf.xml',
help='The configuration file for the Cinder Huawei driver.'),
cfg.StrOpt('hypermetro_devices',
+ default=None,
help='The remote device hypermetro will use.'),
]
def __init__(self, *args, **kwargs):
super(HuaweiBaseDriver, self).__init__(*args, **kwargs)
- self.configuration = kwargs.get('configuration')
+
if not self.configuration:
- msg = _('_instantiate_driver: configuration not found.')
+ msg = _('Configuration is not found.')
raise exception.InvalidInput(reason=msg)
self.configuration.append_config_values(huawei_opts)
- self.xml_file_path = self.configuration.cinder_huawei_conf_file
- self.hypermetro_devices = self.configuration.hypermetro_devices
+ self.huawei_conf = huawei_conf.HuaweiConf(self.configuration)
+ self.metro_flag = False
def do_setup(self, context):
"""Instantiate common class and login storage system."""
- self.restclient = rest_client.RestClient(self.configuration)
- return self.restclient.login()
+ # Set huawei private configuration into Configuration object.
+ self.huawei_conf.update_config_value()
+ # init local client
+ self.client = rest_client.RestClient(self.configuration,
+ self.configuration.san_address,
+ self.configuration.san_user,
+ self.configuration.san_password)
+ self.client.login()
+ # init remote client
+ metro_san_address = self.configuration.safe_get("metro_san_address")
+ metro_san_user = self.configuration.safe_get("metro_san_user")
+ metro_san_password = self.configuration.safe_get("metro_san_password")
+ if metro_san_address and metro_san_user and metro_san_password:
+ self.metro_flag = True
+ metro_san_address = metro_san_address.split(";")
+ self.rmt_client = rest_client.RestClient(self.configuration,
+ metro_san_address,
+ metro_san_user,
+ metro_san_password)
+ self.rmt_client.login()
def check_for_setup_error(self):
- """Check configuration file."""
- return huawei_utils.check_conf_file(self.xml_file_path)
+ pass
def get_volume_stats(self, refresh=False):
- """Get volume status."""
- return self.restclient.update_volume_stats()
+ """Get volume status and reload huawei config file."""
+ self.huawei_conf.update_config_value()
+ if self.metro_flag:
+ self.rmt_client.get_all_pools()
+ return self.client.update_volume_stats()
- @utils.synchronized('huawei', external=True)
- def create_volume(self, volume):
- """Create a volume."""
- opts = huawei_utils.get_volume_params(volume)
- smartx_opts = smartx.SmartX().get_smartx_specs_opts(opts)
- params = huawei_utils.get_lun_params(self.xml_file_path,
- smartx_opts)
- pool_name = volume_utils.extract_host(volume['host'],
- level='pool')
- pools = self.restclient.find_all_pools()
- pool_info = self.restclient.find_pool_info(pool_name, pools)
- if not pool_info:
- # The following code is to keep compatibility with old version of
- # Huawei driver.
- pool_names = huawei_utils.get_pools(self.xml_file_path)
- for pool_name in pool_names.split(";"):
- pool_info = self.restclient.find_pool_info(pool_name,
- pools)
- if pool_info:
- break
+ def _get_volume_type(self, volume):
+ volume_type = None
+ type_id = volume['volume_type_id']
+ if type_id:
+ ctxt = context.get_admin_context()
+ volume_type = volume_types.get_volume_type(ctxt, type_id)
+
+ return volume_type
+
+ def _get_volume_params(self, volume_type):
+ """Return the parameters for creating the volume."""
+ specs = {}
+ if volume_type:
+ specs = dict(volume_type).get('extra_specs')
+
+ opts = self._get_volume_params_from_specs(specs)
+ return opts
+
+ def _get_volume_params_from_specs(self, specs):
+ """Return the volume parameters from extra specs."""
+ opts_capability = {
+ 'smarttier': False,
+ 'smartcache': False,
+ 'smartpartition': False,
+ 'thin_provisioning_support': False,
+ 'thick_provisioning_support': False,
+ 'hypermetro': False,
+ }
- volume_name = huawei_utils.encode_name(volume['id'])
- volume_description = volume['name']
- volume_size = huawei_utils.get_volume_size(volume)
+ opts_value = {
+ 'policy': None,
+ 'partitionname': None,
+ 'cachename': None,
+ }
- LOG.info(_LI(
- 'Create volume: %(volume)s, size: %(size)s.'),
- {'volume': volume_name,
- 'size': volume_size})
+ opts_associate = {
+ 'smarttier': 'policy',
+ 'smartcache': 'cachename',
+ 'smartpartition': 'partitionname',
+ }
- params['pool_id'] = pool_info['ID']
- params['volume_size'] = volume_size
- params['volume_description'] = volume_description
+ opts = self._get_opts_from_specs(opts_capability,
+ opts_value,
+ opts_associate,
+ specs)
+ opts = smartx.SmartX().get_smartx_specs_opts(opts)
+ LOG.debug('volume opts %(opts)s.', {'opts': opts})
+ return opts
+
+ def _get_opts_from_specs(self, opts_capability, opts_value,
+ opts_associate, specs):
+ """Get the well defined extra specs."""
+ opts = {}
+ opts.update(opts_capability)
+ opts.update(opts_value)
+
+ for key, value in specs.items():
+ # Get the scope, if is using scope format.
+ scope = None
+ key_split = key.split(':')
+ if len(key_split) > 2 and key_split[0] != "capabilities":
+ continue
+
+ if len(key_split) == 1:
+ key = key_split[0].lower()
+ else:
+ scope = key_split[0].lower()
+ key = key_split[1].lower()
+
+ if ((not scope or scope == 'capabilities')
+ and key in opts_capability):
+ words = value.split()
+ if not (words and len(words) == 2 and words[0] == '<is>'):
+ LOG.error(_LE("Extra specs must be specified as "
+ "capabilities:%s='<is> True' or "
+ "'<is> true'."), key)
+ else:
+ opts[key] = words[1].lower()
+
+ if ((scope in opts_capability)
+ and (key in opts_value)
+ and (scope in opts_associate)
+ and (opts_associate[scope] == key)):
+ opts[key] = value
+
+ return opts
+
+ def _get_lun_params(self, volume, opts):
+ pool_name = volume_utils.extract_host(volume['host'], level='pool')
+ params = {
+ 'TYPE': '11',
+ 'NAME': huawei_utils.encode_name(volume['id']),
+ 'PARENTTYPE': '216',
+ 'PARENTID': self.client.get_pool_id(volume, pool_name),
+ 'DESCRIPTION': volume['name'],
+ 'ALLOCTYPE': opts.get('LUNType', self.configuration.lun_type),
+ 'CAPACITY': huawei_utils.get_volume_size(volume),
+ 'WRITEPOLICY': self.configuration.lun_write_type,
+ 'MIRRORPOLICY': self.configuration.lun_mirror_switch,
+ 'PREFETCHPOLICY': self.configuration.lun_prefetch_type,
+ 'PREFETCHVALUE': self.configuration.lun_prefetch_value,
+ 'DATATRANSFERPOLICY':
+ opts.get('policy', self.configuration.lun_policy),
+ 'READCACHEPOLICY': self.configuration.lun_read_cache_policy,
+ 'WRITECACHEPOLICY': self.configuration.lun_write_cache_policy, }
+
+ LOG.info(_LI('volume: %(volume)s, lun params: %(params)s.'),
+ {'volume': volume['id'], 'params': params})
+ return params
+
+ def _create_volume(self, volume, lun_params):
+ # Create LUN on the array.
+ model_update = {}
+ lun_info = self.client.create_lun(lun_params)
+ model_update['provider_location'] = lun_info['ID']
- # Prepare LUN parameters.
- lun_param = huawei_utils.init_lun_parameters(volume_name, params)
+ metadata = huawei_utils.get_volume_metadata(volume)
+ model_update['metadata'] = metadata
+ return lun_info, model_update
- # Create LUN on the array.
- lun_info = self.restclient.create_volume(lun_param)
+ def create_volume(self, volume):
+ """Create a volume."""
+ volume_type = self._get_volume_type(volume)
+ opts = self._get_volume_params(volume_type)
+ lun_params = self._get_lun_params(volume, opts)
+ lun_info, model_update = self._create_volume(volume, lun_params)
lun_id = lun_info['ID']
try:
- qos = huawei_utils.get_volume_qos(volume)
+ qos = smartx.SmartQos.get_qos_by_volume_type(volume_type)
if qos:
- smart_qos = smartx.SmartQos(self.restclient)
- smart_qos.create_qos(qos, lun_id)
- smartpartition = smartx.SmartPartition(self.restclient)
+ smart_qos = smartx.SmartQos(self.client)
+ smart_qos.add(qos, lun_id)
+
+ smartpartition = smartx.SmartPartition(self.client)
smartpartition.add(opts, lun_id)
- smartcache = smartx.SmartCache(self.restclient)
+ smartcache = smartx.SmartCache(self.client)
smartcache.add(opts, lun_id)
except Exception as err:
self._delete_lun_with_check(lun_id)
- raise exception.InvalidInput(
- reason=_('Create volume error. Because %s.') % err)
+ msg = _('Create volume error. Because %s.') % six.text_type(err)
+ raise exception.VolumeBackendAPIException(data=msg)
- # Update the metadata.
- LOG.info(_LI('Create volume option: %s.'), opts)
- metadata = huawei_utils.get_volume_metadata(volume)
- if opts.get('hypermetro'):
- hyperm = hypermetro.HuaweiHyperMetro(self.restclient, None,
- self.configuration)
+ if (opts.get('hypermetro') and opts.get('hypermetro') == 'true'):
+ metro = hypermetro.HuaweiHyperMetro(self.client,
+ self.rmt_client,
+ self.configuration,
+ self.db)
try:
- metro_id, remote_lun_id = hyperm.create_hypermetro(lun_id,
- lun_param)
+ metro_info = metro.create_hypermetro(lun_id, lun_params)
+ model_update['metadata'].update(metro_info)
except exception.VolumeBackendAPIException as err:
- LOG.exception(_LE('Create hypermetro error: %s.'), err)
+ LOG.error(_LE('Create hypermetro error: %s.'), err)
self._delete_lun_with_check(lun_id)
raise
- LOG.info(_LI("Hypermetro id: %(metro_id)s. "
- "Remote lun id: %(remote_lun_id)s."),
- {'metro_id': metro_id,
- 'remote_lun_id': remote_lun_id})
+ return model_update
- metadata.update({'hypermetro_id': metro_id,
- 'remote_lun_id': remote_lun_id})
+ def _delete_volume(self, volume):
+ lun_id = volume.get('provider_location')
+ if not lun_id:
+ return
- return {'provider_location': lun_id,
- 'ID': lun_id,
- 'metadata': metadata}
+ lun_group_ids = self.client.get_lungroupids_by_lunid(lun_id)
+ if lun_group_ids and len(lun_group_ids) == 1:
+ self.client.remove_lun_from_lungroup(lun_group_ids[0], lun_id)
+
+ self.client.delete_lun(lun_id)
- @utils.synchronized('huawei', external=True)
def delete_volume(self, volume):
"""Delete a volume.
Secondly, remove associate from QoS policy.
Thirdly, remove the lun.
"""
- name = huawei_utils.encode_name(volume['id'])
lun_id = volume.get('provider_location')
- LOG.info(_LI('Delete volume: %(name)s, array lun id: %(lun_id)s.'),
- {'name': name, 'lun_id': lun_id},)
- if lun_id:
- if self.restclient.check_lun_exist(lun_id):
- qos_id = self.restclient.get_qosid_by_lunid(lun_id)
- if qos_id:
- self.remove_qos_lun(lun_id, qos_id)
-
- metadata = huawei_utils.get_volume_metadata(volume)
- if 'hypermetro_id' in metadata:
- hyperm = hypermetro.HuaweiHyperMetro(self.restclient, None,
- self.configuration)
- try:
- hyperm.delete_hypermetro(volume)
- except exception.VolumeBackendAPIException as err:
- LOG.exception(_LE('Delete hypermetro error: %s.'), err)
- self.restclient.delete_lun(lun_id)
- raise
-
- self.restclient.delete_lun(lun_id)
- else:
+ if not lun_id or not self.client.check_lun_exist(lun_id):
LOG.warning(_LW("Can't find lun %s on the array."), lun_id)
return False
- return True
+ qos_id = self.client.get_qosid_by_lunid(lun_id)
+ if qos_id:
+ smart_qos = smartx.SmartQos(self.client)
+ smart_qos.remove(qos_id, lun_id)
- def remove_qos_lun(self, lun_id, qos_id):
- lun_list = self.restclient.get_lun_list_in_qos(qos_id)
- lun_count = len(lun_list)
- if lun_count <= 1:
- qos = smartx.SmartQos(self.restclient)
- qos.delete_qos(qos_id)
- else:
- self.restclient.remove_lun_from_qos(lun_id,
- lun_list,
- qos_id)
+ metadata = huawei_utils.get_volume_metadata(volume)
+ if 'hypermetro_id' in metadata:
+ metro = hypermetro.HuaweiHyperMetro(self.client,
+ self.rmt_client,
+ self.configuration,
+ self.db)
+ try:
+ metro.delete_hypermetro(volume)
+ except exception.VolumeBackendAPIException as err:
+ LOG.error(_LE('Delete hypermetro error: %s.'), err)
+ self._delete_volume(volume)
+ raise
+
+ self._delete_volume(volume)
+
+ return True
def _delete_lun_with_check(self, lun_id):
- if lun_id:
- if self.restclient.check_lun_exist(lun_id):
- qos_id = self.restclient.get_qosid_by_lunid(lun_id)
- if qos_id:
- self.remove_qos_lun(lun_id, qos_id)
+ if not lun_id:
+ return
- self.restclient.delete_lun(lun_id)
+ if self.client.check_lun_exist(lun_id):
+ qos_id = self.client.get_qosid_by_lunid(lun_id)
+ if qos_id:
+ smart_qos = smartx.SmartQos(self.client)
+ smart_qos.remove(qos_id, lun_id)
+
+ self.client.delete_lun(lun_id)
def _is_lun_migration_complete(self, src_id, dst_id):
- result = self.restclient.get_lun_migration_task()
+ result = self.client.get_lun_migration_task()
found_migration_task = False
- if 'data' in result:
- for item in result['data']:
- if (src_id == item['PARENTID']
- and dst_id == item['TARGETLUNID']):
- found_migration_task = True
- if constants.MIGRATION_COMPLETE == item['RUNNINGSTATUS']:
- return True
- if constants.MIGRATION_FAULT == item['RUNNINGSTATUS']:
- err_msg = _("Lun migration error.")
- LOG.error(err_msg)
- raise exception.VolumeBackendAPIException(data=err_msg)
+ if 'data' not in result:
+ return False
+
+ for item in result['data']:
+ if (src_id == item['PARENTID'] and dst_id == item['TARGETLUNID']):
+ found_migration_task = True
+ if constants.MIGRATION_COMPLETE == item['RUNNINGSTATUS']:
+ return True
+ if constants.MIGRATION_FAULT == item['RUNNINGSTATUS']:
+ msg = _("Lun migration error.")
+ LOG.error(msg)
+ raise exception.VolumeBackendAPIException(data=msg)
+
if not found_migration_task:
err_msg = _("Cannot find migration task.")
LOG.error(err_msg)
def _is_lun_migration_exist(self, src_id, dst_id):
try:
- result = self.restclient.get_lun_migration_task()
+ result = self.client.get_lun_migration_task()
except Exception:
LOG.error(_LE("Get LUN migration error."))
return False
+
if 'data' in result:
for item in result['data']:
if (src_id == item['PARENTID']
def _migrate_lun(self, src_id, dst_id):
try:
- self.restclient.create_lun_migration(src_id, dst_id)
+ self.client.create_lun_migration(src_id, dst_id)
def _is_lun_migration_complete():
return self._is_lun_migration_complete(src_id, dst_id)
wait_interval = constants.MIGRATION_WAIT_INTERVAL
- huawei_utils.wait_for_condition(self.xml_file_path,
- _is_lun_migration_complete,
- wait_interval)
+ huawei_utils.wait_for_condition(_is_lun_migration_complete,
+ wait_interval,
+ self.configuration.lun_timeout)
# Clean up if migration failed.
except Exception as ex:
raise exception.VolumeBackendAPIException(data=ex)
finally:
if self._is_lun_migration_exist(src_id, dst_id):
- self.restclient.delete_lun_migration(src_id, dst_id)
+ self.client.delete_lun_migration(src_id, dst_id)
self._delete_lun_with_check(dst_id)
LOG.debug("Migrate lun %s successfully.", src_id)
return True
def _wait_volume_ready(self, lun_id):
- event_type = 'LUNReadyWaitInterval'
- wait_interval = huawei_utils.get_wait_interval(self.xml_file_path,
- event_type)
+ wait_interval = self.configuration.lun_ready_wait_interval
def _volume_ready():
- result = self.restclient.get_lun_info(lun_id)
+ result = self.client.get_lun_info(lun_id)
if (result['HEALTHSTATUS'] == constants.STATUS_HEALTH
and result['RUNNINGSTATUS'] == constants.STATUS_VOLUME_READY):
return True
return False
- huawei_utils.wait_for_condition(self.xml_file_path,
- _volume_ready,
+ huawei_utils.wait_for_condition(_volume_ready,
wait_interval,
wait_interval * 10)
def _get_original_status(self, volume):
- if not volume['volume_attachment']:
- return 'available'
- else:
- return 'in-use'
+ return 'in-use' if volume.get('volume_attachment') else 'available'
def update_migrated_volume(self, ctxt, volume, new_volume,
original_volume_status=None):
original_name = huawei_utils.encode_name(volume['id'])
current_name = huawei_utils.encode_name(new_volume['id'])
- lun_id = self.restclient.get_volume_by_name(current_name)
+ lun_id = self.client.get_lun_id_by_name(current_name)
try:
- self.restclient.rename_lun(lun_id, original_name)
+ self.client.rename_lun(lun_id, original_name)
except exception.VolumeBackendAPIException:
LOG.error(_LE('Unable to rename lun %s on array.'), current_name)
return {'_name_id': new_volume['_name_id'] or new_volume['id']}
target_device = host['capabilities']['location_info']
# Source and destination should be on same array.
- if target_device != self.restclient.device_id:
+ if target_device != self.client.device_id:
return False
# Same protocol should be used if volume is in-use.
- protocol = huawei_utils.get_protocol(self.xml_file_path)
+ protocol = self.configuration.san_protocol
if (host['capabilities']['storage_protocol'] != protocol
and self._get_original_status(volume) == 'in-use'):
return False
volume_type = volume_types.get_volume_type(None, type_id)
pool_name = host['capabilities']['pool_name']
- pools = self.restclient.find_all_pools()
- pool_info = self.restclient.find_pool_info(pool_name, pools)
+ pools = self.client.get_all_pools()
+ pool_info = self.client.get_pool_info(pool_name, pools)
src_volume_name = huawei_utils.encode_name(volume['id'])
dst_volume_name = six.text_type(hash(src_volume_name))
src_id = volume.get('provider_location')
- src_lun_params = self.restclient.get_lun_info(src_id)
-
opts = None
qos = None
if new_type:
# If new type exists, use new type.
- opts = huawei_utils._get_extra_spec_value(
- new_type['extra_specs'])
- opts = smartx.SmartX().get_smartx_specs_opts(opts)
+ new_specs = new_type['extra_specs']
+ opts = self._get_volume_params_from_specs(new_specs)
if 'LUNType' not in opts:
- opts['LUNType'] = huawei_utils.find_luntype_in_xml(
- self.xml_file_path)
+ opts['LUNType'] = self.configuration.lun_type
- qos = huawei_utils.get_qos_by_volume_type(new_type)
+ qos = smartx.SmartQos.get_qos_by_volume_type(new_type)
elif volume_type:
- qos = huawei_utils.get_qos_by_volume_type(volume_type)
+ qos = smartx.SmartQos.get_qos_by_volume_type(volume_type)
if not opts:
- opts = huawei_utils.get_volume_params(volume)
- opts = smartx.SmartX().get_smartx_specs_opts(opts)
-
- lun_info = self._create_lun_with_extra_feature(pool_info,
- dst_volume_name,
- src_lun_params,
- opts)
+ opts = self._get_volume_params(volume_type)
+
+ lun_info = self.client.get_lun_info(src_id)
+
+ policy = lun_info['DATATRANSFERPOLICY']
+ if opts['policy']:
+ policy = opts['policy']
+ lun_params = {
+ 'NAME': dst_volume_name,
+ 'PARENTID': pool_info['ID'],
+ 'DESCRIPTION': lun_info['DESCRIPTION'],
+ 'ALLOCTYPE': opts.get('LUNType', lun_info['ALLOCTYPE']),
+ 'CAPACITY': lun_info['CAPACITY'],
+ 'WRITEPOLICY': lun_info['WRITEPOLICY'],
+ 'MIRRORPOLICY': lun_info['MIRRORPOLICY'],
+ 'PREFETCHPOLICY': lun_info['PREFETCHPOLICY'],
+ 'PREFETCHVALUE': lun_info['PREFETCHVALUE'],
+ 'DATATRANSFERPOLICY': policy,
+ 'READCACHEPOLICY': lun_info['READCACHEPOLICY'],
+ 'WRITECACHEPOLICY': lun_info['WRITECACHEPOLICY'],
+ 'OWNINGCONTROLLER': lun_info['OWNINGCONTROLLER'], }
+
+ lun_info = self.client.create_lun(lun_params)
lun_id = lun_info['ID']
if qos:
LOG.info(_LI('QoS: %s.'), qos)
- SmartQos = smartx.SmartQos(self.restclient)
- SmartQos.create_qos(qos, lun_id)
+ SmartQos = smartx.SmartQos(self.client)
+ SmartQos.add(qos, lun_id)
if opts:
- smartpartition = smartx.SmartPartition(self.restclient)
+ smartpartition = smartx.SmartPartition(self.client)
smartpartition.add(opts, lun_id)
- smartcache = smartx.SmartCache(self.restclient)
+ smartcache = smartx.SmartCache(self.client)
smartcache.add(opts, lun_id)
dst_id = lun_info['ID']
return moved, {}
- def _create_lun_with_extra_feature(self, pool_info,
- lun_name,
- lun_params,
- spec_opts):
- LOG.info(_LI('Create a new lun %s for migration.'), lun_name)
-
- # Prepare lun parameters.
- lunparam = {"TYPE": '11',
- "NAME": lun_name,
- "PARENTTYPE": '216',
- "PARENTID": pool_info['ID'],
- "ALLOCTYPE": lun_params['ALLOCTYPE'],
- "CAPACITY": lun_params['CAPACITY'],
- "WRITEPOLICY": lun_params['WRITEPOLICY'],
- "MIRRORPOLICY": lun_params['MIRRORPOLICY'],
- "PREFETCHPOLICY": lun_params['PREFETCHPOLICY'],
- "PREFETCHVALUE": lun_params['PREFETCHVALUE'],
- "DATATRANSFERPOLICY": '0',
- "READCACHEPOLICY": lun_params['READCACHEPOLICY'],
- "WRITECACHEPOLICY": lun_params['WRITECACHEPOLICY'],
- "OWNINGCONTROLLER": lun_params['OWNINGCONTROLLER'],
- }
- if 'LUNType' in spec_opts:
- lunparam['ALLOCTYPE'] = spec_opts['LUNType']
- if spec_opts['policy']:
- lunparam['DATATRANSFERPOLICY'] = spec_opts['policy']
-
- lun_info = self.restclient.create_volume(lunparam)
- return lun_info
-
def create_volume_from_snapshot(self, volume, snapshot):
"""Create a volume from a snapshot.
The time needed increases as volume size does.
"""
snapshotname = huawei_utils.encode_name(snapshot['id'])
-
snapshot_id = snapshot.get('provider_location')
if snapshot_id is None:
- snapshot_id = self.restclient.get_snapshotid_by_name(snapshotname)
- if snapshot_id is None:
- err_msg = (_(
- 'create_volume_from_snapshot: Snapshot %(name)s '
- 'does not exist.')
- % {'name': snapshotname})
- LOG.error(err_msg)
- raise exception.VolumeBackendAPIException(data=err_msg)
-
- lun_info = self.create_volume(volume)
-
- tgt_lun_id = lun_info['ID']
- luncopy_name = huawei_utils.encode_name(volume['id'])
+ snapshot_id = self.client.get_snapshot_id_by_name(snapshotname)
+ if snapshot_id is None:
+ err_msg = (_(
+ 'create_volume_from_snapshot: Snapshot %(name)s '
+ 'does not exist.')
+ % {'name': snapshotname})
+ LOG.error(err_msg)
+ raise exception.VolumeBackendAPIException(data=err_msg)
+ model_update = self.create_volume(volume)
+ tgt_lun_id = model_update['provider_location']
+ luncopy_name = huawei_utils.encode_name(volume['id'])
LOG.info(_LI(
'create_volume_from_snapshot: src_lun_id: %(src_lun_id)s, '
'tgt_lun_id: %(tgt_lun_id)s, copy_name: %(copy_name)s.'),
'tgt_lun_id': tgt_lun_id,
'copy_name': luncopy_name})
- event_type = 'LUNReadyWaitInterval'
-
- wait_interval = huawei_utils.get_wait_interval(self.xml_file_path,
- event_type)
+ wait_interval = self.configuration.lun_ready_wait_interval
def _volume_ready():
- result = self.restclient.get_lun_info(tgt_lun_id)
+ result = self.client.get_lun_info(tgt_lun_id)
if (result['HEALTHSTATUS'] == constants.STATUS_HEALTH
and result['RUNNINGSTATUS'] == constants.STATUS_VOLUME_READY):
return True
return False
- huawei_utils.wait_for_condition(self.xml_file_path,
- _volume_ready,
+ huawei_utils.wait_for_condition(_volume_ready,
wait_interval,
wait_interval * 10)
self._copy_volume(volume, luncopy_name,
snapshot_id, tgt_lun_id)
- return {'ID': lun_info['ID'],
- 'lun_info': lun_info}
+ return model_update
def create_cloned_volume(self, volume, src_vref):
"""Clone a new volume from an existing volume."""
+ if src_vref.get('provider_location') is None:
+ msg = (_("Can't find lun id from db, volume: %(id)s") %
+ {"id": volume['id']})
+ LOG.error(msg)
+ raise exception.VolumeBackendAPIException(data=msg)
+
# Form the snapshot structure.
snapshot = {'id': uuid.uuid4().__str__(),
'volume_id': src_vref['id'],
try:
# Create volume from snapshot.
- lun_info = self.create_volume_from_snapshot(volume, snapshot)
+ model_update = self.create_volume_from_snapshot(volume, snapshot)
finally:
try:
# Delete snapshot.
{'snapshot_id': snapshot['id'],
'volume_id': src_vref['id']},)
- return {'provider_location': lun_info['ID'],
- 'lun_info': lun_info}
+ return model_update
- @utils.synchronized('huawei', external=True)
def extend_volume(self, volume, new_size):
"""Extend a volume."""
- volume_size = huawei_utils.get_volume_size(volume)
- new_volume_size = int(new_size) * units.Gi / 512
+ lun_id = volume.get('provider_location')
+ if not lun_id:
+ msg = (_("Can't find lun id from db, volume: %(id)s") %
+ {"id": volume['id']})
+ LOG.error(msg)
+ raise exception.VolumeBackendAPIException(data=msg)
+
+ old_size = huawei_utils.get_volume_size(volume)
+ new_size = int(new_size) * units.Gi / 512
volume_name = huawei_utils.encode_name(volume['id'])
LOG.info(_LI(
- 'Extend volume: %(volumename)s, oldsize:'
- ' %(oldsize)s newsize: %(newsize)s.'),
+ 'Extend volume: %(volumename)s, '
+ 'oldsize: %(oldsize)s, newsize: %(newsize)s.'),
{'volumename': volume_name,
- 'oldsize': volume_size,
- 'newsize': new_volume_size},)
-
- lun_id = self.restclient.get_lunid(volume, volume_name)
- luninfo = self.restclient.extend_volume(lun_id, new_volume_size)
+ 'oldsize': old_size,
+ 'newsize': new_size})
- return {'provider_location': luninfo['ID'],
- 'lun_info': luninfo}
+ lun_info = self.client.extend_lun(lun_id, new_size)
+ return {'provider_location': lun_info['ID'],
+ 'lun_info': lun_info}
- @utils.synchronized('huawei', external=True)
def create_snapshot(self, snapshot):
- snapshot_info = self.restclient.create_snapshot(snapshot)
+ volume = snapshot.get('volume')
+ if not volume:
+ msg = (_("Can't find volume id from db, volume: %(id)s") %
+ {"id": volume['id']})
+ LOG.error(msg)
+ raise exception.VolumeBackendAPIException(data=msg)
+
+ volume_name = huawei_utils.encode_name(snapshot['volume_id'])
+ lun_id = self.client.get_lun_id(volume, volume_name)
+ snapshot_name = huawei_utils.encode_name(snapshot['id'])
+ snapshot_description = snapshot['id']
+ snapshot_info = self.client.create_snapshot(lun_id,
+ snapshot_name,
+ snapshot_description)
snapshot_id = snapshot_info['ID']
- self.restclient.activate_snapshot(snapshot_id)
+ self.client.activate_snapshot(snapshot_id)
return {'provider_location': snapshot_info['ID'],
'lun_info': snapshot_info}
- @utils.synchronized('huawei', external=True)
def delete_snapshot(self, snapshot):
snapshotname = huawei_utils.encode_name(snapshot['id'])
volume_name = huawei_utils.encode_name(snapshot['volume_id'])
snapshot_id = snapshot.get('provider_location')
if snapshot_id is None:
- snapshot_id = self.restclient.get_snapshotid_by_name(snapshotname)
+ snapshot_id = self.client.get_snapshot_id_by_name(snapshotname)
if snapshot_id is not None:
- if self.restclient.check_snapshot_exist(snapshot_id):
- self.restclient.stop_snapshot(snapshot_id)
- self.restclient.delete_snapshot(snapshot_id)
+ if self.client.check_snapshot_exist(snapshot_id):
+ self.client.stop_snapshot(snapshot_id)
+ self.client.delete_snapshot(snapshot_id)
else:
LOG.warning(_LW("Can't find snapshot on the array."))
else:
'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)
new_id = new[0]
new_name = new[1]
if old_id:
- self.restclient.remove_lun_from_partition(lun_id, old_id)
+ self.client.remove_lun_from_partition(lun_id, old_id)
if new_id:
- self.restclient.add_lun_to_partition(lun_id, new_id)
+ self.client.add_lun_to_partition(lun_id, new_id)
LOG.info(_LI("Retype LUN(id: %(lun_id)s) smartpartition from "
"(name: %(old_name)s, id: %(old_id)s) to "
"(name: %(new_name)s, id: %(new_id)s) success."),
new_id = new[0]
new_name = new[1]
if old_id:
- self.restclient.remove_lun_from_cache(lun_id, old_id)
+ self.client.remove_lun_from_cache(lun_id, old_id)
if new_id:
- self.restclient.add_lun_to_cache(lun_id, new_id)
+ self.client.add_lun_to_cache(lun_id, new_id)
LOG.info(_LI("Retype LUN(id: %(lun_id)s) smartcache from "
"(name: %(old_name)s, id: %(old_id)s) to "
"(name: %(new_name)s, id: %(new_id)s) successfully."),
if change_opts.get('policy'):
old_policy, new_policy = change_opts['policy']
- self.restclient.change_lun_smarttier(lun_id, new_policy)
+ self.client.change_lun_smarttier(lun_id, new_policy)
LOG.info(_LI("Retype LUN(id: %(lun_id)s) smarttier policy from "
"%(old_policy)s to %(new_policy)s success."),
{'lun_id': lun_id,
old_qos_id = old_qos[0]
old_qos_value = old_qos[1]
if old_qos_id:
- self.remove_qos_lun(lun_id, old_qos_id)
+ smart_qos = smartx.SmartQos(self.client)
+ smart_qos.remove(old_qos_id, lun_id)
if new_qos:
- smart_qos = smartx.SmartQos(self.restclient)
- smart_qos.create_qos(new_qos, lun_id)
+ smart_qos = smartx.SmartQos(self.client)
+ smart_qos.add(new_qos, lun_id)
LOG.info(_LI("Retype LUN(id: %(lun_id)s) smartqos from "
"%(old_qos_value)s to %(new_qos)s success."),
{'lun_id': lun_id,
'LUNType': None,
}
- lun_info = self.restclient.get_lun_info(lun_id)
- lun_opts['LUNType'] = int(lun_info.get('ALLOCTYPE'))
+ lun_info = self.client.get_lun_info(lun_id)
+ lun_opts['LUNType'] = int(lun_info['ALLOCTYPE'])
if lun_info.get('DATATRANSFERPOLICY'):
lun_opts['policy'] = lun_info['DATATRANSFERPOLICY']
if lun_info.get('SMARTCACHEPARTITIONID'):
new_cache_id = None
new_cache_name = new_opts['cachename']
if new_cache_name:
- new_cache_id = self.restclient.get_cache_id_by_name(new_cache_name)
+ new_cache_id = self.client.get_cache_id_by_name(new_cache_name)
if new_cache_id is None:
msg = (_(
"Can't find cache name on the array, cache name is: "
new_partition_id = None
new_partition_name = new_opts['partitionname']
if new_partition_name:
- new_partition_id = self.restclient.get_partition_id_by_name(
+ new_partition_id = self.client.get_partition_id_by_name(
new_partition_name)
if new_partition_id is None:
msg = (_(
if old_cache_id != new_cache_id:
old_cache_name = None
if old_cache_id:
- cache_info = self.restclient.get_cache_info_by_id(old_cache_id)
+ cache_info = self.client.get_cache_info_by_id(old_cache_id)
old_cache_name = cache_info['NAME']
change_opts['cacheid'] = ([old_cache_id, old_cache_name],
[new_cache_id, new_cache_name])
if old_partition_id != new_partition_id:
old_partition_name = None
if old_partition_id:
- partition_info = self.restclient.get_partition_info_by_id(
+ partition_info = self.client.get_partition_info_by_id(
old_partition_id)
old_partition_name = partition_info['NAME']
change_opts['partitionid'] = ([old_partition_id,
new_partition_name])
# smartqos
- new_qos = huawei_utils.get_qos_by_volume_type(new_type)
- old_qos_id = self.restclient.get_qosid_by_lunid(lun_id)
+ new_qos = smartx.SmartQos.get_qos_by_volume_type(new_type)
+ old_qos_id = self.client.get_qosid_by_lunid(lun_id)
old_qos = self._get_qos_specs_from_array(old_qos_id)
if old_qos != new_qos:
change_opts['qos'] = ([old_qos_id, old_qos], new_qos)
'qos': None,
'host': None,
'LUNType': None,
+ 'replication_enabled': None,
+ 'replication_type': 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)
+ new_opts = self._get_volume_params_from_specs(new_specs)
if 'LUNType' not in new_opts:
- new_opts['LUNType'] = huawei_utils.find_luntype_in_xml(
- self.xml_file_path)
+ new_opts['LUNType'] = self.configuration.lun_type
if volume['host'] != host['host']:
migration = True
qos = {}
qos_info = {}
if qos_id:
- qos_info = self.restclient.get_qos_info(qos_id)
+ qos_info = self.client.get_qos_info(qos_id)
for key, value in qos_info.items():
if key.upper() in constants.QOS_KEYS:
pass
def _copy_volume(self, volume, copy_name, src_lun, tgt_lun):
- luncopy_id = self.restclient.create_luncopy(copy_name,
- src_lun, tgt_lun)
- event_type = 'LUNcopyWaitInterval'
- wait_interval = huawei_utils.get_wait_interval(self.xml_file_path,
- event_type)
+ luncopy_id = self.client.create_luncopy(copy_name,
+ src_lun,
+ tgt_lun)
+ wait_interval = self.configuration.lun_copy_wait_interval
try:
- self.restclient.start_luncopy(luncopy_id)
+ self.client.start_luncopy(luncopy_id)
def _luncopy_complete():
- luncopy_info = self.restclient.get_luncopy_info(luncopy_id)
+ luncopy_info = self.client.get_luncopy_info(luncopy_id)
if luncopy_info['status'] == constants.STATUS_LUNCOPY_READY:
# luncopy_info['status'] means for the running status of
# the luncopy. If luncopy_info['status'] is equal to '40',
'luncopystate': luncopy_info['state']},)
LOG.error(err_msg)
raise exception.VolumeBackendAPIException(data=err_msg)
- huawei_utils.wait_for_condition(self.xml_file_path,
- _luncopy_complete,
- wait_interval)
+ huawei_utils.wait_for_condition(_luncopy_complete,
+ wait_interval,
+ self.configuration.lun_timeout)
except Exception:
with excutils.save_and_reraise_exception():
- self.restclient.delete_luncopy(luncopy_id)
+ self.client.delete_luncopy(luncopy_id)
self.delete_volume(volume)
- self.restclient.delete_luncopy(luncopy_id)
+ self.client.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 exists in a HyperMetroPair.
try:
- hypermetro_pairs = self.restclient.get_hypermetro_pairs()
+ hypermetro_pairs = self.client.get_hypermetro_pairs()
except exception.VolumeBackendAPIException:
msg = _("Failed to get HyperMetroPair.")
raise exception.ManageExistingInvalidReference(
# Check whether the LUN exists in a SplitMirror.
try:
- split_mirrors = self.restclient.get_split_mirrors()
+ split_mirrors = self.client.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,
for mirror in split_mirrors:
try:
- target_luns = self.restclient.get_target_luns(mirror.get('ID'))
+ target_luns = self.client.get_target_luns(mirror.get('ID'))
except exception.VolumeBackendAPIException:
msg = _("Failed to get target LUN of SplitMirror.")
raise exception.VolumeBackendAPIException(data=msg)
# Check whether the LUN exists in a migration task.
try:
- migration_tasks = self.restclient.get_migration_task()
+ migration_tasks = self.client.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,
existing_ref=external_ref, reason=msg)
# Check whether the LUN exists in a LUN mirror.
- if self.restclient.is_lun_in_mirror(lun_id):
+ if self.client.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(
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)
+ new_opts = self._get_volume_params_from_specs(new_specs)
if ('LUNType' in new_opts and
old_opts['LUNType'] != new_opts['LUNType']):
msg = (_("Can't import LUN %(lun_id)s to Cinder. "
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)
+ self.client.rename_lun(lun_id, new_name, # pylint: disable=E1121
+ description)
return {'provider_location': lun_id}
raise exception.ManageExistingInvalidReference(
existing_ref=external_ref, reason=msg)
- lun_id = id or self.restclient.get_volume_by_name(name)
+ lun_id = id or self.client.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)
+ lun_info = self.client.get_lun_info(lun_id)
return lun_info
def unmanage(self, volume):
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)
+ lun_id = self.client.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)
{'lun_name': lun_name,
'new_name': new_name})
try:
- self.restclient.rename_lun(lun_id, new_name)
+ self.client.rename_lun(lun_id, new_name)
except Exception:
LOG.warning(_LW("Rename lun %(lun_id)s fails when "
"unmanaging volume %(volume)s."),
Version history:
1.0.0 - Initial driver
- 1.1.0 - Provide Huawei OceanStor 18000 storage volume driver
+ 1.1.0 - Provide Huawei OceanStor storage 18000 driver
1.1.1 - Code refactor
CHAP support
Multiple pools support
Volume retype support
2.0.0 - Rename to HuaweiISCSIDriver
2.0.1 - Manage/unmanage volume support
+ 2.0.2 - Refactor HuaweiISCSIDriver
"""
- VERSION = "2.0.1"
+ VERSION = "2.0.2"
def __init__(self, *args, **kwargs):
super(HuaweiISCSIDriver, self).__init__(*args, **kwargs)
(iscsi_iqns,
target_ips,
- portgroup_id) = self.restclient.get_iscsi_params(self.xml_file_path,
- connector)
+ portgroup_id) = self.client.get_iscsi_params(connector)
LOG.info(_LI('initialize_connection, iscsi_iqn: %(iscsi_iqn)s, '
'target_ip: %(target_ip)s, '
'portgroup_id: %(portgroup_id)s.'),
'portgroup_id': portgroup_id},)
# Create hostgroup if not exist.
- host_name = connector['host']
- host_name_before_hash = None
- if host_name and (len(host_name) > constants.MAX_HOSTNAME_LENGTH):
- host_name_before_hash = host_name
- host_name = six.text_type(hash(host_name))
- host_id = self.restclient.add_host_with_check(host_name,
- host_name_before_hash)
+ original_host_name = connector['host']
+ host_name = huawei_utils.encode_host_name(original_host_name)
+ host_id = self.client.add_host_with_check(host_name,
+ original_host_name)
# Add initiator to the host.
- self.restclient.ensure_initiator_added(self.xml_file_path,
- initiator_name,
- host_id)
- hostgroup_id = self.restclient.add_host_into_hostgroup(host_id)
+ self.client.ensure_initiator_added(initiator_name,
+ host_id)
+ hostgroup_id = self.client.add_host_to_hostgroup(host_id)
- lun_id = self.restclient.get_lunid(volume, volume_name)
+ lun_id = self.client.get_lun_id(volume, volume_name)
# Mapping lungroup and hostgroup to view.
- self.restclient.do_mapping(lun_id, hostgroup_id,
- host_id, portgroup_id)
+ self.client.do_mapping(lun_id, hostgroup_id,
+ host_id, portgroup_id)
- hostlun_id = self.restclient.find_host_lun_id(host_id, lun_id)
+ hostlun_id = self.client.get_host_lun_id(host_id, lun_id)
LOG.info(_LI("initialize_connection, host lun id is: %s."),
hostlun_id)
- iscsi_conf = huawei_utils.get_iscsi_conf(self.xml_file_path)
- chapinfo = self.restclient.find_chap_info(iscsi_conf,
- initiator_name)
+ chapinfo = self.client.find_chap_info(self.configuration.iscsi_info,
+ initiator_name)
+
# Return iSCSI properties.
properties = {}
properties['target_discovered'] = False
'ini': initiator_name,
'lunid': lun_id},)
- iscsi_conf = huawei_utils.get_iscsi_conf(self.xml_file_path)
portgroup = None
portgroup_id = None
view_id = None
left_lunnum = -1
- for ini in iscsi_conf['Initiator']:
+ for ini in self.configuration.iscsi_info:
if ini['Name'] == initiator_name:
for key in ini:
if key == 'TargetPortGroup':
break
if portgroup:
- portgroup_id = self.restclient.find_tgt_port_group(portgroup)
- if host_name and (len(host_name) > constants.MAX_HOSTNAME_LENGTH):
- host_name = six.text_type(hash(host_name))
- host_id = self.restclient.find_host(host_name)
+ portgroup_id = self.client.get_tgt_port_group(portgroup)
+ host_name = huawei_utils.encode_host_name(host_name)
+ host_id = self.client.get_host_id_by_name(host_name)
if host_id:
mapping_view_name = constants.MAPPING_VIEW_PREFIX + host_id
- view_id = self.restclient.find_mapping_view(mapping_view_name)
+ view_id = self.client.find_mapping_view(mapping_view_name)
if view_id:
- lungroup_id = self.restclient.find_lungroup_from_map(view_id)
+ lungroup_id = self.client.find_lungroup_from_map(view_id)
# Remove lun from lungroup.
- if lun_id and self.restclient.check_lun_exist(lun_id):
+ if lun_id and self.client.check_lun_exist(lun_id):
if lungroup_id:
- lungroup_ids = self.restclient.get_lungroupids_by_lunid(lun_id)
+ lungroup_ids = self.client.get_lungroupids_by_lunid(lun_id)
if lungroup_id in lungroup_ids:
- self.restclient.remove_lun_from_lungroup(lungroup_id,
- lun_id)
+ self.client.remove_lun_from_lungroup(lungroup_id,
+ lun_id)
else:
LOG.warning(_LW("Lun is not in lungroup. "
"Lun id: %(lun_id)s. "
# Remove portgroup from mapping view if no lun left in lungroup.
if lungroup_id:
- left_lunnum = self.restclient.get_lunnum_from_lungroup(lungroup_id)
+ left_lunnum = self.client.get_lunnum_from_lungroup(lungroup_id)
if portgroup_id and view_id and (int(left_lunnum) <= 0):
- if self.restclient.is_portgroup_associated_to_view(view_id,
- portgroup_id):
- self.restclient.delete_portgroup_mapping_view(view_id,
- portgroup_id)
+ if self.client.is_portgroup_associated_to_view(view_id,
+ portgroup_id):
+ self.client.delete_portgroup_mapping_view(view_id,
+ portgroup_id)
if view_id and (int(left_lunnum) <= 0):
- self.restclient.remove_chap(initiator_name)
-
- if self.restclient.lungroup_associated(view_id, lungroup_id):
- self.restclient.delete_lungroup_mapping_view(view_id,
- lungroup_id)
- self.restclient.delete_lungroup(lungroup_id)
- if self.restclient.is_initiator_associated_to_host(initiator_name):
- self.restclient.remove_iscsi_from_host(initiator_name)
+ self.client.remove_chap(initiator_name)
+
+ if self.client.lungroup_associated(view_id, lungroup_id):
+ self.client.delete_lungroup_mapping_view(view_id,
+ lungroup_id)
+ self.client.delete_lungroup(lungroup_id)
+ if self.client.is_initiator_associated_to_host(initiator_name):
+ self.client.remove_iscsi_from_host(initiator_name)
hostgroup_name = constants.HOSTGROUP_PREFIX + host_id
- hostgroup_id = self.restclient.find_hostgroup(hostgroup_name)
+ hostgroup_id = self.client.find_hostgroup(hostgroup_name)
if hostgroup_id:
- if self.restclient.hostgroup_associated(view_id, hostgroup_id):
- self.restclient.delete_hostgoup_mapping_view(view_id,
- hostgroup_id)
- self.restclient.remove_host_from_hostgroup(hostgroup_id,
- host_id)
- self.restclient.delete_hostgroup(hostgroup_id)
- self.restclient.remove_host(host_id)
- self.restclient.delete_mapping_view(view_id)
+ if self.client.hostgroup_associated(view_id, hostgroup_id):
+ self.client.delete_hostgoup_mapping_view(view_id,
+ hostgroup_id)
+ self.client.remove_host_from_hostgroup(hostgroup_id,
+ host_id)
+ self.client.delete_hostgroup(hostgroup_id)
+ self.client.remove_host(host_id)
+ self.client.delete_mapping_view(view_id)
class HuaweiFCDriver(HuaweiBaseDriver, driver.FibreChannelDriver):
- """FC driver for Huawei storage arrays.
+ """FC driver for Huawei OceanStor storage arrays.
Version history:
1.0.0 - Initial driver
Volume hypermetro support
2.0.0 - Rename to HuaweiFCDriver
2.0.1 - Manage/unmanage volume support
+ 2.0.2 - Refactor HuaweiFCDriver
"""
- VERSION = "2.0.1"
+ VERSION = "2.0.2"
def __init__(self, *args, **kwargs):
super(HuaweiFCDriver, self).__init__(*args, **kwargs)
{'wwpns': wwns,
'volume': volume_name},)
- lun_id = self.restclient.get_lunid(volume, volume_name)
+ lun_id = self.client.get_lun_id(volume, volume_name)
- host_name_before_hash = None
- host_name = connector['host']
- if host_name and (len(host_name) > constants.MAX_HOSTNAME_LENGTH):
- host_name_before_hash = host_name
- host_name = six.text_type(hash(host_name))
+ original_host_name = connector['host']
+ host_name = huawei_utils.encode_host_name(original_host_name)
+ host_id = self.client.add_host_with_check(host_name,
+ original_host_name)
if not self.fcsan_lookup_service:
self.fcsan_lookup_service = fczm_utils.create_lookup_service()
if self.fcsan_lookup_service:
# Use FC switch.
- host_id = self.restclient.add_host_with_check(
- host_name, host_name_before_hash)
+ host_id = self.client.add_host_with_check(
+ host_name, original_host_name)
zone_helper = fc_zone_helper.FCZoneHelper(
- self.fcsan_lookup_service, self.restclient)
+ self.fcsan_lookup_service, self.client)
(tgt_port_wwns, init_targ_map) = (
zone_helper.build_ini_targ_map(wwns))
for ini in init_targ_map:
- self.restclient.ensure_fc_initiator_added(ini, host_id)
+ self.client.ensure_fc_initiator_added(ini, host_id)
else:
# Not use FC switch.
- host_id = self.restclient.add_host_with_check(
- host_name, host_name_before_hash)
+ host_id = self.client.add_host_with_check(
+ host_name, original_host_name)
online_wwns_in_host = (
- self.restclient.get_host_online_fc_initiators(host_id))
- online_free_wwns = self.restclient.get_online_free_wwns()
+ self.client.get_host_online_fc_initiators(host_id))
+ online_free_wwns = self.client.get_online_free_wwns()
for wwn in wwns:
if (wwn not in online_wwns_in_host
and wwn not in online_free_wwns):
wwns_in_host = (
- self.restclient.get_host_fc_initiators(host_id))
+ self.client.get_host_fc_initiators(host_id))
iqns_in_host = (
- self.restclient.get_host_iscsi_initiators(host_id))
+ self.client.get_host_iscsi_initiators(host_id))
if not wwns_in_host and not iqns_in_host:
- self.restclient.remove_host(host_id)
+ self.client.remove_host(host_id)
msg = (_('Can not add FC initiator to host.'))
LOG.error(msg)
for wwn in wwns:
if wwn in online_free_wwns:
- self.restclient.add_fc_port_to_host(host_id, wwn)
+ self.client.add_fc_port_to_host(host_id, wwn)
(tgt_port_wwns, init_targ_map) = (
- self.restclient.get_init_targ_map(wwns))
+ self.client.get_init_targ_map(wwns))
# Add host into hostgroup.
- hostgroup_id = self.restclient.add_host_into_hostgroup(host_id)
- map_info = self.restclient.do_mapping(lun_id,
- hostgroup_id,
- host_id)
- host_lun_id = self.restclient.find_host_lun_id(host_id, lun_id)
+ hostgroup_id = self.client.add_host_to_hostgroup(host_id)
+ map_info = self.client.do_mapping(lun_id,
+ hostgroup_id,
+ host_id)
+ host_lun_id = self.client.get_host_lun_id(host_id, lun_id)
# Return FC properties.
fc_info = {'driver_volume_type': 'fibre_channel',
metadata = huawei_utils.get_volume_metadata(volume)
LOG.info(_LI("initialize_connection, metadata is: %s."), metadata)
if 'hypermetro_id' in metadata:
- hyperm = hypermetro.HuaweiHyperMetro(self.restclient, None,
+ hyperm = hypermetro.HuaweiHyperMetro(self.client,
self.configuration)
rmt_fc_info = hyperm.connect_volume_fc(volume, connector)
same_host_id = self._get_same_hostid(loc_map_info,
rmt_map_info)
- self.restclient.change_hostlun_id(loc_map_info, same_host_id)
+ self.client.change_hostlun_id(loc_map_info, same_host_id)
hyperm.rmt_client.change_hostlun_id(rmt_map_info, same_host_id)
fc_info['data']['target_lun'] = same_host_id
return same_host_id
+ @utils.synchronized('huawei', external=True)
@fczm_utils.RemoveFCZone
def terminate_connection(self, volume, connector, **kwargs):
"""Delete map between a volume and a host."""
'wwns': wwns,
'lunid': lun_id},)
- if host_name and len(host_name) > constants.MAX_HOSTNAME_LENGTH:
- host_name = six.text_type(hash(host_name))
- host_id = self.restclient.find_host(host_name)
+ host_name = huawei_utils.encode_host_name(host_name)
+ host_id = self.client.get_host_id_by_name(host_name)
if host_id:
mapping_view_name = constants.MAPPING_VIEW_PREFIX + host_id
- view_id = self.restclient.find_mapping_view(mapping_view_name)
+ view_id = self.client.find_mapping_view(mapping_view_name)
if view_id:
- lungroup_id = self.restclient.find_lungroup_from_map(view_id)
+ lungroup_id = self.client.find_lungroup_from_map(view_id)
- if lun_id and self.restclient.check_lun_exist(lun_id):
+ if (lun_id is not None
+ and self.client.check_lun_exist(lun_id)):
if lungroup_id:
- lungroup_ids = self.restclient.get_lungroupids_by_lunid(lun_id)
+ lungroup_ids = self.client.get_lungroupids_by_lunid(lun_id)
if lungroup_id in lungroup_ids:
- self.restclient.remove_lun_from_lungroup(lungroup_id,
- lun_id)
+ self.client.remove_lun_from_lungroup(lungroup_id,
+ lun_id)
else:
LOG.warning(_LW("Lun is not in lungroup. "
"Lun id: %(lun_id)s. "
"Lungroup id: %(lungroup_id)s."),
{"lun_id": lun_id,
"lungroup_id": lungroup_id})
+
else:
LOG.warning(_LW("Can't find lun on the array."))
if lungroup_id:
- left_lunnum = self.restclient.get_lunnum_from_lungroup(lungroup_id)
+ left_lunnum = self.client.get_lunnum_from_lungroup(lungroup_id)
if int(left_lunnum) > 0:
fc_info = {'driver_volume_type': 'fibre_channel',
'data': {}}
if self.fcsan_lookup_service:
zone_helper = fc_zone_helper.FCZoneHelper(
- self.fcsan_lookup_service, self.restclient)
+ self.fcsan_lookup_service, self.client)
(tgt_port_wwns, init_targ_map) = (
zone_helper.build_ini_targ_map(wwns))
else:
(tgt_port_wwns, init_targ_map) = (
- self.restclient.get_init_targ_map(wwns))
+ self.client.get_init_targ_map(wwns))
for wwn in wwns:
- if self.restclient.is_fc_initiator_associated_to_host(wwn):
- self.restclient.remove_fc_from_host(wwn)
+ if self.client.is_fc_initiator_associated_to_host(wwn):
+ self.client.remove_fc_from_host(wwn)
if lungroup_id:
- if view_id and self.restclient.lungroup_associated(
+ if view_id and self.client.lungroup_associated(
view_id, lungroup_id):
- self.restclient.delete_lungroup_mapping_view(view_id,
- lungroup_id)
- self.restclient.delete_lungroup(lungroup_id)
+ self.client.delete_lungroup_mapping_view(view_id,
+ lungroup_id)
+ self.client.delete_lungroup(lungroup_id)
if host_id:
hostgroup_name = constants.HOSTGROUP_PREFIX + host_id
- hostgroup_id = self.restclient.find_hostgroup(hostgroup_name)
+ hostgroup_id = self.client.find_hostgroup(hostgroup_name)
if hostgroup_id:
- if view_id and self.restclient.hostgroup_associated(
+ if view_id and self.client.hostgroup_associated(
view_id, hostgroup_id):
- self.restclient.delete_hostgoup_mapping_view(
+ self.client.delete_hostgoup_mapping_view(
view_id, hostgroup_id)
- self.restclient.remove_host_from_hostgroup(
+ self.client.remove_host_from_hostgroup(
hostgroup_id, host_id)
- self.restclient.delete_hostgroup(hostgroup_id)
+ self.client.delete_hostgroup(hostgroup_id)
- if not self.restclient.check_fc_initiators_exist_in_host(
+ if not self.client.check_fc_initiators_exist_in_host(
host_id):
- self.restclient.remove_host(host_id)
+ self.client.remove_host(host_id)
if view_id:
- self.restclient.delete_mapping_view(view_id)
+ self.client.delete_mapping_view(view_id)
fc_info = {'driver_volume_type': 'fibre_channel',
'data': {'target_wwn': tgt_port_wwns,
# Deal with hypermetro connection.
metadata = huawei_utils.get_volume_metadata(volume)
LOG.info(_LI("Detach Volume, metadata is: %s."), metadata)
+
if 'hypermetro_id' in metadata:
- hyperm = hypermetro.HuaweiHyperMetro(self.restclient, None,
+ hyperm = hypermetro.HuaweiHyperMetro(self.client,
self.configuration)
hyperm.disconnect_volume_fc(volume, connector)
-# Copyright (c) 2015 Huawei Technologies Co., Ltd.
+# Copyright (c) 2016 Huawei Technologies Co., Ltd.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
import six
import time
import uuid
-from xml.etree import ElementTree as ET
from oslo_log import log as logging
from oslo_service import loopingcall
from oslo_utils import units
-from cinder import context
from cinder import exception
-from cinder import utils
-from cinder.i18n import _, _LE, _LI
+from cinder.i18n import _
+from cinder import objects
from cinder.volume.drivers.huawei import constants
-from cinder.volume import qos_specs
-from cinder.volume import volume_types
LOG = logging.getLogger(__name__)
-opts_capability = {
- 'smarttier': False,
- 'smartcache': False,
- 'smartpartition': False,
- 'thin_provisioning_support': False,
- 'thick_provisioning_support': False,
- 'hypermetro': False,
-}
-
-
-opts_value = {
- 'policy': None,
- 'partitionname': None,
- 'cachename': None,
-}
-
-
-opts_associate = {
- 'smarttier': 'policy',
- 'smartcache': 'cachename',
- 'smartpartition': 'partitionname',
-}
-
-
-def get_volume_params(volume):
- opts = {}
- ctxt = context.get_admin_context()
- type_id = volume['volume_type_id']
- if type_id is not None:
- volume_type = volume_types.get_volume_type(ctxt, type_id)
- specs = dict(volume_type).get('extra_specs')
- opts = _get_extra_spec_value(specs)
- else:
- opts.update(opts_capability)
- opts.update(opts_value)
-
- return opts
-
-
-def _get_extra_spec_value(specs):
- """Return the parameters for creating the volume."""
- opts = {}
- opts.update(opts_capability)
- opts.update(opts_value)
-
- opts = _get_opts_from_specs(opts_capability, opts_value, specs)
- LOG.debug('get_volume_params opts %(opts)s.', {'opts': opts})
-
- return opts
-
-
-def _get_opts_from_specs(opts_capability, opts_value, specs):
- opts = {}
- opts.update(opts_capability)
- opts.update(opts_value)
-
- for key, value in specs.items():
-
- # Get the scope, if is using scope format.
- scope = None
- key_split = key.split(':')
- if len(key_split) > 2 and key_split[0] != "capabilities":
- continue
-
- if len(key_split) == 1:
- key = key_split[0]
- else:
- scope = key_split[0]
- key = key_split[1]
-
- if scope:
- scope = scope.lower()
- if key:
- key = key.lower()
-
- if ((not scope or scope == 'capabilities')
- and key in opts_capability):
-
- words = value.split()
-
- if not (words and len(words) == 2 and words[0] == '<is>'):
- LOG.error(_LE("Extra specs must be specified as "
- "capabilities:%s='<is> True' or "
- "'<is> true'."), key)
- else:
- opts[key] = words[1].lower()
-
- if (scope in opts_capability) and (key in opts_value):
- if (scope in opts_associate) and (opts_associate[scope] == key):
- opts[key] = value
-
- return opts
-
-
-def _get_smartx_specs_params(lunsetinfo, smartx_opts):
- """Get parameters from config file for creating lun."""
- # Default lun set information.
- if 'LUNType' in smartx_opts:
- lunsetinfo['LUNType'] = smartx_opts['LUNType']
- lunsetinfo['policy'] = smartx_opts['policy']
-
- return lunsetinfo
-
-
-def get_lun_params(xml_file_path, smartx_opts):
- lunsetinfo = get_lun_conf_params(xml_file_path)
- lunsetinfo = _get_smartx_specs_params(lunsetinfo, smartx_opts)
- return lunsetinfo
-
-
-def parse_xml_file(xml_file_path):
- """Get root of xml file."""
- try:
- tree = ET.parse(xml_file_path)
- root = tree.getroot()
- return root
- except IOError as err:
- LOG.error(_LE('parse_xml_file: %s.'), err)
- raise
-
-
-def get_xml_item(xml_root, item):
- """Get the given item details.
-
- :param xml_root: The root of xml tree
- :param item: The tag need to get
- :return: A dict contains all the config info of the given item.
- """
- items_list = []
- items = xml_root.findall(item)
- for item in items:
- tmp_dict = {'text': None, 'attrib': {}}
- if item.text:
- tmp_dict['text'] = item.text.strip()
- for key, val in item.attrib.items():
- if val:
- item.attrib[key] = val.strip()
- tmp_dict['attrib'] = item.attrib
- items_list.append(tmp_dict)
- return items_list
-
-
-def get_conf_host_os_type(host_ip, conf):
- """Get host OS type from xml config file.
-
- :param host_ip: The IP of Nova host
- :param config: xml config file
- :return: host OS type
- """
- os_conf = {}
- xml_file_path = conf.cinder_huawei_conf_file
- root = parse_xml_file(xml_file_path)
- hosts_list = get_xml_item(root, 'Host')
- for host in hosts_list:
- os = host['attrib']['OSType'].strip()
- ips = [ip.strip() for ip in host['attrib']['HostIP'].split(',')]
- os_conf[os] = ips
- host_os = None
- for k, v in os_conf.items():
- if host_ip in v:
- host_os = constants.OS_TYPE.get(k, None)
- if not host_os:
- host_os = constants.OS_TYPE['Linux'] # Default OS type.
-
- LOG.debug('_get_host_os_type: Host %(ip)s OS type is %(os)s.',
- {'ip': host_ip, 'os': host_os})
-
- return host_os
-
-
-def get_qos_by_volume_type(volume_type):
- qos = {}
- qos_specs_id = volume_type.get('qos_specs_id')
-
- # We prefer the qos_specs association
- # and override any existing extra-specs settings
- # if present.
- if qos_specs_id is not None:
- kvs = qos_specs.get_qos_specs(context.get_admin_context(),
- qos_specs_id)['specs']
- else:
- return qos
-
- LOG.info(_LI('The QoS sepcs is: %s.'), kvs)
- for key, value in kvs.items():
- if key in constants.HUAWEI_VALID_KEYS:
- if (key.upper() != 'IOTYPE') and (int(value) <= 0):
- err_msg = (_('Qos config is wrong. %(key)s'
- ' must be set greater than 0.')
- % {'key': key})
- LOG.error(err_msg)
- raise exception.VolumeBackendAPIException(data=err_msg)
- elif (key.upper() == 'IOTYPE') and (value not in ['0', '1', '2']):
- raise exception.InvalidInput(
- reason=(_('Illegal value specified for IOTYPE: '
- 'set to either 0, 1, or 2.')))
- else:
- qos[key.upper()] = value
-
- return qos
-
-
-def get_volume_qos(volume):
- qos = {}
- ctxt = context.get_admin_context()
- type_id = volume['volume_type_id']
- if type_id is not None:
- volume_type = volume_types.get_volume_type(ctxt, type_id)
- qos = get_qos_by_volume_type(volume_type)
-
- return qos
-
-
-def _get_volume_type(type_id):
- ctxt = context.get_admin_context()
- return volume_types.get_volume_type(ctxt, type_id)
-
-
-def get_lun_conf_params(xml_file_path):
- """Get parameters from config file for creating lun."""
- lunsetinfo = {
- 'LUNType': 0,
- 'StripUnitSize': '64',
- 'WriteType': '1',
- 'MirrorSwitch': '1',
- 'PrefetchType': '3',
- 'PrefetchValue': '0',
- 'PrefetchTimes': '0',
- 'policy': '0',
- 'readcachepolicy': '2',
- 'writecachepolicy': '5',
- }
- # Default lun set information.
- root = parse_xml_file(xml_file_path)
- luntype = root.findtext('LUN/LUNType')
- if luntype:
- if luntype.strip() in ['Thick', 'Thin']:
- lunsetinfo['LUNType'] = luntype.strip()
- if luntype.strip() == 'Thick':
- lunsetinfo['LUNType'] = 0
- elif luntype.strip() == 'Thin':
- lunsetinfo['LUNType'] = 1
-
- else:
- err_msg = (_(
- "LUNType config is wrong. LUNType must be 'Thin'"
- " or 'Thick'. LUNType: %(fetchtype)s.")
- % {'fetchtype': luntype})
- LOG.error(err_msg)
- raise exception.VolumeBackendAPIException(data=err_msg)
- else:
- lunsetinfo['LUNType'] = 0
-
- stripunitsize = root.findtext('LUN/StripUnitSize')
- if stripunitsize is not None:
- lunsetinfo['StripUnitSize'] = stripunitsize.strip()
- writetype = root.findtext('LUN/WriteType')
- if writetype is not None:
- lunsetinfo['WriteType'] = writetype.strip()
- mirrorswitch = root.findtext('LUN/MirrorSwitch')
- if mirrorswitch is not None:
- lunsetinfo['MirrorSwitch'] = mirrorswitch.strip()
-
- prefetch = root.find('LUN/Prefetch')
- if prefetch is not None and prefetch.attrib['Type']:
- fetchtype = prefetch.attrib['Type']
- if fetchtype in ['0', '1', '2', '3']:
- lunsetinfo['PrefetchType'] = fetchtype.strip()
- typevalue = prefetch.attrib['Value'].strip()
- if lunsetinfo['PrefetchType'] == '1':
- double_value = int(typevalue) * 2
- typevalue_double = six.text_type(double_value)
- lunsetinfo['PrefetchValue'] = typevalue_double
- elif lunsetinfo['PrefetchType'] == '2':
- lunsetinfo['PrefetchValue'] = typevalue
- else:
- err_msg = (_(
- 'PrefetchType config is wrong. PrefetchType'
- ' must be in 0,1,2,3. PrefetchType is: %(fetchtype)s.')
- % {'fetchtype': fetchtype})
- LOG.error(err_msg)
- raise exception.VolumeBackendAPIException(data=err_msg)
- else:
- LOG.info(_LI(
- 'Use default PrefetchType. '
- 'PrefetchType: Intelligent.'))
-
- return lunsetinfo
-
-
-def find_luntype_in_xml(xml_file_path):
- root = parse_xml_file(xml_file_path)
- luntype = root.findtext('LUN/LUNType')
- if luntype:
- if luntype.strip() in ['Thick', 'Thin']:
- if luntype.strip() == 'Thick':
- luntype = constants.THICK_LUNTYPE
- elif luntype.strip() == 'Thin':
- luntype = constants.THIN_LUNTYPE
- else:
- err_msg = (_(
- "LUNType config is wrong. LUNType must be 'Thin'"
- " or 'Thick'. LUNType: %(fetchtype)s.")
- % {'fetchtype': luntype})
- LOG.error(err_msg)
- raise exception.VolumeBackendAPIException(data=err_msg)
- else:
- luntype = constants.THICK_LUNTYPE
- return luntype
-
-
def encode_name(name):
uuid_str = name.replace("-", "")
vol_uuid = uuid.UUID('urn:uuid:%s' % uuid_str)
return newuuid
-def init_lun_parameters(name, parameters):
- """Initialize basic LUN parameters."""
- lunparam = {"TYPE": "11",
- "NAME": name,
- "PARENTTYPE": "216",
- "PARENTID": parameters['pool_id'],
- "DESCRIPTION": parameters['volume_description'],
- "ALLOCTYPE": parameters['LUNType'],
- "CAPACITY": parameters['volume_size'],
- "WRITEPOLICY": parameters['WriteType'],
- "MIRRORPOLICY": parameters['MirrorSwitch'],
- "PREFETCHPOLICY": parameters['PrefetchType'],
- "PREFETCHVALUE": parameters['PrefetchValue'],
- "DATATRANSFERPOLICY": parameters['policy'],
- "READCACHEPOLICY": parameters['readcachepolicy'],
- "WRITECACHEPOLICY": parameters['writecachepolicy'],
- }
-
- return lunparam
-
-
-def volume_in_use(volume):
- """Check if the given volume is in use."""
- return (volume['volume_attachment'] and
- len(volume['volume_attachment']) > 0)
-
+def encode_host_name(name):
+ if name and (len(name) > constants.MAX_HOSTNAME_LENGTH):
+ name = six.text_type(hash(name))
+ return name
-def get_wait_interval(xml_file_path, event_type):
- """Get wait interval from huawei conf file."""
- root = parse_xml_file(xml_file_path)
- wait_interval = root.findtext('LUN/%s' % event_type)
- if wait_interval is None:
- wait_interval = constants.DEFAULT_WAIT_INTERVAL
- LOG.info(_LI(
- "Wait interval for %(event_type)s is not configured in huawei "
- "conf file. Use default: %(default_wait_interval)d."),
- {"event_type": event_type,
- "default_wait_interval": wait_interval})
- return int(wait_interval)
-
-
-def get_default_timeout(xml_file_path):
- """Get timeout from huawei conf file."""
- root = parse_xml_file(xml_file_path)
- timeout = root.findtext('LUN/Timeout')
- if timeout is None:
- timeout = constants.DEFAULT_WAIT_TIMEOUT
- LOG.info(_LI(
- "Timeout is not configured in huawei conf file. "
- "Use default: %(default_timeout)d."),
- {"default_timeout": timeout})
-
- return timeout
-
-
-def wait_for_condition(xml_file_path, func, interval, timeout=None):
+def wait_for_condition(func, interval, timeout):
start_time = time.time()
- if timeout is None:
- timeout = get_default_timeout(xml_file_path)
def _inner():
try:
res = func()
except Exception as ex:
raise exception.VolumeBackendAPIException(data=ex)
+
if res:
raise loopingcall.LoopingCallDone()
timer.start(interval=interval).wait()
-def get_login_info(xml_file_path):
- """Get login IP, user name and password from config file."""
- login_info = {}
- root = parse_xml_file(xml_file_path)
-
- login_info['RestURL'] = root.findtext('Storage/RestURL').strip()
-
- for key in ['UserName', 'UserPassword']:
- node = root.find('Storage/%s' % key)
- node_text = node.text
- login_info[key] = node_text
-
- return login_info
-
-
-def _change_file_mode(filepath):
- utils.execute('chmod', '640', filepath, run_as_root=True)
-
-
-def get_iscsi_conf(xml_file_path):
- """Get iSCSI info from config file."""
- iscsiinfo = {}
- root = parse_xml_file(xml_file_path)
- target_ip = root.findtext('iSCSI/DefaultTargetIP').strip()
- iscsiinfo['DefaultTargetIP'] = target_ip
- initiator_list = []
-
- for dic in root.findall('iSCSI/Initiator'):
- # Strip values of dict.
- tmp_dic = {}
- for k in dic.items():
- tmp_dic[k[0]] = k[1].strip()
-
- initiator_list.append(tmp_dic)
-
- iscsiinfo['Initiator'] = initiator_list
-
- return iscsiinfo
-
-
-def check_qos_high_priority(qos):
- """Check QoS priority."""
- for key, value in qos.items():
- if (key.find('MIN') == 0) or (key.find('LATENCY') == 0):
- return True
-
- return False
-
-
-def check_conf_file(xml_file_path):
- """Check the config file, make sure the essential items are set."""
- root = parse_xml_file(xml_file_path)
- resturl = root.findtext('Storage/RestURL')
- username = root.findtext('Storage/UserName')
- pwd = root.findtext('Storage/UserPassword')
- pool_node = root.findall('LUN/StoragePool')
-
- if (not resturl) or (not username) or (not pwd):
- err_msg = (_(
- 'check_conf_file: Config file invalid. RestURL,'
- ' UserName and UserPassword must be set.'))
- LOG.error(err_msg)
- raise exception.InvalidInput(reason=err_msg)
-
- if not pool_node:
- err_msg = (_(
- 'check_conf_file: Config file invalid. '
- 'StoragePool must be set.'))
- LOG.error(err_msg)
- raise exception.InvalidInput(reason=err_msg)
-
-
def get_volume_size(volume):
"""Calculate the volume size.
return volume_size
-def get_protocol(xml_file_path):
- """Get protocol from huawei conf file."""
- root = parse_xml_file(xml_file_path)
- protocol = root.findtext('Storage/Protocol')
- if not protocol:
- err_msg = (_('Get protocol from huawei conf file error.'))
- LOG.error(err_msg)
- raise exception.InvalidInput(reason=err_msg)
-
- return protocol
-
-
-def get_pools(xml_file_path):
- """Get pools from huawei conf file."""
- root = parse_xml_file(xml_file_path)
- pool_names = root.findtext('LUN/StoragePool')
- if not pool_names:
- msg = _('Invalid resource pool name. '
- 'Please check the config file.')
- LOG.error(msg)
- raise exception.InvalidInput(msg)
- return pool_names
-
-
def get_remote_device_info(valid_hypermetro_devices):
remote_device_info = {}
try:
def get_volume_metadata(volume):
+ if type(volume) is objects.Volume:
+ return volume.metadata
+
if 'volume_metadata' in volume:
metadata = volume.get('volume_metadata')
return {item['key']: item['value'] for item in metadata}
return {}
+
+
+def get_snapshot_metadata_value(snapshot):
+ if type(snapshot) is objects.Snapshot:
+ return snapshot.metadata
+
+ if 'snapshot_metadata' in snapshot:
+ metadata = snapshot.get('snapshot_metadata')
+ return {item['key']: item['value'] for item in metadata}
+
+ return {}
-# Copyright (c) 2015 Huawei Technologies Co., Ltd.
+# Copyright (c) 2016 Huawei Technologies Co., Ltd.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# under the License.
#
-import six
-
from oslo_log import log as logging
from cinder import exception
from cinder.i18n import _, _LI, _LW
from cinder.volume.drivers.huawei import constants
from cinder.volume.drivers.huawei import huawei_utils
-from cinder.volume.drivers.huawei import rest_client
LOG = logging.getLogger(__name__)
class HuaweiHyperMetro(object):
- def __init__(self, client, rmt_client, configuration):
+ def __init__(self, client, rmt_client, configuration, db):
+ self.db = db
self.client = client
self.rmt_client = rmt_client
self.configuration = configuration
- self.xml_file_path = self.configuration.cinder_huawei_conf_file
- def create_hypermetro(self, local_lun_id, lun_param):
+ def create_hypermetro(self, local_lun_id, lun_params):
"""Create hypermetro."""
- metro_devices = self.configuration.hypermetro_devices
- device_info = huawei_utils.get_remote_device_info(metro_devices)
- self.rmt_client = rest_client.RestClient(self.configuration)
- self.rmt_client.login_with_ip(device_info)
try:
# Get the remote pool info.
- config_pool = device_info['StoragePool']
- remote_pool = self.rmt_client.find_all_pools()
- pool = self.rmt_client.find_pool_info(config_pool,
- remote_pool)
- # Create remote lun
- lun_param['PARENTID'] = pool['ID']
- remotelun_info = self.rmt_client.create_volume(lun_param)
+ config_pool = self.configuration.metro_storage_pools
+ remote_pool = self.rmt_client.get_all_pools()
+ pool = self.rmt_client.get_pool_info(config_pool, remote_pool)
+ # Create remote lun.
+ lun_params['pool_id'] = pool['ID']
+ remotelun_info = self.rmt_client.create_lun(lun_params)
remote_lun_id = remotelun_info['ID']
- # Get hypermetro domain
+ # Get hypermetro domain.
try:
- domain_name = device_info['domain_name']
+ domain_name = self.configuration.metro_domain_name
domain_id = self.rmt_client.get_hyper_domain_id(domain_name)
self._wait_volume_ready(remote_lun_id)
hypermetro = self._create_hypermetro_pair(domain_id,
local_lun_id,
remote_lun_id)
- return hypermetro['ID'], remote_lun_id
- except Exception as err:
+ LOG.info(_LI("Hypermetro id: %(metro_id)s. "
+ "Remote lun id: %(remote_lun_id)s."),
+ {'metro_id': hypermetro['ID'],
+ 'remote_lun_id': remote_lun_id})
+
+ return {'hypermetro_id': hypermetro['ID'],
+ 'remote_lun_id': remote_lun_id}
+ except exception.VolumeBackendAPIException as err:
self.rmt_client.delete_lun(remote_lun_id)
msg = _('Create hypermetro error. %s.') % err
raise exception.VolumeBackendAPIException(data=msg)
except exception.VolumeBackendAPIException:
raise
- except Exception as err:
- msg = _("Create remote LUN error. %s.") % err
- LOG.exception(msg)
- raise exception.VolumeBackendAPIException(data=msg)
- finally:
- self.rmt_client.logout()
def delete_hypermetro(self, volume):
"""Delete hypermetro."""
self.client.delete_hypermetro(metro_id)
# Delete remote lun.
- if remote_lun_id:
- metro_devices = self.configuration.hypermetro_devices
- device_info = huawei_utils.get_remote_device_info(metro_devices)
- self.rmt_client = rest_client.RestClient(self.configuration)
- self.rmt_client.login_with_ip(device_info)
-
- try:
- if self.rmt_client.check_lun_exist(remote_lun_id):
- self.rmt_client.delete_lun(remote_lun_id)
- except Exception as err:
- msg = _("Delete remote lun err. %s.") % err
- LOG.exception(msg)
- raise exception.VolumeBackendAPIException(data=msg)
- finally:
- self.rmt_client.logout()
+ if remote_lun_id and self.rmt_client.check_lun_exist(remote_lun_id):
+ self.rmt_client.delete_lun(remote_lun_id)
def _create_hypermetro_pair(self, domain_id, lun_id, remote_lun_id):
"""Create a HyperMetroPair."""
def connect_volume_fc(self, volume, connector):
"""Create map between a volume and a host for FC."""
- self.xml_file_path = self.configuration.cinder_huawei_conf_file
- metro_devices = self.configuration.hypermetro_devices
- device_info = huawei_utils.get_remote_device_info(metro_devices)
- self.rmt_client = rest_client.RestClient(self.configuration)
- self.rmt_client.login_with_ip(device_info)
+ wwns = connector['wwpns']
+ volume_name = huawei_utils.encode_name(volume['id'])
- try:
- wwns = connector['wwpns']
- volume_name = huawei_utils.encode_name(volume['id'])
-
- LOG.info(_LI(
- 'initialize_connection_fc, initiator: %(wwpns)s,'
- ' volume name: %(volume)s.'),
- {'wwpns': wwns,
- 'volume': volume_name})
-
- metadata = huawei_utils.get_volume_metadata(volume)
- lun_id = metadata['remote_lun_id']
-
- if lun_id is None:
- lun_id = self.rmt_client.get_volume_by_name(volume_name)
- if lun_id is None:
- msg = _("Can't get volume id. Volume name: %s.") % volume_name
+ LOG.info(_LI(
+ 'initialize_connection_fc, initiator: %(wwpns)s,'
+ ' volume name: %(volume)s.'),
+ {'wwpns': wwns,
+ 'volume': volume_name})
+
+ metadata = huawei_utils.get_volume_metadata(volume)
+ lun_id = metadata['remote_lun_id']
+
+ if lun_id is None:
+ lun_id = self.rmt_client.get_lun_id_by_name(volume_name)
+ if lun_id is None:
+ msg = _("Can't get volume id. Volume name: %s.") % volume_name
+ LOG.error(msg)
+ raise exception.VolumeBackendAPIException(data=msg)
+
+ original_host_name = connector['host']
+ host_name = huawei_utils.encode_host_name(original_host_name)
+ host_id = self.client.add_host_with_check(host_name,
+ original_host_name)
+
+ # Create hostgroup if not exist.
+ host_id = self.rmt_client.add_host_with_check(
+ host_name, original_host_name)
+
+ online_wwns_in_host = (
+ self.rmt_client.get_host_online_fc_initiators(host_id))
+ online_free_wwns = self.rmt_client.get_online_free_wwns()
+ for wwn in wwns:
+ if (wwn not in online_wwns_in_host
+ and wwn not in online_free_wwns):
+ wwns_in_host = (
+ self.rmt_client.get_host_fc_initiators(host_id))
+ iqns_in_host = (
+ self.rmt_client.get_host_iscsi_initiators(host_id))
+ if not (wwns_in_host or iqns_in_host):
+ self.rmt_client.remove_host(host_id)
+
+ msg = _('Can not add FC port to host.')
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
- host_name_before_hash = None
- host_name = connector['host']
- if host_name and (len(host_name) > constants.MAX_HOSTNAME_LENGTH):
- host_name_before_hash = host_name
- host_name = six.text_type(hash(host_name))
-
- # Create hostgroup if not exist.
- host_id = self.rmt_client.add_host_with_check(
- host_name, host_name_before_hash)
-
- online_wwns_in_host = (
- self.rmt_client.get_host_online_fc_initiators(host_id))
- online_free_wwns = self.rmt_client.get_online_free_wwns()
- for wwn in wwns:
- if (wwn not in online_wwns_in_host
- and wwn not in online_free_wwns):
- wwns_in_host = (
- self.rmt_client.get_host_fc_initiators(host_id))
- iqns_in_host = (
- self.rmt_client.get_host_iscsi_initiators(host_id))
- if not wwns_in_host and not iqns_in_host:
- self.rmt_client.remove_host(host_id)
-
- msg = _('Can not add FC port to host.')
- LOG.error(msg)
- raise exception.VolumeBackendAPIException(data=msg)
-
- for wwn in wwns:
- if wwn in online_free_wwns:
- self.rmt_client.add_fc_port_to_host(host_id, wwn)
-
- (tgt_port_wwns, init_targ_map) = (
- self.rmt_client.get_init_targ_map(wwns))
-
- # Add host into hostgroup.
- hostgroup_id = self.rmt_client.add_host_into_hostgroup(host_id)
- map_info = self.rmt_client.do_mapping(lun_id,
- hostgroup_id,
- host_id)
- host_lun_id = self.rmt_client.find_host_lun_id(host_id, lun_id)
- except exception.VolumeBackendAPIException:
- raise
- except Exception as err:
- msg = _("Connect volume fc: connect volume error. %s.") % err
- LOG.exception(msg)
- raise exception.VolumeBackendAPIException(data=msg)
+ for wwn in wwns:
+ if wwn in online_free_wwns:
+ self.rmt_client.add_fc_port_to_host(host_id, wwn)
+
+ (tgt_port_wwns, init_targ_map) = (
+ self.rmt_client.get_init_targ_map(wwns))
+
+ # Add host into hostgroup.
+ hostgroup_id = self.rmt_client.add_host_to_hostgroup(host_id)
+ map_info = self.rmt_client.do_mapping(lun_id,
+ hostgroup_id,
+ host_id)
+ host_lun_id = self.rmt_client.get_host_lun_id(host_id, lun_id)
# Return FC properties.
fc_info = {'driver_volume_type': 'fibre_channel',
def disconnect_volume_fc(self, volume, connector):
"""Delete map between a volume and a host for FC."""
# Login remote storage device.
- self.xml_file_path = self.configuration.cinder_huawei_conf_file
- metro_devices = self.configuration.hypermetro_devices
- device_info = huawei_utils.get_remote_device_info(metro_devices)
- self.rmt_client = rest_client.RestClient(self.configuration)
- self.rmt_client.login_with_ip(device_info)
- try:
- wwns = connector['wwpns']
- volume_name = huawei_utils.encode_name(volume['id'])
- metadata = huawei_utils.get_volume_metadata(volume)
- lun_id = metadata['remote_lun_id']
- host_name = connector['host']
- left_lunnum = -1
- lungroup_id = None
- view_id = None
-
- LOG.info(_LI('terminate_connection_fc: volume name: %(volume)s, '
- 'wwpns: %(wwns)s, '
- 'lun_id: %(lunid)s.'),
- {'volume': volume_name,
- 'wwns': wwns,
- 'lunid': lun_id},)
-
- if host_name and (len(host_name) > constants.MAX_HOSTNAME_LENGTH):
- host_name = six.text_type(hash(host_name))
-
- hostid = self.rmt_client.find_host(host_name)
- if hostid:
- mapping_view_name = constants.MAPPING_VIEW_PREFIX + hostid
- view_id = self.rmt_client.find_mapping_view(
- mapping_view_name)
- if view_id:
- lungroup_id = self.rmt_client.find_lungroup_from_map(
- view_id)
-
- if lun_id and self.rmt_client.check_lun_exist(lun_id):
- if lungroup_id:
- lungroup_ids = self.rmt_client.get_lungroupids_by_lunid(
- lun_id)
- if lungroup_id in lungroup_ids:
- self.rmt_client.remove_lun_from_lungroup(
- lungroup_id, lun_id)
- else:
- LOG.warning(_LW("Lun is not in lungroup. "
- "Lun id: %(lun_id)s, "
- "lungroup id: %(lungroup_id)s"),
- {"lun_id": lun_id,
- "lungroup_id": lungroup_id})
-
- (tgt_port_wwns, init_targ_map) = (
- self.rmt_client.get_init_targ_map(wwns))
-
- hostid = self.rmt_client.find_host(host_name)
- if hostid:
- mapping_view_name = constants.MAPPING_VIEW_PREFIX + hostid
- view_id = self.rmt_client.find_mapping_view(
- mapping_view_name)
- if view_id:
- lungroup_id = self.rmt_client.find_lungroup_from_map(
- view_id)
+ wwns = connector['wwpns']
+ volume_name = huawei_utils.encode_name(volume['id'])
+ metadata = huawei_utils.get_volume_metadata(volume)
+ lun_id = metadata['remote_lun_id']
+ host_name = connector['host']
+ left_lunnum = -1
+ lungroup_id = None
+ view_id = None
+
+ LOG.info(_LI('terminate_connection_fc: volume name: %(volume)s, '
+ 'wwpns: %(wwns)s, '
+ 'lun_id: %(lunid)s.'),
+ {'volume': volume_name,
+ 'wwns': wwns,
+ 'lunid': lun_id},)
+
+ host_name = huawei_utils.encode_host_name(host_name)
+ hostid = self.rmt_client.get_host_id_by_name(host_name)
+ if hostid:
+ mapping_view_name = constants.MAPPING_VIEW_PREFIX + hostid
+ view_id = self.rmt_client.find_mapping_view(
+ mapping_view_name)
+ if view_id:
+ lungroup_id = self.rmt_client.find_lungroup_from_map(
+ view_id)
+
+ if lun_id and self.rmt_client.check_lun_exist(lun_id):
if lungroup_id:
- left_lunnum = self.rmt_client.get_lunnum_from_lungroup(
- lungroup_id)
-
- except Exception as err:
- msg = _("Remote detatch volume error. %s.") % err
- LOG.exception(msg)
- raise exception.VolumeBackendAPIException(data=msg)
- finally:
- self.rmt_client.logout()
+ lungroup_ids = self.rmt_client.get_lungroupids_by_lunid(
+ lun_id)
+ if lungroup_id in lungroup_ids:
+ self.rmt_client.remove_lun_from_lungroup(
+ lungroup_id, lun_id)
+ else:
+ LOG.warning(_LW("Lun is not in lungroup. "
+ "Lun id: %(lun_id)s, "
+ "lungroup id: %(lungroup_id)s"),
+ {"lun_id": lun_id,
+ "lungroup_id": lungroup_id})
+
+ (tgt_port_wwns, init_targ_map) = (
+ self.rmt_client.get_init_targ_map(wwns))
+
+ hostid = self.rmt_client.get_host_id_by_name(host_name)
+ if hostid:
+ mapping_view_name = constants.MAPPING_VIEW_PREFIX + hostid
+ view_id = self.rmt_client.find_mapping_view(
+ mapping_view_name)
+ if view_id:
+ lungroup_id = self.rmt_client.find_lungroup_from_map(
+ view_id)
+ if lungroup_id:
+ left_lunnum = self.rmt_client.get_lunnum_from_lungroup(
+ lungroup_id)
if int(left_lunnum) > 0:
info = {'driver_volume_type': 'fibre_channel',
return info
def _wait_volume_ready(self, lun_id):
- event_type = 'LUNReadyWaitInterval'
- wait_interval = huawei_utils.get_wait_interval(self.xml_file_path,
- event_type)
+ wait_interval = self.configuration.lun_ready_wait_interval
def _volume_ready():
result = self.rmt_client.get_lun_info(lun_id)
return True
return False
- huawei_utils.wait_for_condition(self.xml_file_path,
- _volume_ready,
+ huawei_utils.wait_for_condition(_volume_ready,
wait_interval,
wait_interval * 10)
-# Copyright (c) 2015 Huawei Technologies Co., Ltd.
+# Copyright (c) 2016 Huawei Technologies Co., Ltd.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
from cinder.i18n import _, _LE, _LI, _LW
from cinder import utils
from cinder.volume.drivers.huawei import constants
-from cinder.volume.drivers.huawei import huawei_utils
LOG = logging.getLogger(__name__)
class RestClient(object):
- """Common class for Huawei OceanStor 18000 storage system."""
+ """Common class for Huawei OceanStor storage system."""
- def __init__(self, configuration):
+ def __init__(self, configuration, san_address, san_user, san_password):
self.configuration = configuration
- self.xml_file_path = configuration.cinder_huawei_conf_file
- self.productversion = None
+ self.san_address = san_address
+ self.san_user = san_user
+ self.san_password = san_password
self.init_http_head()
def init_http_head(self):
def do_call(self, url=None, data=None, method=None,
calltimeout=constants.SOCKET_TIMEOUT):
- """Send requests to 18000 server.
+ """Send requests to Huawei storage server.
Send HTTPS call, get response in JSON.
Convert response into Python Object and return it.
try:
socket.setdefaulttimeout(calltimeout)
+ if data:
+ data = json.dumps(data)
req = urllib.request.Request(url, data, self.headers)
if method:
req.get_method = lambda: method
return res_json
def login(self):
- """Login 18000 array."""
- login_info = huawei_utils.get_login_info(self.xml_file_path)
- urlstr = login_info['RestURL']
- url_list = urlstr.split(";")
+ """Login Huawei storage array."""
device_id = None
- for item_url in url_list:
+ for item_url in self.san_address:
url = item_url + "xx/sessions"
- data = json.dumps({"username": login_info['UserName'],
- "password": login_info['UserPassword'],
- "scope": "0"})
+ data = {"username": self.san_user,
+ "password": self.san_password,
+ "scope": "0"}
self.init_http_head()
result = self.do_call(url, data,
calltimeout=constants.LOGIN_SOCKET_TIMEOUT)
result['error']['code'] = 0
return result
- def login_with_ip(self, login_info):
- """Login 18000 array with the specific URL."""
- urlstr = login_info['RestURL']
- url_list = urlstr.split(";")
- for item_url in url_list:
- url = item_url + "xx/sessions"
- data = json.dumps({"username": login_info['UserName'],
- "password": login_info['UserPassword'],
- "scope": '0'})
- result = self.call(url, data)
-
- if result['error']['code'] == constants.ERROR_CONNECT_TO_SERVER:
- continue
-
- if (result['error']['code'] != 0) or ('data' not in result):
- msg = (_("Login error, reason is: %s.") % result)
- LOG.error(msg)
- raise exception.VolumeBackendAPIException(data=msg)
-
- device_id = result['data']['deviceid']
- self.device_id = device_id
- self.url = item_url + device_id
- self.headers['iBaseToken'] = result['data']['iBaseToken']
-
- return device_id
-
- msg = _("Login error: Can not connect to server.")
- LOG.error(msg)
- raise exception.VolumeBackendAPIException(data=msg)
-
def logout(self):
"""Logout the session."""
url = "/sessions"
def _assert_data_in_result(self, result, msg):
if 'data' not in result:
- err_msg = (_('%s "data" was not in result.') % msg)
+ err_msg = _('%s "data" is not in result.') % msg
LOG.error(err_msg)
raise exception.VolumeBackendAPIException(data=err_msg)
- def create_volume(self, lun_param):
+ def create_lun(self, lun_params):
url = "/lun"
- data = json.dumps(lun_param)
- result = self.call(url, data)
+ result = self.call(url, lun_params)
if result['error']['code'] == constants.ERROR_VOLUME_ALREADY_EXIST:
- lun_id = self.get_volume_by_name(lun_param["NAME"])
+ lun_id = self.get_lun_id_by_name(lun_params['NAME'])
if lun_id:
return self.get_lun_info(lun_id)
- msg = _('Create volume error.')
+ msg = _('Create lun error.')
self._assert_rest_result(result, msg)
self._assert_data_in_result(result, msg)
return True
def delete_lun(self, lun_id):
- lun_group_ids = self.get_lungroupids_by_lunid(lun_id)
- if lun_group_ids and len(lun_group_ids) == 1:
- self.remove_lun_from_lungroup(lun_group_ids[0], lun_id)
-
url = "/lun/" + lun_id
- data = json.dumps({"TYPE": "11",
- "ID": lun_id})
+ data = {"TYPE": "11",
+ "ID": lun_id}
result = self.call(url, data, "DELETE")
self._assert_rest_result(result, _('Delete lun error.'))
- def find_all_pools(self):
+ def get_all_pools(self):
url = "/storagepool"
result = self.call(url, None)
msg = _('Query resource pool error.')
self._assert_rest_result(result, msg)
self._assert_data_in_result(result, msg)
- return result
+ return result['data']
- def find_pool_info(self, pool_name=None, result=None):
- pool_info = {}
+ def get_pool_info(self, pool_name=None, pools=None):
+ info = {}
if not pool_name:
- return pool_info
+ return info
- if 'data' in result:
- for item in result['data']:
- if pool_name.strip() == item['NAME']:
- # USAGETYPE means pool type.
- if ('USAGETYPE' in item and
- item['USAGETYPE'] == constants.FILE_SYSTEM_POOL_TYPE):
- break
- pool_info['ID'] = item['ID']
- pool_info['CAPACITY'] = item.get('DATASPACE',
- item['USERFREECAPACITY'])
- pool_info['TOTALCAPACITY'] = item['USERTOTALCAPACITY']
+ for pool in pools:
+ if pool_name.strip() != pool['NAME']:
+ continue
+
+ if pool.get('USAGETYPE') == constants.FILE_SYSTEM_POOL_TYPE:
+ break
+
+ info['ID'] = pool['ID']
+ info['CAPACITY'] = pool.get('DATASPACE', pool['USERFREECAPACITY'])
+ info['TOTALCAPACITY'] = pool['USERTOTALCAPACITY']
+
+ return info
+
+ def get_pool_id(self, volume, pool_name):
+ pools = self.get_all_pools()
+ pool_info = self.get_pool_info(pool_name, pools)
+ if not pool_info:
+ # The following code is to keep compatibility with old version of
+ # Huawei driver.
+ for pool_name in self.configuration.storage_pools:
+ pool_info = self.get_pool_info(pool_name, pools)
+ if pool_info:
break
- return pool_info
+
+ if not pool_info:
+ msg = _('Can not get pool info. pool: %s') % pool_name
+ LOG.error(msg)
+ raise exception.VolumeBackendAPIException(data=msg)
+
+ return pool_info['ID']
def _get_id_from_result(self, result, name, key):
if 'data' in result:
if name == item.get(key):
return item['ID']
- def get_volume_by_name(self, name):
+ def get_lun_id_by_name(self, name):
url = "/lun?range=[0-65535]"
result = self.call(url, None, "GET")
- self._assert_rest_result(result, _('Get volume by name error.'))
+ self._assert_rest_result(result, _('Get lun id by name error.'))
return self._get_id_from_result(result, name, 'NAME')
def activate_snapshot(self, snapshot_id):
- activate_url = "/snapshot/activate"
- data = json.dumps({"SNAPSHOTLIST": [snapshot_id]})
- result = self.call(activate_url, data)
+ url = "/snapshot/activate"
+ data = {"SNAPSHOTLIST": [snapshot_id]}
+ result = self.call(url, data)
self._assert_rest_result(result, _('Activate snapshot error.'))
- def create_snapshot(self, snapshot):
- snapshot_name = huawei_utils.encode_name(snapshot['id'])
- snapshot_description = snapshot['id']
- volume_name = huawei_utils.encode_name(snapshot['volume_id'])
-
- LOG.info(_LI(
- 'create_snapshot:snapshot name: %(snapshot)s, '
- 'volume name: %(volume)s.'),
- {'snapshot': snapshot_name,
- 'volume': volume_name})
-
- volume = snapshot['volume']
- lun_id = self.get_lunid(volume, volume_name)
-
+ def create_snapshot(self, lun_id, snapshot_name, snapshot_description):
url = "/snapshot"
- data = json.dumps({"TYPE": "27",
- "NAME": snapshot_name,
- "PARENTTYPE": "11",
- "DESCRIPTION": snapshot_description,
- "PARENTID": lun_id})
+ data = {"TYPE": "27",
+ "NAME": snapshot_name,
+ "PARENTTYPE": "11",
+ "DESCRIPTION": snapshot_description,
+ "PARENTID": lun_id}
result = self.call(url, data)
msg = _('Create snapshot error.')
return result['data']
- def get_lunid(self, volume, volume_name):
+ def get_lun_id(self, volume, volume_name):
lun_id = (volume.get('provider_location') or
- self.get_volume_by_name(volume_name))
+ self.get_lun_id_by_name(volume_name))
if not lun_id:
- msg = (_("Can't find lun info on the array, "
- "lun name is: %(name)s.") % {'name': volume_name})
+ msg = (_("Can't find lun info on the array. "
+ "volume: %(id)s, lun name: %(name)s.") %
+ {'id': volume['id'], 'name': volume_name})
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
return lun_id
def stop_snapshot(self, snapshot_id):
url = "/snapshot/stop"
- stopdata = json.dumps({"ID": snapshot_id})
+ stopdata = {"ID": snapshot_id}
result = self.call(url, stopdata, "PUT")
self._assert_rest_result(result, _('Stop snapshot error.'))
def delete_snapshot(self, snapshotid):
url = "/snapshot/%s" % snapshotid
- data = json.dumps({"TYPE": "27", "ID": snapshotid})
+ data = {"TYPE": "27", "ID": snapshotid}
result = self.call(url, data, "DELETE")
self._assert_rest_result(result, _('Delete snapshot error.'))
- def get_snapshotid_by_name(self, name):
+ def get_snapshot_id_by_name(self, name):
url = "/snapshot?range=[0-32767]"
result = self.call(url, None, "GET")
self._assert_rest_result(result, _('Get snapshot id error.'))
def create_luncopy(self, luncopyname, srclunid, tgtlunid):
"""Create a luncopy."""
url = "/luncopy"
- data = json.dumps({"TYPE": 219,
- "NAME": luncopyname,
- "DESCRIPTION": luncopyname,
- "COPYSPEED": 2,
- "LUNCOPYTYPE": "1",
- "SOURCELUN": ("INVALID;%s;INVALID;INVALID;INVALID"
- % srclunid),
- "TARGETLUN": ("INVALID;%s;INVALID;INVALID;INVALID"
- % tgtlunid)})
+ data = {"TYPE": 219,
+ "NAME": luncopyname,
+ "DESCRIPTION": luncopyname,
+ "COPYSPEED": 2,
+ "LUNCOPYTYPE": "1",
+ "SOURCELUN": ("INVALID;%s;INVALID;INVALID;INVALID"
+ % srclunid),
+ "TARGETLUN": ("INVALID;%s;INVALID;INVALID;INVALID"
+ % tgtlunid)}
result = self.call(url, data)
msg = _('Create luncopy error.')
return result['data']['ID']
- def add_host_into_hostgroup(self, host_id):
+ def add_host_to_hostgroup(self, host_id):
"""Associate host to hostgroup.
If hostgroup doesn't exist, create one.
return hostgroup_id
- def find_tgt_port_group(self, tgt_port_group):
+ def get_tgt_port_group(self, tgt_port_group):
"""Find target portgroup id by target port group name."""
url = "/portgroup?range=[0-8191]&TYPE=257"
result = self.call(url, None, "GET")
def _associate_portgroup_to_view(self, view_id, portgroup_id):
url = "/MAPPINGVIEW/CREATE_ASSOCIATE"
- data = json.dumps({"ASSOCIATEOBJTYPE": "257",
- "ASSOCIATEOBJID": portgroup_id,
- "TYPE": "245",
- "ID": view_id})
+ data = {"ASSOCIATEOBJTYPE": "257",
+ "ASSOCIATEOBJID": portgroup_id,
+ "TYPE": "245",
+ "ID": view_id}
result = self.call(url, data, "PUT")
self._assert_rest_result(result, _('Associate portgroup to mapping '
'view error.'))
return map_info
- def ensure_initiator_added(self, xml_file_path, initiator_name, host_id):
+ def check_iscsi_initiators_exist_in_host(self, host_id):
+ url = "/iscsi_initiator?range=[0-100]&PARENTID=%s" % host_id
+ result = self.call(url, None, "GET")
+ self._assert_rest_result(result, 'Get host initiators info failed.')
+ if "data" in result:
+ return True
+
+ return False
+
+ def ensure_initiator_added(self, initiator_name, host_id):
added = self._initiator_is_added_to_array(initiator_name)
if not added:
self._add_initiator_to_array(initiator_name)
if not self.is_initiator_associated_to_host(initiator_name):
- self._associate_initiator_to_host(xml_file_path,
- initiator_name,
+ self._associate_initiator_to_host(initiator_name,
host_id)
def _get_iscsi_tgt_port(self):
def _create_hostgroup(self, hostgroup_name):
url = "/hostgroup"
- data = json.dumps({"TYPE": "14", "NAME": hostgroup_name})
+ data = {"TYPE": "14", "NAME": hostgroup_name}
result = self.call(url, data)
msg = _('Create hostgroup error.')
def _create_lungroup(self, lungroup_name):
url = "/lungroup"
- data = json.dumps({"DESCRIPTION": lungroup_name,
- "APPTYPE": '0',
- "GROUPTYPE": '0',
- "NAME": lungroup_name})
+ data = {"DESCRIPTION": lungroup_name,
+ "APPTYPE": '0',
+ "GROUPTYPE": '0',
+ "NAME": lungroup_name}
result = self.call(url, data)
msg = _('Create lungroup error.')
self._assert_rest_result(result, _('Delete lungroup error.'))
def lungroup_associated(self, view_id, lungroup_id):
- url_subfix = ("/mappingview/associate?TYPE=245&"
- "ASSOCIATEOBJTYPE=256&ASSOCIATEOBJID=%s" % lungroup_id)
- url = url_subfix
+ url = ("/mappingview/associate?TYPE=245&"
+ "ASSOCIATEOBJTYPE=256&ASSOCIATEOBJID=%s" % lungroup_id)
result = self.call(url, None, "GET")
self._assert_rest_result(result, _('Check lungroup associate error.'))
return False
def hostgroup_associated(self, view_id, hostgroup_id):
- url_subfix = ("/mappingview/associate?TYPE=245&"
- "ASSOCIATEOBJTYPE=14&ASSOCIATEOBJID=%s" % hostgroup_id)
- url = url_subfix
+ url = ("/mappingview/associate?TYPE=245&"
+ "ASSOCIATEOBJTYPE=14&ASSOCIATEOBJID=%s" % hostgroup_id)
result = self.call(url, None, "GET")
self._assert_rest_result(result, _('Check hostgroup associate error.'))
return True
return False
- def find_host_lun_id(self, host_id, lun_id):
+ def get_host_lun_id(self, host_id, lun_id):
url = ("/lun/associate?TYPE=11&ASSOCIATEOBJTYPE=21"
"&ASSOCIATEOBJID=%s" % (host_id))
result = self.call(url, None, "GET")
raise
return host_lun_id
- def find_host(self, host_name):
+ def get_host_id_by_name(self, host_name):
"""Get the given host ID."""
url = "/host?range=[0-65535]"
result = self.call(url, None, "GET")
return self._get_id_from_result(result, host_name, 'NAME')
def add_host_with_check(self, host_name, host_name_before_hash):
- host_id = self.find_host(host_name)
+ host_id = self.get_host_id_by_name(host_name)
if host_id:
LOG.info(_LI(
'add_host_with_check. '
'Failed to create host: %(name)s. '
'Check if it exists on the array.'),
{'name': host_name})
- host_id = self.find_host(host_name)
+ host_id = self.get_host_id_by_name(host_name)
if not host_id:
err_msg = (_(
'Failed to create host: %(name)s. '
def _add_host(self, hostname, host_name_before_hash):
"""Add a new host."""
url = "/host"
- data = json.dumps({"TYPE": "21",
- "NAME": hostname,
- "OPERATIONSYSTEM": "0",
- "DESCRIPTION": host_name_before_hash})
+ data = {"TYPE": "21",
+ "NAME": hostname,
+ "OPERATIONSYSTEM": "0",
+ "DESCRIPTION": host_name_before_hash}
result = self.call(url, data)
self._assert_rest_result(result, _('Add new host error.'))
def _is_host_associate_to_hostgroup(self, hostgroup_id, host_id):
"""Check whether the host is associated to the hostgroup."""
- url_subfix = ("/host/associate?TYPE=21&"
- "ASSOCIATEOBJTYPE=14&ASSOCIATEOBJID=%s" % hostgroup_id)
+ url = ("/host/associate?TYPE=21&"
+ "ASSOCIATEOBJTYPE=14&ASSOCIATEOBJID=%s" % hostgroup_id)
- url = url_subfix
result = self.call(url, None, "GET")
self._assert_rest_result(result, _('Check hostgroup associate error.'))
def _is_lun_associated_to_lungroup(self, lungroup_id, lun_id):
"""Check whether the lun is associated to the lungroup."""
- url_subfix = ("/lun/associate?TYPE=11&"
- "ASSOCIATEOBJTYPE=256&ASSOCIATEOBJID=%s" % lungroup_id)
+ url = ("/lun/associate?TYPE=11&"
+ "ASSOCIATEOBJTYPE=256&ASSOCIATEOBJID=%s" % lungroup_id)
- url = url_subfix
result = self.call(url, None, "GET")
self._assert_rest_result(result, _('Check lungroup associate error.'))
def _associate_host_to_hostgroup(self, hostgroup_id, host_id):
url = "/hostgroup/associate"
- data = json.dumps({"TYPE": "14",
- "ID": hostgroup_id,
- "ASSOCIATEOBJTYPE": "21",
- "ASSOCIATEOBJID": host_id})
+ data = {"TYPE": "14",
+ "ID": hostgroup_id,
+ "ASSOCIATEOBJTYPE": "21",
+ "ASSOCIATEOBJID": host_id}
result = self.call(url, data)
self._assert_rest_result(result, _('Associate host to hostgroup '
def associate_lun_to_lungroup(self, lungroup_id, lun_id):
"""Associate lun to lungroup."""
url = "/lungroup/associate"
- data = json.dumps({"ID": lungroup_id,
- "ASSOCIATEOBJTYPE": "11",
- "ASSOCIATEOBJID": lun_id})
+ data = {"ID": lungroup_id,
+ "ASSOCIATEOBJTYPE": "11",
+ "ASSOCIATEOBJID": lun_id}
result = self.call(url, data)
self._assert_rest_result(result, _('Associate lun to lungroup error.'))
def _add_initiator_to_array(self, initiator_name):
"""Add a new initiator to storage device."""
url = "/iscsi_initiator"
- data = json.dumps({"TYPE": "222",
- "ID": initiator_name,
- "USECHAP": "false"})
+ data = {"TYPE": "222",
+ "ID": initiator_name,
+ "USECHAP": "false"}
result = self.call(url, data, "POST")
self._assert_rest_result(result,
_('Add initiator to array error.'))
def _add_initiator_to_host(self, initiator_name, host_id):
url = "/iscsi_initiator/" + initiator_name
- data = json.dumps({"TYPE": "222",
- "ID": initiator_name,
- "USECHAP": "false",
- "PARENTTYPE": "21",
- "PARENTID": host_id})
+ data = {"TYPE": "222",
+ "ID": initiator_name,
+ "USECHAP": "false",
+ "PARENTTYPE": "21",
+ "PARENTID": host_id}
result = self.call(url, data, "PUT")
self._assert_rest_result(result,
_('Associate initiator to host error.'))
def _associate_initiator_to_host(self,
- xml_file_path,
initiator_name,
host_id):
"""Associate initiator with the host."""
- iscsi_conf = huawei_utils.get_iscsi_conf(xml_file_path)
-
- chapinfo = self.find_chap_info(iscsi_conf,
+ chapinfo = self.find_chap_info(self.configuration.iscsi_info,
initiator_name)
- multipath_type = self._find_alua_info(iscsi_conf,
+ multipath_type = self._find_alua_info(self.configuration.iscsi_info,
initiator_name)
if chapinfo:
LOG.info(_LI('Use CHAP when adding initiator to host.'))
LOG.info(_LI('Use ALUA when adding initiator to host.'))
self._use_alua(initiator_name, multipath_type)
- def find_chap_info(self, iscsi_conf, initiator_name):
+ def find_chap_info(self, iscsi_info, initiator_name):
"""Find CHAP info from xml."""
chapinfo = None
- for ini in iscsi_conf['Initiator']:
+ for ini in iscsi_info:
if ini['Name'] == initiator_name:
if 'CHAPinfo' in ini:
chapinfo = ini['CHAPinfo']
return chapinfo
- def _find_alua_info(self, iscsi_conf, initiator_name):
+ def _find_alua_info(self, iscsi_info, initiator_name):
"""Find ALUA info from xml."""
multipath_type = 0
- for ini in iscsi_conf['Initiator']:
+ for ini in iscsi_info:
if ini['Name'] == initiator_name:
if 'ALUA' in ini:
if ini['ALUA'] != '1' and ini['ALUA'] != '0':
(chap_username, chap_password) = chapinfo.split(";")
url = "/iscsi_initiator/" + initiator_name
- data = json.dumps({"TYPE": "222",
- "USECHAP": "true",
- "CHAPNAME": chap_username,
- "CHAPPASSWORD": chap_password,
- "ID": initiator_name,
- "PARENTTYPE": "21",
- "PARENTID": host_id})
+ data = {"TYPE": "222",
+ "USECHAP": "true",
+ "CHAPNAME": chap_username,
+ "CHAPPASSWORD": chap_password,
+ "ID": initiator_name,
+ "PARENTTYPE": "21",
+ "PARENTID": host_id}
result = self.call(url, data, "PUT")
msg = _('Use CHAP to associate initiator to host error. '
'Please check the CHAP username and password.')
def _use_alua(self, initiator_name, multipath_type):
"""Use ALUA when adding initiator to host."""
url = "/iscsi_initiator"
- data = json.dumps({"ID": initiator_name,
- "MULTIPATHTYPE": multipath_type})
+ data = {"ID": initiator_name,
+ "MULTIPATHTYPE": multipath_type}
result = self.call(url, data, "PUT")
self._assert_rest_result(
def remove_chap(self, initiator_name):
"""Remove CHAP when terminate connection."""
url = "/iscsi_initiator"
- data = json.dumps({"USECHAP": "false",
+ data = {"USECHAP": "false",
"MULTIPATHTYPE": "0",
- "ID": initiator_name})
+ "ID": initiator_name}
result = self.call(url, data, "PUT")
self._assert_rest_result(result, _('Remove CHAP error.'))
def _add_mapping_view(self, name):
url = "/mappingview"
- data = json.dumps({"NAME": name, "TYPE": "245"})
+ data = {"NAME": name, "TYPE": "245"}
result = self.call(url, data)
self._assert_rest_result(result, _('Add mapping view error.'))
def _associate_hostgroup_to_view(self, view_id, hostgroup_id):
url = "/MAPPINGVIEW/CREATE_ASSOCIATE"
- data = json.dumps({"ASSOCIATEOBJTYPE": "14",
- "ASSOCIATEOBJID": hostgroup_id,
- "TYPE": "245",
- "ID": view_id})
+ data = {"ASSOCIATEOBJTYPE": "14",
+ "ASSOCIATEOBJID": hostgroup_id,
+ "TYPE": "245",
+ "ID": view_id}
result = self.call(url, data, "PUT")
self._assert_rest_result(result, _('Associate host to mapping view '
'error.'))
def _associate_lungroup_to_view(self, view_id, lungroup_id):
url = "/MAPPINGVIEW/CREATE_ASSOCIATE"
- data = json.dumps({"ASSOCIATEOBJTYPE": "256",
- "ASSOCIATEOBJID": lungroup_id,
- "TYPE": "245",
- "ID": view_id})
+ data = {"ASSOCIATEOBJTYPE": "256",
+ "ASSOCIATEOBJID": lungroup_id,
+ "TYPE": "245",
+ "ID": view_id}
+
result = self.call(url, data, "PUT")
self._assert_rest_result(
result, _('Associate lungroup to mapping view error.'))
def delete_lungroup_mapping_view(self, view_id, lungroup_id):
"""Remove lungroup associate from the mapping view."""
url = "/mappingview/REMOVE_ASSOCIATE"
- data = json.dumps({"ASSOCIATEOBJTYPE": "256",
- "ASSOCIATEOBJID": lungroup_id,
- "TYPE": "245",
- "ID": view_id})
+ data = {"ASSOCIATEOBJTYPE": "256",
+ "ASSOCIATEOBJID": lungroup_id,
+ "TYPE": "245",
+ "ID": view_id}
result = self.call(url, data, "PUT")
self._assert_rest_result(result, _('Delete lungroup from mapping view '
'error.'))
def delete_hostgoup_mapping_view(self, view_id, hostgroup_id):
"""Remove hostgroup associate from the mapping view."""
url = "/mappingview/REMOVE_ASSOCIATE"
- data = json.dumps({"ASSOCIATEOBJTYPE": "14",
- "ASSOCIATEOBJID": hostgroup_id,
- "TYPE": "245",
- "ID": view_id})
+ data = {"ASSOCIATEOBJTYPE": "14",
+ "ASSOCIATEOBJID": hostgroup_id,
+ "TYPE": "245",
+ "ID": view_id}
+
result = self.call(url, data, "PUT")
self._assert_rest_result(
result, _('Delete hostgroup from mapping view error.'))
def delete_portgroup_mapping_view(self, view_id, portgroup_id):
"""Remove portgroup associate from the mapping view."""
url = "/mappingview/REMOVE_ASSOCIATE"
- data = json.dumps({"ASSOCIATEOBJTYPE": "257",
- "ASSOCIATEOBJID": portgroup_id,
- "TYPE": "245",
- "ID": view_id})
+ data = {"ASSOCIATEOBJTYPE": "257",
+ "ASSOCIATEOBJID": portgroup_id,
+ "TYPE": "245",
+ "ID": view_id}
+
result = self.call(url, data, "PUT")
self._assert_rest_result(
result, _('Delete portgroup from mapping view error.'))
def get_lunnum_from_lungroup(self, lungroup_id):
"""Check if there are still other luns associated to the lungroup."""
- url_subfix = ("/lun/count?TYPE=11&ASSOCIATEOBJTYPE=256&"
- "ASSOCIATEOBJID=%s" % lungroup_id)
- url = url_subfix
+ url = ("/lun/count?TYPE=11&ASSOCIATEOBJTYPE=256&"
+ "ASSOCIATEOBJID=%s" % lungroup_id)
result = self.call(url, None, "GET")
self._assert_rest_result(result, _('Find lun number error.'))
lunnum = -1
def is_portgroup_associated_to_view(self, view_id, portgroup_id):
"""Check whether the port group is associated to the mapping view."""
- url_subfix = ("/portgroup/associate?ASSOCIATEOBJTYPE=245&"
- "ASSOCIATEOBJID=%s&range=[0-8191]" % view_id)
- url = url_subfix
+ url = ("/portgroup/associate?ASSOCIATEOBJTYPE=245&"
+ "ASSOCIATEOBJID=%s&range=[0-8191]" % view_id)
result = self.call(url, None, "GET")
self._assert_rest_result(result, _('Find portgroup from mapping view '
'error.'))
def find_lungroup_from_map(self, view_id):
"""Get lungroup from the given map"""
- url_subfix = ("/mappingview/associate/lungroup?TYPE=256&"
- "ASSOCIATEOBJTYPE=245&ASSOCIATEOBJID=%s" % view_id)
- url = url_subfix
+ url = ("/mappingview/associate/lungroup?TYPE=256&"
+ "ASSOCIATEOBJTYPE=245&ASSOCIATEOBJID=%s" % view_id)
result = self.call(url, None, "GET")
self._assert_rest_result(result, _('Find lun group from mapping view '
'error.'))
def start_luncopy(self, luncopy_id):
"""Start a LUNcopy."""
url = "/LUNCOPY/start"
- data = json.dumps({"TYPE": "219", "ID": luncopy_id})
+ data = {"TYPE": "219", "ID": luncopy_id}
result = self.call(url, data, "PUT")
self._assert_rest_result(result, _('Start LUNcopy error.'))
def _get_capacity(self, pool_name, result):
"""Get free capacity and total capacity of the pool."""
- pool_info = self.find_pool_info(pool_name, result)
+ pool_info = self.get_pool_info(pool_name, result)
pool_capacity = {'total_capacity': 0.0,
'free_capacity': 0.0}
def add_fc_port_to_host(self, host_id, wwn):
"""Add a FC port to the host."""
url = "/fc_initiator/" + wwn
- data = json.dumps({"TYPE": "223",
- "ID": wwn,
- "PARENTTYPE": 21,
- "PARENTID": host_id})
+ data = {"TYPE": "223",
+ "ID": wwn,
+ "PARENTTYPE": 21,
+ "PARENTID": host_id}
result = self.call(url, data, "PUT")
self._assert_rest_result(result, _('Add FC port to host error.'))
return fc_wwpns
def update_volume_stats(self):
- root = huawei_utils.parse_xml_file(self.xml_file_path)
- pool_names = root.findtext('LUN/StoragePool')
- if not pool_names:
- msg = _(
- 'Invalid resource pool name. '
- 'Please check the config file.')
- LOG.error(msg)
- raise exception.InvalidInput(msg)
data = {}
data['pools'] = []
- result = self.find_all_pools()
- for pool_name in pool_names.split(";"):
- pool_name = pool_name.strip(' \t\n\r')
+ result = self.get_all_pools()
+ for pool_name in self.configuration.storage_pools:
capacity = self._get_capacity(pool_name, result)
pool = {}
pool.update(dict(
def _update_qos_policy_lunlist(self, lun_list, policy_id):
url = "/ioclass/" + policy_id
- data = json.dumps({"TYPE": "230",
- "ID": policy_id,
- "LUNLIST": lun_list})
+ data = {"TYPE": "230",
+ "ID": policy_id,
+ "LUNLIST": lun_list}
+
result = self.call(url, data, "PUT")
self._assert_rest_result(result, _('Update QoS policy error.'))
return target_ips
- def get_iscsi_params(self, xml_file_path, connector):
+ def get_iscsi_params(self, connector):
"""Get target iSCSI params, including iqn, IP."""
initiator = connector['initiator']
- iscsi_conf = huawei_utils.get_iscsi_conf(xml_file_path)
target_ips = []
target_iqns = []
portgroup = None
portgroup_id = None
- for ini in iscsi_conf['Initiator']:
+ for ini in self.configuration.iscsi_info:
if ini['Name'] == initiator:
for key in ini:
if key == 'TargetPortGroup':
target_ips.append(ini['TargetIP'])
if portgroup:
- portgroup_id = self.find_tgt_port_group(portgroup)
+ portgroup_id = self.get_tgt_port_group(portgroup)
target_ips = self._get_tgt_ip_from_portgroup(portgroup_id)
# If not specify target IP for some initiators, use default IP.
if not target_ips:
- if iscsi_conf['DefaultTargetIP']:
- target_ips.append(iscsi_conf['DefaultTargetIP'])
+ default_target_ips = self.configuration.iscsi_default_target_ip
+ if default_target_ips:
+ target_ips.append(default_target_ips[0])
else:
msg = (_(
# Package QoS name.
qos_name = constants.QOS_NAME_PREFIX + lun_id + '_' + localtime
- mergedata = {"TYPE": "230",
- "NAME": qos_name,
- "LUNLIST": ["%s" % lun_id],
- "CLASSTYPE": "1",
- "SCHEDULEPOLICY": "2",
- "SCHEDULESTARTTIME": "1410969600",
- "STARTTIME": "08:00",
- "DURATION": "86400",
- "CYCLESET": "[1,2,3,4,5,6,0]",
- }
- mergedata.update(qos)
- data = json.dumps(mergedata)
+ data = {"TYPE": "230",
+ "NAME": qos_name,
+ "LUNLIST": ["%s" % lun_id],
+ "CLASSTYPE": "1",
+ "SCHEDULEPOLICY": "2",
+ "SCHEDULESTARTTIME": "1410969600",
+ "STARTTIME": "08:00",
+ "DURATION": "86400",
+ "CYCLESET": "[1,2,3,4,5,6,0]",
+ }
+ data.update(qos)
url = "/ioclass/"
result = self.call(url, data)
def delete_qos_policy(self, qos_id):
"""Delete a QoS policy."""
url = "/ioclass/" + qos_id
- data = json.dumps({"TYPE": "230",
- "ID": qos_id})
+ data = {"TYPE": "230", "ID": qos_id}
result = self.call(url, data, 'DELETE')
self._assert_rest_result(result, _('Delete QoS policy error.'))
enbalestatus: false (deactivate)
"""
url = "/ioclass/active/" + qos_id
- data = json.dumps({"TYPE": 230,
- "ID": qos_id,
- "ENABLESTATUS": enablestatus})
+ data = {"TYPE": 230,
+ "ID": qos_id,
+ "ENABLESTATUS": enablestatus}
result = self.call(url, data, "PUT")
self._assert_rest_result(
result, _('Activate or deactivate QoS error.'))
return result['data']
- def get_lun_list_in_qos(self, qos_id):
+ def get_lun_list_in_qos(self, qos_id, qos_info):
"""Get the lun list in QoS."""
- qos_info = self.get_qos_info(qos_id)
lun_list = []
lun_string = qos_info['LUNLIST'][1:-1]
"""Remove lun from QoS."""
lun_list = [i for i in lun_list if i != lun_id]
url = "/ioclass/" + qos_id
- data = json.dumps({"LUNLIST": lun_list,
- "TYPE": 230,
- "ID": qos_id})
+ data = {"LUNLIST": lun_list,
+ "TYPE": 230,
+ "ID": qos_id}
result = self.call(url, data, "PUT")
- msg = _('Remove lun from Qos error.')
+ msg = _('Remove lun from QoS error.')
self._assert_rest_result(result, msg)
self._assert_data_in_result(result, msg)
def change_lun_priority(self, lun_id):
"""Change lun priority to high."""
url = "/lun/" + lun_id
- data = json.dumps({"TYPE": "11",
- "ID": lun_id,
- "IOPRIORITY": "3"})
+ data = {"TYPE": "11",
+ "ID": lun_id,
+ "IOPRIORITY": "3"}
result = self.call(url, data, "PUT")
self._assert_rest_result(result, _('Change lun priority error.'))
def change_lun_smarttier(self, lunid, smarttier_policy):
"""Change lun smarttier policy."""
url = "/lun/" + lunid
- data = json.dumps({"TYPE": "11",
- "ID": lunid,
- "DATATRANSFERPOLICY": smarttier_policy})
+ data = {"TYPE": "11",
+ "ID": lunid,
+ "DATATRANSFERPOLICY": smarttier_policy}
result = self.call(url, data, "PUT")
self._assert_rest_result(
return result['data']
- def extend_volume(self, lun_id, new_volume_size):
+ def extend_lun(self, lun_id, new_volume_size):
url = "/lun/expand"
- data = json.dumps({"TYPE": 11, "ID": lun_id,
- "CAPACITY": new_volume_size})
+ data = {"TYPE": 11, "ID": lun_id,
+ "CAPACITY": new_volume_size}
result = self.call(url, data, 'PUT')
msg = _('Extend volume error.')
def create_lun_migration(self, src_id, dst_id, speed=2):
url = "/LUN_MIGRATION"
- data = json.dumps({"TYPE": '253',
- "PARENTID": src_id,
- "TARGETLUNID": dst_id,
- "SPEED": speed,
- "WORKMODE": 0})
+ data = {"TYPE": '253',
+ "PARENTID": src_id,
+ "TARGETLUNID": dst_id,
+ "SPEED": speed,
+ "WORKMODE": 0}
result = self.call(url, data, "POST")
msg = _('Create lun migration error.')
def add_lun_to_partition(self, lun_id, partition_id):
url = "/lun/associate/cachepartition"
- data = json.dumps({"ID": partition_id,
- "ASSOCIATEOBJTYPE": 11,
- "ASSOCIATEOBJID": lun_id, })
+ data = {"ID": partition_id,
+ "ASSOCIATEOBJTYPE": 11,
+ "ASSOCIATEOBJID": lun_id}
result = self.call(url, data, "POST")
self._assert_rest_result(result, _('Add lun to partition error.'))
def get_cache_info_by_id(self, cacheid):
url = "/SMARTCACHEPARTITION/" + cacheid
- data = json.dumps({"TYPE": "273",
- "ID": cacheid})
+ data = {"TYPE": "273",
+ "ID": cacheid}
result = self.call(url, data, "GET")
self._assert_rest_result(
def remove_lun_from_cache(self, lun_id, cache_id):
url = "/SMARTCACHEPARTITION/REMOVE_ASSOCIATE"
- data = json.dumps({"ID": cache_id,
- "ASSOCIATEOBJTYPE": 11,
- "ASSOCIATEOBJID": lun_id,
- "TYPE": 273})
+ data = {"ID": cache_id,
+ "ASSOCIATEOBJTYPE": 11,
+ "ASSOCIATEOBJID": lun_id,
+ "TYPE": 273}
result = self.call(url, data, "PUT")
self._assert_rest_result(result, _('Remove lun from cache error.'))
new_lun_list.append(lun_id)
- data = json.dumps({"LUNLIST": new_lun_list,
- "TYPE": 230,
- "ID": qos_id})
+ data = {"LUNLIST": new_lun_list,
+ "TYPE": 230,
+ "ID": qos_id}
result = self.call(url, data, "PUT")
- msg = _('Associate lun to Qos error.')
+ msg = _('Associate lun to QoS error.')
self._assert_rest_result(result, msg)
self._assert_data_in_result(result, msg)
def add_lun_to_cache(self, lun_id, cache_id):
url = "/SMARTCACHEPARTITION/CREATE_ASSOCIATE"
- data = json.dumps({"ID": cache_id,
- "ASSOCIATEOBJTYPE": 11,
- "ASSOCIATEOBJID": lun_id,
- "TYPE": 273})
+ data = {"ID": cache_id,
+ "ASSOCIATEOBJTYPE": 11,
+ "ASSOCIATEOBJID": lun_id,
+ "TYPE": 273}
result = self.call(url, data, "PUT")
self._assert_rest_result(result, _('Add lun to cache error.'))
def find_array_version(self):
url = "/system/"
- result = self.call(url, None)
+ result = self.call(url, None, "GET")
self._assert_rest_result(result, _('Find array version error.'))
return result['data']['PRODUCTVERSION']
def remove_iscsi_from_host(self, initiator):
url = "/iscsi_initiator/remove_iscsi_from_host"
- data = json.dumps({"TYPE": '222',
- "ID": initiator})
+ data = {"TYPE": '222',
+ "ID": initiator}
result = self.call(url, data, "PUT")
self._assert_rest_result(result, _('Remove iscsi from host error.'))
return initiators
- def rename_lun(self, lun_id, new_name, description=None):
+ def rename_lun(self, lun_id, new_name):
url = "/lun/" + lun_id
- data = json.dumps({"NAME": new_name})
- if description:
- data.update({"DESCRIPTION": description})
+ data = {"NAME": new_name}
result = self.call(url, data, "PUT")
msg = _('Rename lun on array error.')
self._assert_rest_result(result, msg)
def remove_fc_from_host(self, initiator):
url = '/fc_initiator/remove_fc_from_host'
- data = json.dumps({"TYPE": '223',
- "ID": initiator})
+ data = {"TYPE": '223',
+ "ID": initiator}
result = self.call(url, data, "PUT")
self._assert_rest_result(result, _('Remove fc from host error.'))
def _add_fc_initiator_to_array(self, ininame):
"""Add a fc initiator to storage device."""
url = '/fc_initiator/'
- data = json.dumps({"TYPE": '223',
- "ID": ininame})
+ data = {"TYPE": '223',
+ "ID": ininame}
result = self.call(url, data)
self._assert_rest_result(result, _('Add fc initiator to array error.'))
def create_hypermetro(self, hcp_param):
url = "/HyperMetroPair"
- data = json.dumps(hcp_param)
- result = self.call(url, data, "POST")
+ result = self.call(url, hcp_param, "POST")
msg = _('create_hypermetro_pair error.')
self._assert_rest_result(result, msg)
def sync_hypermetro(self, metro_id):
url = "/HyperMetroPair/synchronize_hcpair"
- data = json.dumps({"ID": metro_id,
- "TYPE": "15361"})
+ data = {"ID": metro_id,
+ "TYPE": "15361"}
result = self.call(url, data, "PUT")
msg = _('sync_hypermetro error.')
def stop_hypermetro(self, metro_id):
url = '/HyperMetroPair/disable_hcpair'
- data = json.dumps({"ID": metro_id,
- "TYPE": "15361"})
+ data = {"ID": metro_id,
+ "TYPE": "15361"}
result = self.call(url, data, "PUT")
msg = _('stop_hypermetro error.')
return True
+ def get_volume_by_name(self, name):
+ url = "/lun?range=[0-65535]"
+ result = self.call(url, None, "GET")
+ self._assert_rest_result(result, _('Get volume by name error.'))
+
def change_hostlun_id(self, map_info, hostlun_id):
url = "/mappingview"
view_id = six.text_type(map_info['view_id'])
lun_id = six.text_type(map_info['lun_id'])
hostlun_id = six.text_type(hostlun_id)
- data = json.dumps({"TYPE": 245,
- "ID": view_id,
- "ASSOCIATEOBJTYPE": 11,
- "ASSOCIATEOBJID": lun_id,
- "ASSOCIATEMETADATA": [{"LUNID": lun_id,
- "hostLUNId": hostlun_id}]
- })
+ data = {"TYPE": 245,
+ "ID": view_id,
+ "ASSOCIATEOBJTYPE": 11,
+ "ASSOCIATEOBJID": lun_id,
+ "ASSOCIATEMETADATA": [{"LUNID": lun_id,
+ "hostLUNId": hostlun_id}]}
result = self.call(url, data, "PUT")
-# Copyright (c) 2015 Huawei Technologies Co., Ltd.
+# Copyright (c) 2016 Huawei Technologies Co., Ltd.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
from oslo_log import log as logging
from oslo_utils import excutils
+from cinder import context
from cinder import exception
-from cinder.i18n import _
+from cinder.i18n import _, _LI
from cinder.volume.drivers.huawei import constants
-from cinder.volume.drivers.huawei import huawei_utils
+from cinder.volume import qos_specs
LOG = logging.getLogger(__name__)
def __init__(self, client):
self.client = client
- def create_qos(self, qos, lun_id):
+ @staticmethod
+ def get_qos_by_volume_type(volume_type):
+ # We prefer the qos_specs association
+ # and override any existing extra-specs settings
+ # if present.
+ if not volume_type:
+ return {}
+
+ qos_specs_id = volume_type.get('qos_specs_id')
+ if not qos_specs_id:
+ return {}
+
+ qos = {}
+ ctxt = context.get_admin_context()
+ kvs = qos_specs.get_qos_specs(ctxt, qos_specs_id)['specs']
+ LOG.info(_LI('The QoS sepcs is: %s.'), kvs)
+ for k, v in kvs.items():
+ if k not in constants.HUAWEI_VALID_KEYS:
+ continue
+
+ if k.upper() != 'IOTYPE' and int(v) <= 0:
+ msg = _('QoS config is wrong. %s must > 0.') % k
+ LOG.error(msg)
+ raise exception.InvalidInput(reason=msg)
+ elif k.upper() == 'IOTYPE' and v not in ['0', '1', '2']:
+ msg = _('Illegal value specified for IOTYPE: 0, 1, or 2.')
+ LOG.error(msg)
+ raise exception.InvalidInput(reason=msg)
+ else:
+ qos[k.upper()] = v
+
+ return qos
+
+ def _is_high_priority(self, qos):
+ """Check QoS priority."""
+ for key, value in qos.items():
+ if (key.find('MIN') == 0) or (key.find('LATENCY') == 0):
+ return True
+
+ return False
+
+ def add(self, qos, lun_id):
policy_id = None
try:
# Check QoS priority.
- if huawei_utils.check_qos_high_priority(qos):
+ if self._is_high_priority(qos):
self.client.change_lun_priority(lun_id)
# Create QoS policy and activate it.
version = self.client.find_array_version()
if policy_id is not None:
self.client.delete_qos_policy(policy_id)
- def delete_qos(self, qos_id):
+ def remove(self, qos_id, lun_id):
qos_info = self.client.get_qos_info(qos_id)
- qos_status = qos_info['RUNNINGSTATUS']
- # 2: Active status.
- if qos_status == constants.STATUS_QOS_ACTIVE:
- self.client.activate_deactivate_qos(qos_id, False)
- self.client.delete_qos_policy(qos_id)
+ lun_list = self.client.get_lun_list_in_qos(qos_id, qos_info)
+ if len(lun_list) <= 1:
+ qos_status = qos_info['RUNNINGSTATUS']
+ # 2: Active status.
+ if qos_status == constants.STATUS_QOS_ACTIVE:
+ self.client.activate_deactivate_qos(qos_id, False)
+ self.client.delete_qos_policy(qos_id)
+ else:
+ self.client.remove_lun_from_qos(lun_id, lun_list, qos_id)
class SmartPartition(object):
reason=(_('Illegal value specified for thin: '
'Can not set thin and thick at the same time.')))
else:
- opts['LUNType'] = 1
+ opts['LUNType'] = constants.THIN_LUNTYPE
if opts['thick_provisioning_support'] == 'true':
- opts['LUNType'] = 0
+ opts['LUNType'] = constants.THICK_LUNTYPE
return opts