From ddc91052e4eb58c2686e9ccc9aaa90e5fa13bbd5 Mon Sep 17 00:00:00 2001 From: Thiago Correa Date: Wed, 28 Aug 2019 11:40:33 -0300 Subject: [PATCH] NetApp SolidFire: Add options for replication mode This adds the option to specify the replication mode as: Synchronous, Asynchronous or SnapshotOnly. Depends-On: I920f958db1d48e52b548082d852c03f427a279ca Change-Id: I74d31e7a262f3a59f5e82c710f3ca5fd84e3ea60 --- .../drivers/solidfire/test_solidfire.py | 40 ++++++++++++------- cinder/volume/drivers/solidfire.py | 31 ++++++++++---- ...add-replication-mode-eb26535d0ec78cb4.yaml | 7 ++++ 3 files changed, 57 insertions(+), 21 deletions(-) create mode 100644 releasenotes/notes/netapp-solidfire-add-replication-mode-eb26535d0ec78cb4.yaml diff --git a/cinder/tests/unit/volume/drivers/solidfire/test_solidfire.py b/cinder/tests/unit/volume/drivers/solidfire/test_solidfire.py index 84423510e09..89d86777128 100644 --- a/cinder/tests/unit/volume/drivers/solidfire/test_solidfire.py +++ b/cinder/tests/unit/volume/drivers/solidfire/test_solidfire.py @@ -18,7 +18,10 @@ import datetime import re import six -import ddt +from ddt import data +from ddt import ddt +from ddt import file_data +from ddt import unpack import mock from oslo_utils import timeutils from oslo_utils import units @@ -63,7 +66,7 @@ f_uuid = ['262b9ce2-a71a-4fbe-830c-c20c5596caea', '362b9ce2-a71a-4fbe-830c-c20c5596caea'] -@ddt.ddt +@ddt class SolidFireVolumeTestCase(test.TestCase): EXPECTED_QOS = {'minIOPS': 110, 'burstIOPS': 1530, 'maxIOPS': 1020} @@ -1237,8 +1240,8 @@ class SolidFireVolumeTestCase(test.TestCase): qos = sfv._set_qos_by_volume_type(self.ctxt, type_ref['id'], size) self.assertEqual(self.expected_qos_results, qos) - @ddt.file_data("scaled_iops_test_data.json") - @ddt.unpack + @file_data("scaled_iops_test_data.json") + @unpack def test_scaled_qos_spec_by_type(self, argument): sfv = solidfire.SolidFireDriver(configuration=self.configuration) size = argument[0].pop('size') @@ -1246,8 +1249,8 @@ class SolidFireVolumeTestCase(test.TestCase): qos = sfv._set_qos_by_volume_type(self.ctxt, type_ref['id'], size) self.assertEqual(argument[1], qos) - @ddt.file_data("scaled_iops_invalid_data.json") - @ddt.unpack + @file_data("scaled_iops_invalid_data.json") + @unpack def test_set_scaled_qos_by_type_invalid(self, inputs): sfv = solidfire.SolidFireDriver(configuration=self.configuration) size = inputs[0].pop('size') @@ -2684,22 +2687,31 @@ class SolidFireVolumeTestCase(test.TestCase): except Exception: pass - def test_set_rep_by_volume_type(self): + @data('Async', 'Sync', 'SnapshotsOnly') + @mock.patch.object(volume_types, 'get_volume_type') + def test_set_rep_by_volume_type(self, mode, mock_get_volume_type): + mock_get_volume_type.return_value = { + 'name': 'sf-1', 'deleted': False, + 'created_at': '2014-02-06 04:58:11', + 'updated_at': None, 'extra_specs': + {'replication_enabled': ' True', + 'solidfire:replication_mode': mode}, + 'deleted_at': None, + 'id': '290edb2a-f5ea-11e5-9ce9-5e5517507c66'} + rep_opts = {} sfv = solidfire.SolidFireDriver(configuration=self.configuration) sfv.cluster_pairs = self.cluster_pairs ctxt = None type_id = '290edb2a-f5ea-11e5-9ce9-5e5517507c66' - fake_type = {'extra_specs': {'replication_enabled': ' True'}} - with mock.patch.object(volume_types, - 'get_volume_type', - return_value=fake_type): - self.assertEqual('Async', sfv._set_rep_by_volume_type( - ctxt, type_id)) + rep_opts['rep_type'] = mode + self.assertEqual(rep_opts, sfv._set_rep_by_volume_type(ctxt, type_id)) + mock_get_volume_type.assert_called() def test_replicate_volume(self): replication_status = fields.ReplicationStatus.ENABLED fake_vol = {'project_id': 1, 'volumeID': 1, 'size': 1} params = {'attributes': {}} + rep_info = {'rep_type': 'Async'} sf_account = {'initiatorSecret': 'shhh', 'targetSecret': 'dont-tell'} model_update = {'provider_id': '1 2 xxxx'} sfv = solidfire.SolidFireDriver(configuration=self.configuration) @@ -2716,7 +2728,7 @@ class SolidFireVolumeTestCase(test.TestCase): return_value=model_update): self.assertEqual({'replication_status': replication_status}, sfv._replicate_volume(fake_vol, params, - sf_account, {})) + sf_account, rep_info)) def test_pythons_try_except(self): def _fake_retrieve_rep(vol): diff --git a/cinder/volume/drivers/solidfire.py b/cinder/volume/drivers/solidfire.py index 73a4b54214b..e6b97a346d6 100644 --- a/cinder/volume/drivers/solidfire.py +++ b/cinder/volume/drivers/solidfire.py @@ -221,9 +221,11 @@ class SolidFireDriver(san.SanISCSIDriver): 2.0.12 - Fix bug #1744005 2.0.14 - Fix bug #1782588 qos settings on extend 2.0.15 - Fix bug #1834013 NetApp SolidFire replication errors + 2.0.16 - Add options for replication mode (Async, Sync and + SnapshotsOnly) """ - VERSION = '2.0.15' + VERSION = '2.0.16' # ThirdPartySystems wiki page CI_WIKI_NAME = "NetApp_SolidFire_CI" @@ -323,6 +325,18 @@ class SolidFireDriver(san.SanISCSIDriver): def get_driver_options(): return sf_opts + def _init_vendor_properties(self): + properties = {} + self._set_property( + properties, + "solidfire:replication_mode", + "Replication mode", + _("Specifies replication mode."), + "string", + enum=["Async", "Sync", "SnapshotsOnly"]) + + return properties, 'solidfire' + def __getattr__(self, attr): if hasattr(self.target_driver, attr): return getattr(self.target_driver, attr) @@ -1412,15 +1426,17 @@ class SolidFireDriver(san.SanISCSIDriver): return rep_data def _set_rep_by_volume_type(self, ctxt, type_id): - rep_type = None + rep_modes = ['Async', 'Sync', 'SnapshotsOnly'] + rep_opts = {} type_ref = volume_types.get_volume_type(ctxt, type_id) specs = type_ref.get('extra_specs') - - # TODO(erlon): Add support for sync/snapshot replication if specs.get('replication_enabled', "") == " True": - rep_type = 'Async' + if specs.get('solidfire:replication_mode') in rep_modes: + rep_opts['rep_type'] = specs.get('solidfire:replication_mode') + else: + rep_opts['rep_type'] = 'Async' - return rep_type + return rep_opts def _replicate_volume(self, volume, params, parent_sfaccount, rep_info): @@ -1476,7 +1492,8 @@ class SolidFireDriver(san.SanISCSIDriver): volume['volumeID']) # Make sure we split any pair the volume have - params = {'volumeID': volume['volumeID'], 'mode': rep_info} + params = {'volumeID': volume['volumeID'], + 'mode': rep_info['rep_type']} self._issue_api_request('RemoveVolumePair', params, '8.0') rep_key = self._issue_api_request( diff --git a/releasenotes/notes/netapp-solidfire-add-replication-mode-eb26535d0ec78cb4.yaml b/releasenotes/notes/netapp-solidfire-add-replication-mode-eb26535d0ec78cb4.yaml new file mode 100644 index 00000000000..c044ee06029 --- /dev/null +++ b/releasenotes/notes/netapp-solidfire-add-replication-mode-eb26535d0ec78cb4.yaml @@ -0,0 +1,7 @@ +--- +upgrade: + - | + SolidFire supports Synchronous, Asynchronous and SnapshotsOnly replication + modes. This adds the config option `solidfire:replication_mode` to specify + the mode to be used by Cinder. Its value can be `Sync`, `Async` or + `SnapshotsOnly`.