+++ /dev/null
-# Copyright (c) 2013 - 2014 Huawei Technologies Co., Ltd.
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-""" Tests for huawei 18000 storage."""
-import json
-import os
-import shutil
-import tempfile
-import time
-from xml.dom import minidom
-
-import mock
-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_18000
-from cinder.volume.drivers.huawei import rest_common
-
-LOG = logging.getLogger(__name__)
-
-test_volume = {'name': 'volume-21ec7341-9256-497b-97d9-ef48edcf0635',
- 'size': 2,
- 'volume_name': 'vol1',
- 'id': '21ec7341-9256-497b-97d9-ef48edcf0635',
- 'volume_id': '21ec7341-9256-497b-97d9-ef48edcf0635',
- 'provider_auth': None,
- 'project_id': 'project',
- 'display_name': 'vol1',
- 'display_description': 'test volume',
- 'volume_type_id': None,
- 'provider_location': '11'}
-
-test_snap = {'name': 'volume-21ec7341-9256-497b-97d9-ef48edcf0635',
- 'size': 1,
- 'volume_name': 'vol1',
- 'id': '21ec7341-9256-497b-97d9-ef48edcf0635',
- 'volume_id': '21ec7341-9256-497b-97d9-ef48edcf0635',
- 'provider_auth': None,
- 'project_id': 'project',
- 'display_name': 'vol1',
- 'display_description': 'test volume',
- 'volume_type_id': None,
- 'provider_location': '11'}
-
-FakeConnector = {'initiator': 'iqn.1993-08.debian:01:ec2bff7ac3a3',
- 'wwpns': ['10000090fa0d6754'],
- 'wwnns': ['10000090fa0d6755'],
- 'host': 'ubuntuc'}
-
-
-def find_data(method):
- if method is None:
- data = """{"error":{"code":0},
- "data":{"ID":"1",
- "NAME":"5mFHcBv4RkCcD+JyrWc0SA"}}"""
- if method == 'GET':
- data = """{"error":{"code":0},
- "data":[{"ID":"1",
- "NAME":"IexzQZJWSXuX2e9I7c8GNQ"}]}"""
- return data
-
-
-def find_data_lun(method):
- if method == 'GET':
- data = """{"error":{"code":0},
- "data":{"ID":"1",
- "NAME":"IexzQZJWSXuX2e9I7c8GNQ",
- "HEALTHSTATUS":"1",
- "RUNNINGSTATUS":"27"}}"""
- return data
-
-
-def find_data_lungroup(method):
- if method is None:
- data = '{"error":{"code":0},\
- "data":{"NAME":"5mFHcBv4RkCcD+JyrWc0SA",\
- "DESCRIPTION":"5mFHcBv4RkCcD+JyrWc0SA",\
- "ID":"11",\
- "TYPE":256}}'
-
- if method == "GET":
- data = """{"error":{"code":0},
- "data":[{
- "NAME":"OpenStack_LunGroup_1",
- "DESCRIPTION":"5mFHcBv4RkCcD+JyrWc0SA",
- "ID":"11",
- "TYPE":256}]}"""
-
- if method == "DELETE":
- data = """{"error":{"code":0},
- "data":[{
- "NAME":"IexzQZJWSXuX2e9I7c8GNQ",
- "DESCRIPTION":"5mFHcBv4RkCcD+JyrWc0SA",
- "ID":"11",
- "TYPE":256}]}"""
- return data
-
-
-def find_data_hostgroup(method):
- if method is None:
- data = """{"error":{"code":0},"data":{
- "NAME":"ubuntuc",
- "DESCRIPTION":"",
- "ID":"0",
- "TYPE":14}}"""
-
- if method == "GET":
- data = """{"error":{"code":0},"data":[{
- "NAME":"ubuntuc",
- "DESCRIPTION":"",
- "ID":"0","TYPE":14}]}"""
- return data
-
-
-def Fake_sleep(time):
- pass
-
-
-def find_data_mappingview(method, other_flag):
- if method is None:
- data = """{"error":{"code":0},"data":
- {"WORKMODE":"255","HEALTHSTATUS":"1",
- "NAME":"mOWtSXnaQKi3hpB3tdFRIQ",
- "RUNNINGSTATUS":"27","DESCRIPTION":"",
- "ENABLEINBANDCOMMAND":"true",
- "ID":"1","INBANDLUNWWN":"",
- "TYPE":245}}
- """
-
- if method == "GET":
- if other_flag:
- data = """{"error":{"code":0},"data":[
- {"WORKMODE":"255","HEALTHSTATUS":"1",
- "NAME":"mOWtSXnaQKi3hpB3tdFRIQ",
- "RUNNINGSTATUS":"27","DESCRIPTION":"",
- "ENABLEINBANDCOMMAND":"true","ID":"1",
- "INBANDLUNWWN":"","TYPE":245},
- {"WORKMODE":"255","HEALTHSTATUS":"1",
- "NAME":"YheUoRwbSX2BxN767nvLSw",
- "RUNNINGSTATUS":"27","DESCRIPTION":"",
- "ENABLEINBANDCOMMAND":"true",
- "ID":"2","INBANDLUNWWN":"",
- "TYPE":245}]}
- """
- else:
- data = """{"error":{"code":0},"data":[
- {"WORKMODE":"255","HEALTHSTATUS":"1",
- "NAME":"IexzQZJWSXuX2e9I7c8GNQ",
- "RUNNINGSTATUS":"27","DESCRIPTION":"",
- "ENABLEINBANDCOMMAND":"true","ID":"1",
- "INBANDLUNWWN":"","TYPE":245},
- {"WORKMODE":"255","HEALTHSTATUS":"1",
- "NAME":"YheUoRwbSX2BxN767nvLSw",
- "RUNNINGSTATUS":"27","DESCRIPTION":"",
- "ENABLEINBANDCOMMAND":"true","ID":"2",
- "INBANDLUNWWN":"","TYPE":245}]}
- """
- return data
-
-
-def find_data_snapshot(method):
- if method is None:
- data = '{"error":{"code":0},"data":{"ID":11,"NAME":"YheUoRwbSX2BxN7"}}'
- if method == "GET":
- data = """{"error":{"code":0},"data":[
- {"ID":11,"NAME":"SDFAJSDFLKJ"},
- {"ID":12,"NAME":"SDFAJSDFLKJ2"}]}"""
- return data
-
-
-def find_data_host(method):
- if method is None:
- data = """{"error":{"code":0},
- "data":
- {"PARENTTYPE":245,
- "NAME":"Default Host",
- "DESCRIPTION":"",
- "RUNNINGSTATUS":"1",
- "IP":"",
- "PARENTNAME":"0",
- "OPERATIONSYSTEM":"1",
- "LOCATION":"",
- "HEALTHSTATUS":"1",
- "MODEL":"",
- "ID":"0",
- "PARENTID":"0",
- "NETWORKNAME":"",
- "TYPE":21}} """
-
- if method == "GET":
- data = """{"error":{"code":0},
- "data":[
- {"PARENTTYPE":245,"NAME":"ubuntuc",
- "DESCRIPTION":"","RUNNINGSTATUS":"1",
- "IP":"","PARENTNAME":"",
- "OPERATIONSYSTEM":"0","LOCATION":"",
- "HEALTHSTATUS":"1","MODEL":"",
- "ID":"1","PARENTID":"",
- "NETWORKNAME":"","TYPE":21},
- {"PARENTTYPE":245,"NAME":"ubuntu",
- "DESCRIPTION":"","RUNNINGSTATUS":"1",
- "IP":"","PARENTNAME":"","OPERATIONSYSTEM":"0",
- "LOCATION":"","HEALTHSTATUS":"1",
- "MODEL":"","ID":"2","PARENTID":"",
- "NETWORKNAME":"","TYPE":21}]} """
- return data
-
-
-def find_data_host_associate(method):
- if (method is None) or (method == "GET"):
- data = '{"error":{"code":0}}'
- return data
-
-
-def data_session(url):
- if url == "/xx/sessions":
- data = """{"error":{"code":0},
- "data":{"username":"admin",
- "iBaseToken":"2001031430",
- "deviceid":"210235G7J20000000000"}}"""
- if url == "sessions":
- data = '{"error":{"code":0},"data":{"ID":11}}'
- return data
-
-
-def data_lun(url, method):
- if url == "lun":
- data = find_data(method)
- if url == "lun/1":
- data = find_data_lun(method)
- if url == "lun?range=[0-65535]":
- data = find_data(method)
- if url == "lungroup?range=[0-8191]":
- data = find_data_lungroup(method)
- if url == "lungroup":
- data = find_data_lungroup(method)
- if url == "lungroup/associate":
- data = """{"error":{"code":0},
- "data":{"NAME":"5mFHcBv4RkCcD+JyrWc0SA",
- "DESCRIPTION":"5mFHcBv4RkCcD+JyrWc0SA",
- "ID":"11",
- "TYPE":256}}"""
- return data
-
-
-def data_host(url, method):
- if url == "hostgroup":
- data = find_data_hostgroup(method)
- if url == "hostgroup?range=[0-8191]":
- data = find_data_hostgroup(method)
- if url == "host":
- data = find_data_host(method)
- if url == "host?range=[0-65534]":
- data = find_data_host(method)
- if url == "host/associate":
- data = find_data_host_associate(method)
- if url == "host/associate?TYPE=21&ASSOCIATEOBJTYPE=14&ASSOCIATEOBJID=0":
- data = find_data_host_associate(method)
- return data
-
-
-def find_data_storagepool_snapshot(url, method):
- if url == "storagepool":
- data = """{"error":{"code":0},
- "data":[{"USERFREECAPACITY":"985661440",
- "ID":"0",
- "NAME":"OpenStack_Pool",
- "USERTOTALCAPACITY":"985661440"
- }]}"""
- if url == "snapshot":
- data = find_data_snapshot(method)
- if url == "snapshot/activate":
- data = """{"error":{"code":0},"data":[
- {"ID":11,"NAME":"SDFAJSDFLKJ"},
- {"ID":12,"NAME":"SDFAJSDFLKJ"}]}"""
-
- return data
-
-
-def find_data_luncpy_range_eth_port(url):
- if url == "luncopy":
- data = """{"error":{"code":0},
- "data":{"COPYSTOPTIME":"-1",
- "HEALTHSTATUS":"1",
- "NAME":"w1PSNvu6RumcZMmSh4/l+Q==",
- "RUNNINGSTATUS":"36",
- "DESCRIPTION":"w1PSNvu6RumcZMmSh4/l+Q==",
- "ID":"0","LUNCOPYTYPE":"1",
- "COPYPROGRESS":"0","COPYSPEED":"2",
- "TYPE":219,"COPYSTARTTIME":"-1"}}"""
-
- if url == "LUNCOPY?range=[0-100000]":
- data = """{"error":{"code":0},
- "data":[{"COPYSTOPTIME":"1372209335",
- "HEALTHSTATUS":"1",
- "NAME":"w1PSNvu6RumcZMmSh4/l+Q==",
- "RUNNINGSTATUS":"40",
- "DESCRIPTION":"w1PSNvu6RumcZMmSh4/l+Q==",
- "ID":"0","LUNCOPYTYPE":"1",
- "COPYPROGRESS":"100",
- "COPYSPEED":"2",
- "TYPE":219,
- "COPYSTARTTIME":"1372209329"}]}"""
-
- if url == "eth_port":
- data = """{"error":{"code":0},
- "data":[{"PARENTTYPE":209,
- "MACADDRESS":"00:22:a1:0a:79:57",
- "ETHNEGOTIATE":"-1","ERRORPACKETS":"0",
- "IPV4ADDR":"192.168.100.2",
- "IPV6GATEWAY":"","IPV6MASK":"0",
- "OVERFLOWEDPACKETS":"0","ISCSINAME":"P0",
- "HEALTHSTATUS":"1","ETHDUPLEX":"2",
- "ID":"16909568","LOSTPACKETS":"0",
- "TYPE":213,"NAME":"P0","INIORTGT":"4",
- "RUNNINGSTATUS":"10","IPV4GATEWAY":"",
- "BONDNAME":"","STARTTIME":"1371684218",
- "SPEED":"1000","ISCSITCPPORT":"0",
- "IPV4MASK":"255.255.0.0","IPV6ADDR":"",
- "LOGICTYPE":"0","LOCATION":"ENG0.B5.P0",
- "MTU":"1500","PARENTID":"1.5"}]}"""
- return data
-
-
-class Fake18000Common(rest_common.RestCommon):
-
- def __init__(self, configuration):
- rest_common.RestCommon.__init__(self, configuration)
- self.test_normal = True
- self.other_flag = True
- self.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()
+++ /dev/null
-# Copyright 2012 OpenStack Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-
-from oslo_config import cfg
-from oslo_utils import importutils
-
-from cinder import context
-from cinder import test
-
-
-CONF = cfg.CONF
-HUAWEI_ISCSI_MODULE = ("cinder.volume.drivers.huawei.huawei_18000."
- "Huawei18000ISCSIDriver")
-HUAWEI_FC_MODULE = ("cinder.volume.drivers.huawei.huawei_18000."
- "Huawei18000FCDriver")
-
-
-class VolumeDriverCompatibility(test.TestCase):
- """Test backwards compatibility for volume drivers."""
-
- def fake_update_cluster_status(self):
- return
-
- def setUp(self):
- super(VolumeDriverCompatibility, self).setUp()
- self.manager = importutils.import_object(CONF.volume_manager)
- self.context = context.get_admin_context()
-
- def _load_driver(self, driver):
- self.manager.__init__(volume_driver=driver)
-
- def _driver_module_name(self):
- return "%s.%s" % (self.manager.driver.__class__.__module__,
- self.manager.driver.__class__.__name__)
-
- def test_huawei_driver_iscsi_old(self):
- self._load_driver(
- 'cinder.volume.drivers.huawei.huawei_hvs.HuaweiHVSISCSIDriver')
- self.assertEqual(self._driver_module_name(), HUAWEI_ISCSI_MODULE)
-
- def test_huawei_driver_iscsi_new(self):
- self._load_driver(HUAWEI_ISCSI_MODULE)
- self.assertEqual(self._driver_module_name(), HUAWEI_ISCSI_MODULE)
-
- def test_huawei_driver_fc_old(self):
- self._load_driver(
- 'cinder.volume.drivers.huawei.huawei_hvs.HuaweiHVSFCDriver')
- self.assertEqual(self._driver_module_name(), HUAWEI_FC_MODULE)
-
- def test_huawei_driver_fc_new(self):
- self._load_driver(HUAWEI_FC_MODULE)
- self.assertEqual(self._driver_module_name(), HUAWEI_FC_MODULE)
+++ /dev/null
-
-# Copyright (c) 2013 Huawei Technologies Co., Ltd.
-# Copyright (c) 2012 OpenStack Foundation
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-"""
-Unit Tests for Huawei T and Dorado volume drivers.
-"""
-
-import os
-import shutil
-import socket
-import tempfile
-import time
-from xml.dom import minidom
-from xml.etree import ElementTree as ET
-
-import mox
-
-from cinder import context
-from cinder import exception
-from cinder import ssh_utils
-from cinder import test
-from cinder.volume import configuration as conf
-from cinder.volume.drivers import huawei
-from cinder.volume.drivers.huawei import huawei_utils
-from cinder.volume.drivers.huawei import ssh_common
-from cinder.volume import volume_types
-
-
-LUN_INFO = {'ID': None,
- 'Name': None,
- 'Size': None,
- 'LUN WWN': None,
- 'Status': None,
- 'Visible Capacity': None,
- 'Disk Pool ID': None,
- 'Cache Prefetch Strategy': None,
- 'Lun Type': None,
- 'Consumed Capacity': None,
- 'Pool ID': None,
- 'SnapShot ID': None,
- 'LunCopy ID': None,
- 'Owner Controller': None,
- 'Worker Controller': None,
- 'RAID Group ID': None}
-
-CLONED_LUN_INFO = {'ID': None,
- 'Name': None,
- 'Size': None,
- 'LUN WWN': None,
- 'Status': None,
- 'Visible Capacity': None,
- 'Disk Pool ID': None,
- 'Cache Prefetch Strategy': None,
- 'Lun Type': None,
- 'Consumed Capacity': None,
- 'Pool ID': None,
- 'SnapShot ID': None,
- 'LunCopy ID': None,
- 'Owner Controller': None,
- 'Worker Controller': None,
- 'RAID Group ID': None}
-
-SNAPSHOT_INFO = {'Source LUN ID': None,
- 'Source LUN Name': None,
- 'ID': None,
- 'Name': None,
- 'Type': 'Public',
- 'Status': None}
-
-MAP_INFO = {'Host Group ID': None,
- 'Host Group Name': None,
- 'Host ID': None,
- 'Host Name': None,
- 'Os Type': None,
- 'INI Port ID': None,
- 'INI Port Name': None,
- 'INI Port Info': None,
- 'INI Port WWN': None,
- 'INI Port Type': None,
- 'Link Status': None,
- 'LUN WWN': None,
- 'DEV LUN ID': None,
- 'Host LUN ID': None,
- 'CHAP status': False}
-
-HOST_PORT_INFO = {'ID': None,
- 'Name': None,
- 'Info': None,
- 'WWN': None,
- 'Type': None}
-
-LUNCOPY_INFO = {'Name': None,
- 'ID': None,
- 'Type': None,
- 'State': None,
- 'Status': None}
-
-LUNCOPY_SETTING = {'ID': '1',
- 'Type': 'FULL',
- 'State': 'Created',
- 'Status': 'Normal'}
-
-POOL_SETTING = {'ID': '2',
- 'Level': 'RAID6',
- 'Status': 'Normal',
- 'Free Capacity': '10240',
- 'Disk List': '0,1;0,2;0,3;0,4;0,5;0,6',
- 'Name': 'RAID_001',
- 'Type': 'Thick'}
-
-INITIATOR_SETTING = {'TargetIQN': 'iqn.2006-08.com.huawei:oceanspace:2103037:',
- 'TargetIQN-form': 'iqn.2006-08.com.huawei:oceanspace:'
- '2103037::1020001:192.168.100.2',
- 'Initiator Name': 'iqn.1993-08.debian:01:ec2bff7ac3a3',
- 'Initiator TargetIP': '192.168.100.2',
- 'WWN': ['2011666666666565']}
-
-FAKE_VOLUME = {'name': 'Volume-lele34fe-223f-dd33-4423-asdfghjklqwe',
- 'id': 'lele34fe-223f-dd33-4423-asdfghjklqwe',
- 'size': '2',
- 'provider_auth': None,
- 'volume_type_id': None,
- 'provider_location': None}
-
-FAKE_CLONED_VOLUME = {'name': 'Volume-jeje34fe-223f-dd33-4423-asdfghjklqwg',
- 'id': 'jeje34fe-223f-dd33-4423-asdfghjklqwg',
- 'size': '3',
- 'provider_auth': None,
- 'volume_type_id': None,
- 'provider_location': None}
-
-FAKE_SNAPSHOT = {'name': 'keke34fe-223f-dd33-4423-asdfghjklqwf',
- 'id': '223f-dd33-4423-asdfghjklqwf',
- 'volume_name': 'Volume-lele34fe-223f-dd33-4423-asdfghjklqwe',
- 'provider_location': None}
-
-FAKE_CONNECTOR = {'initiator': 'iqn.1993-08.debian:01:ec2bff7ac3a3',
- 'wwpns': ['1000000164s45126'],
- 'wwnns': ['2000666666666565'],
- 'host': 'fakehost',
- 'ip': '10.10.0.1'}
-
-RESPOOL_A_SIM = {'Size': '10240', 'Valid Size': '5120'}
-RESPOOL_B_SIM = {'Size': '10240', 'Valid Size': '10240'}
-VOLUME_SNAP_ID = {'vol': '0', 'vol_copy': '1', 'snap': '2'}
-
-cmd_error_list = [] # CLI cmds in this list will run failed
-Curr_test = [''] # show current testing driver
-
-
-class FakeChannel():
- def __init__(self):
- if Curr_test[0] == 'T':
- self.simu = HuaweiTCLIResSimulator()
- elif Curr_test[0] == 'Dorado5100':
- self.simu = HuaweiDorado5100CLIResSimulator()
- else:
- self.simu = HuaweiDorado2100G2CLIResSimulator()
-
- def resize_pty(self, width=80, height=24):
- pass
-
- def settimeout(self, time):
- pass
-
- def send(self, s):
- self.command = s
-
- def recv(self, nbytes):
- command = self.command.split()
- cmd = command[0]
- params = command[1:]
- if cmd in cmd_error_list:
- reset_error_flg(cmd)
- out = self.command[:-1] + 'ERROR' + '\nadmin:/>'
- return out.replace('\n', '\r\n')
- func_name = 'cli_' + cmd
- cli_func = getattr(self.simu, func_name)
- out = cli_func(params)
- out = self.command[:-1] + out + '\nadmin:/>'
- return out.replace('\n', '\r\n')
-
- def close(self):
- pass
-
-
-class FakeSSHClient():
- def invoke_shell(self):
- return FakeChannel()
-
- def get_transport(self):
-
- class transport():
- def __init__(self):
- self.sock = sock()
-
- class sock():
- def settimeout(self, time):
- pass
-
- return transport()
-
- def close(self):
- pass
-
-
-class FakeSSHPool():
- def __init__(self, ip, port, conn_timeout, login, password=None,
- *args, **kwargs):
- self.ip = ip
- self.port = port
- self.login = login
- self.password = password
-
- def create(self):
- return FakeSSHClient()
-
- def get(self):
- return FakeSSHClient()
-
- def put(self, ssh):
- pass
-
- def remove(self, ssh):
- pass
-
-
-def Fake_sleep(time):
- pass
-
-
-def Fake_change_file_mode(obj, filepath):
- pass
-
-
-def create_fake_conf_file(filename):
- doc = minidom.Document()
-
- config = doc.createElement('config')
- doc.appendChild(config)
-
- storage = doc.createElement('Storage')
- config.appendChild(storage)
- product = doc.createElement('Product')
- product_text = doc.createTextNode('T')
- product.appendChild(product_text)
- storage.appendChild(product)
- config.appendChild(storage)
- protocol = doc.createElement('Protocol')
- protocol_text = doc.createTextNode('iSCSI')
- protocol.appendChild(protocol_text)
- storage.appendChild(protocol)
- 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('123456')
- userpassword.appendChild(userpassword_text)
- storage.appendChild(userpassword)
-
- lun = doc.createElement('LUN')
- config.appendChild(lun)
- storagepool = doc.createElement('StoragePool')
- storagepool.setAttribute('Name', 'RAID_001')
- lun.appendChild(storagepool)
- luntype = doc.createElement('LUNType')
- luntype_text = doc.createTextNode('Thick')
- luntype.appendChild(luntype_text)
- lun.appendChild(luntype)
-
- iscsi = doc.createElement('iSCSI')
- config.appendChild(iscsi)
- defaulttargetip = doc.createElement('DefaultTargetIP')
- defaulttargetip_text = doc.createTextNode('192.168.100.1')
- 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)
-
- os_type = doc.createElement('Host')
- os_type.setAttribute('OSType', 'Linux')
- os_type.setAttribute('HostIP', '10.10.0.1')
- config.appendChild(os_type)
-
- tmp_file = open(filename, 'w')
- tmp_file.write(doc.toprettyxml(indent=''))
- tmp_file.close()
-
-
-def modify_conf(conf, item, val, attrib=None):
- tree = ET.parse(conf)
- root = tree.getroot()
- conf_item = root.find('%s' % item)
- if not attrib:
- conf_item.text = '%s' % val
- else:
- conf_item.attrib['%s' % attrib] = '%s' % val
- tree.write(conf, 'UTF-8')
-
-
-def set_error_flg(cmd):
- cmd_error_list.append(cmd)
-
-
-def reset_error_flg(cmd):
- cmd_error_list.remove(cmd)
-
-
-class HuaweiTCLIResSimulator():
- def _paras_name(self, params):
- index = params.index('-n')
- return params[index + 1]
-
- def cli_showsys(self, params):
- pass
-
- def cli_createlun(self, params):
- lun_type = ('THIN' if '-pool' in params else 'THICK')
- if LUN_INFO['ID'] is None:
- LUN_INFO['Name'] = self._paras_name(params)
- LUN_INFO['ID'] = VOLUME_SNAP_ID['vol']
- LUN_INFO['Size'] = FAKE_VOLUME['size']
- LUN_INFO['Lun Type'] = lun_type
- LUN_INFO['Owner Controller'] = 'A'
- LUN_INFO['Worker Controller'] = 'A'
- LUN_INFO['RAID Group ID'] = POOL_SETTING['ID']
- FAKE_VOLUME['provider_location'] = LUN_INFO['ID']
- else:
- CLONED_LUN_INFO['Name'] = self._paras_name(params)
- CLONED_LUN_INFO['ID'] = VOLUME_SNAP_ID['vol_copy']
- CLONED_LUN_INFO['Size'] = FAKE_CLONED_VOLUME['size']
- CLONED_LUN_INFO['Lun Type'] = lun_type
- CLONED_LUN_INFO['Owner Controller'] = 'A'
- CLONED_LUN_INFO['Worker Controller'] = 'A'
- CLONED_LUN_INFO['RAID Group ID'] = POOL_SETTING['ID']
- FAKE_CLONED_VOLUME['provider_location'] = CLONED_LUN_INFO['ID']
- out = 'command operates successfully'
- return out
-
- def cli_showlun(self, params):
- if '-lun' not in params:
- if LUN_INFO['ID'] is None:
- out = 'command operates successfully, but no information.'
- elif CLONED_LUN_INFO['ID'] is None:
- msg = """/>showlun
-===========================================================================
- LUN Information
----------------------------------------------------------------------------
- ID RAID Group ID Disk Pool ID Status Controller Visible Capacity(MB) \
- LUN Name Stripe Unit Size(KB) Lun Type
----------------------------------------------------------------------------
- %s %s -- Normal %s %s %s 64 THICK
-===========================================================================
-"""
- out = msg % (LUN_INFO['ID'], LUN_INFO['RAID Group ID'],
- LUN_INFO['Owner Controller'],
- str(int(LUN_INFO['Size']) * 1024),
- LUN_INFO['Name'])
- else:
- msg = """/>showlun
-============================================================================
- LUN Information
-----------------------------------------------------------------------------
- ID RAID Group ID Disk Pool ID Status Controller Visible Capacity(MB)\
- LUN Name Stripe Unit Size(KB) Lun Type
-----------------------------------------------------------------------------
- %s %s -- Normal %s %s %s 64 THICK
- %s %s -- Normal %s %s %s 64 THICK
-============================================================================
-"""
- out = msg % (
- LUN_INFO['ID'], LUN_INFO['RAID Group ID'],
- LUN_INFO['Owner Controller'],
- str(int(LUN_INFO['Size']) * 1024), LUN_INFO['Name'],
- CLONED_LUN_INFO['ID'], CLONED_LUN_INFO['RAID Group ID'],
- CLONED_LUN_INFO['Owner Controller'],
- str(int(CLONED_LUN_INFO['Size']) * 1024),
- CLONED_LUN_INFO['Name'])
-
- elif params[params.index('-lun') + 1] in VOLUME_SNAP_ID.values():
- msg = """/>showlun
-================================================
- LUN Information
-------------------------------------------------
- ID | %s
- Name | %s
- LUN WWN | --
- Visible Capacity | %s
- RAID GROUP ID | %s
- Owning Controller | %s
- Workong Controller | %s
- Lun Type | %s
- SnapShot ID | %s
- LunCopy ID | %s
-================================================
-"""
- out = msg % (
- (LUN_INFO['ID'], LUN_INFO['Name'],
- LUN_INFO['Visible Capacity'],
- LUN_INFO['RAID Group ID'], LUN_INFO['Owner Controller'],
- LUN_INFO['Worker Controller'], LUN_INFO['Lun Type'],
- LUN_INFO['SnapShot ID'], LUN_INFO['LunCopy ID'])
- if (params[params.index('-lun') + 1] ==
- VOLUME_SNAP_ID['vol']) else
- (CLONED_LUN_INFO['ID'], CLONED_LUN_INFO['Name'],
- CLONED_LUN_INFO['Visible Capacity'],
- CLONED_LUN_INFO['RAID Group ID'],
- CLONED_LUN_INFO['Owner Controller'],
- CLONED_LUN_INFO['Worker Controller'],
- CLONED_LUN_INFO['Lun Type'],
- CLONED_LUN_INFO['SnapShot ID'],
- CLONED_LUN_INFO['LunCopy ID']))
- else:
- out = 'ERROR: The object does not exist.'
- return out
-
- def cli_dellun(self, params):
- if params[params.index('-lun') + 1] == VOLUME_SNAP_ID['vol']:
- LUN_INFO['Name'] = None
- LUN_INFO['ID'] = None
- LUN_INFO['Size'] = None
- LUN_INFO['Lun Type'] = None
- LUN_INFO['LUN WWN'] = None
- LUN_INFO['Owner Controller'] = None
- LUN_INFO['Worker Controller'] = None
- LUN_INFO['RAID Group ID'] = None
- FAKE_VOLUME['provider_location'] = None
- else:
- CLONED_LUN_INFO['Name'] = None
- CLONED_LUN_INFO['ID'] = None
- CLONED_LUN_INFO['Size'] = None
- CLONED_LUN_INFO['Lun Type'] = None
- CLONED_LUN_INFO['LUN WWN'] = None
- CLONED_LUN_INFO['Owner Controller'] = None
- CLONED_LUN_INFO['Worker Controller'] = None
- CLONED_LUN_INFO['RAID Group ID'] = None
- CLONED_LUN_INFO['provider_location'] = None
- FAKE_CLONED_VOLUME['provider_location'] = None
- out = 'command operates successfully'
- return out
-
- def cli_showrg(self, params):
- msg = """/>showrg
-=====================================================================
- RAID Group Information
----------------------------------------------------------------------
- ID Level Status Free Capacity(MB) Disk List Name
----------------------------------------------------------------------
- 0 RAID6 Normal 1024 0,0;0,2; RAID003
- %s %s %s %s %s %s
-=====================================================================
--"""
- out = msg % (POOL_SETTING['ID'], POOL_SETTING['Level'],
- POOL_SETTING['Status'], POOL_SETTING['Free Capacity'],
- POOL_SETTING['Disk List'], POOL_SETTING['Name'])
- return out
-
- def cli_showpool(self, params):
- out = """/>showpool
-=====================================================================
- Pool Information
----------------------------------------------------------------------
- Level Status Available Capacity(MB) Disk List
----------------------------------------------------------------------
- RAID6 Normal %s 0,0;0,2;0,4;0,5;
-=====================================================================
--""" % POOL_SETTING['Free Capacity']
- return out
-
- def cli_createluncopy(self, params):
- src_id = params[params.index('-slun') + 1]
- tgt_id = params[params.index('-tlun') + 1]
- LUNCOPY_INFO['Name'] = 'OpenStack_%s_%s' % (src_id, tgt_id)
- LUNCOPY_INFO['ID'] = LUNCOPY_SETTING['ID']
- LUNCOPY_INFO['Type'] = LUNCOPY_SETTING['Type']
- LUNCOPY_INFO['State'] = LUNCOPY_SETTING['State']
- LUNCOPY_INFO['Status'] = LUNCOPY_SETTING['Status']
- out = 'command operates successfully'
- return out
-
- def cli_chgluncopystatus(self, params):
- LUNCOPY_INFO['State'] = 'Start'
- out = 'command operates successfully'
- return out
-
- def cli_showluncopy(self, params):
- if LUNCOPY_INFO['State'] == 'Start':
- LUNCOPY_INFO['State'] = 'Copying'
- elif LUNCOPY_INFO['State'] == 'Copying':
- LUNCOPY_INFO['State'] = 'Complete'
- msg = """/>showluncopy
-============================================================================
- LUN Copy Information
-----------------------------------------------------------------------------
- LUN Copy Name LUN Copy ID Type LUN Copy State LUN Copy Status
-----------------------------------------------------------------------------
- %s %s %s %s %s
-============================================================================
-"""
- out = msg % (LUNCOPY_INFO['Name'], LUNCOPY_INFO['ID'],
- LUNCOPY_INFO['Type'], LUNCOPY_INFO['State'],
- LUNCOPY_INFO['Status'])
- return out
-
- def cli_delluncopy(self, params):
- LUNCOPY_INFO['Name'] = None
- LUNCOPY_INFO['ID'] = None
- LUNCOPY_INFO['Type'] = None
- LUNCOPY_INFO['State'] = None
- LUNCOPY_INFO['Status'] = None
- out = 'command operates successfully'
- return out
-
- def cli_createsnapshot(self, params):
- SNAPSHOT_INFO['Source LUN ID'] = LUN_INFO['ID']
- SNAPSHOT_INFO['Source LUN Name'] = LUN_INFO['Name']
- SNAPSHOT_INFO['ID'] = VOLUME_SNAP_ID['snap']
- SNAPSHOT_INFO['Name'] = self._paras_name(params)
- SNAPSHOT_INFO['Status'] = 'Disable'
- out = 'command operates successfully'
- return out
-
- def cli_showsnapshot(self, params):
- if SNAPSHOT_INFO['ID'] is None:
- out = 'command operates successfully, but no information.'
- else:
- out = """/>showsnapshot
-==========================================================================
- Snapshot Information
---------------------------------------------------------------------------
- Name ID Type Status Time Stamp
---------------------------------------------------------------------------
- %s %s Public %s 2013-01-15 14:21:13
-==========================================================================
-""" % (SNAPSHOT_INFO['Name'], SNAPSHOT_INFO['ID'], SNAPSHOT_INFO['Status'])
- return out
-
- def cli_actvsnapshot(self, params):
- SNAPSHOT_INFO['Status'] = 'Active'
- FAKE_SNAPSHOT['provider_location'] = SNAPSHOT_INFO['ID']
- out = 'command operates successfully'
- return out
-
- def cli_disablesnapshot(self, params):
- SNAPSHOT_INFO['Status'] = 'Disable'
- out = 'command operates successfully'
- return out
-
- def cli_delsnapshot(self, params):
- SNAPSHOT_INFO['Source LUN ID'] = None
- SNAPSHOT_INFO['Source LUN Name'] = None
- SNAPSHOT_INFO['ID'] = None
- SNAPSHOT_INFO['Name'] = None
- SNAPSHOT_INFO['Status'] = None
- FAKE_SNAPSHOT['provider_location'] = None
- out = 'command operates successfully'
- return out
-
- def cli_showrespool(self, params):
- msg = """/>showrespool
-===========================================================================
- Resource Pool Information
----------------------------------------------------------------------------
- Pool ID Size(MB) Usage(MB) Valid Size(MB) Alarm Threshold
----------------------------------------------------------------------------
- A %s 0.0 %s 80
- B %s 0.0 %s 80
-===========================================================================
--"""
- out = msg % (RESPOOL_A_SIM['Size'], RESPOOL_A_SIM['Valid Size'],
- RESPOOL_B_SIM['Size'], RESPOOL_B_SIM['Valid Size'])
- return out
-
- def cli_showiscsitgtname(self, params):
- iqn = INITIATOR_SETTING['TargetIQN']
- out = """/>showiscsitgtname
-===================================================================
- ISCSI Name
--------------------------------------------------------------------
- Iscsi Name | %s
-===================================================================
-""" % iqn
- return out
-
- def cli_showiscsiip(self, params):
- out = """/>showiscsiip
-============================================================================
- iSCSI IP Information
-----------------------------------------------------------------------------
- Controller ID Interface Module ID Port ID IP Address Mask
-----------------------------------------------------------------------------
- B 0 P1 %s 255.255.255.0
-============================================================================
--""" % INITIATOR_SETTING['Initiator TargetIP']
- return out
-
- def cli_showhostgroup(self, params):
- if MAP_INFO['Host Group ID'] is None:
- out = """/>showhostgroup
-============================================================
- Host Group Information
-------------------------------------------------------------
- Host Group ID Name File Engine Cluster
-------------------------------------------------------------
- 0 Default Group NO
-============================================================
-"""
- else:
- out = """/>showhostgroup
-============================================================
- Host Group Information
-------------------------------------------------------------
- Host Group ID Name File Engine Cluster
-------------------------------------------------------------
- 0 Default Group NO
- %s %s NO
-============================================================
-""" % (MAP_INFO['Host Group ID'], MAP_INFO['Host Group Name'])
- return out
-
- def cli_createhostgroup(self, params):
- MAP_INFO['Host Group ID'] = '1'
- MAP_INFO['Host Group Name'] = 'HostGroup_OpenStack'
- out = 'command operates successfully'
- return out
-
- def cli_showhost(self, params):
- if MAP_INFO['Host ID'] is None:
- out = 'command operates successfully, but no information.'
- else:
- out = """/>showhost
-=======================================================
- Host Information
--------------------------------------------------------
- Host ID Host Name Host Group ID Os Type
--------------------------------------------------------
- %s %s %s Linux
-=======================================================
-""" % (MAP_INFO['Host ID'], MAP_INFO['Host Name'], MAP_INFO['Host Group ID'])
- return out
-
- def cli_addhost(self, params):
- MAP_INFO['Host ID'] = '1'
- MAP_INFO['Host Name'] = 'Host_' + FAKE_CONNECTOR['host']
- MAP_INFO['Os Type'] = 'Linux'
- out = 'command operates successfully'
- return out
-
- def cli_delhost(self, params):
- MAP_INFO['Host ID'] = None
- MAP_INFO['Host Name'] = None
- MAP_INFO['Os Type'] = None
- out = 'command operates successfully'
- return out
-
- def cli_showiscsiini(self, params):
- if HOST_PORT_INFO['ID'] is None:
- out = 'Error: The parameter is wrong.'
- else:
- out = """/>showiscsiini
-========================================================
- Initiator Information
---------------------------------------------------------
- Initiator Name Chap Status
---------------------------------------------------------
- %s Disable
-========================================================
-""" % HOST_PORT_INFO['Info']
- return out
-
- def cli_addiscsiini(self, params):
- HOST_PORT_INFO['ID'] = '1'
- HOST_PORT_INFO['Name'] = 'iSCSIInitiator001'
- HOST_PORT_INFO['Info'] = INITIATOR_SETTING['Initiator Name']
- HOST_PORT_INFO['Type'] = 'ISCSITGT'
- out = 'command operates successfully'
- return out
-
- def cli_deliscsiini(self, params):
- HOST_PORT_INFO['ID'] = None
- HOST_PORT_INFO['Name'] = None
- HOST_PORT_INFO['Info'] = None
- HOST_PORT_INFO['Type'] = None
- out = 'command operates successfully'
- return out
-
- def cli_showhostport(self, params):
- if MAP_INFO['INI Port ID'] is None:
- out = 'command operates successfully, but no information.'
- else:
- msg = """/>showhostport
-============================================================================
- Host Port Information
-----------------------------------------------------------------------------
-Port ID Port Name Port Information Port Type Host ID Link Status \
-Multipath Type
-----------------------------------------------------------------------------
- %s %s %s %s %s Unconnected Default
-============================================================================
-"""
- out = msg % (MAP_INFO['INI Port ID'], MAP_INFO['INI Port Name'],
- MAP_INFO['INI Port Info'], MAP_INFO['INI Port Type'],
- MAP_INFO['Host ID'])
- return out
-
- def cli_addhostport(self, params):
- MAP_INFO['INI Port ID'] = HOST_PORT_INFO['ID']
- MAP_INFO['INI Port Name'] = HOST_PORT_INFO['Name']
- MAP_INFO['INI Port Info'] = HOST_PORT_INFO['Info']
- MAP_INFO['INI Port Type'] = HOST_PORT_INFO['Type']
- out = 'command operates successfully'
- return out
-
- def cli_delhostport(self, params):
- MAP_INFO['INI Port ID'] = None
- MAP_INFO['INI Port Name'] = None
- MAP_INFO['INI Port Info'] = None
- MAP_INFO['INI Port Type'] = None
- HOST_PORT_INFO['ID'] = None
- HOST_PORT_INFO['Name'] = None
- HOST_PORT_INFO['Info'] = None
- HOST_PORT_INFO['Type'] = None
- out = 'command operates successfully'
- return out
-
- def cli_showhostmap(self, params):
- if MAP_INFO['DEV LUN ID'] is None:
- out = 'command operates successfully, but no information.'
- else:
- msg = """/>showhostmap
-===========================================================================
- Map Information
----------------------------------------------------------------------------
- Map ID Working Controller Dev LUN ID LUN WWN Host LUN ID Mapped to\
- RAID ID Dev LUN Cap(MB) Map Type Whether Command LUN Pool ID
-----------------------------------------------------------------------------
- 2147483649 %s %s %s %s Host: %s %s %s HOST No --
-============================================================================
-"""
- out = msg % (LUN_INFO['Worker Controller'], LUN_INFO['ID'],
- LUN_INFO['LUN WWN'], MAP_INFO['Host LUN ID'],
- MAP_INFO['Host ID'], LUN_INFO['RAID Group ID'],
- str(int(LUN_INFO['Size']) * 1024))
- return out
-
- def cli_addhostmap(self, params):
- MAP_INFO['DEV LUN ID'] = LUN_INFO['ID']
- MAP_INFO['LUN WWN'] = LUN_INFO['LUN WWN']
- MAP_INFO['Host LUN ID'] = '2'
- MAP_INFO['Link Status'] = 'Linked'
- out = 'command operates successfully'
- return out
-
- def cli_delhostmap(self, params):
- if MAP_INFO['Link Status'] == 'Linked':
- MAP_INFO['Link Status'] = 'Deleting'
- out = 'there are IOs accessing the system, please try later'
- else:
- MAP_INFO['Link Status'] = None
- MAP_INFO['DEV LUN ID'] = None
- MAP_INFO['LUN WWN'] = None
- MAP_INFO['Host LUN ID'] = None
- out = 'command operates successfully'
- return out
-
- def cli_showfreeport(self, params):
- out = """/>showfreeport
-=======================================================================
- Host Free Port Information
------------------------------------------------------------------------
- WWN Or MAC Type Location Connection Status
------------------------------------------------------------------------
- 1000000164s45126 FC Primary Controller Connected
-=======================================================================
-"""
- HOST_PORT_INFO['ID'] = '2'
- HOST_PORT_INFO['Name'] = 'FCInitiator001'
- HOST_PORT_INFO['Info'] = '1000000164s45126'
- HOST_PORT_INFO['Type'] = 'FC'
- return out
-
- def cli_showhostpath(self, params):
- host = params[params.index('-host') + 1]
- out = """/>showhostpath -host 1
-=======================================
- Multi Path Information
----------------------------------------
- Host ID | %s
- Controller ID | B
- Port Type | FC
- Initiator WWN | 1000000164s45126
- Target WWN | %s
- Host Port ID | 0
- Link Status | Normal
-=======================================
-""" % (host, INITIATOR_SETTING['WWN'][0])
- return out
-
- def cli_showfcmode(self, params):
- out = """/>showfcport
-=========================================================================
- FC Port Topology Mode
--------------------------------------------------------------------------
- Controller ID Interface Module ID Port ID WWN Current Mode
--------------------------------------------------------------------------
- B 1 P0 %s --
-=========================================================================
--""" % INITIATOR_SETTING['WWN'][0]
- return out
-
- def cli_chglun(self, params):
- if params[params.index('-lun') + 1] == VOLUME_SNAP_ID['vol']:
- LUN_INFO['Owner Controller'] = 'B'
- else:
- CLONED_LUN_INFO['Owner Controller'] = 'B'
- out = 'command operates successfully'
- return out
-
- def cli_addluntoextlun(self, params):
- LUN_INFO['Size'] = int(LUN_INFO['Size']) + int(CLONED_LUN_INFO['Size'])
- out = 'command operates successfully'
- return out
-
- def cli_rmlunfromextlun(self, patams):
- LUN_INFO['Size'] = int(LUN_INFO['Size']) - int(CLONED_LUN_INFO['Size'])
- out = 'command operates successfully'
- return out
-
-
-class HuaweiDorado5100CLIResSimulator(HuaweiTCLIResSimulator):
- def cli_showsys(self, params):
- out = """/>showsys
-=============================================================
- System Information
--------------------------------------------------------------
- System Name | SN_Dorado5100
- Device Type | Oceanstor Dorado5100
- Current System Mode | Double Controllers Normal
- Mirroring Link Status | Link Up
- Location |
- Time | 2013-01-01 01:01:01
- Product Version | V100R001C00
-=============================================================
-"""
- return out
-
- def cli_showlun(self, params):
- if '-lun' not in params:
- if LUN_INFO['ID'] is None:
- out = 'command operates successfully, but no information.'
- elif CLONED_LUN_INFO['ID'] is None:
- msg = """/>showlun
-===========================================================================
- LUN Information
----------------------------------------------------------------------------
- ID RAIDgroup ID Status Controller Visible Capacity(MB) LUN Name..\
- Strip Unit Size(KB) Lun Type
----------------------------------------------------------------------------
- %s %s Normal %s %s %s 64 THICK
-===========================================================================
-"""
- out = msg % (LUN_INFO['ID'], LUN_INFO['RAID Group ID'],
- LUN_INFO['Owner Controller'],
- str(int(LUN_INFO['Size']) * 1024),
- LUN_INFO['Name'])
- else:
- msg = """/>showlun
-===========================================================================
- LUN Information
----------------------------------------------------------------------------
- ID RAIDgroup ID Status Controller Visible Capacity(MB) LUN Name \
- Strip Unit Size(KB) Lun Type
----------------------------------------------------------------------------
- %s %s Normal %s %s %s 64 THICK
- %s %s Norma %s %s %s 64 THICK
-===========================================================================
-"""
- out = msg % (LUN_INFO['ID'], LUN_INFO['RAID Group ID'],
- LUN_INFO['Owner Controller'],
- str(int(LUN_INFO['Size']) * 1024),
- LUN_INFO['Name'], CLONED_LUN_INFO['ID'],
- CLONED_LUN_INFO['RAID Group ID'],
- CLONED_LUN_INFO['Owner Controller'],
- str(int(CLONED_LUN_INFO['Size']) * 1024),
- CLONED_LUN_INFO['Name'])
- elif params[params.index('-lun') + 1] in VOLUME_SNAP_ID.values():
- msg = """/>showlun
-================================================
- LUN Information
-------------------------------------------------
- ID | %s
- Name | %s
- LUN WWN | --
- Visible Capacity | %s
- RAID GROUP ID | %s
- Owning Controller | %s
- Workong Controller | %s
- Lun Type | %s
- SnapShot ID | %s
- LunCopy ID | %s
-================================================
-"""
- out = msg % (
- (LUN_INFO['ID'], LUN_INFO['Name'],
- LUN_INFO['Visible Capacity'],
- LUN_INFO['RAID Group ID'], LUN_INFO['Owner Controller'],
- LUN_INFO['Worker Controller'], LUN_INFO['Lun Type'],
- LUN_INFO['SnapShot ID'], LUN_INFO['LunCopy ID'])
- if (params[params.index('-lun') + 1] ==
- VOLUME_SNAP_ID['vol']) else
- (CLONED_LUN_INFO['ID'], CLONED_LUN_INFO['Name'],
- CLONED_LUN_INFO['Visible Capacity'],
- CLONED_LUN_INFO['RAID Group ID'],
- CLONED_LUN_INFO['Owner Controller'],
- CLONED_LUN_INFO['Worker Controller'],
- CLONED_LUN_INFO['Lun Type'], CLONED_LUN_INFO['SnapShot ID'],
- CLONED_LUN_INFO['LunCopy ID']))
- else:
- out = 'ERROR: The object does not exist.'
- return out
-
-
-class HuaweiDorado2100G2CLIResSimulator(HuaweiTCLIResSimulator):
- def cli_showsys(self, params):
- out = """/>showsys
-==========================================================================
- System Information
---------------------------------------------------------------------------
- System Name | SN_Dorado2100_G2
- Device Type | Oceanstor Dorado2100 G2
- Current System Mode | Double Controllers Normal
- Mirroring Link Status | Link Up
- Location |
- Time | 2013-01-01 01:01:01
- Product Version | V100R001C00
-===========================================================================
-"""
- return out
-
- def cli_createlun(self, params):
- lun_type = ('THIN' if params[params.index('-type') + 1] == '2' else
- 'THICK')
- if LUN_INFO['ID'] is None:
- LUN_INFO['Name'] = self._paras_name(params)
- LUN_INFO['ID'] = VOLUME_SNAP_ID['vol']
- LUN_INFO['Size'] = FAKE_VOLUME['size']
- LUN_INFO['Lun Type'] = lun_type
- LUN_INFO['Owner Controller'] = 'A'
- LUN_INFO['Worker Controller'] = 'A'
- LUN_INFO['RAID Group ID'] = POOL_SETTING['ID']
- FAKE_VOLUME['provider_location'] = LUN_INFO['ID']
- else:
- CLONED_LUN_INFO['Name'] = self._paras_name(params)
- CLONED_LUN_INFO['ID'] = VOLUME_SNAP_ID['vol_copy']
- CLONED_LUN_INFO['Size'] = FAKE_CLONED_VOLUME['size']
- CLONED_LUN_INFO['Lun Type'] = lun_type
- CLONED_LUN_INFO['Owner Controller'] = 'A'
- CLONED_LUN_INFO['Worker Controller'] = 'A'
- CLONED_LUN_INFO['RAID Group ID'] = POOL_SETTING['ID']
- CLONED_LUN_INFO['provider_location'] = CLONED_LUN_INFO['ID']
- FAKE_CLONED_VOLUME['provider_location'] = CLONED_LUN_INFO['ID']
- out = 'command operates successfully'
- return out
-
- def cli_showlun(self, params):
- if '-lun' not in params:
- if LUN_INFO['ID'] is None:
- out = 'command operates successfully, but no information.'
- elif CLONED_LUN_INFO['ID'] is None:
- msg = """/>showlun
-===========================================================================
- LUN Information
----------------------------------------------------------------------------
- ID Status Controller Visible Capacity(MB) LUN Name Lun Type
----------------------------------------------------------------------------
- %s Normal %s %s %s THICK
-===========================================================================
-"""
- out = msg % (LUN_INFO['ID'], LUN_INFO['Owner Controller'],
- str(int(LUN_INFO['Size']) * 1024),
- LUN_INFO['Name'])
- else:
- msg = """/>showlun
-===========================================================================
- LUN Information
----------------------------------------------------------------------------
- ID Status Controller Visible Capacity(MB) LUN Name Lun Type
----------------------------------------------------------------------------
- %s Normal %s %s %s THICK
- %s Normal %s %s %s THICK
-===========================================================================
-"""
- out = msg % (LUN_INFO['ID'], LUN_INFO['Owner Controller'],
- str(int(LUN_INFO['Size']) * 1024),
- LUN_INFO['Name'],
- CLONED_LUN_INFO['ID'],
- CLONED_LUN_INFO['Owner Controller'],
- str(int(CLONED_LUN_INFO['Size']) * 1024),
- CLONED_LUN_INFO['Name'])
-
- elif params[params.index('-lun') + 1] in VOLUME_SNAP_ID.values():
- msg = """/>showlun
-================================================
- LUN Information
-------------------------------------------------
- ID | %s
- Name | %s
- LUN WWN | --
- Visible Capacity | %s
- RAID GROUP ID | %s
- Owning Controller | %s
- Workong Controller | %s
- Lun Type | %s
- SnapShot ID | %s
- LunCopy ID | %s
-================================================
-"""
- out = msg % (
- (LUN_INFO['ID'], LUN_INFO['Name'],
- LUN_INFO['Visible Capacity'],
- LUN_INFO['RAID Group ID'], LUN_INFO['Owner Controller'],
- LUN_INFO['Worker Controller'], LUN_INFO['Lun Type'],
- LUN_INFO['SnapShot ID'], LUN_INFO['LunCopy ID'])
- if params[params.index('-lun')] == VOLUME_SNAP_ID['vol'] else
- (CLONED_LUN_INFO['ID'], CLONED_LUN_INFO['Name'],
- CLONED_LUN_INFO['Visible Capacity'],
- CLONED_LUN_INFO['RAID Group ID'],
- CLONED_LUN_INFO['Owner Controller'],
- CLONED_LUN_INFO['Worker Controller'],
- CLONED_LUN_INFO['Lun Type'], CLONED_LUN_INFO['SnapShot ID'],
- CLONED_LUN_INFO['LunCopy ID']))
-
- else:
- out = 'ERROR: The object does not exist.'
-
- return out
-
-
-class HuaweiTISCSIDriverTestCase(test.TestCase):
- def __init__(self, *args, **kwargs):
- super(HuaweiTISCSIDriverTestCase, self).__init__(*args, **kwargs)
-
- def setUp(self):
- super(HuaweiTISCSIDriverTestCase, self).setUp()
-
- self.tmp_dir = tempfile.mkdtemp()
- self.addCleanup(shutil.rmtree, self.tmp_dir)
-
- self.fake_conf_file = self.tmp_dir + '/cinder_huawei_conf.xml'
- self.addCleanup(os.remove, self.fake_conf_file)
-
- create_fake_conf_file(self.fake_conf_file)
- self.configuration = mox.MockObject(conf.Configuration)
- self.configuration.cinder_huawei_conf_file = self.fake_conf_file
- self.configuration.append_config_values(mox.IgnoreArg())
-
- self.stubs.Set(time, 'sleep', Fake_sleep)
- self.stubs.Set(ssh_utils, 'SSHPool', FakeSSHPool)
- self.stubs.Set(ssh_common.TseriesCommon, '_change_file_mode',
- Fake_change_file_mode)
- self._init_driver()
-
- def _init_driver(self):
- Curr_test[0] = 'T'
- self.driver = huawei.HuaweiVolumeDriver(
- configuration=self.configuration)
- self.driver.do_setup(None)
-
- def test_conf_invalid(self):
- # Test config file not found
- tmp_fonf_file = '/xxx/cinder_huawei_conf.xml'
- tmp_configuration = mox.MockObject(conf.Configuration)
- tmp_configuration.cinder_huawei_conf_file = tmp_fonf_file
- tmp_configuration.append_config_values(mox.IgnoreArg())
- self.assertRaises(IOError,
- huawei.HuaweiVolumeDriver,
- configuration=tmp_configuration)
- # Test Product and Protocol invalid
- tmp_dict = {'Storage/Product': 'T', 'Storage/Protocol': 'iSCSI'}
- for k, v in tmp_dict.items():
- modify_conf(self.fake_conf_file, k, 'xx')
- self.assertRaises(exception.InvalidInput,
- huawei.HuaweiVolumeDriver,
- configuration=self.configuration)
- modify_conf(self.fake_conf_file, k, v)
- # Test ctr ip, UserName and password unspecified
- tmp_dict = {'Storage/ControllerIP0': '10.10.10.1',
- 'Storage/ControllerIP1': '10.10.10.2',
- 'Storage/UserName': 'admin',
- 'Storage/UserPassword': '123456'}
- for k, v in tmp_dict.items():
- modify_conf(self.fake_conf_file, k, '')
- tmp_driver = huawei.HuaweiVolumeDriver(
- configuration=self.configuration)
- self.assertRaises(exception.InvalidInput,
- tmp_driver.do_setup, None)
- modify_conf(self.fake_conf_file, k, v)
- # Test StoragePool unspecified
- modify_conf(self.fake_conf_file, 'LUN/StoragePool', '', attrib='Name')
- tmp_driver = huawei. HuaweiVolumeDriver(
- configuration=self.configuration)
- self.assertRaises(exception.InvalidInput,
- tmp_driver.do_setup, None)
- modify_conf(self.fake_conf_file, 'LUN/StoragePool', 'RAID_001',
- attrib='Name')
- # Test LUN type invalid
- modify_conf(self.fake_conf_file, 'LUN/LUNType', 'thick')
- tmp_driver = huawei.HuaweiVolumeDriver(
- configuration=self.configuration)
- tmp_driver.do_setup(None)
- self.assertRaises(exception.InvalidInput,
- tmp_driver.create_volume, FAKE_VOLUME)
- modify_conf(self.fake_conf_file, 'LUN/LUNType', 'Thick')
- # Test OSType invalid
- modify_conf(self.fake_conf_file, 'Host', 'invalid_type',
- attrib='OSType')
- tmp_driver = huawei.HuaweiVolumeDriver(
- configuration=self.configuration)
- self.assertRaises(exception.InvalidInput,
- tmp_driver.do_setup, None)
- modify_conf(self.fake_conf_file, 'Host', 'Linux', attrib='OSType')
- # Test TargetIP not found
- modify_conf(self.fake_conf_file, 'iSCSI/DefaultTargetIP', '')
- modify_conf(self.fake_conf_file, 'iSCSI/Initiator', '', attrib='Name')
- tmp_driver = huawei.HuaweiVolumeDriver(
- configuration=self.configuration)
- tmp_driver.do_setup(None)
- tmp_driver.create_volume(FAKE_VOLUME)
- self.assertRaises(exception.InvalidInput,
- tmp_driver.initialize_connection,
- FAKE_VOLUME, FAKE_CONNECTOR)
- tmp_driver.delete_volume(FAKE_VOLUME)
- modify_conf(self.fake_conf_file, 'iSCSI/DefaultTargetIP',
- '192.168.100.1')
- modify_conf(self.fake_conf_file, 'iSCSI/Initiator',
- 'iqn.1993-08.debian:01:ec2bff7ac3a3', attrib='Name')
-
- def test_volume_type(self):
- ctxt = context.get_admin_context()
- extra_specs = {'drivers:LUNType': 'Thin'}
- type_ref = volume_types.create(ctxt, 'THIN', extra_specs)
- FAKE_VOLUME['volume_type_id'] = type_ref['id']
- self.driver.create_volume(FAKE_VOLUME)
- self.assertEqual(LUN_INFO["ID"], VOLUME_SNAP_ID['vol'])
- self.assertEqual(LUN_INFO['Lun Type'], 'THIN')
- self.driver.delete_volume(FAKE_VOLUME)
- FAKE_VOLUME['volume_type_id'] = None
-
- # Test volume type invalid
- extra_specs = {'drivers:InvalidLUNType': 'Thin'}
- type_ref = volume_types.create(ctxt, 'Invalid_THIN', extra_specs)
- FAKE_VOLUME['volume_type_id'] = type_ref['id']
- self.driver.create_volume(FAKE_VOLUME)
- self.assertEqual(LUN_INFO["ID"], VOLUME_SNAP_ID['vol'])
- self.assertNotEqual(LUN_INFO['Lun Type'], 'THIN')
- self.driver.delete_volume(FAKE_VOLUME)
- FAKE_VOLUME['volume_type_id'] = None
-
- def test_create_delete_volume(self):
- # Test create lun cli exception
- set_error_flg('createlun')
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.create_volume, FAKE_VOLUME)
-
- ret = self.driver.create_volume(FAKE_VOLUME)
- self.assertEqual(LUN_INFO['ID'], VOLUME_SNAP_ID['vol'])
- self.assertEqual(ret['provider_location'], LUN_INFO['ID'])
-
- # Test delete lun cli exception
- set_error_flg('dellun')
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.delete_volume, FAKE_VOLUME)
-
- self.driver.delete_volume(FAKE_VOLUME)
- self.assertIsNone(LUN_INFO['ID'])
- self.assertIsNone(FAKE_VOLUME['provider_location'])
-
- def test_create_delete_cloned_volume(self):
- # Test no source volume
- self.assertRaises(exception.VolumeNotFound,
- self.driver.create_cloned_volume,
- FAKE_CLONED_VOLUME, FAKE_VOLUME)
-
- self.driver.create_volume(FAKE_VOLUME)
- # Test create luncopy failed
- self.assertEqual(LUN_INFO['ID'], VOLUME_SNAP_ID['vol'])
- set_error_flg('createluncopy')
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.create_cloned_volume,
- FAKE_CLONED_VOLUME, FAKE_VOLUME)
- self.assertEqual(CLONED_LUN_INFO['ID'], VOLUME_SNAP_ID['vol_copy'])
- self.driver.delete_volume(FAKE_CLONED_VOLUME)
- self.assertIsNone(CLONED_LUN_INFO['ID'])
- # Test start luncopy failed
- self.assertEqual(LUN_INFO['ID'], VOLUME_SNAP_ID['vol'])
- set_error_flg('chgluncopystatus')
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.create_cloned_volume,
- FAKE_CLONED_VOLUME, FAKE_VOLUME)
- self.assertIsNone(CLONED_LUN_INFO['ID'])
- self.assertEqual(LUN_INFO['ID'], VOLUME_SNAP_ID['vol'])
- # Test luncopy status abnormal
- LUNCOPY_SETTING['Status'] = 'Disable'
- self.assertEqual(LUN_INFO['ID'], '0')
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.create_cloned_volume,
- FAKE_CLONED_VOLUME, FAKE_VOLUME)
- self.assertIsNone(CLONED_LUN_INFO['ID'])
- self.assertEqual(LUN_INFO['ID'], VOLUME_SNAP_ID['vol'])
- LUNCOPY_SETTING['Status'] = 'Normal'
- # Test delete luncopy failed
- set_error_flg('delluncopy')
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.create_cloned_volume,
- FAKE_CLONED_VOLUME, FAKE_VOLUME)
- self.assertEqual(CLONED_LUN_INFO['ID'], VOLUME_SNAP_ID['vol_copy'])
- self.driver.delete_volume(FAKE_CLONED_VOLUME)
- self.assertIsNone(CLONED_LUN_INFO['ID'])
- # need to clean up LUNCopy
- LUNCOPY_INFO['Name'] = None
- LUNCOPY_INFO['ID'] = None
- LUNCOPY_INFO['Type'] = None
- LUNCOPY_INFO['State'] = None
- LUNCOPY_INFO['Status'] = None
-
- # Test normal create and delete cloned volume
- self.assertEqual(LUN_INFO['ID'], VOLUME_SNAP_ID['vol'])
- ret = self.driver.create_cloned_volume(FAKE_CLONED_VOLUME, FAKE_VOLUME)
- self.assertEqual(CLONED_LUN_INFO['ID'], VOLUME_SNAP_ID['vol_copy'])
- self.assertEqual(ret['provider_location'], CLONED_LUN_INFO['ID'])
- self.driver.delete_volume(FAKE_CLONED_VOLUME)
- self.assertIsNone(CLONED_LUN_INFO['ID'])
- self.assertIsNone(FAKE_CLONED_VOLUME['provider_location'])
- self.driver.delete_volume(FAKE_VOLUME)
- self.assertIsNone(LUN_INFO['ID'])
-
- def test_extend_volume(self):
- VOLUME_SIZE = 5
- # Test no extended volume
- self.assertRaises(exception.VolumeNotFound,
- self.driver.extend_volume, FAKE_VOLUME, VOLUME_SIZE)
-
- self.driver.create_volume(FAKE_VOLUME)
- self.assertEqual(LUN_INFO['Size'], '2')
- # Test extend volume cli exception
- set_error_flg('addluntoextlun')
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.extend_volume, FAKE_VOLUME, VOLUME_SIZE)
- self.assertEqual(CLONED_LUN_INFO['Name'], None)
-
- self.driver.extend_volume(FAKE_VOLUME, VOLUME_SIZE)
- self.assertEqual(LUN_INFO['Size'], VOLUME_SIZE)
- self.driver.delete_volume(FAKE_VOLUME)
- self.assertEqual(LUN_INFO['Name'], None)
-
- def test_create_delete_snapshot(self):
- # Test no resource pool
- RESPOOL_A_SIM['Valid Size'] = '0'
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.create_snapshot, FAKE_SNAPSHOT)
- RESPOOL_A_SIM['Valid Size'] = '5120'
- # Test no source volume
- self.assertRaises(exception.VolumeNotFound,
- self.driver.create_snapshot, FAKE_SNAPSHOT)
- # Test create snapshot cli exception
- self.driver.create_volume(FAKE_VOLUME)
- set_error_flg('createsnapshot')
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.create_snapshot,
- FAKE_SNAPSHOT)
- self.assertEqual(LUN_INFO['ID'], VOLUME_SNAP_ID['vol'])
- # Test active snapshot failed
- set_error_flg('actvsnapshot')
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.create_snapshot,
- FAKE_SNAPSHOT)
- self.assertIsNone(SNAPSHOT_INFO['ID'])
- self.assertIsNone(SNAPSHOT_INFO['Status'])
- # Test disable snapshot failed
- set_error_flg('disablesnapshot')
- self.driver.create_snapshot(FAKE_SNAPSHOT)
- self.assertEqual(SNAPSHOT_INFO['ID'], VOLUME_SNAP_ID['snap'])
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.delete_snapshot,
- FAKE_SNAPSHOT)
- self.assertEqual(SNAPSHOT_INFO['Status'], 'Active')
- # Test delsnapshot failed
- set_error_flg('delsnapshot')
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.delete_snapshot,
- FAKE_SNAPSHOT)
- self.assertEqual(SNAPSHOT_INFO['Status'], 'Disable')
-
- self.driver.delete_snapshot(FAKE_SNAPSHOT)
-
- # Test normal create and delete snapshot
- self.driver.create_volume(FAKE_VOLUME)
- ret = self.driver.create_snapshot(FAKE_SNAPSHOT)
- self.assertEqual(SNAPSHOT_INFO['ID'], VOLUME_SNAP_ID['snap'])
- self.assertEqual(SNAPSHOT_INFO['Status'], 'Active')
- self.assertEqual(ret['provider_location'], SNAPSHOT_INFO['ID'])
- self.driver.delete_snapshot(FAKE_SNAPSHOT)
- self.assertIsNone(SNAPSHOT_INFO['ID'])
- self.assertIsNone(SNAPSHOT_INFO['Status'])
-
- def test_create_delete_snapshot_volume(self):
- # Test no source snapshot
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.create_volume_from_snapshot,
- FAKE_CLONED_VOLUME, FAKE_SNAPSHOT)
- # Test normal create and delete snapshot volume
- self.driver.create_volume(FAKE_VOLUME)
- self.driver.create_snapshot(FAKE_SNAPSHOT)
- self.assertEqual(LUN_INFO['ID'], VOLUME_SNAP_ID['vol'])
- self.assertEqual(SNAPSHOT_INFO['ID'], VOLUME_SNAP_ID['snap'])
- ret = self.driver.create_volume_from_snapshot(FAKE_CLONED_VOLUME,
- FAKE_SNAPSHOT)
- self.assertEqual(CLONED_LUN_INFO['ID'], VOLUME_SNAP_ID['vol_copy'])
- self.assertEqual(ret['provider_location'], CLONED_LUN_INFO['ID'])
- self.driver.delete_snapshot(FAKE_SNAPSHOT)
- self.driver.delete_volume(FAKE_VOLUME)
- self.driver.delete_volume(FAKE_CLONED_VOLUME)
- self.assertIsNone(LUN_INFO['ID'])
- self.assertIsNone(CLONED_LUN_INFO['ID'])
- self.assertIsNone(SNAPSHOT_INFO['ID'])
-
- def test_initialize_connection(self):
- # Test can not get iscsi iqn
- set_error_flg('showiscsitgtname')
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.initialize_connection,
- FAKE_VOLUME, FAKE_CONNECTOR)
- # Test failed to get iSCSI port info
- set_error_flg('showiscsiip')
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.initialize_connection,
- FAKE_VOLUME, FAKE_CONNECTOR)
- # Test create hostgroup failed
- set_error_flg('createhostgroup')
- MAP_INFO['Host Group ID'] = None
- MAP_INFO['Host Group Name'] = None
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.initialize_connection,
- FAKE_VOLUME, FAKE_CONNECTOR)
- # Test create host failed
- set_error_flg('addhost')
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.initialize_connection,
- FAKE_VOLUME, FAKE_CONNECTOR)
- # Test add iSCSI initiator failed
- set_error_flg('addiscsiini')
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.initialize_connection,
- FAKE_VOLUME, FAKE_CONNECTOR)
- # Test add hostport failed
- set_error_flg('addhostport')
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.initialize_connection,
- FAKE_VOLUME, FAKE_CONNECTOR)
- # Test no volume
- FAKE_VOLUME['provider_location'] = '100'
- self.assertRaises(exception.VolumeNotFound,
- self.driver.initialize_connection,
- FAKE_VOLUME, FAKE_CONNECTOR)
- FAKE_VOLUME['provider_location'] = None
- # Test map volume failed
- self.driver.create_volume(FAKE_VOLUME)
- set_error_flg('addhostmap')
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.initialize_connection,
- FAKE_VOLUME, FAKE_CONNECTOR)
- # Test normal initialize connection
- self.assertEqual(FAKE_VOLUME['provider_location'],
- VOLUME_SNAP_ID['vol'])
- self.assertEqual(LUN_INFO['Owner Controller'], 'A')
- ret = self.driver.initialize_connection(FAKE_VOLUME, FAKE_CONNECTOR)
- iscsi_propers = ret['data']
- self.assertEqual(iscsi_propers['target_iqn'],
- INITIATOR_SETTING['TargetIQN-form'])
- self.assertEqual(iscsi_propers['target_portal'],
- INITIATOR_SETTING['Initiator TargetIP'] + ':3260')
- self.assertEqual(MAP_INFO["DEV LUN ID"], LUN_INFO['ID'])
- self.assertEqual(MAP_INFO["INI Port Info"],
- FAKE_CONNECTOR['initiator'])
- self.assertEqual(LUN_INFO['Owner Controller'], 'B')
- self.driver.terminate_connection(FAKE_VOLUME, FAKE_CONNECTOR)
- self.driver.delete_volume(FAKE_VOLUME)
- self.assertIsNone(LUN_INFO['ID'])
-
- def test_terminate_connection(self):
- # Test no host was found
- self.assertRaises(exception.HostNotFound,
- self.driver.terminate_connection,
- FAKE_VOLUME, FAKE_CONNECTOR)
- # Test no volume was found
- self.driver .create_volume(FAKE_VOLUME)
- self.driver.initialize_connection(FAKE_VOLUME, FAKE_CONNECTOR)
- FAKE_VOLUME['provider_location'] = None
- self.assertRaises(exception.VolumeNotFound,
- self.driver.terminate_connection,
- FAKE_VOLUME, FAKE_CONNECTOR)
- FAKE_VOLUME['provider_location'] = LUN_INFO['ID']
- # Test delete map failed
- set_error_flg('delhostmap')
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.terminate_connection,
- FAKE_VOLUME, FAKE_CONNECTOR)
- # Delete hostport failed
- set_error_flg('delhostport')
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.terminate_connection,
- FAKE_VOLUME, FAKE_CONNECTOR)
- # Test delete initiator failed
- set_error_flg('deliscsiini')
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.terminate_connection,
- FAKE_VOLUME, FAKE_CONNECTOR)
- # Test delete host failed
- set_error_flg('delhost')
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.terminate_connection,
- FAKE_VOLUME, FAKE_CONNECTOR)
- # Test normal terminate connection
- self.assertEqual(LUN_INFO['ID'], VOLUME_SNAP_ID['vol'])
- self.driver.initialize_connection(FAKE_VOLUME, FAKE_CONNECTOR)
- self.driver.terminate_connection(FAKE_VOLUME, FAKE_CONNECTOR)
- self.assertIsNone(MAP_INFO["DEV LUN ID"])
- self.driver.delete_volume(FAKE_VOLUME)
- self.assertIsNone(LUN_INFO['ID'])
-
- def test_get_volume_stats(self):
- stats = self.driver.get_volume_stats(True)
- free_capacity = float(POOL_SETTING['Free Capacity']) / 1024
- self.assertEqual(stats['free_capacity_gb'], free_capacity)
- self.assertEqual(stats['storage_protocol'], 'iSCSI')
-
-
-class HuaweiTFCDriverTestCase(test.TestCase):
- def __init__(self, *args, **kwargs):
- super(HuaweiTFCDriverTestCase, self).__init__(*args, **kwargs)
-
- def setUp(self):
- super(HuaweiTFCDriverTestCase, self).setUp()
-
- self.tmp_dir = tempfile.mkdtemp()
- self.addCleanup(shutil.rmtree, self.tmp_dir)
-
- self.fake_conf_file = self.tmp_dir + '/cinder_huawei_conf.xml'
- self.addCleanup(os.remove, self.fake_conf_file)
-
- create_fake_conf_file(self.fake_conf_file)
- modify_conf(self.fake_conf_file, 'Storage/Protocol', 'FC')
- self.configuration = mox.MockObject(conf.Configuration)
- self.configuration.cinder_huawei_conf_file = self.fake_conf_file
- self.configuration.append_config_values(mox.IgnoreArg())
-
- self.stubs.Set(time, 'sleep', Fake_sleep)
- self.stubs.Set(ssh_utils, 'SSHPool', FakeSSHPool)
- self.stubs.Set(ssh_common.TseriesCommon, '_change_file_mode',
- Fake_change_file_mode)
- self._init_driver()
-
- def _init_driver(self):
- Curr_test[0] = 'T'
- self.driver = huawei.HuaweiVolumeDriver(
- configuration=self.configuration)
- self.driver.do_setup(None)
-
- def test_validate_connector_failed(self):
- invalid_connector = {'host': 'testhost'}
- self.assertRaises(exception.InvalidConnectorException,
- self.driver.validate_connector,
- invalid_connector)
-
- def test_create_delete_volume(self):
- self.driver.create_volume(FAKE_VOLUME)
- self.assertEqual(LUN_INFO['ID'], VOLUME_SNAP_ID['vol'])
- self.driver.delete_volume(FAKE_VOLUME)
- self.assertIsNone(LUN_INFO['ID'])
-
- def test_create_delete_snapshot(self):
- self.driver.create_volume(FAKE_VOLUME)
- self.driver.create_snapshot(FAKE_SNAPSHOT)
- self.assertEqual(SNAPSHOT_INFO['ID'], VOLUME_SNAP_ID['snap'])
- self.driver.delete_snapshot(FAKE_SNAPSHOT)
- self.assertIsNone(SNAPSHOT_INFO['ID'])
- self.driver.delete_volume(FAKE_VOLUME)
- self.assertIsNone(LUN_INFO['ID'])
-
- def test_create_cloned_volume(self):
- self.driver.create_volume(FAKE_VOLUME)
- ret = self.driver.create_cloned_volume(FAKE_CLONED_VOLUME, FAKE_VOLUME)
- self.assertEqual(CLONED_LUN_INFO['ID'], VOLUME_SNAP_ID['vol_copy'])
- self.assertEqual(ret['provider_location'], CLONED_LUN_INFO['ID'])
- self.driver.delete_volume(FAKE_CLONED_VOLUME)
- self.driver.delete_volume(FAKE_VOLUME)
- self.assertIsNone(CLONED_LUN_INFO['ID'])
- self.assertIsNone(LUN_INFO['ID'])
-
- def test_create_snapshot_volume(self):
- self.driver.create_volume(FAKE_VOLUME)
- self.driver.create_snapshot(FAKE_SNAPSHOT)
- ret = self.driver.create_volume_from_snapshot(FAKE_CLONED_VOLUME,
- FAKE_SNAPSHOT)
- self.assertEqual(CLONED_LUN_INFO['ID'], VOLUME_SNAP_ID['vol_copy'])
- self.assertEqual(ret['provider_location'], CLONED_LUN_INFO['ID'])
- self.driver.delete_volume(FAKE_CLONED_VOLUME)
- self.driver.delete_volume(FAKE_VOLUME)
- self.assertIsNone(CLONED_LUN_INFO['ID'])
- self.assertIsNone(LUN_INFO['ID'])
-
- def test_initialize_terminitat_connection(self):
- self.driver.create_volume(FAKE_VOLUME)
- ret = self.driver.initialize_connection(FAKE_VOLUME, FAKE_CONNECTOR)
- fc_properties = ret['data']
- self.assertEqual(fc_properties['target_wwn'],
- INITIATOR_SETTING['WWN'])
- self.assertEqual(MAP_INFO["DEV LUN ID"], LUN_INFO['ID'])
-
- self.driver.terminate_connection(FAKE_VOLUME, FAKE_CONNECTOR)
- self.assertIsNone(MAP_INFO["DEV LUN ID"])
- self.assertIsNone(MAP_INFO["Host LUN ID"])
- self.driver.delete_volume(FAKE_VOLUME)
- self.assertIsNone(LUN_INFO['ID'])
-
- def _test_get_volume_stats(self):
- stats = self.driver.get_volume_stats(True)
- fakecapacity = float(POOL_SETTING['Free Capacity']) / 1024
- self.assertEqual(stats['free_capacity_gb'], fakecapacity)
- self.assertEqual(stats['storage_protocol'], 'FC')
-
-
-class HuaweiDorado5100FCDriverTestCase(HuaweiTFCDriverTestCase):
- def __init__(self, *args, **kwargs):
- super(HuaweiDorado5100FCDriverTestCase, self).__init__(*args, **kwargs)
-
- def setUp(self):
- super(HuaweiDorado5100FCDriverTestCase, self).setUp()
-
- def _init_driver(self):
- Curr_test[0] = 'Dorado5100'
- modify_conf(self.fake_conf_file, 'Storage/Product', 'Dorado')
- self.driver = huawei.HuaweiVolumeDriver(
- configuration=self.configuration)
- self.driver.do_setup(None)
-
- def test_create_cloned_volume(self):
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.create_cloned_volume,
- FAKE_CLONED_VOLUME, FAKE_VOLUME)
-
- def test_create_snapshot_volume(self):
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.create_volume_from_snapshot,
- FAKE_CLONED_VOLUME, FAKE_SNAPSHOT)
-
-
-class HuaweiDorado2100G2FCDriverTestCase(HuaweiTFCDriverTestCase):
- def __init__(self, *args, **kwargs):
- super(HuaweiDorado2100G2FCDriverTestCase, self).__init__(*args,
- **kwargs)
-
- def setUp(self):
- super(HuaweiDorado2100G2FCDriverTestCase, self).setUp()
-
- def _init_driver(self):
- Curr_test[0] = 'Dorado2100G2'
- modify_conf(self.fake_conf_file, 'Storage/Product', 'Dorado')
- self.driver = huawei.HuaweiVolumeDriver(
- configuration=self.configuration)
- self.driver.do_setup(None)
-
- def test_create_cloned_volume(self):
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.create_cloned_volume,
- FAKE_CLONED_VOLUME, FAKE_VOLUME)
-
- def test_create_delete_snapshot(self):
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.create_snapshot, FAKE_SNAPSHOT)
-
- def test_create_snapshot_volume(self):
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.create_volume_from_snapshot,
- FAKE_CLONED_VOLUME, FAKE_SNAPSHOT)
-
- def test_extend_volume(self):
- NEWSIZE = 5
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.extend_volume,
- FAKE_VOLUME, NEWSIZE)
-
-
-class HuaweiDorado5100ISCSIDriverTestCase(HuaweiTISCSIDriverTestCase):
- def __init__(self, *args, **kwargs):
- super(HuaweiDorado5100ISCSIDriverTestCase, self).__init__(*args,
- **kwargs)
-
- def setUp(self):
- super(HuaweiDorado5100ISCSIDriverTestCase, self).setUp()
-
- def _init_driver(self):
- Curr_test[0] = 'Dorado5100'
- modify_conf(self.fake_conf_file, 'Storage/Product', 'Dorado')
- self.driver = huawei.HuaweiVolumeDriver(
- configuration=self.configuration)
- self.driver.do_setup(None)
-
- def test_create_delete_cloned_volume(self):
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.create_cloned_volume,
- FAKE_CLONED_VOLUME, FAKE_VOLUME)
-
- def test_create_delete_snapshot_volume(self):
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.create_volume_from_snapshot,
- FAKE_CLONED_VOLUME, FAKE_SNAPSHOT)
-
- def test_volume_type(self):
- pass
-
-
-class HuaweiDorado2100G2ISCSIDriverTestCase(HuaweiTISCSIDriverTestCase):
- def __init__(self, *args, **kwargs):
- super(HuaweiDorado2100G2ISCSIDriverTestCase, self).__init__(*args,
- **kwargs)
-
- def setUp(self):
- super(HuaweiDorado2100G2ISCSIDriverTestCase, self).setUp()
-
- def _init_driver(self):
- Curr_test[0] = 'Dorado2100G2'
- modify_conf(self.fake_conf_file, 'Storage/Product', 'Dorado')
- self.driver = huawei.HuaweiVolumeDriver(
- configuration=self.configuration)
- self.driver.do_setup(None)
-
- def test_conf_invalid(self):
- pass
-
- def test_create_delete_cloned_volume(self):
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.create_cloned_volume,
- FAKE_CLONED_VOLUME, FAKE_VOLUME)
-
- def test_create_delete_snapshot(self):
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.create_snapshot, FAKE_SNAPSHOT)
-
- def test_create_delete_snapshot_volume(self):
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.create_volume_from_snapshot,
- FAKE_CLONED_VOLUME, FAKE_SNAPSHOT)
-
- def test_initialize_connection(self):
- self.driver.create_volume(FAKE_VOLUME)
- ret = self.driver.initialize_connection(FAKE_VOLUME, FAKE_CONNECTOR)
- iscsi_propers = ret['data']
- self.assertEqual(iscsi_propers['target_iqn'],
- INITIATOR_SETTING['TargetIQN-form'])
- self.assertEqual(iscsi_propers['target_portal'],
- INITIATOR_SETTING['Initiator TargetIP'] + ':3260')
- self.assertEqual(MAP_INFO["DEV LUN ID"], LUN_INFO['ID'])
- self.assertEqual(MAP_INFO["INI Port Info"],
- FAKE_CONNECTOR['initiator'])
- self.driver.terminate_connection(FAKE_VOLUME, FAKE_CONNECTOR)
- self.driver.delete_volume(FAKE_VOLUME)
- self.assertIsNone(LUN_INFO['ID'])
-
- def test_extend_volume(self):
- NEWSIZE = 5
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.extend_volume,
- FAKE_VOLUME, NEWSIZE)
-
-
-class SSHMethodTestCase(test.TestCase):
- def __init__(self, *args, **kwargs):
- super(SSHMethodTestCase, self).__init__(*args, **kwargs)
-
- def setUp(self):
- super(SSHMethodTestCase, self).setUp()
- self.tmp_dir = tempfile.mkdtemp()
- self.addCleanup(shutil.rmtree, self.tmp_dir)
-
- self.fake_conf_file = self.tmp_dir + '/cinder_huawei_conf.xml'
- self.addCleanup(os.remove, self.fake_conf_file)
-
- create_fake_conf_file(self.fake_conf_file)
- self.configuration = mox.MockObject(conf.Configuration)
- self.configuration.cinder_huawei_conf_file = self.fake_conf_file
- self.configuration.append_config_values(mox.IgnoreArg())
-
- self.stubs.Set(time, 'sleep', Fake_sleep)
- self.stubs.Set(ssh_utils, 'SSHPool', FakeSSHPool)
- self.stubs.Set(ssh_common.TseriesCommon, '_change_file_mode',
- Fake_change_file_mode)
- Curr_test[0] = 'T'
- self.driver = huawei.HuaweiVolumeDriver(
- configuration=self.configuration)
- self.driver.do_setup(None)
-
- def test_reach_max_connection_limit(self):
- self.stubs.Set(FakeChannel, 'recv', self._fake_recv1)
- self.assertRaises(exception.CinderException,
- self.driver.create_volume, FAKE_VOLUME)
-
- def test_socket_timeout(self):
- self.stubs.Set(FakeChannel, 'recv', self._fake_recv2)
- self.assertRaises(socket.timeout,
- self.driver.create_volume, FAKE_VOLUME)
-
- def _fake_recv1(self, nbytes):
- return "No response message"
-
- def _fake_recv2(self, nBytes):
- raise socket.timeout()
-
-
-class HuaweiUtilsTestCase(test.TestCase):
- def __init__(self, *args, **kwargs):
- super(HuaweiUtilsTestCase, self).__init__(*args, **kwargs)
-
- def setUp(self):
- super(HuaweiUtilsTestCase, self).setUp()
-
- self.tmp_dir = tempfile.mkdtemp()
- self.addCleanup(shutil.rmtree, self.tmp_dir)
- self.fake_conf_file = self.tmp_dir + '/cinder_huawei_conf.xml'
- self.addCleanup(os.remove, self.fake_conf_file)
- create_fake_conf_file(self.fake_conf_file)
-
- def test_parse_xml_file_ioerror(self):
- tmp_fonf_file = '/xxx/cinder_huawei_conf.xml'
- self.assertRaises(IOError, huawei_utils.parse_xml_file, tmp_fonf_file)
-
- def test_is_xml_item_exist(self):
- root = huawei_utils.parse_xml_file(self.fake_conf_file)
- res = huawei_utils.is_xml_item_exist(root, 'Storage/UserName')
- self.assertTrue(res)
- res = huawei_utils.is_xml_item_exist(root, 'xxx')
- self.assertFalse(res)
- res = huawei_utils.is_xml_item_exist(root, 'LUN/StoragePool', 'Name')
- self.assertTrue(res)
- res = huawei_utils.is_xml_item_exist(root, 'LUN/StoragePool', 'xxx')
- self.assertFalse(res)
-
- def test_is_xml_item_valid(self):
- root = huawei_utils.parse_xml_file(self.fake_conf_file)
- res = huawei_utils.is_xml_item_valid(root, 'LUN/LUNType',
- ['Thin', 'Thick'])
- self.assertTrue(res)
- res = huawei_utils.is_xml_item_valid(root, 'LUN/LUNType', ['test'])
- self.assertFalse(res)
- res = huawei_utils.is_xml_item_valid(root, 'Host',
- ['Linux', 'Windows'], 'OSType')
- self.assertTrue(res)
- res = huawei_utils.is_xml_item_valid(root, 'Host', ['test'], 'OSType')
- self.assertFalse(res)
-
- def test_get_conf_host_os_type(self):
- # Default os is Linux
- res = huawei_utils.get_conf_host_os_type('10.10.10.1',
- self.fake_conf_file)
- self.assertEqual(res, '0')
- modify_conf(self.fake_conf_file, 'Host', 'Windows', 'OSType')
- res = huawei_utils.get_conf_host_os_type(FAKE_CONNECTOR['ip'],
- self.fake_conf_file)
- self.assertEqual(res, '1')
+++ /dev/null
-# Copyright (c) 2013 - 2014 Huawei Technologies Co., Ltd.
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-"""
-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_dorado
-from cinder.volume.drivers.huawei import huawei_t
-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 = {'T': huawei_t, 'Dorado': huawei_dorado,
- '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.warn(_LW("Product name %s is deprecated, update your "
- "configuration to the new product name."), product)
- product = MAPPING[product]
-
- driver_module = self._product[product]
- driver_class = 'Huawei' + product + self._protocol[protocol]
-
- 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 either T, Dorado or 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)
+++ /dev/null
-# Copyright (c) 2013 - 2014 Huawei Technologies Co., Ltd.
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-"""
-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
+++ /dev/null
-# Copyright (c) 2013 Huawei Technologies Co., Ltd.
-# Copyright (c) 2012 OpenStack Foundation
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-"""
-Volume Drivers for Huawei OceanStor Dorado series storage arrays.
-"""
-
-import re
-
-from oslo_log import log as logging
-
-from cinder.volume.drivers.huawei import huawei_t
-from cinder.volume.drivers.huawei import ssh_common
-
-LOG = logging.getLogger(__name__)
-
-
-class HuaweiDoradoISCSIDriver(huawei_t.HuaweiTISCSIDriver):
- """ISCSI driver class for Huawei OceanStor Dorado storage arrays."""
-
- def __init__(self, *args, **kwargs):
- super(HuaweiDoradoISCSIDriver, self).__init__(*args, **kwargs)
-
- def do_setup(self, context):
- """Instantiate common class."""
- self.common = ssh_common.DoradoCommon(configuration=self.configuration)
-
- self.common.do_setup(context)
- self._assert_cli_out = self.common._assert_cli_out
- self._assert_cli_operate_out = self.common._assert_cli_operate_out
-
-
-class HuaweiDoradoFCDriver(huawei_t.HuaweiTFCDriver):
- """FC driver class for Huawei OceanStor Dorado storage arrays."""
-
- def __init__(self, *args, **kwargs):
- super(HuaweiDoradoFCDriver, self).__init__(*args, **kwargs)
-
- def do_setup(self, context):
- """Instantiate common class."""
- self.common = ssh_common.DoradoCommon(configuration=self.configuration)
-
- self.common.do_setup(context)
- self._assert_cli_out = self.common._assert_cli_out
- self._assert_cli_operate_out = self.common._assert_cli_operate_out
-
- def _get_host_port_details(self, hostid):
- cli_cmd = 'showfcmode'
- out = self.common._execute_cli(cli_cmd)
-
- self._assert_cli_out(re.search('FC Port Topology Mode', out),
- '_get_tgt_fc_port_wwns',
- 'Failed to get FC port WWNs.',
- cli_cmd, out)
-
- return [line.split()[3] for line in out.split('\r\n')[6:-2]]
-
- def _get_tgt_fc_port_wwns(self, port_details):
- return port_details
-
- def initialize_connection(self, volume, connector):
- """Create FC connection between a volume and a host."""
- LOG.debug('initialize_connection: volume name: %(vol)s '
- 'host: %(host)s initiator: %(wwn)s'
- % {'vol': volume['name'],
- 'host': connector['host'],
- 'wwn': connector['wwpns']})
-
- self.common._update_login_info()
- # First, add a host if it is not added before.
- host_id = self.common.add_host(connector['host'], connector['ip'])
- # Then, add free FC ports to the host.
- ini_wwns = connector['wwpns']
- free_wwns = self._get_connected_free_wwns()
- for wwn in free_wwns:
- if wwn in ini_wwns:
- self._add_fc_port_to_host(host_id, wwn)
- fc_port_details = self._get_host_port_details(host_id)
- tgt_wwns = self._get_tgt_fc_port_wwns(fc_port_details)
-
- LOG.debug('initialize_connection: Target FC ports WWNS: %s'
- % tgt_wwns)
-
- # Finally, map the volume to the host.
- volume_id = volume['provider_location']
- hostlun_id = self.common.map_volume(host_id, volume_id)
-
- properties = {}
- properties['target_discovered'] = False
- properties['target_wwn'] = tgt_wwns
- properties['target_lun'] = int(hostlun_id)
- properties['volume_id'] = volume['id']
-
- return {'driver_volume_type': 'fibre_channel',
- 'data': properties}
+++ /dev/null
-# Copyright (c) 2013 Huawei Technologies Co., Ltd.
-# Copyright (c) 2012 OpenStack Foundation
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-"""
-Volume Drivers for Huawei OceanStor T series storage arrays.
-"""
-
-import re
-import time
-
-from oslo_log import log as logging
-from oslo_utils import excutils
-
-from cinder import exception
-from cinder.i18n import _, _LE, _LW
-from cinder.volume import driver
-from cinder.volume.drivers.huawei import huawei_utils
-from cinder.volume.drivers.huawei import ssh_common
-from cinder.zonemanager import utils as fczm_utils
-
-
-LOG = logging.getLogger(__name__)
-
-HOST_PORT_PREFIX = 'HostPort_'
-
-
-class HuaweiTISCSIDriver(driver.ISCSIDriver):
- """ISCSI driver for Huawei OceanStor T series storage arrays."""
-
- VERSION = '1.1.0'
-
- def __init__(self, *args, **kwargs):
- super(HuaweiTISCSIDriver, self).__init__(*args, **kwargs)
-
- def do_setup(self, context):
- """Instantiate common class."""
- self.common = ssh_common.TseriesCommon(configuration=
- self.configuration)
- self.common.do_setup(context)
- self._assert_cli_out = self.common._assert_cli_out
- self._assert_cli_operate_out = self.common._assert_cli_operate_out
-
- def check_for_setup_error(self):
- """Check something while starting."""
- self.common.check_for_setup_error()
-
- def create_volume(self, volume):
- """Create a new volume."""
- volume_id = self.common.create_volume(volume)
- return {'provider_location': volume_id}
-
- def create_volume_from_snapshot(self, volume, snapshot):
- """Create a volume from a snapshot."""
- volume_id = self.common.create_volume_from_snapshot(volume, snapshot)
- return {'provider_location': volume_id}
-
- def create_cloned_volume(self, volume, src_vref):
- """Create a clone of the specified volume."""
- volume_id = self.common.create_cloned_volume(volume, src_vref)
- return {'provider_location': volume_id}
-
- def extend_volume(self, volume, new_size):
- """Extend a volume."""
- self.common.extend_volume(volume, new_size)
-
- def delete_volume(self, volume):
- """Delete a volume."""
- self.common.delete_volume(volume)
-
- 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
-
- def create_snapshot(self, snapshot):
- """Create a snapshot."""
- snapshot_id = self.common.create_snapshot(snapshot)
- return {'provider_location': snapshot_id}
-
- def delete_snapshot(self, snapshot):
- """Delete a snapshot."""
- self.common.delete_snapshot(snapshot)
-
- def initialize_connection(self, volume, connector):
- """Map a volume to a host and return target iSCSI information."""
- LOG.debug('initialize_connection: volume name: %(vol)s, '
- 'host: %(host)s, initiator: %(ini)s'
- % {'vol': volume['name'],
- 'host': connector['host'],
- 'ini': connector['initiator']})
-
- self.common._update_login_info()
- (iscsi_iqn, target_ip, port_ctr) = \
- self._get_iscsi_params(connector['initiator'])
-
- # First, add a host if not added before.
- host_id = self.common.add_host(connector['host'], connector['ip'],
- connector['initiator'])
-
- # Then, add the iSCSI port to the host.
- self._add_iscsi_port_to_host(host_id, connector)
-
- # Finally, map the volume to the host.
- volume_id = volume['provider_location']
- try:
- hostlun_id = self.common.map_volume(host_id, volume_id)
- except Exception:
- with excutils.save_and_reraise_exception():
- # Remove the iSCSI port from the host if the map failed.
- self._remove_iscsi_port(host_id, connector)
-
- # Change LUN ctr for better performance, just for single path.
- lun_details = self.common.get_lun_details(volume_id)
- if (lun_details['LunType'] == 'THICK' and
- lun_details['OwningController'] != port_ctr):
- self.common.change_lun_ctr(volume_id, port_ctr)
-
- 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']
- auth = volume['provider_auth']
- if auth:
- (auth_method, auth_username, auth_secret) = auth.split()
-
- properties['auth_method'] = auth_method
- properties['auth_username'] = auth_username
- properties['auth_password'] = auth_secret
-
- return {'driver_volume_type': 'iscsi', 'data': properties}
-
- def _get_iscsi_params(self, initiator):
- """Get target iSCSI params, including iqn and IP."""
- conf_file = self.common.configuration.cinder_huawei_conf_file
- iscsi_conf = self._get_iscsi_conf(conf_file)
- 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)
-
- (target_iqn, port_ctr) = self._get_tgt_iqn(target_ip)
- return (target_iqn, target_ip, port_ctr)
-
- def _get_iscsi_conf(self, filename):
- """Get iSCSI info from config file.
-
- This function returns a dict:
- {'DefaultTargetIP': '11.11.11.11',
- 'Initiator': [{'Name': 'iqn.xxxxxx.1', 'TargetIP': '11.11.11.12'},
- {'Name': 'iqn.xxxxxx.2', 'TargetIP': '11.11.11.13'}
- ]
- }
-
- """
-
- iscsiinfo = {}
- root = huawei_utils.parse_xml_file(filename)
-
- default_ip = root.findtext('iSCSI/DefaultTargetIP')
- if default_ip:
- iscsiinfo['DefaultTargetIP'] = default_ip.strip()
- else:
- iscsiinfo['DefaultTargetIP'] = None
- initiator_list = []
- tmp_dic = {}
- for dic in root.findall('iSCSI/Initiator'):
- # Strip the values of dict.
- for k, v in dic.items():
- tmp_dic[k] = v.strip()
- initiator_list.append(tmp_dic)
- iscsiinfo['Initiator'] = initiator_list
- return iscsiinfo
-
- def _get_tgt_iqn(self, port_ip):
- """Run CLI command to get target iSCSI iqn.
-
- The iqn is formed with three parts:
- iSCSI target name + iSCSI port info + iSCSI IP
-
- """
-
- LOG.debug('_get_tgt_iqn: iSCSI IP is %s.' % port_ip)
-
- cli_cmd = 'showiscsitgtname'
- out = self.common._execute_cli(cli_cmd)
-
- self._assert_cli_out(re.search('ISCSI Name', out),
- '_get_tgt_iqn',
- 'Failed to get iSCSI target %s iqn.' % port_ip,
- cli_cmd, out)
-
- lines = out.split('\r\n')
- index = lines[4].index('iqn')
- iqn_prefix = lines[4][index:].strip()
- # Here we make sure port_info won't be None.
- port_info = self._get_iscsi_tgt_port_info(port_ip)
- ctr = ('0' if port_info[0] == 'A' else '1')
- interface = '0' + port_info[1]
- port = '0' + port_info[2][1:]
- iqn_suffix = ctr + '02' + interface + port
- # iqn_suffix should not start with 0
- while(True):
- if iqn_suffix.startswith('0'):
- iqn_suffix = iqn_suffix[1:]
- else:
- break
-
- iqn = iqn_prefix + ':' + iqn_suffix + ':' + port_info[3]
-
- LOG.debug('_get_tgt_iqn: iSCSI target iqn is %s.' % iqn)
-
- return (iqn, port_info[0])
-
- def _get_iscsi_tgt_port_info(self, port_ip):
- """Get iSCSI Port information of storage device."""
- cli_cmd = 'showiscsiip'
- out = self.common._execute_cli(cli_cmd)
- if re.search('iSCSI IP Information', out):
- for line in out.split('\r\n')[6:-2]:
- tmp_line = line.split()
- if tmp_line[3] == port_ip:
- return tmp_line
-
- err_msg = _('_get_iscsi_tgt_port_info: Failed to get iSCSI port '
- 'info. Please make sure the iSCSI port IP %s is '
- 'configured in array.') % port_ip
- LOG.error(err_msg)
- raise exception.VolumeBackendAPIException(data=err_msg)
-
- def _add_iscsi_port_to_host(self, hostid, connector, multipathtype=0):
- """Add an iSCSI port to the given host.
-
- First, add an initiator if needed, the initiator is equivalent to
- an iSCSI port. Then, add the initiator to host if not added before.
-
- """
-
- initiator = connector['initiator']
- # Add an iSCSI initiator.
- if not self._initiator_added(initiator):
- self._add_initiator(initiator)
- # Add the initiator to host if not added before.
- port_name = HOST_PORT_PREFIX + str(hash(initiator))
- portadded = False
- hostport_info = self.common._get_host_port_info(hostid)
- if hostport_info:
- for hostport in hostport_info:
- if hostport[2] == initiator:
- portadded = True
- break
- if not portadded:
- cli_cmd = ('addhostport -host %(id)s -type 5 '
- '-info %(info)s -n %(name)s -mtype %(multype)s'
- % {'id': hostid,
- 'info': initiator,
- 'name': port_name,
- 'multype': multipathtype})
- out = self.common._execute_cli(cli_cmd)
-
- msg = ('Failed to add iSCSI port %(port)s to host %(host)s'
- % {'port': port_name,
- 'host': hostid})
- self._assert_cli_operate_out('_add_iscsi_port_to_host',
- msg, cli_cmd, out)
-
- def _initiator_added(self, ininame):
- """Check whether the initiator is already added."""
- cli_cmd = 'showiscsiini -ini %(name)s' % {'name': ininame}
- out = self.common._execute_cli(cli_cmd)
- return (True if re.search('Initiator Information', out) else False)
-
- def _add_initiator(self, ininame):
- """Add a new initiator to storage device."""
- cli_cmd = 'addiscsiini -n %(name)s' % {'name': ininame}
- out = self.common._execute_cli(cli_cmd)
-
- self._assert_cli_operate_out('_add_iscsi_host_port',
- 'Failed to add initiator %s' % ininame,
- cli_cmd, out)
-
- def _delete_initiator(self, ininame, attempts=2):
- """Delete an initiator."""
- cli_cmd = 'deliscsiini -n %(name)s' % {'name': ininame}
- while(attempts > 0):
- out = self.common._execute_cli(cli_cmd)
- if re.search('the port is in use', out):
- attempts -= 1
- time.sleep(2)
- else:
- break
-
- self._assert_cli_operate_out('_map_lun',
- 'Failed to delete initiator %s.'
- % ininame,
- cli_cmd, out)
-
- def terminate_connection(self, volume, connector, **kwargs):
- """Terminate the map."""
- LOG.debug('terminate_connection: volume: %(vol)s, host: %(host)s, '
- 'connector: %(initiator)s'
- % {'vol': volume['name'],
- 'host': connector['host'],
- 'initiator': connector['initiator']})
-
- self.common._update_login_info()
- host_id = self.common.remove_map(volume['provider_location'],
- connector['host'],
- connector['initiator'])
- if not self.common._get_host_map_info(host_id):
- self._remove_iscsi_port(host_id, connector)
-
- def _remove_iscsi_port(self, hostid, connector):
- """Remove iSCSI ports and delete host."""
- initiator = connector['initiator']
- # Delete the host initiator if no LUN mapped to it.
- port_num = 0
- port_info = self.common._get_host_port_info(hostid)
- if port_info:
- port_num = len(port_info)
- for port in port_info:
- if port[2] == initiator:
- self.common._delete_hostport(port[0])
- self._delete_initiator(initiator)
- port_num -= 1
- break
- else:
- LOG.warn(_LW('_remove_iscsi_port: iSCSI port was not found '
- 'on host %(hostid)s.') % {'hostid': hostid})
-
- # Delete host if no initiator added to it.
- if port_num == 0:
- self.common._delete_host(hostid)
-
- def get_volume_stats(self, refresh=False):
- """Get volume stats."""
- self._stats = self.common.get_volume_stats(refresh)
- self._stats['storage_protocol'] = 'iSCSI'
- self._stats['driver_version'] = self.VERSION
- backend_name = self.configuration.safe_get('volume_backend_name')
- self._stats['volume_backend_name'] = (backend_name or
- self.__class__.__name__)
- return self._stats
-
-
-class HuaweiTFCDriver(driver.FibreChannelDriver):
- """FC driver for Huawei OceanStor T series storage arrays."""
-
- VERSION = '1.0.0'
-
- def __init__(self, *args, **kwargs):
- super(HuaweiTFCDriver, self).__init__(*args, **kwargs)
-
- def do_setup(self, context):
- """Instantiate common class."""
- self.common = ssh_common.TseriesCommon(configuration=
- self.configuration)
- self.common.do_setup(context)
- self._assert_cli_out = self.common._assert_cli_out
- self._assert_cli_operate_out = self.common._assert_cli_operate_out
-
- def check_for_setup_error(self):
- """Check something while starting."""
- self.common.check_for_setup_error()
-
- def create_volume(self, volume):
- """Create a new volume."""
- volume_id = self.common.create_volume(volume)
- return {'provider_location': volume_id}
-
- def create_volume_from_snapshot(self, volume, snapshot):
- """Create a volume from a snapshot."""
- volume_id = self.common.create_volume_from_snapshot(volume, snapshot)
- return {'provider_location': volume_id}
-
- def create_cloned_volume(self, volume, src_vref):
- """Create a clone of the specified volume."""
- volume_id = self.common.create_cloned_volume(volume, src_vref)
- return {'provider_location': volume_id}
-
- def extend_volume(self, volume, new_size):
- """Extend a volume."""
- self.common.extend_volume(volume, new_size)
-
- def delete_volume(self, volume):
- """Delete a volume."""
- self.common.delete_volume(volume)
-
- 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
-
- def create_snapshot(self, snapshot):
- """Create a snapshot."""
- snapshot_id = self.common.create_snapshot(snapshot)
- return {'provider_location': snapshot_id}
-
- def delete_snapshot(self, snapshot):
- """Delete a snapshot."""
- self.common.delete_snapshot(snapshot)
-
- def validate_connector(self, connector):
- """Check for wwpns in connector."""
- if 'wwpns' not in connector:
- err_msg = (_LE('validate_connector: The FC driver requires the'
- ' wwpns in the connector.'))
- LOG.error(err_msg)
- raise exception.InvalidConnectorException(missing='wwpns')
-
- @fczm_utils.AddFCZone
- def initialize_connection(self, volume, connector):
- """Create FC connection between a volume and a host."""
- LOG.debug('initialize_connection: volume name: %(vol)s, '
- 'host: %(host)s, initiator: %(wwn)s'
- % {'vol': volume['name'],
- 'host': connector['host'],
- 'wwn': connector['wwpns']})
-
- self.common._update_login_info()
- # First, add a host if it is not added before.
- host_id = self.common.add_host(connector['host'], connector['ip'])
- # Then, add free FC ports to the host.
- ini_wwns = connector['wwpns']
- free_wwns = self._get_connected_free_wwns()
- for wwn in free_wwns:
- if wwn in ini_wwns:
- self._add_fc_port_to_host(host_id, wwn)
- fc_port_details = self._get_host_port_details(host_id)
- tgt_wwns = self._get_tgt_fc_port_wwns(fc_port_details)
-
- LOG.debug('initialize_connection: Target FC ports WWNS: %s'
- % tgt_wwns)
-
- # Finally, map the volume to the host.
- volume_id = volume['provider_location']
- try:
- hostlun_id = self.common.map_volume(host_id, volume_id)
- except Exception:
- with excutils.save_and_reraise_exception():
- # Remove the FC port from the host if the map failed.
- self._remove_fc_ports(host_id, connector)
-
- # Change LUN ctr for better performance, just for single path.
- if len(tgt_wwns) == 1:
- lun_details = self.common.get_lun_details(volume_id)
- port_ctr = self._get_fc_port_ctr(fc_port_details[0])
- if (lun_details['LunType'] == 'THICK' and
- lun_details['OwningController'] != port_ctr):
- self.common.change_lun_ctr(volume_id, port_ctr)
-
- properties = {}
- properties['target_discovered'] = False
- properties['target_wwn'] = tgt_wwns
- properties['target_lun'] = int(hostlun_id)
- properties['volume_id'] = volume['id']
-
- return {'driver_volume_type': 'fibre_channel',
- 'data': properties}
-
- def _get_connected_free_wwns(self):
- """Get free connected FC port WWNs.
-
- If no new ports connected, return an empty list.
-
- """
-
- cli_cmd = 'showfreeport'
- out = self.common._execute_cli(cli_cmd)
- wwns = []
- if re.search('Host Free Port Information', out):
- for line in out.split('\r\n')[6:-2]:
- tmp_line = line.split()
- if (tmp_line[1] == 'FC') and (tmp_line[4] == 'Connected'):
- wwns.append(tmp_line[0])
-
- return list(set(wwns))
-
- def _add_fc_port_to_host(self, hostid, wwn, multipathtype=0):
- """Add a FC port to host."""
- portname = HOST_PORT_PREFIX + wwn
- cli_cmd = ('addhostport -host %(id)s -type 1 '
- '-wwn %(wwn)s -n %(name)s -mtype %(multype)s'
- % {'id': hostid,
- 'wwn': wwn,
- 'name': portname,
- 'multype': multipathtype})
- out = self.common._execute_cli(cli_cmd)
-
- msg = ('Failed to add FC port %(port)s to host %(host)s.'
- % {'port': portname, 'host': hostid})
- self._assert_cli_operate_out('_add_fc_port_to_host', msg, cli_cmd, out)
-
- def _get_host_port_details(self, host_id):
- cli_cmd = 'showhostpath -host %s' % host_id
- out = self.common._execute_cli(cli_cmd)
-
- self._assert_cli_out(re.search('Multi Path Information', out),
- '_get_host_port_details',
- 'Failed to get host port details.',
- cli_cmd, out)
-
- port_details = []
- tmp_details = {}
- for line in out.split('\r\n')[4:-2]:
- line = line.split('|')
- # Cut-point of multipal details, usually is "-------".
- if len(line) == 1:
- port_details.append(tmp_details)
- continue
- key = ''.join(line[0].strip().split())
- val = line[1].strip()
- tmp_details[key] = val
- port_details.append(tmp_details)
- return port_details
-
- def _get_tgt_fc_port_wwns(self, port_details):
- wwns = []
- for port in port_details:
- wwns.append(port['TargetWWN'])
- return wwns
-
- def _get_fc_port_ctr(self, port_details):
- return port_details['ControllerID']
-
- @fczm_utils.RemoveFCZone
- def terminate_connection(self, volume, connector, **kwargs):
- """Terminate the map."""
- LOG.debug('terminate_connection: volume: %(vol)s, host: %(host)s, '
- 'connector: %(initiator)s'
- % {'vol': volume['name'],
- 'host': connector['host'],
- 'initiator': connector['initiator']})
-
- self.common._update_login_info()
- host_id = self.common.remove_map(volume['provider_location'],
- connector['host'])
- # Remove all FC ports and delete the host if
- # no volume mapping to it.
- if not self.common._get_host_map_info(host_id):
- self._remove_fc_ports(host_id, connector)
-
- def _remove_fc_ports(self, hostid, connector):
- """Remove FC ports and delete host."""
- wwns = connector['wwpns']
- port_num = 0
- port_info = self.common._get_host_port_info(hostid)
- if port_info:
- port_num = len(port_info)
- for port in port_info:
- if port[2] in wwns:
- self.common._delete_hostport(port[0])
- port_num -= 1
- else:
- LOG.warn(_LW('_remove_fc_ports: FC port was not found '
- 'on host %(hostid)s.') % {'hostid': hostid})
-
- if port_num == 0:
- self.common._delete_host(hostid)
-
- def get_volume_stats(self, refresh=False):
- """Get volume stats."""
- self._stats = self.common.get_volume_stats(refresh)
- self._stats['storage_protocol'] = 'FC'
- self._stats['driver_version'] = self.VERSION
- backend_name = self.configuration.safe_get('volume_backend_name')
- self._stats['volume_backend_name'] = (backend_name or
- self.__class__.__name__)
- return self._stats
+++ /dev/null
-# Copyright (c) 2013 Huawei Technologies Co., Ltd.
-# Copyright (c) 2012 OpenStack LLC.
-# 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.
-
-from xml.etree import ElementTree as ET
-
-from oslo_log import log as logging
-
-from cinder.i18n import _LE
-
-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'}
-
-
-def parse_xml_file(filepath):
- """Get root of xml file."""
- try:
- tree = ET.parse(filepath)
- root = tree.getroot()
- return root
- except IOError as err:
- LOG.error(_LE('parse_xml_file: %s') % err)
- raise err
-
-
-def get_xml_item(xml_root, item):
- """Get the given item details.
-
- :param xml_root: The root of xml tree
- :param item: The tag need to get
- :return: A dict contains all the config info of the given item.
- """
- items_list = []
- items = xml_root.findall(item)
- for item in items:
- tmp_dict = {'text': None, 'attrib': {}}
- if item.text:
- tmp_dict['text'] = item.text.strip()
- for key, val in item.attrib.items():
- if val:
- item.attrib[key] = val.strip()
- tmp_dict['attrib'] = item.attrib
- items_list.append(tmp_dict)
- return items_list
-
-
-def 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):
- """Get host OS type from xml config file.
-
- :param host_ip: The IP of Nova host
- :param config: xml config file
- :return: host OS type
- """
- os_conf = {}
- root = parse_xml_file(config)
- hosts_list = get_xml_item(root, 'Host')
- for host in hosts_list:
- os = host['attrib']['OSType'].strip()
- ips = [ip.strip() for ip in host['attrib']['HostIP'].split(',')]
- os_conf[os] = ips
- host_os = None
- for k, v in os_conf.items():
- if host_ip in v:
- host_os = os_type.get(k, None)
- if not host_os:
- host_os = 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
+++ /dev/null
-# Copyright (c) 2013 - 2014 Huawei Technologies Co., Ltd.
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-"""Common class for Huawei 18000 storage drivers."""
-
-import base64
-import cookielib
-import json
-import time
-import urllib2
-import uuid
-from xml.etree import ElementTree as ET
-
-from oslo_log import log as logging
-from oslo_utils import excutils
-from oslo_utils import units
-import six
-
-from cinder import context
-from cinder import exception
-from cinder.i18n import _, _LE, _LI, _LW
-from cinder.openstack.common import loopingcall
-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():
- """Common class for Huawei OceanStor 18000 storage system."""
-
- def __init__(self, configuration):
- self.configuration = configuration
- self.cookie = cookielib.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 = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.cookie))
- urllib2.install_opener(opener)
-
- try:
- urllib2.socket.setdefaulttimeout(720)
- req = urllib2.Request(url, data, self.headers)
- if method:
- req.get_method = lambda: method
- res = urllib2.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:
- err_msg = (_LE('JSON transfer error: %s.') % err)
- LOG.error(err_msg)
- 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)
- 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': six.text_type(lungroup_id),
- 'view_id': six.text_type(view_id),
- 'lun_id': six.text_type(lun_id)})
-
- try:
- # Create lungroup and add LUN into to lungroup.
- if lungroup_id is None:
- lungroup_id = self._create_lungroup(lungroup_name)
- 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():
- 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, 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:
- msg = (_LE("JSON transfer data error. %s") % err)
- LOG.error(msg)
- 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
- 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.iteritems():
- if key in huawei_valid_keys:
- qos[key.upper()] = value
-
- return qos
-
- def _get_qos_value(self, qos, key, default=None):
- if key in qos:
- return qos[key]
- else:
- return default
-
- def _create_qos_policy(self, qos, lun_id):
-
- # Get local time.
- localtime = time.strftime('%Y%m%d%H%M%S', time.localtime(time.time()))
- # Package QoS name.
- qos_name = QOS_NAME_PREFIX + lun_id + '_' + localtime
- baseData = {"TYPE": "230",
- "NAME": qos_name,
- "LUNLIST": ["%s" % lun_id],
- "CLASSTYPE": "1",
- "SCHEDULEPOLICY": "2",
- "SCHEDULESTARTTIME": "1410969600",
- "STARTTIME": "08:00",
- "DURATION": "86400",
- "CYCLESET": "[1,2,3,4,5,6,0]"
- }
-
- mergedata = dict(baseData.items() + qos.items())
- url = self.url + "/ioclass/"
- data = json.dumps(mergedata)
-
- result = self.call(url, data)
- self._assert_rest_result(result, 'Create QoS policy error.')
-
- return result['data']['ID']
-
- def _delete_qos_policy(self, qos_id):
- """Delete a QoS policy."""
-
- url = self.url + "/ioclass/" + qos_id
- data = json.dumps({"TYPE": "230",
- "ID": qos_id})
-
- result = self.call(url, data, 'DELETE')
- self._assert_rest_result(result, 'Delete QoS policy error.')
-
- def _active_deactive_qos(self, qos_id, enablestatus):
- """Active or deactive QoS.
-
- enablestatus: true (active)
- enbalestatus: false (deactive)
- """
-
- url = self.url + "/ioclass/active/" + qos_id
- data = json.dumps({"TYPE": 230,
- "ID": qos_id,
- "ENABLESTATUS": enablestatus})
- result = self.call(url, data, "PUT")
- self._assert_rest_result(result, 'Active or Deactive QoS error.')
-
- def _get_qos_info(self, qos_id):
- """Get QoS information."""
-
- url = self.url + "/ioclass/" + qos_id
- data = json.dumps({"TYPE": "230",
- "ID": qos_id})
- result = self.call(url, data, "GET")
- self._assert_rest_result(result, 'Get QoS information error.')
-
- return result['data']
-
- def _check_qos_high_priority(self, qos):
- """Check QoS priority."""
-
- for key, value in qos.iteritems():
- if (key.find('MIN') == 0) or (key.find('LATENCY') == 0):
- return True
-
- return False
-
- def _change_lun_priority(self, lunid):
- """Change lun priority to high."""
-
- url = self.url + "/lun/" + lunid
- data = json.dumps({"TYPE": "11",
- "ID": lunid,
- "IOPRIORITY": "3"})
-
- result = self.call(url, data, "PUT")
- self._assert_rest_result(result, 'Change lun priority error.')
-
- def _get_qosid_by_lunid(self, lunid):
- """Get qosid by lunid."""
-
- url = self.url + "/lun/" + lunid
- data = json.dumps({"TYPE": "11",
- "ID": lunid})
-
- result = self.call(url, data, "GET")
- self._assert_rest_result(result, 'Get qosid by lunid error.')
-
- return result['data']['IOCLASSID']
-
- def _get_lungroupid_by_lunid(self, lunid):
- """Get lungroupid by lunid."""
-
- url = self.url + ("/lungroup/associate?TYPE=256"
- "&ASSOCIATEOBJTYPE=11&ASSOCIATEOBJID=%s" % lunid)
-
- result = self.call(url, None, "GET")
- self._assert_rest_result(result, 'Get lungroupid by lunid error.')
-
- lun_group_id = None
- # Lun only in one lungroup.
- if 'data' in result:
- for item in result['data']:
- lun_group_id = item['ID']
-
- return lun_group_id
+++ /dev/null
-# Copyright (c) 2013 Huawei Technologies Co., Ltd.
-# Copyright (c) 2012 OpenStack Foundation
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-"""
-Common classes for Huawei OceanStor T series and Dorado series storage arrays.
-
-The common classes provide the drivers command line operation using SSH.
-"""
-
-import base64
-import re
-import socket
-import threading
-import time
-from xml.etree import ElementTree as ET
-
-from oslo_log import log as logging
-from oslo_utils import excutils
-
-from cinder import context
-from cinder import exception
-from cinder.i18n import _, _LE, _LI, _LW
-from cinder import ssh_utils
-from cinder import utils
-from cinder.volume.drivers.huawei import huawei_utils
-from cinder.volume import volume_types
-
-
-LOG = logging.getLogger(__name__)
-
-HOST_GROUP_NAME = 'HostGroup_OpenStack'
-HOST_NAME_PREFIX = 'Host_'
-VOL_AND_SNAP_NAME_PREFIX = 'OpenStack_'
-HOST_LUN_ERR_MSG = 'host LUN is mapped or does not exist'
-
-
-def ssh_read(user, channel, cmd, timeout):
- """Get results of CLI commands."""
- result = ''
- channel.settimeout(timeout)
- while True:
- try:
- result = result + channel.recv(8192)
- except socket.timeout as err:
- msg = _('ssh_read: Read SSH timeout. %s') % err
- LOG.error(msg)
- raise err
- else:
- # CLI returns welcome information when first log in. So need to
- # deal differently.
- if not re.search('Welcome', result):
- # Complete CLI response starts with CLI cmd and
- # ends with "username:/>".
- if result.startswith(cmd) and result.endswith(user + ':/>'):
- break
- # Some commands need to send 'y'.
- elif re.search('(y/n)|y or n', result):
- break
- # Reach maximum limit of SSH connection.
- elif re.search('No response message', result):
- msg = _('No response message. Please check system status.')
- LOG.error(msg)
- raise exception.CinderException(msg)
- elif (re.search(user + ':/>' + cmd, result) and
- result.endswith(user + ':/>')):
- break
-
- # Filter the last line: username:/> .
- result = '\r\n'.join(result.split('\r\n')[:-1])
- # Filter welcome information.
- index = result.find(user + ':/>')
-
- return (result[index:] if index > -1 else result)
-
-
-class TseriesCommon():
- """Common class for Huawei T series storage arrays."""
-
- def __init__(self, configuration=None):
- self.configuration = configuration
- self.xml_conf = self.configuration.cinder_huawei_conf_file
- self.login_info = {}
- self.lun_distribution = [0, 0]
- self.hostgroup_id = None
- self.ssh_pool = None
- self.lock_ip = threading.Lock()
- self.luncopy_list = [] # to store LUNCopy name
- self.extended_lun_dict = {}
-
- def do_setup(self, context):
- """Check config file."""
- LOG.debug('do_setup')
-
- self._check_conf_file()
- self.login_info = self._get_login_info()
- exist_luns = self._get_all_luns_info()
- self.lun_distribution = self._get_lun_distribution_info(exist_luns)
- self.luncopy_list = self._get_all_luncopy_name()
- self.hostgroup_id = self._get_hostgroup_id(HOST_GROUP_NAME)
- self.extended_lun_dict = self._get_extended_lun(exist_luns)
-
- def _check_conf_file(self):
- """Check config file, make sure essential items are set."""
- root = huawei_utils.parse_xml_file(self.xml_conf)
- check_list = ['Storage/ControllerIP0', 'Storage/ControllerIP1',
- 'Storage/UserName', 'Storage/UserPassword']
- for item in check_list:
- if not huawei_utils.is_xml_item_exist(root, item):
- err_msg = (_('_check_conf_file: Config file invalid. '
- '%s must be set.') % item)
- LOG.error(err_msg)
- raise exception.InvalidInput(reason=err_msg)
-
- # Make sure storage pool is set.
- if not huawei_utils.is_xml_item_exist(root, 'LUN/StoragePool', 'Name'):
- err_msg = _('_check_conf_file: Config file invalid. '
- 'StoragePool must be set.')
- LOG.error(err_msg)
- raise exception.InvalidInput(reason=err_msg)
-
- # If setting os type, make sure it valid.
- if huawei_utils.is_xml_item_exist(root, 'Host', 'OSType'):
- os_list = huawei_utils.os_type.keys()
- if not huawei_utils.is_xml_item_valid(root, 'Host', os_list,
- 'OSType'):
- err_msg = (_('_check_conf_file: Config file invalid. '
- 'Host OSType is invalid.\n'
- 'The valid values are: %(os_list)s')
- % {'os_list': os_list})
- LOG.error(err_msg)
- raise exception.InvalidInput(reason=err_msg)
-
- 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['ControllerIP0'] = (
- root.findtext('Storage/ControllerIP0')).strip()
- logininfo['ControllerIP1'] = (
- root.findtext('Storage/ControllerIP1')).strip()
-
- need_encode = False
- for key in ['UserName', 'UserPassword']:
- node = root.find('Storage/%s' % key)
- node_text = node.text.strip()
- # Prefix !$$$ means encoded already.
- if node_text.find('!$$$') > -1:
- logininfo[key] = base64.b64decode(node_text[4:])
- else:
- logininfo[key] = node_text
- 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.info(_LI('_get_login_info: %s') % err)
-
- return logininfo
-
- def _change_file_mode(self, filepath):
- utils.execute('chmod', '777', filepath, run_as_root=True)
-
- def _get_lun_distribution_info(self, luns):
- """Get LUN distribution information.
-
- For we have two controllers for each array, we want to make all
- LUNs(just for Thick LUN) distributed evenly. The driver uses the
- LUN distribution info to determine in which controller to create
- a new LUN.
-
- """
-
- ctr_info = [0, 0]
- for lun in luns:
- if (lun[6].startswith(VOL_AND_SNAP_NAME_PREFIX) and
- lun[8] == 'THICK'):
- if lun[4] == 'A':
- ctr_info[0] += 1
- else:
- ctr_info[1] += 1
- return ctr_info
-
- def check_for_setup_error(self):
- pass
-
- def _get_all_luncopy_name(self):
- cli_cmd = 'showluncopy'
- out = self._execute_cli(cli_cmd)
- luncopy_ids = []
- if re.search('LUN Copy Information', out):
- for line in out.split('\r\n')[6:-2]:
- tmp_line = line.split()
- if tmp_line[0].startswith(VOL_AND_SNAP_NAME_PREFIX):
- luncopy_ids.append(tmp_line[0])
- return luncopy_ids
-
- def _get_extended_lun(self, luns):
- extended_dict = {}
- for lun in luns:
- if lun[6].startswith('ext'):
- vol_name = lun[6].split('_')[1]
- add_ids = extended_dict.get(vol_name, [])
- add_ids = add_ids.append(lun[0])
- extended_dict[vol_name] = add_ids
- return extended_dict
-
- def create_volume(self, volume):
- """Create a new volume."""
- volume_name = self._name_translate(volume['name'])
-
- LOG.debug('create_volume: volume name: %s' % volume_name)
-
- self._update_login_info()
- if int(volume['size']) == 0:
- volume_size = '100M'
- else:
- volume_size = '%sG' % volume['size']
- type_id = volume['volume_type_id']
- parameters = self._parse_volume_type(type_id)
- volume_id = self._create_volume(volume_name, volume_size, parameters)
- return volume_id
-
- def _name_translate(self, name):
- """Form new names for volume and snapshot because of
- 32-character limit on names.
- """
- newname = VOL_AND_SNAP_NAME_PREFIX + str(hash(name))
-
- LOG.debug('_name_translate: Name in cinder: %(old)s, new name in '
- 'storage system: %(new)s' % {'old': name, 'new': newname})
-
- return newname
-
- def _update_login_info(self):
- """Update user name and password."""
- self.login_info = self._get_login_info()
-
- def _parse_volume_type(self, typeid):
- """Parse volume type form extra_specs by type id.
-
- The keys in extra_specs must be consistent with the element in config
- file. And the keys can starts with "drivers" to make them distinguished
- from capabilities keys, if you like.
-
- """
-
- params = self._get_lun_params()
- if typeid is not None:
- ctxt = context.get_admin_context()
- volume_type = volume_types.get_volume_type(ctxt, typeid)
- specs = volume_type.get('extra_specs')
- for key, value in specs.iteritems():
- key_split = key.split(':')
- if len(key_split) > 1:
- if key_split[0] == 'drivers':
- key = key_split[1]
- else:
- continue
- else:
- key = key_split[0]
-
- if key in params.keys():
- params[key] = value.strip()
- else:
- conf = self.configuration.cinder_huawei_conf_file
- LOG.warn(_LW('_parse_volume_type: Unacceptable parameter '
- '%(key)s. Please check this key in '
- 'extra_specs '
- 'and make it consistent with the element in '
- 'configuration file %(conf)s.')
- % {'key': key,
- 'conf': conf})
-
- return params
-
- def _create_volume(self, name, size, params):
- """Create a new volume with the given name and size."""
- cli_cmd = ('createlun -n %(name)s -lunsize %(size)s '
- '-wrtype %(wrtype)s ' % {'name': name,
- 'size': size,
- 'wrtype': params['WriteType']})
-
- # If write type is "write through", no need to set mirror switch.
- if params['WriteType'] != '2':
- cli_cmd = cli_cmd + ('-mirrorsw %(mirrorsw)s '
- % {'mirrorsw': params['MirrorSwitch']})
-
- # Differences exist between "Thin" and "thick" LUN in CLI commands.
- luntype = params['LUNType']
- ctr = None
- if luntype == 'Thin':
- cli_cmd = cli_cmd + ('-pool %(pool)s '
- % {'pool': params['StoragePool']})
- else:
- # Make LUN distributed to A/B controllers evenly,
- # just for Thick LUN.
- ctr = self._calculate_lun_ctr()
- cli_cmd = cli_cmd + ('-rg %(raidgroup)s -susize %(susize)s '
- '-c %(ctr)s '
- % {'raidgroup': params['StoragePool'],
- 'susize': params['StripUnitSize'],
- 'ctr': ctr})
-
- prefetch_value_or_times = ''
- pretype = '-pretype %s ' % params['PrefetchType']
- # If constant prefetch, we should specify prefetch value.
- if params['PrefetchType'] == '1':
- prefetch_value_or_times = '-value %s' % params['PrefetchValue']
- # If variable prefetch, we should specify prefetch multiple.
- elif params['PrefetchType'] == '2':
- prefetch_value_or_times = '-times %s' % params['PrefetchTimes']
-
- cli_cmd = cli_cmd + pretype + prefetch_value_or_times
- out = self._execute_cli(cli_cmd)
-
- self._assert_cli_operate_out('_create_volume',
- 'Failed to create volume %s' % name,
- cli_cmd, out)
- if ctr:
- self._update_lun_distribution(ctr)
-
- return self._get_lun_id(name)
-
- def _calculate_lun_ctr(self):
- return ('a' if self.lun_distribution[0] <= self.lun_distribution[1]
- else 'b')
-
- def _update_lun_distribution(self, ctr):
- index = (0 if ctr == 'a' else 1)
- self.lun_distribution[index] += 1
-
- def _get_lun_params(self):
- params_conf = self._parse_conf_lun_params()
- # Select a pool with maximum capacity.
- pools_dev = self._get_dev_pool_info(params_conf['LUNType'])
- params_conf['StoragePool'] = \
- self._get_maximum_capacity_pool_id(params_conf['StoragePool'],
- pools_dev,
- params_conf['LUNType'])
- return params_conf
-
- def _parse_conf_lun_params(self):
- """Get parameters from config file for creating LUN."""
- # Default LUN parameters.
- conf_params = {'LUNType': 'Thin',
- 'StripUnitSize': '64',
- 'WriteType': '1',
- 'MirrorSwitch': '1',
- 'PrefetchType': '3',
- 'PrefetchValue': '0',
- 'PrefetchTimes': '0',
- 'StoragePool': []}
-
- root = huawei_utils.parse_xml_file(self.xml_conf)
-
- luntype = root.findtext('LUN/LUNType')
- if luntype:
- if luntype.strip() in ['Thick', 'Thin']:
- conf_params['LUNType'] = luntype.strip()
- else:
- err_msg = (_('LUNType must be "Thin" or "Thick". '
- 'LUNType:%(type)s') % {'type': luntype})
- LOG.error(err_msg)
- raise exception.InvalidInput(reason=err_msg)
-
- stripunitsize = root.findtext('LUN/StripUnitSize')
- if stripunitsize:
- conf_params['StripUnitSize'] = stripunitsize.strip()
- writetype = root.findtext('LUN/WriteType')
- if writetype:
- conf_params['WriteType'] = writetype.strip()
- mirrorswitch = root.findtext('LUN/MirrorSwitch')
- if mirrorswitch:
- conf_params['MirrorSwitch'] = mirrorswitch.strip()
- prefetch = root.find('LUN/Prefetch')
- if prefetch is not None and prefetch.attrib['Type']:
- conf_params['PrefetchType'] = prefetch.attrib['Type'].strip()
- if conf_params['PrefetchType'] == '1':
- conf_params['PrefetchValue'] = prefetch.attrib['Value'].strip()
- elif conf_params['PrefetchType'] == '2':
- conf_params['PrefetchTimes'] = prefetch.attrib['Value'].strip()
- else:
- LOG.debug('_parse_conf_lun_params: Use default prefetch type. '
- 'Prefetch type: Intelligent')
-
- pools_conf = root.findall('LUN/StoragePool')
- for pool in pools_conf:
- conf_params['StoragePool'].append(pool.attrib['Name'].strip())
-
- return conf_params
-
- def _get_maximum_capacity_pool_id(self, pools_conf, pools_dev, luntype):
- """Get the maximum pool from config file.
-
- According to the given pools' names in config file,
- we select the pool with maximum free capacity.
-
- """
-
- nameindex, sizeindex = ((1, 4) if luntype == 'Thin' else (5, 3))
- pools_dev = sorted(pools_dev, key=lambda x: float(x[sizeindex]))
- while len(pools_dev) > 0:
- pool = pools_dev.pop()
- if pool[nameindex] in pools_conf:
- return pool[0]
-
- err_msg = (_('_get_maximum_capacity_pool_id: Failed to get pool '
- 'id. Please check config file and make sure '
- 'the StoragePool %s is created in storage '
- 'array.') % pools_conf)
- LOG.error(err_msg)
- raise exception.InvalidInput(reason=err_msg)
-
- def _execute_cli(self, cmd):
- """Build SSH connection and execute CLI commands.
-
- If the connection to first controller timeout,
- try to connect to the other controller.
-
- """
-
- LOG.debug('CLI command: %s' % cmd)
- connect_times = 1
- ip0 = self.login_info['ControllerIP0']
- ip1 = self.login_info['ControllerIP1']
- user = self.login_info['UserName']
- pwd = self.login_info['UserPassword']
- if not self.ssh_pool:
- self.ssh_pool = ssh_utils.SSHPool(ip0, 22, 30, user, pwd,
- max_size=2)
- ssh_client = None
- while True:
- try:
- if connect_times == 2:
- # Switch to the other controller.
- with self.lock_ip:
- if ssh_client:
- if ssh_client.server_ip == self.ssh_pool.ip:
- self.ssh_pool.ip = (ip1
- if self.ssh_pool.ip == ip0
- else ip0)
- old_ip = ssh_client.server_ip
- # Create a new client to replace the old one.
- if getattr(ssh_client, 'chan', None):
- ssh_client.chan.close()
- ssh_client.close()
- ssh_client = self.ssh_pool.create()
- self._reset_transport_timeout(ssh_client, 0.1)
- else:
- self.ssh_pool.ip = ip1
- old_ip = ip0
-
- LOG.info(_LI('_execute_cli: Can not connect to IP '
- '%(old)s, try to connect to the other '
- 'IP %(new)s.')
- % {'old': old_ip, 'new': self.ssh_pool.ip})
-
- if not ssh_client:
- # Get an SSH client from SSH pool.
- ssh_client = self.ssh_pool.get()
- self._reset_transport_timeout(ssh_client, 0.1)
- # "server_ip" shows the IP of SSH server.
- if not getattr(ssh_client, 'server_ip', None):
- with self.lock_ip:
- setattr(ssh_client, 'server_ip', self.ssh_pool.ip)
- # An SSH client owns one "chan".
- if not getattr(ssh_client, 'chan', None):
- setattr(ssh_client, 'chan',
- utils.create_channel(ssh_client, 600, 800))
-
- while True:
- ssh_client.chan.send(cmd + '\n')
- out = ssh_read(user, ssh_client.chan, cmd, 20)
- if out.find('(y/n)') > -1 or out.find('y or n') > -1:
- cmd = 'y'
- else:
- # Put SSH client back into SSH pool.
- self.ssh_pool.put(ssh_client)
- return out
-
- except Exception as err:
- if connect_times < 2:
- connect_times += 1
- continue
- else:
- if ssh_client:
- self.ssh_pool.remove(ssh_client)
- LOG.error(_LE('_execute_cli: %s') % err)
- raise err
-
- def _reset_transport_timeout(self, ssh, time):
- transport = ssh.get_transport()
- transport.sock.settimeout(time)
-
- def delete_volume(self, volume):
- volume_name = self._name_translate(volume['name'])
-
- LOG.debug('delete_volume: volume name: %s' % volume_name)
-
- self._update_login_info()
- volume_id = volume.get('provider_location', None)
- if volume_id is None or not self._check_volume_created(volume_id):
- err_msg = (_('delete_volume: Volume %(name)s does not exist.')
- % {'name': volume['name']})
- LOG.warn(err_msg)
- return
- else:
- name = volume_name[len(VOL_AND_SNAP_NAME_PREFIX):]
- added_vol_ids = self.extended_lun_dict.get(name, None)
- if added_vol_ids:
- self._del_lun_from_extended_lun(volume_id, added_vol_ids)
- self.extended_lun_dict.pop(name)
- self._delete_volume(volume_id)
-
- def _check_volume_created(self, volume_id):
- cli_cmd = 'showlun -lun %s' % volume_id
- out = self._execute_cli(cli_cmd)
- return (True if re.search('LUN Information', out) else False)
-
- def _del_lun_from_extended_lun(self, extended_id, added_ids):
- cli_cmd = 'rmlunfromextlun -ext %s' % extended_id
- out = self._execute_cli(cli_cmd)
-
- self._assert_cli_operate_out('_del_lun_from_extended_lun',
- ('Failed to remove LUN from extended '
- 'LUN: %s' % extended_id),
- cli_cmd, out)
- for id in added_ids:
- cli_cmd = 'dellun -lun %s' % id
- out = self._execute_cli(cli_cmd)
-
- self._assert_cli_operate_out('_del_lun_from_extended_lun',
- 'Failed to delete LUN: %s' % id,
- cli_cmd, out)
-
- def _delete_volume(self, volumeid):
- """Run CLI command to delete volume."""
- cli_cmd = 'dellun -force -lun %s' % volumeid
- out = self._execute_cli(cli_cmd)
-
- self._assert_cli_operate_out('_delete_volume',
- ('Failed to delete volume. volume id: %s'
- % volumeid),
- cli_cmd, out)
-
- 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._name_translate(snapshot['name'])
- volume_name = self._name_translate(volume['name'])
-
- LOG.debug('create_volume_from_snapshot: snapshot '
- 'name: %(snapshot)s, volume name: %(volume)s'
- % {'snapshot': snapshot_name,
- 'volume': volume_name})
-
- self._update_login_info()
- snapshot_id = snapshot.get('provider_location', None)
- if not snapshot_id:
- snapshot_id = self._get_snapshot_id(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)
-
- # Create a target LUN.
- if int(volume['size']) == 0:
- volume_size = '%sG' % snapshot['volume_size']
- else:
- volume_size = '%sG' % volume['size']
- type_id = volume['volume_type_id']
- parameters = self._parse_volume_type(type_id)
- tgt_vol_id = self._create_volume(volume_name, volume_size, parameters)
- self._copy_volume(snapshot_id, tgt_vol_id)
-
- return tgt_vol_id
-
- def _copy_volume(self, src_vol_id, tgt_vol_id):
- """Copy a volume or snapshot to target volume."""
- luncopy_name = VOL_AND_SNAP_NAME_PREFIX + src_vol_id + '_' + tgt_vol_id
- self._create_luncopy(luncopy_name, src_vol_id, tgt_vol_id)
- self.luncopy_list.append(luncopy_name)
- luncopy_id = self._get_luncopy_info(luncopy_name)[1]
- try:
- self._start_luncopy(luncopy_id)
- self._wait_for_luncopy(luncopy_name)
- # Delete the target volume if LUNcopy failed.
- except Exception:
- with excutils.save_and_reraise_exception():
- # Need to remove the LUNcopy of the volume first.
- self._delete_luncopy(luncopy_id)
- self.luncopy_list.remove(luncopy_name)
- self._delete_volume(tgt_vol_id)
- # Need to delete LUNcopy finally.
- self._delete_luncopy(luncopy_id)
- self.luncopy_list.remove(luncopy_name)
-
- def _create_luncopy(self, luncopyname, srclunid, tgtlunid):
- """Run CLI command to create LUNcopy."""
- cli_cmd = ('createluncopy -n %(name)s -l 4 -slun %(srclunid)s '
- '-tlun %(tgtlunid)s' % {'name': luncopyname,
- 'srclunid': srclunid,
- 'tgtlunid': tgtlunid})
- out = self._execute_cli(cli_cmd)
-
- self._assert_cli_operate_out('_create_luncopy',
- ('Failed to create LUNcopy %s'
- % luncopyname),
- cli_cmd, out)
-
- def _start_luncopy(self, luncopyid):
- """Run CLI command to start LUNcopy."""
- cli_cmd = ('chgluncopystatus -luncopy %(luncopyid)s -start'
- % {'luncopyid': luncopyid})
- out = self._execute_cli(cli_cmd)
-
- self._assert_cli_operate_out('_start_luncopy',
- 'Failed to start LUNcopy %s' % luncopyid,
- cli_cmd, out)
-
- def _wait_for_luncopy(self, luncopyname):
- """Wait for LUNcopy to complete."""
- while True:
- luncopy_info = self._get_luncopy_info(luncopyname)
- # If state is complete
- if luncopy_info[3] == 'Complete':
- break
- # If status is not normal
- elif luncopy_info[4] != 'Normal':
- err_msg = (_('_wait_for_luncopy: LUNcopy %(luncopyname)s '
- 'status is %(status)s.')
- % {'luncopyname': luncopyname,
- 'status': luncopy_info[4]})
- LOG.error(err_msg)
- raise exception.VolumeBackendAPIException(data=err_msg)
-
- time.sleep(10)
-
- def _get_luncopy_info(self, luncopyname):
- """Return a LUNcopy information list."""
- cli_cmd = 'showluncopy'
- out = self._execute_cli(cli_cmd)
-
- self._assert_cli_out(re.search('LUN Copy Information', out),
- '_get_luncopy_info',
- 'No LUNcopy information was found.',
- cli_cmd, out)
-
- for line in out.split('\r\n')[6:-2]:
- tmp_line = line.split()
- if tmp_line[0] == luncopyname:
- return tmp_line
- return None
-
- def _delete_luncopy(self, luncopyid):
- """Run CLI command to delete LUNcopy."""
- cli_cmd = 'delluncopy -luncopy %(id)s' % {'id': luncopyid}
- out = self._execute_cli(cli_cmd)
-
- self._assert_cli_operate_out('_delete_luncopy',
- 'Failed to delete LUNcopy %s' % luncopyid,
- cli_cmd, out)
-
- def create_cloned_volume(self, tgt_volume, src_volume):
- src_vol_name = self._name_translate(src_volume['name'])
- tgt_vol_name = self._name_translate(tgt_volume['name'])
-
- LOG.debug('create_cloned_volume: src volume: %(src)s, '
- 'tgt volume: %(tgt)s' % {'src': src_vol_name,
- 'tgt': tgt_vol_name})
-
- self._update_login_info()
- src_vol_id = src_volume.get('provider_location', None)
- if not src_vol_id:
- src_vol_id = self._get_lun_id(src_vol_name)
- if src_vol_id is None:
- err_msg = (_('Source volume %(name)s does not exist.')
- % {'name': src_vol_name})
- LOG.error(err_msg)
- raise exception.VolumeNotFound(volume_id=src_vol_name)
-
- # Create a target volume.
- if int(tgt_volume['size']) == 0:
- tgt_vol_size = '%sG' % src_vol_name['size']
- else:
- tgt_vol_size = '%sG' % tgt_volume['size']
- type_id = tgt_volume['volume_type_id']
- params = self._parse_volume_type(type_id)
- tgt_vol_id = self._create_volume(tgt_vol_name, tgt_vol_size, params)
- self._copy_volume(src_vol_id, tgt_vol_id)
-
- return tgt_vol_id
-
- def _get_all_luns_info(self):
- cli_cmd = 'showlun'
- out = self._execute_cli(cli_cmd)
- luns = []
- if re.search('LUN Information', out):
- for line in out.split('\r\n')[6:-2]:
- luns.append(line.replace('Not format', 'Notformat').split())
- return luns
-
- def _get_lun_id(self, lun_name):
- luns = self._get_all_luns_info()
- if luns:
- for lun in luns:
- if lun[6] == lun_name:
- return lun[0]
- return None
-
- def extend_volume(self, volume, new_size):
- extended_vol_name = self._name_translate(volume['name'])
- name = extended_vol_name[len(VOL_AND_SNAP_NAME_PREFIX):]
- added_vol_ids = self.extended_lun_dict.get(name, [])
- added_vol_name = ('ext_' + extended_vol_name.split('_')[1] + '_' +
- str(len(added_vol_ids)))
- added_vol_size = str(int(new_size) - int(volume['size'])) + 'G'
-
- LOG.debug('extend_volume: extended volume name: %(extended_name)s '
- 'new added volume name: %(added_name)s '
- 'new added volume size: %(added_size)s'
- % {'extended_name': extended_vol_name,
- 'added_name': added_vol_name,
- 'added_size': added_vol_size})
-
- if not volume['provider_location']:
- err_msg = (_('extend_volume: volume %s does not exist.')
- % extended_vol_name)
- LOG.error(err_msg)
- raise exception.VolumeNotFound(volume_id=extended_vol_name)
-
- type_id = volume['volume_type_id']
- parameters = self._parse_volume_type(type_id)
- added_vol_id = self._create_volume(added_vol_name, added_vol_size,
- parameters)
- try:
- self._extend_volume(volume['provider_location'], added_vol_id)
- except Exception:
- with excutils.save_and_reraise_exception():
- self._delete_volume(added_vol_id)
-
- added_vol_ids.append(added_vol_id)
- self.extended_lun_dict[name] = added_vol_ids
-
- def _extend_volume(self, extended_vol_id, added_vol_id):
- cli_cmd = ('addluntoextlun -extlun %(extended_vol)s '
- '-lun %(added_vol)s' % {'extended_vol': extended_vol_id,
- 'added_vol': added_vol_id})
- out = self._execute_cli(cli_cmd)
- self._assert_cli_operate_out('_extend_volume',
- ('Failed to extend volume %s'
- % extended_vol_id),
- cli_cmd, out)
-
- def create_snapshot(self, snapshot):
- snapshot_name = self._name_translate(snapshot['name'])
- volume_name = self._name_translate(snapshot['volume_name'])
-
- LOG.debug('create_snapshot: snapshot name: %(snapshot)s, '
- 'volume name: %(volume)s'
- % {'snapshot': snapshot_name,
- 'volume': volume_name})
-
- if self._resource_pool_enough() is False:
- err_msg = (_('create_snapshot: '
- 'Resource pool needs 1GB valid size at least.'))
- LOG.error(err_msg)
- raise exception.VolumeBackendAPIException(data=err_msg)
-
- lun_id = self._get_lun_id(volume_name)
- if lun_id is None:
- err_msg = (_('create_snapshot: Volume %(name)s does not exist.')
- % {'name': volume_name})
- LOG.error(err_msg)
- raise exception.VolumeNotFound(volume_id=volume_name)
-
- self._create_snapshot(snapshot_name, lun_id)
- snapshot_id = self._get_snapshot_id(snapshot_name)
- try:
- self._active_snapshot(snapshot_id)
- except Exception:
- with excutils.save_and_reraise_exception():
- self._delete_snapshot(snapshot_id)
-
- return snapshot_id
-
- def _resource_pool_enough(self):
- """Check whether resource pools' valid size is more than 1GB."""
- cli_cmd = 'showrespool'
- out = self._execute_cli(cli_cmd)
- for line in out.split('\r\n')[6:-2]:
- tmp_line = line.split()
- if float(tmp_line[3]) < 1024.0:
- return False
-
- return True
-
- def _create_snapshot(self, snapshotname, srclunid):
- """Create a snapshot with snapshot name and source LUN ID."""
- cli_cmd = ('createsnapshot -lun %(lunid)s -n %(snapname)s'
- % {'lunid': srclunid,
- 'snapname': snapshotname})
- out = self._execute_cli(cli_cmd)
-
- self._assert_cli_operate_out('_create_snapshot',
- ('Failed to create snapshot %s'
- % snapshotname),
- cli_cmd, out)
-
- def _get_snapshot_id(self, snapshotname):
- cli_cmd = 'showsnapshot'
- out = self._execute_cli(cli_cmd)
- if re.search('Snapshot Information', out):
- for line in out.split('\r\n')[6:-2]:
- emp_line = line.split()
- if emp_line[0] == snapshotname:
- return emp_line[1]
- return None
-
- def _active_snapshot(self, snapshotid):
- """Run CLI command to active snapshot."""
- cli_cmd = ('actvsnapshot -snapshot %(snapshotid)s'
- % {'snapshotid': snapshotid})
- out = self._execute_cli(cli_cmd)
-
- self._assert_cli_operate_out('_active_snapshot',
- ('Failed to active snapshot %s'
- % snapshotid),
- cli_cmd, out)
-
- def delete_snapshot(self, snapshot):
- snapshot_name = self._name_translate(snapshot['name'])
- volume_name = self._name_translate(snapshot['volume_name'])
-
- LOG.debug('delete_snapshot: snapshot name: %(snapshot)s, '
- 'volume name: %(volume)s' % {'snapshot': snapshot_name,
- 'volume': volume_name})
-
- self._update_login_info()
- snapshot_id = snapshot.get('provider_location', None)
- if ((snapshot_id is not None) and
- self._check_snapshot_created(snapshot_id)):
- # Not allow to delete snapshot if it is copying.
- if self._snapshot_in_luncopy(snapshot_id):
- err_msg = (_('delete_snapshot: Can not delete snapshot %s '
- 'for it is a source LUN of LUNCopy.')
- % snapshot_name)
- LOG.error(err_msg)
- raise exception.VolumeBackendAPIException(data=err_msg)
-
- self._delete_snapshot(snapshot_id)
- else:
- err_msg = (_('delete_snapshot: Snapshot %(snap)s does not exist.')
- % {'snap': snapshot_name})
- LOG.warn(err_msg)
-
- def _check_snapshot_created(self, snapshot_id):
- cli_cmd = 'showsnapshot -snapshot %(snap)s' % {'snap': snapshot_id}
- out = self._execute_cli(cli_cmd)
- return (True if re.search('Snapshot Information', out) else False)
-
- def _snapshot_in_luncopy(self, snapshot_id):
- for name in self.luncopy_list:
- if name.startswith(VOL_AND_SNAP_NAME_PREFIX + snapshot_id):
- return True
- return False
-
- def _delete_snapshot(self, snapshotid):
- """Send CLI command to delete snapshot.
-
- Firstly, disable the snapshot, then delete it.
-
- """
-
- cli_cmd = ('disablesnapshot -snapshot %(snapshotid)s'
- % {'snapshotid': snapshotid})
- out = self._execute_cli(cli_cmd)
-
- self._assert_cli_operate_out('_delete_snapshot',
- ('Failed to disable snapshot %s'
- % snapshotid),
- cli_cmd, out)
-
- cli_cmd = ('delsnapshot -snapshot %(snapshotid)s'
- % {'snapshotid': snapshotid})
- out = self._execute_cli(cli_cmd)
-
- self._assert_cli_operate_out('_delete_snapshot',
- ('Failed to delete snapshot %s'
- % snapshotid),
- cli_cmd, out)
-
- def _assert_cli_out(self, condition, func, msg, cmd, cliout):
- """Assertion for CLI query out."""
- if not condition:
- err_msg = (_('%(func)s: %(msg)s\nCLI command: %(cmd)s\n'
- 'CLI out: %(out)s') % {'func': func,
- 'msg': msg,
- 'cmd': cmd,
- 'out': cliout})
- LOG.error(err_msg)
- raise exception.VolumeBackendAPIException(data=err_msg)
-
- def _assert_cli_operate_out(self, func, msg, cmd, cliout):
- """Assertion for CLI out string: command operates successfully."""
- condition = re.search('command operates successfully', cliout)
- self._assert_cli_out(condition, func, msg, cmd, cliout)
-
- def map_volume(self, host_id, volume_id):
- """Map a volume to a host."""
- # Map a LUN to a host if not mapped.
- if not self._check_volume_created(volume_id):
- LOG.error(_LE('map_volume: Volume %s was not found.') % volume_id)
- raise exception.VolumeNotFound(volume_id=volume_id)
-
- hostlun_id = None
- map_info = self._get_host_map_info(host_id)
- # Make sure the host LUN ID starts from 1.
- new_hostlun_id = 1
- new_hostlunid_found = False
- if map_info:
- for maping in map_info:
- if maping[2] == volume_id:
- hostlun_id = maping[4]
- break
- elif not new_hostlunid_found:
- if new_hostlun_id < int(maping[4]):
- new_hostlunid_found = True
- else:
- new_hostlun_id = int(maping[4]) + 1
-
- if not hostlun_id:
- cli_cmd = ('addhostmap -host %(host_id)s -devlun %(lunid)s '
- '-hostlun %(hostlunid)s'
- % {'host_id': host_id,
- 'lunid': volume_id,
- 'hostlunid': new_hostlun_id})
- out = self._execute_cli(cli_cmd)
- # Check whether the hostlunid has already been assigned.
- condition = re.search(HOST_LUN_ERR_MSG, out)
- while condition:
- new_hostlun_id = new_hostlun_id + 1
- cli_cmd = ('addhostmap -host %(host_id)s -devlun %(lunid)s '
- '-hostlun %(hostlunid)s'
- % {'host_id': host_id,
- 'lunid': volume_id,
- 'hostlunid': new_hostlun_id})
- out = self._execute_cli(cli_cmd)
- condition = re.search(HOST_LUN_ERR_MSG, out)
-
- msg = ('Failed to map LUN %s to host %s. host LUN ID: %s'
- % (volume_id, host_id, new_hostlun_id))
- self._assert_cli_operate_out('map_volume', msg, cli_cmd, out)
-
- hostlun_id = new_hostlun_id
-
- return hostlun_id
-
- def add_host(self, host_name, host_ip, initiator=None):
- """Create a host and add it to hostgroup."""
- # Create an OpenStack hostgroup if not created before.
- hostgroup_name = HOST_GROUP_NAME
- self.hostgroup_id = self._get_hostgroup_id(hostgroup_name)
- if self.hostgroup_id is None:
- self._create_hostgroup(hostgroup_name)
- self.hostgroup_id = self._get_hostgroup_id(hostgroup_name)
-
- # Create a host and add it to the hostgroup.
- # Check the old host name to support the upgrade from grizzly to
- # higher versions.
- if initiator:
- old_host_name = HOST_NAME_PREFIX + str(hash(initiator))
- old_host_id = self._get_host_id(old_host_name, self.hostgroup_id)
- if old_host_id is not None:
- return old_host_id
-
- host_name = HOST_NAME_PREFIX + host_name
- host_id = self._get_host_id(host_name, self.hostgroup_id)
- if host_id is None:
- os_type = huawei_utils.get_conf_host_os_type(host_ip,
- self.xml_conf)
- self._create_host(host_name, self.hostgroup_id, os_type)
- host_id = self._get_host_id(host_name, self.hostgroup_id)
-
- return host_id
-
- def _get_hostgroup_id(self, groupname):
- """Get the given hostgroup ID.
-
- If the hostgroup not found, return None.
-
- """
-
- cli_cmd = 'showhostgroup'
- out = self._execute_cli(cli_cmd)
- if re.search('Host Group Information', out):
- for line in out.split('\r\n')[6:-2]:
- tmp_line = line.split()
- if tmp_line[1] == groupname:
- return tmp_line[0]
- return None
-
- def _create_hostgroup(self, hostgroupname):
- """Run CLI command to create host group."""
- cli_cmd = 'createhostgroup -n %(name)s' % {'name': hostgroupname}
- out = self._execute_cli(cli_cmd)
-
- self._assert_cli_operate_out('_create_hostgroup',
- ('Failed to Create hostgroup %s.'
- % hostgroupname),
- cli_cmd, out)
-
- def _get_host_id(self, hostname, hostgroupid):
- """Get the given host ID."""
- cli_cmd = 'showhost -group %(groupid)s' % {'groupid': hostgroupid}
- out = self._execute_cli(cli_cmd)
- if re.search('Host Information', out):
- for line in out.split('\r\n')[6:-2]:
- tmp_line = line.split()
- if tmp_line[1] == hostname:
- return tmp_line[0]
- return None
-
- def _create_host(self, hostname, hostgroupid, type):
- """Run CLI command to add host."""
- cli_cmd = ('addhost -group %(groupid)s -n %(hostname)s -t %(type)s'
- % {'groupid': hostgroupid,
- 'hostname': hostname,
- 'type': type})
- out = self._execute_cli(cli_cmd)
-
- self._assert_cli_operate_out('_create_host',
- 'Failed to create host %s' % hostname,
- cli_cmd, out)
-
- def _get_host_port_info(self, hostid):
- """Run CLI command to get host port information."""
- cli_cmd = ('showhostport -host %(hostid)s' % {'hostid': hostid})
- out = self._execute_cli(cli_cmd)
- if re.search('Host Port Information', out):
- return [line.split() for line in out.split('\r\n')[6:-2]]
- else:
- return None
-
- def _get_host_map_info(self, hostid):
- """Get map information of the given host."""
-
- cli_cmd = 'showhostmap -host %(hostid)s' % {'hostid': hostid}
- out = self._execute_cli(cli_cmd)
- if re.search('Map Information', out):
- mapinfo = [line.split() for line in out.split('\r\n')[6:-2]]
- # Sorted by host LUN ID.
- return sorted(mapinfo, key=lambda x: int(x[4]))
- else:
- return None
-
- def get_lun_details(self, lun_id):
- cli_cmd = 'showlun -lun %s' % lun_id
- out = self._execute_cli(cli_cmd)
- lun_details = {}
- if re.search('LUN Information', out):
- for line in out.split('\r\n')[4:-2]:
- line = line.split('|')
- key = ''.join(line[0].strip().split())
- val = line[1].strip()
- lun_details[key] = val
- return lun_details
-
- def change_lun_ctr(self, lun_id, ctr):
- LOG.debug('change_lun_ctr: Changing LUN %(lun)s ctr to %(ctr)s.'
- % {'lun': lun_id, 'ctr': ctr})
-
- cli_cmd = 'chglun -lun %s -c %s' % (lun_id, ctr)
- out = self._execute_cli(cli_cmd)
-
- self._assert_cli_operate_out('change_lun_ctr',
- 'Failed to change owning controller for '
- 'LUN %s' % lun_id,
- cli_cmd, out)
-
- def remove_map(self, volume_id, host_name, initiator=None):
- """Remove host map."""
- # Check the old host name to support the upgrade from grizzly to
- # higher versions.
- host_id = None
- if initiator:
- old_host_name = HOST_NAME_PREFIX + str(hash(initiator))
- host_id = self._get_host_id(old_host_name, self.hostgroup_id)
- if host_id is None:
- host_name = HOST_NAME_PREFIX + host_name
- host_id = self._get_host_id(host_name, self.hostgroup_id)
- if host_id is None:
- LOG.error(_LE('remove_map: Host %s does '
- 'not exist.') % host_name)
- raise exception.HostNotFound(host=host_name)
-
- if not self._check_volume_created(volume_id):
- LOG.error(_LE('remove_map: Volume %s does not exist.') % volume_id)
- raise exception.VolumeNotFound(volume_id=volume_id)
-
- map_id = None
- map_info = self._get_host_map_info(host_id)
- if map_info:
- for maping in map_info:
- if maping[2] == volume_id:
- map_id = maping[0]
- break
- if map_id is not None:
- self._delete_map(map_id)
- else:
- LOG.warn(_LW('remove_map: No map between host %(host)s and '
- 'volume %(volume)s.') % {'host': host_name,
- 'volume': volume_id})
- return host_id
-
- def _delete_map(self, mapid, attempts=2):
- """Run CLI command to remove map."""
- cli_cmd = 'delhostmap -force -map %(mapid)s' % {'mapid': mapid}
- while True:
- out = self._execute_cli(cli_cmd)
-
- # We retry to delete host map 10s later if there are
- # IOs accessing the system.
- if re.search('command operates successfully', out):
- break
- else:
- if (re.search('there are IOs accessing the system', out) and
- (attempts > 0)):
-
- LOG.debug('_delete_map: There are IOs accessing '
- 'the system. Retry to delete host map '
- '%(mapid)s 10s later.' % {'mapid': mapid})
-
- time.sleep(10)
- attempts -= 1
- continue
- else:
- err_msg = (_('_delete_map: Failed to delete host map '
- '%(mapid)s.\nCLI out: %(out)s')
- % {'mapid': mapid,
- 'times': attempts,
- 'out': out})
- LOG.error(err_msg)
- raise exception.VolumeBackendAPIException(data=err_msg)
-
- def _delete_hostport(self, portid):
- """Run CLI command to delete host port."""
- cli_cmd = ('delhostport -force -p %(portid)s' % {'portid': portid})
- out = self._execute_cli(cli_cmd)
-
- self._assert_cli_operate_out('_delete_hostport',
- 'Failed to delete host port %s.' % portid,
- cli_cmd, out)
-
- def _delete_host(self, hostid):
- """Run CLI command to delete host."""
- cli_cmd = ('delhost -force -host %(hostid)s' % {'hostid': hostid})
- out = self._execute_cli(cli_cmd)
-
- self._assert_cli_operate_out('_delete_host',
- 'Failed to delete host. %s.' % hostid,
- cli_cmd, out)
-
- def get_volume_stats(self, refresh=False):
- """Get volume stats.
-
- If 'refresh' is True, run update the stats first.
- """
- if refresh:
- self._update_volume_stats()
-
- return self._stats
-
- def _update_volume_stats(self):
- """Retrieve stats info from volume group."""
-
- LOG.debug("_update_volume_stats: Updating volume stats.")
- data = {}
- data['vendor_name'] = 'Huawei'
- data['total_capacity_gb'] = 'infinite'
- data['free_capacity_gb'] = self._get_free_capacity()
- data['reserved_percentage'] = 0
- data['QoS_support'] = False
-
- self._stats = data
-
- def _get_free_capacity(self):
- """Get total free capacity of pools."""
- self._update_login_info()
- params_conf = self._parse_conf_lun_params()
- lun_type = params_conf['LUNType']
- pools_conf = params_conf['StoragePool']
- pools_dev = self._get_dev_pool_info(lun_type)
- total_free_capacity = 0.0
- for pool_dev in pools_dev:
- for pool_conf in pools_conf:
- if ((lun_type == 'Thick') and
- (pool_dev[5] == pool_conf)):
- total_free_capacity += float(pool_dev[3])
- break
- elif pool_dev[1] == pool_conf:
- total_free_capacity += float(pool_dev[4])
- break
-
- return total_free_capacity / 1024
-
- def _get_dev_pool_info(self, pooltype):
- """Get pools information created in storage device.
-
- Return a list whose elements are also list.
-
- """
-
- cli_cmd = ('showpool' if pooltype == 'Thin' else 'showrg')
- out = self._execute_cli(cli_cmd)
-
- test = (re.search('Pool Information', out) or
- re.search('RAID Group Information', out))
- self._assert_cli_out(test, '_get_dev_pool_info',
- 'No pools information found.', cli_cmd, out)
-
- pool = out.split('\r\n')[6:-2]
- return [line.split() for line in pool]
-
-
-class DoradoCommon(TseriesCommon):
- """Common class for Huawei Dorado2100 G2 and Dorado5100 storage arrays.
-
- Dorados share a lot of common codes with T series storage systems,
- so this class inherited from class TseriesCommon and just rewrite some
- methods.
-
- """
-
- def __init__(self, configuration=None):
- TseriesCommon.__init__(self, configuration)
- self.device_type = None
-
- def do_setup(self, context):
- """Check config file."""
- LOG.debug('do_setup')
-
- self._check_conf_file()
- exist_luns = self._get_all_luns_info()
- self.lun_distribution = self._get_lun_distribution_info(exist_luns)
- self.hostgroup_id = self._get_hostgroup_id(HOST_GROUP_NAME)
- self.extended_lun_dict = self._get_extended_lun(exist_luns)
-
- def _check_conf_file(self):
- """Check the config file, make sure the key elements are set."""
- root = huawei_utils.parse_xml_file(self.xml_conf)
- # Check login information
- check_list = ['Storage/ControllerIP0', 'Storage/ControllerIP1',
- 'Storage/UserName', 'Storage/UserPassword']
- for item in check_list:
- if not huawei_utils.is_xml_item_exist(root, item):
- err_msg = (_('_check_conf_file: Config file invalid. '
- '%s must be set.') % item)
- LOG.error(err_msg)
- raise exception.InvalidInput(reason=err_msg)
-
- # Check storage pool
- # No need for Dorado2100 G2
- self.login_info = self._get_login_info()
- self.device_type = self._get_device_type()
- if self.device_type == 'Dorado5100':
- if not huawei_utils.is_xml_item_exist(root, 'LUN/StoragePool',
- 'Name'):
- err_msg = (_('_check_conf_file: Config file invalid. '
- 'StoragePool must be specified.'))
- LOG.error(err_msg)
- raise exception.InvalidInput(reason=err_msg)
-
- # If setting os type, make sure it valid
- if huawei_utils.is_xml_item_exist(root, 'Host', 'OSType'):
- os_list = huawei_utils.os_type.keys()
- if not huawei_utils.is_xml_item_valid(root, 'Host', os_list,
- 'OSType'):
- err_msg = (_('_check_conf_file: Config file invalid. '
- 'Host OSType is invalid.\n'
- 'The valid values are: %(os_list)s')
- % {'os_list': os_list})
- LOG.error(err_msg)
- raise exception.InvalidInput(reason=err_msg)
-
- def _get_device_type(self):
- """Run CLI command to get system type."""
- cli_cmd = 'showsys'
- out = self._execute_cli(cli_cmd)
-
- self._assert_cli_out(re.search('System Information', out),
- '_get_device_type',
- 'Failed to get system information',
- cli_cmd, out)
-
- for line in out.split('\r\n')[4:-2]:
- if re.search('Device Type', line):
- if re.search('Dorado2100 G2$', line):
- return 'Dorado2100 G2'
- elif re.search('Dorado5100$', line):
- return 'Dorado5100'
- else:
- LOG.error(_LE('_get_device_type: The driver only supports '
- 'Dorado5100 and Dorado 2100 G2 now.'))
- raise exception.InvalidResults()
-
- def _get_lun_distribution_info(self, luns):
- ctr_info = [0, 0]
- (c, n) = ((2, 4) if self.device_type == 'Dorado2100 G2' else (3, 5))
- for lun in luns:
- if lun[n].startswith(VOL_AND_SNAP_NAME_PREFIX):
- if lun[c] == 'A':
- ctr_info[0] += 1
- else:
- ctr_info[1] += 1
- return ctr_info
-
- def _get_extended_lun(self, luns):
- extended_dict = {}
- n = 4 if self.device_type == 'Dorado2100 G2' else 5
- for lun in luns:
- if lun[n].startswith('ext'):
- vol_name = lun[n].split('_')[1]
- add_ids = extended_dict.get(vol_name, [])
- add_ids.append(lun[0])
- extended_dict[vol_name] = add_ids
- return extended_dict
-
- def _create_volume(self, name, size, params):
- """Create a new volume with the given name and size."""
- cli_cmd = ('createlun -n %(name)s -lunsize %(size)s '
- '-wrtype %(wrtype)s '
- % {'name': name,
- 'size': size,
- 'wrtype': params['WriteType']})
-
- # If write type is "write through", no need to set mirror switch.
- if params['WriteType'] != '2':
- cli_cmd = cli_cmd + ('-mirrorsw %(mirrorsw)s '
- % {'mirrorsw': params['MirrorSwitch']})
-
- ctr = self._calculate_lun_ctr()
- # Dorado5100 does not support thin LUN.
- if self.device_type == 'Dorado5100':
- cli_cmd = cli_cmd + ('-rg %(raidgroup)s -susize %(susize)s '
- '-c %(ctr)s'
- % {'raidgroup': params['StoragePool'],
- 'susize': params['StripUnitSize'],
- 'ctr': ctr})
- else:
- if params['LUNType'] == 'Thin':
- # Not allowed to specify ctr for thin LUN.
- ctr_str = ''
- luntype_str = '-type 2'
- else:
- ctr_str = ' -c %s' % ctr
- luntype_str = '-type 3'
-
- cli_cmd = cli_cmd + luntype_str + ctr_str
-
- out = self._execute_cli(cli_cmd)
-
- self._assert_cli_operate_out('_create_volume',
- 'Failed to create volume %s' % name,
- cli_cmd, out)
-
- self._update_lun_distribution(ctr)
-
- return self._get_lun_id(name)
-
- def _get_lun_id(self, name):
- luns = self._get_all_luns_info()
- if luns:
- n_index = (4 if 'Dorado2100 G2' == self.device_type else 5)
- for lun in luns:
- if lun[n_index] == name:
- return lun[0]
- return None
-
- def create_volume_from_snapshot(self, volume, snapshot):
- err_msg = (_('create_volume_from_snapshot: %(device)s does '
- 'not support create volume from snapshot.')
- % {'device': self.device_type})
- LOG.error(err_msg)
- raise exception.VolumeBackendAPIException(data=err_msg)
-
- def create_cloned_volume(self, volume, src_vref):
- err_msg = (_('create_cloned_volume: %(device)s does '
- 'not support clone volume.')
- % {'device': self.device_type})
- LOG.error(err_msg)
- raise exception.VolumeBackendAPIException(data=err_msg)
-
- def extend_volume(self, volume, new_size):
- if self.device_type == 'Dorado2100 G2':
- err_msg = (_('extend_volume: %(device)s does not support '
- 'extend volume.') % {'device': self.device_type})
- LOG.error(err_msg)
- raise exception.VolumeBackendAPIException(data=err_msg)
- else:
- return TseriesCommon.extend_volume(self, volume, new_size)
-
- def create_snapshot(self, snapshot):
- if self.device_type == 'Dorado2100 G2':
- err_msg = (_('create_snapshot: %(device)s does not support '
- 'snapshot.') % {'device': self.device_type})
- LOG.error(err_msg)
- raise exception.VolumeBackendAPIException(data=err_msg)
- else:
- return TseriesCommon.create_snapshot(self, snapshot)
-
- def delete_snapshot(self, snapshot):
- if self.device_type == 'Dorado2100 G2':
- return
- else:
- TseriesCommon.delete_snapshot(self, snapshot)
-
- def _get_lun_params(self):
- params_conf = self._parse_conf_lun_params()
- # Select a pool with maximum capacity.
- if self.device_type == 'Dorado5100':
- pools_dev = self._get_dev_pool_info('Thick')
- params_conf['StoragePool'] = \
- self._get_maximum_capacity_pool_id(params_conf['StoragePool'],
- pools_dev, 'Thick')
- return params_conf
-
- def _parse_conf_lun_params(self):
- """Get parameters from config file for creating LUN."""
- # Default LUN parameters.
- conf_params = {'LUNType': 'Thin',
- 'StripUnitSize': '64',
- 'WriteType': '1',
- 'MirrorSwitch': '1'}
-
- root = huawei_utils.parse_xml_file(self.xml_conf)
-
- luntype = root.findtext('LUN/LUNType')
- if luntype:
- if luntype.strip() in ['Thick', 'Thin']:
- conf_params['LUNType'] = luntype.strip()
- else:
- err_msg = (_('LUNType must be "Thin" or "Thick". '
- 'LUNType:%(type)s') % {'type': luntype})
- LOG.error(err_msg)
- raise exception.InvalidInput(reason=err_msg)
-
- # Here we do not judge whether the parameters are set correct.
- # CLI will return error responses if the parameters are invalid.
- stripunitsize = root.findtext('LUN/StripUnitSize')
- if stripunitsize:
- conf_params['StripUnitSize'] = stripunitsize.strip()
- writetype = root.findtext('LUN/WriteType')
- if writetype:
- conf_params['WriteType'] = writetype.strip()
- mirrorswitch = root.findtext('LUN/MirrorSwitch')
- if mirrorswitch:
- conf_params['MirrorSwitch'] = mirrorswitch.strip()
-
- # No need to set StoragePool for Dorado2100 G2.
- if self.device_type == 'Dorado2100 G2':
- return conf_params
-
- pools_conf = root.findall('LUN/StoragePool')
- conf_params['StoragePool'] = []
- for pool in pools_conf:
- conf_params['StoragePool'].append(pool.attrib['Name'].strip())
-
- return conf_params
-
- def _get_free_capacity(self):
- """Get total free capacity of pools."""
- self._update_login_info()
- lun_type = ('Thin' if self.device_type == 'Dorado2100 G2' else 'Thick')
- pools_dev = self._get_dev_pool_info(lun_type)
- total_free_capacity = 0.0
- for pool_dev in pools_dev:
- if self.device_type == 'Dorado2100 G2':
- total_free_capacity += float(pool_dev[2])
- continue
- else:
- params_conf = self._parse_conf_lun_params()
- pools_conf = params_conf['StoragePool']
- for pool_conf in pools_conf:
- if pool_dev[5] == pool_conf:
- total_free_capacity += float(pool_dev[3])
- break
-
- return total_free_capacity / 1024