Add a new PowerDNS backend based on MiniDNS

This is stage one of phasing out the old PowerDNS driver, as we move
through the Kilo cycle and mDNS/pools become reality, the original driver
can be removed.

Implements-Blueprint: transition-powerdns-to-mdns
Change-Id: I7a3aa0a894e2c0a4fa565e8d58cccba3644e0cac
This commit is contained in:
Kiall Mac Innes
2014-10-09 18:01:19 +01:00
parent c1364da4d1
commit 74817f90aa
5 changed files with 397 additions and 0 deletions

View File

@@ -0,0 +1,115 @@
# 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_MDNS_PORT"
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

View File

@@ -0,0 +1,198 @@
# 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
OPTS = [
cfg.ListOpt('masters', help="Master servers from which to transfer from."),
]
CONF.register_group(cfg.OptGroup(
name='backend:powerdns_mdns',
title="Configuration for PowerDNS MDNS Backend"
))
CONF.register_opts(OPTS + options.database_opts, group='backend:powerdns_mdns')
# 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_mdns.sqlite',
group='backend:powerdns_mdns')
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'
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

View File

@@ -0,0 +1,23 @@
# 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

View File

@@ -0,0 +1,59 @@
# 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)

View File

@@ -66,6 +66,7 @@ designate.notification.handler =
designate.backend =
bind9 = designate.backend.impl_bind9:Bind9Backend
powerdns = designate.backend.impl_powerdns:PowerDNSBackend
powerdns_mdns = designate.backend.impl_powerdns_mdns:PowerDNSMDNSBackend
rpc = designate.backend.impl_rpc:RPCBackend
fake = designate.backend.impl_fake:FakeBackend
nsd4slave = designate.backend.impl_nsd4slave:NSD4SlaveBackend
@@ -84,6 +85,7 @@ designate.quota =
designate.manage =
database = designate.manage.database:DatabaseCommands
powerdns = designate.manage.powerdns:DatabaseCommands
powerdns-mdns = designate.manage.powerdns_mdns:DatabaseCommands
tlds = designate.manage.tlds:TLDCommands