From ccffe1edf88a7dc6fe146841c3fa72bb5c522fab Mon Sep 17 00:00:00 2001 From: zengyingzhe Date: Sat, 2 Feb 2019 11:29:03 +0800 Subject: [PATCH] Huawei driver refactor(2/10) Huawei driver code is terribly decayed and hard to maintain. From this patch on, we'll try to progressively refactor total Huawei driver, make the code more clear, reliable and maintainable. This patch mainly optimized the huawei_smartx.py and huawei_utils.py files and the logic that according to it. Change-Id: I132b917e338d43da1e7b46127b1f228cefb1c16a --- .../drivers/huawei/test_huawei_drivers.py | 467 ++++++++---------- cinder/volume/drivers/huawei/common.py | 313 ++++-------- cinder/volume/drivers/huawei/constants.py | 4 +- cinder/volume/drivers/huawei/huawei_driver.py | 4 +- cinder/volume/drivers/huawei/huawei_utils.py | 453 +++++++++++++---- cinder/volume/drivers/huawei/hypermetro.py | 8 +- cinder/volume/drivers/huawei/replication.py | 4 +- cinder/volume/drivers/huawei/rest_client.py | 51 +- cinder/volume/drivers/huawei/smartx.py | 264 ++++------ 9 files changed, 806 insertions(+), 762 deletions(-) diff --git a/cinder/tests/unit/volume/drivers/huawei/test_huawei_drivers.py b/cinder/tests/unit/volume/drivers/huawei/test_huawei_drivers.py index e8ab0cb534e..ac3c35ddd07 100644 --- a/cinder/tests/unit/volume/drivers/huawei/test_huawei_drivers.py +++ b/cinder/tests/unit/volume/drivers/huawei/test_huawei_drivers.py @@ -20,12 +20,14 @@ import json import mock import re import requests +import retrying import tempfile from xml.dom import minidom from xml.etree import ElementTree from cinder import context from cinder import exception +from cinder import objects from cinder.objects import fields from cinder import test from cinder.tests.unit import fake_group @@ -74,7 +76,8 @@ ENCODE_NAME = huawei_utils.encode_name(ID) ID2 = 'ee00eb7c-40dc-4256-bfea-6c3a16ab850d' OLD_ENCODE_NAME = huawei_utils.old_encode_name(ID2) -METADATA = {} +METADATA = [{'key': 'huawei_lun_id', 'value': '11'}, + {'key': 'huawei_lun_wwn', 'value': '6643e8c1004c5f6723e9f454003'}] TEST_PAIR_ID = "3400a30d844d0004" VOL_METADATA = [{'key': 'hypermetro_id', 'value': '11'}, {'key': 'remote_lun_id', 'value': '1'}] @@ -112,13 +115,13 @@ fake_hypermetro_opts = {'hypermetro': 'true', 'thick_provisioning_support': False, } -sync_replica_specs = {'replication_enabled': ' True', +sync_replica_specs = {'capabilities:replication_enabled': ' True', 'replication_type': ' sync'} -async_replica_specs = {'replication_enabled': ' True', +async_replica_specs = {'capabilities:replication_enabled': ' True', 'replication_type': ' async'} -replica_hypermetro_specs = {'hypermetro': ' True', - 'replication_enabled': ' True'} +replica_hypermetro_specs = {'capabilities:hypermetro': ' True', + 'capabilities:replication_enabled': ' True'} test_host = {'host': 'ubuntu001@backend001#OpenStack_Pool', 'capabilities': {'smartcache': True, @@ -149,11 +152,11 @@ test_new_type = { 'created_at': None, 'updated_at': None, 'extra_specs': { - 'smarttier': ' true', - 'smartcache': ' true', - 'smartpartition': ' true', - 'thin_provisioning_support': ' true', - 'thick_provisioning_support': ' False', + 'capabilities:smarttier': ' true', + 'capabilities:smartcache': ' true', + 'capabilities:smartpartition': ' true', + 'capabilities:thin_provisioning_support': ' true', + 'capabilities:thick_provisioning_support': ' False', 'policy': '2', 'smartcache:cachename': 'cache-test', 'smartpartition:partitionname': 'partition-test', @@ -171,7 +174,7 @@ test_new_replication_type = { 'created_at': None, 'updated_at': None, 'extra_specs': { - 'replication_enabled': ' True', + 'capabilities:replication_enabled': ' True', 'replication_type': ' sync', }, 'is_public': True, @@ -187,7 +190,7 @@ test_hypermetro_type = { 'created_at': None, 'updated_at': None, 'extra_specs': { - 'hypermetro': ' True' + 'capabilities:hypermetro': ' True' }, 'is_public': True, 'deleted_at': None, @@ -431,7 +434,7 @@ FAKE_LUN_GET_SUCCESS_RESPONSE = """ "RUNNINGSTATUS": "10", "HEALTHSTATUS": "1", "RUNNINGSTATUS": "27", - "LUNLIST": "", + "LUNLIST": "[]", "ALLOCTYPE": "1", "CAPACITY": "2097152", "WRITEPOLICY": "1", @@ -455,8 +458,25 @@ FAKE_QUERY_ALL_LUN_RESPONSE = { "code": 0 }, "data": [{ - "ID": "1", - "NAME": ENCODE_NAME + "ID": "11", + "NAME": ENCODE_NAME, + "WWN": "6643e8c1004c5f6723e9f454003", + "DESCRIPTION": "21ec7341-9256-497b-97d9-ef48edcf0635", + "HEALTHSTATUS": "1", + "RUNNINGSTATUS": "27", + "LUNLIST": "[]", + "ALLOCTYPE": "1", + "CAPACITY": "2097152", + "WRITEPOLICY": "1", + "MIRRORPOLICY": "0", + "PREFETCHPOLICY": "1", + "PREFETCHVALUE": "20", + "DATATRANSFERPOLICY": "1", + "READCACHEPOLICY": "2", + "WRITECACHEPOLICY": "5", + "OWNINGCONTROLLER": "0B", + "SMARTCACHEPARTITIONID": "", + "CACHEPARTITIONID": "", }] } @@ -2283,7 +2303,7 @@ class HuaweiTestBase(test.TestCase): self.replica_volume = fake_volume.fake_volume_obj( admin_contex, host=HOST, provider_location=PROVIDER_LOCATION, - metadata=METADATA, replication_status='disabled', + volume_metadata=METADATA, replication_status='disabled', replication_driver_data=REPLICA_DRIVER_DATA, id=ID) self.hyper_volume = fake_volume.fake_volume_obj( @@ -2310,6 +2330,13 @@ class HuaweiTestBase(test.TestCase): constants.DEFAULT_WAIT_TIMEOUT = .5 constants.MIGRATION_WAIT_INTERVAL = .1 + constants.QOS_SPEC_KEYS = ( + 'maxIOPS', 'minIOPS', 'minBandWidth', + 'maxBandWidth', 'latency', 'IOType') + constants.QOS_IOTYPES = ('0', '1', '2') + constants.SUPPORT_LUN_TYPES = ('Thick', 'Thin') + constants.DEFAULT_LUN_TYPE = 'Thick' + def test_encode_name(self): lun_name = huawei_utils.encode_name(self.volume.id) self.assertEqual('21ec7341-ca82ece92e1ac480c963f1', lun_name) @@ -2417,13 +2444,14 @@ class HuaweiTestBase(test.TestCase): ), 'expect': {'huawei_lun_id': '11', 'huawei_lun_wwn': 'FAKE_WWN', + 'huawei_sn': None, 'hypermetro_id': '11', 'remote_lun_id': '1'} } ) @ddt.unpack - def test_get_lun_metadata(self, volume, expect): - metadata = huawei_utils.get_lun_metadata(volume) + def test_get_volume_private_data(self, volume, expect): + metadata = huawei_utils.get_volume_private_data(volume) self.assertEqual(expect, metadata) @ddt.data( @@ -2448,50 +2476,55 @@ class HuaweiTestBase(test.TestCase): { 'snapshot': fake_snapshot.fake_snapshot_obj( admin_contex, - provider_location='11'), - 'expect': {'huawei_snapshot_id': '11'} + provider_location='11', + snapshot_metadata=[{'key': 'huawei_snapshot_wwn', + 'value': 'fake_wwn'}, + ], + expected_attrs=['metadata'], + ), + 'expect': {'huawei_snapshot_id': '11', + 'huawei_snapshot_wwn': 'fake_wwn', + } } ) @ddt.unpack - def test_get_snapshot_metadata(self, snapshot, expect): - metadata = huawei_utils.get_snapshot_metadata(snapshot) - self.assertEqual(expect, metadata) + def test_get_snapshot_private_data(self, snapshot, expect): + metadata = huawei_utils.get_snapshot_private_data(snapshot) + self.assertDictEqual(expect, metadata) @ddt.data( { - 'volume': fake_volume.fake_volume_obj( - admin_contex, provider_location=PROVIDER_LOCATION), - 'expect': ('11', '6643e8c1004c5f6723e9f454003'), + 'provider_location': PROVIDER_LOCATION, + 'mock_func': None, }, { - 'volume': fake_volume.fake_volume_obj( - admin_contex, id=ID), - 'expect': ('1', None), + 'provider_location': '', + 'mock_func': None, }, { - 'volume': fake_volume.fake_volume_obj( - admin_contex, id=ID2), - 'expect': ('1', None), + 'provider_location': PROVIDER_LOCATION, + 'mock_func': 'get_lun_info_by_name', }, { - 'volume': fake_volume.fake_volume_obj( - admin_contex, id='001e7071-413c-4c60-b087-863067ecdd72'), - 'expect': (None, None), - } + 'provider_location': '{"huawei_lun_wwn": "fake_wwn"}', + 'mock_func': None, + }, ) @ddt.unpack - def test_get_volume_lun_id(self, volume, expect): - volume_id = huawei_utils.get_volume_lun_id(self.driver.client, - volume) - self.assertEqual(expect, volume_id) + def test_get_lun_info(self, provider_location, mock_func): + volume = fake_volume.fake_volume_obj( + admin_contex, id=ID, provider_location=provider_location) + + if mock_func: + self.mock_object(self.driver.client, mock_func, return_value=None) + lun_info = huawei_utils.get_lun_info(self.driver.client, volume) + + if provider_location in (PROVIDER_LOCATION, ''): + self.assertEqual('6643e8c1004c5f6723e9f454003', lun_info['WWN']) + else: + self.assertIsNone(lun_info) @ddt.data( - { - 'snapshot': fake_snapshot.fake_snapshot_obj( - admin_contex, - provider_location=SNAP_PROVIDER_LOCATION), - 'expect': '11', - }, { 'snapshot': fake_snapshot.fake_snapshot_obj( admin_contex, id=ID), @@ -2509,10 +2542,13 @@ class HuaweiTestBase(test.TestCase): } ) @ddt.unpack - def test_get_snapshot_id(self, snapshot, expect): - snapshot_id = huawei_utils.get_snapshot_id(self.driver.client, - snapshot) - self.assertEqual(expect, snapshot_id) + def test_get_snapshot_info(self, snapshot, expect): + snapshot_info = huawei_utils.get_snapshot_info( + self.driver.client, snapshot) + if expect: + self.assertEqual(expect, snapshot_info['ID']) + else: + self.assertIsNone(snapshot_info) @ddt.data( {'host_name': HOST, @@ -2654,8 +2690,7 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase): side_effect=[{}, task_info]) moved, model_update = self.driver.migrate_volume(None, self.volume, - test_host, - None) + test_host) self.assertTrue(moved) self.assertEqual(empty_dict, model_update) @@ -2684,8 +2719,7 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase): return_value=task_info) moved, model_update = self.driver.migrate_volume(None, self.replica_volume, - test_host, - None) + test_host) self.assertTrue(moved) self.assertEqual(empty_dict, model_update) @@ -2712,7 +2746,7 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase): return_value=task_info) self.assertRaises(exception.VolumeBackendAPIException, self.driver.migrate_volume, - None, self.volume, test_host, None) + None, self.volume, test_host) @ddt.data(FAKE_POOLS_UNSUPPORT_REPORT, FAKE_POOLS_SUPPORT_REPORT) def test_migrate_volume_fail_no_migrate_task(self, pool_data): @@ -2737,11 +2771,12 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase): return_value=task_info) self.assertRaises(exception.VolumeBackendAPIException, self.driver.migrate_volume, - None, self.volume, test_host, None) + None, self.volume, test_host) @ddt.data(FAKE_POOLS_UNSUPPORT_REPORT, FAKE_POOLS_SUPPORT_REPORT) def test_migrate_volume_with_type_id(self, pool_data): self.driver.support_func = pool_data + self.volume.volume_type = None self.volume.volume_type_id = '550c089b-bfdd-4f7f-86e1-3ba88125555c' task_info = {"data": [{"ENDTIME": "1436816174", "ID": "9", @@ -2766,8 +2801,7 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase): return_value=task_info) moved, model_update = self.driver.migrate_volume(None, self.volume, - test_host, - None) + test_host) self.assertTrue(moved) self.assertEqual(empty_dict, model_update) @@ -2807,9 +2841,9 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase): def test_create_volume_from_snapsuccess(self): self.mock_object( - common.HuaweiBaseDriver, - '_get_volume_type', - return_value={'extra_specs': sync_replica_specs}) + huawei_utils, + 'get_volume_params', + return_value={'replication_enabled': True}) self.mock_object(replication.ReplicaCommonDriver, 'sync') model_update = self.driver.create_volume_from_snapshot(self.volume, self.snapshot) @@ -3066,12 +3100,17 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase): self.driver.create_volume, self.volume) def test_delete_volume_fail(self): - self.driver.client.test_fail = True - self.driver.delete_volume(self.volume) + self.mock_object( + self.driver.client, 'delete_lun', + side_effect=exception.VolumeBackendAPIException(data='')) + self.driver.support_func = {} + self.assertRaises(exception.VolumeBackendAPIException, + self.driver.delete_volume, self.volume) def test_delete_snapshot_fail(self): self.driver.client.test_fail = True - self.driver.delete_snapshot(self.snapshot) + self.assertRaises(exception.VolumeBackendAPIException, + self.driver.delete_snapshot, self.snapshot) def test_delete_snapshot_with_snapshot_nonexistent(self): self.snapshot.provider_location = None @@ -3193,14 +3232,11 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase): 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']) - @ddt.data(FAKE_POOLS_UNSUPPORT_REPORT, FAKE_POOLS_SUPPORT_REPORT) - @mock.patch.object(smartx.SmartQos, 'get_qos_by_volume_type', - return_value={'MAXIOPS': '100', - 'IOType': '2'}) + @mock.patch.object(huawei_utils, 'get_volume_params', + return_value={'qos': {'MAXIOPS': '100', + 'IOType': '2'} + }) def test_create_smartqos(self, mock_qos_value, pool_data): self.driver.support_func = pool_data lun_info = self.driver.create_volume(self.volume) @@ -3210,7 +3246,7 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase): json.loads(lun_info['provider_location'])) @ddt.data('front-end', 'back-end') - @mock.patch.object(common.HuaweiBaseDriver, '_get_volume_params', + @mock.patch.object(huawei_utils, 'get_volume_params', return_value={'smarttier': 'true', 'smartcache': 'true', 'smartpartition': 'true', @@ -3247,49 +3283,29 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase): @ddt.unpack def test_create_smartqos_failed(self, qos_specs_value, pool_data): self.driver.support_func = pool_data - self.mock_object( - common.HuaweiBaseDriver, - '_get_volume_params', - return_value={'smarttier': 'true', - 'smartcache': 'true', - 'smartpartition': 'true', - 'thin_provisioning_support': 'true', - 'thick_provisioning_support': 'false', - 'policy': '2', - 'cachename': 'cache-test', - 'partitionname': 'partition-test'}) - self.mock_object(common.HuaweiBaseDriver, '_get_volume_type', - return_value={'qos_specs_id': u'025ce295-15e9-41a7'}) self.mock_object(qos_specs, 'get_qos_specs', return_value=qos_specs_value) - self.assertRaises(exception.VolumeBackendAPIException, + self.volume.volume_type = objects.VolumeType( + extra_specs={}, qos_specs_id=ID) + self.assertRaises(exception.InvalidInput, self.driver.create_volume, self.volume) @ddt.data(FAKE_POOLS_UNSUPPORT_REPORT, FAKE_POOLS_SUPPORT_REPORT) def test_create_smartqos_without_huawei_type(self, pool_data): self.driver.support_func = pool_data - self.mock_object( - common.HuaweiBaseDriver, - '_get_volume_params', - return_value={'smarttier': 'true', - 'smartcache': 'true', - 'smartpartition': 'true', - 'thin_provisioning_support': 'true', - 'thick_provisioning_support': 'false', - 'policy': '2', - 'cachename': 'cache-test', - 'partitionname': 'partition-test'}) - self.mock_object(common.HuaweiBaseDriver, '_get_volume_type', - return_value={'qos_specs_id': u'025ce295-15e9-41a7'}) self.mock_object(qos_specs, 'get_qos_specs', return_value={'specs': {'fake_qos_type': '100', - 'IOType': '2'}}) - self.assertRaises(exception.VolumeBackendAPIException, + 'IOType': '2'}} + ) + self.volume.volume_type = objects.VolumeType( + extra_specs={}, qos_specs_id=ID) + self.assertRaises(exception.InvalidInput, self.driver.create_volume, self.volume) - @mock.patch.object(smartx.SmartQos, 'get_qos_by_volume_type', - return_value={'MAXIOPS': '100', - 'IOType': '2'}) + @mock.patch.object(huawei_utils, 'get_volume_params', + return_value={'qos': {'MAXIOPS': '100', + 'IOType': '2'} + }) @mock.patch.object(rest_client.RestClient, 'find_array_version', return_value='V300R003C00') @mock.patch.object(rest_client.RestClient, 'find_available_qos', @@ -3305,9 +3321,10 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase): self.assertDictEqual(expect_value, json.loads(lun_info['provider_location'])) - @mock.patch.object(smartx.SmartQos, 'get_qos_by_volume_type', - return_value={'MINIOPS': '100', - 'IOType': '2'}) + @mock.patch.object(huawei_utils, 'get_volume_params', + return_value={'qos': {'MAXIOPS': '100', + 'IOType': '2'} + }) @mock.patch.object(rest_client.RestClient, 'find_array_version', return_value='V300R003C00') @mock.patch.object(rest_client.RestClient, 'find_available_qos', @@ -3323,51 +3340,33 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase): self.assertDictEqual(expect_value, json.loads(lun_info['provider_location'])) - @mock.patch.object(smartx.SmartQos, 'get_qos_by_volume_type', - return_value={'MINIOPS': '100', - 'IOType': '2'}) - @mock.patch.object(rest_client.RestClient, 'find_array_version', - return_value='V300R003C00') - @mock.patch.object(rest_client.RestClient, 'find_available_qos', - return_value=('11', u'["0", "2", "3"]')) - def test_create_smartqos_on_v3r3_with_unsupport_qos( - self, mock_find_available_qos, - mock_qos_value, mock_array_version): - self.driver.support_func = FAKE_POOLS_UNSUPPORT_REPORT - self.assertRaises(exception.VolumeBackendAPIException, - self.driver.create_volume, self.volume) - @ddt.data(FAKE_POOLS_UNSUPPORT_REPORT, FAKE_POOLS_SUPPORT_REPORT) - @mock.patch.object(smartx.SmartQos, 'get_qos_by_volume_type', - return_value={'MINIOPS': '100', - 'IOType': '2'}) - @mock.patch.object(rest_client.RestClient, 'find_array_version', - return_value='V300R003C00') - @mock.patch.object(rest_client.RestClient, 'find_available_qos', - return_value=(None, [])) - @mock.patch.object(rest_client.RestClient, 'activate_deactivate_qos') - def test_create_smartqos_on_v3r3_active_failed(self, - pool_data, - mock_activate_qos, - mock_find_available_qos, - mock_qos_value, - mock_array_version): + def test_create_smartqos_on_v3r3_active_failed(self, pool_data): self.driver.support_func = pool_data - mock_activate_qos.side_effect = ( - exception.VolumeBackendAPIException(data='Activate or deactivate ' - 'QoS error. ')) + + self.mock_object(huawei_utils, 'get_volume_params', + return_value={'qos': {'MAXIOPS': '100', + 'IOType': '2'} + } + ) + self.mock_object(self.driver.client, 'activate_deactivate_qos', + side_effect=exception.VolumeBackendAPIException( + data='Activate or deactivate QoS error.') + ) + self.mock_object(smartx.SmartQos, 'remove') self.assertRaises(exception.VolumeBackendAPIException, self.driver.create_volume, self.volume) @ddt.data(FAKE_POOLS_UNSUPPORT_REPORT, FAKE_POOLS_SUPPORT_REPORT) - @mock.patch.object(smartx.SmartQos, 'get_qos_by_volume_type', - return_value={'MINIOPS': '100', - 'IOType': '2'}) + @mock.patch.object(huawei_utils, 'get_volume_params', + return_value={'qos': {'MAXIOPS': '100', + 'IOType': '2'} + }) @mock.patch.object(rest_client.RestClient, 'find_array_version', return_value='V300R003C00') @mock.patch.object(rest_client.RestClient, 'find_available_qos', return_value=(None, [])) - @mock.patch.object(rest_client.RestClient, 'create_qos_policy') + @mock.patch.object(rest_client.RestClient, 'create_qos') def test_create_smartqos_on_v3r3_qos_failed(self, pool_data, mock_create_qos, @@ -3398,7 +3397,7 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase): self.driver.delete_volume(self.volume) @mock.patch.object(rest_client.RestClient, 'add_lun_to_partition') - @mock.patch.object(common.HuaweiBaseDriver, '_get_volume_params', + @mock.patch.object(huawei_utils, 'get_volume_params', return_value={'smarttier': 'true', 'smartcache': 'true', 'smartpartition': 'true', @@ -3414,46 +3413,20 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase): self.assertDictEqual(expect_value, json.loads(lun_info['provider_location'])) - @ddt.data([{'smarttier': 'true', 'smartcache': 'true', - 'smartpartition': 'true', - 'thin_provisioning_support': 'true', - 'thick_provisioning_support': 'false', - 'policy': '2', 'cachename': None, - 'partitionname': 'partition-test'}, - FAKE_POOLS_UNSUPPORT_REPORT], - [{'smarttier': 'true', 'smartcache': 'true', - 'smartpartition': 'true', - 'thin_provisioning_support': 'true', - 'thick_provisioning_support': 'false', - 'policy': '2', 'cachename': 'cache-test', - 'partitionname': None}, - FAKE_POOLS_SUPPORT_REPORT], - [{'smarttier': 'true', 'smartcache': 'true', - 'smartpartition': 'true', - 'thin_provisioning_support': 'true', - 'thick_provisioning_support': 'false', - 'policy': '2', 'cachename': None, - 'partitionname': 'partition-test'}, - FAKE_POOLS_SUPPORT_REPORT], - [{'smarttier': 'true', 'smartcache': 'true', - 'smartpartition': 'true', - 'thin_provisioning_support': 'true', - 'thick_provisioning_support': 'false', - 'policy': '2', 'cachename': 'cache-test', - 'partitionname': None}, - FAKE_POOLS_UNSUPPORT_REPORT]) - @ddt.unpack - def test_create_smartCache_failed(self, opts, pool_data): - self.driver.support_func = pool_data - self.mock_object( - common.HuaweiBaseDriver, - '_get_volume_params', - return_value=opts) - self.assertRaises(exception.VolumeBackendAPIException, + @ddt.data({'capabilities:smartcache': 'true', + 'cachename': 'fake_name'}, + {'capabilities:smartcache': ' true', + 'cachename': None}, + {'capabilities:smartcache': ' true', + 'cachename': ''}, + ) + def test_create_smartCache_failed(self, extra_specs): + self.volume.volume_type = objects.VolumeType(extra_specs=extra_specs) + self.assertRaises(exception.InvalidInput, self.driver.create_volume, self.volume) @ddt.data(FAKE_POOLS_UNSUPPORT_REPORT, FAKE_POOLS_SUPPORT_REPORT) - @mock.patch.object(common.HuaweiBaseDriver, '_get_volume_params', + @mock.patch.object(huawei_utils, 'get_volume_params', return_value={'smarttier': 'true', 'smartcache': 'true', 'smartpartition': 'true', @@ -3471,7 +3444,7 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase): self.driver.create_volume, self.volume) @ddt.data(FAKE_POOLS_UNSUPPORT_REPORT, FAKE_POOLS_SUPPORT_REPORT) - @mock.patch.object(common.HuaweiBaseDriver, '_get_volume_params', + @mock.patch.object(huawei_utils, 'get_volume_params', return_value={'smarttier': 'true', 'smartcache': 'true', 'smartpartition': 'true', @@ -3539,7 +3512,7 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase): (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(common.HuaweiBaseDriver, '_get_volume_params', + @mock.patch.object(huawei_utils, 'get_volume_params', return_value=fake_hypermetro_opts) @mock.patch.object(rest_client.RestClient, 'get_all_pools', return_value=FAKE_STORAGE_POOL_RESPONSE) @@ -3564,7 +3537,7 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase): json.loads(lun_info['provider_location'])) @ddt.data(FAKE_POOLS_UNSUPPORT_REPORT, FAKE_POOLS_SUPPORT_REPORT) - @mock.patch.object(common.HuaweiBaseDriver, '_get_volume_params', + @mock.patch.object(huawei_utils, 'get_volume_params', return_value=fake_hypermetro_opts) @mock.patch.object(rest_client.RestClient, 'get_all_pools', return_value=FAKE_STORAGE_POOL_RESPONSE) @@ -4010,10 +3983,8 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase): @ddt.data(sync_replica_specs, async_replica_specs) def test_create_replication_success(self, mock_type): self.mock_object(replication.ReplicaCommonDriver, 'sync') - self.mock_object( - common.HuaweiBaseDriver, - '_get_volume_type', - return_value={'extra_specs': mock_type}) + self.replica_volume.volume_type = objects.VolumeType( + extra_specs=mock_type, qos_specs_id=None) model_update = self.driver.create_volume(self.replica_volume) driver_data = {'pair_id': TEST_PAIR_ID, @@ -4113,10 +4084,9 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase): def test_create_replication_fail(self, mock_module, mock_func, mock_value, pool_data): self.driver.support_func = pool_data - self.mock_object( - common.HuaweiBaseDriver, - '_get_volume_type', - return_value={'extra_specs': sync_replica_specs}) + self.replica_volume.volume_type = objects.VolumeType( + extra_specs=sync_replica_specs, qos_specs_id=None) + self.mock_object(replication.ReplicaPairManager, '_delete_pair') self.mock_object(mock_module, mock_func, mock_value) self.assertRaises( @@ -4150,7 +4120,7 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase): with mock.patch.object(rest_client.RestClient, 'get_lun_info', return_value=offline_status): - self.assertRaises(exception.VolumeBackendAPIException, + self.assertRaises(retrying.RetryError, replica.wait_volume_online, self.driver.client, lun_info) @@ -4165,7 +4135,7 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase): return_value={'SECRESACCESS': access_ro}) common_driver.wait_second_access(pair_id, access_ro) - self.assertRaises(exception.VolumeBackendAPIException, + self.assertRaises(retrying.RetryError, common_driver.wait_second_access, pair_id, access_rw) def test_wait_replica_ready(self): @@ -4282,8 +4252,8 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase): old_replica_client = driver.replica_client old_replica = driver.replica self.mock_object(replication.ReplicaCommonDriver, 'failover') - self.mock_object(common.HuaweiBaseDriver, '_get_volume_params', - return_value={'replication_enabled': 'true'}) + self.mock_object(huawei_utils, 'get_volume_params', + return_value={'replication_enabled': True}) secondary_id, volumes_update, __ = driver.failover_host( None, [self.replica_volume], REPLICA_BACKEND_ID, []) self.assertEqual(REPLICA_BACKEND_ID, driver.active_backend_id) @@ -4302,7 +4272,7 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase): expect_location, json.loads(v_update['provider_location'])) self.assertEqual('failed-over', v_update['replication_status']) - metadata = huawei_utils.get_lun_metadata(self.replica_volume) + metadata = huawei_utils.get_volume_metadata(self.replica_volume) new_drv_data = {'pair_id': TEST_PAIR_ID, 'rmt_lun_id': metadata['huawei_lun_id'], 'rmt_lun_wwn': metadata['huawei_lun_wwn']} @@ -4319,8 +4289,8 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase): old_client = driver.client old_replica_client = driver.replica_client old_replica = driver.replica - self.mock_object(common.HuaweiBaseDriver, '_get_volume_params', - return_value={'replication_enabled': 'true'}) + self.mock_object(huawei_utils, 'get_volume_params', + return_value={'replication_enabled': True}) secondary_id, volumes_update, __ = driver.failover_host( None, [volume], REPLICA_BACKEND_ID, []) self.assertEqual(driver.active_backend_id, REPLICA_BACKEND_ID) @@ -4338,8 +4308,8 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase): self.mock_object(replication.ReplicaCommonDriver, 'enable') self.mock_object(replication.ReplicaCommonDriver, 'wait_replica_ready') self.mock_object(replication.ReplicaCommonDriver, 'failover') - self.mock_object(common.HuaweiBaseDriver, '_get_volume_params', - return_value={'replication_enabled': 'true'}) + self.mock_object(huawei_utils, 'get_volume_params', + return_value={'replication_enabled': True}) volume = self.replica_volume @@ -4367,7 +4337,7 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase): expect_location, json.loads(v_update['provider_location'])) self.assertEqual('available', v_update['replication_status']) - metadata = huawei_utils.get_lun_metadata(self.replica_volume) + metadata = huawei_utils.get_volume_metadata(self.replica_volume) new_drv_data = {'pair_id': TEST_PAIR_ID, 'rmt_lun_id': metadata['huawei_lun_id'], 'rmt_lun_wwn': metadata['huawei_lun_wwn']} @@ -4376,8 +4346,8 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase): @ddt.data({}, {'pair_id': TEST_PAIR_ID}) def test_failback_replica_volumes_invalid_drv_data(self, mock_drv_data): - self.mock_object(common.HuaweiBaseDriver, '_get_volume_params', - return_value={'replication_enabled': 'true'}) + self.mock_object(huawei_utils, 'get_volume_params', + return_value={'replication_enabled': True}) volume = self.replica_volume volume['replication_driver_data'] = replication.to_string( @@ -4840,14 +4810,6 @@ class HuaweiFCDriverTestCase(HuaweiTestBase): self.assertRaises(exception.VolumeBackendAPIException, self.driver.create_volume, self.volume) - def test_delete_volume_fail(self): - self.driver.client.test_fail = True - self.driver.delete_volume(self.volume) - - def test_delete_snapshot_fail(self): - self.driver.client.test_fail = True - self.driver.delete_snapshot(self.snapshot) - def test_initialize_connection_fail(self): self.driver.client.test_fail = True self.assertRaises(exception.VolumeBackendAPIException, @@ -4876,26 +4838,7 @@ class HuaweiFCDriverTestCase(HuaweiTestBase): self.driver.support_func = pool_data moved, model_update = self.driver.migrate_volume(None, self.volume, - test_host, - None) - self.assertTrue(moved) - self.assertEqual(empty_dict, model_update) - - # Migrate volume with new type. - empty_dict = {} - new_type = {'extra_specs': - {'smarttier': ' true', - 'smartcache': ' true', - 'smartpartition': ' true', - 'thin_provisioning_support': ' true', - 'thick_provisioning_support': ' False', - 'policy': '2', - 'smartcache:cachename': 'cache-test', - 'smartpartition:partitionname': 'partition-test'}} - moved, model_update = self.driver.migrate_volume(None, - self.volume, - test_host, - new_type) + test_host) self.assertTrue(moved) self.assertEqual(empty_dict, model_update) @@ -4905,21 +4848,7 @@ class HuaweiFCDriverTestCase(HuaweiTestBase): # Migrate volume without new type. self.assertRaises(exception.VolumeBackendAPIException, self.driver.migrate_volume, None, - self.volume, test_host, None) - - # Migrate volume with new type. - new_type = {'extra_specs': - {'smarttier': ' true', - 'smartcache': ' true', - 'thin_provisioning_support': ' true', - 'thick_provisioning_support': ' False', - 'policy': '2', - 'smartcache:cachename': 'cache-test', - 'partitionname': 'partition-test'}} - self.driver.client.test_fail = True - self.assertRaises(exception.VolumeBackendAPIException, - self.driver.migrate_volume, None, - self.volume, test_host, new_type) + self.volume, test_host) def test_check_migration_valid(self): is_valid = self.driver._check_migration_valid(test_host, @@ -5060,12 +4989,19 @@ class HuaweiFCDriverTestCase(HuaweiTestBase): self.driver.support_func = pool_data self.mock_object(mock_module, mock_func, side_effect=side_effect) self.mock_object(rest_client.RestClient, 'add_lun_to_partition') - self.mock_object( - common.HuaweiBaseDriver, - '_get_volume_type', - return_value={'extra_specs': sync_replica_specs}) + + if mock_func == 'create_replica': + extra_specs = {} + new_type = test_new_replication_type + else: + extra_specs = {'capabilities:replication_enabled': ' True'} + new_type = {'extra_specs': {}, + } + + self.volume.volume_type = objects.VolumeType( + extra_specs=extra_specs, qos_specs_id=None) retype = self.driver.retype(None, self.volume, - test_new_replication_type, None, test_host) + new_type, None, test_host) self.assertFalse(retype) @ddt.data(FAKE_POOLS_UNSUPPORT_REPORT, FAKE_POOLS_SUPPORT_REPORT) @@ -5088,7 +5024,6 @@ class HuaweiFCDriverTestCase(HuaweiTestBase): @mock.patch.object(rest_client.RestClient, 'add_lun_to_partition') def test_retype_volume_fail(self, mock_add_lun_to_partition): - self.driver.support_func = FAKE_POOLS_SUPPORT_REPORT mock_add_lun_to_partition.side_effect = ( exception.VolumeBackendAPIException(data='Error occurred.')) @@ -5240,7 +5175,7 @@ class HuaweiFCDriverTestCase(HuaweiTestBase): None) self.assertEqual(expected_pool_capacity, pool_capacity) - @mock.patch.object(common.HuaweiBaseDriver, '_get_volume_params', + @mock.patch.object(huawei_utils, 'get_volume_params', return_value=fake_hypermetro_opts) @mock.patch.object(rest_client.RestClient, 'get_all_pools', return_value=FAKE_STORAGE_POOL_RESPONSE) @@ -5273,7 +5208,7 @@ class HuaweiFCDriverTestCase(HuaweiTestBase): json.loads(lun_info['provider_location'])) @ddt.data(FAKE_POOLS_UNSUPPORT_REPORT, FAKE_POOLS_SUPPORT_REPORT) - @mock.patch.object(common.HuaweiBaseDriver, '_get_volume_params', + @mock.patch.object(huawei_utils, 'get_volume_params', return_value=fake_hypermetro_opts) @mock.patch.object(rest_client.RestClient, 'get_all_pools', return_value=FAKE_STORAGE_POOL_RESPONSE) @@ -5299,7 +5234,7 @@ class HuaweiFCDriverTestCase(HuaweiTestBase): self.assertRaises(exception.VolumeBackendAPIException, self.driver.metro.create_hypermetro, "11", {}) - @mock.patch.object(huawei_driver.huawei_utils, 'get_lun_metadata', + @mock.patch.object(huawei_utils, 'get_volume_private_data', return_value={'hypermetro_id': '3400a30d844d0007', 'remote_lun_id': '1'}) @mock.patch.object(rest_client.RestClient, 'do_mapping', @@ -5313,13 +5248,13 @@ class HuaweiFCDriverTestCase(HuaweiTestBase): mock_map.assert_called_once_with('1', '0', '1', hypermetro_lun=True) - @mock.patch.object(huawei_driver.huawei_utils, 'get_lun_metadata', + @mock.patch.object(huawei_driver.huawei_utils, 'get_volume_metadata', return_value={'hypermetro_id': '3400a30d844d0007', 'remote_lun_id': '1'}) def test_terminate_hypermetro_connection_success(self, mock_metradata): self.driver.metro.disconnect_volume_fc(self.volume, FakeConnector) - @mock.patch.object(huawei_driver.huawei_utils, 'get_lun_metadata', + @mock.patch.object(huawei_utils, 'get_volume_private_data', return_value={'hypermetro_id': '3400a30d844d0007', 'remote_lun_id': None}) @mock.patch.object(rest_client.RestClient, 'get_lun_id_by_name', @@ -5334,7 +5269,7 @@ class HuaweiFCDriverTestCase(HuaweiTestBase): flag = self.driver.metro._wait_volume_ready("11") self.assertIsNone(flag) - @mock.patch.object(huawei_driver.huawei_utils, 'get_lun_metadata', + @mock.patch.object(huawei_driver.huawei_utils, 'get_volume_metadata', return_value={'hypermetro_id': '3400a30d844d0007', 'remote_lun_id': '1'}) @mock.patch.object(rest_client.RestClient, 'get_online_free_wwns', @@ -5350,17 +5285,15 @@ class HuaweiFCDriverTestCase(HuaweiTestBase): FakeConnector) def test_create_snapshot_fail_hypermetro(self): - self.mock_object( - common.HuaweiBaseDriver, - '_get_volume_type', - return_value={'extra_specs': replica_hypermetro_specs}) + self.volume.volume_type = objects.VolumeType( + extra_specs=replica_hypermetro_specs, qos_specs_id=None) self.assertRaises(exception.VolumeBackendAPIException, self.driver.create_volume_from_snapshot, self.volume, self.snapshot) def test_create_snapshot_fail_no_snapshot_id(self): self.snapshot.provider_location = None - self.mock_object(rest_client.RestClient, 'get_snapshot_id_by_name', + self.mock_object(self.driver.client, 'get_snapshot_info_by_name', return_value=None) self.assertRaises(exception.VolumeBackendAPIException, self.driver.create_volume_from_snapshot, @@ -5438,6 +5371,8 @@ class HuaweiFCDriverTestCase(HuaweiTestBase): delete_snap_mock = self.mock_object( self.driver, '_delete_group_snapshot', wraps=self.driver._delete_group_snapshot) + self.mock_object(self.driver.client, 'get_snapshot_info_by_name', + return_value={'ID': ID}) model_update, volumes_model_update = self.driver.create_group_from_src( None, self.group, [self.volume], snapshots=snapshots, @@ -5469,13 +5404,13 @@ class HuaweiFCDriverTestCase(HuaweiTestBase): @mock.patch.object(common.HuaweiBaseDriver, '_get_group_type', return_value=[{"hypermetro": "true"}]) - @mock.patch.object(huawei_driver.huawei_utils, 'get_lun_metadata', + @mock.patch.object(huawei_driver.huawei_utils, 'get_volume_metadata', return_value={'hypermetro_id': '3400a30d844d0007', 'remote_lun_id': '59'}) def test_update_group_success(self, mock_grouptype, mock_metadata): ctxt = context.get_admin_context() - add_volumes = [self.volume] - remove_volumes = [self.volume] + add_volumes = [self.hyper_volume] + remove_volumes = [] self.mock_object(volume_utils, 'is_group_a_cg_snapshot_type', return_value=True) model_update = self.driver.update_group( diff --git a/cinder/volume/drivers/huawei/common.py b/cinder/volume/drivers/huawei/common.py index 3ae6fd4e945..c270a6d0b13 100644 --- a/cinder/volume/drivers/huawei/common.py +++ b/cinder/volume/drivers/huawei/common.py @@ -221,90 +221,6 @@ class HuaweiBaseDriver(driver.VolumeDriver): 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, - 'replication_enabled': False, - 'replication_type': 'async', - } - - opts_value = { - 'policy': None, - 'partitionname': None, - 'cachename': None, - } - - opts_associate = { - 'smarttier': 'policy', - 'smartcache': 'cachename', - 'smartpartition': 'partitionname', - } - - opts = self._get_opts_from_specs(opts_capability, - opts_value, - opts_associate, - specs) - opts = smartx.SmartX().get_smartx_specs_opts(opts) - opts = replication.get_replication_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 it 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 words and len(words) == 2 and words[0] in ('', ''): - opts[key] = words[1].lower() - elif key == 'replication_type': - LOG.error("Extra specs must be specified as " - "replication_type=' sync' or " - "' async'.") - else: - LOG.error("Extra specs must be specified as " - "capabilities:%s=' True'.", key) - - 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 = { @@ -314,7 +230,7 @@ class HuaweiBaseDriver(driver.VolumeDriver): 'PARENTID': self.client.get_pool_id(pool_name), 'DESCRIPTION': volume.name, 'ALLOCTYPE': opts.get('LUNType', self.configuration.lun_type), - 'CAPACITY': huawei_utils.get_volume_size(volume), + 'CAPACITY': int(volume.size) * constants.CAPACITY_UNIT, 'READCACHEPOLICY': self.configuration.lun_read_cache_policy, 'WRITECACHEPOLICY': self.configuration.lun_write_cache_policy, } @@ -341,7 +257,7 @@ class HuaweiBaseDriver(driver.VolumeDriver): return lun_info, model_update - def _create_base_type_volume(self, opts, volume, volume_type): + def _create_base_type_volume(self, opts, volume): """Create volume and add some base type. Base type is the service type which doesn't conflict with the other. @@ -351,21 +267,17 @@ class HuaweiBaseDriver(driver.VolumeDriver): lun_id = lun_info['ID'] try: - qos = smartx.SmartQos.get_qos_by_volume_type(volume_type) - if qos: - if not self.support_func.get('QoS_support'): - msg = (_("Can't support qos on the array")) - LOG.error(msg) - raise exception.VolumeBackendAPIException(data=msg) - else: - smart_qos = smartx.SmartQos(self.client) - smart_qos.add(qos, lun_id) + if opts.get('qos'): + smartqos = smartx.SmartQos(self.client) + smartqos.add(opts['qos'], lun_id) - smartpartition = smartx.SmartPartition(self.client) - smartpartition.add(opts, lun_id) + if opts.get('smartpartition'): + smartpartition = smartx.SmartPartition(self.client) + smartpartition.add(opts['partitionname'], lun_id) - smartcache = smartx.SmartCache(self.client) - smartcache.add(opts, lun_id) + if opts.get('smartcache'): + smartcache = smartx.SmartCache(self.client) + smartcache.add(opts['cachename'], lun_id) except Exception as err: self._delete_lun_with_check(lun_id) msg = _('Create volume error. Because %s.') % six.text_type(err) @@ -381,7 +293,7 @@ class HuaweiBaseDriver(driver.VolumeDriver): So add it after those services. """ lun_id = lun_info['ID'] - if opts.get('hypermetro') == 'true': + if opts.get('hypermetro'): metro = hypermetro.HuaweiHyperMetro(self.client, self.rmt_client, self.configuration) @@ -393,7 +305,7 @@ class HuaweiBaseDriver(driver.VolumeDriver): self._delete_lun_with_check(lun_id) raise - if opts.get('replication_enabled') == 'true': + if opts.get('replication_enabled'): replica_model = opts.get('replication_type') try: replica_info = self.replica.create_replica(lun_info, @@ -408,17 +320,15 @@ class HuaweiBaseDriver(driver.VolumeDriver): def create_volume(self, volume): """Create a volume.""" - volume_type = self._get_volume_type(volume) - opts = self._get_volume_params(volume_type) - if (opts.get('hypermetro') == 'true' - and opts.get('replication_enabled') == 'true'): + opts = huawei_utils.get_volume_params(volume) + if opts.get('hypermetro') and opts.get('replication_enabled'): err_msg = _("Hypermetro and Replication can not be " "used in the same volume_type.") LOG.error(err_msg) raise exception.VolumeBackendAPIException(data=err_msg) - lun_params, lun_info, model_update = ( - self._create_base_type_volume(opts, volume, volume_type)) + lun_params, lun_info, model_update = self._create_base_type_volume( + opts, volume) model_update = self._add_extend_type_to_volume(opts, lun_params, lun_info, model_update) @@ -429,10 +339,11 @@ class HuaweiBaseDriver(driver.VolumeDriver): return model_update def _delete_volume(self, volume): - lun_id, lun_wwn = huawei_utils.get_volume_lun_id(self.client, volume) - if not lun_id: + lun_info = huawei_utils.get_lun_info(self.client, volume) + if not lun_info: return + lun_id = lun_info['ID'] 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) @@ -458,7 +369,7 @@ class HuaweiBaseDriver(driver.VolumeDriver): smart_qos = smartx.SmartQos(self.client) smart_qos.remove(qos_id, lun_id) - metadata = huawei_utils.get_lun_metadata(volume) + metadata = huawei_utils.get_volume_private_data(volume) if metadata.get('hypermetro_id'): metro = hypermetro.HuaweiHyperMetro(self.client, self.rmt_client, @@ -576,9 +487,15 @@ class HuaweiBaseDriver(driver.VolumeDriver): def update_migrated_volume(self, ctxt, volume, new_volume, original_volume_status=None): orig_lun_name = huawei_utils.encode_name(volume.id) - new_lun_id, lun_wwn = huawei_utils.get_volume_lun_id( + new_lun_info = huawei_utils.get_lun_info( self.client, new_volume) - new_metadata = huawei_utils.get_lun_metadata(new_volume) + if not new_lun_info: + msg = _("Volume %s doesn't exist.") % volume.id + LOG.error(msg) + raise exception.VolumeBackendAPIException(data=msg) + + new_lun_id = new_lun_info['ID'] + new_metadata = huawei_utils.get_volume_private_data(new_volume) model_update = { 'provider_location': huawei_utils.to_string(**new_metadata), } @@ -596,7 +513,7 @@ class HuaweiBaseDriver(driver.VolumeDriver): return model_update - def migrate_volume(self, ctxt, volume, host, new_type=None): + def migrate_volume(self, ctxt, volume, host): """Migrate a volume within the same array.""" self._check_volume_exist_on_array(volume, constants.VOLUME_NOT_EXISTS_RAISE) @@ -604,12 +521,11 @@ class HuaweiBaseDriver(driver.VolumeDriver): # NOTE(jlc): Replication volume can't migrate. But retype # can remove replication relationship first then do migrate. # So don't add this judgement into _check_migration_valid(). - volume_type = self._get_volume_type(volume) - opts = self._get_volume_params(volume_type) - if opts.get('replication_enabled') == 'true': + opts = huawei_utils.get_volume_params(volume) + if opts.get('replication_enabled'): return (False, None) - return self._migrate_volume(volume, host, new_type) + return self._migrate_volume(volume, host) def _check_migration_valid(self, host, volume): if 'pool_name' not in host['capabilities']: @@ -637,35 +553,24 @@ class HuaweiBaseDriver(driver.VolumeDriver): if not self._check_migration_valid(host, volume): return (False, None) - type_id = volume.volume_type_id - - volume_type = None - if type_id: - volume_type = volume_types.get_volume_type(None, type_id) - pool_name = host['capabilities']['pool_name'] pools = self.client.get_all_pools() pool_info = self.client.get_pool_info(pool_name, pools) dst_volume_name = six.text_type(uuid.uuid4()) - src_id, lun_wwn = huawei_utils.get_volume_lun_id(self.client, volume) - opts = None - qos = None + lun_info = huawei_utils.get_lun_info(self.client, volume) + if not lun_info: + msg = _("Volume %s doesn't exist.") % volume.id + LOG.error(msg) + raise exception.VolumeBackendAPIException(data=msg) + + src_id = lun_info['ID'] + if new_type: # If new type exists, use new type. - new_specs = new_type['extra_specs'] - opts = self._get_volume_params_from_specs(new_specs) - if 'LUNType' not in opts: - opts['LUNType'] = self.configuration.lun_type - - qos = smartx.SmartQos.get_qos_by_volume_type(new_type) - elif volume_type: - qos = smartx.SmartQos.get_qos_by_volume_type(volume_type) - - if not opts: - opts = self._get_volume_params(volume_type) - - lun_info = self.client.get_lun_info(src_id) + opts = huawei_utils.get_volume_type_params(new_type) + else: + opts = huawei_utils.get_volume_params(volume) if opts['policy']: policy = opts['policy'] @@ -698,15 +603,15 @@ class HuaweiBaseDriver(driver.VolumeDriver): lun_info = self.client.create_lun(lun_params) lun_id = lun_info['ID'] - if qos: - LOG.info('QoS: %s.', qos) + if opts.get('qos'): SmartQos = smartx.SmartQos(self.client) - SmartQos.add(qos, lun_id) - if opts: + SmartQos.add(opts['qos'], lun_id) + if opts.get('smartpartition'): smartpartition = smartx.SmartPartition(self.client) - smartpartition.add(opts, lun_id) + smartpartition.add(opts['partitionname'], lun_id) + if opts.get('smartcache'): smartcache = smartx.SmartCache(self.client) - smartcache.add(opts, lun_id) + smartcache.add(opts['cachename'], lun_id) dst_id = lun_info['ID'] self._wait_volume_ready(dst_id) @@ -720,24 +625,24 @@ class HuaweiBaseDriver(driver.VolumeDriver): We use LUNcopy to copy a new volume from snapshot. The time needed increases as volume size does. """ - volume_type = self._get_volume_type(volume) - opts = self._get_volume_params(volume_type) - if (opts.get('hypermetro') == 'true' - and opts.get('replication_enabled') == 'true'): + opts = huawei_utils.get_volume_params(volume) + if opts.get('hypermetro') and opts.get('replication_enabled'): msg = _("Hypermetro and Replication can not be " "used in the same volume_type.") LOG.error(msg) raise exception.VolumeBackendAPIException(data=msg) - snapshot_id = huawei_utils.get_snapshot_id(self.client, snapshot) - if snapshot_id is None: + snapshot_info = huawei_utils.get_snapshot_info(self.client, snapshot) + if not snapshot_info: msg = _('create_volume_from_snapshot: Snapshot %(name)s ' 'does not exist.') % {'name': snapshot.id} LOG.error(msg) raise exception.VolumeBackendAPIException(data=msg) - lun_params, lun_info, model_update = ( - self._create_base_type_volume(opts, volume, volume_type)) + snapshot_id = snapshot_info['ID'] + + lun_params, lun_info, model_update = self._create_base_type_volume( + opts, volume) tgt_lun_id = lun_info['ID'] luncopy_name = huawei_utils.encode_name(volume.id) @@ -810,8 +715,8 @@ class HuaweiBaseDriver(driver.VolumeDriver): If the volume exists on the array, return the LUN ID. If not exists, raise or log warning. """ - lun_id, lun_wwn = huawei_utils.get_volume_lun_id(self.client, volume) - if not lun_id: + lun_info = huawei_utils.get_lun_info(self.client, volume) + if not lun_info: msg = _("Volume %s does not exist on the array.") % volume.id if action == constants.VOLUME_NOT_EXISTS_WARN: LOG.warning(msg) @@ -819,27 +724,15 @@ class HuaweiBaseDriver(driver.VolumeDriver): raise exception.VolumeBackendAPIException(data=msg) return - if not lun_wwn: - LOG.debug("No LUN WWN recorded for volume %s.", volume.id) - - if not self.client.check_lun_exist(lun_id, lun_wwn): - msg = (_("Volume %s does not exist on the array.") - % volume.id) - if action == constants.VOLUME_NOT_EXISTS_WARN: - LOG.warning(msg) - if action == constants.VOLUME_NOT_EXISTS_RAISE: - raise exception.VolumeBackendAPIException(data=msg) - return - return lun_id + return lun_info['ID'] def extend_volume(self, volume, new_size): """Extend a volume.""" lun_id = self._check_volume_exist_on_array( volume, constants.VOLUME_NOT_EXISTS_RAISE) - volume_type = self._get_volume_type(volume) - opts = self._get_volume_params(volume_type) - if opts.get('replication_enabled') == 'true': + opts = huawei_utils.get_volume_params(volume) + if opts.get('replication_enabled'): msg = (_("Can't extend replication volume, volume: %(id)s") % {"id": volume.id}) LOG.error(msg) @@ -875,17 +768,16 @@ class HuaweiBaseDriver(driver.VolumeDriver): self.client.extend_lun(lun_id, new_size) def _create_snapshot_base(self, snapshot): - volume = snapshot.volume - if not volume: - msg = _("Can't get volume id from snapshot, snapshot: %(id)s" - ) % {'id': snapshot.id} + lun_info = huawei_utils.get_lun_info(self.client, snapshot.volume) + if not lun_info: + msg = _("Parent volume of snapshot %s doesn't exist." + ) % snapshot.id LOG.error(msg) raise exception.VolumeBackendAPIException(data=msg) - lun_id, lun_wwn = huawei_utils.get_volume_lun_id(self.client, volume) snapshot_name = huawei_utils.encode_name(snapshot.id) snapshot_description = snapshot.id - snapshot_info = self.client.create_snapshot(lun_id, + snapshot_info = self.client.create_snapshot(lun_info['ID'], snapshot_name, snapshot_description) snapshot_id = snapshot_info['ID'] @@ -910,10 +802,10 @@ class HuaweiBaseDriver(driver.VolumeDriver): def delete_snapshot(self, snapshot): LOG.info('Delete snapshot %s.', snapshot.id) - snapshot_id = huawei_utils.get_snapshot_id(self.client, snapshot) - if snapshot_id and self.client.check_snapshot_exist(snapshot_id): - self.client.stop_snapshot(snapshot_id) - self.client.delete_snapshot(snapshot_id) + snapshot_info = huawei_utils.get_snapshot_info(self.client, snapshot) + if snapshot_info: + self.client.stop_snapshot(snapshot_info['ID']) + self.client.delete_snapshot(snapshot_info['ID']) else: LOG.warning("Can't find snapshot on the array.") @@ -934,7 +826,7 @@ class HuaweiBaseDriver(driver.VolumeDriver): model_update = {} replica_enabled_change = change_opts.get('replication_enabled') replica_type_change = change_opts.get('replication_type') - if replica_enabled_change and replica_enabled_change[0] == 'true': + if replica_enabled_change and replica_enabled_change[0]: try: self.replica.delete_replica(volume) model_update.update({'replication_status': 'disabled', @@ -960,7 +852,7 @@ class HuaweiBaseDriver(driver.VolumeDriver): LOG.exception('Retype volume error.') return False - if replica_enabled_change and replica_enabled_change[1] == 'true': + if replica_enabled_change and replica_enabled_change[1]: try: # If replica_enabled_change is not None, the # replica_type_change won't be None. See function @@ -1081,15 +973,13 @@ class HuaweiBaseDriver(driver.VolumeDriver): LOG.error(msg) raise exception.VolumeBackendAPIException(data=msg) - new_qos = smartx.SmartQos.get_qos_by_volume_type(new_type) - if not self.support_func.get('QoS_support'): - if new_qos: + if new_opts['qos']: + if not self.support_func.get('QoS_support'): msg = (_("Can't support qos on the array.")) LOG.error(msg) raise exception.VolumeBackendAPIException(data=msg) - def _check_needed_changes(self, lun_id, old_opts, new_opts, - change_opts, new_type): + def _check_needed_changes(self, lun_id, old_opts, new_opts, change_opts): new_cache_id = None new_cache_name = new_opts['cachename'] if new_cache_name: @@ -1155,7 +1045,7 @@ class HuaweiBaseDriver(driver.VolumeDriver): new_partition_name]) # smartqos - new_qos = smartx.SmartQos.get_qos_by_volume_type(new_type) + new_qos = new_opts.get('qos') if not self.support_func.get('QoS_support'): if new_qos: msg = (_("Can't support qos on the array.")) @@ -1182,11 +1072,16 @@ class HuaweiBaseDriver(driver.VolumeDriver): 'replication_type': None, } - lun_id, lun_wwn = huawei_utils.get_volume_lun_id(self.client, volume) + lun_info = huawei_utils.get_lun_info(self.client, volume) + if not lun_info: + msg = _("Volume %s doesn't exist.") % volume.id + LOG.error(msg) + raise exception.VolumeBackendAPIException(data=msg) + + lun_id = lun_info['ID'] old_opts = self.get_lun_specs(lun_id) - new_specs = new_type['extra_specs'] - new_opts = self._get_volume_params_from_specs(new_specs) + new_opts = huawei_utils.get_volume_type_params(new_type) if 'LUNType' not in new_opts: new_opts['LUNType'] = self.configuration.lun_type @@ -1198,10 +1093,9 @@ class HuaweiBaseDriver(driver.VolumeDriver): migration = True change_opts['LUNType'] = (old_opts['LUNType'], new_opts['LUNType']) - volume_type = self._get_volume_type(volume) - volume_opts = self._get_volume_params(volume_type) - if (volume_opts['replication_enabled'] == 'true' - or new_opts['replication_enabled'] == 'true'): + volume_opts = huawei_utils.get_volume_params(volume) + if (volume_opts['replication_enabled'] or + new_opts['replication_enabled']): # If replication_enabled changes, # then replication_type in change_opts will be set. change_opts['replication_enabled'] = ( @@ -1212,7 +1106,7 @@ class HuaweiBaseDriver(driver.VolumeDriver): new_opts['replication_type']) change_opts = self._check_needed_changes(lun_id, old_opts, new_opts, - change_opts, new_type) + change_opts) LOG.debug("Determine changes when retype. Migration: " "%(migration)s, change_opts: %(change_opts)s.", @@ -1434,8 +1328,7 @@ class HuaweiBaseDriver(driver.VolumeDriver): # Handle volume type if specified. 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 = self._get_volume_params_from_specs(new_specs) + new_opts = huawei_utils.get_volume_type_params(volume_type) if ('LUNType' in new_opts and old_opts['LUNType'] != new_opts['LUNType']): msg = (_("Can't import LUN %(lun_id)s to Cinder. " @@ -1447,9 +1340,8 @@ class HuaweiBaseDriver(driver.VolumeDriver): change_opts = {'policy': None, 'partitionid': None, 'cacheid': None, 'qos': None} - change_opts = self._check_needed_changes(lun_id, old_opts, - new_opts, change_opts, - volume_type) + change_opts = self._check_needed_changes( + lun_id, old_opts, new_opts, change_opts) self.modify_lun(lun_id, change_opts) # Rename the LUN to make it manageable for Cinder. @@ -1502,7 +1394,7 @@ class HuaweiBaseDriver(driver.VolumeDriver): def manage_existing_get_size(self, volume, external_ref): """Get the size of the existing volume.""" lun_info = self._get_lun_info_by_ref(external_ref) - size = int(math.ceil(lun_info.get('CAPACITY') / + size = int(math.ceil(float(lun_info.get('CAPACITY')) / constants.CAPACITY_UNIT)) return size @@ -1546,9 +1438,10 @@ class HuaweiBaseDriver(driver.VolumeDriver): snapshot_info = self._get_snapshot_info_by_ref(existing_ref) snapshot_id = snapshot_info.get('ID') - parent_lun_id, lun_wwn = huawei_utils.get_volume_lun_id( + parent_lun_info = huawei_utils.get_lun_info( self.client, snapshot.volume) - if parent_lun_id != snapshot_info.get('PARENTID'): + if (not parent_lun_info or + parent_lun_info['ID'] != snapshot_info.get('PARENTID')): msg = (_("Can't import snapshot %s to Cinder. " "Snapshot doesn't belong to volume."), snapshot_id) raise exception.ManageExistingInvalidReference( @@ -1574,7 +1467,7 @@ class HuaweiBaseDriver(driver.VolumeDriver): def manage_existing_snapshot_get_size(self, snapshot, existing_ref): """Get the size of the existing snapshot.""" snapshot_info = self._get_snapshot_info_by_ref(existing_ref) - size = int(math.ceil(snapshot_info.get('USERCAPACITY') / + size = int(math.ceil(float(snapshot_info.get('USERCAPACITY')) / constants.CAPACITY_UNIT)) return size @@ -1594,8 +1487,7 @@ class HuaweiBaseDriver(driver.VolumeDriver): def _get_group_type(self, group): opts = [] for vol_type in group.volume_types: - specs = vol_type.extra_specs - opts.append(self._get_volume_params_from_specs(specs)) + opts.append(huawei_utils.get_volume_type_params(vol_type)) return opts @@ -1818,9 +1710,8 @@ class HuaweiBaseDriver(driver.VolumeDriver): replica_volumes = [] for v in volumes: - volume_type = self._get_volume_type(v) - opts = self._get_volume_params(volume_type) - if opts.get('replication_enabled') == 'true': + opts = huawei_utils.get_volume_params(v) + if opts.get('replication_enabled'): replica_volumes.append(v) else: normal_volumes.append(v) @@ -1938,7 +1829,7 @@ class HuaweiBaseDriver(driver.VolumeDriver): def get_lun_id_and_type(self, volume): if hasattr(volume, 'lun_type'): - metadata = huawei_utils.get_snapshot_metadata(volume) + metadata = huawei_utils.get_snapshot_private_data(volume) lun_id = metadata['huawei_snapshot_id'] lun_type = constants.SNAPSHOT_TYPE else: diff --git a/cinder/volume/drivers/huawei/constants.py b/cinder/volume/drivers/huawei/constants.py index e1e1ae26fe4..4da327d2779 100644 --- a/cinder/volume/drivers/huawei/constants.py +++ b/cinder/volume/drivers/huawei/constants.py @@ -19,7 +19,7 @@ STATUS_RUNNING = '10' STATUS_VOLUME_READY = '27' STATUS_LUNCOPY_READY = '40' STATUS_QOS_ACTIVE = '2' -STATUS_QOS_INACTIVE = '45' +QOS_INACTIVATED = '45' LUN_TYPE = '11' SNAPSHOT_TYPE = '27' @@ -36,7 +36,7 @@ ARRAY_VERSION = 'V300R003C00' FC_PORT_CONNECTED = '10' FC_INIT_ONLINE = '27' FC_PORT_MODE_FABRIC = '0' -CAPACITY_UNIT = 1024.0 * 1024.0 * 2 +CAPACITY_UNIT = 1024 * 1024 * 2 DEFAULT_WAIT_TIMEOUT = 3600 * 24 * 30 DEFAULT_WAIT_INTERVAL = 5 diff --git a/cinder/volume/drivers/huawei/huawei_driver.py b/cinder/volume/drivers/huawei/huawei_driver.py index 4baffdc16e6..48fe327af15 100644 --- a/cinder/volume/drivers/huawei/huawei_driver.py +++ b/cinder/volume/drivers/huawei/huawei_driver.py @@ -331,7 +331,7 @@ class HuaweiFCDriver(common.HuaweiBaseDriver, driver.FibreChannelDriver): # Add host into hostgroup. hostgroup_id = self.client.add_host_to_hostgroup(host_id) - metadata = huawei_utils.get_lun_metadata(volume) + metadata = huawei_utils.get_volume_private_data(volume) LOG.info("initialize_connection, metadata is: %s.", metadata) hypermetro_lun = metadata.get('hypermetro_id') is not None @@ -488,7 +488,7 @@ class HuaweiFCDriver(common.HuaweiBaseDriver, driver.FibreChannelDriver): self.client.delete_mapping_view(view_id) # Deal with hypermetro connection. - metadata = huawei_utils.get_lun_metadata(volume) + metadata = huawei_utils.get_volume_private_data(volume) LOG.info("Detach Volume, metadata is: %s.", metadata) if metadata.get('hypermetro_id'): diff --git a/cinder/volume/drivers/huawei/huawei_utils.py b/cinder/volume/drivers/huawei/huawei_utils.py index 15d0e33e155..e83e804f9e4 100644 --- a/cinder/volume/drivers/huawei/huawei_utils.py +++ b/cinder/volume/drivers/huawei/huawei_utils.py @@ -17,29 +17,33 @@ import hashlib import json from oslo_log import log as logging -from oslo_utils import units +from oslo_utils import strutils import retrying import six +from cinder import context from cinder import exception from cinder.i18n import _ from cinder import objects +from cinder.objects import fields from cinder.volume.drivers.huawei import constants -from cinder.volume import utils +from cinder.volume import qos_specs +from cinder.volume import volume_types + LOG = logging.getLogger(__name__) -def encode_name(id): - encoded_name = hashlib.md5(id.encode('utf-8')).hexdigest() - prefix = id.split('-')[0] + '-' +def encode_name(name): + encoded_name = hashlib.md5(name.encode('utf-8')).hexdigest() + prefix = name.split('-')[0] + '-' postfix = encoded_name[:constants.MAX_NAME_LENGTH - len(prefix)] return prefix + postfix -def old_encode_name(id): - pre_name = id.split("-")[0] - vol_encoded = six.text_type(hash(id)) +def old_encode_name(name): + pre_name = name.split("-")[0] + vol_encoded = six.text_type(hash(name)) if vol_encoded.startswith('-'): newuuid = pre_name + vol_encoded else: @@ -61,101 +65,287 @@ def old_encode_host_name(name): def wait_for_condition(func, interval, timeout): + def _retry_on_result(result): + return not result - r = retrying.Retrying(retry_on_result=lambda result: not result, - retry_on_exception=lambda result: False, + def _retry_on_exception(result): + return False + + r = retrying.Retrying(retry_on_result=_retry_on_result, + retry_on_exception=_retry_on_exception, wait_fixed=interval * 1000, stop_max_delay=timeout * 1000) - try: - r.call(func) - except retrying.RetryError: - msg = _('wait_for_condition: %s timed out.') % func.__name__ + r.call(func) + + +def _get_volume_type(volume): + if volume.volume_type: + return volume.volume_type + if volume.volume_type_id: + return volume_types.get_volume_type(None, volume.volume_type_id) + + +def get_volume_params(volume): + volume_type = _get_volume_type(volume) + return get_volume_type_params(volume_type) + + +def get_volume_type_params(volume_type): + specs = {} + if isinstance(volume_type, dict) and volume_type.get('extra_specs'): + specs = volume_type['extra_specs'] + elif isinstance(volume_type, objects.VolumeType + ) and volume_type.extra_specs: + specs = volume_type.extra_specs + + vol_params = get_volume_params_from_specs(specs) + vol_params['qos'] = None + + if isinstance(volume_type, dict) and volume_type.get('qos_specs_id'): + vol_params['qos'] = _get_qos_specs(volume_type['qos_specs_id']) + elif isinstance(volume_type, objects.VolumeType + ) and volume_type.qos_specs_id: + vol_params['qos'] = _get_qos_specs(volume_type.qos_specs_id) + + LOG.info('volume opts %s.', vol_params) + return vol_params + + +def get_volume_params_from_specs(specs): + opts = _get_opts_from_specs(specs) + + _verify_smartcache_opts(opts) + _verify_smartpartition_opts(opts) + _verify_smartthin_opts(opts) + + return opts + + +def _get_opts_from_specs(specs): + """Get the well defined extra specs.""" + opts = {} + + def _get_bool_param(k, v): + words = v.split() + if len(words) == 2 and words[0] == '': + return strutils.bool_from_string(words[1], strict=True) + + msg = _("%(k)s spec must be specified as %(k)s=' True' " + "or ' False'.") % {'k': k} LOG.error(msg) - raise exception.VolumeBackendAPIException(data=msg) + raise exception.InvalidInput(reason=msg) + + def _get_replication_type_param(k, v): + words = v.split() + if len(words) == 2 and words[0] == '': + REPLICA_SYNC_TYPES = {'sync': constants.REPLICA_SYNC_MODEL, + 'async': constants.REPLICA_ASYNC_MODEL} + sync_type = words[1].lower() + if sync_type in REPLICA_SYNC_TYPES: + return REPLICA_SYNC_TYPES[sync_type] + + msg = _("replication_type spec must be specified as " + "replication_type=' sync' or ' async'.") + LOG.error(msg) + raise exception.InvalidInput(reason=msg) + + def _get_string_param(k, v): + if not v: + msg = _("%s spec must be specified as a string.") % k + LOG.error(msg) + raise exception.InvalidInput(reason=msg) + return v + + opts_capability = { + 'capabilities:smarttier': (_get_bool_param, False), + 'capabilities:smartcache': (_get_bool_param, False), + 'capabilities:smartpartition': (_get_bool_param, False), + 'capabilities:thin_provisioning_support': (_get_bool_param, False), + 'capabilities:thick_provisioning_support': (_get_bool_param, False), + 'capabilities:hypermetro': (_get_bool_param, False), + 'capabilities:replication_enabled': (_get_bool_param, False), + 'replication_type': (_get_replication_type_param, + constants.REPLICA_ASYNC_MODEL), + 'smarttier:policy': (_get_string_param, None), + 'smartcache:cachename': (_get_string_param, None), + 'smartpartition:partitionname': (_get_string_param, None), + 'huawei_controller:controllername': (_get_string_param, None), + 'capabilities:dedup': (_get_bool_param, None), + 'capabilities:compression': (_get_bool_param, None), + } + + def _get_opt_key(spec_key): + key_split = spec_key.split(':') + if len(key_split) == 1: + return key_split[0] + else: + return key_split[1] + + for spec_key in opts_capability: + opt_key = _get_opt_key(spec_key) + opts[opt_key] = opts_capability[spec_key][1] + + for key, value in six.iteritems(specs): + if key not in opts_capability: + continue + func = opts_capability[key][0] + opt_key = _get_opt_key(key) + opts[opt_key] = func(key, value) + + return opts -def get_volume_size(volume): - """Calculate the volume size. +def _get_qos_specs(qos_specs_id): + ctxt = context.get_admin_context() + specs = qos_specs.get_qos_specs(ctxt, qos_specs_id) + if specs is None: + return {} - We should divide the given volume size by 512 for the 18000 system - calculates volume size with sectors, which is 512 bytes. - """ - volume_size = units.Gi / 512 # 1G - if int(volume.size) != 0: - volume_size = int(volume.size) * units.Gi / 512 + if specs.get('consumer') == 'front-end': + return {} - return volume_size + kvs = specs.get('specs', {}) + LOG.info('The QoS specs is: %s.', kvs) + + qos = {'IOTYPE': kvs.pop('IOType', None)} + + if qos['IOTYPE'] not in constants.QOS_IOTYPES: + msg = _('IOType must be in %(types)s.' + ) % {'types': constants.QOS_IOTYPES} + LOG.error(msg) + raise exception.InvalidInput(reason=msg) + + for k, v in kvs.items(): + if k not in constants.QOS_SPEC_KEYS: + msg = _('QoS key %s is not valid.') % k + LOG.error(msg) + raise exception.InvalidInput(reason=msg) + + if int(v) <= 0: + msg = _('QoS value for %s must > 0.') % k + LOG.error(msg) + raise exception.InvalidInput(reason=msg) + + qos[k.upper()] = v + + if len(qos) < 2: + msg = _('QoS policy must specify both IOType and one another ' + 'qos spec, got policy: %s.') % qos + LOG.error(msg) + raise exception.InvalidInput(reason=msg) + + qos_keys = set(qos.keys()) + if (qos_keys & set(constants.UPPER_LIMIT_KEYS) and + qos_keys & set(constants.LOWER_LIMIT_KEYS)): + msg = _('QoS policy upper limit and lower limit ' + 'conflict, QoS policy: %s.') % qos + LOG.error(msg) + raise exception.InvalidInput(reason=msg) + + return qos -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 _verify_smartthin_opts(opts): + if (opts['thin_provisioning_support'] and + opts['thick_provisioning_support']): + msg = _('Cannot set thin and thick at the same time.') + LOG.error(msg) + raise exception.InvalidInput(reason=msg) + elif opts['thin_provisioning_support']: + opts['LUNType'] = constants.THIN_LUNTYPE + elif opts['thick_provisioning_support']: + opts['LUNType'] = constants.THICK_LUNTYPE -def get_admin_metadata(volume): - admin_metadata = {} - if 'admin_metadata' in volume: - admin_metadata = volume.admin_metadata - elif 'volume_admin_metadata' in volume: - metadata = volume.get('volume_admin_metadata', []) - admin_metadata = {item['key']: item['value'] for item in metadata} - - LOG.debug("Volume ID: %(id)s, admin_metadata: %(admin_metadata)s.", - {"id": volume.id, "admin_metadata": admin_metadata}) - return admin_metadata +def _verify_smartcache_opts(opts): + if opts['smartcache'] and not opts['cachename']: + msg = _('Cache name is not specified, please set ' + 'smartcache:cachename in extra specs.') + LOG.error(msg) + raise exception.InvalidInput(reason=msg) -def get_snapshot_metadata_value(snapshot): - if type(snapshot) is objects.Snapshot: - return snapshot.metadata - - if 'snapshot_metadata' in snapshot: - metadata = snapshot.snapshot_metadata - return {item['key']: item['value'] for item in metadata} - - return {} +def _verify_smartpartition_opts(opts): + if opts['smartpartition'] and not opts['partitionname']: + msg = _('Partition name is not specified, please set ' + 'smartpartition:partitionname in extra specs.') + LOG.error(msg) + raise exception.InvalidInput(reason=msg) -def check_whether_operate_consistency_group(func): - def wrapper(self, context, group, *args, **kwargs): - if not utils.is_group_a_cg_snapshot_type(group): - msg = _("%s, the group or group snapshot is not cg or " - "cg_snapshot") % func.__name__ - LOG.debug(msg) - raise NotImplementedError(msg) - return func(self, context, group, *args, **kwargs) - return wrapper +def wait_lun_online(client, lun_id, wait_interval=None, wait_timeout=None): + def _lun_online(): + result = client.get_lun_info_by_id(lun_id) + if result['HEALTHSTATUS'] != constants.STATUS_HEALTH: + err_msg = _('LUN %s is abnormal.') % lun_id + LOG.error(err_msg) + raise exception.VolumeBackendAPIException(data=err_msg) + + if result['RUNNINGSTATUS'] == constants.LUN_INITIALIZING: + return False + + return True + + if not wait_interval: + wait_interval = constants.DEFAULT_WAIT_INTERVAL + if not wait_timeout: + wait_timeout = wait_interval * 10 + + wait_for_condition(_lun_online, wait_interval, wait_timeout) + + +def is_not_exist_exc(exc): + msg = getattr(exc, 'msg', '') + return 'not exist' in msg def to_string(**kwargs): return json.dumps(kwargs) if kwargs else '' -def get_lun_metadata(volume): +def to_dict(text): + return json.loads(text) if text else {} + + +def get_volume_private_data(volume): if not volume.provider_location: return {} - info = json.loads(volume.provider_location) + try: + info = json.loads(volume.provider_location) + except Exception: + LOG.exception("Decode volume provider_location error") + return {} + if isinstance(info, dict): return info # To keep compatible with old driver version - admin_metadata = get_admin_metadata(volume) - metadata = get_volume_metadata(volume) return {'huawei_lun_id': six.text_type(info), - 'huawei_lun_wwn': admin_metadata.get('huawei_lun_wwn'), - 'hypermetro_id': metadata.get('hypermetro_id'), - 'remote_lun_id': metadata.get('remote_lun_id') + 'huawei_lun_wwn': volume.admin_metadata.get('huawei_lun_wwn'), + 'huawei_sn': volume.metadata.get('huawei_sn'), + 'hypermetro_id': volume.metadata.get('hypermetro_id'), + 'remote_lun_id': volume.metadata.get('remote_lun_id') } -def get_snapshot_metadata(snapshot): +def get_volume_metadata(volume): + if isinstance(volume, objects.Volume): + return volume.metadata + if volume.get('volume_metadata'): + return {item['key']: item['value'] for item in + volume['volume_metadata']} + return {} + + +def get_replication_data(volume): + if not volume.replication_driver_data: + return {} + + return json.loads(volume.replication_driver_data) + + +def get_snapshot_private_data(snapshot): if not snapshot.provider_location: return {} @@ -164,41 +354,66 @@ def get_snapshot_metadata(snapshot): return info # To keep compatible with old driver version - return {'huawei_snapshot_id': six.text_type(info)} + return {'huawei_snapshot_id': six.text_type(info), + 'huawei_snapshot_wwn': snapshot.metadata.get( + 'huawei_snapshot_wwn'), + } -def get_volume_lun_id(client, volume): - metadata = get_lun_metadata(volume) - lun_id = metadata.get('huawei_lun_id') +def get_external_lun_info(client, external_ref): + lun_info = None + if 'source-id' in external_ref: + lun = client.get_lun_info_by_id(external_ref['source-id']) + lun_info = client.get_lun_info_by_name(lun['NAME']) + elif 'source-name' in external_ref: + lun_info = client.get_lun_info_by_name(external_ref['source-name']) - # First try the new encoded way. - if not lun_id: - volume_name = encode_name(volume.id) - lun_id = client.get_lun_id_by_name(volume_name) + return lun_info + + +def get_external_snapshot_info(client, external_ref): + snapshot_info = None + if 'source-id' in external_ref: + snapshot_info = client.get_snapshot_info_by_id( + external_ref['source-id']) + elif 'source-name' in external_ref: + snapshot_info = client.get_snapshot_info_by_name( + external_ref['source-name']) + + return snapshot_info + + +def get_lun_info(client, volume): + metadata = get_volume_private_data(volume) + + volume_name = encode_name(volume.id) + lun_info = client.get_lun_info_by_name(volume_name) # If new encoded way not found, try the old encoded way. - if not lun_id: + if not lun_info: volume_name = old_encode_name(volume.id) - lun_id = client.get_lun_id_by_name(volume_name) + lun_info = client.get_lun_info_by_name(volume_name) - return lun_id, metadata.get('huawei_lun_wwn') + if not lun_info and metadata.get('huawei_lun_id'): + lun_info = client.get_lun_info_by_id(metadata['huawei_lun_id']) + + if lun_info and ('huawei_lun_wwn' in metadata and + lun_info.get('WWN') != metadata['huawei_lun_wwn']): + return None + + return lun_info -def get_snapshot_id(client, snapshot): - metadata = get_snapshot_metadata(snapshot) - snapshot_id = metadata.get('huawei_snapshot_id') - - # First try the new encoded way. - if not snapshot_id: - name = encode_name(snapshot.id) - snapshot_id = client.get_snapshot_id_by_name(name) +def get_snapshot_info(client, snapshot): + name = encode_name(snapshot.id) + snapshot_info = client.get_snapshot_info_by_name(name) # If new encoded way not found, try the old encoded way. - if not snapshot_id: + if not snapshot_info: name = old_encode_name(snapshot.id) - snapshot_id = client.get_snapshot_id_by_name(name) + snapshot_info = client.get_snapshot_info_by_name(name) - return snapshot_id + return snapshot_info def get_host_id(client, host_name): @@ -212,3 +427,57 @@ def get_host_id(client, host_name): host_id = client.get_host_id_by_name(encoded_name) return host_id + + +def get_hypermetro_group(client, group_id): + encoded_name = encode_name(group_id) + group = client.get_metrogroup_by_name(encoded_name) + if not group: + encoded_name = old_encode_name(group_id) + group = client.get_metrogroup_by_name(encoded_name) + return group + + +def get_replication_group(client, group_id): + encoded_name = encode_name(group_id) + group = client.get_replication_group_by_name(encoded_name) + if not group: + encoded_name = old_encode_name(group_id) + group = client.get_replication_group_by_name(encoded_name) + return group + + +def get_volume_model_update(volume, **kwargs): + private_data = get_volume_private_data(volume) + + if kwargs.get('hypermetro_id'): + private_data['hypermetro_id'] = kwargs.get('hypermetro_id') + elif 'hypermetro_id' in private_data: + private_data.pop('hypermetro_id') + + if 'huawei_lun_id' in kwargs: + private_data['huawei_lun_id'] = kwargs['huawei_lun_id'] + if 'huawei_lun_wwn' in kwargs: + private_data['huawei_lun_wwn'] = kwargs['huawei_lun_wwn'] + if 'huawei_sn' in kwargs: + private_data['huawei_sn'] = kwargs['huawei_sn'] + + model_update = {'provider_location': to_string(**private_data)} + + if kwargs.get('replication_id'): + model_update['replication_driver_data'] = to_string( + pair_id=kwargs.get('replication_id')) + model_update['replication_status'] = fields.ReplicationStatus.ENABLED + else: + model_update['replication_driver_data'] = None + model_update['replication_status'] = fields.ReplicationStatus.DISABLED + + return model_update + + +def get_group_type_params(group): + opts = [] + for volume_type in group.volume_types: + opt = get_volume_type_params(volume_type) + opts.append(opt) + return opts diff --git a/cinder/volume/drivers/huawei/hypermetro.py b/cinder/volume/drivers/huawei/hypermetro.py index 170b6d85e10..c73dd4f999f 100644 --- a/cinder/volume/drivers/huawei/hypermetro.py +++ b/cinder/volume/drivers/huawei/hypermetro.py @@ -75,7 +75,7 @@ class HuaweiHyperMetro(object): def delete_hypermetro(self, volume): """Delete hypermetro.""" - metadata = huawei_utils.get_lun_metadata(volume) + metadata = huawei_utils.get_volume_private_data(volume) metro_id = metadata['hypermetro_id'] remote_lun_id = metadata['remote_lun_id'] @@ -110,7 +110,7 @@ class HuaweiHyperMetro(object): {'wwpns': wwns, 'id': volume.id}) - metadata = huawei_utils.get_lun_metadata(volume) + metadata = huawei_utils.get_volume_private_data(volume) lun_id = metadata.get('remote_lun_id') if lun_id is None: msg = _("Can't get volume id. Volume name: %s.") % volume.id @@ -179,7 +179,7 @@ class HuaweiHyperMetro(object): def disconnect_volume_fc(self, volume, connector): """Delete map between a volume and a host for FC.""" wwns = connector['wwpns'] - metadata = huawei_utils.get_lun_metadata(volume) + metadata = huawei_utils.get_volume_private_data(volume) lun_id = metadata.get('remote_lun_id') host_name = connector['host'] left_lunnum = -1 @@ -324,7 +324,7 @@ class HuaweiHyperMetro(object): raise exception.VolumeBackendAPIException(data=msg) def check_metro_need_to_stop(self, volume): - metadata = huawei_utils.get_lun_metadata(volume) + metadata = huawei_utils.get_volume_private_data(volume) metro_id = metadata['hypermetro_id'] metro_existed = self.client.check_hypermetro_exist(metro_id) diff --git a/cinder/volume/drivers/huawei/replication.py b/cinder/volume/drivers/huawei/replication.py index bb25e76d729..55d55684e5a 100644 --- a/cinder/volume/drivers/huawei/replication.py +++ b/cinder/volume/drivers/huawei/replication.py @@ -588,7 +588,7 @@ class ReplicaPairManager(object): # Switch replication pair role, and start synchronize. self.rmt_driver.enable(pair_id) - local_metadata = huawei_utils.get_lun_metadata(v) + local_metadata = huawei_utils.get_volume_private_data(v) new_drv_data = to_string( {'pair_id': pair_id, 'rmt_lun_id': local_metadata.get('huawei_lun_id'), @@ -630,7 +630,7 @@ class ReplicaPairManager(object): self.rmt_driver.failover(pair_id) - local_metadata = huawei_utils.get_lun_metadata(v) + local_metadata = huawei_utils.get_volume_private_data(v) new_drv_data = to_string( {'pair_id': pair_id, 'rmt_lun_id': local_metadata.get('huawei_lun_id'), diff --git a/cinder/volume/drivers/huawei/rest_client.py b/cinder/volume/drivers/huawei/rest_client.py index 9bae12d794c..99b6e66a207 100644 --- a/cinder/volume/drivers/huawei/rest_client.py +++ b/cinder/volume/drivers/huawei/rest_client.py @@ -1416,7 +1416,7 @@ class RestClient(object): return target_iqn - def create_qos_policy(self, qos, lun_id): + def create_qos(self, qos, lun_id): # Get local time. localtime = time.strftime('%Y%m%d%H%M%S', time.localtime(time.time())) # Package QoS name. @@ -1440,8 +1440,7 @@ class RestClient(object): return result['data']['ID'] - def delete_qos_policy(self, qos_id): - """Delete a QoS policy.""" + def delete_qos(self, qos_id): url = "/ioclass/" + qos_id data = {"TYPE": "230", "ID": qos_id} @@ -2371,3 +2370,49 @@ class RestClient(object): if result.get("data"): return result.get("data").get("COUNT") + + def get_lun_info_by_name(self, name): + url = "/lun?filter=NAME::%s" % name + result = self.call(url, None, "GET") + + msg = _('Get lun by name %s error.') % name + self._assert_rest_result(result, msg) + + if result.get('data'): + return result['data'][0] + + def get_lun_info_by_id(self, lun_id): + url = "/lun/" + lun_id + result = self.call(url, None, "GET") + + msg = _('Get lun by id %s error.') % lun_id + self._assert_rest_result(result, msg) + + return result['data'] + + def get_snapshot_info_by_name(self, name): + url = "/snapshot?filter=NAME::%s" % name + result = self.call(url, None, "GET") + + msg = _('Get snapshot by name %s error.') % name + self._assert_rest_result(result, msg) + + if result.get('data'): + return result['data'][0] + + def get_snapshot_info_by_id(self, snapshot_id): + url = "/snapshot/" + snapshot_id + result = self.call(url, None, "GET") + + msg = _('Get snapshot by id %s error.') % snapshot_id + self._assert_rest_result(result, msg) + + return result['data'] + + def update_qos_luns(self, qos_id, lun_list): + url = "/ioclass/" + qos_id + data = {"LUNLIST": lun_list} + result = self.call(url, data, "PUT") + + msg = _('Update luns of qos %s error.') % qos_id + self._assert_rest_result(result, msg) diff --git a/cinder/volume/drivers/huawei/smartx.py b/cinder/volume/drivers/huawei/smartx.py index b1d35093f14..e9edab0606f 100644 --- a/cinder/volume/drivers/huawei/smartx.py +++ b/cinder/volume/drivers/huawei/smartx.py @@ -13,15 +13,14 @@ # License for the specific language governing permissions and limitations # under the License. -from oslo_log import log as logging -from oslo_utils import excutils +import json + +from oslo_log import log as logging -from cinder import context from cinder import exception from cinder.i18n import _ from cinder import utils from cinder.volume.drivers.huawei import constants -from cinder.volume import qos_specs LOG = logging.getLogger(__name__) @@ -30,211 +29,116 @@ class SmartQos(object): def __init__(self, client): self.client = client - @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 {} + def _check_qos_consistency(self, policy, qos): + for key in [k.upper() for k in constants.QOS_SPEC_KEYS]: + if qos.get(key, '0') != policy.get(key, '0'): + return False + return True - qos_specs_id = volume_type.get('qos_specs_id') - if not qos_specs_id: - return {} - - qos = {} - io_type_flag = None - ctxt = context.get_admin_context() - consumer = qos_specs.get_qos_specs(ctxt, qos_specs_id)['consumer'] - if consumer == 'front-end': - return {} - - kvs = qos_specs.get_qos_specs(ctxt, qos_specs_id)['specs'] - LOG.info('The QoS sepcs is: %s.', kvs) - for k, v in kvs.items(): - if k not in constants.HUAWEI_VALID_KEYS: - continue - if k != 'IOType' and int(v) <= 0: - msg = _('QoS config is wrong. %s must > 0.') % k - LOG.error(msg) - raise exception.InvalidInput(reason=msg) - if k == 'IOType': - if v not in ['0', '1', '2']: - msg = _('Illegal value specified for IOTYPE: 0, 1, or 2.') - LOG.error(msg) - raise exception.InvalidInput(reason=msg) - io_type_flag = 1 - qos[k.upper()] = v - else: - qos[k.upper()] = v - - if not io_type_flag: - msg = (_('QoS policy must specify for IOTYPE: 0, 1, or 2, ' - 'QoS policy: %(qos_policy)s ') % {'qos_policy': qos}) - LOG.error(msg) - raise exception.InvalidInput(reason=msg) - - # QoS policy must specify for IOTYPE and another qos_specs. - if len(qos) < 2: - msg = (_('QoS policy must specify for IOTYPE and another ' - 'qos_specs, QoS policy: %(qos_policy)s.') - % {'qos_policy': qos}) - LOG.error(msg) - raise exception.InvalidInput(reason=msg) - - for upper_limit in constants.UPPER_LIMIT_KEYS: - for lower_limit in constants.LOWER_LIMIT_KEYS: - if upper_limit in qos and lower_limit in qos: - msg = (_('QoS policy upper_limit and lower_limit ' - 'conflict, QoS policy: %(qos_policy)s.') - % {'qos_policy': qos}) - LOG.error(msg) - raise exception.InvalidInput(reason=msg) - - 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 _change_lun_priority(self, qos, lun_id): + for key in qos: + if key.startswith('MIN') or key.startswith('LATENCY'): + data = {"IOPRIORITY": "3"} + self.client.update_lun(lun_id, data) + break @utils.synchronized('huawei_qos', external=True) def add(self, qos, lun_id): - policy_id = None + self._change_lun_priority(qos, lun_id) + qos_id = self.client.create_qos(qos, lun_id) try: - # Check QoS priority. - 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() - if version >= constants.ARRAY_VERSION: - (qos_id, lun_list) = self.client.find_available_qos(qos) - if qos_id: - self.client.add_lun_to_qos(qos_id, lun_id, lun_list) - else: - policy_id = self.client.create_qos_policy(qos, lun_id) - self.client.activate_deactivate_qos(policy_id, True) - else: - policy_id = self.client.create_qos_policy(qos, lun_id) - self.client.activate_deactivate_qos(policy_id, True) + self.client.activate_deactivate_qos(qos_id, True) except exception.VolumeBackendAPIException: - with excutils.save_and_reraise_exception(): - if policy_id is not None: - self.client.delete_qos_policy(policy_id) + self.remove(qos_id, lun_id) + raise + + return qos_id @utils.synchronized('huawei_qos', external=True) - def remove(self, qos_id, lun_id): - qos_info = self.client.get_qos_info(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_INACTIVE: + def remove(self, qos_id, lun_id, qos_info=None): + if not qos_info: + qos_info = self.client.get_qos_info(qos_id) + lun_list = json.loads(qos_info['LUNLIST']) + if lun_id in lun_list: + lun_list.remove(lun_id) + + if len(lun_list) == 0: + if qos_info['RUNNINGSTATUS'] != constants.QOS_INACTIVATED: self.client.activate_deactivate_qos(qos_id, False) - self.client.delete_qos_policy(qos_id) + self.client.delete_qos(qos_id) else: - self.client.remove_lun_from_qos(lun_id, lun_list, qos_id) + self.client.update_qos_luns(qos_id, lun_list) + + def update(self, qos_id, new_qos, lun_id): + qos_info = self.client.get_qos_info(qos_id) + if self._check_qos_consistency(qos_info, new_qos): + return + + self.remove(qos_id, lun_id, qos_info) + self.add(new_qos, lun_id) class SmartPartition(object): def __init__(self, client): self.client = client - def add(self, opts, lun_id): - if opts['smartpartition'] != 'true': - return - if not opts['partitionname']: - raise exception.InvalidInput( - reason=_('Partition name is None, please set ' - 'smartpartition:partitionname in key.')) - - partition_id = self.client.get_partition_id_by_name( - opts['partitionname']) + def add(self, partitionname, lun_id): + partition_id = self.client.get_partition_id_by_name(partitionname) if not partition_id: - raise exception.InvalidInput( - reason=(_('Can not find partition id by name %(name)s.') - % {'name': opts['partitionname']})) + msg = _('Cannot find partition by name %s.') % partitionname + LOG.error(msg) + raise exception.InvalidInput(reason=msg) self.client.add_lun_to_partition(lun_id, partition_id) + return partition_id + + def remove(self, partition_id, lun_id): + self.client.remove_lun_from_partition(lun_id, partition_id) + + def update(self, partition_id, partitionname, lun_id): + partition_info = self.client.get_partition_info_by_id(partition_id) + if partition_info['NAME'] == partitionname: + return + + self.remove(partition_id, lun_id) + self.add(partitionname, lun_id) + + def check_partition_valid(self, partitionname): + partition_id = self.client.get_partition_id_by_name(partitionname) + if not partition_id: + msg = _("Partition %s doesn't exist.") % partitionname + LOG.error(msg) + raise exception.InvalidInput(reason=msg) class SmartCache(object): def __init__(self, client): self.client = client - def add(self, opts, lun_id): - if opts['smartcache'] != 'true': - return - if not opts['cachename']: - raise exception.InvalidInput( - reason=_('Cache name is None, please set ' - 'smartcache:cachename in key.')) - - cache_id = self.client.get_cache_id_by_name(opts['cachename']) + def add(self, cachename, lun_id): + cache_id = self.client.get_cache_id_by_name(cachename) if not cache_id: - raise exception.InvalidInput( - reason=(_('Can not find cache id by cache name %(name)s.') - % {'name': opts['cachename']})) + msg = _('Cannot find cache by name %s.') % cachename + LOG.error(msg) + raise exception.InvalidInput(reason=msg) self.client.add_lun_to_cache(lun_id, cache_id) + return cache_id + def remove(self, cache_id, lun_id): + self.client.remove_lun_from_cache(lun_id, cache_id) -class SmartX(object): - def get_smartx_specs_opts(self, opts): - # Check that smarttier is 0/1/2/3 - opts = self.get_smarttier_opts(opts) - opts = self.get_smartthin_opts(opts) - opts = self.get_smartcache_opts(opts) - opts = self.get_smartpartition_opts(opts) - return opts + def update(self, cache_id, cachename, lun_id): + cache_info = self.client.get_cache_info_by_id(cache_id) + if cache_info['NAME'] == cachename: + return - def get_smarttier_opts(self, opts): - if opts['smarttier'] == 'true': - if not opts['policy']: - opts['policy'] = '1' - elif opts['policy'] not in ['0', '1', '2', '3']: - raise exception.InvalidInput( - reason=(_('Illegal value specified for smarttier: ' - 'set to either 0, 1, 2, or 3.'))) - else: - opts['policy'] = '0' + self.remove(cache_id, lun_id) + self.add(cachename, lun_id) - return opts - - def get_smartthin_opts(self, opts): - if opts['thin_provisioning_support'] == 'true': - if opts['thick_provisioning_support'] == 'true': - raise exception.InvalidInput( - reason=(_('Illegal value specified for thin: ' - 'Can not set thin and thick at the same time.'))) - else: - opts['LUNType'] = constants.THIN_LUNTYPE - if opts['thick_provisioning_support'] == 'true': - opts['LUNType'] = constants.THICK_LUNTYPE - - return opts - - def get_smartcache_opts(self, opts): - if opts['smartcache'] == 'true': - if not opts['cachename']: - raise exception.InvalidInput( - reason=_('Cache name is None, please set ' - 'smartcache:cachename in key.')) - else: - opts['cachename'] = None - - return opts - - def get_smartpartition_opts(self, opts): - if opts['smartpartition'] == 'true': - if not opts['partitionname']: - raise exception.InvalidInput( - reason=_('Partition name is None, please set ' - 'smartpartition:partitionname in key.')) - else: - opts['partitionname'] = None - - return opts + def check_cache_valid(self, cachename): + cache_id = self.client.get_cache_id_by_name(cachename) + if not cache_id: + msg = _("Cache %s doesn't exit.") % cachename + LOG.error(msg) + raise exception.InvalidInput(reason=msg)