Move Pool NS Records to their own table

The provides part 1 of the Pools API changes discussed at the
mid cycle. Th majority of the remaining changes are new code/
objects rather than refactoring - so this change has been
split into it's own review.

Change-Id: I2291d76d36e8fcdeb8dffca855e7292d8cb22bfb
This commit is contained in:
Kiall Mac Innes 2015-03-17 14:19:42 +00:00
parent eaa914489d
commit e0dc0e77b4
25 changed files with 711 additions and 758 deletions

View File

@ -29,17 +29,16 @@ domains_schema = schema.Schema('v1', 'domains')
servers_schema = schema.Schema('v1', 'servers') servers_schema = schema.Schema('v1', 'servers')
def _poolattribute_to_server(pool_attribute): def _pool_ns_record_to_server(pool_ns_record):
server_values = { server_values = {
'id': pool_attribute.id, 'id': pool_ns_record.id,
'created_at': pool_attribute.created_at, 'created_at': pool_ns_record.created_at,
'updated_at': pool_attribute.updated_at, 'updated_at': pool_ns_record.updated_at,
'version': pool_attribute.version, 'version': pool_ns_record.version,
'name': pool_attribute.value 'name': pool_ns_record.hostname
} }
server = objects.Server(**server_values) return objects.Server.from_dict(server_values)
return server
@blueprint.route('/schemas/domain', methods=['GET']) @blueprint.route('/schemas/domain', methods=['GET'])
@ -153,6 +152,6 @@ def get_domain_servers(domain_id):
servers = objects.ServerList() servers = objects.ServerList()
for ns in nameservers: for ns in nameservers:
servers.append(_poolattribute_to_server(ns)) servers.append(_pool_ns_record_to_server(ns))
return flask.jsonify(servers_schema.filter({'servers': servers})) return flask.jsonify(servers_schema.filter({'servers': servers}))

View File

@ -34,17 +34,16 @@ default_pool_id = cfg.CONF['service:central'].default_pool_id
# to work # to work
def _poolattribute_to_server(pool_attribute): def _pool_ns_record_to_server(pool_ns_record):
server_values = { server_values = {
'id': pool_attribute.id, 'id': pool_ns_record.id,
'created_at': pool_attribute.created_at, 'created_at': pool_ns_record.created_at,
'updated_at': pool_attribute.updated_at, 'updated_at': pool_ns_record.updated_at,
'version': pool_attribute.version, 'version': pool_ns_record.version,
'name': pool_attribute.value 'name': pool_ns_record.hostname
} }
server = objects.Server(**server_values) return objects.Server.from_dict(server_values)
return server
@blueprint.route('/schemas/server', methods=['GET']) @blueprint.route('/schemas/server', methods=['GET'])
@ -65,19 +64,18 @@ def create_server():
# Validate against the original server schema # Validate against the original server schema
server_schema.validate(values) server_schema.validate(values)
# Create a PoolAttribute object # Create a PoolNsRecord object
pa_values = { pns_values = {
'pool_id': default_pool_id, 'priority': 10,
'key': 'name_server', 'hostname': values['name']
'value': values['name']
} }
nameserver = objects.NameServer(**pa_values) ns_record = objects.PoolNsRecord.from_dict(pns_values)
# Get the default pool # Get the default pool
pool = central_api.get_pool(context, default_pool_id) pool = central_api.get_pool(context, default_pool_id)
# Add the new PoolAttribute to the pool as a nameserver # Add the new PoolAttribute to the pool as a nameserver
pool.nameservers.append(nameserver) pool.ns_records.append(ns_record)
try: try:
# Update the pool # Update the pool
@ -86,15 +84,15 @@ def create_server():
except exceptions.DuplicatePoolAttribute: except exceptions.DuplicatePoolAttribute:
raise exceptions.DuplicateServer() raise exceptions.DuplicateServer()
# Go through the pool.nameservers to find the right one to get the ID # Go through the pool.ns_records to find the right one to get the ID
for ns in updated_pool.nameservers: for ns in updated_pool.ns_records:
if ns.value == pa_values['value']: if ns.hostname == pns_values['hostname']:
created_nameserver = ns created_ns_record = ns
break break
# Convert the PoolAttribute to a Server so we can validate with the # Convert the PoolAttribute to a Server so we can validate with the
# original schema and display # original schema and display
server = _poolattribute_to_server(created_nameserver) server = _pool_ns_record_to_server(created_ns_record)
response = flask.jsonify(server_schema.filter(server)) response = flask.jsonify(server_schema.filter(server))
response.status_int = 201 response.status_int = 201
@ -114,8 +112,8 @@ def get_servers():
servers = objects.ServerList() servers = objects.ServerList()
for ns in pool.nameservers: for ns in pool.ns_records:
servers.append(_poolattribute_to_server(ns)) servers.append(_pool_ns_record_to_server(ns))
return flask.jsonify(servers_schema.filter({'servers': servers})) return flask.jsonify(servers_schema.filter({'servers': servers}))
@ -129,11 +127,11 @@ def get_server(server_id):
# Get the default pool # Get the default pool
pool = central_api.get_pool(context, default_pool_id) pool = central_api.get_pool(context, default_pool_id)
# Create an empty PoolAttribute object # Create an empty PoolNsRecord object
nameserver = objects.NameServer() nameserver = objects.PoolNsRecord()
# Get the desired nameserver from the pool # Get the desired nameserver from the pool
for ns in pool.nameservers: for ns in pool.ns_records:
if ns.id == server_id: if ns.id == server_id:
nameserver = ns nameserver = ns
break break
@ -142,7 +140,7 @@ def get_server(server_id):
if nameserver.id != server_id: if nameserver.id != server_id:
raise exceptions.ServerNotFound raise exceptions.ServerNotFound
server = _poolattribute_to_server(nameserver) server = _pool_ns_record_to_server(nameserver)
return flask.jsonify(server_schema.filter(server)) return flask.jsonify(server_schema.filter(server))
@ -159,31 +157,31 @@ def update_server(server_id):
# Get the Nameserver from the pool # Get the Nameserver from the pool
index = -1 index = -1
nameservers = pool.nameservers ns_records = pool.ns_records
for ns in nameservers: for ns in ns_records:
if ns.id == server_id: if ns.id == server_id:
index = nameservers.index(ns) index = ns_records.index(ns)
break break
if index == -1: if index == -1:
raise exceptions.ServerNotFound raise exceptions.ServerNotFound
# Get the nameserver from the pool so we can update it # Get the ns_record from the pool so we can update it
nameserver = nameservers.pop(index) nameserver = ns_records.pop(index)
# Update it with the new values # Update it with the new values
nameserver.update({'value': values['name']}) nameserver.update({'hostname': values['name']})
# Change it to a server, so we can use the original validation. We want # Change it to a server, so we can use the original validation. We want
# to make sure we don't change anything in v1 # to make sure we don't change anything in v1
server = _poolattribute_to_server(nameserver) server = _pool_ns_record_to_server(nameserver)
server_data = server_schema.filter(server) server_data = server_schema.filter(server)
server_data.update(values) server_data.update(values)
# Validate the new set of data # Validate the new set of data
server_schema.validate(server_data) server_schema.validate(server_data)
# Now that it's been validated, add it back to the pool and persist it # Now that it's been validated, add it back to the pool and persist it
pool.nameservers.append(nameserver) pool.ns_records.append(nameserver)
central_api.update_pool(context, pool) central_api.update_pool(context, pool)
return flask.jsonify(server_schema.filter(server)) return flask.jsonify(server_schema.filter(server))
@ -200,17 +198,17 @@ def delete_server(server_id):
# Get the Nameserver from the pool # Get the Nameserver from the pool
index = -1 index = -1
nameservers = pool.nameservers ns_records = pool.ns_records
for ns in nameservers: for ns in ns_records:
if ns.id == server_id: if ns.id == server_id:
index = nameservers.index(ns) index = ns_records.index(ns)
break break
if index == -1: if index == -1:
raise exceptions.ServerNotFound raise exceptions.ServerNotFound
# Remove the nameserver from the pool so it will be deleted # Remove the nameserver from the pool so it will be deleted
nameservers.pop(index) ns_records.pop(index)
# Update the pool without the deleted server # Update the pool without the deleted server
central_api.update_pool(context, pool) central_api.update_pool(context, pool)

View File

@ -1,38 +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 pecan
from oslo_log import log as logging
from designate import utils
from designate.api.v2.controllers import rest
from designate.api.v2.views import nameservers as nameservers_view
LOG = logging.getLogger(__name__)
class NameServersController(rest.RestController):
_view = nameservers_view.NameServerView()
@pecan.expose(template='json:', content_type='application/json')
@utils.validate_uuid('zone_id')
def get_all(self, zone_id):
request = pecan.request
context = pecan.request.environ['context']
servers = self.central_api.get_domain_servers(context, zone_id)
return self._view.list(context, request, servers, [zone_id])

View File

@ -23,7 +23,6 @@ from designate import utils
from designate import schema from designate import schema
from designate import dnsutils from designate import dnsutils
from designate.api.v2.controllers import rest from designate.api.v2.controllers import rest
from designate.api.v2.controllers import nameservers
from designate.api.v2.controllers import recordsets from designate.api.v2.controllers import recordsets
from designate.api.v2.controllers.zones import tasks from designate.api.v2.controllers.zones import tasks
from designate.api.v2.views import zones as zones_view from designate.api.v2.views import zones as zones_view
@ -40,7 +39,6 @@ class ZonesController(rest.RestController):
SORT_KEYS = ['created_at', 'id', 'updated_at', 'name', 'tenant_id', SORT_KEYS = ['created_at', 'id', 'updated_at', 'name', 'tenant_id',
'serial', 'ttl', 'status'] 'serial', 'ttl', 'status']
nameservers = nameservers.NameServersController()
recordsets = recordsets.RecordSetsController() recordsets = recordsets.RecordSetsController()
tasks = tasks.TasksController() tasks = tasks.TasksController()

View File

@ -1,43 +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 NameServerView(base_view.BaseView):
"""Model a NameServer API response as a python dictionary"""
_resource_name = 'nameserver'
_collection_name = 'nameservers'
def _get_base_href(self, parents=None):
assert len(parents) == 1
href = "%s/v2/zones/%s/nameservers" % (self.base_uri, parents[0])
return href.rstrip('?')
def show_basic(self, context, request, nameserver):
"""Basic view of a nameserver"""
return {
"id": nameserver["id"],
"name": nameserver["value"]
}

View File

