Add share-server-delete API
This API is useful when share-server autodeletion is disabled, which is default behaviour. Changes: - added new api method with deletion of share server - added db method, that takes list of shares by share server id - changed logic of share manager, that is related to concurrency issues - added unit and tempest tests to cover changes Partially-implements blueprint add-admin-api-delete-share-server Change-Id: I03b452de2cd4fe34c648b2434dab1b9244b1b005
This commit is contained in:
parent
b98d3ae6f7
commit
83e94d735e
@ -72,12 +72,13 @@ class ShareServersAdminTest(base.BaseSharesAdminTest):
|
|||||||
# Project id is not empty
|
# Project id is not empty
|
||||||
self.assertTrue(len(server["project_id"]) > 0)
|
self.assertTrue(len(server["project_id"]) > 0)
|
||||||
|
|
||||||
# Server we used is present
|
# Server we used is present.
|
||||||
# allow 'creating' status too, because there can be
|
# Use 'allowed_statuses' to cover possible statuses of share servers
|
||||||
# another server with same requirements.
|
# in general, because we get info for whole cluster.
|
||||||
|
allowed_statuses = ["active", "creating", "deleting"]
|
||||||
any((s["share_network_name"] in self.sn_name_and_id and
|
any((s["share_network_name"] in self.sn_name_and_id and
|
||||||
self.assertIn(s["status"].lower(),
|
self.assertIn(s["status"].lower(),
|
||||||
["active", "creating"])) for s in servers)
|
allowed_statuses)) for s in servers)
|
||||||
|
|
||||||
@test.attr(type=["gate", "smoke", ])
|
@test.attr(type=["gate", "smoke", ])
|
||||||
def test_list_share_servers_with_host_filter(self):
|
def test_list_share_servers_with_host_filter(self):
|
||||||
@ -207,3 +208,33 @@ class ShareServersAdminTest(base.BaseSharesAdminTest):
|
|||||||
for k, v in details.iteritems():
|
for k, v in details.iteritems():
|
||||||
self.assertTrue(isinstance(k, basestring))
|
self.assertTrue(isinstance(k, basestring))
|
||||||
self.assertTrue(isinstance(v, basestring))
|
self.assertTrue(isinstance(v, basestring))
|
||||||
|
|
||||||
|
@test.attr(type=["gate", "smoke", ])
|
||||||
|
def test_delete_share_server(self):
|
||||||
|
# Get client with isolated creds
|
||||||
|
client = self.get_client_with_isolated_creds()
|
||||||
|
|
||||||
|
# Create server with share
|
||||||
|
__, share = self.create_share(client=client)
|
||||||
|
|
||||||
|
# Get share to be able to get its share_network_id
|
||||||
|
__, share = client.get_share(share["id"])
|
||||||
|
|
||||||
|
# Delete share, so we will have share server without shares
|
||||||
|
client.delete_share(share["id"])
|
||||||
|
|
||||||
|
# Wait for share deletion
|
||||||
|
client.wait_for_resource_deletion(share_id=share["id"])
|
||||||
|
|
||||||
|
# List share servers, filtered by share_network_id,
|
||||||
|
# list with only one item is expected - our share server.
|
||||||
|
search_opts = {"share_network": share["share_network_id"]}
|
||||||
|
__, servers = client.list_share_servers(search_opts)
|
||||||
|
self.assertEqual(len(servers), 1)
|
||||||
|
|
||||||
|
# Delete share server
|
||||||
|
resp, server = client.delete_share_server(servers[0]["id"])
|
||||||
|
self.assertIn(int(resp["status"]), test.HTTP_SUCCESS)
|
||||||
|
|
||||||
|
# Wait for share server deletion
|
||||||
|
client.wait_for_resource_deletion(server_id=servers[0]["id"])
|
||||||
|
@ -92,3 +92,15 @@ class ShareServersNegativeAdminTest(base.BaseSharesAdminTest):
|
|||||||
}
|
}
|
||||||
__, servers = self.shares_client.list_share_servers(search_opts)
|
__, servers = self.shares_client.list_share_servers(search_opts)
|
||||||
self.assertEqual(len(servers), 0)
|
self.assertEqual(len(servers), 0)
|
||||||
|
|
||||||
|
@test.attr(type=["gate", "smoke", "negative", ])
|
||||||
|
def test_delete_share_server_with_nonexistent_id(self):
|
||||||
|
self.assertRaises(exceptions.NotFound,
|
||||||
|
self.shares_client.delete_share_server,
|
||||||
|
"fake_nonexistent_share_server_id")
|
||||||
|
|
||||||
|
@test.attr(type=["gate", "smoke", "negative", ])
|
||||||
|
def test_delete_share_server_with_member(self):
|
||||||
|
self.assertRaises(exceptions.Unauthorized,
|
||||||
|
self.member_shares_client.delete_share_server,
|
||||||
|
"fake_nonexistent_share_server_id")
|
||||||
|
@ -270,7 +270,7 @@ class SharesClient(rest_client.RestClient):
|
|||||||
"""Verifies deleted resource or not.
|
"""Verifies deleted resource or not.
|
||||||
|
|
||||||
:param kwargs: expected keys are 'share_id', 'rule_id',
|
:param kwargs: expected keys are 'share_id', 'rule_id',
|
||||||
:param kwargs: 'snapshot_id', 'sn_id', 'ss_id' and 'vt_id'
|
:param kwargs: 'snapshot_id', 'sn_id', 'ss_id', 'vt_id' and 'server_id'
|
||||||
:raises share_exceptions.InvalidResource
|
:raises share_exceptions.InvalidResource
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -315,6 +315,12 @@ class SharesClient(rest_client.RestClient):
|
|||||||
self.get_volume_type(kwargs.get("vt_id"))
|
self.get_volume_type(kwargs.get("vt_id"))
|
||||||
except exceptions.NotFound:
|
except exceptions.NotFound:
|
||||||
return True
|
return True
|
||||||
|
elif "server_id" in kwargs:
|
||||||
|
# Whether share server deleted or not
|
||||||
|
try:
|
||||||
|
self.show_share_server(kwargs.get("server_id"))
|
||||||
|
except exceptions.NotFound:
|
||||||
|
return True
|
||||||
else:
|
else:
|
||||||
raise share_exceptions.InvalidResource(message=str(kwargs))
|
raise share_exceptions.InvalidResource(message=str(kwargs))
|
||||||
return False
|
return False
|
||||||
@ -626,6 +632,11 @@ class SharesClient(rest_client.RestClient):
|
|||||||
resp, body = self.get(uri)
|
resp, body = self.get(uri)
|
||||||
return resp, self._parse_resp(body)
|
return resp, self._parse_resp(body)
|
||||||
|
|
||||||
|
def delete_share_server(self, share_server_id):
|
||||||
|
"""Delete share server by its ID."""
|
||||||
|
uri = "share-servers/%s" % share_server_id
|
||||||
|
return self.delete(uri)
|
||||||
|
|
||||||
def show_share_server(self, share_server_id):
|
def show_share_server(self, share_server_id):
|
||||||
"""Get share server info."""
|
"""Get share server info."""
|
||||||
uri = "share-servers/%s" % share_server_id
|
uri = "share-servers/%s" % share_server_id
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
"share_server:index": [["rule:admin_api"]],
|
"share_server:index": [["rule:admin_api"]],
|
||||||
"share_server:show": [["rule:admin_api"]],
|
"share_server:show": [["rule:admin_api"]],
|
||||||
"share_server:details": [["rule:admin_api"]],
|
"share_server:details": [["rule:admin_api"]],
|
||||||
|
"share_server:delete": [["rule:admin_api"]],
|
||||||
|
|
||||||
"share_network:create": [],
|
"share_network:create": [],
|
||||||
"share_network:delete": [],
|
"share_network:delete": [],
|
||||||
|
@ -14,15 +14,18 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import six
|
import six
|
||||||
|
import webob
|
||||||
from webob import exc
|
from webob import exc
|
||||||
|
|
||||||
from manila.api.openstack import wsgi
|
from manila.api.openstack import wsgi
|
||||||
from manila.api.views import share_servers as share_servers_views
|
from manila.api.views import share_servers as share_servers_views
|
||||||
from manila.api import xmlutil
|
from manila.api import xmlutil
|
||||||
|
from manila.common import constants
|
||||||
from manila.db import api as db_api
|
from manila.db import api as db_api
|
||||||
from manila import exception
|
from manila import exception
|
||||||
from manila.openstack.common import log as logging
|
from manila.openstack.common import log as logging
|
||||||
from manila import policy
|
from manila import policy
|
||||||
|
from manila import share
|
||||||
|
|
||||||
RESOURCE_NAME = 'share_server'
|
RESOURCE_NAME = 'share_server'
|
||||||
RESOURCES_NAME = 'share_servers'
|
RESOURCES_NAME = 'share_servers'
|
||||||
@ -63,7 +66,10 @@ class ShareServersTemplate(xmlutil.TemplateBuilder):
|
|||||||
class ShareServerController(wsgi.Controller):
|
class ShareServerController(wsgi.Controller):
|
||||||
"""The Share Server API controller for the OpenStack API."""
|
"""The Share Server API controller for the OpenStack API."""
|
||||||
|
|
||||||
_view_builder_class = share_servers_views.ViewBuilder
|
def __init__(self):
|
||||||
|
self.share_api = share.API()
|
||||||
|
self._view_builder_class = share_servers_views.ViewBuilder
|
||||||
|
super(ShareServerController, self).__init__()
|
||||||
|
|
||||||
@wsgi.serializers(xml=ShareServersTemplate)
|
@wsgi.serializers(xml=ShareServersTemplate)
|
||||||
def index(self, req):
|
def index(self, req):
|
||||||
@ -118,6 +124,30 @@ class ShareServerController(wsgi.Controller):
|
|||||||
details = db_api.share_server_backend_details_get(context, id)
|
details = db_api.share_server_backend_details_get(context, id)
|
||||||
return self._view_builder.build_share_server_details(details)
|
return self._view_builder.build_share_server_details(details)
|
||||||
|
|
||||||
|
def delete(self, req, id):
|
||||||
|
"""Delete specified share server."""
|
||||||
|
context = req.environ['manila.context']
|
||||||
|
policy.check_policy(context, RESOURCE_NAME, 'delete')
|
||||||
|
try:
|
||||||
|
share_server = db_api.share_server_get(context, id)
|
||||||
|
except exception.ShareServerNotFound as e:
|
||||||
|
raise exc.HTTPNotFound(explanation=str(e))
|
||||||
|
allowed_statuses = [constants.STATUS_ERROR, constants.STATUS_ACTIVE]
|
||||||
|
if share_server['status'] not in allowed_statuses:
|
||||||
|
data = {
|
||||||
|
'status': share_server['status'],
|
||||||
|
'allowed_statuses': allowed_statuses,
|
||||||
|
}
|
||||||
|
msg = _("Share server's actual status is %(status)s, allowed "
|
||||||
|
"statuses for deletion are %(allowed_statuses)s.") % (data)
|
||||||
|
raise exc.HTTPForbidden(explanation=msg)
|
||||||
|
LOG.debug("Deleting share server with id: %s." % id)
|
||||||
|
try:
|
||||||
|
self.share_api.delete_share_server(context, share_server)
|
||||||
|
except exception.ShareServerInUse as e:
|
||||||
|
raise exc.HTTPConflict(explanation=str(e))
|
||||||
|
return webob.Response(status_int=202)
|
||||||
|
|
||||||
|
|
||||||
def create_resource():
|
def create_resource():
|
||||||
return wsgi.Resource(ShareServerController())
|
return wsgi.Resource(ShareServerController())
|
||||||
|
@ -341,6 +341,11 @@ def share_get_all_by_project(context, project_id):
|
|||||||
return IMPL.share_get_all_by_project(context, project_id)
|
return IMPL.share_get_all_by_project(context, project_id)
|
||||||
|
|
||||||
|
|
||||||
|
def share_get_all_by_share_server(context, share_server_id):
|
||||||
|
"""Returns all shares with given share server."""
|
||||||
|
return IMPL.share_get_all_by_share_server(context, share_server_id)
|
||||||
|
|
||||||
|
|
||||||
def share_delete(context, share_id):
|
def share_delete(context, share_id):
|
||||||
"""Delete share."""
|
"""Delete share."""
|
||||||
return IMPL.share_delete(context, share_id)
|
return IMPL.share_delete(context, share_id)
|
||||||
|
@ -1146,6 +1146,13 @@ def share_get_all_by_project(context, project_id):
|
|||||||
return _share_get_query(context).filter_by(project_id=project_id).all()
|
return _share_get_query(context).filter_by(project_id=project_id).all()
|
||||||
|
|
||||||
|
|
||||||
|
@require_context
|
||||||
|
def share_get_all_by_share_server(context, share_server_id):
|
||||||
|
"""Returns list of shares with given share server."""
|
||||||
|
return _share_get_query(context).filter_by(
|
||||||
|
share_server_id=share_server_id).all()
|
||||||
|
|
||||||
|
|
||||||
@require_context
|
@require_context
|
||||||
def share_delete(context, share_id):
|
def share_delete(context, share_id):
|
||||||
session = get_session()
|
session = get_session()
|
||||||
|
@ -209,6 +209,19 @@ class API(base.Base):
|
|||||||
|
|
||||||
self.share_rpcapi.delete_share(context, share)
|
self.share_rpcapi.delete_share(context, share)
|
||||||
|
|
||||||
|
def delete_share_server(self, context, server):
|
||||||
|
"""Delete share server."""
|
||||||
|
policy.check_policy(context, 'share_server', 'delete', server)
|
||||||
|
shares = self.db.share_get_all_by_share_server(context, server['id'])
|
||||||
|
if shares:
|
||||||
|
raise exception.ShareServerInUse(share_server_id=server['id'])
|
||||||
|
# NOTE(vponomaryov): There is no share_server status update here,
|
||||||
|
# it is intentional.
|
||||||
|
# Status will be changed in manila.share.manager after verification
|
||||||
|
# for race condition between share creation on server
|
||||||
|
# and server deletion.
|
||||||
|
self.share_rpcapi.delete_share_server(context, server)
|
||||||
|
|
||||||
def create_snapshot(self, context, share, name, description,
|
def create_snapshot(self, context, share, name, description,
|
||||||
force=False):
|
force=False):
|
||||||
policy.check_policy(context, 'share', 'create_snapshot', share)
|
policy.check_policy(context, 'share', 'create_snapshot', share)
|
||||||
|
@ -98,10 +98,26 @@ class ShareManager(manager.SchedulerDependentManager):
|
|||||||
|
|
||||||
self.publish_service_capabilities(ctxt)
|
self.publish_service_capabilities(ctxt)
|
||||||
|
|
||||||
def _share_server_get_or_create(self, context, share_network_id):
|
def _provide_share_server_for_share(self, context, share_network_id,
|
||||||
|
share_id):
|
||||||
|
"""Gets or creates share_server and updates share with its id.
|
||||||
|
|
||||||
|
Active share_server can be deleted if there are no dependent shares
|
||||||
|
on it.
|
||||||
|
So we need avoid possibility to delete share_server in time gap
|
||||||
|
between reaching active state for share_server and setting up
|
||||||
|
share_server_id for share. It is possible, for example, with first
|
||||||
|
share creation, which starts share_server creation.
|
||||||
|
For this purpose used shared lock between this method and the one
|
||||||
|
with deletion of share_server.
|
||||||
|
|
||||||
|
:returns: dict, dict -- first value is share_server, that
|
||||||
|
has been choosen for share schedule. Second value is
|
||||||
|
share updated with share_server_id.
|
||||||
|
"""
|
||||||
|
|
||||||
@lockutils.synchronized(share_network_id)
|
@lockutils.synchronized(share_network_id)
|
||||||
def _get_or_create_server():
|
def _provide_share_server_for_share():
|
||||||
try:
|
try:
|
||||||
share_server = \
|
share_server = \
|
||||||
self.db.share_server_get_by_host_and_share_net_valid(
|
self.db.share_server_get_by_host_and_share_net_valid(
|
||||||
@ -111,9 +127,16 @@ class ShareManager(manager.SchedulerDependentManager):
|
|||||||
context, share_network_id)
|
context, share_network_id)
|
||||||
share_server = self._setup_server(context, share_network)
|
share_server = self._setup_server(context, share_network)
|
||||||
LOG.info(_("Share server created successfully."))
|
LOG.info(_("Share server created successfully."))
|
||||||
return share_server
|
LOG.debug("Using share_server "
|
||||||
|
"%s for share %s" % (share_server['id'], share_id))
|
||||||
|
share_ref = self.db.share_update(
|
||||||
|
context,
|
||||||
|
share_id,
|
||||||
|
{'share_server_id': share_server['id']},
|
||||||
|
)
|
||||||
|
return share_server, share_ref
|
||||||
|
|
||||||
return _get_or_create_server()
|
return _provide_share_server_for_share()
|
||||||
|
|
||||||
def _get_share_server(self, context, share):
|
def _get_share_server(self, context, share):
|
||||||
if share['share_server_id']:
|
if share['share_server_id']:
|
||||||
@ -143,6 +166,10 @@ class ShareManager(manager.SchedulerDependentManager):
|
|||||||
try:
|
try:
|
||||||
share_server = self.db.share_server_get(context,
|
share_server = self.db.share_server_get(context,
|
||||||
parent_share_server_id)
|
parent_share_server_id)
|
||||||
|
LOG.debug("Using share_server "
|
||||||
|
"%s for share %s" % (share_server['id'], share_id))
|
||||||
|
share_ref = self.db.share_update(
|
||||||
|
context, share_id, {'share_server_id': share_server['id']})
|
||||||
except exception.ShareServerNotFound:
|
except exception.ShareServerNotFound:
|
||||||
with excutils.save_and_reraise_exception():
|
with excutils.save_and_reraise_exception():
|
||||||
LOG.error(_("Share server %s does not exist.")
|
LOG.error(_("Share server %s does not exist.")
|
||||||
@ -151,8 +178,8 @@ class ShareManager(manager.SchedulerDependentManager):
|
|||||||
{'status': 'error'})
|
{'status': 'error'})
|
||||||
elif share_network_id:
|
elif share_network_id:
|
||||||
try:
|
try:
|
||||||
share_server = self._share_server_get_or_create(
|
share_server, share_ref = self._provide_share_server_for_share(
|
||||||
context, share_network_id)
|
context, share_network_id, share_id)
|
||||||
except Exception:
|
except Exception:
|
||||||
with excutils.save_and_reraise_exception():
|
with excutils.save_and_reraise_exception():
|
||||||
LOG.error(_("Failed to get share server"
|
LOG.error(_("Failed to get share server"
|
||||||
@ -162,11 +189,6 @@ class ShareManager(manager.SchedulerDependentManager):
|
|||||||
else:
|
else:
|
||||||
share_server = None
|
share_server = None
|
||||||
|
|
||||||
if share_server:
|
|
||||||
LOG.debug("Using share server %s" % share_server['id'])
|
|
||||||
share_ref = self.db.share_update(
|
|
||||||
context, share_id, {'share_server_id': share_server['id']})
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if snapshot_ref:
|
if snapshot_ref:
|
||||||
export_location = self.driver.create_share_from_snapshot(
|
export_location = self.driver.create_share_from_snapshot(
|
||||||
@ -225,7 +247,10 @@ class ShareManager(manager.SchedulerDependentManager):
|
|||||||
if CONF.delete_share_server_with_last_share:
|
if CONF.delete_share_server_with_last_share:
|
||||||
share_server = self._get_share_server(context, share_ref)
|
share_server = self._get_share_server(context, share_ref)
|
||||||
if share_server and not share_server.shares:
|
if share_server and not share_server.shares:
|
||||||
self._teardown_server(context, share_server)
|
LOG.debug("Scheduled deletion of share-server "
|
||||||
|
"with id '%s' automatically by "
|
||||||
|
"deletion of last share." % share_server['id'])
|
||||||
|
self.delete_share_server(context, share_server)
|
||||||
|
|
||||||
def create_snapshot(self, context, share_id, snapshot_id):
|
def create_snapshot(self, context, share_id, snapshot_id):
|
||||||
"""Create snapshot for share."""
|
"""Create snapshot for share."""
|
||||||
@ -388,10 +413,23 @@ class ShareManager(manager.SchedulerDependentManager):
|
|||||||
{'status': constants.STATUS_ERROR})
|
{'status': constants.STATUS_ERROR})
|
||||||
self.network_api.deallocate_network(context, share_network)
|
self.network_api.deallocate_network(context, share_network)
|
||||||
|
|
||||||
def _teardown_server(self, context, share_server):
|
def delete_share_server(self, context, share_server):
|
||||||
|
|
||||||
@lockutils.synchronized(share_server['share_network_id'])
|
@lockutils.synchronized(share_server['share_network_id'])
|
||||||
def _teardown_server():
|
def _teardown_server():
|
||||||
|
# NOTE(vponomaryov): Verify that there are no dependent shares.
|
||||||
|
# Without this verification we can get here exception in next case:
|
||||||
|
# share-server-delete API was called after share creation scheduled
|
||||||
|
# and share_server reached ACTIVE status, but before update
|
||||||
|
# of share_server_id field for share. If so, after lock realese
|
||||||
|
# this method starts executing when amount of dependent shares
|
||||||
|
# has been changed.
|
||||||
|
shares = self.db.share_get_all_by_share_server(
|
||||||
|
context, share_server['id'])
|
||||||
|
if shares:
|
||||||
|
raise exception.ShareServerInUse(
|
||||||
|
share_server_id=share_server['id'])
|
||||||
|
|
||||||
self.db.share_server_update(context, share_server['id'],
|
self.db.share_server_update(context, share_server['id'],
|
||||||
{'status': constants.STATUS_DELETING})
|
{'status': constants.STATUS_DELETING})
|
||||||
sec_services = self.db.share_network_get(
|
sec_services = self.db.share_network_get(
|
||||||
|
@ -59,6 +59,10 @@ class ShareAPI(object):
|
|||||||
cctxt = self.client.prepare(server=share['host'], version='1.0')
|
cctxt = self.client.prepare(server=share['host'], version='1.0')
|
||||||
cctxt.cast(ctxt, 'delete_share', share_id=share['id'])
|
cctxt.cast(ctxt, 'delete_share', share_id=share['id'])
|
||||||
|
|
||||||
|
def delete_share_server(self, ctxt, share_server):
|
||||||
|
cctxt = self.client.prepare(server=share_server['host'], version='1.0')
|
||||||
|
cctxt.cast(ctxt, 'delete_share_server', share_server=share_server)
|
||||||
|
|
||||||
def create_snapshot(self, ctxt, share, snapshot):
|
def create_snapshot(self, ctxt, share, snapshot):
|
||||||
cctxt = self.client.prepare(server=share['host'])
|
cctxt = self.client.prepare(server=share['host'])
|
||||||
cctxt.cast(
|
cctxt.cast(
|
||||||
|
@ -14,11 +14,13 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
|
from webob import exc
|
||||||
|
|
||||||
from manila.api.v1 import share_servers
|
from manila.api.v1 import share_servers
|
||||||
from manila.common import constants
|
from manila.common import constants
|
||||||
from manila import context
|
from manila import context
|
||||||
from manila.db import api as db_api
|
from manila.db import api as db_api
|
||||||
|
from manila import exception
|
||||||
from manila import policy
|
from manila import policy
|
||||||
from manila import test
|
from manila import test
|
||||||
|
|
||||||
@ -242,3 +244,94 @@ class ShareServerAPITest(test.TestCase):
|
|||||||
CONTEXT, fake_share_server_get_result['share_server']['id'])
|
CONTEXT, fake_share_server_get_result['share_server']['id'])
|
||||||
self.assertEqual(result,
|
self.assertEqual(result,
|
||||||
fake_share_server_backend_details_get_result)
|
fake_share_server_backend_details_get_result)
|
||||||
|
|
||||||
|
def test_delete_active_server(self):
|
||||||
|
share_server = FakeShareServer(status=constants.STATUS_ACTIVE)
|
||||||
|
self.stubs.Set(db_api, 'share_server_get',
|
||||||
|
mock.Mock(return_value=share_server))
|
||||||
|
self.stubs.Set(self.controller.share_api, 'delete_share_server',
|
||||||
|
mock.Mock())
|
||||||
|
result = self.controller.delete(FakeRequestAdmin,
|
||||||
|
fake_share_server_get_result['share_server']['id'])
|
||||||
|
policy.check_policy.assert_called_once_with(CONTEXT,
|
||||||
|
share_servers.RESOURCE_NAME, 'delete')
|
||||||
|
db_api.share_server_get.assert_called_once_with(CONTEXT,
|
||||||
|
fake_share_server_get_result['share_server']['id'])
|
||||||
|
self.controller.share_api.delete_share_server.assert_called_once_with(
|
||||||
|
CONTEXT, share_server)
|
||||||
|
|
||||||
|
def test_delete_error_server(self):
|
||||||
|
share_server = FakeShareServer(status=constants.STATUS_ERROR)
|
||||||
|
self.stubs.Set(db_api, 'share_server_get',
|
||||||
|
mock.Mock(return_value=share_server))
|
||||||
|
self.stubs.Set(self.controller.share_api, 'delete_share_server',
|
||||||
|
mock.Mock())
|
||||||
|
result = self.controller.delete(FakeRequestAdmin,
|
||||||
|
fake_share_server_get_result['share_server']['id'])
|
||||||
|
policy.check_policy.assert_called_once_with(CONTEXT,
|
||||||
|
share_servers.RESOURCE_NAME, 'delete')
|
||||||
|
db_api.share_server_get.assert_called_once_with(CONTEXT,
|
||||||
|
fake_share_server_get_result['share_server']['id'])
|
||||||
|
self.controller.share_api.delete_share_server.assert_called_once_with(
|
||||||
|
CONTEXT, share_server)
|
||||||
|
|
||||||
|
def test_delete_used_server(self):
|
||||||
|
share_server_id = fake_share_server_get_result['share_server']['id']
|
||||||
|
|
||||||
|
def raise_not_share_server_in_use(*args, **kwargs):
|
||||||
|
raise exception.ShareServerInUse(share_server_id=share_server_id)
|
||||||
|
|
||||||
|
share_server = fake_share_server_get()
|
||||||
|
self.stubs.Set(db_api, 'share_server_get',
|
||||||
|
mock.Mock(return_value=share_server))
|
||||||
|
self.stubs.Set(self.controller.share_api, 'delete_share_server',
|
||||||
|
mock.Mock(side_effect=raise_not_share_server_in_use))
|
||||||
|
self.assertRaises(exc.HTTPConflict,
|
||||||
|
self.controller.delete,
|
||||||
|
FakeRequestAdmin,
|
||||||
|
share_server_id)
|
||||||
|
db_api.share_server_get.assert_called_once_with(CONTEXT,
|
||||||
|
share_server_id)
|
||||||
|
self.controller.share_api.delete_share_server.assert_called_once_with(
|
||||||
|
CONTEXT, share_server)
|
||||||
|
|
||||||
|
def test_delete_not_found(self):
|
||||||
|
share_server_id = fake_share_server_get_result['share_server']['id']
|
||||||
|
|
||||||
|
def raise_not_found(*args, **kwargs):
|
||||||
|
raise exception.ShareServerNotFound(
|
||||||
|
share_server_id=share_server_id)
|
||||||
|
|
||||||
|
share_server = fake_share_server_get()
|
||||||
|
self.stubs.Set(db_api, 'share_server_get',
|
||||||
|
mock.Mock(side_effect=raise_not_found))
|
||||||
|
self.assertRaises(exc.HTTPNotFound,
|
||||||
|
self.controller.delete,
|
||||||
|
FakeRequestAdmin,
|
||||||
|
share_server_id)
|
||||||
|
db_api.share_server_get.assert_called_once_with(
|
||||||
|
CONTEXT, share_server_id)
|
||||||
|
policy.check_policy.assert_called_once_with(CONTEXT,
|
||||||
|
share_servers.RESOURCE_NAME, 'delete')
|
||||||
|
|
||||||
|
def test_delete_creating_server(self):
|
||||||
|
share_server = FakeShareServer(status=constants.STATUS_CREATING)
|
||||||
|
self.stubs.Set(db_api, 'share_server_get',
|
||||||
|
mock.Mock(return_value=share_server))
|
||||||
|
self.assertRaises(exc.HTTPForbidden,
|
||||||
|
self.controller.delete,
|
||||||
|
FakeRequestAdmin,
|
||||||
|
share_server['id'])
|
||||||
|
policy.check_policy.assert_called_once_with(CONTEXT,
|
||||||
|
share_servers.RESOURCE_NAME, 'delete')
|
||||||
|
|
||||||
|
def test_delete_deleting_server(self):
|
||||||
|
share_server = FakeShareServer(status=constants.STATUS_DELETING)
|
||||||
|
self.stubs.Set(db_api, 'share_server_get',
|
||||||
|
mock.Mock(return_value=share_server))
|
||||||
|
self.assertRaises(exc.HTTPForbidden,
|
||||||
|
self.controller.delete,
|
||||||
|
FakeRequestAdmin,
|
||||||
|
share_server['id'])
|
||||||
|
policy.check_policy.assert_called_once_with(CONTEXT,
|
||||||
|
share_servers.RESOURCE_NAME, 'delete')
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
"share_server:index": [["rule:admin_api"]],
|
"share_server:index": [["rule:admin_api"]],
|
||||||
"share_server:show": [["rule:admin_api"]],
|
"share_server:show": [["rule:admin_api"]],
|
||||||
"share_server:details": [["rule:admin_api"]],
|
"share_server:details": [["rule:admin_api"]],
|
||||||
|
"share_server:delete": [["rule:admin_api"]],
|
||||||
|
|
||||||
"share:get_share_metadata": [],
|
"share:get_share_metadata": [],
|
||||||
"share:delete_share_metadata": [],
|
"share:delete_share_metadata": [],
|
||||||
|
@ -185,6 +185,32 @@ class ShareAPITestCase(test.TestCase):
|
|||||||
db_driver.share_snapshot_create.assert_called_once_with(
|
db_driver.share_snapshot_create.assert_called_once_with(
|
||||||
self.context, options)
|
self.context, options)
|
||||||
|
|
||||||
|
@mock.patch.object(db_driver, 'share_get_all_by_share_server',
|
||||||
|
mock.Mock(return_value=[]))
|
||||||
|
def test_delete_share_server_no_dependent_shares(self):
|
||||||
|
server = {'id': 'fake_share_server_id'}
|
||||||
|
server_returned = {
|
||||||
|
'id': 'fake_share_server_id',
|
||||||
|
}
|
||||||
|
self.stubs.Set(db_driver, 'share_server_update',
|
||||||
|
mock.Mock(return_value=server_returned))
|
||||||
|
self.api.delete_share_server(self.context, server)
|
||||||
|
db_driver.share_get_all_by_share_server.assert_called_once_with(
|
||||||
|
self.context, server['id'])
|
||||||
|
self.share_rpcapi.delete_share_server.assert_called_once_with(
|
||||||
|
self.context, server_returned)
|
||||||
|
|
||||||
|
@mock.patch.object(db_driver, 'share_get_all_by_share_server',
|
||||||
|
mock.Mock(return_value=['fake_share', ]))
|
||||||
|
def test_delete_share_server_dependent_share_exists(self):
|
||||||
|
server = {'id': 'fake_share_server_id'}
|
||||||
|
self.assertRaises(exception.ShareServerInUse,
|
||||||
|
self.api.delete_share_server,
|
||||||
|
self.context,
|
||||||
|
server)
|
||||||
|
db_driver.share_get_all_by_share_server.assert_called_once_with(
|
||||||
|
self.context, server['id'])
|
||||||
|
|
||||||
@mock.patch.object(db_driver, 'share_snapshot_update', mock.Mock())
|
@mock.patch.object(db_driver, 'share_snapshot_update', mock.Mock())
|
||||||
def test_delete_snapshot(self):
|
def test_delete_snapshot(self):
|
||||||
date = datetime.datetime(1, 1, 1, 1, 1, 1)
|
date = datetime.datetime(1, 1, 1, 1, 1, 1)
|
||||||
|
@ -47,27 +47,24 @@ class ShareRpcAPITestCase(test.TestCase):
|
|||||||
snap = {}
|
snap = {}
|
||||||
snap['share_id'] = share['id']
|
snap['share_id'] = share['id']
|
||||||
snapshot = db.share_snapshot_create(self.context, snap)
|
snapshot = db.share_snapshot_create(self.context, snap)
|
||||||
|
server = {'id': 'fake_share_server_id', 'host': 'fake_host'}
|
||||||
|
share_server = db.share_server_create(self.context, server)
|
||||||
self.fake_share = jsonutils.to_primitive(share)
|
self.fake_share = jsonutils.to_primitive(share)
|
||||||
self.fake_access = jsonutils.to_primitive(access)
|
self.fake_access = jsonutils.to_primitive(access)
|
||||||
self.fake_snapshot = jsonutils.to_primitive(snapshot)
|
self.fake_snapshot = jsonutils.to_primitive(snapshot)
|
||||||
|
self.fake_share_server = jsonutils.to_primitive(share_server)
|
||||||
super(ShareRpcAPITestCase, self).setUp()
|
super(ShareRpcAPITestCase, self).setUp()
|
||||||
|
self.ctxt = context.RequestContext('fake_user', 'fake_project')
|
||||||
|
self.rpcapi = share_rpcapi.ShareAPI()
|
||||||
|
|
||||||
def test_serialized_share_has_id(self):
|
def test_serialized_share_has_id(self):
|
||||||
self.assertTrue('id' in self.fake_share)
|
self.assertTrue('id' in self.fake_share)
|
||||||
|
|
||||||
def _test_share_api(self, method, rpc_method, **kwargs):
|
def _test_share_api(self, method, rpc_method, **kwargs):
|
||||||
ctxt = context.RequestContext('fake_user', 'fake_project')
|
|
||||||
|
|
||||||
if 'rpcapi_class' in kwargs:
|
|
||||||
rpcapi_class = kwargs['rpcapi_class']
|
|
||||||
del kwargs['rpcapi_class']
|
|
||||||
else:
|
|
||||||
rpcapi_class = share_rpcapi.ShareAPI
|
|
||||||
rpcapi = rpcapi_class()
|
|
||||||
expected_retval = 'foo' if method == 'call' else None
|
expected_retval = 'foo' if method == 'call' else None
|
||||||
|
|
||||||
target = {
|
target = {
|
||||||
"version": kwargs.pop('version', rpcapi.BASE_RPC_API_VERSION)
|
"version": kwargs.pop('version', self.rpcapi.BASE_RPC_API_VERSION)
|
||||||
}
|
}
|
||||||
expected_msg = copy.deepcopy(kwargs)
|
expected_msg = copy.deepcopy(kwargs)
|
||||||
if 'share' in expected_msg:
|
if 'share' in expected_msg:
|
||||||
@ -88,6 +85,8 @@ class ShareRpcAPITestCase(test.TestCase):
|
|||||||
|
|
||||||
if 'host' in kwargs:
|
if 'host' in kwargs:
|
||||||
host = kwargs['host']
|
host = kwargs['host']
|
||||||
|
elif 'share_server' in kwargs:
|
||||||
|
host = kwargs['share_server']['host']
|
||||||
else:
|
else:
|
||||||
host = kwargs['share']['host']
|
host = kwargs['share']['host']
|
||||||
target['server'] = host
|
target['server'] = host
|
||||||
@ -96,12 +95,12 @@ class ShareRpcAPITestCase(test.TestCase):
|
|||||||
self.fake_args = None
|
self.fake_args = None
|
||||||
self.fake_kwargs = None
|
self.fake_kwargs = None
|
||||||
|
|
||||||
real_prepare = rpcapi.client.prepare
|
real_prepare = self.rpcapi.client.prepare
|
||||||
|
|
||||||
def _fake_prepare_method(*args, **kwds):
|
def _fake_prepare_method(*args, **kwds):
|
||||||
for kwd in kwds:
|
for kwd in kwds:
|
||||||
self.assertEqual(kwds[kwd], target[kwd])
|
self.assertEqual(kwds[kwd], target[kwd])
|
||||||
return rpcapi.client
|
return self.rpcapi.client
|
||||||
|
|
||||||
def _fake_rpc_method(*args, **kwargs):
|
def _fake_rpc_method(*args, **kwargs):
|
||||||
self.fake_args = args
|
self.fake_args = args
|
||||||
@ -109,13 +108,13 @@ class ShareRpcAPITestCase(test.TestCase):
|
|||||||
if expected_retval:
|
if expected_retval:
|
||||||
return expected_retval
|
return expected_retval
|
||||||
|
|
||||||
self.stubs.Set(rpcapi.client, "prepare", _fake_prepare_method)
|
self.stubs.Set(self.rpcapi.client, "prepare", _fake_prepare_method)
|
||||||
self.stubs.Set(rpcapi.client, rpc_method, _fake_rpc_method)
|
self.stubs.Set(self.rpcapi.client, rpc_method, _fake_rpc_method)
|
||||||
|
|
||||||
retval = getattr(rpcapi, method)(ctxt, **kwargs)
|
retval = getattr(self.rpcapi, method)(self.ctxt, **kwargs)
|
||||||
|
|
||||||
self.assertEqual(retval, expected_retval)
|
self.assertEqual(retval, expected_retval)
|
||||||
expected_args = [ctxt, method]
|
expected_args = [self.ctxt, method]
|
||||||
for arg, expected_arg in zip(self.fake_args, expected_args):
|
for arg, expected_arg in zip(self.fake_args, expected_args):
|
||||||
self.assertEqual(arg, expected_arg)
|
self.assertEqual(arg, expected_arg)
|
||||||
|
|
||||||
@ -159,3 +158,8 @@ class ShareRpcAPITestCase(test.TestCase):
|
|||||||
rpc_method='cast',
|
rpc_method='cast',
|
||||||
snapshot=self.fake_snapshot,
|
snapshot=self.fake_snapshot,
|
||||||
host='fake_host')
|
host='fake_host')
|
||||||
|
|
||||||
|
def test_delete_share_server(self):
|
||||||
|
self._test_share_api('delete_share_server',
|
||||||
|
rpc_method='cast',
|
||||||
|
share_server=self.fake_share_server)
|
||||||
|
Loading…
Reference in New Issue
Block a user