Merge "Add hypermetro support for Huawei driver"
This commit is contained in:
		@@ -30,11 +30,24 @@ from cinder.volume.drivers.huawei import constants
 | 
				
			|||||||
from cinder.volume.drivers.huawei import fc_zone_helper
 | 
					from cinder.volume.drivers.huawei import fc_zone_helper
 | 
				
			||||||
from cinder.volume.drivers.huawei import huawei_driver
 | 
					from cinder.volume.drivers.huawei import huawei_driver
 | 
				
			||||||
from cinder.volume.drivers.huawei import huawei_utils
 | 
					from cinder.volume.drivers.huawei import huawei_utils
 | 
				
			||||||
 | 
					from cinder.volume.drivers.huawei import hypermetro
 | 
				
			||||||
from cinder.volume.drivers.huawei import rest_client
 | 
					from cinder.volume.drivers.huawei import rest_client
 | 
				
			||||||
from cinder.volume.drivers.huawei import smartx
 | 
					from cinder.volume.drivers.huawei import smartx
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOG = logging.getLogger(__name__)
 | 
					LOG = logging.getLogger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					hypermetro_devices = """{
 | 
				
			||||||
 | 
					    "remote_device": {
 | 
				
			||||||
 | 
					        "RestURL": "http://100.115.10.69:8082/deviceManager/rest",
 | 
				
			||||||
 | 
					        "UserName": "admin",
 | 
				
			||||||
 | 
					        "UserPassword": "Admin@storage1",
 | 
				
			||||||
 | 
					        "StoragePool": "StoragePool001",
 | 
				
			||||||
 | 
					        "domain_name": "hypermetro-domain",
 | 
				
			||||||
 | 
					        "remote_target_ip": "111.111.101.241"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
test_volume = {'name': 'volume-21ec7341-9256-497b-97d9-ef48edcf0635',
 | 
					test_volume = {'name': 'volume-21ec7341-9256-497b-97d9-ef48edcf0635',
 | 
				
			||||||
               'size': 2,
 | 
					               'size': 2,
 | 
				
			||||||
               'volume_name': 'vol1',
 | 
					               'volume_name': 'vol1',
 | 
				
			||||||
@@ -59,6 +72,32 @@ fake_smartx_value = {'smarttier': 'true',
 | 
				
			|||||||
                     'partitionname': 'partition-test',
 | 
					                     'partitionname': 'partition-test',
 | 
				
			||||||
                     }
 | 
					                     }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fake_hypermetro_opts = {'hypermetro': 'true',
 | 
				
			||||||
 | 
					                        'smarttier': False,
 | 
				
			||||||
 | 
					                        'smartcache': False,
 | 
				
			||||||
 | 
					                        'smartpartition': False,
 | 
				
			||||||
 | 
					                        'thin_provisioning_support': False,
 | 
				
			||||||
 | 
					                        'thick_provisioning_support': False,
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					hyper_volume = {'name': 'volume-21ec7341-9256-497b-97d9-ef48edcf0635',
 | 
				
			||||||
 | 
					                'size': 2,
 | 
				
			||||||
 | 
					                'volume_name': 'vol1',
 | 
				
			||||||
 | 
					                'id': '21ec7341-9256-497b-97d9-ef48edcf0635',
 | 
				
			||||||
 | 
					                'volume_id': '21ec7341-9256-497b-97d9-ef48edcf0635',
 | 
				
			||||||
 | 
					                'provider_auth': None,
 | 
				
			||||||
 | 
					                'project_id': 'project',
 | 
				
			||||||
 | 
					                'display_name': 'vol1',
 | 
				
			||||||
 | 
					                'display_description': 'test volume',
 | 
				
			||||||
 | 
					                'volume_type_id': None,
 | 
				
			||||||
 | 
					                'host': 'ubuntu@huawei#OpenStack_Pool',
 | 
				
			||||||
 | 
					                'provider_location': '11',
 | 
				
			||||||
 | 
					                'volume_metadata': [{'key': 'hypermetro_id',
 | 
				
			||||||
 | 
					                                     'value': '1'},
 | 
				
			||||||
 | 
					                                    {'key': 'remote_lun_id',
 | 
				
			||||||
 | 
					                                     'value': '11'}],
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
test_snap = {'name': 'volume-21ec7341-9256-497b-97d9-ef48edcf0635',
 | 
					test_snap = {'name': 'volume-21ec7341-9256-497b-97d9-ef48edcf0635',
 | 
				
			||||||
             'size': 1,
 | 
					             'size': 1,
 | 
				
			||||||
             'volume_name': 'vol1',
 | 
					             'volume_name': 'vol1',
 | 
				
			||||||
@@ -118,6 +157,24 @@ test_new_type = {
 | 
				
			|||||||
    'description': None,
 | 
					    'description': None,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					hypermetro_devices = """
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    "remote_device": {
 | 
				
			||||||
 | 
					        "RestURL": "http://100.115.10.69:8082/deviceManager/rest",
 | 
				
			||||||
 | 
					        "UserName":"admin",
 | 
				
			||||||
 | 
					        "UserPassword":"Admin@storage2",
 | 
				
			||||||
 | 
					        "StoragePool":"StoragePool001",
 | 
				
			||||||
 | 
					        "domain_name":"hypermetro_test"}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FAKE_FIND_POOL_RESPONSE = {'CAPACITY': '985661440',
 | 
				
			||||||
 | 
					                           'ID': '0',
 | 
				
			||||||
 | 
					                           'TOTALCAPACITY': '985661440'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FAKE_CREATE_VOLUME_RESPONSE = {"ID": "1",
 | 
				
			||||||
 | 
					                               "NAME": "5mFHcBv4RkCcD+JyrWc0SA"}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
FakeConnector = {'initiator': 'iqn.1993-08.debian:01:ec2bff7ac3a3',
 | 
					FakeConnector = {'initiator': 'iqn.1993-08.debian:01:ec2bff7ac3a3',
 | 
				
			||||||
                 'wwpns': ['10000090fa0d6754'],
 | 
					                 'wwpns': ['10000090fa0d6754'],
 | 
				
			||||||
                 'wwnns': ['10000090fa0d6755'],
 | 
					                 'wwnns': ['10000090fa0d6755'],
 | 
				
			||||||
@@ -265,7 +322,7 @@ FAKE_QUERY_ALL_LUN_RESPONSE = """
 | 
				
			|||||||
        "code": 0
 | 
					        "code": 0
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "data": [{
 | 
					    "data": [{
 | 
				
			||||||
        "ID": "11",
 | 
					        "ID": "1",
 | 
				
			||||||
        "NAME": "IexzQZJWSXuX2e9I7c8GNQ"
 | 
					        "NAME": "IexzQZJWSXuX2e9I7c8GNQ"
 | 
				
			||||||
    }]
 | 
					    }]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -920,6 +977,20 @@ FAKE_GET_FC_INI_RESPONSE = """
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FAKE_HYPERMETRODOMAIN_RESPONSE = """
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    "error":{
 | 
				
			||||||
 | 
					        "code": 0
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "data":{
 | 
				
			||||||
 | 
					        "PRODUCTVERSION": "V100R001C10",
 | 
				
			||||||
 | 
					        "ID": "11",
 | 
				
			||||||
 | 
					        "NAME": "hypermetro_test",
 | 
				
			||||||
 | 
					        "RUNNINGSTATUS": "42"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
FAKE_QOS_INFO_RESPONSE = """
 | 
					FAKE_QOS_INFO_RESPONSE = """
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    "error":{
 | 
					    "error":{
 | 
				
			||||||
@@ -944,8 +1015,42 @@ FAKE_GET_FC_PORT_RESPONSE = """
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FAKE_SMARTCACHEPARTITION_RESPONSE = """
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    "error":{
 | 
				
			||||||
 | 
					        "code":0
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "data":{
 | 
				
			||||||
 | 
					        "ID":"11",
 | 
				
			||||||
 | 
					        "NAME":"cache-name"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FAKE_CONNECT_FC_RESPONCE = {
 | 
				
			||||||
 | 
					    "driver_volume_type": 'fibre_channel',
 | 
				
			||||||
 | 
					    "data": {
 | 
				
			||||||
 | 
					        "target_wwn": ["10000090fa0d6754"],
 | 
				
			||||||
 | 
					        "target_lun": "1",
 | 
				
			||||||
 | 
					        "volume_id": "21ec7341-9256-497b-97d9-ef48edcf0635"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FAKE_METRO_INFO_RESPONCE = {
 | 
				
			||||||
 | 
					    "error": {
 | 
				
			||||||
 | 
					        "code": 0
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "data": {
 | 
				
			||||||
 | 
					        "PRODUCTVERSION": "V100R001C10",
 | 
				
			||||||
 | 
					        "ID": "11",
 | 
				
			||||||
 | 
					        "NAME": "hypermetro_test",
 | 
				
			||||||
 | 
					        "RUNNINGSTATUS": "42"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# mock login info map
 | 
					# mock login info map
 | 
				
			||||||
MAP_COMMAND_TO_FAKE_RESPONSE = {}
 | 
					MAP_COMMAND_TO_FAKE_RESPONSE = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
MAP_COMMAND_TO_FAKE_RESPONSE['/xx/sessions'] = (
 | 
					MAP_COMMAND_TO_FAKE_RESPONSE['/xx/sessions'] = (
 | 
				
			||||||
    FAKE_GET_LOGIN_STORAGE_RESPONSE)
 | 
					    FAKE_GET_LOGIN_STORAGE_RESPONSE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1243,6 +1348,12 @@ MAP_COMMAND_TO_FAKE_RESPONSE['/fc_port/GET'] = (
 | 
				
			|||||||
MAP_COMMAND_TO_FAKE_RESPONSE['/fc_initiator/GET'] = (
 | 
					MAP_COMMAND_TO_FAKE_RESPONSE['/fc_initiator/GET'] = (
 | 
				
			||||||
    FAKE_GET_FC_PORT_RESPONSE)
 | 
					    FAKE_GET_FC_PORT_RESPONSE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MAP_COMMAND_TO_FAKE_RESPONSE['fc_initiator?range=[0-100]/GET'] = (
 | 
				
			||||||
 | 
					    FAKE_GET_FC_PORT_RESPONSE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MAP_COMMAND_TO_FAKE_RESPONSE['/fc_initiator?PARENTTYPE=21&PARENTID=1/GET'] = (
 | 
				
			||||||
 | 
					    FAKE_GET_FC_PORT_RESPONSE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
MAP_COMMAND_TO_FAKE_RESPONSE['/lun/associate/cachepartition/POST'] = (
 | 
					MAP_COMMAND_TO_FAKE_RESPONSE['/lun/associate/cachepartition/POST'] = (
 | 
				
			||||||
    FAKE_SYSTEM_VERSION_RESPONSE)
 | 
					    FAKE_SYSTEM_VERSION_RESPONSE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1252,6 +1363,33 @@ MAP_COMMAND_TO_FAKE_RESPONSE['/fc_initiator?range=[0-100]&PARENTID=1/GET'] = (
 | 
				
			|||||||
MAP_COMMAND_TO_FAKE_RESPONSE['/fc_initiator?PARENTTYPE=21&PARENTID=1/GET'] = (
 | 
					MAP_COMMAND_TO_FAKE_RESPONSE['/fc_initiator?PARENTTYPE=21&PARENTID=1/GET'] = (
 | 
				
			||||||
    FAKE_GET_FC_PORT_RESPONSE)
 | 
					    FAKE_GET_FC_PORT_RESPONSE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MAP_COMMAND_TO_FAKE_RESPONSE['/system/'] = (
 | 
				
			||||||
 | 
					    FAKE_SYSTEM_VERSION_RESPONSE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MAP_COMMAND_TO_FAKE_RESPONSE['/SMARTCACHEPARTITION/0/GET'] = (
 | 
				
			||||||
 | 
					    FAKE_SMARTCACHEPARTITION_RESPONSE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MAP_COMMAND_TO_FAKE_RESPONSE['/SMARTCACHEPARTITION/REMOVE_ASSOCIATE/PUT'] = (
 | 
				
			||||||
 | 
					    FAKE_COMMON_SUCCESS_RESPONSE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MAP_COMMAND_TO_FAKE_RESPONSE['/cachepartition/0/GET'] = (
 | 
				
			||||||
 | 
					    FAKE_SMARTCACHEPARTITION_RESPONSE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MAP_COMMAND_TO_FAKE_RESPONSE['/HyperMetroDomain?range=[0-100]/GET'] = (
 | 
				
			||||||
 | 
					    FAKE_HYPERMETRODOMAIN_RESPONSE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MAP_COMMAND_TO_FAKE_RESPONSE['/HyperMetroPair/POST'] = (
 | 
				
			||||||
 | 
					    FAKE_HYPERMETRODOMAIN_RESPONSE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MAP_COMMAND_TO_FAKE_RESPONSE['/HyperMetroPair/11/GET'] = (
 | 
				
			||||||
 | 
					    FAKE_HYPERMETRODOMAIN_RESPONSE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MAP_COMMAND_TO_FAKE_RESPONSE['/HyperMetroPair/disable_hcpair/PUT'] = (
 | 
				
			||||||
 | 
					    FAKE_COMMON_SUCCESS_RESPONSE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MAP_COMMAND_TO_FAKE_RESPONSE['/HyperMetroPair/11/DELETE'] = (
 | 
				
			||||||
 | 
					    FAKE_COMMON_SUCCESS_RESPONSE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def Fake_sleep(time):
 | 
					def Fake_sleep(time):
 | 
				
			||||||
    pass
 | 
					    pass
 | 
				
			||||||
@@ -1373,6 +1511,7 @@ class Huawei18000ISCSIDriverTestCase(test.TestCase):
 | 
				
			|||||||
        self.configuration = mock.Mock(spec=conf.Configuration)
 | 
					        self.configuration = mock.Mock(spec=conf.Configuration)
 | 
				
			||||||
        self.configuration.cinder_huawei_conf_file = self.fake_conf_file
 | 
					        self.configuration.cinder_huawei_conf_file = self.fake_conf_file
 | 
				
			||||||
        self.xml_file_path = self.configuration.cinder_huawei_conf_file
 | 
					        self.xml_file_path = self.configuration.cinder_huawei_conf_file
 | 
				
			||||||
 | 
					        self.configuration.hypermetro_devices = hypermetro_devices
 | 
				
			||||||
        self.stubs.Set(time, 'sleep', Fake_sleep)
 | 
					        self.stubs.Set(time, 'sleep', Fake_sleep)
 | 
				
			||||||
        driver = Fake18000ISCSIStorage(configuration=self.configuration)
 | 
					        driver = Fake18000ISCSIStorage(configuration=self.configuration)
 | 
				
			||||||
        self.driver = driver
 | 
					        self.driver = driver
 | 
				
			||||||
@@ -1722,26 +1861,133 @@ class Huawei18000ISCSIDriverTestCase(test.TestCase):
 | 
				
			|||||||
            (qos_id, lun_list) = self.driver.restclient.find_available_qos(qos)
 | 
					            (qos_id, lun_list) = self.driver.restclient.find_available_qos(qos)
 | 
				
			||||||
            self.assertEqual(("11", u'["0", "1", "2"]'), (qos_id, lun_list))
 | 
					            self.assertEqual(("11", u'["0", "1", "2"]'), (qos_id, lun_list))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @mock.patch.object(rest_client.RestClient, 'get_volume_by_name',
 | 
					    @mock.patch.object(huawei_utils, 'get_volume_params',
 | 
				
			||||||
 | 
					                       return_value=fake_hypermetro_opts)
 | 
				
			||||||
 | 
					    @mock.patch.object(rest_client.RestClient, 'login_with_ip',
 | 
				
			||||||
 | 
					                       return_value='123456789')
 | 
				
			||||||
 | 
					    @mock.patch.object(rest_client.RestClient, 'find_all_pools',
 | 
				
			||||||
 | 
					                       return_value=FAKE_STORAGE_POOL_RESPONSE)
 | 
				
			||||||
 | 
					    @mock.patch.object(rest_client.RestClient, 'find_pool_info',
 | 
				
			||||||
 | 
					                       return_value=FAKE_FIND_POOL_RESPONSE)
 | 
				
			||||||
 | 
					    @mock.patch.object(rest_client.RestClient, 'create_volume',
 | 
				
			||||||
 | 
					                       return_value=FAKE_CREATE_VOLUME_RESPONSE)
 | 
				
			||||||
 | 
					    @mock.patch.object(rest_client.RestClient, 'get_hyper_domain_id',
 | 
				
			||||||
                       return_value='11')
 | 
					                       return_value='11')
 | 
				
			||||||
    @mock.patch.object(rest_client.RestClient, 'get_lun_info',
 | 
					    @mock.patch.object(hypermetro.HuaweiHyperMetro, '_wait_volume_ready',
 | 
				
			||||||
                       return_value={'ID': '11'})
 | 
					                       return_value=True)
 | 
				
			||||||
    def test_create_volume_exist(self, mock_lun_info, mock_volume_info):
 | 
					    @mock.patch.object(hypermetro.HuaweiHyperMetro,
 | 
				
			||||||
 | 
					                       '_create_hypermetro_pair',
 | 
				
			||||||
 | 
					                       return_value={"ID": '11',
 | 
				
			||||||
 | 
					                                     "NAME": 'hypermetro-pair'})
 | 
				
			||||||
 | 
					    @mock.patch.object(rest_client.RestClient, 'logout',
 | 
				
			||||||
 | 
					                       return_value=None)
 | 
				
			||||||
 | 
					    def test_create_hypermetro_success(self, mock_logout,
 | 
				
			||||||
 | 
					                                       mock_hyper_pair_info,
 | 
				
			||||||
 | 
					                                       mock_volume_ready,
 | 
				
			||||||
 | 
					                                       mock_hyper_domain,
 | 
				
			||||||
 | 
					                                       mock_create_volume,
 | 
				
			||||||
 | 
					                                       mock_pool_info,
 | 
				
			||||||
 | 
					                                       mock_all_pool_info,
 | 
				
			||||||
 | 
					                                       mock_login_return,
 | 
				
			||||||
 | 
					                                       mock_hypermetro_opts):
 | 
				
			||||||
        self.driver.restclient.login()
 | 
					        self.driver.restclient.login()
 | 
				
			||||||
        lun_param = {'NAME': 'IexzQZJWSXuX2e9I7c8GNQ'}
 | 
					        metadata = {"hypermetro_id": '11',
 | 
				
			||||||
 | 
					                    "remote_lun_id": '1'}
 | 
				
			||||||
 | 
					        lun_info = self.driver.create_volume(hyper_volume)
 | 
				
			||||||
 | 
					        mock_logout.assert_called_with()
 | 
				
			||||||
 | 
					        self.assertEqual(metadata, lun_info['metadata'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        fack_error_volume_exist = {"error": {"code": 1077948993}}
 | 
					    @mock.patch.object(huawei_utils, 'get_volume_params',
 | 
				
			||||||
        with mock.patch.object(rest_client.RestClient, 'call',
 | 
					                       return_value=fake_hypermetro_opts)
 | 
				
			||||||
                               return_value=fack_error_volume_exist):
 | 
					    @mock.patch.object(rest_client.RestClient, 'login_with_ip',
 | 
				
			||||||
            lun_info = self.driver.restclient.create_volume(lun_param)
 | 
					                       return_value='123456789')
 | 
				
			||||||
            self.assertEqual('11', lun_info['ID'])
 | 
					    @mock.patch.object(rest_client.RestClient, 'find_all_pools',
 | 
				
			||||||
 | 
					                       return_value=FAKE_STORAGE_POOL_RESPONSE)
 | 
				
			||||||
 | 
					    @mock.patch.object(rest_client.RestClient, 'find_pool_info',
 | 
				
			||||||
 | 
					                       return_value=FAKE_FIND_POOL_RESPONSE)
 | 
				
			||||||
 | 
					    @mock.patch.object(rest_client.RestClient, 'create_volume',
 | 
				
			||||||
 | 
					                       return_value=FAKE_CREATE_VOLUME_RESPONSE)
 | 
				
			||||||
 | 
					    @mock.patch.object(rest_client.RestClient, 'get_hyper_domain_id',
 | 
				
			||||||
 | 
					                       return_value='11')
 | 
				
			||||||
 | 
					    @mock.patch.object(hypermetro.HuaweiHyperMetro, '_wait_volume_ready',
 | 
				
			||||||
 | 
					                       return_value=True)
 | 
				
			||||||
 | 
					    @mock.patch.object(hypermetro.HuaweiHyperMetro,
 | 
				
			||||||
 | 
					                       '_create_hypermetro_pair')
 | 
				
			||||||
 | 
					    @mock.patch.object(rest_client.RestClient, 'delete_lun',
 | 
				
			||||||
 | 
					                       return_value=None)
 | 
				
			||||||
 | 
					    @mock.patch.object(rest_client.RestClient, 'logout',
 | 
				
			||||||
 | 
					                       return_value=None)
 | 
				
			||||||
 | 
					    def test_create_hypermetro_fail(self, mock_logout,
 | 
				
			||||||
 | 
					                                    mock_delete_lun,
 | 
				
			||||||
 | 
					                                    mock_hyper_pair_info,
 | 
				
			||||||
 | 
					                                    mock_volume_ready,
 | 
				
			||||||
 | 
					                                    mock_hyper_domain,
 | 
				
			||||||
 | 
					                                    mock_create_volume,
 | 
				
			||||||
 | 
					                                    mock_pool_info,
 | 
				
			||||||
 | 
					                                    mock_all_pool_info,
 | 
				
			||||||
 | 
					                                    mock_login_return,
 | 
				
			||||||
 | 
					                                    mock_hypermetro_opts):
 | 
				
			||||||
 | 
					        self.driver.restclient.login()
 | 
				
			||||||
 | 
					        mock_hyper_pair_info.side_effect = exception.VolumeBackendAPIException(
 | 
				
			||||||
 | 
					            data='Create hypermetro error.')
 | 
				
			||||||
 | 
					        self.assertRaises(exception.VolumeBackendAPIException,
 | 
				
			||||||
 | 
					                          self.driver.create_volume, hyper_volume)
 | 
				
			||||||
 | 
					        mock_delete_lun.assert_called_with('1')
 | 
				
			||||||
 | 
					        mock_logout.assert_called_with()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        fack_error_volume_exist = {"error": {"code": 123456789}}
 | 
					    @mock.patch.object(rest_client.RestClient, 'login_with_ip',
 | 
				
			||||||
        with mock.patch.object(rest_client.RestClient, 'call',
 | 
					                       return_value='123456789')
 | 
				
			||||||
                               return_value=fack_error_volume_exist):
 | 
					    @mock.patch.object(rest_client.RestClient, 'check_lun_exist',
 | 
				
			||||||
            self.assertRaises(exception.VolumeBackendAPIException,
 | 
					                       return_value=True)
 | 
				
			||||||
                              self.driver.restclient.create_volume,
 | 
					    @mock.patch.object(rest_client.RestClient, 'check_hypermetro_exist',
 | 
				
			||||||
                              lun_param)
 | 
					                       return_value=True)
 | 
				
			||||||
 | 
					    @mock.patch.object(rest_client.RestClient, 'get_hypermetro_by_id',
 | 
				
			||||||
 | 
					                       return_value=FAKE_METRO_INFO_RESPONCE)
 | 
				
			||||||
 | 
					    @mock.patch.object(rest_client.RestClient, 'delete_hypermetro',
 | 
				
			||||||
 | 
					                       return_value=FAKE_COMMON_SUCCESS_RESPONSE)
 | 
				
			||||||
 | 
					    @mock.patch.object(rest_client.RestClient, 'delete_lun',
 | 
				
			||||||
 | 
					                       return_value=None)
 | 
				
			||||||
 | 
					    @mock.patch.object(rest_client.RestClient, 'logout',
 | 
				
			||||||
 | 
					                       return_value=None)
 | 
				
			||||||
 | 
					    def test_delete_hypermetro_success(self, mock_logout,
 | 
				
			||||||
 | 
					                                       mock_delete_lun,
 | 
				
			||||||
 | 
					                                       mock_delete_hypermetro,
 | 
				
			||||||
 | 
					                                       mock_metro_info,
 | 
				
			||||||
 | 
					                                       mock_check_hyermetro,
 | 
				
			||||||
 | 
					                                       mock_lun_exit,
 | 
				
			||||||
 | 
					                                       mock_login_info):
 | 
				
			||||||
 | 
					        self.driver.restclient.login()
 | 
				
			||||||
 | 
					        result = self.driver.delete_volume(hyper_volume)
 | 
				
			||||||
 | 
					        mock_logout.assert_called_with()
 | 
				
			||||||
 | 
					        self.assertTrue(result)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @mock.patch.object(rest_client.RestClient, 'login_with_ip',
 | 
				
			||||||
 | 
					                       return_value='123456789')
 | 
				
			||||||
 | 
					    @mock.patch.object(rest_client.RestClient, 'check_lun_exist',
 | 
				
			||||||
 | 
					                       return_value=True)
 | 
				
			||||||
 | 
					    @mock.patch.object(rest_client.RestClient, 'check_hypermetro_exist',
 | 
				
			||||||
 | 
					                       return_value=True)
 | 
				
			||||||
 | 
					    @mock.patch.object(rest_client.RestClient, 'get_hypermetro_by_id',
 | 
				
			||||||
 | 
					                       return_value=FAKE_METRO_INFO_RESPONCE)
 | 
				
			||||||
 | 
					    @mock.patch.object(rest_client.RestClient, 'delete_hypermetro')
 | 
				
			||||||
 | 
					    @mock.patch.object(rest_client.RestClient, 'delete_lun',
 | 
				
			||||||
 | 
					                       return_value=None)
 | 
				
			||||||
 | 
					    @mock.patch.object(rest_client.RestClient, 'logout',
 | 
				
			||||||
 | 
					                       return_value=None)
 | 
				
			||||||
 | 
					    def test_delete_hypermetro_fail(self, mock_logout,
 | 
				
			||||||
 | 
					                                    mock_delete_lun,
 | 
				
			||||||
 | 
					                                    mock_delete_hypermetro,
 | 
				
			||||||
 | 
					                                    mock_metro_info,
 | 
				
			||||||
 | 
					                                    mock_check_hyermetro,
 | 
				
			||||||
 | 
					                                    mock_lun_exit,
 | 
				
			||||||
 | 
					                                    mock_login_info):
 | 
				
			||||||
 | 
					        self.driver.restclient.login()
 | 
				
			||||||
 | 
					        mock_delete_hypermetro.side_effect = (
 | 
				
			||||||
 | 
					            exception.VolumeBackendAPIException(data='Delete hypermetro '
 | 
				
			||||||
 | 
					                                                'error.'))
 | 
				
			||||||
 | 
					        self.assertRaises(exception.VolumeBackendAPIException,
 | 
				
			||||||
 | 
					                          self.driver.delete_volume, hyper_volume)
 | 
				
			||||||
 | 
					        mock_delete_lun.assert_called_with('11')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def create_fake_conf_file(self):
 | 
					    def create_fake_conf_file(self):
 | 
				
			||||||
        """Create a fake Config file.
 | 
					        """Create a fake Config file.
 | 
				
			||||||
@@ -1848,6 +2094,7 @@ class Huawei18000FCDriverTestCase(test.TestCase):
 | 
				
			|||||||
        self.configuration = mock.Mock(spec=conf.Configuration)
 | 
					        self.configuration = mock.Mock(spec=conf.Configuration)
 | 
				
			||||||
        self.configuration.cinder_huawei_conf_file = self.fake_conf_file
 | 
					        self.configuration.cinder_huawei_conf_file = self.fake_conf_file
 | 
				
			||||||
        self.xml_file_path = self.configuration.cinder_huawei_conf_file
 | 
					        self.xml_file_path = self.configuration.cinder_huawei_conf_file
 | 
				
			||||||
 | 
					        self.configuration.hypermetro_devices = hypermetro_devices
 | 
				
			||||||
        self.stubs.Set(time, 'sleep', Fake_sleep)
 | 
					        self.stubs.Set(time, 'sleep', Fake_sleep)
 | 
				
			||||||
        driver = Fake18000FCStorage(configuration=self.configuration)
 | 
					        driver = Fake18000FCStorage(configuration=self.configuration)
 | 
				
			||||||
        self.driver = driver
 | 
					        self.driver = driver
 | 
				
			||||||
@@ -2243,6 +2490,41 @@ class Huawei18000FCDriverTestCase(test.TestCase):
 | 
				
			|||||||
                                                             None)
 | 
					                                                             None)
 | 
				
			||||||
        self.assertEqual(expected_pool_capacity, pool_capacity)
 | 
					        self.assertEqual(expected_pool_capacity, pool_capacity)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @mock.patch.object(huawei_utils, 'get_volume_params',
 | 
				
			||||||
 | 
					                       return_value=fake_hypermetro_opts)
 | 
				
			||||||
 | 
					    @mock.patch.object(rest_client.RestClient, 'login_with_ip',
 | 
				
			||||||
 | 
					                       return_value='123456789')
 | 
				
			||||||
 | 
					    @mock.patch.object(rest_client.RestClient, 'find_all_pools',
 | 
				
			||||||
 | 
					                       return_value=FAKE_STORAGE_POOL_RESPONSE)
 | 
				
			||||||
 | 
					    @mock.patch.object(rest_client.RestClient, 'find_pool_info',
 | 
				
			||||||
 | 
					                       return_value=FAKE_FIND_POOL_RESPONSE)
 | 
				
			||||||
 | 
					    @mock.patch.object(rest_client.RestClient, 'create_volume',
 | 
				
			||||||
 | 
					                       return_value=FAKE_CREATE_VOLUME_RESPONSE)
 | 
				
			||||||
 | 
					    @mock.patch.object(rest_client.RestClient, 'get_hyper_domain_id',
 | 
				
			||||||
 | 
					                       return_value='11')
 | 
				
			||||||
 | 
					    @mock.patch.object(hypermetro.HuaweiHyperMetro, '_wait_volume_ready',
 | 
				
			||||||
 | 
					                       return_value=True)
 | 
				
			||||||
 | 
					    @mock.patch.object(hypermetro.HuaweiHyperMetro,
 | 
				
			||||||
 | 
					                       '_create_hypermetro_pair',
 | 
				
			||||||
 | 
					                       return_value={"ID": '11',
 | 
				
			||||||
 | 
					                                     "NAME": 'hypermetro-pair'})
 | 
				
			||||||
 | 
					    @mock.patch.object(rest_client.RestClient, 'logout',
 | 
				
			||||||
 | 
					                       return_value=None)
 | 
				
			||||||
 | 
					    def test_create_hypermetro_success(self, mock_hypermetro_opts,
 | 
				
			||||||
 | 
					                                       mock_login_return,
 | 
				
			||||||
 | 
					                                       mock_all_pool_info,
 | 
				
			||||||
 | 
					                                       mock_pool_info,
 | 
				
			||||||
 | 
					                                       mock_create_volume,
 | 
				
			||||||
 | 
					                                       mock_hyper_domain,
 | 
				
			||||||
 | 
					                                       mock_volume_ready,
 | 
				
			||||||
 | 
					                                       mock_pair_info,
 | 
				
			||||||
 | 
					                                       mock_logout):
 | 
				
			||||||
 | 
					        self.driver.restclient.login()
 | 
				
			||||||
 | 
					        metadata = {"hypermetro_id": '11',
 | 
				
			||||||
 | 
					                    "remote_lun_id": '1'}
 | 
				
			||||||
 | 
					        lun_info = self.driver.create_volume(hyper_volume)
 | 
				
			||||||
 | 
					        self.assertEqual(metadata, lun_info['metadata'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def create_fake_conf_file(self):
 | 
					    def create_fake_conf_file(self):
 | 
				
			||||||
        """Create a fake Config file
 | 
					        """Create a fake Config file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -42,6 +42,10 @@ ERROR_UNAUTHORIZED_TO_SERVER = -401
 | 
				
			|||||||
SOCKET_TIMEOUT = 52
 | 
					SOCKET_TIMEOUT = 52
 | 
				
			||||||
ERROR_VOLUME_ALREADY_EXIST = 1077948993
 | 
					ERROR_VOLUME_ALREADY_EXIST = 1077948993
 | 
				
			||||||
LOGIN_SOCKET_TIMEOUT = 4
 | 
					LOGIN_SOCKET_TIMEOUT = 4
 | 
				
			||||||
 | 
					ERROR_VOLUME_NOT_EXIST = 1077939726
 | 
				
			||||||
 | 
					RELOGIN_ERROR_PASS = [ERROR_VOLUME_NOT_EXIST]
 | 
				
			||||||
 | 
					HYPERMETRO_RUNNSTATUS_STOP = 41
 | 
				
			||||||
 | 
					HYPERMETRO_RUNNSTATUS_NORMAL = 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
THICK_LUNTYPE = 0
 | 
					THICK_LUNTYPE = 0
 | 
				
			||||||
THIN_LUNTYPE = 1
 | 
					THIN_LUNTYPE = 1
 | 
				
			||||||
@@ -62,3 +66,4 @@ HUAWEI_VALID_KEYS = ['maxIOPS', 'minIOPS', 'minBandWidth',
 | 
				
			|||||||
QOS_KEYS = ['MAXIOPS', 'MINIOPS', 'MINBANDWidth',
 | 
					QOS_KEYS = ['MAXIOPS', 'MINIOPS', 'MINBANDWidth',
 | 
				
			||||||
            'MAXBANDWidth', 'LATENCY', 'IOTYPE']
 | 
					            'MAXBANDWidth', 'LATENCY', 'IOTYPE']
 | 
				
			||||||
MAX_LUN_NUM_IN_QOS = 64
 | 
					MAX_LUN_NUM_IN_QOS = 64
 | 
				
			||||||
 | 
					HYPERMETRO_CLASS = "cinder.volume.drivers.huawei.hypermetro.HuaweiHyperMetro"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,6 +13,7 @@
 | 
				
			|||||||
#    License for the specific language governing permissions and limitations
 | 
					#    License for the specific language governing permissions and limitations
 | 
				
			||||||
#    under the License.
 | 
					#    under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import json
 | 
				
			||||||
import six
 | 
					import six
 | 
				
			||||||
import uuid
 | 
					import uuid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -28,6 +29,7 @@ from cinder.volume import driver
 | 
				
			|||||||
from cinder.volume.drivers.huawei import constants
 | 
					from cinder.volume.drivers.huawei import constants
 | 
				
			||||||
from cinder.volume.drivers.huawei import fc_zone_helper
 | 
					from cinder.volume.drivers.huawei import fc_zone_helper
 | 
				
			||||||
from cinder.volume.drivers.huawei import huawei_utils
 | 
					from cinder.volume.drivers.huawei import huawei_utils
 | 
				
			||||||
 | 
					from cinder.volume.drivers.huawei import hypermetro
 | 
				
			||||||
from cinder.volume.drivers.huawei import rest_client
 | 
					from cinder.volume.drivers.huawei import rest_client
 | 
				
			||||||
from cinder.volume.drivers.huawei import smartx
 | 
					from cinder.volume.drivers.huawei import smartx
 | 
				
			||||||
from cinder.volume import utils as volume_utils
 | 
					from cinder.volume import utils as volume_utils
 | 
				
			||||||
@@ -39,8 +41,11 @@ LOG = logging.getLogger(__name__)
 | 
				
			|||||||
huawei_opts = [
 | 
					huawei_opts = [
 | 
				
			||||||
    cfg.StrOpt('cinder_huawei_conf_file',
 | 
					    cfg.StrOpt('cinder_huawei_conf_file',
 | 
				
			||||||
               default='/etc/cinder/cinder_huawei_conf.xml',
 | 
					               default='/etc/cinder/cinder_huawei_conf.xml',
 | 
				
			||||||
               help='The configuration file for the Cinder Huawei '
 | 
					               help='The configuration file for the Cinder Huawei driver.'),
 | 
				
			||||||
                    'driver.')]
 | 
					    cfg.StrOpt('hypermetro_devices',
 | 
				
			||||||
 | 
					               default=None,
 | 
				
			||||||
 | 
					               help='The remote device hypermetro will use.'),
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CONF = cfg.CONF
 | 
					CONF = cfg.CONF
 | 
				
			||||||
CONF.register_opts(huawei_opts)
 | 
					CONF.register_opts(huawei_opts)
 | 
				
			||||||
@@ -57,6 +62,7 @@ class HuaweiBaseDriver(driver.VolumeDriver):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        self.configuration.append_config_values(huawei_opts)
 | 
					        self.configuration.append_config_values(huawei_opts)
 | 
				
			||||||
        self.xml_file_path = self.configuration.cinder_huawei_conf_file
 | 
					        self.xml_file_path = self.configuration.cinder_huawei_conf_file
 | 
				
			||||||
 | 
					        self.hypermetro_devices = self.configuration.hypermetro_devices
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def do_setup(self, context):
 | 
					    def do_setup(self, context):
 | 
				
			||||||
        """Instantiate common class and login storage system."""
 | 
					        """Instantiate common class and login storage system."""
 | 
				
			||||||
@@ -127,9 +133,31 @@ class HuaweiBaseDriver(driver.VolumeDriver):
 | 
				
			|||||||
            raise exception.InvalidInput(
 | 
					            raise exception.InvalidInput(
 | 
				
			||||||
                reason=_('Create volume error. Because %s.') % err)
 | 
					                reason=_('Create volume error. Because %s.') % err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return {'provider_location': lun_info['ID'],
 | 
					        # Update the metadata.
 | 
				
			||||||
 | 
					        LOG.info(_LI('Create volume option: %s.'), opts)
 | 
				
			||||||
 | 
					        metadata = huawei_utils.get_volume_metadata(volume)
 | 
				
			||||||
 | 
					        if opts.get('hypermetro'):
 | 
				
			||||||
 | 
					            hyperm = hypermetro.HuaweiHyperMetro(self.restclient, None,
 | 
				
			||||||
 | 
					                                                 self.configuration)
 | 
				
			||||||
 | 
					            try:
 | 
				
			||||||
 | 
					                metro_id, remote_lun_id = hyperm.create_hypermetro(lun_id,
 | 
				
			||||||
 | 
					                                                                   lun_param)
 | 
				
			||||||
 | 
					            except exception.VolumeBackendAPIException as err:
 | 
				
			||||||
 | 
					                LOG.exception(_LE('Create hypermetro error: %s.'), err)
 | 
				
			||||||
 | 
					                self._delete_lun_with_check(lun_id)
 | 
				
			||||||
 | 
					                raise
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            LOG.info(_LI("Hypermetro id: %(metro_id)s. "
 | 
				
			||||||
 | 
					                         "Remote lun id: %(remote_lun_id)s."),
 | 
				
			||||||
 | 
					                     {'metro_id': metro_id,
 | 
				
			||||||
 | 
					                      'remote_lun_id': remote_lun_id})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            metadata.update({'hypermetro_id': metro_id,
 | 
				
			||||||
 | 
					                             'remote_lun_id': remote_lun_id})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return {'provider_location': lun_id,
 | 
				
			||||||
                'ID': lun_id,
 | 
					                'ID': lun_id,
 | 
				
			||||||
                'lun_info': lun_info}
 | 
					                'metadata': metadata}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @utils.synchronized('huawei', external=True)
 | 
					    @utils.synchronized('huawei', external=True)
 | 
				
			||||||
    def delete_volume(self, volume):
 | 
					    def delete_volume(self, volume):
 | 
				
			||||||
@@ -150,6 +178,17 @@ class HuaweiBaseDriver(driver.VolumeDriver):
 | 
				
			|||||||
                if qos_id:
 | 
					                if qos_id:
 | 
				
			||||||
                    self.remove_qos_lun(lun_id, qos_id)
 | 
					                    self.remove_qos_lun(lun_id, qos_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                metadata = huawei_utils.get_volume_metadata(volume)
 | 
				
			||||||
 | 
					                if 'hypermetro_id' in metadata:
 | 
				
			||||||
 | 
					                    hyperm = hypermetro.HuaweiHyperMetro(self.restclient, None,
 | 
				
			||||||
 | 
					                                                         self.configuration)
 | 
				
			||||||
 | 
					                    try:
 | 
				
			||||||
 | 
					                        hyperm.delete_hypermetro(volume)
 | 
				
			||||||
 | 
					                    except exception.VolumeBackendAPIException as err:
 | 
				
			||||||
 | 
					                        LOG.exception(_LE('Delete hypermetro error: %s.'), err)
 | 
				
			||||||
 | 
					                        self.restclient.delete_lun(lun_id)
 | 
				
			||||||
 | 
					                        raise
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                self.restclient.delete_lun(lun_id)
 | 
					                self.restclient.delete_lun(lun_id)
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            LOG.warning(_LW("Can't find lun %s on the array."), lun_id)
 | 
					            LOG.warning(_LW("Can't find lun %s on the array."), lun_id)
 | 
				
			||||||
@@ -188,11 +227,11 @@ class HuaweiBaseDriver(driver.VolumeDriver):
 | 
				
			|||||||
                    if constants.MIGRATION_COMPLETE == item['RUNNINGSTATUS']:
 | 
					                    if constants.MIGRATION_COMPLETE == item['RUNNINGSTATUS']:
 | 
				
			||||||
                        return True
 | 
					                        return True
 | 
				
			||||||
                    if constants.MIGRATION_FAULT == item['RUNNINGSTATUS']:
 | 
					                    if constants.MIGRATION_FAULT == item['RUNNINGSTATUS']:
 | 
				
			||||||
                        err_msg = (_('Lun migration error.'))
 | 
					                        err_msg = _('Lun migration error.')
 | 
				
			||||||
                        LOG.error(err_msg)
 | 
					                        LOG.error(err_msg)
 | 
				
			||||||
                        raise exception.VolumeBackendAPIException(data=err_msg)
 | 
					                        raise exception.VolumeBackendAPIException(data=err_msg)
 | 
				
			||||||
        if not found_migration_task:
 | 
					        if not found_migration_task:
 | 
				
			||||||
            err_msg = (_("Cannot find migration task."))
 | 
					            err_msg = _("Cannot find migration task.")
 | 
				
			||||||
            LOG.error(err_msg)
 | 
					            LOG.error(err_msg)
 | 
				
			||||||
            raise exception.VolumeBackendAPIException(data=err_msg)
 | 
					            raise exception.VolumeBackendAPIException(data=err_msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1010,6 +1049,7 @@ class Huawei18000FCDriver(HuaweiBaseDriver, driver.FibreChannelDriver):
 | 
				
			|||||||
                Volume migration support
 | 
					                Volume migration support
 | 
				
			||||||
                Volume retype support
 | 
					                Volume retype support
 | 
				
			||||||
                FC zone enhancement
 | 
					                FC zone enhancement
 | 
				
			||||||
 | 
					                Volume hypermetro support
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    VERSION = "1.1.1"
 | 
					    VERSION = "1.1.1"
 | 
				
			||||||
@@ -1090,23 +1130,82 @@ class Huawei18000FCDriver(HuaweiBaseDriver, driver.FibreChannelDriver):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        # Add host into hostgroup.
 | 
					        # Add host into hostgroup.
 | 
				
			||||||
        hostgroup_id = self.restclient.add_host_into_hostgroup(host_id)
 | 
					        hostgroup_id = self.restclient.add_host_into_hostgroup(host_id)
 | 
				
			||||||
        self.restclient.do_mapping(lun_id, hostgroup_id, host_id)
 | 
					        map_info = self.restclient.do_mapping(lun_id,
 | 
				
			||||||
 | 
					                                              hostgroup_id,
 | 
				
			||||||
 | 
					                                              host_id)
 | 
				
			||||||
        host_lun_id = self.restclient.find_host_lun_id(host_id, lun_id)
 | 
					        host_lun_id = self.restclient.find_host_lun_id(host_id, lun_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Return FC properties.
 | 
					        # Return FC properties.
 | 
				
			||||||
        info = {'driver_volume_type': 'fibre_channel',
 | 
					        fc_info = {'driver_volume_type': 'fibre_channel',
 | 
				
			||||||
                'data': {'target_lun': int(host_lun_id),
 | 
					                   'data': {'target_lun': int(host_lun_id),
 | 
				
			||||||
                         'target_discovered': True,
 | 
					                            'target_discovered': True,
 | 
				
			||||||
                         'target_wwn': tgt_port_wwns,
 | 
					                            'target_wwn': tgt_port_wwns,
 | 
				
			||||||
                         'volume_id': volume['id'],
 | 
					                            'volume_id': volume['id'],
 | 
				
			||||||
                         'initiator_target_map': init_targ_map}, }
 | 
					                            'initiator_target_map': init_targ_map,
 | 
				
			||||||
 | 
					                            'map_info': map_info}, }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        LOG.info(_LI("initialize_connection, return data is: %s."),
 | 
					        loc_tgt_wwn = fc_info['data']['target_wwn']
 | 
				
			||||||
                 info)
 | 
					        local_ini_tgt_map = fc_info['data']['initiator_target_map']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return info
 | 
					        # Deal with hypermetro connection.
 | 
				
			||||||
 | 
					        metadata = huawei_utils.get_volume_metadata(volume)
 | 
				
			||||||
 | 
					        LOG.info(_LI("initialize_connection, metadata is: %s."), metadata)
 | 
				
			||||||
 | 
					        if 'hypermetro_id' in metadata:
 | 
				
			||||||
 | 
					            hyperm = hypermetro.HuaweiHyperMetro(self.restclient, None,
 | 
				
			||||||
 | 
					                                                 self.configuration)
 | 
				
			||||||
 | 
					            rmt_fc_info = hyperm.connect_volume_fc(volume, connector)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            rmt_tgt_wwn = rmt_fc_info['data']['target_wwn']
 | 
				
			||||||
 | 
					            rmt_ini_tgt_map = rmt_fc_info['data']['initiator_target_map']
 | 
				
			||||||
 | 
					            fc_info['data']['target_wwn'] = (loc_tgt_wwn + rmt_tgt_wwn)
 | 
				
			||||||
 | 
					            wwns = connector['wwpns']
 | 
				
			||||||
 | 
					            for wwn in wwns:
 | 
				
			||||||
 | 
					                if (wwn in local_ini_tgt_map
 | 
				
			||||||
 | 
					                        and wwn in rmt_ini_tgt_map):
 | 
				
			||||||
 | 
					                    fc_info['data']['initiator_target_map'][wwn].extend(
 | 
				
			||||||
 | 
					                        rmt_ini_tgt_map[wwn])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                elif (wwn not in local_ini_tgt_map
 | 
				
			||||||
 | 
					                        and wwn in rmt_ini_tgt_map):
 | 
				
			||||||
 | 
					                    fc_info['data']['initiator_target_map'][wwn] = (
 | 
				
			||||||
 | 
					                        rmt_ini_tgt_map[wwn])
 | 
				
			||||||
 | 
					                # else, do nothing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            loc_map_info = fc_info['data']['map_info']
 | 
				
			||||||
 | 
					            rmt_map_info = rmt_fc_info['data']['map_info']
 | 
				
			||||||
 | 
					            same_host_id = self._get_same_hostid(loc_map_info,
 | 
				
			||||||
 | 
					                                                 rmt_map_info)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            self.restclient.change_hostlun_id(loc_map_info, same_host_id)
 | 
				
			||||||
 | 
					            hyperm.rmt_client.change_hostlun_id(rmt_map_info, same_host_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            fc_info['data']['target_lun'] = same_host_id
 | 
				
			||||||
 | 
					            hyperm.rmt_client.logout()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        LOG.info(_LI("Return FC info is: %s."), fc_info)
 | 
				
			||||||
 | 
					        return fc_info
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _get_same_hostid(self, loc_fc_info, rmt_fc_info):
 | 
				
			||||||
 | 
					        loc_aval_luns = loc_fc_info['aval_luns']
 | 
				
			||||||
 | 
					        loc_aval_luns = json.loads(loc_aval_luns)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        rmt_aval_luns = rmt_fc_info['aval_luns']
 | 
				
			||||||
 | 
					        rmt_aval_luns = json.loads(rmt_aval_luns)
 | 
				
			||||||
 | 
					        same_host_id = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for i in range(1, 512):
 | 
				
			||||||
 | 
					            if i in rmt_aval_luns and i in loc_aval_luns:
 | 
				
			||||||
 | 
					                same_host_id = i
 | 
				
			||||||
 | 
					                break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        LOG.info(_LI("The same hostid is: %s."), same_host_id)
 | 
				
			||||||
 | 
					        if not same_host_id:
 | 
				
			||||||
 | 
					            msg = _("Can't find the same host id from arrays.")
 | 
				
			||||||
 | 
					            LOG.error(msg)
 | 
				
			||||||
 | 
					            raise exception.VolumeBackendAPIException(data=msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return same_host_id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @utils.synchronized('huawei', external=True)
 | 
					 | 
				
			||||||
    @fczm_utils.RemoveFCZone
 | 
					    @fczm_utils.RemoveFCZone
 | 
				
			||||||
    def terminate_connection(self, volume, connector, **kwargs):
 | 
					    def terminate_connection(self, volume, connector, **kwargs):
 | 
				
			||||||
        """Delete map between a volume and a host."""
 | 
					        """Delete map between a volume and a host."""
 | 
				
			||||||
@@ -1150,8 +1249,8 @@ class Huawei18000FCDriver(HuaweiBaseDriver, driver.FibreChannelDriver):
 | 
				
			|||||||
        if lungroup_id:
 | 
					        if lungroup_id:
 | 
				
			||||||
            left_lunnum = self.restclient.get_lunnum_from_lungroup(lungroup_id)
 | 
					            left_lunnum = self.restclient.get_lunnum_from_lungroup(lungroup_id)
 | 
				
			||||||
        if int(left_lunnum) > 0:
 | 
					        if int(left_lunnum) > 0:
 | 
				
			||||||
            info = {'driver_volume_type': 'fibre_channel',
 | 
					            fc_info = {'driver_volume_type': 'fibre_channel',
 | 
				
			||||||
                    'data': {}}
 | 
					                       'data': {}}
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            if not self.fcsan_lookup_service:
 | 
					            if not self.fcsan_lookup_service:
 | 
				
			||||||
                self.fcsan_lookup_service = fczm_utils.create_lookup_service()
 | 
					                self.fcsan_lookup_service = fczm_utils.create_lookup_service()
 | 
				
			||||||
@@ -1195,10 +1294,19 @@ class Huawei18000FCDriver(HuaweiBaseDriver, driver.FibreChannelDriver):
 | 
				
			|||||||
            if view_id:
 | 
					            if view_id:
 | 
				
			||||||
                self.restclient.delete_mapping_view(view_id)
 | 
					                self.restclient.delete_mapping_view(view_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            info = {'driver_volume_type': 'fibre_channel',
 | 
					            fc_info = {'driver_volume_type': 'fibre_channel',
 | 
				
			||||||
                    'data': {'target_wwn': tgt_port_wwns,
 | 
					                       'data': {'target_wwn': tgt_port_wwns,
 | 
				
			||||||
                             'initiator_target_map': init_targ_map}}
 | 
					                                'initiator_target_map': init_targ_map}}
 | 
				
			||||||
        LOG.info(_LI("terminate_connection, return data is: %s."),
 | 
					 | 
				
			||||||
                 info)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return info
 | 
					        # Deal with hypermetro connection.
 | 
				
			||||||
 | 
					        metadata = huawei_utils.get_volume_metadata(volume)
 | 
				
			||||||
 | 
					        LOG.info(_LI("Detach Volume, metadata is: %s."), metadata)
 | 
				
			||||||
 | 
					        if 'hypermetro_id' in metadata:
 | 
				
			||||||
 | 
					            hyperm = hypermetro.HuaweiHyperMetro(self.restclient, None,
 | 
				
			||||||
 | 
					                                                 self.configuration)
 | 
				
			||||||
 | 
					            hyperm.disconnect_volume_fc(volume, connector)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        LOG.info(_LI("terminate_connection, return data is: %s."),
 | 
				
			||||||
 | 
					                 fc_info)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return fc_info
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,6 +14,7 @@
 | 
				
			|||||||
#    under the License.
 | 
					#    under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import base64
 | 
					import base64
 | 
				
			||||||
 | 
					import json
 | 
				
			||||||
import six
 | 
					import six
 | 
				
			||||||
import time
 | 
					import time
 | 
				
			||||||
import uuid
 | 
					import uuid
 | 
				
			||||||
@@ -40,6 +41,7 @@ opts_capability = {
 | 
				
			|||||||
    'smartpartition': False,
 | 
					    'smartpartition': False,
 | 
				
			||||||
    'thin_provisioning_support': False,
 | 
					    'thin_provisioning_support': False,
 | 
				
			||||||
    'thick_provisioning_support': False,
 | 
					    'thick_provisioning_support': False,
 | 
				
			||||||
 | 
					    'hypermetro': False,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -539,3 +541,29 @@ def get_pools(xml_file_path):
 | 
				
			|||||||
        LOG.error(msg)
 | 
					        LOG.error(msg)
 | 
				
			||||||
        raise exception.InvalidInput(msg)
 | 
					        raise exception.InvalidInput(msg)
 | 
				
			||||||
    return pool_names
 | 
					    return pool_names
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_remote_device_info(valid_hypermetro_devices):
 | 
				
			||||||
 | 
					    remote_device_info = {}
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        if valid_hypermetro_devices:
 | 
				
			||||||
 | 
					            remote_device_info = json.loads(valid_hypermetro_devices)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    except ValueError as err:
 | 
				
			||||||
 | 
					        msg = _("Get remote device info error. %s.") % err
 | 
				
			||||||
 | 
					        LOG.error(msg)
 | 
				
			||||||
 | 
					        raise exception.VolumeBackendAPIException(data=msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if len(remote_device_info) == 1:
 | 
				
			||||||
 | 
					        for device_key, device_value in remote_device_info.items():
 | 
				
			||||||
 | 
					            return remote_device_info.get(device_key)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_volume_metadata(volume):
 | 
				
			||||||
 | 
					    if 'volume_metadata' in volume:
 | 
				
			||||||
 | 
					        metadata = volume.get('volume_metadata')
 | 
				
			||||||
 | 
					        return {item['key']: item['value'] for item in metadata}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return {}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										321
									
								
								cinder/volume/drivers/huawei/hypermetro.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										321
									
								
								cinder/volume/drivers/huawei/hypermetro.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,321 @@
 | 
				
			|||||||
 | 
					# Copyright (c) 2015 Huawei Technologies Co., Ltd.
 | 
				
			||||||
 | 
					# All Rights Reserved.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
				
			||||||
 | 
					#    not use this file except in compliance with the License. You may obtain
 | 
				
			||||||
 | 
					#    a copy of the License at
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					#         http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					#    Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
				
			||||||
 | 
					#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
				
			||||||
 | 
					#    License for the specific language governing permissions and limitations
 | 
				
			||||||
 | 
					#    under the License.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import six
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from oslo_log import log as logging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from cinder import exception
 | 
				
			||||||
 | 
					from cinder.i18n import _, _LI, _LW
 | 
				
			||||||
 | 
					from cinder.volume.drivers.huawei import constants
 | 
				
			||||||
 | 
					from cinder.volume.drivers.huawei import huawei_utils
 | 
				
			||||||
 | 
					from cinder.volume.drivers.huawei import rest_client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					LOG = logging.getLogger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class HuaweiHyperMetro(object):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, client, rmt_client, configuration):
 | 
				
			||||||
 | 
					        self.client = client
 | 
				
			||||||
 | 
					        self.rmt_client = rmt_client
 | 
				
			||||||
 | 
					        self.configuration = configuration
 | 
				
			||||||
 | 
					        self.xml_file_path = self.configuration.cinder_huawei_conf_file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def create_hypermetro(self, local_lun_id, lun_param):
 | 
				
			||||||
 | 
					        """Create hypermetro."""
 | 
				
			||||||
 | 
					        metro_devices = self.configuration.hypermetro_devices
 | 
				
			||||||
 | 
					        device_info = huawei_utils.get_remote_device_info(metro_devices)
 | 
				
			||||||
 | 
					        self.rmt_client = rest_client.RestClient(self.configuration)
 | 
				
			||||||
 | 
					        self.rmt_client.login_with_ip(device_info)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            # Get the remote pool info.
 | 
				
			||||||
 | 
					            config_pool = device_info['StoragePool']
 | 
				
			||||||
 | 
					            remote_pool = self.rmt_client.find_all_pools()
 | 
				
			||||||
 | 
					            pool = self.rmt_client.find_pool_info(config_pool,
 | 
				
			||||||
 | 
					                                                  remote_pool)
 | 
				
			||||||
 | 
					            # Create remote lun
 | 
				
			||||||
 | 
					            lun_param['PARENTID'] = pool['ID']
 | 
				
			||||||
 | 
					            remotelun_info = self.rmt_client.create_volume(lun_param)
 | 
				
			||||||
 | 
					            remote_lun_id = remotelun_info['ID']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # Get hypermetro domain
 | 
				
			||||||
 | 
					            try:
 | 
				
			||||||
 | 
					                domain_name = device_info['domain_name']
 | 
				
			||||||
 | 
					                domain_id = self.rmt_client.get_hyper_domain_id(domain_name)
 | 
				
			||||||
 | 
					                self._wait_volume_ready(remote_lun_id)
 | 
				
			||||||
 | 
					                hypermetro = self._create_hypermetro_pair(domain_id,
 | 
				
			||||||
 | 
					                                                          local_lun_id,
 | 
				
			||||||
 | 
					                                                          remote_lun_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return hypermetro['ID'], remote_lun_id
 | 
				
			||||||
 | 
					            except Exception as err:
 | 
				
			||||||
 | 
					                self.rmt_client.delete_lun(remote_lun_id)
 | 
				
			||||||
 | 
					                msg = _('Create hypermetro error. %s.') % err
 | 
				
			||||||
 | 
					                raise exception.VolumeBackendAPIException(data=msg)
 | 
				
			||||||
 | 
					        except exception.VolumeBackendAPIException:
 | 
				
			||||||
 | 
					            raise
 | 
				
			||||||
 | 
					        except Exception as err:
 | 
				
			||||||
 | 
					            msg = _("Create remote LUN error. %s.") % err
 | 
				
			||||||
 | 
					            LOG.exception(msg)
 | 
				
			||||||
 | 
					            raise exception.VolumeBackendAPIException(data=msg)
 | 
				
			||||||
 | 
					        finally:
 | 
				
			||||||
 | 
					            self.rmt_client.logout()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def delete_hypermetro(self, volume):
 | 
				
			||||||
 | 
					        """Delete hypermetro."""
 | 
				
			||||||
 | 
					        metadata = huawei_utils.get_volume_metadata(volume)
 | 
				
			||||||
 | 
					        metro_id = metadata['hypermetro_id']
 | 
				
			||||||
 | 
					        remote_lun_id = metadata['remote_lun_id']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if metro_id:
 | 
				
			||||||
 | 
					            exst_flag = self.client.check_hypermetro_exist(metro_id)
 | 
				
			||||||
 | 
					            if exst_flag:
 | 
				
			||||||
 | 
					                metro_info = self.client.get_hypermetro_by_id(metro_id)
 | 
				
			||||||
 | 
					                metro_status = int(metro_info['data']['RUNNINGSTATUS'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                LOG.debug("Hypermetro status is: %s.", metro_status)
 | 
				
			||||||
 | 
					                if constants.HYPERMETRO_RUNNSTATUS_STOP != metro_status:
 | 
				
			||||||
 | 
					                    self.client.stop_hypermetro(metro_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                # Delete hypermetro
 | 
				
			||||||
 | 
					                self.client.delete_hypermetro(metro_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Delete remote lun.
 | 
				
			||||||
 | 
					        if remote_lun_id:
 | 
				
			||||||
 | 
					            metro_devices = self.configuration.hypermetro_devices
 | 
				
			||||||
 | 
					            device_info = huawei_utils.get_remote_device_info(metro_devices)
 | 
				
			||||||
 | 
					            self.rmt_client = rest_client.RestClient(self.configuration)
 | 
				
			||||||
 | 
					            self.rmt_client.login_with_ip(device_info)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            try:
 | 
				
			||||||
 | 
					                if self.rmt_client.check_lun_exist(remote_lun_id):
 | 
				
			||||||
 | 
					                    self.rmt_client.delete_lun(remote_lun_id)
 | 
				
			||||||
 | 
					            except Exception as err:
 | 
				
			||||||
 | 
					                msg = _("Delete remote lun err. %s.") % err
 | 
				
			||||||
 | 
					                LOG.exception(msg)
 | 
				
			||||||
 | 
					                raise exception.VolumeBackendAPIException(data=msg)
 | 
				
			||||||
 | 
					            finally:
 | 
				
			||||||
 | 
					                self.rmt_client.logout()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _create_hypermetro_pair(self, domain_id, lun_id, remote_lun_id):
 | 
				
			||||||
 | 
					        """Create a HyperMetroPair."""
 | 
				
			||||||
 | 
					        hcp_param = {"DOMAINID": domain_id,
 | 
				
			||||||
 | 
					                     "HCRESOURCETYPE": '1',
 | 
				
			||||||
 | 
					                     "ISFIRSTSYNC": False,
 | 
				
			||||||
 | 
					                     "LOCALOBJID": lun_id,
 | 
				
			||||||
 | 
					                     "RECONVERYPOLICY": '1',
 | 
				
			||||||
 | 
					                     "REMOTEOBJID": remote_lun_id,
 | 
				
			||||||
 | 
					                     "SPEED": '2'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return self.client.create_hypermetro(hcp_param)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def connect_volume_fc(self, volume, connector):
 | 
				
			||||||
 | 
					        """Create map between a volume and a host for FC."""
 | 
				
			||||||
 | 
					        self.xml_file_path = self.configuration.cinder_huawei_conf_file
 | 
				
			||||||
 | 
					        metro_devices = self.configuration.hypermetro_devices
 | 
				
			||||||
 | 
					        device_info = huawei_utils.get_remote_device_info(metro_devices)
 | 
				
			||||||
 | 
					        self.rmt_client = rest_client.RestClient(self.configuration)
 | 
				
			||||||
 | 
					        self.rmt_client.login_with_ip(device_info)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            wwns = connector['wwpns']
 | 
				
			||||||
 | 
					            volume_name = huawei_utils.encode_name(volume['id'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            LOG.info(_LI(
 | 
				
			||||||
 | 
					                'initialize_connection_fc, initiator: %(wwpns)s,'
 | 
				
			||||||
 | 
					                ' volume name: %(volume)s.'),
 | 
				
			||||||
 | 
					                {'wwpns': wwns,
 | 
				
			||||||
 | 
					                 'volume': volume_name})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            metadata = huawei_utils.get_volume_metadata(volume)
 | 
				
			||||||
 | 
					            lun_id = metadata['remote_lun_id']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if lun_id is None:
 | 
				
			||||||
 | 
					                lun_id = self.rmt_client.get_volume_by_name(volume_name)
 | 
				
			||||||
 | 
					            if lun_id is None:
 | 
				
			||||||
 | 
					                msg = _("Can't get volume id. Volume name: %s.") % volume_name
 | 
				
			||||||
 | 
					                LOG.error(msg)
 | 
				
			||||||
 | 
					                raise exception.VolumeBackendAPIException(data=msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            host_name_before_hash = None
 | 
				
			||||||
 | 
					            host_name = connector['host']
 | 
				
			||||||
 | 
					            if host_name and (len(host_name) > constants.MAX_HOSTNAME_LENGTH):
 | 
				
			||||||
 | 
					                host_name_before_hash = host_name
 | 
				
			||||||
 | 
					                host_name = six.text_type(hash(host_name))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # Create hostgroup if not exist.
 | 
				
			||||||
 | 
					            host_id = self.rmt_client.add_host_with_check(
 | 
				
			||||||
 | 
					                host_name, host_name_before_hash)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            online_wwns_in_host = (
 | 
				
			||||||
 | 
					                self.rmt_client.get_host_online_fc_initiators(host_id))
 | 
				
			||||||
 | 
					            online_free_wwns = self.rmt_client.get_online_free_wwns()
 | 
				
			||||||
 | 
					            for wwn in wwns:
 | 
				
			||||||
 | 
					                if (wwn not in online_wwns_in_host
 | 
				
			||||||
 | 
					                        and wwn not in online_free_wwns):
 | 
				
			||||||
 | 
					                    wwns_in_host = (
 | 
				
			||||||
 | 
					                        self.rmt_client.get_host_fc_initiators(host_id))
 | 
				
			||||||
 | 
					                    iqns_in_host = (
 | 
				
			||||||
 | 
					                        self.rmt_client.get_host_iscsi_initiators(host_id))
 | 
				
			||||||
 | 
					                    if not wwns_in_host and not iqns_in_host:
 | 
				
			||||||
 | 
					                        self.rmt_client.remove_host(host_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    msg = _('Can not add FC port to host.')
 | 
				
			||||||
 | 
					                    LOG.error(msg)
 | 
				
			||||||
 | 
					                    raise exception.VolumeBackendAPIException(data=msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for wwn in wwns:
 | 
				
			||||||
 | 
					                if wwn in online_free_wwns:
 | 
				
			||||||
 | 
					                    self.rmt_client.add_fc_port_to_host(host_id, wwn)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            (tgt_port_wwns, init_targ_map) = (
 | 
				
			||||||
 | 
					                self.rmt_client.get_init_targ_map(wwns))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # Add host into hostgroup.
 | 
				
			||||||
 | 
					            hostgroup_id = self.rmt_client.add_host_into_hostgroup(host_id)
 | 
				
			||||||
 | 
					            map_info = self.rmt_client.do_mapping(lun_id,
 | 
				
			||||||
 | 
					                                                  hostgroup_id,
 | 
				
			||||||
 | 
					                                                  host_id)
 | 
				
			||||||
 | 
					            host_lun_id = self.rmt_client.find_host_lun_id(host_id, lun_id)
 | 
				
			||||||
 | 
					        except exception.VolumeBackendAPIException:
 | 
				
			||||||
 | 
					            raise
 | 
				
			||||||
 | 
					        except Exception as err:
 | 
				
			||||||
 | 
					            msg = _("Connect volume fc: connect volume error. %s.") % err
 | 
				
			||||||
 | 
					            LOG.exception(msg)
 | 
				
			||||||
 | 
					            raise exception.VolumeBackendAPIException(data=msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Return FC properties.
 | 
				
			||||||
 | 
					        fc_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,
 | 
				
			||||||
 | 
					                            'map_info': map_info},
 | 
				
			||||||
 | 
					                   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        LOG.info(_LI('Remote return FC info is: %s.'), fc_info)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return fc_info
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def disconnect_volume_fc(self, volume, connector):
 | 
				
			||||||
 | 
					        """Delete map between a volume and a host for FC."""
 | 
				
			||||||
 | 
					        # Login remote storage device.
 | 
				
			||||||
 | 
					        self.xml_file_path = self.configuration.cinder_huawei_conf_file
 | 
				
			||||||
 | 
					        metro_devices = self.configuration.hypermetro_devices
 | 
				
			||||||
 | 
					        device_info = huawei_utils.get_remote_device_info(metro_devices)
 | 
				
			||||||
 | 
					        self.rmt_client = rest_client.RestClient(self.configuration)
 | 
				
			||||||
 | 
					        self.rmt_client.login_with_ip(device_info)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            wwns = connector['wwpns']
 | 
				
			||||||
 | 
					            volume_name = huawei_utils.encode_name(volume['id'])
 | 
				
			||||||
 | 
					            metadata = huawei_utils.get_volume_metadata(volume)
 | 
				
			||||||
 | 
					            lun_id = metadata['remote_lun_id']
 | 
				
			||||||
 | 
					            host_name = connector['host']
 | 
				
			||||||
 | 
					            left_lunnum = -1
 | 
				
			||||||
 | 
					            lungroup_id = None
 | 
				
			||||||
 | 
					            view_id = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            LOG.info(_LI('terminate_connection_fc: volume name: %(volume)s, '
 | 
				
			||||||
 | 
					                         'wwpns: %(wwns)s, '
 | 
				
			||||||
 | 
					                         'lun_id: %(lunid)s.'),
 | 
				
			||||||
 | 
					                     {'volume': volume_name,
 | 
				
			||||||
 | 
					                      'wwns': wwns,
 | 
				
			||||||
 | 
					                      'lunid': lun_id},)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if host_name and (len(host_name) > constants.MAX_HOSTNAME_LENGTH):
 | 
				
			||||||
 | 
					                host_name = six.text_type(hash(host_name))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            hostid = self.rmt_client.find_host(host_name)
 | 
				
			||||||
 | 
					            if hostid:
 | 
				
			||||||
 | 
					                mapping_view_name = constants.MAPPING_VIEW_PREFIX + hostid
 | 
				
			||||||
 | 
					                view_id = self.rmt_client.find_mapping_view(
 | 
				
			||||||
 | 
					                    mapping_view_name)
 | 
				
			||||||
 | 
					                if view_id:
 | 
				
			||||||
 | 
					                    lungroup_id = self.rmt_client.find_lungroup_from_map(
 | 
				
			||||||
 | 
					                        view_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if lun_id and self.rmt_client.check_lun_exist(lun_id):
 | 
				
			||||||
 | 
					                if lungroup_id:
 | 
				
			||||||
 | 
					                    lungroup_ids = self.rmt_client.get_lungroupids_by_lunid(
 | 
				
			||||||
 | 
					                        lun_id)
 | 
				
			||||||
 | 
					                    if lungroup_id in lungroup_ids:
 | 
				
			||||||
 | 
					                        self.rmt_client.remove_lun_from_lungroup(
 | 
				
			||||||
 | 
					                            lungroup_id, lun_id)
 | 
				
			||||||
 | 
					                    else:
 | 
				
			||||||
 | 
					                        LOG.warning(_LW("Lun is not in lungroup. "
 | 
				
			||||||
 | 
					                                        "Lun id: %(lun_id)s, "
 | 
				
			||||||
 | 
					                                        "lungroup id: %(lungroup_id)s"),
 | 
				
			||||||
 | 
					                                    {"lun_id": lun_id,
 | 
				
			||||||
 | 
					                                     "lungroup_id": lungroup_id})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            (tgt_port_wwns, init_targ_map) = (
 | 
				
			||||||
 | 
					                self.rmt_client.get_init_targ_map(wwns))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            hostid = self.rmt_client.find_host(host_name)
 | 
				
			||||||
 | 
					            if hostid:
 | 
				
			||||||
 | 
					                mapping_view_name = constants.MAPPING_VIEW_PREFIX + hostid
 | 
				
			||||||
 | 
					                view_id = self.rmt_client.find_mapping_view(
 | 
				
			||||||
 | 
					                    mapping_view_name)
 | 
				
			||||||
 | 
					                if view_id:
 | 
				
			||||||
 | 
					                    lungroup_id = self.rmt_client.find_lungroup_from_map(
 | 
				
			||||||
 | 
					                        view_id)
 | 
				
			||||||
 | 
					            if lungroup_id:
 | 
				
			||||||
 | 
					                left_lunnum = self.rmt_client.get_lunnum_from_lungroup(
 | 
				
			||||||
 | 
					                    lungroup_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        except Exception as err:
 | 
				
			||||||
 | 
					            msg = _("Remote detatch volume error. %s.") % err
 | 
				
			||||||
 | 
					            LOG.exception(msg)
 | 
				
			||||||
 | 
					            raise exception.VolumeBackendAPIException(data=msg)
 | 
				
			||||||
 | 
					        finally:
 | 
				
			||||||
 | 
					            self.rmt_client.logout()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if int(left_lunnum) > 0:
 | 
				
			||||||
 | 
					            info = {'driver_volume_type': 'fibre_channel',
 | 
				
			||||||
 | 
					                    'data': {}}
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            info = {'driver_volume_type': 'fibre_channel',
 | 
				
			||||||
 | 
					                    'data': {'target_wwn': tgt_port_wwns,
 | 
				
			||||||
 | 
					                             'initiator_target_map': init_targ_map}, }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return info
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _wait_volume_ready(self, lun_id):
 | 
				
			||||||
 | 
					        event_type = 'LUNReadyWaitInterval'
 | 
				
			||||||
 | 
					        wait_interval = huawei_utils.get_wait_interval(self.xml_file_path,
 | 
				
			||||||
 | 
					                                                       event_type)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def _volume_ready():
 | 
				
			||||||
 | 
					            result = self.rmt_client.get_lun_info(lun_id)
 | 
				
			||||||
 | 
					            if (result['HEALTHSTATUS'] == constants.STATUS_HEALTH
 | 
				
			||||||
 | 
					               and result['RUNNINGSTATUS'] == constants.STATUS_VOLUME_READY):
 | 
				
			||||||
 | 
					                return True
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        huawei_utils.wait_for_condition(self.xml_file_path,
 | 
				
			||||||
 | 
					                                        _volume_ready,
 | 
				
			||||||
 | 
					                                        wait_interval,
 | 
				
			||||||
 | 
					                                        wait_interval * 10)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def retype(self, volume, new_type):
 | 
				
			||||||
 | 
					        return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_hypermetro_stats(self, hypermetro_id):
 | 
				
			||||||
 | 
					        pass
 | 
				
			||||||
@@ -14,6 +14,7 @@
 | 
				
			|||||||
#    under the License.
 | 
					#    under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import json
 | 
					import json
 | 
				
			||||||
 | 
					import six
 | 
				
			||||||
import socket
 | 
					import socket
 | 
				
			||||||
import time
 | 
					import time
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -38,6 +39,7 @@ class RestClient(object):
 | 
				
			|||||||
        self.configuration = configuration
 | 
					        self.configuration = configuration
 | 
				
			||||||
        self.xml_file_path = configuration.cinder_huawei_conf_file
 | 
					        self.xml_file_path = configuration.cinder_huawei_conf_file
 | 
				
			||||||
        self.productversion = None
 | 
					        self.productversion = None
 | 
				
			||||||
 | 
					        self.init_http_head()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def init_http_head(self):
 | 
					    def init_http_head(self):
 | 
				
			||||||
        self.cookie = http_cookiejar.CookieJar()
 | 
					        self.cookie = http_cookiejar.CookieJar()
 | 
				
			||||||
@@ -149,8 +151,47 @@ class RestClient(object):
 | 
				
			|||||||
                      {'old_url': old_url,
 | 
					                      {'old_url': old_url,
 | 
				
			||||||
                       'new_url': self.url})
 | 
					                       'new_url': self.url})
 | 
				
			||||||
            result = self.do_call(url, data, method)
 | 
					            result = self.do_call(url, data, method)
 | 
				
			||||||
 | 
					            if result['error']['code'] in constants.RELOGIN_ERROR_PASS:
 | 
				
			||||||
 | 
					                result['error']['code'] = 0
 | 
				
			||||||
        return result
 | 
					        return result
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def login_with_ip(self, login_info):
 | 
				
			||||||
 | 
					        """Login 18000 array with the specific URL."""
 | 
				
			||||||
 | 
					        urlstr = login_info['RestURL']
 | 
				
			||||||
 | 
					        url_list = urlstr.split(";")
 | 
				
			||||||
 | 
					        for item_url in url_list:
 | 
				
			||||||
 | 
					            url = item_url + "xx/sessions"
 | 
				
			||||||
 | 
					            data = json.dumps({"username": login_info['UserName'],
 | 
				
			||||||
 | 
					                               "password": login_info['UserPassword'],
 | 
				
			||||||
 | 
					                               "scope": '0'})
 | 
				
			||||||
 | 
					            result = self.call(url, data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if result['error']['code'] == constants.ERROR_CONNECT_TO_SERVER:
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (result['error']['code'] != 0) or ('data' not in result):
 | 
				
			||||||
 | 
					                msg = (_("Login error, reason is: %s.") % result)
 | 
				
			||||||
 | 
					                LOG.error(msg)
 | 
				
			||||||
 | 
					                raise exception.VolumeBackendAPIException(data=msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            device_id = result['data']['deviceid']
 | 
				
			||||||
 | 
					            self.device_id = device_id
 | 
				
			||||||
 | 
					            self.url = item_url + device_id
 | 
				
			||||||
 | 
					            self.headers['iBaseToken'] = result['data']['iBaseToken']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return device_id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        msg = _("Login error: Can not connect to server.")
 | 
				
			||||||
 | 
					        LOG.error(msg)
 | 
				
			||||||
 | 
					        raise exception.VolumeBackendAPIException(data=msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def logout(self):
 | 
				
			||||||
 | 
					        """Logout the session."""
 | 
				
			||||||
 | 
					        url = "/sessions"
 | 
				
			||||||
 | 
					        if self.url:
 | 
				
			||||||
 | 
					            result = self.call(url, None, "DELETE")
 | 
				
			||||||
 | 
					            self._assert_rest_result(result, _('Logout session error.'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _assert_rest_result(self, result, err_str):
 | 
					    def _assert_rest_result(self, result, err_str):
 | 
				
			||||||
        if result['error']['code'] != 0:
 | 
					        if result['error']['code'] != 0:
 | 
				
			||||||
            msg = (_('%(err)s\nresult: %(res)s.') % {'err': err_str,
 | 
					            msg = (_('%(err)s\nresult: %(res)s.') % {'err': err_str,
 | 
				
			||||||
@@ -383,6 +424,7 @@ class RestClient(object):
 | 
				
			|||||||
        mapping_view_name = constants.MAPPING_VIEW_PREFIX + host_id
 | 
					        mapping_view_name = constants.MAPPING_VIEW_PREFIX + host_id
 | 
				
			||||||
        lungroup_id = self._find_lungroup(lungroup_name)
 | 
					        lungroup_id = self._find_lungroup(lungroup_name)
 | 
				
			||||||
        view_id = self.find_mapping_view(mapping_view_name)
 | 
					        view_id = self.find_mapping_view(mapping_view_name)
 | 
				
			||||||
 | 
					        map_info = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        LOG.info(_LI(
 | 
					        LOG.info(_LI(
 | 
				
			||||||
            'do_mapping, lun_group: %(lun_group)s, '
 | 
					            'do_mapping, lun_group: %(lun_group)s, '
 | 
				
			||||||
@@ -418,6 +460,13 @@ class RestClient(object):
 | 
				
			|||||||
                        self._associate_portgroup_to_view(view_id,
 | 
					                        self._associate_portgroup_to_view(view_id,
 | 
				
			||||||
                                                          tgtportgroup_id)
 | 
					                                                          tgtportgroup_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            version = self.find_array_version()
 | 
				
			||||||
 | 
					            if version >= constants.ARRAY_VERSION:
 | 
				
			||||||
 | 
					                aval_luns = self.find_view_by_id(view_id)
 | 
				
			||||||
 | 
					                map_info["lun_id"] = lun_id
 | 
				
			||||||
 | 
					                map_info["view_id"] = view_id
 | 
				
			||||||
 | 
					                map_info["aval_luns"] = aval_luns
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        except Exception:
 | 
					        except Exception:
 | 
				
			||||||
            with excutils.save_and_reraise_exception():
 | 
					            with excutils.save_and_reraise_exception():
 | 
				
			||||||
                LOG.error(_LE(
 | 
					                LOG.error(_LE(
 | 
				
			||||||
@@ -425,6 +474,8 @@ class RestClient(object):
 | 
				
			|||||||
                    'view. Remove lun from lungroup now.'))
 | 
					                    'view. Remove lun from lungroup now.'))
 | 
				
			||||||
                self.remove_lun_from_lungroup(lungroup_id, lun_id)
 | 
					                self.remove_lun_from_lungroup(lungroup_id, lun_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return map_info
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def ensure_initiator_added(self, xml_file_path, initiator_name, host_id):
 | 
					    def ensure_initiator_added(self, xml_file_path, initiator_name, host_id):
 | 
				
			||||||
        added = self._initiator_is_added_to_array(initiator_name)
 | 
					        added = self._initiator_is_added_to_array(initiator_name)
 | 
				
			||||||
        if not added:
 | 
					        if not added:
 | 
				
			||||||
@@ -1114,6 +1165,7 @@ class RestClient(object):
 | 
				
			|||||||
                smarttier=True,
 | 
					                smarttier=True,
 | 
				
			||||||
                smartcache=True,
 | 
					                smartcache=True,
 | 
				
			||||||
                smartpartition=True,
 | 
					                smartpartition=True,
 | 
				
			||||||
 | 
					                hypermetro=True,
 | 
				
			||||||
            ))
 | 
					            ))
 | 
				
			||||||
            data['pools'].append(pool)
 | 
					            data['pools'].append(pool)
 | 
				
			||||||
        return data
 | 
					        return data
 | 
				
			||||||
@@ -1200,6 +1252,9 @@ class RestClient(object):
 | 
				
			|||||||
                LOG.error(msg)
 | 
					                LOG.error(msg)
 | 
				
			||||||
                raise exception.InvalidInput(reason=msg)
 | 
					                raise exception.InvalidInput(reason=msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Deal with the remote tgt ip.
 | 
				
			||||||
 | 
					        if 'remote_target_ip' in connector:
 | 
				
			||||||
 | 
					            target_ips.append(connector['remote_target_ip'])
 | 
				
			||||||
        LOG.info(_LI('Get the default ip: %s.'), target_ips)
 | 
					        LOG.info(_LI('Get the default ip: %s.'), target_ips)
 | 
				
			||||||
        for ip in target_ips:
 | 
					        for ip in target_ips:
 | 
				
			||||||
            target_iqn = self._get_tgt_iqn_from_rest(ip)
 | 
					            target_iqn = self._get_tgt_iqn_from_rest(ip)
 | 
				
			||||||
@@ -1678,3 +1733,112 @@ class RestClient(object):
 | 
				
			|||||||
                                              constants.FC_PORT_CONNECTED):
 | 
					                                              constants.FC_PORT_CONNECTED):
 | 
				
			||||||
                port_list_from_contr.append(item['WWN'])
 | 
					                port_list_from_contr.append(item['WWN'])
 | 
				
			||||||
        return port_list_from_contr
 | 
					        return port_list_from_contr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_hyper_domain_id(self, domain_name):
 | 
				
			||||||
 | 
					        url = "/HyperMetroDomain?range=[0-100]"
 | 
				
			||||||
 | 
					        result = self.call(url, None, "GET")
 | 
				
			||||||
 | 
					        domain_id = None
 | 
				
			||||||
 | 
					        if "data" in result:
 | 
				
			||||||
 | 
					            for item in result['data']:
 | 
				
			||||||
 | 
					                if domain_name == item['NAME']:
 | 
				
			||||||
 | 
					                    domain_id = item['ID']
 | 
				
			||||||
 | 
					                    break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        msg = _('get_hyper_domain_id error.')
 | 
				
			||||||
 | 
					        self._assert_rest_result(result, msg)
 | 
				
			||||||
 | 
					        return domain_id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def create_hypermetro(self, hcp_param):
 | 
				
			||||||
 | 
					        url = "/HyperMetroPair"
 | 
				
			||||||
 | 
					        data = json.dumps(hcp_param)
 | 
				
			||||||
 | 
					        result = self.call(url, data, "POST")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        msg = _('create_hypermetro_pair error.')
 | 
				
			||||||
 | 
					        self._assert_rest_result(result, msg)
 | 
				
			||||||
 | 
					        self._assert_data_in_result(result, msg)
 | 
				
			||||||
 | 
					        return result['data']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def delete_hypermetro(self, metro_id):
 | 
				
			||||||
 | 
					        url = "/HyperMetroPair/" + metro_id
 | 
				
			||||||
 | 
					        result = self.call(url, None, "DELETE")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        msg = _('delete_hypermetro error.')
 | 
				
			||||||
 | 
					        self._assert_rest_result(result, msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def sync_hypermetro(self, metro_id):
 | 
				
			||||||
 | 
					        url = "/HyperMetroPair/synchronize_hcpair"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        data = json.dumps({"ID": metro_id,
 | 
				
			||||||
 | 
					                           "TYPE": "15361"})
 | 
				
			||||||
 | 
					        result = self.call(url, data, "PUT")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        msg = _('sync_hypermetro error.')
 | 
				
			||||||
 | 
					        self._assert_rest_result(result, msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def stop_hypermetro(self, metro_id):
 | 
				
			||||||
 | 
					        url = '/HyperMetroPair/disable_hcpair'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        data = json.dumps({"ID": metro_id,
 | 
				
			||||||
 | 
					                           "TYPE": "15361"})
 | 
				
			||||||
 | 
					        result = self.call(url, data, "PUT")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        msg = _('stop_hypermetro error.')
 | 
				
			||||||
 | 
					        self._assert_rest_result(result, msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_hypermetro_by_id(self, metro_id):
 | 
				
			||||||
 | 
					        url = "/HyperMetroPair/" + metro_id
 | 
				
			||||||
 | 
					        result = self.call(url, None, "GET")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        msg = _('get_hypermetro_by_id error.')
 | 
				
			||||||
 | 
					        self._assert_rest_result(result, msg)
 | 
				
			||||||
 | 
					        return result
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def check_hypermetro_exist(self, metro_id):
 | 
				
			||||||
 | 
					        url = "/HyperMetroPair/" + metro_id
 | 
				
			||||||
 | 
					        result = self.call(url, None, "GET")
 | 
				
			||||||
 | 
					        error_code = result['error']['code']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (error_code == constants.ERROR_CONNECT_TO_SERVER
 | 
				
			||||||
 | 
					                or error_code == constants.ERROR_UNAUTHORIZED_TO_SERVER):
 | 
				
			||||||
 | 
					            LOG.error(_LE("Can not open the recent url, login again."))
 | 
				
			||||||
 | 
					            self.login()
 | 
				
			||||||
 | 
					            result = self.call(url, None, "GET")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        error_code = result['error']['code']
 | 
				
			||||||
 | 
					        if (error_code == constants.ERROR_CONNECT_TO_SERVER
 | 
				
			||||||
 | 
					                or error_code == constants.ERROR_UNAUTHORIZED_TO_SERVER):
 | 
				
			||||||
 | 
					            msg = _("check_hypermetro_exist error.")
 | 
				
			||||||
 | 
					            LOG.error(msg)
 | 
				
			||||||
 | 
					            raise exception.VolumeBackendAPIException(data=msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if error_code != 0:
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def change_hostlun_id(self, map_info, hostlun_id):
 | 
				
			||||||
 | 
					        url = "/mappingview"
 | 
				
			||||||
 | 
					        view_id = six.text_type(map_info['view_id'])
 | 
				
			||||||
 | 
					        lun_id = six.text_type(map_info['lun_id'])
 | 
				
			||||||
 | 
					        hostlun_id = six.text_type(hostlun_id)
 | 
				
			||||||
 | 
					        data = json.dumps({"TYPE": 245,
 | 
				
			||||||
 | 
					                           "ID": view_id,
 | 
				
			||||||
 | 
					                           "ASSOCIATEOBJTYPE": 11,
 | 
				
			||||||
 | 
					                           "ASSOCIATEOBJID": lun_id,
 | 
				
			||||||
 | 
					                           "ASSOCIATEMETADATA": [{"LUNID": lun_id,
 | 
				
			||||||
 | 
					                                                  "hostLUNId": hostlun_id}]
 | 
				
			||||||
 | 
					                           })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        result = self.call(url, data, "PUT")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        msg = 'change hostlun id error.'
 | 
				
			||||||
 | 
					        self._assert_rest_result(result, msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def find_view_by_id(self, view_id):
 | 
				
			||||||
 | 
					        url = "/MAPPINGVIEW/" + view_id
 | 
				
			||||||
 | 
					        result = self.call(url, None, "GET")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        msg = _('Change hostlun id error.')
 | 
				
			||||||
 | 
					        self._assert_rest_result(result, msg)
 | 
				
			||||||
 | 
					        if 'data' in result:
 | 
				
			||||||
 | 
					            return result["data"]["AVAILABLEHOSTLUNIDLIST"]
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user