From 5e26b0c1d1ae1984e51ae47441e8a243f224bf29 Mon Sep 17 00:00:00 2001 From: Mike Perez Date: Thu, 19 Mar 2015 12:38:55 -0700 Subject: [PATCH] Removing Huawei drivers for no reported CI CI deadlines were set and pushed since last year. An email about this requirement and the deadline of March 19th 2015 has been sent to each individual driver maintainer, as well as the mailing list [1]. This driver is being removed because the maintainer does not have a CI reporting to ensure their driver integration is successful. Therfore, we can not validate the driver is working in Cinder today in a continuous way. DocImpact [1]- http://lists.openstack.org/pipermail/openstack-dev/2015-January/054614.html Change-Id: I0599a8721b14ba4f2a255188b71be6d62ae8cc1a --- cinder/tests/test_huawei_18000.py | 1017 ---------- .../test_huawei_drivers_compatibility.py | 64 - cinder/tests/test_huawei_t_dorado.py | 1793 ----------------- cinder/volume/drivers/huawei/__init__.py | 112 - cinder/volume/drivers/huawei/huawei_18000.py | 199 -- cinder/volume/drivers/huawei/huawei_dorado.py | 107 - cinder/volume/drivers/huawei/huawei_t.py | 608 ------ cinder/volume/drivers/huawei/huawei_utils.py | 133 -- cinder/volume/drivers/huawei/rest_common.py | 1758 ---------------- cinder/volume/drivers/huawei/ssh_common.py | 1512 -------------- 10 files changed, 7303 deletions(-) delete mode 100644 cinder/tests/test_huawei_18000.py delete mode 100644 cinder/tests/test_huawei_drivers_compatibility.py delete mode 100644 cinder/tests/test_huawei_t_dorado.py delete mode 100644 cinder/volume/drivers/huawei/__init__.py delete mode 100644 cinder/volume/drivers/huawei/huawei_18000.py delete mode 100644 cinder/volume/drivers/huawei/huawei_dorado.py delete mode 100644 cinder/volume/drivers/huawei/huawei_t.py delete mode 100644 cinder/volume/drivers/huawei/huawei_utils.py delete mode 100644 cinder/volume/drivers/huawei/rest_common.py delete mode 100644 cinder/volume/drivers/huawei/ssh_common.py diff --git a/cinder/tests/test_huawei_18000.py b/cinder/tests/test_huawei_18000.py deleted file mode 100644 index 512afd75e..000000000 --- a/cinder/tests/test_huawei_18000.py +++ /dev/null @@ -1,1017 +0,0 @@ -# Copyright (c) 2013 - 2014 Huawei Technologies Co., Ltd. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -""" Tests for huawei 18000 storage.""" -import json -import os -import shutil -import tempfile -import time -from xml.dom import minidom - -import mock -from 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() diff --git a/cinder/tests/test_huawei_drivers_compatibility.py b/cinder/tests/test_huawei_drivers_compatibility.py deleted file mode 100644 index 98d6b308e..000000000 --- a/cinder/tests/test_huawei_drivers_compatibility.py +++ /dev/null @@ -1,64 +0,0 @@ -# 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) diff --git a/cinder/tests/test_huawei_t_dorado.py b/cinder/tests/test_huawei_t_dorado.py deleted file mode 100644 index b817fdfea..000000000 --- a/cinder/tests/test_huawei_t_dorado.py +++ /dev/null @@ -1,1793 +0,0 @@ - -# 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') diff --git a/cinder/volume/drivers/huawei/__init__.py b/cinder/volume/drivers/huawei/__init__.py deleted file mode 100644 index 92351a805..000000000 --- a/cinder/volume/drivers/huawei/__init__.py +++ /dev/null @@ -1,112 +0,0 @@ -# Copyright (c) 2013 - 2014 Huawei Technologies Co., Ltd. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -""" -Provide a unified driver class for users. - -The product type and the protocol should be specified in config file before. -""" - -from oslo_config import cfg -from oslo_log import log as logging -import six - -from cinder import exception -from cinder.i18n import _, _LI, _LW -from cinder.volume.drivers.huawei import huawei_18000 -from cinder.volume.drivers.huawei import huawei_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) diff --git a/cinder/volume/drivers/huawei/huawei_18000.py b/cinder/volume/drivers/huawei/huawei_18000.py deleted file mode 100644 index c306fecb2..000000000 --- a/cinder/volume/drivers/huawei/huawei_18000.py +++ /dev/null @@ -1,199 +0,0 @@ -# Copyright (c) 2013 - 2014 Huawei Technologies Co., Ltd. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -""" -Volume Drivers for Huawei OceanStor 18000 storage arrays. -""" - -from cinder.volume import driver -from cinder.volume.drivers.huawei import rest_common -from cinder.zonemanager import utils as fczm_utils - - -class Huawei18000ISCSIDriver(driver.ISCSIDriver): - """ISCSI driver for Huawei OceanStor 18000 storage arrays. - - Version history: - 1.0.0 - Initial driver - 1.1.0 - Provide Huawei OceanStor 18000 storage volume driver. - """ - - VERSION = "1.1.0" - - def __init__(self, *args, **kwargs): - super(Huawei18000ISCSIDriver, self).__init__(*args, **kwargs) - - def do_setup(self, context): - """Instantiate common class and log in storage system.""" - self.common = rest_common.RestCommon(configuration=self.configuration) - return self.common.login() - - def check_for_setup_error(self): - """Check configuration file.""" - return self.common._check_conf_file() - - def create_volume(self, volume): - """Create a volume.""" - lun_info = self.common.create_volume(volume) - return {'provider_location': lun_info['ID'], - 'lun_info': lun_info} - - def create_volume_from_snapshot(self, volume, snapshot): - """Create a volume from a snapshot.""" - lun_info = self.common.create_volume_from_snapshot(volume, snapshot) - return {'provider_location': lun_info['ID'], - 'lun_info': lun_info} - - def create_cloned_volume(self, volume, src_vref): - """Create a clone of the specified volume.""" - lun_info = self.common.create_cloned_volume(volume, src_vref) - return {'provider_location': lun_info['ID'], - 'lun_info': lun_info} - - def extend_volume(self, volume, new_size): - """Extend a volume.""" - return self.common.extend_volume(volume, new_size) - - def delete_volume(self, volume): - """Delete a volume.""" - return self.common.delete_volume(volume) - - def create_snapshot(self, snapshot): - """Create a snapshot.""" - lun_info = self.common.create_snapshot(snapshot) - return {'provider_location': lun_info['ID'], - 'lun_info': lun_info} - - def delete_snapshot(self, snapshot): - """Delete a snapshot.""" - return self.common.delete_snapshot(snapshot) - - def get_volume_stats(self, refresh=False): - """Get volume stats.""" - data = self.common.update_volume_stats() - backend_name = self.configuration.safe_get('volume_backend_name') - data['volume_backend_name'] = backend_name or self.__class__.__name__ - data['storage_protocol'] = 'iSCSI' - data['driver_version'] = self.VERSION - return data - - def initialize_connection(self, volume, connector): - """Map a volume to a host.""" - return self.common.initialize_connection_iscsi(volume, connector) - - def terminate_connection(self, volume, connector, **kwargs): - """Terminate the map.""" - self.common.terminate_connection_iscsi(volume, connector) - - def create_export(self, context, volume): - """Export the volume.""" - pass - - def ensure_export(self, context, volume): - """Synchronously recreate an export for a volume.""" - pass - - def remove_export(self, context, volume): - """Remove an export for a volume.""" - pass - - -class Huawei18000FCDriver(driver.FibreChannelDriver): - """FC driver for Huawei OceanStor 18000 storage arrays. - - Version history: - 1.0.0 - Initial driver - 1.1.0 - Provide Huawei OceanStor 18000 storage volume driver. - """ - - VERSION = "1.1.0" - - def __init__(self, *args, **kwargs): - super(Huawei18000FCDriver, self).__init__(*args, **kwargs) - - def do_setup(self, context): - """Instantiate common class and log in storage system.""" - self.common = rest_common.RestCommon(configuration=self.configuration) - return self.common.login() - - def check_for_setup_error(self): - """Check configuration file.""" - return self.common._check_conf_file() - - def create_volume(self, volume): - """Create a volume.""" - lun_info = self.common.create_volume(volume) - return {'provider_location': lun_info['ID'], - 'lun_info': lun_info} - - def create_volume_from_snapshot(self, volume, snapshot): - """Create a volume from a snapshot.""" - lun_info = self.common.create_volume_from_snapshot(volume, snapshot) - return {'provider_location': lun_info['ID'], - 'lun_info': lun_info} - - def create_cloned_volume(self, volume, src_vref): - """Create a clone of the specified volume.""" - lun_info = self.common.create_cloned_volume(volume, src_vref) - return {'provider_location': lun_info['ID'], - 'lun_info': lun_info} - - def extend_volume(self, volume, new_size): - """Extend a volume.""" - return self.common.extend_volume(volume, new_size) - - def delete_volume(self, volume): - """Delete a volume.""" - return self.common.delete_volume(volume) - - def create_snapshot(self, snapshot): - """Create a snapshot.""" - lun_info = self.common.create_snapshot(snapshot) - return {'provider_location': lun_info['ID'], - 'lun_info': lun_info} - - def delete_snapshot(self, snapshot): - """Delete a snapshot.""" - return self.common.delete_snapshot(snapshot) - - def get_volume_stats(self, refresh=False): - """Get volume stats.""" - data = self.common.update_volume_stats() - backend_name = self.configuration.safe_get('volume_backend_name') - data['volume_backend_name'] = backend_name or self.__class__.__name__ - data['storage_protocol'] = 'FC' - data['driver_version'] = self.VERSION - return data - - @fczm_utils.AddFCZone - def initialize_connection(self, volume, connector): - """Map a volume to a host.""" - return self.common.initialize_connection_fc(volume, connector) - - @fczm_utils.RemoveFCZone - def terminate_connection(self, volume, connector, **kwargs): - """Terminate the map.""" - return self.common.terminate_connection_fc(volume, connector) - - def create_export(self, context, volume): - """Export the volume.""" - pass - - def ensure_export(self, context, volume): - """Synchronously recreate an export for a volume.""" - pass - - def remove_export(self, context, volume): - """Remove an export for a volume.""" - pass diff --git a/cinder/volume/drivers/huawei/huawei_dorado.py b/cinder/volume/drivers/huawei/huawei_dorado.py deleted file mode 100644 index 71966fb21..000000000 --- a/cinder/volume/drivers/huawei/huawei_dorado.py +++ /dev/null @@ -1,107 +0,0 @@ -# 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} diff --git a/cinder/volume/drivers/huawei/huawei_t.py b/cinder/volume/drivers/huawei/huawei_t.py deleted file mode 100644 index afb13a97d..000000000 --- a/cinder/volume/drivers/huawei/huawei_t.py +++ /dev/null @@ -1,608 +0,0 @@ -# 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 diff --git a/cinder/volume/drivers/huawei/huawei_utils.py b/cinder/volume/drivers/huawei/huawei_utils.py deleted file mode 100644 index bec102051..000000000 --- a/cinder/volume/drivers/huawei/huawei_utils.py +++ /dev/null @@ -1,133 +0,0 @@ -# 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 diff --git a/cinder/volume/drivers/huawei/rest_common.py b/cinder/volume/drivers/huawei/rest_common.py deleted file mode 100644 index 9653fe1d9..000000000 --- a/cinder/volume/drivers/huawei/rest_common.py +++ /dev/null @@ -1,1758 +0,0 @@ -# Copyright (c) 2013 - 2014 Huawei Technologies Co., Ltd. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -"""Common class for Huawei 18000 storage drivers.""" - -import base64 -import 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 diff --git a/cinder/volume/drivers/huawei/ssh_common.py b/cinder/volume/drivers/huawei/ssh_common.py deleted file mode 100644 index 6688f3734..000000000 --- a/cinder/volume/drivers/huawei/ssh_common.py +++ /dev/null @@ -1,1512 +0,0 @@ -# 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 -- 2.45.2