Abstract API exception handling
Change-Id: I5017dfa60d4a8266908cc09c40b95139f01da0de
This commit is contained in:
parent
19087e5ba5
commit
f0c49181d9
@ -8,12 +8,15 @@ paste.app_factory = moniker.api.versions:factory
|
||||
|
||||
[composite:osapi_dns_v1]
|
||||
use = call:moniker.api.auth:pipeline_factory
|
||||
noauth = noauthcontext osapi_dns_app_v1
|
||||
keystone = authtoken keystonecontext osapi_dns_app_v1
|
||||
noauth = noauthcontext faultwrapper_v1 osapi_dns_app_v1
|
||||
keystone = authtoken keystonecontext faultwrapper_v1 osapi_dns_app_v1
|
||||
|
||||
[app:osapi_dns_app_v1]
|
||||
paste.app_factory = moniker.api.v1:factory
|
||||
|
||||
[filter:faultwrapper_v1]
|
||||
paste.filter_factory = moniker.api.v1:FaultWrapperMiddleware.factory
|
||||
|
||||
[filter:noauthcontext]
|
||||
paste.filter_factory = moniker.api.auth:NoAuthContextMiddleware.factory
|
||||
|
||||
|
@ -14,10 +14,15 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
import flask
|
||||
import webob.dec
|
||||
from stevedore import extension
|
||||
from stevedore import named
|
||||
from moniker.openstack.common import cfg
|
||||
from moniker.openstack.common import jsonutils as json
|
||||
from moniker.openstack.common import log as logging
|
||||
from moniker.openstack.common.rpc import common as rpc_common
|
||||
from moniker import exceptions
|
||||
from moniker import wsgi
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
@ -29,6 +34,9 @@ cfg.CONF.register_opts([
|
||||
|
||||
def factory(global_config, **local_conf):
|
||||
app = flask.Flask('moniker.api.v1')
|
||||
app.config.update(
|
||||
PROPAGATE_EXCEPTIONS=True
|
||||
)
|
||||
|
||||
# TODO(kiall): Ideally, we want to make use of the Plugin class here.
|
||||
# This works for the moment though.
|
||||
@ -48,3 +56,61 @@ def factory(global_config, **local_conf):
|
||||
extmgr.map(_register_blueprint)
|
||||
|
||||
return app
|
||||
|
||||
|
||||
class FaultWrapperMiddleware(wsgi.Middleware):
|
||||
@webob.dec.wsgify
|
||||
def __call__(self, request):
|
||||
try:
|
||||
return request.get_response(self.application)
|
||||
except exceptions.Base, e:
|
||||
# Handle Moniker Exceptions
|
||||
status = e.error_code if hasattr(e, 'error_code') else 500
|
||||
|
||||
# Start building up a response
|
||||
response = {
|
||||
'code': status
|
||||
}
|
||||
|
||||
if e.error_type:
|
||||
response['type'] = e.error_type
|
||||
|
||||
if e.error_message:
|
||||
response['message'] = e.error_message
|
||||
|
||||
if e.errors:
|
||||
response['errors'] = e.errors
|
||||
|
||||
return self._handle_exception(request, e, status, response)
|
||||
except rpc_common.Timeout, e:
|
||||
# Special case for RPC timeout's
|
||||
response = {
|
||||
'code': 504,
|
||||
'type': 'timeout',
|
||||
}
|
||||
|
||||
return self._handle_exception(request, e, 504, response)
|
||||
except Exception, e:
|
||||
# Handle all other exception types
|
||||
return self._handle_exception(request, e)
|
||||
|
||||
def _handle_exception(self, request, e, status=500, response={}):
|
||||
# Log the exception ASAP
|
||||
LOG.exception(e)
|
||||
|
||||
headers = [
|
||||
('Content-Type', 'application/json'),
|
||||
]
|
||||
|
||||
# Set a response code and type, if they are missing.
|
||||
if 'code' not in response:
|
||||
response['code'] = status
|
||||
|
||||
if 'type' not in response:
|
||||
response['type'] = 'unknown'
|
||||
|
||||
# TODO(kiall): Send a fault notification
|
||||
|
||||
# Return the new response
|
||||
return flask.Response(status=status, headers=headers,
|
||||
response=json.dumps(response))
|
||||
|
@ -15,9 +15,6 @@
|
||||
# under the License.
|
||||
import flask
|
||||
from moniker.openstack.common import log as logging
|
||||
from moniker.openstack.common import jsonutils as json
|
||||
from moniker.openstack.common.rpc import common as rpc_common
|
||||
from moniker import exceptions
|
||||
from moniker import schema
|
||||
from moniker.central import api as central_api
|
||||
|
||||
@ -43,29 +40,13 @@ def create_domain():
|
||||
context = flask.request.environ.get('context')
|
||||
values = flask.request.json
|
||||
|
||||
try:
|
||||
domain_schema.validate(values)
|
||||
domain = central_api.create_domain(context, values)
|
||||
except exceptions.InvalidObject, e:
|
||||
response_body = json.dumps({'errors': e.errors})
|
||||
return flask.Response(status=400, response=response_body)
|
||||
except exceptions.BadRequest:
|
||||
return flask.Response(status=400)
|
||||
except exceptions.Forbidden:
|
||||
return flask.Response(status=401)
|
||||
except exceptions.DuplicateDomain:
|
||||
return flask.Response(status=409)
|
||||
except exceptions.NoServersConfigured:
|
||||
return flask.Response(status=500)
|
||||
except rpc_common.Timeout:
|
||||
return flask.Response(status=504)
|
||||
else:
|
||||
domain = domain_schema.filter(domain)
|
||||
|
||||
response = flask.jsonify(domain)
|
||||
response = flask.jsonify(domain_schema.filter(domain))
|
||||
response.status_int = 201
|
||||
response.location = flask.url_for('.get_domain',
|
||||
domain_id=domain['id'])
|
||||
response.location = flask.url_for('.get_domain', domain_id=domain['id'])
|
||||
|
||||
return response
|
||||
|
||||
|
||||
@ -73,38 +54,18 @@ def create_domain():
|
||||
def get_domains():
|
||||
context = flask.request.environ.get('context')
|
||||
|
||||
try:
|
||||
domains = central_api.get_domains(context)
|
||||
except exceptions.BadRequest:
|
||||
return flask.Response(status=400)
|
||||
except exceptions.Forbidden:
|
||||
return flask.Response(status=401)
|
||||
except rpc_common.Timeout:
|
||||
return flask.Response(status=504)
|
||||
else:
|
||||
domains = domains_schema.filter({'domains': domains})
|
||||
|
||||
return flask.jsonify(domains)
|
||||
return flask.jsonify(domains_schema.filter({'domains': domains}))
|
||||
|
||||
|
||||
@blueprint.route('/domains/<domain_id>', methods=['GET'])
|
||||
def get_domain(domain_id):
|
||||
context = flask.request.environ.get('context')
|
||||
|
||||
try:
|
||||
domain = central_api.get_domain(context, domain_id)
|
||||
except exceptions.BadRequest:
|
||||
return flask.Response(status=400)
|
||||
except exceptions.Forbidden:
|
||||
return flask.Response(status=401)
|
||||
except exceptions.DomainNotFound:
|
||||
return flask.Response(status=404)
|
||||
except rpc_common.Timeout:
|
||||
return flask.Response(status=504)
|
||||
else:
|
||||
domain = domain_schema.filter(domain)
|
||||
|
||||
return flask.jsonify(domain)
|
||||
return flask.jsonify(domain_schema.filter(domain))
|
||||
|
||||
|
||||
@blueprint.route('/domains/<domain_id>', methods=['PUT'])
|
||||
@ -112,46 +73,21 @@ def update_domain(domain_id):
|
||||
context = flask.request.environ.get('context')
|
||||
values = flask.request.json
|
||||
|
||||
try:
|
||||
domain = central_api.get_domain(context, domain_id)
|
||||
domain.update(values)
|
||||
|
||||
domain_schema.validate(domain)
|
||||
domain = central_api.update_domain(context, domain_id, values)
|
||||
except exceptions.InvalidObject, e:
|
||||
response_body = json.dumps({'errors': e.errors})
|
||||
return flask.Response(status=400, response=response_body)
|
||||
except exceptions.BadRequest:
|
||||
return flask.Response(status=400)
|
||||
except exceptions.Forbidden:
|
||||
return flask.Response(status=401)
|
||||
except exceptions.DomainNotFound:
|
||||
return flask.Response(status=404)
|
||||
except exceptions.DuplicateDomain:
|
||||
return flask.Response(status=409)
|
||||
except rpc_common.Timeout:
|
||||
return flask.Response(status=504)
|
||||
else:
|
||||
domain = domain_schema.filter(domain)
|
||||
|
||||
return flask.jsonify(domain)
|
||||
return flask.jsonify(domain_schema.filter(domain))
|
||||
|
||||
|
||||
@blueprint.route('/domains/<domain_id>', methods=['DELETE'])
|
||||
def delete_domain(domain_id):
|
||||
context = flask.request.environ.get('context')
|
||||
|
||||
try:
|
||||
central_api.delete_domain(context, domain_id)
|
||||
except exceptions.BadRequest:
|
||||
return flask.Response(status=400)
|
||||
except exceptions.Forbidden:
|
||||
return flask.Response(status=401)
|
||||
except exceptions.DomainNotFound:
|
||||
return flask.Response(status=404)
|
||||
except rpc_common.Timeout:
|
||||
return flask.Response(status=504)
|
||||
else:
|
||||
|
||||
return flask.Response(status=200)
|
||||
|
||||
|
||||
@ -159,17 +95,6 @@ def delete_domain(domain_id):
|
||||
def get_domain_servers(domain_id):
|
||||
context = flask.request.environ.get('context')
|
||||
|
||||
try:
|
||||
servers = central_api.get_domain_servers(context, domain_id)
|
||||
except exceptions.BadRequest:
|
||||
return flask.Response(status=400)
|
||||
except exceptions.Forbidden:
|
||||
return flask.Response(status=401)
|
||||
except exceptions.DomainNotFound:
|
||||
return flask.Response(status=404)
|
||||
except rpc_common.Timeout:
|
||||
return flask.Response(status=504)
|
||||
else:
|
||||
servers = servers_schema.filter({'servers': servers})
|
||||
|
||||
return flask.jsonify(servers)
|
||||
return flask.jsonify(servers_schema.filter({'servers': servers}))
|
||||
|
@ -15,9 +15,6 @@
|
||||
# under the License.
|
||||
import flask
|
||||
from moniker.openstack.common import log as logging
|
||||
from moniker.openstack.common import jsonutils as json
|
||||
from moniker.openstack.common.rpc import common as rpc_common
|
||||
from moniker import exceptions
|
||||
from moniker import schema
|
||||
from moniker.central import api as central_api
|
||||
|
||||
@ -42,28 +39,14 @@ def create_record(domain_id):
|
||||
context = flask.request.environ.get('context')
|
||||
values = flask.request.json
|
||||
|
||||
try:
|
||||
record_schema.validate(values)
|
||||
record = central_api.create_record(context, domain_id, values)
|
||||
except exceptions.InvalidObject, e:
|
||||
response_body = json.dumps({'errors': e.errors})
|
||||
return flask.Response(status=400, response=response_body)
|
||||
except exceptions.BadRequest:
|
||||
return flask.Response(status=400)
|
||||
except exceptions.Forbidden:
|
||||
return flask.Response(status=401)
|
||||
except exceptions.DuplicateRecord:
|
||||
return flask.Response(status=409)
|
||||
except rpc_common.Timeout:
|
||||
return flask.Response(status=504)
|
||||
else:
|
||||
record = record_schema.filter(record)
|
||||
|
||||
response = flask.jsonify(record)
|
||||
response = flask.jsonify(record_schema.filter(record))
|
||||
response.status_int = 201
|
||||
response.location = flask.url_for('.get_record',
|
||||
domain_id=domain_id,
|
||||
response.location = flask.url_for('.get_record', domain_id=domain_id,
|
||||
record_id=record['id'])
|
||||
|
||||
return response
|
||||
|
||||
|
||||
@ -71,40 +54,18 @@ def create_record(domain_id):
|
||||
def get_records(domain_id):
|
||||
context = flask.request.environ.get('context')
|
||||
|
||||
try:
|
||||
records = central_api.get_records(context, domain_id)
|
||||
except exceptions.BadRequest:
|
||||
return flask.Response(status=400)
|
||||
except exceptions.Forbidden:
|
||||
return flask.Response(status=401)
|
||||
except exceptions.DomainNotFound:
|
||||
return flask.Response(status=404)
|
||||
except rpc_common.Timeout:
|
||||
return flask.Response(status=504)
|
||||
else:
|
||||
records = records_schema.filter({'records': records})
|
||||
|
||||
return flask.jsonify(records)
|
||||
return flask.jsonify(records_schema.filter({'records': records}))
|
||||
|
||||
|
||||
@blueprint.route('/domains/<domain_id>/records/<record_id>', methods=['GET'])
|
||||
def get_record(domain_id, record_id):
|
||||
context = flask.request.environ.get('context')
|
||||
|
||||
try:
|
||||
record = central_api.get_record(context, domain_id, record_id)
|
||||
except exceptions.BadRequest:
|
||||
return flask.Response(status=400)
|
||||
except exceptions.Forbidden:
|
||||
return flask.Response(status=401)
|
||||
except (exceptions.RecordNotFound, exceptions.DomainNotFound):
|
||||
return flask.Response(status=404)
|
||||
except rpc_common.Timeout:
|
||||
return flask.Response(status=504)
|
||||
else:
|
||||
record = record_schema.filter(record)
|
||||
|
||||
return flask.jsonify(record)
|
||||
return flask.jsonify(record_schema.filter(record))
|
||||
|
||||
|
||||
@blueprint.route('/domains/<domain_id>/records/<record_id>', methods=['PUT'])
|
||||
@ -112,30 +73,13 @@ def update_record(domain_id, record_id):
|
||||
context = flask.request.environ.get('context')
|
||||
values = flask.request.json
|
||||
|
||||
try:
|
||||
record = central_api.get_record(context, domain_id, record_id)
|
||||
record.update(values)
|
||||
|
||||
record_schema.validate(record)
|
||||
record = central_api.update_record(context, domain_id, record_id,
|
||||
values)
|
||||
except exceptions.InvalidObject, e:
|
||||
response_body = json.dumps({'errors': e.errors})
|
||||
return flask.Response(status=400, response=response_body)
|
||||
except exceptions.BadRequest:
|
||||
return flask.Response(status=400)
|
||||
except exceptions.Forbidden:
|
||||
return flask.Response(status=401)
|
||||
except (exceptions.RecordNotFound, exceptions.DomainNotFound):
|
||||
return flask.Response(status=404)
|
||||
except exceptions.DuplicateRecord:
|
||||
return flask.Response(status=409)
|
||||
except rpc_common.Timeout:
|
||||
return flask.Response(status=504)
|
||||
else:
|
||||
record = record_schema.filter(record)
|
||||
record = central_api.update_record(context, domain_id, record_id, values)
|
||||
|
||||
return flask.jsonify(record)
|
||||
return flask.jsonify(record_schema.filter(record))
|
||||
|
||||
|
||||
@blueprint.route('/domains/<domain_id>/records/<record_id>',
|
||||
@ -143,15 +87,6 @@ def update_record(domain_id, record_id):
|
||||
def delete_record(domain_id, record_id):
|
||||
context = flask.request.environ.get('context')
|
||||
|
||||
try:
|
||||
central_api.delete_record(context, domain_id, record_id)
|
||||
except exceptions.BadRequest:
|
||||
return flask.Response(status=400)
|
||||
except exceptions.Forbidden:
|
||||
return flask.Response(status=401)
|
||||
except (exceptions.RecordNotFound, exceptions.DomainNotFound):
|
||||
return flask.Response(status=404)
|
||||
except rpc_common.Timeout:
|
||||
return flask.Response(status=504)
|
||||
else:
|
||||
|
||||
return flask.Response(status=200)
|
||||
|
@ -15,9 +15,6 @@
|
||||
# under the License.
|
||||
import flask
|
||||
from moniker.openstack.common import log as logging
|
||||
from moniker.openstack.common import jsonutils as json
|
||||
from moniker.openstack.common.rpc import common as rpc_common
|
||||
from moniker import exceptions
|
||||
from moniker import schema
|
||||
from moniker.central import api as central_api
|
||||
|
||||
@ -42,27 +39,13 @@ def create_server():
|
||||
context = flask.request.environ.get('context')
|
||||
values = flask.request.json
|
||||
|
||||
try:
|
||||
server_schema.validate(values)
|
||||
server = central_api.create_server(context, values=flask.request.json)
|
||||
except exceptions.InvalidObject, e:
|
||||
response_body = json.dumps({'errors': e.errors})
|
||||
return flask.Response(status=400, response=response_body)
|
||||
except exceptions.BadRequest:
|
||||
return flask.Response(status=400)
|
||||
except exceptions.Forbidden:
|
||||
return flask.Response(status=401)
|
||||
except exceptions.DuplicateServer:
|
||||
return flask.Response(status=409)
|
||||
except rpc_common.Timeout:
|
||||
return flask.Response(status=504)
|
||||
else:
|
||||
server = server_schema.filter(server)
|
||||
|
||||
response = flask.jsonify(server)
|
||||
response = flask.jsonify(server_schema.filter(server))
|
||||
response.status_int = 201
|
||||
response.location = flask.url_for('.get_server',
|
||||
server_id=server['id'])
|
||||
response.location = flask.url_for('.get_server', server_id=server['id'])
|
||||
|
||||
return response
|
||||
|
||||
|
||||
@ -70,38 +53,18 @@ def create_server():
|
||||
def get_servers():
|
||||
context = flask.request.environ.get('context')
|
||||
|
||||
try:
|
||||
servers = central_api.get_servers(context)
|
||||
except exceptions.BadRequest:
|
||||
return flask.Response(status=400)
|
||||
except exceptions.Forbidden:
|
||||
return flask.Response(status=401)
|
||||
except rpc_common.Timeout:
|
||||
return flask.Response(status=504)
|
||||
else:
|
||||
servers = servers_schema.filter({'servers': servers})
|
||||
|
||||
return flask.jsonify(servers)
|
||||
return flask.jsonify(servers_schema.filter({'servers': servers}))
|
||||
|
||||
|
||||
@blueprint.route('/servers/<server_id>', methods=['GET'])
|
||||
def get_server(server_id):
|
||||
context = flask.request.environ.get('context')
|
||||
|
||||
try:
|
||||
server = central_api.get_server(context, server_id)
|
||||
except exceptions.BadRequest:
|
||||
return flask.Response(status=400)
|
||||
except exceptions.Forbidden:
|
||||
return flask.Response(status=401)
|
||||
except exceptions.ServerNotFound:
|
||||
return flask.Response(status=404)
|
||||
except rpc_common.Timeout:
|
||||
return flask.Response(status=504)
|
||||
else:
|
||||
server = server_schema.filter(server)
|
||||
|
||||
return flask.jsonify(server)
|
||||
return flask.jsonify(server_schema.filter(server))
|
||||
|
||||
|
||||
@blueprint.route('/servers/<server_id>', methods=['PUT'])
|
||||
@ -109,44 +72,19 @@ def update_server(server_id):
|
||||
context = flask.request.environ.get('context')
|
||||
values = flask.request.json
|
||||
|
||||
try:
|
||||
server = central_api.get_server(context, server_id)
|
||||
server.update(values)
|
||||
|
||||
server_schema.validate(server)
|
||||
server = central_api.update_server(context, server_id, values=values)
|
||||
except exceptions.InvalidObject, e:
|
||||
response_body = json.dumps({'errors': e.errors})
|
||||
return flask.Response(status=400, response=response_body)
|
||||
except exceptions.BadRequest:
|
||||
return flask.Response(status=400)
|
||||
except exceptions.Forbidden:
|
||||
return flask.Response(status=401)
|
||||
except exceptions.ServerNotFound:
|
||||
return flask.Response(status=404)
|
||||
except exceptions.DuplicateServer:
|
||||
return flask.Response(status=409)
|
||||
except rpc_common.Timeout:
|
||||
return flask.Response(status=504)
|
||||
else:
|
||||
server = server_schema.filter(server)
|
||||
|
||||
return flask.jsonify(server)
|
||||
return flask.jsonify(server_schema.filter(server))
|
||||
|
||||
|
||||
@blueprint.route('/servers/<server_id>', methods=['DELETE'])
|
||||
def delete_server(server_id):
|
||||
context = flask.request.environ.get('context')
|
||||
|
||||
try:
|
||||
central_api.delete_server(context, server_id)
|
||||
except exceptions.BadRequest:
|
||||
return flask.Response(status=400)
|
||||
except exceptions.Forbidden:
|
||||
return flask.Response(status=401)
|
||||
except exceptions.ServerNotFound:
|
||||
return flask.Response(status=404)
|
||||
except rpc_common.Timeout:
|
||||
return flask.Response(status=504)
|
||||
else:
|
||||
|
||||
return flask.Response(status=200)
|
||||
|
@ -15,9 +15,6 @@
|
||||
# under the License.
|
||||
import flask
|
||||
from moniker.openstack.common import log as logging
|
||||
from moniker.openstack.common import jsonutils as json
|
||||
from moniker.openstack.common.rpc import common as rpc_common
|
||||
from moniker import exceptions
|
||||
from moniker import schema
|
||||
from moniker.central import api as central_api
|
||||
|
||||
@ -42,28 +39,13 @@ def create_tsigkey():
|
||||
context = flask.request.environ.get('context')
|
||||
values = flask.request.json
|
||||
|
||||
try:
|
||||
tsigkey_schema.validate(values)
|
||||
tsigkey = central_api.create_tsigkey(context,
|
||||
values=flask.request.json)
|
||||
except exceptions.InvalidObject, e:
|
||||
response_body = json.dumps({'errors': e.errors})
|
||||
return flask.Response(status=400, response=response_body)
|
||||
except exceptions.BadRequest:
|
||||
return flask.Response(status=400)
|
||||
except exceptions.Forbidden:
|
||||
return flask.Response(status=401)
|
||||
except exceptions.DuplicateTsigKey:
|
||||
return flask.Response(status=409)
|
||||
except rpc_common.Timeout:
|
||||
return flask.Response(status=504)
|
||||
else:
|
||||
tsigkey = tsigkey_schema.filter(tsigkey)
|
||||
tsigkey = central_api.create_tsigkey(context, values=flask.request.json)
|
||||
|
||||
response = flask.jsonify(tsigkey)
|
||||
response = flask.jsonify(tsigkey_schema.filter(tsigkey))
|
||||
response.status_int = 201
|
||||
response.location = flask.url_for('.get_tsigkey',
|
||||
tsigkey_id=tsigkey['id'])
|
||||
response.location = flask.url_for('.get_tsigkey', tsigkey_id=tsigkey['id'])
|
||||
|
||||
return response
|
||||
|
||||
|
||||
@ -71,38 +53,18 @@ def create_tsigkey():
|
||||
def get_tsigkeys():
|
||||
context = flask.request.environ.get('context')
|
||||
|
||||
try:
|
||||
tsigkeys = central_api.get_tsigkeys(context)
|
||||
except exceptions.BadRequest:
|
||||
return flask.Response(status=400)
|
||||
except exceptions.Forbidden:
|
||||
return flask.Response(status=401)
|
||||
except rpc_common.Timeout:
|
||||
return flask.Response(status=504)
|
||||
else:
|
||||
tsigkeys = tsigkeys_schema.filter({'tsigkeys': tsigkeys})
|
||||
|
||||
return flask.jsonify(tsigkeys)
|
||||
return flask.jsonify(tsigkeys_schema.filter({'tsigkeys': tsigkeys}))
|
||||
|
||||
|
||||
@blueprint.route('/tsigkeys/<tsigkey_id>', methods=['GET'])
|
||||
def get_tsigkey(tsigkey_id):
|
||||
context = flask.request.environ.get('context')
|
||||
|
||||
try:
|
||||
tsigkey = central_api.get_tsigkey(context, tsigkey_id)
|
||||
except exceptions.BadRequest:
|
||||
return flask.Response(status=400)
|
||||
except exceptions.Forbidden:
|
||||
return flask.Response(status=401)
|
||||
except exceptions.TsigKeyNotFound:
|
||||
return flask.Response(status=404)
|
||||
except rpc_common.Timeout:
|
||||
return flask.Response(status=504)
|
||||
else:
|
||||
tsigkey = tsigkey_schema.filter(tsigkey)
|
||||
|
||||
return flask.jsonify(tsigkey)
|
||||
return flask.jsonify(tsigkey_schema.filter(tsigkey))
|
||||
|
||||
|
||||
@blueprint.route('/tsigkeys/<tsigkey_id>', methods=['PUT'])
|
||||
@ -110,45 +72,19 @@ def update_tsigkey(tsigkey_id):
|
||||
context = flask.request.environ.get('context')
|
||||
values = flask.request.json
|
||||
|
||||
try:
|
||||
tsigkey = central_api.get_tsigkey(context, tsigkey_id)
|
||||
tsigkey.update(values)
|
||||
|
||||
tsigkey_schema.validate(tsigkey)
|
||||
tsigkey = central_api.update_tsigkey(context, tsigkey_id,
|
||||
values=values)
|
||||
except exceptions.InvalidObject, e:
|
||||
response_body = json.dumps({'errors': e.errors})
|
||||
return flask.Response(status=400, response=response_body)
|
||||
except exceptions.BadRequest:
|
||||
return flask.Response(status=400)
|
||||
except exceptions.Forbidden:
|
||||
return flask.Response(status=401)
|
||||
except exceptions.TsigKeyNotFound:
|
||||
return flask.Response(status=404)
|
||||
except exceptions.DuplicateTsigKey:
|
||||
return flask.Response(status=409)
|
||||
except rpc_common.Timeout:
|
||||
return flask.Response(status=504)
|
||||
else:
|
||||
tsigkey = tsigkey_schema.filter(tsigkey)
|
||||
tsigkey = central_api.update_tsigkey(context, tsigkey_id, values=values)
|
||||
|
||||
return flask.jsonify(tsigkey)
|
||||
return flask.jsonify(tsigkey_schema.filter(tsigkey))
|
||||
|
||||
|
||||
@blueprint.route('/tsigkeys/<tsigkey_id>', methods=['DELETE'])
|
||||
def delete_tsigkey(tsigkey_id):
|
||||
context = flask.request.environ.get('context')
|
||||
|
||||
try:
|
||||
central_api.delete_tsigkey(context, tsigkey_id)
|
||||
except exceptions.BadRequest:
|
||||
return flask.Response(status=400)
|
||||
except exceptions.Forbidden:
|
||||
return flask.Response(status=401)
|
||||
except exceptions.TsigKeyNotFound:
|
||||
return flask.Response(status=404)
|
||||
except rpc_common.Timeout:
|
||||
return flask.Response(status=504)
|
||||
else:
|
||||
|
||||
return flask.Response(status=200)
|
||||
|
@ -72,27 +72,28 @@ class Service(rpc_service.Service):
|
||||
def _is_valid_domain_name(self, context, domain_name):
|
||||
# Validate domain name length
|
||||
if len(domain_name) > cfg.CONF['service:central'].max_domain_name_len:
|
||||
raise exceptions.BadRequest('Domain name too long')
|
||||
raise exceptions.InvalidDomainName('Name too long')
|
||||
|
||||
# Break the domain name up into its component labels
|
||||
domain_labels = domain_name.strip('.').split('.')
|
||||
|
||||
# We need more than 1 label.
|
||||
if len(domain_labels) <= 1:
|
||||
raise exceptions.BadRequest('Invalid Domain Name')
|
||||
raise exceptions.InvalidDomainName('More than one label is '
|
||||
'required')
|
||||
|
||||
# Check the TLD for validity
|
||||
if self.accepted_tld_list:
|
||||
domain_tld = domain_labels[-1].lower()
|
||||
|
||||
if domain_tld not in self.accepted_tld_list:
|
||||
raise exceptions.BadRequest('Invalid TLD')
|
||||
raise exceptions.InvalidDomainName('Unknown or invalid TLD')
|
||||
|
||||
# Check domain name blacklist
|
||||
if self._is_blacklisted_domain_name(context, domain_name):
|
||||
# Some users are allowed bypass the blacklist.. Is this one?
|
||||
if not policy.check('use_blacklisted_domain', context, exc=None):
|
||||
raise exceptions.BadRequest('Blacklisted domain name')
|
||||
raise exceptions.InvalidDomainName('Blacklisted domain name')
|
||||
|
||||
return True
|
||||
|
||||
@ -102,17 +103,18 @@ class Service(rpc_service.Service):
|
||||
|
||||
# Validate record name length
|
||||
if len(record_name) > cfg.CONF['service:central'].max_record_name_len:
|
||||
raise exceptions.BadRequest('Record name too long')
|
||||
raise exceptions.InvalidRecordName('Name too long')
|
||||
|
||||
# Record must be contained in the parent zone.
|
||||
if not record_name.endswith(domain['name']):
|
||||
raise exceptions.BadRequest('Records must be contained within '
|
||||
'their parent zone.')
|
||||
raise exceptions.InvalidRecordLocation('Record is not contained '
|
||||
'within it\'s parent '
|
||||
'domain')
|
||||
|
||||
# CNAME's must not be created at the zone apex.
|
||||
if record_type == 'CNAME' and record_name == domain['name']:
|
||||
raise exceptions.BadRequest('%s records may not be created at '
|
||||
'the zone apex' % record_type)
|
||||
raise exceptions.InvalidRecordLocation('CNAME records may not be '
|
||||
'created at the zone apex')
|
||||
|
||||
# CNAME's must not share a name with other records
|
||||
criterion = {'name': record_name}
|
||||
@ -123,8 +125,9 @@ class Service(rpc_service.Service):
|
||||
records = self.storage.get_records(context, domain['id'],
|
||||
criterion=criterion)
|
||||
if len(records) > 0:
|
||||
raise exceptions.BadRequest('CNAME records must not share a name'
|
||||
'with any other records')
|
||||
raise exceptions.InvalidRecordLocation('CNAME records may not '
|
||||
'share a name with any '
|
||||
'other records')
|
||||
|
||||
if record_type == 'CNAME':
|
||||
# CNAME's may not have children. Ever.
|
||||
@ -133,15 +136,17 @@ class Service(rpc_service.Service):
|
||||
criterion=criterion)
|
||||
|
||||
if len(records) > 0:
|
||||
raise exceptions.BadRequest('CNAME records must not have any '
|
||||
'child records')
|
||||
raise exceptions.InvalidRecordLocation('CNAME records may not '
|
||||
'have any child '
|
||||
'records')
|
||||
|
||||
else:
|
||||
# No record may have a CNAME as a parent
|
||||
if self._is_subrecord(context, domain, record_name,
|
||||
{'type': 'CNAME'}):
|
||||
raise exceptions.BadRequest('Records must not be children of '
|
||||
'a CNAME record')
|
||||
raise exceptions.InvalidRecordLocation('CNAME records may not '
|
||||
'have any child '
|
||||
'records')
|
||||
|
||||
return True
|
||||
|
||||
|
@ -16,7 +16,18 @@
|
||||
|
||||
|
||||
class Base(Exception):
|
||||
pass
|
||||
error_code = 500
|
||||
error_type = None
|
||||
error_message = None
|
||||
errors = None
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.errors = kwargs.pop('errors', None)
|
||||
|
||||
super(Base, self).__init__(*args, **kwargs)
|
||||
|
||||
if len(args) > 0 and isinstance(args[0], basestring):
|
||||
self.error_message = args[0]
|
||||
|
||||
|
||||
class Backend(Exception):
|
||||
@ -28,7 +39,7 @@ class NotImplemented(Base, NotImplementedError):
|
||||
|
||||
|
||||
class ConfigurationError(Base):
|
||||
pass
|
||||
error_type = 'configuration_error'
|
||||
|
||||
|
||||
class NoServersConfigured(ConfigurationError):
|
||||
@ -36,17 +47,33 @@ class NoServersConfigured(ConfigurationError):
|
||||
|
||||
|
||||
class InvalidObject(Base):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.errors = kwargs.pop('errors', None)
|
||||
super(InvalidObject, self).__init__(*args, **kwargs)
|
||||
error_code = 400
|
||||
error_type = 'invalid_object'
|
||||
|
||||
|
||||
class BadRequest(Base):
|
||||
pass
|
||||
error_code = 400
|
||||
error_type = 'bad_request'
|
||||
|
||||
|
||||
class InvalidDomainName(Base):
|
||||
error_code = 400
|
||||
error_type = 'invalid_domain_name'
|
||||
|
||||
|
||||
class InvalidRecordName(Base):
|
||||
error_code = 400
|
||||
error_type = 'invalid_record_name'
|
||||
|
||||
|
||||
class InvalidRecordLocation(Base):
|
||||
error_code = 400
|
||||
error_type = 'invalid_record_location'
|
||||
|
||||
|
||||
class Forbidden(Base):
|
||||
pass
|
||||
error_code = 401
|
||||
error_type = 'forbidden'
|
||||
|
||||
|
||||
class InvalidSortKey(Base):
|
||||
@ -54,44 +81,47 @@ class InvalidSortKey(Base):
|
||||
|
||||
|
||||
class Duplicate(Base):
|
||||
pass
|
||||
error_code = 409
|
||||
error_type = 'duplicate'
|
||||
|
||||
|
||||
class DuplicateServer(Duplicate):
|
||||
pass
|
||||
error_type = 'duplicate_server'
|
||||
|
||||
|
||||
class DuplicateTsigKey(Duplicate):
|
||||
pass
|
||||
error_type = 'duplicate_tsigkey'
|
||||
|
||||
|
||||
class DuplicateDomain(Duplicate):
|
||||
pass
|
||||
error_type = 'duplicate_domain'
|
||||
|
||||
|
||||
class DuplicateRecord(Duplicate):
|
||||
pass
|
||||
error_type = 'duplicate_record'
|
||||
|
||||
|
||||
class NotFound(Base):
|
||||
pass
|
||||
error_code = 404
|
||||
error_type = 'not_found'
|
||||
|
||||
|
||||
class ServerNotFound(NotFound):
|
||||
pass
|
||||
error_type = 'server_not_found'
|
||||
|
||||
|
||||
class TsigKeyNotFound(NotFound):
|
||||
pass
|
||||
error_type = 'tsigkey_not_found'
|
||||
|
||||
|
||||
class DomainNotFound(NotFound):
|
||||
pass
|
||||
error_type = 'domain_not_found'
|
||||
|
||||
|
||||
class RecordNotFound(NotFound):
|
||||
pass
|
||||
error_type = 'record_not_found'
|
||||
|
||||
|
||||
class ResourceNotFound(NotFound):
|
||||
# TODO(kiall): Should this be extending NotFound??
|
||||
pass
|
||||
|
@ -313,7 +313,7 @@ def deserialize_remote_exception(conf, data):
|
||||
if not issubclass(klass, Exception):
|
||||
raise TypeError("Can only deserialize Exceptions")
|
||||
|
||||
failure = klass(**failure.get('kwargs', {}))
|
||||
failure = klass(*failure.get('args', []), **failure.get('kwargs', {}))
|
||||
except (AttributeError, TypeError, ImportError):
|
||||
return RemoteError(name, failure.get('message'), trace)
|
||||
|
||||
|
48
moniker/resources/schemas/v1/fault.json
Normal file
48
moniker/resources/schemas/v1/fault.json
Normal file
@ -0,0 +1,48 @@
|
||||
{
|
||||
"id": "/schemas/fault",
|
||||
|
||||
"$schema": "http://json-schema.org/draft-03/hyper-schema",
|
||||
|
||||
"title": "fault",
|
||||
"description": "Fault",
|
||||
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"description": "Fault Code",
|
||||
"required": true,
|
||||
"readonly": true
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"description": "Fault Type",
|
||||
"required": true,
|
||||
"readonly": true
|
||||
},
|
||||
"message": {
|
||||
"type": "string",
|
||||
"description": "Fault Message",
|
||||
"readonly": true
|
||||
},
|
||||
"errors": {
|
||||
"type": "array",
|
||||
"description": "List of Errors",
|
||||
"readonly": true,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"path": {
|
||||
"type": "string",
|
||||
"description": "Error Path",
|
||||
"readonly": true
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"description": "Error Type",
|
||||
"readonly": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -21,6 +21,10 @@ class ApiServiceTest(ApiTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(ApiServiceTest, self).setUp()
|
||||
|
||||
# Use a random port for the API
|
||||
self.config(api_port=0, group='service:api')
|
||||
|
||||
self.service = self.get_api_service()
|
||||
|
||||
def test_start_and_stop(self):
|
||||
|
@ -14,8 +14,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from moniker.openstack.common import log as logging
|
||||
from moniker.api.v1 import factory
|
||||
from moniker.api.auth import NoAuthContextMiddleware
|
||||
from moniker.api import v1 as api_v1
|
||||
from moniker.api import auth
|
||||
from moniker.tests.test_api import ApiTestCase
|
||||
|
||||
|
||||
@ -29,10 +29,13 @@ class ApiV1Test(ApiTestCase):
|
||||
super(ApiV1Test, self).setUp()
|
||||
|
||||
# Create a Flask application
|
||||
self.app = factory({})
|
||||
self.app = api_v1.factory({})
|
||||
|
||||
# Inject the FaultWrapper middleware
|
||||
self.app.wsgi_app = api_v1.FaultWrapperMiddleware(self.app.wsgi_app)
|
||||
|
||||
# Inject the NoAuth middleware
|
||||
self.app.wsgi_app = NoAuthContextMiddleware(self.app.wsgi_app)
|
||||
self.app.wsgi_app = auth.NoAuthContextMiddleware(self.app.wsgi_app)
|
||||
|
||||
# Obtain a test client
|
||||
self.client = self.app.test_client()
|
||||
|
@ -43,10 +43,10 @@ class CentralServiceTest(CentralTestCase):
|
||||
|
||||
self.central_service._is_valid_domain_name(context, 'valid.org.')
|
||||
|
||||
with self.assertRaises(exceptions.BadRequest):
|
||||
with self.assertRaises(exceptions.InvalidDomainName):
|
||||
self.central_service._is_valid_domain_name(context, 'example.org.')
|
||||
|
||||
with self.assertRaises(exceptions.BadRequest):
|
||||
with self.assertRaises(exceptions.InvalidDomainName):
|
||||
self.central_service._is_valid_domain_name(context, 'example.tld.')
|
||||
|
||||
def test_is_valid_record_name(self):
|
||||
@ -62,15 +62,15 @@ class CentralServiceTest(CentralTestCase):
|
||||
'valid.example.org.',
|
||||
'A')
|
||||
|
||||
with self.assertRaises(exceptions.BadRequest):
|
||||
with self.assertRaises(exceptions.InvalidRecordName):
|
||||
self.central_service._is_valid_record_name(
|
||||
context, domain, 'toolong.example.org.', 'A')
|
||||
|
||||
with self.assertRaises(exceptions.BadRequest):
|
||||
with self.assertRaises(exceptions.InvalidRecordLocation):
|
||||
self.central_service._is_valid_record_name(
|
||||
context, domain, 'a.example.COM.', 'A')
|
||||
|
||||
with self.assertRaises(exceptions.BadRequest):
|
||||
with self.assertRaises(exceptions.InvalidRecordLocation):
|
||||
self.central_service._is_valid_record_name(
|
||||
context, domain, 'example.org.', 'CNAME')
|
||||
|
||||
@ -386,7 +386,7 @@ class CentralServiceTest(CentralTestCase):
|
||||
email='info@blacklisted.com'
|
||||
)
|
||||
|
||||
with self.assertRaises(exceptions.BadRequest):
|
||||
with self.assertRaises(exceptions.InvalidDomainName):
|
||||
# Create a domain
|
||||
self.central_service.create_domain(context, values=values)
|
||||
|
||||
@ -412,7 +412,7 @@ class CentralServiceTest(CentralTestCase):
|
||||
email='info@invalid.com'
|
||||
)
|
||||
|
||||
with self.assertRaises(exceptions.BadRequest):
|
||||
with self.assertRaises(exceptions.InvalidDomainName):
|
||||
# Create an invalid domain
|
||||
self.central_service.create_domain(context, values=values)
|
||||
|
||||
@ -630,7 +630,7 @@ class CentralServiceTest(CentralTestCase):
|
||||
)
|
||||
|
||||
# Attempt to create a CNAME record at the apex
|
||||
with self.assertRaises(exceptions.BadRequest):
|
||||
with self.assertRaises(exceptions.InvalidRecordLocation):
|
||||
self.central_service.create_record(context, domain['id'],
|
||||
values=values)
|
||||
|
||||
@ -648,7 +648,7 @@ class CentralServiceTest(CentralTestCase):
|
||||
values=values)
|
||||
|
||||
# Attempt to create a CNAME record alongside an A record
|
||||
with self.assertRaises(exceptions.BadRequest):
|
||||
with self.assertRaises(exceptions.InvalidRecordLocation):
|
||||
values = dict(
|
||||
name='www.%s' % domain['name'],
|
||||
type='CNAME',
|
||||
@ -672,7 +672,7 @@ class CentralServiceTest(CentralTestCase):
|
||||
values=values)
|
||||
|
||||
# Attempt to create a CNAME record alongside an A record
|
||||
with self.assertRaises(exceptions.BadRequest):
|
||||
with self.assertRaises(exceptions.InvalidRecordLocation):
|
||||
values = dict(
|
||||
name='www.%s' % domain['name'],
|
||||
type='CNAME',
|
||||
@ -696,7 +696,7 @@ class CentralServiceTest(CentralTestCase):
|
||||
values=values)
|
||||
|
||||
# Attempt to create a CNAME record alongside an A record
|
||||
with self.assertRaises(exceptions.BadRequest):
|
||||
with self.assertRaises(exceptions.InvalidRecordLocation):
|
||||
values = dict(
|
||||
name='www.%s' % domain['name'],
|
||||
type='A',
|
||||
@ -720,7 +720,7 @@ class CentralServiceTest(CentralTestCase):
|
||||
values=values)
|
||||
|
||||
# Attempt to create a CNAME record alongside an A record
|
||||
with self.assertRaises(exceptions.BadRequest):
|
||||
with self.assertRaises(exceptions.InvalidRecordLocation):
|
||||
values = dict(
|
||||
name='t.www.%s' % domain['name'],
|
||||
type='A',
|
||||
|
Loading…
Reference in New Issue
Block a user