@ -34,7 +34,8 @@ class PoolsView(base_view.BaseView):
"name": pool['name'], "name": pool['name'],
"project_id": pool['tenant_id'], "project_id": pool['tenant_id'],
"attributes": dict((r.key, r.value) for r in pool['attributes']), "attributes": dict((r.key, r.value) for r in pool['attributes']),
"nameservers": [r.value for r in pool['nameservers']], "ns_records": [{'priority': n.priority, 'hostname': n.hostname}
for n in pool['ns_records']],
"description": pool['description'], "description": pool['description'],
"created_at": pool['created_at'], "created_at": pool['created_at'],
"updated_at": pool['updated_at'], "updated_at": pool['updated_at'],
@ -43,13 +44,14 @@ class PoolsView(base_view.BaseView):
def load(self, context, request, body): def load(self, context, request, body):
"""Extract a "central" compatible dict from an API call""" """Extract a "central" compatible dict from an API call"""
valid_keys = ('name', 'attributes', 'nameservers', 'description') valid_keys = ('name', 'attributes', 'ns_records', 'description')
result = self._load(context, request, body, valid_keys) result = self._load(context, request, body, valid_keys)
if 'nameservers' in result: if 'ns_records' in result:
result['nameservers'] = objects.NameServerList( result['ns_records'] = objects.PoolNsRecordList(
objects=[objects.NameServer(key='name_server', value=r) objects=[objects.PoolNsRecord(priority=r['priority'],
for r in result['nameservers']]) hostname=r['hostname'])
for r in result['ns_records']])
if 'attributes' in result: if 'attributes' in result:
result['attributes'] = objects.PoolAttributeList( result['attributes'] = objects.PoolAttributeList(

View File

@ -477,8 +477,8 @@ class Service(service.RPCService, service.Service):
return domain return domain
# SOA Recordset Methods # SOA Recordset Methods
def _build_soa_record(self, zone, nameservers): def _build_soa_record(self, zone, ns_records):
return "%s %s. %d %d %d %d %d" % (nameservers[0]['value'], return "%s %s. %d %d %d %d %d" % (ns_records[0]['hostname'],
zone['email'].replace("@", "."), zone['email'].replace("@", "."),
zone['serial'], zone['serial'],
zone['refresh'], zone['refresh'],
@ -487,17 +487,14 @@ class Service(service.RPCService, service.Service):
zone['minimum']) zone['minimum'])
def _create_soa(self, context, zone): def _create_soa(self, context, zone):
# Need elevated context to get the servers # Need elevated context to get the pool
elevated_context = context.elevated() elevated_context = context.elevated()
elevated_context.all_tenants = True elevated_context.all_tenants = True
# Get the nameservers # Get the pool for it's list of ns_records
nameservers = self.storage.find_pool_attributes( pool = self.storage.get_pool(elevated_context, zone.pool_id)
context=elevated_context,
criterion={'pool_id': zone.pool_id, 'key': 'name_server'}
)
soa_values = [self._build_soa_record(zone, nameservers)] soa_values = [self._build_soa_record(zone, pool.ns_records)]
recordlist = objects.RecordList(objects=[ recordlist = objects.RecordList(objects=[
objects.Record(data=r, managed=True) for r in soa_values]) objects.Record(data=r, managed=True) for r in soa_values])
values = { values = {
@ -515,29 +512,31 @@ class Service(service.RPCService, service.Service):
if zone.type != 'PRIMARY': if zone.type != 'PRIMARY':
return return
nameservers = self.get_domain_servers(context, zone['id']) # Need elevated context to get the pool
elevated_context = context.elevated()
elevated_context.all_tenants = True
# Get the pool for it's list of ns_records
pool = self.storage.get_pool(elevated_context, zone.pool_id)
soa = self.find_recordset(context, soa = self.find_recordset(context,
criterion={'domain_id': zone['id'], criterion={'domain_id': zone['id'],
'type': "SOA"}) 'type': "SOA"})
soa.records[0].data = self._build_soa_record(zone, nameservers) soa.records[0].data = self._build_soa_record(zone, pool.ns_records)
self._update_recordset_in_storage(context, zone, soa, self._update_recordset_in_storage(context, zone, soa,
increment_serial=False) increment_serial=False)
# NS Recordset Methods # NS Recordset Methods
def _create_ns(self, context, zone, nameservers): def _create_ns(self, context, zone, ns_records):
# NOTE: We should not be creating NS records when a zone is SECONDARY. # NOTE: We should not be creating NS records when a zone is SECONDARY.
if zone.type != 'PRIMARY': if zone.type != 'PRIMARY':
return return
# Create an NS record for each server # Create an NS record for each server
ns_values = []
for s in nameservers:
ns_values.append(s.value)
recordlist = objects.RecordList(objects=[ recordlist = objects.RecordList(objects=[
objects.Record(data=r, managed=True) for r in ns_values]) objects.Record(data=r, managed=True) for r in ns_records])
values = { values = {
'name': zone['name'], 'name': zone['name'],
'type': "NS", 'type': "NS",
@ -549,51 +548,32 @@ class Service(service.RPCService, service.Service):
return ns return ns
def _update_ns(self, context, zone, orig_name, new_name): def _add_ns(self, context, zone, ns_record):
# NOTE: We should not be updating NS records when a zone is SECONDARY.
if zone.type != 'PRIMARY':
return
# Get the zone's NS recordset
ns = self.find_recordset(context,
criterion={'domain_id': zone['id'],
'type': "NS"})
for r in ns.records:
if r.data == orig_name:
r.data = new_name
self._update_recordset_in_storage(context, zone, ns)
def _add_ns(self, context, zone, nameserver):
# Get NS recordset # Get NS recordset
# If the zone doesn't have an NS recordset yet, create one # If the zone doesn't have an NS recordset yet, create one
try: try:
ns = self.find_recordset(context, ns_recordset = self.find_recordset(
criterion={'domain_id': zone['id'], context, criterion={'domain_id': zone['id'], 'type': "NS"})
'type': "NS"})
except exceptions.RecordSetNotFound: except exceptions.RecordSetNotFound:
nameservers = objects.PoolAttributeList() self._create_ns(context, zone, [ns_record])
nameservers.append(nameserver)
self._create_ns(context, zone, nameservers)
return return
# Add new record to recordset based on the new nameserver # Add new record to recordset based on the new nameserver
ns_record = objects.Record(data=nameserver.value) ns_recordset.records.append(
ns.records.append(ns_record) objects.Record(data=ns_record, managed=True))
self._update_recordset_in_storage(context, zone, ns) self._update_recordset_in_storage(context, zone, ns_recordset)
def _delete_ns(self, context, zone, nameserver): def _delete_ns(self, context, zone, ns_record):
ns = self.find_recordset(context, ns_recordset = self.find_recordset(
criterion={'domain_id': zone['id'], context, criterion={'domain_id': zone['id'], 'type': "NS"})
'type': "NS"})
records = ns.records
for r in records: for record in copy.deepcopy(ns_recordset.records):
if r.data == nameserver.value: if record.data == ns_record:
ns.records.remove(r) ns_recordset.records.remove(record)
self._update_recordset_in_storage(context, zone, ns) self._update_recordset_in_storage(context, zone, ns_recordset)
# Quota Enforcement Methods # Quota Enforcement Methods
def _enforce_domain_quota(self, context, tenant_id): def _enforce_domain_quota(self, context, tenant_id):
@ -833,12 +813,9 @@ class Service(service.RPCService, service.Service):
# configured. # configured.
elevated_context = context.elevated() elevated_context = context.elevated()
elevated_context.all_tenants = True elevated_context.all_tenants = True
nameservers = self.storage.find_pool_attributes( pool = self.storage.get_pool(elevated_context, domain.pool_id)
context=elevated_context,
criterion={'pool_id': domain.pool_id, 'key': 'name_server'}
)
if len(nameservers) == 0: if len(pool.ns_records) == 0:
LOG.critical(_LC('No nameservers configured. ' LOG.critical(_LC('No nameservers configured. '
'Please create at least one nameserver')) 'Please create at least one nameserver'))
raise exceptions.NoServersConfigured() raise exceptions.NoServersConfigured()
@ -871,12 +848,12 @@ class Service(service.RPCService, service.Service):
domain.status = 'PENDING' domain.status = 'PENDING'
domain = self.storage.create_domain(context, domain) domain = self.storage.create_domain(context, domain)
nameservers = self.get_domain_servers(context, domain['id']) pool_ns_records = self.get_domain_servers(context, domain['id'])
# Create the SOA and NS recordsets for the new domain. The SOA # Create the SOA and NS recordsets for the new domain. The SOA
# record will always be the first 'created_at' record for a domain. # record will always be the first 'created_at' record for a domain.
self._create_soa(context, domain) self._create_soa(context, domain)
self._create_ns(context, domain, nameservers) self._create_ns(context, domain, [n.hostname for n in pool_ns_records])
if domain.obj_attr_is_set('recordsets'): if domain.obj_attr_is_set('recordsets'):
for rrset in domain.recordsets: for rrset in domain.recordsets:
@ -913,15 +890,14 @@ class Service(service.RPCService, service.Service):
policy.check('get_domain_servers', context, target) policy.check('get_domain_servers', context, target)
nameservers = self.storage.find_pool_attributes( # Need elevated context to get the pool
context=context, elevated_context = context.elevated()
criterion={ elevated_context.all_tenants = True
'pool_id': pool_id,
'key': 'name_server'
}
)
return nameservers # Get the pool for it's list of ns_records
pool = self.storage.get_pool(elevated_context, pool_id)
return pool.ns_records
def find_domains(self, context, criterion=None, marker=None, limit=None, def find_domains(self, context, criterion=None, marker=None, limit=None,
sort_key=None, sort_dir=None): sort_key=None, sort_dir=None):
@ -2018,53 +1994,52 @@ class Service(service.RPCService, service.Service):
# If there is a nameserver, then additional steps need to be done # If there is a nameserver, then additional steps need to be done
# Since these are treated as mutable objects, we're only going to # Since these are treated as mutable objects, we're only going to
# be comparing the nameserver.value which is the FQDN # be comparing the nameserver.value which is the FQDN
if pool.obj_attr_is_set('nameservers'): if pool.obj_attr_is_set('ns_records'):
elevated_context = context.elevated() elevated_context = context.elevated()
elevated_context.all_tenants = True elevated_context.all_tenants = True
# Get all existing nameserver FQDNs and put them in a set
existing_ns = \
set([ns.value for ns in self.storage.find_pool_attributes(
context=context,
criterion={'pool_id': pool.id, 'key': 'name_server'}
)])
# Get all nameserver FQDNs from the request and put them in a set # TODO(kiall): ListObjects should be able to give you their
request_ns = set([ns.value for ns in pool.nameservers.objects]) # original set of values.
# Get the new ones to be created original_pool = self.storage.get_pool(elevated_context, pool.id)
# Find the current NS hostnames
existing_ns = set([n.hostname for n in original_pool.ns_records])
# Find the desired NS hostnames
request_ns = set([n.hostname for n in pool.ns_records])
# Get the NS's to be created and deleted, ignoring the ones that
# are in both sets, as those haven't changed.
# TODO(kiall): Factor in priority
create_ns = request_ns.difference(existing_ns) create_ns = request_ns.difference(existing_ns)
# Get the ones to be deleted
delete_ns = existing_ns.difference(request_ns) delete_ns = existing_ns.difference(request_ns)
# Ignore the ones that are in both sets, as those haven't changed
updated_pool = self.storage.update_pool(context, pool) updated_pool = self.storage.update_pool(context, pool)
# After the update, handle new nameservers # After the update, handle new ns_records
for ns in create_ns: for ns in create_ns:
# Create new NS recordsets for every zone # Create new NS recordsets for every zone
zones = self.find_domains( zones = self.find_domains(
context=elevated_context, context=elevated_context,
criterion={'pool_id': pool.id}) criterion={'pool_id': pool.id})
for z in zones:
self._add_ns(elevated_context, z,
objects.PoolAttribute(value=ns))
# Then handle the nameservers to delete for z in zones:
self._add_ns(elevated_context, z, ns)
# Then handle the ns_records to delete
for ns in delete_ns: for ns in delete_ns:
# Cannot delete the last nameserver, so verify that first. # Cannot delete the last nameserver, so verify that first.
nameservers = pool.nameservers if len(pool.ns_records) == 0:
if len(nameservers) == 0:
raise exceptions.LastServerDeleteNotAllowed( raise exceptions.LastServerDeleteNotAllowed(
"Not allowed to delete last of servers" "Not allowed to delete last of servers"
) )
# Delete the NS recordsets for every zone # Delete the NS record for every zone
zones = self.find_domains( zones = self.find_domains(
context=elevated_context, context=elevated_context,
criterion={'pool_id': pool.id}) criterion={'pool_id': pool.id})
for z in zones: for z in zones:
self._delete_ns(elevated_context, z, self._delete_ns(elevated_context, z, ns)
objects.PoolAttribute(value=ns))
return updated_pool return updated_pool

View File

@ -252,6 +252,10 @@ class DuplicateDomainAttribute(Duplicate):
error_type = 'duplicate_domain_attribute' error_type = 'duplicate_domain_attribute'
class DuplicatePoolNsRecord(Duplicate):
error_type = 'duplicate_pool_ns_record'
class MethodNotAllowed(Base): class MethodNotAllowed(Base):
expected = True expected = True
error_code = 405 error_code = 405
@ -324,6 +328,10 @@ class PoolAttributeNotFound(NotFound):
error_type = 'pool_attribute_not_found' error_type = 'pool_attribute_not_found'
class PoolNsRecordNotFound(NotFound):
error_type = 'pool_ns_record_not_found'
class ZoneTransferRequestNotFound(NotFound): class ZoneTransferRequestNotFound(NotFound):
error_type = 'zone_transfer_request_not_found' error_type = 'zone_transfer_request_not_found'

View File

@ -26,7 +26,7 @@ from designate.objects.pool_manager_status import PoolManagerStatus, PoolManager
from designate.objects.pool_server import PoolServer, PoolServerList # noqa from designate.objects.pool_server import PoolServer, PoolServerList # noqa
from designate.objects.pool import Pool, PoolList # noqa from designate.objects.pool import Pool, PoolList # noqa
from designate.objects.pool_attribute import PoolAttribute, PoolAttributeList # noqa from designate.objects.pool_attribute import PoolAttribute, PoolAttributeList # noqa
from designate.objects.nameserver import NameServer, NameServerList # noqa from designate.objects.pool_ns_record import PoolNsRecord, PoolNsRecordList # noqa
from designate.objects.quota import Quota, QuotaList # noqa from designate.objects.quota import Quota, QuotaList # noqa
from designate.objects.rrdata_a import RRData_A # noqa from designate.objects.rrdata_a import RRData_A # noqa
from designate.objects.rrdata_aaaa import RRData_AAAA # noqa from designate.objects.rrdata_aaaa import RRData_AAAA # noqa

View File

@ -1,28 +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 designate.objects import base
class NameServer(base.DictObjectMixin, base.PersistentObjectMixin,
base.DesignateObject):
FIELDS = {
'pool_id': {},
'key': {},
'value': {}
}
class NameServerList(base.ListObjectMixin, base.DesignateObject):
LIST_ITEM_TYPE = NameServer

View File

@ -55,9 +55,9 @@ class Pool(base.DictObjectMixin, base.PersistentObjectMixin,
'relation_cls': 'PoolAttributeList', 'relation_cls': 'PoolAttributeList',
'required': True 'required': True
}, },
'nameservers': { 'ns_records': {
'relation': True, 'relation': True,
'relation_cls': 'NameServerList', 'relation_cls': 'PoolNsRecordList',
'required': True 'required': True
}, },
} }

View File

@ -0,0 +1,51 @@
# 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 designate.objects import base
class PoolNsRecord(base.DictObjectMixin, base.PersistentObjectMixin,
base.DesignateObject):
FIELDS = {
'pool_id': {
'schema': {
'type': 'string',
'description': 'Pool identifier',
'format': 'uuid',
},
'required': True
},
'priority': {
'schema': {
'type': 'integer',
'description': 'NS Record Priority Order',
'minimum': 1,
'maximum': 10000
},
},
'hostname': {
'schema': {
'type': 'string',
'description': 'NS Record Hostname',
'format': 'domainname',
'maxLength': 255,
},
'immutable': True,
'required': True
}
}
class PoolNsRecordList(base.ListObjectMixin, base.DesignateObject):
LIST_ITEM_TYPE = PoolNsRecord

View File

@ -13,7 +13,7 @@
"pool": { "pool": {
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"required": ["name", "attributes", "nameservers"], "required": ["name", "attributes", "ns_records"],
"properties": { "properties": {
"id": { "id": {
@ -42,6 +42,7 @@
"attributes": { "attributes": {
"type": "object", "type": "object",
"description": "Pool attributes as name value pairs", "description": "Pool attributes as name value pairs",
"additionalProperties": true,
"properties": { "properties": {
"scope": { "scope": {
"type": "string", "type": "string",
@ -49,15 +50,27 @@
} }
} }
}, },
"nameservers": { "ns_records": {
"type": "array", "type": "array",
"description": "Nameservers that would go in the NS record", "description": "List of NS Records for Zones in this pool",
"minItems": 1, "minItems": 1,
"items": { "items": {
"type": "object",
"additionalProperties": false,
"required": ["priority", "hostname"],
"properties": {
"priority": {
"type": "integer",
"minimum": 0,
"maximum": 1000
},
"hostname": {
"type": "string", "type": "string",
"format": "hostname", "format": "hostname",
"maxLength": 255 "maxLength": 255
} }
}
}
}, },
"version": { "version": {
"type": "integer", "type": "integer",

View File

@ -706,71 +706,70 @@ class SQLAlchemyStorage(sqlalchemy_base.SQLAlchemy, storage_base.Storage):
# Pool methods # Pool methods
def _find_pools(self, context, criterion, one=False, marker=None, def _find_pools(self, context, criterion, one=False, marker=None,
limit=None, sort_key=None, sort_dir=None): limit=None, sort_key=None, sort_dir=None):
return self._find(context, tables.pools, objects.Pool, pools = self._find(context, tables.pools, objects.Pool,
objects.PoolList, exceptions.PoolNotFound, objects.PoolList, exceptions.PoolNotFound,
criterion, one, marker, limit, sort_key, criterion, one, marker, limit, sort_key,
sort_dir) sort_dir)
# Load Relations
def _load_relations(pool):
pool.attributes = self._find_pool_attributes(
context, {'pool_id': pool.id})
pool.ns_records = self._find_pool_ns_records(
context, {'pool_id': pool.id})
pool.obj_reset_changes(['attributes', 'ns_records'])
if one:
_load_relations(pools)
else:
for pool in pools:
_load_relations(pool)
return pools
def create_pool(self, context, pool): def create_pool(self, context, pool):
pool = self._create( pool = self._create(
tables.pools, pool, exceptions.DuplicatePool, tables.pools, pool, exceptions.DuplicatePool,
['attributes', 'nameservers']) ['attributes', 'ns_records'])
if pool.obj_attr_is_set('attributes'): if pool.obj_attr_is_set('attributes'):
for pool_attribute in pool.attributes: for pool_attribute in pool.attributes:
self.create_pool_attribute(context, pool.id, pool_attribute) self.create_pool_attribute(context, pool.id, pool_attribute)
else: else:
pool.attributes = objects.PoolAttributeList() pool.attributes = objects.PoolAttributeList()
pool.obj_reset_changes(['attributes'])
if pool.obj_attr_is_set('nameservers'): if pool.obj_attr_is_set('ns_records'):
for nameserver in pool.nameservers: for ns_record in pool.ns_records:
self.create_pool_attribute(context, pool.id, nameserver) self.create_pool_ns_record(context, pool.id, ns_record)
else: else:
pool.nameservers = objects.NameServerList() pool.ns_records = objects.PoolNsRecordList()
pool.obj_reset_changes(['nameservers'])
pool.obj_reset_changes(['attributes', 'ns_records'])
return pool return pool
def get_pool(self, context, pool_id): def get_pool(self, context, pool_id):
pool = self._find_pools(context, {'id': pool_id}, one=True) return self._find_pools(context, {'id': pool_id}, one=True)
pool.attributes = self._find_pool_attributes(
context, {'pool_id': pool_id, 'key': '!name_server'})
pool.nameservers = self._find_pool_attributes(
context, {'pool_id': pool_id, 'key': 'name_server'})
pool.obj_reset_changes(['attributes', 'nameservers'])
return pool
def find_pools(self, context, criterion=None, marker=None, def find_pools(self, context, criterion=None, marker=None,
limit=None, sort_key=None, sort_dir=None): limit=None, sort_key=None, sort_dir=None):
pools = self._find_pools(context, criterion, marker=marker, return self._find_pools(context, criterion, marker=marker,
limit=limit, sort_key=sort_key, limit=limit, sort_key=sort_key,
sort_dir=sort_dir) sort_dir=sort_dir)
for pool in pools:
pool.attributes = self._find_pool_attributes(
context, {'pool_id': pool.id, 'key': '!name_server'})
pool.nameservers = self._find_pool_attributes(
context, {'pool_id': pool.id, 'key': 'name_server'})
pool.obj_reset_changes(['attributes', 'nameservers'])
return pools
def find_pool(self, context, criterion): def find_pool(self, context, criterion):
pool = self._find_pools(context, criterion, one=True) return self._find_pools(context, criterion, one=True)
pool.attributes = self._find_pool_attributes(
context, {'pool_id': pool.id, 'key': '!name_server'})
pool.nameservers = self._find_pool_attributes(
context, {'pool_id': pool.id, 'key': 'name_server'})
pool.obj_reset_changes(['attributes', 'nameservers'])
return pool
def update_pool(self, context, pool): def update_pool(self, context, pool):
pool = self._update(context, tables.pools, pool, pool = self._update(context, tables.pools, pool,
exceptions.DuplicatePool, exceptions.PoolNotFound, exceptions.DuplicatePool, exceptions.PoolNotFound,
['attributes', 'nameservers']) ['attributes', 'ns_records'])
if pool.obj_attr_is_set('attributes') or \
pool.obj_attr_is_set('nameservers'): # TODO(kiall): These two sections below are near identical, we should
# refactor into a single reusable method.
if pool.obj_attr_is_set('attributes'):
# Gather the pool ID's we have # Gather the pool ID's we have
have_attributes = set([r.id for r in self._find_pool_attributes( have_attributes = set([r.id for r in self._find_pool_attributes(
context, {'pool_id': pool.id})]) context, {'pool_id': pool.id})])
@ -784,9 +783,6 @@ class SQLAlchemyStorage(sqlalchemy_base.SQLAlchemy, storage_base.Storage):
if pool.obj_attr_is_set('attributes'): if pool.obj_attr_is_set('attributes'):
for r in pool.attributes.objects: for r in pool.attributes.objects:
attributes.append(r) attributes.append(r)
if pool.obj_attr_is_set('nameservers'):
for r in pool.nameservers.objects:
attributes.append(r)
# Determine what to change # Determine what to change
for attribute in attributes: for attribute in attributes:
@ -801,7 +797,7 @@ class SQLAlchemyStorage(sqlalchemy_base.SQLAlchemy, storage_base.Storage):
# NOTE: Since we're dealing with mutable objects, the return value # NOTE: Since we're dealing with mutable objects, the return value
# of create/update/delete attribute is not needed. The # of create/update/delete attribute is not needed. The
# original item will be mutated in place on the input # original item will be mutated in place on the input
# "pool.attributes" or "pool.nameservers" list. # "pool.attributes" list.
# Delete attributes # Delete attributes
for attribute_id in have_attributes - keep_attributes: for attribute_id in have_attributes - keep_attributes:
@ -816,7 +812,50 @@ class SQLAlchemyStorage(sqlalchemy_base.SQLAlchemy, storage_base.Storage):
self.create_pool_attribute( self.create_pool_attribute(
context, pool.id, attribute) context, pool.id, attribute)
# Call get_pool to get the ids of all the attributes/nameservers if pool.obj_attr_is_set('ns_records'):
# Gather the pool ID's we have
have_ns_records = set([r.id for r in self._find_pool_ns_records(
context, {'pool_id': pool.id})])
# Prep some lists of changes
keep_ns_records = set([])
create_ns_records = []
update_ns_records = []
ns_records = []
if pool.obj_attr_is_set('ns_records'):
for r in pool.ns_records.objects:
ns_records.append(r)
# Determine what to change
for ns_record in ns_records:
keep_ns_records.add(ns_record.id)
try:
ns_record.obj_get_original_value('id')
except KeyError:
create_ns_records.append(ns_record)
else:
update_ns_records.append(ns_record)
# NOTE: Since we're dealing with mutable objects, the return value
# of create/update/delete ns_record is not needed. The
# original item will be mutated in place on the input
# "pool.ns_records" list.
# Delete ns_records
for ns_record_id in have_ns_records - keep_ns_records:
self.delete_pool_ns_record(context, ns_record_id)
# Update ns_records
for ns_record in update_ns_records:
self.update_pool_ns_record(context, ns_record)
# Create ns_records
for ns_record in create_ns_records:
self.create_pool_ns_record(
context, pool.id, ns_record)
# Call get_pool to get the ids of all the attributes/ns_records
# refreshed in the pool object # refreshed in the pool object
updated_pool = self.get_pool(context, pool.id) updated_pool = self.get_pool(context, pool.id)
@ -869,6 +908,47 @@ class SQLAlchemyStorage(sqlalchemy_base.SQLAlchemy, storage_base.Storage):
return deleted_pool_attribute return deleted_pool_attribute
# Pool ns_record methods
def _find_pool_ns_records(self, context, criterion, one=False, marker=None,
limit=None, sort_key=None, sort_dir=None):
return self._find(context, tables.pool_ns_records,
objects.PoolNsRecord, objects.PoolNsRecordList,
exceptions.PoolNsRecordNotFound, criterion, one,
marker, limit, sort_key, sort_dir)
def create_pool_ns_record(self, context, pool_id, pool_ns_record):
pool_ns_record.pool_id = pool_id
return self._create(tables.pool_ns_records, pool_ns_record,
exceptions.DuplicatePoolNsRecord)
def get_pool_ns_record(self, context, pool_ns_record_id):
return self._find_pool_ns_records(
context, {'id': pool_ns_record_id}, one=True)
def find_pool_ns_records(self, context, criterion=None, marker=None,
limit=None, sort_key=None, sort_dir=None):
return self._find_pool_ns_records(context, criterion, marker=marker,
limit=limit, sort_key=sort_key,
sort_dir=sort_dir)
def find_pool_ns_record(self, context, criterion):
return self._find_pool_ns_records(context, criterion, one=True)
def update_pool_ns_record(self, context, pool_ns_record):
return self._update(context, tables.pool_ns_records, pool_ns_record,
exceptions.DuplicatePoolNsRecord,
exceptions.PoolNsRecordNotFound)
def delete_pool_ns_record(self, context, pool_ns_record_id):
pool_ns_record = self._find_pool_ns_records(
context, {'id': pool_ns_record_id}, one=True)
deleted_pool_ns_record = self._delete(
context, tables.pool_ns_records, pool_ns_record,
exceptions.PoolNsRecordNotFound)
return deleted_pool_ns_record
# Zone Transfer Methods # Zone Transfer Methods
def _find_zone_transfer_requests(self, context, criterion, one=False, def _find_zone_transfer_requests(self, context, criterion, one=False,
marker=None, limit=None, sort_key=None, marker=None, limit=None, sort_key=None,

View File

@ -0,0 +1,120 @@
# 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 sqlalchemy import Integer, String, DateTime, ForeignKeyConstraint
from sqlalchemy.schema import Table, Column, MetaData
from sqlalchemy.sql import select
from oslo.config import cfg
from designate import utils
from designate.sqlalchemy.types import UUID
meta = MetaData()
# Get the default pool_id from the config file
default_pool_id = cfg.CONF['service:central'].default_pool_id.replace('-', '')
pool_ns_records_table = Table('pool_ns_records', meta,
Column('id', UUID(), default=utils.generate_uuid, primary_key=True),
Column('created_at', DateTime()),
Column('updated_at', DateTime()),
Column('version', Integer(), default=1, nullable=False),
Column('pool_id', UUID(), nullable=False),
Column('priority', Integer(), nullable=False),
Column('hostname', String(255), nullable=False),
ForeignKeyConstraint(['pool_id'], ['pools.id'], ondelete='CASCADE'),
mysql_engine='INNODB',
mysql_charset='utf8')
def upgrade(migrate_engine):
meta.bind = migrate_engine
# Load the pool_attributes_table table schema
pool_attributes_table = Table('pool_attributes', meta, autoload=True)
# Create the pool_ns_records DB table
pool_ns_records_table.create()
# Find the existing name server entries
pool_ns_records = select(
columns=[
pool_attributes_table.c.id,
pool_attributes_table.c.key,
pool_attributes_table.c.value,
pool_attributes_table.c.created_at,
pool_attributes_table.c.updated_at,
pool_attributes_table.c.version
]
).where(pool_attributes_table.c.key == 'name_server').execute().fetchall()
# Create matching entries in the new table.
for pool_ns_record in pool_ns_records:
pool_ns_records_table.insert().execute(
id=pool_ns_record.id,
created_at=pool_ns_record.created_at,
updated_at=pool_ns_record.updated_at,
version=pool_ns_record.version,
pool_id=default_pool_id,
priority=1,
hostname=pool_ns_record.value,
)
# Delete the old nameserver attr rows from the Database
pool_attributes_table.delete()\
.where(pool_attributes_table.c.key == 'name_server')\
.execute()
def downgrade(migrate_engine):
meta.bind = migrate_engine
# Load the pool_attributes and pool_ns_records table schema
pool_attributes_table = Table('pool_attributes', meta, autoload=True)
pool_ns_records_table = Table('pool_ns_records', meta, autoload=True)
# Find the nameservers for the default_pool_id
pool_ns_records = select(
columns=[
pool_ns_records_table.c.id,
pool_ns_records_table.c.created_at,
pool_ns_records_table.c.updated_at,
pool_ns_records_table.c.version,
pool_ns_records_table.c.hostname,
]
).where(pool_attributes_table.c.pool_id == default_pool_id)\
.execute().fetchall()
# Create matching entries in the new table.
for pool_ns_record in pool_ns_records:
pool_attributes_table.insert().execute(
id=pool_ns_record.id,
created_at=pool_ns_record.created_at,
updated_at=pool_ns_record.updated_at,
version=pool_ns_record.version,
key='name_server',
value=pool_ns_record.hostname,
)
# Delete the pool_ns_records table from the DB
pool_ns_records_table.drop()

View File

@ -249,6 +249,21 @@ pool_attributes = Table('pool_attributes', metadata,
mysql_charset='utf8' mysql_charset='utf8'
) )
pool_ns_records = Table('pool_ns_records', metadata,
Column('id', UUID(), default=utils.generate_uuid, primary_key=True),
Column('created_at', DateTime, default=lambda: timeutils.utcnow()),
Column('updated_at', DateTime, onupdate=lambda: timeutils.utcnow()),
Column('version', Integer(), default=1, nullable=False),
Column('pool_id', UUID(), nullable=False),
Column('priority', Integer(), nullable=False),
Column('hostname', String(255), nullable=False),
ForeignKeyConstraint(['pool_id'], ['pools.id'], ondelete='CASCADE'),
mysql_engine='INNODB',
mysql_charset='utf8')
zone_transfer_requests = Table('zone_transfer_requests', metadata, zone_transfer_requests = Table('zone_transfer_requests', metadata,
Column('id', UUID, default=utils.generate_uuid, primary_key=True), Column('id', UUID, default=utils.generate_uuid, primary_key=True),
Column('version', Integer(), default=1, nullable=False), Column('version', Integer(), default=1, nullable=False),

View File

@ -190,24 +190,34 @@ class TestCase(base.BaseTestCase):
'pattern': 'blacklisted.org.' 'pattern': 'blacklisted.org.'
}] }]
pool_fixtures = [
{'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.'}]},
{'name': 'Pool-Two',
'description': 'Pool-Two description',
'attributes': [{'key': 'scope', 'value': 'public'}],
'ns_records': [{'priority': 0, 'hostname': 'ns1.example.org.'}]},
]
pool_attribute_fixtures = [
{'scope': 'public'},
{'scope': 'private'},
{'scope': 'unknown'}
]
pool_attributes_fixtures = [ pool_attributes_fixtures = [
{'pool_id': default_pool_id, {'pool_id': default_pool_id,
'key': 'name_server', 'key': 'continent',
'value': 'ns1.example.com.'}, 'value': 'NA'},
{'pool_id': default_pool_id, {'pool_id': default_pool_id,
'key': 'scope', 'key': 'scope',
'value': 'public'} 'value': 'public'}
] ]
pool_attribute_nameserver_fixtures = [
{'pool_id': default_pool_id,
'key': 'name_server',
'value': 'ns1.example.org'},
{'pool_id': default_pool_id,
'key': 'name_server',
'value': 'ns2.example.org'},
]
pool_manager_status_fixtures = [{ pool_manager_status_fixtures = [{
'server_id': '1d7a26e6-e604-4aa0-bbc5-d01081bf1f45', 'server_id': '1d7a26e6-e604-4aa0-bbc5-d01081bf1f45',
'status': 'SUCCESS', 'status': 'SUCCESS',
@ -220,29 +230,6 @@ class TestCase(base.BaseTestCase):
'action': 'DELETE' 'action': 'DELETE'
}] }]
pool_fixtures = [
{'name': 'Pool-One',
'description': 'Pool-One description',
'attributes': [{'key': 'scope', 'value': 'public'}],
'nameservers': [{'key': 'name_server', 'value': 'ns1.example.org.'}]},
{'name': 'Pool-Two',
'description': 'Pool-Two description',
'attributes': [{'key': 'scope', 'value': 'public'}],
'nameservers': [{'key': 'name_server', 'value': 'ns1.example.org.'}]},
]
pool_attribute_fixtures = [
{'scope': 'public'},
{'scope': 'private'},
{'scope': 'unknown'}
]
name_server_fixtures = [
['examplens1.com.', 'examplens2.com.'],
['examplens1.org.', 'examplens2.org.']
]
zone_transfers_request_fixtures = [{ zone_transfers_request_fixtures = [{
"description": "Test Transfer", "description": "Test Transfer",
}, { }, {
@ -315,6 +302,9 @@ class TestCase(base.BaseTestCase):
storage_driver = cfg.CONF['service:central'].storage_driver storage_driver = cfg.CONF['service:central'].storage_driver
self.storage = storage.get_storage(storage_driver) self.storage = storage.get_storage(storage_driver)
# Setup the Default Pool with some useful settings
self._setup_default_pool()
def _setup_pool_manager_cache(self): def _setup_pool_manager_cache(self):
self.config( self.config(
@ -340,6 +330,17 @@ class TestCase(base.BaseTestCase):
connection_debug=connection_debug, connection_debug=connection_debug,
group='pool_manager_cache:sqlalchemy') group='pool_manager_cache:sqlalchemy')
def _setup_default_pool(self):
# Fetch the default pool
pool = self.storage.get_pool(self.admin_context, default_pool_id)
# Add a NS record to it
pool.ns_records.append(
objects.PoolNsRecord(priority=0, hostname='ns1.example.org.'))
# Save the default pool
self.storage.update_pool(self.admin_context, pool)
# Config Methods # Config Methods
def config(self, **kwargs): def config(self, **kwargs):
group = kwargs.pop('group', None) group = kwargs.pop('group', None)
@ -461,27 +462,6 @@ class TestCase(base.BaseTestCase):
_values.update(values) _values.update(values)
return _values return _values
def get_pool_attributes_fixture(self, fixture=0, values=None):
values = values or {}
_values = copy.copy(self.pool_attributes_fixtures[fixture])
_values.update(values)
return _values
def get_pool_attribute_nameserver_fixtures(self, fixture=0, values=None):
values = values or {}
_values = copy.copy(self.pool_attribute_nameserver_fixtures[fixture])
_values.update(values)
return _values
def get_pool_manager_status_fixture(self, fixture=0, values=None):
values = values or {}
_values = copy.copy(self.pool_manager_status_fixtures[fixture])
_values.update(values)
return _values
def get_pool_fixture(self, fixture=0, values=None): def get_pool_fixture(self, fixture=0, values=None):
values = values or {} values = values or {}
@ -496,11 +476,20 @@ class TestCase(base.BaseTestCase):
_values.update(values) _values.update(values)
return _values return _values
def get_nameserver_fixture(self, fixture=0, values=None): def get_pool_attributes_fixture(self, fixture=0, values=None):
if values: # TODO(kiall): Remove this method, in favor of the
_values = copy.copy(values) # get_pool_attribute_fixture method above.
else: values = values or {}
_values = copy.copy(self.name_server_fixtures[fixture])
_values = copy.copy(self.pool_attributes_fixtures[fixture])
_values.update(values)
return _values
def get_pool_manager_status_fixture(self, fixture=0, values=None):
values = values or {}
_values = copy.copy(self.pool_manager_status_fixtures[fixture])
_values.update(values)
return _values return _values
def get_zone_transfer_request_fixture(self, fixture=0, values=None): def get_zone_transfer_request_fixture(self, fixture=0, values=None):
@ -517,33 +506,6 @@ class TestCase(base.BaseTestCase):
_values.update(values) _values.update(values)
return _values return _values
def create_nameserver(self, **kwargs):
context = kwargs.pop('context', self.admin_context)
fixture = kwargs.pop('fixture', 0)
values = self.get_pool_attribute_nameserver_fixtures(fixture=fixture,
values=kwargs)
nameserver = objects.PoolAttribute.from_dict(values)
# Get the default pool
pool = self.central_service.get_pool(
self.admin_context, default_pool_id)
# Add the new PoolAttribute to the pool as a nameserver
pool.nameservers.append(nameserver)
# Update the pool
self.central_service.update_pool(self.admin_context, pool)
# Get the new PoolAttribute from the db in order to return it
created_nameserver = self.storage.find_pool_attribute(
context=context,
criterion=values
)
return created_nameserver
def create_tld(self, **kwargs): def create_tld(self, **kwargs):
context = kwargs.pop('context', self.admin_context) context = kwargs.pop('context', self.admin_context)
fixture = kwargs.pop('fixture', 0) fixture = kwargs.pop('fixture', 0)
@ -583,18 +545,6 @@ class TestCase(base.BaseTestCase):
fixture = kwargs.pop('fixture', 0) fixture = kwargs.pop('fixture', 0)
domain_type = kwargs.pop('type', None) domain_type = kwargs.pop('type', None)
try:
# We always need a server to create a server
nameservers = self.storage.find_pool_attributes(
context=self.admin_context,
criterion={'pool_id': default_pool_id,
'key': 'name_server'}
)
if len(nameservers) == 0:
self.create_nameserver()
except exceptions.DuplicatePoolAttribute:
pass
values = self.get_domain_fixture(domain_type=domain_type, values = self.get_domain_fixture(domain_type=domain_type,
fixture=fixture, values=kwargs) fixture=fixture, values=kwargs)
@ -647,6 +597,21 @@ class TestCase(base.BaseTestCase):
return self.central_service.create_pool( return self.central_service.create_pool(
context, objects.Pool.from_dict(values)) context, objects.Pool.from_dict(values))
def create_pool_attribute(self, **kwargs):
# TODO(kiall): This method should require a "pool" be passed in,
# rather than hardcoding the default pool ID into the
# fixture.
context = kwargs.pop('context', self.admin_context)
fixture = kwargs.pop('fixture', 0)
values = self.get_pool_attributes_fixture(fixture=fixture,
values=kwargs)
# TODO(kiall): We shouldn't be assuming the default_pool_id here
return self.storage.create_pool_attribute(
context, default_pool_id,
objects.PoolAttribute.from_dict(values))
def create_zone_transfer_request(self, domain, **kwargs): def create_zone_transfer_request(self, domain, **kwargs):
context = kwargs.pop('context', self.admin_context) context = kwargs.pop('context', self.admin_context)
fixture = kwargs.pop('fixture', 0) fixture = kwargs.pop('fixture', 0)
@ -680,18 +645,6 @@ class TestCase(base.BaseTestCase):
return self.central_service.create_zone_transfer_accept( return self.central_service.create_zone_transfer_accept(
context, objects.ZoneTransferAccept.from_dict(values)) context, objects.ZoneTransferAccept.from_dict(values))
def create_pool_attribute(self, **kwargs):
context = kwargs.pop('context', self.admin_context)
fixture = kwargs.pop('fixture', 0)
values = self.get_pool_attributes_fixture(fixture=fixture,
values=kwargs)
# TODO(kiall): We shouldn't be assuming the default_pool_id here
return self.storage.create_pool_attribute(
context, default_pool_id,
objects.PoolAttribute.from_dict(values))
def _ensure_interface(self, interface, implementation): def _ensure_interface(self, interface, implementation):
for name in interface.__abstractmethods__: for name in interface.__abstractmethods__:
in_arginfo = inspect.getargspec(getattr(interface, name)) in_arginfo = inspect.getargspec(getattr(interface, name))

View File

@ -31,9 +31,6 @@ LOG = logging.getLogger(__name__)
class ApiV1DomainsTest(ApiV1Test): class ApiV1DomainsTest(ApiV1Test):
def test_create_domain(self): def test_create_domain(self):
# Create a server
self.create_nameserver()
# Create a domain # Create a domain
fixture = self.get_domain_fixture(0) fixture = self.get_domain_fixture(0)
@ -47,9 +44,6 @@ class ApiV1DomainsTest(ApiV1Test):
self.assertEqual(response.json['name'], fixture['name']) self.assertEqual(response.json['name'], fixture['name'])
def test_create_domain_junk(self): def test_create_domain_junk(self):
# Create a server
self.create_nameserver()
# Create a domain # Create a domain
fixture = self.get_domain_fixture(0) fixture = self.get_domain_fixture(0)
@ -59,15 +53,6 @@ class ApiV1DomainsTest(ApiV1Test):
# Ensure it fails with a 400 # Ensure it fails with a 400
self.post('domains', data=fixture, status_code=400) self.post('domains', data=fixture, status_code=400)
def test_create_domain_no_servers(self):
# Create a domain
fixture = self.get_domain_fixture(0)
# V1 doesn't have these
del fixture['type']
self.post('domains', data=fixture, status_code=500)
@patch.object(central_service.Service, 'create_domain', @patch.object(central_service.Service, 'create_domain',
side_effect=messaging.MessagingTimeout()) side_effect=messaging.MessagingTimeout())
def test_create_domain_timeout(self, _): def test_create_domain_timeout(self, _):
@ -109,9 +94,6 @@ class ApiV1DomainsTest(ApiV1Test):
self.post('domains', data=fixture, status_code=400) self.post('domains', data=fixture, status_code=400)
def test_create_domain_utf_description(self): def test_create_domain_utf_description(self):
# Create a nameserver
self.create_nameserver()
# Create a domain # Create a domain
fixture = self.get_domain_fixture(0) fixture = self.get_domain_fixture(0)
@ -125,9 +107,6 @@ class ApiV1DomainsTest(ApiV1Test):
self.post('domains', data=fixture) self.post('domains', data=fixture)
def test_create_domain_description_too_long(self): def test_create_domain_description_too_long(self):
# Create a server
self.create_nameserver()
# Create a domain # Create a domain
fixture = self.get_domain_fixture(0) fixture = self.get_domain_fixture(0)
fixture['description'] = "x" * 161 fixture['description'] = "x" * 161
@ -136,12 +115,11 @@ class ApiV1DomainsTest(ApiV1Test):
self.post('domains', data=fixture, status_code=400) self.post('domains', data=fixture, status_code=400)
def test_create_domain_with_unwanted_attributes(self): def test_create_domain_with_unwanted_attributes(self):
# Create a server
domain_id = "2d1d1d1d-1324-4a80-aa32-1f69a91bf2c8" domain_id = "2d1d1d1d-1324-4a80-aa32-1f69a91bf2c8"
created_at = datetime.datetime(2014, 6, 22, 21, 50, 0) created_at = datetime.datetime(2014, 6, 22, 21, 50, 0)
updated_at = datetime.datetime(2014, 6, 22, 21, 50, 0) updated_at = datetime.datetime(2014, 6, 22, 21, 50, 0)
serial = 1234567 serial = 1234567
self.create_nameserver()
# Create a domain # Create a domain
fixture = self.get_domain_fixture(0) fixture = self.get_domain_fixture(0)

View File

@ -15,6 +15,7 @@
# under the License. # under the License.
from mock import patch from mock import patch
from oslo import messaging from oslo import messaging
from oslo_config import cfg
from oslo_log import log as logging from oslo_log import log as logging
from designate import exceptions from designate import exceptions
@ -22,6 +23,10 @@ from designate import objects
from designate.central import service as central_service from designate.central import service as central_service
from designate.tests.test_api.test_v1 import ApiV1Test from designate.tests.test_api.test_v1 import ApiV1Test
cfg.CONF.import_opt('default_pool_id',
'designate.central',
group='service:central')
default_pool_id = cfg.CONF['service:central'].default_pool_id
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -82,26 +87,39 @@ class ApiV1ServersTest(ApiV1Test):
self.post('servers', data=fixture, status_code=409) self.post('servers', data=fixture, status_code=409)
def test_get_servers(self): def test_get_servers(self):
# Fetch the default pool
pool = self.storage.get_pool(self.admin_context, default_pool_id)
# Fetch the list of servers
response = self.get('servers') response = self.get('servers')
self.assertIn('servers', response.json) self.assertIn('servers', response.json)
self.assertEqual(0, len(response.json['servers'])) self.assertEqual(len(pool.ns_records), len(response.json['servers']))
# Create the nameserver # Add a new NS record to the pool
self.create_nameserver() pool.ns_records.append(
objects.PoolNsRecord(priority=0, hostname='new-ns1.example.org.'))
# Save the pool to add a new nameserver
self.storage.update_pool(self.admin_context, pool)
# Fetch the list of servers
response = self.get('servers')
self.assertIn('servers', response.json)
self.assertEqual(len(pool.ns_records), len(response.json['servers']))
# Add a new NS record to the pool
pool.ns_records.append(
objects.PoolNsRecord(priority=0, hostname='new-ns2.example.org.'))
# Save the pool to add a new nameserver
self.storage.update_pool(self.admin_context, pool)
response = self.get('servers') response = self.get('servers')
self.assertIn('servers', response.json) self.assertIn('servers', response.json)
self.assertEqual(1, len(response.json['servers'])) self.assertEqual(len(pool.ns_records), len(response.json['servers']))
# Create a second server
self.create_nameserver(fixture=1)
response = self.get('servers')
self.assertIn('servers', response.json)
self.assertEqual(2, len(response.json['servers']))
@patch.object(central_service.Service, 'get_pool', @patch.object(central_service.Service, 'get_pool',
side_effect=messaging.MessagingTimeout()) side_effect=messaging.MessagingTimeout())
@ -109,29 +127,22 @@ class ApiV1ServersTest(ApiV1Test):
self.get('servers', status_code=504) self.get('servers', status_code=504)
def test_get_server(self): def test_get_server(self):
# Create a server # Fetch the default pool
nameserver = self.create_nameserver() pool = self.storage.get_pool(self.admin_context, default_pool_id)
response = self.get('servers/%s' % nameserver['id']) # Fetch the Server from the pool
response = self.get('servers/%s' % pool.ns_records[0].id)
self.assertIn('id', response.json) self.assertIn('id', response.json)
self.assertEqual(response.json['id'], nameserver['id']) self.assertEqual(response.json['id'], pool.ns_records[0]['id'])
@patch.object(central_service.Service, 'get_pool', @patch.object(central_service.Service, 'get_pool',
side_effect=messaging.MessagingTimeout()) side_effect=messaging.MessagingTimeout())
def test_get_server_timeout(self, _): def test_get_server_timeout(self, _):
# # Create a server # Fetch the default pool
# nameserver = self.create_nameserver() pool = self.storage.get_pool(self.admin_context, default_pool_id)
fixture = self.get_server_fixture(0) self.get('servers/%s' % pool.ns_records[0].id, status_code=504)
values = {
'key': 'name_server',
'value': fixture['name'],
'id': '2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980'
}
nameserver = objects.PoolAttribute.from_dict(values)
self.get('servers/%s' % nameserver['id'], status_code=504)
def test_get_server_with_invalid_id(self): def test_get_server_with_invalid_id(self):
self.get('servers/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff98GH', self.get('servers/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff98GH',
@ -142,18 +153,19 @@ class ApiV1ServersTest(ApiV1Test):
status_code=404) status_code=404)
def test_update_server(self): def test_update_server(self):
# Create a server # Fetch the default pool
server = self.create_nameserver() pool = self.storage.get_pool(self.admin_context, default_pool_id)
data = {'name': 'test.example.org.'} data = {'name': 'new-ns1.example.org.'}
response = self.put('servers/%s' % server['id'], data=data) response = self.put('servers/%s' % pool.ns_records[0].id,
data=data)
self.assertIn('id', response.json) self.assertIn('id', response.json)
self.assertEqual(response.json['id'], server['id']) self.assertEqual(response.json['id'], pool.ns_records[0].id)
self.assertIn('name', response.json) self.assertIn('name', response.json)
self.assertEqual(response.json['name'], 'test.example.org.') self.assertEqual(response.json['name'], 'new-ns1.example.org.')
def test_update_server_missing(self): def test_update_server_missing(self):
data = {'name': 'test.example.org.'} data = {'name': 'test.example.org.'}
@ -161,29 +173,35 @@ class ApiV1ServersTest(ApiV1Test):
status_code=404) status_code=404)
def test_update_server_junk(self): def test_update_server_junk(self):
# Create a server # Fetch the default pool
server = self.create_nameserver() pool = self.storage.get_pool(self.admin_context, default_pool_id)
data = {'name': 'test.example.org.', 'junk': 'Junk Field'} data = {'name': 'test.example.org.', 'junk': 'Junk Field'}
self.put('servers/%s' % server['id'], data=data, status_code=400) self.put('servers/%s' % pool.ns_records[0].id, data=data,
status_code=400)
def test_delete_server(self): def test_delete_server(self):
# Create a server # Fetch the default pool
server = self.create_nameserver() pool = self.storage.get_pool(self.admin_context, default_pool_id)
# Create a second server so that we can delete the first # Create a second server so that we can delete the first
# because the last remaining server is not allowed to be deleted # because the last remaining server is not allowed to be deleted
server2 = self.create_nameserver(fixture=1) # Add a new NS record to the pool
pool.ns_records.append(
objects.PoolNsRecord(priority=0, hostname='new-ns2.example.org.'))
# Save the pool to add a new nameserver
self.storage.update_pool(self.admin_context, pool)
# Now delete the server # Now delete the server
self.delete('servers/%s' % server['id']) self.delete('servers/%s' % pool.ns_records[1].id)
# Ensure we can no longer fetch the deleted server # Ensure we can no longer fetch the deleted server
self.get('servers/%s' % server['id'], status_code=404) self.get('servers/%s' % pool.ns_records[1].id, status_code=404)
# Also, verify we cannot delete last remaining server # Also, verify we cannot delete last remaining server
self.delete('servers/%s' % server2['id'], status_code=400) self.delete('servers/%s' % pool.ns_records[0].id, status_code=400)
def test_delete_server_with_invalid_id(self): def test_delete_server_with_invalid_id(self):
self.delete('servers/9fdadfb1-cf96-4259-ac6b-bb7b6d2ff98GH', self.delete('servers/9fdadfb1-cf96-4259-ac6b-bb7b6d2ff98GH',

View File

@ -44,8 +44,6 @@ class ApiV2ReverseFloatingIPTest(ApiV2TestCase):
self.assertEqual(None, fip_record['ptrdname']) self.assertEqual(None, fip_record['ptrdname'])
def test_get_floatingip_with_record(self): def test_get_floatingip_with_record(self):
self.create_nameserver()
fixture = self.get_ptr_fixture() fixture = self.get_ptr_fixture()
context = self.get_context(tenant='a') context = self.get_context(tenant='a')
@ -110,8 +108,6 @@ class ApiV2ReverseFloatingIPTest(ApiV2TestCase):
self.assertEqual(None, fip_record['description']) self.assertEqual(None, fip_record['description'])
def test_list_floatingip_with_record(self): def test_list_floatingip_with_record(self):
self.create_nameserver()
fixture = self.get_ptr_fixture() fixture = self.get_ptr_fixture()
context = self.get_context(tenant='a') context = self.get_context(tenant='a')
@ -138,7 +134,6 @@ class ApiV2ReverseFloatingIPTest(ApiV2TestCase):
self.assertEqual(fixture['ptrdname'], fip_record['ptrdname']) self.assertEqual(fixture['ptrdname'], fip_record['ptrdname'])
def test_set_floatingip(self): def test_set_floatingip(self):
self.create_nameserver()
fixture = self.get_ptr_fixture() fixture = self.get_ptr_fixture()
fip = self.network_api.fake.allocate_floatingip('tenant') fip = self.network_api.fake.allocate_floatingip('tenant')
@ -184,8 +179,6 @@ class ApiV2ReverseFloatingIPTest(ApiV2TestCase):
url, {}) url, {})
def test_unset_floatingip(self): def test_unset_floatingip(self):
self.create_nameserver()
fixture = self.get_ptr_fixture() fixture = self.get_ptr_fixture()
context = self.get_context(tenant='a') context = self.get_context(tenant='a')
elevated_context = context.elevated() elevated_context = context.elevated()
@ -229,8 +222,6 @@ class ApiV2ReverseFloatingIPTest(ApiV2TestCase):
self.assertEqual(None, fip['ptrdname']) self.assertEqual(None, fip['ptrdname'])
def test_unset_floatingip_not_allocated(self): def test_unset_floatingip_not_allocated(self):
self.create_nameserver()
fixture = self.get_ptr_fixture() fixture = self.get_ptr_fixture()
context = self.get_context(tenant='a') context = self.get_context(tenant='a')

View File

@ -1,73 +0,0 @@
# Copyright 2013 Hewlett-Packard Development Company, L.P.
#
# Author: Kiall Mac Innes <kiall@managedit.ie>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from mock import patch
from oslo import messaging
from oslo_log import log as logging
from designate.central import service as central_service
from designate.tests.test_api.test_v2 import ApiV2TestCase
LOG = logging.getLogger(__name__)
class ApiV2NameServersTest(ApiV2TestCase):
def setUp(self):
super(ApiV2NameServersTest, self).setUp()
# Create a domain
self.domain = self.create_domain()
def test_get_nameservers(self):
url = '/zones/%s/nameservers' % self.domain['id']
response = self.client.get(url)
# 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('nameservers', response.json)
self.assertIn('links', response.json)
self.assertIn('self', response.json['links'])
# We should start with 0 nameservers
self.assertEqual(1, len(response.json['nameservers']))
servers = self.central_service.get_domain_servers(
self.admin_context, self.domain['id'])
self.assertEqual(servers[0]['id'],
response.json['nameservers'][0]['id'])
self.assertEqual(servers[0]['value'],
response.json['nameservers'][0]['name'])
self.create_nameserver(value='nsx.mydomain.com.')
response = self.client.get(url)
self.assertEqual(2, len(response.json['nameservers']))
def test_get_nameservers_invalid_id(self):
self._assert_invalid_uuid(self.client.get, '/zones/%s/nameservers')
@patch.object(central_service.Service, 'get_domain_servers',
side_effect=messaging.MessagingTimeout())
def test_get_nameservers_timeout(self, _):
url = '/zones/ba751950-6193-11e3-949a-0800200c9a66/nameservers'
self._assert_exception('timeout', 504, self.client.get, url)

View File

@ -20,6 +20,14 @@ from designate.tests.test_api.test_v2 import ApiV2TestCase
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
def _attributes_to_api(attributes):
result = {}
for attribute in attributes:
result[attribute['key']] = attribute['value']
return result
class ApiV2PoolsTest(ApiV2TestCase): class ApiV2PoolsTest(ApiV2TestCase):
def setUp(self): def setUp(self):
super(ApiV2PoolsTest, self).setUp() super(ApiV2PoolsTest, self).setUp()
@ -31,8 +39,8 @@ class ApiV2PoolsTest(ApiV2TestCase):
def test_create_pool(self): def test_create_pool(self):
# Prepare a Pool fixture # Prepare a Pool fixture
fixture = self.get_pool_fixture(fixture=0) fixture = self.get_pool_fixture(fixture=0)
fixture['attributes'] = self.get_pool_attribute_fixture(fixture=0) fixture['attributes'] = _attributes_to_api(fixture['attributes'])
fixture['nameservers'] = self.get_nameserver_fixture(fixture=0)
response = self.client.post_json( response = self.client.post_json(
'/pools', {'pool': fixture}) '/pools', {'pool': fixture})
@ -55,16 +63,18 @@ class ApiV2PoolsTest(ApiV2TestCase):
self.assertEqual( self.assertEqual(
response.json['pool']['attributes'], fixture['attributes']) response.json['pool']['attributes'], fixture['attributes'])
self.assertEqual( self.assertEqual(
response.json['pool']['nameservers'], fixture['nameservers']) response.json['pool']['ns_records'], fixture['ns_records'])
def test_create_pool_validation(self): def test_create_pool_validation(self):
# NOTE: The schemas should be tested separatly to the API. So we # NOTE: The schemas should be tested separatly to the API. So we
# don't need to test every variation via the API itself. # don't need to test every variation via the API itself.
# Fetch a fixture # Fetch a fixture
fixture = self.get_pool_fixture(fixture=0) fixture = self.get_pool_fixture(fixture=0)
# Set an invalid scope # Set an invalid scope
fixture['attributes'] = self.get_pool_attribute_fixture(fixture=2) fixture['attributes'] = {
fixture['nameservers'] = self.get_nameserver_fixture(fixture=0) 'scope': 'INVALID'
}
body = {'pool': fixture} body = {'pool': fixture}
# Ensure it fails with a 400 # Ensure it fails with a 400
@ -88,8 +98,8 @@ class ApiV2PoolsTest(ApiV2TestCase):
def test_create_pool_duplicate(self): def test_create_pool_duplicate(self):
# Prepare a Pool fixture # Prepare a Pool fixture
fixture = self.get_pool_fixture(fixture=0) fixture = self.get_pool_fixture(fixture=0)
fixture['attributes'] = self.get_pool_attribute_fixture(fixture=0) fixture['attributes'] = _attributes_to_api(fixture['attributes'])
fixture['nameservers'] = self.get_nameserver_fixture(fixture=0)
body = {'pool': fixture} body = {'pool': fixture}
response = self.client.post_json('/pools', body) response = self.client.post_json('/pools', body)
@ -158,10 +168,11 @@ class ApiV2PoolsTest(ApiV2TestCase):
attribute['value'], attribute['value'],
response.json['pool']['attributes'][attribute['key']]) response.json['pool']['attributes'][attribute['key']])
self.assertEqual(len(pool['nameservers']), self.assertEqual(len(pool['ns_records']),
len(response.json['pool']['nameservers'])) len(response.json['pool']['ns_records']))
self.assertEqual([r.value for r in pool['nameservers']], self.assertEqual(
response.json['pool']['nameservers']) [n.hostname for n in pool['ns_records']],
[n['hostname'] for n in response.json['pool']['ns_records']])
def test_update_pool(self): def test_update_pool(self):
# Create a pool # Create a pool
@ -196,17 +207,21 @@ class ApiV2PoolsTest(ApiV2TestCase):
attribute['value'], attribute['value'],
response.json['pool']['attributes'][attribute['key']]) response.json['pool']['attributes'][attribute['key']])
self.assertEqual(len(pool['nameservers']), self.assertEqual(len(pool['ns_records']),
len(response.json['pool']['nameservers'])) len(response.json['pool']['ns_records']))
self.assertEqual([r.value for r in pool['nameservers']], self.assertEqual(
response.json['pool']['nameservers']) [n.hostname for n in pool['ns_records']],
[n['hostname'] for n in response.json['pool']['ns_records']])
def test_update_pool_nameservers(self): def test_update_pool_ns_records(self):
# Create a pool # Create a pool
pool = self.create_pool() pool = self.create_pool()
# Prepare an update body # Prepare an update body
body = {'pool': {'nameservers': ['newns1.com.', 'newns2.com.']}} body = {'pool': {'ns_records': [
{'priority': 1, 'hostname': 'new-ns1.example.org.'},
{'priority': 2, 'hostname': 'new-ns2.example.org.'},
]}}
url = '/pools/%s' % pool['id'] url = '/pools/%s' % pool['id']
response = self.client.patch_json(url, body, status=200) response = self.client.patch_json(url, body, status=200)
@ -217,26 +232,14 @@ class ApiV2PoolsTest(ApiV2TestCase):
# Check the body structure is what we expect # Check the body structure is what we expect
self.assertIn('pool', response.json) self.assertIn('pool', response.json)
self.assertIn('id', response.json['pool'])
self.assertIn('links', response.json['pool']) self.assertIn('links', response.json['pool'])
self.assertIn('self', response.json['pool']['links'])
self.assertEqual(2, len(response.json['pool']['nameservers']))
self.assertEqual(['newns1.com.', 'newns2.com.'],
response.json['pool']['nameservers'])
# Check the values returned are what we expect # Check the values returned are what we expect
self.assertIn('id', response.json['pool']) self.assertEqual(2, len(response.json['pool']['ns_records']))
self.assertIsNotNone(response.json['pool']['updated_at']) self.assertEqual(['new-ns1.example.org.', 'new-ns2.example.org.'],
[n['hostname'] for n in
# Check the rest of the values are unchanged response.json['pool']['ns_records']])
self.assertEqual(pool['name'], response.json['pool']['name'])
self.assertEqual(
pool['description'], response.json['pool']['description'])
self.assertEqual(len(pool['attributes']),
len(response.json['pool']['attributes']))
for attribute in pool['attributes']:
self.assertEqual(
attribute['value'],
response.json['pool']['attributes'][attribute['key']])
def test_update_pool_attributes(self): def test_update_pool_attributes(self):
# Create a pool # Create a pool
@ -252,27 +255,11 @@ class ApiV2PoolsTest(ApiV2TestCase):
self.assertEqual(200, response.status_int) self.assertEqual(200, response.status_int)
self.assertEqual('application/json', response.content_type) 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'])
# Check the values returned are what we expect # Check the values returned are what we expect
self.assertIn('id', response.json['pool'])
self.assertIsNotNone(response.json['pool']['updated_at'])
self.assertEqual(1, len(response.json['pool']['attributes'])) self.assertEqual(1, len(response.json['pool']['attributes']))
self.assertEqual('private', self.assertEqual('private',
response.json['pool']['attributes']['scope']) response.json['pool']['attributes']['scope'])
# Check the rest of the values are unchanged
self.assertEqual(pool['name'], response.json['pool']['name'])
self.assertEqual(
pool['description'], response.json['pool']['description'])
self.assertEqual(len(pool['nameservers']),
len(response.json['pool']['nameservers']))
self.assertEqual([r.value for r in pool['nameservers']],
response.json['pool']['nameservers'])
def test_delete_pool(self): def test_delete_pool(self):
pool = self.create_pool() pool = self.create_pool()
url = '/pools/%s' % pool['id'] url = '/pools/%s' % pool['id']

View File

@ -31,9 +31,6 @@ class ApiV2ZonesTest(ApiV2TestCase):
def setUp(self): def setUp(self):
super(ApiV2ZonesTest, self).setUp() super(ApiV2ZonesTest, self).setUp()
# Create a server
self.create_nameserver()
# Create the default TLDs # Create the default TLDs
self.create_default_tlds() self.create_default_tlds()

View File

@ -408,9 +408,6 @@ class CentralServiceTest(CentralTestCase):
# Domain Tests # Domain Tests
@mock.patch.object(notifier.Notifier, "info") @mock.patch.object(notifier.Notifier, "info")
def _test_create_domain(self, values, mock_notifier): def _test_create_domain(self, values, mock_notifier):
# Create a server
self.create_nameserver()
# Reset the mock to avoid the calls from the create_nameserver() call # Reset the mock to avoid the calls from the create_nameserver() call
mock_notifier.reset_mock() mock_notifier.reset_mock()
@ -426,6 +423,20 @@ class CentralServiceTest(CentralTestCase):
self.assertEqual(mock_notifier.call_count, 1) self.assertEqual(mock_notifier.call_count, 1)
# Ensure the correct NS Records are in place
pool = self.central_service.get_pool(
self.admin_context, domain.pool_id)
ns_recordset = self.central_service.find_recordset(
self.admin_context,
criterion={'domain_id': domain.id, 'type': "NS"})
self.assertIsNotNone(ns_recordset.id)
self.assertEqual(ns_recordset.type, 'NS')
self.assertIsNotNone(ns_recordset.records)
self.assertEqual(set([n.hostname for n in pool.ns_records]),
set([n.data for n in ns_recordset.records]))
mock_notifier.assert_called_once_with( mock_notifier.assert_called_once_with(
self.admin_context, 'dns.domain.create', domain) self.admin_context, 'dns.domain.create', domain)
@ -567,9 +578,6 @@ class CentralServiceTest(CentralTestCase):
email='info@blacklisted.com' email='info@blacklisted.com'
) )
# Create a server
self.create_nameserver()
# Create a zone that is blacklisted # Create a zone that is blacklisted
domain = self.central_service.create_domain( domain = self.central_service.create_domain(
self.admin_context, objects.Domain.from_dict(values)) self.admin_context, objects.Domain.from_dict(values))
@ -603,9 +611,6 @@ class CentralServiceTest(CentralTestCase):
self.admin_context, objects.Domain.from_dict(values)) self.admin_context, objects.Domain.from_dict(values))
def test_create_domain_invalid_tld_fail(self): def test_create_domain_invalid_tld_fail(self):
# Create a server
self.create_nameserver()
# add a tld for com # add a tld for com
self.create_tld(fixture=0) self.create_tld(fixture=0)
@ -638,9 +643,6 @@ class CentralServiceTest(CentralTestCase):
values = self.get_domain_fixture(fixture=1) values = self.get_domain_fixture(fixture=1)
values['ttl'] = 0 values['ttl'] = 0
# Create a server
self.create_nameserver()
with testtools.ExpectedException(exceptions.InvalidTTL): with testtools.ExpectedException(exceptions.InvalidTTL):
self.central_service.create_domain( self.central_service.create_domain(
context, objects.Domain.from_dict(values)) context, objects.Domain.from_dict(values))
@ -652,9 +654,6 @@ class CentralServiceTest(CentralTestCase):
values = self.get_domain_fixture(fixture=1) values = self.get_domain_fixture(fixture=1)
values['ttl'] = -100 values['ttl'] = -100
# Create a server
self.create_nameserver()
# Create domain with random TTL # Create domain with random TTL
domain = self.central_service.create_domain( domain = self.central_service.create_domain(
self.admin_context, objects.Domain.from_dict(values)) self.admin_context, objects.Domain.from_dict(values))
@ -1886,8 +1885,6 @@ class CentralServiceTest(CentralTestCase):
self.central_service.count_records(self.get_context()) self.central_service.count_records(self.get_context())
def test_get_floatingip_no_record(self): def test_get_floatingip_no_record(self):
self.create_nameserver()
context = self.get_context(tenant='a') context = self.get_context(tenant='a')
fip = self.network_api.fake.allocate_floatingip(context.tenant) fip = self.network_api.fake.allocate_floatingip(context.tenant)
@ -1901,8 +1898,6 @@ class CentralServiceTest(CentralTestCase):
self.assertEqual(None, fip_ptr['ptrdname']) self.assertEqual(None, fip_ptr['ptrdname'])
def test_get_floatingip_with_record(self): def test_get_floatingip_with_record(self):
self.create_nameserver()
context = self.get_context(tenant='a') context = self.get_context(tenant='a')
fixture = self.get_ptr_fixture() fixture = self.get_ptr_fixture()
@ -1929,8 +1924,6 @@ class CentralServiceTest(CentralTestCase):
context, fip['region'], fip['id']) context, fip['region'], fip['id'])
def test_get_floatingip_deallocated_and_invalidate(self): def test_get_floatingip_deallocated_and_invalidate(self):
self.create_nameserver()
context_a = self.get_context(tenant='a') context_a = self.get_context(tenant='a')
elevated_a = context_a.elevated() elevated_a = context_a.elevated()
elevated_a.all_tenants = True elevated_a.all_tenants = True
@ -2008,8 +2001,6 @@ class CentralServiceTest(CentralTestCase):
self.assertEqual(None, fips[0]['description']) self.assertEqual(None, fips[0]['description'])
def test_list_floatingips_with_record(self): def test_list_floatingips_with_record(self):
self.create_nameserver()
context = self.get_context(tenant='a') context = self.get_context(tenant='a')
fixture = self.get_ptr_fixture() fixture = self.get_ptr_fixture()
@ -2029,8 +2020,6 @@ class CentralServiceTest(CentralTestCase):
self.assertEqual(fip_ptr['description'], fips[0]['description']) self.assertEqual(fip_ptr['description'], fips[0]['description'])
def test_list_floatingips_deallocated_and_invalidate(self): def test_list_floatingips_deallocated_and_invalidate(self):
self.create_nameserver()
context_a = self.get_context(tenant='a') context_a = self.get_context(tenant='a')
elevated_a = context_a.elevated() elevated_a = context_a.elevated()
elevated_a.all_tenants = True elevated_a.all_tenants = True
@ -2086,8 +2075,6 @@ class CentralServiceTest(CentralTestCase):
self.central_service.find_record(elevated_a, criterion) self.central_service.find_record(elevated_a, criterion)
def test_set_floatingip(self): def test_set_floatingip(self):
self.create_nameserver()
context = self.get_context(tenant='a') context = self.get_context(tenant='a')
fixture = self.get_ptr_fixture() fixture = self.get_ptr_fixture()
@ -2103,8 +2090,6 @@ class CentralServiceTest(CentralTestCase):
self.assertIsNotNone(fip_ptr['ttl']) self.assertIsNotNone(fip_ptr['ttl'])
def test_set_floatingip_removes_old_record(self): def test_set_floatingip_removes_old_record(self):
self.create_nameserver()
context_a = self.get_context(tenant='a') context_a = self.get_context(tenant='a')
elevated_a = context_a.elevated() elevated_a = context_a.elevated()
elevated_a.all_tenants = True elevated_a.all_tenants = True
@ -2176,8 +2161,6 @@ class CentralServiceTest(CentralTestCase):
context, fip['region'], fip['id'], fixture) context, fip['region'], fip['id'], fixture)
def test_unset_floatingip(self): def test_unset_floatingip(self):
self.create_nameserver()
context = self.get_context(tenant='a') context = self.get_context(tenant='a')
fixture = self.get_ptr_fixture() fixture = self.get_ptr_fixture()
@ -2349,68 +2332,6 @@ class CentralServiceTest(CentralTestCase):
self.assertEqual(int(soa_record_values[2]), updated_zone['serial']) self.assertEqual(int(soa_record_values[2]), updated_zone['serial'])
# NS Recordset tests
def test_create_ns(self):
# Anytime a zone is created, an NS recordset should be
# automatically be created, with a record for each server
# Create a nameserver
nameserver = self.create_nameserver(value='ns1.example.org.')
# Create a zone
zone = self.create_domain(name='example3.org.')
# Make sure an NS recordset was created
ns = self.central_service.find_recordset(self.admin_context,
criterion={'domain_id': zone['id'],
'type': "NS"})
# Ensure all values have been set correctly
self.assertIsNotNone(ns.id)
self.assertEqual('NS', ns.type)
self.assertIsNotNone(ns.records)
self.assertEqual(ns.records[0].data, nameserver.value)
def test_add_ns(self):
# Anytime a server is created, the NS recordset for each zone
# should be automatically updated to contain the new server
# Create a server
nameserver1 = self.create_nameserver(value='ns1.example.net.')
# Create a zone
zone = self.create_domain(name='example3.net.')
original_serial = zone.serial
# Make sure an NS recordset was created
ns_rs = self.central_service.find_recordset(
self.admin_context,
criterion={'domain_id': zone['id'], 'type': "NS"})
# Ensure all values have been set correctly
self.assertIsNotNone(ns_rs.id)
self.assertEqual('NS', ns_rs.type)
self.assertIsNotNone(ns_rs.records)
self.assertEqual(ns_rs.records[0].data, nameserver1.value)
# Create another server
nameserver2 = self.create_nameserver(value='ns2.example.net.')
# Get the NS recordset again
ns_rs = self.central_service.find_recordset(
self.admin_context,
criterion={'domain_id': zone['id'], 'type': "NS"})
# Get zone again to check serial number
updated_zone = self.central_service.get_domain(self.admin_context,
zone.id)
new_serial = updated_zone.serial
# Ensure another record was added to the recordset
self.assertEqual(ns_rs.records[0].data, nameserver1.value)
self.assertEqual(ns_rs.records[1].data, nameserver2.value)
self.assertThat(new_serial, GreaterThan(original_serial))
# Pool Tests # Pool Tests
def test_create_pool(self): def test_create_pool(self):
# Get the values # Get the values
@ -2426,21 +2347,21 @@ class CentralServiceTest(CentralTestCase):
self.assertIsNotNone(pool['tenant_id']) self.assertIsNotNone(pool['tenant_id'])
self.assertIsNone(pool['updated_at']) self.assertIsNone(pool['updated_at'])
self.assertIsNotNone(pool['attributes']) self.assertIsNotNone(pool['attributes'])
self.assertIsNotNone(pool['nameservers']) self.assertIsNotNone(pool['ns_records'])
self.assertEqual(pool['name'], values['name']) self.assertEqual(pool['name'], values['name'])
# Compare the actual values of attributes and nameservers # Compare the actual values of attributes and ns_records
for k in range(0, len(values['attributes'])): for k in range(0, len(values['attributes'])):
self.assertDictContainsSubset( self.assertDictContainsSubset(
values['attributes'][k], values['attributes'][k],
pool['attributes'][k].to_primitive()['designate_object.data'] pool['attributes'][k].to_primitive()['designate_object.data']
) )
for k in range(0, len(values['nameservers'])): for k in range(0, len(values['ns_records'])):
self.assertDictContainsSubset( self.assertDictContainsSubset(
values['nameservers'][k], values['ns_records'][k],
pool['nameservers'][k].to_primitive()['designate_object.data']) pool['ns_records'][k].to_primitive()['designate_object.data'])
def test_get_pool(self): def test_get_pool(self):
# Create a server pool # Create a server pool
@ -2456,17 +2377,17 @@ class CentralServiceTest(CentralTestCase):
self.assertEqual(pool['tenant_id'], expected['tenant_id']) self.assertEqual(pool['tenant_id'], expected['tenant_id'])
self.assertEqual(pool['name'], expected['name']) self.assertEqual(pool['name'], expected['name'])
# Compare the actual values of attributes and nameservers # Compare the actual values of attributes and ns_records
for k in range(0, len(expected['attributes'])): for k in range(0, len(expected['attributes'])):
self.assertEqual( self.assertEqual(
pool['attributes'][k].to_primitive()['designate_object.data'], pool['attributes'][k].to_primitive()['designate_object.data'],
expected['attributes'][k].to_primitive() expected['attributes'][k].to_primitive()
['designate_object.data']) ['designate_object.data'])
for k in range(0, len(expected['nameservers'])): for k in range(0, len(expected['ns_records'])):
self.assertEqual( self.assertEqual(
pool['nameservers'][k].to_primitive()['designate_object.data'], pool['ns_records'][k].to_primitive()['designate_object.data'],
expected['nameservers'][k].to_primitive() expected['ns_records'][k].to_primitive()
['designate_object.data']) ['designate_object.data'])
def test_find_pools(self): def test_find_pools(self):
@ -2485,18 +2406,18 @@ class CentralServiceTest(CentralTestCase):
self.assertEqual(len(pools), 2) self.assertEqual(len(pools), 2)
self.assertEqual(pools[1]['name'], values['name']) self.assertEqual(pools[1]['name'], values['name'])
# Compare the actual values of attributes and nameservers # Compare the actual values of attributes and ns_records
expected_attributes = values['attributes'][0] expected_attributes = values['attributes'][0]
actual_attributes = \ actual_attributes = \
pools[1]['attributes'][0].to_primitive()['designate_object.data'] pools[1]['attributes'][0].to_primitive()['designate_object.data']
for k in expected_attributes: for k in expected_attributes:
self.assertEqual(actual_attributes[k], expected_attributes[k]) self.assertEqual(actual_attributes[k], expected_attributes[k])
expected_nameservers = values['nameservers'][0] expected_ns_records = values['ns_records'][0]
actual_nameservers = \ actual_ns_records = \
pools[1]['nameservers'][0].to_primitive()['designate_object.data'] pools[1]['ns_records'][0].to_primitive()['designate_object.data']
for k in expected_nameservers: for k in expected_ns_records:
self.assertEqual(actual_nameservers[k], expected_nameservers[k]) self.assertEqual(actual_ns_records[k], expected_ns_records[k])
def test_find_pool(self): def test_find_pool(self):
# Create a server pool # Create a server pool
@ -2508,60 +2429,91 @@ class CentralServiceTest(CentralTestCase):
self.assertEqual(pool['name'], expected['name']) self.assertEqual(pool['name'], expected['name'])
# Compare the actual values of attributes and nameservers # Compare the actual values of attributes and ns_records
for k in range(0, len(expected['attributes'])): for k in range(0, len(expected['attributes'])):
self.assertEqual( self.assertEqual(
pool['attributes'][k].to_primitive()['designate_object.data'], pool['attributes'][k].to_primitive()['designate_object.data'],
expected['attributes'][k].to_primitive() expected['attributes'][k].to_primitive()
['designate_object.data']) ['designate_object.data'])
for k in range(0, len(expected['nameservers'])): for k in range(0, len(expected['ns_records'])):
self.assertEqual( self.assertEqual(
pool['nameservers'][k].to_primitive()['designate_object.data'], pool['ns_records'][k].to_primitive()['designate_object.data'],
expected['nameservers'][k].to_primitive() expected['ns_records'][k].to_primitive()
['designate_object.data']) ['designate_object.data'])
def test_update_pool(self): def test_update_pool(self):
# Create a server pool # Create a server pool
pool = self.create_pool(fixture=0) pool = self.create_pool(fixture=0)
# Update the pool # Update and save the pool
pool.description = 'New Comment' pool.description = 'New Comment'
attribute_values = self.get_pool_attribute_fixture(fixture=1) self.central_service.update_pool(self.admin_context, pool)
pool_attributes = pool.attributes = objects.PoolAttributeList(
objects=[objects.PoolAttribute(key=r, value=attribute_values[r])
for r in attribute_values])
nameserver_values = self.get_nameserver_fixture(fixture=1) # Fetch the pool
pool_nameservers = pool.nameservers = objects.NameServerList(
objects=[objects.NameServer(key='name_server', value=r)
for r in nameserver_values])
# Update pool
pool = self.central_service.update_pool(self.admin_context, pool)
# GET the pool
pool = self.central_service.get_pool(self.admin_context, pool.id) pool = self.central_service.get_pool(self.admin_context, pool.id)
# Verify that the pool was updated correctly # Verify that the pool was updated correctly
self.assertEqual("New Comment", pool.description) self.assertEqual("New Comment", pool.description)
# Compare the actual values of attributes and nameservers def test_update_pool_add_ns_record(self):
for k in range(0, len(pool_attributes)): # Create a server pool and domain
actual_attributes = \ pool = self.create_pool(fixture=0)
pool['attributes'][0].to_primitive()['designate_object.data'] domain = self.create_domain(pool_id=pool.id)
expected_attributes = \
pool_attributes[0].to_primitive()['designate_object.data']
self.assertDictContainsSubset(
expected_attributes, actual_attributes)
for k in range(0, len(pool_nameservers)): ns_record_count = len(pool.ns_records)
actual_nameservers = \ new_ns_record = objects.PoolNsRecord(
pool['nameservers'][k].to_primitive()['designate_object.data'] priority=10,
expected_nameservers = \ hostname='ns-new.example.org.')
pool_nameservers[k].to_primitive()['designate_object.data']
self.assertDictContainsSubset( # Update and save the pool
expected_nameservers, actual_nameservers) pool.ns_records.append(new_ns_record)
self.central_service.update_pool(self.admin_context, pool)
# Fetch the pool
pool = self.central_service.get_pool(self.admin_context, pool.id)
# Verify that the pool was updated correctly
self.assertEqual(ns_record_count + 1, len(pool.ns_records))
self.assertIn(new_ns_record.hostname,
[n.hostname for n in pool.ns_records])
# Fetch the domains NS recordset
ns_recordset = self.central_service.find_recordset(
self.admin_context,
criterion={'domain_id': domain.id, 'type': "NS"})
# Verify that the doamins NS records ware updated correctly
self.assertEqual(set([n.hostname for n in pool.ns_records]),
set([n.data for n in ns_recordset.records]))
def test_update_pool_remove_ns_record(self):
# Create a server pool and domain
pool = self.create_pool(fixture=0)
domain = self.create_domain(pool_id=pool.id)
ns_record_count = len(pool.ns_records)
# Update and save the pool
removed_ns_record = pool.ns_records.pop(-1)
self.central_service.update_pool(self.admin_context, pool)
# Fetch the pool
pool = self.central_service.get_pool(self.admin_context, pool.id)
# Verify that the pool was updated correctly
self.assertEqual(ns_record_count - 1, len(pool.ns_records))
self.assertNotIn(removed_ns_record.hostname,
[n.hostname for n in pool.ns_records])
# Fetch the domains NS recordset
ns_recordset = self.central_service.find_recordset(
self.admin_context,
criterion={'domain_id': domain.id, 'type': "NS"})
# Verify that the doamins NS records ware updated correctly
self.assertEqual(set([n.hostname for n in pool.ns_records]),
set([n.data for n in ns_recordset.records]))
def test_delete_pool(self): def test_delete_pool(self):
# Create a server pool # Create a server pool

View File

@ -2179,8 +2179,8 @@ class StorageTestCase(object):
def test_create_pool_attribute(self): def test_create_pool_attribute(self):
values = { values = {
'pool_id': "d5d10661-0312-4ae1-8664-31188a4310b7", 'pool_id': "d5d10661-0312-4ae1-8664-31188a4310b7",
'key': "name_server", 'key': "test-attribute",
'value': 'ns1.example.org.' 'value': 'test-value'
} }
result = self.storage.create_pool_attribute( result = self.storage.create_pool_attribute(