manila/manila/share/rpcapi.py
Goutham Pacha Ravi 35648819aa Fix force-delete on snapshot resource
When using snapshot-delete API, if the share driver
logs an exception, the status of the snapshot is set
to 'error_deleting'. If an administrator wants to remove
this snapshot, he/she would expect to use the
snapshot-force-delete API. However, if the share
driver still cannot delete the resource, it will remain
in 'error_deleting' state; leaving the administrator
with no choice but to resort to making changes to
the snapshot record in Manila's database.

Propagate the force flag to the share manager and log
driver exceptions, while ignoring them to obliterate
the snapshot on Manila's database if the delete
operation is forced.

Also fix data being sent in create/delete snapshot
driver interfaces and refactor corresponding unit tests.

Change-Id: Ic2494e1f9eb9104f96ef3a50f12d0f304735ee4f
Closes-Bug: #1560119
2016-04-01 18:33:26 +00:00

320 lines
14 KiB
Python

# Copyright 2012, Intel, Inc.
#
# 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.
"""
Client side of the share RPC API.
"""
from oslo_config import cfg
import oslo_messaging as messaging
from oslo_serialization import jsonutils
from manila import rpc
from manila.share import utils
CONF = cfg.CONF
class ShareAPI(object):
"""Client side of the share rpc API.
API version history:
1.0 - Initial version.
1.1 - Add manage_share() and unmanage_share() methods
1.2 - Add extend_share() method
1.3 - Add shrink_share() method
1.4 - Introduce Share Instances:
create_share() -> create_share_instance()
delete_share() -> delete_share_instance()
Add share_instance argument to allow_access() & deny_access()
1.5 - Add create_consistency_group, delete_consistency_group
create_cgsnapshot, and delete_cgsnapshot methods
1.6 - Introduce Share migration:
migrate_share()
get_migration_info()
get_driver_migration_info()
1.7 - Update target call API in allow/deny access methods
1.8 - Introduce Share Replication:
create_share_replica()
delete_share_replica()
promote_share_replica()
update_share_replica()
1.9 - Add manage_snapshot() and unmanage_snapshot() methods
1.10 - Add migration_complete(), migration_cancel() and
migration_get_progress(), rename migrate_share() to
migration_start(), rename get_migration_info() to
migration_get_info(), rename get_driver_migration_info() to
migration_get_driver_info()
1.11 - Add create_replicated_snapshot() and
delete_replicated_snapshot() methods
"""
BASE_RPC_API_VERSION = '1.0'
def __init__(self, topic=None):
super(ShareAPI, self).__init__()
target = messaging.Target(topic=CONF.share_topic,
version=self.BASE_RPC_API_VERSION)
self.client = rpc.get_client(target, version_cap='1.11')
def create_share_instance(self, context, share_instance, host,
request_spec, filter_properties,
snapshot_id=None):
new_host = utils.extract_host(host)
call_context = self.client.prepare(server=new_host, version='1.4')
request_spec_p = jsonutils.to_primitive(request_spec)
call_context.cast(context,
'create_share_instance',
share_instance_id=share_instance['id'],
request_spec=request_spec_p,
filter_properties=filter_properties,
snapshot_id=snapshot_id)
def manage_share(self, context, share, driver_options=None):
host = utils.extract_host(share['instance']['host'])
call_context = self.client.prepare(server=host, version='1.1')
call_context.cast(context,
'manage_share',
share_id=share['id'],
driver_options=driver_options)
def unmanage_share(self, context, share):
host = utils.extract_host(share['instance']['host'])
call_context = self.client.prepare(server=host, version='1.1')
call_context.cast(context, 'unmanage_share', share_id=share['id'])
def manage_snapshot(self, context, snapshot, host,
driver_options=None):
new_host = utils.extract_host(host)
call_context = self.client.prepare(server=new_host, version='1.9')
call_context.cast(context,
'manage_snapshot',
snapshot_id=snapshot['id'],
driver_options=driver_options)
def unmanage_snapshot(self, context, snapshot, host):
new_host = utils.extract_host(host)
call_context = self.client.prepare(server=new_host, version='1.9')
call_context.cast(context,
'unmanage_snapshot',
snapshot_id=snapshot['id'])
def delete_share_instance(self, context, share_instance, force=False):
host = utils.extract_host(share_instance['host'])
call_context = self.client.prepare(server=host, version='1.4')
call_context.cast(context,
'delete_share_instance',
share_instance_id=share_instance['id'],
force=force)
def migration_start(self, context, share, dest_host, force_host_copy,
notify):
new_host = utils.extract_host(share['instance']['host'])
call_context = self.client.prepare(server=new_host, version='1.6')
host_p = {'host': dest_host.host,
'capabilities': dest_host.capabilities}
call_context.cast(context,
'migration_start',
share_id=share['id'],
host=host_p,
force_host_copy=force_host_copy,
notify=notify)
def migration_get_info(self, context, share_instance):
new_host = utils.extract_host(share_instance['host'])
call_context = self.client.prepare(server=new_host, version='1.6')
return call_context.call(context,
'migration_get_info',
share_instance_id=share_instance['id'])
def migration_get_driver_info(self, context, share_instance):
new_host = utils.extract_host(share_instance['host'])
call_context = self.client.prepare(server=new_host, version='1.6')
return call_context.call(context,
'migration_get_driver_info',
share_instance_id=share_instance['id'])
def delete_share_server(self, context, share_server):
host = utils.extract_host(share_server['host'])
call_context = self.client.prepare(server=host, version='1.0')
call_context.cast(context,
'delete_share_server',
share_server=share_server)
def create_snapshot(self, context, share, snapshot):
host = utils.extract_host(share['instance']['host'])
call_context = self.client.prepare(server=host)
call_context.cast(context,
'create_snapshot',
share_id=share['id'],
snapshot_id=snapshot['id'])
def delete_snapshot(self, context, snapshot, host, force=False):
new_host = utils.extract_host(host)
call_context = self.client.prepare(server=new_host)
call_context.cast(context,
'delete_snapshot',
snapshot_id=snapshot['id'],
force=force)
def create_replicated_snapshot(self, context, share, replicated_snapshot):
host = utils.extract_host(share['instance']['host'])
call_context = self.client.prepare(server=host, version='1.11')
call_context.cast(context,
'create_replicated_snapshot',
snapshot_id=replicated_snapshot['id'],
share_id=share['id'])
def delete_replicated_snapshot(self, context, replicated_snapshot, host,
share_id=None, force=False):
host = utils.extract_host(host)
call_context = self.client.prepare(server=host, version='1.11')
call_context.cast(context,
'delete_replicated_snapshot',
snapshot_id=replicated_snapshot['id'],
share_id=share_id,
force=force)
@staticmethod
def _get_access_rules(access):
if isinstance(access, list):
return [rule['id'] for rule in access]
else:
return [access['id']]
def allow_access(self, context, share_instance, access):
host = utils.extract_host(share_instance['host'])
call_context = self.client.prepare(server=host, version='1.7')
call_context.cast(context,
'allow_access',
share_instance_id=share_instance['id'],
access_rules=self._get_access_rules(access))
def deny_access(self, context, share_instance, access):
host = utils.extract_host(share_instance['host'])
call_context = self.client.prepare(server=host, version='1.7')
call_context.cast(context,
'deny_access',
share_instance_id=share_instance['id'],
access_rules=self._get_access_rules(access))
def publish_service_capabilities(self, context):
call_context = self.client.prepare(fanout=True, version='1.0')
call_context.cast(context, 'publish_service_capabilities')
def extend_share(self, context, share, new_size, reservations):
host = utils.extract_host(share['instance']['host'])
call_context = self.client.prepare(server=host, version='1.2')
call_context.cast(context,
'extend_share',
share_id=share['id'],
new_size=new_size,
reservations=reservations)
def shrink_share(self, context, share, new_size):
host = utils.extract_host(share['instance']['host'])
call_context = self.client.prepare(server=host, version='1.3')
call_context.cast(context,
'shrink_share',
share_id=share['id'],
new_size=new_size)
def create_consistency_group(self, context, cg, host):
new_host = utils.extract_host(host)
call_context = self.client.prepare(server=new_host, version='1.5')
call_context.cast(context,
'create_consistency_group',
cg_id=cg['id'])
def delete_consistency_group(self, context, cg):
new_host = utils.extract_host(cg['host'])
call_context = self.client.prepare(server=new_host, version='1.5')
call_context.cast(context,
'delete_consistency_group',
cg_id=cg['id'])
def create_cgsnapshot(self, context, cgsnapshot, host):
new_host = utils.extract_host(host)
call_context = self.client.prepare(server=new_host, version='1.5')
call_context.cast(context,
'create_cgsnapshot',
cgsnapshot_id=cgsnapshot['id'])
def delete_cgsnapshot(self, context, cgsnapshot, host):
new_host = utils.extract_host(host)
call_context = self.client.prepare(server=new_host, version='1.5')
call_context.cast(context,
'delete_cgsnapshot',
cgsnapshot_id=cgsnapshot['id'])
def create_share_replica(self, context, share_replica, host,
request_spec, filter_properties):
new_host = utils.extract_host(host)
call_context = self.client.prepare(server=new_host, version='1.8')
request_spec_p = jsonutils.to_primitive(request_spec)
call_context.cast(context,
'create_share_replica',
share_replica_id=share_replica['id'],
request_spec=request_spec_p,
filter_properties=filter_properties,
share_id=share_replica['share_id'])
def delete_share_replica(self, context, share_replica, force=False):
host = utils.extract_host(share_replica['host'])
call_context = self.client.prepare(server=host, version='1.8')
call_context.cast(context,
'delete_share_replica',
share_replica_id=share_replica['id'],
share_id=share_replica['share_id'],
force=force)
def promote_share_replica(self, context, share_replica):
host = utils.extract_host(share_replica['host'])
call_context = self.client.prepare(server=host, version='1.8')
call_context.cast(context,
'promote_share_replica',
share_replica_id=share_replica['id'],
share_id=share_replica['share_id'])
def update_share_replica(self, context, share_replica):
host = utils.extract_host(share_replica['host'])
call_context = self.client.prepare(server=host, version='1.8')
call_context.cast(context,
'update_share_replica',
share_replica_id=share_replica['id'],
share_id=share_replica['share_id'])
def migration_complete(self, context, share, share_instance_id,
new_share_instance_id):
new_host = utils.extract_host(share['host'])
call_context = self.client.prepare(server=new_host, version='1.10')
call_context.cast(context,
'migration_complete',
share_id=share['id'],
share_instance_id=share_instance_id,
new_share_instance_id=new_share_instance_id)
def migration_cancel(self, context, share):
new_host = utils.extract_host(share['host'])
call_context = self.client.prepare(server=new_host, version='1.10')
call_context.call(context, 'migration_cancel', share_id=share['id'])
def migration_get_progress(self, context, share):
new_host = utils.extract_host(share['host'])
call_context = self.client.prepare(server=new_host, version='1.10')
return call_context.call(context,
'migration_get_progress',
share_id=share['id'])