Commit c318106c authored by Graham Hayes's avatar Graham Hayes Committed by Dai Dang Van

Remove v1 API

This completes the long awaited removal of the V1 API.

Change-Id: I30c8a5e8569b1b86286c5e3cb07856c06ebe5803
parent 11ab86e3
{
"versions": {
"values": [
{
"id": "v1",
"links": [
{
"href": "http://127.0.0.1:9001/v1",
"rel": "self"
}
],
"status": "DEPRECATED"
},
{
"id": "v2",
"links": [
......
This diff is collapsed.
......@@ -49,11 +49,6 @@ api_opts = [
cfg.StrOpt('auth_strategy', default='keystone',
help='The strategy to use for auth. Supports noauth or '
'keystone'),
cfg.BoolOpt('enable-api-v1', default=False,
deprecated_for_removal=True,
deprecated_reason="V1 API is being removed in a future"
"release",
help='enable-api-v1 which removed in a future'),
cfg.BoolOpt('enable-api-v2', default=True,
help='enable-api-v2 which enable in a future'),
cfg.BoolOpt('enable-api-admin', default=False,
......@@ -65,11 +60,6 @@ api_opts = [
"Keystone v3 API with big service catalogs)."),
]
api_v1_opts = [
cfg.ListOpt('enabled-extensions-v1', default=[],
help='Enabled API Extensions'),
]
api_v2_opts = [
cfg.ListOpt('enabled-extensions-v2', default=[],
help='Enabled API Extensions for the V2 API'),
......@@ -109,7 +99,6 @@ api_middleware_opts = [
cfg.CONF.register_group(api_group)
cfg.CONF.register_opts(api_opts, group=api_group)
cfg.CONF.register_opts(api_v1_opts, group=api_group)
cfg.CONF.register_opts(api_v2_opts, group=api_group)
cfg.CONF.register_opts(api_admin_opts, group=api_group)
cfg.CONF.register_opts(api_middleware_opts, group=api_group)
......@@ -117,7 +106,6 @@ cfg.CONF.register_opts(api_middleware_opts, group=api_group)
def list_opts():
yield api_group, api_opts
yield api_group, api_v1_opts
yield api_group, api_v2_opts
yield api_group, api_admin_opts
yield api_group, api_middleware_opts
......@@ -308,17 +308,6 @@ class FaultWrapperMiddleware(base.Middleware):
response=json.dumps(response))
class FaultWrapperMiddlewareV1(FaultWrapperMiddleware):
def _format_error(self, data):
replace_map = [
("zone", "domain",)
]
for i in replace_map:
data["type"] = data["type"].replace(i[0], i[1])
print(data)
class ValidationErrorMiddleware(base.Middleware):
def __init__(self, application):
......@@ -370,12 +359,6 @@ class ValidationErrorMiddleware(base.Middleware):
response=json.dumps(response))
class APIv1ValidationErrorMiddleware(ValidationErrorMiddleware):
def __init__(self, application):
super(APIv1ValidationErrorMiddleware, self).__init__(application)
self.api_version = 'API_v1'
class APIv2ValidationErrorMiddleware(ValidationErrorMiddleware):
def __init__(self, application):
super(APIv2ValidationErrorMiddleware, self).__init__(application)
......
# Copyright 2012 Managed I.T.
#
# Author: Kiall Mac Innes <kiall@managedit.ie>
#
# 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.
import six
import flask
from stevedore import extension
from stevedore import named
from werkzeug import exceptions as wexceptions
from werkzeug import wrappers
from werkzeug.routing import BaseConverter
from werkzeug.routing import ValidationError
from oslo_config import cfg
from oslo_log import log as logging
from oslo_serialization import jsonutils
from designate import exceptions
from designate import utils
LOG = logging.getLogger(__name__)
class DesignateRequest(flask.Request, wrappers.AcceptMixin,
wrappers.CommonRequestDescriptorsMixin):
def __init__(self, *args, **kwargs):
super(DesignateRequest, self).__init__(*args, **kwargs)
self._validate_content_type()
self._validate_accept()
def _validate_content_type(self):
if (self.method in ['POST', 'PUT', 'PATCH']
and self.mimetype != 'application/json'):
msg = 'Unsupported Content-Type: %s' % self.mimetype
raise exceptions.UnsupportedContentType(msg)
def _validate_accept(self):
if 'accept' in self.headers and not self.accept_mimetypes.accept_json:
msg = 'Unsupported Accept: %s' % self.accept_mimetypes
raise exceptions.UnsupportedAccept(msg)
class JSONEncoder(flask.json.JSONEncoder):
@staticmethod
def default(o):
return jsonutils.to_primitive(o)
def factory(global_config, **local_conf):
if not cfg.CONF['service:api'].enable_api_v1:
def disabled_app(environ, start_response):
status = '404 Not Found'
start_response(status, [])
return []
return disabled_app
app = flask.Flask('designate.api.v1')
app.request_class = DesignateRequest
app.json_encoder = JSONEncoder
app.config.update(
PROPAGATE_EXCEPTIONS=True
)
# Install custom converters (URL param varidators)
app.url_map.converters['uuid'] = UUIDConverter
# Ensure all error responses are JSON
def _json_error(ex):
code = ex.code if isinstance(ex, wexceptions.HTTPException) else 500
response = {
'code': code
}
if code == 405:
response['type'] = 'invalid_method'
response = flask.jsonify(**response)
response.status_code = code
return response
for code in six.iterkeys(wexceptions.default_exceptions):
app.register_error_handler(code, _json_error)
# TODO(kiall): Ideally, we want to make use of the Plugin class here.
# This works for the moment though.
def _register_blueprint(ext):
app.register_blueprint(ext.plugin)
# Add all in-built APIs
mgr = extension.ExtensionManager('designate.api.v1')
mgr.map(_register_blueprint)
# Add any (enabled) optional extensions
extensions = cfg.CONF['service:api'].enabled_extensions_v1
if len(extensions) > 0:
extmgr = named.NamedExtensionManager('designate.api.v1.extensions',
names=extensions)
extmgr.map(_register_blueprint)
return app
class UUIDConverter(BaseConverter):
"""Validates UUID URL parameters"""
def to_python(self, value):
if not utils.is_uuid_like(value):
raise ValidationError()
return value
def to_url(self, value):
return str(value)
def load_values(request, valid_keys):
"""Load valid attributes from request"""
result = {}
error_keys = []
values = request.json
for k in values:
if k in valid_keys:
result[k] = values[k]
else:
error_keys.append(k)
if error_keys:
error_msg = 'Provided object does not match schema. Keys {0} are not \
valid in the request body', error_keys
raise exceptions.InvalidObject(error_msg)
return result
# Copyright 2012 Managed I.T.
#
# Author: Kiall Mac Innes <kiall@managedit.ie>
#
# 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.
import flask
from oslo_log import log as logging
from designate import schema
from designate.api.v1 import load_values
from designate.central import rpcapi as central_rpcapi
from designate.i18n import _LI
from designate import objects
LOG = logging.getLogger(__name__)
blueprint = flask.Blueprint('domains', __name__)
domain_schema = schema.Schema('v1', 'domain')
domains_schema = schema.Schema('v1', 'domains')
servers_schema = schema.Schema('v1', 'servers')
def _pool_ns_record_to_server(pool_ns_record):
server_values = {
'id': pool_ns_record.id,
'created_at': pool_ns_record.created_at,
'updated_at': pool_ns_record.updated_at,
'version': pool_ns_record.version,
'name': pool_ns_record.hostname
}
return objects.Server.from_dict(server_values)
@blueprint.route('/schemas/domain', methods=['GET'])
def get_domain_schema():
return flask.jsonify(domain_schema.raw)
@blueprint.route('/schemas/domains', methods=['GET'])
def get_domains_schema():
return flask.jsonify(domains_schema.raw)
@blueprint.route('/domains', methods=['POST'])
def create_domain():
valid_attributes = ['name', 'email', 'ttl', 'description']
context = flask.request.environ.get('context')
values = load_values(flask.request, valid_attributes)
domain_schema.validate(values)
central_api = central_rpcapi.CentralAPI.get_instance()
# A V1 zone only supports being a primary (No notion of a type)
values['type'] = 'PRIMARY'
domain = central_api.create_zone(context, objects.Zone(**values))
LOG.info(_LI("Created %(zone)s"), {'zone': domain})
response = flask.jsonify(domain_schema.filter(domain))
response.status_int = 201
response.location = flask.url_for('.get_domain', domain_id=domain['id'])
return response
@blueprint.route('/domains', methods=['GET'])
def get_domains():
"""List existing zones except those flagged for deletion
"""
context = flask.request.environ.get('context')
central_api = central_rpcapi.CentralAPI.get_instance()
domains = central_api.find_zones(context, criterion={"type": "PRIMARY",
"action": "!DELETE"})
LOG.info(_LI("Retrieved %(zones)s"), {'zones': domains})
return flask.jsonify(domains_schema.filter({'domains': domains}))
@blueprint.route('/domains/<uuid:domain_id>', methods=['GET'])
def get_domain(domain_id):
"""Return zone data unless the zone is flagged for purging
"""
context = flask.request.environ.get('context')
central_api = central_rpcapi.CentralAPI.get_instance()
criterion = {"id": domain_id, "type": "PRIMARY", "action": "!DELETE"}
domain = central_api.find_zone(context, criterion=criterion)
LOG.info(_LI("Retrieved %(zone)s"), {'zone': domain})
return flask.jsonify(domain_schema.filter(domain))
@blueprint.route('/domains/<uuid:domain_id>', methods=['PUT'])
def update_domain(domain_id):
context = flask.request.environ.get('context')
values = flask.request.json
central_api = central_rpcapi.CentralAPI.get_instance()
# Fetch the existing resource
criterion = {"id": domain_id, "type": "PRIMARY", "action": "!DELETE"}
domain = central_api.find_zone(context, criterion=criterion)
# Prepare a dict of fields for validation
domain_data = domain_schema.filter(domain)
domain_data.update(values)
# Validate the new set of data
domain_schema.validate(domain_data)
# Update and persist the resource
domain.update(values)
domain = central_api.update_zone(context, domain)
LOG.info(_LI("Updated %(zone)s"), {'zone': domain})
return flask.jsonify(domain_schema.filter(domain))
@blueprint.route('/domains/<uuid:domain_id>', methods=['DELETE'])
def delete_domain(domain_id):
context = flask.request.environ.get('context')
central_api = central_rpcapi.CentralAPI.get_instance()
# TODO(ekarlso): Fix this to something better.
criterion = {"id": domain_id, "type": "PRIMARY", "action": "!DELETE"}
central_api.find_zone(context, criterion=criterion)
domain = central_api.delete_zone(context, domain_id)
LOG.info(_LI("Deleted %(zone)s"), {'zone': domain})
return flask.Response(status=200)
@blueprint.route('/domains/<uuid:domain_id>/servers', methods=['GET'])
def get_domain_servers(domain_id):
context = flask.request.environ.get('context')
central_api = central_rpcapi.CentralAPI.get_instance()
# TODO(ekarlso): Fix this to something better.
criterion = {"id": domain_id, "type": "PRIMARY", "action": "!DELETE"}
central_api.find_zone(context, criterion=criterion)
nameservers = central_api.get_zone_ns_records(context, domain_id)
servers = objects.ServerList()
for ns in nameservers:
servers.append(_pool_ns_record_to_server(ns))
return flask.jsonify(servers_schema.filter({'servers': servers}))
# Copyright 2012 Hewlett-Packard Development Company, L.P. All Rights Reserved.
#
# Author: Kiall Mac Innes <kiall@hpe.com>
#
# 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.
import flask
import oslo_messaging as messaging
from designate import rpc
blueprint = flask.Blueprint('diagnostics', __name__)
@blueprint.route('/diagnostics/ping/<topic>/<host>', methods=['GET'])
def ping_host(topic, host):
context = flask.request.environ.get('context')
client = rpc.get_client(messaging.Target(topic=topic))
cctxt = client.prepare(server=host, timeout=10)
pong = cctxt.call(context, 'ping')
return flask.jsonify(pong)
# Copyright 2012 Hewlett-Packard Development Company, L.P.
#
# Author: Kiall Mac Innes <kiall@hpe.com>
#
# 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.
import flask
from designate.central import rpcapi as central_rpcapi
central_api = central_rpcapi.CentralAPI()
blueprint = flask.Blueprint('quotas', __name__)
KEYS_TO_SWAP = {
'zones': 'domains',
'zone_records': 'domain_records',
'zone_recordsets': 'domain_recordsets',
'recordset_records': 'recordset_records',
'api_export_size': 'api_export_size',
}
KEYS_TO_SWAP_REVERSE = {
'domains': 'zones',
'domain_records': 'zone_records',
'domain_recordsets': 'zone_recordsets',
'recordset_records': 'recordset_records',
'api_export_size': 'api_export_size',
}
def swap_keys(quotas, reverse=False):
if reverse:
quotas = {KEYS_TO_SWAP_REVERSE[k]: quotas[k] for k in quotas}
else:
quotas = {KEYS_TO_SWAP[k]: quotas[k] for k in quotas}
return quotas
@blueprint.route('/quotas/<tenant_id>', methods=['GET'])
def get_quotas(tenant_id):
context = flask.request.environ.get('context')
quotas = central_api.get_quotas(context, tenant_id)
quotas = swap_keys(quotas)
return flask.jsonify(quotas)
@blueprint.route('/quotas/<tenant_id>', methods=['PUT', 'POST'])
def set_quota(tenant_id):
context = flask.request.environ.get('context')
values = flask.request.json
values = swap_keys(values, reverse=True)
for resource, hard_limit in values.items():
central_api.set_quota(context, tenant_id, resource, hard_limit)
quotas = central_api.get_quotas(context, tenant_id)
quotas = swap_keys(quotas)
return flask.jsonify(quotas)
@blueprint.route('/quotas/<tenant_id>', methods=['DELETE'])
def reset_quotas(tenant_id):
context = flask.request.environ.get('context')
central_api.reset_quotas(context, tenant_id)
return flask.Response(status=200)
# Copyright 2012 Hewlett-Packard Development Company, L.P. All Rights Reserved.
#
# Author: Simon McCartney <simon.mccartney@hpe.com>
#
# 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.
import flask
from designate.central import rpcapi as central_rpcapi
central_api = central_rpcapi.CentralAPI()
blueprint = flask.Blueprint('reports', __name__)
@blueprint.route('/reports/tenants', methods=['GET'])
def reports_tenants():
context = flask.request.environ.get('context')
tenants = central_api.find_tenants(context)
return flask.jsonify(tenants=tenants)
@blueprint.route('/reports/tenants/<tenant_id>', methods=['GET'])
def reports_tenant(tenant_id):
context = flask.request.environ.get('context')
tenant = central_api.get_tenant(context, tenant_id)
return flask.jsonify(tenant)
@blueprint.route('/reports/counts', methods=['GET'])
def reports_counts():
context = flask.request.environ.get('context')
tenants = central_api.count_tenants(context)
domains = central_api.count_zones(context)
records = central_api.count_records(context)
return flask.jsonify(tenants=tenants, domains=domains, records=records)
@blueprint.route('/reports/counts/tenants', methods=['GET'])
def reports_counts_tenants():
context = flask.request.environ.get('context')
count = central_api.count_tenants(context)
return flask.jsonify(tenants=count)
@blueprint.route('/reports/counts/domains', methods=['GET'])
def reports_counts_domains():
context = flask.request.environ.get('context')
count = central_api.count_zones(context)
return flask.jsonify(domains=count)
@blueprint.route('/reports/counts/records', methods=['GET'])
def reports_counts_records():
context = flask.request.environ.get('context')
count = central_api.count_records(context)
return flask.jsonify(records=count)
# Copyright 2012 Hewlett-Packard Development Company, L.P. All Rights Reserved.
#
# Author: Kiall Mac Innes <kiall@hpe.com>
#
# 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.
import flask
from designate.central import rpcapi as central_rpcapi
central_api = central_rpcapi.CentralAPI()
blueprint = flask.Blueprint('sync', __name__)
@blueprint.route('/domains/sync', methods=['POST'])
def sync_domains():
context = flask.request.environ.get('context')
central_api.sync_zones(context)
return flask.Response(status=200)
@blueprint.route('/domains/<uuid:domain_id>/sync', methods=['POST'])
def sync_domain(domain_id):
context = flask.request.environ.get('context')
central_api.sync_zone(context, domain_id)
return flask.Response(status=200)
@blueprint.route('/domains/<uuid:domain_id>/records/<uuid:record_id>/sync',
methods=['POST'])
def sync_record(domain_id, record_id):
context = flask.request.environ.get('context')
record = central_api.find_record(context, {'id': record_id})
central_api.sync_record(context, domain_id, record['recordset_id'],
record_id)
return flask.Response(status=200)
# Copyright 2013 Hewlett-Packard Development Company, L.P.
#
# Author: Kiall Mac Innes <kiall@hpe.com>
#
# 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.
import flask
from designate.central import rpcapi as central_rpcapi
central_api = central_rpcapi.CentralAPI()
blueprint = flask.Blueprint('touch', __name__)
@blueprint.route('/domains/<uuid:domain_id>/touch', methods=['POST'])
def touch_domain(domain_id):
context = flask.request.environ.get('context')
central_api.touch_zone(context, domain_id)
return flask.Response(status=200)