Return 503 rather than 500 error when _ds_manager unreachable

It can happen that the _ds_manager is temporarily unreachable
due service not ready or network issues. In these cases,
the patch makes sure the API returns 503 http status code
instead of 500, so that the client knows the issue is
temporary.

Change-Id: I8a03a2e0ac84c2395b5ea2015647c5c7d2fed03e
Closes-bug: 1811754
This commit is contained in:
Eric K 2019-03-12 15:14:43 -07:00
parent e84057a6f5
commit 18e3060af5
4 changed files with 36 additions and 2 deletions

View File

@ -93,6 +93,11 @@ class DatasourceModel(base.APIModel):
LOG.debug(_("Datasource creation failed."))
raise webservice.DataModelException(
e.code, webservice.original_msg(e), http_status_code=e.code)
except exception.RpcTargetNotFound as e:
LOG.debug("Datasource creation failed.")
LOG.warning(webservice.original_msg(e))
raise webservice.DataModelException(
e.code, webservice.original_msg(e), http_status_code=503)
return (obj['id'], obj)
@ -116,7 +121,13 @@ class DatasourceModel(base.APIModel):
# Let PE synchronizer takes care of deleting policy
except (exception.DatasourceNotFound,
exception.DanglingReference) as e:
LOG.debug("Datasource deletion failed.")
raise webservice.DataModelException(e.code, str(e))
except exception.RpcTargetNotFound as e:
LOG.debug("Datasource deletion failed.")
LOG.warning(webservice.original_msg(e))
raise webservice.DataModelException(
e.code, webservice.original_msg(e), http_status_code=503)
# Note(thread-safety): blocking function
def request_refresh_action(self, params, context=None, request=None):

View File

@ -320,7 +320,7 @@ class DseNode(object):
except (messaging_exceptions.MessagingTimeout,
messaging_exceptions.MessageDeliveryFailure):
msg = "service '%s' could not be found"
raise exception.NotFound(msg % service_id)
raise exception.RpcTargetNotFound(msg % service_id)
if kwargs is None:
kwargs = {}
try:
@ -356,7 +356,7 @@ class DseNode(object):
kwargs = {}
if not self.is_valid_service(service_id):
msg = "service '%s' is not a registered service"
raise exception.NotFound(msg % service_id)
raise exception.RpcTargetNotFound(msg % service_id)
target = self.service_rpc_target(service_id, fanout=True)
LOG.trace("<%s> Casting RPC '%s' on %s", self.node_id, method, target)

View File

@ -235,6 +235,10 @@ class InvalidDatasourceName(BadConfig):
"start with underscore. Must be valid in policy language")
class RpcTargetNotFound(NotFound):
pass
class DatasourceNotFound(NotFound):
msg_fmt = _("Datasource not found %(id)s")

View File

@ -81,6 +81,16 @@ class TestDatasourceModel(base.SqlTestCase):
self.assertEqual('datasource_test_3', obj.name)
self.assertIsNotNone(ds_obj)
def test_add_item_manager_unreachable(self):
datasource3 = self._get_datasource_request()
datasource3['name'] = 'datasource_test_3'
self.datasource_model.invoke_rpc = mock.Mock(
side_effect=exception.RpcTargetNotFound())
try:
self.datasource_model.add_item(datasource3, {})
except webservice.DataModelException as e:
self.assertEqual(e.http_status_code, 503)
def test_add_datasource_with_custom_driver(self):
datasource4 = self._get_datasource_request()
datasource4['name'] = 'datasource_test_4'
@ -124,6 +134,15 @@ class TestDatasourceModel(base.SqlTestCase):
self.datasource_model.delete_item,
None, {}, context=context)
def test_delete_item_manager_unreachable(self):
context = {'ds_id': 'fake'}
self.datasource_model.invoke_rpc = mock.Mock(
side_effect=exception.RpcTargetNotFound())
try:
self.datasource_model.add_item(None, {}, context=context)
except webservice.DataModelException as e:
self.assertEqual(e.http_status_code, 503)
def test_datasource_api_model_execute(self):
def _execute_api(client, action, action_args):
positional_args = action_args.get('positional', [])