193085cf56
Change-Id: I4b9c7b277a524058408d5c551bb51531e9a3949c
842 lines
28 KiB
Python
842 lines
28 KiB
Python
# Copyright 2012 Managed I.T.
|
|
#
|
|
# Author: Kiall Mac Innes <kiall@managedit.ie>
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
import re
|
|
from designate.openstack.common import cfg
|
|
from designate.openstack.common import log as logging
|
|
from designate.openstack.common.rpc import service as rpc_service
|
|
from designate import exceptions
|
|
from designate import policy
|
|
from designate import storage
|
|
from designate import quota
|
|
from designate import utils
|
|
from designate import backend
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class Service(rpc_service.Service):
|
|
RPC_API_VERSION = '1.3'
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
backend_driver = cfg.CONF['service:central'].backend_driver
|
|
self.backend = backend.get_backend(backend_driver,
|
|
central_service=self)
|
|
|
|
kwargs.update(
|
|
host=cfg.CONF.host,
|
|
topic=cfg.CONF.central_topic,
|
|
)
|
|
|
|
policy.init_policy()
|
|
|
|
super(Service, self).__init__(*args, **kwargs)
|
|
|
|
# Get a storage connection
|
|
self.storage = storage.get_storage()
|
|
|
|
# Get a quota manager instance
|
|
self.quota = quota.get_quota()
|
|
|
|
def start(self):
|
|
self.backend.start()
|
|
|
|
super(Service, self).start()
|
|
|
|
def stop(self):
|
|
super(Service, self).stop()
|
|
|
|
self.backend.stop()
|
|
|
|
@property
|
|
def accepted_tld_list(self):
|
|
# Only iterate the list once please..
|
|
if hasattr(self, '_accepted_tld_list'):
|
|
return self._accepted_tld_list
|
|
|
|
accepted_tld_list = cfg.CONF['service:central'].accepted_tld_list
|
|
|
|
if accepted_tld_list:
|
|
accepted_tld_list = [tld.lower() for tld in accepted_tld_list]
|
|
|
|
self._accepted_tld_list = accepted_tld_list
|
|
|
|
return accepted_tld_list
|
|
|
|
def _is_valid_domain_name(self, context, domain_name):
|
|
# Validate domain name length
|
|
if len(domain_name) > cfg.CONF['service:central'].max_domain_name_len:
|
|
raise exceptions.InvalidDomainName('Name too long')
|
|
|
|
# Break the domain name up into its component labels
|
|
domain_labels = domain_name.strip('.').split('.')
|
|
|
|
# We need more than 1 label.
|
|
if len(domain_labels) <= 1:
|
|
raise exceptions.InvalidDomainName('More than one label is '
|
|
'required')
|
|
|
|
# Check the TLD for validity
|
|
if self.accepted_tld_list:
|
|
domain_tld = domain_labels[-1].lower()
|
|
|
|
if domain_tld not in self.accepted_tld_list:
|
|
raise exceptions.InvalidDomainName('Unknown or invalid TLD')
|
|
|
|
# Check domain name blacklist
|
|
if self._is_blacklisted_domain_name(context, domain_name):
|
|
# Some users are allowed bypass the blacklist.. Is this one?
|
|
if not policy.check('use_blacklisted_domain', context, exc=None):
|
|
raise exceptions.InvalidDomainName('Blacklisted domain name')
|
|
|
|
return True
|
|
|
|
def _is_valid_record_name(self, context, domain, record_name, record_type):
|
|
if not record_name.endswith('.'):
|
|
raise ValueError('Please supply a FQDN')
|
|
|
|
# Validate record name length
|
|
if len(record_name) > cfg.CONF['service:central'].max_record_name_len:
|
|
raise exceptions.InvalidRecordName('Name too long')
|
|
|
|
# Record must be contained in the parent zone
|
|
if not record_name.endswith(domain['name']):
|
|
raise exceptions.InvalidRecordLocation('Record is not contained '
|
|
'within it\'s parent '
|
|
'domain')
|
|
|
|
# CNAME's must not be created at the zone apex.
|
|
if record_type == 'CNAME' and record_name == domain['name']:
|
|
raise exceptions.InvalidRecordLocation('CNAME records may not be '
|
|
'created at the zone apex')
|
|
|
|
def _is_valid_record_placement(self, context, domain, record_name,
|
|
record_type, record_id=None):
|
|
# CNAME's must not share a name with other records
|
|
criterion = {'name': record_name}
|
|
|
|
if record_type != 'CNAME':
|
|
criterion['type'] = 'CNAME'
|
|
|
|
records = self.storage.get_records(context, domain['id'],
|
|
criterion=criterion)
|
|
if ((len(records) == 1 and records[0]['id'] != record_id)
|
|
or len(records) > 1):
|
|
raise exceptions.InvalidRecordLocation('CNAME records may not '
|
|
'share a name with any '
|
|
'other records')
|
|
|
|
if record_type == 'CNAME':
|
|
# CNAME's may not have children. Ever.
|
|
criterion = {'name': '%%.%s' % record_name}
|
|
records = self.storage.get_records(context, domain['id'],
|
|
criterion=criterion)
|
|
|
|
if len(records) > 0:
|
|
raise exceptions.InvalidRecordLocation('CNAME records may not '
|
|
'have any child '
|
|
'records')
|
|
|
|
else:
|
|
# No record may have a CNAME as a parent
|
|
if self._is_subrecord(context, domain, record_name,
|
|
{'type': 'CNAME'}):
|
|
raise exceptions.InvalidRecordLocation('CNAME records may not '
|
|
'have any child '
|
|
'records')
|
|
|
|
# Duplicate PTR's with the same name are not allowed
|
|
if record_type == 'PTR':
|
|
criterion = {'name': record_name, 'type': 'PTR'}
|
|
records = self.storage.get_records(context, domain['id'],
|
|
criterion=criterion)
|
|
if ((len(records) == 1 and records[0]['id'] != record_id)
|
|
or len(records) > 1):
|
|
raise exceptions.DuplicateRecord()
|
|
|
|
return True
|
|
|
|
def _is_blacklisted_domain_name(self, context, domain_name):
|
|
"""
|
|
Ensures the provided domain_name is not blacklisted.
|
|
"""
|
|
blacklists = cfg.CONF['service:central'].domain_name_blacklist
|
|
|
|
for blacklist in blacklists:
|
|
if bool(re.search(blacklist, domain_name)):
|
|
return blacklist
|
|
|
|
return False
|
|
|
|
def _is_subdomain(self, context, domain_name):
|
|
# Break the name up into it's component labels
|
|
labels = domain_name.split(".")
|
|
|
|
i = 1
|
|
|
|
# Starting with label #2, search for matching domain's in the database
|
|
while (i < len(labels)):
|
|
name = '.'.join(labels[i:])
|
|
|
|
try:
|
|
domain = self.storage.find_domain(context, {'name': name})
|
|
except exceptions.DomainNotFound:
|
|
i += 1
|
|
else:
|
|
return domain
|
|
|
|
return False
|
|
|
|
def _is_subrecord(self, context, domain, record_name, criterion):
|
|
# Break the names up into their component labels
|
|
domain_labels = domain['name'].split(".")
|
|
record_labels = record_name.split(".")
|
|
|
|
i = 1
|
|
j = len(record_labels) - len(domain_labels)
|
|
|
|
# Starting with label #2, search for matching records's in the database
|
|
while (i <= j):
|
|
criterion['name'] = '.'.join(record_labels[i:])
|
|
|
|
records = self.storage.get_records(context, domain['id'],
|
|
criterion)
|
|
|
|
if len(records) == 0:
|
|
i += 1
|
|
else:
|
|
return records
|
|
|
|
return False
|
|
|
|
def _increment_domain_serial(self, context, domain_id):
|
|
domain = self.storage.get_domain(context, domain_id)
|
|
|
|
# Increment the serial number
|
|
values = {'serial': utils.increment_serial(domain['serial'])}
|
|
domain = self.storage.update_domain(context, domain_id, values)
|
|
|
|
try:
|
|
self.backend.update_domain(context, domain)
|
|
except exceptions.Backend:
|
|
# Re-raise Backend exceptions as is..
|
|
raise
|
|
except Exception, e:
|
|
raise exceptions.Backend('Unknown backend failure: %s' % e)
|
|
|
|
return domain
|
|
|
|
# Misc Methods
|
|
def get_absolute_limits(self, context):
|
|
return self.quota.get_tenant_quotas(context, context.tenant_id)
|
|
|
|
# Server Methods
|
|
def create_server(self, context, values):
|
|
policy.check('create_server', context)
|
|
|
|
server = self.storage.create_server(context, values)
|
|
|
|
utils.notify(context, 'central', 'server.create', server)
|
|
|
|
return server
|
|
|
|
def get_servers(self, context, criterion=None):
|
|
policy.check('get_servers', context)
|
|
|
|
return self.storage.get_servers(context, criterion)
|
|
|
|
def get_server(self, context, server_id):
|
|
policy.check('get_server', context, {'server_id': server_id})
|
|
|
|
return self.storage.get_server(context, server_id)
|
|
|
|
def update_server(self, context, server_id, values):
|
|
policy.check('update_server', context, {'server_id': server_id})
|
|
|
|
server = self.storage.update_server(context, server_id, values)
|
|
|
|
utils.notify(context, 'central', 'server.update', server)
|
|
|
|
return server
|
|
|
|
def delete_server(self, context, server_id):
|
|
policy.check('delete_server', context, {'server_id': server_id})
|
|
|
|
server = self.storage.get_server(context, server_id)
|
|
|
|
utils.notify(context, 'central', 'server.delete', server)
|
|
|
|
return self.storage.delete_server(context, server_id)
|
|
|
|
# TSIG Key Methods
|
|
def create_tsigkey(self, context, values):
|
|
policy.check('create_tsigkey', context)
|
|
|
|
tsigkey = self.storage.create_tsigkey(context, values)
|
|
|
|
try:
|
|
self.backend.create_tsigkey(context, tsigkey)
|
|
except exceptions.Backend:
|
|
# Re-raise Backend exceptions as is..
|
|
raise
|
|
except Exception, e:
|
|
raise exceptions.Backend('Unknown backend failure: %s' % e)
|
|
|
|
utils.notify(context, 'central', 'tsigkey.create', tsigkey)
|
|
|
|
return tsigkey
|
|
|
|
def get_tsigkeys(self, context, criterion=None):
|
|
policy.check('get_tsigkeys', context)
|
|
|
|
return self.storage.get_tsigkeys(context, criterion)
|
|
|
|
def get_tsigkey(self, context, tsigkey_id):
|
|
policy.check('get_tsigkey', context, {'tsigkey_id': tsigkey_id})
|
|
|
|
return self.storage.get_tsigkey(context, tsigkey_id)
|
|
|
|
def update_tsigkey(self, context, tsigkey_id, values):
|
|
policy.check('update_tsigkey', context, {'tsigkey_id': tsigkey_id})
|
|
|
|
tsigkey = self.storage.update_tsigkey(context, tsigkey_id, values)
|
|
|
|
try:
|
|
self.backend.update_tsigkey(context, tsigkey)
|
|
except exceptions.Backend:
|
|
# Re-raise Backend exceptions as is..
|
|
raise
|
|
except Exception, e:
|
|
raise exceptions.Backend('Unknown backend failure: %s' % e)
|
|
|
|
utils.notify(context, 'central', 'tsigkey.update', tsigkey)
|
|
|
|
return tsigkey
|
|
|
|
def delete_tsigkey(self, context, tsigkey_id):
|
|
policy.check('delete_tsigkey', context, {'tsigkey_id': tsigkey_id})
|
|
|
|
tsigkey = self.storage.get_tsigkey(context, tsigkey_id)
|
|
|
|
try:
|
|
self.backend.delete_tsigkey(context, tsigkey)
|
|
except exceptions.Backend:
|
|
# Re-raise Backend exceptions as is..
|
|
raise
|
|
except Exception, e:
|
|
raise exceptions.Backend('Unknown backend failure: %s' % e)
|
|
|
|
utils.notify(context, 'central', 'tsigkey.delete', tsigkey)
|
|
|
|
return self.storage.delete_tsigkey(context, tsigkey_id)
|
|
|
|
# Tenant Methods
|
|
def get_tenants(self, context):
|
|
policy.check('get_tenants', context)
|
|
return self.storage.get_tenants(context)
|
|
|
|
def get_tenant(self, context, tenant_id):
|
|
target = {
|
|
'tenant_id': tenant_id
|
|
}
|
|
|
|
policy.check('get_tenant', context, target)
|
|
|
|
return self.storage.get_tenant(context, tenant_id)
|
|
|
|
def count_tenants(self, context):
|
|
policy.check('count_tenants', context)
|
|
return self.storage.count_tenants(context)
|
|
|
|
# Domain Methods
|
|
def create_domain(self, context, values):
|
|
values['tenant_id'] = context.tenant_id
|
|
|
|
target = {
|
|
'tenant_id': values['tenant_id'],
|
|
'domain_name': values['name']
|
|
}
|
|
|
|
policy.check('create_domain', context, target)
|
|
|
|
# Ensure the tenant has enough quota to continue
|
|
quota_criterion = {'tenant_id': values['tenant_id']}
|
|
domain_count = self.storage.count_domains(context,
|
|
criterion=quota_criterion)
|
|
self.quota.limit_check(context, values['tenant_id'],
|
|
domains=domain_count)
|
|
|
|
# Ensure the domain name is valid
|
|
self._is_valid_domain_name(context, values['name'])
|
|
|
|
# Handle sub-domains appropriately
|
|
parent_domain = self._is_subdomain(context, values['name'])
|
|
|
|
if parent_domain:
|
|
if parent_domain['tenant_id'] == values['tenant_id']:
|
|
# Record the Parent Domain ID
|
|
values['parent_domain_id'] = parent_domain['id']
|
|
else:
|
|
raise exceptions.Forbidden('Unable to create subdomain in '
|
|
'another tenants domain')
|
|
|
|
# TODO(kiall): Handle super-domains properly
|
|
|
|
# NOTE(kiall): Fetch the servers before creating the domain, this way
|
|
# we can prevent domain creation if no servers are
|
|
# configured.
|
|
servers = self.storage.get_servers(context)
|
|
|
|
if len(servers) == 0:
|
|
LOG.critical('No servers configured. Please create at least one '
|
|
'server')
|
|
raise exceptions.NoServersConfigured()
|
|
|
|
# Set the serial number
|
|
values['serial'] = utils.increment_serial()
|
|
|
|
domain = self.storage.create_domain(context, values)
|
|
|
|
try:
|
|
self.backend.create_domain(context, domain)
|
|
except exceptions.Backend:
|
|
# Re-raise Backend exceptions as is..
|
|
raise
|
|
except Exception, e:
|
|
raise exceptions.Backend('Unknown backend failure: %s' % e)
|
|
|
|
utils.notify(context, 'central', 'domain.create', domain)
|
|
|
|
return domain
|
|
|
|
def get_domains(self, context, criterion=None):
|
|
target = {'tenant_id': context.tenant_id}
|
|
policy.check('get_domains', context, target)
|
|
|
|
if criterion is None:
|
|
criterion = {}
|
|
|
|
if not context.is_admin:
|
|
criterion['tenant_id'] = context.tenant_id
|
|
|
|
return self.storage.get_domains(context, criterion)
|
|
|
|
def get_domain(self, context, domain_id):
|
|
domain = self.storage.get_domain(context, domain_id)
|
|
|
|
target = {
|
|
'domain_id': domain_id,
|
|
'domain_name': domain['name'],
|
|
'tenant_id': domain['tenant_id']
|
|
}
|
|
policy.check('get_domain', context, target)
|
|
|
|
return domain
|
|
|
|
def get_domain_servers(self, context, domain_id, criterion=None):
|
|
domain = self.storage.get_domain(context, domain_id)
|
|
|
|
target = {
|
|
'domain_id': domain_id,
|
|
'domain_name': domain['name'],
|
|
'tenant_id': domain['tenant_id']
|
|
}
|
|
|
|
policy.check('get_domain_servers', context, target)
|
|
|
|
if criterion is None:
|
|
criterion = {}
|
|
|
|
# TODO(kiall): Once we allow domains to be allocated on 1 of N server
|
|
# pools, return the filtered list here.
|
|
return self.storage.get_servers(context, criterion)
|
|
|
|
def find_domains(self, context, criterion):
|
|
target = {'tenant_id': context.tenant_id}
|
|
policy.check('find_domains', context, target)
|
|
|
|
if not context.is_admin:
|
|
criterion['tenant_id'] = context.tenant_id
|
|
|
|
return self.storage.find_domains(context, criterion)
|
|
|
|
def find_domain(self, context, criterion):
|
|
target = {'tenant_id': context.tenant_id}
|
|
policy.check('find_domain', context, target)
|
|
|
|
if not context.is_admin:
|
|
criterion['tenant_id'] = context.tenant_id
|
|
|
|
return self.storage.find_domain(context, criterion)
|
|
|
|
def update_domain(self, context, domain_id, values, increment_serial=True):
|
|
domain = self.storage.get_domain(context, domain_id)
|
|
|
|
target = {
|
|
'domain_id': domain_id,
|
|
'domain_name': domain['name'],
|
|
'tenant_id': domain['tenant_id']
|
|
}
|
|
|
|
policy.check('update_domain', context, target)
|
|
|
|
if 'tenant_id' in values:
|
|
# NOTE(kiall): Ensure the user is allowed to delete a domain from
|
|
# the original tenant.
|
|
policy.check('delete_domain', context, target)
|
|
|
|
# NOTE(kiall): Ensure the user is allowed to create a domain in
|
|
# the new tenant.
|
|
target = {'domain_id': domain_id, 'tenant_id': values['tenant_id']}
|
|
policy.check('create_domain', context, target)
|
|
|
|
if 'name' in values and values['name'] != domain['name']:
|
|
raise exceptions.BadRequest('Renaming a domain is not allowed')
|
|
|
|
if increment_serial:
|
|
# Increment the serial number
|
|
values['serial'] = utils.increment_serial(domain['serial'])
|
|
|
|
domain = self.storage.update_domain(context, domain_id, values)
|
|
|
|
try:
|
|
self.backend.update_domain(context, domain)
|
|
except exceptions.Backend:
|
|
# Re-raise Backend exceptions as is..
|
|
raise
|
|
except Exception, e:
|
|
raise exceptions.Backend('Unknown backend failure: %s' % e)
|
|
|
|
utils.notify(context, 'central', 'domain.update', domain)
|
|
|
|
return domain
|
|
|
|
def delete_domain(self, context, domain_id):
|
|
domain = self.storage.get_domain(context, domain_id)
|
|
|
|
target = {
|
|
'domain_id': domain_id,
|
|
'domain_name': domain['name'],
|
|
'tenant_id': domain['tenant_id']
|
|
}
|
|
|
|
policy.check('delete_domain', context, target)
|
|
|
|
# Prevent deletion of a zone which has child zones
|
|
criterion = {'parent_domain_id': domain_id}
|
|
|
|
if self.storage.count_domains(context, criterion) > 0:
|
|
raise exceptions.DomainHasSubdomain('Please delete any subdomains'
|
|
' before deleting this domain')
|
|
|
|
try:
|
|
self.backend.delete_domain(context, domain)
|
|
except exceptions.Backend:
|
|
# Re-raise Backend exceptions as is..
|
|
raise
|
|
except Exception, e:
|
|
raise exceptions.Backend('Unknown backend failure: %s' % e)
|
|
|
|
utils.notify(context, 'central', 'domain.delete', domain)
|
|
|
|
return self.storage.delete_domain(context, domain_id)
|
|
|
|
def count_domains(self, context, criterion=None):
|
|
if criterion is None:
|
|
criterion = {}
|
|
|
|
target = {
|
|
'tenant_id': criterion.get('tenant_id', None)
|
|
}
|
|
|
|
policy.check('count_domains', context, target)
|
|
|
|
return self.storage.count_domains(context, criterion)
|
|
|
|
def touch_domain(self, context, domain_id):
|
|
domain = self.storage.get_domain(context, domain_id)
|
|
|
|
target = {
|
|
'domain_id': domain_id,
|
|
'domain_name': domain['name'],
|
|
'tenant_id': domain['tenant_id']
|
|
}
|
|
|
|
policy.check('touch_domain', context, target)
|
|
|
|
domain = self._increment_domain_serial(context, domain_id)
|
|
|
|
utils.notify(context, 'central', 'domain.touch', domain)
|
|
|
|
return domain
|
|
|
|
# Record Methods
|
|
def create_record(self, context, domain_id, values, increment_serial=True):
|
|
domain = self.storage.get_domain(context, domain_id)
|
|
|
|
target = {
|
|
'domain_id': domain_id,
|
|
'domain_name': domain['name'],
|
|
'record_name': values['name'],
|
|
'tenant_id': domain['tenant_id']
|
|
}
|
|
|
|
policy.check('create_record', context, target)
|
|
|
|
# Ensure the tenant has enough quota to continue
|
|
quota_criterion = {'domain_id': domain_id}
|
|
record_count = self.storage.count_records(context,
|
|
criterion=quota_criterion)
|
|
self.quota.limit_check(context, domain['tenant_id'],
|
|
domain_records=record_count)
|
|
|
|
# Ensure the record name and placement is valid
|
|
self._is_valid_record_name(context, domain, values['name'],
|
|
values['type'])
|
|
self._is_valid_record_placement(context, domain, values['name'],
|
|
values['type'])
|
|
|
|
record = self.storage.create_record(context, domain_id, values)
|
|
|
|
try:
|
|
self.backend.create_record(context, domain, record)
|
|
except exceptions.Backend:
|
|
# Re-raise Backend exceptions as is..
|
|
raise
|
|
except Exception, e:
|
|
raise exceptions.Backend('Unknown backend failure: %s' % e)
|
|
|
|
if increment_serial:
|
|
self._increment_domain_serial(context, domain_id)
|
|
|
|
# Send Record creation notification
|
|
utils.notify(context, 'central', 'record.create', record)
|
|
|
|
return record
|
|
|
|
def get_records(self, context, domain_id, criterion=None):
|
|
domain = self.storage.get_domain(context, domain_id)
|
|
|
|
target = {
|
|
'domain_id': domain_id,
|
|
'domain_name': domain['name'],
|
|
'tenant_id': domain['tenant_id']
|
|
}
|
|
|
|
policy.check('get_records', context, target)
|
|
|
|
return self.storage.get_records(context, domain_id, criterion)
|
|
|
|
def get_record(self, context, domain_id, record_id):
|
|
domain = self.storage.get_domain(context, domain_id)
|
|
record = self.storage.get_record(context, record_id)
|
|
|
|
# Ensure the domain_id matches the record's domain_id
|
|
if domain['id'] != record['domain_id']:
|
|
raise exceptions.RecordNotFound()
|
|
|
|
target = {
|
|
'domain_id': domain_id,
|
|
'domain_name': domain['name'],
|
|
'record_id': record['id'],
|
|
'tenant_id': domain['tenant_id']
|
|
}
|
|
|
|
policy.check('get_record', context, target)
|
|
|
|
return record
|
|
|
|
def find_records(self, context, criterion):
|
|
target = {'tenant_id': context.tenant_id}
|
|
policy.check('find_records', context, target)
|
|
|
|
if not context.is_admin:
|
|
criterion['tenant_id'] = context.tenant_id
|
|
|
|
return self.storage.find_records(context, criterion)
|
|
|
|
def find_record(self, context, criterion):
|
|
target = {'tenant_id': context.tenant_id}
|
|
policy.check('find_record', context, target)
|
|
|
|
if not context.is_admin:
|
|
criterion['tenant_id'] = context.tenant_id
|
|
|
|
return self.storage.find_record(context, criterion)
|
|
|
|
def update_record(self, context, domain_id, record_id, values,
|
|
increment_serial=True):
|
|
domain = self.storage.get_domain(context, domain_id)
|
|
record = self.storage.get_record(context, record_id)
|
|
|
|
# Ensure the domain_id matches the record's domain_id
|
|
if domain['id'] != record['domain_id']:
|
|
raise exceptions.RecordNotFound()
|
|
|
|
target = {
|
|
'domain_id': domain_id,
|
|
'domain_name': domain['name'],
|
|
'record_id': record['id'],
|
|
'tenant_id': domain['tenant_id']
|
|
}
|
|
|
|
policy.check('update_record', context, target)
|
|
|
|
# Ensure the record name is valid
|
|
record_name = values['name'] if 'name' in values else record['name']
|
|
record_type = values['type'] if 'type' in values else record['type']
|
|
|
|
self._is_valid_record_name(context, domain, record_name, record_type)
|
|
self._is_valid_record_placement(context, domain, record_name,
|
|
record_type, record_id)
|
|
|
|
# Update the record
|
|
record = self.storage.update_record(context, record_id, values)
|
|
|
|
try:
|
|
self.backend.update_record(context, domain, record)
|
|
except exceptions.Backend:
|
|
# Re-raise Backend exceptions as is..
|
|
raise
|
|
except Exception, e:
|
|
raise exceptions.Backend('Unknown backend failure: %s' % e)
|
|
|
|
if increment_serial:
|
|
self._increment_domain_serial(context, domain_id)
|
|
|
|
# Send Record update notification
|
|
utils.notify(context, 'central', 'record.update', record)
|
|
|
|
return record
|
|
|
|
def delete_record(self, context, domain_id, record_id,
|
|
increment_serial=True):
|
|
domain = self.storage.get_domain(context, domain_id)
|
|
record = self.storage.get_record(context, record_id)
|
|
|
|
# Ensure the domain_id matches the record's domain_id
|
|
if domain['id'] != record['domain_id']:
|
|
raise exceptions.RecordNotFound()
|
|
|
|
target = {
|
|
'domain_id': domain_id,
|
|
'domain_name': domain['name'],
|
|
'record_id': record['id'],
|
|
'tenant_id': domain['tenant_id']
|
|
}
|
|
|
|
policy.check('delete_record', context, target)
|
|
|
|
try:
|
|
self.backend.delete_record(context, domain, record)
|
|
except exceptions.Backend:
|
|
# Re-raise Backend exceptions as is..
|
|
raise
|
|
except Exception, e:
|
|
raise exceptions.Backend('Unknown backend failure: %s' % e)
|
|
|
|
if increment_serial:
|
|
self._increment_domain_serial(context, domain_id)
|
|
|
|
# Send Record deletion notification
|
|
utils.notify(context, 'central', 'record.delete', record)
|
|
|
|
return self.storage.delete_record(context, record_id)
|
|
|
|
def count_records(self, context, criterion=None):
|
|
if criterion is None:
|
|
criterion = {}
|
|
|
|
target = {
|
|
'tenant_id': criterion.get('tenant_id', None)
|
|
}
|
|
|
|
policy.check('count_records', context, target)
|
|
return self.storage.count_records(context, criterion)
|
|
|
|
# Diagnostics Methods
|
|
def sync_domains(self, context):
|
|
policy.check('diagnostics_sync_domains', context)
|
|
|
|
domains = self.storage.get_domains(context)
|
|
results = {}
|
|
|
|
for domain in domains:
|
|
servers = self.storage.get_servers(context)
|
|
records = self.storage.get_records(context, domain['id'])
|
|
|
|
results[domain['id']] = self.backend.sync_domain(context,
|
|
domain,
|
|
records,
|
|
servers)
|
|
|
|
return results
|
|
|
|
def sync_domain(self, context, domain_id):
|
|
domain = self.storage.get_domain(context, domain_id)
|
|
|
|
target = {
|
|
'domain_id': domain_id,
|
|
'domain_name': domain['name'],
|
|
'tenant_id': domain['tenant_id']
|
|
}
|
|
|
|
policy.check('diagnostics_sync_domain', context, target)
|
|
|
|
records = self.storage.get_records(context, domain_id)
|
|
|
|
return self.backend.sync_domain(context, domain, records)
|
|
|
|
def sync_record(self, context, domain_id, record_id):
|
|
domain = self.storage.get_domain(context, domain_id)
|
|
|
|
target = {
|
|
'domain_id': domain_id,
|
|
'domain_name': domain['name'],
|
|
'record_id': record_id,
|
|
'tenant_id': domain['tenant_id']
|
|
}
|
|
|
|
policy.check('diagnostics_sync_record', context, target)
|
|
|
|
record = self.storage.get_record(context, record_id)
|
|
|
|
return self.backend.sync_record(context, domain, record)
|
|
|
|
def ping(self, context):
|
|
policy.check('diagnostics_ping', context)
|
|
|
|
try:
|
|
backend_status = self.backend.ping(context)
|
|
except Exception, e:
|
|
backend_status = {'status': False, 'message': str(e)}
|
|
|
|
try:
|
|
storage_status = self.storage.ping(context)
|
|
except Exception, e:
|
|
storage_status = {'status': False, 'message': str(e)}
|
|
|
|
if backend_status and storage_status:
|
|
status = True
|
|
else:
|
|
status = False
|
|
|
|
return {
|
|
'host': cfg.CONF.host,
|
|
'status': status,
|
|
'backend': backend_status,
|
|
'storage': storage_status
|
|
}
|