From 3ebf07c4a8f9b620880e9d8285ed2fb3b3dd196a Mon Sep 17 00:00:00 2001 From: Kiran Pawar Date: Wed, 7 Sep 2022 16:10:35 +0000 Subject: [PATCH] [WIP] Update replica state before promote. During promote of replica, retrieve replica state from backend before promotion. In case DB state is in-sync, but backend reports out-sync, halt promotion. --- manila/share/api.py | 3 ++ .../netapp/dataontap/cluster_mode/lib_base.py | 32 ++++++++++++++++++- manila/share/drivers/netapp/options.py | 6 ++++ .../dataontap/cluster_mode/test_lib_base.py | 9 ++++++ manila/tests/share/test_api.py | 10 ++++++ 5 files changed, 59 insertions(+), 1 deletion(-) diff --git a/manila/share/api.py b/manila/share/api.py index a4aa8802eb..6d811c2fcb 100644 --- a/manila/share/api.py +++ b/manila/share/api.py @@ -783,6 +783,9 @@ def delete_share_replica(self, context, share_replica, force=False): def promote_share_replica(self, context, share_replica): + self.share_rpcapi.update_share_replica(context, share_replica) + share_replica = self.db.share_replica_get(context, + share_replica['id']) if share_replica.get('status') != constants.STATUS_AVAILABLE: msg = _("Replica %(replica_id)s must be in %(status)s state to be " "promoted.") diff --git a/manila/share/drivers/netapp/dataontap/cluster_mode/lib_base.py b/manila/share/drivers/netapp/dataontap/cluster_mode/lib_base.py index 2daf3e4f64..eb6984708f 100644 --- a/manila/share/drivers/netapp/dataontap/cluster_mode/lib_base.py +++ b/manila/share/drivers/netapp/dataontap/cluster_mode/lib_base.py @@ -750,7 +750,6 @@ def _get_logical_space_options(self, vserver_client, share_name): src_volume.get('is-space-reporting-logical') } - def _get_efficiency_options(self, vserver_client, share_name): status = vserver_client.get_volume_efficiency_status(share_name) provisioning_opts = { @@ -2954,6 +2953,37 @@ def update_replica_state(self, context, replica_list, replica, .isoformat(), 3600))): return constants.REPLICA_STATE_OUT_OF_SYNC + for k, v in snapmirror.items(): + LOG.error("===== snapmirror Key %(k)s Value %(v)s ", {'k': k, 'v': v}) + + ''' + replica_backend = share_utils.extract_host(replica['host'], + level='backend_name') + config = data_motion.get_backend_configuration(replica_backend) + config_size = (int(config.safe_get( + 'netapp_snapmirror_last_transfer_size_limit')) * units.Ki) + last_transfer_size = snapmirror.get('last-transfer-size', 0) + for unit in ['B', 'KB', 'MB', 'GB']: + last_transfer_size = last_transfer_size.split(unit)[0] + try: + last_transfer_size = float(last_transfer_size) + if unit == 'KB': + last_transfer_size = last_transfer_size * 1024 ** 1 + if unit == 'MB': + last_transfer_size = last_transfer_size * 1024 ** 2 + if unit == 'GB': + last_transfer_size = last_transfer_size * 1024 ** 3 + break + except ValueError: + continue + if last_transfer_size > config_size: + return constants.REPLICA_STATE_OUT_OF_SYNC + + last_transfer_error = snapmirror.get('last-transfer-error', None) + if last_transfer_error: + return constants.REPLICA_STATE_OUT_OF_SYNC + ''' + # Check all snapshots exist snapshots = [snap['share_replica_snapshot'] for snap in share_snapshots] diff --git a/manila/share/drivers/netapp/options.py b/manila/share/drivers/netapp/options.py index cc2431acb8..be6cfd2360 100644 --- a/manila/share/drivers/netapp/options.py +++ b/manila/share/drivers/netapp/options.py @@ -232,6 +232,12 @@ default=3600, # One Hour help='The maximum time in seconds to wait for a snapmirror ' 'release when breaking snapmirror relationships.'), + cfg.IntOpt('netapp_snapmirror_last_transfer_size_limit', + min=512, + default=1024, # One MB + help='This option set the last transfer size limit (in KB) ' + 'of snapmirror to decide whether replica is in sync or ' + 'out of sync.'), cfg.IntOpt('netapp_volume_move_cutover_timeout', min=0, default=3600, # One Hour, diff --git a/manila/tests/share/drivers/netapp/dataontap/cluster_mode/test_lib_base.py b/manila/tests/share/drivers/netapp/dataontap/cluster_mode/test_lib_base.py index ff04658cd1..e75487f899 100644 --- a/manila/tests/share/drivers/netapp/dataontap/cluster_mode/test_lib_base.py +++ b/manila/tests/share/drivers/netapp/dataontap/cluster_mode/test_lib_base.py @@ -4371,6 +4371,9 @@ def test_update_replica_state_in_sync(self): self.mock_object(self.library, '_is_readable_replica', mock.Mock(return_value=False)) + mock_backend_config = fake.get_config_cmode() + self.mock_object(data_motion, 'get_backend_configuration', + mock.Mock(return_value=mock_backend_config)) result = self.library.update_replica_state(None, [fake.SHARE], fake.SHARE, None, [], @@ -4414,6 +4417,9 @@ def test_update_replica_state_in_sync_with_snapshots(self): self.mock_object(self.library, '_is_readable_replica', mock.Mock(return_value=False)) + mock_backend_config = fake.get_config_cmode() + self.mock_object(data_motion, 'get_backend_configuration', + mock.Mock(return_value=mock_backend_config)) result = self.library.update_replica_state(None, [fake.SHARE], fake.SHARE, None, snapshots, @@ -4442,6 +4448,9 @@ def test_update_replica_state_missing_snapshot(self): self.mock_object(self.library, '_is_readable_replica', mock.Mock(return_value=False)) + mock_backend_config = fake.get_config_cmode() + self.mock_object(data_motion, 'get_backend_configuration', + mock.Mock(return_value=mock_backend_config)) result = self.library.update_replica_state(None, [fake.SHARE], fake.SHARE, None, snapshots, diff --git a/manila/tests/share/test_api.py b/manila/tests/share/test_api.py index f5557fa9ad..bc77da7fe2 100644 --- a/manila/tests/share/test_api.py +++ b/manila/tests/share/test_api.py @@ -4033,6 +4033,10 @@ def test_delete_share_replica(self, force): def test_promote_share_replica_non_available_status(self, status): replica = fakes.fake_replica( status=status, replica_state=constants.REPLICA_STATE_IN_SYNC) + mock_rpcapi_update_share_replica_call = self.mock_object( + self.share_rpcapi, 'update_share_replica') + self.mock_object(db_api, 'share_replica_get', + mock.Mock(return_value=replica)) mock_rpcapi_promote_share_replica_call = self.mock_object( self.share_rpcapi, 'promote_share_replica') @@ -4040,6 +4044,7 @@ def test_promote_share_replica_non_available_status(self, status): self.api.promote_share_replica, self.context, replica) + self.assertTrue(mock_rpcapi_update_share_replica_call.called) self.assertFalse(mock_rpcapi_promote_share_replica_call.called) @ddt.data(constants.REPLICA_STATE_OUT_OF_SYNC, constants.STATUS_ERROR) @@ -4050,6 +4055,10 @@ def test_promote_share_replica_out_of_sync_non_admin(self, replica_state): replica = fakes.fake_replica( status=constants.STATUS_AVAILABLE, replica_state=replica_state) + mock_rpcapi_update_share_replica_call = self.mock_object( + self.share_rpcapi, 'update_share_replica') + self.mock_object(db_api, 'share_replica_get', + mock.Mock(return_value=replica)) mock_rpcapi_promote_share_replica_call = self.mock_object( self.share_rpcapi, 'promote_share_replica') @@ -4057,6 +4066,7 @@ def test_promote_share_replica_out_of_sync_non_admin(self, replica_state): self.api.promote_share_replica, fake_user_context, replica) + self.assertTrue(mock_rpcapi_update_share_replica_call.called) self.assertFalse(mock_rpcapi_promote_share_replica_call.called) @ddt.data(constants.REPLICA_STATE_OUT_OF_SYNC, constants.STATUS_ERROR)