Remove dead code - replication RPC APIs

The REST APIs for promote_replica and reenable_replication were removed
in If862bcd18515098639f94a8294a8e44e1358c52a with implementation of
replication V2.1. Some code was left behind and now can be considered
dead. This commit removes it.

This should be safe from upgradeability point of view as we've deleted
the APIs back in Mitaka and we're supporting backward compatibility with
only one previous release.

Changes in the drivers are left for the driver maintainers.

Change-Id: Ia7dfcaa7601176ecb3a4b19bb59f6d9329e62635
Closes-Bug: 1620001
This commit is contained in:
Michał Dulko 2016-09-05 13:59:20 +02:00
parent ecd2567374
commit 154404f5dd
8 changed files with 7 additions and 343 deletions

View File

@ -175,9 +175,6 @@ global_opts = [
cfg.StrOpt('transfer_api_class',
default='cinder.transfer.api.API',
help='The full class name of the volume transfer API class'),
cfg.StrOpt('replication_api_class',
default='cinder.replication.api.API',
help='The full class name of the volume replication API class'),
cfg.StrOpt('consistencygroup_api_class',
default='cinder.consistencygroup.api.API',
help='The full class name of the consistencygroup API class'),

View File

@ -1,23 +0,0 @@
# Copyright 2014 IBM Corp.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_config import cfg
from oslo_utils import importutils
CONF = cfg.CONF
cls = CONF.replication_api_class
API = importutils.import_class(cls)

View File

@ -1,108 +0,0 @@
# Copyright 2014 IBM Corp.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Handles all requests relating to volume replication.
"""
import functools
from cinder.db import base
from cinder import exception
from cinder.i18n import _
from cinder import policy
from cinder import volume as cinder_volume
from cinder.volume import rpcapi as volume_rpcapi
from cinder.volume import utils as volume_utils
PROMOTE_PROCEED_STATUS = ('active', 'active-stopped')
REENABLE_PROCEED_STATUS = ('inactive', 'active-stopped', 'error')
def wrap_check_policy(func):
"""Check policy corresponding to the wrapped methods prior to execution.
This decorator requires the first 3 args of the wrapped function
to be (self, context, relationship_id)
"""
@functools.wraps(func)
def wrapped(self, context, target_obj, *args, **kwargs):
check_policy(context, func.__name__, target_obj)
return func(self, context, target_obj, *args, **kwargs)
return wrapped
def check_policy(context, action, target_obj=None):
target = {
'project_id': context.project_id,
'user_id': context.user_id,
}
target.update(target_obj or {})
_action = 'volume_extension:replication:%s' % action
policy.enforce(context, _action, target)
class API(base.Base):
"""API for interacting with volume replication relationships."""
def __init__(self, db_driver=None):
super(API, self).__init__(db_driver)
self.volume_rpcapi = volume_rpcapi.VolumeAPI()
self.volume_api = cinder_volume.API()
@wrap_check_policy
def promote(self, context, vol):
if vol['replication_status'] == 'disabled':
msg = _("Replication is not enabled for volume")
raise exception.ReplicationError(
reason=msg,
volume_id=vol['id'])
if vol['replication_status'] not in PROMOTE_PROCEED_STATUS:
msg = _("Replication status for volume must be active or "
"active-stopped, but current status "
"is: %s") % vol['replication_status']
raise exception.ReplicationError(
reason=msg,
volume_id=vol['id'])
if vol['status'] != 'available':
msg = _("Volume status for volume must be available, but current "
"status is: %s") % vol['status']
raise exception.ReplicationError(
reason=msg,
volume_id=vol['id'])
volume_utils.notify_about_replication_usage(context,
vol,
'promote')
self.volume_rpcapi.promote_replica(context, vol)
@wrap_check_policy
def reenable(self, context, vol):
if vol['replication_status'] == 'disabled':
msg = _("Replication is not enabled")
raise exception.ReplicationError(
reason=msg,
volume_id=vol['id'])
if vol['replication_status'] not in REENABLE_PROCEED_STATUS:
msg = _("Replication status for volume must be inactive,"
" active-stopped, or error, but current status "
"is: %s") % vol['replication_status']
raise exception.ReplicationError(
reason=msg,
volume_id=vol['id'])
volume_utils.notify_about_replication_usage(context,
vol,
'sync')
self.volume_rpcapi.reenable_replication(context, vol)

View File

@ -559,18 +559,6 @@ class VolumeRpcAPITestCase(test.TestCase):
host='fake_host',
version='3.0')
def test_promote_replica(self):
self._test_volume_api('promote_replica',
rpc_method='cast',
volume=self.fake_volume,
version='3.0')
def test_reenable_replica(self):
self._test_volume_api('reenable_replication',
rpc_method='cast',
volume=self.fake_volume,
version='3.0')
def test_freeze_host(self):
self._test_volume_api('freeze_host', rpc_method='call',
host='fake_host', version='3.0')

View File

@ -2187,103 +2187,9 @@ class ManageableSnapshotsVD(object):
pass
# TODO(jdg): Remove this after the V2.1 code merges
@six.add_metaclass(abc.ABCMeta)
class ReplicaVD(object):
@abc.abstractmethod
def reenable_replication(self, context, volume):
"""Re-enable replication between the replica and primary volume.
This is used to re-enable/fix the replication between primary
and secondary. One use is as part of the fail-back process, when
you re-synchorize your old primary with the promoted volume
(the old replica).
Returns model_update for the volume to reflect the actions of the
driver.
The driver is expected to update the following entries:
- 'replication_status'
- 'replication_extended_status'
- 'replication_driver_data'
Possible 'replication_status' values (in model_update) are:
- 'error' - replication in error state
- 'copying' - replication copying data to secondary (inconsistent)
- 'active' - replication copying data to secondary (consistent)
- 'active-stopped' - replication data copy on hold (consistent)
- 'inactive' - replication data copy on hold (inconsistent)
Values in 'replication_extended_status' and 'replication_driver_data'
are managed by the driver.
:param context: Context
:param volume: A dictionary describing the volume
"""
return
def get_replication_status(self, context, volume):
"""Query the actual volume replication status from the driver.
Returns model_update for the volume.
The driver is expected to update the following entries:
- 'replication_status'
- 'replication_extended_status'
- 'replication_driver_data'
Possible 'replication_status' values (in model_update) are:
- 'error' - replication in error state
- 'copying' - replication copying data to secondary (inconsistent)
- 'active' - replication copying data to secondary (consistent)
- 'active-stopped' - replication data copy on hold (consistent)
- 'inactive' - replication data copy on hold (inconsistent)
Values in 'replication_extended_status' and 'replication_driver_data'
are managed by the driver.
:param context: Context
:param volume: A dictionary describing the volume
"""
return None
@abc.abstractmethod
def promote_replica(self, context, volume):
"""Promote the replica to be the primary volume.
Following this command, replication between the volumes at
the storage level should be stopped, the replica should be
available to be attached, and the replication status should
be in status 'inactive'.
Returns model_update for the volume.
The driver is expected to update the following entries:
- 'replication_status'
- 'replication_extended_status'
- 'replication_driver_data'
Possible 'replication_status' values (in model_update) are:
- 'error' - replication in error state
- 'inactive' - replication data copy on hold (inconsistent)
Values in 'replication_extended_status' and 'replication_driver_data'
are managed by the driver.
:param context: Context
:param volume: A dictionary describing the volume
"""
return
@abc.abstractmethod
def create_replica_test_volume(self, volume, src_vref):
"""Creates a test replica clone of the specified replicated volume.
Create a clone of the replicated (secondary) volume.
"""
return
class VolumeDriver(ConsistencyGroupVD, TransferVD, ManageableVD,
ExtendVD, CloneableImageVD, ManageableSnapshotsVD,
SnapshotVD, ReplicaVD, LocalVD, MigrateVD, BaseVD):
SnapshotVD, LocalVD, MigrateVD, BaseVD):
def check_for_setup_error(self):
raise NotImplementedError()
@ -2360,14 +2266,6 @@ class VolumeDriver(ConsistencyGroupVD, TransferVD, ManageableVD,
def retype(self, context, volume, new_type, diff, host):
return False, None
def reenable_replication(self, context, volume):
msg = _("sync_replica not implemented.")
raise NotImplementedError(msg)
def promote_replica(self, context, volume):
msg = _("promote_replica not implemented.")
raise NotImplementedError(msg)
# ####### Interface methods for DataPath (Connector) ########
def ensure_export(self, context, volume):
raise NotImplementedError()

View File

@ -1858,7 +1858,7 @@ class CLIResponse(object):
class StorwizeSVCCommonDriver(san.SanDriver,
driver.ManageableVD,
driver.ExtendVD, driver.SnapshotVD,
driver.MigrateVD, driver.ReplicaVD,
driver.MigrateVD,
driver.ConsistencyGroupVD,
driver.CloneableImageVD,
driver.TransferVD):

View File

@ -2446,87 +2446,6 @@ class VolumeManager(manager.SchedulerDependentManager):
"to driver error."))
return driver_entries
def promote_replica(self, ctxt, volume_id):
"""Promote volume replica secondary to be the primary volume."""
volume = self.db.volume_get(ctxt, volume_id)
model_update = None
try:
utils.require_driver_initialized(self.driver)
except exception.DriverNotInitialized:
with excutils.save_and_reraise_exception():
LOG.exception(_LE("Promote volume replica failed."),
resource=volume)
try:
model_update = self.driver.promote_replica(ctxt, volume)
except exception.CinderException:
err_msg = (_('Error promoting secondary volume to primary'))
raise exception.ReplicationError(reason=err_msg,
volume_id=volume_id)
try:
if model_update:
volume = self.db.volume_update(ctxt,
volume_id,
model_update)
except exception.CinderException:
err_msg = (_("Failed updating model"
" with driver provided model %(model)s") %
{'model': model_update})
raise exception.ReplicationError(reason=err_msg,
volume_id=volume_id)
LOG.info(_LI("Promote volume replica completed successfully."),
resource=volume)
def reenable_replication(self, ctxt, volume_id):
"""Re-enable replication of secondary volume with primary volumes."""
volume = self.db.volume_get(ctxt, volume_id)
model_update = None
try:
utils.require_driver_initialized(self.driver)
except exception.DriverNotInitialized:
with excutils.save_and_reraise_exception():
LOG.exception(_LE("Sync volume replica failed."),
resource=volume)
try:
model_update = self.driver.reenable_replication(ctxt, volume)
except exception.CinderException:
err_msg = (_("Synchronizing secondary volume to primary failed."))
raise exception.ReplicationError(reason=err_msg,
volume_id=volume_id)
try:
if model_update:
volume = self.db.volume_update(ctxt,
volume_id,
model_update)
except exception.CinderException:
err_msg = (_("Failed updating model"
" with driver provided model %(model)s") %
{'model': model_update})
raise exception.ReplicationError(reason=err_msg,
volume_id=volume_id)
def _update_replication_relationship_status(self, ctxt):
# Only want volumes that do not have a 'disabled' replication status
filters = {'replication_status': ['active', 'copying', 'error',
'active-stopped', 'inactive']}
volumes = self.db.volume_get_all_by_host(ctxt, self.host,
filters=filters)
for vol in volumes:
model_update = None
try:
model_update = self.driver.get_replication_status(
ctxt, vol)
if model_update:
self.db.volume_update(ctxt, vol['id'], model_update)
except Exception:
LOG.exception(_LE("Get replication status for volume failed."),
resource=vol)
def create_consistencygroup(self, context, group):
"""Creates the consistency group."""
return self._create_group(context, group, False)
@ -4352,7 +4271,7 @@ class VolumeManager(manager.SchedulerDependentManager):
# TODO(dulek): This goes away immediately in Ocata and is just present in
# Newton so that we can receive v2.x and v3.0 messages.
class _VolumeV3Proxy(object):
target = messaging.Target(version='3.0')
target = messaging.Target(version='3.1')
def __init__(self, manager):
self.manager = manager

View File

@ -110,9 +110,12 @@ class VolumeAPI(rpc.RPCAPI):
the version_cap being set to 2.6.
3.0 - Drop 2.x compatibility
3.1 - Remove promote_replica and reenable_replication. This is
non-backward compatible, but the user-facing API was removed
back in Mitaka when introducing cheesecake replication.
"""
RPC_API_VERSION = '3.0'
RPC_API_VERSION = '3.1'
TOPIC = constants.VOLUME_TOPIC
BINARY = 'cinder-volume'
@ -339,16 +342,6 @@ class VolumeAPI(rpc.RPCAPI):
cctxt = self._get_cctxt(volume.host, version)
cctxt.cast(ctxt, 'manage_existing', **msg_args)
def promote_replica(self, ctxt, volume):
version = self._compat_ver('3.0', '2.0')
cctxt = self._get_cctxt(volume['host'], version)
cctxt.cast(ctxt, 'promote_replica', volume_id=volume['id'])
def reenable_replication(self, ctxt, volume):
version = self._compat_ver('3.0', '2.0')
cctxt = self._get_cctxt(volume['host'], version)
cctxt.cast(ctxt, 'reenable_replication', volume_id=volume['id'])
def update_migrated_volume(self, ctxt, volume, new_volume,
original_volume_status):
version = self._compat_ver('3.0', '2.0')