Replace API_v2 views with Adapters

* Copied old base view into /admin/
* Removed all v2/ views
* Controllers moved to Adapters
* Validation Error Middleware now in use for validation errors
* Updated tests to remove wrapper object
* Updated Docs to remove wrapper
* Moved Quota Docs to /admin/

Change-Id: I345552bb271222d41321523acc9c4100cbe4878e
Partially-Implements: blueprint validation-cleanup
APIImpact: Removes wrapping object on APIv2 single resource gets
(i.e. get zone by id moves from {zone:{<zone_info>}} to {<zone_info>})
This commit is contained in:
Graham Hayes 2015-03-23 19:29:25 +00:00
parent 19c574989b
commit 129b00e819
46 changed files with 922 additions and 1647 deletions

View File

@ -15,7 +15,7 @@
# under the License.
from oslo_log import log as logging
from designate.api.v2.views import base as base_view
from designate.api.admin.views import base as base_view
LOG = logging.getLogger(__name__)

View File

@ -15,7 +15,7 @@
# under the License.
from oslo_log import log as logging
from designate.api.v2.views import base as base_view
from designate.api.admin.views import base as base_view
LOG = logging.getLogger(__name__)

View File

@ -17,20 +17,16 @@
import pecan
from oslo_log import log as logging
from designate import schema
from designate import utils
from designate.api.v2.controllers import rest
from designate.api.v2.views import blacklists as blacklists_view
from designate.objects import Blacklist
from designate.objects.adapters import DesignateAdapter
LOG = logging.getLogger(__name__)
class BlacklistsController(rest.RestController):
_view = blacklists_view.BlacklistsView()
_resource_schema = schema.Schema('v2', 'blacklist')
_collection_schema = schema.Schema('v2', 'blacklists')
SORT_KEYS = ['created_at', 'id', 'updated_at', 'pattern']
@pecan.expose(template='json:', content_type='application/json')
@ -41,9 +37,10 @@ class BlacklistsController(rest.RestController):
request = pecan.request
context = request.environ['context']
blacklist = self.central_api.get_blacklist(context, blacklist_id)
return self._view.show(context, request, blacklist)
return DesignateAdapter.render(
'API_v2',
self.central_api.get_blacklist(context, blacklist_id),
request=request)
@pecan.expose(template='json:', content_type='application/json')
def get_all(self, **params):
@ -59,10 +56,11 @@ class BlacklistsController(rest.RestController):
criterion = dict((k, params[k]) for k in accepted_filters
if k in params)
blacklist = self.central_api.find_blacklists(
context, criterion, marker, limit, sort_key, sort_dir)
return self._view.list(context, request, blacklist)
return DesignateAdapter.render(
'API_v2',
self.central_api.find_blacklists(
context, criterion, marker, limit, sort_key, sort_dir),
request=request)
@pecan.expose(template='json:', content_type='application/json')
def post_all(self):
@ -70,26 +68,25 @@ class BlacklistsController(rest.RestController):
request = pecan.request
response = pecan.response
context = request.environ['context']
body = request.body_dict
# Validate the request conforms to the schema
self._resource_schema.validate(body)
blacklist = DesignateAdapter.parse('API_v2', body, Blacklist())
# Convert from APIv2 -> Central format
values = self._view.load(context, request, body)
blacklist.validate()
# Create the blacklist
blacklist = self.central_api.create_blacklist(
context, Blacklist(**values))
context, blacklist)
response.status_int = 201
response.headers['Location'] = self._view._get_resource_href(
request, blacklist)
blacklist = DesignateAdapter.render(
'API_v2', blacklist, request=request)
response.headers['Location'] = blacklist['links']['self']
# Prepare and return the response body
return self._view.show(context, request, blacklist)
return blacklist
@pecan.expose(template='json:', content_type='application/json')
@pecan.expose(template='json:', content_type='application/json-patch+json')
@ -101,27 +98,21 @@ class BlacklistsController(rest.RestController):
body = request.body_dict
response = pecan.response
if request.content_type == 'application/json-patch+json':
raise NotImplemented('json-patch not implemented')
# Fetch the existing blacklist entry
blacklist = self.central_api.get_blacklist(context, blacklist_id)
# Convert to APIv2 Format
blacklist_data = self._view.show(context, request, blacklist)
blacklist = DesignateAdapter.parse('API_v2', body, blacklist)
if request.content_type == 'application/json-patch+json':
raise NotImplemented('json-patch not implemented')
else:
blacklist_data = utils.deep_dict_merge(blacklist_data, body)
blacklist.validate()
# Validate the new set of data
self._resource_schema.validate(blacklist_data)
# Update and persist the resource
blacklist.update(self._view.load(context, request, body))
blacklist = self.central_api.update_blacklist(context, blacklist)
blacklist = self.central_api.update_blacklist(context, blacklist)
response.status_int = 200
return self._view.show(context, request, blacklist)
return DesignateAdapter.render('API_v2', blacklist, request=request)
@pecan.expose(template=None, content_type='application/json')
@utils.validate_uuid('blacklist_id')

View File

