--- /dev/null
+# Copyright (c) 2013 - 2014 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.
+""" Tests for huawei 18000 storage."""
+import json
+import os
+import shutil
+import tempfile
+import time
+from xml.dom.minidom import Document
+
+import mock
+
+from cinder import exception
+from cinder.openstack.common import log as logging
+from cinder import test
+from cinder.volume import configuration as conf
+from cinder.volume.drivers.huawei import huawei_18000
+from cinder.volume.drivers.huawei import rest_common
+
+LOG = logging.getLogger(__name__)
+
+test_volume = {'name': 'volume-21ec7341-9256-497b-97d9-ef48edcf0635',
+ 'size': 2,
+ 'volume_name': 'vol1',
+ 'id': '21ec7341-9256-497b-97d9-ef48edcf0635',
+ 'volume_id': '21ec7341-9256-497b-97d9-ef48edcf0635',
+ 'provider_auth': None,
+ 'project_id': 'project',
+ 'display_name': 'vol1',
+ 'display_description': 'test volume',
+ 'volume_type_id': None,
+ 'provider_location': '11'}
+
+test_snap = {'name': 'volume-21ec7341-9256-497b-97d9-ef48edcf0635',
+ 'size': 1,
+ 'volume_name': 'vol1',
+ 'id': '21ec7341-9256-497b-97d9-ef48edcf0635',
+ 'volume_id': '21ec7341-9256-497b-97d9-ef48edcf0635',
+ 'provider_auth': None,
+ 'project_id': 'project',
+ 'display_name': 'vol1',
+ 'display_description': 'test volume',
+ 'volume_type_id': None,
+ 'provider_location': '11'}
+
+FakeConnector = {'initiator': 'iqn.1993-08.debian:01:ec2bff7ac3a3',
+ 'wwpns': ['10000090fa0d6754'],
+ 'wwnns': ['10000090fa0d6755'],
+ 'host': 'ubuntuc'}
+
+
+def find_data(method):
+ if method is None:
+ data = """{"error":{"code":0},
+ "data":{"ID":"1",
+ "NAME":"5mFHcBv4RkCcD+JyrWc0SA"}}"""
+ if method == 'GET':
+ data = """{"error":{"code":0},
+ "data":[{"ID":"1",
+ "NAME":"IexzQZJWSXuX2e9I7c8GNQ"}]}"""
+ return data
+
+
+def find_data_lun(method):
+ if method == 'GET':
+ data = """{"error":{"code":0},
+ "data":{"ID":"1",
+ "NAME":"IexzQZJWSXuX2e9I7c8GNQ",
+ "HEALTHSTATUS":"1",
+ "RUNNINGSTATUS":"27"}}"""
+ return data
+
+
+def find_data_lungroup(method):
+ if method is None:
+ data = '{"error":{"code":0},\
+ "data":{"NAME":"5mFHcBv4RkCcD+JyrWc0SA",\
+ "DESCRIPTION":"5mFHcBv4RkCcD+JyrWc0SA",\
+ "ID":"11",\
+ "TYPE":256}}'
+
+ if method == "GET":
+ data = """{"error":{"code":0},
+ "data":[{
+ "NAME":"OpenStack_LunGroup_1",
+ "DESCRIPTION":"5mFHcBv4RkCcD+JyrWc0SA",
+ "ID":"11",
+ "TYPE":256}]}"""
+
+ if method == "DELETE":
+ data = """{"error":{"code":0},
+ "data":[{
+ "NAME":"IexzQZJWSXuX2e9I7c8GNQ",
+ "DESCRIPTION":"5mFHcBv4RkCcD+JyrWc0SA",
+ "ID":"11",
+ "TYPE":256}]}"""
+ return data
+
+
+def find_data_hostgroup(method):
+ if method is None:
+ data = """{"error":{"code":0},"data":{
+ "NAME":"ubuntuc",
+ "DESCRIPTION":"",
+ "ID":"0",
+ "TYPE":14}}"""
+
+ if method == "GET":
+ data = """{"error":{"code":0},"data":[{
+ "NAME":"ubuntuc",
+ "DESCRIPTION":"",
+ "ID":"0","TYPE":14}]}"""
+ return data
+
+
+def Fake_sleep(time):
+ pass
+
+
+def find_data_mappingview(method, other_flag):
+ if method is None:
+ data = """{"error":{"code":0},"data":
+ {"WORKMODE":"255","HEALTHSTATUS":"1",
+ "NAME":"mOWtSXnaQKi3hpB3tdFRIQ",
+ "RUNNINGSTATUS":"27","DESCRIPTION":"",
+ "ENABLEINBANDCOMMAND":"true",
+ "ID":"1","INBANDLUNWWN":"",
+ "TYPE":245}}
+ """
+
+ if method == "GET":
+ if other_flag:
+ data = """{"error":{"code":0},"data":[
+ {"WORKMODE":"255","HEALTHSTATUS":"1",
+ "NAME":"mOWtSXnaQKi3hpB3tdFRIQ",
+ "RUNNINGSTATUS":"27","DESCRIPTION":"",
+ "ENABLEINBANDCOMMAND":"true","ID":"1",
+ "INBANDLUNWWN":"","TYPE":245},
+ {"WORKMODE":"255","HEALTHSTATUS":"1",
+ "NAME":"YheUoRwbSX2BxN767nvLSw",
+ "RUNNINGSTATUS":"27","DESCRIPTION":"",
+ "ENABLEINBANDCOMMAND":"true",
+ "ID":"2","INBANDLUNWWN":"",
+ "TYPE":245}]}
+ """
+ else:
+ data = """{"error":{"code":0},"data":[
+ {"WORKMODE":"255","HEALTHSTATUS":"1",
+ "NAME":"IexzQZJWSXuX2e9I7c8GNQ",
+ "RUNNINGSTATUS":"27","DESCRIPTION":"",
+ "ENABLEINBANDCOMMAND":"true","ID":"1",
+ "INBANDLUNWWN":"","TYPE":245},
+ {"WORKMODE":"255","HEALTHSTATUS":"1",
+ "NAME":"YheUoRwbSX2BxN767nvLSw",
+ "RUNNINGSTATUS":"27","DESCRIPTION":"",
+ "ENABLEINBANDCOMMAND":"true","ID":"2",
+ "INBANDLUNWWN":"","TYPE":245}]}
+ """
+ return data
+
+
+def find_data_snapshot(method):
+ if method is None:
+ data = '{"error":{"code":0},"data":{"ID":11,"NAME":"YheUoRwbSX2BxN7"}}'
+ if method == "GET":
+ data = """{"error":{"code":0},"data":[
+ {"ID":11,"NAME":"SDFAJSDFLKJ"},
+ {"ID":12,"NAME":"SDFAJSDFLKJ2"}]}"""
+ return data
+
+
+def find_data_host(method):
+ if method is None:
+ data = """{"error":{"code":0},
+ "data":
+ {"PARENTTYPE":245,
+ "NAME":"Default Host",
+ "DESCRIPTION":"",
+ "RUNNINGSTATUS":"1",
+ "IP":"",
+ "PARENTNAME":"0",
+ "OPERATIONSYSTEM":"1",
+ "LOCATION":"",
+ "HEALTHSTATUS":"1",
+ "MODEL":"",
+ "ID":"0",
+ "PARENTID":"0",
+ "NETWORKNAME":"",
+ "TYPE":21}} """
+
+ if method == "GET":
+ data = """{"error":{"code":0},
+ "data":[
+ {"PARENTTYPE":245,"NAME":"ubuntuc",
+ "DESCRIPTION":"","RUNNINGSTATUS":"1",
+ "IP":"","PARENTNAME":"",
+ "OPERATIONSYSTEM":"0","LOCATION":"",
+ "HEALTHSTATUS":"1","MODEL":"",
+ "ID":"1","PARENTID":"",
+ "NETWORKNAME":"","TYPE":21},
+ {"PARENTTYPE":245,"NAME":"ubuntu",
+ "DESCRIPTION":"","RUNNINGSTATUS":"1",
+ "IP":"","PARENTNAME":"","OPERATIONSYSTEM":"0",
+ "LOCATION":"","HEALTHSTATUS":"1",
+ "MODEL":"","ID":"2","PARENTID":"",
+ "NETWORKNAME":"","TYPE":21}]} """
+ return data
+
+
+def find_data_host_associate(method):
+ if (method is None) or (method == "GET"):
+ data = '{"error":{"code":0}}'
+ return data
+
+
+def data_session(url):
+ if url == "/xx/sessions":
+ data = """{"error":{"code":0},
+ "data":{"username":"admin",
+ "iBaseToken":"2001031430",
+ "deviceid":"210235G7J20000000000"}}"""
+ if url == "sessions":
+ data = '{"error":{"code":0},"data":{"ID":11}}'
+ return data
+
+
+def data_lun(url, method):
+ if url == "lun":
+ data = find_data(method)
+ if url == "lun/1":
+ data = find_data_lun(method)
+ if url == "lun?range=[0-65535]":
+ data = find_data(method)
+ if url == "lungroup?range=[0-8191]":
+ data = find_data_lungroup(method)
+ if url == "lungroup":
+ data = find_data_lungroup(method)
+ if url == "lungroup/associate":
+ data = """{"error":{"code":0},
+ "data":{"NAME":"5mFHcBv4RkCcD+JyrWc0SA",
+ "DESCRIPTION":"5mFHcBv4RkCcD+JyrWc0SA",
+ "ID":"11",
+ "TYPE":256}}"""
+ return data
+
+
+def data_host(url, method):
+ if url == "hostgroup":
+ data = find_data_hostgroup(method)
+ if url == "hostgroup?range=[0-8191]":
+ data = find_data_hostgroup(method)
+ if url == "host":
+ data = find_data_host(method)
+ if url == "host?range=[0-65534]":
+ data = find_data_host(method)
+ if url == "host/associate":
+ data = find_data_host_associate(method)
+ if url == "host/associate?TYPE=21&ASSOCIATEOBJTYPE=14&ASSOCIATEOBJID=0":
+ data = find_data_host_associate(method)
+ return data
+
+
+def find_data_storagepool_snapshot(url, method):
+ if url == "storagepool":
+ data = """{"error":{"code":0},
+ "data":[{"USERFREECAPACITY":"985661440",
+ "ID":"0",
+ "NAME":"OpenStack_Pool",
+ "USERTOTALCAPACITY":"985661440"
+ }]}"""
+ if url == "snapshot":
+ data = find_data_snapshot(method)
+ if url == "snapshot/activate":
+ data = """{"error":{"code":0},"data":[
+ {"ID":11,"NAME":"SDFAJSDFLKJ"},
+ {"ID":12,"NAME":"SDFAJSDFLKJ"}]}"""
+
+ return data
+
+
+def find_data_luncpy_range_eth_port(url):
+ if url == "luncopy":
+ data = """{"error":{"code":0},
+ "data":{"COPYSTOPTIME":"-1",
+ "HEALTHSTATUS":"1",
+ "NAME":"w1PSNvu6RumcZMmSh4/l+Q==",
+ "RUNNINGSTATUS":"36",
+ "DESCRIPTION":"w1PSNvu6RumcZMmSh4/l+Q==",
+ "ID":"0","LUNCOPYTYPE":"1",
+ "COPYPROGRESS":"0","COPYSPEED":"2",
+ "TYPE":219,"COPYSTARTTIME":"-1"}}"""
+
+ if url == "LUNCOPY?range=[0-100000]":
+ data = """{"error":{"code":0},
+ "data":[{"COPYSTOPTIME":"1372209335",
+ "HEALTHSTATUS":"1",
+ "NAME":"w1PSNvu6RumcZMmSh4/l+Q==",
+ "RUNNINGSTATUS":"40",
+ "DESCRIPTION":"w1PSNvu6RumcZMmSh4/l+Q==",
+ "ID":"0","LUNCOPYTYPE":"1",
+ "COPYPROGRESS":"100",
+ "COPYSPEED":"2",
+ "TYPE":219,
+ "COPYSTARTTIME":"1372209329"}]}"""
+
+ if url == "eth_port":
+ data = """{"error":{"code":0},
+ "data":[{"PARENTTYPE":209,
+ "MACADDRESS":"00:22:a1:0a:79:57",
+ "ETHNEGOTIATE":"-1","ERRORPACKETS":"0",
+ "IPV4ADDR":"192.168.100.2",
+ "IPV6GATEWAY":"","IPV6MASK":"0",
+ "OVERFLOWEDPACKETS":"0","ISCSINAME":"P0",
+ "HEALTHSTATUS":"1","ETHDUPLEX":"2",
+ "ID":"16909568","LOSTPACKETS":"0",
+ "TYPE":213,"NAME":"P0","INIORTGT":"4",
+ "RUNNINGSTATUS":"10","IPV4GATEWAY":"",
+ "BONDNAME":"","STARTTIME":"1371684218",
+ "SPEED":"1000","ISCSITCPPORT":"0",
+ "IPV4MASK":"255.255.0.0","IPV6ADDR":"",
+ "LOGICTYPE":"0","LOCATION":"ENG0.B5.P0",
+ "MTU":"1500","PARENTID":"1.5"}]}"""
+ return data
+
+
+class Fake18000Common(rest_common.RestCommon):
+
+ def __init__(self, configuration):
+ rest_common.RestCommon.__init__(self, configuration)
+ self.test_normal = True
+ self.other_flag = True
+ self.connect_flag = False
+ self.delete_flag = False
+ self.terminateFlag = False
+ self.deviceid = None
+
+ 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 _get_qosid_by_lunid(self, lunid):
+ return ""
+
+ def _check_snapshot_exist(self, snapshot_id):
+ return True
+
+ def fc_initiator_data(self):
+ data = """{"error":{"code":0},"data":[
+ {"HEALTHSTATUS":"1","NAME":"",
+ "MULTIPATHTYPE":"1","ISFREE":"true",
+ "RUNNINGSTATUS":"27","ID":"10000090fa0d6754",
+ "OPERATIONSYSTEM":"255","TYPE":223},
+ {"HEALTHSTATUS":"1","NAME":"",
+ "MULTIPATHTYPE":"1","ISFREE":"true",
+ "RUNNINGSTATUS":"27","ID":"10000090fa0d6755",
+ "OPERATIONSYSTEM":"255","TYPE":223}]}"""
+ return data
+
+ def host_link(self):
+ data = """{"error":{"code":0},
+ "data":[{"PARENTTYPE":21,
+ "TARGET_ID":"0000000000000000",
+ "INITIATOR_NODE_WWN":"20000090fa0d6754",
+ "INITIATOR_TYPE":"223",
+ "RUNNINGSTATUS":"27",
+ "PARENTNAME":"ubuntuc",
+ "INITIATOR_ID":"10000090fa0d6754",
+ "TARGET_PORT_WWN":"24000022a10a2a39",
+ "HEALTHSTATUS":"1",
+ "INITIATOR_PORT_WWN":"10000090fa0d6754",
+ "ID":"010000090fa0d675-0000000000110400",
+ "TARGET_NODE_WWN":"21000022a10a2a39",
+ "PARENTID":"1","CTRL_ID":"0",
+ "TYPE":255,"TARGET_TYPE":"212"}]}"""
+ self.connect_flag = True
+ return data
+
+ def call(self, url=False, data=None, method=None):
+
+ url = url.replace('http://100.115.10.69:8082/deviceManager/rest', '')
+ url = url.replace('/210235G7J20000000000/', '')
+ data = None
+
+ if self.test_normal:
+ if url == "/xx/sessions" or url == "sessions":
+ data = data_session(url)
+
+ if url == "lun/count?TYPE=11&ASSOCIATEOBJTYPE=256&"\
+ "ASSOCIATEOBJID=11":
+ data = """{"data":{"COUNT":"7"},
+ "error":{"code":0,"description":"0"}}"""
+
+ if url == "lungroup/associate?TYPE=256&ASSOCIATEOBJTYPE=11&"\
+ "ASSOCIATEOBJID=11":
+ data = """{"error":{"code":0},
+ "data":[{"ID":11}]}"""
+
+ if url == "storagepool" or url == "snapshot" or url == "snaps"\
+ "hot/activate":
+ data = find_data_storagepool_snapshot(url, method)
+
+ if url == "lungroup" or url == "lungroup/associate"\
+ or url == "lun" or url == "lun/1":
+ data = data_lun(url, method)
+
+ if url == "lun?range=[0-65535]" or url == "lungroup?r"\
+ "ange=[0-8191]":
+ data = data_lun(url, method)
+
+ if url == "lungroup/associate?ID=11"\
+ "&ASSOCIATEOBJTYPE=11&ASSOCIATEOBJID=11":
+ data = '{"error":{"code":0}}'
+ self.terminateFlag = True
+
+ if url == "fc_initiator/10000090fa0d6754" or url == "lun/11"\
+ or url == "LUNCOPY/0"\
+ or url == "mappingview/REMOVE_ASSOCIATE":
+ data = '{"error":{"code":0}}'
+ self.delete_flag = True
+
+ if url == "LUNCOPY/start" or url == "mappingview/1"\
+ or url == "hostgroup/associate":
+ data = '{"error":{"code":0}}'
+
+ if url == "MAPPINGVIEW/CREATE_ASSOCIATE" or url == "snapshot/11"\
+ or url == "snapshot/stop" or url == "LUNGroup/11":
+ data = '{"error":{"code":0}}'
+ self.delete_flag = True
+
+ if url == "luncopy" or url == "eth_port" or url == "LUNC"\
+ "OPY?range=[0-100000]":
+ data = find_data_luncpy_range_eth_port(url)
+
+ if url == "iscsidevicename":
+ data = """{"error":{"code":0},
+ "data":[{"CMO_ISCSI_DEVICE_NAME":
+"iqn.2006-08.com.huawei:oceanstor:21000022a10a2a39:iscsinametest"}]}"""
+
+ if url == "hostgroup" or url == "host" or url == "host/associate":
+ data = data_host(url, method)
+
+ if url == "host/associate?TYPE=21&ASSOCIATEOBJTYPE=14&AS"\
+ "SOCIATEOBJID=0":
+ data = data_host(url, method)
+
+ if url == "hostgroup?range=[0-8191]" or url == "host?ra"\
+ "nge=[0-65534]":
+ data = data_host(url, method)
+
+ if url == "iscsi_initiator/iqn.1993-08.debian:01:ec2bff7ac3a3":
+ data = """{"error":{"code":0},"data":{
+ "ID":"iqn.1993-08.debian:01:ec2bff7ac3a3",
+ "NAME":"iqn.1993-08.debian:01:ec2bff7ac3a3",
+ "ISFREE":"True"}}"""
+
+ if url == "iscsi_initiator" or url == "iscsi_initiator/"\
+ or url == "iscsi_initiator?range=[0-65535]":
+ data = '{"error":{"code":0}}'
+
+ if url == "mappingview" or url == "mappingview?range=[0-65535]":
+ data = find_data_mappingview(method, self.other_flag)
+
+ if url == ("lun/associate?ID=1&TYPE=11&"
+ "ASSOCIATEOBJTYPE=21&ASSOCIATEOBJID=0"):
+ data = '{"error":{"code":0}}'
+
+ if url == "fc_initiator?ISFREE=true&range=[0-1000]":
+ data = self.fc_initiator_data()
+
+ if url == "host_link?INITIATOR_TYPE=223&INITIATOR_PORT_WWN="\
+ "10000090fa0d6754":
+ data = self.host_link()
+
+ if url == "mappingview/associate?TYPE=245&"\
+ "ASSOCIATEOBJTYPE=14&ASSOCIATEOBJID=0"\
+ or url == "mappingview/associate?TYPE=245&"\
+ "ASSOCIATEOBJTYPE=256&ASSOCIATEOBJID=11":
+ data = '{"error":{"code":0},"data":[{"ID":11,"NAME":"test"}]}'
+
+ if url == "lun/associate?TYPE=11&"\
+ "ASSOCIATEOBJTYPE=21&ASSOCIATEOBJID=1":
+ data = '{"error":{"code":0}}'
+ self.connect_flag = True
+
+ if url == "iscsi_tgt_port":
+ data = '{"data":[{"ETHPORTID":"139267",\
+ "ID":"iqn.oceanstor:21004846fb8ca15f::22003:111.111.101.244",\
+ "TPGT":"8196","TYPE":249}],\
+ "error":{"code":0,"description":"0"}}'
+
+ else:
+ data = '{"error":{"code":31755596}}'
+ if url == "lun/11":
+ if method == "GET":
+ data = """{"error":{"code":0},"data":{"ID":"11",
+ "IOCLASSID":"11",
+ "NAME":"5mFHcBv4RkCcD+JyrWc0SA"}}"""
+ res_json = json.loads(data)
+
+ return res_json
+
+
+class Fake18000Storage(huawei_18000.Huawei18000ISCSIDriver):
+ """Fake Huawei Storage, Rewrite some methods of HuaweiISCSIDriver."""
+
+ def __init__(self, configuration):
+ super(Fake18000Storage, self).__init__(configuration)
+ self.configuration = configuration
+
+ def do_setup(self):
+ self.common = Fake18000Common(configuration=self.configuration)
+
+
+class Fake18000FCStorage(huawei_18000.Huawei18000FCDriver):
+ """Fake Huawei Storage, Rewrite some methods of HuaweiISCSIDriver."""
+ def __init__(self, configuration):
+ super(Fake18000FCStorage, self).__init__(configuration)
+ self.configuration = configuration
+
+ def do_setup(self):
+ self.common = Fake18000Common(configuration=self.configuration)
+
+
+class Huawei18000ISCSIDriverTestCase(test.TestCase):
+
+ def setUp(self):
+ super(Huawei18000ISCSIDriverTestCase, 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.stubs.Set(time, 'sleep', Fake_sleep)
+ driver = Fake18000Storage(configuration=self.configuration)
+ self.driver = driver
+ self.driver.do_setup()
+ self.driver.common.test_normal = True
+
+ def testloginSuccess(self):
+ deviceid = self.driver.common.login()
+ self.assertEqual(deviceid, '210235G7J20000000000')
+
+ def testcreatevolumesuccess(self):
+ self.driver.common.login()
+ lun_info = self.driver.create_volume(test_volume)
+ self.assertEqual(lun_info['provider_location'], '1')
+ self.assertEqual(lun_info['lun_info']['NAME'],
+ '5mFHcBv4RkCcD+JyrWc0SA')
+
+ def testcreatesnapshotsuccess(self):
+ self.driver.common.login()
+ lun_info = self.driver.create_snapshot(test_volume)
+ self.assertEqual(lun_info['provider_location'], 11)
+ self.assertEqual(lun_info['lun_info']['NAME'], 'YheUoRwbSX2BxN7')
+
+ def testdeletevolumesuccess(self):
+ self.driver.common.login()
+ self.driver.common.delete_flag = False
+ self.driver.delete_volume(test_volume)
+ self.assertTrue(self.driver.common.delete_flag)
+
+ def testdeletesnapshotsuccess(self):
+ self.driver.common.login()
+ self.driver.common.delete_flag = False
+ self.driver.delete_snapshot(test_snap)
+ self.assertTrue(self.driver.common.delete_flag)
+
+ def testcolonevolumesuccess(self):
+ self.driver.common.login()
+ lun_info = self.driver.create_cloned_volume(test_volume,
+ test_volume)
+ self.assertEqual(lun_info['provider_location'], '1')
+ self.assertEqual(lun_info['lun_info']['NAME'],
+ '5mFHcBv4RkCcD+JyrWc0SA')
+
+ def testcreateolumefromsnapsuccess(self):
+ self.driver.common.login()
+ lun_info = self.driver.create_volume_from_snapshot(test_volume,
+ test_volume)
+ self.assertEqual(lun_info['provider_location'], '1')
+ self.assertEqual(lun_info['lun_info']['NAME'],
+ '5mFHcBv4RkCcD+JyrWc0SA')
+
+ def testinitializeconnectionsuccess(self):
+ self.driver.common.login()
+ iscsi_properties = self.driver.initialize_connection(test_volume,
+ FakeConnector)
+ self.assertEqual(iscsi_properties['data']['target_lun'], 1)
+
+ def testterminateconnectionsuccess(self):
+ self.driver.common.login()
+ self.driver.common.terminateFlag = False
+ self.driver.terminate_connection(test_volume, FakeConnector)
+ self.assertTrue(self.driver.common.terminateFlag)
+
+ def testinitializeconnectionnoviewsuccess(self):
+ self.driver.common.login()
+ self.driver.common.other_flag = False
+ self.driver.common.connect_flag = False
+ self.driver.initialize_connection(test_volume, FakeConnector)
+ self.assertTrue(self.driver.common.connect_flag)
+
+ def testterminateconnectionoviewnsuccess(self):
+ self.driver.common.login()
+ self.driver.common.terminateFlag = False
+ self.driver.terminate_connection(test_volume, FakeConnector)
+ self.assertTrue(self.driver.common.terminateFlag)
+
+ def testgetvolumestatus(self):
+ self.driver.common.login()
+ data = self.driver.get_volume_stats()
+ self.assertEqual(data['driver_version'], '1.1.0')
+
+ def testloginfail(self):
+ self.driver.common.test_normal = False
+ self.assertRaises(exception.CinderException, self.driver.common.login)
+
+ def testcreatesnapshotfail(self):
+ self.driver.common.login()
+ self.driver.common.test_normal = False
+ self.assertRaises(exception.CinderException,
+ self.driver.create_snapshot, test_volume)
+
+ def testcreatevolumefail(self):
+ self.driver.common.login()
+ self.driver.common.test_normal = False
+ self.assertRaises(exception.CinderException,
+ self.driver.create_volume, test_volume)
+
+ def testdeletevolumefail(self):
+ self.driver.common.login()
+ self.driver.common.test_normal = False
+ self.assertRaises(exception.CinderException,
+ self.driver.delete_volume, test_volume)
+
+ def testdeletesnapshotfail(self):
+ self.driver.common.login()
+ self.driver.common.test_normal = False
+ self.assertRaises(exception.CinderException,
+ self.driver.delete_snapshot, test_volume)
+
+ def testinitializeconnectionfail(self):
+ self.driver.common.login()
+ self.driver.common.test_normal = False
+ self.assertRaises(exception.CinderException,
+ self.driver.initialize_connection,
+ test_volume, FakeConnector)
+
+ 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 = 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)
+ lun = doc.createElement('LUN')
+ config.appendChild(lun)
+ storagepool = doc.createElement('StoragePool')
+ pool_text = doc.createTextNode('OpenStack_Pool')
+ storagepool.appendChild(pool_text)
+ lun.appendChild(storagepool)
+
+ prefetch = doc.createElement('Prefetch')
+ prefetch.setAttribute('Type', '0')
+ 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.100.2')
+ iscsi.appendChild(initiator)
+
+ fakefile = open(self.fake_conf_file, 'w')
+ fakefile.write(doc.toprettyxml(indent=''))
+ fakefile.close()
+
+
+class Huawei18000FCDriverTestCase(test.TestCase):
+
+ def setUp(self):
+ super(Huawei18000FCDriverTestCase, 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.stubs.Set(time, 'sleep', Fake_sleep)
+ driver = Fake18000FCStorage(configuration=self.configuration)
+ self.driver = driver
+ self.driver.do_setup()
+ self.driver.common.test_normal = True
+
+ def testloginSuccess(self):
+ deviceid = self.driver.common.login()
+ self.assertEqual(deviceid, '210235G7J20000000000')
+
+ def testcreatevolumesuccess(self):
+ self.driver.common.login()
+ lun_info = self.driver.create_volume(test_volume)
+ self.assertEqual(lun_info['provider_location'], '1')
+ self.assertEqual(lun_info['lun_info']['NAME'],
+ '5mFHcBv4RkCcD+JyrWc0SA')
+
+ def testcreatesnapshotsuccess(self):
+ self.driver.common.login()
+ lun_info = self.driver.create_snapshot(test_volume)
+ self.assertEqual(lun_info['provider_location'], 11)
+ self.assertEqual(lun_info['lun_info']['NAME'], 'YheUoRwbSX2BxN7')
+
+ def testdeletevolumesuccess(self):
+ self.driver.common.login()
+ self.driver.common.delete_flag = False
+ self.driver.delete_volume(test_volume)
+ self.assertTrue(self.driver.common.delete_flag)
+
+ def testdeletesnapshotsuccess(self):
+ self.driver.common.login()
+ self.driver.common.delete_flag = False
+ self.driver.delete_snapshot(test_snap)
+ self.assertTrue(self.driver.common.delete_flag)
+
+ def testcolonevolumesuccess(self):
+ self.driver.common.login()
+ lun_info = self.driver.create_cloned_volume(test_volume,
+ test_volume)
+ self.assertEqual(lun_info['provider_location'], '1')
+ self.assertEqual(lun_info['lun_info']['NAME'],
+ '5mFHcBv4RkCcD+JyrWc0SA')
+
+ def testcreateolumefromsnapsuccess(self):
+ self.driver.common.login()
+ volumeid = self.driver.create_volume_from_snapshot(test_volume,
+ test_volume)
+ self.assertEqual(volumeid['provider_location'], '1')
+
+ def testinitializeconnectionsuccess(self):
+ self.driver.common.login()
+ properties = self.driver.initialize_connection(test_volume,
+ FakeConnector)
+ self.assertEqual(properties['data']['target_lun'], 1)
+
+ def testterminateconnectionsuccess(self):
+ self.driver.common.login()
+ self.driver.common.terminateFlag = False
+ self.driver.terminate_connection(test_volume, FakeConnector)
+ self.assertTrue(self.driver.common.terminateFlag)
+
+ def testinitializeconnectionnoviewsuccess(self):
+ self.driver.common.login()
+ self.driver.common.other_flag = False
+ self.driver.common.connect_flag = False
+ self.driver.initialize_connection(test_volume, FakeConnector)
+ self.assertTrue(self.driver.common.connect_flag)
+
+ def testterminateconnectionoviewnsuccess(self):
+ self.driver.common.login()
+ self.driver.common.terminateFlag = False
+ self.driver.terminate_connection(test_volume, FakeConnector)
+ self.assertTrue(self.driver.common.terminateFlag)
+
+ def testgetvolumestatus(self):
+ self.driver.common.login()
+ data = self.driver.get_volume_stats()
+ self.assertEqual(data['driver_version'], '1.1.0')
+
+ def testloginfail(self):
+ self.driver.common.test_normal = False
+ self.assertRaises(exception.CinderException,
+ self.driver.common.login)
+
+ def testcreatesnapshotfail(self):
+ self.driver.common.login()
+ self.driver.common.test_normal = False
+ self.assertRaises(exception.CinderException,
+ self.driver.create_snapshot, test_volume)
+
+ def testcreatevolumefail(self):
+ self.driver.common.login()
+ self.driver.common.test_normal = False
+ self.assertRaises(exception.CinderException,
+ self.driver.create_volume, test_volume)
+
+ def testdeletevolumefail(self):
+ self.driver.common.login()
+ self.driver.common.test_normal = False
+ self.assertRaises(exception.CinderException,
+ self.driver.delete_volume, test_volume)
+
+ def testdeletesnapshotfail(self):
+ self.driver.common.login()
+ self.driver.common.test_normal = False
+ self.assertRaises(exception.CinderException,
+ self.driver.delete_snapshot, test_volume)
+
+ def testinitializeconnectionfail(self):
+ self.driver.common.login()
+ self.driver.common.test_normal = False
+ self.assertRaises(exception.CinderException,
+ self.driver.initialize_connection,
+ test_volume, FakeConnector)
+
+ 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 = 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)
+
+ lun = doc.createElement('LUN')
+ config.appendChild(lun)
+ storagepool = doc.createElement('StoragePool')
+ pool_text = doc.createTextNode('OpenStack_Pool')
+ storagepool.appendChild(pool_text)
+ lun.appendChild(storagepool)
+
+ prefetch = doc.createElement('Prefetch')
+ prefetch.setAttribute('Type', '0')
+ 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.100.2')
+ iscsi.appendChild(initiator)
+
+ fakefile = open(self.fake_conf_file, 'w')
+ fakefile.write(doc.toprettyxml(indent=''))
+ fakefile.close()
--- /dev/null
+# Copyright 2012 OpenStack Foundation
+#
+# 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.
+
+
+from oslo.config import cfg
+from oslo.utils import importutils
+
+from cinder import context
+from cinder import test
+
+
+CONF = cfg.CONF
+HUAWEI_ISCSI_MODULE = ("cinder.volume.drivers.huawei.huawei_18000."
+ "Huawei18000ISCSIDriver")
+HUAWEI_FC_MODULE = ("cinder.volume.drivers.huawei.huawei_18000."
+ "Huawei18000FCDriver")
+
+
+class VolumeDriverCompatibility(test.TestCase):
+ """Test backwards compatibility for volume drivers."""
+
+ def fake_update_cluster_status(self):
+ return
+
+ def setUp(self):
+ super(VolumeDriverCompatibility, self).setUp()
+ self.manager = importutils.import_object(CONF.volume_manager)
+ self.context = context.get_admin_context()
+
+ def _load_driver(self, driver):
+ self.manager.__init__(volume_driver=driver)
+
+ def _driver_module_name(self):
+ return "%s.%s" % (self.manager.driver.__class__.__module__,
+ self.manager.driver.__class__.__name__)
+
+ def test_huawei_driver_iscsi_old(self):
+ self._load_driver(
+ 'cinder.volume.drivers.huawei.huawei_hvs.HuaweiHVSISCSIDriver')
+ self.assertEqual(self._driver_module_name(), HUAWEI_ISCSI_MODULE)
+
+ def test_huawei_driver_iscsi_new(self):
+ self._load_driver(HUAWEI_ISCSI_MODULE)
+ self.assertEqual(self._driver_module_name(), HUAWEI_ISCSI_MODULE)
+
+ def test_huawei_driver_fc_old(self):
+ self._load_driver(
+ 'cinder.volume.drivers.huawei.huawei_hvs.HuaweiHVSFCDriver')
+ self.assertEqual(self._driver_module_name(), HUAWEI_FC_MODULE)
+
+ def test_huawei_driver_fc_new(self):
+ self._load_driver(HUAWEI_FC_MODULE)
+ self.assertEqual(self._driver_module_name(), HUAWEI_FC_MODULE)
+++ /dev/null
-
-# Copyright (c) 2013 Huawei Technologies Co., Ltd.
-# Copyright (c) 2013 OpenStack Foundation
-# 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.
-"""
-Unit Tests for Huawei HVS volume drivers.
-"""
-
-import json
-import os
-import shutil
-import tempfile
-import time
-from xml.dom.minidom import Document
-
-import mox
-
-from cinder import exception
-from cinder import test
-from cinder.volume import configuration as conf
-from cinder.volume.drivers.huawei import huawei_hvs
-from cinder.volume.drivers.huawei import rest_common
-
-
-test_volume = {'name': 'volume-21ec7341-9256-497b-97d9-ef48edcf0635',
- 'size': 2,
- 'volume_name': 'vol1',
- 'id': '21ec7341-9256-497b-97d9-ef48edcf0635',
- 'volume_id': '21ec7341-9256-497b-97d9-ef48edcf0635',
- 'provider_auth': None,
- 'project_id': 'project',
- 'display_name': 'vol1',
- 'display_description': 'test volume',
- 'volume_type_id': None}
-
-test_snap = {'name': 'volume-21ec7341-9256-497b-97d9-ef48edcf0635',
- 'size': 1,
- 'volume_name': 'vol1',
- 'id': '21ec7341-9256-497b-97d9-ef48edcf0635',
- 'volume_id': '21ec7341-9256-497b-97d9-ef48edcf0635',
- 'provider_auth': None,
- 'project_id': 'project',
- 'display_name': 'vol1',
- 'display_description': 'test volume',
- 'volume_type_id': None}
-
-FakeConnector = {'initiator': 'iqn.1993-08.debian:01:ec2bff7ac3a3',
- 'wwpns': ['10000090fa0d6754'],
- 'wwnns': ['10000090fa0d6755'],
- 'host': 'fakehost',
- 'ip': '10.10.0.1'}
-
-volume_size = 3
-
-
-def Fake_sleep(time):
- pass
-
-
-class FakeHVSCommon(rest_common.HVSCommon):
-
- def __init__(self, configuration):
- rest_common.HVSCommon.__init__(self, configuration)
- self.test_normal = True
- self.other_flag = True
- self.deviceid = None
- self.lun_id = None
- self.snapshot_id = None
- self.luncopy_id = None
- self.termin_flag = False
-
- def _parse_volume_type(self, volume):
- self._get_lun_conf_params()
- 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 _change_file_mode(self, filepath):
- # NOTE(flaper87): Changing file permissions is
- # not needed since we're using a tempfile created
- # within this test.
- pass
-
- def call(self, url=False, data=None, method=None): # noqa
-
- url = url.replace('http://100.115.10.69:8082/deviceManager/rest', '')
- url = url.replace('/210235G7J20000000000/', '')
- data = None
-
- if self.test_normal:
- if url == "/xx/sessions":
- data = """{"error":{"code":0},
- "data":{"username":"admin",
- "deviceid":"210235G7J20000000000"
- }}"""
- if url == "sessions":
- data = """{"error":{"code":0},
- "data":{"ID":11}}"""
-
- if url == "storagepool":
- data = """{"error":{"code":0},
- "data":[{"ID":"0",
- "NAME":"OpenStack_Pool",
- "USERFREECAPACITY":"985661440",
- "USERTOTALCAPACITY":"985661440"
- }]}"""
-
- if url == "lun":
- if method is None:
- data = """{"error":{"code":0},
- "data":{"ID":"1",
- "NAME":"5mFHcBv4RkCcD+JyrWc0SA"}}"""
- self.lun_id = "0"
-
- if method == 'GET':
- data = """{"error":{"code":0},
- "data":[{"ID":"1",
- "NAME":"IexzQZJWSXuX2e9I7c8GNQ"}]}"""
-
- if url == "lungroup":
- if method is None:
- data = """{"error":{"code":0},
- "data":{"NAME":"5mFHcBv4RkCcD+JyrWc0SA",
- "DESCRIPTION":"5mFHcBv4RkCcD",
- "ID":"11",
- "TYPE":256}}"""
-
- if method == "GET":
- data = """{"error":{"code":0},
- "data":[{"NAME":"IexzQZJWSXuX2e9I7c8GNQ",
- "DESCRIPTION":"5mFHcBv4RkCcD",
- "ID":"11",
- "TYPE":256}]}"""
-
- if method == "DELETE":
- data = """{"error":{"code":0},
- "data":[{"NAME":"IexzQZJWSXuX2e9I7c8GNQ",
- "DESCRIPTION":"5mFHcBv4RkCcD+JyrWc0SA",
- "ID":"11",
- "TYPE":256}]}"""
-
- if url == "lungroup/associate":
- data = """{"error":{"code":0},
- "data":{"NAME":"5mFHcBv4RkCcD+JyrWc0SA",
- "DESCRIPTION":"5mFHcBv4RkCcD+JyrWc0SA",
- "ID":"11",
- "TYPE":256}}"""
-
- if url == "snapshot":
- if method is None:
- data = """{"error":{"code":0},
- "data":{"ID":11}}"""
- self.snapshot_id = "3"
-
- if method == "GET":
- data = """{"error":{"code":0},
- "data":[{"ID":11,"NAME":"SDFAJSDFLKJ"},
- {"ID":12,"NAME":"SDFAJSDFLKJ"}]}"""
-
- if url == "snapshot/activate":
- data = """{"error":{"code":0}}"""
-
- if url == ("lungroup/associate?ID=11"
- "&ASSOCIATEOBJTYPE=11&ASSOCIATEOBJID=1"):
- data = """{"error":{"code":0}}"""
-
- if url == "LUNGroup/11":
- data = """{"error":{"code":0}}"""
-
- if url == 'lun/1':
- data = """{"error":{"code":0}}"""
- self.lun_id = None
-
- if url == 'snapshot':
- if method == "GET":
- data = """{"error":{"code":0},
- "data":[{"PARENTTYPE":11,
- "NAME":"IexzQZJWSXuX2e9I7c8GNQ",
- "WWN":"60022a11000a2a3907ce96cb00000b",
- "ID":"11",
- "CONSUMEDCAPACITY":"0"}]}"""
-
- if url == "snapshot/stop":
- data = """{"error":{"code":0}}"""
-
- if url == "snapshot/11":
- data = """{"error":{"code":0}}"""
- self.snapshot_id = None
-
- if url == "luncopy":
- data = """{"error":{"code":0},
- "data":{"COPYSTOPTIME":"-1",
- "HEALTHSTATUS":"1",
- "NAME":"w1PSNvu6RumcZMmSh4/l+Q==",
- "RUNNINGSTATUS":"36",
- "DESCRIPTION":"w1PSNvu6RumcZMmSh4/l+Q==",
- "ID":"0","LUNCOPYTYPE":"1",
- "COPYPROGRESS":"0","COPYSPEED":"2",
- "TYPE":219,"COPYSTARTTIME":"-1"}}"""
- self.luncopy_id = "7"
-
- if url == "LUNCOPY/start":
- data = """{"error":{"code":0}}"""
-
- if url == "LUNCOPY?range=[0-100000]":
- data = """{"error":{"code":0},
- "data":[{"COPYSTOPTIME":"1372209335",
- "HEALTHSTATUS":"1",
- "NAME":"w1PSNvu6RumcZMmSh4/l+Q==",
- "RUNNINGSTATUS":"40",
- "DESCRIPTION":"w1PSNvu6RumcZMmSh4/l+Q==",
- "ID":"0","LUNCOPYTYPE":"1",
- "COPYPROGRESS":"100",
- "COPYSPEED":"2",
- "TYPE":219,
- "COPYSTARTTIME":"1372209329"}]}"""
-
- if url == "LUNCOPY/0":
- data = '{"error":{"code":0}}'
-
- if url == "eth_port":
- data = """{"error":{"code":0},
- "data":[{"PARENTTYPE":209,
- "MACADDRESS":"00:22:a1:0a:79:57",
- "ETHNEGOTIATE":"-1","ERRORPACKETS":"0",
- "IPV4ADDR":"100.115.10.68",
- "IPV6GATEWAY":"","IPV6MASK":"0",
- "OVERFLOWEDPACKETS":"0","ISCSINAME":"P0",
- "HEALTHSTATUS":"1","ETHDUPLEX":"2",
- "ID":"16909568","LOSTPACKETS":"0",
- "TYPE":213,"NAME":"P0","INIORTGT":"4",
- "RUNNINGSTATUS":"10","IPV4GATEWAY":"",
- "BONDNAME":"","STARTTIME":"1371684218",
- "SPEED":"1000","ISCSITCPPORT":"0",
- "IPV4MASK":"255.255.0.0","IPV6ADDR":"",
- "LOGICTYPE":"0","LOCATION":"ENG0.B5.P0",
- "MTU":"1500","PARENTID":"1.5"}]}"""
-
- if url == "iscsidevicename":
- data = """{"error":{"code":0},
-"data":[{"CMO_ISCSI_DEVICE_NAME":
-"iqn.2006-08.com.huawei:oceanstor:21000022a10a2a39:iscsinametest"}]}"""
-
- if url == "hostgroup":
- if method is None:
- data = """{"error":{"code":0},
- "data":{"NAME":"ubuntuc",
- "DESCRIPTION":"",
- "ID":"0",
- "TYPE":14}}"""
-
- if method == "GET":
- data = """{"error":{"code":0},
- "data":[{"NAME":"ubuntuc",
- "DESCRIPTION":"",
- "ID":"0",
- "TYPE":14}]}"""
-
- if url == "host":
- if method is None:
- data = """{"error":{"code":0},
- "data":{"PARENTTYPE":245,
- "NAME":"Default Host",
- "DESCRIPTION":"",
- "RUNNINGSTATUS":"1",
- "IP":"","PARENTNAME":"0",
- "OPERATIONSYSTEM":"1","LOCATION":"",
- "HEALTHSTATUS":"1","MODEL":"",
- "ID":"0","PARENTID":"0",
- "NETWORKNAME":"","TYPE":21}} """
-
- if method == "GET":
- data = """{"error":{"code":0},
- "data":[{"PARENTTYPE":245,
- "NAME":"ubuntuc",
- "DESCRIPTION":"",
- "RUNNINGSTATUS":"1",
- "IP":"","PARENTNAME":"",
- "OPERATIONSYSTEM":"0",
- "LOCATION":"",
- "HEALTHSTATUS":"1",
- "MODEL":"",
- "ID":"1","PARENTID":"",
- "NETWORKNAME":"","TYPE":21},
- {"PARENTTYPE":245,
- "NAME":"ubuntu",
- "DESCRIPTION":"",
- "RUNNINGSTATUS":"1",
- "IP":"","PARENTNAME":"",
- "OPERATIONSYSTEM":"0",
- "LOCATION":"",
- "HEALTHSTATUS":"1",
- "MODEL":"","ID":"2",
- "PARENTID":"",
- "NETWORKNAME":"","TYPE":21}]} """
-
- if url == "host/associate":
- if method is None:
- data = """{"error":{"code":0}}"""
- if method == "GET":
- data = """{"error":{"code":0}}"""
-
- if url == "iscsi_initiator/iqn.1993-08.debian:01:ec2bff7ac3a3":
- data = """{"error":{"code":0},
- "data":{"ID":"iqn.1993-08.win:01:ec2bff7ac3a3",
- "NAME":"iqn.1993-08.win:01:ec2bff7ac3a3",
- "ISFREE":"True"}}"""
-
- if url == "iscsi_initiator/":
- data = """{"error":{"code":0}}"""
-
- if url == "iscsi_initiator":
- data = """{"error":{"code":0}}"""
-
- if url == "mappingview":
- self.termin_flag = True
- if method is None:
- data = """{"error":{"code":0},
- "data":{"WORKMODE":"255",
- "HEALTHSTATUS":"1",
- "NAME":"mOWtSXnaQKi3hpB3tdFRIQ",
- "RUNNINGSTATUS":"27","DESCRIPTION":"",
- "ENABLEINBANDCOMMAND":"true",
- "ID":"1","INBANDLUNWWN":"",
- "TYPE":245}}"""
-
- if method == "GET":
- if self.other_flag:
- data = """{"error":{"code":0},
- "data":[{"WORKMODE":"255",
- "HEALTHSTATUS":"1",
- "NAME":"mOWtSXnaQKi3hpB3tdFRIQ",
- "RUNNINGSTATUS":"27",
- "DESCRIPTION":"",
- "ENABLEINBANDCOMMAND":
- "true","ID":"1",
- "INBANDLUNWWN":"",
- "TYPE":245},
- {"WORKMODE":"255",
- "HEALTHSTATUS":"1",
- "NAME":"YheUoRwbSX2BxN767nvLSw",
- "RUNNINGSTATUS":"27",
- "DESCRIPTION":"",
- "ENABLEINBANDCOMMAND":"true",
- "ID":"2",
- "INBANDLUNWWN":"",
- "TYPE":245}]}"""
- else:
- data = """{"error":{"code":0},
- "data":[{"WORKMODE":"255",
- "HEALTHSTATUS":"1",
- "NAME":"IexzQZJWSXuX2e9I7c8GNQ",
- "RUNNINGSTATUS":"27",
- "DESCRIPTION":"",
- "ENABLEINBANDCOMMAND":"true",
- "ID":"1",
- "INBANDLUNWWN":"",
- "TYPE":245},
- {"WORKMODE":"255",
- "HEALTHSTATUS":"1",
- "NAME":"YheUoRwbSX2BxN767nvLSw",
- "RUNNINGSTATUS":"27",
- "DESCRIPTION":"",
- "ENABLEINBANDCOMMAND":"true",
- "ID":"2",
- "INBANDLUNWWN":"",
- "TYPE":245}]}"""
-
- if url == "MAPPINGVIEW/CREATE_ASSOCIATE":
- data = """{"error":{"code":0}}"""
-
- if url == ("lun/associate?TYPE=11&"
- "ASSOCIATEOBJTYPE=21&ASSOCIATEOBJID=0"):
- data = """{"error":{"code":0}}"""
-
- if url == "fc_initiator?ISFREE=true&range=[0-1000]":
- data = """{"error":{"code":0},
- "data":[{"HEALTHSTATUS":"1",
- "NAME":"",
- "MULTIPATHTYPE":"1",
- "ISFREE":"true",
- "RUNNINGSTATUS":"27",
- "ID":"10000090fa0d6754",
- "OPERATIONSYSTEM":"255",
- "TYPE":223},
- {"HEALTHSTATUS":"1",
- "NAME":"",
- "MULTIPATHTYPE":"1",
- "ISFREE":"true",
- "RUNNINGSTATUS":"27",
- "ID":"10000090fa0d6755",
- "OPERATIONSYSTEM":"255",
- "TYPE":223}]}"""
-
- if url == "host_link?INITIATOR_TYPE=223&INITIATOR_PORT_WWN="\
- "10000090fa0d6754":
-
- data = """{"error":{"code":0},
- "data":[{"PARENTTYPE":21,
- "TARGET_ID":"0000000000000000",
- "INITIATOR_NODE_WWN":"20000090fa0d6754",
- "INITIATOR_TYPE":"223",
- "RUNNINGSTATUS":"27",
- "PARENTNAME":"ubuntuc",
- "INITIATOR_ID":"10000090fa0d6754",
- "TARGET_PORT_WWN":"24000022a10a2a39",
- "HEALTHSTATUS":"1",
- "INITIATOR_PORT_WWN":"10000090fa0d6754",
- "ID":"010000090fa0d675-0000000000110400",
- "TARGET_NODE_WWN":"21000022a10a2a39",
- "PARENTID":"1","CTRL_ID":"0",
- "TYPE":255,"TARGET_TYPE":"212"}]}"""
-
- if url == ("mappingview/associate?TYPE=245&"
- "ASSOCIATEOBJTYPE=14&ASSOCIATEOBJID=0"):
-
- data = """{"error":{"code":0},
- "data":[{"ID":11,"NAME":"test"}]}"""
-
- if url == ("mappingview/associate?TYPE=245&"
- "ASSOCIATEOBJTYPE=256&ASSOCIATEOBJID=11"):
-
- data = """{"error":{"code":0},
- "data":[{"ID":11,"NAME":"test"}]}"""
-
- if url == "fc_initiator/10000090fa0d6754":
- data = """{"error":{"code":0}}"""
-
- if url == "mappingview/REMOVE_ASSOCIATE":
- data = """{"error":{"code":0}}"""
- self.termin_flag = True
-
- if url == "mappingview/1":
- data = """{"error":{"code":0}}"""
-
- if url == "ioclass":
- data = """{"error":{"code":0},
- "data":[{"NAME":"OpenStack_Qos_High",
- "ID":"0",
- "LUNLIST":"[]",
- "TYPE":230}]}"""
-
- if url == "ioclass/0":
- data = """{"error":{"code":0}}"""
-
- if url == "lun/expand":
- data = """{"error":{"code":0}}"""
- self.lun_id = '0'
-
- else:
- data = """{"error":{"code":31755596}}"""
-
- res_json = json.loads(data)
- return res_json
-
-
-class FakeHVSiSCSIStorage(huawei_hvs.HuaweiHVSISCSIDriver):
-
- def __init__(self, configuration):
- super(FakeHVSiSCSIStorage, self).__init__(configuration)
- self.configuration = configuration
-
- def do_setup(self, context):
- self.common = FakeHVSCommon(configuration=self.configuration)
-
-
-class FakeHVSFCStorage(huawei_hvs.HuaweiHVSFCDriver):
-
- def __init__(self, configuration):
- super(FakeHVSFCStorage, self).__init__(configuration)
- self.configuration = configuration
-
- def do_setup(self, context):
- self.common = FakeHVSCommon(configuration=self.configuration)
-
-
-class HVSRESTiSCSIDriverTestCase(test.TestCase):
- def setUp(self):
- super(HVSRESTiSCSIDriverTestCase, self).setUp()
- self.tmp_dir = tempfile.mkdtemp()
- self.addCleanup(shutil.rmtree, self.tmp_dir)
- self.fake_conf_file = self.tmp_dir + '/cinder_huawei_conf.xml'
- self.addCleanup(os.remove, self.fake_conf_file)
-
- self.create_fake_conf_file()
- self.configuration = mox.MockObject(conf.Configuration)
- self.configuration.cinder_huawei_conf_file = self.fake_conf_file
- self.configuration.append_config_values(mox.IgnoreArg())
-
- self.stubs.Set(time, 'sleep', Fake_sleep)
- #self.stubs.Set(greenthread, 'sleep', Fake_sleep)
-
- self.driver = FakeHVSiSCSIStorage(configuration=self.configuration)
- self.driver.do_setup({})
- self.driver.common.test_normal = True
-
- def test_log_in_success(self):
- deviceid = self.driver.common.login()
- self.assertIsNotNone(deviceid)
-
- def test_log_out_success(self):
- self.driver.common.login()
- self.driver.common.login_out()
-
- def test_create_volume_success(self):
- self.driver.common.login()
- self.driver.create_volume(test_volume)
- self.assertEqual(self.driver.common.lun_id, "0")
-
- def test_extend_volume_success(self):
- self.driver.common.login()
- self.driver.extend_volume(test_volume, volume_size)
- self.assertEqual(self.driver.common.lun_id, "0")
-
- def test_create_snapshot_success(self):
- self.driver.common.login()
- self.driver.create_snapshot(test_volume)
- self.assertEqual(self.driver.common.snapshot_id, "3")
-
- def test_delete_volume_success(self):
- self.driver.common.login()
- self.driver.delete_volume(test_volume)
- self.assertIsNone(self.driver.common.lun_id)
-
- def test_delete_snapshot_success(self):
- self.driver.common.login()
- self.driver.delete_snapshot(test_snap)
- self.assertIsNone(self.driver.common.snapshot_id)
-
- def test_colone_volume_success(self):
- self.driver.common.login()
- self.driver.create_cloned_volume(test_volume, test_volume)
- self.assertEqual(self.driver.common.luncopy_id, "7")
-
- def test_create_volume_from_snapshot_success(self):
- self.driver.common.login()
- self.driver.create_volume_from_snapshot(test_volume, test_volume)
- self.assertEqual(self.driver.common.luncopy_id, "7")
-
- def test_initialize_connection_success(self):
- self.driver.common.login()
- conn = self.driver.initialize_connection(test_volume, FakeConnector)
- self.assertEqual(conn['data']['target_lun'], 1)
-
- def test_terminate_connection_success(self):
- self.driver.common.login()
- self.driver.terminate_connection(test_volume, FakeConnector)
- self.assertEqual(self.driver.common.termin_flag, True)
-
- def test_initialize_connection_no_view_success(self):
- self.driver.common.login()
- self.driver.common.other_flag = False
- conn = self.driver.initialize_connection(test_volume, FakeConnector)
- self.assertEqual(conn['data']['target_lun'], 1)
-
- def test_terminate_connectio_no_view_success(self):
- self.driver.common.login()
- self.driver.common.other_flag = False
- self.driver.terminate_connection(test_volume, FakeConnector)
- self.assertEqual(self.driver.common.termin_flag, True)
-
- def test_get_volume_stats(self):
- self.driver.common.login()
- status = self.driver.get_volume_stats()
- self.assertIsNotNone(status['free_capacity_gb'])
-
- def test_create_snapshot_fail(self):
- self.driver.common.login()
- self.driver.common.test_normal = False
- self.assertRaises(exception.CinderException,
- self.driver.create_snapshot, test_volume)
-
- def test_create_volume_fail(self):
- self.driver.common.login()
- self.driver.common.test_normal = False
- self.assertRaises(exception.CinderException,
- self.driver.create_volume, test_volume)
-
- def test_delete_volume_fail(self):
- self.driver.common.login()
- self.driver.common.test_normal = False
- self.assertRaises(exception.CinderException,
- self.driver.delete_volume, test_volume)
-
- def test_delete_snapshot_fail(self):
- self.driver.common.login()
- self.driver.common.test_normal = False
- self.assertRaises(exception.CinderException,
- self.driver.delete_snapshot, test_volume)
-
- def test_initialize_connection_fail(self):
- self.driver.common.login()
- self.driver.common.test_normal = False
- self.assertRaises(exception.CinderException,
- self.driver.initialize_connection,
- test_volume, FakeConnector)
-
- def create_fake_conf_file(self):
- doc = Document()
-
- config = doc.createElement('config')
- doc.appendChild(config)
-
- storage = doc.createElement('Storage')
- config.appendChild(storage)
-
- product = doc.createElement('Product')
- product_text = doc.createTextNode('HVS')
- product.appendChild(product_text)
- storage.appendChild(product)
-
- protocol = doc.createElement('Protocol')
- protocol_text = doc.createTextNode('iSCSI')
- protocol.appendChild(protocol_text)
- storage.appendChild(protocol)
-
- 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('HVSURL')
- url_text = doc.createTextNode('http://100.115.10.69:8082/'
- 'deviceManager/rest/')
- url.appendChild(url_text)
- storage.appendChild(url)
- lun = doc.createElement('LUN')
- config.appendChild(lun)
- storagepool = doc.createElement('StoragePool')
- pool_text = doc.createTextNode('OpenStack_Pool')
- storagepool.appendChild(pool_text)
- lun.appendChild(storagepool)
-
- luntype = doc.createElement('LUNType')
- luntype_text = doc.createTextNode('Thick')
- luntype.appendChild(luntype_text)
- lun.appendChild(luntype)
-
- writetype = doc.createElement('WriteType')
- writetype_text = doc.createTextNode('1')
- writetype.appendChild(writetype_text)
- lun.appendChild(writetype)
-
- prefetchType = doc.createElement('Prefetch')
- prefetchType.setAttribute('Type', '2')
- prefetchType.setAttribute('Value', '20')
- lun.appendChild(prefetchType)
-
- 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', '100.115.10.68')
- iscsi.appendChild(initiator)
-
- newefile = open(self.fake_conf_file, 'w')
- newefile.write(doc.toprettyxml(indent=''))
- newefile.close()
-
-
-class HVSRESTFCDriverTestCase(test.TestCase):
- def setUp(self):
- super(HVSRESTFCDriverTestCase, self).setUp()
- self.tmp_dir = tempfile.mkdtemp()
- self.addCleanup(shutil.rmtree, self.tmp_dir)
- self.fake_conf_file = self.tmp_dir + '/cinder_huawei_conf.xml'
- self.addCleanup(os.remove, self.fake_conf_file)
-
- self.create_fake_conf_file()
- self.configuration = mox.MockObject(conf.Configuration)
- self.configuration.cinder_huawei_conf_file = self.fake_conf_file
- self.configuration.append_config_values(mox.IgnoreArg())
-
- self.stubs.Set(time, 'sleep', Fake_sleep)
-
- self.driver = FakeHVSFCStorage(configuration=self.configuration)
- self.driver.do_setup({})
- self.driver.common.test_normal = True
-
- def test_log_in_Success(self):
- deviceid = self.driver.common.login()
- self.assertIsNotNone(deviceid)
-
- def test_create_volume_success(self):
- self.driver.common.login()
- self.driver.create_volume(test_volume)
- self.assertEqual(self.driver.common.lun_id, "0")
-
- def test_extend_volume_success(self):
- self.driver.common.login()
- self.driver.extend_volume(test_volume, volume_size)
- self.assertEqual(self.driver.common.lun_id, "0")
-
- def test_create_snapshot_success(self):
- self.driver.common.login()
- self.driver.create_snapshot(test_volume)
- self.assertEqual(self.driver.common.snapshot_id, "3")
-
- def test_delete_volume_success(self):
- self.driver.common.login()
- self.driver.delete_volume(test_volume)
- self.assertIsNone(self.driver.common.lun_id)
-
- def test_delete_snapshot_success(self):
- self.driver.common.login()
- self.driver.delete_snapshot(test_snap)
- self.assertIsNone(self.driver.common.snapshot_id)
-
- def test_colone_volume_success(self):
- self.driver.common.login()
- self.driver.create_cloned_volume(test_volume, test_volume)
- self.assertEqual(self.driver.common.luncopy_id, "7")
-
- def test_create_volume_from_snapshot_success(self):
- self.driver.common.login()
- self.driver.create_volume_from_snapshot(test_volume, test_volume)
- self.assertEqual(self.driver.common.luncopy_id, "7")
-
- def test_initialize_connection_success(self):
- self.driver.common.login()
- conn = self.driver.initialize_connection(test_volume, FakeConnector)
- self.assertEqual(conn['data']['target_lun'], 1)
-
- def test_terminate_connection_success(self):
- self.driver.common.login()
- self.driver.terminate_connection(test_volume, FakeConnector)
- self.assertEqual(self.driver.common.termin_flag, True)
-
- def test_initialize_connection_no_view_success(self):
- self.driver.common.login()
- self.driver.common.other_flag = False
- conn = self.driver.initialize_connection(test_volume, FakeConnector)
- self.assertEqual(conn['data']['target_lun'], 1)
-
- def test_terminate_connection_no_viewn_success(self):
- self.driver.common.login()
- self.driver.common.other_flag = False
- self.driver.terminate_connection(test_volume, FakeConnector)
- self.assertEqual(self.driver.common.termin_flag, True)
-
- def test_get_volume_stats(self):
- self.driver.common.login()
- status = self.driver.get_volume_stats()
- self.assertIsNotNone(status['free_capacity_gb'])
-
- def test_create_snapshot_fail(self):
- self.driver.common.login()
- self.driver.common.test_normal = False
- self.assertRaises(exception.CinderException,
- self.driver.create_snapshot, test_volume)
-
- def test_create_volume_fail(self):
- self.driver.common.login()
- self.driver.common.test_normal = False
- self.assertRaises(exception.CinderException,
- self.driver.create_volume, test_volume)
-
- def test_delete_volume_fail(self):
- self.driver.common.login()
- self.driver.common.test_normal = False
- self.assertRaises(exception.CinderException,
- self.driver.delete_volume, test_volume)
-
- def test_delete_snapshot_fail(self):
- self.driver.common.login()
- self.driver.common.test_normal = False
- self.assertRaises(exception.CinderException,
- self.driver.delete_snapshot, test_volume)
-
- def create_fake_conf_file(self):
- doc = Document()
-
- config = doc.createElement('config')
- doc.appendChild(config)
-
- storage = doc.createElement('Storage')
- config.appendChild(storage)
-
- product = doc.createElement('Product')
- product_text = doc.createTextNode('HVS')
- product.appendChild(product_text)
- storage.appendChild(product)
-
- protocol = doc.createElement('Protocol')
- protocol_text = doc.createTextNode('FC')
- protocol.appendChild(protocol_text)
- storage.appendChild(protocol)
-
- 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('HVSURL')
- url_text = doc.createTextNode('http://100.115.10.69:8082/'
- 'deviceManager/rest/')
- url.appendChild(url_text)
- storage.appendChild(url)
-
- lun = doc.createElement('LUN')
- config.appendChild(lun)
- storagepool = doc.createElement('StoragePool')
- pool_text = doc.createTextNode('OpenStack_Pool')
- storagepool.appendChild(pool_text)
- lun.appendChild(storagepool)
-
- luntype = doc.createElement('LUNType')
- luntype_text = doc.createTextNode('Thick')
- luntype.appendChild(luntype_text)
- lun.appendChild(luntype)
-
- writetype = doc.createElement('WriteType')
- writetype_text = doc.createTextNode('1')
- writetype.appendChild(writetype_text)
- lun.appendChild(writetype)
-
- prefetchType = doc.createElement('Prefetch')
- prefetchType.setAttribute('Type', '2')
- prefetchType.setAttribute('Value', '20')
- lun.appendChild(prefetchType)
-
- newfile = open(self.fake_conf_file, 'w')
- newfile.write(doc.toprettyxml(indent=''))
- newfile.close()
-# Copyright (c) 2013 Huawei Technologies Co., Ltd.
-# Copyright (c) 2012 OpenStack Foundation
+# Copyright (c) 2013 - 2014 Huawei Technologies Co., Ltd.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
"""
from oslo.config import cfg
+import six
from cinder import exception
-from cinder.i18n import _
+from cinder.i18n import _, _LI, _LW
from cinder.openstack.common import log as logging
+from cinder.volume.drivers.huawei import huawei_18000
from cinder.volume.drivers.huawei import huawei_dorado
-from cinder.volume.drivers.huawei import huawei_hvs
from cinder.volume.drivers.huawei import huawei_t
from cinder.volume.drivers.huawei import huawei_utils
+
LOG = logging.getLogger(__name__)
huawei_opt = [
CONF = cfg.CONF
CONF.register_opts(huawei_opt)
+MAPPING = {'HVS': '18000'}
class HuaweiVolumeDriver(object):
def __init__(self, *args, **kwargs):
super(HuaweiVolumeDriver, self).__init__()
self._product = {'T': huawei_t, 'Dorado': huawei_dorado,
- 'HVS': huawei_hvs}
+ '18000': huawei_18000, 'HVS': huawei_18000}
self._protocol = {'iSCSI': 'ISCSIDriver', 'FC': 'FCDriver'}
self.driver = self._instantiate_driver(*args, **kwargs)
conf_file = self.configuration.cinder_huawei_conf_file
(product, protocol) = self._get_conf_info(conf_file)
- LOG.debug('_instantiate_driver: Loading %(protocol)s driver for '
- 'Huawei OceanStor %(product)s series storage arrays.'
- % {'protocol': protocol,
- 'product': product})
+ LOG.info(_LI(
+ '_instantiate_driver: Loading %(protocol)s driver for '
+ 'Huawei OceanStor %(product)s series storage arrays.')
+ % {'protocol': protocol,
+ 'product': product})
+ #Map HVS to 18000
+ if product in MAPPING:
+ LOG.warn(_LW("Product name %s is deprecated, update your "
+ "configuration to the new product name."), product)
+ product = MAPPING[product]
driver_module = self._product[product]
driver_class = 'Huawei' + product + self._protocol[protocol]
return (product, protocol)
else:
msg = (_('"Product" or "Protocol" is illegal. "Product" should '
- 'be set to either T, Dorado or HVS. "Protocol" should '
+ 'be set to either T, Dorado or 18000. "Protocol" should '
'be set to either iSCSI or FC. Product: %(product)s '
'Protocol: %(protocol)s')
- % {'product': product,
- 'protocol': protocol})
+ % {'product': six.text_type(product),
+ 'protocol': six.text_type(protocol)})
raise exception.InvalidInput(reason=msg)
def __setattr__(self, name, value):
-# Copyright (c) 2013 Huawei Technologies Co., Ltd.
-# Copyright (c) 2013 OpenStack Foundation
+# Copyright (c) 2013 - 2014 Huawei Technologies Co., Ltd.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# License for the specific language governing permissions and limitations
# under the License.
"""
-Volume Drivers for Huawei OceanStor HVS storage arrays.
+Volume Drivers for Huawei OceanStor 18000 storage arrays.
"""
from cinder.volume import driver
-from cinder.volume.drivers.huawei.rest_common import HVSCommon
+from cinder.volume.drivers.huawei.rest_common import RestCommon
from cinder.zonemanager import utils as fczm_utils
-class HuaweiHVSISCSIDriver(driver.ISCSIDriver):
- """ISCSI driver for Huawei OceanStor HVS storage arrays."""
+class Huawei18000ISCSIDriver(driver.ISCSIDriver):
+ """ISCSI driver for Huawei OceanStor 18000 storage arrays.
- VERSION = '1.0.0'
+ Version history:
+ 1.0.0 - Initial driver
+ 1.1.0 - Provide Huawei OceanStor 18000 storage volume driver.
+ """
+
+ VERSION = "1.1.0"
def __init__(self, *args, **kwargs):
- super(HuaweiHVSISCSIDriver, self).__init__(*args, **kwargs)
+ super(Huawei18000ISCSIDriver, self).__init__(*args, **kwargs)
def do_setup(self, context):
"""Instantiate common class and log in storage system."""
- self.common = HVSCommon(configuration=self.configuration)
+ self.common = RestCommon(configuration=self.configuration)
+ return self.common.login()
def check_for_setup_error(self):
- """Check configuration file."""
- self.common._check_conf_file()
- self.common.login()
+ """Check configuration file."""
+ return self.common._check_conf_file()
def create_volume(self, volume):
"""Create a volume."""
- self.common.create_volume(volume)
+ lun_info = self.common.create_volume(volume)
+ return {'provider_location': lun_info['ID'],
+ 'lun_info': lun_info}
def create_volume_from_snapshot(self, volume, snapshot):
"""Create a volume from a snapshot."""
- self.common.create_volume_from_snapshot(volume, snapshot)
+ lun_info = self.common.create_volume_from_snapshot(volume, snapshot)
+ return {'provider_location': lun_info['ID'],
+ 'lun_info': lun_info}
def create_cloned_volume(self, volume, src_vref):
"""Create a clone of the specified volume."""
- self.common.create_cloned_volume(volume, src_vref)
+ lun_info = self.common.create_cloned_volume(volume, src_vref)
+ return {'provider_location': lun_info['ID'],
+ 'lun_info': lun_info}
def extend_volume(self, volume, new_size):
"""Extend a volume."""
- self.common.extend_volume(volume, new_size)
+ return self.common.extend_volume(volume, new_size)
def delete_volume(self, volume):
"""Delete a volume."""
- self.common.delete_volume(volume)
+ return self.common.delete_volume(volume)
def create_snapshot(self, snapshot):
"""Create a snapshot."""
- self.common.create_snapshot(snapshot)
+ lun_info = self.common.create_snapshot(snapshot)
+ return {'provider_location': lun_info['ID'],
+ 'lun_info': lun_info}
def delete_snapshot(self, snapshot):
"""Delete a snapshot."""
- self.common.delete_snapshot(snapshot)
+ return self.common.delete_snapshot(snapshot)
def get_volume_stats(self, refresh=False):
"""Get volume stats."""
- data = self.common.update_volume_stats(refresh)
+ data = self.common.update_volume_stats()
backend_name = self.configuration.safe_get('volume_backend_name')
data['volume_backend_name'] = backend_name or self.__class__.__name__
data['storage_protocol'] = 'iSCSI'
def terminate_connection(self, volume, connector, **kwargs):
"""Terminate the map."""
- self.common.terminate_connection(volume, connector, **kwargs)
+ self.common.terminate_connection_iscsi(volume, connector)
def create_export(self, context, volume):
"""Export the volume."""
pass
-class HuaweiHVSFCDriver(driver.FibreChannelDriver):
- """FC driver for Huawei OceanStor HVS storage arrays."""
+class Huawei18000FCDriver(driver.FibreChannelDriver):
+ """FC driver for Huawei OceanStor 18000 storage arrays.
+
+ Version history:
+ 1.0.0 - Initial driver
+ 1.1.0 - Provide Huawei OceanStor 18000 storage volume driver.
+ """
- VERSION = '1.0.0'
+ VERSION = "1.1.0"
def __init__(self, *args, **kwargs):
- super(HuaweiHVSFCDriver, self).__init__(*args, **kwargs)
+ super(Huawei18000FCDriver, self).__init__(*args, **kwargs)
def do_setup(self, context):
"""Instantiate common class and log in storage system."""
- self.common = HVSCommon(configuration=self.configuration)
- self.common.login()
+ self.common = RestCommon(configuration=self.configuration)
+ return self.common.login()
def check_for_setup_error(self):
- """Check configuration file."""
- self.common._check_conf_file()
+ """Check configuration file."""
+ return self.common._check_conf_file()
def create_volume(self, volume):
"""Create a volume."""
- self.common.create_volume(volume)
+ lun_info = self.common.create_volume(volume)
+ return {'provider_location': lun_info['ID'],
+ 'lun_info': lun_info}
def create_volume_from_snapshot(self, volume, snapshot):
"""Create a volume from a snapshot."""
- self.common.create_volume_from_snapshot(volume, snapshot)
+ lun_info = self.common.create_volume_from_snapshot(volume, snapshot)
+ return {'provider_location': lun_info['ID'],
+ 'lun_info': lun_info}
def create_cloned_volume(self, volume, src_vref):
"""Create a clone of the specified volume."""
- self.common.create_cloned_volume(volume, src_vref)
+ lun_info = self.common.create_cloned_volume(volume, src_vref)
+ return {'provider_location': lun_info['ID'],
+ 'lun_info': lun_info}
def extend_volume(self, volume, new_size):
"""Extend a volume."""
- self.common.extend_volume(volume, new_size)
+ return self.common.extend_volume(volume, new_size)
def delete_volume(self, volume):
"""Delete a volume."""
- self.common.delete_volume(volume)
+ return self.common.delete_volume(volume)
def create_snapshot(self, snapshot):
"""Create a snapshot."""
- self.common.create_snapshot(snapshot)
+ lun_info = self.common.create_snapshot(snapshot)
+ return {'provider_location': lun_info['ID'],
+ 'lun_info': lun_info}
def delete_snapshot(self, snapshot):
"""Delete a snapshot."""
- self.common.delete_snapshot(snapshot)
+ return self.common.delete_snapshot(snapshot)
def get_volume_stats(self, refresh=False):
"""Get volume stats."""
- data = self.common.update_volume_stats(refresh)
+ data = self.common.update_volume_stats()
backend_name = self.configuration.safe_get('volume_backend_name')
data['volume_backend_name'] = backend_name or self.__class__.__name__
data['storage_protocol'] = 'FC'
@fczm_utils.RemoveFCZone
def terminate_connection(self, volume, connector, **kwargs):
"""Terminate the map."""
- self.common.terminate_connection(volume, connector, **kwargs)
+ return self.common.terminate_connection_fc(volume, connector)
def create_export(self, context, volume):
"""Export the volume."""
-# Copyright (c) 2013 Huawei Technologies Co., Ltd.
-# Copyright (c) 2013 OpenStack Foundation
+# Copyright (c) 2013 - 2014 Huawei Technologies Co., Ltd.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
-"""Common class for Huawei HVS storage drivers."""
+"""Common class for Huawei 18000 storage drivers."""
import base64
import cookielib
from oslo.utils import excutils
from oslo.utils import units
+import six
from cinder import context
from cinder import exception
-from cinder.i18n import _, _LE, _LW
+from cinder.i18n import _, _LE, _LI, _LW
from cinder.openstack.common import log as logging
from cinder import utils
-from cinder.volume.drivers.huawei import huawei_utils
+from cinder.volume import qos_specs
from cinder.volume import volume_types
-
LOG = logging.getLogger(__name__)
-QOS_KEY = ["Qos-high", "Qos-normal", "Qos-low"]
-TIER_KEY = ["Tier-high", "Tier-normal", "Tier-low"]
+HOSTGROUP_PREFIX = 'OpenStack_HostGroup_'
+LUNGROUP_PREFIX = 'OpenStack_LunGroup_'
+MAPPING_VIEW_PREFIX = 'OpenStack_Mapping_View_'
+QOS_NAME_PREFIX = 'OpenStack_'
+huawei_valid_keys = ['maxIOPS', 'minIOPS', 'minBandWidth',
+ 'maxBandWidth', 'latency', 'IOType']
+
+class RestCommon():
+ """Common class for Huawei OceanStor 18000 storage system."""
-class HVSCommon():
- """Common class for Huawei OceanStor HVS storage system."""
+ SLEEP_FOR_LUN_READY = 2
+ SLEEP_FOR_LUNCOPY = 15
def __init__(self, configuration):
self.configuration = configuration
self.cookie = cookielib.CookieJar()
self.url = None
- self.xml_conf = self.configuration.cinder_huawei_conf_file
+ self.productversion = None
+ self.headers = {"Connection": "keep-alive",
+ "Content-Type": "application/json"}
def call(self, url=False, data=None, method=None):
- """Send requests to HVS server.
-
+ """Send requests to 18000 server.
Send HTTPS call, get response in JSON.
Convert response into Python Object and return it.
"""
- LOG.debug('HVS Request URL: %(url)s' % {'url': url})
- LOG.debug('HVS Request Data: %(data)s' % {'data': data})
-
- headers = {"Connection": "keep-alive",
- "Content-Type": "application/json"}
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.cookie))
urllib2.install_opener(opener)
try:
urllib2.socket.setdefaulttimeout(720)
- req = urllib2.Request(url, data, headers)
+ req = urllib2.Request(url, data, self.headers)
if method:
req.get_method = lambda: method
res = urllib2.urlopen(req).read().decode("utf-8")
- LOG.debug('HVS Response Data: %(res)s' % {'res': res})
+
+ if "xx/sessions" not in url:
+ LOG.info(_LI('\n\n\n\nRequest URL: %(url)s\n\n'
+ 'Call Method: %(method)s\n\n'
+ 'Request Data: %(data)s\n\n'), {'url': url,
+ 'method': method,
+ 'data': data})
+
except Exception as err:
- err_msg = _('Bad response from server: %s') % err
- LOG.error(err_msg)
+ LOG.error(_LE('\nBad response from server: %s.') % err)
raise err
try:
res_json = json.loads(res)
except Exception as err:
- LOG.error(_LE('JSON transfer error'))
+ err_msg = (_LE('JSON transfer error: %s.') % err)
+ LOG.error(err_msg)
raise err
return res_json
def login(self):
- """Log in HVS array.
-
- If login failed, the driver will sleep 30's to avoid frequent
- connection to the server.
- """
+ """Log in 18000 array."""
login_info = self._get_login_info()
- url = login_info['HVSURL'] + "xx/sessions"
+ url = login_info['RestURL'] + "xx/sessions"
data = json.dumps({"username": login_info['UserName'],
"password": login_info['UserPassword'],
"scope": "0"})
result = self.call(url, data)
if (result['error']['code'] != 0) or ("data" not in result):
- time.sleep(30)
- msg = _("Login error, reason is %s") % result
+ msg = (_("Login error, reason is: %s.") % result)
LOG.error(msg)
raise exception.CinderException(msg)
deviceid = result['data']['deviceid']
- self.url = login_info['HVSURL'] + deviceid
+ self.url = login_info['RestURL'] + deviceid
+ self.headers['iBaseToken'] = result['data']['iBaseToken']
return deviceid
- def _init_tier_parameters(self, parameters, lunparam):
- """Init the LUN parameters through the volume type "performance"."""
- if "tier" in parameters:
- smart_tier = parameters['tier']
- if smart_tier == 'Tier_high':
- lunparam['INITIALDISTRIBUTEPOLICY'] = "1"
- elif smart_tier == 'Tier_normal':
- lunparam['INITIALDISTRIBUTEPOLICY'] = "2"
- elif smart_tier == 'Tier_low':
- lunparam['INITIALDISTRIBUTEPOLICY'] = "3"
- else:
- lunparam['INITIALDISTRIBUTEPOLICY'] = "2"
-
def _init_lun_parameters(self, name, parameters):
"""Init basic LUN parameters."""
lunparam = {"TYPE": "11",
"NAME": name,
"PARENTTYPE": "216",
"PARENTID": parameters['pool_id'],
- "DESCRIPTION": "",
+ "DESCRIPTION": parameters['volume_description'],
"ALLOCTYPE": parameters['LUNType'],
"CAPACITY": parameters['volume_size'],
"WRITEPOLICY": parameters['WriteType'],
"MIRRORPOLICY": parameters['MirrorSwitch'],
"PREFETCHPOLICY": parameters['PrefetchType'],
- "PREFETCHVALUE": parameters['PrefetchValue'],
- "DATATRANSFERPOLICY": "1",
- "INITIALDISTRIBUTEPOLICY": "0"}
+ "PREFETCHVALUE": parameters['PrefetchValue']}
return lunparam
- def _init_qos_parameters(self, parameters, lun_param):
- """Init the LUN parameters through the volume type "Qos-xxx"."""
- policy_id = None
- policy_info = None
- if "qos" in parameters:
- policy_info = self._find_qos_policy_info(parameters['qos'])
- if policy_info:
- policy_id = policy_info['ID']
-
- lun_param['IOClASSID'] = policy_info['ID']
- qos_level = parameters['qos_level']
- if qos_level == 'Qos-high':
- lun_param['IOPRIORITY'] = "3"
- elif qos_level == 'Qos-normal':
- lun_param['IOPRIORITY'] = "2"
- elif qos_level == 'Qos-low':
- lun_param['IOPRIORITY'] = "1"
- else:
- lun_param['IOPRIORITY'] = "2"
-
- return (policy_info, policy_id)
-
def _assert_rest_result(self, result, err_str):
error_code = result['error']['code']
if error_code != 0:
- msg = _('%(err)s\nresult: %(res)s') % {'err': err_str,
- 'res': result}
+ msg = (_('%(err)s\nresult: %(res)s.') % {'err': err_str,
+ 'res': result})
LOG.error(msg)
raise exception.CinderException(msg)
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" was not in result.') % msg)
LOG.error(err_msg)
raise exception.CinderException(err_msg)
self._assert_rest_result(result, msg)
self._assert_data_in_result(result, msg)
- return result['data']['ID']
+ return result['data']
+ @utils.synchronized('huawei', external=True)
def create_volume(self, volume):
+
+ poolinfo = self._find_pool_info()
volume_name = self._encode_name(volume['id'])
- config_params = self._parse_volume_type(volume)
-
- # Prepare lun parameters, including qos parameter and tier parameter.
- lun_param = self._init_lun_parameters(volume_name, config_params)
- self._init_tier_parameters(config_params, lun_param)
- policy_info, policy_id = self._init_qos_parameters(config_params,
- lun_param)
-
- # Create LUN in array
- lunid = self._create_volume(lun_param)
-
- # Enable qos, need to add lun into qos policy
- if "qos" in config_params:
- lun_list = policy_info['LUNLIST']
- lun_list.append(lunid)
- if policy_id:
- self._update_qos_policy_lunlist(lun_list, policy_id)
- else:
- LOG.warn(_LW("Can't find the Qos policy in array"))
+ volume_description = volume['name']
+ volume_size = self._get_volume_size(volume)
+
+ LOG.info(_LI(
+ 'Create Volume: %(volume)s Size: %(size)s.')
+ % {'volume': volume_name,
+ 'size': volume_size})
+
+ params = self._get_lun_conf_params()
+ params['pool_id'] = poolinfo['ID']
+ params['volume_size'] = volume_size
+ params['volume_description'] = volume_description
+
+ # Prepare lun parameters.
+ lun_param = self._init_lun_parameters(volume_name, params)
+
+ # Create LUN on the array.
+ lun_info = self._create_volume(lun_param)
+ lunid = lun_info['ID']
+
+ type_id = volume.get('volume_type_id', None)
+ policy_id = None
+
+ if type_id is not None:
+ volume_type = self._get_volume_type(type_id)
+ qos = self._get_qos_by_volume_type(volume_type)
+
+ if qos is None:
+ msg = (_('Find QoS configuration error!'))
+ LOG.error(msg)
+ raise exception.CinderException(msg)
+
+ try:
+ # Check QoS priority. if high, change lun priority to high.
+ if self._check_qos_high_priority(qos) is True:
+ self._change_lun_priority(lunid)
+
+ # Create QoS policy and active.
+ policy_id = self._create_qos_policy(qos, lunid)
+ self._active_deactive_qos(policy_id, True)
+ except Exception:
+ with excutils.save_and_reraise_exception():
+ if policy_id is not None:
+ self._delete_qos_policy(policy_id)
- # Create lun group and add LUN into to lun group
- lungroup_id = self._create_lungroup(volume_name)
- self._associate_lun_to_lungroup(lungroup_id, lunid)
+ self._delete_lun(lunid)
- return lunid
+ return lun_info
- def _get_volume_size(self, poolinfo, volume):
+ def _get_volume_size(self, volume):
"""Calculate the volume size.
- We should divide the given volume size by 512 for the HVS system
+ We should divide the given volume size by 512 for the 18000 system
calculates volume size with sectors, which is 512 bytes.
"""
return volume_size
+ @utils.synchronized('huawei', external=True)
def delete_volume(self, volume):
"""Delete a volume.
- Three steps: first, remove associate from lun group.
- Second, remove associate from qos policy. Third, remove the lun.
+ Three steps: first, remove associate from lungroup.
+ Second, remove associate from QoS policy. Third, remove the lun.
"""
name = self._encode_name(volume['id'])
- lun_id = self._get_volume_by_name(name)
- lungroup_id = self._find_lungroup(name)
-
- if lun_id and lungroup_id:
- self._delete_lun_from_qos_policy(volume, lun_id)
- self._delete_associated_lun_from_lungroup(lungroup_id, lun_id)
- self._delete_lungroup(lungroup_id)
- self._delete_lun(lun_id)
+ lun_id = volume.get('provider_location', None)
+ LOG.info(_LI('Delete Volume: %(name)s array lun id: %(lun_id)s.')
+ % {'name': name, 'lun_id': lun_id})
+ if lun_id:
+ if self._check_lun_exist(lun_id) is True:
+ # Get qos_id by lun_id.
+ qos_id = self._get_qosid_by_lunid(lun_id)
+
+ if qos_id != "":
+ qos_info = self._get_qos_info(qos_id)
+ qos_status = qos_info['RUNNINGSTATUS']
+ # 2: Active status.
+ if qos_status == '2':
+ self._active_deactive_qos(qos_id, False)
+
+ self._delete_qos_policy(qos_id)
+ self._delete_lun(lun_id)
else:
- LOG.warn(_LW("Can't find lun or lun group in array"))
-
- def _delete_lun_from_qos_policy(self, volume, lun_id):
- """Remove lun from qos policy."""
- parameters = self._parse_volume_type(volume)
-
- if "qos" in parameters:
- qos = parameters['qos']
- policy_info = self._find_qos_policy_info(qos)
- if policy_info:
- lun_list = policy_info['LUNLIST']
- for item in lun_list:
- if lun_id == item:
- lun_list.remove(item)
- self._update_qos_policy_lunlist(lun_list, policy_info['ID'])
+ LOG.warning(_LW("Can't find lun or lungroup on the array."))
+
+ def _check_lun_exist(self, lun_id):
+ url = self.url + "/lun/" + lun_id
+ data = json.dumps({"TYPE": "11",
+ "ID": lun_id})
+ result = self.call(url, data, "GET")
+ error_code = result['error']['code']
+ if error_code != 0:
+ return False
+
+ return True
def _delete_lun(self, lun_id):
url = self.url + "/lun/" + lun_id
data = json.dumps({"TYPE": "11",
"ID": lun_id})
result = self.call(url, data, "DELETE")
- self._assert_rest_result(result, 'delete lun error')
+ self._assert_rest_result(result, 'Delete lun error.')
+
+ def _read_xml(self):
+ """Open xml file and parse the content."""
+ filename = self.configuration.cinder_huawei_conf_file
+ try:
+ tree = ET.parse(filename)
+ root = tree.getroot()
+ except Exception as err:
+ LOG.error(_LE('_read_xml: %s') % err)
+ raise err
+ return root
def _encode_name(self, name):
uuid_str = name.replace("-", "")
return newuuid
def _find_pool_info(self):
- root = huawei_utils.parse_xml_file(self.xml_conf)
+ root = self._read_xml()
pool_name = root.findtext('LUN/StoragePool')
if not pool_name:
- err_msg = _("Invalid resource pool: %s") % pool_name
+ err_msg = (_("Invalid resource pool: %s.") % pool_name)
LOG.error(err_msg)
raise exception.InvalidInput(err_msg)
url = self.url + "/storagepool"
result = self.call(url, None)
- self._assert_rest_result(result, 'Query resource pool error')
+ self._assert_rest_result(result, 'Query resource pool error.')
poolinfo = {}
if "data" in result:
break
if not poolinfo:
- msg = (_('Get pool info error, pool name is:%s') % pool_name)
+ msg = (_('Get pool info error, pool name is: %s.') % pool_name)
LOG.error(msg)
raise exception.CinderException(msg)
return poolinfo
def _get_volume_by_name(self, name):
- url = self.url + "/lun"
+ url = self.url + "/lun?range=[0-65535]"
result = self.call(url, None, "GET")
self._assert_rest_result(result, 'Get volume by name error!')
def _create_snapshot(self, snapshot):
snapshot_name = self._encode_name(snapshot['id'])
+ snapshot_description = snapshot['id']
volume_name = self._encode_name(snapshot['volume_id'])
- LOG.debug('create_snapshot:snapshot name:%(snapshot)s, '
- 'volume name:%(volume)s.'
- % {'snapshot': snapshot_name,
- 'volume': volume_name})
+ LOG.info(_LI(
+ '_create_snapshot:snapshot name: %(snapshot)s, '
+ 'volume name: %(volume)s.')
+ % {'snapshot': snapshot_name,
+ 'volume': volume_name})
lun_id = self._get_volume_by_name(volume_name)
+ if lun_id is None:
+ msg = (_("Can't find lun info on the array, "
+ "lun name is: %(name)s") % {'name': volume_name})
+ LOG.error(msg)
+ raise exception.CinderException(msg)
+
url = self.url + "/snapshot"
data = json.dumps({"TYPE": "27",
"NAME": snapshot_name,
"PARENTTYPE": "11",
+ "DESCRIPTION": snapshot_description,
"PARENTID": lun_id})
result = self.call(url, data)
self._assert_rest_result(result, msg)
self._assert_data_in_result(result, msg)
- return result['data']['ID']
+ return result['data']
+ @utils.synchronized('huawei', external=True)
def create_snapshot(self, snapshot):
- snapshot_id = self._create_snapshot(snapshot)
+ snapshot_info = self._create_snapshot(snapshot)
+ snapshot_id = snapshot_info['ID']
self._active_snapshot(snapshot_id)
- def _stop_snapshot(self, snapshot):
- snapshot_name = self._encode_name(snapshot['id'])
- volume_name = self._encode_name(snapshot['volume_id'])
+ return snapshot_info
- LOG.debug('_stop_snapshot:snapshot name:%(snapshot)s, '
- 'volume name:%(volume)s.'
- % {'snapshot': snapshot_name,
- 'volume': volume_name})
+ def _check_snapshot_exist(self, snapshot_id):
+ url = self.url + "/snapshot/" + snapshot_id
+ data = json.dumps({"TYPE": "27",
+ "ID": snapshot_id})
+ result = self.call(url, data, "GET")
+ error_code = result['error']['code']
+ if error_code != 0:
+ return False
- snapshotid = self._get_snapshotid_by_name(snapshot_name)
- stopdata = json.dumps({"ID": snapshotid})
+ return True
+
+ def _stop_snapshot(self, snapshot_id):
url = self.url + "/snapshot/stop"
+ stopdata = json.dumps({"ID": snapshot_id})
result = self.call(url, stopdata, "PUT")
self._assert_rest_result(result, 'Stop snapshot error.')
- return snapshotid
-
def _delete_snapshot(self, snapshotid):
url = self.url + "/snapshot/%s" % snapshotid
data = json.dumps({"TYPE": "27", "ID": snapshotid})
result = self.call(url, data, "DELETE")
self._assert_rest_result(result, 'Delete snapshot error.')
+ @utils.synchronized('huawei', external=True)
def delete_snapshot(self, snapshot):
- snapshotid = self._stop_snapshot(snapshot)
- self._delete_snapshot(snapshotid)
+ snapshot_name = self._encode_name(snapshot['id'])
+ volume_name = self._encode_name(snapshot['volume_id'])
+
+ LOG.info(_LI(
+ 'stop_snapshot:snapshot name: %(snapshot)s, '
+ 'volume name: %(volume)s.')
+ % {'snapshot': snapshot_name,
+ 'volume': volume_name})
+
+ snapshot_id = snapshot.get('provider_location', None)
+ if snapshot_id is None:
+ snapshot_id = self._get_snapshotid_by_name(snapshot_name)
+
+ if snapshot_id is not None:
+ if self._check_snapshot_exist(snapshot_id) is True:
+ self._stop_snapshot(snapshot_id)
+ self._delete_snapshot(snapshot_id)
+ else:
+ LOG.warning(_LW("Can't find snapshot on the array."))
+ else:
+ LOG.warning(_LW("Can't find snapshot on the array."))
def _get_snapshotid_by_name(self, name):
- url = self.url + "/snapshot"
+ url = self.url + "/snapshot?range=[0-65535]"
data = json.dumps({"TYPE": "27"})
result = self.call(url, data, "GET")
self._assert_rest_result(result, 'Get snapshot id error.')
if name == item['NAME']:
snapshot_id = item['ID']
break
+
return snapshot_id
def _copy_volume(self, volume, copy_name, src_lun, tgt_lun):
+
luncopy_id = self._create_luncopy(copy_name,
src_lun, tgt_lun)
try:
self._start_luncopy(luncopy_id)
self._wait_for_luncopy(luncopy_id)
+
except Exception:
with excutils.save_and_reraise_exception():
self._delete_luncopy(luncopy_id)
self._delete_luncopy(luncopy_id)
+ def _is_vol_ready(self, lunid):
+ url = self.url + "/lun/" + lunid
+ result = self.call(url, None, "GET")
+ self._assert_rest_result(result, 'Get volume by id error!')
+
+ if "data" in result:
+ if (result['data']['HEALTHSTATUS'] == "1") and\
+ (result['data']['RUNNINGSTATUS'] == "27"):
+ return True
+ return False
+
def create_volume_from_snapshot(self, volume, snapshot):
"""Create a volume from a snapshot.
"""
snapshot_name = self._encode_name(snapshot['id'])
- src_lun_id = self._get_snapshotid_by_name(snapshot_name)
- tgt_lun_id = self.create_volume(volume)
+
+ snapshot_id = snapshot.get('provider_location', None)
+ if snapshot_id is None:
+ snapshot_id = self._get_snapshotid_by_name(snapshot_name)
+ if snapshot_id is None:
+ err_msg = (_(
+ 'create_volume_from_snapshot: Snapshot %(name)s '
+ 'does not exist.')
+ % {'name': snapshot_name})
+ LOG.error(err_msg)
+ raise exception.VolumeBackendAPIException(data=err_msg)
+
+ lun_info = self.create_volume(volume)
+ tgt_lun_id = lun_info['ID']
luncopy_name = self._encode_name(volume['id'])
- self._copy_volume(volume, luncopy_name, src_lun_id, tgt_lun_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')
+ % {'src_lun_id': snapshot_id,
+ 'tgt_lun_id': tgt_lun_id,
+ 'copy_name': luncopy_name})
+
+ timeout = 10
+ time_pass = 0
+ while True:
+ if self._is_vol_ready(tgt_lun_id):
+ break
+ LOG.info(_LI('Waiting newly created lun to be ready.'))
+ time.sleep(self.SLEEP_FOR_LUN_READY)
+ time_pass = time_pass + self.SLEEP_FOR_LUN_READY
+ if time_pass >= timeout:
+ msg = _("Waited %s seconds. Timeout when waiting the newly"
+ " created lun to be ready.")
+ raise exception.VolumeBackendAPIException(msg % time_pass)
+
+ self._copy_volume(volume, luncopy_name, snapshot_id, tgt_lun_id)
+
+ return lun_info
def create_cloned_volume(self, volume, src_vref):
"""Clone a new volume from an existing volume."""
- volume_name = self._encode_name(src_vref['id'])
- src_lun_id = self._get_volume_by_name(volume_name)
- tgt_lun_id = self.create_volume(volume)
- luncopy_name = self._encode_name(volume['id'])
- self._copy_volume(volume, luncopy_name, src_lun_id, tgt_lun_id)
+ # Form the snapshot structure.
+ snapshot = {'id': uuid.uuid4().__str__(), 'volume_id': src_vref['id']}
+
+ # Create snapshot.
+ self.create_snapshot(snapshot)
+
+ try:
+ # Create volume from snapshot.
+ lun_info = self.create_volume_from_snapshot(volume, snapshot)
+ finally:
+ try:
+ # Delete snapshot.
+ self.delete_snapshot(snapshot)
+ except exception.CinderException:
+ LOG.warning(_LW(
+ 'Failure deleting the snapshot %(snapshot_id)s '
+ 'of volume %(volume_id)s.')
+ % {'snapshot_id': snapshot['id'],
+ 'volume_id': src_vref['id']})
+
+ return lun_info
def _create_luncopy(self, luncopyname, srclunid, tgtlunid):
"""Create a luncopy."""
url = self.url + "/luncopy"
- data = json.dumps({"TYPE": "219",
+ data = json.dumps({"TYPE": 219,
"NAME": luncopyname,
"DESCRIPTION": luncopyname,
- "COPYSPEED": "2",
+ "COPYSPEED": 2,
"LUNCOPYTYPE": "1",
"SOURCELUN": ("INVALID;%s;INVALID;INVALID;INVALID"
% srclunid),
return result['data']['ID']
- def _add_host_into_hostgroup(self, host_name, host_ip):
+ def _add_host_into_hostgroup(self, host_id):
"""Associate host to hostgroup.
- If host group doesn't exist, create one.
+ If hostgroup doesn't exist, create one.
"""
+ host_group_name = HOSTGROUP_PREFIX + host_id
+ hostgroup_id = self._find_hostgroup(host_group_name)
+
+ LOG.info(_LI(
+ '_add_host_into_hostgroup, hostgroup name: %(name)s, '
+ 'hostgroup id: %(id)s.')
+ % {'name': host_group_name,
+ 'id': hostgroup_id})
- hostgroup_id = self._find_hostgroup(host_name)
if hostgroup_id is None:
- hostgroup_id = self._create_hostgroup(host_name)
+ hostgroup_id = self._create_hostgroup(host_group_name)
- hostid = self._find_host(host_name)
- if hostid is None:
- os_type = huawei_utils.get_conf_host_os_type(host_ip,
- self.xml_conf)
- hostid = self._add_host(host_name, os_type)
- self._associate_host_to_hostgroup(hostgroup_id, hostid)
+ isAssociate = self._is_host_associate_to_hostgroup(hostgroup_id,
+ host_id)
+ if isAssociate is False:
+ self._associate_host_to_hostgroup(hostgroup_id, host_id)
- return hostid, hostgroup_id
+ return hostgroup_id
def _mapping_hostgroup_and_lungroup(self, volume_name,
hostgroup_id, host_id):
"""Add hostgroup and lungroup to view."""
- lungroup_id = self._find_lungroup(volume_name)
+ lungroup_name = LUNGROUP_PREFIX + host_id
+ mapping_view_name = MAPPING_VIEW_PREFIX + host_id
+ lungroup_id = self._find_lungroup(lungroup_name)
lun_id = self._get_volume_by_name(volume_name)
- view_id = self._find_mapping_view(volume_name)
+ view_id = self._find_mapping_view(mapping_view_name)
- LOG.debug('_mapping_hostgroup_and_lungroup: lun_group: %(lun_group)s'
- 'view_id: %(view_id)s'
- % {'lun_group': lungroup_id,
- 'view_id': view_id})
+ LOG.info(_LI(
+ '_mapping_hostgroup_and_lungroup, lun_group: %(lun_group)s, '
+ 'view_id: %(view_id)s, lun_id: %(lun_id)s.')
+ % {'lun_group': six.text_type(lungroup_id),
+ 'view_id': six.text_type(view_id),
+ 'lun_id': six.text_type(lun_id)})
try:
+ # Create lungroup and add LUN into to lungroup.
+ if lungroup_id is None:
+ lungroup_id = self._create_lungroup(lungroup_name)
+ self._associate_lun_to_lungroup(lungroup_id, lun_id)
+
if view_id is None:
- view_id = self._add_mapping_view(volume_name, host_id)
+ view_id = self._add_mapping_view(mapping_view_name)
self._associate_hostgroup_to_view(view_id, hostgroup_id)
self._associate_lungroup_to_view(view_id, lungroup_id)
else:
except Exception:
with excutils.save_and_reraise_exception():
- self._delete_hostgoup_mapping_view(view_id, hostgroup_id)
- self._delete_lungroup_mapping_view(view_id, lungroup_id)
- self._delete_mapping_view(view_id)
+ err_msg = (_LE(
+ 'Error occured when adding hostgroup and lungroup to view.'
+ ' Remove lun from lungroup now.'))
+ LOG.error(err_msg)
+ self._remove_lun_from_lungroup(lungroup_id, lun_id)
return lun_id
added = self._initiator_is_added_to_array(initiator_name)
if not added:
self._add_initiator_to_array(initiator_name)
+ if self._is_initiator_associated_to_host(initiator_name) is False:
+ self._associate_initiator_to_host(initiator_name, hostid)
else:
if self._is_initiator_associated_to_host(initiator_name) is False:
self._associate_initiator_to_host(initiator_name, hostid)
+ @utils.synchronized('huawei', external=True)
def initialize_connection_iscsi(self, volume, connector):
"""Map a volume to a host and return target iSCSI information."""
+
+ LOG.info(_LI('Enter initialize_connection_iscsi.'))
initiator_name = connector['initiator']
volume_name = self._encode_name(volume['id'])
- LOG.debug('initiator name:%(initiator_name)s, '
- 'volume name:%(volume)s.'
- % {'initiator_name': initiator_name,
- 'volume': volume_name})
+ LOG.info(_LI(
+ 'initiator name: %(initiator_name)s, '
+ 'volume name: %(volume)s.')
+ % {'initiator_name': initiator_name,
+ 'volume': volume_name})
(iscsi_iqn, target_ip) = self._get_iscsi_params(connector)
+ LOG.info(_LI(
+ 'initialize_connection_iscsi,iscsi_iqn: %(iscsi_iqn)s, '
+ 'target_ip: %(target_ip)s.')
+ % {'iscsi_iqn': iscsi_iqn,
+ 'target_ip': target_ip})
+
+ # Create host_group if not exist.
+ host_name = connector['host']
+ hostid = self._find_host(host_name)
+ if hostid is None:
+ hostid = self._add_host(host_name)
- #create host_group if not exist
- hostid, hostgroup_id = self._add_host_into_hostgroup(connector['host'],
- connector['ip'])
+ # Add initiator to the host.
self._ensure_initiator_added(initiator_name, hostid)
+ hostgroup_id = self._add_host_into_hostgroup(hostid)
- # Mapping lungroup and hostgroup to view
+ # Mapping lungroup and hostgroup to view.
lun_id = self._mapping_hostgroup_and_lungroup(volume_name,
hostgroup_id, hostid)
+
hostlunid = self._find_host_lun_id(hostid, lun_id)
- LOG.debug("host lun id is %s" % hostlunid)
+
+ LOG.info(_LI("initialize_connection_iscsi, host lun id is: %s.")
+ % hostlunid)
# Return iSCSI properties.
properties = {}
properties['target_discovered'] = False
- properties['target_portal'] = ('%s:%s' % (target_ip, '3260'))
+ properties['target_portal'] = ('%s: %s' % (target_ip, '3260'))
properties['target_iqn'] = iscsi_iqn
properties['target_lun'] = int(hostlunid)
properties['volume_id'] = volume['id']
+ LOG.info(_LI("initialize_connection_iscsi success. Return data: %s.")
+ % properties)
return {'driver_volume_type': 'iscsi', 'data': properties}
+ @utils.synchronized('huawei', external=True)
def initialize_connection_fc(self, volume, connector):
wwns = connector['wwpns']
+ host_name = connector['host']
volume_name = self._encode_name(volume['id'])
- LOG.debug('initiator name:%(initiator_name)s, '
- 'volume name:%(volume)s.'
- % {'initiator_name': wwns,
- 'volume': volume_name})
+ LOG.info(_LI(
+ 'initialize_connection_fc, initiator: %(initiator_name)s,'
+ ' volume name: %(volume)s.')
+ % {'initiator_name': wwns,
+ 'volume': volume_name})
- # Create host group if not exist
- hostid, hostgroup_id = self._add_host_into_hostgroup(connector['host'],
- connector['ip'])
+ # Create host_group if not exist.
+ hostid = self._find_host(host_name)
+ if hostid is None:
+ hostid = self._add_host(host_name)
+
+ # Add host into hostgroup.
+ hostgroup_id = self._add_host_into_hostgroup(hostid)
free_wwns = self._get_connected_free_wwns()
- LOG.debug("the free wwns %s" % free_wwns)
+ LOG.info(_LI("initialize_connection_fc, the array has free wwns: %s")
+ % free_wwns)
for wwn in wwns:
if wwn in free_wwns:
self._add_fc_port_to_host(hostid, wwn)
if tgtwwpns:
tgt_port_wwns.append(tgtwwpns)
+ init_targ_map = {}
+ for initiator in wwns:
+ init_targ_map[initiator] = tgt_port_wwns
+
# Return FC properties.
- properties = {}
- properties['target_discovered'] = False
- properties['target_wwn'] = tgt_port_wwns
- properties['target_lun'] = int(host_lun_id)
- properties['volume_id'] = volume['id']
- LOG.debug("the fc server properties is:%s" % properties)
+ info = {'driver_volume_type': 'fibre_channel',
+ 'data': {'target_lun': int(host_lun_id),
+ 'target_discovered': True,
+ 'target_wwn': tgt_port_wwns,
+ 'volume_id': volume['id'],
+ 'initiator_target_map': init_targ_map}}
- return {'driver_volume_type': 'fibre_channel',
- 'data': properties}
+ LOG.info(_LI("initialize_connection_fc, return data is: %s.")
+ % info)
+
+ return info
def _get_iscsi_tgt_port(self):
url = self.url + "/iscsidevicename"
def _find_hostgroup(self, groupname):
"""Get the given hostgroup id."""
- url = self.url + "/hostgroup"
+ url = self.url + "/hostgroup?range=[0-8191]"
result = self.call(url, None, "GET")
- self._assert_rest_result(result, 'Get host group information error.')
+ self._assert_rest_result(result, 'Get hostgroup information error.')
host_group_id = None
if "data" in result:
def _find_lungroup(self, lungroupname):
"""Get the given hostgroup id."""
- url = self.url + "/lungroup"
+ url = self.url + "/lungroup?range=[0-8191]"
result = self.call(url, None, "GET")
- self._assert_rest_result(result, 'Get lun group information error.')
+ self._assert_rest_result(result, 'Get lungroup information error.')
lun_group_id = None
if 'data' in result:
data = json.dumps({"TYPE": "14", "NAME": hostgroupname})
result = self.call(url, data)
- msg = 'Create host group error.'
+ msg = 'Create hostgroup error.'
self._assert_rest_result(result, msg)
self._assert_data_in_result(result, msg)
def _create_lungroup(self, lungroupname):
url = self.url + "/lungroup"
data = json.dumps({"DESCRIPTION": lungroupname,
+ "APPTYPE": '0',
+ "GROUPTYPE": '0',
"NAME": lungroupname})
result = self.call(url, data)
- msg = 'Create lun group error.'
+ msg = 'Create lungroup error.'
self._assert_rest_result(result, msg)
self._assert_data_in_result(result, msg)
def _delete_lungroup(self, lungroupid):
url = self.url + "/LUNGroup/" + lungroupid
result = self.call(url, None, "DELETE")
- self._assert_rest_result(result, 'Delete lun group error.')
+ self._assert_rest_result(result, 'Delete lungroup error.')
def _lungroup_associated(self, viewid, lungroupid):
url_subfix = ("/mappingview/associate?TYPE=245&"
"ASSOCIATEOBJTYPE=256&ASSOCIATEOBJID=%s" % lungroupid)
url = self.url + url_subfix
result = self.call(url, None, "GET")
- self._assert_rest_result(result, 'Check lun group associated error.')
+ self._assert_rest_result(result, 'Check lungroup associated error.')
if "data" in result:
for item in result['data']:
"ASSOCIATEOBJTYPE=14&ASSOCIATEOBJID=%s" % hostgroupid)
url = self.url + url_subfix
result = self.call(url, None, "GET")
- self._assert_rest_result(result, 'Check host group associated error.')
+ self._assert_rest_result(result, 'Check hostgroup associated error.')
if "data" in result:
for item in result['data']:
return False
def _find_host_lun_id(self, hostid, lunid):
- time.sleep(2)
+
url = self.url + ("/lun/associate?TYPE=11&ASSOCIATEOBJTYPE=21"
"&ASSOCIATEOBJID=%s" % (hostid))
result = self.call(url, None, "GET")
host_lun_id = hostassoinfo['HostLUNID']
break
except Exception as err:
- msg = _("JSON transfer data error. %s") % err
+ msg = (_LE("JSON transfer data error. %s") % err)
LOG.error(msg)
raise err
return host_lun_id
def _find_host(self, hostname):
"""Get the given host ID."""
- url = self.url + "/host"
+ url = self.url + "/host?range=[0-65534]"
data = json.dumps({"TYPE": "21"})
result = self.call(url, data, "GET")
- self._assert_rest_result(result, 'Find host in host group error.')
+ self._assert_rest_result(result, 'Find host in hostgroup error.')
host_id = None
if "data" in result:
break
return host_id
- def _add_host(self, hostname, type):
+ def _add_host(self, hostname):
"""Add a new host."""
url = self.url + "/host"
data = json.dumps({"TYPE": "21",
"NAME": hostname,
- "OPERATIONSYSTEM": type})
+ "OPERATIONSYSTEM": "0"})
result = self.call(url, data)
self._assert_rest_result(result, 'Add new host error.')
else:
return None
- def _associate_host_to_hostgroup(self, hostgroupid, hostid):
- url = self.url + "/host/associate"
- data = json.dumps({"ID": hostgroupid,
+ def _is_host_associate_to_hostgroup(self, hostgroup_id, host_id):
+ """Check whether the host is associate to the hostgroup."""
+ url_subfix = ("/host/associate?TYPE=21&"
+ "ASSOCIATEOBJTYPE=14&ASSOCIATEOBJID=%s" % hostgroup_id)
+
+ url = self.url + url_subfix
+ result = self.call(url, None, "GET")
+ self._assert_rest_result(result, 'Check hostgroup associated error.')
+
+ if "data" in result:
+ for item in result['data']:
+ if host_id == item['ID']:
+ return True
+
+ return False
+
+ def _associate_host_to_hostgroup(self, hostgroup_id, host_id):
+ url = self.url + "/hostgroup/associate"
+ data = json.dumps({"TYPE": "14",
+ "ID": hostgroup_id,
"ASSOCIATEOBJTYPE": "21",
- "ASSOCIATEOBJID": hostid})
+ "ASSOCIATEOBJID": host_id})
result = self.call(url, data)
- self._assert_rest_result(result, 'Associate host to host group error.')
+ self._assert_rest_result(result, 'Associate host to hostgroup error.')
def _associate_lun_to_lungroup(self, lungroupid, lunid):
- """Associate lun to lun group."""
+ """Associate lun to lungroup."""
url = self.url + "/lungroup/associate"
data = json.dumps({"ID": lungroupid,
"ASSOCIATEOBJTYPE": "11",
"ASSOCIATEOBJID": lunid})
result = self.call(url, data)
- self._assert_rest_result(result, 'Associate lun to lun group error.')
+ self._assert_rest_result(result, 'Associate lun to lungroup error.')
- def _delete_associated_lun_from_lungroup(self, lungroupid, lunid):
- """Remove lun from lun group."""
+ def _remove_lun_from_lungroup(self, lungroupid, lunid):
+ """Remove lun from lungroup."""
url = self.url + ("/lungroup/associate?ID=%s"
"&ASSOCIATEOBJTYPE=11&ASSOCIATEOBJID=%s"
result = self.call(url, None, 'DELETE')
self._assert_rest_result(result,
- 'Delete associated lun from lun group error')
+ 'Delete associated lun from lungroup error.')
def _initiator_is_added_to_array(self, ininame):
- """Check whether the initiator is already added in array."""
+ """Check whether the initiator is already added on the array."""
url = self.url + "/iscsi_initiator"
data = json.dumps({"TYPE": "222", "ID": ininame})
result = self.call(url, data, "GET")
url = self.url + "/iscsi_initiator/"
data = json.dumps({"TYPE": "222",
"ID": ininame,
- "USECHAP": "False"})
+ "USECHAP": "false"})
result = self.call(url, data)
self._assert_rest_result(result, 'Add initiator to array error.')
url = self.url + "/iscsi_initiator/" + ininame
data = json.dumps({"TYPE": "222",
"ID": ininame,
- "USECHAP": "False",
+ "USECHAP": "false",
"PARENTTYPE": "21",
"PARENTID": hostid})
result = self.call(url, data, "PUT")
def _find_mapping_view(self, name):
"""Find mapping view."""
- url = self.url + "/mappingview"
+ url = self.url + "/mappingview?range=[0-65535]"
data = json.dumps({"TYPE": "245"})
result = self.call(url, data, "GET")
msg = 'Find map view error.'
self._assert_rest_result(result, msg)
- self._assert_data_in_result(result, msg)
-
viewid = None
- for item in result['data']:
- if name == item['NAME']:
- viewid = item['ID']
- break
+ if "data" in result:
+ for item in result['data']:
+ if name == item['NAME']:
+ viewid = item['ID']
+ break
+
return viewid
- def _add_mapping_view(self, name, host_id):
+ def _add_mapping_view(self, name):
url = self.url + "/mappingview"
data = json.dumps({"NAME": name, "TYPE": "245"})
result = self.call(url, data)
"TYPE": "245",
"ID": viewID})
result = self.call(url, data, "PUT")
- self._assert_rest_result(result, 'Associate lun group to view error.')
+ self._assert_rest_result(result, 'Associate lungroup to view error.')
def _delete_lungroup_mapping_view(self, view_id, lungroup_id):
- """remove lun group associate from the mapping view."""
+ """Remove lungroup associate from the mapping view."""
url = self.url + "/mappingview/REMOVE_ASSOCIATE"
data = json.dumps({"ASSOCIATEOBJTYPE": "256",
"ASSOCIATEOBJID": lungroup_id,
"TYPE": "245",
"ID": view_id})
result = self.call(url, data, "PUT")
- self._assert_rest_result(result, 'Delete lun group from view error.')
+ self._assert_rest_result(result, 'Delete lungroup from view error.')
def _delete_hostgoup_mapping_view(self, view_id, hostgroup_id):
- """remove host group associate from the mapping view."""
+ """Remove hostgroup associate from the mapping view."""
url = self.url + "/mappingview/REMOVE_ASSOCIATE"
data = json.dumps({"ASSOCIATEOBJTYPE": "14",
"ASSOCIATEOBJID": hostgroup_id,
"TYPE": "245",
"ID": view_id})
result = self.call(url, data, "PUT")
- self._assert_rest_result(result, 'Delete host group from view error.')
+ self._assert_rest_result(result, 'Delete hostgroup from view error.')
def _delete_mapping_view(self, view_id):
- """remove mapping view from the storage."""
+ """Remove mapping view from the storage."""
url = self.url + "/mappingview/" + view_id
result = self.call(url, None, "DELETE")
self._assert_rest_result(result, 'Delete map view error.')
- def terminate_connection(self, volume, connector, **kwargs):
+ 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 = self.url + url_subfix
+ result = self.call(url, None, "GET")
+ self._assert_rest_result(result, 'Find lun number error.')
+ if "data" in result:
+ lunnum = result['data']['COUNT']
+ return lunnum
+ return None
+
+ @utils.synchronized('huawei', external=True)
+ def terminate_connection_iscsi(self, volume, connector):
"""Delete map between a volume and a host."""
initiator_name = connector['initiator']
volume_name = self._encode_name(volume['id'])
- host_name = connector['host']
+ lun_id = volume.get('provider_location', None)
+ LOG.info(_LI(
+ 'terminate_connection:volume name: %(volume)s, '
+ 'initiator name: %(ini)s, '
+ 'lun_id: %(lunid)s.')
+ % {'volume': volume_name,
+ 'ini': initiator_name,
+ 'lunid': lun_id})
+
+ if lun_id:
+ if self._check_lun_exist(lun_id) is True:
+ # Get lungroupid by lun_id.
+ lungroup_id = self._get_lungroupid_by_lunid(lun_id)
- LOG.debug('terminate_connection:volume name: %(volume)s, '
- 'initiator name: %(ini)s.'
- % {'volume': volume_name,
- 'ini': initiator_name})
+ if lungroup_id is None:
+ LOG.info(_LI("Can't find lun in lungroup."))
+ else:
+ self._remove_lun_from_lungroup(lungroup_id, lun_id)
+ LOG.info(_LI(
+ "Check if there are still other luns associated"
+ " to the lungroup."))
+ left_lunnum = self._get_lunnum_from_lungroup(lungroup_id)
+ return left_lunnum
- view_id = self._find_mapping_view(volume_name)
- hostgroup_id = self._find_hostgroup(host_name)
- lungroup_id = self._find_lungroup(volume_name)
+ else:
+ LOG.warning(_LW("Can't find lun on the array."))
- if view_id is not None:
- self._delete_hostgoup_mapping_view(view_id, hostgroup_id)
- self._delete_lungroup_mapping_view(view_id, lungroup_id)
- self._delete_mapping_view(view_id)
+ def terminate_connection_fc(self, volume, connector):
+ """Delete map between a volume and a host."""
+ wwns = connector['wwpns']
+ left_lunnum = self.terminate_connection_iscsi(volume, connector)
+
+ tgt_port_wwns = []
+ for wwn in wwns:
+ tgtwwpns = self._get_fc_target_wwpns(wwn)
+ if tgtwwpns:
+ tgt_port_wwns.append(tgtwwpns)
+
+ init_targ_map = {}
+ for initiator in wwns:
+ init_targ_map[initiator] = tgt_port_wwns
+
+ if left_lunnum and left_lunnum > 0:
+ info = {'driver_volume_type': 'fibre_channel',
+ 'data': {}}
+ else:
+ info = {'driver_volume_type': 'fibre_channel',
+ 'data': {'target_wwn': tgt_port_wwns,
+ 'initiator_target_map': init_targ_map}}
+
+ return info
def login_out(self):
"""logout the session."""
def _get_lun_conf_params(self):
"""Get parameters from config file for creating lun."""
- # Default lun set information
+ # Default lun set information.
lunsetinfo = {'LUNType': 'Thick',
'StripUnitSize': '64',
'WriteType': '1',
'PrefetchValue': '0',
'PrefetchTimes': '0'}
- root = huawei_utils.parse_xml_file(self.xml_conf)
+ root = self._read_xml()
luntype = root.findtext('LUN/LUNType')
if luntype:
if luntype.strip() in ['Thick', 'Thin']:
lunsetinfo['LUNType'] = 1
elif luntype is not '' and luntype is not None:
- err_msg = (_('Config file is wrong. LUNType must be "Thin"'
- ' or "Thick". LUNType:%(fetchtype)s')
- % {'fetchtype': luntype})
+ err_msg = (_(
+ 'Config file is wrong. LUNType must be "Thin"'
+ ' or "Thick". LUNType: %(fetchtype)s.')
+ % {'fetchtype': luntype})
LOG.error(err_msg)
raise exception.VolumeBackendAPIException(data=err_msg)
lunsetinfo['PrefetchType'] = fetchtype.strip()
typevalue = prefetch.attrib['Value'].strip()
if lunsetinfo['PrefetchType'] == '1':
- lunsetinfo['PrefetchValue'] = typevalue
+ 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 in 1,2,3,4. fetchtype is:%(fetchtype)s')
- % {'fetchtype': fetchtype})
+ err_msg = (_(
+ 'PrefetchType config is wrong. PrefetchType'
+ ' must be in 1,2,3,4. fetchtype is: %(fetchtype)s.')
+ % {'fetchtype': fetchtype})
LOG.error(err_msg)
raise exception.CinderException(err_msg)
else:
- LOG.debug('Use default prefetch fetchtype. '
- 'Prefetch fetchtype:Intelligent.')
+ LOG.info(_LI(
+ 'Use default prefetch fetchtype. '
+ 'Prefetch fetchtype:Intelligent.'))
return lunsetinfo
if luncopy_info['status'] == '40':
break
elif luncopy_info['state'] != '1':
- err_msg = (_('_wait_for_luncopy:LUNcopy status is not normal.'
- 'LUNcopy name: %(luncopyname)s')
- % {'luncopyname': luncopyid})
+ err_msg = (_(
+ '_wait_for_luncopy: LUNcopy status is not normal.'
+ 'LUNcopy name: %(luncopyname)s.')
+ % {'luncopyname': luncopyid})
LOG.error(err_msg)
raise exception.VolumeBackendAPIException(data=err_msg)
- time.sleep(10)
+ LOG.info(_LI('Waiting for luncopy to be complete.'))
+ time.sleep(self.SLEEP_FOR_LUNCOPY)
def _get_luncopy_info(self, luncopyid):
"""Get LUNcopy information."""
msg = 'Get connected free FC wwn error.'
self._assert_rest_result(result, msg)
- self._assert_data_in_result(result, msg)
wwns = []
- for item in result['data']:
- wwns.append(item['ID'])
+ if 'data' in result:
+ for item in result['data']:
+ wwns.append(item['ID'])
+
return wwns
- def _add_fc_port_to_host(self, hostid, wwn, multipathtype=0):
+ def _add_fc_port_to_host(self, hostid, wwn):
"""Add a FC port to the host."""
url = self.url + "/fc_initiator/" + wwn
data = json.dumps({"TYPE": "223",
iscsi_port_info = item['LOCATION']
break
- if not iscsi_port_info:
- msg = (_('_get_iscsi_port_info: Failed to get iscsi port info '
- 'through config IP %(ip)s, please check config file.')
- % {'ip': ip})
- LOG.error(msg)
- raise exception.InvalidInput(reason=msg)
-
return iscsi_port_info
def _get_iscsi_conf(self):
"""Get iSCSI info from config file."""
iscsiinfo = {}
- root = huawei_utils.parse_xml_file(self.xml_conf)
- iscsiinfo['DefaultTargetIP'] = \
- root.findtext('iSCSI/DefaultTargetIP').strip()
+ root = self._read_xml()
+ TargetIP = root.findtext('iSCSI/DefaultTargetIP').strip()
+ iscsiinfo['DefaultTargetIP'] = TargetIP
initiator_list = []
tmp_dic = {}
for dic in root.findall('iSCSI/Initiator'):
- # Strip values of dic
+ # Strip values of dic.
for k, v in dic.items():
tmp_dic[k] = v.strip()
initiator_list.append(tmp_dic)
def _get_tgt_iqn(self, iscsiip):
"""Get target iSCSI iqn."""
- LOG.debug('_get_tgt_iqn: iSCSI IP is %s.' % iscsiip)
+
ip_info = self._get_iscsi_port_info(iscsiip)
iqn_prefix = self._get_iscsi_tgt_port()
+ LOG.info(_LI('Request ip info is: %s.') % ip_info)
split_list = ip_info.split(".")
newstr = split_list[1] + split_list[2]
- if newstr[0] == 'A':
- ctr = "0"
- elif newstr[0] == 'B':
- ctr = "1"
- interface = '0' + newstr[1]
- port = '0' + newstr[3]
- iqn_suffix = ctr + '02' + interface + port
- for i in range(0, len(iqn_suffix)):
- if iqn_suffix[i] != '0':
- iqn_suffix = iqn_suffix[i:]
- break
- iqn = iqn_prefix + ':' + iqn_suffix + ':' + iscsiip
- LOG.debug('_get_tgt_iqn: iSCSI target iqn is %s' % iqn)
- return iqn
+ LOG.info(_LI('New str info is: %s.') % newstr)
+
+ if ip_info:
+ if newstr[0] == 'A':
+ ctr = "0"
+ elif newstr[0] == 'B':
+ ctr = "1"
+ interface = '0' + newstr[1]
+ port = '0' + newstr[3]
+ iqn_suffix = ctr + '02' + interface + port
+ for i in range(0, len(iqn_suffix)):
+ if iqn_suffix[i] != '0':
+ iqn_suffix = iqn_suffix[i:]
+ break
+ iqn = iqn_prefix + ':' + iqn_suffix + ':' + iscsiip
+ LOG.info(_LI('_get_tgt_iqn: iSCSI target iqn is: %s.') % iqn)
+ return iqn
+ else:
+ return None
def _get_fc_target_wwpns(self, wwn):
url = (self.url +
msg = 'Get FC target wwpn error.'
self._assert_rest_result(result, msg)
- self._assert_data_in_result(result, msg)
fc_wwpns = None
- for item in result['data']:
- if wwn == item['INITIATOR_PORT_WWN']:
- fc_wwpns = item['TARGET_PORT_WWN']
- break
+ if "data" in result:
+ for item in result['data']:
+ if wwn == item['INITIATOR_PORT_WWN']:
+ fc_wwpns = item['TARGET_PORT_WWN']
+ break
return fc_wwpns
- def _parse_volume_type(self, volume):
- type_id = volume['volume_type_id']
- params = self._get_lun_conf_params()
- LOG.debug('_parse_volume_type: type id: %(type_id)s '
- 'config parameter is: %(params)s'
- % {'type_id': type_id,
- 'params': params})
-
- poolinfo = self._find_pool_info()
- volume_size = self._get_volume_size(poolinfo, volume)
- params['volume_size'] = volume_size
- params['pool_id'] = poolinfo['ID']
-
- if type_id is not None:
- ctxt = context.get_admin_context()
- volume_type = volume_types.get_volume_type(ctxt, type_id)
- specs = volume_type.get('extra_specs')
- for key, value in specs.iteritems():
- key_split = key.split(':')
- if len(key_split) > 1:
- if key_split[0] == 'drivers':
- key = key_split[1]
- else:
- continue
- else:
- key = key_split[0]
-
- if key in QOS_KEY:
- params["qos"] = value.strip()
- params["qos_level"] = key
- elif key in TIER_KEY:
- params["tier"] = value.strip()
- elif key in params.keys():
- params[key] = value.strip()
- else:
- conf = self.configuration.cinder_huawei_conf_file
- LOG.warn(_LW('_parse_volume_type: Unacceptable parameter '
- '%(key)s. Please check this key in '
- 'extra_specs and make it consistent with the '
- 'configuration file '
- '%(conf)s.') % {'key': key, 'conf': conf})
-
- LOG.debug("The config parameters are: %s" % params)
- return params
-
- def update_volume_stats(self, refresh=False):
+ def update_volume_stats(self):
capacity = self._get_capacity()
data = {}
data['vendor_name'] = 'Huawei'
url = self.url + "/ioclass"
result = self.call(url, None, "GET")
- msg = 'Get qos policy error.'
+ msg = 'Get QoS policy error.'
self._assert_rest_result(result, msg)
- self._assert_data_in_result(result, msg)
qos_info = {}
- for item in result['data']:
- if policy_name == item['NAME']:
- qos_info['ID'] = item['ID']
- lun_list = json.loads(item['LUNLIST'])
- qos_info['LUNLIST'] = lun_list
- break
+ if "data" in result:
+ for item in result['data']:
+ if policy_name == item['NAME']:
+ qos_info['ID'] = item['ID']
+ lun_list = json.loads(item['LUNLIST'])
+ qos_info['LUNLIST'] = lun_list
+ qos_info['RUNNINGSTATUS'] = item['RUNNINGSTATUS']
+ break
+
return qos_info
def _update_qos_policy_lunlist(self, lunlist, policy_id):
"ID": policy_id,
"LUNLIST": lunlist})
result = self.call(url, data, "PUT")
- self._assert_rest_result(result, 'Up date qos policy error.')
+ self._assert_rest_result(result, 'Update QoS policy error.')
def _get_login_info(self):
"""Get login IP, username and password from config file."""
filename = self.configuration.cinder_huawei_conf_file
tree = ET.parse(filename)
root = tree.getroot()
- logininfo['HVSURL'] = root.findtext('Storage/HVSURL').strip()
+ logininfo['RestURL'] = root.findtext('Storage/RestURL').strip()
need_encode = False
for key in ['UserName', 'UserPassword']:
try:
tree.write(filename, 'UTF-8')
except Exception as err:
- LOG.warn(_LW('%s') % err)
+ LOG.warning(_LW('Unable to access config file. %s') % err)
return logininfo
def _change_file_mode(self, filepath):
- utils.execute('chmod', '777', filepath, run_as_root=True)
+ utils.execute('chmod', '640', filepath, run_as_root=True)
def _check_conf_file(self):
"""Check the config file, make sure the essential items are set."""
- root = huawei_utils.parse_xml_file(self.xml_conf)
- check_list = ['Storage/HVSURL', 'Storage/UserName',
- 'Storage/UserPassword']
- for item in check_list:
- if not huawei_utils.is_xml_item_exist(root, item):
- err_msg = (_('_check_conf_file: Config file invalid. '
- '%s must be set.') % item)
- LOG.error(err_msg)
- raise exception.InvalidInput(reason=err_msg)
-
- # make sure storage pool is set
- if not huawei_utils.is_xml_item_exist(root, 'LUN/StoragePool'):
- err_msg = _('_check_conf_file: Config file invalid. '
- 'StoragePool must be set.')
+ root = self._read_xml()
+ 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)
- # make sure host os type valid
- if huawei_utils.is_xml_item_exist(root, 'Host', 'OSType'):
- os_list = huawei_utils.os_type.keys()
- if not huawei_utils.is_xml_item_valid(root, 'Host', os_list,
- 'OSType'):
- err_msg = (_('_check_conf_file: Config file invalid. '
- 'Host OSType invalid.\n'
- 'The valid values are: %(os_list)s')
- % {'os_list': os_list})
- 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_iscsi_params(self, connector):
"""Get target iSCSI params, including iqn, IP."""
target_ip = iscsi_conf['DefaultTargetIP']
else:
- msg = (_('_get_iscsi_params: Failed to get target IP '
- 'for initiator %(ini)s, please check config file.')
- % {'ini': initiator})
+ msg = (_(
+ '_get_iscsi_params: Failed to get target IP '
+ 'for initiator %(ini)s, please check config file.')
+ % {'ini': initiator})
LOG.error(msg)
raise exception.InvalidInput(reason=msg)
- target_iqn = self._get_tgt_iqn(target_ip)
+ # If didn't get target IP for rest, Automated assembly target ip.
+ target_iqn = self._get_tgt_iqn_from_rest(target_ip)
+
+ if not target_iqn:
+ target_iqn = self._get_tgt_iqn(target_ip)
return (target_iqn, target_ip)
+ def _get_tgt_iqn_from_rest(self, target_ip):
+ url = self.url + "/iscsi_tgt_port"
+ result = self.call(url, None, "GET")
+
+ target_iqn = None
+ if result['error']['code'] != 0:
+ LOG.warning(_LW("Can't find target iqn from rest."))
+ return target_iqn
+
+ if 'data' in result:
+ for item in result['data']:
+ if target_ip in item['ID']:
+ target_iqn = item['ID']
+
+ if not target_iqn:
+ LOG.warning(_LW("Can't find target iqn from rest."))
+ return target_iqn
+
+ split_list = target_iqn.split(",")
+ target_iqn_before = split_list[0]
+
+ split_list_new = target_iqn_before.split("+")
+ target_iqn = split_list_new[1]
+
+ return target_iqn
+
+ @utils.synchronized('huawei', external=True)
def extend_volume(self, volume, new_size):
- name = self._encode_name(volume['id'])
- lun_id = self._get_volume_by_name(name)
- if lun_id:
- url = self.url + "/lun/expand"
- capacity = int(new_size) * units.Gi / 512
- data = json.dumps({"TYPE": "11",
- "ID": lun_id,
- "CAPACITY": capacity})
- result = self.call(url, data, "PUT")
- self._assert_rest_result(result, 'Extend lun error.')
+ """Extends a Huawei volume."""
+
+ LOG.info(_LI('Entering extend_volume.'))
+ volume_size = self._get_volume_size(volume)
+ new_volume_size = int(new_size) * units.Gi / 512
+ volume_name = self._encode_name(volume['id'])
+
+ LOG.info(_LI(
+ 'Extend Volume: %(volumename)s, oldsize:'
+ ' %(oldsize)s newsize: %(newsize)s.')
+ % {'volumename': volume_name,
+ 'oldsize': volume_size,
+ 'newsize': new_volume_size})
+
+ lun_id = self._get_volume_by_name(volume_name)
+
+ if lun_id is None:
+ msg = (_(
+ "Can't find lun info on the array, lun name is: %(name)s.")
+ % {'name': volume_name})
+ LOG.error(msg)
+ raise exception.CinderException(msg)
+
+ url = self.url + "/lun/expand"
+ data = json.dumps({"TYPE": 11, "ID": lun_id,
+ "CAPACITY": new_volume_size})
+ result = self.call(url, data, 'PUT')
+
+ msg = 'Extend volume error.'
+ self._assert_rest_result(result, msg)
+ self._assert_data_in_result(result, msg)
+
+ return result['data']['ID']
+
+ def _get_volume_type(self, type_id):
+ ctxt = context.get_admin_context()
+ return volume_types.get_volume_type(ctxt, type_id)
+
+ def _get_qos_by_volume_type(self, volume_type):
+ qos = {}
+ qos_specs_id = volume_type.get('qos_specs_id')
+ specs = volume_type.get('extra_specs')
+
+ # NOTE(kmartin): 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:
+ kvs = specs
+
+ LOG.info(_LI('The QoS sepcs is: %s.') % kvs)
+ for key, value in kvs.iteritems():
+ if key in huawei_valid_keys:
+ qos[key.upper()] = value
+
+ return qos
+
+ def _get_qos_value(self, qos, key, default=None):
+ if key in qos:
+ return qos[key]
else:
- LOG.warn(_LW('Can not find lun in array'))
+ return default
+
+ def _create_qos_policy(self, qos, lun_id):
+
+ # Get local time.
+ localtime = time.strftime('%Y%m%d%H%M%S', time.localtime(time.time()))
+ # Package QoS name.
+ qos_name = QOS_NAME_PREFIX + lun_id + '_' + localtime
+ baseData = {"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 = dict(baseData.items() + qos.items())
+ url = self.url + "/ioclass/"
+ data = json.dumps(mergedata)
+
+ result = self.call(url, data)
+ self._assert_rest_result(result, 'Create QoS policy error.')
+
+ return result['data']['ID']
+
+ def _delete_qos_policy(self, qos_id):
+ """Delete a QoS policy."""
+
+ url = self.url + "/ioclass/" + qos_id
+ data = json.dumps({"TYPE": "230",
+ "ID": qos_id})
+
+ result = self.call(url, data, 'DELETE')
+ self._assert_rest_result(result, 'Delete QoS policy error.')
+
+ def _active_deactive_qos(self, qos_id, enablestatus):
+ """Active or deactive QoS.
+
+ enablestatus: true (active)
+ enbalestatus: false (deactive)
+ """
+
+ url = self.url + "/ioclass/active/" + qos_id
+ data = json.dumps({"TYPE": 230,
+ "ID": qos_id,
+ "ENABLESTATUS": enablestatus})
+ result = self.call(url, data, "PUT")
+ self._assert_rest_result(result, 'Active or Deactive QoS error.')
+
+ def _get_qos_info(self, qos_id):
+ """Get QoS information."""
+
+ url = self.url + "/ioclass/" + qos_id
+ data = json.dumps({"TYPE": "230",
+ "ID": qos_id})
+ result = self.call(url, data, "GET")
+ self._assert_rest_result(result, 'Get QoS information error.')
+
+ return result['data']
+
+ def _check_qos_high_priority(self, qos):
+ """Check QoS priority."""
+
+ for key, value in qos.iteritems():
+ if (key.find('MIN') == 0) or (key.find('LATENCY') == 0):
+ return True
+
+ return False
+
+ def _change_lun_priority(self, lunid):
+ """Change lun priority to high."""
+
+ url = self.url + "/lun/" + lunid
+ data = json.dumps({"TYPE": "11",
+ "ID": lunid,
+ "IOPRIORITY": "3"})
+
+ result = self.call(url, data, "PUT")
+ self._assert_rest_result(result, 'Change lun priority error.')
+
+ def _get_qosid_by_lunid(self, lunid):
+ """Get qosid by lunid."""
+
+ url = self.url + "/lun/" + lunid
+ data = json.dumps({"TYPE": "11",
+ "ID": lunid})
+
+ result = self.call(url, data, "GET")
+ self._assert_rest_result(result, 'Get qosid by lunid error.')
+
+ return result['data']['IOCLASSID']
+
+ def _get_lungroupid_by_lunid(self, lunid):
+ """Get lungroupid by lunid."""
+
+ url = self.url + ("/lungroup/associate?TYPE=256"
+ "&ASSOCIATEOBJTYPE=11&ASSOCIATEOBJID=%s" % lunid)
+
+ result = self.call(url, None, "GET")
+ self._assert_rest_result(result, 'Get lungroupid by lunid error.')
+
+ lun_group_id = None
+ # Lun only in one lungroup.
+ if 'data' in result:
+ for item in result['data']:
+ lun_group_id = item['ID']
+
+ return lun_group_id
CONF = cfg.CONF
CONF.register_opts(volume_manager_opts)
+MAPPING = {
+ 'cinder.volume.drivers.huawei.huawei_hvs.HuaweiHVSISCSIDriver':
+ 'cinder.volume.drivers.huawei.huawei_18000.Huawei18000ISCSIDriver',
+ 'cinder.volume.drivers.huawei.huawei_hvs.HuaweiHVSFCDriver':
+ 'cinder.volume.drivers.huawei.huawei_18000.Huawei18000FCDriver', }
+
def locked_volume_operation(f):
"""Lock decorator for volume operations.
# Get from configuration, which will get the default
# if its not using the multi backend
volume_driver = self.configuration.volume_driver
+ if volume_driver in MAPPING:
+ LOG.warning(_LW("Driver path %s is deprecated, update your "
+ "configuration to the new path."), volume_driver)
+ volume_driver = MAPPING[volume_driver]
vol_db_empty = self._set_voldb_empty_at_startup_indicator(
context.get_admin_context())