Merge "Remove v1 API"
This commit is contained in:
commit
037272bf62
|
@ -1,16 +1,6 @@
|
|||
{
|
||||
"versions": {
|
||||
"values": [
|
||||
{
|
||||
"id": "v1",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://127.0.0.1:9001/v1",
|
||||
"rel": "self"
|
||||
}
|
||||
],
|
||||
"status": "DEPRECATED"
|
||||
},
|
||||
{
|
||||
"id": "v2",
|
||||
"links": [
|
||||
|
|
|
@ -1,362 +0,0 @@
|
|||
# Copyright (C) 2014 Red Hat, Inc.
|
||||
#
|
||||
# Author: Rich Megginson <rmeggins@redhat.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 sys
|
||||
import logging
|
||||
import pprint
|
||||
import json
|
||||
import copy
|
||||
|
||||
import requests
|
||||
from oslo_config import cfg
|
||||
|
||||
from designate.backend import impl_ipa
|
||||
from designate.i18n import _LI
|
||||
from designate.i18n import _LW
|
||||
from designate.i18n import _LE
|
||||
from designate import utils
|
||||
|
||||
|
||||
logging.basicConfig()
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
cfg.CONF.import_opt('api_base_uri', 'designate.api', 'service:api')
|
||||
cfg.CONF.import_opt('backend_driver', 'designate.central', 'service:central')
|
||||
|
||||
|
||||
class NoNameServers(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class AddServerError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class DeleteServerError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class AddDomainError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class DeleteDomainError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class AddRecordError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
cuiberrorstr = """ERROR: You cannot have Designate configured
|
||||
to use the IPA backend when running this script. It will wipe
|
||||
out your IPA DNS data. Please follow these steps:
|
||||
* shutdown designate-central
|
||||
* edit designate.conf
|
||||
[service:central]
|
||||
backend_driver = fake # or something other than ipa
|
||||
* restart designate-central and other designate services
|
||||
"""
|
||||
|
||||
|
||||
class CannotUseIPABackend(Exception):
|
||||
pass
|
||||
|
||||
|
||||
# create mapping of ipa record types to designate types
|
||||
iparectype2designate = {}
|
||||
for rectype, tup in list(impl_ipa.rectype2iparectype.items()):
|
||||
iparectype = tup[0]
|
||||
iparectype2designate[iparectype] = rectype
|
||||
|
||||
|
||||
# using the all: True flag returns fields we can't use
|
||||
# strip these keys from zones
|
||||
zoneskips = ['dn', 'nsrecord', 'idnszoneactive', 'objectclass']
|
||||
|
||||
|
||||
def rec2des(rec, zonename):
|
||||
"""Convert an IPA record to Designate format. A single IPA record
|
||||
returned from the search may translate into multiple Designate.
|
||||
IPA dnsrecord_find returns a "name". Each DNS name may contain
|
||||
multiple record types. Each record type may contain multiple
|
||||
values. Each one of these values must be added separately to
|
||||
Designate. This function returns all of those as a list of
|
||||
dict designate records.
|
||||
"""
|
||||
# convert record name
|
||||
if rec['idnsname'][0] == '@':
|
||||
name = zonename
|
||||
else:
|
||||
name = rec['idnsname'][0] + "." + zonename
|
||||
# find all record types
|
||||
rectypes = []
|
||||
for k in rec:
|
||||
if k.endswith("record"):
|
||||
if k in iparectype2designate:
|
||||
rectypes.append(k)
|
||||
else:
|
||||
LOG.info(_LI("Skipping unknown record type "
|
||||
"%(type)s in %(name)s"),
|
||||
{'type': k, 'name': name})
|
||||
|
||||
desrecs = []
|
||||
for rectype in rectypes:
|
||||
dtype = iparectype2designate[rectype]
|
||||
for ddata in rec[rectype]:
|
||||
desreq = {'name': name, 'type': dtype}
|
||||
if dtype == 'SRV' or dtype == 'MX':
|
||||
# split off the priority and send in a separate field
|
||||
idx = ddata.find(' ')
|
||||
desreq['priority'] = int(ddata[:idx])
|
||||
if dtype == 'SRV' and not ddata.endswith("."):
|
||||
# if server is specified as relative, add zonename
|
||||
desreq['data'] = ddata[(idx + 1):] + "." + zonename
|
||||
else:
|
||||
desreq['data'] = ddata[(idx + 1):]
|
||||
else:
|
||||
desreq['data'] = ddata
|
||||
if rec.get('description', [None])[0]:
|
||||
desreq['description'] = rec.get('description')[0]
|
||||
if rec.get('ttl', [None])[0]:
|
||||
desreq['ttl'] = int(rec['dnsttl'][0])
|
||||
desrecs.append(desreq)
|
||||
return desrecs
|
||||
|
||||
|
||||
def zone2des(ipazone):
|
||||
# next, try to add the fake domain to Designate
|
||||
zonename = ipazone['idnsname'][0].rstrip(".") + "."
|
||||
email = ipazone['idnssoarname'][0].rstrip(".").replace(".", "@", 1)
|
||||
desreq = {"name": zonename,
|
||||
"ttl": int(ipazone['idnssoarefresh'][0]),
|
||||
"email": email}
|
||||
return desreq
|
||||
|
||||
|
||||
def getipadomains(ipabackend, version):
|
||||
# get the list of domains/zones from IPA
|
||||
ipareq = {'method': 'dnszone_find',
|
||||
'params': [[], {'version': version,
|
||||
'all': True}]}
|
||||
iparesp = ipabackend._call_and_handle_error(ipareq)
|
||||
LOG.debug("Response: %s" % pprint.pformat(iparesp))
|
||||
return iparesp['result']['result']
|
||||
|
||||
|
||||
def getiparecords(ipabackend, zonename, version):
|
||||
ipareq = {'method': 'dnsrecord_find',
|
||||
'params': [[zonename], {"version": version,
|
||||
"all": True}]}
|
||||
iparesp = ipabackend._call_and_handle_error(ipareq)
|
||||
return iparesp['result']['result']
|
||||
|
||||
|
||||
def syncipaservers2des(servers, designatereq, designateurl):
|
||||
# get existing servers from designate
|
||||
dservers = {}
|
||||
srvurl = designateurl + "/servers"
|
||||
resp = designatereq.get(srvurl)
|
||||
LOG.debug("Response: %s" % pprint.pformat(resp.json()))
|
||||
if resp and resp.status_code == 200 and resp.json() and \
|
||||
'servers' in resp.json():
|
||||
for srec in resp.json()['servers']:
|
||||
dservers[srec['name']] = srec['id']
|
||||
else:
|
||||
LOG.warning(_LW("No servers in designate"))
|
||||
|
||||
# first - add servers from ipa not already in designate
|
||||
for server in servers:
|
||||
if server in dservers:
|
||||
LOG.info(_LI("Skipping ipa server %s already in designate"),
|
||||
server)
|
||||
else:
|
||||
desreq = {"name": server}
|
||||
resp = designatereq.post(srvurl, data=json.dumps(desreq))
|
||||
LOG.debug("Response: %s" % pprint.pformat(resp.json()))
|
||||
if resp.status_code == 200:
|
||||
LOG.info(_LI("Added server %s to designate"), server)
|
||||
else:
|
||||
raise AddServerError("Unable to add %s: %s" %
|
||||
(server, pprint.pformat(resp.json())))
|
||||
|
||||
# next - delete servers in designate not in ipa
|
||||
for server, sid in list(dservers.items()):
|
||||
if server not in servers:
|
||||
delresp = designatereq.delete(srvurl + "/" + sid)
|
||||
if delresp.status_code == 200:
|
||||
LOG.info(_LI("Deleted server %s"), server)
|
||||
else:
|
||||
raise DeleteServerError("Unable to delete %s: %s" %
|
||||
(server,
|
||||
pprint.pformat(delresp.json())))
|
||||
|
||||
|
||||
def main():
|
||||
# HACK HACK HACK - allow required config params to be passed
|
||||
# via the command line
|
||||
cfg.CONF['service:api']._group._opts['api_base_uri']['cli'] = True
|
||||
for optdict in cfg.CONF['backend:ipa']._group._opts.values():
|
||||
if 'cli' in optdict:
|
||||
optdict['cli'] = True
|
||||
# HACK HACK HACK - allow api url to be passed in the usual way
|
||||
utils.read_config('designate', sys.argv)
|
||||
if cfg.CONF['service:central'].backend_driver == 'ipa':
|
||||
raise CannotUseIPABackend(cuiberrorstr)
|
||||
if cfg.CONF.debug:
|
||||
LOG.setLevel(logging.DEBUG)
|
||||
else:
|
||||
LOG.setLevel(logging.INFO)
|
||||
ipabackend = impl_ipa.IPABackend(None)
|
||||
ipabackend.start()
|
||||
version = cfg.CONF['backend:ipa'].ipa_version
|
||||
designateurl = cfg.CONF['service:api'].api_base_uri + "v1"
|
||||
|
||||
# get the list of domains/zones from IPA
|
||||
ipazones = getipadomains(ipabackend, version)
|
||||
# get unique list of name servers
|
||||
servers = {}
|
||||
for zonerec in ipazones:
|
||||
for nsrec in zonerec['nsrecord']:
|
||||
servers[nsrec] = nsrec
|
||||
if not servers:
|
||||
raise NoNameServers("Error: no name servers found in IPA")
|
||||
|
||||
# let's see if designate is using the IPA backend
|
||||
# create a fake domain in IPA
|
||||
# create a fake server in Designate
|
||||
# try to create the same fake domain in Designate
|
||||
# if we get a DuplicateZone error from Designate, then
|
||||
# raise the CannotUseIPABackend error, after deleting
|
||||
# the fake server and fake domain
|
||||
# find the first non-reverse zone
|
||||
zone = {}
|
||||
for zrec in ipazones:
|
||||
if not zrec['idnsname'][0].endswith("in-addr.arpa.") and \
|
||||
zrec['idnszoneactive'][0] == 'TRUE':
|
||||
# ipa returns every data field as a list
|
||||
# convert the list to a scalar
|
||||
for n, v in list(zrec.items()):
|
||||
if n in zoneskips:
|
||||
continue
|
||||
if isinstance(v, list):
|
||||
zone[n] = v[0]
|
||||
else:
|
||||
zone[n] = v
|
||||
break
|
||||
|
||||
assert(zone)
|
||||
|
||||
# create a fake subdomain of this zone
|
||||
domname = "%s.%s" % (utils.generate_uuid(), zone['idnsname'])
|
||||
args = copy.copy(zone)
|
||||
del args['idnsname']
|
||||
args['version'] = version
|
||||
ipareq = {'method': 'dnszone_add',
|
||||
'params': [[domname], args]}
|
||||
iparesp = ipabackend._call_and_handle_error(ipareq)
|
||||
LOG.debug("Response: %s" % pprint.pformat(iparesp))
|
||||
if iparesp['error']:
|
||||
raise AddDomainError(pprint.pformat(iparesp))
|
||||
|
||||
# set up designate connection
|
||||
designatereq = requests.Session()
|
||||
xtra_hdrs = {'Content-Type': 'application/json'}
|
||||
designatereq.headers.update(xtra_hdrs)
|
||||
|
||||
# sync ipa name servers to designate
|
||||
syncipaservers2des(servers, designatereq, designateurl)
|
||||
|
||||
domainurl = designateurl + "/domains"
|
||||
# next, try to add the fake domain to Designate
|
||||
email = zone['idnssoarname'].rstrip(".").replace(".", "@", 1)
|
||||
desreq = {"name": domname,
|
||||
"ttl": int(zone['idnssoarefresh'][0]),
|
||||
"email": email}
|
||||
resp = designatereq.post(domainurl, data=json.dumps(desreq))
|
||||
exc = None
|
||||
fakezoneid = None
|
||||
if resp.status_code == 200:
|
||||
LOG.info(_LI("Added domain %s"), domname)
|
||||
fakezoneid = resp.json()['id']
|
||||
delresp = designatereq.delete(domainurl + "/" + fakezoneid)
|
||||
if delresp.status_code != 200:
|
||||
LOG.error(_LE("Unable to delete %(name)s: %(response)s") %
|
||||
{'name': domname, 'response': pprint.pformat(
|
||||
delresp.json())})
|
||||
else:
|
||||
exc = CannotUseIPABackend(cuiberrorstr)
|
||||
|
||||
# cleanup fake stuff
|
||||
ipareq = {'method': 'dnszone_del',
|
||||
'params': [[domname], {'version': version}]}
|
||||
iparesp = ipabackend._call_and_handle_error(ipareq)
|
||||
LOG.debug("Response: %s" % pprint.pformat(iparesp))
|
||||
if iparesp['error']:
|
||||
LOG.error(_LE("%s") % pprint.pformat(iparesp))
|
||||
|
||||
if exc:
|
||||
raise exc
|
||||
|
||||
# get and delete existing domains
|
||||
resp = designatereq.get(domainurl)
|
||||
LOG.debug("Response: %s" % pprint.pformat(resp.json()))
|
||||
if resp and resp.status_code == 200 and resp.json() and \
|
||||
'domains' in resp.json():
|
||||
# domains must be deleted in child/parent order i.e. delete
|
||||
# sub-domains before parent domains - simple way to get this
|
||||
# order is to sort the domains in reverse order of name len
|
||||
dreclist = sorted(resp.json()['domains'],
|
||||
key=lambda drec: len(drec['name']),
|
||||
reverse=True)
|
||||
for drec in dreclist:
|
||||
delresp = designatereq.delete(domainurl + "/" + drec['id'])
|
||||
if delresp.status_code != 200:
|
||||
raise DeleteDomainError("Unable to delete %s: %s" %
|
||||
(drec['name'],
|
||||
pprint.pformat(delresp.json())))
|
||||
|
||||
# key is zonename, val is designate rec id
|
||||
zonerecs = {}
|
||||
for zonerec in ipazones:
|
||||
desreq = zone2des(zonerec)
|
||||
resp = designatereq.post(domainurl, data=json.dumps(desreq))
|
||||
if resp.status_code == 200:
|
||||
LOG.info(_LI("Added domain %s"), desreq['name'])
|
||||
else:
|
||||
raise AddDomainError("Unable to add domain %s: %s" %
|
||||
(desreq['name'], pprint.pformat(resp.json())))
|
||||
zonerecs[desreq['name']] = resp.json()['id']
|
||||
|
||||
# get the records for each zone
|
||||
for zonename, domainid in list(zonerecs.items()):
|
||||
recurl = designateurl + "/domains/" + domainid + "/records"
|
||||
iparecs = getiparecords(ipabackend, zonename, version)
|
||||
for rec in iparecs:
|
||||
desreqs = rec2des(rec, zonename)
|
||||
for desreq in desreqs:
|
||||
resp = designatereq.post(recurl, data=json.dumps(desreq))
|
||||
if resp.status_code == 200:
|
||||
LOG.info(_LI("Added record %(record)s "
|
||||
"for domain %(domain)s"),
|
||||
{'record': desreq['name'], 'domain': zonename})
|
||||
else:
|
||||
raise AddRecordError("Could not add record %s: %s" %
|
||||
(desreq['name'],
|
||||
pprint.pformat(resp.json())))
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
|
@ -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)
|
||||
|
|
|
@ -1,149 +0,0 @@
|
|||
# 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
|
|
@ -1,173 +0,0 @@
|
|||
# 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}))
|
|
@ -1,34 +0,0 @@
|
|||
# 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)
|
|
@ -1,83 +0,0 @@
|
|||
# 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)
|
|
@ -1,78 +0,0 @@
|
|||
# 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)
|
|
@ -1,52 +0,0 @@
|
|||
# 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)
|
|
@ -1,31 +0,0 @@
|
|||
# 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)
|
|
@ -1,46 +0,0 @@
|
|||
# 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 designate import schema
|
||||
from designate.central import rpcapi as central_rpcapi
|
||||
|
||||
|
||||
blueprint = flask.Blueprint('limits', __name__)
|
||||
limits_schema = schema.Schema('v1', 'limits')
|
||||
|
||||
|
||||
@blueprint.route('/schemas/limits', methods=['GET'])
|
||||
def get_limits_schema():
|
||||
return flask.jsonify(limits_schema.raw)
|
||||
|
||||
|
||||
@blueprint.route('/limits', methods=['GET'])
|
||||
def get_limits():
|
||||
context = flask.request.environ.get('context')
|
||||
|
||||
central_api = central_rpcapi.CentralAPI.get_instance()
|
||||
|
||||
absolute_limits = central_api.get_absolute_limits(context)
|
||||
|
||||
return flask.jsonify(limits_schema.filter({
|
||||
"limits": {
|
||||
"absolute": {
|
||||
"maxDomains": absolute_limits['zones'],
|
||||
"maxDomainRecords": absolute_limits['zone_records']
|
||||
}
|
||||
}
|
||||
}))
|
|
@ -1,277 +0,0 @@
|
|||
# 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.central import rpcapi as central_rpcapi
|
||||
from designate import exceptions
|
||||
from designate import objects
|
||||
from designate import schema
|
||||
from designate import utils
|
||||
from designate.i18n import _LI
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
blueprint = flask.Blueprint('records', __name__)
|
||||
record_schema = schema.Schema('v1', 'record')
|
||||
records_schema = schema.Schema('v1', 'records')
|
||||
|
||||
|
||||
def _find_recordset(context, domain_id, name, type):
|
||||
central_api = central_rpcapi.CentralAPI.get_instance()
|
||||
|
||||
return central_api.find_recordset(context, {
|
||||
'zone_id': domain_id,
|
||||
'name': name,
|
||||
'type': type,
|
||||
})
|
||||
|
||||
|
||||
def _find_or_create_recordset(context, domain_id, name, type, ttl):
|
||||
central_api = central_rpcapi.CentralAPI.get_instance()
|
||||
|
||||
criterion = {"id": domain_id, "type": "PRIMARY", "action": "!DELETE"}
|
||||
central_api.find_zone(context, criterion=criterion)
|
||||
|
||||
try:
|
||||
# Attempt to create an empty recordset
|
||||
values = {
|
||||
'name': name,
|
||||
'type': type,
|
||||
'ttl': ttl,
|
||||
}
|
||||
|
||||
recordset = central_api.create_recordset(
|
||||
context, domain_id, objects.RecordSet(**values))
|
||||
|
||||
except exceptions.DuplicateRecordSet:
|
||||
# Fetch the existing recordset
|
||||
recordset = _find_recordset(context, domain_id, name, type)
|
||||
|
||||
return recordset
|
||||
|
||||
|
||||
def _extract_record_values(values):
|
||||
record_values = dict((k, values[k]) for k in ('data', 'description',)
|
||||
if k in values)
|
||||
if values.get('priority', None) is not None:
|
||||
record_values['data'] = '%d %s' % (
|
||||
values['priority'], record_values['data'])
|
||||
return record_values
|
||||
|
||||
|
||||
def _extract_recordset_values(values):
|
||||
recordset_values = ('name', 'type', 'ttl',)
|
||||
return dict((k, values[k]) for k in recordset_values if k in values)
|
||||
|
||||
|
||||
def _format_record_v1(record, recordset):
|
||||
record = dict(record)
|
||||
|
||||
record['priority'], record['data'] = utils.extract_priority_from_data(
|
||||
recordset.type, record)
|
||||
|
||||
record['domain_id'] = record['zone_id']
|
||||
|
||||
del record['zone_id']
|
||||
|
||||
record.update({
|
||||
'name': recordset['name'],
|
||||
'type': recordset['type'],
|
||||
'ttl': recordset['ttl'],
|
||||
})
|
||||
|
||||
return record
|
||||
|
||||
|
||||
@blueprint.route('/schemas/record', methods=['GET'])
|
||||
def get_record_schema():
|
||||
return flask.jsonify(record_schema.raw)
|
||||
|
||||
|
||||
@blueprint.route('/schemas/records', methods=['GET'])
|
||||
def get_records_schema():
|
||||
return flask.jsonify(records_schema.raw)
|
||||
|
||||
|
||||
@blueprint.route('/domains/<uuid:domain_id>/records', methods=['POST'])
|
||||
def create_record(domain_id):
|
||||
context = flask.request.environ.get('context')
|
||||
values = flask.request.json
|
||||
|
||||
record_schema.validate(values)
|
||||
|
||||
if values['type'] == 'SOA':
|
||||
raise exceptions.BadRequest('SOA records cannot be manually created.')
|
||||
|
||||
recordset = _find_or_create_recordset(context,
|
||||
domain_id,
|
||||
values['name'],
|
||||
values['type'],
|
||||
values.get('ttl', None))
|
||||
|
||||
record = objects.Record(**_extract_record_values(values))
|
||||
|
||||
central_api = central_rpcapi.CentralAPI.get_instance()
|
||||
record = central_api.create_record(context, domain_id,
|
||||
recordset['id'],
|
||||
record)
|
||||
LOG.info(_LI("Created %(record)s"), {'record': record})
|
||||
|
||||
record = _format_record_v1(record, recordset)
|
||||
|
||||
response = flask.jsonify(record_schema.filter(record))
|
||||
response.status_int = 201
|
||||
response.location = flask.url_for('.get_record', domain_id=domain_id,
|
||||
record_id=record['id'])
|
||||
|
||||
return response
|
||||
|
||||
|
||||
@blueprint.route('/domains/<uuid:domain_id>/records', methods=['GET'])
|
||||
def get_records(domain_id):
|
||||
context = flask.request.environ.get('context')
|
||||
|
||||
central_api = central_rpcapi.CentralAPI.get_instance()
|
||||
|
||||
# NOTE: We need to ensure the domain actually exists, otherwise we may
|
||||
# return an empty records array instead of a domain not found
|
||||
central_api.get_zone(context, domain_id)
|
||||
|
||||
recordsets = central_api.find_recordsets(context, {'zone_id': domain_id})
|
||||
LOG.info(_LI("Retrieved %(recordsets)s"), {'recordsets': recordsets})
|
||||
|
||||
records = []
|
||||
|
||||
for rrset in recordsets:
|
||||
records.extend([_format_record_v1(r, rrset) for r in rrset.records])
|
||||
|
||||
return flask.jsonify(records_schema.filter({'records': records}))
|
||||
|
||||
|
||||
@blueprint.route('/domains/<uuid:domain_id>/records/<uuid:record_id>',
|
||||
methods=['GET'])
|
||||
def get_record(domain_id, record_id):
|
||||
context = flask.request.environ.get('context')
|
||||
|
||||
central_api = central_rpcapi.CentralAPI.get_instance()
|
||||
|
||||
# NOTE: We need to ensure the domain actually exists, otherwise we may
|
||||
# return an record not found instead of a domain not found
|
||||
central_api.get_zone(context, domain_id)
|
||||
|
||||
criterion = {'zone_id': domain_id, 'id': record_id}
|
||||
record = central_api.find_record(context, criterion)
|
||||
|
||||
recordset = central_api.get_recordset(
|
||||
context, domain_id, record['recordset_id'])
|
||||
|
||||
LOG.info(_LI("Retrieved %(recordset)s"), {'recordset': recordset})
|
||||
|
||||
record = _format_record_v1(record, recordset)
|
||||
|
||||
return flask.jsonify(record_schema.filter(record))
|
||||
|
||||
|
||||
@blueprint.route('/domains/<uuid:domain_id>/records/<uuid:record_id>',
|
||||
methods=['PUT'])
|
||||
def update_record(domain_id, record_id):
|
||||
context = flask.request.environ.get('context')
|
||||
values = flask.request.json
|
||||
|
||||
central_api = central_rpcapi.CentralAPI.get_instance()
|
||||
|
||||
# NOTE: We need to ensure the domain actually exists, otherwise we may
|
||||
# return a record not found instead of a domain not found
|
||||
criterion = {"id": domain_id, "type": "PRIMARY", "action": "!DELETE"}
|
||||
central_api.find_zone(context, criterion)
|
||||
|
||||
# Fetch the existing resource
|
||||
# NOTE(kiall): We use "find_record" rather than "get_record" as we do not
|
||||
# have the recordset_id.
|
||||
criterion = {'zone_id': domain_id, 'id': record_id}
|
||||
record = central_api.find_record(context, criterion)
|
||||
|
||||
# TODO(graham): Move this further down the stack
|
||||
if record.managed and not context.edit_managed_records:
|
||||
raise exceptions.BadRequest('Managed records may not be updated')
|
||||
|
||||
# Find the associated recordset
|
||||
recordset = central_api.get_recordset(
|
||||
context, domain_id, record.recordset_id)
|
||||
|
||||
# Prepare a dict of fields for validation
|
||||
record_data = record_schema.filter(_format_record_v1(record, recordset))
|
||||
record_data.update(values)
|
||||
|
||||
# Validate the new set of data
|
||||
record_schema.validate(record_data)
|
||||
|
||||
# Update and persist the resource
|
||||
record.update(_extract_record_values(values))
|
||||
record = central_api.update_record(context, record)
|
||||
|
||||
# Update the recordset resource (if necessary)
|
||||
recordset.update(_extract_recordset_values(values))
|
||||
if len(recordset.obj_what_changed()) > 0:
|
||||
recordset = central_api.update_recordset(context, recordset)
|
||||
LOG.info(_LI("Updated %(recordset)s"), {'recordset': recordset})
|
||||
|
||||
# Format and return the response
|
||||
record = _format_record_v1(record, recordset)
|
||||
|
||||
return flask.jsonify(record_schema.filter(record))
|
||||
|
||||
|
||||
def _delete_recordset_if_empty(context, domain_id, recordset_id):
|
||||
central_api = central_rpcapi.CentralAPI.get_instance()
|
||||
|
||||
recordset = central_api.find_recordset(context, {
|
||||
'id': recordset_id
|
||||
})
|
||||
# Make sure it's the right recordset
|
||||
if len(recordset.records) == 0:
|
||||
recordset = central_api.delete_recordset(
|
||||
context, domain_id, recordset_id)
|
||||
LOG.info(_LI("Deleted %(recordset)s"), {'recordset': recordset})
|
||||
|
||||
|
||||
@blueprint.route('/domains/<uuid:domain_id>/records/<uuid:record_id>',
|
||||
methods=['DELETE'])
|
||||
def delete_record(domain_id, record_id):
|
||||
context = flask.request.environ.get('context')
|
||||
|
||||
central_api = central_rpcapi.CentralAPI.get_instance()
|
||||
|
||||
# NOTE: We need to ensure the domain actually exists, otherwise we may
|
||||
# return a record not found instead of a domain not found
|
||||
criterion = {"id": domain_id, "type": "PRIMARY", "action": "!DELETE"}
|
||||
central_api.find_zone(context, criterion=criterion)
|
||||
|
||||
# Find the record
|
||||
criterion = {'zone_id': domain_id, 'id': record_id}
|
||||
record = central_api.find_record(context, criterion)
|
||||
|
||||
# Cannot delete a managed record via the API.
|
||||
if record['managed'] is True:
|
||||
raise exceptions.BadRequest('Managed records may not be deleted')
|
||||
|
||||
record = central_api.delete_record(
|
||||
context, domain_id, record['recordset_id'], record_id)
|
||||
LOG.info(_LI("Deleted %(record)s"), {'record': record})
|
||||
|
||||
_delete_recordset_if_empty(context, domain_id, record['recordset_id'])
|
||||
return flask.Response(status=200)
|
|
@ -1,226 +0,0 @@
|
|||
# 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_config import cfg
|
||||
from oslo_log import log as logging
|
||||
|
||||
from designate import exceptions
|
||||
from designate import schema
|
||||
from designate import objects
|
||||
from designate.i18n import _LI
|
||||
from designate.central import rpcapi as central_rpcapi
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
blueprint = flask.Blueprint('servers', __name__)
|
||||
server_schema = schema.Schema('v1', 'server')
|
||||
servers_schema = schema.Schema('v1', 'servers')
|
||||
default_pool_id = cfg.CONF['service:central'].default_pool_id
|
||||
|
||||
# Servers are no longer used. They have been replaced by nameservers, which
|
||||
# is stored as a PoolAttribute. However, the v1 server API calls still need
|
||||
# to work
|
||||
|
||||
|
||||
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/server', methods=['GET'])
|
||||
def get_server_schema():
|
||||
return flask.jsonify(server_schema.raw)
|
||||
|
||||
|
||||
@blueprint.route('/schemas/servers', methods=['GET'])
|
||||
def get_servers_schema():
|
||||
return flask.jsonify(servers_schema.raw)
|
||||
|
||||
|
||||
@blueprint.route('/servers', methods=['POST'])
|
||||
def create_server():
|
||||
context = flask.request.environ.get('context')
|
||||
values = flask.request.json
|
||||
central_api = central_rpcapi.CentralAPI.get_instance()
|
||||
# Validate against the original server schema
|
||||
server_schema.validate(values)
|
||||
|
||||
# Create a PoolNsRecord object
|
||||
pns_values = {
|
||||
'priority': 10,
|
||||
'hostname': values['name']
|
||||
}
|
||||
ns_record = objects.PoolNsRecord.from_dict(pns_values)
|
||||
|
||||
# Get the default pool
|
||||
pool = central_api.get_pool(context, default_pool_id)
|
||||
|
||||
# Add the new PoolAttribute to the pool as a nameserver
|
||||
pool.ns_records.append(ns_record)
|
||||
|
||||
try:
|
||||
# Update the pool
|
||||
updated_pool = central_api.update_pool(context, pool)
|
||||
LOG.info(_LI("Updated %(pool)s"), {'pool': pool})
|
||||
|
||||
except exceptions.DuplicatePoolAttribute:
|
||||
raise exceptions.DuplicateServer()
|
||||
|
||||
# Go through the pool.ns_records to find the right one to get the ID
|
||||
for ns in updated_pool.ns_records:
|
||||
if ns.hostname == pns_values['hostname']:
|
||||
created_ns_record = ns
|
||||
break
|
||||
|
||||
# Convert the PoolAttribute to a Server so we can validate with the
|
||||
# original schema and display
|
||||
server = _pool_ns_record_to_server(created_ns_record)
|
||||
|
||||
response = flask.jsonify(server_schema.filter(server))
|
||||
response.status_int = 201
|
||||
response.location = flask.url_for('.get_server', server_id=server['id'])
|
||||
|
||||
return response
|
||||
|
||||
|
||||
@blueprint.route('/servers', methods=['GET'])
|
||||
def get_servers():
|
||||
context = flask.request.environ.get('context')
|
||||
|
||||
central_api = central_rpcapi.CentralAPI.get_instance()
|
||||
|
||||
# Get the default pool
|
||||
pool = central_api.get_pool(context, default_pool_id)
|
||||
LOG.info(_LI("Retrieved %(pool)s"), {'pool': pool})
|
||||
|
||||
servers = objects.ServerList()
|
||||
|
||||
for ns in pool.ns_records:
|
||||
servers.append(_pool_ns_record_to_server(ns))
|
||||
|
||||
LOG.info(_LI("Retrieved %(servers)s"), {'servers': servers})
|
||||
|
||||
return flask.jsonify(servers_schema.filter({'servers': servers}))
|
||||
|
||||
|
||||
@blueprint.route('/servers/<uuid:server_id>', methods=['GET'])
|
||||
def get_server(server_id):
|
||||
context = flask.request.environ.get('context')
|
||||
|
||||
central_api = central_rpcapi.CentralAPI.get_instance()
|
||||
|
||||
# Get the default pool
|
||||
pool = central_api.get_pool(context, default_pool_id)
|
||||
LOG.info(_LI("Retrieved %(pool)s"), {'pool': pool})
|
||||
|
||||
# Create an empty PoolNsRecord object
|
||||
nameserver = objects.PoolNsRecord()
|
||||
|
||||
# Get the desired nameserver from the pool
|
||||
for ns in pool.ns_records:
|
||||
if ns.id == server_id:
|
||||
nameserver = ns
|
||||
break
|
||||
|
||||
# If the nameserver wasn't found, raise an exception
|
||||
if nameserver.id != server_id:
|
||||
raise exceptions.ServerNotFound
|
||||
|
||||
LOG.info(_LI("Retrieved %(server)s"), {'server': nameserver})
|
||||
|
||||
server = _pool_ns_record_to_server(nameserver)
|
||||
|
||||
return flask.jsonify(server_schema.filter(server))
|
||||
|
||||
|
||||
@blueprint.route('/servers/<uuid:server_id>', methods=['PUT'])
|
||||
def update_server(server_id):
|
||||
context = flask.request.environ.get('context')
|
||||
values = flask.request.json
|
||||
|
||||
central_api = central_rpcapi.CentralAPI.get_instance()
|
||||
|
||||
# Get the default pool
|
||||
pool = central_api.get_pool(context, default_pool_id)
|
||||
|
||||
# Get the Nameserver from the pool
|
||||
index = -1
|
||||
ns_records = pool.ns_records
|
||||
for ns in ns_records:
|
||||
if ns.id == server_id:
|
||||
index = ns_records.index(ns)
|
||||
break
|
||||
|
||||
if index == -1:
|
||||
raise exceptions.ServerNotFound
|
||||
|
||||
# Get the ns_record from the pool so we can update it
|
||||
nameserver = ns_records.pop(index)
|
||||
|
||||
# Update it with the new values
|
||||
nameserver.update({'hostname': values['name']})
|
||||
|
||||
# Change it to a server, so we can use the original validation. We want
|
||||
# to make sure we don't change anything in v1
|
||||
server = _pool_ns_record_to_server(nameserver)
|
||||
server_data = server_schema.filter(server)
|
||||
server_data.update(values)
|
||||
# Validate the new set of data
|
||||
server_schema.validate(server_data)
|
||||
|
||||
# Now that it's been validated, add it back to the pool and persist it
|
||||
pool.ns_records.append(nameserver)
|
||||
pool = central_api.update_pool(context, pool)
|
||||
LOG.info(_LI("Updated %(pool)s"), {'pool': pool})
|
||||
|
||||
return flask.jsonify(server_schema.filter(server))
|
||||
|
||||
|
||||
@blueprint.route('/servers/<uuid:server_id>', methods=['DELETE'])
|
||||
def delete_server(server_id):
|
||||
context = flask.request.environ.get('context')
|
||||
|
||||
central_api = central_rpcapi.CentralAPI.get_instance()
|
||||
|
||||
# Get the default pool
|
||||
pool = central_api.get_pool(context, default_pool_id)
|
||||
|
||||
# Get the Nameserver from the pool
|
||||
index = -1
|
||||
ns_records = pool.ns_records
|
||||
for ns in ns_records:
|
||||
if ns.id == server_id:
|
||||
index = ns_records.index(ns)
|
||||
break
|
||||
|
||||
if index == -1:
|
||||
raise exceptions.ServerNotFound
|
||||
|
||||
# Remove the nameserver from the pool so it will be deleted
|
||||
ns_records.pop(index)
|
||||
|
||||
# Update the pool without the deleted server
|
||||
pool = central_api.update_pool(context, pool)
|
||||
LOG.info(_LI("Updated %(pool)s"), {'pool': pool})
|
||||
|
||||
return flask.Response(status=200)
|
|
@ -1,155 +0,0 @@
|
|||
# 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 oslo_config import cfg
|
||||
|
||||
from designate import schema
|
||||
from designate.central import rpcapi as central_rpcapi
|
||||
from designate.objects import TsigKey
|
||||
from designate.i18n import _LI
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
blueprint = flask.Blueprint('tsigkeys', __name__)
|
||||
tsigkey_schema = schema.Schema('v1', 'tsigkey')
|
||||
tsigkeys_schema = schema.Schema('v1', 'tsigkeys')
|
||||
default_pool_id = cfg.CONF['service:central'].default_pool_id
|
||||
|
||||
|
||||
@blueprint.route('/schemas/tsigkey', methods=['GET'])
|
||||
def get_tsigkey_schema():
|
||||
return flask.jsonify(tsigkey_schema.raw)
|
||||
|
||||
|
||||
@blueprint.route('/schemas/tsigkeys', methods=['GET'])
|
||||
def get_tsigkeys_schema():
|
||||
return flask.jsonify(tsigkeys_schema.raw)
|
||||
|
||||
|
||||
@blueprint.route('/tsigkeys', methods=['POST'])
|
||||
def create_tsigkey():
|
||||
context = flask.request.environ.get('context')
|
||||
values = flask.request.json
|
||||
|
||||
central_api = central_rpcapi.CentralAPI.get_instance()
|
||||
|
||||
tsigkey_schema.validate(values)
|
||||
|
||||
tsigkey = TsigKey.from_dict(values)
|
||||
|
||||
# The V1 API only deals with the default pool, so we restrict the view
|
||||
# of TSIG Keys to those scoped to the default pool.
|
||||
tsigkey.scope = 'POOL'
|
||||
tsigkey.resource_id = default_pool_id
|
||||
|
||||
tsigkey = central_api.create_tsigkey(context, tsigkey)
|
||||
|
||||
LOG.info(_LI("Created %(tsigkey)s"), {'tsigkey': tsigkey})
|
||||
|
||||
response = flask.jsonify(tsigkey_schema.filter(tsigkey))
|
||||
response.status_int = 201
|
||||
response.location = flask.url_for('.get_tsigkey', tsigkey_id=tsigkey['id'])
|
||||
|
||||
return response
|
||||
|
||||
|
||||
@blueprint.route('/tsigkeys', methods=['GET'])
|
||||
def get_tsigkeys():
|
||||
context = flask.request.environ.get('context')
|
||||
|
||||
central_api = central_rpcapi.CentralAPI.get_instance()
|
||||
|
||||
criterion = {'scope': 'POOL', 'resource_id': default_pool_id}
|
||||
tsigkeys = central_api.find_tsigkeys(context, criterion)
|
||||
|
||||
LOG.info(_LI("Retrieved %(tsigkeys)s"), {'tsigkeys': tsigkeys})
|
||||
|
||||
return flask.jsonify(tsigkeys_schema.filter({'tsigkeys': tsigkeys}))
|
||||
|
||||
|
||||
@blueprint.route('/tsigkeys/<uuid:tsigkey_id>', methods=['GET'])
|
||||
def get_tsigkey(tsigkey_id):
|
||||
context = flask.request.environ.get('context')
|
||||
|
||||
central_api = central_rpcapi.CentralAPI.get_instance()
|
||||
|
||||
criterion = {
|
||||
'scope': 'POOL',
|
||||
'resource_id': default_pool_id,
|
||||
'id': tsigkey_id,
|
||||
}
|
||||
|
||||
tsigkey = central_api.find_tsigkeys(context, criterion)
|
||||
|
||||
LOG.info(_LI("Retrieved %(tsigkey)s"), {'tsigkey': tsigkey})
|
||||
|
||||
return flask.jsonify(tsigkey_schema.filter(tsigkey))
|
||||
|
||||
|
||||
@blueprint.route('/tsigkeys/<uuid:tsigkey_id>', methods=['PUT'])
|
||||
def update_tsigkey(tsigkey_id):
|
||||
context = flask.request.environ.get('context')
|
||||
values = flask.request.json
|
||||
|
||||
central_api = central_rpcapi.CentralAPI.get_instance()
|
||||
|
||||
# Fetch the existing tsigkey
|
||||
criterion = {
|
||||
'scope': 'POOL',
|
||||
'resource_id': default_pool_id,
|
||||
'id': tsigkey_id,
|
||||
}
|
||||
|
||||
tsigkey = central_api.find_tsigkey(context, criterion)
|
||||
|
||||
# Prepare a dict of fields for validation
|
||||
tsigkey_data = tsigkey_schema.filter(tsigkey)
|
||||
tsigkey_data.update(values)
|
||||
|
||||
# Validate the new set of data
|
||||
tsigkey_schema.validate(tsigkey_data)
|
||||
|
||||
# Update and persist the resource
|
||||
tsigkey.update(values)
|
||||
tsigkey = central_api.update_tsigkey(context, tsigkey)
|
||||
|
||||
LOG.info(_LI("Updated %(tsigkey)s"), {'tsigkey': tsigkey})
|
||||
|
||||
return flask.jsonify(tsigkey_schema.filter(tsigkey))
|
||||
|
||||
|
||||
@blueprint.route('/tsigkeys/<uuid:tsigkey_id>', methods=['DELETE'])
|
||||
def delete_tsigkey(tsigkey_id):
|
||||
context = flask.request.environ.get('context')
|
||||
|
||||
central_api = central_rpcapi.CentralAPI.get_instance()
|
||||
|
||||
# Fetch the existing resource, this ensures the key to be deleted has the
|
||||
# correct scope/resource_id values, otherwise it will trigger a 404.
|
||||
criterion = {
|
||||
'scope': 'POOL',
|
||||
'resource_id': default_pool_id,
|
||||
'id': tsigkey_id,
|
||||
}
|
||||
central_api.find_tsigkeys(context, criterion)
|
||||
|
||||
# Delete the TSIG Key
|
||||
tsigkey = central_api.delete_tsigkey(context, tsigkey_id)
|
||||
|
||||
LOG.info(_LI("Deleted %(tsigkey)s"), {'tsigkey': tsigkey})
|
||||
|
||||
return flask.Response(status=200)
|
|
@ -29,7 +29,6 @@ def factory(global_config, **local_conf):
|
|||
def _host_header_links():
|
||||
del versions[:]
|
||||
host_url = flask.request.host_url.rstrip('/')
|
||||
_version('v1', 'DEPRECATED', host_url)
|
||||
_version('v2', 'CURRENT', host_url)
|
||||
|
||||
def _version(version, status, base_uri):
|
||||
|
@ -42,9 +41,6 @@ def factory(global_config, **local_conf):
|
|||
}]
|
||||
})
|
||||
|
||||
if cfg.CONF['service:api'].enable_api_v1:
|
||||
_version('v1', 'DEPRECATED', base)
|
||||
|
||||
if cfg.CONF['service:api'].enable_api_v2:
|
||||
_version('v2', 'CURRENT', base)
|
||||
|
||||
|
|
|
@ -18,39 +18,6 @@ from oslo_policy import policy
|
|||
from designate.common.policies import base
|
||||
|
||||
rules = [
|
||||
policy.DocumentedRuleDefault(
|
||||
name="create_record",
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='Create record.',
|
||||
operations=[
|
||||
{
|
||||
'path': '/v1/domains/<uuid:domain_id>/records',
|
||||
'method': 'POST'
|
||||
}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name="get_records",
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='Get records.',
|
||||
operations=[
|
||||
{
|
||||
'path': '/v1/domains/<uuid:domain_id>/records',
|
||||
'method': 'GET'
|
||||
}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name="get_record",
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='Get record.',
|
||||
operations=[
|
||||
{
|
||||
'path': '/v1/domains/<uuid:domain_id>/records/<uuid:record_id>', # noqa
|
||||
'method': 'GET'
|
||||
}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name="find_records",
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
|
@ -65,45 +32,6 @@ rules = [
|
|||
}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name="find_record",
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='Find record.',
|
||||
operations=[
|
||||
{
|
||||
'path': '/v1/domains/<uuid:domain_id>/records/<uuid:record_id>', # noqa
|
||||
'method': 'GET'
|
||||
}, {
|
||||
'path': '/v1/domains/<uuid:domain_id>/records/<uuid:record_id>', # noqa
|
||||
'method': 'DELETE'
|
||||
}, {
|
||||
'path': '/v1/domains/<uuid:domain_id>/records/<uuid:record_id>', # noqa
|
||||
'method': 'PUT'
|
||||
}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name="update_record",
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='Update record.',
|
||||
operations=[
|
||||
{
|
||||
'path': '/v1/domains/<uuid:domain_id>/records/<uuid:record_id>', # noqa
|
||||
'method': 'PUT'
|
||||
}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name="delete_record",
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description='Delete record.',
|
||||
operations=[
|
||||
{
|
||||
'path': '/v1/domains/<uuid:domain_id>/records/<uuid:record_id>', # noqa
|
||||
'method': 'DELETE'
|
||||
}
|
||||
]
|
||||
),
|
||||
policy.RuleDefault(
|
||||
name="count_records",
|
||||
check_str=base.RULE_ADMIN_OR_OWNER)
|
||||
|
|
|
@ -42,12 +42,6 @@ rules = [
|
|||
description="Get recordset",
|
||||
operations=[
|
||||
{
|
||||
'path': '/v1/domains/<uuid:domain_id>/records/<uuid:record_id>', # noqa
|
||||
'method': 'GET'
|
||||
}, {
|
||||
'path': '/v1/domains/<uuid:domain_id>/records/<uuid:record_id>', # noqa
|
||||
'method': 'PUT'
|
||||
}, {
|
||||
'path': '/v2/zones/{zone_id}/recordsets/{recordset_id}',
|
||||
'method': 'GET'
|
||||
}, {
|
||||
|
@ -59,40 +53,12 @@ rules = [
|
|||
}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name="find_recordsets",
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description="Find recordsets",
|
||||
operations=[
|
||||
{
|
||||
'path': '/v1/domains/<uuid:domain_id>/records',
|
||||
'method': 'GET'
|
||||
}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name="find_recordset",
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description="Find recordset",
|
||||
operations=[
|
||||
{
|
||||
'path': '/v1/domains/<uuid:domain_id>/records',
|
||||
'method': 'POST'
|
||||
}, {
|
||||
'path': '/v1/domains/<uuid:domain_id>/records/<uuid:record_id>', # noqa
|
||||
'method': 'DELETE'
|
||||
}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name="update_recordset",
|
||||
check_str=base.RULE_ZONE_PRIMARY_OR_ADMIN,
|
||||
description="Update recordset",
|
||||
operations=[
|
||||
{
|
||||
'path': '/v1/domains/<uuid:domain_id>/records/<uuid:record_id>', # noqa
|
||||
'method': 'PUT'
|
||||
}, {
|
||||
'path': '/v2/zones/{zone_id}/recordsets/{recordset_id}',
|
||||
'method': 'PUT'
|
||||
}, {
|
||||
|
@ -107,9 +73,6 @@ rules = [
|
|||
description="Delete RecordSet",
|
||||
operations=[
|
||||
{
|
||||
'path': '/v1/domains/<uuid:domain_id>/records/<uuid:record_id>', # noqa
|
||||
'method': 'DELETE'
|
||||
}, {
|
||||
'path': '/v2/zones/{zone_id}/recordsets/{recordset_id}',
|
||||
'method': 'DELETE'
|
||||
}
|
||||
|
|
|
@ -24,9 +24,6 @@ rules = [
|
|||
description="Create Tsigkey",
|
||||
operations=[
|
||||
{
|
||||
'path': '/v1/tsigkeys',
|
||||
'method': 'POST'
|
||||
}, {
|
||||
'path': '/v2/tsigkeys',
|
||||
'method': 'POST'
|
||||
}
|
||||
|
@ -38,15 +35,6 @@ rules = [
|
|||
description="List Tsigkeys",
|
||||
operations=[
|
||||
{
|
||||
'path': '/v1/tsigkeys',
|
||||
'method': 'GET'
|
||||
}, {
|
||||
'path': '/v1/tsigkeys/<uuid:tsigkey_id>',
|
||||
'method': 'GET'
|
||||
}, {
|
||||
'path': '/v1/tsigkeys/<uuid:tsigkey_id>',
|
||||
'method': 'DELETE'
|
||||
}, {
|
||||
'path': '/v2/tsigkeys',
|
||||
'method': 'GET'
|
||||
}
|
||||
|
@ -72,9 +60,6 @@ rules = [
|
|||
description="Update Tsigkey",
|
||||
operations=[
|
||||
{
|
||||
'path': '/v1/tsigkeys/{tsigkey_id}',
|
||||
'method': 'PATCH'
|
||||
}, {
|
||||
'path': '/v2/tsigkeys/{tsigkey_id}',
|
||||
'method': 'PATCH'
|
||||
}
|
||||
|
@ -86,9 +71,6 @@ rules = [
|
|||
description="Delete a Tsigkey",
|
||||
operations=[
|
||||
{
|
||||
'path': '/v1/tsigkeys/{tsigkey_id}',
|
||||
'method': 'DELETE'
|
||||
}, {
|
||||
'path': '/v2/tsigkeys/{tsigkey_id}',
|
||||
'method': 'DELETE'
|
||||
}
|
||||
|
|
|
@ -24,9 +24,6 @@ rules = [
|
|||
description="Create Zone",
|
||||
operations=[
|
||||
{
|
||||
'path': '/v1//domains',
|
||||
'method': 'POST'
|
||||
}, {
|
||||
'path': '/v2/zones',
|
||||
'method': 'POST'
|
||||
}
|
||||
|
@ -42,12 +39,6 @@ rules = [
|
|||
description="Get Zone",
|
||||
operations=[
|
||||
{
|
||||
'path': '/v1/domains/<uuid:domain_id>/records/<uuid:record_id>', # noqa
|
||||
'method': 'GET'
|
||||
}, {
|
||||
'path': '/v1/domains/<uuid:domain_id>/records',
|
||||
'method': 'GET'
|
||||
}, {
|
||||
'path': '/v2/zones/{zone_id}',
|
||||
'method': 'GET'
|
||||
}, {
|
||||
|
@ -69,43 +60,17 @@ rules = [
|
|||
description="List existing zones",
|
||||
operations=[
|
||||
{
|
||||
'path': '/v1/domains',
|
||||
'method': 'GET'
|
||||
}, {
|
||||
'path': '/v2/zones',
|
||||
'method': 'GET'
|
||||
}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name="find_zone",
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description="Find Zone",
|
||||
operations=[
|
||||
{
|
||||
'path': '/v1/domains/<uuid:domain_id>',
|
||||
'method': 'GET'
|
||||
}, {
|
||||
'path': '/v1/domains/<uuid:domain_id>/servers',
|
||||
'method': 'GET'
|
||||
}, {
|
||||
'path': '/v1/domains/<uuid:domain_id>',
|
||||
'method': 'PUT'
|
||||
}, {
|
||||
'path': '/v1/domains/<uuid:domain_id>',
|
||||
'method': 'DELETE'
|
||||
}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name="update_zone",
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description="Update Zone",
|
||||
operations=[
|
||||
{
|
||||
'path': '/v1/domains/<uuid:domain_id>',
|
||||
'method': 'PUT'
|
||||
}, {
|
||||
'path': '/v2/zones/{zone_id}',
|
||||
'method': 'PATCH'
|
||||
}
|
||||
|
@ -117,9 +82,6 @@ rules = [
|
|||
description="Delete Zone",
|
||||
operations=[
|
||||
{
|
||||
'path': '/v1/domains/<uuid:domain_id>',
|
||||
'method': 'DELETE'
|
||||
}, {
|
||||
'path': '/v2/zones/{zone_id}',
|
||||
'method': 'DELETE'
|
||||
}
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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.
|
||||
|
||||
from designate.objects.adapters import base
|
||||
|
||||
|
||||
class APIv1Adapter(base.DesignateAdapter):
|
||||
|
||||
ADAPTER_FORMAT = 'API_v1'
|
|
@ -1,76 +0,0 @@
|
|||
{
|
||||
"id": "domain",
|
||||
|
||||
"$schema": "http://json-schema.org/draft-03/hyper-schema",
|
||||
|
||||
"title": "domain",
|
||||
"description": "Domain",
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Domain Identifier",
|
||||
"format": "uuid",
|
||||
"readonly": true
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Domain name",
|
||||
"format": "domain-name",
|
||||
"maxLength": 255,
|
||||
"required": true,
|
||||
"readonly": true
|
||||
},
|
||||
"email": {
|
||||
"type": "string",
|
||||
"description": "Hostmaster email address",
|
||||
"format": "email",
|
||||
"maxLength": 255,
|
||||
"required": true
|
||||
},
|
||||
"ttl": {
|
||||
"type": "integer",
|
||||
"description": "Time to live",
|
||||
"minimum": 1,
|
||||
"maximum": 2147483647
|
||||
},
|
||||
"serial": {
|
||||
"type": "integer",
|
||||
"description": "Serial Number",
|
||||
"minimum": 1,
|
||||
"maximum": 4294967295,
|
||||
"readonly": true
|
||||
},
|
||||
"description": {
|
||||
"type": ["string", "null"],
|
||||
"description": "Description for the Domain",
|
||||
"maxLength": 160
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"description": "Date and time of domain creation",
|
||||
"format": "date-time",
|
||||
"readonly": true
|
||||
},
|
||||
"updated_at": {
|
||||
"type": ["string", "null"],
|
||||
"description": "Date and time of last domain update",
|
||||
"format": "date-time",
|
||||
"readonly": true
|
||||
}
|
||||
},
|
||||
"links": [{
|
||||
"rel": "self",
|
||||
"href": "/domains/{id}"
|
||||
}, {
|
||||
"rel": "records",
|
||||
"href": "/domains/{id}/records"
|
||||
}, {
|
||||
"rel": "servers",
|
||||
"href": "/domains/{id}/servers"
|
||||
}, {
|
||||
"rel": "collection",
|
||||
"href": "/domains"
|
||||
}]
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
{
|
||||
"id": "domains",
|
||||
|
||||
"$schema": "http://json-schema.org/draft-03/hyper-schema",
|
||||
|
||||
"title": "domains",
|
||||
"description": "Domains",
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"domains": {
|
||||
"type": "array",
|
||||
"description": "Domains",
|
||||
"items": {"$ref": "domain#"}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
{
|
||||
"id": "fault",
|
||||
|
||||
"$schema": "http://json-schema.org/draft-03/hyper-schema",
|
||||
|
||||
"title": "fault",
|
||||
"description": "Fault",
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"description": "Fault Code",
|
||||
"required": true,
|
||||
"readonly": true
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"description": "Fault Type",
|
||||
"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
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"request_id": {
|
||||
"type": "string",
|
||||
"description": "Request ID",
|
||||
"readonly": true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
{
|
||||
"id": "limits",
|
||||
|
||||
"$schema": "http://json-schema.org/draft-03/hyper-schema",
|
||||
|
||||
"title": "limits",
|
||||
"description": "Limits",
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"limits": {
|
||||
"type": "object",
|
||||
"description": "Limits",
|
||||
"properties": {
|
||||
"absolute": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"maxDomains": {
|
||||
"type": "integer"
|
||||
},
|
||||
"maxDomainRecords": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,246 +0,0 @@
|
|||
{
|
||||
"id": "record",
|
||||
|
||||
"$schema": "http://json-schema.org/draft-03/hyper-schema",
|
||||
|
||||
"title": "record",
|
||||
"description": "Record",
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Record Identifier",
|
||||
"format": "uuid",
|
||||
"readonly": true
|
||||
},
|
||||
"domain_id": {
|
||||
"type": "string",
|
||||
"description": "Domain Identifier",
|
||||
"format": "uuid",
|
||||
"readonly": true
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "DNS Record Name",
|
||||
"format": "host-name",
|
||||
"maxLength": 255,
|
||||
"required": true
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"description": "DNS Record Type",
|
||||
"enum": ["A", "AAAA", "CNAME", "MX", "SRV", "TXT", "SPF", "NS", "PTR", "SSHFP", "SOA"],
|
||||
"required": true
|
||||
},
|
||||
"data": {
|
||||
"type": "string",
|
||||
"description": "DNS Record Value",
|
||||
"maxLength": 255,
|
||||
"required": true
|
||||
},
|
||||
"priority": {
|
||||
"type": ["integer", "null"],
|
||||
"description": "DNS Record Priority",
|
||||
"minimum": 0,
|
||||
"maximum": 65535
|
||||
},
|
||||
"ttl": {
|
||||
"type": ["integer", "null"],
|
||||
"description": "Time to live",
|
||||
"minimum": 1,
|
||||
"maximum": 2147483647
|
||||
},
|
||||
"description": {
|
||||
"type": ["string", "null"],
|
||||
"description": "Description for the record",
|
||||
"maxLength": 160
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"description": "Date and time of record creation",
|
||||
"format": "date-time",
|
||||
"readonly": true
|
||||
},
|
||||
"updated_at": {
|
||||
"type": ["string", "null"],
|
||||
"description": "Date and time of last record update",
|
||||
"format": "date-time",
|
||||
"readonly": true
|
||||
}
|
||||
},
|
||||
"oneOf": [{
|
||||
"description": "An A Record",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["A"]
|
||||
},
|
||||
"data": {
|
||||
"format": "ip-address",
|
||||
"required": true
|
||||
},
|
||||
"priority": {
|
||||
"type": "null"
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"description": "An AAAA Record",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["AAAA"]
|
||||
},
|
||||
"data": {
|
||||
"format": "ipv6",
|
||||
"required": true
|
||||
},
|
||||
"priority": {
|
||||
"type": "null"
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"description": "A CNAME Record",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["CNAME"]
|
||||
},
|
||||
"data": {
|
||||
"format": "host-name",
|
||||
"required": true
|
||||
},
|
||||
"priority": {
|
||||
"type": "null"
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"description": "A MX Record",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["MX"]
|
||||
},
|
||||
"data": {
|
||||
"format": "host-name",
|
||||
"required": true
|
||||
},
|
||||
"priority": {
|
||||
"type": "integer",
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"description": "A SRV Record",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["SRV"]
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"pattern": "^(?:_[A-Za-z0-9_\\-]{1,62}\\.){2}"
|
||||
},
|
||||
"data": {
|
||||
"type": "string",
|
||||
"pattern": "^(?:(?:6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[1-5][0-9]{4}|[1-9][0-9]{1,3}|[0-9])\\s){2}(?!.{255,})((?!\\-)[A-Za-z0-9_\\-]{1,63}(?<!\\-)\\.)+$"
|
||||
},
|
||||
"priority": {
|
||||
"type": "integer",
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"description": "A TXT Record",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["TXT"]
|
||||
},
|
||||
"priority": {
|
||||
"type": "null"
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"description": "A SPF Record",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["SPF"]
|
||||
},
|
||||
"priority": {
|
||||
"type": "null"
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"description": "A NS Record",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["NS"]
|
||||
},
|
||||
"data": {
|
||||
"format": "host-name",
|
||||
"required": true
|
||||
},
|
||||
"priority": {
|
||||
"type": "null"
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"description": "A PTR Record",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["PTR"]
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"pattern": "^(?:(?:\\d{1,3}\\.){4}in-addr\\.arpa\\.|(?:[a-f|\\d]\\.){32}ip6\\.arpa\\.)$"
|
||||
},
|
||||
"data": {
|
||||
"format": "host-name",
|
||||
"required": true
|
||||
},
|
||||
"priority": {
|
||||
"type": "null"
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"description": "A SSHFP Record",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["SSHFP"]
|
||||
},
|
||||
"data": {
|
||||
"pattern": "^[1-2] 1 [0-9A-Fa-f]{40}$",
|
||||
"required": true
|
||||
},
|
||||
"priority": {
|
||||
"type": "null"
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"description": "A SOA Record",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["SOA"]
|
||||
},
|
||||
"priority": {
|
||||
"type": "null"
|
||||
}
|
||||
}
|
||||
}],
|
||||
"links": [{
|
||||
"rel": "self",
|
||||
"href": "/domains/{domain_id}/records/{id}"
|
||||
}, {
|
||||
"rel": "domain",
|
||||
"href": "/domains/{domain_id}"
|
||||
}, {
|
||||
"rel": "collection",
|
||||
"href": "/domains/{domain_id}/records"
|
||||
}]
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
{
|
||||
"id": "records",
|
||||
|
||||
"$schema": "http://json-schema.org/draft-03/hyper-schema",
|
||||
|
||||
"title": "records",
|
||||
"description": "Records",
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"records": {
|
||||
"type": "array",
|
||||
"description": "Records",
|
||||
"items": {"$ref": "record#"}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
{
|
||||
"id": "server",
|
||||
|
||||
"$schema": "http://json-schema.org/draft-03/hyper-schema",
|
||||
|
||||
"title": "server",
|
||||
"description": "Server",
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Server Identifier",
|
||||
"format": "uuid",
|
||||
"readonly": true
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Server DNS name",
|
||||
"format": "host-name",
|
||||
"maxLength": 255,
|
||||
"required": true
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"description": "Date and time of server creation",
|
||||
"format": "date-time",
|
||||
"readonly": true
|
||||
},
|
||||
"updated_at": {
|
||||
"type": ["string", "null"],
|
||||
"description": "Date and time of last server update",
|
||||
"format": "date-time",
|
||||
"readonly": true
|
||||
}
|
||||
},
|
||||
"links": [{
|
||||
"rel": "self",
|
||||
"href": "/servers/{id}"
|
||||
}, {
|
||||
"rel": "collection",
|
||||
"href": "/servers"
|
||||
}]
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
{
|
||||
"id": "servers",
|
||||
|
||||
"$schema": "http://json-schema.org/draft-03/hyper-schema",
|
||||
|
||||
"title": "servers",
|
||||
"description": "Servers",
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"servers": {
|
||||
"type": "array",
|
||||
"description": "Servers",
|
||||
"items": {"$ref": "server#"}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
{
|
||||
"id": "tsigkey",
|
||||
|
||||
"$schema": "http://json-schema.org/draft-03/hyper-schema",
|
||||
|
||||
"title": "tsigkey",
|
||||
"description": "TSIG Key",
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "TSIG Key Identifier",
|
||||
"format": "uuid",
|
||||
"readonly": true
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "TSIG Key Name",
|
||||
"maxLength": 255,
|
||||
"required": true
|
||||
},
|
||||
"algorithm": {
|
||||
"type": "string",
|
||||
"description": "TSIG Algorithm",
|
||||
"enum": ["hmac-md5", "hmac-sha1", "hmac-sha224", "hmac-sha256", "hmac-sha384", "hmac-sha512"],
|
||||
"required": true
|
||||
},
|
||||
"secret": {
|
||||
"type": "string",
|
||||
"description": "TSIG Secret",
|
||||
"maxLength": 255,
|
||||
"required": true
|
||||
}
|
||||
},
|
||||
"links": [{
|
||||
"rel": "self",
|
||||
"href": "/tsigkeys/{id}"
|
||||
}, {
|
||||
"rel": "collection",
|
||||
"href": "/tsigkeys"
|
||||
}]
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
{
|
||||
"id": "tsigkeys",
|
||||
|
||||
"$schema": "http://json-schema.org/draft-03/hyper-schema",
|
||||
|
||||
"title": "tsigkeys",
|
||||
"description": "TSIG Keys",
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"tsigkeys": {
|
||||
"type": "array",
|
||||
"description": "TSIG Keys",
|
||||
"items": {"$ref": "tsigkey#"}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -30,11 +30,7 @@ class Schema(object):
|
|||
self.resolver = resolvers.LocalResolver.from_schema(
|
||||
version, self.raw_schema)
|
||||
|
||||
if version == 'v1':
|
||||
self.validator = validators.Draft3Validator(
|
||||
self.raw_schema, resolver=self.resolver,
|
||||
format_checker=format.draft3_format_checker)
|
||||
elif version in ['v2', 'admin']:
|
||||
if version in ['v2', 'admin']:
|
||||
self.validator = validators.Draft4Validator(
|
||||
self.raw_schema, resolver=self.resolver,
|
||||
format_checker=format.draft4_format_checker)
|
||||
|
|
|
@ -1,118 +0,0 @@
|
|||
# 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.
|
||||
from oslo_log import log as logging
|
||||
from oslo_serialization import jsonutils as json
|
||||
|
||||
from designate.api import v1 as api_v1
|
||||
from designate.api import middleware
|
||||
from designate.tests.test_api import ApiTestCase
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ApiV1Test(ApiTestCase):
|
||||
def setUp(self):
|
||||
super(ApiV1Test, self).setUp()
|
||||
|
||||
# Ensure the v1 API is enabled
|
||||
self.config(enable_api_v1=True, group='service:api')
|
||||
|
||||
# Create the application
|
||||
self.app = api_v1.factory({})
|
||||
|
||||
# Inject the NormalizeURIMiddleware middleware
|
||||
self.app.wsgi_app = middleware.NormalizeURIMiddleware(
|
||||
self.app.wsgi_app)
|
||||
|
||||
# Inject the FaultWrapper middleware
|
||||
self.app.wsgi_app = middleware.FaultWrapperMiddleware(
|
||||
self.app.wsgi_app)
|
||||
|
||||
# Inject the ValidationError middleware
|
||||
self.app.wsgi_app = middleware.APIv1ValidationErrorMiddleware(
|
||||
self.app.wsgi_app)
|
||||
|
||||
# Inject the TestAuth middleware
|
||||
self.app.wsgi_app = middleware.TestContextMiddleware(
|
||||
self.app.wsgi_app, self.admin_context.tenant,
|
||||
self.admin_context.user)
|
||||
|
||||
# Obtain a test client
|
||||
self.client = self.app.test_client()
|
||||
|
||||
def get(self, path, **kw):
|
||||
expected_status_code = kw.pop('status_code', 200)
|
||||
|
||||
resp = self.client.get(path=path)
|
||||
|
||||
LOG.debug('Response Body: %r' % resp.data)
|
||||
|
||||
self.assertEqual(expected_status_code, resp.status_code)
|
||||
|
||||
try:
|
||||
resp.json = json.loads(resp.data)
|
||||
except ValueError:
|
||||
resp.json = None
|
||||
|
||||
return resp
|
||||
|
||||
def post(self, path, data, content_type="application/json", **kw):
|
||||
expected_status_code = kw.pop('status_code', 200)
|
||||
|
||||
content = json.dumps(data)
|
||||
resp = self.client.post(path=path, content_type=content_type,
|
||||
data=content)
|
||||
|
||||
LOG.debug('Response Body: %r' % resp.data)
|
||||
|
||||
self.assertEqual(expected_status_code, resp.status_code)
|
||||
|
||||
try:
|
||||
resp.json = json.loads(resp.data)
|
||||
except ValueError:
|
||||
resp.json = None
|
||||
|
||||
return resp
|
||||
|
||||
def put(self, path, data, content_type="application/json", **kw):
|
||||
expected_status_code = kw.pop('status_code', 200)
|
||||
|
||||
content = json.dumps(data)
|
||||
resp = self.client.put(path=path, content_type=content_type,
|
||||
data=content)
|
||||
|
||||
LOG.debug('Response Body: %r' % resp.data)
|
||||
|
||||
self.assertEqual(expected_status_code, resp.status_code)
|
||||
|
||||
try:
|
||||
resp.json = json.loads(resp.data)
|
||||
except ValueError:
|
||||
resp.json = None
|
||||
|
||||
return resp
|
||||
|
||||
def delete(self, path, **kw):
|
||||
expected_status_code = kw.pop('status_code', 200)
|
||||
|
||||
resp = self.client.delete(path=path)
|
||||
|
||||
LOG.debug('Response Body: %r' % resp.data)
|
||||
|
||||
self.assertEqual(expected_status_code, resp.status_code)
|
||||
|
||||
return resp
|
|
@ -1,522 +0,0 @@
|
|||
# coding=utf-8
|
||||
# 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 datetime
|
||||
|
||||
import testtools
|
||||
from mock import patch
|
||||
import oslo_messaging as messaging
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
|
||||
from designate import exceptions
|
||||
from designate.central import service as central_service
|
||||
from designate.tests.test_api.test_v1 import ApiV1Test
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ApiV1zonesTest(ApiV1Test):
|
||||
def test_get_zone_schema(self):
|
||||
response = self.get('schemas/domain')
|
||||
self.assertIn('description', response.json)
|
||||
self.assertIn('links', response.json)
|
||||
self.assertIn('title', response.json)
|
||||
self.assertIn('id', response.json)
|
||||
self.assertIn('additionalProperties', response.json)
|
||||
self.assertIn('properties', response.json)
|
||||
self.assertIn('description', response.json['properties'])
|
||||
self.assertIn('created_at', response.json['properties'])
|
||||
self.assertIn('updated_at', response.json['properties'])
|
||||
self.assertIn('name', response.json['properties'])
|
||||
self.assertIn('email', response.json['properties'])
|
||||
self.assertIn('ttl', response.json['properties'])
|
||||
self.assertIn('serial', response.json['properties'])
|
||||
|
||||
def test_get_zones_schema(self):
|
||||
response = self.get('schemas/domains')
|
||||
self.assertIn('description', response.json)
|
||||
self.assertIn('additionalProperties', response.json)
|
||||
self.assertIn('properties', response.json)
|
||||
self.assertIn('title', response.json)
|
||||
self.assertIn('id', response.json)
|
||||
|
||||
def test_create_zone(self):
|
||||
# Create a zone
|
||||
fixture = self.get_zone_fixture(0)
|
||||
|
||||
# V1 doesn't have these
|
||||
del fixture['type']
|
||||
|
||||
response = self.post('domains', data=fixture)
|
||||
|
||||
self.assertIn('id', response.json)
|
||||
self.assertIn('name', response.json)
|
||||
self.assertEqual(response.json['name'], fixture['name'])
|
||||
|
||||
def test_create_zone_junk(self):
|
||||
# Create a zone
|
||||
fixture = self.get_zone_fixture(0)
|
||||
|
||||
# Add a junk property
|
||||
fixture['junk'] = 'Junk Field'
|
||||
|
||||
# Ensure it fails with a 400
|
||||
self.post('domains', data=fixture, status_code=400)
|
||||
|
||||
@patch.object(central_service.Service, 'create_zone',
|
||||
side_effect=messaging.MessagingTimeout())
|
||||
def test_create_zone_timeout(self, _):
|
||||
# Create a zone
|
||||
fixture = self.get_zone_fixture(0)
|
||||
|
||||
# V1 doesn't have these
|
||||
del fixture['type']
|
||||
|
||||
self.post('domains', data=fixture, status_code=504)
|
||||
|
||||
@patch.object(central_service.Service, 'create_zone',
|
||||
side_effect=exceptions.DuplicateZone())
|
||||
def test_create_zone_duplicate(self, _):
|
||||
# Create a zone
|
||||
fixture = self.get_zone_fixture(0)
|
||||
|
||||
# V1 doesn't have these
|
||||
del fixture['type']
|
||||
|
||||
self.post('domains', data=fixture, status_code=409)
|
||||
|
||||
def test_create_zone_null_ttl(self):
|
||||
# Create a zone
|
||||
fixture = self.get_zone_fixture(0)
|
||||
fixture['ttl'] = None
|
||||
self.post('domains', data=fixture, status_code=400)
|
||||
|
||||
def test_create_zone_negative_ttl(self):
|
||||
# Create a zone
|
||||
fixture = self.get_zone_fixture(0)
|
||||
fixture['ttl'] = -1
|
||||
self.post('domains', data=fixture, status_code=400)
|
||||
|
||||
def test_create_zone_zero_ttl(self):
|
||||
# Create a zone
|
||||
fixture = self.get_zone_fixture(0)
|
||||
fixture['ttl'] = 0
|
||||
self.post('domains', data=fixture, status_code=400)
|
||||
|
||||
def test_create_zone_invalid_ttl(self):
|
||||
# Create a zone
|
||||
fixture = self.get_zone_fixture(0)
|
||||
fixture['ttl'] = "$?>&"
|
||||
self.post('domains', data=fixture, status_code=400)
|
||||
|
||||
def test_create_zone_ttl_greater_than_max(self):
|
||||
fixture = self.get_zone_fixture(0)
|
||||
fixture['ttl'] = 2147483648
|
||||
self.post('domains', data=fixture, status_code=400)
|
||||
|
||||
def test_create_zone_utf_description(self):
|
||||
# Create a zone
|
||||
fixture = self.get_zone_fixture(0)
|
||||
|
||||
# V1 doesn't have type
|
||||
del fixture['type']
|
||||
|
||||
# Give it a UTF-8 filled description
|
||||
fixture['description'] = "utf-8:2H₂+O₂⇌2H₂O,R=4.7kΩ,⌀200mm∮E⋅da=Q,n" \
|
||||
",∑f(i)=∏g(i),∀x∈ℝ:⌈x⌉"
|
||||
# Create the zone, ensuring it succeeds, thus UTF-8 is supported
|
||||
self.post('domains', data=fixture)
|
||||
|
||||
def test_create_zone_description_too_long(self):
|
||||
# Create a zone
|
||||
fixture = self.get_zone_fixture(0)
|
||||
fixture['description'] = "x" * 161
|
||||
|
||||
# Create the zone, ensuring it fails with a 400
|
||||
self.post('domains', data=fixture, status_code=400)
|
||||
|
||||
def test_create_zone_with_unwanted_attributes(self):
|
||||
|
||||
zone_id = "2d1d1d1d-1324-4a80-aa32-1f69a91bf2c8"
|
||||
created_at = datetime.datetime(2014, 6, 22, 21, 50, 0)
|
||||
updated_at = datetime.datetime(2014, 6, 22, 21, 50, 0)
|
||||
serial = 1234567
|
||||
|
||||
# Create a zone
|
||||
fixture = self.get_zone_fixture(0)
|
||||
fixture['id'] = zone_id
|
||||
fixture['created_at'] = created_at
|
||||
fixture['updated_at'] = updated_at
|
||||
fixture['serial'] = serial
|
||||
|
||||
self.post('domains', data=fixture, status_code=400)
|
||||
|
||||
def test_create_invalid_name(self):
|
||||
# Prepare a zone
|
||||
fixture = self.get_zone_fixture(0)
|
||||
|
||||
invalid_names = [
|
||||
'org',
|
||||
'example.org',
|
||||
'example.321',
|
||||
]
|
||||
|
||||
for invalid_name in invalid_names:
|
||||
fixture['name'] = invalid_name
|
||||
|
||||
# Create a record
|
||||
response = self.post('domains', data=fixture, status_code=400)
|
||||
|
||||
self.assertNotIn('id', response.json)
|
||||
|
||||
def test_create_zone_name_too_long(self):
|
||||
fixture = self.get_zone_fixture(0)
|
||||
|
||||
long_name = 'a' * 255 + ".org."
|
||||
fixture['name'] = long_name
|
||||
|
||||
response = self.post('domains', data=fixture, status_code=400)
|
||||
|
||||
self.assertNotIn('id', response.json)
|
||||
|
||||
def test_create_zone_name_is_not_present(self):
|
||||
fixture = self.get_zone_fixture(0)
|
||||
del fixture['name']
|
||||
self.post('domains', data=fixture, status_code=400)
|
||||
|
||||
def test_create_invalid_email(self):
|
||||
# Prepare a zone
|
||||
fixture = self.get_zone_fixture(0)
|
||||
|
||||
invalid_emails = [
|
||||
'org',
|
||||
'example.org',
|
||||
'bla.example.org',
|
||||
'org.',
|
||||
'example.org.',
|
||||
'bla.example.org.',
|
||||
'bla.example.org.',
|
||||
]
|
||||
|
||||
for invalid_email in invalid_emails:
|
||||
fixture['email'] = invalid_email
|
||||
|
||||
# Create a record
|
||||
response = self.post('domains', data=fixture, status_code=400)
|
||||
|
||||
self.assertNotIn('id', response.json)
|
||||
|
||||
def test_create_zone_email_too_long(self):
|
||||
fixture = self.get_zone_fixture(0)
|
||||
|
||||
long_email = 'a' * 255 + "@org.com"
|
||||
fixture['email'] = long_email
|
||||
|
||||
response = self.post('domains', data=fixture, status_code=400)
|
||||
|
||||
self.assertNotIn('id', response.json)
|
||||
|
||||
def test_create_zone_email_not_present(self):
|
||||
fixture = self.get_zone_fixture(0)
|
||||
del fixture['email']
|
||||
self.post('domains', data=fixture, status_code=400)
|
||||
|
||||
def test_create_zone_twice(self):
|
||||
self.create_zone()
|
||||
with testtools.ExpectedException(exceptions.DuplicateZone):
|
||||
self.create_zone()
|
||||
|
||||
def test_create_zone_pending_deletion(self):
|
||||
zone = self.create_zone()
|
||||
self.delete('domains/%s' % zone['id'])
|
||||
with testtools.ExpectedException(exceptions.DuplicateZone):
|
||||
self.create_zone()
|
||||
|
||||
def test_get_zones(self):
|
||||
response = self.get('domains')
|
||||
|
||||
self.assertIn('domains', response.json)
|
||||
self.assertEqual(0, len(response.json['domains']))
|
||||
|
||||
# Create a zone
|
||||
self.create_zone()
|
||||
|
||||
response = self.get('domains')
|
||||
|
||||
self.assertIn('domains', response.json)
|
||||
self.assertEqual(1, len(response.json['domains']))
|
||||
|
||||
# Create a second zone
|
||||
self.create_zone(fixture=1)
|
||||
|
||||
response = self.get('domains')
|
||||
|
||||
self.assertIn('domains', response.json)
|
||||
self.assertEqual(2, len(response.json['domains']))
|
||||
|
||||
def test_get_zone_servers(self):
|
||||
# Create a zone
|
||||
zone = self.create_zone()
|
||||
response = self.get('domains/%s/servers' % zone['id'])
|
||||
# Verify length of zone servers
|
||||
self.assertEqual(1, len(response.json['servers']))
|
||||
|
||||
@patch.object(central_service.Service, 'find_zones',
|
||||
side_effect=messaging.MessagingTimeout())
|
||||
def test_get_zones_timeout(self, _):
|
||||
self.get('domains', status_code=504)
|
||||
|
||||
def test_get_zone(self):
|
||||
# Create a zone
|
||||
zone = self.create_zone()
|
||||
|
||||
response = self.get('domains/%s' % zone['id'])
|
||||
|
||||
self.assertIn('id', response.json)
|
||||
self.assertEqual(response.json['id'], zone['id'])
|
||||
|
||||
@patch.object(central_service.Service, 'find_zone',
|
||||
side_effect=messaging.MessagingTimeout())
|
||||
def test_get_zone_timeout(self, _):
|
||||
# Create a zone
|
||||
zone = self.create_zone()
|
||||
|
||||
self.get('domains/%s' % zone['id'], status_code=504)
|
||||
|
||||
def test_get_zone_missing(self):
|
||||
self.get('domains/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980',
|
||||
status_code=404)
|
||||
|
||||
def test_get_zone_invalid_id(self):
|
||||
# The letter "G" is not valid in a UUID
|
||||
self.get('domains/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff9GG',
|
||||
status_code=404)
|
||||
|
||||
self.get('domains/2fdadfb1cf964259ac6bbb7b6d2ff980', status_code=404)
|
||||
|
||||
def test_update_zone(self):
|
||||
# Create a zone
|
||||
zone = self.create_zone()
|
||||
|
||||
data = {'email': 'prefix-%s' % zone['email']}
|
||||
|
||||
response = self.put('domains/%s' % zone['id'], data=data)
|
||||
|
||||
self.assertIn('id', response.json)
|
||||
self.assertEqual(response.json['id'], zone['id'])
|
||||
|
||||
self.assertIn('email', response.json)
|
||||
self.assertEqual('prefix-%s' % zone['email'], response.json['email'])
|
||||
|
||||
def test_update_zone_junk(self):
|
||||
# Create a zone
|
||||
zone = self.create_zone()
|
||||
|
||||
data = {'email': 'prefix-%s' % zone['email'], 'junk': 'Junk Field'}
|
||||
|
||||
self.put('domains/%s' % zone['id'], data=data, status_code=400)
|
||||
|
||||
def test_update_zone_name_fail(self):
|
||||
# Create a zone
|
||||
zone = self.create_zone()
|
||||
|
||||
data = {'name': 'renamed.com.'}
|
||||
|
||||
self.put('domains/%s' % zone['id'], data=data, status_code=400)
|
||||
|
||||
def test_update_zone_null_ttl(self):
|
||||
# Create a zone
|
||||
zone = self.create_zone()
|
||||
|
||||
data = {'ttl': None}
|
||||
|
||||
self.put('domains/%s' % zone['id'], data=data, status_code=400)
|
||||
|
||||
def test_update_zone_negative_ttl(self):
|
||||
# Create a zone
|
||||
zone = self.create_zone()
|
||||
|
||||
data = {'ttl': -1}
|
||||
|
||||
self.put('domains/%s' % zone['id'], data=data, status_code=400)
|
||||
|
||||
def test_update_zone_zero_ttl(self):
|
||||
# Create a zone
|
||||
zone = self.create_zone()
|
||||
|
||||
data = {'ttl': 0}
|
||||
|
||||
self.put('domains/%s' % zone['id'], data=data, status_code=400)
|
||||
|
||||
@patch.object(central_service.Service, 'update_zone',
|
||||
side_effect=messaging.MessagingTimeout())
|
||||
def test_update_zone_timeout(self, _):
|
||||
# Create a zone
|
||||
zone = self.create_zone()
|
||||
|
||||
data = {'email': 'prefix-%s' % zone['email']}
|
||||
|
||||
self.put('domains/%s' % zone['id'], data=data, status_code=504)
|
||||
|
||||
@patch.object(central_service.Service, 'update_zone',
|
||||
side_effect=exceptions.DuplicateZone())
|
||||
def test_update_zone_duplicate(self, _):
|
||||
# Create a zone
|
||||
zone = self.create_zone()
|
||||
|
||||
data = {'email': 'prefix-%s' % zone['email']}
|
||||
|
||||
self.put('domains/%s' % zone['id'], data=data, status_code=409)
|
||||
|
||||
def test_update_zone_missing(self):
|
||||
data = {'email': 'bla@bla.com'}
|
||||
|
||||
self.put('domains/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980', data=data,
|
||||
status_code=404)
|
||||
|
||||
def test_update_zone_invalid_id(self):
|
||||
data = {'email': 'bla@bla.com'}
|
||||
|
||||
# The letter "G" is not valid in a UUID
|
||||
self.put('domains/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff9GG', data=data,
|
||||
status_code=404)
|
||||
|
||||
self.put('domains/2fdadfb1cf964259ac6bbb7b6d2ff980', data=data,
|
||||
status_code=404)
|
||||
|
||||
def test_update_zone_ttl_greter_than_max(self):
|
||||
# Create a zone
|
||||
zone = self.create_zone()
|
||||
|
||||
data = {'ttl': 2147483648}
|
||||
|
||||
self.put('domains/%s' % zone['id'], data=data, status_code=400)
|
||||
|
||||
def test_update_zone_invalid_email(self):
|
||||
# Create a zone
|
||||
zone = self.create_zone()
|
||||
|
||||
invalid_emails = [
|
||||
'org',
|
||||
'example.org',
|
||||
'bla.example.org',
|
||||
'org.',
|
||||
'example.org.',
|
||||
'bla.example.org.',
|
||||
'bla.example.org.',
|
||||
'a' * 255 + "@com",
|
||||
''
|
||||
]
|
||||
|
||||
for invalid_email in invalid_emails:
|
||||
data = {'email': invalid_email}
|
||||
self.put('domains/%s' % zone['id'], data=data, status_code=400)
|
||||
|
||||
def test_update_zone_description_too_long(self):
|
||||
# Create a zone
|
||||
zone = self.create_zone()
|
||||
|
||||
invalid_des = 'a' * 165
|
||||
|
||||
data = {'description': invalid_des}
|
||||
self.put('domains/%s' % zone['id'], data=data, status_code=400)
|
||||
|
||||
def test_update_zone_in_pending_deletion(self):
|
||||
zone = self.create_zone()
|
||||
self.delete('domains/%s' % zone['id'])
|
||||
self.put('domains/%s' % zone['id'], data={}, status_code=404)
|
||||
|
||||
def test_delete_zone(self):
|
||||
# Create a zone
|
||||
zone = self.create_zone()
|
||||
|
||||
self.delete('domains/%s' % zone['id'])
|
||||
|
||||
# Simulate the zone having been deleted on the backend
|
||||
zone_serial = self.central_service.get_zone(
|
||||
self.admin_context, zone['id']).serial
|
||||
self.central_service.update_status(
|
||||
self.admin_context, zone['id'], "SUCCESS", zone_serial)
|
||||
|
||||
# Ensure we can no longer fetch the zone
|
||||
self.get('domains/%s' % zone['id'], status_code=404)
|
||||
|
||||
def test_zone_in_pending_deletion(self):
|
||||
zone1 = self.create_zone()
|
||||
self.create_zone(fixture=1)
|
||||
response = self.get('domains')
|
||||
self.assertEqual(2, len(response.json['domains']))
|
||||
|
||||
# Delete zone1
|
||||
self.delete('domains/%s' % zone1['id'])
|
||||
|
||||
# Ensure we can no longer list nor fetch the deleted zone
|
||||
response = self.get('domains')
|
||||
self.assertEqual(1, len(response.json['domains']))
|
||||
|
||||
self.get('domains/%s' % zone1['id'], status_code=404)
|
||||
|
||||
@patch.object(central_service.Service, 'delete_zone',
|
||||
side_effect=messaging.MessagingTimeout())
|
||||
def test_delete_zone_timeout(self, _):
|
||||
# Create a zone
|
||||
zone = self.create_zone()
|
||||
|
||||
self.delete('domains/%s' % zone['id'], status_code=504)
|
||||
|
||||
def test_delete_zone_missing(self):
|
||||
self.delete('domains/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980',
|
||||
status_code=404)
|
||||
|
||||
def test_delete_zone_invalid_id(self):
|
||||
# The letter "G" is not valid in a UUID
|
||||
self.delete('domains/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff9GG',
|
||||
status_code=404)
|
||||
|
||||
self.delete('domains/2fdadfb1cf964259ac6bbb7b6d2ff980',
|
||||
status_code=404)
|
||||
|
||||
def test_get_secondary_missing(self):
|
||||
fixture = self.get_zone_fixture('SECONDARY', 0)
|
||||
fixture['email'] = cfg.CONF['service:central'].managed_resource_email
|
||||
|
||||
zone = self.create_zone(**fixture)
|
||||
|
||||
self.get('domains/%s' % zone.id, status_code=404)
|
||||
|
||||
def test_update_secondary_missing(self):
|
||||
fixture = self.get_zone_fixture('SECONDARY', 0)
|
||||
fixture['email'] = cfg.CONF['service:central'].managed_resource_email
|
||||
|
||||
zone = self.create_zone(**fixture)
|
||||
|
||||
self.put('domains/%s' % zone.id, {}, status_code=404)
|
||||
|
||||
def test_delete_secondary_missing(self):
|
||||
fixture = self.get_zone_fixture('SECONDARY', 0)
|
||||
fixture['email'] = cfg.CONF['service:central'].managed_resource_email
|
||||
|
||||
zone = self.create_zone(**fixture)
|
||||
self.delete('domains/%s' % zone.id, status_code=404)
|
||||
|
||||
def test_get_zone_servers_from_secondary(self):
|
||||
fixture = self.get_zone_fixture('SECONDARY', 0)
|
||||
fixture['email'] = cfg.CONF['service:central'].managed_resource_email
|
||||
|
||||
zone = self.create_zone(**fixture)
|
||||
self.get('domains/%s/servers' % zone.id, status_code=404)
|
|
@ -1,39 +0,0 @@
|
|||
# coding=utf-8
|
||||
# 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.
|
||||
from oslo_log import log as logging
|
||||
|
||||
from designate.tests.test_api.test_v1 import ApiV1Test
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ApiV1LimitsTest(ApiV1Test):
|
||||
def test_get_limits_schema(self):
|
||||
response = self.get('/schemas/limits')
|
||||
self.assertIn('id', response.json)
|
||||
self.assertIn('description', response.json)
|
||||
self.assertIn('title', response.json)
|
||||
self.assertIn('additionalProperties', response.json)
|
||||
self.assertIn('properties', response.json)
|
||||
|
||||
def test_get_limits(self):
|
||||
response = self.get('/limits')
|
||||
self.assertIn('limits', response.json)
|
||||
self.assertIn('absolute', response.json['limits'])
|
||||
self.assertIn('maxDomains', response.json['limits']['absolute'])
|
||||
self.assertIn('maxDomainRecords', response.json['limits']['absolute'])
|
|
@ -1,862 +0,0 @@
|
|||
# coding=utf-8
|
||||
# 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.
|
||||
from mock import patch
|
||||
import oslo_messaging as messaging
|
||||
from oslo_log import log as logging
|
||||
|
||||
from designate.central import service as central_service
|
||||
from designate.tests.test_api.test_v1 import ApiV1Test
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ApiV1RecordsTest(ApiV1Test):
|
||||
def setUp(self):
|
||||
super(ApiV1RecordsTest, self).setUp()
|
||||
|
||||
self.zone = self.create_zone()
|
||||
self.recordset = self.create_recordset(self.zone, 'A')
|
||||
|
||||
def test_get_record_schema(self):
|
||||
response = self.get('schemas/record')
|
||||
self.assertIn('description', response.json)
|
||||
self.assertIn('links', response.json)
|
||||
self.assertIn('title', response.json)
|
||||
self.assertIn('id', response.json)
|
||||
self.assertIn('additionalProperties', response.json)
|
||||
self.assertIn('properties', response.json)
|
||||
self.assertIn('id', response.json['properties'])
|
||||
self.assertIn('domain_id', response.json['properties'])
|
||||
self.assertIn('type', response.json['properties'])
|
||||
self.assertIn('data', response.json['properties'])
|
||||
self.assertIn('priority', response.json['properties'])
|
||||
self.assertIn('description', response.json['properties'])
|
||||
self.assertIn('created_at', response.json['properties'])
|
||||
self.assertIn('updated_at', response.json['properties'])
|
||||
self.assertIn('name', response.json['properties'])
|
||||
self.assertIn('ttl', response.json['properties'])
|
||||
self.assertIn('oneOf', response.json)
|
||||
|
||||
def test_get_records_schema(self):
|
||||
response = self.get('schemas/records')
|
||||
self.assertIn('description', response.json)
|
||||
self.assertIn('additionalProperties', response.json)
|
||||
self.assertIn('properties', response.json)
|
||||
self.assertIn('title', response.json)
|
||||
self.assertIn('id', response.json)
|
||||
|
||||
def test_create_record(self):
|
||||
recordset_fixture = self.get_recordset_fixture(
|
||||
self.zone['name'])
|
||||
|
||||
fixture = self.get_record_fixture(recordset_fixture['type'])
|
||||
fixture.update({
|
||||
'name': recordset_fixture['name'],
|
||||
'type': recordset_fixture['type'],
|
||||
})
|
||||
|
||||
# Create a record
|
||||
response = self.post('domains/%s/records' % self.zone['id'],
|
||||
data=fixture)
|
||||
|
||||
self.assertIn('id', response.json)
|
||||
self.assertIn('name', response.json)
|
||||
self.assertEqual(response.json['name'], fixture['name'])
|
||||
|
||||
def test_create_record_existing_recordset(self):
|
||||
fixture = self.get_record_fixture(self.recordset['type'])
|
||||
fixture.update({
|
||||
'name': self.recordset['name'],
|
||||
'type': self.recordset['type'],
|
||||
})
|
||||
|
||||
# Create a record
|
||||
response = self.post('domains/%s/records' % self.zone['id'],
|
||||
data=fixture)
|
||||
|
||||
self.assertIn('id', response.json)
|
||||
self.assertIn('name', response.json)
|
||||
self.assertEqual(response.json['name'], fixture['name'])
|
||||
|
||||
def test_create_record_name_reuse(self):
|
||||
fixture_1 = self.get_record_fixture(self.recordset['type'])
|
||||
fixture_1.update({
|
||||
'name': self.recordset['name'],
|
||||
'type': self.recordset['type'],
|
||||
})
|
||||
|
||||
fixture_2 = self.get_record_fixture(self.recordset['type'], fixture=1)
|
||||
fixture_2.update({
|
||||
'name': self.recordset['name'],
|
||||
'type': self.recordset['type'],
|
||||
})
|
||||
|
||||
# Create 2 records
|
||||
record_1 = self.post('domains/%s/records' % self.zone['id'],
|
||||
data=fixture_1)
|
||||
record_2 = self.post('domains/%s/records' % self.zone['id'],
|
||||
data=fixture_2)
|
||||
|
||||
# Delete record 1, this should not have any side effects
|
||||
self.delete('domains/%s/records/%s' % (self.zone['id'],
|
||||
record_1.json['id']))
|
||||
|
||||
# Simulate the record 1 having been deleted on the backend
|
||||
zone_serial = self.central_service.get_zone(
|
||||
self.admin_context, self.zone['id']).serial
|
||||
self.central_service.update_status(
|
||||
self.admin_context, self.zone['id'], "SUCCESS", zone_serial)
|
||||
|
||||
# Get the record 2 to ensure recordset did not get deleted
|
||||
rec_2_get_response = self.get('domains/%s/records/%s' %
|
||||
(self.zone['id'], record_2.json['id']))
|
||||
|
||||
self.assertIn('id', rec_2_get_response.json)
|
||||
self.assertIn('name', rec_2_get_response.json)
|
||||
self.assertEqual(rec_2_get_response.json['name'], fixture_1['name'])
|
||||
|
||||
# Delete record 2, this should delete the null recordset too
|
||||
self.delete('domains/%s/records/%s' % (self.zone['id'],
|
||||
record_2.json['id']))
|
||||
|
||||
# Simulate the record 2 having been deleted on the backend
|
||||
zone_serial = self.central_service.get_zone(
|
||||
self.admin_context, self.zone['id']).serial
|
||||
self.central_service.update_status(
|
||||
self.admin_context, self.zone['id'], "SUCCESS", zone_serial)
|
||||
|
||||
# Re-create as a different type, but use the same name
|
||||
fixture = self.get_record_fixture('CNAME')
|
||||
fixture.update({
|
||||
'name': self.recordset['name'],
|
||||
'type': 'CNAME'
|
||||
})
|
||||
|
||||
response = self.post('domains/%s/records' % self.zone['id'],
|
||||
data=fixture)
|
||||
|
||||
self.assertIn('id', response.json)
|
||||
self.assertIn('name', response.json)
|
||||
self.assertEqual(response.json['name'], fixture['name'])
|
||||
|
||||
def test_create_record_junk(self):
|
||||
fixture = self.get_record_fixture(self.recordset['type'])
|
||||
fixture.update({
|
||||
'name': self.recordset['name'],
|
||||
'type': self.recordset['type'],
|
||||
})
|
||||
|
||||
# Add a junk property
|
||||
fixture['junk'] = 'Junk Field'
|
||||
|
||||
# Create a record, ensuring it fails with a 400
|
||||
self.post('domains/%s/records' % self.zone['id'], data=fixture,
|
||||
status_code=400)
|
||||
|
||||
def test_create_wildcard_record_after_named(self):
|
||||
# We want to test that a wildcard record rs does not use the
|
||||
# previous one
|
||||
# https://bugs.launchpad.net/designate/+bug/1391426
|
||||
|
||||
name = "foo.%s" % self.zone.name
|
||||
fixture = {
|
||||
"name": name,
|
||||
"type": "A",
|
||||
"data": "10.0.0.1"
|
||||
}
|
||||
|
||||
self.post('domains/%s/records' % self.zone['id'],
|
||||
data=fixture)
|
||||
|
||||
wildcard_name = '*.%s' % self.zone["name"]
|
||||
|
||||
fixture['name'] = wildcard_name
|
||||
self.post('domains/%s/records' % self.zone['id'],
|
||||
data=fixture)
|
||||
|
||||
named_rs = self.central_service.find_recordset(
|
||||
self.admin_context, {"name": name})
|
||||
wildcard_rs = self.central_service.find_recordset(
|
||||
self.admin_context, {"name": wildcard_name})
|
||||
|
||||
self.assertNotEqual(named_rs.name, wildcard_rs.name)
|
||||
self.assertNotEqual(named_rs.id, wildcard_rs.id)
|
||||
|
||||
def test_create_record_utf_description(self):
|
||||
fixture = self.get_record_fixture(self.recordset['type'])
|
||||
fixture.update({
|
||||
'name': self.recordset['name'],
|
||||
'type': self.recordset['type'],
|
||||
})
|
||||
|
||||
# Add a UTF-8 riddled description
|
||||
fixture['description'] = "utf-8:2H₂+O₂⇌2H₂O,R=4.7kΩ,⌀200mm∮E⋅da=Q,n" \
|
||||
",∑f(i)=∏g(i),∀x∈ℝ:⌈x⌉"
|
||||
|
||||
# Create a record, ensuring it succeeds
|
||||
self.post('domains/%s/records' % self.zone['id'], data=fixture)
|
||||
|
||||
def test_create_record_description_too_long(self):
|
||||
fixture = self.get_record_fixture(self.recordset['type'])
|
||||
fixture.update({
|
||||
'name': self.recordset['name'],
|
||||
'type': self.recordset['type'],
|
||||
})
|
||||
|
||||
# Add a description that is too long
|
||||
fixture['description'] = "x" * 161
|
||||
|
||||
# Create a record, ensuring it fails with a 400
|
||||
self.post('domains/%s/records' % self.zone['id'], data=fixture,
|
||||
status_code=400)
|
||||
|
||||
def test_create_record_name_too_long(self):
|
||||
fixture = self.get_record_fixture(self.recordset['type'])
|
||||
fixture.update({'type': self.recordset['type']})
|
||||
fixture['name'] = 'w' * 255 + ".%s" % self.zone.name
|
||||
self.post('domains/%s/records' % self.zone['id'], data=fixture,
|
||||
status_code=400)
|
||||
|
||||
def test_create_record_name_is_missing(self):
|
||||
fixture = self.get_record_fixture(self.recordset['type'])
|
||||
fixture.update({'type': self.recordset['type']})
|
||||
self.post('domains/%s/records' % self.zone['id'], data=fixture,
|
||||
status_code=400)
|
||||
|
||||
def test_create_record_type_is_missing(self):
|
||||
fixture = self.get_record_fixture(self.recordset['type'])
|
||||
fixture['name'] = "www.%s" % self.zone.name
|
||||
self.post('domains/%s/records' % self.zone['id'], data=fixture,
|
||||
status_code=400)
|
||||
|
||||
def test_create_record_invalid_type(self):
|
||||
fixture = self.get_record_fixture(self.recordset['type'])
|
||||
fixture.update({'type': "ABC", 'name': self.recordset['name']})
|
||||
self.post('domains/%s/records' % self.zone['id'], data=fixture,
|
||||
status_code=400)
|
||||
|
||||
def test_create_record_data_is_missing(self):
|
||||
fixture = self.get_record_fixture(self.recordset['type'])
|
||||
fixture.update({'type': self.recordset['type'],
|
||||
'name': self.recordset['name']})
|
||||
del fixture['data']
|
||||
self.post('domains/%s/records' % self.zone['id'], data=fixture,
|
||||
status_code=400)
|
||||
|
||||
def test_create_record_ttl_greater_than_max(self):
|
||||
fixture = self.get_record_fixture(self.recordset['type'])
|
||||
fixture.update({
|
||||
'name': self.recordset['name'],
|
||||
'type': self.recordset['type'],
|
||||
})
|
||||
|
||||
fixture['ttl'] = 2174483648
|
||||
self.post('domains/%s/records' % self.zone['id'], data=fixture,
|
||||
status_code=400)
|
||||
|
||||
def test_create_record_negative_ttl(self):
|
||||
fixture = self.get_record_fixture(self.recordset['type'])
|
||||
fixture.update({
|
||||
'name': self.recordset['name'],
|
||||
'type': self.recordset['type'],
|
||||
})
|
||||
|
||||
# Set the TTL to a negative value
|
||||
fixture['ttl'] = -1
|
||||
|
||||
# Create a record, ensuring it fails with a 400
|
||||
self.post('domains/%s/records' % self.zone['id'], data=fixture,
|
||||
status_code=400)
|
||||
|
||||
def test_create_record_zero_ttl(self):
|
||||
fixture = self.get_record_fixture(self.recordset['type'])
|
||||
fixture.update({
|
||||
'name': self.recordset['name'],
|
||||
'type': self.recordset['type'],
|
||||
})
|
||||
|
||||
# Set the TTL to a value zero
|
||||
fixture['ttl'] = 0
|
||||
|
||||
# Create a record, ensuring it fails with a 400
|
||||
self.post('domains/%s/records' % self.zone['id'], data=fixture,
|
||||
status_code=400)
|
||||
|
||||
def test_create_record_invalid_ttl(self):
|
||||
fixture = self.get_record_fixture(self.recordset['type'])
|
||||
fixture.update({
|
||||
'name': self.recordset['name'],
|
||||
'type': self.recordset['type'],
|
||||
})
|
||||
|
||||
# Set the TTL to an invalid value
|
||||
fixture['ttl'] = "$?!."
|
||||
|
||||
# Create a record, ensuring it fails with a 400
|
||||
self.post('domains/%s/records' % self.zone['id'], data=fixture,
|
||||
status_code=400)
|
||||
|
||||
def test_create_record_invalid_priority(self):
|
||||
fixture = self.get_record_fixture(self.recordset['type'])
|
||||
fixture.update({
|
||||
'name': self.recordset['name'],
|
||||
'type': self.recordset['type'],
|
||||
})
|
||||
fixture['priority'] = "$?!."
|
||||
self.post('domains/%s/records' % self.zone['id'], data=fixture,
|
||||
status_code=400)
|
||||
|
||||
def test_create_record_negative_priority(self):
|
||||
fixture = self.get_record_fixture(self.recordset['type'])
|
||||
fixture.update({
|
||||
'name': self.recordset['name'],
|
||||
'type': self.recordset['type'],
|
||||
})
|
||||
fixture['priority'] = -1
|
||||
self.post('domains/%s/records' % self.zone['id'], data=fixture,
|
||||
status_code=400)
|
||||
|
||||
def test_create_record_priority_greater_than_max(self):
|
||||
fixture = self.get_record_fixture(self.recordset['type'])
|
||||
fixture.update({
|
||||
'name': self.recordset['name'],
|
||||
'type': self.recordset['type'],
|
||||
})
|
||||
fixture['priority'] = 65536
|
||||
self.post('domains/%s/records' % self.zone['id'], data=fixture,
|
||||
status_code=400)
|
||||
|
||||
@patch.object(central_service.Service, 'create_record',
|
||||
side_effect=messaging.MessagingTimeout())
|
||||
def test_create_record_timeout(self, _):
|
||||
fixture = self.get_record_fixture(self.recordset['type'])
|
||||
fixture.update({
|
||||
'name': self.recordset['name'],
|
||||
'type': self.recordset['type'],
|
||||
})
|
||||
|
||||
# Create a record
|
||||
self.post('domains/%s/records' % self.zone['id'], data=fixture,
|
||||
status_code=504)
|
||||
|
||||
def test_create_wildcard_record(self):
|
||||
# Prepare a record
|
||||
fixture = self.get_record_fixture(self.recordset['type'])
|
||||
fixture.update({
|
||||
'name': '*.%s' % self.recordset['name'],
|
||||
'type': self.recordset['type'],
|
||||
})
|
||||
|
||||
# Create a record
|
||||
response = self.post('domains/%s/records' % self.zone['id'],
|
||||
data=fixture)
|
||||
|
||||
self.assertIn('id', response.json)
|
||||
self.assertIn('name', response.json)
|
||||
self.assertEqual(response.json['name'], fixture['name'])
|
||||
|
||||
def test_create_srv_record(self):
|
||||
recordset_fixture = self.get_recordset_fixture(
|
||||
self.zone['name'], 'SRV')
|
||||
|
||||
fixture = self.get_record_fixture(recordset_fixture['type'])
|
||||
priority, _, data = fixture['data'].partition(" ")
|
||||
|
||||
fixture.update({
|
||||
'data': data,
|
||||
'priority': int(priority),
|
||||
'name': recordset_fixture['name'],
|
||||
'type': recordset_fixture['type'],
|
||||
})
|
||||
|
||||
# Create a record
|
||||
response = self.post('domains/%s/records' % self.zone['id'],
|
||||
data=fixture)
|
||||
|
||||
self.assertIn('id', response.json)
|
||||
self.assertEqual(fixture['type'], response.json['type'])
|
||||
self.assertEqual(fixture['name'], response.json['name'])
|
||||
|
||||
self.assertEqual(fixture['priority'], response.json['priority'])
|
||||
self.assertEqual(fixture['data'], response.json['data'])
|
||||
|
||||
def test_create_invalid_data_srv_record(self):
|
||||
recordset_fixture = self.get_recordset_fixture(
|
||||
self.zone['name'], 'SRV')
|
||||
|
||||
fixture = self.get_record_fixture(recordset_fixture['type'])
|
||||
fixture.update({
|
||||
'name': recordset_fixture['name'],
|
||||
'type': recordset_fixture['type'],
|
||||
})
|
||||
|
||||
invalid_datas = [
|
||||
'I 5060 sip.%s' % self.zone['name'],
|
||||
'5060 sip.%s' % self.zone['name'],
|
||||
'5060 I sip.%s' % self.zone['name'],
|
||||
'0 5060 sip',
|
||||
'sip',
|
||||
'sip.%s' % self.zone['name'],
|
||||
]
|
||||
|
||||
for invalid_data in invalid_datas:
|
||||
fixture['data'] = invalid_data
|
||||
# Attempt to create the record
|
||||
self.post('domains/%s/records' % self.zone['id'], data=fixture,
|
||||
status_code=400)
|
||||
|
||||
def test_create_invalid_name_srv_record(self):
|
||||
recordset_fixture = self.get_recordset_fixture(
|
||||
self.zone['name'], 'SRV')
|
||||
|
||||
fixture = self.get_record_fixture(recordset_fixture['type'])
|
||||
fixture.update({
|
||||
'name': recordset_fixture['name'],
|
||||
'type': recordset_fixture['type'],
|
||||
})
|
||||
|
||||
invalid_names = [
|
||||
'%s' % self.zone['name'],
|
||||
'_udp.%s' % self.zone['name'],
|
||||
'sip._udp.%s' % self.zone['name'],
|
||||
'_sip.udp.%s' % self.zone['name'],
|
||||
]
|
||||
|
||||
for invalid_name in invalid_names:
|
||||
fixture['name'] = invalid_name
|
||||
|
||||
# Attempt to create the record
|
||||
self.post('domains/%s/records' % self.zone['id'], data=fixture,
|
||||
status_code=400)
|
||||
|
||||
def test_create_invalid_name(self):
|
||||
# Prepare a record
|
||||
fixture = self.get_record_fixture(self.recordset['type'])
|
||||
fixture.update({
|
||||
'name': self.recordset['name'],
|
||||
'type': self.recordset['type'],
|
||||
})
|
||||
|
||||
invalid_names = [
|
||||
'org',
|
||||
'example.org',
|
||||
'$$.example.org',
|
||||
'*example.org.',
|
||||
'*.*.example.org.',
|
||||
'abc.*.example.org.',
|
||||
]
|
||||
|
||||
for invalid_name in invalid_names:
|
||||
fixture['name'] = invalid_name
|
||||
|
||||
# Create a record
|
||||
response = self.post('domains/%s/records' % self.zone['id'],
|
||||
data=fixture, status_code=400)
|
||||
|
||||
self.assertNotIn('id', response.json)
|
||||
|
||||
def test_get_records(self):
|
||||
response = self.get('domains/%s/records' % self.zone['id'])
|
||||
|
||||
# Verify that the SOA & NS records are already created
|
||||
self.assertIn('records', response.json)
|
||||
self.assertEqual(2, len(response.json['records']))
|
||||
|
||||
# Create a record
|
||||
self.create_record(self.zone, self.recordset)
|
||||
|
||||
response = self.get('domains/%s/records' % self.zone['id'])
|
||||
|
||||
# Verify that one more record has been added
|
||||
self.assertIn('records', response.json)
|
||||
self.assertEqual(3, len(response.json['records']))
|
||||
|
||||
# Create a second record
|
||||
self.create_record(self.zone, self.recordset, fixture=1)
|
||||
|
||||
response = self.get('domains/%s/records' % self.zone['id'])
|
||||
|
||||
# Verfiy that all 4 records are there
|
||||
self.assertIn('records', response.json)
|
||||
self.assertEqual(4, len(response.json['records']))
|
||||
|
||||
@patch.object(central_service.Service, 'get_zone',
|
||||
side_effect=messaging.MessagingTimeout())
|
||||
def test_get_records_timeout(self, _):
|
||||
self.get('domains/%s/records' % self.zone['id'],
|
||||
status_code=504)
|
||||
|
||||
def test_get_records_missing_zone(self):
|
||||
self.get('domains/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980/records',
|
||||
status_code=404)
|
||||
|
||||
def test_get_records_invalid_zone_id(self):
|
||||
self.get('domains/2fdadfb1cf964259ac6bbb7b6d2ff980/records',
|
||||
status_code=404)
|
||||
|
||||
def test_get_record_missing(self):
|
||||
self.get('domains/%s/records/2fdadfb1-cf96-4259-ac6b-'
|
||||
'bb7b6d2ff980' % self.zone['id'],
|
||||
status_code=404)
|
||||
|
||||
def test_get_record_with_invalid_id(self):
|
||||
self.get('domains/%s/records/2fdadfb1-cf96-4259-ac6b-'
|
||||
'bb7b6d2ff980GH' % self.zone['id'],
|
||||
status_code=404)
|
||||
|
||||
def test_get_record(self):
|
||||
# Create a record
|
||||
record = self.create_record(self.zone, self.recordset)
|
||||
|
||||
response = self.get('domains/%s/records/%s' % (self.zone['id'],
|
||||
record['id']))
|
||||
|
||||
self.assertIn('id', response.json)
|
||||
self.assertEqual(response.json['id'], record['id'])
|
||||
self.assertEqual(response.json['name'], self.recordset['name'])
|
||||
self.assertEqual(response.json['type'], self.recordset['type'])
|
||||
|
||||
def test_update_record(self):
|
||||
# Create a record
|
||||
record = self.create_record(self.zone, self.recordset)
|
||||
|
||||
# Fetch another fixture to use in the update
|
||||
fixture = self.get_record_fixture(self.recordset['type'], fixture=1)
|
||||
|
||||
# Update the record
|
||||
data = {'data': fixture['data']}
|
||||
response = self.put('domains/%s/records/%s' % (self.zone['id'],
|
||||
record['id']),
|
||||
data=data)
|
||||
|
||||
self.assertIn('id', response.json)
|
||||
self.assertEqual(response.json['id'], record['id'])
|
||||
self.assertEqual(response.json['data'], fixture['data'])
|
||||
self.assertEqual(response.json['type'], self.recordset['type'])
|
||||
|
||||
def test_update_record_ttl(self):
|
||||
# Create a record
|
||||
record = self.create_record(self.zone, self.recordset)
|
||||
|
||||
# Update the record
|
||||
data = {'ttl': 100}
|
||||
response = self.put('domains/%s/records/%s' % (self.zone['id'],
|
||||
record['id']),
|
||||
data=data)
|
||||
|
||||
self.assertIn('id', response.json)
|
||||
self.assertEqual(record['id'], response.json['id'])
|
||||
self.assertEqual(record['data'], response.json['data'])
|
||||
self.assertEqual(self.recordset['type'], response.json['type'])
|
||||
self.assertEqual(100, response.json['ttl'])
|
||||
|
||||
def test_update_record_junk(self):
|
||||
# Create a record
|
||||
record = self.create_record(self.zone, self.recordset)
|
||||
|
||||
data = {'ttl': 100, 'junk': 'Junk Field'}
|
||||
|
||||
self.put('domains/%s/records/%s' % (self.zone['id'], record['id']),
|
||||
data=data, status_code=400)
|
||||
|
||||
def test_update_record_negative_ttl(self):
|
||||
# Create a record
|
||||
record = self.create_record(self.zone, self.recordset)
|
||||
|
||||
data = {'ttl': -1}
|
||||
|
||||
self.put('domains/%s/records/%s' % (self.zone['id'], record['id']),
|
||||
data=data, status_code=400)
|
||||
|
||||
def test_update_record_ttl_greater_than_max(self):
|
||||
record = self.create_record(self.zone, self.recordset)
|
||||
data = {'ttl': 2174483648}
|
||||
self.put('domains/%s/records/%s' % (self.zone['id'], record['id']),
|
||||
data=data, status_code=400)
|
||||
|
||||
def test_update_record_zero_ttl(self):
|
||||
# Create a record
|
||||
record = self.create_record(self.zone, self.recordset)
|
||||
|
||||
data = {'ttl': 0}
|
||||
|
||||
self.put('domains/%s/records/%s' % (self.zone['id'], record['id']),
|
||||
data=data, status_code=400)
|
||||
|
||||
def test_update_record_invalid_ttl(self):
|
||||
# Create a record
|
||||
record = self.create_record(self.zone, self.recordset)
|
||||
|
||||
data = {'ttl': "$?>%"}
|
||||
|
||||
self.put('domains/%s/records/%s' % (self.zone['id'], record['id']),
|
||||
data=data, status_code=400)
|
||||
|
||||
def test_update_record_description_too_long(self):
|
||||
record = self.create_record(self.zone, self.recordset)
|
||||
data = {'description': 'x' * 165}
|
||||
self.put('domains/%s/records/%s' % (self.zone['id'], record['id']),
|
||||
data=data, status_code=400)
|
||||
|
||||
def test_update_record_negative_priority(self):
|
||||
record = self.create_record(self.zone, self.recordset)
|
||||
data = {'priority': -1}
|
||||
self.put('domains/%s/records/%s' % (self.zone['id'], record['id']),
|
||||
data=data, status_code=400)
|
||||
|
||||
def test_update_record_invalid_priority(self):
|
||||
record = self.create_record(self.zone, self.recordset)
|
||||
data = {'priority': "?!:>"}
|
||||
self.put('domains/%s/records/%s' % (self.zone['id'], record['id']),
|
||||
data=data, status_code=400)
|
||||
|
||||
def test_update_record_priority_greater_than_max(self):
|
||||
record = self.create_record(self.zone, self.recordset)
|
||||
data = {'priority': 65536}
|
||||
self.put('domains/%s/records/%s' % (self.zone['id'], record['id']),
|
||||
data=data, status_code=400)
|
||||
|
||||
def test_update_record_name_too_long(self):
|
||||
record = self.create_record(self.zone, self.recordset)
|
||||
data = {'name': 'w' * 256 + ".%s" % self.zone.name}
|
||||
self.put('domains/%s/records/%s' % (self.zone['id'], record['id']),
|
||||
data=data, status_code=400)
|
||||
|
||||
def test_update_record_invalid_type(self):
|
||||
record = self.create_record(self.zone, self.recordset)
|
||||
data = {'type': 'ABC'}
|
||||
self.put('domains/%s/records/%s' % (self.zone['id'], record['id']),
|
||||
data=data, status_code=400)
|
||||
|
||||
def test_update_record_data_too_long(self):
|
||||
record = self.create_record(self.zone, self.recordset)
|
||||
data = {'data': '1' * 255 + '.2.3.4'}
|
||||
self.put('domains/%s/records/%s' % (self.zone['id'], record['id']),
|
||||
data=data, status_code=400)
|
||||
|
||||
def test_update_record_outside_zone_fail(self):
|
||||
# Create a record
|
||||
record = self.create_record(self.zone, self.recordset)
|
||||
|
||||
data = {'name': 'test.someotherzone.com.'}
|
||||
|
||||
self.put('domains/%s/records/%s' % (self.zone['id'], record['id']),
|
||||
data=data, status_code=400)
|
||||
|
||||
@patch.object(central_service.Service, 'find_zone',
|
||||
side_effect=messaging.MessagingTimeout())
|
||||
def test_update_record_timeout(self, _):
|
||||
# Create a record
|
||||
record = self.create_record(self.zone, self.recordset)
|
||||
|
||||
data = {'name': 'test.example.org.'}
|
||||
|
||||
self.put('domains/%s/records/%s' % (self.zone['id'], record['id']),
|
||||
data=data, status_code=504)
|
||||
|
||||
def test_update_record_missing(self):
|
||||
data = {'name': 'test.example.org.'}
|
||||
|
||||
self.put('domains/%s/records/2fdadfb1-cf96-4259-ac6b-'
|
||||
'bb7b6d2ff980' % self.zone['id'],
|
||||
data=data,
|
||||
status_code=404)
|
||||
|
||||
def test_update_record_invalid_id(self):
|
||||
data = {'name': 'test.example.org.'}
|
||||
|
||||
self.put('domains/%s/records/2fdadfb1cf964259ac6bbb7b6d2ff980' %
|
||||
self.zone['id'],
|
||||
data=data,
|
||||
status_code=404)
|
||||
|
||||
def test_update_record_missing_zone(self):
|
||||
data = {'name': 'test.example.org.'}
|
||||
|
||||
self.put('domains/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980/records/'
|
||||
'2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980',
|
||||
data=data,
|
||||
status_code=404)
|
||||
|
||||
def test_update_record_invalid_zone_id(self):
|
||||
data = {'name': 'test.example.org.'}
|
||||
|
||||
self.put('domains/2fdadfb1cf964259ac6bbb7b6d2ff980/records/'
|
||||
'2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980',
|
||||
data=data,
|
||||
status_code=404)
|
||||
|
||||
def test_delete_record(self):
|
||||
# Create a record
|
||||
record = self.create_record(self.zone, self.recordset)
|
||||
|
||||
self.delete('domains/%s/records/%s' % (self.zone['id'],
|
||||
record['id']))
|
||||
|
||||
# Simulate the record having been deleted on the backend
|
||||
zone_serial = self.central_service.get_zone(
|
||||
self.admin_context, self.zone['id']).serial
|
||||
self.central_service.update_status(
|
||||
self.admin_context, self.zone['id'], "SUCCESS", zone_serial)
|
||||
|
||||
# Ensure we can no longer fetch the record
|
||||
self.get('domains/%s/records/%s' % (self.zone['id'],
|
||||
record['id']),
|
||||
status_code=404)
|
||||
|
||||
@patch.object(central_service.Service, 'find_zone',
|
||||
side_effect=messaging.MessagingTimeout())
|
||||
def test_delete_record_timeout(self, _):
|
||||
# Create a record
|
||||
record = self.create_record(self.zone, self.recordset)
|
||||
|
||||
self.delete('domains/%s/records/%s' % (self.zone['id'],
|
||||
record['id']),
|
||||
status_code=504)
|
||||
|
||||
def test_delete_record_missing(self):
|
||||
self.delete('domains/%s/records/2fdadfb1-cf96-4259-ac6b-'
|
||||
'bb7b6d2ff980' % self.zone['id'],
|
||||
status_code=404)
|
||||
|
||||
def test_delete_record_missing_zone(self):
|
||||
self.delete('domains/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980/records/'
|
||||
'2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980',
|
||||
status_code=404)
|
||||
|
||||
def test_delete_record_invalid_zone_id(self):
|
||||
self.delete('domains/2fdadfb1cf964259ac6bbb7b6d2ff980/records/'
|
||||
'2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980',
|
||||
status_code=404)
|
||||
|
||||
def test_delete_record_invalid_id(self):
|
||||
self.delete('domains/%s/records/2fdadfb1-cf96-4259-ac6b-'
|
||||
'bb7b6d2ff980GH' % self.zone['id'],
|
||||
status_code=404)
|
||||
|
||||
def test_get_record_in_secondary(self):
|
||||
fixture = self.get_zone_fixture('SECONDARY', 1)
|
||||
fixture['email'] = "root@example.com"
|
||||
|
||||
zone = self.create_zone(**fixture)
|
||||
|
||||
record = self.create_record(zone, self.recordset)
|
||||
|
||||
url = 'zones/%s/records/%s' % (zone.id, record.id)
|
||||
self.get(url, status_code=404)
|
||||
|
||||
def test_create_record_in_secondary(self):
|
||||
fixture = self.get_zone_fixture('SECONDARY', 1)
|
||||
fixture['email'] = "root@example.com"
|
||||
|
||||
zone = self.create_zone(**fixture)
|
||||
|
||||
record = {
|
||||
"name": "foo.%s" % zone.name,
|
||||
"type": "A",
|
||||
"data": "10.0.0.1"
|
||||
}
|
||||
|
||||
url = 'zones/%s/records' % zone.id
|
||||
self.post(url, record, status_code=404)
|
||||
|
||||
def test_update_record_in_secondary(self):
|
||||
fixture = self.get_zone_fixture('SECONDARY', 1)
|
||||
fixture['email'] = "root@example.com"
|
||||
|
||||
zone = self.create_zone(**fixture)
|
||||
|
||||
record = self.create_record(zone, self.recordset)
|
||||
|
||||
url = 'zones/%s/records/%s' % (zone.id, record.id)
|
||||
self.put(url, {"data": "10.0.0.1"}, status_code=404)
|
||||
|
||||
def test_delete_record_in_secondary(self):
|
||||
fixture = self.get_zone_fixture('SECONDARY', 1)
|
||||
fixture['email'] = "root@example.com"
|
||||
|
||||
zone = self.create_zone(**fixture)
|
||||
|
||||
record = self.create_record(zone, self.recordset)
|
||||
|
||||
url = 'zones/%s/records/%s' % (zone.id, record.id)
|
||||
self.delete(url, status_code=404)
|
||||
|
||||
def test_create_record_deleting_zone(self):
|
||||
recordset_fixture = self.get_recordset_fixture(
|
||||
self.zone['name'])
|
||||
|
||||
fixture = self.get_record_fixture(recordset_fixture['type'])
|
||||
fixture.update({
|
||||
'name': recordset_fixture['name'],
|
||||
'type': recordset_fixture['type'],
|
||||
})
|
||||
|
||||
self.delete('/domains/%s' % self.zone['id'])
|
||||
self.post('domains/%s/records' % self.zone['id'],
|
||||
data=fixture, status_code=404)
|
||||
|
||||
def test_update_record_deleting_zone(self):
|
||||
# Create a record
|
||||
record = self.create_record(self.zone, self.recordset)
|
||||
|
||||
# Fetch another fixture to use in the update
|
||||
fixture = self.get_record_fixture(self.recordset['type'], fixture=1)
|
||||
|
||||
# Update the record
|
||||
data = {'data': fixture['data']}
|
||||
self.delete('/domains/%s' % self.zone['id'])
|
||||
self.put('domains/%s/records/%s' % (self.zone['id'],
|
||||
record['id']),
|
||||
data=data, status_code=404)
|
||||
|
||||
def test_delete_record_deleting_zone(self):
|
||||
# Create a record
|
||||
record = self.create_record(self.zone, self.recordset)
|
||||
|
||||
self.delete('/domains/%s' % self.zone['id'])
|
||||
self.delete('domains/%s/records/%s' % (self.zone['id'],
|
||||
record['id']),
|
||||
status_code=404)
|
||||
|
||||
|
||||
class ApiV1TxtRecordsTest(ApiV1Test):
|
||||
def setUp(self):
|
||||
super(ApiV1TxtRecordsTest, self).setUp()
|
||||
|
||||
self.zone = self.create_zone()
|
||||
self.recordset = self.create_recordset(self.zone, 'TXT')
|
||||
|
||||
def test_create_txt_record(self):
|
||||
# See bug #1474012
|
||||
record = self.create_record(self.zone, self.recordset)
|
||||
data = {'data': 'a' * 255}
|
||||
self.put(
|
||||
'domains/%s/records/%s' % (self.zone['id'], record['id']),
|
||||
data=data
|
||||
)
|
||||
|
||||
def test_create_txt_record_too_long(self):
|
||||
# See bug #1474012
|
||||
record = self.create_record(self.zone, self.recordset)
|
||||
data = {'data': 'a' * 256}
|
||||
self.put(
|
||||
'domains/%s/records/%s' % (self.zone['id'], record['id']),
|
||||
data=data,
|
||||
status_code=400
|
||||
)
|
|
@ -1,244 +0,0 @@
|
|||
# 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.
|
||||
from mock import patch
|
||||
import oslo_messaging as messaging
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
|
||||
from designate import exceptions
|
||||
from designate import objects
|
||||
from designate.central import service as central_service
|
||||
from designate.tests.test_api.test_v1 import ApiV1Test
|
||||
|
||||
cfg.CONF.import_opt('default_pool_id',
|
||||
'designate.central',
|
||||
group='service:central')
|
||||
default_pool_id = cfg.CONF['service:central'].default_pool_id
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ApiV1ServersTest(ApiV1Test):
|
||||
def setUp(self):
|
||||
super(ApiV1ServersTest, self).setUp()
|
||||
|
||||
# All Server Checks should be performed as an admin, so..
|
||||
# Override to policy to make everyone an admin.
|
||||
|
||||
self.policy({'admin': '@'})
|
||||
|
||||
def test_get_server_schema(self):
|
||||
response = self.get('schemas/server')
|
||||
self.assertIn('description', response.json)
|
||||
self.assertIn('id', response.json)
|
||||
self.assertIn('title', response.json)
|
||||
self.assertIn('additionalProperties', response.json)
|
||||
self.assertIn('properties', response.json)
|
||||
self.assertIn('name', response.json['properties'])
|
||||
self.assertIn('links', response.json)
|
||||
self.assertIn('created_at', response.json['properties'])
|
||||
self.assertIn('updated_at', response.json['properties'])
|
||||
|
||||
def test_get_servers_schema(self):
|
||||
response = self.get('schemas/servers')
|
||||
self.assertIn('description', response.json)
|
||||
self.assertIn('id', response.json)
|
||||
self.assertIn('title', response.json)
|
||||
self.assertIn('additionalProperties', response.json)
|
||||
self.assertIn('properties', response.json)
|
||||
|
||||
def test_create_server(self):
|
||||
# Create a server
|
||||
# In a base somewhere, we create the default / 0 server fixture
|
||||
# automatically, so this would trigger a duplicate otherwise.
|
||||
fixture = self.get_server_fixture(1)
|
||||
|
||||
response = self.post('servers', data=fixture)
|
||||
|
||||
self.assertIn('id', response.json)
|
||||
self.assertIn('name', response.json)
|
||||
self.assertEqual(response.json['name'], fixture['name'])
|
||||
|
||||
def test_create_server_junk(self):
|
||||
# Create a server
|
||||
fixture = self.get_server_fixture(0)
|
||||
|
||||
# Add a junk property
|
||||
fixture['junk'] = 'Junk Field'
|
||||
|
||||
# Ensure it fails with a 400
|
||||
self.post('servers', data=fixture, status_code=400)
|
||||
|
||||
def test_create_server_with_invalid_name(self):
|
||||
# Create a server
|
||||
fixture = self.get_server_fixture(0)
|
||||
|
||||
# Add an invalid name
|
||||
fixture['name'] = '$#$%^^'
|
||||
|
||||
# Ensure it fails with a 400
|
||||
self.post('servers', data=fixture, status_code=400)
|
||||
|
||||
def test_create_server_name_missing(self):
|
||||
fixture = self.get_server_fixture(0)
|
||||
del fixture['name']
|
||||
self.post('servers', data=fixture, status_code=400)
|
||||
|
||||
def test_create_server_name_too_long(self):
|
||||
fixture = self.get_server_fixture(0)
|
||||
fixture['name'] = 'a' * 255 + '.example.org.'
|
||||
self.post('servers', data=fixture, status_code=400)
|
||||
|
||||
@patch.object(central_service.Service, 'update_pool',
|
||||
side_effect=messaging.MessagingTimeout())
|
||||
def test_create_server_timeout(self, _):
|
||||
# Create a server
|
||||
fixture = self.get_server_fixture(0)
|
||||
|
||||
self.post('servers', data=fixture, status_code=504)
|
||||
|
||||
@patch.object(central_service.Service, 'update_pool',
|
||||
side_effect=exceptions.DuplicateServer())
|
||||
def test_create_server_duplicate(self, _):
|
||||
# Create a server
|
||||
fixture = self.get_server_fixture(0)
|
||||
|
||||
self.post('servers', data=fixture, status_code=409)
|
||||
|
||||
def test_get_servers(self):
|
||||
# Fetch the default pool
|
||||
pool = self.storage.get_pool(self.admin_context, default_pool_id)
|
||||
|
||||
# Fetch the list of servers
|
||||
response = self.get('servers')
|
||||
|
||||
self.assertIn('servers', response.json)
|
||||
self.assertEqual(len(pool.ns_records), len(response.json['servers']))
|
||||
|
||||
# Add a new NS record to the pool
|
||||
pool.ns_records.append(
|
||||
objects.PoolNsRecord(priority=1, hostname='new-ns1.example.org.'))
|
||||
|
||||
# Save the pool to add a new nameserver
|
||||
self.storage.update_pool(self.admin_context, pool)
|
||||
|
||||
# Fetch the list of servers
|
||||
response = self.get('servers')
|
||||
|
||||
self.assertIn('servers', response.json)
|
||||
self.assertEqual(len(pool.ns_records), len(response.json['servers']))
|
||||
|
||||
# Add a new NS record to the pool
|
||||
pool.ns_records.append(
|
||||
objects.PoolNsRecord(priority=1, hostname='new-ns2.example.org.'))
|
||||
|
||||
# Save the pool to add a new nameserver
|
||||
self.storage.update_pool(self.admin_context, pool)
|
||||
|
||||
response = self.get('servers')
|
||||
|
||||
self.assertIn('servers', response.json)
|
||||
self.assertEqual(len(pool.ns_records), len(response.json['servers']))
|
||||
|
||||
@patch.object(central_service.Service, 'get_pool',
|
||||
side_effect=messaging.MessagingTimeout())
|
||||
def test_get_servers_timeout(self, _):
|
||||
self.get('servers', status_code=504)
|
||||
|
||||
def test_get_server(self):
|
||||
# Fetch the default pool
|
||||
pool = self.storage.get_pool(self.admin_context, default_pool_id)
|
||||
|
||||
# Fetch the Server from the pool
|
||||
response = self.get('servers/%s' % pool.ns_records[0].id)
|
||||
|
||||
self.assertIn('id', response.json)
|
||||
self.assertEqual(response.json['id'], pool.ns_records[0]['id'])
|
||||
|
||||
@patch.object(central_service.Service, 'get_pool',
|
||||
side_effect=messaging.MessagingTimeout())
|
||||
def test_get_server_timeout(self, _):
|
||||
# Fetch the default pool
|
||||
pool = self.storage.get_pool(self.admin_context, default_pool_id)
|
||||
|
||||
self.get('servers/%s' % pool.ns_records[0].id, status_code=504)
|
||||
|
||||
def test_get_server_with_invalid_id(self):
|
||||
self.get('servers/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff98GH',
|
||||
status_code=404)
|
||||
|
||||
def test_get_server_missing(self):
|
||||
self.get('servers/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980',
|
||||
status_code=404)
|
||||
|
||||
def test_update_server(self):
|
||||
# Fetch the default pool
|
||||
pool = self.storage.get_pool(self.admin_context, default_pool_id)
|
||||
|
||||
data = {'name': 'new-ns1.example.org.'}
|
||||
|
||||
response = self.put('servers/%s' % pool.ns_records[0].id,
|
||||
data=data)
|
||||
|
||||
self.assertIn('id', response.json)
|
||||
self.assertEqual(response.json['id'], pool.ns_records[0].id)
|
||||
|
||||
self.assertIn('name', response.json)
|
||||
self.assertEqual(response.json['name'], 'new-ns1.example.org.')
|
||||
|
||||
def test_update_server_missing(self):
|
||||
data = {'name': 'test.example.org.'}
|
||||
self.put('servers/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980', data=data,
|
||||
status_code=404)
|
||||
|
||||
def test_update_server_junk(self):
|
||||
# Fetch the default pool
|
||||
pool = self.storage.get_pool(self.admin_context, default_pool_id)
|
||||
|
||||
data = {'name': 'test.example.org.', 'junk': 'Junk Field'}
|
||||
|
||||
self.put('servers/%s' % pool.ns_records[0].id, data=data,
|
||||
status_code=400)
|
||||
|
||||
def test_delete_server(self):
|
||||
# Fetch the default pool
|
||||
pool = self.storage.get_pool(self.admin_context, default_pool_id)
|
||||
|
||||
# Create a second server so that we can delete the first
|
||||
# because the last remaining server is not allowed to be deleted
|
||||
# Add a new NS record to the pool
|
||||
pool.ns_records.append(
|
||||
objects.PoolNsRecord(priority=1, hostname='new-ns2.example.org.'))
|
||||
|
||||
# Save the pool to add a new nameserver
|
||||
self.storage.update_pool(self.admin_context, pool)
|
||||
|
||||
# Now delete the server
|
||||
self.delete('servers/%s' % pool.ns_records[1].id)
|
||||
|
||||
# Ensure we can no longer fetch the deleted server
|
||||
self.get('servers/%s' % pool.ns_records[1].id, status_code=404)
|
||||
|
||||
# Also, verify we cannot delete last remaining server
|
||||
self.delete('servers/%s' % pool.ns_records[0].id, status_code=400)
|
||||
|
||||
def test_delete_server_with_invalid_id(self):
|
||||
self.delete('servers/9fdadfb1-cf96-4259-ac6b-bb7b6d2ff98GH',
|
||||
status_code=404)
|
||||
|
||||
def test_delete_server_missing(self):
|
||||
self.delete('servers/9fdadfb1-cf96-4259-ac6b-bb7b6d2ff980',
|
||||
status_code=404)
|
|
@ -1,136 +0,0 @@
|
|||
# Copyright 2015 NEC Corporation. All rights reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
from mock import patch
|
||||
import oslo_messaging as messaging
|
||||
from oslo_log import log as logging
|
||||
|
||||
from designate.central import service as central_service
|
||||
from designate.tests.test_api.test_v1 import ApiV1Test
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ApiV1TsigkeysTest(ApiV1Test):
|
||||
def setUp(self):
|
||||
super(ApiV1TsigkeysTest, self).setUp()
|
||||
|
||||
# Set the policy to accept everyone as an admin, as this is an
|
||||
# admin-only API
|
||||
self.policy({'admin': '@'})
|
||||
|
||||
def test_get_tsigkey_schema(self):
|
||||
response = self.get('schemas/tsigkey')
|
||||
self.assertIn('description', response.json)
|
||||
self.assertIn('links', response.json)
|
||||
self.assertIn('title', response.json)
|
||||
self.assertIn('id', response.json)
|
||||
self.assertIn('additionalProperties', response.json)
|
||||
self.assertIn('properties', response.json)
|
||||
self.assertIn('name', response.json['properties'])
|
||||
self.assertIn('algorithm', response.json['properties'])
|
||||
self.assertIn('secret', response.json['properties'])
|
||||
|
||||
def test_get_tsigkeys_schema(self):
|
||||
response = self.get('schemas/tsigkeys')
|
||||
self.assertIn('description', response.json)
|
||||
self.assertIn('title', response.json)
|
||||
self.assertIn('id', response.json)
|
||||
self.assertIn('additionalProperties', response.json)
|
||||
self.assertIn('properties', response.json)
|
||||
self.assertIn('tsigkeys', response.json['properties'])
|
||||
|
||||
def test_create_tsigkeys(self):
|
||||
# Create a Tsigkey
|
||||
fixture = self.get_tsigkey_fixture(0)
|
||||
|
||||
# V1 doesn't have these
|
||||
del fixture['scope']
|
||||
del fixture['resource_id']
|
||||
|
||||
response = self.post('tsigkeys', data=fixture)
|
||||
self.assertIn('id', response.json)
|
||||
|
||||
self.assertIn('name', response.json)
|
||||
self.assertEqual(response.json['name'], fixture['name'])
|
||||
|
||||
def test_create_tsigkeys_junk(self):
|
||||
# Create a tsigkey
|
||||
fixture = self.get_tsigkey_fixture(0)
|
||||
|
||||
# Add a junk property
|
||||
fixture['junk'] = 'Junk Field'
|
||||
|
||||
# Ensure it fails with a 400
|
||||
self.post('tsigkeys', data=fixture, status_code=400)
|
||||
|
||||
def test_create_tsigkey_name_missing(self):
|
||||
# Create tsigkey
|
||||
fixture = self.get_tsigkey_fixture(0)
|
||||
|
||||
del fixture['name']
|
||||
|
||||
self.post('tsigkeys', data=fixture, status_code=400)
|
||||
|
||||
def test_create_tsigkey_algorithm_missing(self):
|
||||
# Create tsigkey
|
||||
fixture = self.get_tsigkey_fixture(0)
|
||||
|
||||
del fixture['algorithm']
|
||||
|
||||
self.post('tsigkeys', data=fixture, status_code=400)
|
||||
|
||||
def test_create_tsigkey_secret_missing(self):
|
||||
# Create tsigkey
|
||||
fixture = self.get_tsigkey_fixture(0)
|
||||
|
||||
del fixture['secret']
|
||||
|
||||
self.post('tsigkeys', data=fixture, status_code=400)
|
||||
|
||||
def test_create_tsigkey_name_too_long(self):
|
||||
# Create a tsigkey
|
||||
fixture = self.get_tsigkey_fixture(0)
|
||||
|
||||
fixture['name'] = 'x' * 300
|
||||
|
||||
self.post('tsigkeys', data=fixture, status_code=400)
|
||||
|
||||
def test_create_tsigkey_secret_too_long(self):
|
||||
# Create a tsigkey
|
||||
fixture = self.get_tsigkey_fixture(0)
|
||||
|
||||
fixture['secret'] = 'x' * 300
|
||||
|
||||
self.post('tsigkeys', data=fixture, status_code=400)
|
||||
|
||||
def test_delete_tsigkey(self):
|
||||
# Delete a tsigkey
|
||||
tsigkey = self.create_tsigkey()
|
||||
|
||||
self.delete('/tsigkeys/%s' % tsigkey['id'], status=200)
|
||||
|
||||
@patch.object(central_service.Service, 'find_tsigkeys',
|
||||
side_effect=messaging.MessagingTimeout())
|
||||
def test_get_tsigkeys_timeout(self, _):
|
||||
self.get('tsigkeys', status_code=504)
|
||||
|
||||
@patch.object(central_service.Service, 'find_tsigkeys',
|
||||
side_effect=messaging.MessagingTimeout())
|
||||
def test_get_tsigkey_timeout(self, _):
|
||||
# Create a tsigkey
|
||||
tsigkey = self.create_tsigkey()
|
||||
|
||||
self.get('tsigkeys/%s' % tsigkey['id'], status_code=504)
|
|
@ -19,6 +19,6 @@ from designate import schema
|
|||
|
||||
class TestSchema(TestCase):
|
||||
def test_constructor(self):
|
||||
zone = schema.Schema('v1', 'domain')
|
||||
quota = schema.Schema('admin', 'quota')
|
||||
|
||||
self.assertIsInstance(zone, schema.Schema)
|
||||
self.assertIsInstance(quota, schema.Schema)
|
||||
|
|
|
@ -47,11 +47,6 @@ class TestUtils(TestCase):
|
|||
with testtools.ExpectedException(ValueError):
|
||||
utils.resource_string()
|
||||
|
||||
def test_load_schema(self):
|
||||
schema = utils.load_schema('v1', 'domain')
|
||||
|
||||
self.assertIsInstance(schema, dict)
|
||||
|
||||
def test_load_schema_missing(self):
|
||||
with testtools.ExpectedException(exceptions.ResourceNotFound):
|
||||
utils.load_schema('v1', 'missing')
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
# How many seconds to wait for the API to be responding before giving up
|
||||
API_RESPONDING_TIMEOUT=20
|
||||
|
||||
if ! timeout ${API_RESPONDING_TIMEOUT} sh -c "while ! curl -s http://127.0.0.1:9001/ 2>/dev/null | grep -q 'v1' ; do sleep 1; done"; then
|
||||
if ! timeout ${API_RESPONDING_TIMEOUT} sh -c "while ! curl -s http://127.0.0.1:9001/ 2>/dev/null | grep -q 'v2' ; do sleep 1; done"; then
|
||||
echo "The Designate API failed to respond within ${API_RESPONDING_TIMEOUT} seconds"
|
||||
exit 1
|
||||
fi
|
||||
|
|
|
@ -83,11 +83,9 @@ function configure_designate {
|
|||
|
||||
# API Configuration
|
||||
sudo cp $DESIGNATE_DIR/etc/designate/api-paste.ini $DESIGNATE_APIPASTE_CONF
|
||||
iniset $DESIGNATE_CONF service:api enabled_extensions_v1 $DESIGNATE_ENABLED_EXTENSIONS_V1
|
||||
iniset $DESIGNATE_CONF service:api enabled_extensions_v2 $DESIGNATE_ENABLED_EXTENSIONS_V2
|
||||
iniset $DESIGNATE_CONF service:api enabled_extensions_admin $DESIGNATE_ENABLED_EXTENSIONS_ADMIN
|
||||
iniset $DESIGNATE_CONF service:api api_base_uri $DESIGNATE_SERVICE_PROTOCOL://$DESIGNATE_SERVICE_HOST:$DESIGNATE_SERVICE_PORT/
|
||||
iniset $DESIGNATE_CONF service:api enable_api_v1 $DESIGNATE_ENABLE_API_V1
|
||||
iniset $DESIGNATE_CONF service:api enable_api_v2 $DESIGNATE_ENABLE_API_V2
|
||||
iniset $DESIGNATE_CONF service:api enable_api_admin $DESIGNATE_ENABLE_API_ADMIN
|
||||
|
||||
|
@ -163,7 +161,6 @@ function configure_designate_tempest() {
|
|||
iniset $TEMPEST_CONFIG service_available designate True
|
||||
|
||||
# Tell tempest which APIs are available
|
||||
iniset $TEMPEST_CONFIG dns_feature_enabled api_v1 $DESIGNATE_ENABLE_API_V1
|
||||
iniset $TEMPEST_CONFIG dns_feature_enabled api_v2 $DESIGNATE_ENABLE_API_V2
|
||||
iniset $TEMPEST_CONFIG dns_feature_enabled api_admin $DESIGNATE_ENABLE_API_ADMIN
|
||||
iniset $TEMPEST_CONFIG dns_feature_enabled api_v2_root_recordsets True
|
||||
|
|
|
@ -20,10 +20,8 @@ DESIGNATE_QUOTA_RECORDSET_RECORDS=${DESIGNATE_QUOTA_RECORDSET_RECORDS:-20}
|
|||
DESIGNATE_QUOTA_API_EXPORT_SIZE=${DESIGNATE_QUOTA_API_EXPORT_SIZE:-1000}
|
||||
|
||||
# Default APIs and Extensions
|
||||
DESIGNATE_ENABLE_API_V1=${DESIGNATE_ENABLE_API_V1:-"True"}
|
||||
DESIGNATE_ENABLE_API_V2=${DESIGNATE_ENABLE_API_V2:-"True"}
|
||||
DESIGNATE_ENABLE_API_ADMIN=${DESIGNATE_ENABLE_API_ADMIN:-"True"}
|
||||
DESIGNATE_ENABLED_EXTENSIONS_V1=${DESIGNATE_ENABLED_EXTENSIONS_V1:-"quotas"}
|
||||
DESIGNATE_ENABLED_EXTENSIONS_V2=${DESIGNATE_ENABLED_EXTENSIONS_V2:-""}
|
||||
DESIGNATE_ENABLED_EXTENSIONS_ADMIN=${DESIGNATE_ENABLED_EXTENSIONS_ADMIN:-"quotas"}
|
||||
|
||||
|
|
|
@ -28,17 +28,6 @@ directory.
|
|||
.. index::
|
||||
double: configure; designate
|
||||
|
||||
#. Create the designate.conf file
|
||||
|
||||
::
|
||||
|
||||
$ editor designate.conf
|
||||
|
||||
#. Copy or mirror the configuration from this sample file here:
|
||||
|
||||
.. literalinclude:: ../examples/basic-config-sample.conf
|
||||
:language: ini
|
||||
|
||||
#. You can generate full sample *designate.conf* (if it does not already exist)::
|
||||
|
||||
$ oslo-config-generator --config-file etc/designate/designate-config-generator.conf --output-file /etc/designate/designate.conf
|
||||
|
|
|
@ -1,130 +0,0 @@
|
|||
[DEFAULT]
|
||||
########################
|
||||
## General Configuration
|
||||
########################
|
||||
# Show debugging output in logs (sets DEBUG log level output)
|
||||
debug = True
|
||||
|
||||
# Top-level directory for maintaining designate's state.
|
||||
state_path = $pybasedir/state
|
||||
|
||||
# Use "sudo designate-rootwrap /etc/designate/rootwrap.conf" to use the real
|
||||
# root filter facility.
|
||||
# Change to "sudo" to skip the filtering and just run the command directly
|
||||
# root_helper = sudo
|
||||
|
||||
# Supported record types
|
||||
#supported_record_type = A, AAAA, CNAME, MX, SRV, TXT, SPF, NS, PTR, SSHFP, SOA
|
||||
|
||||
# RabbitMQ Config
|
||||
transport_url = rabbit://designate:designate@127.0.0.1//
|
||||
|
||||
[oslo_messaging_notifications]
|
||||
# Driver used for issuing notifications
|
||||
driver = messaging
|
||||
|
||||
########################
|
||||
## Service Configuration
|
||||
########################
|
||||
#-----------------------
|
||||
# Central Service
|
||||
#-----------------------
|
||||
[service:central]
|
||||
# Maximum domain name length
|
||||
#max_domain_name_len = 255
|
||||
|
||||
# Maximum record name length
|
||||
#max_record_name_len = 255
|
||||
|
||||
#-----------------------
|
||||
# API Service
|
||||
#-----------------------
|
||||
[service:api]
|
||||
# API host:port pairs to listen on
|
||||
listen = 0.0.0.0:9001
|
||||
|
||||
# Authentication strategy to use - can be either "noauth" or "keystone"
|
||||
auth_strategy = noauth
|
||||
|
||||
# Enable API Version 1
|
||||
enable_api_v1 = True
|
||||
|
||||
# Enabled API Version 1 extensions
|
||||
enabled_extensions_v1 = diagnostics, quotas, reports, sync, touch
|
||||
|
||||
# Enable API Version 2
|
||||
enable_api_v2 = True
|
||||
|
||||
# Enabled API Version 2 extensions
|
||||
enabled_extensions_v2 = quotas, reports
|
||||
|
||||
#-----------------------
|
||||
# mDNS Service
|
||||
#-----------------------
|
||||
[service:mdns]
|
||||
#workers = None
|
||||
#host = 0.0.0.0
|
||||
#port = 5354
|
||||
#tcp_backlog = 100
|
||||
|
||||
#-----------------------
|
||||
# Worker Service
|
||||
#-----------------------
|
||||
[service:worker]
|
||||
# Whether to send events to worker instead of Pool Manager
|
||||
enabled = True
|
||||
#workers = None
|
||||
#threads = 1000
|
||||
#threshold_percentage = 100
|
||||
#poll_timeout = 30
|
||||
#poll_retry_interval = 15
|
||||
#poll_max_retries = 10
|
||||
#poll_delay = 5
|
||||
#notify = True
|
||||
|
||||
#-----------------------
|
||||
# Producer Service
|
||||
#-----------------------
|
||||
[service:producer]
|
||||
#workers = None
|
||||
#threads = 1000
|
||||
#enabled_tasks = None
|
||||
#export_synchronous = True
|
||||
|
||||
#------------------------
|
||||
# Deleted domains purging
|
||||
#------------------------
|
||||
[producer_task:domain_purge]
|
||||
#interval = 3600 # 1h
|
||||
#batch_size = 100
|
||||
#time_threshold = 604800 # 7 days
|
||||
|
||||
#------------------------
|
||||
# Delayed zones NOTIFY
|
||||
#------------------------
|
||||
[producer_task:delayed_notify]
|
||||
#interval = 5
|
||||
|
||||
#------------------------
|
||||
# Worker Periodic Recovery
|
||||
#------------------------
|
||||
[producer_task:worker_periodic_recovery]
|
||||
#interval = 120
|
||||
|
||||
########################
|
||||
## Storage Configuration
|
||||
########################
|
||||
#-----------------------
|
||||
# SQLAlchemy Storage
|
||||
#-----------------------
|
||||
[storage:sqlalchemy]
|
||||
# Database connection string - to configure options for a given implementation
|
||||
# like sqlalchemy or other see below
|
||||
connection = mysql+pymysql://root:password@127.0.0.1/designate?charset=utf8
|
||||
#connection_debug = 100
|
||||
#connection_trace = True
|
||||
#sqlite_synchronous = True
|
||||
#idle_timeout = 3600
|
||||
#max_retries = 10
|
||||
#retry_interval = 10
|
||||
|
|
@ -93,9 +93,7 @@ Install and configure components
|
|||
[service:api]
|
||||
listen = 0.0.0.0:9001
|
||||
auth_strategy = keystone
|
||||
enable_api_v1 = True
|
||||
api_base_uri = http://controller:9001/
|
||||
enabled_extensions_v1 = quotas, reports
|
||||
enable_api_v2 = True
|
||||
enabled_extensions_v2 = quotas, reports
|
||||
|
||||
|
|
|
@ -93,9 +93,7 @@ Install and configure components
|
|||
[service:api]
|
||||
listen = 0.0.0.0:9001
|
||||
auth_strategy = keystone
|
||||
enable_api_v1 = True
|
||||
api_base_uri = http://controller:9001/
|
||||
enabled_extensions_v1 = quotas, reports
|
||||
enable_api_v2 = True
|
||||
enabled_extensions_v2 = quotas, reports
|
||||
|
||||
|
|
|
@ -86,9 +86,7 @@ Install and configure components
|
|||
[service:api]
|
||||
listen = 0.0.0.0:9001
|
||||
auth_strategy = keystone
|
||||
enable_api_v1 = True
|
||||
api_base_uri = http://controller:9001/
|
||||
enabled_extensions_v1 = quotas, reports
|
||||
enable_api_v2 = True
|
||||
enabled_extensions_v2 = quotas, reports
|
||||
|
||||
|
|
|
@ -336,138 +336,3 @@ for more information.
|
|||
Designate, the name of a record MUST be a complete host name.
|
||||
|
||||
.. _RFC 2317: https://tools.ietf.org/html/rfc2317
|
||||
|
||||
|
||||
Using the V1 API
|
||||
----------------
|
||||
|
||||
Using the V1 REST interface let's start by creating a domain.
|
||||
|
||||
.. code-block:: http
|
||||
|
||||
POST /v1/domains HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "example.com.",
|
||||
"ttl": 3600,
|
||||
"email": "admin@example.com"
|
||||
}
|
||||
|
||||
This should return the JSON document describing the new domain.
|
||||
|
||||
.. code-block:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
Content-Length: 238
|
||||
Location: http://127.0.0.1:9001/v1/domains/77c4f4aa-b8c9-4df5-af8e-b54e5fcadef7
|
||||
X-Openstack-Request-Id: req-c3f8478d-1665-4b40-9545-9a856fac17ea
|
||||
Date: Fri, 20 Feb 2015 19:35:37 GMT
|
||||
Connection: keep-alive
|
||||
|
||||
|
||||
{
|
||||
"updated_at": null,
|
||||
"ttl": 3600,
|
||||
"serial": 1424460937,
|
||||
"name": "example.com.",
|
||||
"id": "77c4f4aa-b8c9-4df5-af8e-b54e5fcadef7",
|
||||
"email": "admin@example.com",
|
||||
"description": null,
|
||||
"created_at": "2015-02-20T19:35:37.000000"
|
||||
}
|
||||
|
||||
|
||||
Now that we have a domain we want to return when we use our `PTR`
|
||||
record, we'll create the `in-addr.arpa.` domain that will be used when
|
||||
looking up the IP address.
|
||||
|
||||
Let's configure `192.0.2.10` to return our `example.com.` domain
|
||||
name when we do a reverse look up.
|
||||
|
||||
.. code-block:: http
|
||||
|
||||
POST /v1/domains HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "10.2.0.192.in-addr.arpa.",
|
||||
"ttl": 1200,
|
||||
"email": "admin@thedns.com"
|
||||
}
|
||||
|
||||
We should get a response like
|
||||
|
||||
.. code-block:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
Content-Length: 252
|
||||
Location: http://127.0.0.1:9001/v1/domains/d098abaa-37e3-40e5-b7c5-3794b5a0ec32
|
||||
X-Openstack-Request-Id: req-bc2b1796-bd11-47a9-bb06-fd6a870a4bc2
|
||||
Date: Fri, 20 Feb 2015 19:43:15 GMT
|
||||
Connection: keep-alive
|
||||
|
||||
{
|
||||
"updated_at": null,
|
||||
"ttl": 1200,
|
||||
"serial": 1424461395,
|
||||
"name": "10.2.0.192.in-addr.arpa.",
|
||||
"id": "d098abaa-37e3-40e5-b7c5-3794b5a0ec32",
|
||||
"email": "admin@thedns.com",
|
||||
"description": null,
|
||||
"created_at": "2015-02-20T19:43:15.000000"
|
||||
}
|
||||
|
||||
|
||||
We will use this `in-addr.arpa.` domain to create the actual `PTR`
|
||||
record.
|
||||
|
||||
.. code-block:: http
|
||||
|
||||
POST /v1/domains/d098abaa-37e3-40e5-b7c5-3794b5a0ec32/records HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "10.2.0.192.in-addr.arpa.",
|
||||
"type": "PTR",
|
||||
"data": "example.com."
|
||||
}
|
||||
|
||||
Here is the response.
|
||||
|
||||
.. code-block:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
Content-Length: 315
|
||||
Location: http://127.0.0.1:9001/v1/domains/d098abaa-37e3-40e5-b7c5-3794b5a0ec32/records/0476ed89-9823-4f8e-a991-79422bc2e490
|
||||
X-Openstack-Request-Id: req-36588ba6-e91a-4456-9706-8d156ea7cfd2
|
||||
Date: Fri, 20 Feb 2015 19:48:01 GMT
|
||||
Connection: keep-alive
|
||||
|
||||
{
|
||||
"updated_at": null,
|
||||
"type": "PTR",
|
||||
"ttl": null,
|
||||
"priority": null,
|
||||
"name": "11.2.0.192.in-addr.arpa.",
|
||||
"id": "0476ed89-9823-4f8e-a991-79422bc2e490",
|
||||
"domain_id": "d098abaa-37e3-40e5-b7c5-3794b5a0ec32",
|
||||
"description": null,
|
||||
"data": "example.com.",
|
||||
"created_at": "2015-02-20T19:48:01.000000"
|
||||
}
|
||||
|
||||
We should now have a correct `PTR` record assigned in our nameserver
|
||||
that we can test.
|
||||
|
||||
We'll use dig to make sure our reverse lookup is resolving correctly.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ dig @localhost -x 192.0.2.10 +short
|
||||
example.com.
|
||||
|
||||
It worked!
|
||||
|
|
|
@ -12,18 +12,13 @@ example:
|
|||
|
||||
.. code-block:: http
|
||||
|
||||
POST /v2/pools HTTP/1.1
|
||||
POST /v2/zones HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "Example Pool",
|
||||
"ns_records": [
|
||||
{
|
||||
"hostname": "ns1.example.org.",
|
||||
"priority": 1
|
||||
}
|
||||
]
|
||||
"name": "example.org.",
|
||||
"email": "hostmaster@example.org"
|
||||
}
|
||||
|
||||
With this info we can make this request using the cURL_ tool. We'll
|
||||
|
@ -34,8 +29,8 @@ assume we are running Designate on `localhost`.
|
|||
curl -X POST -i \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"name": "ns1.example.org."}' \
|
||||
http://localhost:9001/v1/servers
|
||||
-d '{"name": "example.org.", "email": "hostmaster@example.org"}' \
|
||||
http://localhost:9001/v2/zones
|
||||
|
||||
The `-i` flag is used to dump the response headers as well as the
|
||||
response body.
|
||||
|
@ -64,24 +59,6 @@ These headers work for all APIs
|
|||
API Versions
|
||||
============
|
||||
|
||||
The API has 2 versions - V1 and V2.
|
||||
|
||||
.. note:: V1 has been deprecated since the Kilo release.
|
||||
|
||||
V1 API
|
||||
------
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:glob:
|
||||
|
||||
rest/v1/servers
|
||||
rest/v1/domains
|
||||
rest/v1/records
|
||||
rest/v1/diagnostics
|
||||
rest/v1/quotas
|
||||
rest/v1/reports
|
||||
rest/v1/sync
|
||||
|
||||
V2 API
|
||||
------
|
||||
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
..
|
||||
Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
|
||||
Author: Endre Karlson <endre.karlson@hp.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.
|
||||
|
||||
|
||||
Diagnostics
|
||||
===========
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
*Note*: Diagnostics is an extension and needs to be enabled before it can be
|
||||
used. If Designate returns a 404 error, ensure that the following line has been
|
||||
added to the designate.conf file::
|
||||
|
||||
enabled_extensions_v1 = diagnostic, ...
|
||||
|
||||
Diagnose parts of the system.
|
||||
|
||||
|
||||
Ping a host on a RPC topic
|
||||
--------------------------
|
||||
|
||||
.. http:get:: /diagnostics/ping/(topic)/(host)
|
||||
|
||||
Ping a host on a RPC topic
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /diagnostics/ping/agents/msdns-1 HTTP/1.1
|
||||
Host: example.com
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"host": "rpc-hostname",
|
||||
"status": true,
|
||||
"backend": "msdns",
|
||||
"storage": {"status": true, "message": "..."}
|
||||
}
|
||||
|
||||
:statuscode 200: Success
|
||||
:statuscode 401: Access Denied
|
|
@ -1,293 +0,0 @@
|
|||
Domains
|
||||
=======
|
||||
|
||||
Domain entries are used to generate zones containing RR
|
||||
|
||||
TODO: More detail.
|
||||
|
||||
|
||||
Create Domain
|
||||
-------------
|
||||
|
||||
.. http:post:: /domains
|
||||
|
||||
Create a domain
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /domains HTTP/1.1
|
||||
Host: example.com
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "domain1.com.",
|
||||
"ttl": 3600,
|
||||
"email": "nsadmin@example.org"
|
||||
}
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id": "89acac79-38e7-497d-807c-a011e1310438",
|
||||
"name": "domain1.com.",
|
||||
"ttl": 3600,
|
||||
"serial": 1351800588,
|
||||
"email": "nsadmin@example.org",
|
||||
"created_at": "2012-11-01T20:09:48.094457",
|
||||
"updated_at": null,
|
||||
"description": null
|
||||
}
|
||||
|
||||
|
||||
:form created_at: timestamp
|
||||
:form updated_at: timestamp
|
||||
:form name: domain name
|
||||
:form id: uuid
|
||||
:form ttl: time-to-live numeric value in seconds
|
||||
:form serial: numeric seconds
|
||||
:form email: email address
|
||||
:form description: UTF-8 text field
|
||||
:statuscode 200: Success
|
||||
:statuscode 401: Access Denied
|
||||
:statuscode 400: Invalid Object
|
||||
:statuscode 409: Duplicate Domain
|
||||
|
||||
Get a Domain
|
||||
-------------
|
||||
|
||||
.. http:get:: /domains/(uuid:id)
|
||||
|
||||
Lists a particular domain
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /domains/09494b72-b65b-4297-9efb-187f65a0553e HTTP/1.1
|
||||
Host: example.com
|
||||
Accept: application/json
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id": "09494b72-b65b-4297-9efb-187f65a0553e",
|
||||
"name": "domain1.com.",
|
||||
"ttl": 3600,
|
||||
"serial": 1351800668,
|
||||
"email": "nsadmin@example.org",
|
||||
"created_at": "2012-11-01T20:11:08.000000",
|
||||
"updated_at": null,
|
||||
"description": null
|
||||
}
|
||||
|
||||
:form created_at: timestamp
|
||||
:form updated_at: timestamp
|
||||
:form name: domain name
|
||||
:form id: uuid
|
||||
:form ttl: time-to-live numeric value in seconds
|
||||
:form serial: numeric seconds
|
||||
:form email: email address
|
||||
:form description: UTF-8 text field
|
||||
:statuscode 200: Success
|
||||
:statuscode 401: Access Denied
|
||||
|
||||
Update a Domain
|
||||
---------------
|
||||
|
||||
.. http:put:: /domains/(uuid:id)
|
||||
|
||||
updates a domain
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
PUT /domains/09494b72-b65b-4297-9efb-187f65a0553e HTTP/1.1
|
||||
Host: example.com
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "domainnamex.com",
|
||||
"ttl": 7200,
|
||||
"email": "nsadmin@example.org"
|
||||
}
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
Content-Length: 422
|
||||
Date: Fri, 02 Nov 2012 01:06:19 GMT
|
||||
|
||||
{
|
||||
"id": "09494b72-b65b-4297-9efb-187f65a0553e",
|
||||
"name": "domain1.com.",
|
||||
"email": "nsadmin@example.org",
|
||||
"ttl": 7200,
|
||||
"serial": 1351818367,
|
||||
"created_at": "2012-11-02T00:58:42.000000",
|
||||
"updated_at": "2012-11-02T01:06:07.000000",
|
||||
"description": null
|
||||
}
|
||||
|
||||
:form created_at: timestamp
|
||||
:form updated_at: timestamp
|
||||
:form name: domain name
|
||||
:form id: uuid
|
||||
:form ttl: time-to-live numeric value in seconds
|
||||
:form serial: numeric seconds
|
||||
:form email: email address
|
||||
:form description: UTF-8 text field
|
||||
:statuscode 200: Success
|
||||
:statuscode 401: Access Denied
|
||||
:statuscode 400: Invalid Object
|
||||
:statuscode 400: Domain not found
|
||||
:statuscode 409: Duplicate Domain
|
||||
|
||||
Delete a Domain
|
||||
---------------
|
||||
|
||||
.. http:delete:: /domains/(uuid:id)
|
||||
|
||||
delete a domain
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
DELETE /domains/09494b72-b65b-4297-9efb-187f65a0553e HTTP/1.1
|
||||
Host: example.com
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: text/html; charset=utf-8
|
||||
Content-Length: 0
|
||||
Date: Fri, 02 Nov 2012 01:26:06 GMT
|
||||
|
||||
:statuscode 200: Success
|
||||
:statuscode 401: Access Denied
|
||||
:statuscode 400: Invalid Object
|
||||
:statuscode 404: Domain not found
|
||||
|
||||
Get Servers Hosting a Domain
|
||||
----------------------------
|
||||
|
||||
.. http:get:: /domains/(uuid:id)/servers
|
||||
|
||||
Lists the nameservers hosting a particular domain
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /domains/09494b72-b65b-4297-9efb-187f65a0553e/servers HTTP/1.1
|
||||
Host: example.com
|
||||
Accept: application/json
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
[
|
||||
{
|
||||
"id": "384a9b20-239c-11e2-81c1-0800200c9a66",
|
||||
"name": "ns1.provider.com.",
|
||||
"created_at": "2011-01-21T11:33:21Z",
|
||||
"updated_at": null
|
||||
},
|
||||
{
|
||||
"id": "cf661142-e577-40b5-b3eb-75795cdc0cd7",
|
||||
"name": "ns2.provider.com.",
|
||||
"created_at": "2011-01-21T11:33:21Z",
|
||||
"updated_at": "2011-01-21T11:33:21Z"
|
||||
}
|
||||
]
|
||||
|
||||
:form id: UUID server_id
|
||||
:form name: Server hostname
|
||||
:form created_at: timestamp
|
||||
:form updated_at: timestamp
|
||||
:statuscode 200: Success
|
||||
:statuscode 401: Access Denied
|
||||
:statuscode 404: Domain Not Found
|
||||
|
||||
List Domains
|
||||
------------
|
||||
|
||||
.. http:get:: /domains
|
||||
|
||||
Lists all domains
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /domains HTTP/1.1
|
||||
Host: example.com
|
||||
Accept: application/json
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"domains": [
|
||||
{
|
||||
"name": "domain1.com.",
|
||||
"created_at": "2012-11-01T20:11:08.000000",
|
||||
"email": "nsadmin@example.org",
|
||||
"ttl": 3600,
|
||||
"serial": 1351800668,
|
||||
"id": "09494b72-b65b-4297-9efb-187f65a0553e"
|
||||
},
|
||||
{
|
||||
"name": "domain2.com.",
|
||||
"created_at": "2012-11-01T20:09:48.000000",
|
||||
"email": "nsadmin@example.org",
|
||||
"ttl": 3600,
|
||||
"serial": 1351800588,
|
||||
"id": "89acac79-38e7-497d-807c-a011e1310438"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
:form name: domain name
|
||||
:form created_at: timestamp
|
||||
:form email: email address
|
||||
:form ttl: time-to-live numeric value in seconds
|
||||
:form serial: numeric seconds
|
||||
:param id: Domain ID
|
||||
:type id: uuid
|
||||
:statuscode 200: Success
|
||||
:statuscode 401: Access Denied
|
||||
|
|
@ -1,141 +0,0 @@
|
|||
..
|
||||
Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
|
||||
Author: Endre Karlson <endre.karlson@hp.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.
|
||||
|
||||
Quotas
|
||||
======
|
||||
|
||||
Overview
|
||||
--------
|
||||
The quotas extension can be used to retrieve a tenant's absolute limits.
|
||||
|
||||
*Note*: Quotas is an extension and needs to be enabled before it can be used.
|
||||
If Designate returns a 404 error, ensure that the following line has been
|
||||
added to the designate.conf file::
|
||||
|
||||
enabled_extensions_v1 = quotas, ...
|
||||
|
||||
Once this line has been added, restart the designate-central and designate-api
|
||||
services.
|
||||
|
||||
Get Quotas
|
||||
----------
|
||||
|
||||
.. http:get:: /quotas/TENANT_ID
|
||||
|
||||
Retrieves quotas for tenant with the specified TENANT_ID. The
|
||||
following example retrieves the quotas for tenant 12345.
|
||||
|
||||
**Example request:**
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /v1/quotas/12345 HTTP/1.1
|
||||
Host: 127.0.0.1:9001
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
|
||||
**Example response:**
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 201 Created
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"api_export_size": 1000,
|
||||
"domains": 10,
|
||||
"recordset_records": 20,
|
||||
"domain_records": 500,
|
||||
"domain_recordsets": 500
|
||||
}
|
||||
|
||||
:from api_export_size: Number of recordsets allowed in a zone export
|
||||
:form domains: Number of domains the tenant is allowed to own
|
||||
:form recordset_records: Number of records allowed per recordset
|
||||
:form domain_records: Number of records allowed per domain
|
||||
:form domain_recordsets: Number of recordsets allowed per domain
|
||||
|
||||
:statuscode 200: Success
|
||||
:statuscode 401: Access Denied
|
||||
|
||||
Update Quotas
|
||||
-------------
|
||||
|
||||
.. http:put:: /quotas/TENANT_ID
|
||||
|
||||
Updates the specified quota(s) to their new values.
|
||||
|
||||
**Example request:**
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
PUT /v1/quotas/12345 HTTP/1.1
|
||||
Host: 127.0.0.1:9001
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"domains": 1000,
|
||||
"domain_records": 50
|
||||
}
|
||||
|
||||
|
||||
**Example response:**
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"api_export_size": 1000,
|
||||
"domains": 1000,
|
||||
"recordset_records": 20,
|
||||
"domain_records": 50,
|
||||
"domain_recordsets": 500
|
||||
}
|
||||
|
||||
:statuscode 200: Success
|
||||
:statuscode 401: Access Denied
|
||||
|
||||
Reset Quotas to Default
|
||||
-----------------------
|
||||
|
||||
.. http:delete:: /quotas/TENANT_ID
|
||||
|
||||
Restores the tenant's quotas back to their default values.
|
||||
|
||||
**Example request:**
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
DELETE /v1/quotas/12345 HTTP/1.1
|
||||
Host: 127.0.0.1:9001
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
|
||||
**Example response:**
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 Success
|
||||
|
||||
:statuscode 200: Success
|
||||
:statuscode 401: Access Denied
|
||||
|
|
@ -1,832 +0,0 @@
|
|||
Records
|
||||
=======
|
||||
|
||||
Resource record entries are used to generate records within a zone
|
||||
|
||||
TODO: More detail.
|
||||
|
||||
.. note:: V1 API has been deprecated since the Kilo release.
|
||||
|
||||
.. note:: The "description" field on Records cannot be accessed from the V2
|
||||
API. Likewise, the "description" field on Record Sets cannot be accessed
|
||||
from the V1 API.
|
||||
|
||||
|
||||
|
||||
Create Record
|
||||
-------------
|
||||
|
||||
.. http:post:: /domains/(uuid:domain_id)/records
|
||||
|
||||
Create an A record for a domain
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /domains/89acac79-38e7-497d-807c-a011e1310438/records HTTP/1.1
|
||||
Host: example.com
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "www.example.com.",
|
||||
"type": "A",
|
||||
"data": "192.0.2.3"
|
||||
}
|
||||
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
Content-Length: 399
|
||||
Location: http://localhost:9001/v1/domains/89acac79-38e7-497d-807c-a011e1310438/records/2e32e609-3a4f-45ba-bdef-e50eacd345ad
|
||||
Date: Fri, 02 Nov 2012 19:56:26 GMT
|
||||
|
||||
{
|
||||
"id": "2e32e609-3a4f-45ba-bdef-e50eacd345ad",
|
||||
"name": "www.example.com.",
|
||||
"type": "A",
|
||||
"created_at": "2012-11-02T19:56:26.366792",
|
||||
"updated_at": null,
|
||||
"domain_id": "89acac79-38e7-497d-807c-a011e1310438",
|
||||
"ttl": null,
|
||||
"priority": null,
|
||||
"data": "192.0.2.3",
|
||||
"description": null
|
||||
}
|
||||
|
||||
|
||||
:param domain_id: domain ID
|
||||
:form id: record ID
|
||||
:form name: name of record FQDN
|
||||
:form type: type of record
|
||||
:form created_at: timestamp
|
||||
:form updated_at: timestamp
|
||||
:form ttl: time-to-live numeric value in seconds
|
||||
:form data: IPv4 address
|
||||
:form domain_id: domain ID
|
||||
:form priority: must be null for 'A' record
|
||||
:form description: UTF-8 text field
|
||||
:statuscode 200: Success
|
||||
:statuscode 401: Access Denied
|
||||
:statuscode 400: Invalid Object
|
||||
:statuscode 404: Not Found
|
||||
:statuscode 409: Duplicate Record
|
||||
|
||||
Create a AAAA record for a domain
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /domains/89acac79-38e7-497d-807c-a011e1310438/records HTTP/1.1
|
||||
Host: example.com
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "www.example.com.",
|
||||
"type": "AAAA",
|
||||
"data": "2001:db8:0:1234:0:5678:9:12"
|
||||
}
|
||||
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
Content-Length: 303
|
||||
Location: http://localhost:9001/v1/domains/89acac79-38e7-497d-807c-a011e1310438/records/11112222-3333-4444-5555-666677778888
|
||||
Date: Fri, 02 Nov 2012 19:56:26 GMT
|
||||
|
||||
{
|
||||
"id": "11112222-3333-4444-5555-666677778888",
|
||||
"name": "www.example.com.",
|
||||
"type": "AAAA",
|
||||
"created_at": "2013-01-07T00:00:00.000000",
|
||||
"updated_at": null,
|
||||
"domain_id": "89acac79-38e7-497d-807c-a011e1310438",
|
||||
"priority": null,
|
||||
"ttl": null,
|
||||
"data": "2001:db8:0:1234:0:5678:9:12",
|
||||
"description": null
|
||||
}
|
||||
|
||||
|
||||
:param domain_id: domain ID
|
||||
:form id: record ID
|
||||
:form name: name of record FQDN
|
||||
:form type: type of record
|
||||
:form created_at: timestamp
|
||||
:form updated_at: timestamp
|
||||
:form ttl: time-to-live numeric value in seconds
|
||||
:form data: IPv6 address
|
||||
:form domain_id: domain ID
|
||||
:form priority: must be null for 'AAAA' records
|
||||
:form description: UTF-8 text field
|
||||
:statuscode 200: Success
|
||||
:statuscode 401: Access Denied
|
||||
:statuscode 400: Invalid Object
|
||||
:statuscode 404: Not Found
|
||||
:statuscode 409: Duplicate Record
|
||||
|
||||
|
||||
Create an MX record for a domain
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /domains/89acac79-38e7-497d-807c-a011e1310438/records HTTP/1.1
|
||||
Host: example.com
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "example.com.",
|
||||
"type": "MX",
|
||||
"data": "mail.example.com.",
|
||||
"priority": 10
|
||||
}
|
||||
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
Content-Length: 420
|
||||
Location: http://localhost:9001/v1/domains/89acac79-38e7-497d-807c-a011e1310438/records/11112222-3333-4444-5555-666677778888
|
||||
Date: Fri, 02 Nov 2012 19:56:26 GMT
|
||||
|
||||
{
|
||||
"id": "11112222-3333-4444-5555-666677778888",
|
||||
"name": "www.example.com.",
|
||||
"type": "MX",
|
||||
"created_at": "2013-01-07T00:00:00.000000",
|
||||
"updated_at": null,
|
||||
"domain_id": "89acac79-38e7-497d-807c-a011e1310438",
|
||||
"priority": 10,
|
||||
"ttl": null,
|
||||
"data": "mail.example.com.",
|
||||
"description": null
|
||||
}
|
||||
|
||||
|
||||
:param domain_id: domain ID
|
||||
:form id: record ID
|
||||
:form name: name of record FQDN
|
||||
:form type: type of record
|
||||
:form created_at: timestamp
|
||||
:form ttl: time-to-live numeric value in seconds
|
||||
:form data: value of record
|
||||
:form domain_id: domain ID
|
||||
:form priority: priority of MX record
|
||||
:form description: UTF-8 text field
|
||||
:statuscode 200: Success
|
||||
:statuscode 401: Access Denied
|
||||
:statuscode 400: Invalid Object
|
||||
:statuscode 404: Not Found
|
||||
:statuscode 409: Duplicate Record
|
||||
|
||||
Create a CNAME record for a domain
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /domains/89acac79-38e7-497d-807c-a011e1310438/records HTTP/1.1
|
||||
Host: example.com
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "www.example.com.",
|
||||
"type": "CNAME",
|
||||
"data": "example.com."
|
||||
}
|
||||
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
Content-Length: 303
|
||||
Location: http://localhost:9001/v1/domains/89acac79-38e7-497d-807c-a011e1310438/records/11112222-3333-4444-5555-666677778889
|
||||
Date: Fri, 02 Nov 2012 19:56:26 GMT
|
||||
|
||||
{
|
||||
"id": "11112222-3333-4444-5555-666677778889",
|
||||
"name": "www.example.com.",
|
||||
"type": "CNAME",
|
||||
"created_at": "2013-01-07T00:00:00.000000",
|
||||
"updated_at": null,
|
||||
"domain_id": "89acac79-38e7-497d-807c-a011e1310438",
|
||||
"priority": null,
|
||||
"ttl": null,
|
||||
"data": "example.com.",
|
||||
"description": null
|
||||
}
|
||||
|
||||
|
||||
:param domain_id: domain ID
|
||||
:form id: record ID
|
||||
:form name: alias for the CNAME
|
||||
:form type: type of record
|
||||
:form created_at: timestamp
|
||||
:form updated_at: timestamp
|
||||
:form ttl: time-to-live numeric value in seconds
|
||||
:form data: CNAME
|
||||
:form domain_id: domain ID
|
||||
:form priority: must be null for 'CNAME' records
|
||||
:form description: UTF-8 text field
|
||||
:statuscode 200: Success
|
||||
:statuscode 401: Access Denied
|
||||
:statuscode 400: Invalid Object
|
||||
:statuscode 404: Not Found
|
||||
:statuscode 409: Duplicate Record
|
||||
|
||||
Create a TXT record for a domain
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /domains/89acac79-38e7-497d-807c-a011e1310438/records HTTP/1.1
|
||||
Host: example.com
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "www.example.com.",
|
||||
"type": "TXT",
|
||||
"data": "This is a TXT record"
|
||||
}
|
||||
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
Content-Length: 303
|
||||
Location: http://localhost:9001/v1/domains/89acac79-38e7-497d-807c-a011e1310438/records/11112222-3333-4444-5555-666677778899
|
||||
Date: Fri, 02 Nov 2012 19:56:26 GMT
|
||||
|
||||
{
|
||||
"id": "11112222-3333-4444-5555-666677778899",
|
||||
"name": "www.example.com.",
|
||||
"type": "TXT",
|
||||
"created_at": "2013-01-07T00:00:00.000000",
|
||||
"updated_at": null,
|
||||
"domain_id": "89acac79-38e7-497d-807c-a011e1310438",
|
||||
"priority": null,
|
||||
"ttl": null,
|
||||
"data": "This is a TXT record",
|
||||
"description": null
|
||||
}
|
||||
|
||||
|
||||
:param domain_id: domain ID
|
||||
:form id: record ID
|
||||
:form name: name of record FQDN
|
||||
:form type: type of record
|
||||
:form created_at: timestamp
|
||||
:form updated_at: timestamp
|
||||
:form ttl: time-to-live numeric value in seconds
|
||||
:form data: Text associated with record.
|
||||
:form domain_id: domain ID
|
||||
:form priority: must be null for 'TXT' records
|
||||
:form description: UTF-8 text field
|
||||
:statuscode 200: Success
|
||||
:statuscode 401: Access Denied
|
||||
:statuscode 400: Invalid Object
|
||||
:statuscode 404: Not Found
|
||||
:statuscode 409: Duplicate Record
|
||||
|
||||
Create an SRV record for a domain
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /domains/89acac79-38e7-497d-807c-a011e1310438/records HTTP/1.1
|
||||
Host: example.com
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "_sip._tcp.example.com.",
|
||||
"type": "SRV",
|
||||
"data": "0 5060 sip.example.com.",
|
||||
"priority": 30
|
||||
}
|
||||
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
Content-Length: 399
|
||||
Location: http://localhost:9001/v1/domains/89acac79-38e7-497d-807c-a011e1310438/records/11112222-3333-4444-5555-666677778999
|
||||
Date: Fri, 02 Nov 2012 19:56:26 GMT
|
||||
|
||||
{
|
||||
"id": "11112222-3333-4444-5555-66667777899",
|
||||
"name": "_sip._tcp.example.com.",
|
||||
"type": "SRV",
|
||||
"created_at": "2012-11-02T19:56:26.366792",
|
||||
"updated_at": null,
|
||||
"domain_id": "89acac79-38e7-497d-807c-a011e1310438",
|
||||
"ttl": null,
|
||||
"priority" : 30,
|
||||
"data": "0 5060 sip.example.com.",
|
||||
"description": null
|
||||
}
|
||||
|
||||
|
||||
:param domain_id: domain ID
|
||||
:form id: record ID
|
||||
:form name: name of service
|
||||
:form type: type of record
|
||||
:form created_at: timestamp
|
||||
:form updated_at: timestamp
|
||||
:form ttl: time-to-live numeric value in seconds
|
||||
:form data: weight port target
|
||||
:form domain_id: domain ID
|
||||
:form priority: priority of SRV record
|
||||
:form description: UTF-8 text field
|
||||
:statuscode 200: Success
|
||||
:statuscode 401: Access Denied
|
||||
:statuscode 400: Invalid Object
|
||||
:statuscode 404: Not Found
|
||||
:statuscode 409: Duplicate Record
|
||||
|
||||
Create an NS record for a domain
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /domains/89acac79-38e7-497d-807c-a011e1310438/records HTTP/1.1
|
||||
Host: example.com
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": ".example.com.",
|
||||
"type": "NS",
|
||||
"data": "ns1.example.com."
|
||||
}
|
||||
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
Content-Length: 399
|
||||
Location: http://localhost:9001/v1/domains/89acac79-38e7-497d-807c-a011e1310438/records/11112222-3333-4444-5555-666677789999
|
||||
Date: Fri, 02 Nov 2012 19:56:26 GMT
|
||||
|
||||
{
|
||||
"id": "11112222-3333-4444-5555-666677789999",
|
||||
"name": ".example.com.",
|
||||
"type": "NS",
|
||||
"created_at": "2012-11-02T19:56:26.366792",
|
||||
"updated_at": null,
|
||||
"domain_id": "89acac79-38e7-497d-807c-a011e1310438",
|
||||
"ttl": null,
|
||||
"priority" : null,
|
||||
"data": "ns1.example.com",
|
||||
"description": null
|
||||
}
|
||||
|
||||
|
||||
:param domain_id: domain ID
|
||||
:form id: record ID
|
||||
:form name: record name
|
||||
:form type: type of record
|
||||
:form created_at: timestamp
|
||||
:form updated_at: timestamps
|
||||
:form ttl: time-to-live numeric value in seconds
|
||||
:form data: record value
|
||||
:form domain_id: domain ID
|
||||
:form priority: must be null for 'NS' record
|
||||
:form description: UTF-8 text field
|
||||
:statuscode 200: Success
|
||||
:statuscode 401: Access Denied
|
||||
:statuscode 400: Invalid Object
|
||||
:statuscode 404: Not Found
|
||||
:statuscode 409: Duplicate Record
|
||||
|
||||
Create a PTR record for a domain
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /domains/89acac79-38e7-497d-807c-a011e1310438/records HTTP/1.1
|
||||
Host: 2.3.192.in-addr.arpa.
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "1.2.3.192.in-addr.arpa.",
|
||||
"type": "PTR",
|
||||
"data": "www.example.com."
|
||||
}
|
||||
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
Content-Length: 399
|
||||
Location: http://localhost:9001/v1/domains/89acac79-38e7-497d-807c-a011e1310438/records/11112222-3333-4444-5555-666677889999
|
||||
Date: Fri, 02 Nov 2012 19:56:26 GMT
|
||||
|
||||
{
|
||||
"id": "11112222-3333-4444-5555-666677889999",
|
||||
"name": "1.2.3.192.in-addr.arpa.",
|
||||
"type": "PTR",
|
||||
"created_at": "2012-11-02T19:56:26.366792",
|
||||
"updated_at": null,
|
||||
"domain_id": "89acac79-38e7-497d-807c-a011e1310438",
|
||||
"ttl": null,
|
||||
"priority" : null,
|
||||
"data": "www.example.com",
|
||||
"description": null
|
||||
}
|
||||
|
||||
|
||||
:param domain_id: domain ID
|
||||
:form id: record ID
|
||||
:form name: PTR record name
|
||||
:form type: type of record
|
||||
:form created_at: timestamp
|
||||
:form updated_at: timestamp
|
||||
:form ttl: time-to-live numeric value in seconds
|
||||
:form data: DNS record value
|
||||
:form domain_id: domain ID
|
||||
:form priority: must be null for 'PTR' record
|
||||
:form description: UTF-8 text field
|
||||
:statuscode 200: Success
|
||||
:statuscode 401: Access Denied
|
||||
:statuscode 400: Invalid Object
|
||||
:statuscode 404: Not Found
|
||||
:statuscode 409: Duplicate Record
|
||||
|
||||
Create an SPF record for a domain
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /domains/89acac79-38e7-497d-807c-a011e1310438/records HTTP/1.1
|
||||
Host: example.com
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": ".example.com.",
|
||||
"type": "SPF",
|
||||
"data": "v=spf1 +mx a:colo.example.com/28 -all"
|
||||
}
|
||||
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
Content-Length: 399
|
||||
Location: http://localhost:9001/v1/domains/89acac79-38e7-497d-807c-a011e1310438/records/11112222-3333-4444-5555-666678889999
|
||||
Date: Fri, 02 Nov 2012 19:56:26 GMT
|
||||
|
||||
{
|
||||
"id": "11112222-3333-4444-5555-666678889999",
|
||||
"name": ".example.com.",
|
||||
"type": "SPF",
|
||||
"created_at": "2012-11-02T19:56:26.366792",
|
||||
"updated_at": null,
|
||||
"domain_id": "89acac79-38e7-497d-807c-a011e1310438",
|
||||
"ttl": null,
|
||||
"priority" : null,
|
||||
"data": "v=spf1 +mx a:colo.example.com/28 -all",
|
||||
"description": null
|
||||
}
|
||||
|
||||
|
||||
:param domain_id: domain ID
|
||||
:form id: record ID
|
||||
:form name: name of record
|
||||
:form type: type of record
|
||||
:form created_at: timestamp
|
||||
:form updated_at: timestamp
|
||||
:form ttl: time-to-live numeric value in seconds
|
||||
:form data: record value
|
||||
:form domain_id: domain ID
|
||||
:form priority: must be null for 'SPF' record
|
||||
:form description: UTF-8 text field
|
||||
:statuscode 200: Success
|
||||
:statuscode 401: Access Denied
|
||||
:statuscode 400: Invalid Object
|
||||
:statuscode 404: Not Found
|
||||
:statuscode 409: Duplicate Record
|
||||
|
||||
Create an SSHFP record for a domain
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /domains/89acac79-38e7-497d-807c-a011e1310438/records HTTP/1.1
|
||||
Host: example.com
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "www.example.com.",
|
||||
"type": "SSHFP",
|
||||
"data": "2 1 6c3c958af43d953f91f40e0d84157f4fe7b4a898"
|
||||
}
|
||||
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
Content-Length: 399
|
||||
Location: http://localhost:9001/v1/domains/89acac79-38e7-497d-807c-a011e1310438/records/11112222-3333-4444-5555-666778889999
|
||||
Date: Fri, 02 Nov 2012 19:56:26 GMT
|
||||
|
||||
{
|
||||
"id": "11112222-3333-4444-5555-666778889999",
|
||||
"name": "www.example.com.",
|
||||
"type": "SSHFP",
|
||||
"created_at": "2012-11-02T19:56:26.366792",
|
||||
"updated_at": null,
|
||||
"domain_id": "89acac79-38e7-497d-807c-a011e1310438",
|
||||
"ttl": null,
|
||||
"priority" : null,
|
||||
"data": "2 1 6c3c958af43d953f91f40e0d84157f4fe7b4a898",
|
||||
"description": null
|
||||
}
|
||||
|
||||
|
||||
:param domain_id: domain ID
|
||||
:form id: record ID
|
||||
:form name: name of record
|
||||
:form type: type of record
|
||||
:form created_at: timestamp
|
||||
:form updated_at: timestamp
|
||||
:form ttl: time-to-live numeric value in seconds
|
||||
:form data: algorithm number, fingerprint type, fingerprint
|
||||
:form domain_id: domain ID
|
||||
:form priority: must be null for 'SSHFP' record
|
||||
:form description: UTF-8 text field
|
||||
:statuscode 200: Success
|
||||
:statuscode 401: Access Denied
|
||||
:statuscode 400: Invalid Object
|
||||
:statuscode 404: Not Found
|
||||
:statuscode 409: Duplicate Record
|
||||
|
||||
|
||||
Get a Record
|
||||
-------------
|
||||
|
||||
.. http:get:: /domains/(uuid:domain_id)/records/(uuid:id)
|
||||
|
||||
Get a particular record
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /domains/09494b72b65b42979efb187f65a0553e/records/2e32e609-3a4f-45ba-bdef-e50eacd345ad HTTP/1.1
|
||||
Host: example.com
|
||||
Accept: application/json
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id": "2e32e609-3a4f-45ba-bdef-e50eacd345ad",
|
||||
"name": "www.example.com.",
|
||||
"type": "A",
|
||||
"created_at": "2012-11-02T19:56:26.366792",
|
||||
"updated_at": "2012-11-04T13:22:36.859786",
|
||||
"priority": null,
|
||||
"ttl": 3600,
|
||||
"data": "15.185.172.153",
|
||||
"domain_id": "89acac79-38e7-497d-807c-a011e1310438",
|
||||
"description": null
|
||||
}
|
||||
|
||||
:param domain_id: Domain ID
|
||||
:param id: Record ID
|
||||
:form id: record ID
|
||||
:form name: name of record FQDN
|
||||
:form type: type of record
|
||||
:form created_at: timestamp
|
||||
:form updated_at: timestamp
|
||||
:form priority: priority of record
|
||||
:form ttl: time-to-live numeric value in seconds
|
||||
:form data: value of record
|
||||
:form description: UTF-8 text field
|
||||
:form domain_id: domain ID
|
||||
:statuscode 200: Success
|
||||
:statuscode 401: Access Denied
|
||||
:statuscode 404: Record Not Found
|
||||
|
||||
Update a record
|
||||
---------------
|
||||
|
||||
.. http:put:: /domains/(uuid:domain_id)/records/(uuid:id)
|
||||
|
||||
Updates a record
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
PUT /domains/89acac79-38e7-497d-807c-a011e1310438/records/2e32e609-3a4f-45ba-bdef-e50eacd345ad HTTP/1.1
|
||||
Host: example.com
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "www.example.com.",
|
||||
"type": "A",
|
||||
"data": "192.0.2.5"
|
||||
}
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
Content-Length: 446
|
||||
Date: Sun, 04 Nov 2012 13:22:36 GMT
|
||||
|
||||
{
|
||||
"id": "2e32e609-3a4f-45ba-bdef-e50eacd345ad",
|
||||
"name": "www.example.com.",
|
||||
"type": "A",
|
||||
"created_at": "2012-11-02T19:56:26.366792",
|
||||
"updated_at": "2012-11-04T13:22:36.859786",
|
||||
"priority": null,
|
||||
"ttl": 3600,
|
||||
"data": "192.0.2.5",
|
||||
"domain_id": "89acac79-38e7-497d-807c-a011e1310438",
|
||||
"description": null
|
||||
}
|
||||
|
||||
:param domain_id: domain ID
|
||||
:param id: record ID
|
||||
:form id: record ID
|
||||
:form name: name of record FQDN
|
||||
:form type: type of record
|
||||
:form created_at: timestamp
|
||||
:form updated_at: timestamp
|
||||
:form priority: priority of record
|
||||
:form ttl: time-to-live numeric value in seconds
|
||||
:form data: value of record
|
||||
:form description: UTF-8 text field
|
||||
:form domain_id: domain ID
|
||||
:statuscode 200: Success
|
||||
:statuscode 401: Access Denied
|
||||
:statuscode 400: Invalid Object
|
||||
:statuscode 409: Duplicate Record
|
||||
|
||||
Delete a record
|
||||
---------------
|
||||
|
||||
.. http:delete:: /domains/(uuid:domain_id)/records/(uuid:id)
|
||||
|
||||
Delete a DNS resource record
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
DELETE /domains/89acac79-38e7-497d-807c-a011e1310438/records/4ad19089-3e62-40f8-9482-17cc8ccb92cb HTTP/1.1
|
||||
|
||||
:param domain_id: domain ID
|
||||
:param id: record ID
|
||||
|
||||
**Example response**:
|
||||
|
||||
Content-Type: text/html; charset=utf-8
|
||||
Content-Length: 0
|
||||
Date: Sun, 04 Nov 2012 14:35:57 GMT
|
||||
|
||||
|
||||
List Records in a Domain
|
||||
------------------------
|
||||
|
||||
.. http:get:: /domains/(uuid:domain_id)/records
|
||||
|
||||
Lists records of a domain
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /domains/89acac79-38e7-497d-807c-a011e1310438/records HTTP/1.1
|
||||
Host: example.com
|
||||
Accept: application/json
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: guess
|
||||
|
||||
Content-Type: application/json
|
||||
Content-Length: 1209
|
||||
Date: Sun, 04 Nov 2012 13:58:21 GMT
|
||||
|
||||
{
|
||||
"records": [
|
||||
{
|
||||
"id": "2e32e609-3a4f-45ba-bdef-e50eacd345ad",
|
||||
"name": "www.example.com.",
|
||||
"type": "A",
|
||||
"ttl": 3600,
|
||||
"created_at": "2012-11-02T19:56:26.000000",
|
||||
"updated_at": "2012-11-04T13:22:36.000000",
|
||||
"data": "15.185.172.153",
|
||||
"domain_id": "89acac79-38e7-497d-807c-a011e1310438",
|
||||
"tenant_id": null,
|
||||
"priority": null,
|
||||
"description": null,
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "8e9ecf3e-fb92-4a3a-a8ae-7596f167bea3",
|
||||
"name": "host1.example.com.",
|
||||
"type": "A",
|
||||
"ttl": 3600,
|
||||
"created_at": "2012-11-04T13:57:50.000000",
|
||||
"updated_at": null,
|
||||
"data": "15.185.172.154",
|
||||
"domain_id": "89acac79-38e7-497d-807c-a011e1310438",
|
||||
"tenant_id": null,
|
||||
"priority": null,
|
||||
"description": null,
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "4ad19089-3e62-40f8-9482-17cc8ccb92cb",
|
||||
"name": "web.example.com.",
|
||||
"type": "CNAME",
|
||||
"ttl": 3600,
|
||||
"created_at": "2012-11-04T13:58:16.393735",
|
||||
"updated_at": null,
|
||||
"data": "www.example.com.",
|
||||
"domain_id": "89acac79-38e7-497d-807c-a011e1310438",
|
||||
"tenant_id": null,
|
||||
"priority": null,
|
||||
"description": null,
|
||||
"version": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
:param domain_id: domain ID
|
||||
:form id: record id
|
||||
:form name: name of record FQDN
|
||||
:form type: type of record
|
||||
:form created_at: timestamp
|
||||
:form updated_at: timestamp
|
||||
:form priority: priority of record
|
||||
:form ttl: time-to-live numeric value in seconds
|
||||
:form data: value of record
|
||||
:form description: UTF-8 text field
|
||||
:form domain_id: domain ID
|
||||
:statuscode 200: Success
|
||||
:statuscode 401: Access Denied
|
|
@ -1,238 +0,0 @@
|
|||
..
|
||||
Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
|
||||
Author: Endre Karlson <endre.karlson@hp.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.
|
||||
|
||||
|
||||
Reports
|
||||
=======
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
*Note*: Reports is an extension and needs to be enabled before it can be
|
||||
used. If Designate returns a 404 error, ensure that the following line has been
|
||||
added to the designate.conf file::
|
||||
|
||||
enabled_extensions_v1 = reports, ...
|
||||
|
||||
Reports about things in the system
|
||||
|
||||
|
||||
Get all tenants
|
||||
---------------
|
||||
|
||||
.. http:get:: /reports/tenants
|
||||
|
||||
Fetch all tenants
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /reports/tenants HTTP/1.1
|
||||
Host: example.com
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"tenants": [{
|
||||
"domain_count": 2,
|
||||
"id": "71ee6d049a49435c8f7dd002cfe08d96"
|
||||
}]
|
||||
}
|
||||
|
||||
:form tenants: List of tenants
|
||||
:statuscode 200: Success
|
||||
:statuscode 401: Access Denied
|
||||
|
||||
Report tenant resources
|
||||
-----------------------
|
||||
|
||||
.. http:get:: /reports/tenants/(tenant_id)
|
||||
|
||||
Report tenant resources
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /reports/tenants/3d8391080d4a4ec4b3eadf18e6b1539a HTTP/1.1
|
||||
Host: example.com
|
||||
Accept: application/json
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"domain_count": 0,
|
||||
"domains": [],
|
||||
"id": "3d8391080d4a4ec4b3eadf18e6b1539a"
|
||||
}
|
||||
|
||||
:param tenant_id: Tenant Id to get reports for
|
||||
:type tenant_id: string
|
||||
:form domain_count: integer
|
||||
:form domains: Server hostname
|
||||
:form id: Tenant Id
|
||||
:statuscode 200: Success
|
||||
:statuscode 401: Access Denied
|
||||
|
||||
Report resource counts
|
||||
----------------------
|
||||
|
||||
.. http:get:: /reports/counts
|
||||
|
||||
Report resource counts
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /reports/counts HTTP/1.1
|
||||
Host: example.com
|
||||
Accept: application/json
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"domains": 0,
|
||||
"records": 0,
|
||||
"tenants": 0
|
||||
}
|
||||
|
||||
:form domains: Domains count
|
||||
:form records: Records count
|
||||
:form tenants: Tenants count
|
||||
:statuscode 200: Success
|
||||
:statuscode 401: Access Denied
|
||||
|
||||
Report tenant counts
|
||||
----------------------
|
||||
|
||||
.. http:get:: /reports/counts/tenants
|
||||
|
||||
Report tenant counts
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /reports/counts/tenants HTTP/1.1
|
||||
Host: example.com
|
||||
Accept: application/json
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"tenants": 0
|
||||
}
|
||||
|
||||
|
||||
|
||||
:form tenants: Tenants count
|
||||
:statuscode 200: Success
|
||||
:statuscode 401: Access Denied
|
||||
|
||||
Report domain counts
|
||||
----------------------
|
||||
|
||||
.. http:get:: /reports/counts/domains
|
||||
|
||||
Report domain counts
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /reports/counts/domains HTTP/1.1
|
||||
Host: example.com
|
||||
Accept: application/json
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"domains": 0
|
||||
}
|
||||
|
||||
|
||||
|
||||
:form domains: Domains count
|
||||
:statuscode 200: Success
|
||||
:statuscode 401: Access Denied
|
||||
|
||||
Report record counts
|
||||
----------------------
|
||||
|
||||
.. http:get:: /reports/counts/records
|
||||
|
||||
Report record counts
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /reports/counts/records HTTP/1.1
|
||||
Host: example.com
|
||||
Accept: application/json
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"records": 0
|
||||
}
|
||||
|
||||
|
||||
|
||||
:form records: Records count
|
||||
:statuscode 200: Success
|
||||
:statuscode 401: Access Denied
|
|
@ -1,205 +0,0 @@
|
|||
Servers
|
||||
=======
|
||||
|
||||
Server entries are used to generate NS records for zones..
|
||||
|
||||
TODO: More detail.
|
||||
|
||||
TODO: Server Groups Concept.
|
||||
|
||||
|
||||
Create Server
|
||||
-------------
|
||||
|
||||
.. http:post:: /servers
|
||||
|
||||
Create a DNS server
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /servers HTTP/1.1
|
||||
Host: example.com
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "ns1.example.org."
|
||||
}
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id": "384a9b20-239c-11e2-81c1-0800200c9a66",
|
||||
"name": "ns1.example.org.",
|
||||
"created_at": "2011-01-21T11:33:21Z",
|
||||
"updated_at": null
|
||||
}
|
||||
|
||||
:form name: Server hostname
|
||||
:statuscode 200: Success
|
||||
:statuscode 401: Access Denied
|
||||
:statuscode 409: Conflict
|
||||
|
||||
Get Server
|
||||
----------
|
||||
|
||||
.. http:get:: /servers/(uuid:server_id)
|
||||
|
||||
Lists all configured DNS servers
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /servers/384a9b20-239c-11e2-81c1-0800200c9a66 HTTP/1.1
|
||||
Host: example.com
|
||||
Accept: application/json
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id": "384a9b20-239c-11e2-81c1-0800200c9a66",
|
||||
"name": "ns1.example.org.",
|
||||
"created_at": "2011-01-21T11:33:21Z",
|
||||
"updated_at": null
|
||||
}
|
||||
|
||||
:param server_id: The server's unique id
|
||||
:type server_id: uuid
|
||||
:form name: Server hostname
|
||||
:form created_at: timestamp
|
||||
:form updated_at: timestamp
|
||||
:statuscode 200: Success
|
||||
:statuscode 401: Access Denied
|
||||
:statuscode 404: Not Found
|
||||
|
||||
Update Server
|
||||
-------------
|
||||
|
||||
.. http:put:: /servers/(uuid:server_id)
|
||||
|
||||
Create a DNS server
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
PUT /servers/879c1100-9c92-4244-bc83-9535ee6534d0 HTTP/1.1
|
||||
Content-Type: application/json
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "ns1.example.org."
|
||||
}
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id": "879c1100-9c92-4244-bc83-9535ee6534d0",
|
||||
"name": "ns1.example.org.",
|
||||
"created_at": "2012-11-02T02:55:44.000000",
|
||||
"updated_at": "2012-11-02T02:58:41.993556"
|
||||
}
|
||||
|
||||
:form id: UUID server_id
|
||||
:form name: Server hostname
|
||||
:form created_at: timestamp
|
||||
:form updated_at: timestamp
|
||||
:statuscode 200: Success
|
||||
:statuscode 401: Access Denied
|
||||
:statuscode 404: Server Not Found
|
||||
:statuscode 409: Duplicate Server
|
||||
|
||||
List Servers
|
||||
------------
|
||||
|
||||
.. http:get:: /servers
|
||||
|
||||
Lists all configured DNS servers
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /servers HTTP/1.1
|
||||
Host: example.com
|
||||
Accept: application/json
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
[
|
||||
{
|
||||
"id": "384a9b20-239c-11e2-81c1-0800200c9a66",
|
||||
"name": "ns1.example.org.",
|
||||
"created_at": "2011-01-21T11:33:21Z",
|
||||
"updated_at": null
|
||||
},
|
||||
{
|
||||
"id": "cf661142-e577-40b5-b3eb-75795cdc0cd7",
|
||||
"name": "ns2.example.org.",
|
||||
"created_at": "2011-01-21T11:33:21Z",
|
||||
"updated_at": "2011-01-21T11:33:21Z"
|
||||
}
|
||||
]
|
||||
|
||||
:form id: UUID server_id
|
||||
:form name: Server hostname
|
||||
:form created_at: timestamp
|
||||
:form updated_at: timestamp
|
||||
:statuscode 200: Success
|
||||
:statuscode 401: Access Denied
|
||||
|
||||
Delete Server
|
||||
-------------
|
||||
|
||||
.. http:delete:: /servers/(uuid:server_id)
|
||||
|
||||
Deletes a specified server
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
DELETE /servers/5d1d7879-b778-4f77-bb95-02f4a5a224d8 HTTP/1.1
|
||||
Host: example.com
|
||||
|
||||
**Example response**
|
||||
|
||||
.. sourcecode:: guess
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: text/html; charset=utf-8
|
||||
Content-Length: 0
|
||||
Date: Thu, 01 Nov 2012 10:00:00 GMT
|
||||
|
||||
:statuscode 200: Success
|
||||
:statuscode 401: Access Denied
|
||||
:statuscode 404: Not Found
|
||||
|
|
@ -1,112 +0,0 @@
|
|||
..
|
||||
Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
|
||||
Author: Endre Karlson <endre.karlson@hp.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.
|
||||
|
||||
|
||||
Synchronize
|
||||
===========
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
*Note*: Synchronize is an extension and needs to be enabled before it can be
|
||||
used. If Designate returns a 404 error, ensure that the following line has been
|
||||
added to the designate.conf file::
|
||||
|
||||
enabled_extensions_v1 = sync, ...
|
||||
|
||||
Trigger a synchronization of one or more resource(s) in the system.
|
||||
|
||||
|
||||
Synchronize all domains
|
||||
-----------------------
|
||||
|
||||
.. http:post:: /domains/sync
|
||||
|
||||
Synchronize all domains
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /domains/sync HTTP/1.1
|
||||
Host: example.com
|
||||
Content-Type: application/json
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
:statuscode 200: Success
|
||||
:statuscode 401: Access Denied
|
||||
|
||||
|
||||
Synchronize one domain
|
||||
----------------------
|
||||
|
||||
.. http:post:: /domains/(uuid:domain_id)/sync
|
||||
|
||||
Synchronize one domain
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /domains/1dd7851a-74e7-4ddb-b6e8-38a610956bd5/sync HTTP/1.1
|
||||
Host: example.com
|
||||
Content-Type: application/json
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
:statuscode 200: Success
|
||||
:statuscode 401: Access Denied
|
||||
|
||||
|
||||
Synchronize one record
|
||||
----------------------
|
||||
|
||||
.. http:post:: /domains/(uuid:domain_id)/records/(uuid:record_id)/sync
|
||||
|
||||
Synchronize one record
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /domains/1dd7851a-74e7-4ddb-b6e8-38a610956bd5/records/1dd7851a-74e7-4ddb-b6e8-38a610956bd5/sync HTTP/1.1
|
||||
Host: example.com
|
||||
Content-Type: application/json
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
:statuscode 200: Success
|
||||
:statuscode 401: Access Denied
|
|
@ -1,7 +1,6 @@
|
|||
[composite:osapi_dns]
|
||||
use = egg:Paste#urlmap
|
||||
/: osapi_dns_versions
|
||||
/v1: osapi_dns_v1
|
||||
/v2: osapi_dns_v2
|
||||
/admin: osapi_dns_admin
|
||||
|
||||
|
@ -13,14 +12,6 @@ keystone = http_proxy_to_wsgi cors maintenance faultwrapper osapi_dns_app_versio
|
|||
[app:osapi_dns_app_versions]
|
||||
paste.app_factory = designate.api.versions:factory
|
||||
|
||||
[composite:osapi_dns_v1]
|
||||
use = call:designate.api.middleware:auth_pipeline_factory
|
||||
noauth = http_proxy_to_wsgi cors request_id noauthcontext maintenance validation_API_v1 faultwrapper_v1 normalizeuri osapi_dns_app_v1
|
||||
keystone = http_proxy_to_wsgi cors request_id authtoken keystonecontext maintenance validation_API_v1 faultwrapper_v1 normalizeuri osapi_dns_app_v1
|
||||
|
||||
|
||||
[app:osapi_dns_app_v1]
|
||||
paste.app_factory = designate.api.v1:factory
|
||||
|
||||
[composite:osapi_dns_v2]
|
||||
use = call:designate.api.middleware:auth_pipeline_factory
|
||||
|
@ -66,11 +57,5 @@ paste.filter_factory = designate.api.middleware:NormalizeURIMiddleware.factory
|
|||
[filter:faultwrapper]
|
||||
paste.filter_factory = designate.api.middleware:FaultWrapperMiddleware.factory
|
||||
|
||||
[filter:faultwrapper_v1]
|
||||
paste.filter_factory = designate.api.middleware:FaultWrapperMiddlewareV1.factory
|
||||
|
||||
[filter:validation_API_v1]
|
||||
paste.filter_factory = designate.api.middleware:APIv1ValidationErrorMiddleware.factory
|
||||
|
||||
[filter:validation_API_v2]
|
||||
paste.filter_factory = designate.api.middleware:APIv2ValidationErrorMiddleware.factory
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
#"admin_or_target": "rule:admin or rule:target"
|
||||
|
||||
#
|
||||
#"zone_primary_or_admin": "('PRIMARY':%(zone_type)s and rule:admin_or_owner) OR ('SECONDARY':%(zone_type)s AND is_admin:True)"
|
||||
#"zone_primary_or_admin": "('PRIMARY':%(zone_type)s and rule:admin_or_owner) OR ('SECONDARY':%(zone_type)s AND is_admin:True)"
|
||||
|
||||
# Create blacklist.
|
||||
# POST /v2/blacklists
|
||||
|
@ -120,75 +120,36 @@
|
|||
# DELETE /v2/quotas/{project_id}
|
||||
#"reset_quotas": "rule:admin"
|
||||
|
||||
# Create record.
|
||||
# POST /v1/domains/<uuid:domain_id>/records
|
||||
#"create_record": "rule:admin_or_owner"
|
||||
|
||||
# Get records.
|
||||
# GET /v1/domains/<uuid:domain_id>/records
|
||||
#"get_records": "rule:admin_or_owner"
|
||||
|
||||
# Get record.
|
||||
# GET /v1/domains/<uuid:domain_id>/records/<uuid:record_id>
|
||||
#"get_record": "rule:admin_or_owner"
|
||||
|
||||
# Find records.
|
||||
# GET /v2/reverse/floatingips/{region}:{floatingip_id}
|
||||
# GET /v2/reverse/floatingips
|
||||
#"find_records": "rule:admin_or_owner"
|
||||
|
||||
# Find record.
|
||||
# GET /v1/domains/<uuid:domain_id>/records/<uuid:record_id>
|
||||
# DELETE /v1/domains/<uuid:domain_id>/records/<uuid:record_id>
|
||||
# PUT /v1/domains/<uuid:domain_id>/records/<uuid:record_id>
|
||||
#"find_record": "rule:admin_or_owner"
|
||||
|
||||
# Update record.
|
||||
# PUT /v1/domains/<uuid:domain_id>/records/<uuid:record_id>
|
||||
#"update_record": "rule:admin_or_owner"
|
||||
|
||||
# Delete record.
|
||||
# DELETE /v1/domains/<uuid:domain_id>/records/<uuid:record_id>
|
||||
#"delete_record": "rule:admin_or_owner"
|
||||
|
||||
#
|
||||
#"count_records": "rule:admin_or_owner"
|
||||
|
||||
# Create Recordset
|
||||
# POST /v2/zones/{zone_id}/recordsets
|
||||
# PATCH /v2/reverse/floatingips/{region}:{floatingip_id}
|
||||
#"create_recordset": "('PRIMARY':%(zone_type)s and rule:admin_or_owner) OR ('SECONDARY':%(zone_type)s AND is_admin:True)"
|
||||
#"create_recordset": "('PRIMARY':%(zone_type)s and rule:admin_or_owner) OR ('SECONDARY':%(zone_type)s AND is_admin:True)"
|
||||
|
||||
#
|
||||
#"get_recordsets": "rule:admin_or_owner"
|
||||
|
||||
# Get recordset
|
||||
# GET /v1/domains/<uuid:domain_id>/records/<uuid:record_id>
|
||||
# PUT /v1/domains/<uuid:domain_id>/records/<uuid:record_id>
|
||||
# GET /v2/zones/{zone_id}/recordsets/{recordset_id}
|
||||
# DELETE /v2/zones/{zone_id}/recordsets/{recordset_id}
|
||||
# PUT /v2/zones/{zone_id}/recordsets/{recordset_id}
|
||||
#"get_recordset": "rule:admin_or_owner"
|
||||
|
||||
# Find recordsets
|
||||
# GET /v1/domains/<uuid:domain_id>/records
|
||||
#"find_recordsets": "rule:admin_or_owner"
|
||||
|
||||
# Find recordset
|
||||
# POST /v1/domains/<uuid:domain_id>/records
|
||||
# DELETE /v1/domains/<uuid:domain_id>/records/<uuid:record_id>
|
||||
#"find_recordset": "rule:admin_or_owner"
|
||||
|
||||
# Update recordset
|
||||
# PUT /v1/domains/<uuid:domain_id>/records/<uuid:record_id>
|
||||
# PUT /v2/zones/{zone_id}/recordsets/{recordset_id}
|
||||
# PATCH /v2/reverse/floatingips/{region}:{floatingip_id}
|
||||
#"update_recordset": "('PRIMARY':%(zone_type)s and rule:admin_or_owner) OR ('SECONDARY':%(zone_type)s AND is_admin:True)"
|
||||
#"update_recordset": "('PRIMARY':%(zone_type)s and rule:admin_or_owner) OR ('SECONDARY':%(zone_type)s AND is_admin:True)"
|
||||
|
||||
# Delete RecordSet
|
||||
# DELETE /v1/domains/<uuid:domain_id>/records/<uuid:record_id>
|
||||
# DELETE /v2/zones/{zone_id}/recordsets/{recordset_id}
|
||||
#"delete_recordset": "('PRIMARY':%(zone_type)s and rule:admin_or_owner) OR ('SECONDARY':%(zone_type)s AND is_admin:True)"
|
||||
#"delete_recordset": "('PRIMARY':%(zone_type)s and rule:admin_or_owner) OR ('SECONDARY':%(zone_type)s AND is_admin:True)"
|
||||
|
||||
# Count recordsets
|
||||
#"count_recordset": "rule:admin_or_owner"
|
||||
|
@ -234,14 +195,10 @@
|
|||
#"delete_tld": "rule:admin"
|
||||
|
||||
# Create Tsigkey
|
||||
# POST /v1/tsigkeys
|
||||
# POST /v2/tsigkeys
|
||||
#"create_tsigkey": "rule:admin"
|
||||
|
||||
# List Tsigkeys
|
||||
# GET /v1/tsigkeys
|
||||
# GET /v1/tsigkeys/<uuid:tsigkey_id>
|
||||
# DELETE /v1/tsigkeys/<uuid:tsigkey_id>
|
||||
# GET /v2/tsigkeys
|
||||
#"find_tsigkeys": "rule:admin"
|
||||
|
||||
|
@ -251,17 +208,14 @@
|
|||
#"get_tsigkey": "rule:admin"
|
||||
|
||||
# Update Tsigkey
|
||||
# PATCH /v1/tsigkeys/{tsigkey_id}
|
||||
# PATCH /v2/tsigkeys/{tsigkey_id}
|
||||
#"update_tsigkey": "rule:admin"
|
||||
|
||||
# Delete a Tsigkey
|
||||
# DELETE /v1/tsigkeys/{tsigkey_id}
|
||||
# DELETE /v2/tsigkeys/{tsigkey_id}
|
||||
#"delete_tsigkey": "rule:admin"
|
||||
|
||||
# Create Zone
|
||||
# POST /v1//domains
|
||||
# POST /v2/zones
|
||||
#"create_zone": "rule:admin_or_owner"
|
||||
|
||||
|
@ -269,8 +223,6 @@
|
|||
#"get_zones": "rule:admin_or_owner"
|
||||
|
||||
# Get Zone
|
||||
# GET /v1/domains/<uuid:domain_id>/records/<uuid:record_id>
|
||||
# GET /v1/domains/<uuid:domain_id>/records
|
||||
# GET /v2/zones/{zone_id}
|
||||
# PATCH /v2/zones/{zone_id}
|
||||
# PUT /v2/zones/{zone_id}/recordsets/{recordset_id}
|
||||
|
@ -280,24 +232,14 @@
|
|||
#"get_zone_servers": "rule:admin_or_owner"
|
||||
|
||||
# List existing zones
|
||||
# GET /v1/domains
|
||||
# GET /v2/zones
|
||||
#"find_zones": "rule:admin_or_owner"
|
||||
|
||||
# Find Zone
|
||||
# GET /v1/domains/<uuid:domain_id>
|
||||
# GET /v1/domains/<uuid:domain_id>/servers
|
||||
# PUT /v1/domains/<uuid:domain_id>
|
||||
# DELETE /v1/domains/<uuid:domain_id>
|
||||
#"find_zone": "rule:admin_or_owner"
|
||||
|
||||
# Update Zone
|
||||
# PUT /v1/domains/<uuid:domain_id>
|
||||
# PATCH /v2/zones/{zone_id}
|
||||
#"update_zone": "rule:admin_or_owner"
|
||||
|
||||
# Delete Zone
|
||||
# DELETE /v1/domains/<uuid:domain_id>
|
||||
# DELETE /v2/zones/{zone_id}
|
||||
#"delete_zone": "rule:admin_or_owner"
|
||||
|
||||
|
@ -364,7 +306,7 @@
|
|||
|
||||
# Create Zone Transfer Accept
|
||||
# POST /v2/zones/tasks/transfer_accepts
|
||||
#"create_zone_transfer_accept": "rule:admin_or_owner or tenant:%(target_tenant_id)s or None:%(target_tenant_id)s"
|
||||
#"create_zone_transfer_accept": "rule:admin_or_owner OR tenant:%(target_tenant_id)s OR None:%(target_tenant_id)s"
|
||||
|
||||
# Get Zone Transfer Accept
|
||||
# GET /v2/zones/tasks/transfer_requests/{zone_transfer_accept_id}
|
||||
|
@ -391,7 +333,7 @@
|
|||
# Show a Zone Transfer Request
|
||||
# GET /v2/zones/tasks/transfer_requests/{zone_transfer_request_id}
|
||||
# PATCH /v2/zones/tasks/transfer_requests/{zone_transfer_request_id}
|
||||
#"get_zone_transfer_request": "rule:admin_or_owner or tenant:%(target_tenant_id)s or None:%(target_tenant_id)s"
|
||||
#"get_zone_transfer_request": "rule:admin_or_owner OR tenant:%(target_tenant_id)s OR None:%(target_tenant_id)s"
|
||||
|
||||
#
|
||||
#"get_zone_transfer_request_detailed": "rule:admin_or_owner"
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
export DEVSTACK_GATE_TEMPEST=1
|
||||
export DEVSTACK_GATE_TEMPEST_ALL_PLUGINS=1
|
||||
export DEVSTACK_GATE_GRENADE=pullup
|
||||
export DEVSTACK_GATE_TEMPEST_REGEX=designate
|
||||
export DEVSTACK_GATE_TEMPEST_REGEX="designate_tempest_plugin(?!\.tests.api.v1).*"
|
||||
export DEVSTACK_GATE_HORIZON=1
|
||||
|
||||
export PROJECTS="openstack/designate $PROJECTS"
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
prelude: >
|
||||
V1 API removal is complete in this version of designate.
|
||||
upgrade:
|
||||
- |
|
||||
Any tooling using the V1 API needs to be reworked to use the v2 API
|
||||
critical:
|
||||
- |
|
||||
V1 API has been removed
|
13
setup.cfg
13
setup.cfg
|
@ -64,19 +64,6 @@ console_scripts =
|
|||
designate-worker = designate.cmd.worker:main
|
||||
designate-producer = designate.cmd.producer:main
|
||||
|
||||
designate.api.v1 =
|
||||
domains = designate.api.v1.domains:blueprint
|
||||
limits = designate.api.v1.limits:blueprint
|
||||
records = designate.api.v1.records:blueprint
|
||||
servers = designate.api.v1.servers:blueprint
|
||||
tsigkeys = designate.api.v1.tsigkeys:blueprint
|
||||
|
||||
designate.api.v1.extensions =
|
||||
diagnostics = designate.api.v1.extensions.diagnostics:blueprint
|
||||
quotas = designate.api.v1.extensions.quotas:blueprint
|
||||
sync = designate.api.v1.extensions.sync:blueprint
|
||||
reports = designate.api.v1.extensions.reports:blueprint
|
||||
touch = designate.api.v1.extensions.touch:blueprint
|
||||
|
||||
designate.api.admin.extensions =
|
||||
reports = designate.api.admin.controllers.extensions.reports:ReportsController
|
||||
|
|
Loading…
Reference in New Issue