@ -18,10 +18,9 @@ import re
import pecan
from designate import exceptions
from designate import schema
from designate import objects
from designate.objects.adapters import DesignateAdapter
from designate.api.v2.controllers import rest
from designate.api.v2.views import floatingips as floatingips_views
FIP_REGEX = '^(?P<region>[A-Za-z0-9\\.\\-_]{1,100}):' \
@ -41,9 +40,6 @@ def fip_key_to_data(key):
class FloatingIPController(rest.RestController):
_view = floatingips_views.FloatingIPView()
_resource_schema = schema.Schema('v2', 'floatingip')
_collection_schema = schema.Schema('v2', 'floatingips')
@pecan.expose(template='json:', content_type='application/json')
def get_all(self, **params):
@ -51,8 +47,10 @@ class FloatingIPController(rest.RestController):
request = pecan.request
context = request.environ['context']
fips = self.central_api.list_floatingips(context)
return self._view.list(context, request, fips)
return DesignateAdapter.render(
'API_v2',
self.central_api.list_floatingips(context),
request=request)
@pecan.expose(template='json:', content_type='application/json')
def patch_one(self, fip_key):
@ -61,21 +59,25 @@ class FloatingIPController(rest.RestController):
"""
request = pecan.request
context = request.environ['context']
body = request.body_dict
try:
body = request.body_dict
except Exception as e:
if e.message != 'TODO: Unsupported Content Type':
raise
else:
# Got a blank body
body = dict()
region, id_ = fip_key_to_data(fip_key)
# Validate the request conforms to the schema
self._resource_schema.validate(body)
fip = DesignateAdapter.parse('API_v2', body, objects.FloatingIP())
fip = self.central_api.update_floatingip(
context,
region,
id_,
objects.FloatingIP().from_dict(body['floatingip']))
fip.validate()
fip = self.central_api.update_floatingip(context, region, id_, fip)
if fip:
return self._view.show(context, request, fip)
return DesignateAdapter.render('API_v2', fip, request=request)
@pecan.expose(template='json:', content_type='application/json')
def get_one(self, fip_key):
@ -87,6 +89,7 @@ class FloatingIPController(rest.RestController):
region, id_ = fip_key_to_data(fip_key)
fip = self.central_api.get_floatingip(context, region, id_)
return self._view.show(context, request, fip)
return DesignateAdapter.render(
'API_v2',
self.central_api.get_floatingip(context, region, id_),
request=request)

View File

@ -18,20 +18,22 @@ import pecan
from oslo_log import log as logging
from designate.api.v2.controllers import rest
from designate.api.v2.views import limits as limits_view
LOG = logging.getLogger(__name__)
class LimitsController(rest.RestController):
_view = limits_view.LimitsView()
@pecan.expose(template='json:', content_type='application/json')
def get_all(self):
request = pecan.request
context = pecan.request.environ['context']
absolute_limits = self.central_api.get_absolute_limits(context)
return self._view.show(context, request, absolute_limits)
return {
"max_zones": absolute_limits['domains'],
"max_zone_recordsets": absolute_limits['domain_recordsets'],
"max_zone_records": absolute_limits['domain_records'],
"max_recordset_records": absolute_limits['recordset_records']
}

View File

@ -15,20 +15,16 @@
import pecan
from oslo_log import log as logging
from designate import schema
from designate import utils
from designate.api.v2.controllers import rest
from designate.api.v2.views import pools as pools_view
from designate.objects import Pool
from designate.objects.adapters import DesignateAdapter
LOG = logging.getLogger(__name__)
class PoolsController(rest.RestController):
_view = pools_view.PoolsView()
_resource_schema = schema.Schema('v2', 'pool')
_collection_schema = schema.Schema('v2', 'pools')
SORT_KEYS = ['created_at', 'id', 'updated_at', 'name']
@pecan.expose(template='json:', content_type='application/json')
@ -38,8 +34,10 @@ class PoolsController(rest.RestController):
request = pecan.request
context = request.environ['context']
pool = self.central_api.get_pool(context, pool_id)
return self._view.show(context, request, pool)
return DesignateAdapter.render(
'API_v2',
self.central_api.get_pool(context, pool_id),
request=request)
@pecan.expose(template='json:', content_type='application/json')
def get_all(self, **params):
@ -55,10 +53,11 @@ class PoolsController(rest.RestController):
criterion = dict((k, params[k]) for k in accepted_filters
if k in params)
pools = self.central_api.find_pools(
context, criterion, marker, limit, sort_key, sort_dir)
return self._view.list(context, request, pools)
return DesignateAdapter.render(
'API_v2',
self.central_api.find_pools(
context, criterion, marker, limit, sort_key, sort_dir),
request=request)
@pecan.expose(template='json:', content_type='application/json')
def post_all(self):
@ -68,20 +67,18 @@ class PoolsController(rest.RestController):
context = request.environ['context']
body = request.body_dict
# Validate the request conforms to the schema
self._resource_schema.validate(body)
pool = DesignateAdapter.parse('API_v2', body, Pool())
# Convert from APIv2 -> Central format
values = self._view.load(context, request, body)
pool.validate()
# Create the pool
pool = self.central_api.create_pool(context, Pool(**values))
pool = self.central_api.create_pool(context, pool)
pool = DesignateAdapter.render('API_v2', pool, request=request)
response.status_int = 201
response.headers['Location'] = self._view._get_resource_href(request,
pool)
response.headers['Location'] = pool['links']['self']
# Prepare and return the response body
return self._view.show(context, request, pool)
return pool
@pecan.expose(template='json:', content_type='application/json')
@pecan.expose(template='json:', content_type='application/json-patch+json')
@ -93,27 +90,21 @@ class PoolsController(rest.RestController):
body = request.body_dict
response = pecan.response
if request.content_type == 'application/json-patch+json':
raise NotImplemented('json-patch not implemented')
# Fetch the existing pool
pool = self.central_api.get_pool(context, pool_id)
# Convert to APIv2 Format
pool_data = self._view.show(context, request, pool)
pool = DesignateAdapter.parse('API_v2', body, pool)
if request.content_type == 'application/json-patch+json':
raise NotImplemented('json-patch not implemented')
else:
pool_data = utils.deep_dict_merge(pool_data, body)
pool.validate()
# Validate the new set of data
self._resource_schema.validate(pool_data)
# Update and persist the resource
pool.update(self._view.load(context, request, body))
pool = self.central_api.update_pool(context, pool)
pool = self.central_api.update_pool(context, pool)
response.status_int = 200
return self._view.show(context, request, pool)
return DesignateAdapter.render('API_v2', pool, request=request)
@pecan.expose(template=None, content_type='application/json')
@utils.validate_uuid('pool_id')

View File

@ -17,21 +17,16 @@ import pecan
from oslo_log import log as logging
from designate import exceptions
from designate import schema
from designate import utils
from designate.api.v2.controllers import rest
from designate.api.v2.views import recordsets as recordsets_view
from designate.objects import RecordSet
from designate.objects import Record
from designate.objects import RecordSetList
from designate.objects.adapters import DesignateAdapter
LOG = logging.getLogger(__name__)
class RecordSetsController(rest.RestController):
_view = recordsets_view.RecordSetsView()
_resource_schema = schema.Schema('v2', 'recordset')
_collection_schema = schema.Schema('v2', 'recordsets')
SORT_KEYS = ['created_at', 'id', 'updated_at', 'domain_id', 'tenant_id',
'name', 'type', 'ttl', 'records']
@ -42,10 +37,11 @@ class RecordSetsController(rest.RestController):
request = pecan.request
context = request.environ['context']
recordset = self.central_api.get_recordset(context, zone_id,
recordset_id)
return self._view.show(context, request, recordset)
return DesignateAdapter.render(
'API_v2',
self.central_api.get_recordset(
context, zone_id, recordset_id),
request=request)
@pecan.expose(template='json:', content_type='application/json')
@utils.validate_uuid('zone_id')
@ -82,10 +78,16 @@ class RecordSetsController(rest.RestController):
context, criterion={'data': data, 'domain_id': zone_id})
recordsets_with_data.update(
[record.recordset_id for record in records])
recordsets = [recordset for recordset in recordsets
if recordset.id in recordsets_with_data]
return self._view.list(context, request, recordsets, [zone_id])
new_rsets = RecordSetList()
for recordset in recordsets:
if recordset.id in recordsets_with_data:
new_rsets.append(recordset)
recordsets = new_rsets
return DesignateAdapter.render('API_v2', recordsets, request=request)
@pecan.expose(template='json:', content_type='application/json')
@utils.validate_uuid('zone_id')
@ -97,31 +99,32 @@ class RecordSetsController(rest.RestController):
body = request.body_dict
# Validate the request conforms to the schema
self._resource_schema.validate(body)
recordset = DesignateAdapter.parse('API_v2', body, RecordSet())
# Convert from APIv2 -> Central format
values = self._view.load(context, request, body)
recordset.validate()
# SOA recordsets cannot be created manually
if values['type'] == 'SOA':
if recordset.type == 'SOA':
raise exceptions.BadRequest(
"Creating a SOA recordset is not allowed")
# Create the recordset
recordset = self.central_api.create_recordset(
context, zone_id, RecordSet(**values))
context, zone_id, recordset)
# Prepare the response headers
if recordset['status'] == 'PENDING':
response.status_int = 202
else:
response.status_int = 201
response.headers['Location'] = self._view._get_resource_href(
request, recordset, [zone_id])
recordset = DesignateAdapter.render(
'API_v2', recordset, request=request)
response.headers['Location'] = recordset['links']['self']
# Prepare and return the response body
return self._view.show(context, request, recordset)
return recordset
@pecan.expose(template='json:', content_type='application/json')
@utils.validate_uuid('zone_id', 'recordset_id')
@ -149,41 +152,10 @@ class RecordSetsController(rest.RestController):
'Updating a root zone NS record is not allowed')
# Convert to APIv2 Format
recordset_data = self._view.show(context, request, recordset)
recordset_data = utils.deep_dict_merge(recordset_data, body)
new_recordset = self._view.load(context, request, body)
# Validate the new set of data
self._resource_schema.validate(recordset_data)
recordset = DesignateAdapter.parse('API_v2', body, recordset)
# Get original list of Records
original_records = set()
for record in recordset.records:
original_records.add(record.data)
# Get new list of Records
new_records = set()
if 'records' in new_recordset:
for record in new_recordset['records']:
new_records.add(record.data)
# Get differences of Records
records_to_add = new_records.difference(original_records)
records_to_rm = original_records.difference(new_records)
# Update all items except records
record_update = False
if 'records' in new_recordset:
record_update = True
del new_recordset['records']
recordset.update(new_recordset)
# Remove deleted records if we have provided a records array
if record_update:
recordset.records[:] = [record for record in recordset.records
if record.data not in records_to_rm]
# Add new records
for record in records_to_add:
recordset.records.append(Record(data=record))
recordset.validate()
# Persist the resource
recordset = self.central_api.update_recordset(context, recordset)
@ -193,7 +165,7 @@ class RecordSetsController(rest.RestController):
else:
response.status_int = 200
return self._view.show(context, request, recordset)
return DesignateAdapter.render('API_v2', recordset, request=request)
@pecan.expose(template='json:', content_type='application/json')
@utils.validate_uuid('zone_id', 'recordset_id')
@ -214,4 +186,4 @@ class RecordSetsController(rest.RestController):
context, zone_id, recordset_id)
response.status_int = 202
return self._view.show(context, request, recordset)
return DesignateAdapter.render('API_v2', recordset, request=request)

View File

@ -19,7 +19,6 @@ from stevedore import named
from designate.api.v2.controllers import limits
from designate.api.v2.controllers import reverse
from designate.api.v2.controllers import schemas
from designate.api.v2.controllers import tlds
from designate.api.v2.controllers import blacklists
from designate.api.v2.controllers import errors
@ -52,7 +51,6 @@ class RootController(object):
setattr(controller, path.split('.')[-1], ext.obj)
limits = limits.LimitsController()
schemas = schemas.SchemasController()
reverse = reverse.ReverseController()
tlds = tlds.TldsController()
zones = zones.ZonesController()

View File

@ -1,40 +0,0 @@
# Copyright 2013 Hewlett-Packard Development Company, L.P.
#
# Author: Kiall Mac Innes <kiall@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.
import os
import pecan
from oslo_log import log as logging
from designate import exceptions
from designate import utils
LOG = logging.getLogger(__name__)
class SchemasController(object):
@pecan.expose(template='json:', content_type='application/schema+json')
def _default(self, *remainder):
if len(remainder) == 0:
pecan.abort(404)
try:
schema_name = os.path.join(*remainder)
schema_json = utils.load_schema('v2', schema_name)
except exceptions.ResourceNotFound:
pecan.abort(404)
return schema_json

View File

@ -15,20 +15,16 @@
import pecan
from oslo_log import log as logging
from designate import schema
from designate import utils
from designate.api.v2.controllers import rest
from designate.api.v2.views import tlds as tlds_view
from designate.objects import Tld
from designate.objects.adapters import DesignateAdapter
LOG = logging.getLogger(__name__)
class TldsController(rest.RestController):
_view = tlds_view.TldsView()
_resource_schema = schema.Schema('v2', 'tld')
_collection_schema = schema.Schema('v2', 'tlds')
SORT_KEYS = ['created_at', 'id', 'updated_at', 'name']
@pecan.expose(template='json:', content_type='application/json')
@ -39,8 +35,10 @@ class TldsController(rest.RestController):
request = pecan.request
context = request.environ['context']
tld = self.central_api.get_tld(context, tld_id)
return self._view.show(context, request, tld)
return DesignateAdapter.render(
'API_v2',
self.central_api.get_tld(context, tld_id),
request=request)
@pecan.expose(template='json:', content_type='application/json')
def get_all(self, **params):
@ -56,10 +54,11 @@ class TldsController(rest.RestController):
criterion = dict((k, params[k]) for k in accepted_filters
if k in params)
tlds = self.central_api.find_tlds(
context, criterion, marker, limit, sort_key, sort_dir)
return self._view.list(context, request, tlds)
return DesignateAdapter.render(
'API_v2',
self.central_api.find_tlds(
context, criterion, marker, limit, sort_key, sort_dir),
request=request)
@pecan.expose(template='json:', content_type='application/json')
def post_all(self):
@ -69,20 +68,19 @@ class TldsController(rest.RestController):
context = request.environ['context']
body = request.body_dict
# Validate the request conforms to the schema
self._resource_schema.validate(body)
tld = DesignateAdapter.parse('API_v2', body, Tld())
# Convert from APIv2 -> Central format
values = self._view.load(context, request, body)
tld.validate()
# Create the tld
tld = self.central_api.create_tld(context, Tld(**values))
tld = self.central_api.create_tld(context, tld)
response.status_int = 201
response.headers['Location'] = self._view._get_resource_href(request,
tld)
tld = DesignateAdapter.render('API_v2', tld, request=request)
response.headers['Location'] = tld['links']['self']
# Prepare and return the response body
return self._view.show(context, request, tld)
return tld
@pecan.expose(template='json:', content_type='application/json')
@pecan.expose(template='json:', content_type='application/json-patch+json')
@ -93,28 +91,21 @@ class TldsController(rest.RestController):
context = request.environ['context']
body = request.body_dict
response = pecan.response
if request.content_type == 'application/json-patch+json':
raise NotImplemented('json-patch not implemented')
# Fetch the existing tld
tld = self.central_api.get_tld(context, tld_id)
# Convert to APIv2 Format
tld_data = self._view.show(context, request, tld)
tld = DesignateAdapter.parse('API_v2', body, tld)
if request.content_type == 'application/json-patch+json':
raise NotImplemented('json-patch not implemented')
else:
tld_data = utils.deep_dict_merge(tld_data, body)
tld.validate()
# Validate the new set of data
self._resource_schema.validate(tld_data)
# Update and persist the resource
tld.update(self._view.load(context, request, body))
tld = self.central_api.update_tld(context, tld)
tld = self.central_api.update_tld(context, tld)
response.status_int = 200
return self._view.show(context, request, tld)
return DesignateAdapter.render('API_v2', tld, request=request)
@pecan.expose(template=None, content_type='application/json')
@utils.validate_uuid('tld_id')

View File

@ -17,20 +17,15 @@
import pecan
from oslo_log import log as logging
from designate import schema
from designate import utils
from designate.api.v2.controllers import rest
from designate.api.v2.views import tsigkeys as tsigkeys_view
from designate.objects import TsigKey
from designate.objects.adapters import DesignateAdapter
LOG = logging.getLogger(__name__)
class TsigKeysController(rest.RestController):
_view = tsigkeys_view.TsigKeysView()
_resource_schema = schema.Schema('v2', 'tsigkey')
_collection_schema = schema.Schema('v2', 'tsigkeys')
SORT_KEYS = ['created_at', 'id', 'updated_at', 'name']
@pecan.expose(template='json:', content_type='application/json')
@ -41,9 +36,10 @@ class TsigKeysController(rest.RestController):
request = pecan.request
context = request.environ['context']
tsigkey = self.central_api.get_tsigkey(context, tsigkey_id)
return self._view.show(context, request, tsigkey)
return DesignateAdapter.render(
'API_v2',
self.central_api.get_tsigkey(context, tsigkey_id),
request=request)
@pecan.expose(template='json:', content_type='application/json')
def get_all(self, **params):
@ -59,10 +55,11 @@ class TsigKeysController(rest.RestController):
criterion = dict((k, params[k]) for k in accepted_filters
if k in params)
tsigkey = self.central_api.find_tsigkeys(
context, criterion, marker, limit, sort_key, sort_dir)
return self._view.list(context, request, tsigkey)
return DesignateAdapter.render(
'API_v2',
self.central_api.find_tsigkeys(
context, criterion, marker, limit, sort_key, sort_dir),
request=request)
@pecan.expose(template='json:', content_type='application/json')
def post_all(self):
@ -70,26 +67,22 @@ class TsigKeysController(rest.RestController):
request = pecan.request
response = pecan.response
context = request.environ['context']
body = request.body_dict
# Validate the request conforms to the schema
self._resource_schema.validate(body)
tsigkey = DesignateAdapter.parse('API_v2', body, TsigKey())
# Convert from APIv2 -> Central format
values = self._view.load(context, request, body)
tsigkey.validate()
# Create the tsigkey
tsigkey = self.central_api.create_tsigkey(
context, TsigKey(**values))
context, tsigkey)
tsigkey = DesignateAdapter.render('API_v2', tsigkey, request=request)
response.headers['Location'] = tsigkey['links']['self']
response.status_int = 201
response.headers['Location'] = self._view._get_resource_href(
request, tsigkey)
# Prepare and return the response body
return self._view.show(context, request, tsigkey)
return tsigkey
@pecan.expose(template='json:', content_type='application/json')
@pecan.expose(template='json:', content_type='application/json-patch+json')
@ -101,27 +94,23 @@ class TsigKeysController(rest.RestController):
body = request.body_dict
response = pecan.response
if request.content_type == 'application/json-patch+json':
raise NotImplemented('json-patch not implemented')
# Fetch the existing tsigkey entry
tsigkey = self.central_api.get_tsigkey(context, tsigkey_id)
# Convert to APIv2 Format
tsigkey_data = self._view.show(context, request, tsigkey)
tsigkey = DesignateAdapter.parse('API_v2', body, tsigkey)
if request.content_type == 'application/json-patch+json':
raise NotImplemented('json-patch not implemented')
else:
tsigkey_data = utils.deep_dict_merge(tsigkey_data, body)
# Validate the new set of data
tsigkey.validate()
# Validate the new set of data
self._resource_schema.validate(tsigkey_data)
# Update and persist the resource
tsigkey.update(self._view.load(context, request, body))
tsigkey = self.central_api.update_tsigkey(context, tsigkey)
# Update and persist the resource
tsigkey = self.central_api.update_tsigkey(context, tsigkey)
response.status_int = 200
return self._view.show(context, request, tsigkey)
return DesignateAdapter.render('API_v2', tsigkey, request=request)
@pecan.expose(template=None, content_type='application/json')
@utils.validate_uuid('tsigkey_id')

View File

@ -20,22 +20,19 @@ from oslo.config import cfg
from designate import exceptions
from designate import utils
from designate import schema
from designate import dnsutils
from designate.api.v2.controllers import rest
from designate.api.v2.controllers import recordsets
from designate.api.v2.controllers.zones import tasks
from designate.api.v2.views import zones as zones_view
from designate import objects
from designate.objects.adapters import DesignateAdapter
CONF = cfg.CONF
class ZonesController(rest.RestController):
_view = zones_view.ZonesView()
_resource_schema = schema.Schema('v2', 'zone')
_collection_schema = schema.Schema('v2', 'zones')
SORT_KEYS = ['created_at', 'id', 'updated_at', 'name', 'tenant_id',
'serial', 'ttl', 'status']
@ -65,9 +62,10 @@ class ZonesController(rest.RestController):
def _get_json(self, request, context, zone_id):
"""'Normal' zone get"""
zone = self.central_api.get_domain(context, zone_id)
return self._view.show(context, request, zone)
return DesignateAdapter.render(
'API_v2',
self.central_api.get_domain(context, zone_id),
request=request)
def _get_zonefile(self, request, context, zone_id):
"""Export zonefile"""
@ -110,13 +108,15 @@ class ZonesController(rest.RestController):
# Extract any filter params.
accepted_filters = ('name', 'email', 'status', )
criterion = self._apply_filter_params(
params, accepted_filters, {})
zones = self.central_api.find_domains(
context, criterion, marker, limit, sort_key, sort_dir)
return self._view.list(context, request, zones)
return DesignateAdapter.render(
'API_v2',
self.central_api.find_domains(
context, criterion, marker, limit, sort_key, sort_dir),
request=request)
@pecan.expose(template='json:', content_type='application/json')
def post_all(self):
@ -134,29 +134,27 @@ class ZonesController(rest.RestController):
def _post_json(self, request, response, context):
"""'Normal' zone creation"""
body = request.body_dict
zone = request.body_dict
# We need to check the zone type before validating the schema since if
# it's the type is SECONDARY we need to set the email to the mgmt email
zone = body.get('zone')
if isinstance(zone, dict):
if 'type' not in zone:
zone['type'] = 'PRIMARY'
if zone['type'] == 'SECONDARY':
mgmt_email = CONF['service:central'].managed_resource_email
body['zone']['email'] = mgmt_email
zone['email'] = mgmt_email
# Validate the request conforms to the schema
self._resource_schema.validate(body)
zone = DesignateAdapter.parse('API_v2', zone, objects.Domain())
# Convert from APIv2 -> Central format
values = self._view.load(context, request, body)
zone.validate()
# TODO(ekarlso): Fix this once setter or so works.
masters = values.pop('masters', [])
zone = objects.Domain.from_dict(values)
zone.set_masters(masters)
# # TODO(ekarlso): Fix this once setter or so works.
# masters = values.pop('masters', [])
# zone = objects.Domain.from_dict(values)
# zone.set_masters(masters)
# Create the zone
zone = self.central_api.create_domain(context, zone)
@ -169,11 +167,12 @@ class ZonesController(rest.RestController):
else:
response.status_int = 201
response.headers['Location'] = self._view._get_resource_href(request,
zone)
# Prepare and return the response body
return self._view.show(context, request, zone)
zone = DesignateAdapter.render('API_v2', zone, request=request)
response.headers['Location'] = zone['links']['self']
return zone
def _post_zonefile(self, request, response, context):
"""Import Zone"""
@ -206,9 +205,11 @@ class ZonesController(rest.RestController):
else:
response.status_int = 201
response.headers['Location'] = self._view._get_resource_href(request,
zone)
return self._view.show(context, request, zone)
zone = DesignateAdapter.render('API_v2', zone, request=request)
response.headers['Location'] = zone['links']['self']
return zone
@pecan.expose(template='json:', content_type='application/json')
@pecan.expose(template='json:', content_type='application/json-patch+json')
@ -230,9 +231,6 @@ class ZonesController(rest.RestController):
if zone.action == "DELETE":
raise exceptions.BadRequest('Can not update a deleting zone')
# Convert to APIv2 Format
zone_data = self._view.show(context, request, zone)
if request.content_type == 'application/json-patch+json':
# Possible pattern:
#
@ -248,23 +246,16 @@ class ZonesController(rest.RestController):
# 3) ...?
raise NotImplemented('json-patch not implemented')
else:
zone_data = utils.deep_dict_merge(zone_data, body)
# Validate the new set of data
self._resource_schema.validate(zone_data)
# Unpack the values
values = self._view.load(context, request, body)
zone.set_masters(values.pop('masters', []))
# Update the zone object with the new values
zone = DesignateAdapter.parse('API_v2', body, zone)
zone.validate()
# If masters are specified then we set zone.transferred_at to None
# which will cause a new transfer
if 'attributes' in zone.obj_what_changed():
zone.transferred_at = None
# Update and persist the resource
zone.update(values)
if zone.type == 'SECONDARY' and 'email' in zone.obj_what_changed():
msg = "Changed email is not allowed."
@ -279,7 +270,7 @@ class ZonesController(rest.RestController):
else:
response.status_int = 200
return self._view.show(context, request, zone)
return DesignateAdapter.render('API_v2', zone, request=request)
@pecan.expose(template='json:', content_type='application/json')
@utils.validate_uuid('zone_id')
@ -292,4 +283,4 @@ class ZonesController(rest.RestController):
zone = self.central_api.delete_domain(context, zone_id)
response.status_int = 202
return self._view.show(context, request, zone)
return DesignateAdapter.render('API_v2', zone, request=request)

View File

@ -16,21 +16,17 @@
import pecan
from oslo_log import log as logging
from designate import schema
from designate import utils
from designate.api.v2.controllers import rest
from designate.api.v2.views.zones.tasks import transfer_accepts as \
zone_transfer_accepts_view
from designate.objects import ZoneTransferAccept
from designate.objects.adapters import DesignateAdapter
LOG = logging.getLogger(__name__)
class TransferAcceptsController(rest.RestController):
_view = zone_transfer_accepts_view.ZoneTransferAcceptsView()
_resource_schema = schema.Schema('v2', 'transfer_accept')
_collection_schema = schema.Schema('v2', 'transfer_accepts')
SORT_KEYS = ['created_at', 'id', 'updated_at']
@pecan.expose(template='json:', content_type='application/json')
@ -41,11 +37,11 @@ class TransferAcceptsController(rest.RestController):
request = pecan.request
context = request.environ['context']
transfer_accepts = \
return DesignateAdapter.render(
'API_v2',
self.central_api.get_zone_transfer_accept(
context, transfer_accept_id)
return self._view.show(context, request, transfer_accepts)
context, transfer_accept_id),
request=request)
@pecan.expose(template='json:', content_type='application/json')
def post_all(self):
@ -55,18 +51,19 @@ class TransferAcceptsController(rest.RestController):
context = request.environ['context']
body = request.body_dict
# Validate the request conforms to the schema
self._resource_schema.validate(body)
zone_transfer_accept = DesignateAdapter.parse(
'API_v2', body, ZoneTransferAccept())
zone_transfer_accept.validate()
# Convert from APIv2 -> Central format
values = self._view.load(context, request, body)
# Create the zone_transfer_request
zone_transfer_accept = self.central_api.create_zone_transfer_accept(
context, ZoneTransferAccept(**values))
context, zone_transfer_accept)
response.status_int = 201
response.headers['Location'] = self._view._get_resource_href(
request,
zone_transfer_accept)
zone_transfer_accept = DesignateAdapter.render(
'API_v2', zone_transfer_accept, request=request)
response.headers['Location'] = zone_transfer_accept['links']['self']
# Prepare and return the response body
return self._view.show(context, request, zone_transfer_accept)
return zone_transfer_accept

View File

@ -16,20 +16,15 @@
import pecan
from oslo_log import log as logging
from designate import schema
from designate import utils
from designate.api.v2.controllers import rest
from designate.api.v2.views.zones.tasks import transfer_requests as \
zone_transfer_requests_view
from designate.objects import ZoneTransferRequest
from designate.objects.adapters import DesignateAdapter
LOG = logging.getLogger(__name__)
class TransferRequestsController(rest.RestController):
_view = zone_transfer_requests_view.ZoneTransferRequestsView()
_resource_schema = schema.Schema('v2', 'transfer_request')
_collection_schema = schema.Schema('v2', 'transfer_requests')
SORT_KEYS = ['created_at', 'id', 'updated_at']
@pecan.expose(template='json:', content_type='application/json')
@ -40,11 +35,12 @@ class TransferRequestsController(rest.RestController):
request = pecan.request
context = request.environ['context']
transfer_request = \
return DesignateAdapter.render(
'API_v2',
self.central_api.get_zone_transfer_request(
context, transfer_request_id)
return self._view.show(context, request, transfer_request)
context, transfer_request_id),
request=request,
context=context)
@pecan.expose(template='json:', content_type='application/json')
def get_all(self, **params):
@ -58,10 +54,12 @@ class TransferRequestsController(rest.RestController):
# Extract any filter params.
criterion = self._apply_filter_params(params, ('status',), {})
zone_transfer_requests = self.central_api.find_zone_transfer_requests(
context, criterion, marker, limit, sort_key, sort_dir)
return self._view.list(context, request, zone_transfer_requests)
return DesignateAdapter.render(
'API_v2',
self.central_api.find_zone_transfer_requests(
context, criterion, marker, limit, sort_key, sort_dir),
request=request,
context=context)
@pecan.expose(template='json:', content_type='application/json')
@utils.validate_uuid('zone_id')
@ -70,27 +68,33 @@ class TransferRequestsController(rest.RestController):
request = pecan.request
response = pecan.response
context = request.environ['context']
body = request.body_dict
try:
body = request.body_dict
except Exception as e:
if e.message != 'TODO: Unsupported Content Type':
raise
else:
# Got a blank body
body = dict()
if body['transfer_request'] is not None:
body['transfer_request']['zone_id'] = zone_id
body['zone_id'] = zone_id
# Validate the request conforms to the schema
self._resource_schema.validate(body)
zone_transfer_request = DesignateAdapter.parse(
'API_v2', body, ZoneTransferRequest())
# Convert from APIv2 -> Central format
values = self._view.load(context, request, body)
zone_transfer_request.validate()
# Create the zone_transfer_request
zone_transfer_request = self.central_api.create_zone_transfer_request(
context, ZoneTransferRequest(**values))
context, zone_transfer_request)
response.status_int = 201
response.headers['Location'] = self._view._get_resource_href(
request,
zone_transfer_request)
zone_transfer_request = DesignateAdapter.render(
'API_v2', zone_transfer_request, request=request, context=context)
response.headers['Location'] = zone_transfer_request['links']['self']
# Prepare and return the response body
return self._view.show(context, request, zone_transfer_request)
return zone_transfer_request
@pecan.expose(template='json:', content_type='application/json')
@pecan.expose(template='json:', content_type='application/json-patch+json')
@ -102,30 +106,25 @@ class TransferRequestsController(rest.RestController):
body = request.body_dict
response = pecan.response
# Fetch the existing zone_transfer_request
zt_request = self.central_api.get_zone_transfer_request(
context, zone_transfer_request_id)
# Convert to APIv2 Format
zt_request_data = self._view.show(context,
request, zt_request)
if request.content_type == 'application/json-patch+json':
raise NotImplemented('json-patch not implemented')
else:
zt_request_data = utils.deep_dict_merge(
zt_request_data, body)
# Validate the request conforms to the schema
self._resource_schema.validate(zt_request_data)
# Fetch the existing zone_transfer_request
zone_transfer_request = self.central_api.get_zone_transfer_request(
context, zone_transfer_request_id)
zt_request.update(self._view.load(context, request, body))
zt_request = self.central_api.update_zone_transfer_request(
context, zt_request)
zone_transfer_request = DesignateAdapter.parse(
'API_v2', body, zone_transfer_request)
zone_transfer_request.validate()
zone_transfer_request = self.central_api.update_zone_transfer_request(
context, zone_transfer_request)
response.status_int = 200
return self._view.show(context, request, zt_request)
return DesignateAdapter.render(
'API_v2', zone_transfer_request, request=request, context=context)
@pecan.expose(template=None, content_type='application/json')
@utils.validate_uuid('zone_transfer_request_id')

View File

@ -1,45 +0,0 @@
# Copyright 2014 Rackspace
#
# Author: Betsy Luzader <betsy.luzader@rackspace.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.
from oslo_log import log as logging
from designate.api.v2.views import base as base_view
LOG = logging.getLogger(__name__)
class BlacklistsView(base_view.BaseView):
"""Model a Blacklist API response as a python dictionary"""
_resource_name = 'blacklist'
_collection_name = 'blacklists'
def show_basic(self, context, request, blacklist):
"""Detailed view of a blacklisted zone"""
return {
"id": blacklist['id'],
"pattern": blacklist['pattern'],
"description": blacklist['description'],
"created_at": blacklist['created_at'],
"updated_at": blacklist['updated_at'],
"links": self._get_resource_links(request, blacklist)
}
def load(self, context, request, body):
"""Extract a "central" compatible dict from an API call"""
valid_keys = ('pattern', 'description')
return self._load(context, request, body, valid_keys)

View File

@ -1,38 +0,0 @@
# Copyright 2013 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.
from oslo_log import log as logging
from designate.api.v2.views import base as base_view
LOG = logging.getLogger(__name__)
class FloatingIPView(base_view.BaseView):
"""Model a FloatingIP PTR record as a python dict"""
_resource_name = 'floatingip'
_collection_name = 'floatingips'
def _get_base_href(self, parents=None):
return '%s/v2/reverse/floatingips' % self.base_uri
def show_basic(self, context, request, item):
item = item.to_dict()
item['id'] = ":".join([item['region'], item['id']])
del item['region']
item['links'] = self._get_resource_links(
request, item, [item['id']])
return item

View File

@ -1,40 +0,0 @@
# Copyright 2013 Hewlett-Packard Development Company, L.P.
#
# Author: Kiall Mac Innes <kiall@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.
from oslo_log import log as logging
from designate.api.v2.views import base as base_view
LOG = logging.getLogger(__name__)
class LimitsView(base_view.BaseView):
"""Model a Limits API response as a python dictionary"""
_resource_name = 'limits'
_collection_name = 'limits'
def show_basic(self, context, request, absolute_limits):
"""Basic view of the limits"""
return {
"absolute": {
"max_zones": absolute_limits['domains'],
"max_zone_recordsets": absolute_limits['domain_recordsets'],
"max_zone_records": absolute_limits['domain_records'],
"max_recordset_records": absolute_limits['recordset_records']
}
}

View File

@ -1,69 +0,0 @@
# Copyright 2013 Hewlett-Packard Development Company, L.P.
#
# Author: Kiall Mac Innes <kiall@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.
from oslo_log import log as logging
from designate import objects
from designate.api.v2.views import base as base_view
LOG = logging.getLogger(__name__)
class RecordSetsView(base_view.BaseView):
"""Model a Zone API response as a python dictionary"""
_resource_name = 'recordset'
_collection_name = 'recordsets'
def _get_base_href(self, parents=None):
assert len(parents) == 1
href = "%s/v2/zones/%s/recordsets" % (self.base_uri, parents[0])
return href.rstrip('?')
def show_basic(self, context, request, recordset):
"""Basic view of a recordset"""
return {
"id": recordset['id'],
"zone_id": recordset['domain_id'],
"name": recordset['name'],
"type": recordset['type'],
"ttl": recordset['ttl'],
"records": [r.data for r in recordset['records']],
"action": recordset.action,
"status": recordset.status,
"description": recordset['description'],
"version": recordset['version'],
"created_at": recordset['created_at'],
"updated_at": recordset['updated_at'],
"links": self._get_resource_links(request, recordset,
[recordset['domain_id']])
}
def load(self, context, request, body):
"""Extract a "central" compatible dict from an API call"""
valid_keys = ('name', 'type', 'ttl', 'description', 'records')
result = self._load(context, request, body, valid_keys)
if 'records' in result:
result['records'] = objects.RecordList(objects=[
objects.Record(data=r) for r in result['records']
])
return result

View File

@ -1,43 +0,0 @@
# Copyright (c) 2014 Rackspace Hosting
# 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 oslo_log import log as logging
from designate.api.v2.views import base as base_view
LOG = logging.getLogger(__name__)
class TldsView(base_view.BaseView):
"""Model a TLD API response as a python dictionary"""
_resource_name = 'tld'
_collection_name = 'tlds'
def show_basic(self, context, request, tld):
"""Basic view of a tld"""
return {
"id": tld['id'],
"name": tld['name'],
"description": tld['description'],
"created_at": tld['created_at'],
"updated_at": tld['updated_at'],
"links": self._get_resource_links(request, tld)
}
def load(self, context, request, body):
"""Extract a "central" compatible dict from an API call"""
valid_keys = ('name', 'description')
return self._load(context, request, body, valid_keys)

View File

@ -1,50 +0,0 @@
# Copyright 2015 Hewlett-Packard Development Company, L.P.
#
# Author: Kiall Mac Innes <kiall@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.
from oslo_log import log as logging
from designate.api.v2.views import base as base_view
LOG = logging.getLogger(__name__)
class TsigKeysView(base_view.BaseView):
"""Model a TsigKey API response as a python dictionary"""
_resource_name = 'tsigkey'
_collection_name = 'tsigkeys'
def show_basic(self, context, request, tsigkey):
"""Detailed view of a TsigKey"""
return {
"id": tsigkey['id'],
"name": tsigkey['name'],
"algorithm": tsigkey['algorithm'],
"secret": tsigkey['secret'],
"scope": tsigkey['scope'],
"resource_id": tsigkey['resource_id'],
"created_at": tsigkey['created_at'],
"updated_at": tsigkey['updated_at'],
"links": self._get_resource_links(request, tsigkey)
}
def load(self, context, request, body):
"""Extract a "central" compatible dict from an API call"""
valid_keys = ('name', 'algorithm', 'secret', 'scope', 'resource_id')
return self._load(context, request, body, valid_keys)

View File

@ -1,57 +0,0 @@
# Copyright 2013 Hewlett-Packard Development Company, L.P.
#
# Author: Kiall Mac Innes <kiall@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.
from oslo_log import log as logging
from designate.api.v2.views import base as base_view
LOG = logging.getLogger(__name__)
class ZonesView(base_view.BaseView):
"""Model a Zone API response as a python dictionary"""
_resource_name = 'zone'
_collection_name = 'zones'
def show_basic(self, context, request, zone):
"""Basic view of a zone"""
values = {
"id": zone['id'],
"pool_id": zone['pool_id'],
"project_id": zone['tenant_id'],
"name": zone['name'],
"type": zone['type'],
"email": zone['email'],
"description": zone['description'],
"ttl": zone['ttl'],
"serial": zone['serial'],
"status": zone['status'],
"action": zone['action'],
"version": zone['version'],
"created_at": zone['created_at'],
"updated_at": zone['updated_at'],
"transferred_at": zone['transferred_at'],
"masters": zone.masters,
"links": self._get_resource_links(request, zone)
}
return values
def load(self, context, request, body):
"""Extract a "central" compatible dict from an API call"""
zone_keys = ('name', 'description', 'type', 'email', 'masters', 'ttl')
return self._load(context, request, body, zone_keys)

View File

@ -1,53 +0,0 @@
# Copyright 2014 Hewlett-Packard Development Company, L.P.
#
# Author: Graham Hayes <graham.hayes@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.
from oslo_log import log as logging
from designate.api.v2.views import base as base_view
LOG = logging.getLogger(__name__)
class ZoneTransferAcceptsView(base_view.BaseView):
"""Model a ZoneTransferRequest API response as a python dictionary"""
_resource_name = 'transfer_accept'
_collection_name = 'transfer_accepts'
def _get_base_href(self, parents=None):
href = "%s/v2/zones/tasks/%s" % (self.base_uri, self._collection_name)
return href.rstrip('?')
def _get_resource_links(self, request, item):
return {
"self": self._get_resource_href(request, item),
"zone": "%s/v2/zones/%s" % (self.base_uri, item.domain_id)
}
def show_basic(self, context, request, zt_accept):
"""Basic view of a ZoneTransferRequest"""
return {
"id": zt_accept.id,
"status": zt_accept.status,
"links": self._get_resource_links(request, zt_accept)
}
def load(self, context, request, body):
"""Extract a "central" compatible dict from an API call"""
valid_keys = ('zone_transfer_request_id', 'key')
return self._load(context, request, body, valid_keys)

View File

@ -1,85 +0,0 @@
# Copyright 2014 Hewlett-Packard Development Company, L.P.
#
# Author: Graham Hayes <graham.hayes@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.
from oslo_log import log as logging
from designate.api.v2.views import base as base_view
from designate import exceptions
from designate import policy
LOG = logging.getLogger(__name__)
class ZoneTransferRequestsView(base_view.BaseView):
"""Model a ZoneTransferRequest API response as a python dictionary"""
_resource_name = 'transfer_request'
_collection_name = 'transfer_requests'
def _get_base_href(self, parents=None):
href = "%s/v2/zones/tasks/%s" % (self.base_uri, self._collection_name)
return href.rstrip('?')
def show_basic(self, context, request, zt_request):
"""Basic view of a ZoneTransferRequest"""
try:
target = {
'tenant_id': zt_request.tenant_id,
}
policy.check('get_zone_transfer_request_detailed', context, target)
except exceptions.Forbidden:
return {
"id": zt_request.id,
"description": zt_request.description,
"zone_id": zt_request.domain_id,
"zone_name": zt_request.domain_name,
"status": zt_request.status,
"links": self._get_resource_links(request, zt_request)
}
else:
return {
"id": zt_request.id,
"description": zt_request.description,
"zone_id": zt_request.domain_id,
"zone_name": zt_request.domain_name,
"target_project_id": zt_request.target_tenant_id,
"project_id": zt_request.tenant_id,
"created_at": zt_request.created_at,
"updated_at": zt_request.updated_at,
"status": zt_request.status,
"key": zt_request.key,
"links": self._get_resource_links(request, zt_request)
}
def load(self, context, request, body):
"""Extract a "central" compatible dict from an API call"""
valid_keys = ('description', 'domain_id', 'target_tenant_id')
zt_request = body["transfer_request"]
old_keys = {
'zone_id': 'domain_id',
'project_id': 'tenant_id',
'target_project_id': 'target_tenant_id',
}
for key in zt_request:
if key in old_keys:
zt_request[old_keys[key]] = ''
zt_request[old_keys[key]] = zt_request.pop(key)
return self._load(context, request, body, valid_keys)

View File

@ -184,7 +184,7 @@ class Domain(base.DictObjectMixin, base.SoftDeleteObjectMixin,
if self.type == 'SECONDARY' and self.masters is None:
errors = ValidationErrorList()
e = ValidationError()
e.absolute_path = ['']
e.path = ['type']
e.validator = 'required'
e.validator_value = ['masters']
e.message = "'masters' is a required property"

View File

@ -194,13 +194,13 @@ class TestCase(base.BaseTestCase):
{'name': 'Pool-One',
'description': 'Pool-One description',
'attributes': [{'key': 'scope', 'value': 'public'}],
'ns_records': [{'priority': 0, 'hostname': 'ns1.example.org.'},
{'priority': 1, 'hostname': 'ns2.example.org.'}]},
'ns_records': [{'priority': 1, 'hostname': 'ns1.example.org.'},
{'priority': 2, 'hostname': 'ns2.example.org.'}]},
{'name': 'Pool-Two',
'description': 'Pool-Two description',
'attributes': [{'key': 'scope', 'value': 'public'}],
'ns_records': [{'priority': 0, 'hostname': 'ns1.example.org.'}]},
'ns_records': [{'priority': 1, 'hostname': 'ns1.example.org.'}]},
]
pool_attribute_fixtures = [

View File

@ -46,12 +46,12 @@ class ApiV2TestCase(ApiTestCase):
# Inject the NormalizeURIMiddleware middleware
self.app = middleware.NormalizeURIMiddleware(self.app)
# Inject the FaultWrapper middleware
self.app = middleware.FaultWrapperMiddleware(self.app)
# Inject the ValidationError middleware
self.app = middleware.APIv2ValidationErrorMiddleware(self.app)
# Inject the FaultWrapper middleware
self.app = middleware.FaultWrapperMiddleware(self.app)
# Inject the TestContext middleware
self.app = middleware.TestContextMiddleware(
self.app, self.admin_context.tenant,

View File

@ -58,16 +58,15 @@ class ApiV2BlacklistsTest(ApiV2TestCase):
self.assertEqual('application/json', response.content_type)
# Verify the body structure
self.assertIn('blacklist', response.json)
self.assertIn('links', response.json['blacklist'])
self.assertIn('self', response.json['blacklist']['links'])
self.assertIn('links', response.json)
self.assertIn('self', response.json['links'])
# Verify the returned values
self.assertIn('id', response.json['blacklist'])
self.assertIn('created_at', response.json['blacklist'])
self.assertIsNone(response.json['blacklist']['updated_at'])
self.assertIn('id', response.json)
self.assertIn('created_at', response.json)
self.assertIsNone(response.json['updated_at'])
self.assertEqual(self.get_blacklist_fixture(0)['pattern'],
response.json['blacklist']['pattern'])
response.json['pattern'])
def test_get_bkaclist_invalid_id(self):
self._assert_invalid_uuid(self.client.get, '/blacklists/%s')
@ -75,24 +74,22 @@ class ApiV2BlacklistsTest(ApiV2TestCase):
def test_create_blacklist(self):
self.policy({'create_blacklist': '@'})
fixture = self.get_blacklist_fixture(0)
response = self.client.post_json('/blacklists/',
{'blacklist': fixture})
response = self.client.post_json('/blacklists/', fixture)
# Verify the headers
self.assertEqual(201, response.status_int)
self.assertEqual('application/json', response.content_type)
# Verify the body structure
self.assertIn('blacklist', response.json)
self.assertIn('links', response.json['blacklist'])
self.assertIn('self', response.json['blacklist']['links'])
self.assertIn('links', response.json)
self.assertIn('self', response.json['links'])
# Verify the returned values
self.assertIn('id', response.json['blacklist'])
self.assertIn('created_at', response.json['blacklist'])
self.assertIsNone(response.json['blacklist']['updated_at'])
self.assertIn('id', response.json)
self.assertIn('created_at', response.json)
self.assertIsNone(response.json['updated_at'])
self.assertEqual(fixture['pattern'],
response.json['blacklist']['pattern'])
response.json['pattern'])
def test_delete_blacklist(self):
blacklist = self.create_blacklist(fixture=0)
@ -108,8 +105,7 @@ class ApiV2BlacklistsTest(ApiV2TestCase):
self.policy({'update_blacklist': '@'})
# Prepare the update body
body = {'blacklist': {'description': 'prefix-%s' %
blacklist['description']}}
body = {'description': 'prefix-%s' % blacklist['description']}
response = self.client.patch_json('/blacklists/%s' %
blacklist['id'], body,
@ -120,15 +116,14 @@ class ApiV2BlacklistsTest(ApiV2TestCase):
self.assertEqual('application/json', response.content_type)
# Verify the body structure
self.assertIn('blacklist', response.json)
self.assertIn('links', response.json['blacklist'])
self.assertIn('self', response.json['blacklist']['links'])
self.assertIn('links', response.json)
self.assertIn('self', response.json['links'])
# Verify the returned values
self.assertIn('id', response.json['blacklist'])
self.assertIsNotNone(response.json['blacklist']['updated_at'])
self.assertIn('id', response.json)
self.assertIsNotNone(response.json['updated_at'])
self.assertEqual('prefix-%s' % blacklist['description'],
response.json['blacklist']['description'])
response.json['description'])
def test_update_bkaclist_invalid_id(self):
self._assert_invalid_uuid(self.client.patch_json, '/blacklists/%s')

View File

@ -33,10 +33,9 @@ class ApiV2ReverseFloatingIPTest(ApiV2TestCase):
self.assertEqual(200, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertIn('floatingip', response.json)
# TODO(ekarlso): Remove the floatingip key - bug in v2 api
fip_record = response.json['floatingip']
fip_record = response.json
self.assertEqual(":".join([fip['region'],
fip['id']]), fip_record['id'])
self.assertEqual(fip['address'], fip_record['address'])
@ -60,10 +59,9 @@ class ApiV2ReverseFloatingIPTest(ApiV2TestCase):
self.assertEqual(200, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertIn('floatingip', response.json)
# TODO(ekarlso): Remove the floatingip key - bug in v2 api
fip_record = response.json['floatingip']
fip_record = response.json
self.assertEqual(":".join([fip['region'], fip['id']]),
fip_record['id'])
self.assertEqual(fip['address'], fip_record['address'])
@ -140,14 +138,13 @@ class ApiV2ReverseFloatingIPTest(ApiV2TestCase):
response = self.client.patch_json(
'/reverse/floatingips/%s' % ":".join([fip['region'], fip['id']]),
{"floatingip": fixture.to_dict()},
fixture.to_dict(),
headers={'X-Test-Tenant-Id': 'tenant'})
self.assertEqual(200, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertIn('floatingip', response.json)
fip_record = response.json['floatingip']
fip_record = response.json
self.assertEqual(":".join([fip['region'], fip['id']]),
fip_record['id'])
self.assertEqual(fip['address'], fip_record['address'])
@ -163,7 +160,7 @@ class ApiV2ReverseFloatingIPTest(ApiV2TestCase):
url = '/reverse/floatingips/%s' % ":".join([fip['region'], fip['id']])
self._assert_exception('not_found', 404, self.client.patch_json, url,
{'floatingip': fixture.to_dict()})
fixture.to_dict())
def test_set_floatingip_invalid_ptrdname(self):
fip = self.network_api.fake.allocate_floatingip('tenant')
@ -171,7 +168,7 @@ class ApiV2ReverseFloatingIPTest(ApiV2TestCase):
url = '/reverse/floatingips/%s' % ":".join([fip['region'], fip['id']])
self._assert_exception('invalid_object', 400, self.client.patch_json,
url, {'floatingip': {'ptrdname': 'test|'}})
url, {'ptrdname': 'test|'})
def test_set_floatingip_invalid_key(self):
url = '/reverse/floatingips/%s' % 'foo:random'
@ -206,7 +203,7 @@ class ApiV2ReverseFloatingIPTest(ApiV2TestCase):
# Unset PTR ('ptrdname' is None aka null in JSON)
response = self.client.patch_json(
'/reverse/floatingips/%s' % ":".join([fip['region'], fip['id']]),
{'floatingip': {'ptrdname': None}},
{'ptrdname': None},
headers={'X-Test-Tenant-Id': context.tenant})
self.assertEqual(None, response.json)
self.assertEqual(200, response.status_int)
@ -235,4 +232,4 @@ class ApiV2ReverseFloatingIPTest(ApiV2TestCase):
url = '/reverse/floatingips/%s' % ":".join([fip['region'], fip['id']])
self._assert_exception('not_found', 404, self.client.patch_json, url,
{"floatingip": {'ptrdname': None}})
{'ptrdname': None})

View File

@ -25,16 +25,14 @@ class ApiV2LimitsTest(ApiV2TestCase):
self.assertEqual(200, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertIn('limits', response.json)
self.assertIn('absolute', response.json['limits'])
self.assertIn('max_zones', response.json['limits']['absolute'])
self.assertIn('max_zone_records', response.json['limits']['absolute'])
self.assertIn('max_zones', response.json)
self.assertIn('max_zone_records', response.json)
self.assertIn('max_zone_recordsets',
response.json['limits']['absolute'])
response.json)
self.assertIn('max_recordset_records',
response.json['limits']['absolute'])
response.json)
absolutelimits = response.json['limits']['absolute']
absolutelimits = response.json
self.assertEqual(cfg.CONF.quota_domains, absolutelimits['max_zones'])
self.assertEqual(cfg.CONF.quota_domain_records,

View File

@ -42,28 +42,27 @@ class ApiV2PoolsTest(ApiV2TestCase):
fixture['attributes'] = _attributes_to_api(fixture['attributes'])
response = self.client.post_json(
'/pools', {'pool': fixture})
'/pools', fixture)
# Check the headers are what we expect
self.assertEqual(201, response.status_int)
self.assertEqual('application/json', response.content_type)
# Check the body structure is what we expect
self.assertIn('pool', response.json)
self.assertIn('links', response.json['pool'])
self.assertIn('self', response.json['pool']['links'])
self.assertIn('links', response.json)
self.assertIn('self', response.json['links'])
# Check the values returned are what we expect
self.assertIn('id', response.json['pool'])
self.assertIn('created_at', response.json['pool'])
self.assertIsNone(response.json['pool']['updated_at'])
self.assertEqual(response.json['pool']['name'], fixture['name'])
self.assertIn('id', response.json)
self.assertIn('created_at', response.json)
self.assertIsNone(response.json['updated_at'])
self.assertEqual(response.json['name'], fixture['name'])
self.assertEqual(
response.json['pool']['description'], fixture['description'])
response.json['description'], fixture['description'])
self.assertEqual(
response.json['pool']['attributes'], fixture['attributes'])
response.json['attributes'], fixture['attributes'])
self.assertEqual(
response.json['pool']['ns_records'], fixture['ns_records'])
response.json['ns_records'], fixture['ns_records'])
def test_create_pool_validation(self):
# NOTE: The schemas should be tested separatly to the API. So we
@ -83,14 +82,10 @@ class ApiV2PoolsTest(ApiV2TestCase):
# Reset the correct attributes
fixture['attributes'] = self.get_pool_attribute_fixture(fixture=0)
body = {'pool': fixture, 'junk': 'Junk Field'}
# Ensure it fails with a 400
self._assert_exception(
'invalid_object', 400, self.client.post_json, '/pools', body)
# Add a junk field to the body
fixture['junk'] = 'Junk Field'
body = {'pool': fixture}
body = fixture
# Ensure it fails with a 400
self._assert_exception(
'invalid_object', 400, self.client.post_json, '/pools', body)
@ -100,7 +95,7 @@ class ApiV2PoolsTest(ApiV2TestCase):
fixture = self.get_pool_fixture(fixture=0)
fixture['attributes'] = _attributes_to_api(fixture['attributes'])
body = {'pool': fixture}
body = fixture
response = self.client.post_json('/pools', body)
# Check that the create went through
@ -149,37 +144,36 @@ class ApiV2PoolsTest(ApiV2TestCase):
self.assertEqual('application/json', response.content_type)
# Check the body structure is what we expect
self.assertIn('pool', response.json)
self.assertIn('links', response.json['pool'])
self.assertIn('self', response.json['pool']['links'])
self.assertIn('links', response.json)
self.assertIn('self', response.json['links'])
# Check the values returned are what we expect
self.assertIn('id', response.json['pool'])
self.assertIn('created_at', response.json['pool'])
self.assertIsNone(response.json['pool']['updated_at'])
self.assertEqual(pool['name'], response.json['pool']['name'])
self.assertIn('id', response.json)
self.assertIn('created_at', response.json)
self.assertIsNone(response.json['updated_at'])
self.assertEqual(pool['name'], response.json['name'])
self.assertEqual(pool['description'],
response.json['pool']['description'])
response.json['description'])
self.assertEqual(len(pool['attributes']),
len(response.json['pool']['attributes']))
len(response.json['attributes']))
for attribute in pool['attributes']:
self.assertEqual(
attribute['value'],
response.json['pool']['attributes'][attribute['key']])
response.json['attributes'][attribute['key']])
self.assertEqual(len(pool['ns_records']),
len(response.json['pool']['ns_records']))
len(response.json['ns_records']))
self.assertEqual(
[n.hostname for n in pool['ns_records']],
[n['hostname'] for n in response.json['pool']['ns_records']])
[n['hostname'] for n in response.json['ns_records']])
def test_update_pool(self):
# Create a pool
pool = self.create_pool()
# Prepare an update body
body = {'pool': {'description': 'Tester'}}
body = {'description': 'Tester'}
url = '/pools/%s' % pool['id']
response = self.client.patch_json(url, body, status=200)
@ -189,39 +183,38 @@ class ApiV2PoolsTest(ApiV2TestCase):
self.assertEqual('application/json', response.content_type)
# Check the body structure is what we expect
self.assertIn('pool', response.json)
self.assertIn('links', response.json['pool'])
self.assertIn('self', response.json['pool']['links'])
self.assertIn('links', response.json)
self.assertIn('self', response.json['links'])
# Check the values returned are what we expect
self.assertIn('id', response.json['pool'])
self.assertIsNotNone(response.json['pool']['updated_at'])
self.assertEqual('Tester', response.json['pool']['description'])
self.assertIn('id', response.json)
self.assertIsNotNone(response.json['updated_at'])
self.assertEqual('Tester', response.json['description'])
# Check the rest of the values are unchanged
self.assertEqual(pool['name'], response.json['pool']['name'])
self.assertEqual(pool['name'], response.json['name'])
self.assertEqual(len(pool['attributes']),
len(response.json['pool']['attributes']))
len(response.json['attributes']))
for attribute in pool['attributes']:
self.assertEqual(
attribute['value'],
response.json['pool']['attributes'][attribute['key']])
response.json['attributes'][attribute['key']])
self.assertEqual(len(pool['ns_records']),
len(response.json['pool']['ns_records']))
len(response.json['ns_records']))
self.assertEqual(
[n.hostname for n in pool['ns_records']],
[n['hostname'] for n in response.json['pool']['ns_records']])
[n['hostname'] for n in response.json['ns_records']])
def test_update_pool_ns_records(self):
# Create a pool
pool = self.create_pool()
# Prepare an update body
body = {'pool': {'ns_records': [
body = {'ns_records': [
{'priority': 1, 'hostname': 'new-ns1.example.org.'},
{'priority': 2, 'hostname': 'new-ns2.example.org.'},
]}}
]}
url = '/pools/%s' % pool['id']
response = self.client.patch_json(url, body, status=200)
@ -231,22 +224,21 @@ class ApiV2PoolsTest(ApiV2TestCase):
self.assertEqual('application/json', response.content_type)
# Check the body structure is what we expect
self.assertIn('pool', response.json)
self.assertIn('id', response.json['pool'])
self.assertIn('links', response.json['pool'])
self.assertIn('id', response.json)
self.assertIn('links', response.json)
# Check the values returned are what we expect
self.assertEqual(2, len(response.json['pool']['ns_records']))
self.assertEqual(2, len(response.json['ns_records']))
self.assertEqual(['new-ns1.example.org.', 'new-ns2.example.org.'],
[n['hostname'] for n in
response.json['pool']['ns_records']])
response.json['ns_records']])
def test_update_pool_attributes(self):
# Create a pool
pool = self.create_pool()
# Prepare an update body
body = {'pool': {'attributes': {'scope': 'private'}}}
body = {"attributes": {"scope": "private"}}
url = '/pools/%s' % pool['id']
response = self.client.patch_json(url, body, status=200)
@ -256,9 +248,9 @@ class ApiV2PoolsTest(ApiV2TestCase):
self.assertEqual('application/json', response.content_type)
# Check the values returned are what we expect
self.assertEqual(1, len(response.json['pool']['attributes']))
self.assertEqual(1, len(response.json['attributes']))
self.assertEqual('private',
response.json['pool']['attributes']['scope'])
response.json['attributes']['scope'])
def test_delete_pool(self):
pool = self.create_pool()

View File

@ -35,25 +35,24 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
# Prepare a RecordSet fixture
fixture = self.get_recordset_fixture(self.domain['name'], fixture=0)
response = self.client.post_json(
'/zones/%s/recordsets' % self.domain['id'], {'recordset': fixture})
'/zones/%s/recordsets' % self.domain['id'], fixture)
# Check the headers are what we expect
self.assertEqual(201, response.status_int)
self.assertEqual('application/json', response.content_type)
# Check the body structure is what we expect
self.assertIn('recordset', response.json)
self.assertIn('links', response.json['recordset'])
self.assertIn('self', response.json['recordset']['links'])
self.assertIn('links', response.json)
self.assertIn('self', response.json['links'])
# Check the values returned are what we expect
self.assertIn('id', response.json['recordset'])
self.assertIn('created_at', response.json['recordset'])
self.assertIsNone(response.json['recordset']['updated_at'])
self.assertIn('records', response.json['recordset'])
self.assertIn('id', response.json)
self.assertIn('created_at', response.json)
self.assertIsNone(response.json['updated_at'])
self.assertIn('records', response.json)
# The action and status are NONE and ACTIVE as there are no records
self.assertEqual('NONE', response.json['recordset']['action'])
self.assertEqual('ACTIVE', response.json['recordset']['status'])
self.assertEqual('NONE', response.json['action'])
self.assertEqual('ACTIVE', response.json['status'])
def test_create_recordset_with_records(self):
# Prepare a RecordSet fixture
@ -65,20 +64,17 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
)
response = self.client.post_json(
'/zones/%s/recordsets' % self.domain['id'], {'recordset': fixture})
'/zones/%s/recordsets' % self.domain['id'], fixture)
# Check the headers are what we expect
self.assertEqual(202, response.status_int)
self.assertEqual('application/json', response.content_type)
# Check the body structure is what we expect
self.assertIn('recordset', response.json)
# Check the values returned are what we expect
self.assertIn('records', response.json['recordset'])
self.assertEqual(2, len(response.json['recordset']['records']))
self.assertEqual('CREATE', response.json['recordset']['action'])
self.assertEqual('PENDING', response.json['recordset']['status'])
self.assertIn('records', response.json)
self.assertEqual(2, len(response.json['records']))
self.assertEqual('CREATE', response.json['action'])
self.assertEqual('PENDING', response.json['status'])
# Check the zone's status is as expected
response = self.client.get('/zones/%s' % self.domain['id'],
@ -86,9 +82,9 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
# Check the headers are what we expect
self.assertEqual(200, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertIn('zone', response.json)
self.assertEqual('UPDATE', response.json['zone']['action'])
self.assertEqual('PENDING', response.json['zone']['status'])
self.assertEqual('UPDATE', response.json['action'])
self.assertEqual('PENDING', response.json['status'])
def test_create_recordset_invalid_id(self):
self._assert_invalid_uuid(self.client.post, '/zones/%s/recordsets')
@ -99,18 +95,11 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
# Fetch a fixture
fixture = self.get_recordset_fixture(self.domain['name'], fixture=0)
# Add a junk field to the wrapper
body = {'recordset': fixture, 'junk': 'Junk Field'}
url = '/zones/%s/recordsets' % self.domain['id']
# Ensure it fails with a 400
self._assert_exception(
'invalid_object', 400, self.client.post_json, url, body)
# Add a junk field to the body
fixture['junk'] = 'Junk Field'
body = {'recordset': fixture}
body = fixture
# Ensure it fails with a 400
self._assert_exception(
@ -121,7 +110,7 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
def test_create_recordset_timeout(self, _):
fixture = self.get_recordset_fixture(self.domain['name'], fixture=0)
body = {'recordset': fixture}
body = fixture
url = '/zones/%s/recordsets' % self.domain['id']
@ -133,7 +122,7 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
def test_create_recordset_duplicate(self, _):
fixture = self.get_recordset_fixture(self.domain['name'], fixture=0)
body = {'recordset': fixture}
body = fixture
url = '/zones/%s/recordsets' % self.domain['id']
@ -143,7 +132,7 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
def test_create_recordset_invalid_domain(self):
fixture = self.get_recordset_fixture(self.domain['name'], fixture=0)
body = {'recordset': fixture}
body = fixture
url = '/zones/ba751950-6193-11e3-949a-0800200c9a66/recordsets'
@ -219,7 +208,7 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
for fixture in fixtures:
response = self.client.post_json(
'/zones/%s/recordsets' % self.domain['id'],
{'recordset': fixture})
fixture)
get_urls = [
'/zones/%s/recordsets?data=192.0.2.1' % self.domain['id'],
@ -284,20 +273,18 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
self.assertEqual(200, response.status_int)
self.assertEqual('application/json', response.content_type)
# Check the body structure is what we expect
self.assertIn('recordset', response.json)
self.assertIn('links', response.json['recordset'])
self.assertIn('self', response.json['recordset']['links'])
self.assertIn('links', response.json)
self.assertIn('self', response.json['links'])
# Check the values returned are what we expect
self.assertIn('id', response.json['recordset'])
self.assertIn('created_at', response.json['recordset'])
self.assertIsNone(response.json['recordset']['updated_at'])
self.assertEqual(recordset['name'], response.json['recordset']['name'])
self.assertEqual(recordset['type'], response.json['recordset']['type'])
self.assertIn('id', response.json)
self.assertIn('created_at', response.json)
self.assertIsNone(response.json['updated_at'])
self.assertEqual(recordset['name'], response.json['name'])
self.assertEqual(recordset['type'], response.json['type'])
# The action and status are NONE and ACTIVE as there are no records
self.assertEqual('NONE', response.json['recordset']['action'])
self.assertEqual('ACTIVE', response.json['recordset']['status'])
self.assertEqual('NONE', response.json['action'])
self.assertEqual('ACTIVE', response.json['status'])
def test_get_recordset_invalid_id(self):
self._assert_invalid_uuid(self.client.get, '/zones/%s/recordsets/%s')
@ -326,7 +313,7 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
recordset = self.create_recordset(self.domain)
# Prepare an update body
body = {'recordset': {'description': 'Tester'}}
body = {'description': 'Tester'}
url = '/zones/%s/recordsets/%s' % (recordset['domain_id'],
recordset['id'])
@ -337,17 +324,16 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
self.assertEqual('application/json', response.content_type)
# Check the body structure is what we expect
self.assertIn('recordset', response.json)
self.assertIn('links', response.json['recordset'])
self.assertIn('self', response.json['recordset']['links'])
self.assertIn('links', response.json)
self.assertIn('self', response.json['links'])
# Check the values returned are what we expect
self.assertIn('id', response.json['recordset'])
self.assertIsNotNone(response.json['recordset']['updated_at'])
self.assertEqual('Tester', response.json['recordset']['description'])
self.assertIn('id', response.json)
self.assertIsNotNone(response.json['updated_at'])
self.assertEqual('Tester', response.json['description'])
# The action and status are NONE and ACTIVE as there are no records
self.assertEqual('NONE', response.json['recordset']['action'])
self.assertEqual('ACTIVE', response.json['recordset']['status'])
self.assertEqual('NONE', response.json['action'])
self.assertEqual('ACTIVE', response.json['status'])
# Check the zone's status is as expected
response = self.client.get('/zones/%s' % recordset['domain_id'],
@ -355,9 +341,9 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
# Check the headers are what we expect
self.assertEqual(200, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertIn('zone', response.json)
self.assertEqual('UPDATE', response.json['zone']['action'])
self.assertEqual('PENDING', response.json['zone']['status'])
self.assertEqual('UPDATE', response.json['action'])
self.assertEqual('PENDING', response.json['status'])
def test_update_recordset_with_record_create(self):
# Create a recordset
@ -368,8 +354,9 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
self.assertEqual('ACTIVE', recordset['status'])
# Prepare an update body
body = {'recordset': {'description': 'Tester',
'records': ['192.0.2.1', '192.0.2.2']}}
body = {'description': 'Tester',
'type': 'A',
'records': ['192.0.2.1', '192.0.2.2']}
url = '/zones/%s/recordsets/%s' % (recordset['domain_id'],
recordset['id'])
@ -379,16 +366,13 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
self.assertEqual(202, response.status_int)
self.assertEqual('application/json', response.content_type)
# Check the body structure is what we expect
self.assertIn('recordset', response.json)
# Check the values returned are what we expect
self.assertIn('records', response.json['recordset'])
self.assertEqual(2, len(response.json['recordset']['records']))
self.assertIn('records', response.json)
self.assertEqual(2, len(response.json['records']))
self.assertEqual(set(['192.0.2.1', '192.0.2.2']),
set(response.json['recordset']['records']))
self.assertEqual('UPDATE', response.json['recordset']['action'])
self.assertEqual('PENDING', response.json['recordset']['status'])
set(response.json['records']))
self.assertEqual('UPDATE', response.json['action'])
self.assertEqual('PENDING', response.json['status'])
# Check the zone's status is as expected
response = self.client.get('/zones/%s' % recordset['domain_id'],
@ -396,9 +380,9 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
# Check the headers are what we expect
self.assertEqual(200, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertIn('zone', response.json)
self.assertEqual('UPDATE', response.json['zone']['action'])
self.assertEqual('PENDING', response.json['zone']['status'])
self.assertEqual('UPDATE', response.json['action'])
self.assertEqual('PENDING', response.json['status'])
def test_update_recordset_with_record_replace(self):
# Create a recordset with one record
@ -406,8 +390,8 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
self.create_record(self.domain, recordset)
# Prepare an update body
body = {'recordset': {'description': 'Tester',
'records': ['192.0.2.201', '192.0.2.202']}}
body = {'description': 'Tester',
'records': ['192.0.2.201', '192.0.2.202']}
url = '/zones/%s/recordsets/%s' % (recordset['domain_id'],
recordset['id'])
@ -417,14 +401,11 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
self.assertEqual(202, response.status_int)
self.assertEqual('application/json', response.content_type)
# Check the body structure is what we expect
self.assertIn('recordset', response.json)
# Check the values returned are what we expect
self.assertIn('records', response.json['recordset'])
self.assertEqual(2, len(response.json['recordset']['records']))
self.assertIn('records', response.json)
self.assertEqual(2, len(response.json['records']))
self.assertEqual(set(['192.0.2.201', '192.0.2.202']),
set(response.json['recordset']['records']))
set(response.json['records']))
# Check the zone's status is as expected
response = self.client.get('/zones/%s' % recordset['domain_id'],
@ -432,9 +413,9 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
# Check the headers are what we expect
self.assertEqual(200, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertIn('zone', response.json)
self.assertEqual('UPDATE', response.json['zone']['action'])
self.assertEqual('PENDING', response.json['zone']['status'])
self.assertEqual('UPDATE', response.json['action'])
self.assertEqual('PENDING', response.json['status'])
def test_update_recordset_with_record_clear(self):
# Create a recordset with one record
@ -442,7 +423,7 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
self.create_record(self.domain, recordset)
# Prepare an update body
body = {'recordset': {'description': 'Tester', 'records': []}}
body = {'description': 'Tester', 'records': []}
url = '/zones/%s/recordsets/%s' % (recordset['domain_id'],
recordset['id'])
@ -452,12 +433,9 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
self.assertEqual(200, response.status_int)
self.assertEqual('application/json', response.content_type)
# Check the body structure is what we expect
self.assertIn('recordset', response.json)
# Check the values returned are what we expect
self.assertIn('records', response.json['recordset'])
self.assertEqual(0, len(response.json['recordset']['records']))
self.assertIn('records', response.json)
self.assertEqual(0, len(response.json['records']))
# Check the zone's status is as expected
response = self.client.get('/zones/%s' % recordset['domain_id'],
@ -465,9 +443,9 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
# Check the headers are what we expect
self.assertEqual(200, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertIn('zone', response.json)
self.assertEqual('UPDATE', response.json['zone']['action'])
self.assertEqual('PENDING', response.json['zone']['status'])
self.assertEqual('UPDATE', response.json['action'])
self.assertEqual('PENDING', response.json['status'])
def test_update_recordset_invalid_id(self):
self._assert_invalid_uuid(
@ -480,9 +458,9 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
recordset = self.create_recordset(self.domain)
# Prepare an update body with junk in the wrapper
body = {'recordset': {'description': 'Tester',
'records': ['192.3.3.17'],
'junk': 'Junk Field'}}
body = {'description': 'Tester',
'records': ['192.3.3.17'],
'junk': 'Junk Field'}
# Ensure it fails with a 400
url = '/zones/%s/recordsets/%s' % (recordset['domain_id'],
@ -492,7 +470,7 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
url, body)
# Prepare an update body with junk in the body
body = {'recordset': {'description': 'Tester', 'junk': 'Junk Field'}}
body = {'description': 'Tester', 'junk': 'Junk Field'}
# Ensure it fails with a 400
self._assert_exception('invalid_object', 400, self.client.put_json,
@ -502,7 +480,7 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
side_effect=exceptions.DuplicateRecordSet())
def test_update_recordset_duplicate(self, _):
# Prepare an update body
body = {'recordset': {'description': 'Tester'}}
body = {'description': 'Tester'}
# Ensure it fails with a 409
url = ('/zones/%s/recordsets/ba751950-6193-11e3-949a-0800200c9a66'
@ -515,7 +493,7 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
side_effect=messaging.MessagingTimeout())
def test_update_recordset_timeout(self, _):
# Prepare an update body
body = {'recordset': {'description': 'Tester'}}
body = {'description': 'Tester'}
# Ensure it fails with a 504
url = ('/zones/%s/recordsets/ba751950-6193-11e3-949a-0800200c9a66'
@ -528,7 +506,7 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
side_effect=exceptions.RecordSetNotFound())
def test_update_recordset_missing(self, _):
# Prepare an update body
body = {'recordset': {'description': 'Tester'}}
body = {'description': 'Tester'}
# Ensure it fails with a 404
url = ('/zones/%s/recordsets/ba751950-6193-11e3-949a-0800200c9a66'
@ -545,11 +523,10 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
response = self.client.delete(url, status=202)
self.assertEqual('application/json', response.content_type)
self.assertIn('recordset', response.json)
# Currently recordset does not have a status field. As there are no
# records, the recordset action/status show up as 'NONE', 'ACTIVE'
self.assertEqual('NONE', response.json['recordset']['action'])
self.assertEqual('ACTIVE', response.json['recordset']['status'])
self.assertEqual('NONE', response.json['action'])
self.assertEqual('ACTIVE', response.json['status'])
# Check the zone's status is as expected
response = self.client.get('/zones/%s' % recordset['domain_id'],
@ -557,9 +534,9 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
# Check the headers are what we expect
self.assertEqual(200, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertIn('zone', response.json)
self.assertEqual('UPDATE', response.json['zone']['action'])
self.assertEqual('PENDING', response.json['zone']['status'])
self.assertEqual('UPDATE', response.json['action'])
self.assertEqual('PENDING', response.json['status'])
def test_delete_recordset_with_records(self):
# Create a recordset with one record
@ -571,9 +548,8 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
response = self.client.delete(url, status=202)
self.assertEqual('application/json', response.content_type)
self.assertIn('recordset', response.json)
self.assertEqual('DELETE', response.json['recordset']['action'])
self.assertEqual('PENDING', response.json['recordset']['status'])
self.assertEqual('DELETE', response.json['action'])
self.assertEqual('PENDING', response.json['status'])
# Check the zone's status is as expected
response = self.client.get('/zones/%s' % recordset['domain_id'],
@ -581,9 +557,9 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
# Check the headers are what we expect
self.assertEqual(200, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertIn('zone', response.json)
self.assertEqual('UPDATE', response.json['zone']['action'])
self.assertEqual('PENDING', response.json['zone']['status'])
self.assertEqual('UPDATE', response.json['action'])
self.assertEqual('PENDING', response.json['status'])
@patch.object(central_service.Service, 'delete_recordset',
side_effect=exceptions.RecordSetNotFound())
@ -618,7 +594,7 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
# Create a recordset
fixture = self.get_recordset_fixture(self.domain['name'], fixture=0)
response = self.client.post_json(
'/zones/%s/recordsets' % self.domain['id'], {'recordset': fixture})
'/zones/%s/recordsets' % self.domain['id'], fixture)
response = self.client.get(url)
@ -629,11 +605,11 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
# Create two recordsets
fixture = self.get_recordset_fixture(self.domain['name'], fixture=0)
response = self.client.post_json(
'/zones/%s/recordsets' % self.domain['id'], {'recordset': fixture})
'/zones/%s/recordsets' % self.domain['id'], fixture)
fixture = self.get_recordset_fixture(self.domain['name'], fixture=1)
response = self.client.post_json(
'/zones/%s/recordsets' % self.domain['id'], {'recordset': fixture})
'/zones/%s/recordsets' % self.domain['id'], fixture)
# Paginate the recordsets to two, there should be four now
url = '/zones/%s/recordsets?limit=2' % self.domain['id']
@ -662,17 +638,15 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
self.assertEqual(200, response.status_int)
self.assertEqual('application/json', response.content_type)
# Check the body structure is what we expect
self.assertIn('recordset', response.json)
self.assertIn('links', response.json['recordset'])
self.assertIn('self', response.json['recordset']['links'])
self.assertIn('links', response.json)
self.assertIn('self', response.json['links'])
# Check the values returned are what we expect
self.assertIn('id', response.json['recordset'])
self.assertIn('created_at', response.json['recordset'])
self.assertIsNone(response.json['recordset']['updated_at'])
self.assertEqual(recordset['name'], response.json['recordset']['name'])
self.assertEqual(recordset['type'], response.json['recordset']['type'])
self.assertIn('id', response.json)
self.assertIn('created_at', response.json)
self.assertIsNone(response.json['updated_at'])
self.assertEqual(recordset['name'], response.json['name'])
self.assertEqual(recordset['type'], response.json['type'])
def test_get_secondary_zone_recordsets(self):
fixture = self.get_domain_fixture('SECONDARY', 1)
@ -716,7 +690,7 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
url = '/zones/%s/recordsets' % secondary['id']
self._assert_exception('forbidden', 403, self.client.post_json, url,
{'recordset': fixture})
fixture)
def test_update_secondary_zone_recordset(self):
fixture = self.get_domain_fixture('SECONDARY', 1)
@ -730,7 +704,7 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
recordset['id'])
self._assert_exception('forbidden', 403, self.client.put_json, url,
{'recordset': {'ttl': 100}})
{'ttl': 100})
def test_delete_secondary_zone_recordset(self):
fixture = self.get_domain_fixture('SECONDARY', 1)
@ -748,7 +722,7 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
def test_no_create_rs_deleting_zone(self):
# Prepare a create
fixture = self.get_recordset_fixture(self.domain['name'], fixture=0)
body = {'recordset': fixture}
body = fixture
self.client.delete('/zones/%s' % self.domain['id'], status=202)
self._assert_exception('bad_request', 400, self.client.post_json,
@ -760,7 +734,7 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
recordset = self.create_recordset(self.domain)
# Prepare an update body
body = {'recordset': {'description': 'Tester'}}
body = {'description': 'Tester'}
url = '/zones/%s/recordsets/%s' % (recordset['domain_id'],
recordset['id'])
self.client.delete('/zones/%s' % self.domain['id'], status=202)

View File

@ -22,22 +22,21 @@ class ApiV2TldsTest(ApiV2TestCase):
def test_create_tld(self):
self.policy({'create_tld': '@'})
fixture = self.get_tld_fixture(0)
response = self.client.post_json('/tlds/', {'tld': fixture})
response = self.client.post_json('/tlds/', fixture)
# Check the headers are what we expect
self.assertEqual(201, response.status_int)
self.assertEqual('application/json', response.content_type)
# Check the body structure is what we expect
self.assertIn('tld', response.json)
self.assertIn('links', response.json['tld'])
self.assertIn('self', response.json['tld']['links'])
self.assertIn('links', response.json)
self.assertIn('self', response.json['links'])
# Check the values returned are what we expect
self.assertIn('id', response.json['tld'])
self.assertIn('created_at', response.json['tld'])
self.assertIsNone(response.json['tld']['updated_at'])
self.assertEqual(fixture['name'], response.json['tld']['name'])
self.assertIn('id', response.json)
self.assertIn('created_at', response.json)
self.assertIsNone(response.json['updated_at'])
self.assertEqual(fixture['name'], response.json['name'])
def test_create_tld_validation(self):
self.policy({'create_tld': '@'})
@ -45,7 +44,7 @@ class ApiV2TldsTest(ApiV2TestCase):
# Ensure it fails with a 400
self._assert_exception('invalid_object', 400, self.client.post_json,
'/tlds', {'tld': invalid_fixture})
'/tlds', invalid_fixture)
def test_get_tlds(self):
self.policy({'find_tlds': '@'})
@ -78,16 +77,15 @@ class ApiV2TldsTest(ApiV2TestCase):
self.assertEqual('application/json', response.content_type)
# Check the body structure is what we expect
self.assertIn('tld', response.json)
self.assertIn('links', response.json['tld'])
self.assertIn('self', response.json['tld']['links'])
self.assertIn('links', response.json)
self.assertIn('self', response.json['links'])
# Check the values returned are what we expect
self.assertIn('id', response.json['tld'])
self.assertIn('created_at', response.json['tld'])
self.assertIsNone(response.json['tld']['updated_at'])
self.assertIn('id', response.json)
self.assertIn('created_at', response.json)
self.assertIsNone(response.json['updated_at'])
self.assertEqual(self.get_tld_fixture(0)['name'],
response.json['tld']['name'])
response.json['name'])
def test_get_tld_invalid_id(self):
self._assert_invalid_uuid(self.client.get, '/tlds/%s')
@ -106,7 +104,7 @@ class ApiV2TldsTest(ApiV2TestCase):
self.policy({'update_tld': '@'})
# Prepare an update body
body = {'tld': {'description': 'prefix-%s' % tld['description']}}
body = {'description': 'prefix-%s' % tld['description']}
response = self.client.patch_json('/tlds/%s' % tld['id'], body,
status=200)
@ -116,15 +114,14 @@ class ApiV2TldsTest(ApiV2TestCase):
self.assertEqual('application/json', response.content_type)
# Check the body structure is what we expect
self.assertIn('tld', response.json)
self.assertIn('links', response.json['tld'])
self.assertIn('self', response.json['tld']['links'])
self.assertIn('links', response.json)
self.assertIn('self', response.json['links'])
# Check the values returned are what we expect
self.assertIn('id', response.json['tld'])
self.assertIsNotNone(response.json['tld']['updated_at'])
self.assertIn('id', response.json)
self.assertIsNotNone(response.json['updated_at'])
self.assertEqual('prefix-%s' % tld['description'],
response.json['tld']['description'])
response.json['description'])
def test_update_tld_invalid_id(self):
self._assert_invalid_uuid(self.client.patch_json, '/tlds/%s')

View File

@ -36,24 +36,23 @@ class ApiV2TsigKeysTest(ApiV2TestCase):
def test_create_tsigkey(self):
# Create a TSIG Key
fixture = self.get_tsigkey_fixture(0)
response = self.client.post_json('/tsigkeys/', {'tsigkey': fixture})
response = self.client.post_json('/tsigkeys/', fixture)
# Check the headers are what we expect
self.assertEqual(201, response.status_int)
self.assertEqual('application/json', response.content_type)
# Check the body structure is what we expect
self.assertIn('tsigkey', response.json)
self.assertIn('links', response.json['tsigkey'])
self.assertIn('self', response.json['tsigkey']['links'])
self.assertIn('links', response.json)
self.assertIn('self', response.json['links'])
# Check the generated values returned are what we expect
self.assertIn('id', response.json['tsigkey'])
self.assertIn('created_at', response.json['tsigkey'])
self.assertIsNone(response.json['tsigkey']['updated_at'])
self.assertIn('id', response.json)
self.assertIn('created_at', response.json)
self.assertIsNone(response.json['updated_at'])
# Check the supplied values returned are what we expect
self.assertDictContainsSubset(fixture, response.json['tsigkey'])
self.assertDictContainsSubset(fixture, response.json)
def test_create_tsigkey_validation(self):
# NOTE: The schemas should be tested separately to the API. So we
@ -61,18 +60,11 @@ class ApiV2TsigKeysTest(ApiV2TestCase):
# Fetch a fixture
fixture = self.get_tsigkey_fixture(0)
# Add a junk field to the wrapper
body = {'tsigkey': fixture, 'junk': 'Junk Field'}
# Ensure it fails with a 400
self._assert_exception('invalid_object', 400, self.client.post_json,
'/tsigkeys', body)
# Add a junk field to the body
fixture['junk'] = 'Junk Field'
# Ensure it fails with a 400
body = {'tsigkey': fixture}
body = fixture
self._assert_exception('invalid_object', 400, self.client.post_json,
'/tsigkeys', body)
@ -80,7 +72,7 @@ class ApiV2TsigKeysTest(ApiV2TestCase):
def test_create_tsigkey_duplicate(self):
# Prepare a TSIG Key fixture
fixture = self.get_tsigkey_fixture(0)
body = {'tsigkey': fixture}
body = fixture
# Create the first TSIG Key
response = self.client.post_json('/tsigkeys', body)
@ -126,23 +118,22 @@ class ApiV2TsigKeysTest(ApiV2TestCase):
self.assertEqual('application/json', response.content_type)
# Check the body structure is what we expect
self.assertIn('tsigkey', response.json)
self.assertIn('links', response.json['tsigkey'])
self.assertIn('self', response.json['tsigkey']['links'])
self.assertIn('links', response.json)
self.assertIn('self', response.json['links'])
# Check the generated values returned are what we expect
self.assertIn('id', response.json['tsigkey'])
self.assertIn('created_at', response.json['tsigkey'])
self.assertIsNone(response.json['tsigkey']['updated_at'])
self.assertIn('id', response.json)
self.assertIn('created_at', response.json)
self.assertIsNone(response.json['updated_at'])
# Check the supplied values returned are what we expect
self.assertEqual(tsigkey.name, response.json['tsigkey']['name'])
self.assertEqual(tsigkey.name, response.json['name'])
self.assertEqual(
tsigkey.algorithm, response.json['tsigkey']['algorithm'])
self.assertEqual(tsigkey.secret, response.json['tsigkey']['secret'])
self.assertEqual(tsigkey.scope, response.json['tsigkey']['scope'])
tsigkey.algorithm, response.json['algorithm'])
self.assertEqual(tsigkey.secret, response.json['secret'])
self.assertEqual(tsigkey.scope, response.json['scope'])
self.assertEqual(
tsigkey.resource_id, response.json['tsigkey']['resource_id'])
tsigkey.resource_id, response.json['resource_id'])
def test_get_tsigkey_invalid_id(self):
self._assert_invalid_uuid(self.client.get, '/tsigkeys/%s')
@ -166,7 +157,7 @@ class ApiV2TsigKeysTest(ApiV2TestCase):
tsigkey = self.create_tsigkey()
# Prepare an update body
body = {'tsigkey': {'secret': 'prefix-%s' % tsigkey.secret}}
body = {'secret': 'prefix-%s' % tsigkey.secret}
response = self.client.patch_json('/tsigkeys/%s' % tsigkey.id, body)
@ -175,15 +166,14 @@ class ApiV2TsigKeysTest(ApiV2TestCase):
self.assertEqual('application/json', response.content_type)
# Check the body structure is what we expect
self.assertIn('tsigkey', response.json)
self.assertIn('links', response.json['tsigkey'])
self.assertIn('self', response.json['tsigkey']['links'])
self.assertIn('links', response.json)
self.assertIn('self', response.json['links'])
# Check the values returned are what we expect
self.assertIn('id', response.json['tsigkey'])
self.assertIsNotNone(response.json['tsigkey']['updated_at'])
self.assertIn('id', response.json)
self.assertIsNotNone(response.json['updated_at'])
self.assertEqual('prefix-%s' % tsigkey['secret'],
response.json['tsigkey']['secret'])
response.json['secret'])
def test_update_tsigkey_invalid_id(self):
self._assert_invalid_uuid(self.client.patch_json, '/tsigkeys/%s')
@ -192,7 +182,7 @@ class ApiV2TsigKeysTest(ApiV2TestCase):
side_effect=exceptions.DuplicateTsigKey())
def test_update_tsigkey_duplicate(self, _):
# Prepare an update body
body = {'tsigkey': {'name': 'AnyOldName'}}
body = {'name': 'AnyOldName'}
url = '/tsigkeys/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980'
@ -204,7 +194,7 @@ class ApiV2TsigKeysTest(ApiV2TestCase):
side_effect=messaging.MessagingTimeout())
def test_update_tsigkey_timeout(self, _):
# Prepare an update body
body = {'tsigkey': {'name': 'AnyOldName'}}
body = {'name': 'AnyOldName'}
url = '/tsigkeys/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980'
@ -216,7 +206,7 @@ class ApiV2TsigKeysTest(ApiV2TestCase):
side_effect=exceptions.TsigKeyNotFound())
def test_update_tsigkey_missing(self, _):
# Prepare an update body
body = {'tsigkey': {'name': 'AnyOldName'}}
body = {'name': 'AnyOldName'}
url = '/tsigkeys/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980'

View File

@ -27,118 +27,113 @@ class ApiV2ZoneTransfersTest(ApiV2TestCase):
def test_create_zone_transfer_request(self):
response = self.client.post_json(
'/zones/%s/tasks/transfer_requests' % (self.domain.id),
{'transfer_request': {}})
{})
# Check the headers are what we expect
self.assertEqual(201, response.status_int)
self.assertEqual('application/json', response.content_type)
# Check the body structure is what we expect
self.assertIn('transfer_request', response.json)
self.assertIn('links', response.json['transfer_request'])
self.assertIn('self', response.json['transfer_request']['links'])
self.assertIn('links', response.json)
self.assertIn('self', response.json['links'])
# Check the values returned are what we expect
self.assertIn('id', response.json['transfer_request'])
self.assertIn('created_at', response.json['transfer_request'])
self.assertEqual('ACTIVE', response.json['transfer_request']['status'])
self.assertIn('id', response.json)
self.assertIn('created_at', response.json)
self.assertEqual('ACTIVE', response.json['status'])
self.assertEqual(
self.domain.id,
response.json['transfer_request']['zone_id'])
self.assertIsNone(response.json['transfer_request']['updated_at'])
response.json['zone_id'])
self.assertIsNone(response.json['updated_at'])
def test_create_zone_transfer_request_scoped(self):
response = self.client.post_json(
'/zones/%s/tasks/transfer_requests' % (self.domain.id),
{'transfer_request':
{'target_project_id': str(self.tenant_1_context.tenant)}})
{'target_project_id': str(self.tenant_1_context.tenant)})
# Check the headers are what we expect
self.assertEqual(201, response.status_int)
self.assertEqual('application/json', response.content_type)
# Check the body structure is what we expect
self.assertIn('transfer_request', response.json)
self.assertIn('links', response.json['transfer_request'])
self.assertIn('self', response.json['transfer_request']['links'])
self.assertIn('links', response.json)
self.assertIn('self', response.json['links'])
# Check the values returned are what we expect
self.assertIn('id', response.json['transfer_request'])
self.assertIn('created_at', response.json['transfer_request'])
self.assertEqual('ACTIVE', response.json['transfer_request']['status'])
self.assertIn('id', response.json)
self.assertIn('created_at', response.json)
self.assertEqual('ACTIVE', response.json['status'])
self.assertEqual(
str(self.tenant_1_context.tenant),
response.json['transfer_request']['target_project_id'])
response.json['target_project_id'])
self.assertEqual(
self.domain.id,
response.json['transfer_request']['zone_id'])
self.assertIsNone(response.json['transfer_request']['updated_at'])
response.json['zone_id'])
self.assertIsNone(response.json['updated_at'])
def test_get_zone_transfer_request(self):
initial = self.client.post_json(
'/zones/%s/tasks/transfer_requests' % (self.domain.id),
{'transfer_request': {}})
{})
response = self.client.get(
'/zones/tasks/transfer_requests/%s' %
(initial.json['transfer_request']['id']))
(initial.json['id']))
# Check the headers are what we expect
self.assertEqual(200, response.status_int)
self.assertEqual('application/json', response.content_type)
# Check the body structure is what we expect
self.assertIn('transfer_request', response.json)
self.assertIn('links', response.json['transfer_request'])
self.assertIn('self', response.json['transfer_request']['links'])
self.assertIn('links', response.json)
self.assertIn('self', response.json['links'])
# Check the values returned are what we expect
self.assertIn('id', response.json['transfer_request'])
self.assertIn('created_at', response.json['transfer_request'])
self.assertEqual('ACTIVE', response.json['transfer_request']['status'])
self.assertIn('id', response.json)
self.assertIn('created_at', response.json)
self.assertEqual('ACTIVE', response.json['status'])
self.assertEqual(
self.domain.id,
response.json['transfer_request']['zone_id'])
self.assertIn('updated_at', response.json['transfer_request'])
response.json['zone_id'])
self.assertIn('updated_at', response.json)
def test_update_zone_transfer_request(self):
initial = self.client.post_json(
'/zones/%s/tasks/transfer_requests' % (self.domain.id),
{'transfer_request': {}})
{})
response = self.client.patch_json(
'/zones/tasks/transfer_requests/%s' %
(initial.json['transfer_request']['id']),
{'transfer_request': {"description": "TEST"}})
(initial.json['id']),
{"description": "TEST"})
# Check the headers are what we expect
self.assertEqual(200, response.status_int)
self.assertEqual('application/json', response.content_type)
# Check the body structure is what we expect
self.assertIn('transfer_request', response.json)
self.assertIn('links', response.json['transfer_request'])
self.assertIn('self', response.json['transfer_request']['links'])
self.assertIn('links', response.json)
self.assertIn('self', response.json['links'])
# Check the values returned are what we expect
self.assertIn('id', response.json['transfer_request'])
self.assertIn('created_at', response.json['transfer_request'])
self.assertEqual('ACTIVE', response.json['transfer_request']['status'])
self.assertIn('id', response.json)
self.assertIn('created_at', response.json)
self.assertEqual('ACTIVE', response.json['status'])
self.assertEqual(
self.domain.id,
response.json['transfer_request']['zone_id'])
response.json['zone_id'])
self.assertEqual(
'TEST', response.json['transfer_request']['description'])
self.assertIn('updated_at', response.json['transfer_request'])
'TEST', response.json['description'])
self.assertIn('updated_at', response.json)
def test_delete_zone_transfer_request(self):
initial = self.client.post_json(
'/zones/%s/tasks/transfer_requests' % (self.domain.id),
{'transfer_request': {}})
{})
response = self.client.delete(
'/zones/tasks/transfer_requests/%s' %
(initial.json['transfer_request']['id']))
(initial.json['id']))
# Check the headers are what we expect
self.assertEqual(204, response.status_int)
@ -146,50 +141,49 @@ class ApiV2ZoneTransfersTest(ApiV2TestCase):
def test_create_zone_transfer_accept(self):
initial = self.client.post_json(
'/zones/%s/tasks/transfer_requests' % (self.domain.id),
{'transfer_request': {}})
{})
response = self.client.post_json(
'/zones/tasks/transfer_accepts',
{'transfer_accept': {
{
'zone_transfer_request_id':
initial.json['transfer_request']['id'],
'key': initial.json['transfer_request']['key']
}})
initial.json['id'],
'key': initial.json['key']
})
new_ztr = self.client.get(
'/zones/tasks/transfer_requests/%s' %
(initial.json['transfer_request']['id']))
(initial.json['id']))
# Check the headers are what we expect
self.assertEqual(201, response.status_int)
self.assertEqual('application/json', response.content_type)
# Check the body structure is what we expect
self.assertIn('transfer_accept', response.json)
self.assertIn('links', response.json['transfer_accept'])
self.assertIn('self', response.json['transfer_accept']['links'])
self.assertIn('zone', response.json['transfer_accept']['links'])
self.assertIn('links', response.json)
self.assertIn('self', response.json['links'])
self.assertIn('zone', response.json['links'])
# Check the values returned are what we expect
self.assertIn('id', response.json['transfer_accept'])
self.assertIn('id', response.json)
self.assertEqual(
'COMPLETE',
response.json['transfer_accept']['status'])
response.json['status'])
self.assertEqual(
'COMPLETE',
new_ztr.json['transfer_request']['status'])
new_ztr.json['status'])
def test_create_zone_transfer_request_deleting_zone(self):
url = '/zones/%s/tasks/transfer_requests' % (self.domain.id)
body = {'transfer_request': {}}
body = {}
self.client.delete('/zones/%s' % self.domain['id'], status=202)
self._assert_exception('bad_request', 400, self.client.post_json, url,
body)
def test_create_zone_transfer_accept_deleting_zone(self):
url = '/zones/%s/tasks/transfer_requests' % (self.domain.id)
body = {'transfer_request': {}}
body = {}
self.client.delete('/zones/%s' % self.domain['id'], status=202)
self._assert_exception('bad_request', 400, self.client.post_json, url,
body)

View File

@ -38,54 +38,52 @@ class ApiV2ZonesTest(ApiV2TestCase):
# Create a zone
fixture = self.get_domain_fixture(fixture=0)
response = self.client.post_json('/zones/', {'zone': fixture})
response = self.client.post_json('/zones/', fixture)
# Check the headers are what we expect
self.assertEqual(202, response.status_int)
self.assertEqual('application/json', response.content_type)
# Check the body structure is what we expect
self.assertIn('zone', response.json)
self.assertIn('links', response.json['zone'])
self.assertIn('self', response.json['zone']['links'])
self.assertIn('links', response.json)
self.assertIn('self', response.json['links'])
# Check the values returned are what we expect
self.assertIn('id', response.json['zone'])
self.assertIn('created_at', response.json['zone'])
self.assertEqual('PENDING', response.json['zone']['status'])
self.assertEqual('PRIMARY', response.json['zone']['type'])
self.assertEqual([], response.json['zone']['masters'])
self.assertIsNone(response.json['zone']['updated_at'])
self.assertIn('id', response.json)
self.assertIn('created_at', response.json)
self.assertEqual('PENDING', response.json['status'])
self.assertEqual('PRIMARY', response.json['type'])
self.assertEqual([], response.json['masters'])
self.assertIsNone(response.json['updated_at'])
for k in fixture:
self.assertEqual(fixture[k], response.json['zone'][k])
self.assertEqual(fixture[k], response.json[k])
def test_create_zone_no_type(self):
# Create a zone
fixture = self.get_domain_fixture(fixture=0)
del fixture['type']
response = self.client.post_json('/zones/', {'zone': fixture})
response = self.client.post_json('/zones/', fixture)
# Check the headers are what we expect
self.assertEqual(202, response.status_int)
self.assertEqual('application/json', response.content_type)
# Check the body structure is what we expect
self.assertIn('zone', response.json)
self.assertIn('links', response.json['zone'])
self.assertIn('self', response.json['zone']['links'])
self.assertIn('links', response.json)
self.assertIn('self', response.json['links'])
# Check the values returned are what we expect
self.assertIn('id', response.json['zone'])
self.assertIn('created_at', response.json['zone'])
self.assertEqual('PENDING', response.json['zone']['status'])
self.assertEqual('PRIMARY', response.json['zone']['type'])
self.assertEqual([], response.json['zone']['masters'])
self.assertIsNone(response.json['zone']['updated_at'])
self.assertIn('id', response.json)
self.assertIn('created_at', response.json)
self.assertEqual('PENDING', response.json['status'])
self.assertEqual('PRIMARY', response.json['type'])
self.assertEqual([], response.json['masters'])
self.assertIsNone(response.json['updated_at'])
for k in fixture:
self.assertEqual(fixture[k], response.json['zone'][k])
self.assertEqual(fixture[k], response.json[k])
def test_create_zone_validation(self):
# NOTE: The schemas should be tested separately to the API. So we
@ -93,18 +91,11 @@ class ApiV2ZonesTest(ApiV2TestCase):
# Fetch a fixture
fixture = self.get_domain_fixture(fixture=0)
# Add a junk field to the wrapper
body = {'zone': fixture, 'junk': 'Junk Field'}
# Ensure it fails with a 400
self._assert_exception('invalid_object', 400, self.client.post_json,
'/zones', body)
# Add a junk field to the body
fixture['junk'] = 'Junk Field'
# Ensure it fails with a 400
body = {'zone': fixture}
body = fixture
self._assert_exception('invalid_object', 400, self.client.post_json,
'/zones', body)
@ -114,7 +105,7 @@ class ApiV2ZonesTest(ApiV2TestCase):
# Add id to the body
fixture['id'] = '2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980'
# Ensure it fails with a 400
body = {'zone': fixture}
body = fixture
self._assert_exception('invalid_object', 400, self.client.post_json,
'/zones', body)
@ -122,7 +113,7 @@ class ApiV2ZonesTest(ApiV2TestCase):
# Add created_at to the body
fixture['created_at'] = '2014-03-12T19:07:53.000000'
# Ensure it fails with a 400
body = {'zone': fixture}
body = fixture
self._assert_exception('invalid_object', 400, self.client.post_json,
'/zones', body)
@ -132,14 +123,14 @@ class ApiV2ZonesTest(ApiV2TestCase):
# Ensure it fails with a 400
self._assert_exception('invalid_object', 400, self.client.post_json,
'/zones', {'zone': fixture})
'/zones', fixture)
@patch.object(central_service.Service, 'create_domain',
side_effect=messaging.MessagingTimeout())
def test_create_zone_timeout(self, _):
fixture = self.get_domain_fixture(fixture=0)
body = {'zone': fixture}
body = fixture
self._assert_exception('timeout', 504, self.client.post_json,
'/zones/', body)
@ -149,7 +140,7 @@ class ApiV2ZonesTest(ApiV2TestCase):
def test_create_zone_duplicate(self, _):
fixture = self.get_domain_fixture(fixture=0)
body = {'zone': fixture}
body = fixture
self._assert_exception('duplicate_domain', 409, self.client.post_json,
'/zones/', body)
@ -215,17 +206,16 @@ class ApiV2ZonesTest(ApiV2TestCase):
self.assertEqual('application/json', response.content_type)
# Check the body structure is what we expect
self.assertIn('zone', response.json)
self.assertIn('links', response.json['zone'])
self.assertIn('self', response.json['zone']['links'])
self.assertIn('links', response.json)
self.assertIn('self', response.json['links'])
# Check the values returned are what we expect
self.assertIn('id', response.json['zone'])
self.assertIn('created_at', response.json['zone'])
self.assertEqual('PENDING', response.json['zone']['status'])
self.assertIsNone(response.json['zone']['updated_at'])
self.assertEqual(zone['name'], response.json['zone']['name'])
self.assertEqual(zone['email'], response.json['zone']['email'])
self.assertIn('id', response.json)
self.assertIn('created_at', response.json)
self.assertEqual('PENDING', response.json['status'])
self.assertIsNone(response.json['updated_at'])
self.assertEqual(zone['name'], response.json['name'])
self.assertEqual(zone['email'], response.json['email'])
def test_get_zone_invalid_id(self):
self._assert_invalid_uuid(self.client.get, '/zones/%s')
@ -259,7 +249,7 @@ class ApiV2ZonesTest(ApiV2TestCase):
zone = self.create_domain()
# Prepare an update body
body = {'zone': {'email': 'prefix-%s' % zone['email']}}
body = {'email': 'prefix-%s' % zone['email']}
response = self.client.patch_json('/zones/%s' % zone['id'], body,
status=202)
@ -269,16 +259,15 @@ class ApiV2ZonesTest(ApiV2TestCase):
self.assertEqual('application/json', response.content_type)
# Check the body structure is what we expect
self.assertIn('zone', response.json)
self.assertIn('links', response.json['zone'])
self.assertIn('self', response.json['zone']['links'])
self.assertIn('status', response.json['zone'])
self.assertIn('links', response.json)
self.assertIn('self', response.json['links'])
self.assertIn('status', response.json)
# Check the values returned are what we expect
self.assertIn('id', response.json['zone'])
self.assertIsNotNone(response.json['zone']['updated_at'])
self.assertIn('id', response.json)
self.assertIsNotNone(response.json['updated_at'])
self.assertEqual('prefix-%s' % zone['email'],
response.json['zone']['email'])
response.json['email'])
def test_update_zone_invalid_id(self):
self._assert_invalid_uuid(self.client.patch_json, '/zones/%s')
@ -289,35 +278,27 @@ class ApiV2ZonesTest(ApiV2TestCase):
# Create a zone
zone = self.create_domain()
# Prepare an update body with junk in the wrapper
body = {'zone': {'email': 'prefix-%s' % zone['email']},
# Prepare an update body with junk in the body
body = {'email': 'prefix-%s' % zone['email'],
'junk': 'Junk Field'}
url = '/zones/%s' % zone['id']
# Ensure it fails with a 400
self._assert_exception('invalid_object', 400, self.client.patch_json,
url, body)
# Prepare an update body with junk in the body
body = {'zone': {'email': 'prefix-%s' % zone['email'],
'junk': 'Junk Field'}}
# Ensure it fails with a 400
self._assert_exception('invalid_object', 400, self.client.patch_json,
url, body)
# Prepare an update body with negative ttl in the body
body = {'zone': {'email': 'prefix-%s' % zone['email'],
'ttl': -20}}
body = {'email': 'prefix-%s' % zone['email'],
'ttl': -20}
# Ensure it fails with a 400
self._assert_exception('invalid_object', 400, self.client.patch_json,
url, body)
# Prepare an update body with ttl > maximum (2147483647) in the body
body = {'zone': {'email': 'prefix-%s' % zone['email'],
'ttl': 2147483648}}
body = {'email': 'prefix-%s' % zone['email'],
'ttl': 2147483648}
# Ensure it fails with a 400
self._assert_exception('invalid_object', 400, self.client.patch_json,
@ -327,7 +308,7 @@ class ApiV2ZonesTest(ApiV2TestCase):
side_effect=exceptions.DuplicateDomain())
def test_update_zone_duplicate(self, _):
# Prepare an update body
body = {'zone': {'email': 'example@example.org'}}
body = {'email': 'example@example.org'}
url = '/zones/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980'
@ -339,7 +320,7 @@ class ApiV2ZonesTest(ApiV2TestCase):
side_effect=messaging.MessagingTimeout())
def test_update_zone_timeout(self, _):
# Prepare an update body
body = {'zone': {'email': 'example@example.org'}}
body = {'email': 'example@example.org'}
url = '/zones/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980'
@ -351,7 +332,7 @@ class ApiV2ZonesTest(ApiV2TestCase):
side_effect=exceptions.DomainNotFound())
def test_update_zone_missing(self, _):
# Prepare an update body
body = {'zone': {'email': 'example@example.org'}}
body = {'email': 'example@example.org'}
url = '/zones/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980'
@ -367,9 +348,8 @@ class ApiV2ZonesTest(ApiV2TestCase):
# Check the headers are what we expect
self.assertEqual(202, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertIn('zone', response.json)
self.assertEqual('DELETE', response.json['zone']['action'])
self.assertEqual('PENDING', response.json['zone']['status'])
self.assertEqual('DELETE', response.json['action'])
self.assertEqual('PENDING', response.json['status'])
def test_delete_zone_invalid_id(self):
self._assert_invalid_uuid(self.client.delete, '/zones/%s')
@ -422,39 +402,38 @@ class ApiV2ZonesTest(ApiV2TestCase):
fixture = self.get_domain_fixture('SECONDARY', 0)
fixture['masters'] = ["10.0.0.1"]
response = self.client.post_json('/zones/', {'zone': fixture})
response = self.client.post_json('/zones/', fixture)
# Check the headers are what we expect
self.assertEqual(202, response.status_int)
self.assertEqual('application/json', response.content_type)
# Check the body structure is what we expect
self.assertIn('zone', response.json)
self.assertIn('links', response.json['zone'])
self.assertIn('self', response.json['zone']['links'])
self.assertIn('links', response.json)
self.assertIn('self', response.json['links'])
# Check the values returned are what we expect
self.assertIn('id', response.json['zone'])
self.assertIn('created_at', response.json['zone'])
self.assertEqual('PENDING', response.json['zone']['status'])
self.assertIn('id', response.json)
self.assertIn('created_at', response.json)
self.assertEqual('PENDING', response.json['status'])
self.assertEqual(cfg.CONF['service:central'].managed_resource_email,
response.json['zone']['email'])
response.json['email'])
self.assertIsNone(response.json['zone']['updated_at'])
self.assertIsNone(response.json['updated_at'])
# Zone is not transferred yet
self.assertIsNone(response.json['zone']['transferred_at'])
self.assertIsNone(response.json['transferred_at'])
# Serial defaults to 1
self.assertEqual(response.json['zone']['serial'], 1)
self.assertEqual(response.json['serial'], 1)
for k in fixture:
self.assertEqual(fixture[k], response.json['zone'][k])
self.assertEqual(fixture[k], response.json[k])
def test_create_secondary_no_masters(self):
# Create a zone
fixture = self.get_domain_fixture('SECONDARY', 0)
self._assert_exception('invalid_object', 400, self.client.post_json,
'/zones/', {'zone': fixture})
'/zones/', fixture)
def test_update_secondary(self):
# Create a zone
@ -468,7 +447,7 @@ class ApiV2ZonesTest(ApiV2TestCase):
masters = ['10.0.0.1', '10.0.0.2']
# Prepare an update body
body = {'zone': {'masters': masters}}
body = {'masters': masters}
response = self.client.patch_json('/zones/%s' % zone['id'], body,
status=202)
@ -478,16 +457,15 @@ class ApiV2ZonesTest(ApiV2TestCase):
self.assertEqual('application/json', response.content_type)
# Check the body structure is what we expect
self.assertIn('zone', response.json)
self.assertIn('links', response.json['zone'])
self.assertIn('self', response.json['zone']['links'])
self.assertIn('status', response.json['zone'])
self.assertIn('links', response.json)
self.assertIn('self', response.json['links'])
self.assertIn('status', response.json)
# Check the values returned are what we expect
self.assertIn('id', response.json['zone'])
self.assertIsNotNone(response.json['zone']['updated_at'])
self.assertEqual(masters, response.json['zone']['masters'])
self.assertEqual(1, response.json['zone']['serial'])
self.assertIn('id', response.json)
self.assertIsNotNone(response.json['updated_at'])
self.assertEqual(masters, response.json['masters'])
self.assertEqual(1, response.json['serial'])
def test_update_secondary_email_invalid_object(self):
# Create a zone
@ -497,7 +475,7 @@ class ApiV2ZonesTest(ApiV2TestCase):
# Create a zone
zone = self.create_domain(**fixture)
body = {'zone': {'email': 'foo@bar.io'}}
body = {'email': 'foo@bar.io'}
self._assert_exception('invalid_object', 400, self.client.patch_json,
'/zones/%s' % zone['id'], body)
@ -528,7 +506,7 @@ class ApiV2ZonesTest(ApiV2TestCase):
self.get_zonefile_fixture(),
headers={'Content-type': 'text/dns'})
get_response = self.client.get('/zones/%s' %
post_response.json['zone']['id'],
post_response.json['id'],
headers={'Accept': 'text/dns'})
exported_zonefile = get_response.body
@ -566,7 +544,7 @@ class ApiV2ZonesTest(ApiV2TestCase):
# Create a zone
fixture = self.get_domain_fixture(fixture=0)
response = self.client.post_json('/zones/', {'zone': fixture})
response = self.client.post_json('/zones/', fixture)
response = self.client.get('/zones/')
@ -576,10 +554,10 @@ class ApiV2ZonesTest(ApiV2TestCase):
def test_total_count_pagination(self):
# Create two zones
fixture = self.get_domain_fixture(fixture=0)
response = self.client.post_json('/zones/', {'zone': fixture})
response = self.client.post_json('/zones/', fixture)
fixture = self.get_domain_fixture(fixture=1)
response = self.client.post_json('/zones/', {'zone': fixture})
response = self.client.post_json('/zones/', fixture)
# Paginate so that there is only one zone returned
response = self.client.get('/zones?limit=1')

View File

@ -17,12 +17,13 @@ example:
Content-Type: application/json
{ # The rest is the body of request
"pool": {
"name": "Example Pool",
"nameservers": [
"ns1.example.org."
]
}
"name": "Example Pool",
"ns_records": [
{
"hostname": "ns1.example.org.",
"priority": 1
}
]
}
With this info we can make this request using the cURL_ tool. We'll
@ -77,3 +78,12 @@ V2 API
rest/v2/blacklists
rest/v2/quotas
rest/v2/pools
Admin API
---------
.. toctree::
:maxdepth: 2
:glob:
rest/admin/quotas

View File

@ -27,7 +27,7 @@ The quotas extension can be used to retrieve a tenant's absolute limits.
If Designate returns a 404 error, ensure that the following line has been
added to the designate.conf file::
enabled_extensions_v2 = quotas
enabled_extensions_admin = quotas
Once this line has been added, restart the designate-central and designate-api
services.
@ -44,7 +44,7 @@ Get Quotas
.. sourcecode:: http
GET /v2/quotas/12345 HTTP/1.1
GET /admin/quotas/12345 HTTP/1.1
Host: 127.0.0.1:9001
Accept: application/json
Content-Type: application/json
@ -85,7 +85,7 @@ Update Quotas
.. sourcecode:: http
PATCH /v2/quotas/12345 HTTP/1.1
PATCH /admin/quotas/12345 HTTP/1.1
Host: 127.0.0.1:9001
Accept: application/json
Content-Type: application/json
@ -128,7 +128,7 @@ Reset Quotas to Default
.. sourcecode:: http
DELETE /v2/quotas/12345 HTTP/1.1
DELETE /admin/quotas/12345 HTTP/1.1
Host: 127.0.0.1:9001
Accept: application/json
Content-Type: application/json

View File

@ -65,10 +65,8 @@ Create a Blacklist
Content-Type: application/json
{
"blacklist" : {
"pattern" : "^([A-Za-z0-9_\\-]+\\.)*example\\.com\\.$",
"description" : "This is a blacklisted domain."
}
"pattern" : "^([A-Za-z0-9_\\-]+\\.)*example\\.com\\.$",
"description" : "This is a blacklisted domain."
}
**Example response**:
@ -79,18 +77,16 @@ Create a Blacklist
Content-Type: application/json; charset=UTF-8
Location: 127.0.0.1:9001/v2/blacklists/c47229fb-0831-4b55-a5b5-380d361be4bd
{
"blacklist":{
"description":"This is a blacklisted domain.",
"links":{
"self":"http://127.0.0.1:9001/v2/blacklists/c47229fb-0831-4b55-a5b5-380d361be4bd"
},
"pattern":"^([A-Za-z0-9_\\-]+\\.)*example\\.com\\.$",
"created_at":"2014-03-11T21:54:57.000000",
"updated_at":null,
"id":"c47229fb-0831-4b55-a5b5-380d361be4bd"
{
"description":"This is a blacklisted domain.",
"links":{
"self":"http://127.0.0.1:9001/v2/blacklists/c47229fb-0831-4b55-a5b5-380d361be4bd"
},
"pattern":"^([A-Za-z0-9_\\-]+\\.)*example\\.com\\.$",
"created_at":"2014-03-11T21:54:57.000000",
"updated_at":null,
"id":"c47229fb-0831-4b55-a5b5-380d361be4bd"
}
}
:form created_at: timestamp
:form updated_at: timestamp
@ -126,16 +122,14 @@ Get a Blacklist
Content-Type: application/json; charset=UTF-8
{
"blacklist":{
"description":"This is a blacklisted domain.",
"links":{
"self":"http://127.0.0.1:9001/v2/blacklists/c47229fb-0831-4b55-a5b5-380d361be4bd"
},
"pattern":"^([A-Za-z0-9_\\-]+\\.)*example\\.com\\.$",
"created_at":"2014-03-11T21:54:57.000000",
"updated_at":null,
"id":"c47229fb-0831-4b55-a5b5-380d361be4bd"
}
"description":"This is a blacklisted domain.",
"links":{
"self":"http://127.0.0.1:9001/v2/blacklists/c47229fb-0831-4b55-a5b5-380d361be4bd"
},
"pattern":"^([A-Za-z0-9_\\-]+\\.)*example\\.com\\.$",
"created_at":"2014-03-11T21:54:57.000000",
"updated_at":null,
"id":"c47229fb-0831-4b55-a5b5-380d361be4bd"
}
:form created_at: timestamp
@ -224,10 +218,8 @@ Update a Blacklist
Content-Type: application/json
{
"blacklist" : {
"pattern" : "^([A-Za-z0-9_\\-]+\\.)*example\\.com\\.$",
"description" : "Updated the description"
}
"pattern" : "^([A-Za-z0-9_\\-]+\\.)*example\\.com\\.$",
"description" : "Updated the description"
}
**Example response**:
@ -238,16 +230,14 @@ Update a Blacklist
Content-Type: application/json; charset=UTF-8
{
"blacklist":{
"description":"Updated the pattern to catch subdomains",
"links":{
"self":"http://127.0.0.1:9001/v2/blacklists/c47229fb-0831-4b55-a5b5-380d361be4bd"
},
"created_at":"2014-03-11T21:54:57.000000",
"updated_at":"2014-03-13T16:49:32.117187",
"id":"c47229fb-0831-4b55-a5b5-380d361be4bd",
"pattern":"^([A-Za-z0-9_\\-]+\\.)*example\\.com\\.$"
}
"description":"Updated the pattern to catch subdomains",
"links":{
"self":"http://127.0.0.1:9001/v2/blacklists/c47229fb-0831-4b55-a5b5-380d361be4bd"
},
"created_at":"2014-03-11T21:54:57.000000",
"updated_at":"2014-03-13T16:49:32.117187",
"id":"c47229fb-0831-4b55-a5b5-380d361be4bd",
"pattern":"^([A-Za-z0-9_\\-]+\\.)*example\\.com\\.$"
}
:form created_at: timestamp

View File

@ -46,13 +46,13 @@ Create Pool
Content-Type: application/json
{
"pool": {
"attributes": {},
"nameservers": [
"ns1.example.org."
],
"name": "example_pool"
}
"name": "Example Pool",
"ns_records": [
{
"hostname": "ns1.example.org.",
"priority": 1
}
]
}
@ -65,21 +65,22 @@ Create Pool
Content-Type: application/json; charset=UTF-8
{
"pool": {
"description": null,
"id": "d1716333-8c16-490f-85ee-29af36907605",
"project_id": "noauth-project",
"created_at": "2015-02-23T21:56:33.000000",
"attributes": null,
"nameservers": [
"ns1.example.org."
],
"links": {
"self": "http://127.0.0.1:9001/v2/pools/d1716333-8c16-490f-85ee-29af36907605"
},
"name": "example_pool",
"updated_at": null
}
"description": null,
"id": "d1716333-8c16-490f-85ee-29af36907605",
"project_id": "noauth-project",
"created_at": "2015-02-23T21:56:33.000000",
"attributes": null,
"ns_records": [
{
"hostname": "ns1.example.org.",
"priority": 1
}
],
"links": {
"self": "http://127.0.0.1:9001/v2/pools/d1716333-8c16-490f-85ee-29af36907605"
},
"name": "example_pool",
"updated_at": null
}
:form name: UTF-8 text field
@ -87,7 +88,7 @@ Create Pool
:form tenant_id: the UUID of the tenant
:form provisioner: the type backend that should be used
:form attributes: meta data for the pool
:form nameservers: a list of nameservers as fully qualified domains
:form ns_records: a list of ns_records as fully qualified domains
:statuscode 201: Created
:statuscode 400: Bad Request
@ -131,8 +132,11 @@ Get Pools
"project_id": null,
"created_at": "2015-02-18T22:18:58.000000",
"attributes": null,
"nameservers": [
"ns1.example.org."
"ns_records": [
{
"hostname": "ns1.example.org.",
"priority": 1
}
],
"links": {
"self": "http://127.0.0.1:9001/v2/pools/794ccc2c-d751-44fe-b57f-8894c9f5c842"
@ -146,8 +150,11 @@ Get Pools
"project_id": "noauth-project",
"created_at": "2015-02-23T21:56:33.000000",
"attributes": null,
"nameservers": [
"ns1.example.org."
"ns_records": [
{
"hostname": "ns2.example.org.",
"priority": 1
}
],
"links": {
"self": "http://127.0.0.1:9001/v2/pools/d1716333-8c16-490f-85ee-29af36907605"
@ -186,21 +193,22 @@ Get Pool
Content-Type: application/json; charset=UTF-8
{
"pool": {
"description": null,
"id": "794ccc2c-d751-44fe-b57f-8894c9f5c842",
"project_id": null,
"created_at": "2015-02-18T22:18:58.000000",
"attributes": null,
"nameservers": [
"ns1.example.org."
],
"links": {
"self": "http://127.0.0.1:9001/v2/pools/794ccc2c-d751-44fe-b57f-8894c9f5c842"
},
"name": "default",
"updated_at": "2015-02-19T15:59:44.000000"
}
"description": null,
"id": "794ccc2c-d751-44fe-b57f-8894c9f5c842",
"project_id": null,
"created_at": "2015-02-18T22:18:58.000000",
"attributes": null,
"ns_records": [
{
"hostname": "ns1.example.org.",
"priority": 1
}
],
"links": {
"self": "http://127.0.0.1:9001/v2/pools/794ccc2c-d751-44fe-b57f-8894c9f5c842"
},
"name": "default",
"updated_at": "2015-02-19T15:59:44.000000"
}
:statuscode 200: OK
@ -225,11 +233,16 @@ Update Pool
Content-Type: application/json
{
"pool": {
"nameservers": [
"ns3.example.org."
]
}
"ns_records": [
{
"hostname": "ns1.example.org.",
"priority": 1
},
{
"hostname": "ns3.example.org.",
"priority": 2
}
],
}
**Example response**:
@ -241,22 +254,26 @@ Update Pool
Content-Type: application/json; charset=UTF-8
{
"pool": {
"description": null,
"id": "794ccc2c-d751-44fe-b57f-8894c9f5c842",
"project_id": null,
"created_at": "2015-02-18T22:18:58.000000",
"attributes": null,
"nameservers": [
"ns1.example.org.",
"ns3.example.org."
],
"links": {
"self": "http://127.0.0.1:9001/v2/pools/794ccc2c-d751-44fe-b57f-8894c9f5c842"
},
"name": "default",
"updated_at": "2015-02-24T17:39:07.000000"
}
"description": null,
"id": "794ccc2c-d751-44fe-b57f-8894c9f5c842",
"project_id": null,
"created_at": "2015-02-18T22:18:58.000000",
"attributes": null,
"ns_records": [
{
"hostname": "ns1.example.org.",
"priority": 1
}
{
"hostname": "ns3.example.org.",
"priority": 2
}
],
"links": {
"self": "http://127.0.0.1:9001/v2/pools/794ccc2c-d751-44fe-b57f-8894c9f5c842"
},
"name": "default",
"updated_at": "2015-02-24T17:39:07.000000"
}
.. note::
@ -268,12 +285,15 @@ Update Pool
.. code-block:: json
{
"pool": {
"nameservers": ["ns3.example.org"]
}
"ns_records": [
{
"hostname": "ns3.example.org.",
"priority": 2
}
]
}
This would **replace** the value of the `nameservers` key.
This would **replace** the value of the `ns_records` key.
It is a good practice to peform a GET and mutate the result
accordingly.

View File

@ -46,15 +46,13 @@ The following format can be used for common record set types including A, AAAA,
Content-Type: application/json
{
"recordset" : {
"name" : "example.org.",
"description" : "This is an example record set.",
"type" : "A",
"ttl" : 3600,
"records" : [
"10.1.0.2"
]
}
"name" : "example.org.",
"description" : "This is an example record set.",
"type" : "A",
"ttl" : 3600,
"records" : [
"10.1.0.2"
]
}
**Example response:**
@ -65,23 +63,21 @@ The following format can be used for common record set types including A, AAAA,
Content-Type: application/json
{
"recordset": {
"description": "This is an example record set.",
"links": {
"self": "https://127.0.0.1:9001/v2/zones/2150b1bf-dee2-4221-9d85-11f7886fb15f/recordsets/f7b10e9b-0cae-4a91-b162-562bc6096648"
},
"updated_at": null,
"records": [
"10.1.0.2"
],
"ttl": 3600,
"id": "f7b10e9b-0cae-4a91-b162-562bc6096648",
"name": "example.org.",
"zone_id": "2150b1bf-dee2-4221-9d85-11f7886fb15f",
"created_at": "2014-10-24T19:59:44.000000",
"version": 1,
"type": "A"
}
"description": "This is an example record set.",
"links": {
"self": "https://127.0.0.1:9001/v2/zones/2150b1bf-dee2-4221-9d85-11f7886fb15f/recordsets/f7b10e9b-0cae-4a91-b162-562bc6096648"
},
"updated_at": null,
"records": [
"10.1.0.2"
],
"ttl": 3600,
"id": "f7b10e9b-0cae-4a91-b162-562bc6096648",
"name": "example.org.",
"zone_id": "2150b1bf-dee2-4221-9d85-11f7886fb15f",
"created_at": "2014-10-24T19:59:44.000000",
"version": 1,
"type": "A"
}
@ -120,23 +116,21 @@ Get Record Set
Content-Type: application/json
{
"recordset": {
"description": "This is an example recordset.",
"links": {
"self": "https://127.0.0.1:9001/v2/zones/2150b1bf-dee2-4221-9d85-11f7886fb15f/recordsets/f7b10e9b-0cae-4a91-b162-562bc6096648"
},
"updated_at": null,
"records": [
"10.1.0.2"
],
"ttl": 3600,
"id": "f7b10e9b-0cae-4a91-b162-562bc6096648",
"name": "example.org.",
"zone_id": "2150b1bf-dee2-4221-9d85-11f7886fb15f",
"created_at": "2014-10-24T19:59:44.000000",
"version": 1,
"type": "A"
}
"description": "This is an example recordset.",
"links": {
"self": "https://127.0.0.1:9001/v2/zones/2150b1bf-dee2-4221-9d85-11f7886fb15f/recordsets/f7b10e9b-0cae-4a91-b162-562bc6096648"
},
"updated_at": null,
"records": [
"10.1.0.2"
],
"ttl": 3600,
"id": "f7b10e9b-0cae-4a91-b162-562bc6096648",
"name": "example.org.",
"zone_id": "2150b1bf-dee2-4221-9d85-11f7886fb15f",
"created_at": "2014-10-24T19:59:44.000000",
"version": 1,
"type": "A"
}
:statuscode 200: Success
@ -248,13 +242,11 @@ Update Record Set
Content-Type: application/json
{
"recordset" : {
"description" : "I updated this example.",
"ttl" : 60,
"records" : [
"10.1.0.2"
]
}
"description" : "I updated this example.",
"ttl" : 60,
"records" : [
"10.1.0.2"
]
}
**Response:**
@ -265,23 +257,21 @@ Update Record Set
Content-Type: application/json
{
"recordset": {
"description": "I updated this example.",
"ttl": 60,
"records": [
"10.1.0.2"
],
"links": {
"self": "https://127.0.0.1:9001/v2/zones/2150b1bf-dee2-4221-9d85-11f7886fb15f/recordsets/f7b10e9b-0cae-4a91-b162-562bc6096648"
},
"updated_at": "2014-10-24T20:15:27.000000",
"id": "f7b10e9b-0cae-4a91-b162-562bc6096648",
"name": "example.org.",
"zone_id": "2150b1bf-dee2-4221-9d85-11f7886fb15f",
"created_at": "2014-10-24T19:59:44.000000",
"version": 1,
"type": "A"
}
"description": "I updated this example.",
"ttl": 60,
"records": [
"10.1.0.2"
],
"links": {
"self": "https://127.0.0.1:9001/v2/zones/2150b1bf-dee2-4221-9d85-11f7886fb15f/recordsets/f7b10e9b-0cae-4a91-b162-562bc6096648"
},
"updated_at": "2014-10-24T20:15:27.000000",
"id": "f7b10e9b-0cae-4a91-b162-562bc6096648",
"name": "example.org.",
"zone_id": "2150b1bf-dee2-4221-9d85-11f7886fb15f",
"created_at": "2014-10-24T19:59:44.000000",
"version": 2,
"type": "A"
}
:form description: UTF-8 text field
@ -334,18 +324,16 @@ Create MX Record Set
Content-Type: application/json
{
"recordset" : {
"name" : "mail.example.org.",
"description" : "An MX recordset.",
"type" : "MX",
"ttl" : 3600,
"records" : [
"name" : "mail.example.org.",
"description" : "An MX recordset.",
"type" : "MX",
"ttl" : 3600,
"records" : [
"10 mail1.example.org.",
"20 mail2.example.org.",
"30 mail3.example.org.",
"40 mail4.example.org."
]
}
"20 mail2.example.org.",
"30 mail3.example.org.",
"40 mail4.example.org."
]
}
**Example response:**
@ -356,26 +344,24 @@ Create MX Record Set
Content-Type: application/json
{
"recordset": {
"description": "An MX recordset.",
"links": {
"self": "https://127.0.0.1:9001/v2/zones/2150b1bf-dee2-4221-9d85-11f7886fb15f/recordsets/f7b10e9b-0cae-4a91-b162-562bc6096649"
},
"updated_at": null,
"records" : [
"10 mail1.example.org.",
"20 mail2.example.org.",
"30 mail3.example.org.",
"40 mail4.example.org."
],
"ttl": 3600,
"id": "f7b10e9b-0cae-4a91-b162-562bc6096649",
"name": "mail.example.org.",
"zone_id": "2150b1bf-dee2-4221-9d85-11f7886fb15f",
"created_at": "2014-10-25T19:59:44.000000",
"version": 1,
"type": "MX"
}
"description": "An MX recordset.",
"links": {
"self": "https://127.0.0.1:9001/v2/zones/2150b1bf-dee2-4221-9d85-11f7886fb15f/recordsets/f7b10e9b-0cae-4a91-b162-562bc6096649"
},
"updated_at": null,
"records" : [
"10 mail1.example.org.",
"20 mail2.example.org.",
"30 mail3.example.org.",
"40 mail4.example.org."
],
"ttl": 3600,
"id": "f7b10e9b-0cae-4a91-b162-562bc6096649",
"name": "mail.example.org.",
"zone_id": "2150b1bf-dee2-4221-9d85-11f7886fb15f",
"created_at": "2014-10-25T19:59:44.000000",
"version": 1,
"type": "MX"
}
@ -405,7 +391,6 @@ Create SSHFP Record Set
Content-Type: application/json
{
"recordset" : {
"name" : "foo.example.org.",
"description" : "An SSHFP recordset.",
"type" : "SSHFP",
@ -413,7 +398,6 @@ Create SSHFP Record Set
"records" : [
"1 2 aa2df857dc65c5359f02ca75ec5c4308c0100594d931e8d243a42f586257b5e8"
]
}
}
**Example response:**
@ -424,23 +408,21 @@ Create SSHFP Record Set
Content-Type: application/json
{
"recordset": {
"description": "An SSHFP recordset.",
"links": {
"self": "https://127.0.0.1:9001/v2/zones/2150b1bf-dee2-4221-9d85-11f7886fb15f/recordsets/f7b10e9b-0cae-4a91-b162-562bc6096650"
},
"updated_at": null,
"records" : [
"1 2 aa2df857dc65c5359f02ca75ec5c4308c0100594d931e8d243a42f586257b5e8"
],
"ttl": 3600,
"id": "f7b10e9b-0cae-4a91-b162-562bc6096650",
"name": "foo.example.org.",
"zone_id": "2150b1bf-dee2-4221-9d85-11f7886fb15f",
"created_at": "2014-11-10T19:59:44.000000",
"version": 1,
"type": "SSHFP"
}
"description": "An SSHFP recordset.",
"links": {
"self": "https://127.0.0.1:9001/v2/zones/2150b1bf-dee2-4221-9d85-11f7886fb15f/recordsets/f7b10e9b-0cae-4a91-b162-562bc6096650"
},
"updated_at": null,
"records" : [
"1 2 aa2df857dc65c5359f02ca75ec5c4308c0100594d931e8d243a42f586257b5e8"
],
"ttl": 3600,
"id": "f7b10e9b-0cae-4a91-b162-562bc6096650",
"name": "foo.example.org.",
"zone_id": "2150b1bf-dee2-4221-9d85-11f7886fb15f",
"created_at": "2014-11-10T19:59:44.000000",
"version": 1,
"type": "SSHFP"
}
@ -470,7 +452,6 @@ Create SPF Record Set
Content-Type: application/json
{
"recordset" : {
"name" : "foospf.example.org.",
"description" : "An SPF recordset.",
"type" : "SPF",
@ -478,7 +459,6 @@ Create SPF Record Set
"records" : [
"v=spf1 +all"
]
}
}
**Example response:**
@ -489,23 +469,21 @@ Create SPF Record Set
Content-Type: application/json
{
"recordset": {
"description": "An SPF recordset.",
"links": {
"self": "https://127.0.0.1:9001/v2/zones/2150b1bf-dee2-4221-9d85-11f7886fb15f/recordsets/f7b10e9b-0cae-4a91-b162-562bc6096651"
},
"updated_at": null,
"records" : [
"v=spf1 +all"
],
"ttl": 3600,
"id": "f7b10e9b-0cae-4a91-b162-562bc6096651",
"name": "foospf.example.org.",
"zone_id": "2150b1bf-dee2-4221-9d85-11f7886fb15f",
"created_at": "2014-11-10T19:59:44.000000",
"version": 1,
"type": "SPF"
}
"description": "An SPF recordset.",
"links": {
"self": "https://127.0.0.1:9001/v2/zones/2150b1bf-dee2-4221-9d85-11f7886fb15f/recordsets/f7b10e9b-0cae-4a91-b162-562bc6096651"
},
"updated_at": null,
"records" : [
"v=spf1 +all"
],
"ttl": 3600,
"id": "f7b10e9b-0cae-4a91-b162-562bc6096651",
"name": "foospf.example.org.",
"zone_id": "2150b1bf-dee2-4221-9d85-11f7886fb15f",
"created_at": "2014-11-10T19:59:44.000000",
"version": 1,
"type": "SPF"
}
@ -535,7 +513,6 @@ Create SRV Record Set
Content-Type: application/json
{
"recordset" : {
"name" : "_sip.tcp.example.org.",
"description" : "An SRV recordset.",
"type" : "SRV",
@ -543,7 +520,6 @@ Create SRV Record Set
"records" : [
"10 0 5060 server1.example.org."
]
}
}
**Example response:**
@ -554,23 +530,21 @@ Create SRV Record Set
Content-Type: application/json
{
"recordset": {
"description": "An SRV recordset.",
"links": {
"self": "https://127.0.0.1:9001/v2/zones/2150b1bf-dee2-4221-9d85-11f7886fb15f/recordsets/f7b10e9b-0cae-4a91-b162-562bc6096652"
},
"updated_at": null,
"records" : [
"10 0 5060 server1.example.org."
],
"ttl": 3600,
"id": "f7b10e9b-0cae-4a91-b162-562bc6096652",
"name": "_sip.tcp.example.org.",
"zone_id": "2150b1bf-dee2-4221-9d85-11f7886fb15f",
"created_at": "2014-11-10T19:59:44.000000",
"version": 1,
"type": "SRV"
}
"description": "An SRV recordset.",
"links": {
"self": "https://127.0.0.1:9001/v2/zones/2150b1bf-dee2-4221-9d85-11f7886fb15f/recordsets/f7b10e9b-0cae-4a91-b162-562bc6096652"
},
"updated_at": null,
"records" : [
"10 0 5060 server1.example.org."
],
"ttl": 3600,
"id": "f7b10e9b-0cae-4a91-b162-562bc6096652",
"name": "_sip.tcp.example.org.",
"zone_id": "2150b1bf-dee2-4221-9d85-11f7886fb15f",
"created_at": "2014-11-10T19:59:44.000000",
"version": 1,
"type": "SRV"
}

View File

@ -55,10 +55,8 @@ Create Tld
Content-Type: application/json
{
"tld" : {
"name" : "com",
"description" : "Tld source http://data.iana.org/TLD/tlds-alpha-by-domain.txt"
}
}
**Example response**:
@ -70,7 +68,6 @@ Create Tld
Location: http://127.0.0.1:9001/v2/tlds/5abe514c-9fb5-41e8-ab73-5ed25f8a73e9
{
"tld":{
"description":"Tld source http://data.iana.org/TLD/tlds-alpha-by-domain.txt",
"links":{
"self":"http://127.0.0.1:9001/v2/tlds/5abe514c-9fb5-41e8-ab73-5ed25f8a73e9"
@ -79,7 +76,6 @@ Create Tld
"updated_at":null,
"id":"5abe514c-9fb5-41e8-ab73-5ed25f8a73e9",
"name":"com"
}
}
@ -117,7 +113,6 @@ Get a Tld
Content-Type: application/json; charset=UTF-8
{
"tld":{
"description":"Tld source http://data.iana.org/TLD/tlds-alpha-by-domain.txt",
"links":{
"self":"http://127.0.0.1:9001/v2/tlds/5abe514c-9fb5-41e8-ab73-5ed25f8a73e9"
@ -126,7 +121,6 @@ Get a Tld
"updated_at":null,
"id":"5abe514c-9fb5-41e8-ab73-5ed25f8a73e9",
"name":"com"
}
}
:form created_at: timestamp
@ -215,10 +209,8 @@ Update a Tld
Content-Type: application/json
{
"tld" : {
"name" : "org",
"description" : "Updated the name from com to org"
}
}
**Example response**:
@ -229,7 +221,6 @@ Update a Tld
Content-Type: application/json; charset=UTF-8
{
"tld":{
"description":"Updated the name from com to org",
"links":{
"self":"http://127.0.0.1:9001/v2/tlds/5abe514c-9fb5-41e8-ab73-5ed25f8a73e9"
@ -238,7 +229,6 @@ Update a Tld
"updated_at":"2014-01-23T20:35:12.449599",
"id":"5abe514c-9fb5-41e8-ab73-5ed25f8a73e9",
"name":"org"
}
}
:form created_at: timestamp

View File

@ -37,12 +37,10 @@ Create Zone
Content-Type: application/json
{
"zone": {
"name": "example.org.",
"email": "joe@example.org",
"ttl": 7200,
"description": "This is an example zone."
}
}
**Example response:**
@ -53,7 +51,6 @@ Create Zone
Content-Type: application/json
{
"zone": {
"id": "a86dba58-0043-4cc6-a1bb-69d5e86f3ca3",
"pool_id": "572ba08c-d929-4c70-8e42-03824bb24ca2",
"project_id": "4335d1f0-f793-11e2-b778-0800200c9a66",
@ -72,7 +69,6 @@ Create Zone
"links": {
"self": "https://127.0.0.1:9001/v2/zones/a86dba58-0043-4cc6-a1bb-69d5e86f3ca3"
}
}
}
:form description: UTF-8 text field.
@ -112,7 +108,6 @@ Get Zone
Content-Type: application/json
{
"zone": {
"id": "a86dba58-0043-4cc6-a1bb-69d5e86f3ca3",
"pool_id": "572ba08c-d929-4c70-8e42-03824bb24ca2",
"project_id": "4335d1f0-f793-11e2-b778-0800200c9a66",
@ -131,7 +126,6 @@ Get Zone
"links": {
"self": "https://127.0.0.1:9001/v2/zones/a86dba58-0043-4cc6-a1bb-69d5e86f3ca3"
}
}
}
:statuscode 200: Success
@ -229,9 +223,7 @@ Update Zone
Content-Type: application/json
{
"zone": {
"ttl": 3600
}
}
**Response:**
@ -242,7 +234,6 @@ Update Zone
Content-Type: application/json
{
"zone": {
"id": "a86dba58-0043-4cc6-a1bb-69d5e86f3ca3",
"pool_id": "572ba08c-d929-4c70-8e42-03824bb24ca2",
"project_id": "4335d1f0-f793-11e2-b778-0800200c9a66",
@ -261,7 +252,6 @@ Update Zone
"links": {
"self": "https://127.0.0.1:9001/v2/zones/a86dba58-0043-4cc6-a1bb-69d5e86f3ca3"
}
}
}
:form description: UTF-8 text field.
@ -334,24 +324,22 @@ Import Zone
Content-Type: application/json
{
"zone": {
"email": "nsadmin@example.com",
"id": "6b78734a-aef1-45cd-9708-8eb3c2d26ff1",
"links": {
"self": "http://127.0.0.1:9001/v2/zones/6b78734a-aef1-45cd-9708-8eb3c2d26ff1"
},
"name": "example.com.",
"pool_id": "572ba08c-d929-4c70-8e42-03824bb24ca2",
"project_id": "d7accc2f8ce343318386886953f2fc6a",
"serial": 1404757531,
"ttl": "42",
"created_at": "2014-07-07T18:25:31.275934",
"updated_at": null,
"version": 1,
"masters": [],
"type": "PRIMARY",
"transferred_at": null
}
"email": "nsadmin@example.com",
"id": "6b78734a-aef1-45cd-9708-8eb3c2d26ff1",
"links": {
"self": "http://127.0.0.1:9001/v2/zones/6b78734a-aef1-45cd-9708-8eb3c2d26ff1"
},
"name": "example.com.",
"pool_id": "572ba08c-d929-4c70-8e42-03824bb24ca2",
"project_id": "d7accc2f8ce343318386886953f2fc6a",
"serial": 1404757531,
"ttl": "42",
"created_at": "2014-07-07T18:25:31.275934",
"updated_at": null,
"version": 1,
"masters": [],
"type": "PRIMARY",
"transferred_at": null
}
:statuscode 201: Created
@ -465,10 +453,8 @@ Create Zone Transfer Request
Content-Type: application/json
{
"transfer_request":{
"target_project_id": "123456",
"description": "Transfer qa.dev.example.com. to QA Team"
}
"target_project_id": "123456",
"description": "Transfer qa.dev.example.com. to QA Team"
}
**Example Response**
@ -479,20 +465,18 @@ Create Zone Transfer Request
Content-Type: application/json
{
"transfer_request": {
"created_at": "2014-07-17T20:34:40.882579",
"description": null,
"id": "f2ad17b5-807a-423f-a991-e06236c247be",
"key": "9Z2R50Y0",
"project_id": "1",
"status": "ACTIVE",
"target_project_id": "123456",
"updated_at": null,
"zone_id": "6b78734a-aef1-45cd-9708-8eb3c2d26ff8",
"zone_name": "qa.dev.example.com.",
"links": {
"self": "http://127.0.0.1:9001/v2/zones/tasks/transfer_requests/f2ad17b5-807a-423f-a991-e06236c247be"
}
"created_at": "2014-07-17T20:34:40.882579",
"description": null,
"id": "f2ad17b5-807a-423f-a991-e06236c247be",
"key": "9Z2R50Y0",
"project_id": "1",
"status": "ACTIVE",
"target_project_id": "123456",
"updated_at": null,
"zone_id": "6b78734a-aef1-45cd-9708-8eb3c2d26ff8",
"zone_name": "qa.dev.example.com.",
"links": {
"self": "http://127.0.0.1:9001/v2/zones/tasks/transfer_requests/f2ad17b5-807a-423f-a991-e06236c247be"
}
}
@ -583,15 +567,13 @@ View a Transfer Request
Content-Type: application/json
{
"transfer_request":{
"description": "This is scoped to the requesting project",
"id": "efd2d720-b0c4-43d4-99f7-d9b53e08860d",
"zone_id": "2c4d5e37-f823-4bee-9859-031cb44f80e7",
"zone_name": "subdomain.example.com.",
"status": "ACTIVE",
"links": {
"self": "http://127.0.0.1:9001/v2/zones/tasks/transfer_requests/efd2d720-b0c4-43d4-99f7-d9b53e08860d"
}
"description": "This is scoped to the requesting project",
"id": "efd2d720-b0c4-43d4-99f7-d9b53e08860d",
"zone_id": "2c4d5e37-f823-4bee-9859-031cb44f80e7",
"zone_name": "subdomain.example.com.",
"status": "ACTIVE",
"links": {
"self": "http://127.0.0.1:9001/v2/zones/tasks/transfer_requests/efd2d720-b0c4-43d4-99f7-d9b53e08860d"
}
}
@ -616,10 +598,8 @@ Accept a Transfer Request
Content-Type: application/json
{
"transfer_accept":{
"key":"9Z2R50Y0",
"zone_transfer_request_id":"f2ad17b5-807a-423f-a991-e06236c247be"
}
"key":"9Z2R50Y0",
"zone_transfer_request_id":"f2ad17b5-807a-423f-a991-e06236c247be"
}
**Example Response**
@ -630,13 +610,11 @@ Accept a Transfer Request
Content-Type: application/json
{
"transfer_accept": {
"id": "581891d5-99f5-49e1-86c3-eec0f44d66fd",
"links": {
"self": "http://127.0.0.1:9001/v2/zones/tasks/transfer_accepts/581891d5-99f5-49e1-86c3-eec0f44d66fd",
"zone": "http://127.0.0.1:9001/v2/zones/6b78734a-aef1-45cd-9708-8eb3c2d26ff8"
},
"status": "COMPLETE"
}
"id": "581891d5-99f5-49e1-86c3-eec0f44d66fd",
"links": {
"self": "http://127.0.0.1:9001/v2/zones/tasks/transfer_accepts/581891d5-99f5-49e1-86c3-eec0f44d66fd",
"zone": "http://127.0.0.1:9001/v2/zones/6b78734a-aef1-45cd-9708-8eb3c2d26ff8"
},
"status": "COMPLETE"
}

View File

@ -16,18 +16,12 @@ limitations under the License.
from functionaltests.common.models import BaseModel
from functionaltests.common.models import CollectionModel
from functionaltests.common.models import EntityModel
class ZoneData(BaseModel):
class ZoneModel(BaseModel):
pass
class ZoneModel(EntityModel):
ENTITY_NAME = 'zone'
MODEL_TYPE = ZoneData
class ZoneListModel(CollectionModel):
COLLECTION_NAME = 'zones'
MODEL_TYPE = ZoneData
MODEL_TYPE = ZoneModel