From: chenzongliang Date: Sat, 12 Dec 2015 09:11:55 +0000 (+0800) Subject: Huawei: Refactor driver for the second time X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=23115ee8e02ae5127681a84abd1615e9e55e958c;p=openstack-build%2Fcinder-build.git Huawei: Refactor driver for the second time After a few new features being added into drivers, we found it hard to maintain the code. For further working, we need to refactor our code. The work contains the following: 1. Define a new class, named HuaweiConf, to parse xml config file. We used an external xml file to store SAN info. But we do config parsing anywhere in our code, even two different functions do the same parsing. 2. Adjust some function structures. 3. Rename some functions & variables. DocImpact Implements: blueprint refactor-huawei-driver Change-Id: If935a38399809cf95848825344fb837bf04a7dd3 --- diff --git a/cinder/tests/unit/test_huawei_drivers.py b/cinder/tests/unit/test_huawei_drivers.py index 5fde206b6..8e75023d4 100644 --- a/cinder/tests/unit/test_huawei_drivers.py +++ b/cinder/tests/unit/test_huawei_drivers.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015 Huawei Technologies Co., Ltd. +# Copyright (c) 2016 Huawei Technologies Co., Ltd. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -16,9 +16,7 @@ import ddt import json import mock -import os import re -import shutil import tempfile import time from xml.dom import minidom @@ -30,8 +28,8 @@ from cinder import test from cinder.volume import configuration as conf from cinder.volume.drivers.huawei import constants from cinder.volume.drivers.huawei import fc_zone_helper +from cinder.volume.drivers.huawei import huawei_conf from cinder.volume.drivers.huawei import huawei_driver -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 smartx @@ -111,6 +109,7 @@ test_snap = {'name': 'volume-21ec7341-9256-497b-97d9-ef48edcf0635', 'display_description': 'test volume', 'volume_type_id': None, 'provider_location': '11', + 'volume': {"volume_id": '21ec7341-9256-497b-97d9-ef48edcf0635'}, 'volume': {'provider_location': '12'}, } @@ -199,9 +198,6 @@ fake_fabric_mapping = { } } -FAKE_CREATE_VOLUME_RESPONSE = {"ID": "1", - "NAME": "5mFHcBv4RkCcD+JyrWc0SA"} - CHANGE_OPTS = {'policy': ('1', '2'), 'partitionid': (['1', 'partition001'], ['2', 'partition002']), 'cacheid': (['1', 'cache001'], ['2', 'cache002']), @@ -297,6 +293,7 @@ FAKE_LUN_GET_SUCCESS_RESPONSE = """ "ID": "11", "IOCLASSID": "11", "NAME": "5mFHcBv4RkCcD+JyrWc0SA", + "DESCRIPTION": "21ec7341-9256-497b-97d9-ef48edcf0635", "RUNNINGSTATUS": "2", "HEALTHSTATUS": "1", "RUNNINGSTATUS": "27", @@ -1337,7 +1334,7 @@ MAP_COMMAND_TO_FAKE_RESPONSE['/portgroup?range=[0-8191]&TYPE=257/GET'] = ( FAKE_PORT_GROUP_RESPONSE) # mock system info map -MAP_COMMAND_TO_FAKE_RESPONSE['/system/'] = ( +MAP_COMMAND_TO_FAKE_RESPONSE['/system//GET'] = ( FAKE_SYSTEM_VERSION_RESPONSE) MAP_COMMAND_TO_FAKE_RESPONSE['/fc_initiator?range=[0-256]/GET'] = ( @@ -1399,43 +1396,61 @@ def Fake_sleep(time): pass +class FakeHuaweiConf(object): + def __init__(self, conf, protocol): + self.conf = conf + self.protocol = protocol + + def update_config_value(self): + setattr(self.conf, 'san_address', + ['http://100.115.10.69:8082/deviceManager/rest/']) + setattr(self.conf, 'san_user', 'admin') + setattr(self.conf, 'san_password', 'Admin@storage') + setattr(self.conf, 'san_product', 'V3') + setattr(self.conf, 'san_protocol', self.protocol) + setattr(self.conf, 'lun_type', constants.THICK_LUNTYPE) + setattr(self.conf, 'lun_ready_wait_interval', 2) + setattr(self.conf, 'lun_copy_wait_interval', 2) + setattr(self.conf, 'lun_timeout', 43200) + setattr(self.conf, 'lun_write_type', '1') + setattr(self.conf, 'lun_mirror_switch', '1') + setattr(self.conf, 'lun_prefetch_type', '1') + setattr(self.conf, 'lun_prefetch_value', '0') + setattr(self.conf, 'lun_policy', '0') + setattr(self.conf, 'lun_read_cache_policy', '2') + setattr(self.conf, 'lun_write_cache_policy', '5') + setattr(self.conf, 'storage_pools', ['OpenStack_Pool']) + setattr(self.conf, 'iscsi_default_target_ip', ['100.115.10.68']) + setattr(self.conf, 'metro_san_address', + ['https://100.97.50.240:8088/deviceManager/rest/']) + setattr(self.conf, 'metro_storage_pools', 'StoragePool001') + setattr(self.conf, 'metro_san_user', 'admin') + setattr(self.conf, 'metro_san_password', 'Admin@storage1') + setattr(self.conf, 'metro_domain_name', 'hypermetro_test') + + iscsi_info = {'Name': 'iqn.1993-08.debian:01:ec2bff7ac3a3', + 'TargetIP': '192.168.100.2', + 'CHAPinfo': 'mm-user;mm-user@storage', + 'ALUA': '1', + 'TargetPortGroup': 'portgroup-test', } + setattr(self.conf, 'iscsi_info', [iscsi_info]) + + class FakeClient(rest_client.RestClient): def __init__(self, configuration): - rest_client.RestClient.__init__(self, configuration) - self.delete_flag = False - self.terminateFlag = False - self.device_id = None + san_address = configuration.san_address + san_user = configuration.san_user + san_password = configuration.san_password + rest_client.RestClient.__init__(self, configuration, + san_address, + san_user, + san_password) self.test_fail = False self.test_multi_url_flag = False - self.checkFlag = False - self.remove_chap_flag = False self.cache_not_exist = False self.partition_not_exist = False - def _change_file_mode(self, filepath): - pass - - def _parse_volume_type(self, volume): - poolinfo = self._find_pool_info() - volume_size = self._get_volume_size(poolinfo, volume) - - params = {'LUNType': 0, - 'WriteType': '1', - 'PrefetchType': '3', - 'qos_level': 'Qos-high', - 'StripUnitSize': '64', - 'PrefetchValue': '0', - 'PrefetchTimes': '0', - 'qos': 'OpenStack_Qos_High', - 'MirrorSwitch': '1', - 'tier': 'Tier_high', - } - - params['volume_size'] = volume_size - params['pool_id'] = poolinfo['ID'] - return params - def _get_snapshotid_by_name(self, snapshot_name): return "11" @@ -1458,7 +1473,7 @@ class FakeClient(rest_client.RestClient): def do_call(self, url=False, data=None, method=None, calltimeout=4): url = url.replace('http://100.115.10.69:8082/deviceManager/rest', '') command = url.replace('/210235G7J20000000000/', '') - data = None + data = json.dumps(data) if data else None if method: command = command + "/" + method @@ -1480,15 +1495,34 @@ class FakeClient(rest_client.RestClient): return json.loads(data) +class FakeDB(object): + + def volume_update(self, context, volume_id, model_update): + pass + + def volume_get_all_by_group(self, context, group_id): + volumes = [] + volumes.append(test_volume) + return volumes + + class FakeISCSIStorage(huawei_driver.HuaweiISCSIDriver): """Fake Huawei Storage, Rewrite some methods of HuaweiISCSIDriver.""" def __init__(self, configuration): self.configuration = configuration - self.xml_file_path = self.configuration.cinder_huawei_conf_file + self.db = FakeDB() + self.huawei_conf = FakeHuaweiConf(self.configuration, 'iSCSI') def do_setup(self): - self.restclient = FakeClient(configuration=self.configuration) + self.metro_flag = True + self.huawei_conf.update_config_value() + self.client = FakeClient(configuration=self.configuration) + self.rmt_client = FakeClient(configuration=self.configuration) + self.metro = hypermetro.HuaweiHyperMetro(self.client, + self.rmt_client, + self.configuration, + self.db) class FakeFCStorage(huawei_driver.HuaweiFCDriver): @@ -1496,11 +1530,19 @@ class FakeFCStorage(huawei_driver.HuaweiFCDriver): def __init__(self, configuration): self.configuration = configuration - self.xml_file_path = self.configuration.cinder_huawei_conf_file self.fcsan_lookup_service = None + self.db = FakeDB() + self.huawei_conf = FakeHuaweiConf(self.configuration, 'iSCSI') def do_setup(self): - self.restclient = FakeClient(configuration=self.configuration) + self.metro_flag = True + self.huawei_conf.update_config_value() + self.client = FakeClient(configuration=self.configuration) + self.rmt_client = FakeClient(configuration=self.configuration) + self.metro = hypermetro.HuaweiHyperMetro(self.client, + self.rmt_client, + self.configuration, + self.db) @ddt.ddt @@ -1508,20 +1550,11 @@ class HuaweiISCSIDriverTestCase(test.TestCase): def setUp(self): super(HuaweiISCSIDriverTestCase, self).setUp() - self.tmp_dir = tempfile.mkdtemp() - self.fake_conf_file = self.tmp_dir + '/cinder_huawei_conf.xml' - self.addCleanup(shutil.rmtree, self.tmp_dir) - self.create_fake_conf_file() - self.addCleanup(os.remove, self.fake_conf_file) self.configuration = mock.Mock(spec=conf.Configuration) - self.configuration.cinder_huawei_conf_file = self.fake_conf_file - self.xml_file_path = self.configuration.cinder_huawei_conf_file self.configuration.hypermetro_devices = hypermetro_devices self.stubs.Set(time, 'sleep', Fake_sleep) - driver = FakeISCSIStorage(configuration=self.configuration) - self.driver = driver + self.driver = FakeISCSIStorage(configuration=self.configuration) self.driver.do_setup() - self.device_id = self.driver.restclient.login() self.portgroup = 'portgroup-test' self.iscsi_iqns = ['iqn.2006-08.com.huawei:oceanstor:21000022a:' ':20503:192.168.1.1', @@ -1530,11 +1563,14 @@ class HuaweiISCSIDriverTestCase(test.TestCase): self.target_ips = ['192.168.1.1', '192.168.1.2'] self.portgroup_id = 11 + self.driver.client.login() def test_login_success(self): - self.assertEqual('210235G7J20000000000', self.device_id) + device_id = self.driver.client.login() + self.assertEqual('210235G7J20000000000', device_id) def test_create_volume_success(self): + # Have pool info in the volume. test_volume = {'name': 'volume-21ec7341-9256-497b-97d9-ef48edcf0635', 'size': 2, @@ -1592,7 +1628,7 @@ class HuaweiISCSIDriverTestCase(test.TestCase): def test_create_volume_from_snapsuccess(self): lun_info = self.driver.create_volume_from_snapshot(test_volume, test_volume) - self.assertEqual('1', lun_info['ID']) + self.assertEqual('1', lun_info['provider_location']) def test_initialize_connection_success(self): iscsi_properties = self.driver.initialize_connection(test_volume, @@ -1600,188 +1636,150 @@ class HuaweiISCSIDriverTestCase(test.TestCase): self.assertEqual(1, iscsi_properties['data']['target_lun']) def test_terminate_connection_success(self): - self.driver.restclient.terminateFlag = True self.driver.terminate_connection(test_volume, FakeConnector) - self.assertTrue(self.driver.restclient.terminateFlag) def test_get_volume_status(self): data = self.driver.get_volume_stats() - self.assertEqual('2.0.1', data['driver_version']) + self.assertEqual('2.0.2', data['driver_version']) def test_extend_volume(self): lun_info = self.driver.extend_volume(test_volume, 3) self.assertEqual('1', lun_info['provider_location']) def test_login_fail(self): - self.driver.restclient.test_fail = True + self.driver.client.test_fail = True self.assertRaises(exception.VolumeBackendAPIException, - self.driver.restclient.login) + self.driver.client.login) def test_create_snapshot_fail(self): - self.driver.restclient.test_fail = True + + self.driver.client.test_fail = True self.assertRaises(exception.VolumeBackendAPIException, self.driver.create_snapshot, test_snap) def test_create_volume_fail(self): - self.driver.restclient.test_fail = True + + self.driver.client.test_fail = True self.assertRaises(exception.VolumeBackendAPIException, self.driver.create_volume, test_volume) def test_delete_volume_fail(self): - self.driver.restclient.test_fail = True - delete_flag = self.driver.delete_volume(test_volume) - self.assertTrue(delete_flag) + + self.driver.client.test_fail = True + self.driver.delete_volume(test_volume) def test_delete_snapshot_fail(self): - self.driver.restclient.test_fail = True - delete_flag = self.driver.delete_volume(test_snap) - self.assertTrue(delete_flag) + + self.driver.client.test_fail = True + self.driver.delete_volume(test_snap) def test_initialize_connection_fail(self): - self.driver.restclient.test_fail = True + + self.driver.client.test_fail = True self.assertRaises(exception.VolumeBackendAPIException, self.driver.initialize_connection, test_volume, FakeConnector) - def test_get_default_timeout(self): - result = huawei_utils.get_default_timeout(self.xml_file_path) - self.assertEqual('43200', result) - - def test_get_wait_interval(self): - result = huawei_utils.get_wait_interval(self.xml_file_path, - 'LUNReadyWaitInterval') - self.assertEqual(2, result) - def test_lun_is_associated_to_lungroup(self): - self.driver.restclient.associate_lun_to_lungroup('11', '11') - result = self.driver.restclient._is_lun_associated_to_lungroup('11', - '11') + + self.driver.client.associate_lun_to_lungroup('11', '11') + result = self.driver.client._is_lun_associated_to_lungroup('11', + '11') self.assertTrue(result) def test_lun_is_not_associated_to_lun_group(self): - self.driver.restclient.associate_lun_to_lungroup('12', '12') - self.driver.restclient.remove_lun_from_lungroup('12', '12') - result = self.driver.restclient._is_lun_associated_to_lungroup('12', - '12') + + self.driver.client.associate_lun_to_lungroup('12', '12') + self.driver.client.remove_lun_from_lungroup('12', '12') + result = self.driver.client._is_lun_associated_to_lungroup('12', '12') self.assertFalse(result) def test_get_tgtip(self): - portg_id = self.driver.restclient.find_tgt_port_group(self.portgroup) - target_ip = self.driver.restclient._get_tgt_ip_from_portgroup(portg_id) - self.assertEqual(self.target_ips, target_ip) - def test_get_iscsi_params(self): - (iscsi_iqns, target_ips, portgroup_id) = ( - self.driver.restclient.get_iscsi_params(self.xml_file_path, - FakeConnector)) - self.assertEqual(self.iscsi_iqns, iscsi_iqns) - self.assertEqual(self.target_ips, target_ips) - self.assertEqual(self.portgroup_id, portgroup_id) - - def test_get_lun_conf_params(self): - luninfo = huawei_utils.get_lun_conf_params(self.xml_file_path) - luninfo['pool_id'] = '0' - luninfo['volume_size'] = 2 - luninfo['volume_description'] = 'test volume' - luninfo = huawei_utils.init_lun_parameters('5mFHcBv4RkCcD+JyrWc0SA', - luninfo) - self.assertEqual('5mFHcBv4RkCcD+JyrWc0SA', luninfo['NAME']) - - def tset_get_iscsi_conf(self): - iscsiinfo = huawei_utils.get_iscsi_conf(self.xml_file_path) - self.assertEqual('iqn.1993-08.debian:01:ec2bff7ac3a3', - iscsiinfo['Initiator']) - - def test_check_conf_file(self): - self.driver.restclient.checkFlag = True - huawei_utils.check_conf_file(self.xml_file_path) - self.assertTrue(self.driver.restclient.checkFlag) - - def test_get_conf_host_os_type(self): - host_os = huawei_utils.get_conf_host_os_type('100.97.10.30', - self.configuration) - self.assertEqual('0', host_os) + portg_id = self.driver.client.get_tgt_port_group(self.portgroup) + target_ip = self.driver.client._get_tgt_ip_from_portgroup(portg_id) + self.assertEqual(self.target_ips, target_ip) def test_find_chap_info(self): + tmp_dict = {} iscsi_info = {} tmp_dict['Name'] = 'iqn.1993-08.debian:01:ec2bff7ac3a3' tmp_dict['CHAPinfo'] = 'mm-user;mm-user@storage' - ini_list = [tmp_dict] - iscsi_info['Initiator'] = ini_list + iscsi_info = [tmp_dict] initiator_name = FakeConnector['initiator'] - chapinfo = self.driver.restclient.find_chap_info(iscsi_info, - initiator_name) + chapinfo = self.driver.client.find_chap_info(iscsi_info, + initiator_name) chap_username, chap_password = chapinfo.split(';') self.assertEqual('mm-user', chap_username) self.assertEqual('mm-user@storage', chap_password) def test_find_alua_info(self): + tmp_dict = {} iscsi_info = {} tmp_dict['Name'] = 'iqn.1993-08.debian:01:ec2bff7ac3a3' tmp_dict['ALUA'] = '1' - ini_list = [tmp_dict] - iscsi_info['Initiator'] = ini_list + iscsi_info = [tmp_dict] initiator_name = FakeConnector['initiator'] - type = self.driver.restclient._find_alua_info(iscsi_info, - initiator_name) + type = self.driver.client._find_alua_info(iscsi_info, + initiator_name) self.assertEqual('1', type) - def test_find_pool_info(self): - pools = { - "error": {"code": 0}, - "data": [{ - "NAME": "test001", - "ID": "0", - "USERFREECAPACITY": "36", - "USERTOTALCAPACITY": "48", - "USAGETYPE": constants.BLOCK_STORAGE_POOL_TYPE}, - {"NAME": "test002", - "ID": "1", - "USERFREECAPACITY": "37", - "USERTOTALCAPACITY": "49", - "USAGETYPE": constants.FILE_SYSTEM_POOL_TYPE}, - {"NAME": "test003", - "ID": "0", - "USERFREECAPACITY": "36", - "DATASPACE": "35", - "USERTOTALCAPACITY": "48", - "USAGETYPE": constants.BLOCK_STORAGE_POOL_TYPE}]} + def test_get_pool_info(self): + + pools = [{"NAME": "test001", + "ID": "0", + "USERFREECAPACITY": "36", + "USERTOTALCAPACITY": "48", + "USAGETYPE": constants.BLOCK_STORAGE_POOL_TYPE}, + {"NAME": "test002", + "ID": "1", + "USERFREECAPACITY": "37", + "USERTOTALCAPACITY": "49", + "USAGETYPE": constants.FILE_SYSTEM_POOL_TYPE}, + {"NAME": "test003", + "ID": "0", + "USERFREECAPACITY": "36", + "DATASPACE": "35", + "USERTOTALCAPACITY": "48", + "USAGETYPE": constants.BLOCK_STORAGE_POOL_TYPE}] pool_name = 'test001' test_info = {'CAPACITY': '36', 'ID': '0', 'TOTALCAPACITY': '48'} - pool_info = self.driver.restclient.find_pool_info(pool_name, pools) + pool_info = self.driver.client.get_pool_info(pool_name, pools) self.assertEqual(test_info, pool_info) pool_name = 'test002' test_info = {} - pool_info = self.driver.restclient.find_pool_info(pool_name, pools) + pool_info = self.driver.client.get_pool_info(pool_name, pools) self.assertEqual(test_info, pool_info) pool_name = 'test000' test_info = {} - pool_info = self.driver.restclient.find_pool_info(pool_name, pools) + pool_info = self.driver.client.get_pool_info(pool_name, pools) self.assertEqual(test_info, pool_info) pool_name = 'test003' test_info = {'CAPACITY': '35', 'ID': '0', 'TOTALCAPACITY': '48'} - pool_info = self.driver.restclient.find_pool_info(pool_name, pools) + pool_info = self.driver.client.get_pool_info(pool_name, pools) self.assertEqual(test_info, pool_info) def test_get_smartx_specs_opts(self): + smartx_opts = smartx.SmartX().get_smartx_specs_opts(smarttier_opts) self.assertEqual('3', smartx_opts['policy']) - @mock.patch.object(huawei_utils, 'get_volume_qos', + @mock.patch.object(smartx.SmartQos, 'get_qos_by_volume_type', return_value={'MAXIOPS': '100', 'IOType': '2'}) def test_create_smartqos(self, mock_qos_value): + lun_info = self.driver.create_volume(test_volume) self.assertEqual('1', lun_info['provider_location']) @mock.patch.object(rest_client.RestClient, 'add_lun_to_partition') - @mock.patch.object(huawei_utils, 'get_volume_params', + @mock.patch.object(huawei_driver.HuaweiBaseDriver, '_get_volume_params', return_value={'smarttier': 'true', 'smartcache': 'true', 'smartpartition': 'true', @@ -1791,10 +1789,12 @@ class HuaweiISCSIDriverTestCase(test.TestCase): 'cachename': 'cache-test', 'partitionname': 'partition-test'}) def test_creat_smartx(self, mock_volume_types, mock_add_lun_to_partition): + lun_info = self.driver.create_volume(test_volume) self.assertEqual('1', lun_info['provider_location']) def test_find_available_qos(self): + qos = {'MAXIOPS': '100', 'IOType': '2'} fake_qos_info_response_equal = { "error": { @@ -1816,7 +1816,7 @@ class HuaweiISCSIDriverTestCase(test.TestCase): # Number of LUNs in QoS is equal to 64 with mock.patch.object(rest_client.RestClient, 'get_qos', return_value=fake_qos_info_response_equal): - (qos_id, lun_list) = self.driver.restclient.find_available_qos(qos) + (qos_id, lun_list) = self.driver.client.find_available_qos(qos) self.assertEqual((None, []), (qos_id, lun_list)) # Number of LUNs in QoS is less than 64 @@ -1833,18 +1833,16 @@ class HuaweiISCSIDriverTestCase(test.TestCase): } with mock.patch.object(rest_client.RestClient, 'get_qos', return_value=fake_qos_info_response_less): - (qos_id, lun_list) = self.driver.restclient.find_available_qos(qos) + (qos_id, lun_list) = self.driver.client.find_available_qos(qos) self.assertEqual(("11", u'["0", "1", "2"]'), (qos_id, lun_list)) - @mock.patch.object(huawei_utils, 'get_volume_params', + @mock.patch.object(huawei_driver.HuaweiBaseDriver, '_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', + @mock.patch.object(rest_client.RestClient, 'get_all_pools', return_value=FAKE_STORAGE_POOL_RESPONSE) - @mock.patch.object(rest_client.RestClient, 'find_pool_info', + @mock.patch.object(rest_client.RestClient, 'get_pool_info', return_value=FAKE_FIND_POOL_RESPONSE) - @mock.patch.object(rest_client.RestClient, 'create_volume', + @mock.patch.object(rest_client.RestClient, 'create_lun', return_value=FAKE_CREATE_VOLUME_RESPONSE) @mock.patch.object(rest_client.RestClient, 'get_hyper_domain_id', return_value='11') @@ -1863,23 +1861,19 @@ class HuaweiISCSIDriverTestCase(test.TestCase): mock_create_volume, mock_pool_info, mock_all_pool_info, - mock_login_return, mock_hypermetro_opts): 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']) - @mock.patch.object(huawei_utils, 'get_volume_params', + @mock.patch.object(huawei_driver.HuaweiBaseDriver, '_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', + @mock.patch.object(rest_client.RestClient, 'get_all_pools', return_value=FAKE_STORAGE_POOL_RESPONSE) - @mock.patch.object(rest_client.RestClient, 'find_pool_info', + @mock.patch.object(rest_client.RestClient, 'get_pool_info', return_value=FAKE_FIND_POOL_RESPONSE) - @mock.patch.object(rest_client.RestClient, 'create_volume', + @mock.patch.object(rest_client.RestClient, 'create_lun', return_value=FAKE_CREATE_VOLUME_RESPONSE) @mock.patch.object(rest_client.RestClient, 'get_hyper_domain_id', return_value='11') @@ -1899,17 +1893,14 @@ class HuaweiISCSIDriverTestCase(test.TestCase): mock_create_volume, mock_pool_info, mock_all_pool_info, - mock_login_return, mock_hypermetro_opts): + self.driver.client.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() - @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', @@ -1927,14 +1918,10 @@ class HuaweiISCSIDriverTestCase(test.TestCase): mock_delete_hypermetro, mock_metro_info, mock_check_hyermetro, - mock_lun_exit, - mock_login_info): + mock_lun_exit): 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', @@ -1951,8 +1938,8 @@ class HuaweiISCSIDriverTestCase(test.TestCase): mock_delete_hypermetro, mock_metro_info, mock_check_hyermetro, - mock_lun_exit, - mock_login_info): + mock_lun_exit): + mock_delete_hypermetro.side_effect = ( exception.VolumeBackendAPIException(data='Delete hypermetro ' 'error.')) @@ -2175,94 +2162,9 @@ class HuaweiISCSIDriverTestCase(test.TestCase): self.driver.unmanage(test_volume) self.assertEqual(ddt_data[1], mock_rename.call_count) - def create_fake_conf_file(self): - """Create a fake Config file. - - Huawei storage customize a XML configuration file, the configuration - file is used to set the Huawei storage custom parameters, therefore, - in the UT test we need to simulate such a configuration file. - """ - doc = minidom.Document() - - config = doc.createElement('config') - doc.appendChild(config) - - storage = doc.createElement('Storage') - config.appendChild(storage) - controllerip0 = doc.createElement('ControllerIP0') - controllerip0_text = doc.createTextNode('10.10.10.1') - controllerip0.appendChild(controllerip0_text) - storage.appendChild(controllerip0) - controllerip1 = doc.createElement('ControllerIP1') - controllerip1_text = doc.createTextNode('10.10.10.2') - controllerip1.appendChild(controllerip1_text) - storage.appendChild(controllerip1) - username = doc.createElement('UserName') - username_text = doc.createTextNode('admin') - username.appendChild(username_text) - storage.appendChild(username) - userpassword = doc.createElement('UserPassword') - userpassword_text = doc.createTextNode('Admin@storage') - userpassword.appendChild(userpassword_text) - storage.appendChild(userpassword) - url = doc.createElement('RestURL') - url_text = doc.createTextNode('http://100.115.10.69:8082/' - 'deviceManager/rest/') - url.appendChild(url_text) - storage.appendChild(url) - - storagepool = doc.createElement('StoragePool') - pool_text = doc.createTextNode('OpenStack_Pool') - storagepool.appendChild(pool_text) - storage.appendChild(storagepool) - - lun = doc.createElement('LUN') - config.appendChild(lun) - storagepool = doc.createElement('StoragePool') - pool_text = doc.createTextNode('OpenStack_Pool;OpenStack_Pool2') - storagepool.appendChild(pool_text) - lun.appendChild(storagepool) - - timeout = doc.createElement('Timeout') - timeout_text = doc.createTextNode('43200') - timeout.appendChild(timeout_text) - lun.appendChild(timeout) - - lun_ready_wait_interval = doc.createElement('LUNReadyWaitInterval') - lun_ready_wait_interval_text = doc.createTextNode('2') - lun_ready_wait_interval.appendChild(lun_ready_wait_interval_text) - lun.appendChild(lun_ready_wait_interval) - - prefetch = doc.createElement('Prefetch') - prefetch.setAttribute('Type', '1') - prefetch.setAttribute('Value', '0') - lun.appendChild(prefetch) - - iscsi = doc.createElement('iSCSI') - config.appendChild(iscsi) - defaulttargetip = doc.createElement('DefaultTargetIP') - defaulttargetip_text = doc.createTextNode('100.115.10.68') - defaulttargetip.appendChild(defaulttargetip_text) - iscsi.appendChild(defaulttargetip) - initiator = doc.createElement('Initiator') - initiator.setAttribute('Name', 'iqn.1993-08.debian:01:ec2bff7ac3a3') - initiator.setAttribute('TargetIP', '192.168.1.2') - initiator.setAttribute('CHAPinfo', 'mm-user;mm-user@storage') - initiator.setAttribute('ALUA', '1') - initiator.setAttribute('TargetPortGroup', 'portgroup-test') - iscsi.appendChild(initiator) - - host = doc.createElement('Host') - host.setAttribute('HostIP', '100.97.10.30') - host.setAttribute('OSType', 'Linux') - config.appendChild(host) - - fakefile = open(self.fake_conf_file, 'w') - fakefile.write(doc.toprettyxml(indent='')) - fakefile.close() - class FCSanLookupService(object): + def get_device_mapping_from_network(self, initiator_list, target_list): return fake_fabric_mapping @@ -2272,33 +2174,31 @@ class HuaweiFCDriverTestCase(test.TestCase): def setUp(self): super(HuaweiFCDriverTestCase, self).setUp() - self.tmp_dir = tempfile.mkdtemp() - self.fake_conf_file = self.tmp_dir + '/cinder_huawei_conf.xml' - self.addCleanup(shutil.rmtree, self.tmp_dir) - self.create_fake_conf_file() - self.addCleanup(os.remove, self.fake_conf_file) self.configuration = mock.Mock(spec=conf.Configuration) - self.configuration.cinder_huawei_conf_file = self.fake_conf_file - self.xml_file_path = self.configuration.cinder_huawei_conf_file + self.huawei_conf = FakeHuaweiConf(self.configuration, 'FC') self.configuration.hypermetro_devices = hypermetro_devices self.stubs.Set(time, 'sleep', Fake_sleep) driver = FakeFCStorage(configuration=self.configuration) self.driver = driver self.driver.do_setup() - self.device_id = self.driver.restclient.login() + self.driver.client.login() def test_login_success(self): - self.assertEqual('210235G7J20000000000', self.device_id) + device_id = self.driver.client.login() + self.assertEqual('210235G7J20000000000', device_id) def test_create_volume_success(self): + lun_info = self.driver.create_volume(test_volume) self.assertEqual('1', lun_info['provider_location']) def test_delete_volume_success(self): + delete_flag = self.driver.delete_volume(test_volume) self.assertTrue(delete_flag) def test_create_snapshot_success(self): + lun_info = self.driver.create_snapshot(test_snap) self.assertEqual(11, lun_info['provider_location']) @@ -2311,106 +2211,89 @@ class HuaweiFCDriverTestCase(test.TestCase): self.assertEqual(11, lun_info['provider_location']) def test_delete_snapshot_success(self): + delete_flag = self.driver.delete_snapshot(test_snap) self.assertTrue(delete_flag) def test_create_volume_from_snapsuccess(self): + lun_info = self.driver.create_volume_from_snapshot(test_volume, test_volume) - self.assertEqual('1', lun_info['ID']) + self.assertEqual('1', lun_info['provider_location']) def test_initialize_connection_success(self): + iscsi_properties = self.driver.initialize_connection(test_volume, FakeConnector) self.assertEqual(1, iscsi_properties['data']['target_lun']) def test_terminate_connection_success(self): - self.driver.restclient.terminateFlag = True + + self.driver.client.terminateFlag = True self.driver.terminate_connection(test_volume, FakeConnector) - self.assertTrue(self.driver.restclient.terminateFlag) + self.assertTrue(self.driver.client.terminateFlag) def test_get_volume_status(self): + data = self.driver.get_volume_stats() - self.assertEqual('2.0.1', data['driver_version']) + self.assertEqual('2.0.2', data['driver_version']) def test_extend_volume(self): + lun_info = self.driver.extend_volume(test_volume, 3) self.assertEqual('1', lun_info['provider_location']) def test_login_fail(self): - self.driver.restclient.test_fail = True + self.driver.client.test_fail = True self.assertRaises(exception.VolumeBackendAPIException, - self.driver.restclient.login) + self.driver.client.login) def test_create_snapshot_fail(self): - self.driver.restclient.test_fail = True + + self.driver.client.test_fail = True self.assertRaises(exception.VolumeBackendAPIException, self.driver.create_snapshot, test_snap) def test_create_volume_fail(self): - self.driver.restclient.test_fail = True + + self.driver.client.test_fail = True self.assertRaises(exception.VolumeBackendAPIException, self.driver.create_volume, test_volume) def test_delete_volume_fail(self): - self.driver.restclient.test_fail = True - delete_flag = self.driver.delete_volume(test_volume) - self.assertTrue(delete_flag) + self.driver.client.test_fail = True + self.driver.delete_volume(test_volume) def test_delete_snapshot_fail(self): - self.driver.restclient.test_fail = True + self.driver.client.test_fail = True delete_flag = self.driver.delete_snapshot(test_snap) self.assertTrue(delete_flag) def test_initialize_connection_fail(self): - self.driver.restclient.test_fail = True + + self.driver.client.test_fail = True self.assertRaises(exception.VolumeBackendAPIException, self.driver.initialize_connection, test_volume, FakeConnector) - def test_get_default_timeout(self): - result = huawei_utils.get_default_timeout(self.xml_file_path) - self.assertEqual('43200', result) - - def test_get_wait_interval(self): - result = huawei_utils.get_wait_interval(self.xml_file_path, - 'LUNReadyWaitInterval') - self.assertEqual(2, result) - def test_lun_is_associated_to_lungroup(self): - self.driver.restclient.associate_lun_to_lungroup('11', '11') - result = self.driver.restclient._is_lun_associated_to_lungroup('11', - '11') + + self.driver.client.associate_lun_to_lungroup('11', '11') + result = self.driver.client._is_lun_associated_to_lungroup('11', + '11') self.assertTrue(result) def test_lun_is_not_associated_to_lun_group(self): - self.driver.restclient.associate_lun_to_lungroup('12', '12') - self.driver.restclient.remove_lun_from_lungroup('12', '12') - result = self.driver.restclient._is_lun_associated_to_lungroup('12', - '12') - self.assertFalse(result) - def test_get_lun_conf_params(self): - luninfo = huawei_utils.get_lun_conf_params(self.xml_file_path) - luninfo['pool_id'] = '0' - luninfo['volume_size'] = 2 - luninfo['volume_description'] = 'test volume' - luninfo = huawei_utils.init_lun_parameters('5mFHcBv4RkCcD+JyrWc0SA', - luninfo) - self.assertEqual('5mFHcBv4RkCcD+JyrWc0SA', luninfo['NAME']) - - def test_check_conf_file(self): - self.driver.restclient.checkFlag = True - huawei_utils.check_conf_file(self.xml_file_path) - self.assertTrue(self.driver.restclient.checkFlag) - - def test_get_conf_host_os_type(self): - host_os = huawei_utils.get_conf_host_os_type('100.97.10.30', - self.configuration) - self.assertEqual('0', host_os) + self.driver.client.associate_lun_to_lungroup('12', '12') + self.driver.client.remove_lun_from_lungroup('12', '12') + result = self.driver.client._is_lun_associated_to_lungroup('12', + '12') + self.assertFalse(result) @mock.patch.object(rest_client.RestClient, 'add_lun_to_partition') def test_migrate_volume_success(self, mock_add_lun_to_partition): + # Migrate volume without new type. model_update = None moved = False @@ -2443,7 +2326,8 @@ class HuaweiFCDriverTestCase(test.TestCase): self.assertEqual(empty_dict, model_update) def test_migrate_volume_fail(self): - self.driver.restclient.test_fail = True + + self.driver.client.test_fail = True # Migrate volume without new type. self.assertRaises(exception.VolumeBackendAPIException, @@ -2459,12 +2343,13 @@ class HuaweiFCDriverTestCase(test.TestCase): 'policy': '2', 'smartcache:cachename': 'cache-test', 'partitionname': 'partition-test'}} - self.driver.restclient.test_fail = True + self.driver.client.test_fail = True self.assertRaises(exception.VolumeBackendAPIException, self.driver.migrate_volume, None, test_volume, test_host, new_type) def test_check_migration_valid(self): + is_valid = self.driver._check_migration_valid(test_host, test_volume) self.assertTrue(is_valid) @@ -2522,6 +2407,7 @@ class HuaweiFCDriverTestCase(test.TestCase): @mock.patch.object(rest_client.RestClient, 'rename_lun') def test_update_migrated_volume_success(self, mock_rename_lun): + original_volume = {'id': '21ec7341-9256-497b-97d9-ef48edcf0635'} current_volume = {'id': '21ec7341-9256-497b-97d9-ef48edcf0636'} model_update = self.driver.update_migrated_volume(None, @@ -2532,6 +2418,7 @@ class HuaweiFCDriverTestCase(test.TestCase): @mock.patch.object(rest_client.RestClient, 'rename_lun') def test_update_migrated_volume_fail(self, mock_rename_lun): + mock_rename_lun.side_effect = exception.VolumeBackendAPIException( data='Error occurred.') original_volume = {'id': '21ec7341-9256-497b-97d9-ef48edcf0635'} @@ -2546,24 +2433,28 @@ class HuaweiFCDriverTestCase(test.TestCase): @mock.patch.object(rest_client.RestClient, 'add_lun_to_partition') def test_retype_volume_success(self, mock_add_lun_to_partition): + retype = self.driver.retype(None, test_volume, test_new_type, None, test_host) self.assertTrue(retype) def test_retype_volume_cache_fail(self): - self.driver.restclient.cache_not_exist = True + self.driver.client.cache_not_exist = True + self.assertRaises(exception.VolumeBackendAPIException, self.driver.retype, None, test_volume, test_new_type, None, test_host) def test_retype_volume_partition_fail(self): - self.driver.restclient.partition_not_exist = True + self.driver.client.partition_not_exist = True + self.assertRaises(exception.VolumeBackendAPIException, self.driver.retype, None, test_volume, test_new_type, None, test_host) @mock.patch.object(rest_client.RestClient, 'add_lun_to_partition') def test_retype_volume_fail(self, mock_add_lun_to_partition): + mock_add_lun_to_partition.side_effect = ( exception.VolumeBackendAPIException(data='Error occurred.')) retype = self.driver.retype(None, test_volume, @@ -2571,12 +2462,13 @@ class HuaweiFCDriverTestCase(test.TestCase): self.assertFalse(retype) def test_build_ini_targ_map(self): + fake_lookup_service = FCSanLookupService() fake_lookup_service.get_device_mapping_from_network = mock.Mock( return_value=fake_fabric_mapping) zone_helper = fc_zone_helper.FCZoneHelper( - fake_lookup_service, self.driver.restclient) + fake_lookup_service, self.driver.client) (tgt_port_wwns, init_targ_map) = (zone_helper.build_ini_targ_map( ['10000090fa0d6754'])) @@ -2586,6 +2478,7 @@ class HuaweiFCDriverTestCase(test.TestCase): self.assertEqual(ini_target_map, init_targ_map) def test_filter_port_by_contr(self): + # Six ports in one fabric. ports_in_fabric = ['1', '2', '3', '4', '5', '6'] # Ports 1,3,4,7 belonged to controller A @@ -2601,57 +2494,57 @@ class HuaweiFCDriverTestCase(test.TestCase): self.assertEqual(expected_filtered_ports, filtered_ports) def test_multi_resturls_success(self): - self.driver.restclient.test_multi_url_flag = True + + self.driver.client.test_multi_url_flag = True lun_info = self.driver.create_volume(test_volume) self.assertEqual('1', lun_info['provider_location']) def test_get_id_from_result(self): + result = {} name = 'test_name' key = 'NAME' - re = self.driver.restclient._get_id_from_result(result, name, key) + re = self.driver.client._get_id_from_result(result, name, key) self.assertIsNone(re) result = {'data': {}} - re = self.driver.restclient._get_id_from_result(result, name, key) + re = self.driver.client._get_id_from_result(result, name, key) self.assertIsNone(re) result = {'data': [{'COUNT': 1, 'ID': '1'}, {'COUNT': 2, 'ID': '2'}]} - re = self.driver.restclient._get_id_from_result(result, name, key) + re = self.driver.client._get_id_from_result(result, name, key) self.assertIsNone(re) result = {'data': [{'NAME': 'test_name1', 'ID': '1'}, {'NAME': 'test_name2', 'ID': '2'}]} - re = self.driver.restclient._get_id_from_result(result, name, key) + re = self.driver.client._get_id_from_result(result, name, key) self.assertIsNone(re) result = {'data': [{'NAME': 'test_name', 'ID': '1'}, {'NAME': 'test_name2', 'ID': '2'}]} - re = self.driver.restclient._get_id_from_result(result, name, key) + re = self.driver.client._get_id_from_result(result, name, key) self.assertEqual('1', re) - @mock.patch.object(rest_client.RestClient, 'find_pool_info', + @mock.patch.object(rest_client.RestClient, 'get_pool_info', return_value={'ID': 1, 'CAPACITY': 110362624, 'TOTALCAPACITY': 209715200}) - def test_get_capacity(self, mock_find_pool_info): + def test_get_capacity(self, mock_get_pool_info): expected_pool_capacity = {'total_capacity': 100.0, 'free_capacity': 52.625} - pool_capacity = self.driver.restclient._get_capacity(None, - None) + pool_capacity = self.driver.client._get_capacity(None, + None) self.assertEqual(expected_pool_capacity, pool_capacity) - @mock.patch.object(huawei_utils, 'get_volume_params', + @mock.patch.object(huawei_driver.HuaweiBaseDriver, '_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', + @mock.patch.object(rest_client.RestClient, 'get_all_pools', return_value=FAKE_STORAGE_POOL_RESPONSE) - @mock.patch.object(rest_client.RestClient, 'find_pool_info', + @mock.patch.object(rest_client.RestClient, 'get_pool_info', return_value=FAKE_FIND_POOL_RESPONSE) - @mock.patch.object(rest_client.RestClient, 'create_volume', + @mock.patch.object(rest_client.RestClient, 'create_lun', return_value=FAKE_CREATE_VOLUME_RESPONSE) @mock.patch.object(rest_client.RestClient, 'get_hyper_domain_id', return_value='11') @@ -2670,20 +2563,31 @@ class HuaweiFCDriverTestCase(test.TestCase): mock_create_volume, mock_hyper_domain, mock_volume_ready, - mock_pair_info, mock_logout): + 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): - """Create a fake Config file - Huawei storage customize a XML configuration file, - the configuration file is used to set the Huawei storage custom - parameters, therefore, in the UT test we need to simulate such a - configuration file +class HuaweiConfTestCase(test.TestCase): + def setUp(self): + super(HuaweiConfTestCase, self).setUp() + + self.tmp_dir = tempfile.mkdtemp() + self.fake_xml_file = self.tmp_dir + '/cinder_huawei_conf.xml' + + self.conf = mock.Mock() + self.conf.cinder_huawei_conf_file = self.fake_xml_file + self.huawei_conf = huawei_conf.HuaweiConf(self.conf) + + def _create_fake_conf_file(self): + """Create a fake Config file. + + Huawei storage customize a XML configuration file, the configuration + file is used to set the Huawei storage custom parameters, therefore, + in the UT test we need to simulate such a configuration file. """ doc = minidom.Document() @@ -2692,60 +2596,62 @@ class HuaweiFCDriverTestCase(test.TestCase): storage = doc.createElement('Storage') config.appendChild(storage) - controllerip0 = doc.createElement('ControllerIP0') - controllerip0_text = doc.createTextNode('10.10.10.1') - controllerip0.appendChild(controllerip0_text) - storage.appendChild(controllerip0) - controllerip1 = doc.createElement('ControllerIP1') - controllerip1_text = doc.createTextNode('10.10.10.2') - controllerip1.appendChild(controllerip1_text) - storage.appendChild(controllerip1) + url = doc.createElement('RestURL') + url_text = doc.createTextNode('http://100.115.10.69:8082/' + 'deviceManager/rest/') + url.appendChild(url_text) + storage.appendChild(url) username = doc.createElement('UserName') username_text = doc.createTextNode('admin') username.appendChild(username_text) storage.appendChild(username) - userpassword = doc.createElement('UserPassword') - userpassword_text = doc.createTextNode('Admin@storage') - userpassword.appendChild(userpassword_text) - storage.appendChild(userpassword) - + password = doc.createElement('UserPassword') + password_text = doc.createTextNode('Admin@storage') + password.appendChild(password_text) + storage.appendChild(password) + product = doc.createElement('Product') + product_text = doc.createTextNode('V3') + product.appendChild(product_text) + storage.appendChild(product) protocol = doc.createElement('Protocol') - protocol_text = doc.createTextNode('FC') + protocol_text = doc.createTextNode('iSCSI') protocol.appendChild(protocol_text) storage.appendChild(protocol) - url = doc.createElement('RestURL') - url_text = doc.createTextNode('http://100.115.10.69:8082/' - 'deviceManager/rest/') - url.appendChild(url_text) - storage.appendChild(url) - - storagepool = doc.createElement('StoragePool') - pool_text = doc.createTextNode('OpenStack_Pool') - storagepool.appendChild(pool_text) - storage.appendChild(storagepool) - lun = doc.createElement('LUN') config.appendChild(lun) - storagepool = doc.createElement('StoragePool') - pool_text = doc.createTextNode('OpenStack_Pool;OpenStack_Pool2') - storagepool.appendChild(pool_text) - lun.appendChild(storagepool) - - lun_type = doc.createElement('LUNType') - lun_type_text = doc.createTextNode('Thick') - lun_type.appendChild(lun_type_text) - lun.appendChild(lun_type) - - timeout = doc.createElement('Timeout') - timeout_text = doc.createTextNode('43200') - timeout.appendChild(timeout_text) - lun.appendChild(timeout) - + luntype = doc.createElement('LUNType') + luntype_text = doc.createTextNode('Thick') + luntype.appendChild(luntype_text) + lun.appendChild(luntype) lun_ready_wait_interval = doc.createElement('LUNReadyWaitInterval') lun_ready_wait_interval_text = doc.createTextNode('2') lun_ready_wait_interval.appendChild(lun_ready_wait_interval_text) lun.appendChild(lun_ready_wait_interval) + lun_copy_wait_interval = doc.createElement('LUNcopyWaitInterval') + lun_copy_wait_interval_text = doc.createTextNode('2') + lun_copy_wait_interval.appendChild(lun_copy_wait_interval_text) + lun.appendChild(lun_copy_wait_interval) + timeout = doc.createElement('Timeout') + timeout_text = doc.createTextNode('43200') + timeout.appendChild(timeout_text) + lun.appendChild(timeout) + write_type = doc.createElement('WriteType') + write_type_text = doc.createTextNode('1') + write_type.appendChild(write_type_text) + lun.appendChild(write_type) + mirror_switch = doc.createElement('MirrorSwitch') + mirror_switch_text = doc.createTextNode('1') + mirror_switch.appendChild(mirror_switch_text) + lun.appendChild(mirror_switch) + prefetch = doc.createElement('Prefetch') + prefetch.setAttribute('Type', '1') + prefetch.setAttribute('Value', '0') + lun.appendChild(prefetch) + pool = doc.createElement('StoragePool') + pool_text = doc.createTextNode('OpenStack_Pool') + pool.appendChild(pool_text) + lun.appendChild(pool) iscsi = doc.createElement('iSCSI') config.appendChild(iscsi) @@ -2755,19 +2661,12 @@ class HuaweiFCDriverTestCase(test.TestCase): iscsi.appendChild(defaulttargetip) initiator = doc.createElement('Initiator') initiator.setAttribute('Name', 'iqn.1993-08.debian:01:ec2bff7ac3a3') - initiator.setAttribute('TargetIP', '192.168.1.2') + initiator.setAttribute('TargetIP', '192.168.100.2') + initiator.setAttribute('CHAPinfo', 'mm-user;mm-user@storage') + initiator.setAttribute('ALUA', '1') + initiator.setAttribute('TargetPortGroup', 'PortGroup001') iscsi.appendChild(initiator) - prefetch = doc.createElement('Prefetch') - prefetch.setAttribute('Type', '1') - prefetch.setAttribute('Value', '0') - lun.appendChild(prefetch) - - host = doc.createElement('Host') - host.setAttribute('HostIP', '100.97.10.30') - host.setAttribute('OSType', 'Linux') - config.appendChild(host) - - fakefile = open(self.fake_conf_file, 'w') + fakefile = open(self.conf.cinder_huawei_conf_file, 'w') fakefile.write(doc.toprettyxml(indent='')) fakefile.close() diff --git a/cinder/volume/drivers/huawei/constants.py b/cinder/volume/drivers/huawei/constants.py index eb64b24e3..213ae59ef 100644 --- a/cinder/volume/drivers/huawei/constants.py +++ b/cinder/volume/drivers/huawei/constants.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015 Huawei Technologies Co., Ltd. +# Copyright (c) 2016 Huawei Technologies Co., Ltd. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may diff --git a/cinder/volume/drivers/huawei/fc_zone_helper.py b/cinder/volume/drivers/huawei/fc_zone_helper.py index 64af9875e..4f93c5e62 100644 --- a/cinder/volume/drivers/huawei/fc_zone_helper.py +++ b/cinder/volume/drivers/huawei/fc_zone_helper.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015 Huawei Technologies Co., Ltd. +# Copyright (c) 2016 Huawei Technologies Co., Ltd. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -23,14 +23,14 @@ LOG = logging.getLogger(__name__) class FCZoneHelper(object): """FC zone helper for Huawei driver.""" - def __init__(self, fcsan_lookup_service, restclient): + def __init__(self, fcsan_lookup_service, client): self.fcsan_lookup_service = fcsan_lookup_service - self.restclient = restclient + self.client = client def _get_fc_port_contr_map(self): port_list = [] port_contr_map = {} - data = self.restclient.get_fc_ports_on_array() + data = self.client.get_fc_ports_on_array() for item in data: if item['RUNNINGSTATUS'] == constants.FC_PORT_CONNECTED: port_list.append(item['WWN']) diff --git a/cinder/volume/drivers/huawei/huawei_conf.py b/cinder/volume/drivers/huawei/huawei_conf.py new file mode 100644 index 000000000..06a8ef499 --- /dev/null +++ b/cinder/volume/drivers/huawei/huawei_conf.py @@ -0,0 +1,254 @@ +# Copyright (c) 2016 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. + +""" +Set Huawei private configuration into Configuration object. + +For conveniently get private configuration. We parse Huawei config file +and set every property into Configuration object as an attribute. +""" + +import base64 +import six +from xml.etree import ElementTree as ET + +from oslo_log import log as logging + +from cinder import exception +from cinder.i18n import _ +from cinder import utils +from cinder.volume.drivers.huawei import constants + +LOG = logging.getLogger(__name__) + + +class HuaweiConf(object): + def __init__(self, conf): + self.conf = conf + + def _encode_authentication(self): + need_encode = False + tree = ET.parse(self.conf.cinder_huawei_conf_file) + xml_root = tree.getroot() + name_node = xml_root.find('Storage/UserName') + pwd_node = xml_root.find('Storage/UserPassword') + if (name_node is not None + and not name_node.text.startswith('!$$$')): + name_node.text = '!$$$' + base64.b64encode(name_node.text) + need_encode = True + if (pwd_node is not None + and not pwd_node.text.startswith('!$$$')): + pwd_node.text = '!$$$' + base64.b64encode(pwd_node.text) + need_encode = True + + if need_encode: + utils.execute('chmod', + '600', + self.conf.cinder_huawei_conf_file, + run_as_root=True) + tree.write(self.conf.cinder_huawei_conf_file, 'UTF-8') + + def update_config_value(self): + self._encode_authentication() + + set_attr_funcs = (self._san_address, + self._san_user, + self._san_password, + self._san_product, + self._san_protocol, + self._lun_type, + self._lun_ready_wait_interval, + self._lun_copy_wait_interval, + self._lun_timeout, + self._lun_write_type, + self._lun_mirror_switch, + self._lun_prefetch, + self._lun_policy, + self._lun_read_cache_policy, + self._lun_write_cache_policy, + self._storage_pools, + self._iscsi_default_target_ip, + self._iscsi_info,) + + tree = ET.parse(self.conf.cinder_huawei_conf_file) + xml_root = tree.getroot() + for f in set_attr_funcs: + f(xml_root) + + def _san_address(self, xml_root): + text = xml_root.findtext('Storage/RestURL') + if not text: + msg = _("RestURL is not configured.") + LOG.error(msg) + raise exception.InvalidInput(reason=msg) + + addrs = text.split(';') + addrs = list(set([x.strip() for x in addrs if x.strip()])) + setattr(self.conf, 'san_address', addrs) + + def _san_user(self, xml_root): + text = xml_root.findtext('Storage/UserName') + if not text: + msg = _("UserName is not configured.") + LOG.error(msg) + raise exception.InvalidInput(reason=msg) + + user = base64.b64decode(text[4:]) + setattr(self.conf, 'san_user', user) + + def _san_password(self, xml_root): + text = xml_root.findtext('Storage/UserPassword') + if not text: + msg = _("UserPassword is not configured.") + LOG.error(msg) + raise exception.InvalidInput(reason=msg) + + pwd = base64.b64decode(text[4:]) + setattr(self.conf, 'san_password', pwd) + + def _san_product(self, xml_root): + text = xml_root.findtext('Storage/Product') + if not text: + msg = _("SAN product is not configured.") + LOG.error(msg) + raise exception.InvalidInput(reason=msg) + + product = text.strip() + setattr(self.conf, 'san_product', product) + + def _san_protocol(self, xml_root): + text = xml_root.findtext('Storage/Protocol') + if not text: + msg = _("SAN protocol is not configured.") + LOG.error(msg) + raise exception.InvalidInput(reason=msg) + + protocol = text.strip() + setattr(self.conf, 'san_protocol', protocol) + + def _lun_type(self, xml_root): + lun_type = constants.THICK_LUNTYPE + + text = xml_root.findtext('LUN/LUNType') + if text: + lun_type = text.strip() + if lun_type == 'Thick': + lun_type = constants.THICK_LUNTYPE + elif lun_type == 'Thin': + lun_type = constants.THIN_LUNTYPE + else: + msg = (_("Invalid lun type %s is configured.") % lun_type) + LOG.exception(msg) + + raise exception.InvalidInput(reason=msg) + + setattr(self.conf, 'lun_type', lun_type) + + def _lun_ready_wait_interval(self, xml_root): + text = xml_root.findtext('LUN/LUNReadyWaitInterval') + interval = text.strip() if text else constants.DEFAULT_WAIT_INTERVAL + setattr(self.conf, 'lun_ready_wait_interval', int(interval)) + + def _lun_copy_wait_interval(self, xml_root): + text = xml_root.findtext('LUN/LUNcopyWaitInterval') + interval = text.strip() if text else constants.DEFAULT_WAIT_INTERVAL + setattr(self.conf, 'lun_copy_wait_interval', int(interval)) + + def _lun_timeout(self, xml_root): + text = xml_root.findtext('LUN/Timeout') + interval = text.strip() if text else constants.DEFAULT_WAIT_TIMEOUT + setattr(self.conf, 'lun_timeout', int(interval)) + + def _lun_write_type(self, xml_root): + text = xml_root.findtext('LUN/WriteType') + write_type = text.strip() if text else '1' + setattr(self.conf, 'lun_write_type', write_type) + + def _lun_mirror_switch(self, xml_root): + text = xml_root.findtext('LUN/MirrorSwitch') + mirror_switch = text.strip() if text else '1' + setattr(self.conf, 'lun_mirror_switch', mirror_switch) + + def _lun_prefetch(self, xml_root): + prefetch_type = '3' + prefetch_value = '0' + + node = xml_root.find('LUN/Prefetch') + if (node is not None + and node.attrib['Type'] + and node.attrib['Value']): + prefetch_type = node.attrib['Type'].strip() + if prefetch_type not in ['0', '1', '2', '3']: + msg = (_( + "Invalid prefetch type '%s' is configured. " + "PrefetchType must be in 0,1,2,3.") % prefetch_type) + LOG.error(msg) + raise exception.InvalidInput(reason=msg) + + prefetch_value = node.attrib['Value'].strip() + factor = {'1': 2, '2': 2} + factor = int(factor.get('prefetch_type', '1')) + prefetch_value = int(prefetch_value) * factor + prefetch_value = six.text_type(prefetch_value) + + setattr(self.conf, 'lun_prefetch_type', prefetch_type) + setattr(self.conf, 'lun_prefetch_value', prefetch_value) + + def _lun_policy(self, xml_root): + setattr(self.conf, 'lun_policy', '0') + + def _lun_read_cache_policy(self, xml_root): + setattr(self.conf, 'lun_read_cache_policy', '2') + + def _lun_write_cache_policy(self, xml_root): + setattr(self.conf, 'lun_write_cache_policy', '5') + + def _storage_pools(self, xml_root): + nodes = xml_root.findall('LUN/StoragePool') + if not nodes: + msg = _('Storage pool is not configured.') + LOG.error(msg) + raise exception.InvalidInput(reason=msg) + + texts = [x.text for x in nodes] + merged_text = ';'.join(texts) + pools = set(x.strip() for x in merged_text.split(';') if x.strip()) + if not pools: + msg = _('Invalid storage pool is configured.') + LOG.error(msg) + raise exception.InvalidInput(msg) + + setattr(self.conf, 'storage_pools', list(pools)) + + def _iscsi_default_target_ip(self, xml_root): + text = xml_root.findtext('iSCSI/DefaultTargetIP') + target_ip = text.split() if text else [] + setattr(self.conf, 'iscsi_default_target_ip', target_ip) + + def _iscsi_info(self, xml_root): + nodes = xml_root.findall('iSCSI/Initiator') + if nodes is None: + setattr(self.conf, 'iscsi_info', []) + return + + iscsi_info = [] + for node in nodes: + props = {} + for item in node.items(): + props[item[0].strip()] = item[1].strip() + + iscsi_info.append(props) + + setattr(self.conf, 'iscsi_info', iscsi_info) diff --git a/cinder/volume/drivers/huawei/huawei_driver.py b/cinder/volume/drivers/huawei/huawei_driver.py index 3d5497a2a..5b541a8a7 100644 --- a/cinder/volume/drivers/huawei/huawei_driver.py +++ b/cinder/volume/drivers/huawei/huawei_driver.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015 Huawei Technologies Co., Ltd. +# Copyright (c) 2016 Huawei Technologies Co., Ltd. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -23,12 +23,14 @@ from oslo_log import log as logging from oslo_utils import excutils from oslo_utils import units +from cinder import context from cinder import exception from cinder.i18n import _, _LE, _LI, _LW from cinder import utils from cinder.volume import driver from cinder.volume.drivers.huawei import constants from cinder.volume.drivers.huawei import fc_zone_helper +from cinder.volume.drivers.huawei import huawei_conf from cinder.volume.drivers.huawei import huawei_utils from cinder.volume.drivers.huawei import hypermetro from cinder.volume.drivers.huawei import rest_client @@ -44,6 +46,7 @@ huawei_opts = [ default='/etc/cinder/cinder_huawei_conf.xml', help='The configuration file for the Cinder Huawei driver.'), cfg.StrOpt('hypermetro_devices', + default=None, help='The remote device hypermetro will use.'), ] @@ -55,111 +58,218 @@ class HuaweiBaseDriver(driver.VolumeDriver): def __init__(self, *args, **kwargs): super(HuaweiBaseDriver, self).__init__(*args, **kwargs) - self.configuration = kwargs.get('configuration') + if not self.configuration: - msg = _('_instantiate_driver: configuration not found.') + msg = _('Configuration is not found.') raise exception.InvalidInput(reason=msg) self.configuration.append_config_values(huawei_opts) - self.xml_file_path = self.configuration.cinder_huawei_conf_file - self.hypermetro_devices = self.configuration.hypermetro_devices + self.huawei_conf = huawei_conf.HuaweiConf(self.configuration) + self.metro_flag = False def do_setup(self, context): """Instantiate common class and login storage system.""" - self.restclient = rest_client.RestClient(self.configuration) - return self.restclient.login() + # Set huawei private configuration into Configuration object. + self.huawei_conf.update_config_value() + # init local client + self.client = rest_client.RestClient(self.configuration, + self.configuration.san_address, + self.configuration.san_user, + self.configuration.san_password) + self.client.login() + # init remote client + metro_san_address = self.configuration.safe_get("metro_san_address") + metro_san_user = self.configuration.safe_get("metro_san_user") + metro_san_password = self.configuration.safe_get("metro_san_password") + if metro_san_address and metro_san_user and metro_san_password: + self.metro_flag = True + metro_san_address = metro_san_address.split(";") + self.rmt_client = rest_client.RestClient(self.configuration, + metro_san_address, + metro_san_user, + metro_san_password) + self.rmt_client.login() def check_for_setup_error(self): - """Check configuration file.""" - return huawei_utils.check_conf_file(self.xml_file_path) + pass def get_volume_stats(self, refresh=False): - """Get volume status.""" - return self.restclient.update_volume_stats() + """Get volume status and reload huawei config file.""" + self.huawei_conf.update_config_value() + if self.metro_flag: + self.rmt_client.get_all_pools() + return self.client.update_volume_stats() - @utils.synchronized('huawei', external=True) - def create_volume(self, volume): - """Create a volume.""" - opts = huawei_utils.get_volume_params(volume) - smartx_opts = smartx.SmartX().get_smartx_specs_opts(opts) - params = huawei_utils.get_lun_params(self.xml_file_path, - smartx_opts) - pool_name = volume_utils.extract_host(volume['host'], - level='pool') - pools = self.restclient.find_all_pools() - pool_info = self.restclient.find_pool_info(pool_name, pools) - if not pool_info: - # The following code is to keep compatibility with old version of - # Huawei driver. - pool_names = huawei_utils.get_pools(self.xml_file_path) - for pool_name in pool_names.split(";"): - pool_info = self.restclient.find_pool_info(pool_name, - pools) - if pool_info: - break + def _get_volume_type(self, volume): + volume_type = None + type_id = volume['volume_type_id'] + if type_id: + ctxt = context.get_admin_context() + volume_type = volume_types.get_volume_type(ctxt, type_id) + + return volume_type + + def _get_volume_params(self, volume_type): + """Return the parameters for creating the volume.""" + specs = {} + if volume_type: + specs = dict(volume_type).get('extra_specs') + + opts = self._get_volume_params_from_specs(specs) + return opts + + def _get_volume_params_from_specs(self, specs): + """Return the volume parameters from extra specs.""" + opts_capability = { + 'smarttier': False, + 'smartcache': False, + 'smartpartition': False, + 'thin_provisioning_support': False, + 'thick_provisioning_support': False, + 'hypermetro': False, + } - volume_name = huawei_utils.encode_name(volume['id']) - volume_description = volume['name'] - volume_size = huawei_utils.get_volume_size(volume) + opts_value = { + 'policy': None, + 'partitionname': None, + 'cachename': None, + } - LOG.info(_LI( - 'Create volume: %(volume)s, size: %(size)s.'), - {'volume': volume_name, - 'size': volume_size}) + opts_associate = { + 'smarttier': 'policy', + 'smartcache': 'cachename', + 'smartpartition': 'partitionname', + } - params['pool_id'] = pool_info['ID'] - params['volume_size'] = volume_size - params['volume_description'] = volume_description + opts = self._get_opts_from_specs(opts_capability, + opts_value, + opts_associate, + specs) + opts = smartx.SmartX().get_smartx_specs_opts(opts) + LOG.debug('volume opts %(opts)s.', {'opts': opts}) + return opts + + def _get_opts_from_specs(self, opts_capability, opts_value, + opts_associate, specs): + """Get the well defined extra specs.""" + opts = {} + opts.update(opts_capability) + opts.update(opts_value) + + for key, value in specs.items(): + # Get the scope, if is using scope format. + scope = None + key_split = key.split(':') + if len(key_split) > 2 and key_split[0] != "capabilities": + continue + + if len(key_split) == 1: + key = key_split[0].lower() + else: + scope = key_split[0].lower() + key = key_split[1].lower() + + if ((not scope or scope == 'capabilities') + and key in opts_capability): + words = value.split() + if not (words and len(words) == 2 and words[0] == ''): + LOG.error(_LE("Extra specs must be specified as " + "capabilities:%s=' True' or " + "' true'."), key) + else: + opts[key] = words[1].lower() + + if ((scope in opts_capability) + and (key in opts_value) + and (scope in opts_associate) + and (opts_associate[scope] == key)): + opts[key] = value + + return opts + + def _get_lun_params(self, volume, opts): + pool_name = volume_utils.extract_host(volume['host'], level='pool') + params = { + 'TYPE': '11', + 'NAME': huawei_utils.encode_name(volume['id']), + 'PARENTTYPE': '216', + 'PARENTID': self.client.get_pool_id(volume, pool_name), + 'DESCRIPTION': volume['name'], + 'ALLOCTYPE': opts.get('LUNType', self.configuration.lun_type), + 'CAPACITY': huawei_utils.get_volume_size(volume), + 'WRITEPOLICY': self.configuration.lun_write_type, + 'MIRRORPOLICY': self.configuration.lun_mirror_switch, + 'PREFETCHPOLICY': self.configuration.lun_prefetch_type, + 'PREFETCHVALUE': self.configuration.lun_prefetch_value, + 'DATATRANSFERPOLICY': + opts.get('policy', self.configuration.lun_policy), + 'READCACHEPOLICY': self.configuration.lun_read_cache_policy, + 'WRITECACHEPOLICY': self.configuration.lun_write_cache_policy, } + + LOG.info(_LI('volume: %(volume)s, lun params: %(params)s.'), + {'volume': volume['id'], 'params': params}) + return params + + def _create_volume(self, volume, lun_params): + # Create LUN on the array. + model_update = {} + lun_info = self.client.create_lun(lun_params) + model_update['provider_location'] = lun_info['ID'] - # Prepare LUN parameters. - lun_param = huawei_utils.init_lun_parameters(volume_name, params) + metadata = huawei_utils.get_volume_metadata(volume) + model_update['metadata'] = metadata + return lun_info, model_update - # Create LUN on the array. - lun_info = self.restclient.create_volume(lun_param) + def create_volume(self, volume): + """Create a volume.""" + volume_type = self._get_volume_type(volume) + opts = self._get_volume_params(volume_type) + lun_params = self._get_lun_params(volume, opts) + lun_info, model_update = self._create_volume(volume, lun_params) lun_id = lun_info['ID'] try: - qos = huawei_utils.get_volume_qos(volume) + qos = smartx.SmartQos.get_qos_by_volume_type(volume_type) if qos: - smart_qos = smartx.SmartQos(self.restclient) - smart_qos.create_qos(qos, lun_id) - smartpartition = smartx.SmartPartition(self.restclient) + smart_qos = smartx.SmartQos(self.client) + smart_qos.add(qos, lun_id) + + smartpartition = smartx.SmartPartition(self.client) smartpartition.add(opts, lun_id) - smartcache = smartx.SmartCache(self.restclient) + smartcache = smartx.SmartCache(self.client) smartcache.add(opts, lun_id) except Exception as err: self._delete_lun_with_check(lun_id) - raise exception.InvalidInput( - reason=_('Create volume error. Because %s.') % err) + msg = _('Create volume error. Because %s.') % six.text_type(err) + raise exception.VolumeBackendAPIException(data=msg) - # 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) + if (opts.get('hypermetro') and opts.get('hypermetro') == 'true'): + metro = hypermetro.HuaweiHyperMetro(self.client, + self.rmt_client, + self.configuration, + self.db) try: - metro_id, remote_lun_id = hyperm.create_hypermetro(lun_id, - lun_param) + metro_info = metro.create_hypermetro(lun_id, lun_params) + model_update['metadata'].update(metro_info) except exception.VolumeBackendAPIException as err: - LOG.exception(_LE('Create hypermetro error: %s.'), err) + LOG.error(_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}) + return model_update - metadata.update({'hypermetro_id': metro_id, - 'remote_lun_id': remote_lun_id}) + def _delete_volume(self, volume): + lun_id = volume.get('provider_location') + if not lun_id: + return - return {'provider_location': lun_id, - 'ID': lun_id, - 'metadata': metadata} + lun_group_ids = self.client.get_lungroupids_by_lunid(lun_id) + if lun_group_ids and len(lun_group_ids) == 1: + self.client.remove_lun_from_lungroup(lun_group_ids[0], lun_id) + + self.client.delete_lun(lun_id) - @utils.synchronized('huawei', external=True) def delete_volume(self, volume): """Delete a volume. @@ -168,68 +278,61 @@ class HuaweiBaseDriver(driver.VolumeDriver): Secondly, remove associate from QoS policy. Thirdly, remove the lun. """ - name = huawei_utils.encode_name(volume['id']) lun_id = volume.get('provider_location') - LOG.info(_LI('Delete volume: %(name)s, array lun id: %(lun_id)s.'), - {'name': name, 'lun_id': lun_id},) - if lun_id: - if self.restclient.check_lun_exist(lun_id): - qos_id = self.restclient.get_qosid_by_lunid(lun_id) - if 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) - else: + if not lun_id or not self.client.check_lun_exist(lun_id): LOG.warning(_LW("Can't find lun %s on the array."), lun_id) return False - return True + qos_id = self.client.get_qosid_by_lunid(lun_id) + if qos_id: + smart_qos = smartx.SmartQos(self.client) + smart_qos.remove(qos_id, lun_id) - def remove_qos_lun(self, lun_id, qos_id): - lun_list = self.restclient.get_lun_list_in_qos(qos_id) - lun_count = len(lun_list) - if lun_count <= 1: - qos = smartx.SmartQos(self.restclient) - qos.delete_qos(qos_id) - else: - self.restclient.remove_lun_from_qos(lun_id, - lun_list, - qos_id) + metadata = huawei_utils.get_volume_metadata(volume) + if 'hypermetro_id' in metadata: + metro = hypermetro.HuaweiHyperMetro(self.client, + self.rmt_client, + self.configuration, + self.db) + try: + metro.delete_hypermetro(volume) + except exception.VolumeBackendAPIException as err: + LOG.error(_LE('Delete hypermetro error: %s.'), err) + self._delete_volume(volume) + raise + + self._delete_volume(volume) + + return True def _delete_lun_with_check(self, lun_id): - if lun_id: - if self.restclient.check_lun_exist(lun_id): - qos_id = self.restclient.get_qosid_by_lunid(lun_id) - if qos_id: - self.remove_qos_lun(lun_id, qos_id) + if not lun_id: + return - self.restclient.delete_lun(lun_id) + if self.client.check_lun_exist(lun_id): + qos_id = self.client.get_qosid_by_lunid(lun_id) + if qos_id: + smart_qos = smartx.SmartQos(self.client) + smart_qos.remove(qos_id, lun_id) + + self.client.delete_lun(lun_id) def _is_lun_migration_complete(self, src_id, dst_id): - result = self.restclient.get_lun_migration_task() + result = self.client.get_lun_migration_task() found_migration_task = False - if 'data' in result: - for item in result['data']: - if (src_id == item['PARENTID'] - and dst_id == item['TARGETLUNID']): - found_migration_task = True - if constants.MIGRATION_COMPLETE == item['RUNNINGSTATUS']: - return True - if constants.MIGRATION_FAULT == item['RUNNINGSTATUS']: - err_msg = _("Lun migration error.") - LOG.error(err_msg) - raise exception.VolumeBackendAPIException(data=err_msg) + if 'data' not in result: + return False + + for item in result['data']: + if (src_id == item['PARENTID'] and dst_id == item['TARGETLUNID']): + found_migration_task = True + if constants.MIGRATION_COMPLETE == item['RUNNINGSTATUS']: + return True + if constants.MIGRATION_FAULT == item['RUNNINGSTATUS']: + msg = _("Lun migration error.") + LOG.error(msg) + raise exception.VolumeBackendAPIException(data=msg) + if not found_migration_task: err_msg = _("Cannot find migration task.") LOG.error(err_msg) @@ -239,10 +342,11 @@ class HuaweiBaseDriver(driver.VolumeDriver): def _is_lun_migration_exist(self, src_id, dst_id): try: - result = self.restclient.get_lun_migration_task() + result = self.client.get_lun_migration_task() except Exception: LOG.error(_LE("Get LUN migration error.")) return False + if 'data' in result: for item in result['data']: if (src_id == item['PARENTID'] @@ -252,57 +356,51 @@ class HuaweiBaseDriver(driver.VolumeDriver): def _migrate_lun(self, src_id, dst_id): try: - self.restclient.create_lun_migration(src_id, dst_id) + self.client.create_lun_migration(src_id, dst_id) def _is_lun_migration_complete(): return self._is_lun_migration_complete(src_id, dst_id) wait_interval = constants.MIGRATION_WAIT_INTERVAL - huawei_utils.wait_for_condition(self.xml_file_path, - _is_lun_migration_complete, - wait_interval) + huawei_utils.wait_for_condition(_is_lun_migration_complete, + wait_interval, + self.configuration.lun_timeout) # Clean up if migration failed. except Exception as ex: raise exception.VolumeBackendAPIException(data=ex) finally: if self._is_lun_migration_exist(src_id, dst_id): - self.restclient.delete_lun_migration(src_id, dst_id) + self.client.delete_lun_migration(src_id, dst_id) self._delete_lun_with_check(dst_id) LOG.debug("Migrate lun %s successfully.", src_id) return True def _wait_volume_ready(self, lun_id): - event_type = 'LUNReadyWaitInterval' - wait_interval = huawei_utils.get_wait_interval(self.xml_file_path, - event_type) + wait_interval = self.configuration.lun_ready_wait_interval def _volume_ready(): - result = self.restclient.get_lun_info(lun_id) + result = self.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, + huawei_utils.wait_for_condition(_volume_ready, wait_interval, wait_interval * 10) def _get_original_status(self, volume): - if not volume['volume_attachment']: - return 'available' - else: - return 'in-use' + return 'in-use' if volume.get('volume_attachment') else 'available' def update_migrated_volume(self, ctxt, volume, new_volume, original_volume_status=None): original_name = huawei_utils.encode_name(volume['id']) current_name = huawei_utils.encode_name(new_volume['id']) - lun_id = self.restclient.get_volume_by_name(current_name) + lun_id = self.client.get_lun_id_by_name(current_name) try: - self.restclient.rename_lun(lun_id, original_name) + self.client.rename_lun(lun_id, original_name) except exception.VolumeBackendAPIException: LOG.error(_LE('Unable to rename lun %s on array.'), current_name) return {'_name_id': new_volume['_name_id'] or new_volume['id']} @@ -327,11 +425,11 @@ class HuaweiBaseDriver(driver.VolumeDriver): target_device = host['capabilities']['location_info'] # Source and destination should be on same array. - if target_device != self.restclient.device_id: + if target_device != self.client.device_id: return False # Same protocol should be used if volume is in-use. - protocol = huawei_utils.get_protocol(self.xml_file_path) + protocol = self.configuration.san_protocol if (host['capabilities']['storage_protocol'] != protocol and self._get_original_status(volume) == 'in-use'): return False @@ -353,47 +451,59 @@ class HuaweiBaseDriver(driver.VolumeDriver): volume_type = volume_types.get_volume_type(None, type_id) pool_name = host['capabilities']['pool_name'] - pools = self.restclient.find_all_pools() - pool_info = self.restclient.find_pool_info(pool_name, pools) + pools = self.client.get_all_pools() + pool_info = self.client.get_pool_info(pool_name, pools) src_volume_name = huawei_utils.encode_name(volume['id']) dst_volume_name = six.text_type(hash(src_volume_name)) src_id = volume.get('provider_location') - src_lun_params = self.restclient.get_lun_info(src_id) - opts = None qos = None if new_type: # If new type exists, use new type. - opts = huawei_utils._get_extra_spec_value( - new_type['extra_specs']) - opts = smartx.SmartX().get_smartx_specs_opts(opts) + new_specs = new_type['extra_specs'] + opts = self._get_volume_params_from_specs(new_specs) if 'LUNType' not in opts: - opts['LUNType'] = huawei_utils.find_luntype_in_xml( - self.xml_file_path) + opts['LUNType'] = self.configuration.lun_type - qos = huawei_utils.get_qos_by_volume_type(new_type) + qos = smartx.SmartQos.get_qos_by_volume_type(new_type) elif volume_type: - qos = huawei_utils.get_qos_by_volume_type(volume_type) + qos = smartx.SmartQos.get_qos_by_volume_type(volume_type) if not opts: - opts = huawei_utils.get_volume_params(volume) - opts = smartx.SmartX().get_smartx_specs_opts(opts) - - lun_info = self._create_lun_with_extra_feature(pool_info, - dst_volume_name, - src_lun_params, - opts) + opts = self._get_volume_params(volume_type) + + lun_info = self.client.get_lun_info(src_id) + + policy = lun_info['DATATRANSFERPOLICY'] + if opts['policy']: + policy = opts['policy'] + lun_params = { + 'NAME': dst_volume_name, + 'PARENTID': pool_info['ID'], + 'DESCRIPTION': lun_info['DESCRIPTION'], + 'ALLOCTYPE': opts.get('LUNType', lun_info['ALLOCTYPE']), + 'CAPACITY': lun_info['CAPACITY'], + 'WRITEPOLICY': lun_info['WRITEPOLICY'], + 'MIRRORPOLICY': lun_info['MIRRORPOLICY'], + 'PREFETCHPOLICY': lun_info['PREFETCHPOLICY'], + 'PREFETCHVALUE': lun_info['PREFETCHVALUE'], + 'DATATRANSFERPOLICY': policy, + 'READCACHEPOLICY': lun_info['READCACHEPOLICY'], + 'WRITECACHEPOLICY': lun_info['WRITECACHEPOLICY'], + 'OWNINGCONTROLLER': lun_info['OWNINGCONTROLLER'], } + + lun_info = self.client.create_lun(lun_params) lun_id = lun_info['ID'] if qos: LOG.info(_LI('QoS: %s.'), qos) - SmartQos = smartx.SmartQos(self.restclient) - SmartQos.create_qos(qos, lun_id) + SmartQos = smartx.SmartQos(self.client) + SmartQos.add(qos, lun_id) if opts: - smartpartition = smartx.SmartPartition(self.restclient) + smartpartition = smartx.SmartPartition(self.client) smartpartition.add(opts, lun_id) - smartcache = smartx.SmartCache(self.restclient) + smartcache = smartx.SmartCache(self.client) smartcache.add(opts, lun_id) dst_id = lun_info['ID'] @@ -402,36 +512,6 @@ class HuaweiBaseDriver(driver.VolumeDriver): return moved, {} - def _create_lun_with_extra_feature(self, pool_info, - lun_name, - lun_params, - spec_opts): - LOG.info(_LI('Create a new lun %s for migration.'), lun_name) - - # Prepare lun parameters. - lunparam = {"TYPE": '11', - "NAME": lun_name, - "PARENTTYPE": '216', - "PARENTID": pool_info['ID'], - "ALLOCTYPE": lun_params['ALLOCTYPE'], - "CAPACITY": lun_params['CAPACITY'], - "WRITEPOLICY": lun_params['WRITEPOLICY'], - "MIRRORPOLICY": lun_params['MIRRORPOLICY'], - "PREFETCHPOLICY": lun_params['PREFETCHPOLICY'], - "PREFETCHVALUE": lun_params['PREFETCHVALUE'], - "DATATRANSFERPOLICY": '0', - "READCACHEPOLICY": lun_params['READCACHEPOLICY'], - "WRITECACHEPOLICY": lun_params['WRITECACHEPOLICY'], - "OWNINGCONTROLLER": lun_params['OWNINGCONTROLLER'], - } - if 'LUNType' in spec_opts: - lunparam['ALLOCTYPE'] = spec_opts['LUNType'] - if spec_opts['policy']: - lunparam['DATATRANSFERPOLICY'] = spec_opts['policy'] - - lun_info = self.restclient.create_volume(lunparam) - return lun_info - def create_volume_from_snapshot(self, volume, snapshot): """Create a volume from a snapshot. @@ -439,23 +519,20 @@ class HuaweiBaseDriver(driver.VolumeDriver): The time needed increases as volume size does. """ snapshotname = huawei_utils.encode_name(snapshot['id']) - snapshot_id = snapshot.get('provider_location') if snapshot_id is None: - snapshot_id = self.restclient.get_snapshotid_by_name(snapshotname) - if snapshot_id is None: - err_msg = (_( - 'create_volume_from_snapshot: Snapshot %(name)s ' - 'does not exist.') - % {'name': snapshotname}) - LOG.error(err_msg) - raise exception.VolumeBackendAPIException(data=err_msg) - - lun_info = self.create_volume(volume) - - tgt_lun_id = lun_info['ID'] - luncopy_name = huawei_utils.encode_name(volume['id']) + snapshot_id = self.client.get_snapshot_id_by_name(snapshotname) + if snapshot_id is None: + err_msg = (_( + 'create_volume_from_snapshot: Snapshot %(name)s ' + 'does not exist.') + % {'name': snapshotname}) + LOG.error(err_msg) + raise exception.VolumeBackendAPIException(data=err_msg) + model_update = self.create_volume(volume) + tgt_lun_id = model_update['provider_location'] + luncopy_name = huawei_utils.encode_name(volume['id']) LOG.info(_LI( 'create_volume_from_snapshot: src_lun_id: %(src_lun_id)s, ' 'tgt_lun_id: %(tgt_lun_id)s, copy_name: %(copy_name)s.'), @@ -463,32 +540,33 @@ class HuaweiBaseDriver(driver.VolumeDriver): 'tgt_lun_id': tgt_lun_id, 'copy_name': luncopy_name}) - event_type = 'LUNReadyWaitInterval' - - wait_interval = huawei_utils.get_wait_interval(self.xml_file_path, - event_type) + wait_interval = self.configuration.lun_ready_wait_interval def _volume_ready(): - result = self.restclient.get_lun_info(tgt_lun_id) + result = self.client.get_lun_info(tgt_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, + huawei_utils.wait_for_condition(_volume_ready, wait_interval, wait_interval * 10) self._copy_volume(volume, luncopy_name, snapshot_id, tgt_lun_id) - return {'ID': lun_info['ID'], - 'lun_info': lun_info} + return model_update def create_cloned_volume(self, volume, src_vref): """Clone a new volume from an existing volume.""" + if src_vref.get('provider_location') is None: + msg = (_("Can't find lun id from db, volume: %(id)s") % + {"id": volume['id']}) + LOG.error(msg) + raise exception.VolumeBackendAPIException(data=msg) + # Form the snapshot structure. snapshot = {'id': uuid.uuid4().__str__(), 'volume_id': src_vref['id'], @@ -499,7 +577,7 @@ class HuaweiBaseDriver(driver.VolumeDriver): try: # Create volume from snapshot. - lun_info = self.create_volume_from_snapshot(volume, snapshot) + model_update = self.create_volume_from_snapshot(volume, snapshot) finally: try: # Delete snapshot. @@ -511,39 +589,53 @@ class HuaweiBaseDriver(driver.VolumeDriver): {'snapshot_id': snapshot['id'], 'volume_id': src_vref['id']},) - return {'provider_location': lun_info['ID'], - 'lun_info': lun_info} + return model_update - @utils.synchronized('huawei', external=True) def extend_volume(self, volume, new_size): """Extend a volume.""" - volume_size = huawei_utils.get_volume_size(volume) - new_volume_size = int(new_size) * units.Gi / 512 + lun_id = volume.get('provider_location') + if not lun_id: + msg = (_("Can't find lun id from db, volume: %(id)s") % + {"id": volume['id']}) + LOG.error(msg) + raise exception.VolumeBackendAPIException(data=msg) + + old_size = huawei_utils.get_volume_size(volume) + new_size = int(new_size) * units.Gi / 512 volume_name = huawei_utils.encode_name(volume['id']) LOG.info(_LI( - 'Extend volume: %(volumename)s, oldsize:' - ' %(oldsize)s newsize: %(newsize)s.'), + 'Extend volume: %(volumename)s, ' + 'oldsize: %(oldsize)s, newsize: %(newsize)s.'), {'volumename': volume_name, - 'oldsize': volume_size, - 'newsize': new_volume_size},) - - lun_id = self.restclient.get_lunid(volume, volume_name) - luninfo = self.restclient.extend_volume(lun_id, new_volume_size) + 'oldsize': old_size, + 'newsize': new_size}) - return {'provider_location': luninfo['ID'], - 'lun_info': luninfo} + lun_info = self.client.extend_lun(lun_id, new_size) + return {'provider_location': lun_info['ID'], + 'lun_info': lun_info} - @utils.synchronized('huawei', external=True) def create_snapshot(self, snapshot): - snapshot_info = self.restclient.create_snapshot(snapshot) + volume = snapshot.get('volume') + if not volume: + msg = (_("Can't find volume id from db, volume: %(id)s") % + {"id": volume['id']}) + LOG.error(msg) + raise exception.VolumeBackendAPIException(data=msg) + + volume_name = huawei_utils.encode_name(snapshot['volume_id']) + lun_id = self.client.get_lun_id(volume, volume_name) + snapshot_name = huawei_utils.encode_name(snapshot['id']) + snapshot_description = snapshot['id'] + snapshot_info = self.client.create_snapshot(lun_id, + snapshot_name, + snapshot_description) snapshot_id = snapshot_info['ID'] - self.restclient.activate_snapshot(snapshot_id) + self.client.activate_snapshot(snapshot_id) return {'provider_location': snapshot_info['ID'], 'lun_info': snapshot_info} - @utils.synchronized('huawei', external=True) def delete_snapshot(self, snapshot): snapshotname = huawei_utils.encode_name(snapshot['id']) volume_name = huawei_utils.encode_name(snapshot['volume_id']) @@ -556,12 +648,12 @@ class HuaweiBaseDriver(driver.VolumeDriver): snapshot_id = snapshot.get('provider_location') if snapshot_id is None: - snapshot_id = self.restclient.get_snapshotid_by_name(snapshotname) + snapshot_id = self.client.get_snapshot_id_by_name(snapshotname) if snapshot_id is not None: - if self.restclient.check_snapshot_exist(snapshot_id): - self.restclient.stop_snapshot(snapshot_id) - self.restclient.delete_snapshot(snapshot_id) + if self.client.check_snapshot_exist(snapshot_id): + self.client.stop_snapshot(snapshot_id) + self.client.delete_snapshot(snapshot_id) else: LOG.warning(_LW("Can't find snapshot on the array.")) else: @@ -577,6 +669,7 @@ class HuaweiBaseDriver(driver.VolumeDriver): 'new_type': new_type, 'diff': diff, 'host': host}) + # Check what changes are needed migration, change_opts, lun_id = self.determine_changes_when_retype( volume, new_type, host) @@ -608,9 +701,9 @@ class HuaweiBaseDriver(driver.VolumeDriver): new_id = new[0] new_name = new[1] if old_id: - self.restclient.remove_lun_from_partition(lun_id, old_id) + self.client.remove_lun_from_partition(lun_id, old_id) if new_id: - self.restclient.add_lun_to_partition(lun_id, new_id) + self.client.add_lun_to_partition(lun_id, new_id) LOG.info(_LI("Retype LUN(id: %(lun_id)s) smartpartition from " "(name: %(old_name)s, id: %(old_id)s) to " "(name: %(new_name)s, id: %(new_id)s) success."), @@ -625,9 +718,9 @@ class HuaweiBaseDriver(driver.VolumeDriver): new_id = new[0] new_name = new[1] if old_id: - self.restclient.remove_lun_from_cache(lun_id, old_id) + self.client.remove_lun_from_cache(lun_id, old_id) if new_id: - self.restclient.add_lun_to_cache(lun_id, new_id) + self.client.add_lun_to_cache(lun_id, new_id) LOG.info(_LI("Retype LUN(id: %(lun_id)s) smartcache from " "(name: %(old_name)s, id: %(old_id)s) to " "(name: %(new_name)s, id: %(new_id)s) successfully."), @@ -637,7 +730,7 @@ class HuaweiBaseDriver(driver.VolumeDriver): if change_opts.get('policy'): old_policy, new_policy = change_opts['policy'] - self.restclient.change_lun_smarttier(lun_id, new_policy) + self.client.change_lun_smarttier(lun_id, new_policy) LOG.info(_LI("Retype LUN(id: %(lun_id)s) smarttier policy from " "%(old_policy)s to %(new_policy)s success."), {'lun_id': lun_id, @@ -649,10 +742,11 @@ class HuaweiBaseDriver(driver.VolumeDriver): old_qos_id = old_qos[0] old_qos_value = old_qos[1] if old_qos_id: - self.remove_qos_lun(lun_id, old_qos_id) + smart_qos = smartx.SmartQos(self.client) + smart_qos.remove(old_qos_id, lun_id) if new_qos: - smart_qos = smartx.SmartQos(self.restclient) - smart_qos.create_qos(new_qos, lun_id) + smart_qos = smartx.SmartQos(self.client) + smart_qos.add(new_qos, lun_id) LOG.info(_LI("Retype LUN(id: %(lun_id)s) smartqos from " "%(old_qos_value)s to %(new_qos)s success."), {'lun_id': lun_id, @@ -667,8 +761,8 @@ class HuaweiBaseDriver(driver.VolumeDriver): 'LUNType': None, } - lun_info = self.restclient.get_lun_info(lun_id) - lun_opts['LUNType'] = int(lun_info.get('ALLOCTYPE')) + lun_info = self.client.get_lun_info(lun_id) + lun_opts['LUNType'] = int(lun_info['ALLOCTYPE']) if lun_info.get('DATATRANSFERPOLICY'): lun_opts['policy'] = lun_info['DATATRANSFERPOLICY'] if lun_info.get('SMARTCACHEPARTITIONID'): @@ -683,7 +777,7 @@ class HuaweiBaseDriver(driver.VolumeDriver): new_cache_id = None new_cache_name = new_opts['cachename'] if new_cache_name: - new_cache_id = self.restclient.get_cache_id_by_name(new_cache_name) + new_cache_id = self.client.get_cache_id_by_name(new_cache_name) if new_cache_id is None: msg = (_( "Can't find cache name on the array, cache name is: " @@ -694,7 +788,7 @@ class HuaweiBaseDriver(driver.VolumeDriver): new_partition_id = None new_partition_name = new_opts['partitionname'] if new_partition_name: - new_partition_id = self.restclient.get_partition_id_by_name( + new_partition_id = self.client.get_partition_id_by_name( new_partition_name) if new_partition_id is None: msg = (_( @@ -712,7 +806,7 @@ class HuaweiBaseDriver(driver.VolumeDriver): if old_cache_id != new_cache_id: old_cache_name = None if old_cache_id: - cache_info = self.restclient.get_cache_info_by_id(old_cache_id) + cache_info = self.client.get_cache_info_by_id(old_cache_id) old_cache_name = cache_info['NAME'] change_opts['cacheid'] = ([old_cache_id, old_cache_name], [new_cache_id, new_cache_name]) @@ -722,7 +816,7 @@ class HuaweiBaseDriver(driver.VolumeDriver): if old_partition_id != new_partition_id: old_partition_name = None if old_partition_id: - partition_info = self.restclient.get_partition_info_by_id( + partition_info = self.client.get_partition_info_by_id( old_partition_id) old_partition_name = partition_info['NAME'] change_opts['partitionid'] = ([old_partition_id, @@ -731,8 +825,8 @@ class HuaweiBaseDriver(driver.VolumeDriver): new_partition_name]) # smartqos - new_qos = huawei_utils.get_qos_by_volume_type(new_type) - old_qos_id = self.restclient.get_qosid_by_lunid(lun_id) + new_qos = smartx.SmartQos.get_qos_by_volume_type(new_type) + old_qos_id = self.client.get_qosid_by_lunid(lun_id) old_qos = self._get_qos_specs_from_array(old_qos_id) if old_qos != new_qos: change_opts['qos'] = ([old_qos_id, old_qos], new_qos) @@ -748,18 +842,18 @@ class HuaweiBaseDriver(driver.VolumeDriver): 'qos': None, 'host': None, 'LUNType': None, + 'replication_enabled': None, + 'replication_type': None, } lun_id = volume.get('provider_location') old_opts = self.get_lun_specs(lun_id) new_specs = new_type['extra_specs'] - new_opts = huawei_utils._get_extra_spec_value(new_specs) - new_opts = smartx.SmartX().get_smartx_specs_opts(new_opts) + new_opts = self._get_volume_params_from_specs(new_specs) if 'LUNType' not in new_opts: - new_opts['LUNType'] = huawei_utils.find_luntype_in_xml( - self.xml_file_path) + new_opts['LUNType'] = self.configuration.lun_type if volume['host'] != host['host']: migration = True @@ -780,7 +874,7 @@ class HuaweiBaseDriver(driver.VolumeDriver): qos = {} qos_info = {} if qos_id: - qos_info = self.restclient.get_qos_info(qos_id) + qos_info = self.client.get_qos_info(qos_id) for key, value in qos_info.items(): if key.upper() in constants.QOS_KEYS: @@ -803,17 +897,16 @@ class HuaweiBaseDriver(driver.VolumeDriver): pass def _copy_volume(self, volume, copy_name, src_lun, tgt_lun): - luncopy_id = self.restclient.create_luncopy(copy_name, - src_lun, tgt_lun) - event_type = 'LUNcopyWaitInterval' - wait_interval = huawei_utils.get_wait_interval(self.xml_file_path, - event_type) + luncopy_id = self.client.create_luncopy(copy_name, + src_lun, + tgt_lun) + wait_interval = self.configuration.lun_copy_wait_interval try: - self.restclient.start_luncopy(luncopy_id) + self.client.start_luncopy(luncopy_id) def _luncopy_complete(): - luncopy_info = self.restclient.get_luncopy_info(luncopy_id) + luncopy_info = self.client.get_luncopy_info(luncopy_id) if luncopy_info['status'] == constants.STATUS_LUNCOPY_READY: # luncopy_info['status'] means for the running status of # the luncopy. If luncopy_info['status'] is equal to '40', @@ -834,16 +927,16 @@ class HuaweiBaseDriver(driver.VolumeDriver): 'luncopystate': luncopy_info['state']},) LOG.error(err_msg) raise exception.VolumeBackendAPIException(data=err_msg) - huawei_utils.wait_for_condition(self.xml_file_path, - _luncopy_complete, - wait_interval) + huawei_utils.wait_for_condition(_luncopy_complete, + wait_interval, + self.configuration.lun_timeout) except Exception: with excutils.save_and_reraise_exception(): - self.restclient.delete_luncopy(luncopy_id) + self.client.delete_luncopy(luncopy_id) self.delete_volume(volume) - self.restclient.delete_luncopy(luncopy_id) + self.client.delete_luncopy(luncopy_id) def _check_lun_valid_for_manage(self, lun_info, external_ref): lun_id = lun_info.get('ID') @@ -856,7 +949,7 @@ class HuaweiBaseDriver(driver.VolumeDriver): # Check whether the LUN exists in a HyperMetroPair. try: - hypermetro_pairs = self.restclient.get_hypermetro_pairs() + hypermetro_pairs = self.client.get_hypermetro_pairs() except exception.VolumeBackendAPIException: msg = _("Failed to get HyperMetroPair.") raise exception.ManageExistingInvalidReference( @@ -871,7 +964,7 @@ class HuaweiBaseDriver(driver.VolumeDriver): # Check whether the LUN exists in a SplitMirror. try: - split_mirrors = self.restclient.get_split_mirrors() + split_mirrors = self.client.get_split_mirrors() except exception.VolumeBackendAPIException as ex: if re.search('License is unavailable', ex.msg): # Can't check whether the LUN has SplitMirror with it, @@ -884,7 +977,7 @@ class HuaweiBaseDriver(driver.VolumeDriver): for mirror in split_mirrors: try: - target_luns = self.restclient.get_target_luns(mirror.get('ID')) + target_luns = self.client.get_target_luns(mirror.get('ID')) except exception.VolumeBackendAPIException: msg = _("Failed to get target LUN of SplitMirror.") raise exception.VolumeBackendAPIException(data=msg) @@ -897,7 +990,7 @@ class HuaweiBaseDriver(driver.VolumeDriver): # Check whether the LUN exists in a migration task. try: - migration_tasks = self.restclient.get_migration_task() + migration_tasks = self.client.get_migration_task() except exception.VolumeBackendAPIException as ex: if re.search('License is unavailable', ex.msg): # Can't check whether the LUN has migration task with it, @@ -933,7 +1026,7 @@ class HuaweiBaseDriver(driver.VolumeDriver): existing_ref=external_ref, reason=msg) # Check whether the LUN exists in a LUN mirror. - if self.restclient.is_lun_in_mirror(lun_id): + if self.client.is_lun_in_mirror(lun_id): msg = (_("Can't import LUN %s to Cinder. Already exists in " "a LUN mirror.") % lun_id) raise exception.ManageExistingInvalidReference( @@ -975,8 +1068,7 @@ class HuaweiBaseDriver(driver.VolumeDriver): old_opts = self.get_lun_specs(lun_id) volume_type = volume_types.get_volume_type(None, type_id) new_specs = volume_type.get('extra_specs') - new_opts = huawei_utils._get_extra_spec_value(new_specs) - new_opts = smartx.SmartX().get_smartx_specs_opts(new_opts) + new_opts = self._get_volume_params_from_specs(new_specs) if ('LUNType' in new_opts and old_opts['LUNType'] != new_opts['LUNType']): msg = (_("Can't import LUN %(lun_id)s to Cinder. " @@ -995,7 +1087,8 @@ class HuaweiBaseDriver(driver.VolumeDriver): LOG.debug("Rename LUN %(old_name)s to %(new_name)s.", {'old_name': lun_info.get('NAME'), 'new_name': new_name}) - self.restclient.rename_lun(lun_id, new_name, description) + self.client.rename_lun(lun_id, new_name, # pylint: disable=E1121 + description) return {'provider_location': lun_id} @@ -1008,14 +1101,14 @@ class HuaweiBaseDriver(driver.VolumeDriver): raise exception.ManageExistingInvalidReference( existing_ref=external_ref, reason=msg) - lun_id = id or self.restclient.get_volume_by_name(name) + lun_id = id or self.client.get_volume_by_name(name) if not lun_id: msg = _("Can't find LUN on the array, please check the " "source-name or source-id.") raise exception.ManageExistingInvalidReference( existing_ref=external_ref, reason=msg) - lun_info = self.restclient.get_lun_info(lun_id) + lun_info = self.client.get_lun_info(lun_id) return lun_info def unmanage(self, volume): @@ -1023,7 +1116,7 @@ class HuaweiBaseDriver(driver.VolumeDriver): volume_id = volume['id'] LOG.debug("Unmanage volume: %s.", volume_id) lun_name = huawei_utils.encode_name(volume_id) - lun_id = self.restclient.get_volume_by_name(lun_name) + lun_id = self.client.get_volume_by_name(lun_name) if not lun_id: LOG.error(_LE("Can't find LUN on the array for volume: %s."), volume_id) @@ -1033,7 +1126,7 @@ class HuaweiBaseDriver(driver.VolumeDriver): {'lun_name': lun_name, 'new_name': new_name}) try: - self.restclient.rename_lun(lun_id, new_name) + self.client.rename_lun(lun_id, new_name) except Exception: LOG.warning(_LW("Rename lun %(lun_id)s fails when " "unmanaging volume %(volume)s."), @@ -1055,7 +1148,7 @@ class HuaweiISCSIDriver(HuaweiBaseDriver, driver.ISCSIDriver): Version history: 1.0.0 - Initial driver - 1.1.0 - Provide Huawei OceanStor 18000 storage volume driver + 1.1.0 - Provide Huawei OceanStor storage 18000 driver 1.1.1 - Code refactor CHAP support Multiple pools support @@ -1065,9 +1158,10 @@ class HuaweiISCSIDriver(HuaweiBaseDriver, driver.ISCSIDriver): Volume retype support 2.0.0 - Rename to HuaweiISCSIDriver 2.0.1 - Manage/unmanage volume support + 2.0.2 - Refactor HuaweiISCSIDriver """ - VERSION = "2.0.1" + VERSION = "2.0.2" def __init__(self, *args, **kwargs): super(HuaweiISCSIDriver, self).__init__(*args, **kwargs) @@ -1097,8 +1191,7 @@ class HuaweiISCSIDriver(HuaweiBaseDriver, driver.ISCSIDriver): (iscsi_iqns, target_ips, - portgroup_id) = self.restclient.get_iscsi_params(self.xml_file_path, - connector) + portgroup_id) = self.client.get_iscsi_params(connector) LOG.info(_LI('initialize_connection, iscsi_iqn: %(iscsi_iqn)s, ' 'target_ip: %(target_ip)s, ' 'portgroup_id: %(portgroup_id)s.'), @@ -1107,34 +1200,30 @@ class HuaweiISCSIDriver(HuaweiBaseDriver, driver.ISCSIDriver): 'portgroup_id': portgroup_id},) # Create hostgroup if not exist. - host_name = connector['host'] - host_name_before_hash = None - if host_name and (len(host_name) > constants.MAX_HOSTNAME_LENGTH): - host_name_before_hash = host_name - host_name = six.text_type(hash(host_name)) - host_id = self.restclient.add_host_with_check(host_name, - host_name_before_hash) + original_host_name = connector['host'] + host_name = huawei_utils.encode_host_name(original_host_name) + host_id = self.client.add_host_with_check(host_name, + original_host_name) # Add initiator to the host. - self.restclient.ensure_initiator_added(self.xml_file_path, - initiator_name, - host_id) - hostgroup_id = self.restclient.add_host_into_hostgroup(host_id) + self.client.ensure_initiator_added(initiator_name, + host_id) + hostgroup_id = self.client.add_host_to_hostgroup(host_id) - lun_id = self.restclient.get_lunid(volume, volume_name) + lun_id = self.client.get_lun_id(volume, volume_name) # Mapping lungroup and hostgroup to view. - self.restclient.do_mapping(lun_id, hostgroup_id, - host_id, portgroup_id) + self.client.do_mapping(lun_id, hostgroup_id, + host_id, portgroup_id) - hostlun_id = self.restclient.find_host_lun_id(host_id, lun_id) + hostlun_id = self.client.get_host_lun_id(host_id, lun_id) LOG.info(_LI("initialize_connection, host lun id is: %s."), hostlun_id) - iscsi_conf = huawei_utils.get_iscsi_conf(self.xml_file_path) - chapinfo = self.restclient.find_chap_info(iscsi_conf, - initiator_name) + chapinfo = self.client.find_chap_info(self.configuration.iscsi_info, + initiator_name) + # Return iSCSI properties. properties = {} properties['target_discovered'] = False @@ -1179,12 +1268,11 @@ class HuaweiISCSIDriver(HuaweiBaseDriver, driver.ISCSIDriver): 'ini': initiator_name, 'lunid': lun_id},) - iscsi_conf = huawei_utils.get_iscsi_conf(self.xml_file_path) portgroup = None portgroup_id = None view_id = None left_lunnum = -1 - for ini in iscsi_conf['Initiator']: + for ini in self.configuration.iscsi_info: if ini['Name'] == initiator_name: for key in ini: if key == 'TargetPortGroup': @@ -1192,23 +1280,22 @@ class HuaweiISCSIDriver(HuaweiBaseDriver, driver.ISCSIDriver): break if portgroup: - portgroup_id = self.restclient.find_tgt_port_group(portgroup) - if host_name and (len(host_name) > constants.MAX_HOSTNAME_LENGTH): - host_name = six.text_type(hash(host_name)) - host_id = self.restclient.find_host(host_name) + portgroup_id = self.client.get_tgt_port_group(portgroup) + host_name = huawei_utils.encode_host_name(host_name) + host_id = self.client.get_host_id_by_name(host_name) if host_id: mapping_view_name = constants.MAPPING_VIEW_PREFIX + host_id - view_id = self.restclient.find_mapping_view(mapping_view_name) + view_id = self.client.find_mapping_view(mapping_view_name) if view_id: - lungroup_id = self.restclient.find_lungroup_from_map(view_id) + lungroup_id = self.client.find_lungroup_from_map(view_id) # Remove lun from lungroup. - if lun_id and self.restclient.check_lun_exist(lun_id): + if lun_id and self.client.check_lun_exist(lun_id): if lungroup_id: - lungroup_ids = self.restclient.get_lungroupids_by_lunid(lun_id) + lungroup_ids = self.client.get_lungroupids_by_lunid(lun_id) if lungroup_id in lungroup_ids: - self.restclient.remove_lun_from_lungroup(lungroup_id, - lun_id) + self.client.remove_lun_from_lungroup(lungroup_id, + lun_id) else: LOG.warning(_LW("Lun is not in lungroup. " "Lun id: %(lun_id)s. " @@ -1220,37 +1307,37 @@ class HuaweiISCSIDriver(HuaweiBaseDriver, driver.ISCSIDriver): # Remove portgroup from mapping view if no lun left in lungroup. if lungroup_id: - left_lunnum = self.restclient.get_lunnum_from_lungroup(lungroup_id) + left_lunnum = self.client.get_lunnum_from_lungroup(lungroup_id) if portgroup_id and view_id and (int(left_lunnum) <= 0): - if self.restclient.is_portgroup_associated_to_view(view_id, - portgroup_id): - self.restclient.delete_portgroup_mapping_view(view_id, - portgroup_id) + if self.client.is_portgroup_associated_to_view(view_id, + portgroup_id): + self.client.delete_portgroup_mapping_view(view_id, + portgroup_id) if view_id and (int(left_lunnum) <= 0): - self.restclient.remove_chap(initiator_name) - - if self.restclient.lungroup_associated(view_id, lungroup_id): - self.restclient.delete_lungroup_mapping_view(view_id, - lungroup_id) - self.restclient.delete_lungroup(lungroup_id) - if self.restclient.is_initiator_associated_to_host(initiator_name): - self.restclient.remove_iscsi_from_host(initiator_name) + self.client.remove_chap(initiator_name) + + if self.client.lungroup_associated(view_id, lungroup_id): + self.client.delete_lungroup_mapping_view(view_id, + lungroup_id) + self.client.delete_lungroup(lungroup_id) + if self.client.is_initiator_associated_to_host(initiator_name): + self.client.remove_iscsi_from_host(initiator_name) hostgroup_name = constants.HOSTGROUP_PREFIX + host_id - hostgroup_id = self.restclient.find_hostgroup(hostgroup_name) + hostgroup_id = self.client.find_hostgroup(hostgroup_name) if hostgroup_id: - if self.restclient.hostgroup_associated(view_id, hostgroup_id): - self.restclient.delete_hostgoup_mapping_view(view_id, - hostgroup_id) - self.restclient.remove_host_from_hostgroup(hostgroup_id, - host_id) - self.restclient.delete_hostgroup(hostgroup_id) - self.restclient.remove_host(host_id) - self.restclient.delete_mapping_view(view_id) + if self.client.hostgroup_associated(view_id, hostgroup_id): + self.client.delete_hostgoup_mapping_view(view_id, + hostgroup_id) + self.client.remove_host_from_hostgroup(hostgroup_id, + host_id) + self.client.delete_hostgroup(hostgroup_id) + self.client.remove_host(host_id) + self.client.delete_mapping_view(view_id) class HuaweiFCDriver(HuaweiBaseDriver, driver.FibreChannelDriver): - """FC driver for Huawei storage arrays. + """FC driver for Huawei OceanStor storage arrays. Version history: 1.0.0 - Initial driver @@ -1264,9 +1351,10 @@ class HuaweiFCDriver(HuaweiBaseDriver, driver.FibreChannelDriver): Volume hypermetro support 2.0.0 - Rename to HuaweiFCDriver 2.0.1 - Manage/unmanage volume support + 2.0.2 - Refactor HuaweiFCDriver """ - VERSION = "2.0.1" + VERSION = "2.0.2" def __init__(self, *args, **kwargs): super(HuaweiFCDriver, self).__init__(*args, **kwargs) @@ -1293,43 +1381,42 @@ class HuaweiFCDriver(HuaweiBaseDriver, driver.FibreChannelDriver): {'wwpns': wwns, 'volume': volume_name},) - lun_id = self.restclient.get_lunid(volume, volume_name) + lun_id = self.client.get_lun_id(volume, volume_name) - 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)) + original_host_name = connector['host'] + host_name = huawei_utils.encode_host_name(original_host_name) + host_id = self.client.add_host_with_check(host_name, + original_host_name) if not self.fcsan_lookup_service: self.fcsan_lookup_service = fczm_utils.create_lookup_service() if self.fcsan_lookup_service: # Use FC switch. - host_id = self.restclient.add_host_with_check( - host_name, host_name_before_hash) + host_id = self.client.add_host_with_check( + host_name, original_host_name) zone_helper = fc_zone_helper.FCZoneHelper( - self.fcsan_lookup_service, self.restclient) + self.fcsan_lookup_service, self.client) (tgt_port_wwns, init_targ_map) = ( zone_helper.build_ini_targ_map(wwns)) for ini in init_targ_map: - self.restclient.ensure_fc_initiator_added(ini, host_id) + self.client.ensure_fc_initiator_added(ini, host_id) else: # Not use FC switch. - host_id = self.restclient.add_host_with_check( - host_name, host_name_before_hash) + host_id = self.client.add_host_with_check( + host_name, original_host_name) online_wwns_in_host = ( - self.restclient.get_host_online_fc_initiators(host_id)) - online_free_wwns = self.restclient.get_online_free_wwns() + self.client.get_host_online_fc_initiators(host_id)) + online_free_wwns = self.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.restclient.get_host_fc_initiators(host_id)) + self.client.get_host_fc_initiators(host_id)) iqns_in_host = ( - self.restclient.get_host_iscsi_initiators(host_id)) + self.client.get_host_iscsi_initiators(host_id)) if not wwns_in_host and not iqns_in_host: - self.restclient.remove_host(host_id) + self.client.remove_host(host_id) msg = (_('Can not add FC initiator to host.')) LOG.error(msg) @@ -1337,17 +1424,17 @@ class HuaweiFCDriver(HuaweiBaseDriver, driver.FibreChannelDriver): for wwn in wwns: if wwn in online_free_wwns: - self.restclient.add_fc_port_to_host(host_id, wwn) + self.client.add_fc_port_to_host(host_id, wwn) (tgt_port_wwns, init_targ_map) = ( - self.restclient.get_init_targ_map(wwns)) + self.client.get_init_targ_map(wwns)) # Add host into hostgroup. - hostgroup_id = self.restclient.add_host_into_hostgroup(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) + hostgroup_id = self.client.add_host_to_hostgroup(host_id) + map_info = self.client.do_mapping(lun_id, + hostgroup_id, + host_id) + host_lun_id = self.client.get_host_lun_id(host_id, lun_id) # Return FC properties. fc_info = {'driver_volume_type': 'fibre_channel', @@ -1365,7 +1452,7 @@ class HuaweiFCDriver(HuaweiBaseDriver, driver.FibreChannelDriver): 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, + hyperm = hypermetro.HuaweiHyperMetro(self.client, self.configuration) rmt_fc_info = hyperm.connect_volume_fc(volume, connector) @@ -1390,7 +1477,7 @@ class HuaweiFCDriver(HuaweiBaseDriver, driver.FibreChannelDriver): same_host_id = self._get_same_hostid(loc_map_info, rmt_map_info) - self.restclient.change_hostlun_id(loc_map_info, same_host_id) + self.client.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 @@ -1420,6 +1507,7 @@ class HuaweiFCDriver(HuaweiBaseDriver, driver.FibreChannelDriver): return same_host_id + @utils.synchronized('huawei', external=True) @fczm_utils.RemoveFCZone def terminate_connection(self, volume, connector, **kwargs): """Delete map between a volume and a host.""" @@ -1437,31 +1525,32 @@ class HuaweiFCDriver(HuaweiBaseDriver, driver.FibreChannelDriver): 'wwns': wwns, 'lunid': lun_id},) - if host_name and len(host_name) > constants.MAX_HOSTNAME_LENGTH: - host_name = six.text_type(hash(host_name)) - host_id = self.restclient.find_host(host_name) + host_name = huawei_utils.encode_host_name(host_name) + host_id = self.client.get_host_id_by_name(host_name) if host_id: mapping_view_name = constants.MAPPING_VIEW_PREFIX + host_id - view_id = self.restclient.find_mapping_view(mapping_view_name) + view_id = self.client.find_mapping_view(mapping_view_name) if view_id: - lungroup_id = self.restclient.find_lungroup_from_map(view_id) + lungroup_id = self.client.find_lungroup_from_map(view_id) - if lun_id and self.restclient.check_lun_exist(lun_id): + if (lun_id is not None + and self.client.check_lun_exist(lun_id)): if lungroup_id: - lungroup_ids = self.restclient.get_lungroupids_by_lunid(lun_id) + lungroup_ids = self.client.get_lungroupids_by_lunid(lun_id) if lungroup_id in lungroup_ids: - self.restclient.remove_lun_from_lungroup(lungroup_id, - lun_id) + self.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}) + else: LOG.warning(_LW("Can't find lun on the array.")) if lungroup_id: - left_lunnum = self.restclient.get_lunnum_from_lungroup(lungroup_id) + left_lunnum = self.client.get_lunnum_from_lungroup(lungroup_id) if int(left_lunnum) > 0: fc_info = {'driver_volume_type': 'fibre_channel', 'data': {}} @@ -1471,42 +1560,42 @@ class HuaweiFCDriver(HuaweiBaseDriver, driver.FibreChannelDriver): if self.fcsan_lookup_service: zone_helper = fc_zone_helper.FCZoneHelper( - self.fcsan_lookup_service, self.restclient) + self.fcsan_lookup_service, self.client) (tgt_port_wwns, init_targ_map) = ( zone_helper.build_ini_targ_map(wwns)) else: (tgt_port_wwns, init_targ_map) = ( - self.restclient.get_init_targ_map(wwns)) + self.client.get_init_targ_map(wwns)) for wwn in wwns: - if self.restclient.is_fc_initiator_associated_to_host(wwn): - self.restclient.remove_fc_from_host(wwn) + if self.client.is_fc_initiator_associated_to_host(wwn): + self.client.remove_fc_from_host(wwn) if lungroup_id: - if view_id and self.restclient.lungroup_associated( + if view_id and self.client.lungroup_associated( view_id, lungroup_id): - self.restclient.delete_lungroup_mapping_view(view_id, - lungroup_id) - self.restclient.delete_lungroup(lungroup_id) + self.client.delete_lungroup_mapping_view(view_id, + lungroup_id) + self.client.delete_lungroup(lungroup_id) if host_id: hostgroup_name = constants.HOSTGROUP_PREFIX + host_id - hostgroup_id = self.restclient.find_hostgroup(hostgroup_name) + hostgroup_id = self.client.find_hostgroup(hostgroup_name) if hostgroup_id: - if view_id and self.restclient.hostgroup_associated( + if view_id and self.client.hostgroup_associated( view_id, hostgroup_id): - self.restclient.delete_hostgoup_mapping_view( + self.client.delete_hostgoup_mapping_view( view_id, hostgroup_id) - self.restclient.remove_host_from_hostgroup( + self.client.remove_host_from_hostgroup( hostgroup_id, host_id) - self.restclient.delete_hostgroup(hostgroup_id) + self.client.delete_hostgroup(hostgroup_id) - if not self.restclient.check_fc_initiators_exist_in_host( + if not self.client.check_fc_initiators_exist_in_host( host_id): - self.restclient.remove_host(host_id) + self.client.remove_host(host_id) if view_id: - self.restclient.delete_mapping_view(view_id) + self.client.delete_mapping_view(view_id) fc_info = {'driver_volume_type': 'fibre_channel', 'data': {'target_wwn': tgt_port_wwns, @@ -1515,8 +1604,9 @@ class HuaweiFCDriver(HuaweiBaseDriver, driver.FibreChannelDriver): # 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, + hyperm = hypermetro.HuaweiHyperMetro(self.client, self.configuration) hyperm.disconnect_volume_fc(volume, connector) diff --git a/cinder/volume/drivers/huawei/huawei_utils.py b/cinder/volume/drivers/huawei/huawei_utils.py index 0da3efa5d..1c403ee9a 100644 --- a/cinder/volume/drivers/huawei/huawei_utils.py +++ b/cinder/volume/drivers/huawei/huawei_utils.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015 Huawei Technologies Co., Ltd. +# Copyright (c) 2016 Huawei Technologies Co., Ltd. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -18,334 +18,19 @@ import json import six import time import uuid -from xml.etree import ElementTree as ET from oslo_log import log as logging from oslo_service import loopingcall from oslo_utils import units -from cinder import context from cinder import exception -from cinder import utils -from cinder.i18n import _, _LE, _LI +from cinder.i18n import _ +from cinder import objects from cinder.volume.drivers.huawei import constants -from cinder.volume import qos_specs -from cinder.volume import volume_types LOG = logging.getLogger(__name__) -opts_capability = { - 'smarttier': False, - 'smartcache': False, - 'smartpartition': False, - 'thin_provisioning_support': False, - 'thick_provisioning_support': False, - 'hypermetro': False, -} - - -opts_value = { - 'policy': None, - 'partitionname': None, - 'cachename': None, -} - - -opts_associate = { - 'smarttier': 'policy', - 'smartcache': 'cachename', - 'smartpartition': 'partitionname', -} - - -def get_volume_params(volume): - opts = {} - ctxt = context.get_admin_context() - type_id = volume['volume_type_id'] - if type_id is not None: - volume_type = volume_types.get_volume_type(ctxt, type_id) - specs = dict(volume_type).get('extra_specs') - opts = _get_extra_spec_value(specs) - else: - opts.update(opts_capability) - opts.update(opts_value) - - return opts - - -def _get_extra_spec_value(specs): - """Return the parameters for creating the volume.""" - opts = {} - opts.update(opts_capability) - opts.update(opts_value) - - opts = _get_opts_from_specs(opts_capability, opts_value, specs) - LOG.debug('get_volume_params opts %(opts)s.', {'opts': opts}) - - return opts - - -def _get_opts_from_specs(opts_capability, opts_value, specs): - opts = {} - opts.update(opts_capability) - opts.update(opts_value) - - for key, value in specs.items(): - - # Get the scope, if is using scope format. - scope = None - key_split = key.split(':') - if len(key_split) > 2 and key_split[0] != "capabilities": - continue - - if len(key_split) == 1: - key = key_split[0] - else: - scope = key_split[0] - key = key_split[1] - - if scope: - scope = scope.lower() - if key: - key = key.lower() - - if ((not scope or scope == 'capabilities') - and key in opts_capability): - - words = value.split() - - if not (words and len(words) == 2 and words[0] == ''): - LOG.error(_LE("Extra specs must be specified as " - "capabilities:%s=' True' or " - "' true'."), key) - else: - opts[key] = words[1].lower() - - if (scope in opts_capability) and (key in opts_value): - if (scope in opts_associate) and (opts_associate[scope] == key): - opts[key] = value - - return opts - - -def _get_smartx_specs_params(lunsetinfo, smartx_opts): - """Get parameters from config file for creating lun.""" - # Default lun set information. - if 'LUNType' in smartx_opts: - lunsetinfo['LUNType'] = smartx_opts['LUNType'] - lunsetinfo['policy'] = smartx_opts['policy'] - - return lunsetinfo - - -def get_lun_params(xml_file_path, smartx_opts): - lunsetinfo = get_lun_conf_params(xml_file_path) - lunsetinfo = _get_smartx_specs_params(lunsetinfo, smartx_opts) - return lunsetinfo - - -def parse_xml_file(xml_file_path): - """Get root of xml file.""" - try: - tree = ET.parse(xml_file_path) - root = tree.getroot() - return root - except IOError as err: - LOG.error(_LE('parse_xml_file: %s.'), err) - raise - - -def get_xml_item(xml_root, item): - """Get the given item details. - - :param xml_root: The root of xml tree - :param item: The tag need to get - :return: A dict contains all the config info of the given item. - """ - items_list = [] - items = xml_root.findall(item) - for item in items: - tmp_dict = {'text': None, 'attrib': {}} - if item.text: - tmp_dict['text'] = item.text.strip() - for key, val in item.attrib.items(): - if val: - item.attrib[key] = val.strip() - tmp_dict['attrib'] = item.attrib - items_list.append(tmp_dict) - return items_list - - -def get_conf_host_os_type(host_ip, conf): - """Get host OS type from xml config file. - - :param host_ip: The IP of Nova host - :param config: xml config file - :return: host OS type - """ - os_conf = {} - xml_file_path = conf.cinder_huawei_conf_file - root = parse_xml_file(xml_file_path) - hosts_list = get_xml_item(root, 'Host') - for host in hosts_list: - os = host['attrib']['OSType'].strip() - ips = [ip.strip() for ip in host['attrib']['HostIP'].split(',')] - os_conf[os] = ips - host_os = None - for k, v in os_conf.items(): - if host_ip in v: - host_os = constants.OS_TYPE.get(k, None) - if not host_os: - host_os = constants.OS_TYPE['Linux'] # Default OS type. - - LOG.debug('_get_host_os_type: Host %(ip)s OS type is %(os)s.', - {'ip': host_ip, 'os': host_os}) - - return host_os - - -def get_qos_by_volume_type(volume_type): - qos = {} - qos_specs_id = volume_type.get('qos_specs_id') - - # We prefer the qos_specs association - # and override any existing extra-specs settings - # if present. - if qos_specs_id is not None: - kvs = qos_specs.get_qos_specs(context.get_admin_context(), - qos_specs_id)['specs'] - else: - return qos - - LOG.info(_LI('The QoS sepcs is: %s.'), kvs) - for key, value in kvs.items(): - if key in constants.HUAWEI_VALID_KEYS: - if (key.upper() != 'IOTYPE') and (int(value) <= 0): - err_msg = (_('Qos config is wrong. %(key)s' - ' must be set greater than 0.') - % {'key': key}) - LOG.error(err_msg) - raise exception.VolumeBackendAPIException(data=err_msg) - elif (key.upper() == 'IOTYPE') and (value not in ['0', '1', '2']): - raise exception.InvalidInput( - reason=(_('Illegal value specified for IOTYPE: ' - 'set to either 0, 1, or 2.'))) - else: - qos[key.upper()] = value - - return qos - - -def get_volume_qos(volume): - qos = {} - ctxt = context.get_admin_context() - type_id = volume['volume_type_id'] - if type_id is not None: - volume_type = volume_types.get_volume_type(ctxt, type_id) - qos = get_qos_by_volume_type(volume_type) - - return qos - - -def _get_volume_type(type_id): - ctxt = context.get_admin_context() - return volume_types.get_volume_type(ctxt, type_id) - - -def get_lun_conf_params(xml_file_path): - """Get parameters from config file for creating lun.""" - lunsetinfo = { - 'LUNType': 0, - 'StripUnitSize': '64', - 'WriteType': '1', - 'MirrorSwitch': '1', - 'PrefetchType': '3', - 'PrefetchValue': '0', - 'PrefetchTimes': '0', - 'policy': '0', - 'readcachepolicy': '2', - 'writecachepolicy': '5', - } - # Default lun set information. - root = parse_xml_file(xml_file_path) - luntype = root.findtext('LUN/LUNType') - if luntype: - if luntype.strip() in ['Thick', 'Thin']: - lunsetinfo['LUNType'] = luntype.strip() - if luntype.strip() == 'Thick': - lunsetinfo['LUNType'] = 0 - elif luntype.strip() == 'Thin': - lunsetinfo['LUNType'] = 1 - - else: - err_msg = (_( - "LUNType config is wrong. LUNType must be 'Thin'" - " or 'Thick'. LUNType: %(fetchtype)s.") - % {'fetchtype': luntype}) - LOG.error(err_msg) - raise exception.VolumeBackendAPIException(data=err_msg) - else: - lunsetinfo['LUNType'] = 0 - - stripunitsize = root.findtext('LUN/StripUnitSize') - if stripunitsize is not None: - lunsetinfo['StripUnitSize'] = stripunitsize.strip() - writetype = root.findtext('LUN/WriteType') - if writetype is not None: - lunsetinfo['WriteType'] = writetype.strip() - mirrorswitch = root.findtext('LUN/MirrorSwitch') - if mirrorswitch is not None: - lunsetinfo['MirrorSwitch'] = mirrorswitch.strip() - - prefetch = root.find('LUN/Prefetch') - if prefetch is not None and prefetch.attrib['Type']: - fetchtype = prefetch.attrib['Type'] - if fetchtype in ['0', '1', '2', '3']: - lunsetinfo['PrefetchType'] = fetchtype.strip() - typevalue = prefetch.attrib['Value'].strip() - if lunsetinfo['PrefetchType'] == '1': - double_value = int(typevalue) * 2 - typevalue_double = six.text_type(double_value) - lunsetinfo['PrefetchValue'] = typevalue_double - elif lunsetinfo['PrefetchType'] == '2': - lunsetinfo['PrefetchValue'] = typevalue - else: - err_msg = (_( - 'PrefetchType config is wrong. PrefetchType' - ' must be in 0,1,2,3. PrefetchType is: %(fetchtype)s.') - % {'fetchtype': fetchtype}) - LOG.error(err_msg) - raise exception.VolumeBackendAPIException(data=err_msg) - else: - LOG.info(_LI( - 'Use default PrefetchType. ' - 'PrefetchType: Intelligent.')) - - return lunsetinfo - - -def find_luntype_in_xml(xml_file_path): - root = parse_xml_file(xml_file_path) - luntype = root.findtext('LUN/LUNType') - if luntype: - if luntype.strip() in ['Thick', 'Thin']: - if luntype.strip() == 'Thick': - luntype = constants.THICK_LUNTYPE - elif luntype.strip() == 'Thin': - luntype = constants.THIN_LUNTYPE - else: - err_msg = (_( - "LUNType config is wrong. LUNType must be 'Thin'" - " or 'Thick'. LUNType: %(fetchtype)s.") - % {'fetchtype': luntype}) - LOG.error(err_msg) - raise exception.VolumeBackendAPIException(data=err_msg) - else: - luntype = constants.THICK_LUNTYPE - return luntype - - def encode_name(name): uuid_str = name.replace("-", "") vol_uuid = uuid.UUID('urn:uuid:%s' % uuid_str) @@ -355,72 +40,21 @@ def encode_name(name): return newuuid -def init_lun_parameters(name, parameters): - """Initialize basic LUN parameters.""" - lunparam = {"TYPE": "11", - "NAME": name, - "PARENTTYPE": "216", - "PARENTID": parameters['pool_id'], - "DESCRIPTION": parameters['volume_description'], - "ALLOCTYPE": parameters['LUNType'], - "CAPACITY": parameters['volume_size'], - "WRITEPOLICY": parameters['WriteType'], - "MIRRORPOLICY": parameters['MirrorSwitch'], - "PREFETCHPOLICY": parameters['PrefetchType'], - "PREFETCHVALUE": parameters['PrefetchValue'], - "DATATRANSFERPOLICY": parameters['policy'], - "READCACHEPOLICY": parameters['readcachepolicy'], - "WRITECACHEPOLICY": parameters['writecachepolicy'], - } - - return lunparam - - -def volume_in_use(volume): - """Check if the given volume is in use.""" - return (volume['volume_attachment'] and - len(volume['volume_attachment']) > 0) - +def encode_host_name(name): + if name and (len(name) > constants.MAX_HOSTNAME_LENGTH): + name = six.text_type(hash(name)) + return name -def get_wait_interval(xml_file_path, event_type): - """Get wait interval from huawei conf file.""" - root = parse_xml_file(xml_file_path) - wait_interval = root.findtext('LUN/%s' % event_type) - if wait_interval is None: - wait_interval = constants.DEFAULT_WAIT_INTERVAL - LOG.info(_LI( - "Wait interval for %(event_type)s is not configured in huawei " - "conf file. Use default: %(default_wait_interval)d."), - {"event_type": event_type, - "default_wait_interval": wait_interval}) - return int(wait_interval) - - -def get_default_timeout(xml_file_path): - """Get timeout from huawei conf file.""" - root = parse_xml_file(xml_file_path) - timeout = root.findtext('LUN/Timeout') - if timeout is None: - timeout = constants.DEFAULT_WAIT_TIMEOUT - LOG.info(_LI( - "Timeout is not configured in huawei conf file. " - "Use default: %(default_timeout)d."), - {"default_timeout": timeout}) - - return timeout - - -def wait_for_condition(xml_file_path, func, interval, timeout=None): +def wait_for_condition(func, interval, timeout): start_time = time.time() - if timeout is None: - timeout = get_default_timeout(xml_file_path) def _inner(): try: res = func() except Exception as ex: raise exception.VolumeBackendAPIException(data=ex) + if res: raise loopingcall.LoopingCallDone() @@ -434,78 +68,6 @@ def wait_for_condition(xml_file_path, func, interval, timeout=None): timer.start(interval=interval).wait() -def get_login_info(xml_file_path): - """Get login IP, user name and password from config file.""" - login_info = {} - root = parse_xml_file(xml_file_path) - - login_info['RestURL'] = root.findtext('Storage/RestURL').strip() - - for key in ['UserName', 'UserPassword']: - node = root.find('Storage/%s' % key) - node_text = node.text - login_info[key] = node_text - - return login_info - - -def _change_file_mode(filepath): - utils.execute('chmod', '640', filepath, run_as_root=True) - - -def get_iscsi_conf(xml_file_path): - """Get iSCSI info from config file.""" - iscsiinfo = {} - root = parse_xml_file(xml_file_path) - target_ip = root.findtext('iSCSI/DefaultTargetIP').strip() - iscsiinfo['DefaultTargetIP'] = target_ip - initiator_list = [] - - for dic in root.findall('iSCSI/Initiator'): - # Strip values of dict. - tmp_dic = {} - for k in dic.items(): - tmp_dic[k[0]] = k[1].strip() - - initiator_list.append(tmp_dic) - - iscsiinfo['Initiator'] = initiator_list - - return iscsiinfo - - -def check_qos_high_priority(qos): - """Check QoS priority.""" - for key, value in qos.items(): - if (key.find('MIN') == 0) or (key.find('LATENCY') == 0): - return True - - return False - - -def check_conf_file(xml_file_path): - """Check the config file, make sure the essential items are set.""" - root = parse_xml_file(xml_file_path) - resturl = root.findtext('Storage/RestURL') - username = root.findtext('Storage/UserName') - pwd = root.findtext('Storage/UserPassword') - pool_node = root.findall('LUN/StoragePool') - - if (not resturl) or (not username) or (not pwd): - err_msg = (_( - 'check_conf_file: Config file invalid. RestURL,' - ' UserName and UserPassword must be set.')) - LOG.error(err_msg) - raise exception.InvalidInput(reason=err_msg) - - if not pool_node: - err_msg = (_( - 'check_conf_file: Config file invalid. ' - 'StoragePool must be set.')) - LOG.error(err_msg) - raise exception.InvalidInput(reason=err_msg) - - def get_volume_size(volume): """Calculate the volume size. @@ -519,30 +81,6 @@ def get_volume_size(volume): return volume_size -def get_protocol(xml_file_path): - """Get protocol from huawei conf file.""" - root = parse_xml_file(xml_file_path) - protocol = root.findtext('Storage/Protocol') - if not protocol: - err_msg = (_('Get protocol from huawei conf file error.')) - LOG.error(err_msg) - raise exception.InvalidInput(reason=err_msg) - - return protocol - - -def get_pools(xml_file_path): - """Get pools from huawei conf file.""" - root = parse_xml_file(xml_file_path) - pool_names = root.findtext('LUN/StoragePool') - if not pool_names: - msg = _('Invalid resource pool name. ' - 'Please check the config file.') - LOG.error(msg) - raise exception.InvalidInput(msg) - return pool_names - - def get_remote_device_info(valid_hypermetro_devices): remote_device_info = {} try: @@ -562,8 +100,22 @@ def get_remote_device_info(valid_hypermetro_devices): def get_volume_metadata(volume): + if type(volume) is objects.Volume: + return volume.metadata + if 'volume_metadata' in volume: metadata = volume.get('volume_metadata') return {item['key']: item['value'] for item in metadata} return {} + + +def get_snapshot_metadata_value(snapshot): + if type(snapshot) is objects.Snapshot: + return snapshot.metadata + + if 'snapshot_metadata' in snapshot: + metadata = snapshot.get('snapshot_metadata') + return {item['key']: item['value'] for item in metadata} + + return {} diff --git a/cinder/volume/drivers/huawei/hypermetro.py b/cinder/volume/drivers/huawei/hypermetro.py index 22eea83af..0db361334 100644 --- a/cinder/volume/drivers/huawei/hypermetro.py +++ b/cinder/volume/drivers/huawei/hypermetro.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015 Huawei Technologies Co., Ltd. +# Copyright (c) 2016 Huawei Technologies Co., Ltd. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -14,67 +14,59 @@ # 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): + def __init__(self, client, rmt_client, configuration, db): + self.db = db 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): + def create_hypermetro(self, local_lun_id, lun_params): """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) + config_pool = self.configuration.metro_storage_pools + remote_pool = self.rmt_client.get_all_pools() + pool = self.rmt_client.get_pool_info(config_pool, remote_pool) + # Create remote lun. + lun_params['pool_id'] = pool['ID'] + remotelun_info = self.rmt_client.create_lun(lun_params) remote_lun_id = remotelun_info['ID'] - # Get hypermetro domain + # Get hypermetro domain. try: - domain_name = device_info['domain_name'] + domain_name = self.configuration.metro_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: + LOG.info(_LI("Hypermetro id: %(metro_id)s. " + "Remote lun id: %(remote_lun_id)s."), + {'metro_id': hypermetro['ID'], + 'remote_lun_id': remote_lun_id}) + + return {'hypermetro_id': hypermetro['ID'], + 'remote_lun_id': remote_lun_id} + except exception.VolumeBackendAPIException 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.""" @@ -96,21 +88,8 @@ class HuaweiHyperMetro(object): 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() + if remote_lun_id and self.rmt_client.check_lun_exist(remote_lun_id): + self.rmt_client.delete_lun(remote_lun_id) def _create_hypermetro_pair(self, domain_id, lun_id, remote_lun_id): """Create a HyperMetroPair.""" @@ -126,78 +105,64 @@ class HuaweiHyperMetro(object): 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) + wwns = connector['wwpns'] + volume_name = huawei_utils.encode_name(volume['id']) - 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.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_lun_id_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) + + original_host_name = connector['host'] + host_name = huawei_utils.encode_host_name(original_host_name) + host_id = self.client.add_host_with_check(host_name, + original_host_name) + + # Create hostgroup if not exist. + host_id = self.rmt_client.add_host_with_check( + host_name, original_host_name) + + 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 or 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) - 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) + 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_to_hostgroup(host_id) + map_info = self.rmt_client.do_mapping(lun_id, + hostgroup_id, + host_id) + host_lun_id = self.rmt_client.get_host_lun_id(host_id, lun_id) # Return FC properties. fc_info = {'driver_volume_type': 'fibre_channel', @@ -216,76 +181,61 @@ class HuaweiHyperMetro(object): 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) + 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},) + + host_name = huawei_utils.encode_host_name(host_name) + hostid = self.rmt_client.get_host_id_by_name(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: - 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() + 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.get_host_id_by_name(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) if int(left_lunnum) > 0: info = {'driver_volume_type': 'fibre_channel', @@ -298,9 +248,7 @@ class HuaweiHyperMetro(object): 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) + wait_interval = self.configuration.lun_ready_wait_interval def _volume_ready(): result = self.rmt_client.get_lun_info(lun_id) @@ -309,8 +257,7 @@ class HuaweiHyperMetro(object): return True return False - huawei_utils.wait_for_condition(self.xml_file_path, - _volume_ready, + huawei_utils.wait_for_condition(_volume_ready, wait_interval, wait_interval * 10) diff --git a/cinder/volume/drivers/huawei/rest_client.py b/cinder/volume/drivers/huawei/rest_client.py index 90e7b8241..c6b28ca49 100644 --- a/cinder/volume/drivers/huawei/rest_client.py +++ b/cinder/volume/drivers/huawei/rest_client.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015 Huawei Technologies Co., Ltd. +# Copyright (c) 2016 Huawei Technologies Co., Ltd. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -28,18 +28,18 @@ from cinder import exception from cinder.i18n import _, _LE, _LI, _LW from cinder import utils from cinder.volume.drivers.huawei import constants -from cinder.volume.drivers.huawei import huawei_utils LOG = logging.getLogger(__name__) class RestClient(object): - """Common class for Huawei OceanStor 18000 storage system.""" + """Common class for Huawei OceanStor storage system.""" - def __init__(self, configuration): + def __init__(self, configuration, san_address, san_user, san_password): self.configuration = configuration - self.xml_file_path = configuration.cinder_huawei_conf_file - self.productversion = None + self.san_address = san_address + self.san_user = san_user + self.san_password = san_password self.init_http_head() def init_http_head(self): @@ -53,7 +53,7 @@ class RestClient(object): def do_call(self, url=None, data=None, method=None, calltimeout=constants.SOCKET_TIMEOUT): - """Send requests to 18000 server. + """Send requests to Huawei storage server. Send HTTPS call, get response in JSON. Convert response into Python Object and return it. @@ -67,6 +67,8 @@ class RestClient(object): try: socket.setdefaulttimeout(calltimeout) + if data: + data = json.dumps(data) req = urllib.request.Request(url, data, self.headers) if method: req.get_method = lambda: method @@ -98,16 +100,13 @@ class RestClient(object): return res_json def login(self): - """Login 18000 array.""" - login_info = huawei_utils.get_login_info(self.xml_file_path) - urlstr = login_info['RestURL'] - url_list = urlstr.split(";") + """Login Huawei storage array.""" device_id = None - for item_url in url_list: + for item_url in self.san_address: url = item_url + "xx/sessions" - data = json.dumps({"username": login_info['UserName'], - "password": login_info['UserPassword'], - "scope": "0"}) + data = {"username": self.san_user, + "password": self.san_password, + "scope": "0"} self.init_http_head() result = self.do_call(url, data, calltimeout=constants.LOGIN_SOCKET_TIMEOUT) @@ -156,36 +155,6 @@ class RestClient(object): result['error']['code'] = 0 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" @@ -202,20 +171,19 @@ class RestClient(object): def _assert_data_in_result(self, result, msg): if 'data' not in result: - err_msg = (_('%s "data" was not in result.') % msg) + err_msg = _('%s "data" is not in result.') % msg LOG.error(err_msg) raise exception.VolumeBackendAPIException(data=err_msg) - def create_volume(self, lun_param): + def create_lun(self, lun_params): url = "/lun" - data = json.dumps(lun_param) - result = self.call(url, data) + result = self.call(url, lun_params) if result['error']['code'] == constants.ERROR_VOLUME_ALREADY_EXIST: - lun_id = self.get_volume_by_name(lun_param["NAME"]) + lun_id = self.get_lun_id_by_name(lun_params['NAME']) if lun_id: return self.get_lun_info(lun_id) - msg = _('Create volume error.') + msg = _('Create lun error.') self._assert_rest_result(result, msg) self._assert_data_in_result(result, msg) @@ -231,42 +199,55 @@ class RestClient(object): return True def delete_lun(self, lun_id): - lun_group_ids = self.get_lungroupids_by_lunid(lun_id) - if lun_group_ids and len(lun_group_ids) == 1: - self.remove_lun_from_lungroup(lun_group_ids[0], lun_id) - url = "/lun/" + lun_id - data = json.dumps({"TYPE": "11", - "ID": lun_id}) + data = {"TYPE": "11", + "ID": lun_id} result = self.call(url, data, "DELETE") self._assert_rest_result(result, _('Delete lun error.')) - def find_all_pools(self): + def get_all_pools(self): url = "/storagepool" result = self.call(url, None) msg = _('Query resource pool error.') self._assert_rest_result(result, msg) self._assert_data_in_result(result, msg) - return result + return result['data'] - def find_pool_info(self, pool_name=None, result=None): - pool_info = {} + def get_pool_info(self, pool_name=None, pools=None): + info = {} if not pool_name: - return pool_info + return info - if 'data' in result: - for item in result['data']: - if pool_name.strip() == item['NAME']: - # USAGETYPE means pool type. - if ('USAGETYPE' in item and - item['USAGETYPE'] == constants.FILE_SYSTEM_POOL_TYPE): - break - pool_info['ID'] = item['ID'] - pool_info['CAPACITY'] = item.get('DATASPACE', - item['USERFREECAPACITY']) - pool_info['TOTALCAPACITY'] = item['USERTOTALCAPACITY'] + for pool in pools: + if pool_name.strip() != pool['NAME']: + continue + + if pool.get('USAGETYPE') == constants.FILE_SYSTEM_POOL_TYPE: + break + + info['ID'] = pool['ID'] + info['CAPACITY'] = pool.get('DATASPACE', pool['USERFREECAPACITY']) + info['TOTALCAPACITY'] = pool['USERTOTALCAPACITY'] + + return info + + def get_pool_id(self, volume, pool_name): + pools = self.get_all_pools() + pool_info = self.get_pool_info(pool_name, pools) + if not pool_info: + # The following code is to keep compatibility with old version of + # Huawei driver. + for pool_name in self.configuration.storage_pools: + pool_info = self.get_pool_info(pool_name, pools) + if pool_info: break - return pool_info + + if not pool_info: + msg = _('Can not get pool info. pool: %s') % pool_name + LOG.error(msg) + raise exception.VolumeBackendAPIException(data=msg) + + return pool_info['ID'] def _get_id_from_result(self, result, name, key): if 'data' in result: @@ -274,39 +255,26 @@ class RestClient(object): if name == item.get(key): return item['ID'] - def get_volume_by_name(self, name): + def get_lun_id_by_name(self, name): url = "/lun?range=[0-65535]" result = self.call(url, None, "GET") - self._assert_rest_result(result, _('Get volume by name error.')) + self._assert_rest_result(result, _('Get lun id by name error.')) return self._get_id_from_result(result, name, 'NAME') def activate_snapshot(self, snapshot_id): - activate_url = "/snapshot/activate" - data = json.dumps({"SNAPSHOTLIST": [snapshot_id]}) - result = self.call(activate_url, data) + url = "/snapshot/activate" + data = {"SNAPSHOTLIST": [snapshot_id]} + result = self.call(url, data) self._assert_rest_result(result, _('Activate snapshot error.')) - def create_snapshot(self, snapshot): - snapshot_name = huawei_utils.encode_name(snapshot['id']) - snapshot_description = snapshot['id'] - volume_name = huawei_utils.encode_name(snapshot['volume_id']) - - LOG.info(_LI( - 'create_snapshot:snapshot name: %(snapshot)s, ' - 'volume name: %(volume)s.'), - {'snapshot': snapshot_name, - 'volume': volume_name}) - - volume = snapshot['volume'] - lun_id = self.get_lunid(volume, volume_name) - + def create_snapshot(self, lun_id, snapshot_name, snapshot_description): url = "/snapshot" - data = json.dumps({"TYPE": "27", - "NAME": snapshot_name, - "PARENTTYPE": "11", - "DESCRIPTION": snapshot_description, - "PARENTID": lun_id}) + data = {"TYPE": "27", + "NAME": snapshot_name, + "PARENTTYPE": "11", + "DESCRIPTION": snapshot_description, + "PARENTID": lun_id} result = self.call(url, data) msg = _('Create snapshot error.') @@ -315,12 +283,13 @@ class RestClient(object): return result['data'] - def get_lunid(self, volume, volume_name): + def get_lun_id(self, volume, volume_name): lun_id = (volume.get('provider_location') or - self.get_volume_by_name(volume_name)) + self.get_lun_id_by_name(volume_name)) if not lun_id: - msg = (_("Can't find lun info on the array, " - "lun name is: %(name)s.") % {'name': volume_name}) + msg = (_("Can't find lun info on the array. " + "volume: %(id)s, lun name: %(name)s.") % + {'id': volume['id'], 'name': volume_name}) LOG.error(msg) raise exception.VolumeBackendAPIException(data=msg) return lun_id @@ -336,17 +305,17 @@ class RestClient(object): def stop_snapshot(self, snapshot_id): url = "/snapshot/stop" - stopdata = json.dumps({"ID": snapshot_id}) + stopdata = {"ID": snapshot_id} result = self.call(url, stopdata, "PUT") self._assert_rest_result(result, _('Stop snapshot error.')) def delete_snapshot(self, snapshotid): url = "/snapshot/%s" % snapshotid - data = json.dumps({"TYPE": "27", "ID": snapshotid}) + data = {"TYPE": "27", "ID": snapshotid} result = self.call(url, data, "DELETE") self._assert_rest_result(result, _('Delete snapshot error.')) - def get_snapshotid_by_name(self, name): + def get_snapshot_id_by_name(self, name): url = "/snapshot?range=[0-32767]" result = self.call(url, None, "GET") self._assert_rest_result(result, _('Get snapshot id error.')) @@ -356,15 +325,15 @@ class RestClient(object): def create_luncopy(self, luncopyname, srclunid, tgtlunid): """Create a luncopy.""" url = "/luncopy" - data = json.dumps({"TYPE": 219, - "NAME": luncopyname, - "DESCRIPTION": luncopyname, - "COPYSPEED": 2, - "LUNCOPYTYPE": "1", - "SOURCELUN": ("INVALID;%s;INVALID;INVALID;INVALID" - % srclunid), - "TARGETLUN": ("INVALID;%s;INVALID;INVALID;INVALID" - % tgtlunid)}) + data = {"TYPE": 219, + "NAME": luncopyname, + "DESCRIPTION": luncopyname, + "COPYSPEED": 2, + "LUNCOPYTYPE": "1", + "SOURCELUN": ("INVALID;%s;INVALID;INVALID;INVALID" + % srclunid), + "TARGETLUN": ("INVALID;%s;INVALID;INVALID;INVALID" + % tgtlunid)} result = self.call(url, data) msg = _('Create luncopy error.') @@ -373,7 +342,7 @@ class RestClient(object): return result['data']['ID'] - def add_host_into_hostgroup(self, host_id): + def add_host_to_hostgroup(self, host_id): """Associate host to hostgroup. If hostgroup doesn't exist, create one. @@ -387,7 +356,7 @@ class RestClient(object): return hostgroup_id - def find_tgt_port_group(self, tgt_port_group): + def get_tgt_port_group(self, tgt_port_group): """Find target portgroup id by target port group name.""" url = "/portgroup?range=[0-8191]&TYPE=257" result = self.call(url, None, "GET") @@ -401,10 +370,10 @@ class RestClient(object): def _associate_portgroup_to_view(self, view_id, portgroup_id): url = "/MAPPINGVIEW/CREATE_ASSOCIATE" - data = json.dumps({"ASSOCIATEOBJTYPE": "257", - "ASSOCIATEOBJID": portgroup_id, - "TYPE": "245", - "ID": view_id}) + data = {"ASSOCIATEOBJTYPE": "257", + "ASSOCIATEOBJID": portgroup_id, + "TYPE": "245", + "ID": view_id} result = self.call(url, data, "PUT") self._assert_rest_result(result, _('Associate portgroup to mapping ' 'view error.')) @@ -477,13 +446,21 @@ class RestClient(object): return map_info - def ensure_initiator_added(self, xml_file_path, initiator_name, host_id): + def check_iscsi_initiators_exist_in_host(self, host_id): + url = "/iscsi_initiator?range=[0-100]&PARENTID=%s" % host_id + result = self.call(url, None, "GET") + self._assert_rest_result(result, 'Get host initiators info failed.') + if "data" in result: + return True + + return False + + def ensure_initiator_added(self, initiator_name, host_id): added = self._initiator_is_added_to_array(initiator_name) if not added: self._add_initiator_to_array(initiator_name) if not self.is_initiator_associated_to_host(initiator_name): - self._associate_initiator_to_host(xml_file_path, - initiator_name, + self._associate_initiator_to_host(initiator_name, host_id) def _get_iscsi_tgt_port(self): @@ -551,7 +528,7 @@ class RestClient(object): def _create_hostgroup(self, hostgroup_name): url = "/hostgroup" - data = json.dumps({"TYPE": "14", "NAME": hostgroup_name}) + data = {"TYPE": "14", "NAME": hostgroup_name} result = self.call(url, data) msg = _('Create hostgroup error.') @@ -562,10 +539,10 @@ class RestClient(object): def _create_lungroup(self, lungroup_name): url = "/lungroup" - data = json.dumps({"DESCRIPTION": lungroup_name, - "APPTYPE": '0', - "GROUPTYPE": '0', - "NAME": lungroup_name}) + data = {"DESCRIPTION": lungroup_name, + "APPTYPE": '0', + "GROUPTYPE": '0', + "NAME": lungroup_name} result = self.call(url, data) msg = _('Create lungroup error.') @@ -580,9 +557,8 @@ class RestClient(object): self._assert_rest_result(result, _('Delete lungroup error.')) def lungroup_associated(self, view_id, lungroup_id): - url_subfix = ("/mappingview/associate?TYPE=245&" - "ASSOCIATEOBJTYPE=256&ASSOCIATEOBJID=%s" % lungroup_id) - url = url_subfix + url = ("/mappingview/associate?TYPE=245&" + "ASSOCIATEOBJTYPE=256&ASSOCIATEOBJID=%s" % lungroup_id) result = self.call(url, None, "GET") self._assert_rest_result(result, _('Check lungroup associate error.')) @@ -591,9 +567,8 @@ class RestClient(object): return False def hostgroup_associated(self, view_id, hostgroup_id): - url_subfix = ("/mappingview/associate?TYPE=245&" - "ASSOCIATEOBJTYPE=14&ASSOCIATEOBJID=%s" % hostgroup_id) - url = url_subfix + url = ("/mappingview/associate?TYPE=245&" + "ASSOCIATEOBJTYPE=14&ASSOCIATEOBJID=%s" % hostgroup_id) result = self.call(url, None, "GET") self._assert_rest_result(result, _('Check hostgroup associate error.')) @@ -601,7 +576,7 @@ class RestClient(object): return True return False - def find_host_lun_id(self, host_id, lun_id): + def get_host_lun_id(self, host_id, lun_id): url = ("/lun/associate?TYPE=11&ASSOCIATEOBJTYPE=21" "&ASSOCIATEOBJID=%s" % (host_id)) result = self.call(url, None, "GET") @@ -621,7 +596,7 @@ class RestClient(object): raise return host_lun_id - def find_host(self, host_name): + def get_host_id_by_name(self, host_name): """Get the given host ID.""" url = "/host?range=[0-65535]" result = self.call(url, None, "GET") @@ -630,7 +605,7 @@ class RestClient(object): return self._get_id_from_result(result, host_name, 'NAME') def add_host_with_check(self, host_name, host_name_before_hash): - host_id = self.find_host(host_name) + host_id = self.get_host_id_by_name(host_name) if host_id: LOG.info(_LI( 'add_host_with_check. ' @@ -647,7 +622,7 @@ class RestClient(object): 'Failed to create host: %(name)s. ' 'Check if it exists on the array.'), {'name': host_name}) - host_id = self.find_host(host_name) + host_id = self.get_host_id_by_name(host_name) if not host_id: err_msg = (_( 'Failed to create host: %(name)s. ' @@ -668,10 +643,10 @@ class RestClient(object): def _add_host(self, hostname, host_name_before_hash): """Add a new host.""" url = "/host" - data = json.dumps({"TYPE": "21", - "NAME": hostname, - "OPERATIONSYSTEM": "0", - "DESCRIPTION": host_name_before_hash}) + data = {"TYPE": "21", + "NAME": hostname, + "OPERATIONSYSTEM": "0", + "DESCRIPTION": host_name_before_hash} result = self.call(url, data) self._assert_rest_result(result, _('Add new host error.')) @@ -680,10 +655,9 @@ class RestClient(object): def _is_host_associate_to_hostgroup(self, hostgroup_id, host_id): """Check whether the host is associated to the hostgroup.""" - url_subfix = ("/host/associate?TYPE=21&" - "ASSOCIATEOBJTYPE=14&ASSOCIATEOBJID=%s" % hostgroup_id) + url = ("/host/associate?TYPE=21&" + "ASSOCIATEOBJTYPE=14&ASSOCIATEOBJID=%s" % hostgroup_id) - url = url_subfix result = self.call(url, None, "GET") self._assert_rest_result(result, _('Check hostgroup associate error.')) @@ -694,10 +668,9 @@ class RestClient(object): def _is_lun_associated_to_lungroup(self, lungroup_id, lun_id): """Check whether the lun is associated to the lungroup.""" - url_subfix = ("/lun/associate?TYPE=11&" - "ASSOCIATEOBJTYPE=256&ASSOCIATEOBJID=%s" % lungroup_id) + url = ("/lun/associate?TYPE=11&" + "ASSOCIATEOBJTYPE=256&ASSOCIATEOBJID=%s" % lungroup_id) - url = url_subfix result = self.call(url, None, "GET") self._assert_rest_result(result, _('Check lungroup associate error.')) @@ -708,10 +681,10 @@ class RestClient(object): def _associate_host_to_hostgroup(self, hostgroup_id, host_id): url = "/hostgroup/associate" - data = json.dumps({"TYPE": "14", - "ID": hostgroup_id, - "ASSOCIATEOBJTYPE": "21", - "ASSOCIATEOBJID": host_id}) + data = {"TYPE": "14", + "ID": hostgroup_id, + "ASSOCIATEOBJTYPE": "21", + "ASSOCIATEOBJID": host_id} result = self.call(url, data) self._assert_rest_result(result, _('Associate host to hostgroup ' @@ -720,9 +693,9 @@ class RestClient(object): def associate_lun_to_lungroup(self, lungroup_id, lun_id): """Associate lun to lungroup.""" url = "/lungroup/associate" - data = json.dumps({"ID": lungroup_id, - "ASSOCIATEOBJTYPE": "11", - "ASSOCIATEOBJID": lun_id}) + data = {"ID": lungroup_id, + "ASSOCIATEOBJTYPE": "11", + "ASSOCIATEOBJID": lun_id} result = self.call(url, data) self._assert_rest_result(result, _('Associate lun to lungroup error.')) @@ -762,34 +735,31 @@ class RestClient(object): def _add_initiator_to_array(self, initiator_name): """Add a new initiator to storage device.""" url = "/iscsi_initiator" - data = json.dumps({"TYPE": "222", - "ID": initiator_name, - "USECHAP": "false"}) + data = {"TYPE": "222", + "ID": initiator_name, + "USECHAP": "false"} result = self.call(url, data, "POST") self._assert_rest_result(result, _('Add initiator to array error.')) def _add_initiator_to_host(self, initiator_name, host_id): url = "/iscsi_initiator/" + initiator_name - data = json.dumps({"TYPE": "222", - "ID": initiator_name, - "USECHAP": "false", - "PARENTTYPE": "21", - "PARENTID": host_id}) + data = {"TYPE": "222", + "ID": initiator_name, + "USECHAP": "false", + "PARENTTYPE": "21", + "PARENTID": host_id} result = self.call(url, data, "PUT") self._assert_rest_result(result, _('Associate initiator to host error.')) def _associate_initiator_to_host(self, - xml_file_path, initiator_name, host_id): """Associate initiator with the host.""" - iscsi_conf = huawei_utils.get_iscsi_conf(xml_file_path) - - chapinfo = self.find_chap_info(iscsi_conf, + chapinfo = self.find_chap_info(self.configuration.iscsi_info, initiator_name) - multipath_type = self._find_alua_info(iscsi_conf, + multipath_type = self._find_alua_info(self.configuration.iscsi_info, initiator_name) if chapinfo: LOG.info(_LI('Use CHAP when adding initiator to host.')) @@ -801,10 +771,10 @@ class RestClient(object): LOG.info(_LI('Use ALUA when adding initiator to host.')) self._use_alua(initiator_name, multipath_type) - def find_chap_info(self, iscsi_conf, initiator_name): + def find_chap_info(self, iscsi_info, initiator_name): """Find CHAP info from xml.""" chapinfo = None - for ini in iscsi_conf['Initiator']: + for ini in iscsi_info: if ini['Name'] == initiator_name: if 'CHAPinfo' in ini: chapinfo = ini['CHAPinfo'] @@ -812,10 +782,10 @@ class RestClient(object): return chapinfo - def _find_alua_info(self, iscsi_conf, initiator_name): + def _find_alua_info(self, iscsi_info, initiator_name): """Find ALUA info from xml.""" multipath_type = 0 - for ini in iscsi_conf['Initiator']: + for ini in iscsi_info: if ini['Name'] == initiator_name: if 'ALUA' in ini: if ini['ALUA'] != '1' and ini['ALUA'] != '0': @@ -834,13 +804,13 @@ class RestClient(object): (chap_username, chap_password) = chapinfo.split(";") url = "/iscsi_initiator/" + initiator_name - data = json.dumps({"TYPE": "222", - "USECHAP": "true", - "CHAPNAME": chap_username, - "CHAPPASSWORD": chap_password, - "ID": initiator_name, - "PARENTTYPE": "21", - "PARENTID": host_id}) + data = {"TYPE": "222", + "USECHAP": "true", + "CHAPNAME": chap_username, + "CHAPPASSWORD": chap_password, + "ID": initiator_name, + "PARENTTYPE": "21", + "PARENTID": host_id} result = self.call(url, data, "PUT") msg = _('Use CHAP to associate initiator to host error. ' 'Please check the CHAP username and password.') @@ -849,8 +819,8 @@ class RestClient(object): def _use_alua(self, initiator_name, multipath_type): """Use ALUA when adding initiator to host.""" url = "/iscsi_initiator" - data = json.dumps({"ID": initiator_name, - "MULTIPATHTYPE": multipath_type}) + data = {"ID": initiator_name, + "MULTIPATHTYPE": multipath_type} result = self.call(url, data, "PUT") self._assert_rest_result( @@ -859,9 +829,9 @@ class RestClient(object): def remove_chap(self, initiator_name): """Remove CHAP when terminate connection.""" url = "/iscsi_initiator" - data = json.dumps({"USECHAP": "false", + data = {"USECHAP": "false", "MULTIPATHTYPE": "0", - "ID": initiator_name}) + "ID": initiator_name} result = self.call(url, data, "PUT") self._assert_rest_result(result, _('Remove CHAP error.')) @@ -878,7 +848,7 @@ class RestClient(object): def _add_mapping_view(self, name): url = "/mappingview" - data = json.dumps({"NAME": name, "TYPE": "245"}) + data = {"NAME": name, "TYPE": "245"} result = self.call(url, data) self._assert_rest_result(result, _('Add mapping view error.')) @@ -886,20 +856,21 @@ class RestClient(object): def _associate_hostgroup_to_view(self, view_id, hostgroup_id): url = "/MAPPINGVIEW/CREATE_ASSOCIATE" - data = json.dumps({"ASSOCIATEOBJTYPE": "14", - "ASSOCIATEOBJID": hostgroup_id, - "TYPE": "245", - "ID": view_id}) + data = {"ASSOCIATEOBJTYPE": "14", + "ASSOCIATEOBJID": hostgroup_id, + "TYPE": "245", + "ID": view_id} result = self.call(url, data, "PUT") self._assert_rest_result(result, _('Associate host to mapping view ' 'error.')) def _associate_lungroup_to_view(self, view_id, lungroup_id): url = "/MAPPINGVIEW/CREATE_ASSOCIATE" - data = json.dumps({"ASSOCIATEOBJTYPE": "256", - "ASSOCIATEOBJID": lungroup_id, - "TYPE": "245", - "ID": view_id}) + data = {"ASSOCIATEOBJTYPE": "256", + "ASSOCIATEOBJID": lungroup_id, + "TYPE": "245", + "ID": view_id} + result = self.call(url, data, "PUT") self._assert_rest_result( result, _('Associate lungroup to mapping view error.')) @@ -907,10 +878,10 @@ class RestClient(object): def delete_lungroup_mapping_view(self, view_id, lungroup_id): """Remove lungroup associate from the mapping view.""" url = "/mappingview/REMOVE_ASSOCIATE" - data = json.dumps({"ASSOCIATEOBJTYPE": "256", - "ASSOCIATEOBJID": lungroup_id, - "TYPE": "245", - "ID": view_id}) + data = {"ASSOCIATEOBJTYPE": "256", + "ASSOCIATEOBJID": lungroup_id, + "TYPE": "245", + "ID": view_id} result = self.call(url, data, "PUT") self._assert_rest_result(result, _('Delete lungroup from mapping view ' 'error.')) @@ -918,10 +889,11 @@ class RestClient(object): def delete_hostgoup_mapping_view(self, view_id, hostgroup_id): """Remove hostgroup associate from the mapping view.""" url = "/mappingview/REMOVE_ASSOCIATE" - data = json.dumps({"ASSOCIATEOBJTYPE": "14", - "ASSOCIATEOBJID": hostgroup_id, - "TYPE": "245", - "ID": view_id}) + data = {"ASSOCIATEOBJTYPE": "14", + "ASSOCIATEOBJID": hostgroup_id, + "TYPE": "245", + "ID": view_id} + result = self.call(url, data, "PUT") self._assert_rest_result( result, _('Delete hostgroup from mapping view error.')) @@ -929,10 +901,11 @@ class RestClient(object): def delete_portgroup_mapping_view(self, view_id, portgroup_id): """Remove portgroup associate from the mapping view.""" url = "/mappingview/REMOVE_ASSOCIATE" - data = json.dumps({"ASSOCIATEOBJTYPE": "257", - "ASSOCIATEOBJID": portgroup_id, - "TYPE": "245", - "ID": view_id}) + data = {"ASSOCIATEOBJTYPE": "257", + "ASSOCIATEOBJID": portgroup_id, + "TYPE": "245", + "ID": view_id} + result = self.call(url, data, "PUT") self._assert_rest_result( result, _('Delete portgroup from mapping view error.')) @@ -945,9 +918,8 @@ class RestClient(object): def get_lunnum_from_lungroup(self, lungroup_id): """Check if there are still other luns associated to the lungroup.""" - url_subfix = ("/lun/count?TYPE=11&ASSOCIATEOBJTYPE=256&" - "ASSOCIATEOBJID=%s" % lungroup_id) - url = url_subfix + url = ("/lun/count?TYPE=11&ASSOCIATEOBJTYPE=256&" + "ASSOCIATEOBJID=%s" % lungroup_id) result = self.call(url, None, "GET") self._assert_rest_result(result, _('Find lun number error.')) lunnum = -1 @@ -957,9 +929,8 @@ class RestClient(object): def is_portgroup_associated_to_view(self, view_id, portgroup_id): """Check whether the port group is associated to the mapping view.""" - url_subfix = ("/portgroup/associate?ASSOCIATEOBJTYPE=245&" - "ASSOCIATEOBJID=%s&range=[0-8191]" % view_id) - url = url_subfix + url = ("/portgroup/associate?ASSOCIATEOBJTYPE=245&" + "ASSOCIATEOBJID=%s&range=[0-8191]" % view_id) result = self.call(url, None, "GET") self._assert_rest_result(result, _('Find portgroup from mapping view ' 'error.')) @@ -970,9 +941,8 @@ class RestClient(object): def find_lungroup_from_map(self, view_id): """Get lungroup from the given map""" - url_subfix = ("/mappingview/associate/lungroup?TYPE=256&" - "ASSOCIATEOBJTYPE=245&ASSOCIATEOBJID=%s" % view_id) - url = url_subfix + url = ("/mappingview/associate/lungroup?TYPE=256&" + "ASSOCIATEOBJTYPE=245&ASSOCIATEOBJID=%s" % view_id) result = self.call(url, None, "GET") self._assert_rest_result(result, _('Find lun group from mapping view ' 'error.')) @@ -987,13 +957,13 @@ class RestClient(object): def start_luncopy(self, luncopy_id): """Start a LUNcopy.""" url = "/LUNCOPY/start" - data = json.dumps({"TYPE": "219", "ID": luncopy_id}) + data = {"TYPE": "219", "ID": luncopy_id} result = self.call(url, data, "PUT") self._assert_rest_result(result, _('Start LUNcopy error.')) def _get_capacity(self, pool_name, result): """Get free capacity and total capacity of the pool.""" - pool_info = self.find_pool_info(pool_name, result) + pool_info = self.get_pool_info(pool_name, result) pool_capacity = {'total_capacity': 0.0, 'free_capacity': 0.0} @@ -1064,10 +1034,10 @@ class RestClient(object): def add_fc_port_to_host(self, host_id, wwn): """Add a FC port to the host.""" url = "/fc_initiator/" + wwn - data = json.dumps({"TYPE": "223", - "ID": wwn, - "PARENTTYPE": 21, - "PARENTID": host_id}) + data = {"TYPE": "223", + "ID": wwn, + "PARENTTYPE": 21, + "PARENTID": host_id} result = self.call(url, data, "PUT") self._assert_rest_result(result, _('Add FC port to host error.')) @@ -1136,19 +1106,10 @@ class RestClient(object): return fc_wwpns def update_volume_stats(self): - root = huawei_utils.parse_xml_file(self.xml_file_path) - pool_names = root.findtext('LUN/StoragePool') - if not pool_names: - msg = _( - 'Invalid resource pool name. ' - 'Please check the config file.') - LOG.error(msg) - raise exception.InvalidInput(msg) data = {} data['pools'] = [] - result = self.find_all_pools() - for pool_name in pool_names.split(";"): - pool_name = pool_name.strip(' \t\n\r') + result = self.get_all_pools() + for pool_name in self.configuration.storage_pools: capacity = self._get_capacity(pool_name, result) pool = {} pool.update(dict( @@ -1192,9 +1153,10 @@ class RestClient(object): def _update_qos_policy_lunlist(self, lun_list, policy_id): url = "/ioclass/" + policy_id - data = json.dumps({"TYPE": "230", - "ID": policy_id, - "LUNLIST": lun_list}) + data = {"TYPE": "230", + "ID": policy_id, + "LUNLIST": lun_list} + result = self.call(url, data, "PUT") self._assert_rest_result(result, _('Update QoS policy error.')) @@ -1220,15 +1182,14 @@ class RestClient(object): return target_ips - def get_iscsi_params(self, xml_file_path, connector): + def get_iscsi_params(self, connector): """Get target iSCSI params, including iqn, IP.""" initiator = connector['initiator'] - iscsi_conf = huawei_utils.get_iscsi_conf(xml_file_path) target_ips = [] target_iqns = [] portgroup = None portgroup_id = None - for ini in iscsi_conf['Initiator']: + for ini in self.configuration.iscsi_info: if ini['Name'] == initiator: for key in ini: if key == 'TargetPortGroup': @@ -1237,13 +1198,14 @@ class RestClient(object): target_ips.append(ini['TargetIP']) if portgroup: - portgroup_id = self.find_tgt_port_group(portgroup) + portgroup_id = self.get_tgt_port_group(portgroup) target_ips = self._get_tgt_ip_from_portgroup(portgroup_id) # If not specify target IP for some initiators, use default IP. if not target_ips: - if iscsi_conf['DefaultTargetIP']: - target_ips.append(iscsi_conf['DefaultTargetIP']) + default_target_ips = self.configuration.iscsi_default_target_ip + if default_target_ips: + target_ips.append(default_target_ips[0]) else: msg = (_( @@ -1298,18 +1260,17 @@ class RestClient(object): # Package QoS name. qos_name = constants.QOS_NAME_PREFIX + lun_id + '_' + localtime - mergedata = {"TYPE": "230", - "NAME": qos_name, - "LUNLIST": ["%s" % lun_id], - "CLASSTYPE": "1", - "SCHEDULEPOLICY": "2", - "SCHEDULESTARTTIME": "1410969600", - "STARTTIME": "08:00", - "DURATION": "86400", - "CYCLESET": "[1,2,3,4,5,6,0]", - } - mergedata.update(qos) - data = json.dumps(mergedata) + data = {"TYPE": "230", + "NAME": qos_name, + "LUNLIST": ["%s" % lun_id], + "CLASSTYPE": "1", + "SCHEDULEPOLICY": "2", + "SCHEDULESTARTTIME": "1410969600", + "STARTTIME": "08:00", + "DURATION": "86400", + "CYCLESET": "[1,2,3,4,5,6,0]", + } + data.update(qos) url = "/ioclass/" result = self.call(url, data) @@ -1320,8 +1281,7 @@ class RestClient(object): def delete_qos_policy(self, qos_id): """Delete a QoS policy.""" url = "/ioclass/" + qos_id - data = json.dumps({"TYPE": "230", - "ID": qos_id}) + data = {"TYPE": "230", "ID": qos_id} result = self.call(url, data, 'DELETE') self._assert_rest_result(result, _('Delete QoS policy error.')) @@ -1333,9 +1293,9 @@ class RestClient(object): enbalestatus: false (deactivate) """ url = "/ioclass/active/" + qos_id - data = json.dumps({"TYPE": 230, - "ID": qos_id, - "ENABLESTATUS": enablestatus}) + data = {"TYPE": 230, + "ID": qos_id, + "ENABLESTATUS": enablestatus} result = self.call(url, data, "PUT") self._assert_rest_result( result, _('Activate or deactivate QoS error.')) @@ -1348,9 +1308,8 @@ class RestClient(object): return result['data'] - def get_lun_list_in_qos(self, qos_id): + def get_lun_list_in_qos(self, qos_id, qos_info): """Get the lun list in QoS.""" - qos_info = self.get_qos_info(qos_id) lun_list = [] lun_string = qos_info['LUNLIST'][1:-1] @@ -1364,21 +1323,21 @@ class RestClient(object): """Remove lun from QoS.""" lun_list = [i for i in lun_list if i != lun_id] url = "/ioclass/" + qos_id - data = json.dumps({"LUNLIST": lun_list, - "TYPE": 230, - "ID": qos_id}) + data = {"LUNLIST": lun_list, + "TYPE": 230, + "ID": qos_id} result = self.call(url, data, "PUT") - msg = _('Remove lun from Qos error.') + msg = _('Remove lun from QoS error.') self._assert_rest_result(result, msg) self._assert_data_in_result(result, msg) def change_lun_priority(self, lun_id): """Change lun priority to high.""" url = "/lun/" + lun_id - data = json.dumps({"TYPE": "11", - "ID": lun_id, - "IOPRIORITY": "3"}) + data = {"TYPE": "11", + "ID": lun_id, + "IOPRIORITY": "3"} result = self.call(url, data, "PUT") self._assert_rest_result(result, _('Change lun priority error.')) @@ -1386,9 +1345,9 @@ class RestClient(object): def change_lun_smarttier(self, lunid, smarttier_policy): """Change lun smarttier policy.""" url = "/lun/" + lunid - data = json.dumps({"TYPE": "11", - "ID": lunid, - "DATATRANSFERPOLICY": smarttier_policy}) + data = {"TYPE": "11", + "ID": lunid, + "DATATRANSFERPOLICY": smarttier_policy} result = self.call(url, data, "PUT") self._assert_rest_result( @@ -1427,10 +1386,10 @@ class RestClient(object): return result['data'] - def extend_volume(self, lun_id, new_volume_size): + def extend_lun(self, lun_id, new_volume_size): url = "/lun/expand" - data = json.dumps({"TYPE": 11, "ID": lun_id, - "CAPACITY": new_volume_size}) + data = {"TYPE": 11, "ID": lun_id, + "CAPACITY": new_volume_size} result = self.call(url, data, 'PUT') msg = _('Extend volume error.') @@ -1441,11 +1400,11 @@ class RestClient(object): def create_lun_migration(self, src_id, dst_id, speed=2): url = "/LUN_MIGRATION" - data = json.dumps({"TYPE": '253', - "PARENTID": src_id, - "TARGETLUNID": dst_id, - "SPEED": speed, - "WORKMODE": 0}) + data = {"TYPE": '253', + "PARENTID": src_id, + "TARGETLUNID": dst_id, + "SPEED": speed, + "WORKMODE": 0} result = self.call(url, data, "POST") msg = _('Create lun migration error.') @@ -1483,9 +1442,9 @@ class RestClient(object): def add_lun_to_partition(self, lun_id, partition_id): url = "/lun/associate/cachepartition" - data = json.dumps({"ID": partition_id, - "ASSOCIATEOBJTYPE": 11, - "ASSOCIATEOBJID": lun_id, }) + data = {"ID": partition_id, + "ASSOCIATEOBJTYPE": 11, + "ASSOCIATEOBJID": lun_id} result = self.call(url, data, "POST") self._assert_rest_result(result, _('Add lun to partition error.')) @@ -1505,8 +1464,8 @@ class RestClient(object): def get_cache_info_by_id(self, cacheid): url = "/SMARTCACHEPARTITION/" + cacheid - data = json.dumps({"TYPE": "273", - "ID": cacheid}) + data = {"TYPE": "273", + "ID": cacheid} result = self.call(url, data, "GET") self._assert_rest_result( @@ -1516,10 +1475,10 @@ class RestClient(object): def remove_lun_from_cache(self, lun_id, cache_id): url = "/SMARTCACHEPARTITION/REMOVE_ASSOCIATE" - data = json.dumps({"ID": cache_id, - "ASSOCIATEOBJTYPE": 11, - "ASSOCIATEOBJID": lun_id, - "TYPE": 273}) + data = {"ID": cache_id, + "ASSOCIATEOBJTYPE": 11, + "ASSOCIATEOBJID": lun_id, + "TYPE": 273} result = self.call(url, data, "PUT") self._assert_rest_result(result, _('Remove lun from cache error.')) @@ -1568,27 +1527,27 @@ class RestClient(object): new_lun_list.append(lun_id) - data = json.dumps({"LUNLIST": new_lun_list, - "TYPE": 230, - "ID": qos_id}) + data = {"LUNLIST": new_lun_list, + "TYPE": 230, + "ID": qos_id} result = self.call(url, data, "PUT") - msg = _('Associate lun to Qos error.') + msg = _('Associate lun to QoS error.') self._assert_rest_result(result, msg) self._assert_data_in_result(result, msg) def add_lun_to_cache(self, lun_id, cache_id): url = "/SMARTCACHEPARTITION/CREATE_ASSOCIATE" - data = json.dumps({"ID": cache_id, - "ASSOCIATEOBJTYPE": 11, - "ASSOCIATEOBJID": lun_id, - "TYPE": 273}) + data = {"ID": cache_id, + "ASSOCIATEOBJTYPE": 11, + "ASSOCIATEOBJID": lun_id, + "TYPE": 273} result = self.call(url, data, "PUT") self._assert_rest_result(result, _('Add lun to cache error.')) def find_array_version(self): url = "/system/" - result = self.call(url, None) + result = self.call(url, None, "GET") self._assert_rest_result(result, _('Find array version error.')) return result['data']['PRODUCTVERSION'] @@ -1612,8 +1571,8 @@ class RestClient(object): def remove_iscsi_from_host(self, initiator): url = "/iscsi_initiator/remove_iscsi_from_host" - data = json.dumps({"TYPE": '222', - "ID": initiator}) + data = {"TYPE": '222', + "ID": initiator} result = self.call(url, data, "PUT") self._assert_rest_result(result, _('Remove iscsi from host error.')) @@ -1654,11 +1613,9 @@ class RestClient(object): return initiators - def rename_lun(self, lun_id, new_name, description=None): + def rename_lun(self, lun_id, new_name): url = "/lun/" + lun_id - data = json.dumps({"NAME": new_name}) - if description: - data.update({"DESCRIPTION": description}) + data = {"NAME": new_name} result = self.call(url, data, "PUT") msg = _('Rename lun on array error.') self._assert_rest_result(result, msg) @@ -1679,8 +1636,8 @@ class RestClient(object): def remove_fc_from_host(self, initiator): url = '/fc_initiator/remove_fc_from_host' - data = json.dumps({"TYPE": '223', - "ID": initiator}) + data = {"TYPE": '223', + "ID": initiator} result = self.call(url, data, "PUT") self._assert_rest_result(result, _('Remove fc from host error.')) @@ -1706,8 +1663,8 @@ class RestClient(object): def _add_fc_initiator_to_array(self, ininame): """Add a fc initiator to storage device.""" url = '/fc_initiator/' - data = json.dumps({"TYPE": '223', - "ID": ininame}) + data = {"TYPE": '223', + "ID": ininame} result = self.call(url, data) self._assert_rest_result(result, _('Add fc initiator to array error.')) @@ -1753,8 +1710,7 @@ class RestClient(object): def create_hypermetro(self, hcp_param): url = "/HyperMetroPair" - data = json.dumps(hcp_param) - result = self.call(url, data, "POST") + result = self.call(url, hcp_param, "POST") msg = _('create_hypermetro_pair error.') self._assert_rest_result(result, msg) @@ -1771,8 +1727,8 @@ class RestClient(object): def sync_hypermetro(self, metro_id): url = "/HyperMetroPair/synchronize_hcpair" - data = json.dumps({"ID": metro_id, - "TYPE": "15361"}) + data = {"ID": metro_id, + "TYPE": "15361"} result = self.call(url, data, "PUT") msg = _('sync_hypermetro error.') @@ -1781,8 +1737,8 @@ class RestClient(object): def stop_hypermetro(self, metro_id): url = '/HyperMetroPair/disable_hcpair' - data = json.dumps({"ID": metro_id, - "TYPE": "15361"}) + data = {"ID": metro_id, + "TYPE": "15361"} result = self.call(url, data, "PUT") msg = _('stop_hypermetro error.') @@ -1819,18 +1775,22 @@ class RestClient(object): return True + def get_volume_by_name(self, name): + url = "/lun?range=[0-65535]" + result = self.call(url, None, "GET") + self._assert_rest_result(result, _('Get volume by name error.')) + 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}] - }) + data = {"TYPE": 245, + "ID": view_id, + "ASSOCIATEOBJTYPE": 11, + "ASSOCIATEOBJID": lun_id, + "ASSOCIATEMETADATA": [{"LUNID": lun_id, + "hostLUNId": hostlun_id}]} result = self.call(url, data, "PUT") diff --git a/cinder/volume/drivers/huawei/smartx.py b/cinder/volume/drivers/huawei/smartx.py index bd6b504e1..37c0cb149 100644 --- a/cinder/volume/drivers/huawei/smartx.py +++ b/cinder/volume/drivers/huawei/smartx.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015 Huawei Technologies Co., Ltd. +# Copyright (c) 2016 Huawei Technologies Co., Ltd. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -16,10 +16,11 @@ from oslo_log import log as logging from oslo_utils import excutils +from cinder import context from cinder import exception -from cinder.i18n import _ +from cinder.i18n import _, _LI from cinder.volume.drivers.huawei import constants -from cinder.volume.drivers.huawei import huawei_utils +from cinder.volume import qos_specs LOG = logging.getLogger(__name__) @@ -28,11 +29,52 @@ class SmartQos(object): def __init__(self, client): self.client = client - def create_qos(self, qos, lun_id): + @staticmethod + def get_qos_by_volume_type(volume_type): + # We prefer the qos_specs association + # and override any existing extra-specs settings + # if present. + if not volume_type: + return {} + + qos_specs_id = volume_type.get('qos_specs_id') + if not qos_specs_id: + return {} + + qos = {} + ctxt = context.get_admin_context() + kvs = qos_specs.get_qos_specs(ctxt, qos_specs_id)['specs'] + LOG.info(_LI('The QoS sepcs is: %s.'), kvs) + for k, v in kvs.items(): + if k not in constants.HUAWEI_VALID_KEYS: + continue + + if k.upper() != 'IOTYPE' and int(v) <= 0: + msg = _('QoS config is wrong. %s must > 0.') % k + LOG.error(msg) + raise exception.InvalidInput(reason=msg) + elif k.upper() == 'IOTYPE' and v not in ['0', '1', '2']: + msg = _('Illegal value specified for IOTYPE: 0, 1, or 2.') + LOG.error(msg) + raise exception.InvalidInput(reason=msg) + else: + qos[k.upper()] = v + + return qos + + def _is_high_priority(self, qos): + """Check QoS priority.""" + for key, value in qos.items(): + if (key.find('MIN') == 0) or (key.find('LATENCY') == 0): + return True + + return False + + def add(self, qos, lun_id): policy_id = None try: # Check QoS priority. - if huawei_utils.check_qos_high_priority(qos): + if self._is_high_priority(qos): self.client.change_lun_priority(lun_id) # Create QoS policy and activate it. version = self.client.find_array_version() @@ -51,13 +93,17 @@ class SmartQos(object): if policy_id is not None: self.client.delete_qos_policy(policy_id) - def delete_qos(self, qos_id): + def remove(self, qos_id, lun_id): qos_info = self.client.get_qos_info(qos_id) - qos_status = qos_info['RUNNINGSTATUS'] - # 2: Active status. - if qos_status == constants.STATUS_QOS_ACTIVE: - self.client.activate_deactivate_qos(qos_id, False) - self.client.delete_qos_policy(qos_id) + lun_list = self.client.get_lun_list_in_qos(qos_id, qos_info) + if len(lun_list) <= 1: + qos_status = qos_info['RUNNINGSTATUS'] + # 2: Active status. + if qos_status == constants.STATUS_QOS_ACTIVE: + self.client.activate_deactivate_qos(qos_id, False) + self.client.delete_qos_policy(qos_id) + else: + self.client.remove_lun_from_qos(lun_id, lun_list, qos_id) class SmartPartition(object): @@ -132,9 +178,9 @@ class SmartX(object): reason=(_('Illegal value specified for thin: ' 'Can not set thin and thick at the same time.'))) else: - opts['LUNType'] = 1 + opts['LUNType'] = constants.THIN_LUNTYPE if opts['thick_provisioning_support'] == 'true': - opts['LUNType'] = 0 + opts['LUNType'] = constants.THICK_LUNTYPE return opts