diff --git a/zaqar/storage/errors.py b/zaqar/storage/errors.py index 21f88bbce..4e0560336 100644 --- a/zaqar/storage/errors.py +++ b/zaqar/storage/errors.py @@ -191,3 +191,8 @@ class PoolCapabilitiesMismatch(ExceptionBase): msg_format = (u'The pool being added does not ' u'support the minimum set of capabilities') + + +class PoolAlreadyExists(Conflict): + + msg_format = u'The database URI is in use by another pool.' diff --git a/zaqar/storage/mongodb/pools.py b/zaqar/storage/mongodb/pools.py index d2b39188a..31aa687ae 100644 --- a/zaqar/storage/mongodb/pools.py +++ b/zaqar/storage/mongodb/pools.py @@ -23,6 +23,7 @@ Schema: """ import functools +from pymongo import errors as mongo_error from zaqar.common import utils as common_utils from zaqar.storage import base @@ -97,13 +98,16 @@ class PoolsController(base.PoolsBase): @utils.raises_conn_error def _create(self, name, weight, uri, group=None, options=None): options = {} if options is None else options - self._col.update({'n': name}, - {'$set': {'n': name, - 'w': weight, - 'u': uri, - 'g': group, - 'o': options}}, - upsert=True) + try: + self._col.update({'n': name}, + {'$set': {'n': name, + 'w': weight, + 'u': uri, + 'g': group, + 'o': options}}, + upsert=True) + except mongo_error.DuplicateKeyError: + raise errors.PoolAlreadyExists() @utils.raises_conn_error def _exists(self, name): diff --git a/zaqar/tests/unit/storage/test_impl_mongodb.py b/zaqar/tests/unit/storage/test_impl_mongodb.py index 2064e4cdb..cb340d6d4 100644 --- a/zaqar/tests/unit/storage/test_impl_mongodb.py +++ b/zaqar/tests/unit/storage/test_impl_mongodb.py @@ -528,6 +528,13 @@ class MongodbPoolsTests(base.PoolsControllerTest): group=self.pool_group, options={}) + def test_duplicate_uri(self): + with testing.expect(errors.PoolAlreadyExists): + # The url 'localhost' is used in setUp(). So reusing the uri + # 'localhost' here will raise PoolAlreadyExists. + self.pools_controller.create(str(uuid.uuid1()), 100, 'localhost', + group=str(uuid.uuid1()), options={}) + @testing.requires_mongodb class MongodbCatalogueTests(base.CatalogueControllerTest): diff --git a/zaqar/transport/wsgi/v1_0/pools.py b/zaqar/transport/wsgi/v1_0/pools.py index d97f0eb52..54e15a5a2 100644 --- a/zaqar/transport/wsgi/v1_0/pools.py +++ b/zaqar/transport/wsgi/v1_0/pools.py @@ -38,9 +38,11 @@ registered, there is an optional field:: import falcon import jsonschema from oslo_log import log +import six from zaqar.common.api.schemas import pools as schema from zaqar.common import utils as common_utils +from zaqar.i18n import _ from zaqar.storage import errors from zaqar.storage import utils as storage_utils from zaqar.transport import utils as transport_utils @@ -171,11 +173,16 @@ class Resource(object): raise wsgi_errors.HTTPBadRequestBody( 'cannot connect to %s' % data['uri'] ) - self._ctrl.create(pool, weight=data['weight'], - uri=data['uri'], - options=data.get('options', {})) - response.status = falcon.HTTP_201 - response.location = request.path + try: + self._ctrl.create(pool, weight=data['weight'], + uri=data['uri'], + options=data.get('options', {})) + response.status = falcon.HTTP_201 + response.location = request.path + except errors.PoolAlreadyExists as e: + LOG.exception(e) + title = _(u'Unable to create pool') + raise falcon.HTTPConflict(title, six.text_type(e)) def on_delete(self, request, response, project_id, pool): """Deregisters a pool. diff --git a/zaqar/transport/wsgi/v1_1/pools.py b/zaqar/transport/wsgi/v1_1/pools.py index a830a844b..5c37b5370 100644 --- a/zaqar/transport/wsgi/v1_1/pools.py +++ b/zaqar/transport/wsgi/v1_1/pools.py @@ -186,6 +186,10 @@ class Resource(object): LOG.exception(e) title = _(u'Unable to create pool') raise falcon.HTTPBadRequest(title, six.text_type(e)) + except errors.PoolAlreadyExists as e: + LOG.exception(e) + title = _(u'Unable to create pool') + raise falcon.HTTPConflict(title, six.text_type(e)) def on_delete(self, request, response, project_id, pool): """Deregisters a pool. diff --git a/zaqar/transport/wsgi/v2_0/pools.py b/zaqar/transport/wsgi/v2_0/pools.py index a2d3848fd..131b0750e 100644 --- a/zaqar/transport/wsgi/v2_0/pools.py +++ b/zaqar/transport/wsgi/v2_0/pools.py @@ -190,6 +190,10 @@ class Resource(object): LOG.exception(e) title = _(u'Unable to create pool') raise falcon.HTTPBadRequest(title, six.text_type(e)) + except errors.PoolAlreadyExists as e: + LOG.exception(e) + title = _(u'Unable to create pool') + raise falcon.HTTPConflict(title, six.text_type(e)) @acl.enforce("pools:delete") def on_delete(self, request, response, project_id, pool):