]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Refactor Huawei Volume driver
authorchenzongliang <chenzongliang@huawei.com>
Thu, 4 Jun 2015 02:43:07 +0000 (10:43 +0800)
committerLiu Xinguo <295988511@qq.com>
Sat, 18 Jul 2015 08:49:05 +0000 (16:49 +0800)
This patch attempts to refactor Huawei volume driver in liberty.
We add a base driver to implement the basic functions.
The sub-class will inherit from the base driver according to different
storages.

The following changes were made in this refactor:
1. Abstract a base class named HuaweiBaseDriver to make Huawei driver more
universal. You can find it in the huawei_driver.py.
2. Put all static variables into the constants.py.
3. Rename rest_common.py to rest_client.py. rest_client.py stores the
relevant methods implemented for Huawei driver.
4. Migrate some public methods from rest_client.py to huawei_utils.py,
such as parse_xml_file(), _get_volume_type() and so on.
5. This refactor only involves structural adjustment and does not involve
functional changes.

Change-Id: I768889e2577a4d975397218eb31e89b42e08e04f
Implements: blueprint refactor-huawei-volume-driver

12 files changed:
cinder/tests/unit/test_huawei_18000.py [deleted file]
cinder/tests/unit/test_huawei_drivers.py [new file with mode: 0644]
cinder/tests/unit/test_huawei_drivers_compatibility.py
cinder/volume/drivers/huawei/__init__.py
cinder/volume/drivers/huawei/constants.py [new file with mode: 0644]
cinder/volume/drivers/huawei/huawei_18000.py [deleted file]
cinder/volume/drivers/huawei/huawei_driver.py [new file with mode: 0644]
cinder/volume/drivers/huawei/huawei_utils.py
cinder/volume/drivers/huawei/rest_client.py [new file with mode: 0644]
cinder/volume/drivers/huawei/rest_common.py [deleted file]
cinder/volume/manager.py
tox.ini

