Convert PowerDNS to a Pools Backend
Change-Id: I1bf5f91afa1262d73acb0e494c42bdc8166f8195 Blueprint: transition-powerdns-to-pools
This commit is contained in:
@@ -5,7 +5,7 @@ set -ex
|
|||||||
pushd $BASE/new/devstack
|
pushd $BASE/new/devstack
|
||||||
|
|
||||||
export KEEP_LOCALRC=1
|
export KEEP_LOCALRC=1
|
||||||
export ENABLED_SERVICES=designate,designate-api,designate-central,designate-sink,designate-mdns
|
export ENABLED_SERVICES=designate,designate-api,designate-central,designate-sink,designate-mdns,designate-pool-manager
|
||||||
|
|
||||||
echo "DESIGNATE_SERVICE_PORT_DNS=5322" >> $BASE/new/devstack/localrc
|
echo "DESIGNATE_SERVICE_PORT_DNS=5322" >> $BASE/new/devstack/localrc
|
||||||
|
|
||||||
@@ -15,7 +15,6 @@ if [ "$DEVSTACK_GATE_DESIGNATE_DRIVER" == "powerdns" ]; then
|
|||||||
echo "DESIGNATE_BACKEND_DRIVER=powerdns" >> $BASE/new/devstack/localrc
|
echo "DESIGNATE_BACKEND_DRIVER=powerdns" >> $BASE/new/devstack/localrc
|
||||||
|
|
||||||
elif [ "$DEVSTACK_GATE_DESIGNATE_DRIVER" == "bind9" ]; then
|
elif [ "$DEVSTACK_GATE_DESIGNATE_DRIVER" == "bind9" ]; then
|
||||||
export ENABLED_SERVICES+=,designate-pool-manager
|
|
||||||
echo "DESIGNATE_BACKEND_DRIVER=bind9_pool" >> $BASE/new/devstack/localrc
|
echo "DESIGNATE_BACKEND_DRIVER=bind9_pool" >> $BASE/new/devstack/localrc
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
@@ -46,7 +46,15 @@ function install_designate_backend {
|
|||||||
|
|
||||||
# configure_designate_backend - make configuration changes, including those to other services
|
# configure_designate_backend - make configuration changes, including those to other services
|
||||||
function configure_designate_backend {
|
function configure_designate_backend {
|
||||||
|
iniset $DESIGNATE_CONF service:pool_manager backends powerdns
|
||||||
|
iniset $DESIGNATE_CONF service:mdns slave_nameserver_ips_and_ports "$DESIGNATE_SERVICE_HOST:$DESIGNATE_SERVICE_PORT_DNS"
|
||||||
|
|
||||||
|
iniset $DESIGNATE_CONF backend:powerdns server_ids $DESIGNATE_SERVER_ID
|
||||||
iniset $DESIGNATE_CONF backend:powerdns connection `database_connection_url designate_pdns`
|
iniset $DESIGNATE_CONF backend:powerdns connection `database_connection_url designate_pdns`
|
||||||
|
iniset $DESIGNATE_CONF backend:powerdns masters "$DESIGNATE_SERVICE_HOST:$DESIGNATE_SERVICE_PORT_MDNS"
|
||||||
|
|
||||||
|
iniset $DESIGNATE_CONF backend:powerdns:$DESIGNATE_SERVER_ID host $DESIGNATE_SERVICE_HOST
|
||||||
|
iniset $DESIGNATE_CONF backend:powerdns:$DESIGNATE_SERVER_ID port $DESIGNATE_SERVICE_PORT_DNS
|
||||||
|
|
||||||
sudo tee $POWERDNS_CFG_DIR/pdns.conf > /dev/null <<EOF
|
sudo tee $POWERDNS_CFG_DIR/pdns.conf > /dev/null <<EOF
|
||||||
# General Config
|
# General Config
|
||||||
@@ -59,7 +67,8 @@ daemon=yes
|
|||||||
disable-axfr=no
|
disable-axfr=no
|
||||||
local-address=$DESIGNATE_SERVICE_HOST
|
local-address=$DESIGNATE_SERVICE_HOST
|
||||||
local-port=$DESIGNATE_SERVICE_PORT_DNS
|
local-port=$DESIGNATE_SERVICE_PORT_DNS
|
||||||
master=yes
|
master=no
|
||||||
|
slave=yes
|
||||||
cache-ttl=0
|
cache-ttl=0
|
||||||
query-cache-ttl=0
|
query-cache-ttl=0
|
||||||
negquery-cache-ttl=0
|
negquery-cache-ttl=0
|
||||||
|
@@ -1,115 +0,0 @@
|
|||||||
# lib/designate_plugins/backend-powerdns_mdns
|
|
||||||
# Configure the powerdns_mdns backend
|
|
||||||
|
|
||||||
# Enable with:
|
|
||||||
# DESIGNATE_BACKEND_DRIVER=powerdns_mdns
|
|
||||||
|
|
||||||
# Dependencies:
|
|
||||||
# ``functions`` file
|
|
||||||
# ``designate`` configuration
|
|
||||||
|
|
||||||
# install_designate_backend - install any external requirements
|
|
||||||
# configure_designate_backend - make configuration changes, including those to other services
|
|
||||||
# init_designate_backend - initialize databases, etc.
|
|
||||||
# start_designate_backend - start any external services
|
|
||||||
# stop_designate_backend - stop any external services
|
|
||||||
# cleanup_designate_backend - remove transient data and cache
|
|
||||||
|
|
||||||
# Save trace setting
|
|
||||||
DP_PDNS_MDNS_XTRACE=$(set +o | grep xtrace)
|
|
||||||
set +o xtrace
|
|
||||||
|
|
||||||
# Defaults
|
|
||||||
# --------
|
|
||||||
if is_fedora; then
|
|
||||||
POWERDNS_CFG_DIR=/etc/pdns
|
|
||||||
else
|
|
||||||
POWERDNS_CFG_DIR=/etc/powerdns
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Entry Points
|
|
||||||
# ------------
|
|
||||||
|
|
||||||
# install_designate_backend - install any external requirements
|
|
||||||
function install_designate_backend {
|
|
||||||
if is_ubuntu; then
|
|
||||||
PDNS=pdns-server
|
|
||||||
elif is_fedora || is_suse; then
|
|
||||||
PDNS=pdns
|
|
||||||
else
|
|
||||||
PDNS=pdns-server
|
|
||||||
fi
|
|
||||||
|
|
||||||
install_package $PDNS pdns-backend-mysql
|
|
||||||
sudo rm -rf $POWERDNS_CFG_DIR/pdns.d
|
|
||||||
}
|
|
||||||
|
|
||||||
# configure_designate_backend - make configuration changes, including those to other services
|
|
||||||
function configure_designate_backend {
|
|
||||||
iniset $DESIGNATE_CONF backend:powerdns_mdns connection `database_connection_url designate_pdns_mdns`
|
|
||||||
iniset $DESIGNATE_CONF backend:powerdns_mdns masters "$DESIGNATE_SERVICE_HOST:$DESIGNATE_SERVICE_PORT_MDNS"
|
|
||||||
iniset $DESIGNATE_CONF service:mdns slave_nameserver_ips_and_ports "$DESIGNATE_SERVICE_HOST:$DESIGNATE_SERVICE_PORT_DNS"
|
|
||||||
|
|
||||||
sudo tee $POWERDNS_CFG_DIR/pdns.conf > /dev/null <<EOF
|
|
||||||
# General Config
|
|
||||||
setgid=pdns
|
|
||||||
setuid=pdns
|
|
||||||
config-dir=$POWERDNS_CFG_DIR
|
|
||||||
socket-dir=/var/run
|
|
||||||
guardian=yes
|
|
||||||
daemon=yes
|
|
||||||
disable-axfr=no
|
|
||||||
local-address=$DESIGNATE_SERVICE_HOST
|
|
||||||
local-port=$DESIGNATE_SERVICE_PORT_DNS
|
|
||||||
master=no
|
|
||||||
slave=yes
|
|
||||||
cache-ttl=0
|
|
||||||
query-cache-ttl=0
|
|
||||||
negquery-cache-ttl=0
|
|
||||||
EOF
|
|
||||||
|
|
||||||
if is_service_enabled mysql; then
|
|
||||||
sudo tee -a $POWERDNS_CFG_DIR/pdns.conf > /dev/null <<EOF
|
|
||||||
# Launch gmysql backend
|
|
||||||
launch=gmysql
|
|
||||||
|
|
||||||
# gmysql parameters
|
|
||||||
gmysql-host=$DATABASE_HOST
|
|
||||||
gmysql-user=$DATABASE_USER
|
|
||||||
gmysql-password=$DATABASE_PASSWORD
|
|
||||||
gmysql-dbname=designate_pdns_mdns
|
|
||||||
gmysql-dnssec=yes
|
|
||||||
EOF
|
|
||||||
else
|
|
||||||
die $LINENO "PowerDNS mDNS backend only supports MySQL"
|
|
||||||
fi
|
|
||||||
|
|
||||||
restart_service pdns
|
|
||||||
}
|
|
||||||
|
|
||||||
# init_designate_backend - initialize databases, etc.
|
|
||||||
function init_designate_backend {
|
|
||||||
# (Re)create designate_pdns database
|
|
||||||
recreate_database designate_pdns_mdns utf8
|
|
||||||
|
|
||||||
# Init and migrate designate_pdns database
|
|
||||||
designate-manage powerdns-mdns sync
|
|
||||||
}
|
|
||||||
|
|
||||||
# start_designate_backend - start any external services
|
|
||||||
function start_designate_backend {
|
|
||||||
start_service pdns
|
|
||||||
}
|
|
||||||
|
|
||||||
# stop_designate_backend - stop any external services
|
|
||||||
function stop_designate_backend {
|
|
||||||
stop_service pdns
|
|
||||||
}
|
|
||||||
|
|
||||||
# cleanup_designate_backend - remove transient data and cache
|
|
||||||
function cleanup_designate_backend {
|
|
||||||
:
|
|
||||||
}
|
|
||||||
|
|
||||||
# Restore xtrace
|
|
||||||
$DP_PDNS_MDNS_XTRACE
|
|
@@ -1,8 +1,6 @@
|
|||||||
# Copyright 2012 Hewlett-Packard Development Company, L.P. All Rights Reserved.
|
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||||
# Copyright 2012 Managed I.T.
|
|
||||||
#
|
#
|
||||||
# Author: Patrick Galbraith <patg@hp.com>
|
# Author: Kiall Mac Innes <kiall@hp.com>
|
||||||
# Author: Kiall Mac Innes <kiall@managedit.ie>
|
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# 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
|
# not use this file except in compliance with the License. You may obtain
|
||||||
@@ -15,7 +13,7 @@
|
|||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
import base64
|
import copy
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
from oslo.config import cfg
|
from oslo.config import cfg
|
||||||
@@ -24,56 +22,40 @@ from oslo.utils import excutils
|
|||||||
from sqlalchemy.sql import select
|
from sqlalchemy.sql import select
|
||||||
|
|
||||||
from designate.openstack.common import log as logging
|
from designate.openstack.common import log as logging
|
||||||
from designate.i18n import _LC
|
|
||||||
from designate import exceptions
|
from designate import exceptions
|
||||||
from designate import utils
|
from designate.i18n import _LC
|
||||||
from designate.backend import base
|
from designate.backend import base
|
||||||
from designate.backend.impl_powerdns import tables
|
from designate.backend.impl_powerdns import tables
|
||||||
from designate.sqlalchemy import session
|
from designate.sqlalchemy import session
|
||||||
from designate.sqlalchemy.expressions import InsertFromSelect
|
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
TSIG_SUPPORTED_ALGORITHMS = ['hmac-md5']
|
|
||||||
|
|
||||||
|
|
||||||
def _map_col(keys, col):
|
def _map_col(keys, col):
|
||||||
return dict([(keys[i], col[i]) for i in range(len(keys))])
|
return dict([(keys[i], col[i]) for i in range(len(keys))])
|
||||||
|
|
||||||
|
|
||||||
class PowerDNSBackend(base.Backend):
|
class PowerDNSBackend(base.PoolBackend):
|
||||||
__plugin_name__ = 'powerdns'
|
__plugin_name__ = 'powerdns'
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_cfg_opts(cls):
|
def _get_common_cfg_opts(cls):
|
||||||
group = cfg.OptGroup(
|
opts = copy.deepcopy(options.database_opts)
|
||||||
name='backend:powerdns', title="Configuration for PowerDNS Backend"
|
|
||||||
)
|
|
||||||
|
|
||||||
opts = [
|
# Overide the default DB connection path in order to avoid name
|
||||||
cfg.StrOpt('domain-type', default='NATIVE',
|
|
||||||
help='PowerDNS Domain Type'),
|
|
||||||
cfg.ListOpt('also-notify', default=[],
|
|
||||||
help='List of additional IPs to send NOTIFYs to'),
|
|
||||||
] + options.database_opts
|
|
||||||
|
|
||||||
# TODO(kiall):
|
|
||||||
# Overide the default DB connection registered above, to avoid name
|
|
||||||
# conflicts between the Designate and PowerDNS databases.
|
# conflicts between the Designate and PowerDNS databases.
|
||||||
# CONF.set_default('connection',
|
for opt in opts:
|
||||||
# 'sqlite:///$state_path/powerdns.sqlite',
|
if opt.name == 'connection':
|
||||||
# group='backend:powerdns')
|
opt.default = 'sqlite:///$state_path/powerdns.sqlite'
|
||||||
return [(group, opts)]
|
|
||||||
|
return opts
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(PowerDNSBackend, self).__init__(*args, **kwargs)
|
super(PowerDNSBackend, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.local_store = threading.local()
|
self.local_store = threading.local()
|
||||||
|
|
||||||
def start(self):
|
|
||||||
super(PowerDNSBackend, self).start()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def session(self):
|
def session(self):
|
||||||
# NOTE: This uses a thread local store, allowing each greenthread to
|
# NOTE: This uses a thread local store, allowing each greenthread to
|
||||||
@@ -99,26 +81,6 @@ class PowerDNSBackend(base.Backend):
|
|||||||
|
|
||||||
return _map_col(query.columns.keys(), resultproxy.fetchone())
|
return _map_col(query.columns.keys(), resultproxy.fetchone())
|
||||||
|
|
||||||
def _update(self, table, values, exc_notfound, id_col=None):
|
|
||||||
if id_col is None:
|
|
||||||
id_col = table.c.id
|
|
||||||
|
|
||||||
query = table.update()\
|
|
||||||
.where(id_col == values[id_col.name])\
|
|
||||||
.values(**values)
|
|
||||||
|
|
||||||
resultproxy = self.session.execute(query)
|
|
||||||
|
|
||||||
if resultproxy.rowcount != 1:
|
|
||||||
raise exc_notfound()
|
|
||||||
|
|
||||||
# Refetch the row, for generated columns etc
|
|
||||||
query = select([table])\
|
|
||||||
.where(id_col == values[id_col.name])
|
|
||||||
resultproxy = self.session.execute(query)
|
|
||||||
|
|
||||||
return _map_col(query.columns.keys(), resultproxy.fetchone())
|
|
||||||
|
|
||||||
def _get(self, table, id_, exc_notfound, id_col=None):
|
def _get(self, table, id_, exc_notfound, id_col=None):
|
||||||
if id_col is None:
|
if id_col is None:
|
||||||
id_col = table.c.id
|
id_col = table.c.id
|
||||||
@@ -148,143 +110,20 @@ class PowerDNSBackend(base.Backend):
|
|||||||
if resultproxy.rowcount != 1:
|
if resultproxy.rowcount != 1:
|
||||||
raise exc_notfound()
|
raise exc_notfound()
|
||||||
|
|
||||||
# TSIG Key Methods
|
|
||||||
def create_tsigkey(self, context, tsigkey):
|
|
||||||
"""Create a TSIG Key"""
|
|
||||||
|
|
||||||
if tsigkey['algorithm'] not in TSIG_SUPPORTED_ALGORITHMS:
|
|
||||||
raise exceptions.NotImplemented('Unsupported algorithm')
|
|
||||||
|
|
||||||
values = {
|
|
||||||
'designate_id': tsigkey['id'],
|
|
||||||
'name': tsigkey['name'],
|
|
||||||
'algorithm': tsigkey['algorithm'],
|
|
||||||
'secret': base64.b64encode(tsigkey['secret'])
|
|
||||||
}
|
|
||||||
|
|
||||||
self._create(tables.tsigkeys, values)
|
|
||||||
|
|
||||||
# NOTE(kiall): Prepare and execute query to install this TSIG Key on
|
|
||||||
# every domain. We use a manual query here since anything
|
|
||||||
# else would be impossibly slow.
|
|
||||||
query_select = select([
|
|
||||||
tables.domains.c.id,
|
|
||||||
"'TSIG-ALLOW-AXFR'",
|
|
||||||
"'%s'" % tsigkey['name']]
|
|
||||||
)
|
|
||||||
|
|
||||||
columns = [
|
|
||||||
tables.domain_metadata.c.domain_id,
|
|
||||||
tables.domain_metadata.c.kind,
|
|
||||||
tables.domain_metadata.c.content,
|
|
||||||
]
|
|
||||||
|
|
||||||
query = InsertFromSelect(tables.domain_metadata, query_select,
|
|
||||||
columns)
|
|
||||||
|
|
||||||
# NOTE(kiall): A TX is required for, at the least, SQLite.
|
|
||||||
self.session.begin()
|
|
||||||
self.session.execute(query)
|
|
||||||
self.session.commit()
|
|
||||||
|
|
||||||
def update_tsigkey(self, context, tsigkey):
|
|
||||||
"""Update a TSIG Key"""
|
|
||||||
values = self._get(
|
|
||||||
tables.tsigkeys,
|
|
||||||
tsigkey['id'],
|
|
||||||
exceptions.TsigKeyNotFound,
|
|
||||||
id_col=tables.tsigkeys.c.designate_id)
|
|
||||||
|
|
||||||
# Store a copy of the original name..
|
|
||||||
original_name = values['name']
|
|
||||||
|
|
||||||
values.update({
|
|
||||||
'name': tsigkey['name'],
|
|
||||||
'algorithm': tsigkey['algorithm'],
|
|
||||||
'secret': base64.b64encode(tsigkey['secret'])
|
|
||||||
})
|
|
||||||
|
|
||||||
self._update(tables.tsigkeys, values,
|
|
||||||
id_col=tables.tsigkeys.c.designate_id,
|
|
||||||
exc_notfound=exceptions.TsigKeyNotFound)
|
|
||||||
|
|
||||||
# If the name changed, Update the necessary DomainMetadata records
|
|
||||||
if original_name != tsigkey['name']:
|
|
||||||
query = tables.domain_metadata.update()\
|
|
||||||
.where(tables.domain_metadata.c.kind == 'TSIG_ALLOW_AXFR')\
|
|
||||||
.where(tables.domain_metadata.c.content == original_name)
|
|
||||||
|
|
||||||
query.values(content=tsigkey['name'])
|
|
||||||
self.session.execute(query)
|
|
||||||
|
|
||||||
def delete_tsigkey(self, context, tsigkey):
|
|
||||||
"""Delete a TSIG Key"""
|
|
||||||
try:
|
|
||||||
# Delete this TSIG Key itself
|
|
||||||
self._delete(
|
|
||||||
tables.tsigkeys, tsigkey['id'],
|
|
||||||
exceptions.TsigKeyNotFound,
|
|
||||||
id_col=tables.tsigkeys.c.designate_id)
|
|
||||||
except exceptions.TsigKeyNotFound:
|
|
||||||
# If the TSIG Key is already gone, that's ok. We're deleting it
|
|
||||||
# anyway, so just log and continue.
|
|
||||||
LOG.critical(_LC('Attempted to delete a TSIG key which is '
|
|
||||||
'not present in the backend. ID: %s') %
|
|
||||||
tsigkey['id'])
|
|
||||||
return
|
|
||||||
|
|
||||||
query = tables.domain_metadata.delete()\
|
|
||||||
.where(tables.domain_metadata.c.kind == 'TSIG-ALLOW-AXFR')\
|
|
||||||
.where(tables.domain_metadata.c.content == tsigkey['name'])
|
|
||||||
self.session.execute(query)
|
|
||||||
|
|
||||||
# Domain Methods
|
# Domain Methods
|
||||||
def create_domain(self, context, domain):
|
def create_domain(self, context, domain):
|
||||||
try:
|
try:
|
||||||
self.session.begin()
|
self.session.begin()
|
||||||
servers = self.central_service.find_servers(self.admin_context)
|
|
||||||
|
|
||||||
domain_values = {
|
domain_values = {
|
||||||
'designate_id': domain['id'],
|
'designate_id': domain['id'],
|
||||||
'name': domain['name'].rstrip('.'),
|
'name': domain['name'].rstrip('.'),
|
||||||
'master': servers[0]['name'].rstrip('.'),
|
'master': ','.join(CONF['backend:powerdns'].masters),
|
||||||
'type': CONF['backend:powerdns'].domain_type,
|
'type': 'SLAVE',
|
||||||
'account': context.tenant
|
'account': context.tenant
|
||||||
}
|
}
|
||||||
|
|
||||||
domain_ref = self._create(tables.domains, domain_values)
|
self._create(tables.domains, domain_values)
|
||||||
|
|
||||||
# Install all TSIG Keys on this domain
|
|
||||||
query = select([tables.tsigkeys.c.name])
|
|
||||||
resultproxy = self.session.execute(query)
|
|
||||||
values = [i for i in resultproxy.fetchall()]
|
|
||||||
|
|
||||||
self._update_domainmetadata(domain_ref['id'], 'TSIG-ALLOW-AXFR',
|
|
||||||
values)
|
|
||||||
|
|
||||||
# Install all Also Notify's on this domain
|
|
||||||
self._update_domainmetadata(domain_ref['id'], 'ALSO-NOTIFY',
|
|
||||||
CONF['backend:powerdns'].also_notify)
|
|
||||||
except Exception:
|
|
||||||
with excutils.save_and_reraise_exception():
|
|
||||||
self.session.rollback()
|
|
||||||
else:
|
|
||||||
self.session.commit()
|
|
||||||
|
|
||||||
def update_domain(self, context, domain):
|
|
||||||
domain_ref = self._get(tables.domains, domain['id'],
|
|
||||||
exceptions.DomainNotFound,
|
|
||||||
id_col=tables.domains.c.designate_id)
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.session.begin()
|
|
||||||
|
|
||||||
# Update the Records TTLs where necessary
|
|
||||||
query = tables.records.update()\
|
|
||||||
.where(tables.records.c.domain_id == domain_ref['id'])
|
|
||||||
query = query.where(tables.records.c.inherit_ttl == True) # noqa\
|
|
||||||
query = query.values(ttl=domain['ttl'])
|
|
||||||
self.session.execute(query)
|
|
||||||
except Exception:
|
except Exception:
|
||||||
with excutils.save_and_reraise_exception():
|
with excutils.save_and_reraise_exception():
|
||||||
self.session.rollback()
|
self.session.rollback()
|
||||||
@@ -293,9 +132,8 @@ class PowerDNSBackend(base.Backend):
|
|||||||
|
|
||||||
def delete_domain(self, context, domain):
|
def delete_domain(self, context, domain):
|
||||||
try:
|
try:
|
||||||
domain_ref = self._get(tables.domains, domain['id'],
|
self._get(tables.domains, domain['id'], exceptions.DomainNotFound,
|
||||||
exceptions.DomainNotFound,
|
id_col=tables.domains.c.designate_id)
|
||||||
id_col=tables.domains.c.designate_id)
|
|
||||||
except exceptions.DomainNotFound:
|
except exceptions.DomainNotFound:
|
||||||
# If the Domain is already gone, that's ok. We're deleting it
|
# If the Domain is already gone, that's ok. We're deleting it
|
||||||
# anyway, so just log and continue.
|
# anyway, so just log and continue.
|
||||||
@@ -307,189 +145,3 @@ class PowerDNSBackend(base.Backend):
|
|||||||
self._delete(tables.domains, domain['id'],
|
self._delete(tables.domains, domain['id'],
|
||||||
exceptions.DomainNotFound,
|
exceptions.DomainNotFound,
|
||||||
id_col=tables.domains.c.designate_id)
|
id_col=tables.domains.c.designate_id)
|
||||||
|
|
||||||
# Ensure the records are deleted
|
|
||||||
query = tables.records.delete()\
|
|
||||||
.where(tables.records.c.domain_id == domain_ref['id'])
|
|
||||||
self.session.execute(query)
|
|
||||||
|
|
||||||
# Ensure domainmetadata is deleted
|
|
||||||
query = tables.domain_metadata.delete()\
|
|
||||||
.where(tables.domain_metadata.c.domain_id == domain_ref['id'])
|
|
||||||
self.session.execute(query)
|
|
||||||
|
|
||||||
# RecordSet Methods
|
|
||||||
def create_recordset(self, context, domain, recordset):
|
|
||||||
try:
|
|
||||||
self.session.begin(subtransactions=True)
|
|
||||||
|
|
||||||
# Create all the records..
|
|
||||||
for record in recordset.records:
|
|
||||||
self.create_record(context, domain, recordset, record)
|
|
||||||
except Exception:
|
|
||||||
with excutils.save_and_reraise_exception():
|
|
||||||
self.session.rollback()
|
|
||||||
else:
|
|
||||||
self.session.commit()
|
|
||||||
|
|
||||||
def update_recordset(self, context, domain, recordset):
|
|
||||||
# TODO(kiall): This is a total kludge. Intended as the simplest
|
|
||||||
# possible fix for the issue. This needs to be
|
|
||||||
# re-implemented correctly.
|
|
||||||
try:
|
|
||||||
self.session.begin(subtransactions=True)
|
|
||||||
|
|
||||||
self.delete_recordset(context, domain, recordset)
|
|
||||||
self.create_recordset(context, domain, recordset)
|
|
||||||
except Exception:
|
|
||||||
with excutils.save_and_reraise_exception():
|
|
||||||
self.session.rollback()
|
|
||||||
else:
|
|
||||||
self.session.commit()
|
|
||||||
|
|
||||||
def delete_recordset(self, context, domain, recordset):
|
|
||||||
# Ensure records are deleted
|
|
||||||
query = tables.records.delete()\
|
|
||||||
.where(tables.records.c.designate_recordset_id == recordset['id'])
|
|
||||||
self.session.execute(query)
|
|
||||||
|
|
||||||
# Record Methods
|
|
||||||
def create_record(self, context, domain, recordset, record):
|
|
||||||
domain_ref = self._get(tables.domains, domain['id'],
|
|
||||||
exceptions.DomainNotFound,
|
|
||||||
id_col=tables.domains.c.designate_id)
|
|
||||||
|
|
||||||
# Priority is stored in the data field for MX / SRV
|
|
||||||
priority, data = utils.extract_priority_from_data(
|
|
||||||
recordset['type'], record)
|
|
||||||
|
|
||||||
content = self._sanitize_content(recordset['type'], data)
|
|
||||||
ttl = domain['ttl'] if recordset['ttl'] is None else recordset['ttl']
|
|
||||||
|
|
||||||
record_values = {
|
|
||||||
'designate_id': record['id'],
|
|
||||||
'designate_recordset_id': record['recordset_id'],
|
|
||||||
'domain_id': domain_ref['id'],
|
|
||||||
'name': recordset['name'].rstrip('.'),
|
|
||||||
'type': recordset['type'],
|
|
||||||
'content': content,
|
|
||||||
'ttl': ttl,
|
|
||||||
'inherit_ttl': True if recordset['ttl'] is None else False,
|
|
||||||
'prio': priority,
|
|
||||||
'auth': self._is_authoritative(domain, recordset, record)
|
|
||||||
}
|
|
||||||
|
|
||||||
self._create(tables.records, record_values)
|
|
||||||
|
|
||||||
def update_record(self, context, domain, recordset, record):
|
|
||||||
record_ref = self._get_record(record['id'])
|
|
||||||
|
|
||||||
# Priority is stored in the data field for MX / SRV
|
|
||||||
priority, data = utils.extract_priority_from_data(
|
|
||||||
recordset['type'], record)
|
|
||||||
|
|
||||||
content = self._sanitize_content(recordset['type'], data)
|
|
||||||
ttl = domain['ttl'] if recordset['ttl'] is None else recordset['ttl']
|
|
||||||
|
|
||||||
record_ref.update({
|
|
||||||
'content': content,
|
|
||||||
'ttl': ttl,
|
|
||||||
'inherit_ttl': True if recordset['ttl'] is None else False,
|
|
||||||
'prio': priority,
|
|
||||||
'auth': self._is_authoritative(domain, recordset, record)
|
|
||||||
})
|
|
||||||
|
|
||||||
self._update(tables.records, record_ref,
|
|
||||||
exc_notfound=exceptions.RecordNotFound)
|
|
||||||
|
|
||||||
def delete_record(self, context, domain, recordset, record):
|
|
||||||
try:
|
|
||||||
record_ref = self._get(tables.records, record['id'],
|
|
||||||
exceptions.RecordNotFound,
|
|
||||||
id_col=tables.records.c.designate_id)
|
|
||||||
except exceptions.RecordNotFound:
|
|
||||||
# If the Record is already gone, that's ok. We're deleting it
|
|
||||||
# anyway, so just log and continue.
|
|
||||||
LOG.critical(_LC('Attempted to delete a record which is '
|
|
||||||
'not present in the backend. ID: %s') %
|
|
||||||
record['id'])
|
|
||||||
else:
|
|
||||||
self._delete(tables.records, record_ref['id'],
|
|
||||||
exceptions.RecordNotFound)
|
|
||||||
|
|
||||||
# Internal Methods
|
|
||||||
def _update_domainmetadata(self, domain_id, kind, values=None,
|
|
||||||
delete=True):
|
|
||||||
"""Updates a domain's metadata with new values"""
|
|
||||||
# Fetch all current metadata of the specified kind
|
|
||||||
values = values or []
|
|
||||||
|
|
||||||
query = select([tables.domain_metadata.c.content])\
|
|
||||||
.where(tables.domain_metadata.c.domain_id == domain_id)\
|
|
||||||
.where(tables.domain_metadata.c.kind == kind)
|
|
||||||
resultproxy = self.session.execute(query)
|
|
||||||
results = resultproxy.fetchall()
|
|
||||||
|
|
||||||
for metadata_id, content in results:
|
|
||||||
if content not in values:
|
|
||||||
if delete:
|
|
||||||
LOG.debug('Deleting stale domain metadata: %r' %
|
|
||||||
([domain_id, kind, content],))
|
|
||||||
# Delete no longer necessary values
|
|
||||||
# We should never get a notfound here, so UnknownFailure is
|
|
||||||
# a reasonable choice.
|
|
||||||
self._delete(tables.domain_metadata, metadata_id,
|
|
||||||
exceptions.UnknownFailure)
|
|
||||||
else:
|
|
||||||
# Remove pre-existing values from the list of values to insert
|
|
||||||
values.remove(content)
|
|
||||||
|
|
||||||
# Insert new values
|
|
||||||
for value in values:
|
|
||||||
LOG.debug('Inserting new domain metadata: %r' %
|
|
||||||
([domain_id, kind, value],))
|
|
||||||
self._create(
|
|
||||||
tables.domain_metadata,
|
|
||||||
{
|
|
||||||
"domain_id": domain_id,
|
|
||||||
"kind": kind,
|
|
||||||
"content": value
|
|
||||||
})
|
|
||||||
|
|
||||||
def _is_authoritative(self, domain, recordset, record):
|
|
||||||
# NOTE(kiall): See http://doc.powerdns.com/dnssec-modes.html
|
|
||||||
if recordset['type'] == 'NS' and recordset['name'] != domain['name']:
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _sanitize_content(self, type, content):
|
|
||||||
if type in ('CNAME', 'MX', 'SRV', 'NS', 'PTR'):
|
|
||||||
return content.rstrip('.')
|
|
||||||
|
|
||||||
if type in ('TXT', 'SPF'):
|
|
||||||
return '"%s"' % content.replace('"', '\\"')
|
|
||||||
|
|
||||||
return content
|
|
||||||
|
|
||||||
def _get_record(self, record_id=None, domain=None, type_=None):
|
|
||||||
query = select([tables.records])
|
|
||||||
|
|
||||||
if record_id:
|
|
||||||
query = query.where(tables.records.c.designate_id == record_id)
|
|
||||||
|
|
||||||
if type_:
|
|
||||||
query = query.where(tables.records.c.type == type_)
|
|
||||||
|
|
||||||
if domain:
|
|
||||||
query = query.where(tables.records.c.domain_id == domain['id'])
|
|
||||||
|
|
||||||
resultproxy = self.session.execute(query)
|
|
||||||
results = resultproxy.fetchall()
|
|
||||||
|
|
||||||
if len(results) < 1:
|
|
||||||
raise exceptions.RecordNotFound('No record found')
|
|
||||||
elif len(results) > 1:
|
|
||||||
raise exceptions.RecordNotFound('Too many records found')
|
|
||||||
else:
|
|
||||||
return _map_col(query.columns.keys(), results[0])
|
|
||||||
|
@@ -13,8 +13,7 @@
|
|||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
from sqlalchemy import MetaData, Table, Column, String, Text, Integer, Boolean
|
from sqlalchemy import MetaData, Table, Column, String, Integer
|
||||||
|
|
||||||
|
|
||||||
from oslo.config import cfg
|
from oslo.config import cfg
|
||||||
|
|
||||||
@@ -25,27 +24,6 @@ CONF = cfg.CONF
|
|||||||
|
|
||||||
metadata = MetaData()
|
metadata = MetaData()
|
||||||
|
|
||||||
tsigkeys = Table(
|
|
||||||
'tsigkeys', metadata,
|
|
||||||
Column('id', Integer(), primary_key=True, autoincrement=True),
|
|
||||||
|
|
||||||
Column('designate_id', UUID(), nullable=False),
|
|
||||||
Column('name', String(255), default=None, nullable=True),
|
|
||||||
Column('algorithm', String(255), default=None, nullable=True),
|
|
||||||
Column('secret', String(255), default=None, nullable=True),
|
|
||||||
mysql_engine='InnoDB',
|
|
||||||
mysql_charset='utf8')
|
|
||||||
|
|
||||||
domain_metadata = Table(
|
|
||||||
'domainmetadata', metadata,
|
|
||||||
Column('id', Integer(), primary_key=True, autoincrement=True),
|
|
||||||
|
|
||||||
Column('domain_id', Integer(), nullable=False),
|
|
||||||
Column('kind', String(16), default=None, nullable=True),
|
|
||||||
Column('content', Text()),
|
|
||||||
mysql_engine='InnoDB',
|
|
||||||
mysql_charset='utf8')
|
|
||||||
|
|
||||||
domains = Table(
|
domains = Table(
|
||||||
'domains', metadata,
|
'domains', metadata,
|
||||||
Column('id', Integer, primary_key=True, autoincrement=True),
|
Column('id', Integer, primary_key=True, autoincrement=True),
|
||||||
@@ -59,22 +37,3 @@ domains = Table(
|
|||||||
Column('account', String(40), default=None, nullable=True),
|
Column('account', String(40), default=None, nullable=True),
|
||||||
mysql_engine='InnoDB',
|
mysql_engine='InnoDB',
|
||||||
mysql_charset='utf8')
|
mysql_charset='utf8')
|
||||||
|
|
||||||
records = Table(
|
|
||||||
'records', metadata,
|
|
||||||
Column('id', Integer, primary_key=True, autoincrement=True),
|
|
||||||
|
|
||||||
Column('designate_id', UUID(), nullable=False),
|
|
||||||
Column('designate_recordset_id', UUID(), default=None, nullable=True),
|
|
||||||
Column('domain_id', Integer(), default=None, nullable=True),
|
|
||||||
Column('name', String(255), default=None, nullable=True),
|
|
||||||
Column('type', String(10), default=None, nullable=True),
|
|
||||||
Column('content', Text(), default=None, nullable=True),
|
|
||||||
Column('ttl', Integer(), default=None, nullable=True),
|
|
||||||
Column('prio', Integer(), default=None, nullable=True),
|
|
||||||
Column('change_date', Integer(), default=None, nullable=True),
|
|
||||||
Column('ordername', String(255), default=None, nullable=True),
|
|
||||||
Column('auth', Boolean(), default=None, nullable=True),
|
|
||||||
Column('inherit_ttl', Boolean(), default=True),
|
|
||||||
mysql_engine='InnoDB',
|
|
||||||
mysql_charset='utf8')
|
|
||||||
|
@@ -1,202 +0,0 @@
|
|||||||
# Copyright 2014 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 threading
|
|
||||||
|
|
||||||
from oslo.config import cfg
|
|
||||||
from oslo.db import options
|
|
||||||
from oslo.utils import excutils
|
|
||||||
from sqlalchemy.sql import select
|
|
||||||
|
|
||||||
from designate.openstack.common import log as logging
|
|
||||||
from designate import exceptions
|
|
||||||
from designate.i18n import _LC
|
|
||||||
from designate.backend import base
|
|
||||||
from designate.backend.impl_powerdns_mdns import tables
|
|
||||||
from designate.sqlalchemy import session
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
|
||||||
CONF = cfg.CONF
|
|
||||||
|
|
||||||
|
|
||||||
def _map_col(keys, col):
|
|
||||||
return dict([(keys[i], col[i]) for i in range(len(keys))])
|
|
||||||
|
|
||||||
|
|
||||||
class PowerDNSMDNSBackend(base.Backend):
|
|
||||||
__plugin_name__ = 'powerdns_mdns'
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_cfg_opts(cls):
|
|
||||||
group = cfg.OptGroup(
|
|
||||||
name='backend:powerdns_mdns',
|
|
||||||
title="Configuration for PowerDNS MDNS Backend"
|
|
||||||
)
|
|
||||||
|
|
||||||
opts = [
|
|
||||||
cfg.ListOpt('masters',
|
|
||||||
help="Master servers from which to transfer from."),
|
|
||||||
] + options.database_opts
|
|
||||||
|
|
||||||
# TODO(kiall):
|
|
||||||
# Overide the default DB connection registered above, to avoid name
|
|
||||||
# conflicts between the Designate and PowerDNS databases.
|
|
||||||
# CONF.set_default('connection',
|
|
||||||
# 'sqlite:///$state_path/powerdns.sqlite',
|
|
||||||
# group='backend:powerdns')
|
|
||||||
return [(group, opts)]
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(PowerDNSMDNSBackend, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
self.local_store = threading.local()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def session(self):
|
|
||||||
# NOTE: This uses a thread local store, allowing each greenthread to
|
|
||||||
# have it's own session stored correctly. Without this, each
|
|
||||||
# greenthread may end up using a single global session, which
|
|
||||||
# leads to bad things happening.
|
|
||||||
global LOCAL_STORE
|
|
||||||
|
|
||||||
if not hasattr(self.local_store, 'session'):
|
|
||||||
self.local_store.session = session.get_session(self.name)
|
|
||||||
|
|
||||||
return self.local_store.session
|
|
||||||
|
|
||||||
def _create(self, table, values):
|
|
||||||
query = table.insert()
|
|
||||||
|
|
||||||
resultproxy = self.session.execute(query, values)
|
|
||||||
|
|
||||||
# Refetch the row, for generated columns etc
|
|
||||||
query = select([table])\
|
|
||||||
.where(table.c.id == resultproxy.inserted_primary_key[0])
|
|
||||||
resultproxy = self.session.execute(query)
|
|
||||||
|
|
||||||
return _map_col(query.columns.keys(), resultproxy.fetchone())
|
|
||||||
|
|
||||||
def _get(self, table, id_, exc_notfound, id_col=None):
|
|
||||||
if id_col is None:
|
|
||||||
id_col = table.c.id
|
|
||||||
|
|
||||||
query = select([table])\
|
|
||||||
.where(id_col == id_)
|
|
||||||
|
|
||||||
resultproxy = self.session.execute(query)
|
|
||||||
|
|
||||||
results = resultproxy.fetchall()
|
|
||||||
|
|
||||||
if len(results) != 1:
|
|
||||||
raise exc_notfound()
|
|
||||||
|
|
||||||
# Map col keys to values in result
|
|
||||||
return _map_col(query.columns.keys(), results[0])
|
|
||||||
|
|
||||||
def _delete(self, table, id_, exc_notfound, id_col=None):
|
|
||||||
if id_col is None:
|
|
||||||
id_col = table.c.id
|
|
||||||
|
|
||||||
query = table.delete()\
|
|
||||||
.where(id_col == id_)
|
|
||||||
|
|
||||||
resultproxy = self.session.execute(query)
|
|
||||||
|
|
||||||
if resultproxy.rowcount != 1:
|
|
||||||
raise exc_notfound()
|
|
||||||
|
|
||||||
def create_tsigkey(self, context, tsigkey):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def update_tsigkey(self, context, tsigkey):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def delete_tsigkey(self, context, tsigkey):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def create_server(self, context, server):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def update_server(self, context, server):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def delete_server(self, context, server):
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Domain Methods
|
|
||||||
def create_domain(self, context, domain):
|
|
||||||
try:
|
|
||||||
self.session.begin()
|
|
||||||
|
|
||||||
domain_values = {
|
|
||||||
'designate_id': domain['id'],
|
|
||||||
'name': domain['name'].rstrip('.'),
|
|
||||||
'master': ','.join(CONF['backend:powerdns_mdns'].masters),
|
|
||||||
'type': 'SLAVE',
|
|
||||||
'account': context.tenant
|
|
||||||
}
|
|
||||||
|
|
||||||
self._create(tables.domains, domain_values)
|
|
||||||
except Exception:
|
|
||||||
with excutils.save_and_reraise_exception():
|
|
||||||
self.session.rollback()
|
|
||||||
else:
|
|
||||||
self.session.commit()
|
|
||||||
|
|
||||||
def update_domain(self, context, domain):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def delete_domain(self, context, domain):
|
|
||||||
try:
|
|
||||||
self._get(tables.domains, domain['id'], exceptions.DomainNotFound,
|
|
||||||
id_col=tables.domains.c.designate_id)
|
|
||||||
except exceptions.DomainNotFound:
|
|
||||||
# If the Domain is already gone, that's ok. We're deleting it
|
|
||||||
# anyway, so just log and continue.
|
|
||||||
LOG.critical(_LC('Attempted to delete a domain which is '
|
|
||||||
'not present in the backend. ID: %s') %
|
|
||||||
domain['id'])
|
|
||||||
return
|
|
||||||
|
|
||||||
self._delete(tables.domains, domain['id'],
|
|
||||||
exceptions.DomainNotFound,
|
|
||||||
id_col=tables.domains.c.designate_id)
|
|
||||||
|
|
||||||
def create_recordset(self, context, domain, recordset):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def update_recordset(self, context, domain, recordset):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def delete_recordset(self, context, domain, recordset):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def create_record(self, context, domain, recordset, record):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def update_record(self, context, domain, recordset, record):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def delete_record(self, context, domain, recordset, record):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def sync_domain(self, context, domain, records):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def sync_record(self, context, domain, record):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def ping(self, context):
|
|
||||||
pass
|
|
@@ -1,23 +0,0 @@
|
|||||||
# Copyright 2012-2014 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 designate.backend.impl_powerdns import tables
|
|
||||||
|
|
||||||
|
|
||||||
# NOTE(kiall): We import the domains table object here in order to ensure
|
|
||||||
# the PowerDNS mDNS based driver uses only this one table. When
|
|
||||||
# the original PowerDNS driver is removed, we'll move the domains
|
|
||||||
# table definition here.
|
|
||||||
domains = tables.domains
|
|
@@ -1,59 +0,0 @@
|
|||||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
|
||||||
#
|
|
||||||
# Author: Kiall Mac Innes <kiall@hp.com>
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
import os
|
|
||||||
|
|
||||||
from migrate.versioning import api as versioning_api
|
|
||||||
from oslo.config import cfg
|
|
||||||
from oslo.db.sqlalchemy.migration_cli import manager as migration_manager
|
|
||||||
|
|
||||||
from designate.openstack.common import log as logging
|
|
||||||
from designate.manage import base
|
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
REPOSITORY = os.path.abspath(os.path.join(os.path.dirname(__file__), '..',
|
|
||||||
'backend', 'impl_powerdns',
|
|
||||||
'migrate_repo'))
|
|
||||||
cfg.CONF.import_opt('connection', 'designate.backend.impl_powerdns_mdns',
|
|
||||||
group='backend:powerdns_mdns')
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
|
||||||
|
|
||||||
|
|
||||||
def get_manager():
|
|
||||||
migration_config = {
|
|
||||||
'migration_repo_path': REPOSITORY,
|
|
||||||
'db_url': CONF['backend:powerdns_mdns'].connection}
|
|
||||||
return migration_manager.MigrationManager(migration_config)
|
|
||||||
|
|
||||||
|
|
||||||
class DatabaseCommands(base.Commands):
|
|
||||||
def version(self):
|
|
||||||
current = get_manager().version()
|
|
||||||
latest = versioning_api.version(repository=REPOSITORY).value
|
|
||||||
print("Current: %s Latest: %s" % (current, latest))
|
|
||||||
|
|
||||||
def sync(self):
|
|
||||||
get_manager().upgrade(None)
|
|
||||||
|
|
||||||
@base.args('revision', nargs='?')
|
|
||||||
def upgrade(self, revision):
|
|
||||||
get_manager().upgrade(revision)
|
|
||||||
|
|
||||||
@base.args('revision', nargs='?')
|
|
||||||
def downgrade(self, revision):
|
|
||||||
get_manager().downgrade(revision)
|
|
@@ -26,7 +26,6 @@ class BackendTestCase(tests.TestCase, BackendTestMixin):
|
|||||||
('fake', dict(backend_driver='fake', group='service:agent')),
|
('fake', dict(backend_driver='fake', group='service:agent')),
|
||||||
('nsd4slave', dict(backend_driver='nsd4slave', group='service:agent',
|
('nsd4slave', dict(backend_driver='nsd4slave', group='service:agent',
|
||||||
server_fixture=NSD4Fixture)),
|
server_fixture=NSD4Fixture)),
|
||||||
('powerdns', dict(backend_driver='powerdns', group='service:agent')),
|
|
||||||
('ipa', dict(backend_driver='ipa', group='service:agent'))
|
('ipa', dict(backend_driver='ipa', group='service:agent'))
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@@ -1,119 +0,0 @@
|
|||||||
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
|
|
||||||
#
|
|
||||||
# Author: Artom Lifshitz <artom.lifshitz@enovance.com>
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
from mock import MagicMock
|
|
||||||
|
|
||||||
from designate import tests
|
|
||||||
from designate.tests import DatabaseFixture
|
|
||||||
from designate.tests.test_backend import BackendTestMixin
|
|
||||||
from designate import utils
|
|
||||||
|
|
||||||
|
|
||||||
# impl_powerdns needs to register its options before being instanciated.
|
|
||||||
# Import it and pretend to use it to avoid flake8 unused import errors.
|
|
||||||
from designate.backend import impl_powerdns
|
|
||||||
impl_powerdns
|
|
||||||
|
|
||||||
REPOSITORY = os.path.abspath(os.path.join(os.path.dirname(__file__),
|
|
||||||
'..', '..',
|
|
||||||
'backend', 'impl_powerdns',
|
|
||||||
'migrate_repo'))
|
|
||||||
|
|
||||||
|
|
||||||
class PowerDNSBackendTestCase(tests.TestCase, BackendTestMixin):
|
|
||||||
|
|
||||||
def get_tsigkey_fixture(self):
|
|
||||||
return super(PowerDNSBackendTestCase, self).get_tsigkey_fixture(
|
|
||||||
values={
|
|
||||||
'id': utils.generate_uuid()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_server_fixture(self):
|
|
||||||
return super(PowerDNSBackendTestCase, self).get_server_fixture(
|
|
||||||
values={
|
|
||||||
'id': utils.generate_uuid()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_domain_fixture(self):
|
|
||||||
return super(PowerDNSBackendTestCase, self).get_domain_fixture(
|
|
||||||
values={
|
|
||||||
'id': utils.generate_uuid(),
|
|
||||||
'ttl': 42,
|
|
||||||
'serial': 42,
|
|
||||||
'refresh': 42,
|
|
||||||
'retry': 42,
|
|
||||||
'expire': 42,
|
|
||||||
'minimum': 42,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(PowerDNSBackendTestCase, self).setUp()
|
|
||||||
self.db_fixture = DatabaseFixture.get_fixture(REPOSITORY)
|
|
||||||
self.useFixture(self.db_fixture)
|
|
||||||
self.config(backend_driver='powerdns', group='service:agent')
|
|
||||||
self.config(connection=self.db_fixture.url,
|
|
||||||
group='backend:powerdns')
|
|
||||||
self.backend = self.get_backend_driver()
|
|
||||||
self.backend.start()
|
|
||||||
# Since some CRUD methods in impl_powerdns call central's find_servers
|
|
||||||
# method, mock it up to return our fixture.
|
|
||||||
self.backend.central_service.find_servers = MagicMock(
|
|
||||||
return_value=[self.get_server_fixture()])
|
|
||||||
|
|
||||||
def test_create_tsigkey(self):
|
|
||||||
context = self.get_context()
|
|
||||||
tsigkey = self.get_tsigkey_fixture()
|
|
||||||
self.backend.create_tsigkey(context, tsigkey)
|
|
||||||
|
|
||||||
def test_update_tsigkey(self):
|
|
||||||
context = self.get_context()
|
|
||||||
tsigkey = self.get_tsigkey_fixture()
|
|
||||||
self.backend.create_tsigkey(context, tsigkey)
|
|
||||||
self.backend.update_tsigkey(context, tsigkey)
|
|
||||||
|
|
||||||
def test_delete_tsigkey(self):
|
|
||||||
context = self.get_context()
|
|
||||||
tsigkey = self.get_tsigkey_fixture()
|
|
||||||
self.backend.create_tsigkey(context, tsigkey)
|
|
||||||
self.backend.delete_tsigkey(context, tsigkey)
|
|
||||||
|
|
||||||
def test_create_domain(self):
|
|
||||||
context = self.get_context()
|
|
||||||
server = self.get_server_fixture()
|
|
||||||
domain = self.get_domain_fixture()
|
|
||||||
self.backend.create_server(context, server)
|
|
||||||
self.backend.create_domain(context, domain)
|
|
||||||
|
|
||||||
def test_update_domain(self):
|
|
||||||
context = self.get_context()
|
|
||||||
server = self.get_server_fixture()
|
|
||||||
domain = self.get_domain_fixture()
|
|
||||||
self.backend.create_server(context, server)
|
|
||||||
self.backend.create_domain(context, domain)
|
|
||||||
self.backend.update_domain(context, domain)
|
|
||||||
|
|
||||||
def test_delete_domain(self):
|
|
||||||
context = self.get_context()
|
|
||||||
server = self.get_server_fixture()
|
|
||||||
domain = self.get_domain_fixture()
|
|
||||||
self.backend.create_server(context, server)
|
|
||||||
self.backend.create_domain(context, domain)
|
|
||||||
self.backend.delete_domain(context, domain)
|
|
@@ -71,7 +71,6 @@ designate.backend =
|
|||||||
bind9 = designate.backend.impl_bind9:Bind9Backend
|
bind9 = designate.backend.impl_bind9:Bind9Backend
|
||||||
bind9_pool = designate.backend.impl_bind9_pool:Bind9PoolBackend
|
bind9_pool = designate.backend.impl_bind9_pool:Bind9PoolBackend
|
||||||
powerdns = designate.backend.impl_powerdns:PowerDNSBackend
|
powerdns = designate.backend.impl_powerdns:PowerDNSBackend
|
||||||
powerdns_mdns = designate.backend.impl_powerdns_mdns:PowerDNSMDNSBackend
|
|
||||||
rpc = designate.backend.impl_rpc:RPCBackend
|
rpc = designate.backend.impl_rpc:RPCBackend
|
||||||
fake = designate.backend.impl_fake:FakeBackend
|
fake = designate.backend.impl_fake:FakeBackend
|
||||||
nsd4slave = designate.backend.impl_nsd4slave:NSD4SlaveBackend
|
nsd4slave = designate.backend.impl_nsd4slave:NSD4SlaveBackend
|
||||||
@@ -92,7 +91,6 @@ designate.manage =
|
|||||||
database = designate.manage.database:DatabaseCommands
|
database = designate.manage.database:DatabaseCommands
|
||||||
pool-manager-cache = designate.manage.pool_manager_cache:DatabaseCommands
|
pool-manager-cache = designate.manage.pool_manager_cache:DatabaseCommands
|
||||||
powerdns = designate.manage.powerdns:DatabaseCommands
|
powerdns = designate.manage.powerdns:DatabaseCommands
|
||||||
powerdns-mdns = designate.manage.powerdns_mdns:DatabaseCommands
|
|
||||||
tlds = designate.manage.tlds:TLDCommands
|
tlds = designate.manage.tlds:TLDCommands
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user