diff --git a/cinder/tests/unit/test_huawei_18000.py b/cinder/tests/unit/test_huawei_18000.py
deleted file mode 100644 (file)
index 02b7a82..0000000
+++ /dev/null
@@ -1,1015 +0,0 @@
-# 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 import minidom
-
-import mock
-
-from cinder import exception
-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
-
-
-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.associate_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"\
-                      or url == "lungroup/associate?ID=12"\
-                      "&ASSOCIATEOBJTYPE=11&ASSOCIATEOBJID=12":
-                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")
-               or url == ("lun/associate?TYPE=11&ASSOCIATEOBJTYPE=256"
-                          "&ASSOCIATEOBJID=11")
-               or (url == ("lun/associate?TYPE=11&ASSOCIATEOBJTYPE=256"
-                           "&ASSOCIATEOBJID=12")
-               and not self.associate_flag)):
-                data = '{"error":{"code":0},"data":[{"ID":"11"}]}'
-            if ((url == ("lun/associate?TYPE=11&ASSOCIATEOBJTYPE=256"
-                         "&ASSOCIATEOBJID=12"))
-               and self.associate_flag):
-                data = '{"error":{"code":0},"data":[{"ID":"12"}]}'
-
-            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") and (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 testgetdefaulttimeout(self):
-        result = self.driver.common._get_default_timeout()
-        self.assertEqual('43200', result)
-
-    def testgetwaitinterval(self):
-        result = self.driver.common._get_wait_interval('LUNReadyWaitInterval')
-        self.assertEqual('2', result)
-
-    def test_lun_is_associated_to_lungroup(self):
-        self.driver.common.login()
-        self.driver.common._associate_lun_to_lungroup('11', '11')
-        result = self.driver.common._is_lun_associated_to_lungroup('11', '11')
-        self.assertTrue(result)
-
-    def test_lun_is_not_associated_to_lun_group(self):
-        self.driver.common.login()
-        self.driver.common._associate_lun_to_lungroup('12', '12')
-        self.driver.common.associate_flag = True
-        result = self.driver.common._is_lun_associated_to_lungroup('12', '12')
-        self.assertTrue(result)
-        self.driver.common._remove_lun_from_lungroup('12', '12')
-        self.driver.common.associate_flag = False
-        result = self.driver.common._is_lun_associated_to_lungroup('12', '12')
-        self.assertFalse(result)
-
-    def create_fake_conf_file(self):
-        """Create a fake Config file
-
-          Huawei storage customize a XML configuration file, the configuration
-          file is used to set the Huawei storage custom parameters, therefore,
-          in the UT test we need to simulate such a configuration file
-        """
-        doc = minidom.Document()
-
-        config = doc.createElement('config')
-        doc.appendChild(config)
-
-        storage = doc.createElement('Storage')
-        config.appendChild(storage)
-        controllerip0 = doc.createElement('ControllerIP0')
-        controllerip0_text = doc.createTextNode('10.10.10.1')
-        controllerip0.appendChild(controllerip0_text)
-        storage.appendChild(controllerip0)
-        controllerip1 = doc.createElement('ControllerIP1')
-        controllerip1_text = doc.createTextNode('10.10.10.2')
-        controllerip1.appendChild(controllerip1_text)
-        storage.appendChild(controllerip1)
-        username = doc.createElement('UserName')
-        username_text = doc.createTextNode('admin')
-        username.appendChild(username_text)
-        storage.appendChild(username)
-        userpassword = doc.createElement('UserPassword')
-        userpassword_text = doc.createTextNode('Admin@storage')
-        userpassword.appendChild(userpassword_text)
-        storage.appendChild(userpassword)
-        url = doc.createElement('RestURL')
-        url_text = doc.createTextNode('http://100.115.10.69:8082/'
-                                      'deviceManager/rest/')
-        url.appendChild(url_text)
-        storage.appendChild(url)
-
-        lun = doc.createElement('LUN')
-        config.appendChild(lun)
-        storagepool = doc.createElement('StoragePool')
-        pool_text = doc.createTextNode('OpenStack_Pool')
-        storagepool.appendChild(pool_text)
-        lun.appendChild(storagepool)
-
-        timeout = doc.createElement('Timeout')
-        timeout_text = doc.createTextNode('43200')
-        timeout.appendChild(timeout_text)
-        lun.appendChild(timeout)
-
-        lun_ready_wait_interval = doc.createElement('LUNReadyWaitInterval')
-        lun_ready_wait_interval_text = doc.createTextNode('2')
-        lun_ready_wait_interval.appendChild(lun_ready_wait_interval_text)
-        lun.appendChild(lun_ready_wait_interval)
-
-        prefetch = doc.createElement('Prefetch')
-        prefetch.setAttribute('Type', '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 testgetdefaulttimeout(self):
-        result = self.driver.common._get_default_timeout()
-        self.assertEqual('43200', result)
-
-    def testgetwaitinterval(self):
-        result = self.driver.common._get_wait_interval('LUNReadyWaitInterval')
-        self.assertEqual('2', result)
-
-    def test_lun_is_associated_to_lungroup(self):
-        self.driver.common.login()
-        self.driver.common._associate_lun_to_lungroup('11', '11')
-        result = self.driver.common._is_lun_associated_to_lungroup('11', '11')
-        self.assertTrue(result)
-
-    def test_lun_is_not_associated_to_lun_group(self):
-        self.driver.common.login()
-        self.driver.common._associate_lun_to_lungroup('12', '12')
-        self.driver.common.associate_flag = True
-        result = self.driver.common._is_lun_associated_to_lungroup('12', '12')
-        self.assertTrue(result)
-        self.driver.common._remove_lun_from_lungroup('12', '12')
-        self.driver.common.associate_flag = False
-        result = self.driver.common._is_lun_associated_to_lungroup('12', '12')
-        self.assertFalse(result)
-
-    def create_fake_conf_file(self):
-        """Create a fake Config file
-
-          Huawei storage customize a XML configuration file, the configuration
-          file is used to set the Huawei storage custom parameters, therefore,
-          in the UT test we need to simulate such a configuration file
-        """
-        doc = minidom.Document()
-
-        config = doc.createElement('config')
-        doc.appendChild(config)
-
-        storage = doc.createElement('Storage')
-        config.appendChild(storage)
-        controllerip0 = doc.createElement('ControllerIP0')
-        controllerip0_text = doc.createTextNode('10.10.10.1')
-        controllerip0.appendChild(controllerip0_text)
-        storage.appendChild(controllerip0)
-        controllerip1 = doc.createElement('ControllerIP1')
-        controllerip1_text = doc.createTextNode('10.10.10.2')
-        controllerip1.appendChild(controllerip1_text)
-        storage.appendChild(controllerip1)
-        username = doc.createElement('UserName')
-        username_text = doc.createTextNode('admin')
-        username.appendChild(username_text)
-        storage.appendChild(username)
-        userpassword = doc.createElement('UserPassword')
-        userpassword_text = doc.createTextNode('Admin@storage')
-        userpassword.appendChild(userpassword_text)
-        storage.appendChild(userpassword)
-        url = doc.createElement('RestURL')
-        url_text = doc.createTextNode('http://100.115.10.69:8082/'
-                                      'deviceManager/rest/')
-        url.appendChild(url_text)
-        storage.appendChild(url)
-
-        lun = doc.createElement('LUN')
-        config.appendChild(lun)
-        storagepool = doc.createElement('StoragePool')
-        pool_text = doc.createTextNode('OpenStack_Pool')
-        storagepool.appendChild(pool_text)
-        lun.appendChild(storagepool)
-
-        timeout = doc.createElement('Timeout')
-        timeout_text = doc.createTextNode('43200')
-        timeout.appendChild(timeout_text)
-        lun.appendChild(timeout)
-
-        lun_ready_wait_interval = doc.createElement('LUNReadyWaitInterval')
-        lun_ready_wait_interval_text = doc.createTextNode('2')
-        lun_ready_wait_interval.appendChild(lun_ready_wait_interval_text)
-        lun.appendChild(lun_ready_wait_interval)
-
-        prefetch = doc.createElement('Prefetch')
-        prefetch.setAttribute('Type', '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()
diff --git a/cinder/tests/unit/test_huawei_drivers.py b/cinder/tests/unit/test_huawei_drivers.py
new file mode 100644 (file)
index 0000000..6c5e40a
--- /dev/null
@@ -0,0 +1,1363 @@
+# Copyright (c) 2015 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 mock
+import os
+import shutil
+import tempfile
+import time
+from xml.dom import minidom
+
+from oslo_log import log as logging
+
+from cinder import exception
+from cinder import test
+from cinder.volume import configuration as conf
+from cinder.volume.drivers.huawei import huawei_driver
+from cinder.volume.drivers.huawei import huawei_utils
+from cinder.volume.drivers.huawei import rest_client
+
+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,
+               'host': 'ubuntu@huawei#OpenStack_Pool',
+               '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',
+                 }
+
+# A fake response of success response storage
+FAKE_COMMON_SUCCESS_RESPONSE = """
+{
+    "error": {
+        "code": 0
+    }
+}
+"""
+
+# A fake response of login huawei storage
+FAKE_GET_LOGIN_STORAGE_RESPONSE = """
+{
+    "error": {
+        "code": 0
+    },
+    "data": {
+        "username": "admin",
+        "iBaseToken": "2001031430",
+        "deviceid": "210235G7J20000000000"
+    }
+}
+"""
+
+# A fake response of login out huawei storage
+FAKE_LOGIN_OUT_STORAGE_RESPONSE = """
+{
+    "error": {
+        "code": 0
+    },
+    "data": {
+        "ID": 11
+    }
+}
+"""
+
+# A fake response of mock storage pool info
+FAKE_STORAGE_POOL_RESPONSE = """
+{
+    "error": {
+        "code": 0
+    },
+    "data": [{
+        "USERFREECAPACITY": "985661440",
+        "ID": "0",
+        "NAME": "OpenStack_Pool",
+        "USERTOTALCAPACITY": "985661440"
+    }]
+}
+"""
+
+# A fake response of lun or lungroup response
+FAKE_LUN_INFO_RESPONSE = """
+{
+    "error": {
+        "code": 0
+    },
+    "data": {
+        "ID": "1",
+        "NAME": "5mFHcBv4RkCcD+JyrWc0SA"
+    }
+}
+"""
+
+FAKE_LUN_DELETE_SUCCESS_RESPONSE = """
+{
+    "error": {
+        "code": 0
+    },
+    "data": {
+        "ID": "11",
+        "IOCLASSID": "11",
+        "NAME": "5mFHcBv4RkCcD+JyrWc0SA",
+        "RUNNINGSTATUS": "2",
+        "HEALTHSTATUS": "1",
+        "RUNNINGSTATUS": "27"
+    }
+}
+"""
+
+FAKE_QUERY_ALL_LUN_RESPONSE = """
+{
+    "error": {
+        "code": 0
+    },
+    "data": [{
+        "ID": "1",
+        "NAME": "IexzQZJWSXuX2e9I7c8GNQ"
+    }]
+}
+"""
+
+FAKE_LUN_ASSOCIATE_RESPONSE = """
+{
+    "error":{
+        "code":0
+    },
+    "data":[{
+        "ID":"11"
+    }]
+}
+"""
+
+FAKE_QUERY_LUN_GROUP_INFO_RESPONSE = """
+{
+    "error": {
+        "code":0
+    },
+    "data":[{
+        "NAME":"OpenStack_LunGroup_1",
+        "DESCRIPTION":"5mFHcBv4RkCcD+JyrWc0SA",
+        "ID":"11",
+        "TYPE":256
+    }]
+}
+"""
+
+FAKE_QUERY_LUN_GROUP_RESPONSE = """
+{
+    "error": {
+        "code":0
+    },
+    "data":{
+        "NAME":"5mFHcBv4RkCcD+JyrWc0SA",
+        "DESCRIPTION":"5mFHcBv4RkCcD+JyrWc0SA",
+        "ID":"11",
+        "TYPE":256
+    }
+}
+"""
+
+FAKE_QUERY_LUN_GROUP_ASSOCIAT_RESPONSE = """
+{
+    "error":{
+        "code":0
+    },
+    "data":{
+        "NAME":"5mFHcBv4RkCcD+JyrWc0SA",
+        "DESCRIPTION":"5mFHcBv4RkCcD+JyrWc0SA",
+        "ID":"11",
+        "TYPE":256
+    }
+}
+"""
+
+FAKE_LUN_COUNT_RESPONSE = """
+{
+    "data":{
+        "COUNT":"7"
+    },
+    "error":{
+        "code":0,
+        "description":"0"
+    }
+}
+"""
+# A fake response of snapshot list response
+FAKE_SNAPSHOT_LIST_INFO_RESPONSE = """
+{
+    "error": {
+        "code": 0
+    },
+    "data": [{
+        "ID": 11,
+        "NAME": "wr_LMKAjS7O_VtsEIREGYw"
+    },
+    {
+        "ID": 12,
+        "NAME": "SDFAJSDFLKJ"
+    }]
+}
+"""
+
+# A fake response of create snapshot response
+FAKE_CREATE_SNAPSHOT_INFO_RESPONSE = """
+{
+    "error": {
+        "code": 0
+    },
+    "data": {
+        "ID": 11,
+        "NAME": "YheUoRwbSX2BxN7"
+    }
+}
+"""
+
+# A fake response of get snapshot response
+FAKE_GET_SNAPSHOT_INFO_RESPONSE = """
+{
+    "error": {
+        "code": 0
+    },
+    "data": {
+        "ID": 11,
+        "NAME": "YheUoRwbSX2BxN7"
+    }
+}
+"""
+
+# A fake response of get iscsi response
+FAKE_GET_ISCSI_INFO_RESPONSE = """
+{
+    "data": [{
+        "ETHPORTID": "139267",
+        "ID": "iqn.oceanstor:21004846fb8ca15f::22003:111.111.101.244",
+        "TPGT": "8196",
+        "TYPE": 249
+    }],
+    "error": {
+        "code": 0,
+        "description": "0"
+    }
+}
+"""
+
+# A fake response of get eth info response
+FAKE_GET_ETH_INFO_RESPONSE = """
+{
+    "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"
+    }]
+}
+"""
+
+FAKE_GET_ETH_ASSOCIATE_RESPONSE = """
+{
+    "error":{
+        "code":0
+    },
+    "data":[{
+        "IPV4ADDR":"192.168.100.10",
+        "HEALTHSTATUS":"1",
+        "RUNNINGSTATUS":"10"
+    }]
+}
+"""
+# A fake response of get iscsi device info response
+FAKE_GET_ISCSI_DEVICE_RESPONSE = """
+{
+    "error": {
+        "code": 0
+    },
+    "data": [{
+        "CMO_ISCSI_DEVICE_NAME":\
+        "iqn.2006-08.com.huawei:oceanstor:21000022a10a2a39:iscsinametest"
+    }]
+}
+"""
+
+# A fake response of get iscsi device info response
+FAKE_GET_ALL_HOST_INFO_RESPONSE = """
+{
+    "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
+    }]
+}
+"""
+
+# A fake response of get host or hostgroup info response
+FAKE_GET_ALL_HOST_GROUP_INFO_RESPONSE = """
+{
+    "error": {
+        "code": 0
+    },
+    "data": [{
+        "NAME":"ubuntuc",
+        "DESCRIPTION":"",
+        "ID":"0",
+        "TYPE":14
+    }]
+}
+"""
+
+FAKE_GET_HOST_GROUP_INFO_RESPONSE = """
+{
+    "error": {
+        "code": 0
+    },
+    "data":{
+        "NAME":"ubuntuc",
+        "DESCRIPTION":"",
+        "ID":"0",
+        "TYPE":14
+    }
+}
+"""
+
+# A fake response of lun copy info response
+FAKE_GET_LUN_COPY_INFO_RESPONSE = """
+{
+    "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"
+    }
+}
+"""
+
+# A fake response of lun copy list info response
+FAKE_GET_LUN_COPY_LIST_INFO_RESPONSE = """
+{
+    "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"
+    }]
+}
+"""
+
+# A fake response of mappingview info response
+FAKE_GET_MAPPING_VIEW_INFO_RESPONSE = """
+{
+    "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
+    }]
+}
+"""
+
+FAKE_GET_MAPPING_VIEW_RESPONSE = """
+{
+    "error":{
+        "code":0
+    },
+    "data":{
+        "WORKMODE":"255",
+        "HEALTHSTATUS":"1",
+        "NAME":"mOWtSXnaQKi3hpB3tdFRIQ",
+        "RUNNINGSTATUS":"27",
+        "DESCRIPTION":"",
+        "ENABLEINBANDCOMMAND":"true",
+        "ID":"1",
+        "INBANDLUNWWN":"",
+        "TYPE":245
+    }
+}
+"""
+
+FAKE_FC_INFO_RESPONSE = """
+{
+    "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
+    }]
+}
+"""
+
+FAKE_ISCSI_INITIATOR_RESPONSE = """
+{
+    "error":{
+        "code":0
+    },
+    "data":[{
+        "CHAPNAME":"mm-user",
+        "HEALTHSTATUS":"1",
+        "ID":"iqn.1993-08.org.debian:01:9073aba6c6f",
+        "ISFREE":"true",
+        "MULTIPATHTYPE":"1",
+        "NAME":"",
+        "OPERATIONSYSTEM":"255",
+        "RUNNINGSTATUS":"28",
+        "TYPE":222,
+        "USECHAP":"true"
+    }]
+}
+"""
+
+FAKE_HOST_LINK_RESPONSE = """
+{
+    "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"
+    }]
+}
+"""
+
+FAKE_PORT_GROUP_RESPONSE = """
+{
+    "error":{
+        "code":0
+    },
+    "data":[{
+        "ID":11,
+        "NAME":"test"
+    }]
+}
+"""
+
+FAKE_ERROR_INFO_RESPONSE = """
+{
+    "error":{
+        "code":31755596
+    }
+}
+"""
+
+FAKE_ERROR_LUN_INFO_RESPONSE = """
+{
+    "error":{
+        "code":0
+    },
+    "data":{
+        "ID":"11",
+        "IOCLASSID":"11",
+        "NAME":"5mFHcBv4RkCcD+JyrWc0SA"
+    }
+}
+"""
+# mock login info map
+MAP_COMMAND_TO_FAKE_RESPONSE = {}
+MAP_COMMAND_TO_FAKE_RESPONSE['/xx/sessions'] = (
+    FAKE_GET_LOGIN_STORAGE_RESPONSE)
+MAP_COMMAND_TO_FAKE_RESPONSE['sessions'] = (
+    FAKE_LOGIN_OUT_STORAGE_RESPONSE)
+
+# mock storage info map
+MAP_COMMAND_TO_FAKE_RESPONSE['storagepool'] = (
+    FAKE_STORAGE_POOL_RESPONSE)
+
+# mock lun info map
+MAP_COMMAND_TO_FAKE_RESPONSE['lun'] = (
+    FAKE_LUN_INFO_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['lun/11/GET'] = (
+    FAKE_LUN_DELETE_SUCCESS_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['lun/1/GET'] = (
+    FAKE_LUN_DELETE_SUCCESS_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['lun/11/DELETE'] = (
+    FAKE_COMMON_SUCCESS_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['lun?range=[0-65535]/GET'] = (
+    FAKE_QUERY_ALL_LUN_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['lun/associate?TYPE=11&ASSOCIATEOBJTYPE=256'
+                             '&ASSOCIATEOBJID=11/GET'] = (
+    FAKE_LUN_ASSOCIATE_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['lun/associate?TYPE=11&ASSOCIATEOBJTYPE=256'
+                             '&ASSOCIATEOBJID=12/GET'] = (
+    FAKE_LUN_ASSOCIATE_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['lun/associate?ID=1&TYPE=11&ASSOCIATEOBJTYPE=21'
+                             '&ASSOCIATEOBJID=0/GET'] = (
+    FAKE_LUN_ASSOCIATE_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['lun/associate?TYPE=11&ASSOCIATEOBJTYPE=21'
+                             '&ASSOCIATEOBJID=1/GET'] = (
+    FAKE_COMMON_SUCCESS_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['lungroup?range=[0-8191]/GET'] = (
+    FAKE_QUERY_LUN_GROUP_INFO_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['lungroup'] = (
+    FAKE_QUERY_LUN_GROUP_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['lungroup/associate'] = (
+    FAKE_QUERY_LUN_GROUP_ASSOCIAT_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['lungroup/associate?ID=11&ASSOCIATEOBJTYPE=11'
+                             '&ASSOCIATEOBJID=1/DELETE'] = (
+    FAKE_COMMON_SUCCESS_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['lungroup/associate?TYPE=256&ASSOCIATEOBJTYPE=11'
+                             '&ASSOCIATEOBJID=11/GET'] = (
+    FAKE_LUN_ASSOCIATE_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['lungroup/associate?ID=11&ASSOCIATEOBJTYPE=11'
+                             '&ASSOCIATEOBJID=11/DELETE'] = (
+    FAKE_COMMON_SUCCESS_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['lun/count?TYPE=11&ASSOCIATEOBJTYPE=256'
+                             '&ASSOCIATEOBJID=11/GET'] = (
+    FAKE_LUN_COUNT_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['lun/expand/PUT'] = (
+    FAKE_LUN_INFO_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['lungroup/associate?ID=12&ASSOCIATEOBJTYPE=11'
+                             '&ASSOCIATEOBJID=12/DELETE'] = (
+    FAKE_COMMON_SUCCESS_RESPONSE)
+# mock snapshot info map
+MAP_COMMAND_TO_FAKE_RESPONSE['snapshot'] = (
+    FAKE_CREATE_SNAPSHOT_INFO_RESPONSE)
+
+# mock snapshot info map
+MAP_COMMAND_TO_FAKE_RESPONSE['snapshot/11/GET'] = (
+    FAKE_GET_SNAPSHOT_INFO_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['snapshot/activate'] = (
+    FAKE_COMMON_SUCCESS_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['snapshot/stop/PUT'] = (
+    FAKE_COMMON_SUCCESS_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['snapshot/11/DELETE'] = (
+    FAKE_COMMON_SUCCESS_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['snapshot?range=[0-32767]/GET'] = (
+    FAKE_SNAPSHOT_LIST_INFO_RESPONSE)
+
+# mock QoS info map
+MAP_COMMAND_TO_FAKE_RESPONSE['ioclass/11/GET'] = (
+    FAKE_LUN_DELETE_SUCCESS_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['ioclass/11/DELETE'] = (
+    FAKE_COMMON_SUCCESS_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['ioclass/active/11/PUT'] = (
+    FAKE_COMMON_SUCCESS_RESPONSE)
+
+# mock iscsi info map
+MAP_COMMAND_TO_FAKE_RESPONSE['iscsi_tgt_port/GET'] = (
+    FAKE_GET_ISCSI_INFO_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['eth_port/GET'] = (
+    FAKE_GET_ETH_INFO_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['eth_port/associate?TYPE=213&ASSOCIATEOBJTYPE=257'
+                             '&ASSOCIATEOBJID=11/GET'] = (
+    FAKE_GET_ETH_ASSOCIATE_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['iscsidevicename'] = (
+    FAKE_GET_ISCSI_DEVICE_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['iscsi_initiator?range=[0-256]/GET'] = (
+    FAKE_COMMON_SUCCESS_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['iscsi_initiator/'] = (
+    FAKE_ISCSI_INITIATOR_RESPONSE)
+
+# mock host info map
+MAP_COMMAND_TO_FAKE_RESPONSE['host?range=[0-65535]/GET'] = (
+    FAKE_GET_ALL_HOST_INFO_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['hostgroup?range=[0-8191]/GET'] = (
+    FAKE_GET_ALL_HOST_GROUP_INFO_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['hostgroup'] = (
+    FAKE_GET_HOST_GROUP_INFO_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['host/associate?TYPE=21&ASSOCIATEOBJTYPE=14'
+                             '&ASSOCIATEOBJID=0/GET'] = (
+    FAKE_COMMON_SUCCESS_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['hostgroup/associate'] = (
+    FAKE_COMMON_SUCCESS_RESPONSE)
+
+# mock copy info map
+MAP_COMMAND_TO_FAKE_RESPONSE['luncopy'] = (
+    FAKE_GET_LUN_COPY_INFO_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['LUNCOPY?range=[0-1023]/GET'] = (
+    FAKE_GET_LUN_COPY_LIST_INFO_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['LUNCOPY/start/PUT'] = (
+    FAKE_COMMON_SUCCESS_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['LUNCOPY/0/DELETE'] = (
+    FAKE_COMMON_SUCCESS_RESPONSE)
+
+# mock mapping view info map
+MAP_COMMAND_TO_FAKE_RESPONSE['mappingview?range=[0-8191]/GET'] = (
+    FAKE_GET_MAPPING_VIEW_INFO_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['mappingview'] = (
+    FAKE_GET_MAPPING_VIEW_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['MAPPINGVIEW/CREATE_ASSOCIATE/PUT'] = (
+    FAKE_COMMON_SUCCESS_RESPONSE)
+
+# mock FC info map
+MAP_COMMAND_TO_FAKE_RESPONSE['fc_initiator?ISFREE=true&range=[0-8191]/GET'] = (
+    FAKE_FC_INFO_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['fc_initiator/10000090fa0d6754/PUT'] = (
+    FAKE_COMMON_SUCCESS_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['host_link?INITIATOR_TYPE=223'
+                             '&INITIATOR_PORT_WWN=10000090fa0d6754/GET'] = (
+    FAKE_HOST_LINK_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['portgroup?range=[0-8191]&TYPE=257/GET'] = (
+    FAKE_PORT_GROUP_RESPONSE)
+
+
+def Fake_sleep(time):
+    pass
+
+
+class Fake18000Client(rest_client.RestClient):
+
+    def __init__(self, configuration):
+        rest_client.RestClient.__init__(self, configuration)
+        self.delete_flag = False
+        self.terminateFlag = False
+        self.deviceid = None
+        self.test_fail = False
+        self.checkFlag = False
+
+    def _change_file_mode(self, filepath):
+        pass
+
+    def _parse_volume_type(self, volume):
+        poolinfo = self._find_pool_info()
+        volume_size = self._get_volume_size(poolinfo, volume)
+
+        params = {'LUNType': 0,
+                  'WriteType': '1',
+                  'PrefetchType': '3',
+                  'qos_level': 'Qos-high',
+                  'StripUnitSize': '64',
+                  'PrefetchValue': '0',
+                  'PrefetchTimes': '0',
+                  'qos': 'OpenStack_Qos_High',
+                  'MirrorSwitch': '1',
+                  'tier': 'Tier_high',
+                  }
+
+        params['volume_size'] = volume_size
+        params['pool_id'] = poolinfo['ID']
+        return params
+
+    def _get_snapshotid_by_name(self, snapshot_name):
+        return "11"
+
+    def _check_snapshot_exist(self, snapshot_id):
+        return True
+
+    def call(self, url=False, data=None, method=None):
+        url = url.replace('http://100.115.10.69:8082/deviceManager/rest', '')
+        command = url.replace('/210235G7J20000000000/', '')
+        data = None
+
+        if method:
+            command = command + "/" + method
+
+        for item in MAP_COMMAND_TO_FAKE_RESPONSE.keys():
+            if command == item:
+                data = MAP_COMMAND_TO_FAKE_RESPONSE[item]
+                if self.test_fail:
+                    data = FAKE_ERROR_INFO_RESPONSE
+                    if command == 'lun/11/GET':
+                        data = FAKE_ERROR_LUN_INFO_RESPONSE
+
+                    self.test_fail = False
+
+        return json.loads(data)
+
+
+class Fake18000ISCSIStorage(huawei_driver.Huawei18000ISCSIDriver):
+    """Fake Huawei Storage, Rewrite some methods of HuaweiISCSIDriver."""
+
+    def __init__(self, configuration):
+        self.configuration = configuration
+        self.xml_file_path = self.configuration.cinder_huawei_conf_file
+
+    def do_setup(self):
+        self.restclient = Fake18000Client(configuration=self.configuration)
+
+
+class Fake18000FCStorage(huawei_driver.Huawei18000FCDriver):
+    """Fake Huawei Storage, Rewrite some methods of HuaweiISCSIDriver."""
+
+    def __init__(self, configuration):
+        self.configuration = configuration
+        self.xml_file_path = self.configuration.cinder_huawei_conf_file
+
+    def do_setup(self):
+        self.restclient = Fake18000Client(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.xml_file_path = self.configuration.cinder_huawei_conf_file
+        self.stubs.Set(time, 'sleep', Fake_sleep)
+        driver = Fake18000ISCSIStorage(configuration=self.configuration)
+        self.driver = driver
+        self.driver.do_setup()
+        self.portgroup = 'test'
+        self.target_ip = '192.168.100.10'
+
+    def test_login_success(self):
+        deviceid = self.driver.restclient.login()
+        self.assertEqual('210235G7J20000000000', deviceid)
+
+    def test_create_volume_success(self):
+        self.driver.restclient.login()
+        lun_info = self.driver.create_volume(test_volume)
+        self.assertEqual('1', lun_info['provider_location'])
+
+    def test_delete_volume_success(self):
+        self.driver.restclient.login()
+        delete_flag = self.driver.delete_volume(test_volume)
+        self.assertTrue(delete_flag)
+
+    def test_create_snapshot_success(self):
+        self.driver.restclient.login()
+        lun_info = self.driver.create_snapshot(test_volume)
+        self.assertEqual(11, lun_info['provider_location'])
+
+    def test_delete_snapshot_success(self):
+        self.driver.restclient.login()
+        delete_flag = self.driver.delete_snapshot(test_snap)
+        self.assertTrue(delete_flag)
+
+    def test_create_volume_from_snapsuccess(self):
+        self.driver.restclient.login()
+        lun_info = self.driver.create_volume_from_snapshot(test_volume,
+                                                           test_volume)
+        self.assertEqual('1', lun_info['provider_location'])
+
+    def test_initialize_connection_success(self):
+        self.driver.restclient.login()
+        iscsi_properties = self.driver.initialize_connection(test_volume,
+                                                             FakeConnector)
+        self.assertEqual(1, iscsi_properties['data']['target_lun'])
+
+    def test_terminate_connection_success(self):
+        self.driver.restclient.login()
+        self.driver.restclient.terminateFlag = True
+        self.driver.terminate_connection(test_volume, FakeConnector)
+        self.assertTrue(self.driver.restclient.terminateFlag)
+
+    def test_get_volume_status(self):
+        self.driver.restclient.login()
+        data = self.driver.get_volume_stats()
+        self.assertEqual('1.1.1', data['driver_version'])
+
+    def test_extend_volume(self):
+        self.driver.restclient.login()
+        lun_info = self.driver.extend_volume(test_volume, 3)
+        self.assertEqual('1', lun_info['provider_location'])
+
+    def test_login_fail(self):
+        self.driver.restclient.test_fail = True
+        self.assertRaises(exception.VolumeBackendAPIException,
+                          self.driver.restclient.login)
+
+    def test_create_snapshot_fail(self):
+        self.driver.restclient.login()
+        self.driver.restclient.test_fail = True
+        self.assertRaises(exception.VolumeBackendAPIException,
+                          self.driver.create_snapshot, test_volume)
+
+    def test_create_volume_fail(self):
+        self.driver.restclient.login()
+        self.driver.restclient.test_fail = True
+        self.assertRaises(exception.VolumeBackendAPIException,
+                          self.driver.create_volume, test_volume)
+
+    def test_delete_volume_fail(self):
+        self.driver.restclient.login()
+        self.driver.restclient.test_fail = True
+        delete_flag = self.driver.delete_volume(test_volume)
+        self.assertTrue(delete_flag)
+
+    def test_delete_snapshot_fail(self):
+        self.driver.restclient.login()
+        self.driver.restclient.test_fail = True
+        delete_flag = self.driver.delete_volume(test_snap)
+        self.assertTrue(delete_flag)
+
+    def test_initialize_connection_fail(self):
+        self.driver.restclient.login()
+        self.driver.restclient.test_fail = True
+        iscsi_properties = self.driver.initialize_connection(test_volume,
+                                                             FakeConnector)
+        self.assertEqual(1, iscsi_properties['data']['target_lun'])
+
+    def test_get_default_timeout(self):
+        result = huawei_utils._get_default_timeout(self.xml_file_path)
+        self.assertEqual('43200', result)
+
+    def test_get_wait_interval(self):
+        result = huawei_utils.get_wait_interval(self.xml_file_path,
+                                                'LUNReadyWaitInterval')
+        self.assertEqual(2, result)
+
+    def test_lun_is_associated_to_lungroup(self):
+        self.driver.restclient.login()
+        self.driver.restclient._associate_lun_to_lungroup('11', '11')
+        result = self.driver.restclient._is_lun_associated_to_lungroup('11',
+                                                                       '11')
+        self.assertTrue(result)
+
+    def test_lun_is_not_associated_to_lun_group(self):
+        self.driver.restclient.login()
+        self.driver.restclient._associate_lun_to_lungroup('12', '12')
+        self.driver.restclient.remove_lun_from_lungroup('12', '12')
+        result = self.driver.restclient._is_lun_associated_to_lungroup('12',
+                                                                       '12')
+        self.assertFalse(result)
+
+    def test_get_tgtip(self):
+        self.driver.restclient.login()
+        portg_id = self.driver.restclient.find_tgt_port_group(self.portgroup)
+        result = self.driver.restclient._get_tgt_ip_from_portgroup(portg_id)
+        self.assertEqual(result, self.target_ip)
+
+    def test_get_lun_conf_params(self):
+        self.driver.restclient.login()
+        luninfo = huawei_utils.get_lun_conf_params(self.xml_file_path)
+        luninfo['pool_id'] = '0'
+        luninfo['volume_size'] = 2
+        luninfo['volume_description'] = 'test volume'
+        luninfo = huawei_utils.init_lun_parameters('5mFHcBv4RkCcD+JyrWc0SA',
+                                                   luninfo)
+        self.assertEqual('5mFHcBv4RkCcD+JyrWc0SA', luninfo['NAME'])
+
+    def tset_get_iscsi_conf(self):
+        self.driver.restclient.login()
+        iscsiinfo = huawei_utils.get_iscsi_conf(self.xml_file_path)
+        self.assertEqual('iqn.1993-08.debian:01:ec2bff7ac3a3',
+                         iscsiinfo['Initiator'])
+
+    def test_check_conf_file(self):
+        self.driver.restclient.login()
+        self.driver.restclient.checkFlag = True
+        huawei_utils.check_conf_file(self.xml_file_path)
+        self.assertTrue(self.driver.restclient.checkFlag)
+
+    def test_get_conf_host_os_type(self):
+        self.driver.restclient.login()
+        host_os = huawei_utils.get_conf_host_os_type('100.97.10.30',
+                                                     self.configuration)
+        self.assertEqual('0', host_os)
+
+    def create_fake_conf_file(self):
+        """Create a fake Config file.
+
+          Huawei storage customize a XML configuration file, the configuration
+          file is used to set the Huawei storage custom parameters, therefore,
+          in the UT test we need to simulate such a configuration file.
+        """
+        doc = minidom.Document()
+
+        config = doc.createElement('config')
+        doc.appendChild(config)
+
+        storage = doc.createElement('Storage')
+        config.appendChild(storage)
+        controllerip0 = doc.createElement('ControllerIP0')
+        controllerip0_text = doc.createTextNode('10.10.10.1')
+        controllerip0.appendChild(controllerip0_text)
+        storage.appendChild(controllerip0)
+        controllerip1 = doc.createElement('ControllerIP1')
+        controllerip1_text = doc.createTextNode('10.10.10.2')
+        controllerip1.appendChild(controllerip1_text)
+        storage.appendChild(controllerip1)
+        username = doc.createElement('UserName')
+        username_text = doc.createTextNode('admin')
+        username.appendChild(username_text)
+        storage.appendChild(username)
+        userpassword = doc.createElement('UserPassword')
+        userpassword_text = doc.createTextNode('Admin@storage')
+        userpassword.appendChild(userpassword_text)
+        storage.appendChild(userpassword)
+        url = doc.createElement('RestURL')
+        url_text = doc.createTextNode('http://100.115.10.69:8082/'
+                                      'deviceManager/rest/')
+        url.appendChild(url_text)
+        storage.appendChild(url)
+
+        storagepool = doc.createElement('StoragePool')
+        pool_text = doc.createTextNode('OpenStack_Pool')
+        storagepool.appendChild(pool_text)
+        storage.appendChild(storagepool)
+
+        lun = doc.createElement('LUN')
+        config.appendChild(lun)
+        storagepool = doc.createElement('StoragePool')
+        pool_text = doc.createTextNode('OpenStack_Pool')
+        storagepool.appendChild(pool_text)
+        lun.appendChild(storagepool)
+
+        timeout = doc.createElement('Timeout')
+        timeout_text = doc.createTextNode('43200')
+        timeout.appendChild(timeout_text)
+        lun.appendChild(timeout)
+
+        lun_ready_wait_interval = doc.createElement('LUNReadyWaitInterval')
+        lun_ready_wait_interval_text = doc.createTextNode('2')
+        lun_ready_wait_interval.appendChild(lun_ready_wait_interval_text)
+        lun.appendChild(lun_ready_wait_interval)
+
+        prefetch = doc.createElement('Prefetch')
+        prefetch.setAttribute('Type', '1')
+        prefetch.setAttribute('Value', '0')
+        lun.appendChild(prefetch)
+
+        iscsi = doc.createElement('iSCSI')
+        config.appendChild(iscsi)
+        defaulttargetip = doc.createElement('DefaultTargetIP')
+        defaulttargetip_text = doc.createTextNode('100.115.10.68')
+        defaulttargetip.appendChild(defaulttargetip_text)
+        iscsi.appendChild(defaulttargetip)
+        initiator = doc.createElement('Initiator')
+        initiator.setAttribute('Name', 'iqn.1993-08.debian:01:ec2bff7ac3a3')
+        initiator.setAttribute('TargetIP', '192.168.100.2')
+        iscsi.appendChild(initiator)
+
+        host = doc.createElement('Host')
+        host.setAttribute('HostIP', '100.97.10.30')
+        host.setAttribute('OSType', 'Linux')
+        config.appendChild(host)
+
+        fakefile = open(self.fake_conf_file, 'w')
+        fakefile.write(doc.toprettyxml(indent=''))
+        fakefile.close()
+
+
+class 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.xml_file_path = self.configuration.cinder_huawei_conf_file
+        self.stubs.Set(time, 'sleep', Fake_sleep)
+        driver = Fake18000FCStorage(configuration=self.configuration)
+        self.driver = driver
+        self.driver.do_setup()
+
+    def test_login_success(self):
+        deviceid = self.driver.restclient.login()
+        self.assertEqual('210235G7J20000000000', deviceid)
+
+    def test_create_volume_success(self):
+        self.driver.restclient.login()
+        lun_info = self.driver.create_volume(test_volume)
+        self.assertEqual('1', lun_info['provider_location'])
+
+    def test_delete_volume_success(self):
+        self.driver.restclient.login()
+        delete_flag = self.driver.delete_volume(test_volume)
+        self.assertTrue(delete_flag)
+
+    def test_create_snapshot_success(self):
+        self.driver.restclient.login()
+        lun_info = self.driver.create_snapshot(test_volume)
+        self.assertEqual(11, lun_info['provider_location'])
+
+    def test_delete_snapshot_success(self):
+        self.driver.restclient.login()
+        delete_flag = self.driver.delete_snapshot(test_snap)
+        self.assertTrue(delete_flag)
+
+    def test_create_volume_from_snapsuccess(self):
+        self.driver.restclient.login()
+        lun_info = self.driver.create_volume_from_snapshot(test_volume,
+                                                           test_volume)
+        self.assertEqual('1', lun_info['provider_location'])
+
+    def test_initialize_connection_success(self):
+        self.driver.restclient.login()
+        iscsi_properties = self.driver.initialize_connection(test_volume,
+                                                             FakeConnector)
+        self.assertEqual(1, iscsi_properties['data']['target_lun'])
+
+    def test_terminate_connection_success(self):
+        self.driver.restclient.login()
+        self.driver.restclient.terminateFlag = True
+        self.driver.terminate_connection(test_volume, FakeConnector)
+        self.assertTrue(self.driver.restclient.terminateFlag)
+
+    def test_get_volume_status(self):
+        self.driver.restclient.login()
+        data = self.driver.get_volume_stats()
+        self.assertEqual('1.1.1', data['driver_version'])
+
+    def test_extend_volume(self):
+        self.driver.restclient.login()
+        lun_info = self.driver.extend_volume(test_volume, 3)
+        self.assertEqual('1', lun_info['provider_location'])
+
+    def test_login_fail(self):
+        self.driver.restclient.test_fail = True
+        self.assertRaises(exception.VolumeBackendAPIException,
+                          self.driver.restclient.login)
+
+    def test_create_snapshot_fail(self):
+        self.driver.restclient.login()
+        self.driver.restclient.test_fail = True
+        self.assertRaises(exception.VolumeBackendAPIException,
+                          self.driver.create_snapshot, test_volume)
+
+    def test_create_volume_fail(self):
+        self.driver.restclient.login()
+        self.driver.restclient.test_fail = True
+        self.assertRaises(exception.VolumeBackendAPIException,
+                          self.driver.create_volume, test_volume)
+
+    def test_delete_volume_fail(self):
+        self.driver.restclient.login()
+        self.driver.restclient.test_fail = True
+        delete_flag = self.driver.delete_volume(test_volume)
+        self.assertTrue(delete_flag)
+
+    def test_delete_snapshot_fail(self):
+        self.driver.restclient.login()
+        self.driver.restclient.test_fail = True
+        delete_flag = self.driver.delete_snapshot(test_snap)
+        self.assertTrue(delete_flag)
+
+    def test_initialize_connection_fail(self):
+        self.driver.restclient.login()
+        self.driver.restclient.test_fail = True
+        self.assertRaises(exception.VolumeBackendAPIException,
+                          self.driver.initialize_connection,
+                          test_volume, FakeConnector)
+
+    def test_get_default_timeout(self):
+        result = huawei_utils._get_default_timeout(self.xml_file_path)
+        self.assertEqual('43200', result)
+
+    def test_get_wait_interval(self):
+        result = huawei_utils.get_wait_interval(self.xml_file_path,
+                                                'LUNReadyWaitInterval')
+        self.assertEqual(2, result)
+
+    def test_lun_is_associated_to_lungroup(self):
+        self.driver.restclient.login()
+        self.driver.restclient._associate_lun_to_lungroup('11', '11')
+        result = self.driver.restclient._is_lun_associated_to_lungroup('11',
+                                                                       '11')
+        self.assertTrue(result)
+
+    def test_lun_is_not_associated_to_lun_group(self):
+        self.driver.restclient.login()
+        self.driver.restclient._associate_lun_to_lungroup('12', '12')
+        self.driver.restclient.remove_lun_from_lungroup('12', '12')
+        result = self.driver.restclient._is_lun_associated_to_lungroup('12',
+                                                                       '12')
+        self.assertFalse(result)
+
+    def test_get_lun_conf_params(self):
+        self.driver.restclient.login()
+        luninfo = huawei_utils.get_lun_conf_params(self.xml_file_path)
+        luninfo['pool_id'] = '0'
+        luninfo['volume_size'] = 2
+        luninfo['volume_description'] = 'test volume'
+        luninfo = huawei_utils.init_lun_parameters('5mFHcBv4RkCcD+JyrWc0SA',
+                                                   luninfo)
+        self.assertEqual('5mFHcBv4RkCcD+JyrWc0SA', luninfo['NAME'])
+
+    def test_check_conf_file(self):
+        self.driver.restclient.login()
+        self.driver.restclient.checkFlag = True
+        huawei_utils.check_conf_file(self.xml_file_path)
+        self.assertTrue(self.driver.restclient.checkFlag)
+
+    def test_get_conf_host_os_type(self):
+        self.driver.restclient.login()
+        host_os = huawei_utils.get_conf_host_os_type('100.97.10.30',
+                                                     self.configuration)
+        self.assertEqual('0', host_os)
+
+    def create_fake_conf_file(self):
+        """Create a fake Config file
+
+        Huawei storage customize a XML configuration file,
+        the configuration file is used to set the Huawei storage custom
+        parameters, therefore, in the UT test we need to simulate such a
+        configuration file
+        """
+        doc = minidom.Document()
+
+        config = doc.createElement('config')
+        doc.appendChild(config)
+
+        storage = doc.createElement('Storage')
+        config.appendChild(storage)
+        controllerip0 = doc.createElement('ControllerIP0')
+        controllerip0_text = doc.createTextNode('10.10.10.1')
+        controllerip0.appendChild(controllerip0_text)
+        storage.appendChild(controllerip0)
+        controllerip1 = doc.createElement('ControllerIP1')
+        controllerip1_text = doc.createTextNode('10.10.10.2')
+        controllerip1.appendChild(controllerip1_text)
+        storage.appendChild(controllerip1)
+        username = doc.createElement('UserName')
+        username_text = doc.createTextNode('admin')
+        username.appendChild(username_text)
+        storage.appendChild(username)
+        userpassword = doc.createElement('UserPassword')
+        userpassword_text = doc.createTextNode('Admin@storage')
+        userpassword.appendChild(userpassword_text)
+        storage.appendChild(userpassword)
+        url = doc.createElement('RestURL')
+        url_text = doc.createTextNode('http://100.115.10.69:8082/'
+                                      'deviceManager/rest/')
+        url.appendChild(url_text)
+        storage.appendChild(url)
+
+        storagepool = doc.createElement('StoragePool')
+        pool_text = doc.createTextNode('OpenStack_Pool')
+        storagepool.appendChild(pool_text)
+        storage.appendChild(storagepool)
+
+        lun = doc.createElement('LUN')
+        config.appendChild(lun)
+        storagepool = doc.createElement('StoragePool')
+        pool_text = doc.createTextNode('OpenStack_Pool')
+        storagepool.appendChild(pool_text)
+        lun.appendChild(storagepool)
+
+        timeout = doc.createElement('Timeout')
+        timeout_text = doc.createTextNode('43200')
+        timeout.appendChild(timeout_text)
+        lun.appendChild(timeout)
+
+        lun_ready_wait_interval = doc.createElement('LUNReadyWaitInterval')
+        lun_ready_wait_interval_text = doc.createTextNode('2')
+        lun_ready_wait_interval.appendChild(lun_ready_wait_interval_text)
+        lun.appendChild(lun_ready_wait_interval)
+
+        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)
+
+        prefetch = doc.createElement('Prefetch')
+        prefetch.setAttribute('Type', '1')
+        prefetch.setAttribute('Value', '0')
+        lun.appendChild(prefetch)
+
+        host = doc.createElement('Host')
+        host.setAttribute('HostIP', '100.97.10.30')
+        host.setAttribute('OSType', 'Linux')
+        config.appendChild(host)
+
+        fakefile = open(self.fake_conf_file, 'w')
+        fakefile.write(doc.toprettyxml(indent=''))
+        fakefile.close()
index 98d6b308eb56d89a12dab50a353a7f17dcea3fd6..3577a3d8b69f6f2d9cba633e909001063666b005 100644 (file)
@@ -21,9 +21,9 @@ from cinder import test
 
 
 CONF = cfg.CONF
-HUAWEI_ISCSI_MODULE = ("cinder.volume.drivers.huawei.huawei_18000."
+HUAWEI_ISCSI_MODULE = ("cinder.volume.drivers.huawei.huawei_driver."
                        "Huawei18000ISCSIDriver")
-HUAWEI_FC_MODULE = ("cinder.volume.drivers.huawei.huawei_18000."
+HUAWEI_FC_MODULE = ("cinder.volume.drivers.huawei.huawei_driver."
                     "Huawei18000FCDriver")
 
 
@@ -47,7 +47,7 @@ class VolumeDriverCompatibility(test.TestCase):
 
     def test_huawei_driver_iscsi_old(self):
         self._load_driver(
-            'cinder.volume.drivers.huawei.huawei_hvs.HuaweiHVSISCSIDriver')
+            'cinder.volume.drivers.huawei.huawei_18000.Huawei18000ISCSIDriver')
         self.assertEqual(self._driver_module_name(), HUAWEI_ISCSI_MODULE)
 
     def test_huawei_driver_iscsi_new(self):
@@ -56,7 +56,7 @@ class VolumeDriverCompatibility(test.TestCase):
 
     def test_huawei_driver_fc_old(self):
         self._load_driver(
-            'cinder.volume.drivers.huawei.huawei_hvs.HuaweiHVSFCDriver')
+            'cinder.volume.drivers.huawei.huawei_18000.Huawei18000FCDriver')
         self.assertEqual(self._driver_module_name(), HUAWEI_FC_MODULE)
 
     def test_huawei_driver_fc_new(self):
index b2e501d7a627f790a2a72e62cdad0161de201961..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 (file)
@@ -1,108 +0,0 @@
-# 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.
-"""
-Provide a unified driver class for users.
-
-The product type and the protocol should be specified in config file before.
-"""
-
-from oslo_config import cfg
-from oslo_log import log as logging
-import six
-
-from cinder import exception
-from cinder.i18n import _, _LI, _LW
-from cinder.volume.drivers.huawei import huawei_18000
-from cinder.volume.drivers.huawei import huawei_utils
-
-
-LOG = logging.getLogger(__name__)
-
-huawei_opt = [
-    cfg.StrOpt('cinder_huawei_conf_file',
-               default='/etc/cinder/cinder_huawei_conf.xml',
-               help='The configuration file for the Cinder Huawei '
-                    'driver')]
-
-CONF = cfg.CONF
-CONF.register_opts(huawei_opt)
-MAPPING = {'HVS': '18000'}
-
-
-class HuaweiVolumeDriver(object):
-    """Define an unified driver for Huawei drivers."""
-
-    def __init__(self, *args, **kwargs):
-        super(HuaweiVolumeDriver, self).__init__()
-        self._product = {'18000': huawei_18000, 'HVS': huawei_18000}
-        self._protocol = {'iSCSI': 'ISCSIDriver', 'FC': 'FCDriver'}
-
-        self.driver = self._instantiate_driver(*args, **kwargs)
-
-    def _instantiate_driver(self, *args, **kwargs):
-        """Instantiate the specified driver."""
-        self.configuration = kwargs.get('configuration', None)
-        if not self.configuration:
-            msg = (_('_instantiate_driver: configuration not found.'))
-            raise exception.InvalidInput(reason=msg)
-
-        self.configuration.append_config_values(huawei_opt)
-        conf_file = self.configuration.cinder_huawei_conf_file
-        (product, protocol) = self._get_conf_info(conf_file)
-
-        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.warning(_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]
-
-        driver_class = getattr(driver_module, driver_class)
-        return driver_class(*args, **kwargs)
-
-    def _get_conf_info(self, filename):
-        """Get product type and connection protocol from config file."""
-        root = huawei_utils.parse_xml_file(filename)
-        product = root.findtext('Storage/Product').strip()
-        protocol = root.findtext('Storage/Protocol').strip()
-        if (product in self._product.keys() and
-                protocol in self._protocol.keys()):
-            return (product, protocol)
-        else:
-            msg = (_('"Product" or "Protocol" is illegal. "Product" should be '
-                     'set to 18000. "Protocol" should be set to either iSCSI '
-                     'or FC. Product: %(product)s Protocol: %(protocol)s')
-                   % {'product': six.text_type(product),
-                      'protocol': six.text_type(protocol)})
-            raise exception.InvalidInput(reason=msg)
-
-    def __setattr__(self, name, value):
-        """Set the attribute."""
-        if getattr(self, 'driver', None):
-            self.driver.__setattr__(name, value)
-            return
-        object.__setattr__(self, name, value)
-
-    def __getattr__(self, name):
-        """"Get the attribute."""
-        drver = object.__getattribute__(self, 'driver')
-        return getattr(drver, name)
diff --git a/cinder/volume/drivers/huawei/constants.py b/cinder/volume/drivers/huawei/constants.py
new file mode 100644 (file)
index 0000000..20f754a
--- /dev/null
@@ -0,0 +1,41 @@
+# Copyright (c) 2015 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.
+
+STATUS_HEALTH = '1'
+STATUS_RUNNING = '10'
+
+HOSTGROUP_PREFIX = 'OpenStack_HostGroup_'
+LUNGROUP_PREFIX = 'OpenStack_LunGroup_'
+MAPPING_VIEW_PREFIX = 'OpenStack_Mapping_View_'
+QOS_NAME_PREFIX = 'OpenStack_'
+
+DEFAULT_WAIT_TIMEOUT = 3600 * 24 * 30
+DEFAULT_WAIT_INTERVAL = 5
+ERROR_CONNECT_TO_SERVER = -403
+ERROR_UNAUTHORIZED_TO_SERVER = -401
+
+MAX_HOSTNAME_LENTH = 31
+
+OS_TYPE = {'Linux': '0',
+           'Windows': '1',
+           'Solaris': '2',
+           'HP-UX': '3',
+           'AIX': '4',
+           'XenServer': '5',
+           'Mac OS X': '6',
+           'VMware ESX': '7'}
+
+HUAWEI_VALID_KEYS = ['maxIOPS', 'minIOPS', 'minBandWidth',
+                     'maxBandWidth', 'latency', 'IOType']
diff --git a/cinder/volume/drivers/huawei/huawei_18000.py b/cinder/volume/drivers/huawei/huawei_18000.py
deleted file mode 100644 (file)
index c306fec..0000000
+++ /dev/null
@@ -1,199 +0,0 @@
-# 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.
-"""
-Volume Drivers for Huawei OceanStor 18000 storage arrays.
-"""
-
-from cinder.volume import driver
-from cinder.volume.drivers.huawei import rest_common
-from cinder.zonemanager import utils as fczm_utils
-
-
-class Huawei18000ISCSIDriver(driver.ISCSIDriver):
-    """ISCSI 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.1.0"
-
-    def __init__(self, *args, **kwargs):
-        super(Huawei18000ISCSIDriver, self).__init__(*args, **kwargs)
-
-    def do_setup(self, context):
-        """Instantiate common class and log in storage system."""
-        self.common = rest_common.RestCommon(configuration=self.configuration)
-        return self.common.login()
-
-    def check_for_setup_error(self):
-        """Check configuration file."""
-        return self.common._check_conf_file()
-
-    def create_volume(self, volume):
-        """Create a 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."""
-        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."""
-        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."""
-        return self.common.extend_volume(volume, new_size)
-
-    def delete_volume(self, volume):
-        """Delete a volume."""
-        return self.common.delete_volume(volume)
-
-    def create_snapshot(self, snapshot):
-        """Create a 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."""
-        return self.common.delete_snapshot(snapshot)
-
-    def get_volume_stats(self, refresh=False):
-        """Get volume stats."""
-        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'
-        data['driver_version'] = self.VERSION
-        return data
-
-    def initialize_connection(self, volume, connector):
-        """Map a volume to a host."""
-        return self.common.initialize_connection_iscsi(volume, connector)
-
-    def terminate_connection(self, volume, connector, **kwargs):
-        """Terminate the map."""
-        self.common.terminate_connection_iscsi(volume, connector)
-
-    def create_export(self, context, volume):
-        """Export the volume."""
-        pass
-
-    def ensure_export(self, context, volume):
-        """Synchronously recreate an export for a volume."""
-        pass
-
-    def remove_export(self, context, volume):
-        """Remove an export for a volume."""
-        pass
-
-
-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.1.0"
-
-    def __init__(self, *args, **kwargs):
-        super(Huawei18000FCDriver, self).__init__(*args, **kwargs)
-
-    def do_setup(self, context):
-        """Instantiate common class and log in storage system."""
-        self.common = rest_common.RestCommon(configuration=self.configuration)
-        return self.common.login()
-
-    def check_for_setup_error(self):
-        """Check configuration file."""
-        return self.common._check_conf_file()
-
-    def create_volume(self, volume):
-        """Create a 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."""
-        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."""
-        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."""
-        return self.common.extend_volume(volume, new_size)
-
-    def delete_volume(self, volume):
-        """Delete a volume."""
-        return self.common.delete_volume(volume)
-
-    def create_snapshot(self, snapshot):
-        """Create a 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."""
-        return self.common.delete_snapshot(snapshot)
-
-    def get_volume_stats(self, refresh=False):
-        """Get volume stats."""
-        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'
-        data['driver_version'] = self.VERSION
-        return data
-
-    @fczm_utils.AddFCZone
-    def initialize_connection(self, volume, connector):
-        """Map a volume to a host."""
-        return self.common.initialize_connection_fc(volume, connector)
-
-    @fczm_utils.RemoveFCZone
-    def terminate_connection(self, volume, connector, **kwargs):
-        """Terminate the map."""
-        return self.common.terminate_connection_fc(volume, connector)
-
-    def create_export(self, context, volume):
-        """Export the volume."""
-        pass
-
-    def ensure_export(self, context, volume):
-        """Synchronously recreate an export for a volume."""
-        pass
-
-    def remove_export(self, context, volume):
-        """Remove an export for a volume."""
-        pass
diff --git a/cinder/volume/drivers/huawei/huawei_driver.py b/cinder/volume/drivers/huawei/huawei_driver.py
new file mode 100644 (file)
index 0000000..8798927
--- /dev/null
@@ -0,0 +1,633 @@
+# Copyright (c) 2015 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.
+
+import uuid
+
+from oslo_config import cfg
+from oslo_log import log as logging
+from oslo_utils import excutils
+from oslo_utils import units
+
+from cinder import exception
+from cinder.i18n import _, _LI, _LW
+from cinder import utils
+from cinder.volume import driver
+from cinder.volume.drivers.huawei import constants
+from cinder.volume.drivers.huawei import huawei_utils
+from cinder.volume.drivers.huawei import rest_client
+from cinder.zonemanager import utils as fczm_utils
+
+LOG = logging.getLogger(__name__)
+
+huawei_opt = [
+    cfg.StrOpt('cinder_huawei_conf_file',
+               default='/etc/cinder/cinder_huawei_conf.xml',
+               help='The configuration file for the Cinder Huawei '
+                    'driver.')]
+
+CONF = cfg.CONF
+CONF.register_opts(huawei_opt)
+
+
+class HuaweiBaseDriver(driver.VolumeDriver):
+
+    def __init__(self, *args, **kwargs):
+        super(HuaweiBaseDriver, self).__init__(*args, **kwargs)
+        self.configuration = kwargs.get('configuration', None)
+        if not self.configuration:
+            msg = _('_instantiate_driver: configuration not found.')
+            raise exception.InvalidInput(reason=msg)
+
+        self.configuration.append_config_values(huawei_opt)
+        self.xml_file_path = self.configuration.cinder_huawei_conf_file
+
+    def do_setup(self, context):
+        """Instantiate common class and login storage system."""
+        self.restclient = rest_client.RestClient(self.configuration)
+        return self.restclient.login()
+
+    def check_for_setup_error(self):
+        """Check configuration file."""
+        return huawei_utils.check_conf_file(self.xml_file_path)
+
+    def get_volume_stats(self, refresh=False):
+        """Get volume status."""
+        return self.restclient.update_volume_stats()
+
+    @utils.synchronized('huawei', external=True)
+    def create_volume(self, volume):
+        """Create a volume."""
+        pool_info = self.restclient.find_pool_info()
+        volume_name = huawei_utils.encode_name(volume['id'])
+        volume_description = volume['name']
+        volume_size = huawei_utils.get_volume_size(volume)
+
+        LOG.info(_LI(
+            'Create volume: %(volume)s, size: %(size)s.'),
+            {'volume': volume_name,
+             'size': volume_size})
+
+        params = huawei_utils.get_lun_conf_params(self.xml_file_path)
+        params['pool_id'] = pool_info['ID']
+        params['volume_size'] = volume_size
+        params['volume_description'] = volume_description
+
+        # Prepare LUN parameters.
+        lun_param = huawei_utils.init_lun_parameters(volume_name, params)
+
+        # Create LUN on the array.
+        lun_info = self.restclient.create_volume(lun_param)
+        lun_id = lun_info['ID']
+
+        return {'provider_location': lun_info['ID'],
+                'ID': lun_id,
+                'lun_info': lun_info}
+
+    @utils.synchronized('huawei', external=True)
+    def delete_volume(self, volume):
+        """Delete a volume.
+
+        Three steps:
+        Firstly, remove associate from lungroup.
+        Secondly, remove associate from QoS policy.
+        Thirdly, remove the lun.
+        """
+        name = huawei_utils.encode_name(volume['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.restclient.check_lun_exist(lun_id):
+                self.restclient.delete_lun(lun_id)
+        else:
+            LOG.warning(_LW("Can't find %s on the array."), lun_id)
+            return False
+
+        return True
+
+    def create_volume_from_snapshot(self, volume, snapshot):
+        """Create a volume from a snapshot.
+
+        We use LUNcopy to copy a new volume from snapshot.
+        The time needed increases as volume size does.
+        """
+        snapshotname = huawei_utils.encode_name(snapshot['id'])
+
+        snapshot_id = snapshot.get('provider_location', None)
+        if snapshot_id is None:
+            snapshot_id = self.restclient.get_snapshotid_by_name(snapshotname)
+            if snapshot_id is None:
+                err_msg = (_(
+                    'create_volume_from_snapshot: Snapshot %(name)s '
+                    'does not exist.')
+                    % {'name': snapshotname})
+                LOG.error(err_msg)
+                raise exception.VolumeBackendAPIException(data=err_msg)
+
+        lun_info = self.create_volume(volume)
+
+        tgt_lun_id = lun_info['ID']
+        luncopy_name = huawei_utils.encode_name(volume['id'])
+
+        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})
+
+        event_type = 'LUNReadyWaitInterval'
+
+        wait_interval = huawei_utils.get_wait_interval(self.xml_file_path,
+                                                       event_type)
+
+        def _volume_ready():
+            result = self.restclient.get_lun_info(tgt_lun_id)
+
+            if result['HEALTHSTATUS'] == "1":
+                if result['RUNNINGSTATUS'] == "27":
+                    return True
+            return False
+
+        huawei_utils.wait_for_condition(self.xml_file_path,
+                                        _volume_ready,
+                                        wait_interval,
+                                        wait_interval * 10)
+
+        self._copy_volume(volume, luncopy_name,
+                          snapshot_id, tgt_lun_id)
+
+        return {'provider_location': lun_info['ID'],
+                'lun_info': lun_info}
+
+    def create_cloned_volume(self, volume, src_vref):
+        """Clone a new volume from an existing volume."""
+        # 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.VolumeBackendAPIException:
+                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 {'provider_location': lun_info['ID'],
+                'lun_info': lun_info}
+
+    @utils.synchronized('huawei', external=True)
+    def extend_volume(self, volume, new_size):
+        """Extend a volume."""
+        volume_size = huawei_utils.get_volume_size(volume)
+        new_volume_size = int(new_size) * units.Gi / 512
+        volume_name = huawei_utils.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.restclient.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.VolumeBackendAPIException(data=msg)
+
+        luninfo = self.restclient.extend_volume(lun_id, new_volume_size)
+
+        return {'provider_location': luninfo['ID'],
+                'lun_info': luninfo}
+
+    @utils.synchronized('huawei', external=True)
+    def create_snapshot(self, snapshot):
+        snapshot_info = self.restclient.create_snapshot(snapshot)
+        snapshot_id = snapshot_info['ID']
+        self.restclient.active_snapshot(snapshot_id)
+
+        return {'provider_location': snapshot_info['ID'],
+                'lun_info': snapshot_info}
+
+    @utils.synchronized('huawei', external=True)
+    def delete_snapshot(self, snapshot):
+        snapshotname = huawei_utils.encode_name(snapshot['id'])
+        volume_name = huawei_utils.encode_name(snapshot['volume_id'])
+
+        LOG.info(_LI(
+            'stop_snapshot: snapshot name: %(snapshot)s, '
+            'volume name: %(volume)s.'),
+            {'snapshot': snapshotname,
+             'volume': volume_name},)
+
+        snapshot_id = snapshot.get('provider_location', None)
+        if snapshot_id is None:
+            snapshot_id = self.restclient.get_snapshotid_by_name(snapshotname)
+
+        if snapshot_id is not None:
+            if self.restclient.check_snapshot_exist(snapshot_id):
+                self.restclient.stop_snapshot(snapshot_id)
+                self.restclient.delete_snapshot(snapshot_id)
+            else:
+                LOG.warning(_LW("Can't find snapshot on the array."))
+        else:
+            LOG.warning(_LW("Can't find snapshot on the array."))
+            return False
+
+        return True
+
+    @utils.synchronized('huawei', external=True)
+    def initialize_connection_fc(self, volume, connector):
+        wwns = connector['wwpns']
+        volume_name = huawei_utils.encode_name(volume['id'])
+
+        LOG.info(_LI(
+            'initialize_connection_fc, initiator: %(wwpns)s,'
+            ' volume name: %(volume)s.'),
+            {'wwpns': wwns,
+             'volume': volume_name},)
+
+        host_name_before_hash = None
+        host_name = connector['host']
+        if host_name and (len(host_name) > constants.MAX_HOSTNAME_LENTH):
+            host_name_before_hash = host_name
+            host_name = str(hash(host_name))
+
+        # Create hostgroup if not exist.
+        host_id = self.restclient.add_host_with_check(host_name,
+                                                      host_name_before_hash)
+
+        # Add host into hostgroup.
+        hostgroup_id = self.restclient.add_host_into_hostgroup(host_id)
+
+        free_wwns = self.restclient.get_connected_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.restclient.add_fc_port_to_host(host_id, wwn)
+
+        lun_id = self.restclient.mapping_hostgroup_and_lungroup(volume_name,
+                                                                hostgroup_id,
+                                                                host_id)
+        host_lun_id = self.restclient.find_host_lun_id(host_id, lun_id)
+
+        tgt_port_wwns = []
+        for wwn in wwns:
+            tgtwwpns = self.restclient.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
+
+        # Return FC 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}, }
+
+        LOG.info(_LI("initialize_connection_fc, return data is: %s."),
+                 info)
+
+        return info
+
+    @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 = huawei_utils.encode_name(volume['id'])
+
+        LOG.info(_LI(
+            'initiator name: %(initiator_name)s, '
+            'volume name: %(volume)s.'),
+            {'initiator_name': initiator_name,
+             'volume': volume_name})
+
+        (iscsi_iqn,
+         target_ip,
+         portgroup_id) = self.restclient.get_iscsi_params(self.xml_file_path,
+                                                          connector)
+        LOG.info(_LI('initialize_connection_iscsi, iscsi_iqn: %(iscsi_iqn)s, '
+                     'target_ip: %(target_ip)s, '
+                     'TargetPortGroup: %(portgroup_id)s.'),
+                 {'iscsi_iqn': iscsi_iqn,
+                  'target_ip': target_ip,
+                  'portgroup_id': portgroup_id},)
+
+        # Create hostgroup if not exist.
+        host_name = connector['host']
+        host_name_before_hash = None
+        if host_name and (len(host_name) > constants.MAX_HOSTNAME_LENTH):
+            host_name_before_hash = host_name
+            host_name = str(hash(host_name))
+        host_id = self.restclient.add_host_with_check(host_name,
+                                                      host_name_before_hash)
+
+        # Add initiator to the host.
+        self.restclient.ensure_initiator_added(initiator_name, host_id)
+        hostgroup_id = self.restclient.add_host_into_hostgroup(host_id)
+
+        # Mapping lungroup and hostgroup to view.
+        lun_id = self.restclient.mapping_hostgroup_and_lungroup(volume_name,
+                                                                hostgroup_id,
+                                                                host_id,
+                                                                portgroup_id)
+
+        hostlun_id = self.restclient.find_host_lun_id(host_id, lun_id)
+
+        LOG.info(_LI("initialize_connection_iscsi, host lun id is: %s."),
+                 hostlun_id)
+
+        # Return iSCSI properties.
+        properties = {}
+        properties['target_discovered'] = False
+        properties['target_portal'] = ('%s:%s' % (target_ip, '3260'))
+        properties['target_iqn'] = iscsi_iqn
+        properties['target_lun'] = int(hostlun_id)
+        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 terminate_connection_iscsi(self, volume, connector):
+        """Delete map between a volume and a host."""
+        initiator_name = connector['initiator']
+        volume_name = huawei_utils.encode_name(volume['id'])
+        lun_id = volume.get('provider_location', None)
+        host_name = connector['host']
+
+        LOG.info(_LI(
+            'terminate_connection_iscsi: volume name: %(volume)s, '
+            'initiator name: %(ini)s, '
+            'lun_id: %(lunid)s.'),
+            {'volume': volume_name,
+             'ini': initiator_name,
+             'lunid': lun_id},)
+
+        iscsi_conf = huawei_utils.get_iscsi_conf(self.xml_file_path)
+        portgroup = None
+        portgroup_id = None
+        left_lunnum = -1
+        for ini in iscsi_conf['Initiator']:
+            if ini['Name'] == initiator_name:
+                for key in ini:
+                    if key == 'TargetPortGroup':
+                        portgroup = ini['TargetPortGroup']
+                        break
+        # Remove lun from lungroup.
+        if lun_id:
+            if self.restclient.check_lun_exist(lun_id):
+                # Get lungroup id by lun id.
+                lungroup_id = self.restclient.get_lungroupid_by_lunid(lun_id)
+                if lungroup_id:
+                    self.restclient.remove_lun_from_lungroup(lungroup_id,
+                                                             lun_id)
+            else:
+                LOG.warning(_LW("Can't find lun on the array."))
+        # Remove portgroup from mapping view if no lun left in lungroup.
+        if portgroup:
+            portgroup_id = self.restclient.find_tgt_port_group(portgroup)
+        host_id = self.restclient.find_host(host_name)
+        if host_id:
+            mapping_view_name = constants.MAPPING_VIEW_PREFIX + host_id
+            view_id = self.restclient.find_mapping_view(mapping_view_name)
+            if view_id:
+                lungroup_id = self.restclient.find_lungroup_from_map(view_id)
+        if lungroup_id:
+            left_lunnum = self.restclient.get_lunnum_from_lungroup(lungroup_id)
+
+        if portgroup_id and view_id and (int(left_lunnum) <= 0):
+            if self.restclient.is_portgroup_associated_to_view(view_id,
+                                                               portgroup_id):
+                self.restclient.delete_portgroup_mapping_view(view_id,
+                                                              portgroup_id)
+        if view_id and (int(left_lunnum) <= 0):
+            if self.restclient.lungroup_associated(view_id, lungroup_id):
+                self.restclient.delete_lungroup_mapping_view(view_id,
+                                                             lungroup_id)
+            self.restclient.delete_lungroup(lungroup_id)
+            if self.restclient.is_initiator_associated_to_host(initiator_name):
+                self.restclient.remove_iscsi_from_host(initiator_name)
+            hostgroup_name = constants.HOSTGROUP_PREFIX + host_id
+            hostgroup_id = self.restclient.find_hostgroup(hostgroup_name)
+            if hostgroup_id:
+                if self.restclient.hostgroup_associated(view_id, hostgroup_id):
+                    self.restclient.delete_hostgoup_mapping_view(view_id,
+                                                                 hostgroup_id)
+                self.restclient.remove_host_from_hostgroup(hostgroup_id,
+                                                           host_id)
+                self.restclient.delete_hostgroup(hostgroup_id)
+            self.restclient.remove_host(host_id)
+            self.restclient.delete_mapping_view(view_id)
+
+    def terminate_connection_fc(self, volume, connector):
+        """Delete map between a volume and a host."""
+        wwns = connector['wwpns']
+        volume_name = huawei_utils.encode_name(volume['id'])
+        lun_id = volume.get('provider_location', None)
+        host_name = connector['host']
+        left_lunnum = -1
+
+        LOG.info(_LI('terminate_connection_fc: volume name: %(volume)s, '
+                     'wwpns: %(wwns)s, '
+                     'lun_id: %(lunid)s.'),
+                 {'volume': volume_name,
+                  'wwns': wwns,
+                  'lunid': lun_id},)
+        if lun_id:
+            if self.restclient.check_lun_exist(lun_id):
+                # Get lungroup id by lun id.
+                lungroup_id = self.restclient.get_lungroupid_by_lunid(lun_id)
+                if not lungroup_id:
+                    LOG.info(_LI("Can't find lun in lungroup."))
+                else:
+                    self.restclient.remove_lun_from_lungroup(lungroup_id,
+                                                             lun_id)
+            else:
+                LOG.warning(_LW("Can't find lun on the array."))
+        tgt_port_wwns = []
+        for wwn in wwns:
+            tgtwwpns = self.restclient.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
+        host_id = self.restclient.find_host(host_name)
+        if host_id:
+            mapping_view_name = constants.MAPPING_VIEW_PREFIX + host_id
+            view_id = self.restclient.find_mapping_view(mapping_view_name)
+            if view_id:
+                lungroup_id = self.restclient.find_lungroup_from_map(view_id)
+        if lungroup_id:
+            left_lunnum = self.restclient.get_lunnum_from_lungroup(lungroup_id)
+        if int(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 migrate_volume(self, context, volume, host):
+        return (False, None)
+
+    def create_export(self, context, volume):
+        """Export a volume."""
+        pass
+
+    def ensure_export(self, context, volume):
+        """Synchronously recreate an export for a volume."""
+        pass
+
+    def remove_export(self, context, volume):
+        """Remove an export for a volume."""
+        pass
+
+    def _copy_volume(self, volume, copy_name, src_lun, tgt_lun):
+        luncopy_id = self.restclient.create_luncopy(copy_name,
+                                                    src_lun, tgt_lun)
+        event_type = 'LUNcopyWaitInterval'
+        wait_interval = huawei_utils.get_wait_interval(self.xml_file_path,
+                                                       event_type)
+
+        try:
+            self.restclient.start_luncopy(luncopy_id)
+
+            def _luncopy_complete():
+                luncopy_info = self.restclient.get_luncopy_info(luncopy_id)
+                if luncopy_info['status'] == '40':
+                    # luncopy_info['status'] means for the running status of
+                    # the luncopy. If luncopy_info['status'] is equal to '40',
+                    # this luncopy is completely ready.
+                    return True
+                elif luncopy_info['state'] != '1':
+                    # luncopy_info['state'] means for the healthy status of the
+                    # luncopy. If luncopy_info['state'] is not equal to '1',
+                    # this means that an error occurred during the LUNcopy
+                    # operation and we should abort it.
+                    err_msg = (_(
+                        'An error occurred during the LUNcopy operation. '
+                        'LUNcopy name: %(luncopyname)s. '
+                        'LUNcopy status: %(luncopystatus)s. '
+                        'LUNcopy state: %(luncopystate)s.')
+                        % {'luncopyname': luncopy_id,
+                           'luncopystatus': luncopy_info['status'],
+                           'luncopystate': luncopy_info['state']},)
+                    LOG.error(err_msg)
+                    raise exception.VolumeBackendAPIException(data=err_msg)
+            huawei_utils.wait_for_condition(self.xml_file_path,
+                                            _luncopy_complete,
+                                            wait_interval)
+
+        except Exception:
+            with excutils.save_and_reraise_exception():
+                self.restclient.delete_luncopy(luncopy_id)
+                self.delete_volume(volume)
+
+        self.restclient.delete_luncopy(luncopy_id)
+
+
+class Huawei18000ISCSIDriver(HuaweiBaseDriver, driver.ISCSIDriver):
+    """ISCSI 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.1.1"
+
+    def __init__(self, *args, **kwargs):
+        super(Huawei18000ISCSIDriver, self).__init__(*args, **kwargs)
+
+    def get_volume_stats(self, refresh=False):
+        """Get volume status."""
+        data = HuaweiBaseDriver.get_volume_stats(self, refresh=False)
+        backend_name = self.configuration.safe_get('volume_backend_name')
+        data['volume_backend_name'] = backend_name or self.__class__.__name__
+        data['storage_protocol'] = 'iSCSI'
+        data['driver_version'] = self.VERSION
+        data['vendor_name'] = 'Huawei'
+        return data
+
+    def initialize_connection(self, volume, connector):
+        return HuaweiBaseDriver.initialize_connection_iscsi(self,
+                                                            volume,
+                                                            connector)
+
+    def terminate_connection(self, volume, connector, **kwargs):
+        return HuaweiBaseDriver.terminate_connection_iscsi(self,
+                                                           volume,
+                                                           connector)
+
+
+class Huawei18000FCDriver(HuaweiBaseDriver, 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.1.1"
+
+    def __init__(self, *args, **kwargs):
+        super(Huawei18000FCDriver, self).__init__(*args, **kwargs)
+
+    def get_volume_stats(self, refresh=False):
+        """Get volume status."""
+        data = HuaweiBaseDriver.get_volume_stats(self, refresh=False)
+        backend_name = self.configuration.safe_get('volume_backend_name')
+        data['volume_backend_name'] = backend_name or self.__class__.__name__
+        data['storage_protocol'] = 'FC'
+        data['driver_version'] = self.VERSION
+        data['verdor_name'] = 'Huawei'
+        return data
+
+    @fczm_utils.AddFCZone
+    def initialize_connection(self, volume, connector):
+        return HuaweiBaseDriver.initialize_connection_fc(self,
+                                                         volume,
+                                                         connector)
+
+    @fczm_utils.RemoveFCZone
+    def terminate_connection(self, volume, connector, **kwargs):
+        return HuaweiBaseDriver.terminate_connection_fc(self,
+                                                        volume,
+                                                        connector)
index 02b35d094a4e9768d46199a9b9090f5044f3c29b..ae13da95326d8905a68d2f2a192e42dc8040698e 100644 (file)
@@ -1,5 +1,4 @@
-# Copyright (c) 2013 Huawei Technologies Co., Ltd.
-# Copyright (c) 2012 OpenStack LLC.
+# Copyright (c) 2015 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.
 
+import base64
+import six
+import time
+import uuid
 from xml.etree import ElementTree as ET
 
 from oslo_log import log as logging
+from oslo_service import loopingcall
+from oslo_utils import units
 
-from cinder.i18n import _LE
+from cinder import context
+from cinder import exception
+from cinder import utils
+from cinder.i18n import _, _LE, _LI
+from cinder.volume.drivers.huawei import constants
+from cinder.volume import qos_specs
+from cinder.volume import volume_types
 
 LOG = logging.getLogger(__name__)
 
-os_type = {'Linux': '0',
-           'Windows': '1',
-           'Solaris': '2',
-           'HP-UX': '3',
-           'AIX': '4',
-           'XenServer': '5',
-           'Mac OS X': '6',
-           'VMware ESX': '7'}
 
+opts_capability = {
+    'smarttier': False,
+    'smartcache': False,
+    'smartpartition': False,
+    'thin_provisioning': False,
+    'thick_provisioning': False,
+}
 
-def parse_xml_file(filepath):
+
+opts_value = {
+    'policy': None,
+    'partitionname': None,
+    'cachename': None,
+}
+
+
+opts_associate = {
+    'smarttier': 'policy',
+    'smartcache': 'cachename',
+    'smartpartition': 'partitionname',
+}
+
+
+def get_volume_params(volume):
+    opts = {}
+    ctxt = context.get_admin_context()
+    type_id = volume['volume_type_id']
+    if type_id is not None:
+        volume_type = volume_types.get_volume_type(ctxt, type_id)
+        specs = dict(volume_type).get('extra_specs')
+        opts = _get_extra_spec_value(specs)
+    else:
+        opts.update(opts_capability)
+        opts.update(opts_value)
+
+    return opts
+
+
+def _get_extra_spec_value(specs):
+    """Return the parameters for creating the volume."""
+    opts = {}
+    opts.update(opts_capability)
+    opts.update(opts_value)
+
+    opts = _get_opts_from_specs(opts_capability, opts_value, specs)
+    LOG.debug('get_volume_params opts %(opts)s.', {'opts': opts})
+
+    return opts
+
+
+def _get_opts_from_specs(opts_capability, opts_value, specs):
+    opts = {}
+    opts.update(opts_capability)
+    opts.update(opts_value)
+
+    for key, value in specs.iteritems():
+
+        # Get the scope, if is using scope format.
+        scope = None
+        key_split = key.split(':')
+        if len(key_split) > 2 and key_split[0] != "capabilities":
+            continue
+
+        if len(key_split) == 1:
+            key = key_split[0]
+        else:
+            scope = key_split[0]
+            key = key_split[1]
+
+        if scope:
+            scope = scope.lower()
+        if key:
+            key = key.lower()
+
+        if (scope in opts_capability) and (key in opts_value):
+            if (scope in opts_associate) and (opts_associate[scope] == key):
+                opts[key] = value
+
+    return opts
+
+
+def _get_smartx_specs_params(lunsetinfo, smartx_opts):
+    """Get parameters from config file for creating lun."""
+    # Default lun set information.
+    if 'LUNType' in smartx_opts:
+        lunsetinfo['LUNType'] = smartx_opts['LUNType']
+    lunsetinfo['policy'] = smartx_opts['policy']
+
+    return lunsetinfo
+
+
+def get_lun_params(xml_file_path, smartx_opts):
+    lunsetinfo = {}
+    lunsetinfo = get_lun_conf_params(xml_file_path)
+    lunsetinfo = _get_smartx_specs_params(lunsetinfo, smartx_opts)
+    return lunsetinfo
+
+
+def parse_xml_file(xml_file_path):
     """Get root of xml file."""
     try:
-        tree = ET.parse(filepath)
+        tree = ET.parse(xml_file_path)
         root = tree.getroot()
         return root
     except IOError as err:
-        LOG.error(_LE('parse_xml_file: %s'), err)
+        LOG.error(_LE('parse_xml_file: %s.'), err)
         raise
 
 
@@ -64,49 +164,7 @@ def get_xml_item(xml_root, item):
     return items_list
 
 
-def is_xml_item_exist(xml_root, item, attrib_key=None):
-    """Check if the given item exits in xml config file.
-
-    :param xml_root: The root of xml tree
-    :param item: The xml tag to check
-    :param attrib_key: The xml attrib to check
-    :return: True of False
-    """
-    items_list = get_xml_item(xml_root, item)
-    if attrib_key:
-        for tmp_dict in items_list:
-            if tmp_dict['attrib'].get(attrib_key, None):
-                return True
-    else:
-        if items_list and items_list[0]['text']:
-            return True
-    return False
-
-
-def is_xml_item_valid(xml_root, item, valid_list, attrib_key=None):
-    """Check if the given item is valid in xml config file.
-
-    :param xml_root: The root of xml tree
-    :param item: The xml tag to check
-    :param valid_list: The valid item value
-    :param attrib_key: The xml attrib to check
-    :return: True of False
-    """
-    items_list = get_xml_item(xml_root, item)
-    if attrib_key:
-        for tmp_dict in items_list:
-            value = tmp_dict['attrib'].get(attrib_key, None)
-            if value not in valid_list:
-                return False
-    else:
-        value = items_list[0]['text']
-        if value not in valid_list:
-            return False
-
-    return True
-
-
-def get_conf_host_os_type(host_ip, config):
+def get_conf_host_os_type(host_ip, conf):
     """Get host OS type from xml config file.
 
     :param host_ip: The IP of Nova host
@@ -114,7 +172,8 @@ def get_conf_host_os_type(host_ip, config):
     :return: host OS type
     """
     os_conf = {}
-    root = parse_xml_file(config)
+    xml_file_path = conf.cinder_huawei_conf_file
+    root = parse_xml_file(xml_file_path)
     hosts_list = get_xml_item(root, 'Host')
     for host in hosts_list:
         os = host['attrib']['OSType'].strip()
@@ -123,11 +182,302 @@ def get_conf_host_os_type(host_ip, config):
     host_os = None
     for k, v in os_conf.items():
         if host_ip in v:
-            host_os = os_type.get(k, None)
+            host_os = constants.OS_TYPE.get(k, None)
     if not host_os:
-        host_os = os_type['Linux']  # default os type
+        host_os = constants.OS_TYPE['Linux']  # Default OS type.
 
     LOG.debug('_get_host_os_type: Host %(ip)s OS type is %(os)s.',
               {'ip': host_ip, 'os': host_os})
 
     return host_os
+
+
+def get_qos_by_volume_type(volume_type):
+    qos = {}
+    qos_specs_id = volume_type.get('qos_specs_id')
+
+    # We prefer the qos_specs association
+    # and override any existing extra-specs settings
+    # if present.
+    if qos_specs_id is not None:
+        kvs = qos_specs.get_qos_specs(context.get_admin_context(),
+                                      qos_specs_id)['specs']
+    else:
+        return qos
+
+    LOG.info(_LI('The QoS sepcs is: %s.'), kvs)
+    for key, value in kvs.iteritems():
+        if key in constants.HUAWEI_VALID_KEYS:
+            if (key.upper() != 'IOTYPE') and (int(value) <= 0):
+                err_msg = (_('Qos config is wrong. %(key)s'
+                             ' must be set greater than 0.')
+                           % {'key': key})
+                LOG.error(err_msg)
+                raise exception.VolumeBackendAPIException(data=err_msg)
+            elif (key.upper() == 'IOTYPE') and (value not in ['0', '1', '2']):
+                raise exception.InvalidInput(
+                    reason=(_('Illegal value specified for IOTYPE: '
+                              'set to either 0, 1, or 2.')))
+            else:
+                qos[key.upper()] = value
+
+    return qos
+
+
+def get_volume_qos(volume):
+    qos = {}
+    ctxt = context.get_admin_context()
+    type_id = volume['volume_type_id']
+    if type_id is not None:
+        volume_type = volume_types.get_volume_type(ctxt, type_id)
+        qos = get_qos_by_volume_type(volume_type)
+
+    return qos
+
+
+def _get_volume_type(type_id):
+    ctxt = context.get_admin_context()
+    return volume_types.get_volume_type(ctxt, type_id)
+
+
+def get_lun_conf_params(xml_file_path):
+    """Get parameters from config file for creating lun."""
+    lunsetinfo = {
+        'LUNType': 'Thick',
+        'StripUnitSize': '64',
+        'WriteType': '1',
+        'MirrorSwitch': '1',
+        'PrefetchType': '3',
+        'PrefetchValue': '0',
+        'PrefetchTimes': '0',
+        'policy': '0',
+        'readcachepolicy': '2',
+        'writecachepolicy': '5',
+    }
+    # Default lun set information.
+    root = parse_xml_file(xml_file_path)
+    luntype = root.findtext('LUN/LUNType')
+    if luntype:
+        if luntype.strip() in ['Thick', 'Thin']:
+            lunsetinfo['LUNType'] = luntype.strip()
+            if luntype.strip() == 'Thick':
+                lunsetinfo['LUNType'] = 0
+            if luntype.strip() == '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})
+            LOG.error(err_msg)
+            raise exception.VolumeBackendAPIException(data=err_msg)
+
+    stripunitsize = root.findtext('LUN/StripUnitSize')
+    if stripunitsize is not None:
+        lunsetinfo['StripUnitSize'] = stripunitsize.strip()
+    writetype = root.findtext('LUN/WriteType')
+    if writetype is not None:
+        lunsetinfo['WriteType'] = writetype.strip()
+    mirrorswitch = root.findtext('LUN/MirrorSwitch')
+    if mirrorswitch is not None:
+        lunsetinfo['MirrorSwitch'] = mirrorswitch.strip()
+
+    prefetch = root.find('LUN/Prefetch')
+    fetchtype = prefetch.attrib['Type']
+    if prefetch is not None and prefetch.attrib['Type']:
+        if fetchtype in ['0', '1', '2', '3']:
+            lunsetinfo['PrefetchType'] = fetchtype.strip()
+            typevalue = prefetch.attrib['Value'].strip()
+            if lunsetinfo['PrefetchType'] == '1':
+                double_value = int(typevalue) * 2
+                typevalue_double = six.text_type(double_value)
+                lunsetinfo['PrefetchValue'] = typevalue_double
+            elif lunsetinfo['PrefetchType'] == '2':
+                lunsetinfo['PrefetchValue'] = typevalue
+        else:
+            err_msg = (_(
+                'PrefetchType config is wrong. PrefetchType'
+                ' must be in 0,1,2,3. PrefetchType is: %(fetchtype)s.')
+                % {'fetchtype': fetchtype})
+            LOG.error(err_msg)
+            raise exception.VolumeBackendAPIException(data=err_msg)
+    else:
+        LOG.info(_LI(
+            'Use default PrefetchType. '
+            'PrefetchType: Intelligent.'))
+
+    return lunsetinfo
+
+
+def encode_name(name):
+    uuid_str = name.replace("-", "")
+    vol_uuid = uuid.UUID('urn:uuid:%s' % uuid_str)
+    vol_encoded = base64.urlsafe_b64encode(vol_uuid.bytes)
+    vol_encoded = vol_encoded.decode("utf-8")  # Make it compatible to py3.
+    newuuid = vol_encoded.replace("=", "")
+    return newuuid
+
+
+def init_lun_parameters(name, parameters):
+    """Initialize basic LUN parameters."""
+    lunparam = {"TYPE": "11",
+                "NAME": name,
+                "PARENTTYPE": "216",
+                "PARENTID": parameters['pool_id'],
+                "DESCRIPTION": parameters['volume_description'],
+                "ALLOCTYPE": parameters['LUNType'],
+                "CAPACITY": parameters['volume_size'],
+                "WRITEPOLICY": parameters['WriteType'],
+                "MIRRORPOLICY": parameters['MirrorSwitch'],
+                "PREFETCHPOLICY": parameters['PrefetchType'],
+                "PREFETCHVALUE": parameters['PrefetchValue'],
+                "DATATRANSFERPOLICY": parameters['policy'],
+                "READCACHEPOLICY": parameters['readcachepolicy'],
+                "WRITECACHEPOLICY": parameters['writecachepolicy'],
+                }
+
+    return lunparam
+
+
+def volume_in_use(volume):
+    """Check if the given volume is in use."""
+    return (volume['volume_attachment'] and
+            len(volume['volume_attachment']) > 0)
+
+
+def get_wait_interval(xml_file_path, event_type):
+    """Get wait interval from huawei conf file."""
+    root = parse_xml_file(xml_file_path)
+    wait_interval = root.findtext('LUN/%s' % event_type)
+    if wait_interval is None:
+        wait_interval = constants.DEFAULT_WAIT_INTERVAL
+        LOG.info(_LI(
+            "Wait interval for %(event_type)s is not configured in huawei "
+            "conf file. Use default: %(default_wait_interval)d."),
+            {"event_type": event_type,
+             "default_wait_interval": wait_interval})
+
+    return int(wait_interval)
+
+
+def _get_default_timeout(xml_file_path):
+    """Get timeout from huawei conf file."""
+    root = parse_xml_file(xml_file_path)
+    timeout = root.findtext('LUN/Timeout')
+    if timeout is None:
+        timeout = constants.DEFAULT_WAIT_TIMEOUT
+        LOG.info(_LI(
+            "Timeout is not configured in huawei conf file. "
+            "Use default: %(default_timeout)d."),
+            {"default_timeout": timeout})
+
+    return timeout
+
+
+def wait_for_condition(xml_file_path, func, interval, timeout=None):
+    start_time = time.time()
+    if timeout is None:
+        timeout = _get_default_timeout(xml_file_path)
+
+    def _inner():
+        try:
+            res = func()
+        except Exception as ex:
+            raise exception.VolumeBackendAPIException(ex)
+        if res:
+            raise loopingcall.LoopingCallDone()
+
+        if int(time.time()) - start_time > timeout:
+            msg = (_('wait_for_condition: %s timed out.')
+                   % func.__name__)
+            LOG.error(msg)
+            raise exception.VolumeBackendAPIException(data=msg)
+
+    timer = loopingcall.FixedIntervalLoopingCall(_inner)
+    timer.start(interval=interval).wait()
+
+
+def get_login_info(xml_file_path):
+    """Get login IP, user name and password from config file."""
+    logininfo = {}
+    root = parse_xml_file(xml_file_path)
+
+    logininfo['RestURL'] = root.findtext('Storage/RestURL').strip()
+
+    for key in ['UserName', 'UserPassword']:
+        node = root.find('Storage/%s' % key)
+        node_text = node.text
+        logininfo[key] = node_text
+
+    return logininfo
+
+
+def _change_file_mode(filepath):
+    utils.execute('chmod', '640', filepath, run_as_root=True)
+
+
+def get_iscsi_conf(xml_file_path):
+    """Get iSCSI info from config file."""
+    iscsiinfo = {}
+    root = parse_xml_file(xml_file_path)
+    TargetIP = root.findtext('iSCSI/DefaultTargetIP').strip()
+    iscsiinfo['DefaultTargetIP'] = TargetIP
+    initiator_list = []
+
+    for dic in root.findall('iSCSI/Initiator'):
+        # Strip values of dict.
+        tmp_dic = {}
+        for k in dic.items():
+            tmp_dic[k[0]] = k[1].strip()
+
+        initiator_list.append(tmp_dic)
+
+    iscsiinfo['Initiator'] = initiator_list
+
+    return iscsiinfo
+
+
+def check_qos_high_priority(qos):
+    """Check QoS priority."""
+    for key, value in qos.iteritems():
+        if (key.find('MIN') == 0) or (key.find('LATENCY') == 0):
+            return True
+
+    return False
+
+
+def check_conf_file(xml_file_path):
+    """Check the config file, make sure the essential items are set."""
+    root = parse_xml_file(xml_file_path)
+    resturl = root.findtext('Storage/RestURL')
+    username = root.findtext('Storage/UserName')
+    pwd = root.findtext('Storage/UserPassword')
+    pool_node = root.findall('Storage/StoragePool')
+
+    if (not resturl) or (not username) or (not pwd):
+        err_msg = (_(
+            'check_conf_file: Config file invalid. RestURL,'
+            ' UserName and UserPassword must be set.'))
+        LOG.error(err_msg)
+        raise exception.InvalidInput(reason=err_msg)
+
+    if not pool_node:
+        err_msg = (_(
+            'check_conf_file: Config file invalid. '
+            'StoragePool must be set.'))
+        LOG.error(err_msg)
+        raise exception.InvalidInput(reason=err_msg)
+
+
+def get_volume_size(volume):
+    """Calculate the volume size.
+
+    We should divide the given volume size by 512 for the 18000 system
+    calculates volume size with sectors, which is 512 bytes.
+    """
+    volume_size = units.Gi / 512  # 1G
+    if int(volume['size']) != 0:
+        volume_size = int(volume['size']) * units.Gi / 512
+
+    return volume_size
diff --git a/cinder/volume/drivers/huawei/rest_client.py b/cinder/volume/drivers/huawei/rest_client.py
new file mode 100644 (file)
index 0000000..0501a35
--- /dev/null
@@ -0,0 +1,1219 @@
+# Copyright (c) 2015 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.
+
+import json
+import time
+
+from oslo_log import log as logging
+from oslo_utils import excutils
+from six.moves import http_cookiejar
+from six.moves import urllib
+
+from cinder import exception
+from cinder.i18n import _, _LE, _LI, _LW
+from cinder.volume.drivers.huawei import constants
+from cinder.volume.drivers.huawei import huawei_utils
+
+LOG = logging.getLogger(__name__)
+
+
+class RestClient(object):
+    """Common class for Huawei OceanStor 18000 storage system."""
+
+    def __init__(self, configuration):
+        self.configuration = configuration
+        self.xml_file_path = configuration.cinder_huawei_conf_file
+        self.cookie = http_cookiejar.CookieJar()
+        self.url = None
+        self.productversion = None
+        self.headers = {"Connection": "keep-alive",
+                        "Content-Type": "application/json"}
+
+    def call(self, url=False, data=None, method=None):
+        """Send requests to 18000 server.
+
+        Send HTTPS call, get response in JSON.
+        Convert response into Python Object and return it.
+        """
+        opener = urllib.build_opener(urllib.HTTPCookieProcessor(self.cookie))
+        urllib.install_opener(opener)
+        res_json = None
+
+        try:
+            urllib.socket.setdefaulttimeout(720)
+            req = urllib.Request(url, data, self.headers)
+            if method:
+                req.get_method = lambda: method
+            res = urllib.urlopen(req).read().decode("utf-8")
+
+            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'
+                             'Response Data:%(res)s\n\n'), {'url': url,
+                                                            'method': method,
+                                                            'data': data,
+                                                            'res': res})
+
+        except Exception as err:
+            LOG.error(_LE('Bad response from server: %(url)s.'
+                          ' Error: %(err)s'), {'url': url, 'err': err})
+            json_msg = ('{"error":{"code": %s,"description": "Connect to '
+                        'server error."}}'), constants.ERROR_CONNECT_TO_SERVER
+            res_json = json.loads(json_msg)
+            return res_json
+
+        try:
+            res_json = json.loads(res)
+        except Exception as err:
+            LOG.error(_LE('JSON transfer error: %s.'), err)
+            raise
+
+        return res_json
+
+    def login(self):
+        """Login 18000 array."""
+        login_info = huawei_utils.get_login_info(self.xml_file_path)
+        urlstr = login_info['RestURL']
+        url_list = urlstr.split(";")
+        for item_url in url_list:
+            url = item_url + "xx/sessions"
+            data = json.dumps({"username": login_info['UserName'],
+                               "password": login_info['UserPassword'],
+                               "scope": "0"})
+            result = self.call(url, data)
+
+            if result['error']['code'] == constants.ERROR_CONNECT_TO_SERVER:
+                continue
+
+            if (result['error']['code'] != 0) or ("data" not in result):
+                msg = (_("Login error, reason is: %s.") % result)
+                LOG.error(msg)
+                raise exception.VolumeBackendAPIException(data=msg)
+
+            deviceid = result['data']['deviceid']
+            self.url = item_url + deviceid
+            self.headers['iBaseToken'] = result['data']['iBaseToken']
+            return deviceid
+
+        msg = _("Login error: Can't connect to server.")
+        LOG.error(msg)
+        raise exception.VolumeBackendAPIException(data=msg)
+
+    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})
+            LOG.error(msg)
+            raise exception.VolumeBackendAPIException(data=msg)
+
+    def _assert_data_in_result(self, result, msg):
+        if "data" not in result:
+            err_msg = (_('%s "data" was not in result.') % msg)
+            LOG.error(err_msg)
+            raise exception.VolumeBackendAPIException(data=err_msg)
+
+    def create_volume(self, lun_param):
+        url = self.url + "/lun"
+        data = json.dumps(lun_param)
+        result = self.call(url, data)
+
+        msg = 'Create volume error.'
+        self._assert_rest_result(result, msg)
+        self._assert_data_in_result(result, msg)
+
+        return result['data']
+
+    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):
+        lun_group_id = self.get_lungroupid_by_lunid(lun_id)
+        if lun_group_id:
+            self.remove_lun_from_lungroup(lun_group_id, 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.')
+
+    def find_pool_info(self):
+        root = huawei_utils.parse_xml_file(self.xml_file_path)
+        pool_name = root.findtext('Storage/StoragePool')
+        if not 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.')
+
+        poolinfo = {}
+        if "data" in result:
+            for item in result['data']:
+                if pool_name.strip() == item['NAME']:
+                    poolinfo['ID'] = item['ID']
+                    poolinfo['CAPACITY'] = item['USERFREECAPACITY']
+                    poolinfo['TOTALCAPACITY'] = item['USERTOTALCAPACITY']
+                    break
+        if not poolinfo:
+            msg = (_('Get pool info error, pool name is: %s.') % pool_name)
+            LOG.error(msg)
+            raise exception.VolumeBackendAPIException(data=msg)
+        return poolinfo
+
+    def _get_id_from_result(self, result, name, key):
+        if "data" in result:
+            for item in result['data']:
+                if name == item[key]:
+                    return item['ID']
+
+    def get_volume_by_name(self, name):
+        url = self.url + "/lun?range=[0-65535]"
+        result = self.call(url, None, "GET")
+        self._assert_rest_result(result, 'Get volume by name error.')
+
+        return self._get_id_from_result(result, name, 'NAME')
+
+    def active_snapshot(self, snapshot_id):
+        activeurl = self.url + "/snapshot/activate"
+        data = json.dumps({"SNAPSHOTLIST": [snapshot_id]})
+        result = self.call(activeurl, data)
+        self._assert_rest_result(result, 'Active snapshot error.')
+
+    def create_snapshot(self, snapshot):
+        snapshot_name = huawei_utils.encode_name(snapshot['id'])
+        snapshot_description = snapshot['id']
+        volume_name = huawei_utils.encode_name(snapshot['volume_id'])
+
+        LOG.info(_LI(
+            'create_snapshot:snapshot name: %(snapshot)s, '
+            'volume name: %(volume)s.'),
+            {'snapshot': snapshot_name,
+             'volume': volume_name})
+
+        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.VolumeBackendAPIException(data=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)
+
+        msg = 'Create snapshot error.'
+        self._assert_rest_result(result, msg)
+        self._assert_data_in_result(result, msg)
+
+        return result['data']
+
+    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
+
+        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.')
+
+    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.')
+
+    def get_snapshotid_by_name(self, name):
+        url = self.url + "/snapshot?range=[0-32767]"
+        data = json.dumps({"TYPE": "27"})
+        result = self.call(url, data, "GET")
+        self._assert_rest_result(result, 'Get snapshot id error.')
+
+        return self._get_id_from_result(result, name, 'NAME')
+
+    def create_luncopy(self, luncopyname, srclunid, tgtlunid):
+        """Create a luncopy."""
+        url = self.url + "/luncopy"
+        data = json.dumps({"TYPE": 219,
+                           "NAME": luncopyname,
+                           "DESCRIPTION": luncopyname,
+                           "COPYSPEED": 2,
+                           "LUNCOPYTYPE": "1",
+                           "SOURCELUN": ("INVALID;%s;INVALID;INVALID;INVALID"
+                                         % srclunid),
+                           "TARGETLUN": ("INVALID;%s;INVALID;INVALID;INVALID"
+                                         % tgtlunid)})
+        result = self.call(url, data)
+
+        msg = 'Create luncopy error.'
+        self._assert_rest_result(result, msg)
+        self._assert_data_in_result(result, msg)
+
+        return result['data']['ID']
+
+    def add_host_into_hostgroup(self, host_id):
+        """Associate host to hostgroup.
+
+        If hostgroup doesn't exist, create one.
+        """
+        host_group_name = constants.HOSTGROUP_PREFIX + host_id
+        hostgroup_id = self._create_hostgroup_with_check(host_group_name)
+        is_associated = self._is_host_associate_to_hostgroup(hostgroup_id,
+                                                             host_id)
+        if not is_associated:
+            self._associate_host_to_hostgroup(hostgroup_id, host_id)
+
+        return hostgroup_id
+
+    def find_tgt_port_group(self, tgt_port_group):
+        """Find target portgroup id by target port group name."""
+        url = self.url + "/portgroup?range=[0-8191]&TYPE=257"
+        result = self.call(url, None, "GET")
+
+        msg = 'Find portgroup error.'
+        self._assert_rest_result(result, msg)
+        msg = 'Can not find the portgroup on the array.'
+        self._assert_data_in_result(result, msg)
+
+        return self._get_id_from_result(result, tgt_port_group, 'NAME')
+
+    def _associate_portgroup_to_view(self, view_id, portgroup_id):
+        url = self.url + "/MAPPINGVIEW/CREATE_ASSOCIATE"
+        data = json.dumps({"ASSOCIATEOBJTYPE": "257",
+                           "ASSOCIATEOBJID": portgroup_id,
+                           "TYPE": "245",
+                           "ID": view_id})
+        result = self.call(url, data, "PUT")
+        self._assert_rest_result(result, 'Associate portgroup to mapping '
+                                 'view error.')
+
+    def _portgroup_associated(self, view_id, portgroup_id):
+        url_subfix = ("/mappingview/associate?TYPE=245&"
+                      "ASSOCIATEOBJTYPE=257&ASSOCIATEOBJID=%s" % portgroup_id)
+        url = self.url + url_subfix
+        result = self.call(url, None, "GET")
+        self._assert_rest_result(result, 'Check portgroup associate error.')
+
+        if self._get_id_from_result(result, view_id, 'ID'):
+            return True
+
+        return False
+
+    def mapping_hostgroup_and_lungroup(self, volume_name, hostgroup_id,
+                                       host_id, tgtportgroup_id=None):
+        """Add hostgroup and lungroup to mapping view."""
+        lungroup_name = constants.LUNGROUP_PREFIX + host_id
+        mapping_view_name = constants.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(mapping_view_name)
+
+        LOG.info(_LI(
+            'mapping_hostgroup_and_lungroup, lun_group: %(lun_group)s, '
+            'view_id: %(view_id)s, lun_id: %(lun_id)s.'),
+            {'lun_group': lungroup_id,
+             'view_id': view_id,
+             'lun_id': lun_id})
+
+        try:
+            # Create lungroup and add LUN into to lungroup.
+            if lungroup_id is None:
+                lungroup_id = self._create_lungroup(lungroup_name)
+            is_associated = self._is_lun_associated_to_lungroup(lungroup_id,
+                                                                lun_id)
+            if not is_associated:
+                self._associate_lun_to_lungroup(lungroup_id, lun_id)
+
+            if view_id is None:
+                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)
+                if tgtportgroup_id:
+                    self._associate_portgroup_to_view(view_id, tgtportgroup_id)
+
+            else:
+                if not self.hostgroup_associated(view_id, hostgroup_id):
+                    self._associate_hostgroup_to_view(view_id, hostgroup_id)
+                if not self.lungroup_associated(view_id, lungroup_id):
+                    self._associate_lungroup_to_view(view_id, lungroup_id)
+                if tgtportgroup_id:
+                    if not self._portgroup_associated(view_id,
+                                                      tgtportgroup_id):
+                        self._associate_portgroup_to_view(view_id,
+                                                          tgtportgroup_id)
+
+        except Exception:
+            with excutils.save_and_reraise_exception():
+                err_msg = (_LE(
+                    'Error occurred 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
+
+    def ensure_initiator_added(self, initiator_name, host_id):
+        added = self._initiator_is_added_to_array(initiator_name)
+        if not added:
+            self._add_initiator_to_array(initiator_name)
+        if not self.is_initiator_associated_to_host(initiator_name):
+            self._associate_initiator_to_host(initiator_name, host_id)
+
+    def _get_iscsi_tgt_port(self):
+        url = self.url + "/iscsidevicename"
+        result = self.call(url, None)
+
+        msg = 'Get iSCSI target port error.'
+        self._assert_rest_result(result, msg)
+        self._assert_data_in_result(result, msg)
+
+        return result['data'][0]['CMO_ISCSI_DEVICE_NAME']
+
+    def find_hostgroup(self, groupname):
+        """Get the given hostgroup id."""
+        url = self.url + "/hostgroup?range=[0-8191]"
+        result = self.call(url, None, "GET")
+        self._assert_rest_result(result, 'Get hostgroup information error.')
+
+        return self._get_id_from_result(result, groupname, 'NAME')
+
+    def _find_lungroup(self, lungroup_name):
+        """Get the given hostgroup id."""
+        url = self.url + "/lungroup?range=[0-8191]"
+        result = self.call(url, None, "GET")
+        self._assert_rest_result(result, 'Get lungroup information error.')
+
+        return self._get_id_from_result(result, lungroup_name, 'NAME')
+
+    def _create_hostgroup_with_check(self, host_group_name):
+        """Check if host exists on the array, or create it."""
+        hostgroup_id = self.find_hostgroup(host_group_name)
+        if hostgroup_id:
+            LOG.info(_LI(
+                '_create_hostgroup_with_check. '
+                'hostgroup name: %(name)s, '
+                'hostgroup id: %(id)s'),
+                {'name': host_group_name,
+                 'id': hostgroup_id})
+            return hostgroup_id
+
+        try:
+            hostgroup_id = self._create_hostgroup(host_group_name)
+        except Exception:
+            LOG.info(_LI(
+                'Failed to create hostgroup: %(name)s. '
+                'Please check if it exists on the array.'),
+                {'name': host_group_name})
+            hostgroup_id = self.find_hostgroup(host_group_name)
+            if hostgroup_id is None:
+                err_msg = (_(
+                    'Failed to create hostgroup: %(name)s. '
+                    'Check if it exists on the array.'),
+                    {'name': host_group_name})
+                LOG.error(err_msg)
+                raise exception.VolumeBackendAPIException(data=err_msg)
+
+        LOG.info(_LI(
+            '_create_hostgroup_with_check. '
+            'Create hostgroup success. '
+            'hostgroup name: %(name)s, '
+            'hostgroup id: %(id)s'),
+            {'name': host_group_name,
+             'id': hostgroup_id})
+        return hostgroup_id
+
+    def _create_hostgroup(self, hostgroupname):
+        url = self.url + "/hostgroup"
+        data = json.dumps({"TYPE": "14", "NAME": hostgroupname})
+        result = self.call(url, data)
+
+        msg = 'Create hostgroup error.'
+        self._assert_rest_result(result, msg)
+        self._assert_data_in_result(result, msg)
+
+        return result['data']['ID']
+
+    def _create_lungroup(self, lungroup_name):
+        url = self.url + "/lungroup"
+        data = json.dumps({"DESCRIPTION": lungroup_name,
+                           "APPTYPE": '0',
+                           "GROUPTYPE": '0',
+                           "NAME": lungroup_name})
+        result = self.call(url, data)
+
+        msg = 'Create lungroup error.'
+        self._assert_rest_result(result, msg)
+        self._assert_data_in_result(result, msg)
+
+        return result['data']['ID']
+
+    def delete_lungroup(self, lungroup_id):
+        url = self.url + "/LUNGroup/" + lungroup_id
+        result = self.call(url, None, "DELETE")
+        self._assert_rest_result(result, 'Delete lungroup error.')
+
+    def lungroup_associated(self, view_id, lungroup_id):
+        url_subfix = ("/mappingview/associate?TYPE=245&"
+                      "ASSOCIATEOBJTYPE=256&ASSOCIATEOBJID=%s" % lungroup_id)
+        url = self.url + url_subfix
+        result = self.call(url, None, "GET")
+        self._assert_rest_result(result, 'Check lungroup associate error.')
+
+        if self._get_id_from_result(result, view_id, 'ID'):
+            return True
+        return False
+
+    def hostgroup_associated(self, view_id, hostgroup_id):
+        url_subfix = ("/mappingview/associate?TYPE=245&"
+                      "ASSOCIATEOBJTYPE=14&ASSOCIATEOBJID=%s" % hostgroup_id)
+        url = self.url + url_subfix
+        result = self.call(url, None, "GET")
+        self._assert_rest_result(result, 'Check hostgroup associate error.')
+
+        if self._get_id_from_result(result, view_id, 'ID'):
+            return True
+        return False
+
+    def find_host_lun_id(self, host_id, lun_id):
+        url = self.url + ("/lun/associate?TYPE=11&ASSOCIATEOBJTYPE=21"
+                          "&ASSOCIATEOBJID=%s" % (host_id))
+        result = self.call(url, None, "GET")
+        self._assert_rest_result(result, 'Find host lun id error.')
+
+        host_lun_id = 1
+        if "data" in result:
+            for item in result['data']:
+                if lun_id == item['ID']:
+                    associate_data = item['ASSOCIATEMETADATA']
+                    try:
+                        hostassoinfo = json.loads(associate_data)
+                        host_lun_id = hostassoinfo['HostLUNID']
+                        break
+                    except Exception as err:
+                        LOG.error(_LE("JSON transfer data error. %s."), err)
+                        raise
+        return host_lun_id
+
+    def find_host(self, hostname):
+        """Get the given host ID."""
+        url = self.url + "/host?range=[0-65535]"
+        data = json.dumps({"TYPE": "21"})
+        result = self.call(url, data, "GET")
+        self._assert_rest_result(result, 'Find host in hostgroup error.')
+
+        return self._get_id_from_result(result, hostname, 'NAME')
+
+    def add_host_with_check(self, host_name, host_name_before_hash):
+        host_id = self.find_host(host_name)
+        if host_id:
+            LOG.info(_LI(
+                'add_host_with_check. '
+                'host name: %(name)s, '
+                'host id: %(id)s'),
+                {'name': host_name,
+                 'id': host_id})
+            return host_id
+
+        try:
+            host_id = self._add_host(host_name, host_name_before_hash)
+        except Exception:
+            LOG.info(_LI(
+                'Failed to create host: %(name)s. '
+                'Check if it exists on the array.'),
+                {'name': host_name})
+            host_id = self.find_host(host_name)
+            if not host_id:
+                err_msg = (_(
+                    'Failed to create host: %(name)s. '
+                    'Please check if it exists on the array.'),
+                    {'name': host_name})
+                LOG.error(err_msg)
+                raise exception.VolumeBackendAPIException(data=err_msg)
+
+        LOG.info(_LI(
+            'add_host_with_check. '
+            'create host success. '
+            'host name: %(name)s, '
+            'host id: %(id)s'),
+            {'name': host_name,
+             'id': host_id})
+        return host_id
+
+    def _add_host(self, hostname, host_name_before_hash):
+        """Add a new host."""
+        url = self.url + "/host"
+        data = json.dumps({"TYPE": "21",
+                           "NAME": hostname,
+                           "OPERATIONSYSTEM": "0",
+                           "DESCRIPTION": host_name_before_hash})
+        result = self.call(url, data)
+        self._assert_rest_result(result, 'Add new host error.')
+
+        if "data" in result:
+            return result['data']['ID']
+        else:
+            return
+
+    def _is_host_associate_to_hostgroup(self, hostgroup_id, host_id):
+        """Check whether the host is associated to the hostgroup."""
+        url_subfix = ("/host/associate?TYPE=21&"
+                      "ASSOCIATEOBJTYPE=14&ASSOCIATEOBJID=%s" % hostgroup_id)
+
+        url = self.url + url_subfix
+        result = self.call(url, None, "GET")
+        self._assert_rest_result(result, 'Check hostgroup associate error.')
+
+        if self._get_id_from_result(result, host_id, 'ID'):
+            return True
+        return False
+
+    def _is_lun_associated_to_lungroup(self, lungroup_id, lun_id):
+        """Check whether the lun is associated to the lungroup."""
+        url_subfix = ("/lun/associate?TYPE=11&"
+                      "ASSOCIATEOBJTYPE=256&ASSOCIATEOBJID=%s" % lungroup_id)
+
+        url = self.url + url_subfix
+        result = self.call(url, None, "GET")
+        self._assert_rest_result(result, 'Check lungroup associate error.')
+
+        if self._get_id_from_result(result, lun_id, '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": host_id})
+
+        result = self.call(url, data)
+        self._assert_rest_result(result, 'Associate host to hostgroup error.')
+
+    def _associate_lun_to_lungroup(self, lungroup_id, lun_id):
+        """Associate lun to lungroup."""
+        url = self.url + "/lungroup/associate"
+        data = json.dumps({"ID": lungroup_id,
+                           "ASSOCIATEOBJTYPE": "11",
+                           "ASSOCIATEOBJID": lun_id})
+        result = self.call(url, data)
+        self._assert_rest_result(result, 'Associate lun to lungroup error.')
+
+    def remove_lun_from_lungroup(self, lungroup_id, lun_id):
+        """Remove lun from lungroup."""
+        url = self.url + ("/lungroup/associate?ID=%s"
+                          "&ASSOCIATEOBJTYPE=11&ASSOCIATEOBJID=%s"
+                          % (lungroup_id, lun_id))
+
+        result = self.call(url, None, 'DELETE')
+        self._assert_rest_result(result,
+                                 'Delete associated lun from lungroup error.')
+
+    def _initiator_is_added_to_array(self, ininame):
+        """Check whether the initiator is already added on the array."""
+        url = self.url + "/iscsi_initiator?range=[0-256]"
+        result = self.call(url, None, "GET")
+        self._assert_rest_result(result,
+                                 'Check initiator added to array error.')
+
+        if "data" in result:
+            for item in result['data']:
+                if item["ID"] == ininame:
+                    return True
+        return False
+
+    def is_initiator_associated_to_host(self, ininame):
+        """Check whether the initiator is associated to the host."""
+        url = self.url + "/iscsi_initiator?range=[0-256]"
+        result = self.call(url, None, "GET")
+        self._assert_rest_result(result,
+                                 'Check initiator associated to host error.')
+
+        if "data" in result:
+            for item in result['data']:
+                if item['ID'] == ininame and item['ISFREE'] == "true":
+                    return False
+        return True
+
+    def _add_initiator_to_array(self, initiator_name):
+        """Add a new initiator to storage device."""
+        url = self.url + "/iscsi_initiator/"
+        data = json.dumps({"TYPE": "222",
+                           "ID": initiator_name,
+                           "USECHAP": "false"})
+        result = self.call(url, data)
+        self._assert_rest_result(result, 'Add initiator to array error.')
+
+    def _associate_initiator_to_host(self, ininame, host_id):
+        """Associate initiator with the host."""
+        url = self.url + "/iscsi_initiator/" + ininame
+        data = json.dumps({"TYPE": "222",
+                           "ID": ininame,
+                           "USECHAP": "false",
+                           "PARENTTYPE": "21",
+                           "PARENTID": host_id})
+        result = self.call(url, data, "PUT")
+        self._assert_rest_result(result, 'Associate initiator to host error.')
+
+    def find_mapping_view(self, name):
+        """Find mapping view."""
+        url = self.url + "/mappingview?range=[0-8191]"
+        data = json.dumps({"TYPE": "245"})
+        result = self.call(url, data, "GET")
+
+        msg = 'Find mapping view error.'
+        self._assert_rest_result(result, msg)
+
+        return self._get_id_from_result(result, name, 'NAME')
+
+    def _add_mapping_view(self, name):
+        url = self.url + "/mappingview"
+        data = json.dumps({"NAME": name, "TYPE": "245"})
+        result = self.call(url, data)
+        self._assert_rest_result(result, 'Add mapping view error.')
+
+        return result['data']['ID']
+
+    def _associate_hostgroup_to_view(self, viewID, hostGroupID):
+        url = self.url + "/MAPPINGVIEW/CREATE_ASSOCIATE"
+        data = json.dumps({"ASSOCIATEOBJTYPE": "14",
+                           "ASSOCIATEOBJID": hostGroupID,
+                           "TYPE": "245",
+                           "ID": viewID})
+        result = self.call(url, data, "PUT")
+        self._assert_rest_result(result, 'Associate host to mapping view '
+                                 'error.')
+
+    def _associate_lungroup_to_view(self, viewID, lunGroupID):
+        url = self.url + "/MAPPINGVIEW/CREATE_ASSOCIATE"
+        data = json.dumps({"ASSOCIATEOBJTYPE": "256",
+                           "ASSOCIATEOBJID": lunGroupID,
+                           "TYPE": "245",
+                           "ID": viewID})
+        result = self.call(url, data, "PUT")
+        self._assert_rest_result(result, 'Associate lungroup to mapping view '
+                                 'error.')
+
+    def delete_lungroup_mapping_view(self, view_id, lungroup_id):
+        """Remove lungroup associate from the mapping view."""
+        url = 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 lungroup from mapping view '
+                                 'error.')
+
+    def delete_hostgoup_mapping_view(self, view_id, hostgroup_id):
+        """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 hostgroup from mapping view '
+                                 'error.')
+
+    def delete_portgroup_mapping_view(self, view_id, portgroup_id):
+        """Remove portgroup associate from the mapping view."""
+        url = self.url + "/mappingview/REMOVE_ASSOCIATE"
+        data = json.dumps({"ASSOCIATEOBJTYPE": "257",
+                           "ASSOCIATEOBJID": portgroup_id,
+                           "TYPE": "245",
+                           "ID": view_id})
+        result = self.call(url, data, "PUT")
+        self._assert_rest_result(result, 'Delete portgroup from mapping view '
+                                 'error.')
+
+    def delete_mapping_view(self, view_id):
+        """Remove mapping view from the storage."""
+        url = self.url + "/mappingview/" + view_id
+        result = self.call(url, None, "DELETE")
+        self._assert_rest_result(result, 'Delete mapping view error.')
+
+    def get_lunnum_from_lungroup(self, lungroup_id):
+        """Check if there are still other luns associated to the lungroup."""
+        url_subfix = ("/lun/count?TYPE=11&ASSOCIATEOBJTYPE=256&"
+                      "ASSOCIATEOBJID=%s" % lungroup_id)
+        url = self.url + url_subfix
+        result = self.call(url, None, "GET")
+        self._assert_rest_result(result, 'Find lun number error.')
+        lunnum = -1
+        if "data" in result:
+            lunnum = result['data']['COUNT']
+        return lunnum
+
+    def is_portgroup_associated_to_view(self, view_id, portgroup_id):
+        """Check whether the portgroup is associated to the mapping view."""
+        url_subfix = ("/mappingview/associate/portgroup?TYPE=257&"
+                      "ASSOCIATEOBJTYPE=245&ASSOCIATEOBJID=%s" % view_id)
+        url = self.url + url_subfix
+        result = self.call(url, None, "GET")
+        self._assert_rest_result(result, 'Find portgroup from mapping view '
+                                 'error.')
+
+        if self._get_id_from_result(result, portgroup_id, 'ID'):
+            return True
+        return False
+
+    def find_lungroup_from_map(self, view_id):
+        """Get lungroup from the given map"""
+        url_subfix = ("/mappingview/associate/lungroup?TYPE=256&"
+                      "ASSOCIATEOBJTYPE=245&ASSOCIATEOBJID=%s" % view_id)
+        url = self.url + url_subfix
+        result = self.call(url, None, "GET")
+        self._assert_rest_result(result, 'Find lun group from mapping view '
+                                 'error.')
+
+        return self._get_id_from_result(result, view_id, 'ID')
+
+    def start_luncopy(self, luncopyid):
+        """Start a LUNcopy."""
+        url = self.url + "/LUNCOPY/start"
+        data = json.dumps({"TYPE": "219", "ID": luncopyid})
+        result = self.call(url, data, "PUT")
+        self._assert_rest_result(result, 'Start LUNcopy error.')
+
+    def _get_capacity(self):
+        """Get free capacity and total capacity of the pool."""
+        poolinfo = self.find_pool_info()
+        pool_capacity = {'total_capacity': 0.0,
+                         'free_capacity': 0.0}
+
+        if poolinfo:
+            total = int(poolinfo['TOTALCAPACITY']) / 1024.0 / 1024.0 / 2
+            free = int(poolinfo['CAPACITY']) / 1024.0 / 1024.0 / 2
+            pool_capacity['total_capacity'] = total
+            pool_capacity['free_capacity'] = free
+
+        return pool_capacity
+
+    def get_luncopy_info(self, luncopyid):
+        """Get LUNcopy information."""
+        url = self.url + "/LUNCOPY?range=[0-1023]"
+        data = json.dumps({"TYPE": "219", })
+        result = self.call(url, data, "GET")
+        self._assert_rest_result(result, 'Get LUNcopy information error.')
+
+        luncopyinfo = {}
+        if "data" in result:
+            for item in result['data']:
+                if luncopyid == item['ID']:
+                    luncopyinfo['name'] = item['NAME']
+                    luncopyinfo['id'] = item['ID']
+                    luncopyinfo['state'] = item['HEALTHSTATUS']
+                    luncopyinfo['status'] = item['RUNNINGSTATUS']
+                    break
+        return luncopyinfo
+
+    def delete_luncopy(self, luncopyid):
+        """Delete a LUNcopy."""
+        url = self.url + "/LUNCOPY/%s" % luncopyid
+        result = self.call(url, None, "DELETE")
+        self._assert_rest_result(result, 'Delete LUNcopy error.')
+
+    def get_connected_free_wwns(self):
+        """Get free connected FC port WWNs.
+
+        If no new ports connected, return an empty list.
+        """
+        url = self.url + "/fc_initiator?ISFREE=true&range=[0-8191]"
+        result = self.call(url, None, "GET")
+
+        msg = 'Get connected free FC wwn error.'
+        self._assert_rest_result(result, msg)
+
+        wwns = []
+        if 'data' in result:
+            for item in result['data']:
+                wwns.append(item['ID'])
+
+        return wwns
+
+    def add_fc_port_to_host(self, host_id, wwn):
+        """Add a FC port to the host."""
+        url = self.url + "/fc_initiator/" + wwn
+        data = json.dumps({"TYPE": "223",
+                           "ID": wwn,
+                           "PARENTTYPE": 21,
+                           "PARENTID": host_id})
+        result = self.call(url, data, "PUT")
+        self._assert_rest_result(result, 'Add FC port to host error.')
+
+    def _get_iscsi_port_info(self, ip):
+        """Get iscsi port info in order to build the iscsi target iqn."""
+        url = self.url + "/eth_port"
+        result = self.call(url, None, "GET")
+
+        msg = 'Get iSCSI port information error.'
+        self._assert_rest_result(result, msg)
+        self._assert_data_in_result(result, msg)
+
+        iscsi_port_info = None
+        for item in result['data']:
+            if ip == item['IPV4ADDR']:
+                iscsi_port_info = item['LOCATION']
+                break
+
+        return iscsi_port_info
+
+    def _get_tgt_iqn(self, iscsiip):
+        """Get target iSCSI iqn."""
+        ip_info = self._get_iscsi_port_info(iscsiip)
+        iqn_prefix = self._get_iscsi_tgt_port()
+        if not ip_info:
+            err_msg = (_(
+                'Get iSCSI port info error, please check the target IP '
+                'configured in huawei conf file.'))
+            LOG.error(err_msg)
+            raise exception.VolumeBackendAPIException(data=err_msg)
+
+        LOG.debug('Request ip info is: %s.', ip_info)
+        split_list = ip_info.split(".")
+        newstr = split_list[1] + split_list[2]
+        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
+
+    def get_fc_target_wwpns(self, wwn):
+        url = (self.url +
+               "/host_link?INITIATOR_TYPE=223&INITIATOR_PORT_WWN=" + wwn)
+        result = self.call(url, None, "GET")
+
+        msg = 'Get FC target wwpn error.'
+        self._assert_rest_result(result, msg)
+
+        fc_wwpns = None
+        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 update_volume_stats(self):
+        root = huawei_utils.parse_xml_file(self.xml_file_path)
+        pool_name = root.findtext('Storage/StoragePool')
+        if not pool_name:
+            msg = (_(
+                'Invalid resource pool name. '
+                'Please check the config file.'))
+            LOG.error(msg)
+            raise exception.InvalidInput(msg)
+        data = {}
+        capacity = self._get_capacity()
+        data["pools"] = []
+        pool = {}
+        pool.update(dict(
+            pool_name=pool_name,
+            total_capacity_gb=capacity['total_capacity'],
+            free_capacity_gb=capacity['free_capacity'],
+            reserved_percentage=0,
+            QoS_support=True,
+        ))
+        data["pools"].append(pool)
+        return data
+
+    def _find_qos_policy_info(self, policy_name):
+        url = self.url + "/ioclass"
+        result = self.call(url, None, "GET")
+
+        msg = 'Get QoS policy error.'
+        self._assert_rest_result(result, msg)
+
+        qos_info = {}
+        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):
+        url = self.url + "/ioclass/" + policy_id
+        data = json.dumps({"TYPE": "230",
+                           "ID": policy_id,
+                           "LUNLIST": lunlist})
+        result = self.call(url, data, "PUT")
+        self._assert_rest_result(result, 'Update QoS policy error.')
+
+    def _get_tgt_ip_from_portgroup(self, portgroup_id):
+        target_ip = None
+        url = self.url + ("/eth_port/associate?TYPE=213&ASSOCIATEOBJTYPE=257"
+                          "&ASSOCIATEOBJID=%s" % portgroup_id)
+        result = self.call(url, None, "GET")
+
+        msg = 'Get target IP error.'
+        self._assert_rest_result(result, msg)
+        self._assert_data_in_result(result, msg)
+
+        if "data" in result:
+            for item in result['data']:
+                if (item['IPV4ADDR'] and item['HEALTHSTATUS'] ==
+                    constants.STATUS_HEALTH
+                   and item['RUNNINGSTATUS'] == constants.STATUS_RUNNING):
+                    target_ip = item['IPV4ADDR']
+                    LOG.info(_LI('_get_tgt_ip_from_portgroup: Get ip: %s.'),
+                             target_ip)
+                    break
+        return target_ip
+
+    def get_iscsi_params(self, xml_file_path, connector):
+        """Get target iSCSI params, including iqn, IP."""
+        initiator = connector['initiator']
+        iscsi_conf = huawei_utils.get_iscsi_conf(xml_file_path)
+        target_ip = None
+        portgroup = None
+        portgroup_id = None
+        for ini in iscsi_conf['Initiator']:
+            if ini['Name'] == initiator:
+                for key in ini:
+                    if key == 'TargetPortGroup':
+                        portgroup = ini['TargetPortGroup']
+                    elif key == 'TargetIP':
+                        target_ip = ini['TargetIP']
+
+        if portgroup:
+            portgroup_id = self.find_tgt_port_group(portgroup)
+            target_ip = self._get_tgt_ip_from_portgroup(portgroup_id)
+
+        # If not specify target IP for some initiators, use default IP.
+        if not target_ip:
+            if iscsi_conf['DefaultTargetIP']:
+                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})
+                LOG.error(msg)
+                raise exception.InvalidInput(reason=msg)
+
+        LOG.info(_LI('Get the default ip: %s.'), 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, portgroup_id)
+
+    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
+
+        target_iqn = self._get_id_from_result(result, target_ip, '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
+
+    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 = constants.QOS_NAME_PREFIX + lun_id + '_' + localtime
+
+        mergedata = {"TYPE": "230",
+                     "NAME": qos_name,
+                     "LUNLIST": ["%s" % lun_id],
+                     "CLASSTYPE": "1",
+                     "SCHEDULEPOLICY": "2",
+                     "SCHEDULESTARTTIME": "1410969600",
+                     "STARTTIME": "08:00",
+                     "DURATION": "86400",
+                     "CYCLESET": "[1,2,3,4,5,6,0]",
+                     }
+        mergedata.update(qos)
+        data = json.dumps(mergedata)
+        url = self.url + "/ioclass/"
+
+        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 change_lun_priority(self, lun_id):
+        """Change lun priority to high."""
+        url = self.url + "/lun/" + lun_id
+        data = json.dumps({"TYPE": "11",
+                           "ID": lun_id,
+                           "IOPRIORITY": "3"})
+
+        result = self.call(url, data, "PUT")
+        self._assert_rest_result(result, 'Change lun priority error.')
+
+    def get_qosid_by_lunid(self, lun_id):
+        """Get qosid by lun id."""
+        url = self.url + "/lun/" + lun_id
+        data = json.dumps({"TYPE": "11",
+                           "ID": lun_id})
+
+        result = self.call(url, data, "GET")
+        self._assert_rest_result(result, 'Get QoS id by lun id error.')
+
+        return result['data']['IOCLASSID']
+
+    def get_lungroupid_by_lunid(self, lun_id):
+        """Get lungroup id by lun id."""
+        url = self.url + ("/lungroup/associate?TYPE=256"
+                          "&ASSOCIATEOBJTYPE=11&ASSOCIATEOBJID=%s" % lun_id)
+
+        result = self.call(url, None, "GET")
+        self._assert_rest_result(result, 'Get lungroup id by lun id error.')
+
+        return self._get_id_from_result(result, lun_id, 'ID')
+
+    def get_lun_info(self, lun_id):
+        url = self.url + "/lun/" + lun_id
+        data = json.dumps({"TYPE": "11",
+                           "ID": lun_id})
+        result = self.call(url, data, "GET")
+
+        msg = 'Get volume error.'
+        self._assert_rest_result(result, msg)
+        self._assert_data_in_result(result, msg)
+
+        return result['data']
+
+    def extend_volume(self, lun_id, new_volume_size):
+        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']
+
+    def remove_host(self, host_id):
+        url = self.url + "/host/%s" % host_id
+        result = self.call(url, None, "DELETE")
+        self._assert_rest_result(result, 'Remove host from array error.')
+
+    def delete_hostgroup(self, hostgroup_id):
+        url = self.url + "/hostgroup/%s" % hostgroup_id
+        result = self.call(url, None, "DELETE")
+        self._assert_rest_result(result, 'Delete hostgroup error.')
+
+    def remove_host_from_hostgroup(self, hostgroup_id, host_id):
+        url_subfix001 = "/host/associate?TYPE=14&ID=%s" % hostgroup_id
+        url_subfix002 = "&ASSOCIATEOBJTYPE=21&ASSOCIATEOBJID=%s" % host_id
+        url = self.url + url_subfix001 + url_subfix002
+        result = self.call(url, None, "DELETE")
+        self._assert_rest_result(result, 'Remove host from hostgroup error.')
+
+    def remove_iscsi_from_host(self, initiator):
+        url = self.url + "/iscsi_initiator/remove_iscsi_from_host"
+        data = json.dumps({"TYPE": '222',
+                           "ID": initiator})
+        result = self.call(url, data, "PUT")
+        self._assert_rest_result(result, 'Remove iscsi from host error.')
diff --git a/cinder/volume/drivers/huawei/rest_common.py b/cinder/volume/drivers/huawei/rest_common.py
deleted file mode 100644 (file)
index 8ed1eec..0000000
+++ /dev/null
@@ -1,1750 +0,0 @@
-# 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.
-"""Common class for Huawei 18000 storage drivers."""
-
-import base64
-import json
-import socket
-import time
-import uuid
-from xml.etree import ElementTree as ET
-
-from oslo_log import log as logging
-from oslo_service import loopingcall
-from oslo_utils import excutils
-from oslo_utils import units
-import six
-from six.moves import http_cookiejar
-from six.moves import urllib
-
-from cinder import context
-from cinder import exception
-from cinder.i18n import _, _LE, _LI, _LW
-from cinder import utils
-from cinder.volume import qos_specs
-from cinder.volume import volume_types
-
-LOG = logging.getLogger(__name__)
-
-DEFAULT_WAIT_TIMEOUT = 3600 * 24 * 30
-DEFAULT_WAIT_INTERVAL = 5
-
-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(object):
-    """Common class for Huawei OceanStor 18000 storage system."""
-
-    def __init__(self, configuration):
-        self.configuration = configuration
-        self.cookie = http_cookiejar.CookieJar()
-        self.url = None
-        self.productversion = None
-        self.headers = {"Connection": "keep-alive",
-                        "Content-Type": "application/json"}
-
-    def call(self, url=False, data=None, method=None):
-        """Send requests to 18000 server.
-        Send HTTPS call, get response in JSON.
-        Convert response into Python Object and return it.
-        """
-
-        handler = urllib.request.HTTPCookieProcessor(self.cookie)
-        opener = urllib.request.build_opener(handler)
-        urllib.request.install_opener(opener)
-
-        try:
-            socket.setdefaulttimeout(720)
-            req = urllib.request.Request(url, data, self.headers)
-            if method:
-                req.get_method = lambda: method
-            res = urllib.request.urlopen(req).read().decode("utf-8")
-
-            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'
-                             'Response Data:%(res)s\n\n'), {'url': url,
-                                                            'method': method,
-                                                            'data': data,
-                                                            'res': res})
-
-        except Exception as err:
-            LOG.error(_LE('\nBad response from server: %s.'), err)
-            raise
-
-        try:
-            res_json = json.loads(res)
-        except Exception as err:
-            LOG.error(_LE('JSON transfer error: %s.'), err)
-            raise
-
-        return res_json
-
-    def login(self):
-        """Log in 18000 array."""
-
-        login_info = self._get_login_info()
-        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):
-            msg = (_("Login error, reason is: %s.") % result)
-            LOG.error(msg)
-            raise exception.CinderException(msg)
-
-        deviceid = result['data']['deviceid']
-        self.url = login_info['RestURL'] + deviceid
-        self.headers['iBaseToken'] = result['data']['iBaseToken']
-        return deviceid
-
-    def _init_lun_parameters(self, name, parameters):
-        """Init basic LUN parameters."""
-        lunparam = {"TYPE": "11",
-                    "NAME": name,
-                    "PARENTTYPE": "216",
-                    "PARENTID": parameters['pool_id'],
-                    "DESCRIPTION": parameters['volume_description'],
-                    "ALLOCTYPE": parameters['LUNType'],
-                    "CAPACITY": parameters['volume_size'],
-                    "WRITEPOLICY": parameters['WriteType'],
-                    "MIRRORPOLICY": parameters['MirrorSwitch'],
-                    "PREFETCHPOLICY": parameters['PrefetchType'],
-                    "PREFETCHVALUE": parameters['PrefetchValue']}
-
-        return lunparam
-
-    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})
-            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)
-            LOG.error(err_msg)
-            raise exception.CinderException(err_msg)
-
-    def _create_volume(self, lun_param):
-        url = self.url + "/lun"
-        data = json.dumps(lun_param)
-        result = self.call(url, data)
-
-        msg = 'Create volume error.'
-        self._assert_rest_result(result, msg)
-        self._assert_data_in_result(result, msg)
-
-        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'])
-        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)
-
-                    self._delete_lun(lunid)
-
-        return lun_info
-
-    def _get_volume_size(self, volume):
-        """Calculate the volume size.
-
-        We should divide the given volume size by 512 for the 18000 system
-        calculates volume size with sectors, which is 512 bytes.
-        """
-
-        volume_size = units.Gi / 512  # 1G
-        if int(volume['size']) != 0:
-            volume_size = int(volume['size']) * units.Gi / 512
-
-        return volume_size
-
-    @utils.synchronized('huawei', external=True)
-    def delete_volume(self, volume):
-        """Delete a volume.
-
-        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 = 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.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.')
-
-    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
-        return root
-
-    def _encode_name(self, name):
-        uuid_str = name.replace("-", "")
-        vol_uuid = uuid.UUID('urn:uuid:%s' % uuid_str)
-        vol_encoded = base64.urlsafe_b64encode(vol_uuid.bytes)
-        if six.PY3:
-            vol_encoded = vol_encoded.decode('ascii')
-        newuuid = vol_encoded.replace("=", "")
-        return newuuid
-
-    def _find_pool_info(self):
-        root = self._read_xml()
-        pool_name = root.findtext('LUN/StoragePool')
-        if not 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.')
-
-        poolinfo = {}
-        if "data" in result:
-            for item in result['data']:
-                if pool_name.strip() == item['NAME']:
-                    poolinfo['ID'] = item['ID']
-                    poolinfo['CAPACITY'] = item['USERFREECAPACITY']
-                    poolinfo['TOTALCAPACITY'] = item['USERTOTALCAPACITY']
-                    break
-
-        if not poolinfo:
-            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?range=[0-65535]"
-        result = self.call(url, None, "GET")
-        self._assert_rest_result(result, 'Get volume by name error!')
-
-        volume_id = None
-        if "data" in result:
-            for item in result['data']:
-                if name == item['NAME']:
-                    volume_id = item['ID']
-                    break
-        return volume_id
-
-    def _active_snapshot(self, snapshot_id):
-        activeurl = self.url + "/snapshot/activate"
-        data = json.dumps({"SNAPSHOTLIST": [snapshot_id]})
-        result = self.call(activeurl, data)
-        self._assert_rest_result(result, 'Active snapshot 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.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)
-
-        msg = 'Create snapshot error.'
-        self._assert_rest_result(result, msg)
-        self._assert_data_in_result(result, msg)
-
-        return result['data']
-
-    @utils.synchronized('huawei', external=True)
-    def create_snapshot(self, snapshot):
-        snapshot_info = self._create_snapshot(snapshot)
-        snapshot_id = snapshot_info['ID']
-        self._active_snapshot(snapshot_id)
-
-        return snapshot_info
-
-    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
-
-        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.')
-
-    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):
-        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?range=[0-65535]"
-        data = json.dumps({"TYPE": "27"})
-        result = self.call(url, data, "GET")
-        self._assert_rest_result(result, 'Get snapshot id error.')
-
-        snapshot_id = None
-        if "data" in result:
-            for item in result['data']:
-                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)
-        event_type = 'LUNcopyWaitInterval'
-        wait_interval = self._get_wait_interval(event_type)
-        wait_interval = int(wait_interval)
-        try:
-            self._start_luncopy(luncopy_id)
-
-            def _luncopy_complete():
-                luncopy_info = self._get_luncopy_info(luncopy_id)
-                if luncopy_info['status'] == '40':
-                    # luncopy_info['status'] means for the running status of
-                    # the luncopy. If luncopy_info['status'] is equal to '40',
-                    # this luncopy is completely ready.
-                    return True
-                elif luncopy_info['state'] != '1':
-                    # luncopy_info['state'] means for the healthy status of the
-                    # luncopy. If luncopy_info['state'] is not equal to '1',
-                    # this means that an error occurred during the LUNcopy
-                    # operation and we should abort it.
-                    err_msg = (_(
-                        'An error occurred during the LUNcopy operation. '
-                        'LUNcopy name: %(luncopyname)s. '
-                        'LUNcopy status: %(luncopystatus)s. '
-                        'LUNcopy state: %(luncopystate)s.')
-                        % {'luncopyname': luncopy_id,
-                           'luncopystatus': luncopy_info['status'],
-                           'luncopystate': luncopy_info['state']})
-                    LOG.error(err_msg)
-                    raise exception.VolumeBackendAPIException(data=err_msg)
-            self._wait_for_condition(_luncopy_complete, wait_interval)
-
-        except Exception:
-            with excutils.save_and_reraise_exception():
-                self._delete_luncopy(luncopy_id)
-                self.delete_volume(volume)
-
-        self._delete_luncopy(luncopy_id)
-
-    def _get_wait_interval(self, event_type):
-        """Get wait interval from huawei conf file."""
-        root = self._read_xml()
-        wait_interval = root.findtext('LUN/%s' % event_type)
-        if wait_interval:
-            return wait_interval
-        else:
-            LOG.info(_LI(
-                "Wait interval for %(event_type)s is not configured in huawei "
-                "conf file. Use default: %(default_wait_interval)d."),
-                {"event_type": event_type,
-                 "default_wait_interval": DEFAULT_WAIT_INTERVAL})
-            return DEFAULT_WAIT_INTERVAL
-
-    def _get_default_timeout(self):
-        """Get timeout from huawei conf file."""
-        root = self._read_xml()
-        timeout = root.findtext('LUN/Timeout')
-        if timeout is None:
-            timeout = DEFAULT_WAIT_TIMEOUT
-            LOG.info(_LI(
-                "Timeout is not configured in huawei conf file. "
-                "Use default: %(default_timeout)d."),
-                {"default_timeout": timeout})
-
-        return timeout
-
-    def _wait_for_condition(self, func, interval, timeout=None):
-        start_time = time.time()
-        if timeout is None:
-            timeout = self._get_default_timeout()
-
-        def _inner():
-            try:
-                res = func()
-            except Exception as ex:
-                res = False
-                LOG.debug('_wait_for_condition: %(func_name)s '
-                          'failed for %(exception)s.',
-                          {'func_name': func.__name__,
-                           'exception': ex})
-            if res:
-                raise loopingcall.LoopingCallDone()
-
-            if int(time.time()) - start_time > timeout:
-                msg = (_('_wait_for_condition: %s timed out.')
-                       % func.__name__)
-                LOG.error(msg)
-                raise exception.VolumeBackendAPIException(data=msg)
-
-        timer = loopingcall.FixedIntervalLoopingCall(_inner)
-        timer.start(interval=interval).wait()
-
-    def create_volume_from_snapshot(self, volume, snapshot):
-        """Create a volume from a snapshot.
-
-        We use LUNcopy to copy a new volume from snapshot.
-        The time needed increases as volume size does.
-        """
-
-        snapshot_name = self._encode_name(snapshot['id'])
-
-        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'])
-
-        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})
-
-        event_type = 'LUNReadyWaitInterval'
-        wait_interval = self._get_wait_interval(event_type)
-
-        def _volume_ready():
-            url = self.url + "/lun/" + tgt_lun_id
-            result = self.call(url, None, "GET")
-            self._assert_rest_result(result, 'Get volume by id failed!')
-
-            if "data" in result:
-                if (result['data']['HEALTHSTATUS'] == "1" and
-                   result['data']['RUNNINGSTATUS'] == "27"):
-                    return True
-            return False
-
-        self._wait_for_condition(_volume_ready,
-                                 wait_interval,
-                                 wait_interval * 3)
-        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."""
-
-        # 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,
-                           "NAME": luncopyname,
-                           "DESCRIPTION": luncopyname,
-                           "COPYSPEED": 2,
-                           "LUNCOPYTYPE": "1",
-                           "SOURCELUN": ("INVALID;%s;INVALID;INVALID;INVALID"
-                                         % srclunid),
-                           "TARGETLUN": ("INVALID;%s;INVALID;INVALID;INVALID"
-                                         % tgtlunid)})
-        result = self.call(url, data)
-
-        msg = 'Create lun copy error.'
-        self._assert_rest_result(result, msg)
-        self._assert_data_in_result(result, msg)
-
-        return result['data']['ID']
-
-    def _add_host_into_hostgroup(self, host_id):
-        """Associate host to hostgroup.
-
-        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})
-
-        if hostgroup_id is None:
-            hostgroup_id = self._create_hostgroup(host_group_name)
-
-        is_associated = self._is_host_associate_to_hostgroup(hostgroup_id,
-                                                             host_id)
-        if is_associated is False:
-            self._associate_host_to_hostgroup(hostgroup_id, host_id)
-
-        return hostgroup_id
-
-    def _mapping_hostgroup_and_lungroup(self, volume_name,
-                                        hostgroup_id, host_id):
-        """Add hostgroup and lungroup to view."""
-        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(mapping_view_name)
-
-        LOG.info(_LI('_mapping_hostgroup_and_lungroup, lun_group: '
-                     '%(lun_group)s, view_id: %(view_id)s, lun_id: '
-                     '%(lun_id)s.'), {'lun_group': lungroup_id,
-                                      'view_id': view_id,
-                                      'lun_id': lun_id})
-
-        try:
-            # Create lungroup and add LUN into to lungroup.
-            if lungroup_id is None:
-                lungroup_id = self._create_lungroup(lungroup_name)
-            is_associated = self._is_lun_associated_to_lungroup(lungroup_id,
-                                                                lun_id)
-            if not is_associated:
-                self._associate_lun_to_lungroup(lungroup_id, lun_id)
-
-            if view_id is None:
-                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:
-                if not self._hostgroup_associated(view_id, hostgroup_id):
-                    self._associate_hostgroup_to_view(view_id, hostgroup_id)
-                if not self._lungroup_associated(view_id, lungroup_id):
-                    self._associate_lungroup_to_view(view_id, lungroup_id)
-
-        except Exception:
-            with excutils.save_and_reraise_exception():
-                LOG.error(_LE('Error occurred when adding hostgroup and '
-                              'lungroup to view. Remove lun from lungroup '
-                              'now.'))
-                self._remove_lun_from_lungroup(lungroup_id, lun_id)
-
-        return lun_id
-
-    def _ensure_initiator_added(self, initiator_name, hostid):
-        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.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)
-
-        # 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.
-        lun_id = self._mapping_hostgroup_and_lungroup(volume_name,
-                                                      hostgroup_id, hostid)
-
-        hostlunid = self._find_host_lun_id(hostid, lun_id)
-
-        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_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.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 = 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.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)
-
-        lun_id = self._mapping_hostgroup_and_lungroup(volume_name,
-                                                      hostgroup_id, hostid)
-        host_lun_id = self._find_host_lun_id(hostid, lun_id)
-
-        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
-
-        # Return FC 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}}
-
-        LOG.info(_LI("initialize_connection_fc, return data is: %s."), info)
-
-        return info
-
-    def _get_iscsi_tgt_port(self):
-        url = self.url + "/iscsidevicename"
-        result = self.call(url, None)
-
-        msg = 'Get iSCSI target port error.'
-        self._assert_rest_result(result, msg)
-        self._assert_data_in_result(result, msg)
-
-        return result['data'][0]['CMO_ISCSI_DEVICE_NAME']
-
-    def _find_hostgroup(self, groupname):
-        """Get the given hostgroup id."""
-        url = self.url + "/hostgroup?range=[0-8191]"
-        result = self.call(url, None, "GET")
-        self._assert_rest_result(result, 'Get hostgroup information error.')
-
-        host_group_id = None
-        if "data" in result:
-            for item in result['data']:
-                if groupname == item['NAME']:
-                    host_group_id = item['ID']
-                    break
-        return host_group_id
-
-    def _find_lungroup(self, lungroupname):
-        """Get the given hostgroup id."""
-        url = self.url + "/lungroup?range=[0-8191]"
-        result = self.call(url, None, "GET")
-        self._assert_rest_result(result, 'Get lungroup information error.')
-
-        lun_group_id = None
-        if 'data' in result:
-            for item in result['data']:
-                if lungroupname == item['NAME']:
-                    lun_group_id = item['ID']
-                    break
-        return lun_group_id
-
-    def _create_hostgroup(self, hostgroupname):
-        url = self.url + "/hostgroup"
-        data = json.dumps({"TYPE": "14", "NAME": hostgroupname})
-        result = self.call(url, data)
-
-        msg = 'Create hostgroup error.'
-        self._assert_rest_result(result, msg)
-        self._assert_data_in_result(result, msg)
-
-        return result['data']['ID']
-
-    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 lungroup error.'
-        self._assert_rest_result(result, msg)
-        self._assert_data_in_result(result, msg)
-
-        return result['data']['ID']
-
-    def _delete_lungroup(self, lungroupid):
-        url = self.url + "/LUNGroup/" + lungroupid
-        result = self.call(url, None, "DELETE")
-        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 lungroup associated error.')
-
-        if "data" in result:
-            for item in result['data']:
-                if viewid == item['ID']:
-                    return True
-        return False
-
-    def _hostgroup_associated(self, viewid, hostgroupid):
-        url_subfix = ("/mappingview/associate?TYPE=245&"
-                      "ASSOCIATEOBJTYPE=14&ASSOCIATEOBJID=%s" % hostgroupid)
-        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 viewid == item['ID']:
-                    return True
-        return False
-
-    def _find_host_lun_id(self, hostid, lunid):
-
-        url = self.url + ("/lun/associate?TYPE=11&ASSOCIATEOBJTYPE=21"
-                          "&ASSOCIATEOBJID=%s" % (hostid))
-        result = self.call(url, None, "GET")
-        self._assert_rest_result(result, 'Find host lun id error.')
-
-        host_lun_id = 1
-        if "data" in result:
-            for item in result['data']:
-                if lunid == item['ID']:
-                    associate_data = result['data'][0]['ASSOCIATEMETADATA']
-                    try:
-                        hostassoinfo = json.loads(associate_data)
-                        host_lun_id = hostassoinfo['HostLUNID']
-                        break
-                    except Exception as err:
-                        LOG.error(_LE("JSON transfer data error. %s"), err)
-                        raise
-        return host_lun_id
-
-    def _find_host(self, hostname):
-        """Get the given host ID."""
-        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 hostgroup error.')
-
-        host_id = None
-        if "data" in result:
-            for item in result['data']:
-                if hostname == item['NAME']:
-                    host_id = item['ID']
-                    break
-        return host_id
-
-    def _add_host(self, hostname):
-        """Add a new host."""
-        url = self.url + "/host"
-        data = json.dumps({"TYPE": "21",
-                           "NAME": hostname,
-                           "OPERATIONSYSTEM": "0"})
-        result = self.call(url, data)
-        self._assert_rest_result(result, 'Add new host error.')
-
-        if "data" in result:
-            return result['data']['ID']
-        else:
-            return None
-
-    def _is_host_associate_to_hostgroup(self, hostgroup_id, host_id):
-        """Check whether the host is associated to the hostgroup."""
-        url_subfix = ("/host/associate?TYPE=21&"
-                      "ASSOCIATEOBJTYPE=14&ASSOCIATEOBJID=%s" % hostgroup_id)
-
-        url = 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 _is_lun_associated_to_lungroup(self, lungroup_id, lun_id):
-        """Check whether the lun is associated to the lungroup."""
-        url_subfix = ("/lun/associate?TYPE=11&"
-                      "ASSOCIATEOBJTYPE=256&ASSOCIATEOBJID=%s" % lungroup_id)
-
-        url = self.url + url_subfix
-        result = self.call(url, None, "GET")
-        self._assert_rest_result(result, 'Check lungroup associate error.')
-
-        if "data" in result:
-            for item in result['data']:
-                if lun_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": host_id})
-
-        result = self.call(url, data)
-        self._assert_rest_result(result, 'Associate host to hostgroup error.')
-
-    def _associate_lun_to_lungroup(self, lungroupid, lunid):
-        """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 lungroup error.')
-
-    def _remove_lun_from_lungroup(self, lungroupid, lunid):
-        """Remove lun from lungroup."""
-
-        url = self.url + ("/lungroup/associate?ID=%s"
-                          "&ASSOCIATEOBJTYPE=11&ASSOCIATEOBJID=%s"
-                          % (lungroupid, lunid))
-
-        result = self.call(url, None, 'DELETE')
-        self._assert_rest_result(result,
-                                 'Delete associated lun from lungroup error.')
-
-    def _initiator_is_added_to_array(self, ininame):
-        """Check whether the initiator is already added on the array."""
-        url = self.url + "/iscsi_initiator?range=[0-65535]"
-        result = self.call(url, None, "GET")
-        self._assert_rest_result(result,
-                                 'Check initiator added to array error.')
-
-        if "data" in result:
-            for item in result['data']:
-                if item["ID"] == ininame:
-                    return True
-        return False
-
-    def _is_initiator_associated_to_host(self, ininame):
-        """Check whether the initiator is associated to the host."""
-        url = self.url + "/iscsi_initiator?range=[0-65535]"
-        result = self.call(url, None, "GET")
-        self._assert_rest_result(result,
-                                 'Check initiator associated to host error.')
-
-        if "data" in result:
-            for item in result['data']:
-                if item['ID'] == ininame and item['ISFREE'] == "true":
-                    return False
-        return True
-
-    def _add_initiator_to_array(self, ininame):
-        """Add a new initiator to storage device."""
-        url = self.url + "/iscsi_initiator/"
-        data = json.dumps({"TYPE": "222",
-                           "ID": ininame,
-                           "USECHAP": "false"})
-        result = self.call(url, data)
-        self._assert_rest_result(result, 'Add initiator to array error.')
-
-    def _associate_initiator_to_host(self, ininame, hostid):
-        """Associate initiator with the host."""
-        url = self.url + "/iscsi_initiator/" + ininame
-        data = json.dumps({"TYPE": "222",
-                           "ID": ininame,
-                           "USECHAP": "false",
-                           "PARENTTYPE": "21",
-                           "PARENTID": hostid})
-        result = self.call(url, data, "PUT")
-        self._assert_rest_result(result, 'Associate initiator to host error.')
-
-    def _find_mapping_view(self, name):
-        """Find mapping view."""
-        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)
-        viewid = None
-        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):
-        url = self.url + "/mappingview"
-        data = json.dumps({"NAME": name, "TYPE": "245"})
-        result = self.call(url, data)
-        self._assert_rest_result(result, 'Add map view error.')
-
-        return result['data']['ID']
-
-    def _associate_hostgroup_to_view(self, viewID, hostGroupID):
-        url = self.url + "/MAPPINGVIEW/CREATE_ASSOCIATE"
-        data = json.dumps({"ASSOCIATEOBJTYPE": "14",
-                           "ASSOCIATEOBJID": hostGroupID,
-                           "TYPE": "245",
-                           "ID": viewID})
-        result = self.call(url, data, "PUT")
-        self._assert_rest_result(result, 'Associate host to view error.')
-
-    def _associate_lungroup_to_view(self, viewID, lunGroupID):
-        url = self.url + "/MAPPINGVIEW/CREATE_ASSOCIATE"
-        data = json.dumps({"ASSOCIATEOBJTYPE": "256",
-                           "ASSOCIATEOBJID": lunGroupID,
-                           "TYPE": "245",
-                           "ID": viewID})
-        result = self.call(url, data, "PUT")
-        self._assert_rest_result(result, 'Associate lungroup to view error.')
-
-    def _delete_lungroup_mapping_view(self, view_id, lungroup_id):
-        """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 lungroup from view error.')
-
-    def _delete_hostgoup_mapping_view(self, view_id, hostgroup_id):
-        """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 hostgroup from view error.')
-
-    def _delete_mapping_view(self, view_id):
-        """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 _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'])
-        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)
-
-                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
-
-            else:
-                LOG.warning(_LW("Can't find lun on the array."))
-
-    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."""
-        url = self.url + "/sessions"
-        result = self.call(url, None, "DELETE")
-        self._assert_rest_result(result, 'Log out of session error.')
-
-    def _start_luncopy(self, luncopyid):
-        """Start a LUNcopy."""
-        url = self.url + "/LUNCOPY/start"
-        data = json.dumps({"TYPE": "219", "ID": luncopyid})
-        result = self.call(url, data, "PUT")
-        self._assert_rest_result(result, 'Start lun copy error.')
-
-    def _get_capacity(self):
-        """Get free capacity and total capacity of the pools."""
-        poolinfo = self._find_pool_info()
-        pool_capacity = {'total_capacity': 0.0,
-                         'CAPACITY': 0.0}
-
-        if poolinfo:
-            total = int(poolinfo['TOTALCAPACITY']) / 1024.0 / 1024.0 / 2
-            free = int(poolinfo['CAPACITY']) / 1024.0 / 1024.0 / 2
-            pool_capacity['total_capacity'] = total
-            pool_capacity['free_capacity'] = free
-
-        return pool_capacity
-
-    def _get_lun_conf_params(self):
-        """Get parameters from config file for creating lun."""
-        # Default lun set information.
-        lunsetinfo = {'LUNType': 'Thick',
-                      'StripUnitSize': '64',
-                      'WriteType': '1',
-                      'MirrorSwitch': '1',
-                      'PrefetchType': '3',
-                      'PrefetchValue': '0',
-                      'PrefetchTimes': '0'}
-
-        root = self._read_xml()
-        luntype = root.findtext('LUN/LUNType')
-        if luntype:
-            if luntype.strip() in ['Thick', 'Thin']:
-                lunsetinfo['LUNType'] = luntype.strip()
-                if luntype.strip() == 'Thick':
-                    lunsetinfo['LUNType'] = 0
-                if luntype.strip() == '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})
-                LOG.error(err_msg)
-                raise exception.VolumeBackendAPIException(data=err_msg)
-
-        stripunitsize = root.findtext('LUN/StripUnitSize')
-        if stripunitsize is not None:
-            lunsetinfo['StripUnitSize'] = stripunitsize.strip()
-        writetype = root.findtext('LUN/WriteType')
-        if writetype is not None:
-            lunsetinfo['WriteType'] = writetype.strip()
-        mirrorswitch = root.findtext('LUN/MirrorSwitch')
-        if mirrorswitch is not None:
-            lunsetinfo['MirrorSwitch'] = mirrorswitch.strip()
-
-        prefetch = root.find('LUN/Prefetch')
-        fetchtype = prefetch.attrib['Type']
-        if prefetch is not None and prefetch.attrib['Type']:
-            if fetchtype in ['0', '1', '2', '3']:
-                lunsetinfo['PrefetchType'] = fetchtype.strip()
-                typevalue = prefetch.attrib['Value'].strip()
-                if lunsetinfo['PrefetchType'] == '1':
-                    double_value = int(typevalue) * 2
-                    typevalue_double = six.text_type(double_value)
-                    lunsetinfo['PrefetchValue'] = typevalue_double
-                elif lunsetinfo['PrefetchType'] == '2':
-                    lunsetinfo['PrefetchValue'] = typevalue
-            else:
-                err_msg = (_(
-                    'PrefetchType config is wrong. PrefetchType'
-                    ' must be in 0,1,2,3. PrefetchType is: %(fetchtype)s.')
-                    % {'fetchtype': fetchtype})
-                LOG.error(err_msg)
-                raise exception.CinderException(err_msg)
-        else:
-            LOG.info(_LI(
-                'Use default PrefetchType. '
-                'PrefetchType: Intelligent.'))
-
-        return lunsetinfo
-
-    def _get_luncopy_info(self, luncopyid):
-        """Get LUNcopy information."""
-        url = self.url + "/LUNCOPY?range=[0-100000]"
-        data = json.dumps({"TYPE": "219", })
-        result = self.call(url, data, "GET")
-        self._assert_rest_result(result, 'Get lun copy information error.')
-
-        luncopyinfo = {}
-        if "data" in result:
-            for item in result['data']:
-                if luncopyid == item['ID']:
-                    luncopyinfo['name'] = item['NAME']
-                    luncopyinfo['id'] = item['ID']
-                    luncopyinfo['state'] = item['HEALTHSTATUS']
-                    luncopyinfo['status'] = item['RUNNINGSTATUS']
-                    break
-        return luncopyinfo
-
-    def _delete_luncopy(self, luncopyid):
-        """Delete a LUNcopy."""
-        url = self.url + "/LUNCOPY/%s" % luncopyid
-        result = self.call(url, None, "DELETE")
-        self._assert_rest_result(result, 'Delete lun copy error.')
-
-    def _get_connected_free_wwns(self):
-        """Get free connected FC port WWNs.
-
-        If no new ports connected, return an empty list.
-        """
-        url = self.url + "/fc_initiator?ISFREE=true&range=[0-1000]"
-        result = self.call(url, None, "GET")
-
-        msg = 'Get connected free FC wwn error.'
-        self._assert_rest_result(result, msg)
-
-        wwns = []
-        if 'data' in result:
-            for item in result['data']:
-                wwns.append(item['ID'])
-
-        return wwns
-
-    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",
-                           "ID": wwn,
-                           "PARENTTYPE": 21,
-                           "PARENTID": hostid})
-        result = self.call(url, data, "PUT")
-        self._assert_rest_result(result, 'Add FC port to host error.')
-
-    def _get_iscsi_port_info(self, ip):
-        """Get iscsi port info in order to build the iscsi target iqn."""
-        url = self.url + "/eth_port"
-        result = self.call(url, None, "GET")
-
-        msg = 'Get iSCSI port information error.'
-        self._assert_rest_result(result, msg)
-        self._assert_data_in_result(result, msg)
-
-        iscsi_port_info = None
-        for item in result['data']:
-            if ip == item['IPV4ADDR']:
-                iscsi_port_info = item['LOCATION']
-                break
-
-        return iscsi_port_info
-
-    def _get_iscsi_conf(self):
-        """Get iSCSI info from config file."""
-        iscsiinfo = {}
-        root = self._read_xml()
-        TargetIP = root.findtext('iSCSI/DefaultTargetIP').strip()
-        iscsiinfo['DefaultTargetIP'] = TargetIP
-        initiator_list = []
-
-        for dic in root.findall('iSCSI/Initiator'):
-            # Strip values of dic.
-            tmp_dic = {}
-            for k in dic.items():
-                tmp_dic[k[0]] = k[1].strip()
-
-            initiator_list.append(tmp_dic)
-
-        iscsiinfo['Initiator'] = initiator_list
-
-        return iscsiinfo
-
-    def _get_tgt_iqn(self, iscsiip):
-        """Get target iSCSI iqn."""
-
-        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]
-        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 +
-               "/host_link?INITIATOR_TYPE=223&INITIATOR_PORT_WWN=" + wwn)
-        result = self.call(url, None, "GET")
-
-        msg = 'Get FC target wwpn error.'
-        self._assert_rest_result(result, msg)
-
-        fc_wwpns = None
-        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 update_volume_stats(self):
-        capacity = self._get_capacity()
-        data = {}
-        data['vendor_name'] = 'Huawei'
-        data['total_capacity_gb'] = capacity['total_capacity']
-        data['free_capacity_gb'] = capacity['free_capacity']
-        data['reserved_percentage'] = 0
-        data['QoS_support'] = True
-        data['Tier_support'] = True
-        return data
-
-    def _find_qos_policy_info(self, policy_name):
-        url = self.url + "/ioclass"
-        result = self.call(url, None, "GET")
-
-        msg = 'Get QoS policy error.'
-        self._assert_rest_result(result, msg)
-
-        qos_info = {}
-        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):
-        url = self.url + "/ioclass/" + policy_id
-        data = json.dumps({"TYPE": "230",
-                           "ID": policy_id,
-                           "LUNLIST": lunlist})
-        result = self.call(url, data, "PUT")
-        self._assert_rest_result(result, 'Update QoS policy error.')
-
-    def _get_login_info(self):
-        """Get login IP, username and password from config file."""
-        logininfo = {}
-        filename = self.configuration.cinder_huawei_conf_file
-        tree = ET.parse(filename)
-        root = tree.getroot()
-        logininfo['RestURL'] = root.findtext('Storage/RestURL').strip()
-
-        need_encode = False
-        for key in ['UserName', 'UserPassword']:
-            node = root.find('Storage/%s' % key)
-            node_text = node.text
-            # Prefix !$$$ means encoded already.
-            if node_text.find('!$$$') > -1:
-                logininfo[key] = base64.b64decode(node_text[4:])
-            else:
-                logininfo[key] = node_text
-                if six.PY3:
-                    node_b64 = node_text.encode('utf-8')
-                    node_b64 = base64.b64encode(node_b64)
-                    node_b64 = node_b64.decode('ascii')
-                    node.text = '!$$$' + node_b64
-                else:
-                    node.text = '!$$$' + base64.b64encode(node_text)
-                need_encode = True
-        if need_encode:
-            self._change_file_mode(filename)
-            try:
-                tree.write(filename, 'UTF-8')
-            except Exception as err:
-                LOG.warning(_LW('Unable to access config file. %s'), err)
-
-        return logininfo
-
-    def _change_file_mode(self, filepath):
-        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 = 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)
-
-        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."""
-        initiator = connector['initiator']
-        iscsi_conf = self._get_iscsi_conf()
-        target_ip = None
-        for ini in iscsi_conf['Initiator']:
-            if ini['Name'] == initiator:
-                target_ip = ini['TargetIP']
-                break
-        # If didn't specify target IP for some initiator, use default IP.
-        if not target_ip:
-            if iscsi_conf['DefaultTargetIP']:
-                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})
-                LOG.error(msg)
-                raise exception.InvalidInput(reason=msg)
-
-        # 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):
-        """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.items():
-            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:
-            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
-
-        mergedata = {"TYPE": "230",
-                     "NAME": qos_name,
-                     "LUNLIST": ["%s" % lun_id],
-                     "CLASSTYPE": "1",
-                     "SCHEDULEPOLICY": "2",
-                     "SCHEDULESTARTTIME": "1410969600",
-                     "STARTTIME": "08:00",
-                     "DURATION": "86400",
-                     "CYCLESET": "[1,2,3,4,5,6,0]"}
-        mergedata.update(qos)
-        data = json.dumps(mergedata)
-        url = self.url + "/ioclass/"
-
-        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.items():
-            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
index 8c323ab0891869f7bcb4fe993c8dc87ab453d9e9..3ac4aa59f03c8661306cee85b14a2df3f0024e58 100644 (file)
@@ -109,10 +109,10 @@ 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',
+    'cinder.volume.drivers.huawei.huawei_18000.Huawei18000ISCSIDriver':
+    'cinder.volume.drivers.huawei.huawei_driver.Huawei18000ISCSIDriver',
+    'cinder.volume.drivers.huawei.huawei_18000.Huawei18000FCDriver':
+    'cinder.volume.drivers.huawei.huawei_driver.Huawei18000FCDriver',
     'cinder.volume.drivers.fujitsu_eternus_dx_fc.FJDXFCDriver':
     'cinder.volume.drivers.fujitsu.eternus_dx_fc.FJDXFCDriver',
     'cinder.volume.drivers.fujitsu_eternus_dx_iscsi.FJDXISCSIDriver':
diff --git a/tox.ini b/tox.ini
index ce92a0bc536b99f4ddcd0718b3b67e4a7aa724a4..063a4a601b972b4e0184ac8e6d66cb26cc133806 100644 (file)
--- a/tox.ini
+++ b/tox.ini
@@ -66,7 +66,7 @@ commands =
     cinder.tests.unit.test_hitachi_hbsd_snm2_iscsi \
     cinder.tests.unit.test_hp_xp_fc \
     cinder.tests.unit.test_hplefthand \
-    cinder.tests.unit.test_huawei_18000 \
+    cinder.tests.unit.test_huawei_drivers \
     cinder.tests.unit.test_huawei_drivers_compatibility \
     cinder.tests.unit.test_ibm_xiv_ds8k \
     cinder.tests.unit.test_infortrend_cli \