Remove deprecated designate-agent

This patch completely removes the designate-agent service
and all agent related plugins.

Change-Id: Ibe4011fb85797282fa44742d4b9c9b7c00e78622
This commit is contained in:
Erik Olof Gunnar Andersson 2023-08-17 14:15:26 +02:00
parent d2d5e2bfc6
commit 2868db4f66
65 changed files with 10 additions and 5322 deletions

View File

@ -1,110 +0,0 @@
#! /bin/bash
### BEGIN INIT INFO
# Provides: tinydns
# Required-Start: $local_fs $remote_fs $network
# Required-Stop: $local_fs $remote_fs $network
# Should-Start: $syslog
# Should-Stop: $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: tinydns daemon processes
# Description: Start the TinyDNS resolver
### END INIT INFO
# Documentation
# man tinydns
# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh
# Define LSB log_* functions.
. /lib/lsb/init-functions
NAME=tinydns
DAEMON=/usr/bin/$NAME
DAEMON_USER=djbdns
DESC="the tinydns daemon"
ROOTDIR=/var/lib/djbdns
PATH=/sbin:/bin:/usr/sbin:/usr/bin
LAUNCHER=/usr/bin/envuidgid
LAUNCHER_ARGS="$DAEMON_USER envdir ./env softlimit -d300000 $DAEMON"
PIDFILE=/run/$NAME.pid
# Exit if executable is not installed
[ -x "$DAEMON" ] || exit 0
set -x
case "$1" in
start)
if [ ! -d "$ROOTDIR" ]; then
log_action_msg "Not starting $DESC: $ROOTDIR is missing."
exit 0
fi
log_action_begin_msg "Starting $DESC"
if start-stop-daemon --stop --signal 0 --quiet --pidfile $PIDFILE --exec $DAEMON; then
log_action_end_msg 0 "already running"
else
if start-stop-daemon --start --verbose --make-pidfile --chdir $ROOTDIR --pidfile $PIDFILE --exec $LAUNCHER -- $LAUNCHER_ARGS
then
log_action_end_msg 0
else
log_action_end_msg 1
exit 1
fi
fi
;;
stop)
log_action_begin_msg "Stopping $DESC"
pid=$(cat $PIDFILE 2>/dev/null) || true
if test ! -f $PIDFILE -o -z "$pid"; then
log_action_end_msg 0 "not running - there is no $PIDFILE"
exit 0
fi
if start-stop-daemon --stop --signal INT --quiet --pidfile $PIDFILE --exec $DAEMON; then
rm -f $PIDFILE
elif kill -0 $pid 2>/dev/null; then
log_action_end_msg 1 "Is $pid not $NAME? Is $DAEMON a different binary now?"
exit 1
else
log_action_end_msg 1 "$DAEMON died: process $pid not running; or permission denied"
exit 1
fi
;;
reload)
echo "Not implemented, use restart"
exit 1
;;
restart|force-reload)
$0 stop
$0 start
;;
status)
if test ! -r $(dirname $PIDFILE); then
log_failure_msg "cannot read PID file $PIDFILE"
exit 4
fi
pid=$(cat $PIDFILE 2>/dev/null) || true
if test ! -f $PIDFILE -o -z "$pid"; then
log_failure_msg "$NAME is not running"
exit 3
fi
if ps "$pid" >/dev/null 2>&1; then
log_success_msg "$NAME is running"
exit 0
else
log_failure_msg "$NAME is not running"
exit 1
fi
;;
*)
log_action_msg "Usage: $0 {start|stop|restart|force-reload|status}" >&2
exit 1
;;
esac
exit 0

View File

@ -1,44 +0,0 @@
#
# Replace /var/lib/djbdns if needed
#
[Unit]
Description=tinydns DNS resolver
Documentation=man:tinydns
Documentation=https://cr.yp.to/djbdns.html
After=network.target
Requires=network.target
Wants=network.target
ConditionPathExists=/var/lib/djbdns
[Service]
Type=forking
PIDFile=/run/tinydns.pid
Environment="ROOT=/var/lib/djbdns"
ExecStart=/usr/bin/tinydns
ExecStop=-/sbin/start-stop-daemon --quiet --stop --retry=TERM/5/KILL/5 --pidfile /run/tinydns.pid
TimeoutStopSec=30
KillMode=mixed
PermissionsStartOnly=true
Restart=on-abnormal
RestartSec=2s
LimitNOFILE=65536
WorkingDirectory=/var/lib/djbdns
User=$ug_name
Group=$ug_name
# Hardening
# CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_CHOWN CAP_FOWNER
NoNewPrivileges=yes
PrivateDevices=yes
PrivateTmp=yes
ProtectHome=yes
ProtectSystem=full
# TODO: restrict ReadOnlyDirectories
ReadOnlyDirectories=/
ReadWriteDirectories=-/var/lib/djbdns
[Install]
WantedBy=multi-user.target

View File

@ -1,258 +0,0 @@
# Copyright 2014 Rackspace Inc.
#
# Author: Tim Simmons <tim.simmons@rackspace.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
agent.handler
~~~~~~~~~~~~~
Typically runs on the resolver hosts. Listen for incoming DNS requests
on a port different than 53 and execute create_zone/delete_zone on the
backend adaptor (e.g. Bind9)
Configured in [service:agent]
"""
import dns
import dns.flags
import dns.message
import dns.opcode
import dns.rcode
from oslo_config import cfg
from oslo_log import log as logging
from designate.backend import agent_backend
import designate.backend.private_codes as pcodes
from designate import dnsutils
from designate import utils
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
class RequestHandler(object):
def __init__(self):
self.masters = []
for server in CONF['service:agent'].masters:
raw_server = utils.split_host_port(server)
master = {'host': raw_server[0], 'port': int(raw_server[1])}
self.masters.append(master)
LOG.info("Agent masters: %(masters)s", {'masters': self.masters})
self.allow_notify = CONF['service:agent'].allow_notify
self.transfer_source = CONF['service:agent'].transfer_source
backend_driver = cfg.CONF['service:agent'].backend_driver
self.backend = agent_backend.get_backend(backend_driver, self)
# TODO(johnsom) Remove this after the agents framework is removed or
# the protocol has been updated to not use an unassigned opcode(14).
dns.opcode.Opcode = pcodes.OpcodeWith14
def __call__(self, request):
"""
:param request: DNS Request Message
:return: DNS Response Message
"""
# TODO(Tim): Handle multiple questions
rdtype = request.question[0].rdtype
rdclass = request.question[0].rdclass
opcode = request.opcode()
if opcode == dns.opcode.NOTIFY:
response = self._handle_notify(request)
elif opcode == pcodes.CC:
if rdclass == pcodes.CLASSCC:
if rdtype == pcodes.CREATE:
response = self._handle_create(request)
elif rdtype == pcodes.DELETE:
response = self._handle_delete(request)
else:
response = self._handle_query_error(request,
dns.rcode.REFUSED)
else:
response = self._handle_query_error(request, dns.rcode.REFUSED)
else:
# Unhandled OpCodes include STATUS, QUERY, IQUERY, UPDATE
response = self._handle_query_error(request, dns.rcode.REFUSED)
# TODO(Tim): Answer Type 65XXX queries
yield response
return
def _handle_query_error(self, request, rcode):
"""
Construct an error response with the rcode passed in.
:param request: The decoded request from the wire.
:param rcode: The response code to send back.
:return: A dns response message with the response code set to rcode
"""
response = dns.message.make_response(request)
response.set_rcode(rcode)
return response
def _handle_create(self, request):
response = dns.message.make_response(request)
question = request.question[0]
requester = request.environ['addr'][0]
zone_name = question.name.to_text()
if isinstance(zone_name, bytes):
zone_name = zone_name.decode('utf-8')
if not self._allowed(request, requester, "CREATE", zone_name):
response.set_rcode(dns.rcode.from_text("REFUSED"))
return response
serial = self.backend.find_zone_serial(zone_name)
if serial is not None:
# Does this warrant a warning?
# There is a race condition between checking if the zone exists
# and creating it.
LOG.warning("Not creating %(name)s, zone already exists",
{'name': zone_name})
# Provide an authoritative answer
response.flags |= dns.flags.AA
return response
LOG.debug("Received %(verb)s for %(name)s from %(host)s",
{'verb': "CREATE", 'name': zone_name, 'host': requester})
try:
# Receive an AXFR from MiniDNS to populate the zone
zone = dnsutils.do_axfr(zone_name, self.masters,
source=self.transfer_source)
self.backend.create_zone(zone)
except Exception as e:
# TODO(Federico) unknown exceptions should be logged with a full
# traceback. Same in the other methods.
LOG.error("Exception while creating zone %r", e)
response.set_rcode(dns.rcode.from_text("SERVFAIL"))
return response
# Provide an authoritative answer
response.flags |= dns.flags.AA
return response
def _handle_notify(self, request):
"""
Constructs the response to a NOTIFY and acts accordingly on it.
* Decodes the NOTIFY
* Checks if the master sending the NOTIFY is allowed to notify
* Does a serial check to see if further action needs to be taken
* Kicks off an AXFR and returns a valid response
"""
response = dns.message.make_response(request)
question = request.question[0]
requester = request.environ['addr'][0]
zone_name = question.name.to_text()
if isinstance(zone_name, bytes):
zone_name = zone_name.decode('utf-8')
if not self._allowed(request, requester, "NOTIFY", zone_name):
response.set_rcode(dns.rcode.from_text("REFUSED"))
return response
serial = self.backend.find_zone_serial(zone_name)
if serial is None:
LOG.warning("Refusing NOTIFY for %(name)s, doesn't exist",
{'name': zone_name})
response.set_rcode(dns.rcode.from_text("REFUSED"))
return response
LOG.debug("Received %(verb)s for %(name)s from %(host)s",
{'verb': "NOTIFY", 'name': zone_name, 'host': requester})
# According to RFC we should query the server that sent the NOTIFY
# TODO(Tim): Reenable this when it makes more sense
# resolver = dns.resolver.Resolver()
# resolver.nameservers = [requester]
# This assumes that the Master is running on port 53
# soa_answer = resolver.query(zone_name, 'SOA')
# Check that the serial is < serial above
try:
zone = dnsutils.do_axfr(zone_name, self.masters,
source=self.transfer_source)
self.backend.update_zone(zone)
except Exception:
response.set_rcode(dns.rcode.from_text("SERVFAIL"))
return response
# Provide an authoritative answer
response.flags |= dns.flags.AA
return response
def _handle_delete(self, request):
"""
Constructs the response to a DELETE and acts accordingly on it.
* Decodes the message for zone name
* Checks if the master sending the DELETE is in the allowed notify list
* Checks if the zone exists (maybe?)
* Kicks a call to the backend to delete the zone in question
"""
response = dns.message.make_response(request)
question = request.question[0]
requester = request.environ['addr'][0]
zone_name = question.name.to_text()
if isinstance(zone_name, bytes):
zone_name = zone_name.decode('utf-8')
if not self._allowed(request, requester, "DELETE", zone_name):
response.set_rcode(dns.rcode.from_text("REFUSED"))
return response
serial = self.backend.find_zone_serial(zone_name)
if serial is None:
LOG.warning("Not deleting %(name)s, zone doesn't exist",
{'name': zone_name})
# Provide an authoritative answer
response.flags |= dns.flags.AA
return response
LOG.debug("Received DELETE for %(name)s from %(host)s",
{'name': zone_name, 'host': requester})
# Provide an authoritative answer
response.flags |= dns.flags.AA
# Call into the backend to Delete
try:
self.backend.delete_zone(zone_name)
except Exception:
response.set_rcode(dns.rcode.from_text("SERVFAIL"))
return response
return response
def _allowed(self, request, requester, op, zone_name):
# If there are no explict notifiers specified, allow all
if not self.allow_notify:
return True
if requester not in self.allow_notify:
LOG.warning("%(verb)s for %(name)s from %(server)s refused",
{'verb': op, 'name': zone_name, 'server': requester})
return False
return True

View File

@ -1,86 +0,0 @@
# Copyright 2014 Rackspace Inc.
#
# Author: Tim Simmons <tim.simmons@rackspace.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
agent.service
~~~~~~~~~~~~~
Typically runs on the resolver hosts. Listen for incoming DNS requests
on a port different than 53 and execute create_zone/delete_zone on the
backend adaptor (e.g. Bind9)
Configured in [service:agent]
"""
import warnings
from oslo_config import cfg
from designate.agent import handler
from designate.backend import agent_backend
from designate.conf.agent import DEFAULT_AGENT_PORT
from designate import dnsmiddleware
from designate import service
from designate import utils
CONF = cfg.CONF
class Service(service.Service):
_dns_default_port = DEFAULT_AGENT_PORT
def __init__(self):
super(Service, self).__init__(
self.service_name, threads=cfg.CONF['service:agent'].threads
)
warnings.warn('The designate agent service is deprecated as of the '
'Antelope (2023.1) release and will be removed in the '
'"C" release.', DeprecationWarning)
self.dns_service = service.DNSService(
self.dns_application, self.tg,
cfg.CONF['service:agent'].listen,
cfg.CONF['service:agent'].tcp_backlog,
cfg.CONF['service:agent'].tcp_recv_timeout,
)
backend_driver = cfg.CONF['service:agent'].backend_driver
self.backend = agent_backend.get_backend(backend_driver, self)
def start(self):
super(Service, self).start()
self.dns_service.start()
self.backend.start()
def stop(self, graceful=True):
self.dns_service.stop()
self.backend.stop()
super(Service, self).stop(graceful)
@property
def service_name(self):
return 'agent'
@property
@utils.cache_result
def dns_application(self):
# Create an instance of the RequestHandler class
application = handler.RequestHandler()
if cfg.CONF['service:agent'].notify_delay > 0.0:
application = dnsmiddleware.LimitNotifyMiddleware(application)
application = dnsmiddleware.SerializationMiddleware(application)
return application

View File

@ -1,173 +0,0 @@
# Copyright 2014 Rackspace Inc.
#
# Author: Tim Simmons <tim.simmons@rackspace.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
backend.agent
~~~~~~~~~~~~~
Agent backend for Pool Manager.
Sends DNS requests to a remote agent using private OPCODEs to trigger
creation / deletion / update of zones.
Configured in the [service:pool_manager] section
"""
import dns
import dns.exception
import dns.flags
import dns.message
import dns.opcode
import dns.rcode
import dns.rdataclass
import dns.rdatatype
from oslo_config import cfg
from oslo_log import log as logging
from designate.backend import base
from designate.backend import private_codes
from designate.conf.agent import DEFAULT_AGENT_PORT
from designate import dnsutils
from designate import exceptions
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
class AgentPoolBackend(base.Backend):
__plugin_name__ = 'agent'
__backend_status__ = 'untested'
def __init__(self, target):
super(AgentPoolBackend, self).__init__(target)
self.host = self.options.get('host', '127.0.0.1')
self.port = int(self.options.get('port', DEFAULT_AGENT_PORT))
self.timeout = CONF['service:worker'].poll_timeout
self.retry_interval = CONF['service:worker'].poll_retry_interval
self.max_retries = CONF['service:worker'].poll_max_retries
# FIXME: the agent retries creating zones without any interval
# TODO(johnsom) Remove this after the agents framework is removed or
# the protocol has been updated to not use an unassigned opcode(14).
dns.opcode.Opcode = private_codes.OpcodeWith14
def create_zone(self, context, zone):
LOG.debug('Create Zone')
response = self._make_and_send_dns_message(
zone.name,
self.timeout,
private_codes.CC,
private_codes.CREATE,
private_codes.CLASSCC,
self.host,
self.port
)
if response is None:
raise exceptions.Backend('Failed create_zone()')
def update_zone(self, context, zone):
LOG.debug('Update Zone')
def delete_zone(self, context, zone):
LOG.debug('Delete Zone')
response = self._make_and_send_dns_message(
zone.name,
self.timeout,
private_codes.CC,
private_codes.DELETE,
private_codes.CLASSCC,
self.host,
self.port
)
if response is None:
raise exceptions.Backend('Failed delete_zone()')
def _make_and_send_dns_message(self, zone_name, timeout, opcode,
rdatatype, rdclass, dest_ip,
dest_port):
dns_message = self._make_dns_message(
zone_name, opcode, rdatatype, rdclass
)
LOG.info(
"Sending '%(msg)s' for '%(zone)s' to '%(server)s:%(port)d'.",
{
'msg': str(opcode),
'zone': zone_name,
'server': dest_ip,
'port': dest_port
}
)
try:
response = dnsutils.send_dns_message(
dns_message, dest_ip, port=dest_port, timeout=timeout
)
# Check that we actually got a NOERROR in the rcode and and an
# authoritative answer
if not (response.flags & dns.flags.AA) or dns.rcode.from_flags(
response.flags, response.ednsflags) != dns.rcode.NOERROR:
LOG.warning(
"Failed to get expected response while trying to "
"send '%(msg)s' for '%(zone)s' to "
"'%(server)s:%(port)d'. Response message: %(resp)s",
{
'msg': str(opcode),
'zone': zone_name,
'server': dest_ip,
'port': dest_port,
'resp': str(response)
}
)
response = None
except dns.exception.Timeout:
LOG.warning(
"Got Timeout while trying to send '%(msg)s' for "
"'%(zone)s' to '%(server)s:%(port)d'. "
"Timeout='%(timeout)d' seconds.",
{
'msg': str(opcode),
'zone': zone_name,
'server': dest_ip,
'port': dest_port,
'timeout': timeout,
}
)
response = None
except dns.query.BadResponse:
LOG.warning(
"Got BadResponse while trying to send '%(msg)s' for "
"'%(zone)s' to '%(server)s:%(port)d'. "
"Timeout='%(timeout)d' seconds.",
{
'msg': str(opcode),
'zone': zone_name,
'server': dest_ip,
'port': dest_port,
'timeout': timeout,
}
)
response = None
return response
@staticmethod
def _make_dns_message(zone_name, opcode, rdatatype, rdclass):
dns_message = dns.message.make_query(zone_name, rdatatype,
rdclass=rdclass)
dns_message.flags = 0
dns_message.set_opcode(opcode)
dns_message.flags |= dns.flags.AA
return dns_message

View File

@ -1,28 +0,0 @@
# Copyright 2014 Rackspace Inc.
#
# Author: Tim Simmons <tim.simmons@rackspace.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_log import log as logging
from designate.backend.agent_backend import base
LOG = logging.getLogger(__name__)
def get_backend(backend_driver, agent_service):
LOG.debug("Loading backend driver: %s", backend_driver)
cls = base.AgentBackend.get_driver(backend_driver)
return cls(agent_service)

View File

@ -1,55 +0,0 @@
# Copyright 2014 Rackspace Inc.
#
# Author: Tim Simmons <tim.simmons@rackspace.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import abc
from debtcollector import removals
from designate.plugin import DriverPlugin
@removals.removed_class('AgentBackend')
class AgentBackend(DriverPlugin):
"""Base class for backend implementations"""
__plugin_type__ = 'backend'
__plugin_ns__ = 'designate.backend.agent_backend'
def __init__(self, agent_service):
super(AgentBackend, self).__init__()
self.agent_service = agent_service
def start(self):
pass
def stop(self):
pass
@abc.abstractmethod
def find_zone_serial(self, zone_name):
"""Find a DNS Zone"""
@abc.abstractmethod
def create_zone(self, zone):
"""Create a DNS zone"""
"""Zone is a DNSPython Zone object"""
@abc.abstractmethod
def update_zone(self, zone):
"""Update a DNS zone"""
"""Zone is a DNSPython Zone object"""
@abc.abstractmethod
def delete_zone(self, zone_name, zone_params):
"""Delete a DNS zone"""

View File

@ -1,137 +0,0 @@
# Copyright 2014 Rackspace Inc.
#
# Author: Tim Simmons <tim.simmons@rackspace.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
import warnings
import dns
import dns.resolver
from oslo_concurrency import lockutils
from oslo_config import cfg
from oslo_log import log as logging
from designate.backend.agent_backend import base
from designate import exceptions
from designate import utils
CFG_GROUP_NAME = 'backend:agent:bind9'
LOG = logging.getLogger(__name__)
class Bind9Backend(base.AgentBackend):
__plugin_name__ = 'bind9'
__backend_status__ = 'untested'
def __init__(self, agent_service):
super(Bind9Backend, self).__init__(agent_service)
warning_msg = ('The designate agent framework and backend driver "{}" '
'are deprecated as of the Antelope (2023.1) release '
'and will be removed in the "C" '
'release.'.format(self.__plugin_name__))
warnings.warn(warning_msg, DeprecationWarning)
def start(self):
LOG.info("Started bind9 backend")
def find_zone_serial(self, zone_name):
LOG.debug("Finding %s", zone_name)
resolver = dns.resolver.Resolver()
resolver.nameservers = [cfg.CONF[CFG_GROUP_NAME].query_destination]
try:
rdata = resolver.query(zone_name, 'SOA')[0]
except Exception:
return None
return rdata.serial
def create_zone(self, zone):
LOG.debug("Creating %s", zone.origin.to_text())
self._sync_zone(zone, new_zone_flag=True)
def update_zone(self, zone):
LOG.debug("Updating %s", zone.origin.to_text())
self._sync_zone(zone)
def delete_zone(self, zone_name):
LOG.debug('Delete Zone: %s' % zone_name)
rndc_op = 'delzone'
# RNDC doesn't like the trailing dot on the zone name
rndc_call = self._rndc_base() + [rndc_op, zone_name.rstrip('.')]
utils.execute(*rndc_call)
def _rndc_base(self):
rndc_call = [
'rndc',
'-s', cfg.CONF[CFG_GROUP_NAME].rndc_host,
'-p', str(cfg.CONF[CFG_GROUP_NAME].rndc_port),
]
if cfg.CONF[CFG_GROUP_NAME].rndc_config_file:
rndc_call.extend(['-c',
cfg.CONF[CFG_GROUP_NAME].rndc_config_file])
if cfg.CONF[CFG_GROUP_NAME].rndc_key_file:
rndc_call.extend(['-k',
cfg.CONF[CFG_GROUP_NAME].rndc_key_file])
return rndc_call
def _sync_zone(self, zone, new_zone_flag=False):
"""Sync a single zone's zone file and reload bind config"""
# NOTE: Different versions of BIND9 behave differently with a trailing
# dot, so we're just going to take it off.
zone_name = zone.origin.to_text(omit_final_dot=True)
if isinstance(zone_name, bytes):
zone_name = zone_name.decode('utf-8')
# NOTE: Only one thread should be working with the Zonefile at a given
# time. The sleep(1) below introduces a not insignificant risk
# of more than 1 thread working with a zonefile at a given time.
with lockutils.lock('bind9-%s' % zone_name):
LOG.debug('Synchronising Zone: %s' % zone_name)
zone_path = cfg.CONF[CFG_GROUP_NAME].zone_file_path
output_path = os.path.join(zone_path,
'%s.zone' % zone_name)
zone.to_file(output_path, relativize=False)
rndc_call = self._rndc_base()
if new_zone_flag:
rndc_op = [
'addzone',
'%s { type master; file "%s"; };' % (zone_name,
output_path),
]
rndc_call.extend(rndc_op)
else:
rndc_op = 'reload'
rndc_call.extend([rndc_op])
rndc_call.extend([zone_name])
LOG.debug('Calling RNDC with: %s' % " ".join(rndc_call))
self._execute_rndc(rndc_call)
def _execute_rndc(self, rndc_call):
try:
LOG.debug('Executing RNDC call: %s' % " ".join(rndc_call))
utils.execute(*rndc_call)
except utils.processutils.ProcessExecutionError as e:
LOG.debug('RNDC call failure: %s' % e)
raise exceptions.Backend(e)

View File

@ -1,242 +0,0 @@
# Copyright 2015 Dyn Inc.
#
# Author: Yasha Bubnov <ybubnov@dyn.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 itertools
import warnings
import dns.rdata
import dns.rdataclass
import dns.rdatatype
from oslo_concurrency import lockutils
from oslo_config import cfg
from oslo_log import log as logging
from designate.backend.agent_backend import base
from designate import exceptions
from designate import utils
CFG_GROUP_NAME = 'backend:agent:denominator'
LOG = logging.getLogger(__name__)
class Denominator(object):
def __init__(self, config):
super(Denominator, self).__init__()
self.config = config
def update_record(self, zone, **kwargs):
return self._execute(['record', '-z', zone, 'replace'], kwargs)
def create_record(self, zone, **kwargs):
return self._execute(['record', '-z', zone, 'add'], kwargs)
def delete_record(self, zone, **kwargs):
return self._execute(['record', '-z', zone, 'delete'], kwargs)
def get_record(self, zone, **kwargs):
return self._execute(['record', '-z', zone, 'get'], kwargs)
def get_records(self, zone, **kwargs):
return self._execute(['record', '-z', zone, 'list'], kwargs)
def create_zone(self, **kwargs):
return self._execute(['zone', 'add'], kwargs)
def update_zone(self, **kwargs):
return self._execute(['zone', 'update'], kwargs)
def delete_zone(self, **kwargs):
return self._execute(['zone', 'delete'], kwargs)
def _params(self, **kwargs):
params = [('--%s' % k, str(v)) for k, v in kwargs.items()]
return list(itertools.chain(*params))
def _base(self):
call = ['denominator', '-q', '-n', self.config.name]
# NOTE: When path to denominator configuration file is omitted,
# ~/.denominatorconfig file will be used by default.
if self.config.config_file:
call.extend(['-C', self.config.config_file])
return call
def _execute(self, op, kwargs):
try:
call = self._base() + op + self._params(**kwargs)
LOG.debug(('Executing Denominator call: %s' % ' '.join(call)))
stdout, _ = utils.execute(*call)
return stdout
except utils.processutils.ProcessExecutionError as e:
LOG.debug('Denominator call failure: %s' % e)
raise exceptions.DesignateException(e)
class DenominatorBackend(base.AgentBackend):
__plugin_name__ = 'denominator'
__backend_status__ = 'untested'
def __init__(self, agent_service):
super(DenominatorBackend, self).__init__(agent_service)
warning_msg = ('The designate agent framework and backend driver "{}" '
'are deprecated as of the Antelope (2023.1) release '
'and will be removed in the "C" '
'release.'.format(self.__plugin_name__))
warnings.warn(warning_msg, DeprecationWarning)
self.denominator = Denominator(
cfg.CONF[CFG_GROUP_NAME])
def start(self):
LOG.info("Started Denominator backend")
def stop(self):
LOG.info("Stopped Denominator backend")
def find_zone_serial(self, zone_name):
LOG.debug("Finding %s", zone_name)
zone_name = zone_name.rstrip('.')
output = self.denominator.get_record(
zone=zone_name,
type='SOA',
name=zone_name)
try:
text = ' '.join(output.split()[3:])
rdata = dns.rdata.from_text(dns.rdataclass.IN,
dns.rdatatype.SOA,
text)
except Exception:
return None
return rdata.serial
def create_zone(self, zone):
LOG.debug("Creating %s", zone.origin.to_text())
zone_name = zone.origin.to_text(omit_final_dot=True)
if isinstance(zone_name, bytes):
zone_name = zone_name.decode('utf-8')
# Use SOA TTL as zone default TTL
soa_record = zone.find_rrset(zone.origin, dns.rdatatype.SOA)
rname = list(soa_record.items)[0].rname.derelativize(
origin=zone.origin)
# Lock zone to prevent concurrent changes.
with self._sync_zone(zone.origin):
# NOTE: If zone already exists, denominator will update it with
# new values, in other a duplicate zone will be created if
# provider supports such functionality.
self.denominator.create_zone(
name=zone_name,
ttl=soa_record.ttl,
email=rname)
# Add records one by one.
for name, ttl, rtype, data in self._iterate_records(zone):
# Some providers do not support creation of SOA record.
rdatatype = dns.rdatatype.from_text(rtype)
if rdatatype == dns.rdatatype.SOA:
continue
self.denominator.create_record(
zone=zone_name,
name=name,
type=rtype,
ttl=ttl,
data=data)
def update_zone(self, zone):
LOG.debug("Updating %s", zone.origin)
zone_name = zone.origin.to_text(omit_final_dot=True)
if isinstance(zone_name, bytes):
zone_name = zone_name.decode('utf-8')
soa_record = zone.find_rrset(zone.origin, dns.rdatatype.SOA)
rname = list(soa_record.items)[0].rname.derelativize(
origin=zone.origin)
with self._sync_zone(zone.origin):
# Update zone with a new parameters
self.denominator.update_zone(
id=zone_name,
ttl=soa_record.ttl,
email=rname)
# Fetch records to create a differential update of a zone.
output = self.denominator.get_records(zone_name)
subzones = dict()
# subzones dict will contain names of subzones without
# trailing dot.
for raw in output.splitlines():
data = raw.split()
name, rtype = data[0], data[1]
rtypes = subzones.get(name, set())
rtypes.add(rtype)
subzones[name] = rtypes
for name, ttl, rtype, data in self._iterate_records(zone):
record_action = self.denominator.create_record
if name in subzones and rtype in subzones[name]:
# When RR set already exists, replace it with a new one.
rdatatype = dns.rdatatype.from_text(rtype)
record_action = self.denominator.update_record
# So next call will ADD a new record to record set
# instead of replacing of the existing one.
subzones[name].remove(rtype)
# NOTE: DynECT does not support deleting of the SOA
# record. Skip updating of the SOA record.
if rdatatype == dns.rdatatype.SOA:
continue
record_action(zone=zone_name,
name=name,
type=rtype,
ttl=ttl,
data=data)
# Remaining records should be deleted
for name, types in subzones.items():
for rtype in types:
self.denominator.delete_record(
zone=zone_name, id=name, type=rtype)
def delete_zone(self, zone_name):
LOG.debug('Delete Zone: %s' % zone_name)
with self._sync_zone(zone_name):
self.denominator.delete_zone(id=zone_name)
def _sync_zone(self, zone_name):
LOG.debug('Synchronising zone: %s' % zone_name)
return lockutils.lock('denominator-%s' % zone_name)
def _iterate_records(self, zone):
for rname, ttl, rdata in zone.iterate_rdatas():
name = rname.derelativize(origin=zone.origin)
name = name.to_text(omit_final_dot=True)
if isinstance(name, bytes):
name = name.decode('utf-8')
data = rdata.to_text(origin=zone.origin, relativize=False)
yield name, ttl, dns.rdatatype.to_text(rdata.rdtype), data

View File

@ -1,333 +0,0 @@
# Copyright 2016 Hewlett Packard Enterprise Development Company LP
#
# Author: Federico Ceratto <federico.ceratto@hpe.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.
"""
backend.agent_backend.impl_djbdns
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Djbdns DNS agent backend
Create, update, delete zones locally on a Djbdns DNS resolver using the
axfr-get utility.
`Djbdns User documentation <../../admin/backends/djbdns_agent.html>`_
.. WARNING::
Untested, do not use in production.
Configured in [service:agent:djbdns]
Requires rootwrap (or equivalent sudo privileges) to execute:
- tcpclient
- axfr-get
- tinydns-data
"""
import errno
import glob
import os
import random
import tempfile
import warnings
import dns
import dns.resolver
from oslo_concurrency import lockutils
from oslo_concurrency.processutils import ProcessExecutionError
from oslo_config import cfg
from oslo_log import log as logging
from designate.backend.agent_backend import base
from designate import exceptions
from designate import utils
from designate.utils import execute
LOG = logging.getLogger(__name__)
CFG_GROUP_NAME = 'backend:agent:djbdns'
# rootwrap requires a command name instead of full path
TCPCLIENT_DEFAULT_PATH = 'tcpclient'
AXFR_GET_DEFAULT_PATH = 'axfr-get'
TINYDNS_DATA_DEFAULT_PATH = 'tinydns-data'
TINYDNS_DATADIR_DEFAULT_PATH = '/var/lib/djbdns'
SOA_QUERY_TIMEOUT = 1
# TODO(Federico) on zone creation and update, agent.handler unnecessarily
# perfors AXFR from MiniDNS to the Agent to populate the `zone` argument
# (needed by the Bind backend)
def filter_exceptions(fn):
# Let Backend() exceptions pass through, log out every other exception
# and re-raise it as Backend()
def wrapper(*a, **kw):
try:
return fn(*a, **kw)
except exceptions.Backend:
raise
except Exception as e:
LOG.error("Unhandled exception %s", e, exc_info=True)
raise exceptions.Backend(str(e))
return wrapper
class DjbdnsBackend(base.AgentBackend):
__plugin_name__ = 'djbdns'
__backend_status__ = 'experimental'
def __init__(self, *a, **kw):
"""Configure the backend"""
super(DjbdnsBackend, self).__init__(*a, **kw)
warning_msg = ('The designate agent framework and backend driver "{}" '
'are deprecated as of the Antelope (2023.1) release '
'and will be removed in the "C" '
'release.'.format(self.__plugin_name__))
warnings.warn(warning_msg, DeprecationWarning)
conf = cfg.CONF[CFG_GROUP_NAME]
self._resolver = dns.resolver.Resolver(configure=False)
self._resolver.timeout = SOA_QUERY_TIMEOUT
self._resolver.lifetime = SOA_QUERY_TIMEOUT
self._resolver.nameservers = [conf.query_destination]
self._masters = [utils.split_host_port(ns)
for ns in cfg.CONF['service:agent'].masters]
LOG.info("Resolvers: %r", self._resolver.nameservers)
LOG.info("AXFR masters: %r", self._masters)
if not self._masters:
raise exceptions.Backend("Missing agent AXFR masters")
self._tcpclient_cmd_name = conf.tcpclient_cmd_name
self._axfr_get_cmd_name = conf.axfr_get_cmd_name
# Directory where data.cdb lives, usually /var/lib/djbdns/root
tinydns_root_dir = os.path.join(conf.tinydns_datadir, 'root')
# Usually /var/lib/djbdns/root/data.cdb
self._tinydns_cdb_filename = os.path.join(tinydns_root_dir, 'data.cdb')
LOG.info("data.cdb path: %r", self._tinydns_cdb_filename)
# Where the agent puts the zone datafiles,
# usually /var/lib/djbdns/datafiles
self._datafiles_dir = datafiles_dir = os.path.join(
conf.tinydns_datadir,
'datafiles')
self._datafiles_tmp_path_tpl = os.path.join(datafiles_dir, "%s.ztmp")
self._datafiles_path_tpl = os.path.join(datafiles_dir, "%s.zonedata")
self._datafiles_path_glob = self._datafiles_path_tpl % '*'
self._check_dirs(tinydns_root_dir, datafiles_dir)
@staticmethod
def _check_dirs(*dirnames):
"""Check if directories are writable
"""
for dn in dirnames:
if not os.path.isdir(dn):
raise exceptions.Backend("Missing directory %s" % dn)
if not os.access(dn, os.W_OK):
raise exceptions.Backend("Directory not writable: %s" % dn)
def start(self):
"""Start the backend"""
LOG.info("Started djbdns backend")
def find_zone_serial(self, zone_name):
"""Query the local resolver for a zone
Times out after SOA_QUERY_TIMEOUT
"""
LOG.debug("Finding %s", zone_name)
try:
rdata = self._resolver.query(
zone_name, rdtype=dns.rdatatype.SOA)[0]
return rdata.serial
except Exception:
return None
@staticmethod
def _concatenate_zone_datafiles(data_fn, path_glob):
"""Concatenate all zone datafiles into 'data'
"""
with open(data_fn, 'w') as data_f:
zone_cnt = 0
for zone_fn in glob.glob(path_glob):
zone_cnt += 1
with open(zone_fn) as zf:
data_f.write(zf.read())
LOG.info("Loaded %d zone datafiles.", zone_cnt)
def _rebuild_data_cdb(self):
"""Rebuild data.cdb file from zone datafiles
Requires global lock
On zone creation, axfr-get creates datafiles atomically by doing
rename. On zone deletion, os.remove deletes the file atomically
Globbing and reading the datafiles can be done without locking on
them.
The data and data.cdb files are written into a unique temp directory
"""
tmpdir = tempfile.mkdtemp(dir=self._datafiles_dir)
data_fn = os.path.join(tmpdir, 'data')
tmp_cdb_fn = os.path.join(tmpdir, 'data.cdb')
try:
self._concatenate_zone_datafiles(data_fn,
self._datafiles_path_glob)
# Generate the data.cdb file
LOG.info("Updating data.cdb")
LOG.debug("Convert %s to %s", data_fn, tmp_cdb_fn)
try:
out, err = execute(
cfg.CONF[CFG_GROUP_NAME].tinydns_data_cmd_name,
cwd=tmpdir
)
except ProcessExecutionError as e:
LOG.error("Failed to generate data.cdb")
LOG.error("Command output: %(out)r Stderr: %(err)r",
{
'out': e.stdout,
'err': e.stderr
})
raise exceptions.Backend("Failed to generate data.cdb")
LOG.debug("Move %s to %s", tmp_cdb_fn, self._tinydns_cdb_filename)
try:
os.rename(tmp_cdb_fn, self._tinydns_cdb_filename)
except OSError:
os.remove(tmp_cdb_fn)
LOG.error("Unable to move data.cdb to %s",
self._tinydns_cdb_filename)
raise exceptions.Backend("Unable to move data.cdb")
finally:
try:
os.remove(data_fn)
except OSError:
pass
try:
os.removedirs(tmpdir)
except OSError:
pass
def _perform_axfr_from_minidns(self, zone_name):
"""Instruct axfr-get to request an AXFR from MiniDNS.
:raises: exceptions.Backend on error
"""
zone_fn = self._datafiles_path_tpl % zone_name
zone_tmp_fn = self._datafiles_tmp_path_tpl % zone_name
# Perform AXFR, create or update a zone datafile
# No need to lock globally here.
# Axfr-get creates the datafile atomically by doing rename
mdns_hostname, mdns_port = random.choice(self._masters)
with lockutils.lock("%s.lock" % zone_name):
LOG.debug("writing to %s", zone_fn)
cmd = (
self._tcpclient_cmd_name,
mdns_hostname,
"%d" % mdns_port,
self._axfr_get_cmd_name,
zone_name,
zone_fn,
zone_tmp_fn
)
LOG.debug("Executing AXFR as %r", ' '.join(cmd))
try:
out, err = execute(*cmd)
except ProcessExecutionError as e:
LOG.error("Error executing AXFR as %r", ' '.join(cmd))
LOG.error("Command output: %(out)r Stderr: %(err)r",
{
'out': e.stdout,
'err': e.stderr
})
raise exceptions.Backend(str(e))
finally:
try:
os.remove(zone_tmp_fn)
except OSError:
pass
@filter_exceptions
def create_zone(self, zone):
"""Create a new Zone
Do not raise exceptions if the zone already exists.
:param zone: zone to be created
:type zone: raw pythondns Zone
:raises: exceptions.Backend on error
"""
zone_name = zone.origin.to_text(omit_final_dot=True)
if isinstance(zone_name, bytes):
zone_name = zone_name.decode('utf-8')
LOG.debug("Creating %s", zone_name)
# The zone might be already in place due to a race condition between
# checking if the zone is there and creating it across different
# greenlets
LOG.debug("Triggering initial AXFR from MiniDNS to Djbdns for %s",
zone_name)
self._perform_axfr_from_minidns(zone_name)
self._rebuild_data_cdb()
@filter_exceptions
def update_zone(self, zone):
"""Instruct Djbdns DNS to perform AXFR from MiniDNS
:param zone: zone to be created
:type zone: raw pythondns Zone
:raises: exceptions.Backend on error
"""
zone_name = zone.origin.to_text(omit_final_dot=True)
if isinstance(zone_name, bytes):
zone_name = zone_name.decode('utf-8')
LOG.debug("Triggering AXFR from MiniDNS to Djbdns for %s", zone_name)
self._perform_axfr_from_minidns(zone_name)
self._rebuild_data_cdb()
@filter_exceptions
def delete_zone(self, zone_name):
"""Delete a new Zone
Do not raise exceptions if the zone does not exist.
:param zone_name: zone name
:type zone_name: str
:raises: exceptions.Backend on error
"""
zone_name = zone_name.rstrip('.')
LOG.debug('Deleting Zone: %s', zone_name)
zone_fn = self._datafiles_path_tpl % zone_name
try:
os.remove(zone_fn)
LOG.debug('Deleted Zone: %s', zone_name)
except OSError as e:
if errno.ENOENT == e.errno:
LOG.info("Zone datafile %s was already deleted", zone_fn)
return
raise
self._rebuild_data_cdb()

View File

@ -1,53 +0,0 @@
# Copyright 2014 Rackspace Inc.
#
# Author: Tim Simmons <tim.simmons@rackspace.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import warnings
from oslo_log import log as logging
from designate.backend.agent_backend import base
LOG = logging.getLogger(__name__)
class FakeBackend(base.AgentBackend):
__plugin_name__ = 'fake'
def __init__(self, agent_service):
super(FakeBackend, self).__init__(agent_service)
warning_msg = ('The designate agent framework and backend driver "{}" '
'are deprecated as of the Antelope (2023.1) release '
'and will be removed in the "C" '
'release.'.format(self.__plugin_name__))
warnings.warn(warning_msg, DeprecationWarning)
def start(self):
LOG.info("Started fake backend, Pool Manager will not work!")
def stop(self):
LOG.info("Stopped fake backend")
def find_zone_serial(self, zone_name):
LOG.debug("Finding %s", zone_name)
return 0
def create_zone(self, zone):
LOG.debug("Creating %s", zone.origin.to_text())
def update_zone(self, zone):
LOG.debug("Updating %s", zone.origin.to_text())
def delete_zone(self, zone_name):
LOG.debug('Delete Zone: %s', zone_name)

View File

@ -1,240 +0,0 @@
# Copyright 2016 Hewlett Packard Enterprise Development Company LP
#
# Author: Federico Ceratto <federico.ceratto@hpe.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.
"""
backend.agent_backend.impl_gdnsd
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
gdnsd agent backend
Create, update, delete zones locally on a gdnsd resolver using the
gdnsd utility.
Supported Knot versions: >= 2.1, < 3
`User documentation <../../admin/backends/gdnsd_agent.html>`_
.. WARNING::
Untested, do not use in production.
.. NOTE::
If the backend is killed during a configuration transaction it might be
required to manually abort the transaction with `sudo gdnsd conf-abort`
Configured in [service:agent:gdnsd]
"""
import errno
import os
import string
import tempfile
import warnings
import dns
import dns.resolver
from oslo_concurrency.processutils import ProcessExecutionError
from oslo_config import cfg
from oslo_log import log as logging
from designate.backend.agent_backend import base
from designate import exceptions
from designate import utils
CFG_GROUP_NAME = 'backend:agent:gdnsd'
LOG = logging.getLogger(__name__)
# rootwrap requires a command name instead of full path
GDNSD_DEFAULT_PATH = 'gdnsd'
CONFDIR_PATH = '/etc/gdnsd'
SOA_QUERY_TIMEOUT = 1
ZONE_FILE_PERMISSIONS = 0o0644
def filter_exceptions(fn):
# Let Backend() exceptions pass through, log out every other exception
# and re-raise it as Backend()
def wrapper(*a, **kw):
try:
return fn(*a, **kw)
except exceptions.Backend as e:
raise e
except Exception as e:
LOG.error("Unhandled exception %s", e, exc_info=True)
raise exceptions.Backend(e)
return wrapper
class GdnsdBackend(base.AgentBackend):
__plugin_name__ = 'gdnsd'
__backend_status__ = 'experimental'
def __init__(self, *a, **kw):
"""Configure the backend"""
super(GdnsdBackend, self).__init__(*a, **kw)
warning_msg = ('The designate agent framework and backend driver "{}" '
'are deprecated as of the Antelope (2023.1) release '
'and will be removed in the "C" '
'release.'.format(self.__plugin_name__))
warnings.warn(warning_msg, DeprecationWarning)
self._gdnsd_cmd_name = cfg.CONF[CFG_GROUP_NAME].gdnsd_cmd_name
LOG.info("gdnsd command: %r", self._gdnsd_cmd_name)
self._confdir_path = cfg.CONF[CFG_GROUP_NAME].confdir_path
self._zonedir_path = os.path.join(self._confdir_path, 'zones')
LOG.info("gdnsd conf directory: %r", self._confdir_path)
self._resolver = dns.resolver.Resolver(configure=False)
self._resolver.timeout = SOA_QUERY_TIMEOUT
self._resolver.lifetime = SOA_QUERY_TIMEOUT
self._resolver.nameservers = [
cfg.CONF[CFG_GROUP_NAME].query_destination
]
LOG.info("Resolvers: %r", self._resolver.nameservers)
self._check_dirs(self._zonedir_path)
def start(self):
"""Start the backend, check gdnsd configuration
:raises: exception.Backend on invalid configuration
"""
LOG.info("Started gdnsd backend")
self._check_conf()
def _check_conf(self):
"""Run gdnsd to check its configuration
"""
try:
out, err = utils.execute(
cfg.CONF[CFG_GROUP_NAME].gdnsd_cmd_name,
'-D', '-x', 'checkconf', '-c', self._confdir_path,
run_as_root=False,
)
except ProcessExecutionError as e:
LOG.error("Command output: %(out)r Stderr: %(err)r",
{
'out': e.stdout,
'err': e.stderr
})
raise exceptions.Backend("Configuration check failed")
def _check_dirs(self, *dirnames):
"""Check if directories are writable
"""
for dn in dirnames:
if not os.path.isdir(dn):
raise exceptions.Backend("Missing directory %s" % dn)
if not os.access(dn, os.W_OK):
raise exceptions.Backend("Directory not writable: %s" % dn)
def find_zone_serial(self, zone_name):
"""Query the local resolver for a zone
Times out after SOA_QUERY_TIMEOUT
"""
LOG.debug("Finding %s", zone_name)
try:
rdata = self._resolver.query(
zone_name, rdtype=dns.rdatatype.SOA)[0]
return rdata.serial
except Exception:
return None
def _generate_zone_filename(self, zone_name):
"""Generate a filename for a zone file
"/" is traslated into "@"
Non-valid characters are translated into \\ NNN
where NNN is a decimal integer in the range 0 - 255
The filename is lowercase
:returns: valid filename (string)
"""
valid_chars = "-_.@%s%s" % (string.ascii_letters, string.digits)
fname = zone_name.replace('/', '@').lower()
fname = [c if c in valid_chars else "\03%d" % ord(c)
for c in fname]
return ''.join(fname)
def _write_zone_file(self, zone):
"""Create or update a zone file atomically.
The zone file is written to a unique temp file and then renamed
"""
zone_name = zone.origin.to_text(omit_final_dot=True)
if isinstance(zone_name, bytes):
zone_name = zone_name.decode('utf-8')
zone_base_fname = self._generate_zone_filename(zone_name)
zone_fname = os.path.join(self._zonedir_path, zone_base_fname)
try:
# gdnsd ignores hidden files
tmp_zone_fname = tempfile.mkstemp(
prefix=".%s" % zone_base_fname,
dir=self._zonedir_path,
)[1]
LOG.debug("Writing zone %r to %r and renaming it to %r",
zone_name, tmp_zone_fname, zone_fname)
zone.to_file(tmp_zone_fname)
os.chmod(tmp_zone_fname, ZONE_FILE_PERMISSIONS)
os.rename(tmp_zone_fname, zone_fname)
finally:
try:
os.remove(tmp_zone_fname)
except OSError:
pass
@filter_exceptions
def create_zone(self, zone):
"""Create a new Zone
Do not raise exceptions if the zone already exists.
:param zone: zone to be created
:type zone: raw pythondns Zone
:raises: exceptions.Backend on error
"""
# The zone might be already in place due to a race condition between
# checking if the zone is there and creating it across different
# greenlets
self._write_zone_file(zone)
@filter_exceptions
def update_zone(self, zone):
"""Instruct Djbdns DNS to perform AXFR from MiniDNS
:param zone: zone to be created
:type zone: raw pythondns Zone
:raises: exceptions.Backend on error
"""
self._write_zone_file(zone)
@filter_exceptions
def delete_zone(self, zone_name):
"""Delete a new Zone
Do not raise exceptions if the zone does not exist.
:param zone_name: zone name
:type zone_name: str
:raises: exceptions.Backend on error
"""
zone_name = zone_name.rstrip('.')
LOG.debug('Deleting Zone: %s', zone_name)
zone_fn = self._generate_zone_filename(zone_name)
zone_fn = os.path.join(self._zonedir_path, zone_fn)
try:
os.remove(zone_fn)
LOG.debug('Deleted Zone: %s', zone_name)
except OSError as e:
if errno.ENOENT == e.errno:
LOG.info("Zone datafile %s was already deleted", zone_fn)
return
raise

View File

@ -1,215 +0,0 @@
# Copyright 2016 Hewlett Packard Enterprise Development Company LP
#
# Author: Federico Ceratto <federico.ceratto@hpe.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.
"""
backend.agent_backend.impl_knot2
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Knot DNS agent backend
Create, update, delete zones locally on a Knot DNS resolver using the
knotc utility.
Supported Knot versions: >= 2.1, < 3
`Knot DNS 2 User documentation <../../admin/backends/knot2_agent.html>`_
.. WARNING::
Untested, do not use in production.
.. NOTE::
If the backend is killed during a configuration transaction it might be
required to manually abort the transaction with `sudo knotc conf-abort`
Configured in [service:agent:knot2]
"""
import warnings
from oslo_concurrency import lockutils
from oslo_concurrency.processutils import ProcessExecutionError
from oslo_config import cfg
from oslo_log import log as logging
from designate.backend.agent_backend import base
from designate import exceptions
from designate.utils import execute
CFG_GROUP_NAME = 'backend:agent:knot2'
LOG = logging.getLogger(__name__)
# rootwrap requires a command name instead of full path
KNOTC_DEFAULT_PATH = 'knotc'
# TODO(Federico) on zone creation and update, agent.handler unnecessarily
# perfors AXFR from MiniDNS to the Agent to populate the `zone` argument
# (needed by the Bind backend)
class Knot2Backend(base.AgentBackend):
__plugin_name__ = 'knot2'
__backend_status__ = 'untested'
_lock_name = 'knot2.lock'
def __init__(self, *a, **kw):
"""Configure the backend"""
super(Knot2Backend, self).__init__(*a, **kw)
warning_msg = ('The designate agent framework and backend driver "{}" '
'are deprecated as of the Antelope (2023.1) release '
'and will be removed in the "C" '
'release.'.format(self.__plugin_name__))
warnings.warn(warning_msg, DeprecationWarning)
self._knotc_cmd_name = cfg.CONF[CFG_GROUP_NAME].knotc_cmd_name
def start(self):
"""Start the backend"""
LOG.info("Started knot2 backend")
def _execute_knotc(self, *knotc_args, **kw):
"""Run the Knot client and check the output
:param expected_output: expected output (default: 'OK')
:type expected_output: str
:param expected_error: expected alternative output, will be \
logged as info(). Default: not set.
:type expected_error: str
"""
# Knotc returns "0" even on failure, we have to check for 'OK'
# https://gitlab.labs.nic.cz/labs/knot/issues/456
LOG.debug("Executing knotc with %r", knotc_args)
expected = kw.get('expected_output', 'OK')
expected_alt = kw.get('expected_error', None)
try:
out, err = execute(self._knotc_cmd_name, *knotc_args)
out = out.rstrip()
LOG.debug("Command output: %r", out)
if out != expected:
if expected_alt is not None and out == expected_alt:
LOG.info("Ignoring error: %r", out)
else:
raise ProcessExecutionError(stdout=out, stderr=err)
except ProcessExecutionError as e:
LOG.error("Command output: %(out)r Stderr: %(err)r",
{
'out': e.stdout,
'err': e.stderr
})
raise exceptions.Backend(e)
def _start_minidns_to_knot_axfr(self, zone_name):
"""Instruct Knot to request an AXFR from MiniDNS. No need to lock
or enter a configuration transaction.
"""
self._execute_knotc('zone-refresh', zone_name)
def _modify_zone(self, *knotc_args, **kw):
"""Create or delete a zone while locking, and within a
Knot transaction.
Knot supports only one config transaction at a time.
:raises: exceptions.Backend
"""
with lockutils.lock(self._lock_name):
self._execute_knotc('conf-begin')
try:
self._execute_knotc(*knotc_args, **kw)
# conf-diff can be used for debugging
# self._execute_knotc('conf-diff')
except Exception as e:
self._execute_knotc('conf-abort')
LOG.info("Zone change aborted: %r", e)
raise
else:
self._execute_knotc('conf-commit')
def find_zone_serial(self, zone_name):
"""Get serial from a zone by running knotc
:returns: serial (int or None)
:raises: exceptions.Backend
"""
zone_name = zone_name.rstrip('.')
LOG.debug("Finding %s", zone_name)
# Output example:
# [530336536.com.] type: slave | serial: 0 | next-event: idle |
# auto-dnssec: disabled]
try:
out, err = execute(self._knotc_cmd_name, 'zone-status', zone_name)
except ProcessExecutionError as e:
if 'no such zone' in e.stdout:
# Zone not found
return None
LOG.error("Command output: %(out)r Stderr: %(err)r",
{
'out': e.stdout,
'err': e.stderr
})
raise exceptions.Backend(e)
try:
serial = out.split('|')[1].split()[1]
return int(serial)
except Exception:
LOG.error("Unable to parse knotc output: %r", out)
raise exceptions.Backend("Unexpected knotc zone-status output")
def create_zone(self, zone):
"""Create a new Zone by executing knotc
Do not raise exceptions if the zone already exists.
:param zone: zone to be created
:type zone: raw pythondns Zone
"""
zone_name = zone.origin.to_text(omit_final_dot=True)
if isinstance(zone_name, bytes):
zone_name = zone_name.decode('utf-8')
LOG.debug("Creating %s", zone_name)
# The zone might be already in place due to a race condition between
# checking if the zone is there and creating it across different
# greenlets
self._modify_zone('conf-set', 'zone[%s]' % zone_name,
expected_error='duplicate identifier')
LOG.debug("Triggering initial AXFR from MiniDNS to Knot for %s",
zone_name)
self._start_minidns_to_knot_axfr(zone_name)
def update_zone(self, zone):
"""Instruct Knot DNS to perform AXFR from MiniDNS
:param zone: zone to be created
:type zone: raw pythondns Zone
"""
zone_name = zone.origin.to_text(omit_final_dot=True)
if isinstance(zone_name, bytes):
zone_name = zone_name.decode('utf-8')
LOG.debug("Triggering AXFR from MiniDNS to Knot for %s", zone_name)
self._start_minidns_to_knot_axfr(zone_name)
def delete_zone(self, zone_name):
"""Delete a new Zone by executing knotc
Do not raise exceptions if the zone does not exist.
:param zone_name: zone name
:type zone_name: str
"""
LOG.debug('Delete Zone: %s' % zone_name)
self._modify_zone('conf-unset', 'zone[%s]' % zone_name,
expected_error='invalid identifier')

View File

@ -1,111 +0,0 @@
# Copyright 2016 Cloudbase Solutions Srl
# All Rights Reserved.
#
# Author: Alin Balutoiu <abalutoiu@cloudbasesolutions.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 warnings
from os_win import constants
from os_win import exceptions as os_win_exc
from os_win import utilsfactory
from oslo_config import cfg
from oslo_log import log as logging
from designate.backend.agent_backend import base
from designate import exceptions
LOG = logging.getLogger(__name__)
class MSDNSBackend(base.AgentBackend):
__plugin_name__ = 'msdns'
__backend_status__ = 'experimental'
def __init__(self, agent_service):
"""Configure the backend"""
super(MSDNSBackend, self).__init__(agent_service)
warning_msg = ('The designate agent framework and backend driver "{}" '
'are deprecated as of the Antelope (2023.1) release '
'and will be removed in the "C" '
'release.'.format(self.__plugin_name__))
warnings.warn(warning_msg, DeprecationWarning)
self._dnsutils = utilsfactory.get_dnsutils()
masters = cfg.CONF['service:agent'].masters
if not masters:
raise exceptions.Backend("Missing agent AXFR masters")
# Only ip addresses are needed
self._masters = [ns.split(":")[0] for ns in masters]
LOG.info("AXFR masters: %r", self._masters)
def start(self):
"""Start the backend"""
LOG.info("Started msdns backend")
def find_zone_serial(self, zone_name):
"""Return the zone's serial"""
zone_name = zone_name.rstrip(".")
LOG.debug("Finding zone: %s", zone_name)
try:
return self._dnsutils.get_zone_serial(zone_name)
except os_win_exc.DNSZoneNotFound:
# Return None if the zone was not found
return None
def create_zone(self, zone):
"""Create a new DNS Zone"""
zone_name = zone.origin.to_text(omit_final_dot=True)
if isinstance(zone_name, bytes):
zone_name = zone_name.decode('utf-8')
LOG.debug("Creating zone: %s", zone_name)
try:
self._dnsutils.zone_create(
zone_name=zone_name,
zone_type=constants.DNS_ZONE_TYPE_SECONDARY,
ds_integrated=False,
ip_addrs=self._masters)
except os_win_exc.DNSZoneAlreadyExists:
# Zone already exists, check its properties to see if the
# existing zone is identical to the requested one
zone_properties = self._dnsutils.get_zone_properties(zone_name)
identical_zone_exists = (
zone_properties['zone_type'] == (
constants.DNS_ZONE_TYPE_SECONDARY) and
zone_properties['ds_integrated'] is False and
set(zone_properties['master_servers']) == set(self._masters))
if not identical_zone_exists:
raise
def update_zone(self, zone):
"""Instruct MSDNS to request an AXFR from MiniDNS.
"""
zone_name = zone.origin.to_text(omit_final_dot=True)
if isinstance(zone_name, bytes):
zone_name = zone_name.decode('utf-8')
LOG.debug("Updating zone: %s", zone_name)
self._dnsutils.zone_update(zone_name)
def delete_zone(self, zone_name):
"""Delete a DNS Zone
Do not raise exception if the zone does not exist.
"""
LOG.debug('Deleting zone: %s' % zone_name)
zone_name = zone_name.rstrip(".")
self._dnsutils.zone_delete(zone_name)

View File

@ -1,90 +0,0 @@
# Copyright 2016 Hewlett Packard Enterprise Development Company LP
#
# Author: Federico Ceratto <federico.ceratto@hpe.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 dns
from debtcollector import removals
"""
backend.private_codes
~~~~~~~~~~~~~~~~~~~~~~
Private DNS opcodes, classes, RR codes for communication between the
agent and its backends
"""
# Command and Control OPCODE
CC = 14
# Private DNS CLASS Uses
CLASSCC = 65280
# Private RR Code Uses
SUCCESS = 65280
FAILURE = 65281
CREATE = 65282
DELETE = 65283
# TODO(johnsom) Remove this after the agents framework is removed or the
# protocol has been updated to not use an unassigned opcode(14).
#
# This is an Opcode Enum class that includes the unassigned[1][2]
# opcode 14 used in the Designate agent framework until the agent framework
# can be removed or fixed.
# [1] https://www.rfc-editor.org/rfc/rfc6895.html#section-2.2
# [2] https://www.iana.org/assignments/dns-parameters/
# dns-parameters.xhtml#dns-parameters-5
#
# Based on dns.opcode.Opcode:
#
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
# Copyright (C) 2001-2017 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
@removals.removed_class("OpcodeWith14")
class OpcodeWith14(dns.enum.IntEnum):
#: Query
QUERY = 0
#: Inverse Query (historical)
IQUERY = 1
#: Server Status (unspecified and unimplemented anywhere)
STATUS = 2
#: Notify
NOTIFY = 4
#: Dynamic Update
UPDATE = 5
# Unassigned, but used by Designate for command/control in the agents
UNASSIGNED14 = 14
@classmethod
def _maximum(cls):
return 15
@classmethod
def _unknown_exception_class(cls):
return dns.opcode.UnknownOpcode

View File

@ -1,47 +0,0 @@
# Copyright 2014 Rackspace Inc.
#
# Author: Tim Simmons <tim.simmons@rackspace.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import sys
import warnings
from oslo_log import log as logging
from oslo_reports import guru_meditation_report as gmr
from designate.agent import service as agent_service
import designate.conf
from designate import heartbeat_emitter
from designate import service
from designate import utils
from designate import version
CONF = designate.conf.CONF
CONF.import_opt('workers', 'designate.agent', group='service:agent')
def main():
utils.read_config('designate', sys.argv)
logging.setup(CONF, 'designate')
gmr.TextGuruMeditation.setup_autorun(version)
warnings.warn('The designate agent process is deprecated as of the '
'Antelope (2023.1) release and will be removed in the '
'"C" release.', DeprecationWarning)
server = agent_service.Service()
heartbeat = heartbeat_emitter.get_heartbeat_emitter(server.service_name)
service.serve(server, workers=CONF['service:agent'].workers)
heartbeat.start()
service.wait()

View File

@ -13,22 +13,15 @@
# under the License.
from oslo_config import cfg
from designate.conf import agent
from designate.conf import api
from designate.conf import base # noqa
from designate.conf import bind9
from designate.conf import central
from designate.conf import coordination
from designate.conf import denominator
from designate.conf import djbdns
from designate.conf import dynect
from designate.conf import gdnsd
from designate.conf import heartbeat_emitter
from designate.conf import infoblox
from designate.conf import keystone
from designate.conf import knot2
from designate.conf import mdns
from designate.conf import msdns
from designate.conf import network_api
from designate.conf import producer
from designate.conf import proxy
@ -39,21 +32,14 @@ from designate.conf import worker
CONF = cfg.CONF
base.register_opts(CONF)
agent.register_opts(CONF)
api.register_opts(CONF)
bind9.register_opts(CONF)
central.register_opts(CONF)
coordination.register_opts(CONF)
denominator.register_opts(CONF)
djbdns.register_opts(CONF)
dynect.register_opts(CONF)
gdnsd.register_opts(CONF)
heartbeat_emitter.register_opts(CONF)
infoblox.register_opts(CONF)
keystone.register_opts(CONF)
knot2.register_opts(CONF)
mdns.register_opts(CONF)
msdns.register_opts(CONF)
network_api.register_opts(CONF)
producer.register_opts(CONF)
proxy.register_opts(CONF)

View File

@ -1,89 +0,0 @@
# Copyright 2014 Rackspace Inc.
#
# Author: Tim Simmons <tim.simmons@rackspace.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_config import cfg
DEFAULT_AGENT_PORT = 5358
AGENT_GROUP = cfg.OptGroup(
name='service:agent',
title="Configuration for the Agent Service"
)
AGENT_OPTS = [
cfg.IntOpt('workers',
help='Number of agent worker processes to spawn',
deprecated_for_removal=True,
deprecated_since='Antelope(2023.1)',
deprecated_reason='The agent framework is deprecated.'),
cfg.IntOpt('threads', default=1000,
help='Number of agent greenthreads to spawn',
deprecated_for_removal=True,
deprecated_since='Antelope(2023.1)',
deprecated_reason='The agent framework is deprecated.'),
cfg.ListOpt('listen',
default=['0.0.0.0:%d' % DEFAULT_AGENT_PORT],
help='Agent host:port pairs to listen on',
deprecated_for_removal=True,
deprecated_since='Antelope(2023.1)',
deprecated_reason='The agent framework is deprecated.'),
cfg.IntOpt('tcp_backlog', default=100,
help='The Agent TCP Backlog',
deprecated_for_removal=True,
deprecated_since='Antelope(2023.1)',
deprecated_reason='The agent framework is deprecated.'),
cfg.FloatOpt('tcp_recv_timeout', default=0.5,
help='Agent TCP Receive Timeout',
deprecated_for_removal=True,
deprecated_since='Antelope(2023.1)',
deprecated_reason='The agent framework is deprecated.'),
cfg.ListOpt('allow_notify', default=[],
help='List of IP addresses allowed to NOTIFY The Agent',
deprecated_for_removal=True,
deprecated_since='Antelope(2023.1)',
deprecated_reason='The agent framework is deprecated.'),
cfg.ListOpt('masters', default=[],
help='List of masters for the Agent, format ip:port',
deprecated_for_removal=True,
deprecated_since='Antelope(2023.1)',
deprecated_reason='The agent framework is deprecated.'),
cfg.StrOpt('backend_driver', default='bind9',
help='The backend driver to use, e.g. bind9, djbdns, knot2',
deprecated_for_removal=True,
deprecated_since='Antelope(2023.1)',
deprecated_reason='The agent framework is deprecated.'),
cfg.StrOpt('transfer_source',
help='An IP address to be used to fetch zones transferred in',
deprecated_for_removal=True,
deprecated_since='Antelope(2023.1)',
deprecated_reason='The agent framework is deprecated.'),
cfg.FloatOpt('notify_delay', default=0.0,
help='Delay after a NOTIFY arrives for a zone that the Agent '
'will pause and drop subsequent NOTIFYs for that zone',
deprecated_for_removal=True,
deprecated_since='Antelope(2023.1)',
deprecated_reason='The agent framework is deprecated.'),
]
def register_opts(conf):
conf.register_group(AGENT_GROUP)
conf.register_opts(AGENT_OPTS, group=AGENT_GROUP)
def list_opts():
return {
AGENT_GROUP: AGENT_OPTS
}

View File

@ -1,66 +0,0 @@
# Copyright 2014 Rackspace Inc.
#
# Author: Tim Simmons <tim.simmons@rackspace.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_config import cfg
BIND9_GROUP = cfg.OptGroup(
name='backend:agent:bind9',
title="Configuration for bind9 backend"
)
BINS9_OPTS = [
cfg.StrOpt('rndc_host', default='127.0.0.1', help='RNDC Host',
deprecated_for_removal=True,
deprecated_since='Antelope(2023.1)',
deprecated_reason='The agent framework is deprecated.'),
cfg.IntOpt('rndc_port', default=953, help='RNDC Port',
deprecated_for_removal=True,
deprecated_since='Antelope(2023.1)',
deprecated_reason='The agent framework is deprecated.'),
cfg.StrOpt('rndc_config_file',
help='RNDC Config File',
deprecated_for_removal=True,
deprecated_since='Antelope(2023.1)',
deprecated_reason='The agent framework is deprecated.'),
cfg.StrOpt('rndc_key_file', help='RNDC Key File',
deprecated_for_removal=True,
deprecated_since='Antelope(2023.1)',
deprecated_reason='The agent framework is deprecated.'),
cfg.IntOpt('rndc_timeout', default=0, min=0, help='RNDC command timeout',
deprecated_for_removal=True,
deprecated_since='Antelope(2023.1)',
deprecated_reason='The agent framework is deprecated.'),
cfg.StrOpt('zone_file_path', default='$state_path/zones',
help='Path where zone files are stored',
deprecated_for_removal=True,
deprecated_since='Antelope(2023.1)',
deprecated_reason='The agent framework is deprecated.'),
cfg.StrOpt('query_destination', default='127.0.0.1',
help='Host to query when finding zones',
deprecated_for_removal=True,
deprecated_since='Antelope(2023.1)',
deprecated_reason='The agent framework is deprecated.'),
]
def register_opts(conf):
conf.register_group(BIND9_GROUP)
conf.register_opts(BINS9_OPTS, group=BIND9_GROUP)
def list_opts():
return {
BIND9_GROUP: BINS9_OPTS,
}

View File

@ -1,45 +0,0 @@
# Copyright 2015 Dyn Inc.
#
# Author: Yasha Bubnov <ybubnov@dyn.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_config import cfg
DENOMINATOR_GROUP = cfg.OptGroup(
name='backend:agent:denominator',
title='Backend options for Denominator',
)
DENOMINATOR_OPTS = [
cfg.StrOpt('name', default='fake',
help='Name of the affected provider',
deprecated_for_removal=True,
deprecated_since='Antelope(2023.1)',
deprecated_reason='The agent framework is deprecated.'),
cfg.StrOpt('config_file', default='/etc/denominator.conf',
help='Path to Denominator configuration file',
deprecated_for_removal=True,
deprecated_since='Antelope(2023.1)',
deprecated_reason='The agent framework is deprecated.'),
]
def register_opts(conf):
conf.register_group(DENOMINATOR_GROUP)
conf.register_opts(DENOMINATOR_OPTS, group=DENOMINATOR_GROUP)
def list_opts():
return {
DENOMINATOR_GROUP: DENOMINATOR_OPTS,
}

View File

@ -1,68 +0,0 @@
# Copyright 2016 Hewlett Packard Enterprise Development Company LP
#
# Author: Federico Ceratto <federico.ceratto@hpe.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_config import cfg
DJBDNS_GROUP = cfg.OptGroup(
name='backend:agent:djbdns',
title="Configuration for Djbdns backend"
)
DJDNS_OPTS = [
cfg.StrOpt(
'tcpclient_cmd_name',
help='tcpclient executable path or rootwrap command name',
default='tcpclient', deprecated_for_removal=True,
deprecated_since='Antelope(2023.1)',
deprecated_reason='The agent framework is deprecated.'
),
cfg.StrOpt(
'axfr_get_cmd_name',
help='axfr-get executable path or rootwrap command name',
default='axfr-get', deprecated_for_removal=True,
deprecated_since='Antelope(2023.1)',
deprecated_reason='The agent framework is deprecated.'
),
cfg.StrOpt(
'tinydns_data_cmd_name',
help='tinydns-data executable path or rootwrap command name',
default='tinydns-data', deprecated_for_removal=True,
deprecated_since='Antelope(2023.1)',
deprecated_reason='The agent framework is deprecated.'
),
cfg.StrOpt(
'tinydns_datadir',
help='TinyDNS data directory',
default='/var/lib/djbdns', deprecated_for_removal=True,
deprecated_since='Antelope(2023.1)',
deprecated_reason='The agent framework is deprecated.'
),
cfg.StrOpt('query_destination', default='127.0.0.1',
help='Host to query when finding zones',
deprecated_for_removal=True,
deprecated_since='Antelope(2023.1)',
deprecated_reason='The agent framework is deprecated.'),
]
def register_opts(conf):
conf.register_group(DJBDNS_GROUP)
conf.register_opts(DJDNS_OPTS, group=DJBDNS_GROUP)
def list_opts():
return {
DJBDNS_GROUP: DJDNS_OPTS,
}

View File

@ -1,51 +0,0 @@
# Copyright 2016 Hewlett Packard Enterprise Development Company LP
#
# Author: Federico Ceratto <federico.ceratto@hpe.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_config import cfg
GDNSD_GROUP = cfg.OptGroup(
name='backend:agent:gdnsd',
title="Configuration for gdnsd backend"
)
GDNSD_OPTS = [
cfg.StrOpt('gdnsd_cmd_name',
help='gdnsd executable path or rootwrap command name',
default='gdnsd',
deprecated_for_removal=True,
deprecated_since='Antelope(2023.1)',
deprecated_reason='The agent framework is deprecated.'),
cfg.StrOpt('confdir_path',
help='gdnsd configuration directory path',
default='/etc/gdnsd',
deprecated_for_removal=True,
deprecated_since='Antelope(2023.1)',
deprecated_reason='The agent framework is deprecated.'),
cfg.StrOpt('query_destination', default='127.0.0.1',
help='Host to query when finding zones',
deprecated_for_removal=True,
deprecated_since='Antelope(2023.1)',
deprecated_reason='The agent framework is deprecated.'),
]
def register_opts(conf):
conf.register_group(GDNSD_GROUP)
conf.register_opts(GDNSD_OPTS, group=GDNSD_GROUP)
def list_opts():
return {
GDNSD_GROUP: GDNSD_OPTS,
}

View File

@ -1,46 +0,0 @@
# Copyright 2016 Hewlett Packard Enterprise Development Company LP
#
# Author: Federico Ceratto <federico.ceratto@hpe.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_config import cfg
KNOT2_GROUP = cfg.OptGroup(
name='backend:agent:knot2',
title="Configuration for Knot2 backend"
)
KNOT2_OPTS = [
cfg.StrOpt('knotc_cmd_name',
help='knotc executable path or rootwrap command name',
default='knotc',
deprecated_for_removal=True,
deprecated_since='Antelope(2023.1)',
deprecated_reason='The agent framework is deprecated.'),
cfg.StrOpt('query_destination', default='127.0.0.1',
help='Host to query when finding zones',
deprecated_for_removal=True,
deprecated_since='Antelope(2023.1)',
deprecated_reason='The agent framework is deprecated.'),
]
def register_opts(conf):
conf.register_group(KNOT2_GROUP)
conf.register_opts(KNOT2_OPTS, group=KNOT2_GROUP)
def list_opts():
return {
KNOT2_GROUP: KNOT2_OPTS,
}

View File

@ -1,35 +0,0 @@
# Copyright 2016 Cloudbase Solutions Srl
# All Rights Reserved.
#
# Author: Alin Balutoiu <abalutoiu@cloudbasesolutions.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_config import cfg
MSDNS_GROUP = cfg.OptGroup(
name='backend:agent:msdns',
title="Configuration for Microsoft DNS Server"
)
MSDNS_OPTS = [
]
def register_opts(conf):
conf.register_group(MSDNS_GROUP)
conf.register_opts(MSDNS_OPTS, group=MSDNS_GROUP)
def list_opts():
return {
MSDNS_GROUP: MSDNS_OPTS,
}

View File

@ -13,8 +13,6 @@
# 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 time
import dns.exception
import dns.message
import dns.opcode
@ -26,7 +24,6 @@ from oslo_log import log as logging
import designate.conf
from designate import context
from designate import dnsutils
from designate import exceptions
CONF = designate.conf.CONF
@ -176,39 +173,3 @@ class TsigInfoMiddleware(DNSMiddleware):
return self._build_error_response()
return None
class LimitNotifyMiddleware(DNSMiddleware):
"""Middleware that rate limits NOTIFYs to the Agent"""
def __init__(self, application):
super(LimitNotifyMiddleware, self).__init__(application)
self.delay = CONF['service:agent'].notify_delay
self.locker = dnsutils.ZoneLock(self.delay)
def process_request(self, request):
opcode = request.opcode()
if opcode != dns.opcode.NOTIFY:
return None
zone_name = request.question[0].name.to_text()
if isinstance(zone_name, bytes):
zone_name = zone_name.decode('utf-8')
if self.locker.acquire(zone_name):
time.sleep(self.delay)
self.locker.release(zone_name)
return None
else:
LOG.debug(
'Threw away NOTIFY for %(zone)s, already '
'working on an update.',
{
'zone': zone_name
}
)
response = dns.message.make_response(request)
# Provide an authoritative answer
response.flags |= dns.flags.AA
return (response,)

View File

@ -13,14 +13,11 @@
# 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 unittest import mock
import dns
import dns.query
import dns.tsigkeyring
from oslo_config import cfg
from designate import dnsmiddleware
from designate import dnsutils
from designate import exceptions
from designate import objects
@ -194,47 +191,3 @@ class TestUtils(designate.tests.TestCase):
self.assertTrue(lock.acquire('example3.com.'))
lock.release('example3.com.')
self.assertTrue(lock.acquire('example3.com.'))
def test_limit_notify_middleware(self):
self.CONF.set_override('notify_delay', 0.1, 'service:agent')
# Initialize the middlware
placeholder_app = None
middleware = dnsmiddleware.LimitNotifyMiddleware(placeholder_app)
# Prepare a NOTIFY
zone_name = 'example.com.'
notify = dns.message.make_query(zone_name, dns.rdatatype.SOA)
notify.flags = 0
notify.set_opcode(dns.opcode.NOTIFY)
notify.flags |= dns.flags.AA
# Send the NOTIFY through the middleware
# No problem, middleware should return None to pass it on
self.assertIsNone(middleware.process_request(notify))
@mock.patch('designate.dnsutils.ZoneLock.acquire', return_value=False)
def test_limit_notify_middleware_no_acquire(self, mock_acquire):
self.CONF.set_override('notify_delay', 0.1, 'service:agent')
# Initialize the middlware
placeholder_app = None
middleware = dnsmiddleware.LimitNotifyMiddleware(placeholder_app)
# Prepare a NOTIFY
zone_name = 'example.com.'
notify = dns.message.make_query(zone_name, dns.rdatatype.SOA)
notify.flags = 0
notify.set_opcode(dns.opcode.NOTIFY)
notify.flags |= dns.flags.AA
# Make a response object to match the middleware's return
response = dns.message.make_response(notify)
# Provide an authoritative answer
response.flags |= dns.flags.AA
# Send the NOTIFY through the middleware
# Lock can't be acquired, a NOTIFY is already being worked on
# so just return what would have come back for a successful NOTIFY
# This needs to be a one item tuple for the serialization middleware
self.assertEqual(middleware.process_request(notify), (response,))

View File

@ -1,27 +0,0 @@
# 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 dns.zone
def create_dnspy_zone(name):
if not name.endswith('.'):
name = name + '.'
zone_text = (
'$ORIGIN %(name)s\n%(name)s 3600 IN SOA %(ns)s email.email.com. '
'1421777854 3600 600 86400 3600\n%(name)s 3600 IN NS %(ns)s\n'
)
return dns.zone.from_text(
zone_text % {'name': name, 'ns': 'ns1.designate.com'},
check_origin=False
)

View File

@ -1,131 +0,0 @@
# Copyright 2014 Rackspace Inc.
#
# Author: Tim Simmons <tim.simmons@rackspace.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from unittest import mock
import dns.resolver
from designate.backend.agent_backend import impl_bind9
from designate import exceptions
import designate.tests
from designate.tests.unit.agent import backends
from designate import utils
class Bind9AgentBackendTestCase(designate.tests.TestCase):
def setUp(self):
super(Bind9AgentBackendTestCase, self).setUp()
self.CONF.set_override('listen', ['0.0.0.0:0'], 'service:agent')
self.backend = impl_bind9.Bind9Backend('foo')
def test_start_backend(self):
self.backend.start()
def test_stop_backend(self):
self.backend.stop()
@mock.patch.object(dns.resolver.Resolver, 'query')
def test_find_zone_serial(self, mock_query):
self.assertIsNotNone(self.backend.find_zone_serial('example.org.'))
@mock.patch.object(dns.resolver.Resolver, 'query')
def test_find_zone_serial_query_raises(self, mock_query):
mock_query.side_effect = Exception()
self.assertIsNone(self.backend.find_zone_serial('example.org.'))
@mock.patch('designate.utils.execute')
@mock.patch('designate.backend.agent_backend.impl_bind9.Bind9Backend'
'._sync_zone')
def test_create_zone(self, mock_execute, mock_sync_zone):
zone = backends.create_dnspy_zone('example.org')
self.backend.create_zone(zone)
@mock.patch('designate.utils.execute')
@mock.patch('designate.backend.agent_backend.impl_bind9.Bind9Backend'
'._sync_zone')
def test_update_zone(self, mock_execute, mock_sync_zone):
zone = backends.create_dnspy_zone('example.org')
self.backend.update_zone(zone)
@mock.patch('designate.utils.execute')
@mock.patch('designate.backend.agent_backend.impl_bind9.Bind9Backend'
'._sync_zone')
def test_delete_zone(self, mock_execute, mock_sync_zone):
self.backend.delete_zone('example.org.')
@mock.patch('designate.utils.execute')
def test_execute_rndc(self, mock_execute):
self.CONF.set_override(
'rndc_config_file', 'config_file', 'backend:agent:bind9'
)
self.CONF.set_override(
'rndc_key_file', 'key_file', 'backend:agent:bind9'
)
self.backend._execute_rndc(self.backend._rndc_base())
mock_execute.assert_called_once_with(
'rndc', '-s', '127.0.0.1', '-p', '953',
'-c', 'config_file', '-k', 'key_file'
)
@mock.patch('designate.utils.execute')
def test_execute_rndc_raises(self, mock_execute):
mock_execute.side_effect = utils.processutils.ProcessExecutionError()
self.assertRaises(
exceptions.Backend,
self.backend._execute_rndc, self.backend._rndc_base()
)
@mock.patch('designate.utils.execute')
@mock.patch.object(dns.zone.Zone, 'to_file')
def test_sync_zone(self, mock_to_file, mock_execute):
FAKE_STATE_PATH = '/tmp/fake/state/path'
self.CONF.set_override('state_path', FAKE_STATE_PATH)
zone = backends.create_dnspy_zone('example.org')
self.backend._sync_zone(zone)
mock_to_file.assert_called_once_with(
FAKE_STATE_PATH + '/zones/example.org.zone', relativize=False
)
mock_execute.assert_called_once_with(
'rndc', '-s', '127.0.0.1', '-p', '953', 'reload', 'example.org'
)
@mock.patch('designate.utils.execute')
@mock.patch.object(dns.zone.Zone, 'to_file')
def test_sync_zone_with_new_zone(self, mock_to_file, mock_execute):
FAKE_STATE_PATH = '/tmp/fake/state/path'
self.CONF.set_override('state_path', FAKE_STATE_PATH)
zone = backends.create_dnspy_zone('example.org')
self.backend._sync_zone(zone, new_zone_flag=True)
mock_to_file.assert_called_once_with(
FAKE_STATE_PATH + '/zones/example.org.zone', relativize=False
)
mock_execute.assert_called_once_with(
'rndc', '-s', '127.0.0.1', '-p', '953', 'addzone',
'example.org { type master; '
'file "' + FAKE_STATE_PATH + '/zones/example.org.zone"; };'
)

View File

@ -1,168 +0,0 @@
# Copyright 2015 Dyn Inc.
#
# Author: Yasha Bubnov <ybubnov@dyn.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 unittest import mock
from oslo_config import cfg
from designate.backend.agent_backend import impl_denominator
from designate import exceptions
from designate import tests
from designate.tests.unit.agent import backends
from designate import utils
class DenominatorAgentBackendTestCase(tests.TestCase):
def setUp(self):
super(DenominatorAgentBackendTestCase, self).setUp()
self.CONF.set_override('listen', ['0.0.0.0:0'], 'service:agent')
self.backend = impl_denominator.DenominatorBackend('foo')
def test_start_backend(self):
self.backend.start()
def test_stop_backend(self):
self.backend.stop()
@mock.patch('designate.utils.execute', return_value=(
'example.org SOA 86400 ns1.designate.com. '
'hostmaster@example.org. 475 3600 600 604800 1800', None))
def test_find_zone_serial(self, mock_execute):
serial = self.backend.find_zone_serial('example.org.')
# Ensure returned right serial number
self.assertEqual(475, serial)
# Ensure called "denominator zone add"
self.assertIn('record', mock_execute.call_args[0])
self.assertIn('get', mock_execute.call_args[0])
@mock.patch('designate.utils.execute', return_value=('', None))
def test_find_zone_serial_fail(self, mock_execute):
serial = self.backend.find_zone_serial('example.org.')
self.assertIsNone(serial)
@mock.patch('designate.utils.execute', return_value=(None, None))
def test_create_zone(self, mock_execute):
zone = backends.create_dnspy_zone('example.org.')
self.backend.create_zone(zone)
# Ensure denominator called for each record (except SOA)
# plus one to update zone data
self.assertEqual(
mock_execute.call_count, len(list(zone.iterate_rdatas()))
)
@mock.patch('designate.utils.execute')
def test_update_zone(self, mock_execute):
# Output from 'designate record list' command
records = ('example.org SOA 86400 ns1.designate.com. '
'hostmaster@example.org. 475 3600 600 604800 1800\n'
'example.org NS 86400 ns1.designator.net.\n'
'example.org NS 86400 ns2.designator.net.\n'
'example.org MX 86400 10 mx1.designator.net.')
# That should force update_zone to delete A and AAAA records
# from the zone and create a new MX record.
mock_execute.return_value = (records, None)
zone = backends.create_dnspy_zone('example.org.')
self.backend.update_zone(zone)
# Ensure denominator called to:
# *update zone info
# *fetch list of zone records
# *delete one MX record
# *replace one NS record
# *create 0 records
# total: 4 calls
self.assertEqual(4, mock_execute.call_count)
methods = ['update_zone',
'get_records',
'create_record', 'update_record', 'delete_record']
for method in methods:
setattr(self.backend.denominator, method, mock.Mock(
return_value=records))
self.backend.update_zone(zone)
self.assertEqual(1, self.backend.denominator.update_zone.call_count)
self.assertEqual(1, self.backend.denominator.get_records.call_count)
self.assertEqual(0, self.backend.denominator.create_record.call_count)
self.assertEqual(1, self.backend.denominator.update_record.call_count)
self.assertEqual(1, self.backend.denominator.delete_record.call_count)
@mock.patch('designate.utils.execute', return_value=(None, None))
def test_delete_zone(self, mock_execute):
self.backend.delete_zone('example.org.')
# Ensure called 'denominator zone delete'
self.assertEqual(1, mock_execute.call_count)
self.assertIn('zone', mock_execute.call_args[0])
self.assertIn('delete', mock_execute.call_args[0])
class DenominatorAgentBaseTestCase(tests.TestCase):
def setUp(self):
super(DenominatorAgentBaseTestCase, self).setUp()
self.backend = impl_denominator.Denominator(
cfg.CONF['backend:agent:denominator']
)
def test_base(self):
self.assertEqual(
['denominator', '-q', '-n', 'fake', '-C', '/etc/denominator.conf'],
self.backend._base()
)
def test_base_without_config_file(self):
self.CONF.set_override(
'config_file', '', 'backend:agent:denominator'
)
self.assertEqual(
['denominator', '-q', '-n', 'fake'],
self.backend._base()
)
@mock.patch('designate.utils.execute')
def test_execute(self, mock_execute):
mock_execute.return_value = ('stdout', None,)
self.assertEqual(
'stdout',
self.backend._execute(
['record', '-z', 'example.org', 'add'], {'name': 'example.org'}
)
)
mock_execute.assert_called_once_with(
'denominator', '-q', '-n', 'fake', '-C', '/etc/denominator.conf',
'record', '-z', 'example.org', 'add', '--name', 'example.org'
)
@mock.patch('designate.utils.execute')
def test_execute_raises(self, mock_execute):
mock_execute.side_effect = utils.processutils.ProcessExecutionError()
self.assertRaises(
exceptions.DesignateException,
self.backend._execute, ['record', '-z', 'example.org', 'add'], {}
)

View File

@ -1,125 +0,0 @@
# Copyright 2016 Hewlett Packard Enterprise Development Company LP
#
# Author: Federico Ceratto <federico.ceratto@hpe.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 unittest import mock
from designate.backend.agent_backend import impl_djbdns
from designate import exceptions
import designate.tests
from designate.tests.unit.agent import backends
class DjbdnsAgentBackendTestCase(designate.tests.TestCase):
@mock.patch.object(impl_djbdns.DjbdnsBackend, '_check_dirs')
def setUp(self, mock_check_dirs):
super(DjbdnsAgentBackendTestCase, self).setUp()
self.CONF.set_override('masters', ['127.0.0.1:5354'], 'service:agent')
self.backend = impl_djbdns.DjbdnsBackend('foo')
def test_start_backend(self):
self.backend.start()
def test_stop_backend(self):
self.backend.stop()
def test_init(self):
self.assertTrue(hasattr(self.backend, '_resolver'))
self.assertEqual(1, self.backend._resolver.timeout)
self.assertEqual(1, self.backend._resolver.lifetime)
self.assertEqual(['127.0.0.1'], self.backend._resolver.nameservers)
self.assertEqual(
'/var/lib/djbdns/root/data.cdb',
self.backend._tinydns_cdb_filename
)
self.assertEqual(
'/var/lib/djbdns/datafiles',
self.backend._datafiles_dir
)
self.assertEqual(
'/var/lib/djbdns/datafiles/%s.zonedata',
self.backend._datafiles_path_tpl
)
self.assertEqual([('127.0.0.1', 5354)], self.backend._masters)
@mock.patch.object(impl_djbdns.DjbdnsBackend, '_check_dirs')
def test_init_no_masters(self, mock_check_dirs):
self.CONF.set_override('masters', [], 'service:agent')
self.assertRaisesRegex(
exceptions.Backend,
'Missing agent AXFR masters',
impl_djbdns.DjbdnsBackend, 'foo'
)
def test_find_zone_serial(self):
class Data(object):
serial = 3
self.backend._resolver = mock.Mock()
self.backend._resolver.query.return_value = [Data(), ]
serial = self.backend.find_zone_serial('example.com')
self.assertEqual(3, serial)
def test_find_zone_serial_error(self):
self.backend._resolver = mock.Mock()
self.backend._resolver.query.side_effect = RuntimeError('foo')
serial = self.backend.find_zone_serial('example.com')
self.assertIsNone(serial)
@mock.patch('designate.backend.agent_backend.impl_djbdns.execute')
def test_create_zone(self, mock_execute):
self.backend._perform_axfr_from_minidns = mock.Mock()
self.backend._rebuild_data_cdb = mock.Mock()
zone = backends.create_dnspy_zone('example.org')
self.backend.create_zone(zone)
def test_update_zone(self):
self.backend._perform_axfr_from_minidns = mock.Mock()
self.backend._rebuild_data_cdb = mock.Mock()
zone = backends.create_dnspy_zone('example.org')
self.backend.update_zone(zone)
@mock.patch('designate.backend.agent_backend.impl_djbdns.os.remove')
def test_delete_zone(self, mock_rm):
self.backend._rebuild_data_cdb = mock.Mock()
self.backend.delete_zone('foo')
mock_rm.assert_called_once_with(
'/var/lib/djbdns/datafiles/foo.zonedata'
)
@mock.patch('designate.backend.agent_backend.impl_djbdns.os.remove')
def test_exception_filter(self, mock_os_remove):
self.backend._rebuild_data_cdb = mock.Mock()
self.assertRaises(
exceptions.Backend,
self.backend.delete_zone, None
)
@mock.patch('designate.backend.agent_backend.impl_djbdns.os.remove')
def test_exception_filter_pass_through(self, mock_os_remove):
self.backend._rebuild_data_cdb = mock.Mock()
mock_os_remove.side_effect = exceptions.Backend
self.assertRaises(
exceptions.Backend,
self.backend.delete_zone, 'foo'
)

View File

@ -1,47 +0,0 @@
# Copyright 2014 Rackspace Inc.
#
# Author: Tim Simmons <tim.simmons@rackspace.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from designate.backend.agent_backend import impl_fake
import designate.tests
from designate.tests.unit.agent import backends
class FakeAgentBackendTestCase(designate.tests.TestCase):
def setUp(self):
super(FakeAgentBackendTestCase, self).setUp()
self.CONF.set_override('listen', ['0.0.0.0:0'], 'service:agent')
self.backend = impl_fake.FakeBackend('foo')
def test_start_backend(self):
self.backend.start()
def test_stop_backend(self):
self.backend.stop()
def test_find_zone_serial(self):
self.backend.find_zone_serial('example.org.')
def test_create_zone(self):
zone = backends.create_dnspy_zone('example.org')
self.backend.create_zone(zone)
def test_update_zone(self):
zone = backends.create_dnspy_zone('example.org')
self.backend.update_zone(zone)
def test_delete_zone(self):
self.backend.delete_zone('example.org.')

View File

@ -1,73 +0,0 @@
# Copyright 2016 Hewlett Packard Enterprise Development Company LP
#
# Author: Federico Ceratto <federico.ceratto@hpe.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 unittest import mock
from designate.backend.agent_backend import impl_gdnsd
import designate.tests
class GdnsdAgentBackendTestCase(designate.tests.TestCase):
@mock.patch.object(impl_gdnsd.GdnsdBackend, '_check_dirs')
def setUp(self, mock_check_dirs):
super(GdnsdAgentBackendTestCase, self).setUp()
self.backend = impl_gdnsd.GdnsdBackend('foo')
@mock.patch.object(impl_gdnsd.GdnsdBackend, '_check_conf')
def test_start_backend(self, mock_check_conf):
self.backend.start()
self.assertTrue(mock_check_conf.called)
def test_stop_backend(self):
self.backend.stop()
def test_init(self):
self.assertEqual(1, self.backend._resolver.timeout)
self.assertEqual(1, self.backend._resolver.lifetime)
self.assertEqual(['127.0.0.1'], self.backend._resolver.nameservers)
self.assertEqual(
'/etc/gdnsd/zones',
self.backend._zonedir_path
)
self.assertEqual('gdnsd', self.backend._gdnsd_cmd_name)
def test_generate_zone_filename(self):
zone_filename = self.backend._generate_zone_filename('A/bc-d_e.f')
self.assertEqual('a@bc-d_e.f', zone_filename)
def test_find_zone_serial(self):
class Data(object):
serial = 3
self.backend._resolver = mock.Mock()
self.backend._resolver.query.return_value = [Data(), ]
serial = self.backend.find_zone_serial('example.com')
self.assertEqual(3, serial)
def test_find_zone_serial_error(self):
self.backend._resolver = mock.Mock()
self.backend._resolver.query.side_effect = RuntimeError('foo')
serial = self.backend.find_zone_serial('example.com')
self.assertIsNone(serial)
@mock.patch('designate.backend.agent_backend.impl_gdnsd.os.remove')
def test_delete_zone(self, mock_osremove):
self.backend.delete_zone('foo-bar.example.org.')
mock_osremove.assert_called_once_with(
'/etc/gdnsd/zones/foo-bar.example.org'
)

View File

@ -1,226 +0,0 @@
# Copyright 2016 Hewlett Packard Enterprise Development Company LP
#
# Author: Federico Ceratto <federico.ceratto@hpe.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 unittest import mock
from unittest.mock import call
from oslo_concurrency import processutils
from designate.backend.agent_backend import impl_knot2
from designate import exceptions
import designate.tests
from designate.tests.unit.agent import backends
class Knot2AgentBackendTestCase(designate.tests.TestCase):
def setUp(self):
super(Knot2AgentBackendTestCase, self).setUp()
self.backend = impl_knot2.Knot2Backend('foo')
self.backend._execute_knotc = mock.Mock()
def test_start_backend(self):
self.backend.start()
def test_stop_backend(self):
self.backend.stop()
def test_create_zone(self):
zone = backends.create_dnspy_zone('example.org')
self.backend.create_zone(zone)
self.backend._execute_knotc.assert_has_calls([
call('conf-begin'),
call('conf-set', 'zone[example.org]',
expected_error='duplicate identifier'),
call('conf-commit'),
call('zone-refresh', 'example.org')
])
def test_create_zone_already_there(self):
self.backend._execute_knotc.return_value = 'duplicate identifier'
zone = backends.create_dnspy_zone('example.org')
self.backend.create_zone(zone)
self.backend._execute_knotc.assert_has_calls([
call('conf-begin'),
call('conf-set', 'zone[example.org]',
expected_error='duplicate identifier'),
call('conf-commit'),
call('zone-refresh', 'example.org')
])
def test_start_minidns_to_knot_axfr(self):
self.backend._start_minidns_to_knot_axfr('foo')
self.backend._execute_knotc.assert_called_with('zone-refresh', 'foo')
@mock.patch('oslo_concurrency.lockutils.lock')
def test_modify_zone(self, mock_lock):
self.backend._modify_zone('blah', 'bar')
self.assertEqual(3, self.backend._execute_knotc.call_count)
self.backend._execute_knotc.assert_called_with('conf-commit')
@mock.patch('oslo_concurrency.lockutils.lock')
def test_modify_zone_exception(self, mock_lock):
# Raise an exception during the second call to _execute_knotc
self.backend._execute_knotc.side_effect = [None, exceptions.Backend,
None]
self.assertRaises(
exceptions.Backend,
self.backend._modify_zone, 'blah', 'bar'
)
self.assertEqual(3, self.backend._execute_knotc.call_count)
self.backend._execute_knotc.assert_has_calls([
call('conf-begin'),
call('blah', 'bar'),
call('conf-abort'),
])
@mock.patch('designate.backend.agent_backend.impl_knot2.execute')
def test_find_zone_serial(self, mock_execute):
result = (
'[example.com.] type: slave | serial: 20 | next-event: idle | '
'auto-dnssec: disabled]'
)
mock_execute.return_value = result, ''
serial = self.backend.find_zone_serial('example.com')
self.assertEqual(20, serial)
@mock.patch('designate.backend.agent_backend.impl_knot2.execute')
def test_find_zone_serial_zone_not_found(self, mock_execute):
mock_execute.side_effect = processutils.ProcessExecutionError(
'error: [example.com.] (no such zone found)'
)
serial = self.backend.find_zone_serial('example.com')
self.assertIsNone(serial)
@mock.patch('designate.backend.agent_backend.impl_knot2.execute')
def test_find_zone_serial_unexpected_output(self, mock_execute):
mock_execute.return_value = 'bogus output', ''
self.assertRaises(
exceptions.Backend,
self.backend.find_zone_serial, 'example.com'
)
@mock.patch('designate.backend.agent_backend.impl_knot2.execute')
def test_find_zone_serial_error(self, mock_execute):
mock_execute.side_effect = processutils.ProcessExecutionError('blah')
self.assertRaises(
exceptions.Backend,
self.backend.find_zone_serial, 'example.com'
)
def test_update_zone(self):
zone = backends.create_dnspy_zone('example.org')
self.backend.update_zone(zone)
self.backend._execute_knotc.assert_called_once_with(
'zone-refresh', 'example.org'
)
def test_delete_zone(self):
self.backend.delete_zone('example.org')
self.backend._execute_knotc.assert_has_calls([
call('conf-begin'),
call('conf-unset', 'zone[example.org]',
expected_error='invalid identifier'),
call('conf-commit'),
])
def test_delete_zone_already_gone(self):
self.backend._execute_knotc.return_value = 'duplicate identifier'
self.backend.delete_zone('example.org')
self.backend._execute_knotc.assert_has_calls([
call('conf-begin'),
call('conf-unset', 'zone[example.org]',
expected_error='invalid identifier'),
call('conf-commit'),
])
class Knot2AgentExecuteTestCase(designate.tests.TestCase):
def setUp(self):
super(Knot2AgentExecuteTestCase, self).setUp()
self.backend = impl_knot2.Knot2Backend('foo')
def test_init(self):
self.assertEqual('knotc', self.backend._knotc_cmd_name)
@mock.patch('designate.backend.agent_backend.impl_knot2.execute')
def test_execute_knotc_ok(self, mock_execute):
mock_execute.return_value = ('OK', '')
self.backend._execute_knotc('a1', 'a2')
mock_execute.assert_called_with('knotc', 'a1', 'a2')
self.assertEqual(1, mock_execute.call_count)
@mock.patch('designate.backend.agent_backend.impl_knot2.execute')
def test_execute_knotc_expected_error(self, mock_execute):
mock_execute.return_value = ('xyz', '')
self.backend._execute_knotc('a1', 'a2', expected_error='xyz')
mock_execute.assert_called_once_with('knotc', 'a1', 'a2')
@mock.patch('designate.backend.agent_backend.impl_knot2.execute')
def test_execute_knotc_expected_output(self, mock_execute):
mock_execute.return_value = ('xyz', '')
self.backend._execute_knotc('a1', 'a2', expected_output='xyz')
mock_execute.assert_called_once_with('knotc', 'a1', 'a2')
@mock.patch('designate.backend.agent_backend.impl_knot2.execute')
def test_execute_knotc_with_error(self, mock_execute):
mock_execute.return_value = ('xyz', '')
self.assertRaises(
exceptions.Backend,
self.backend._execute_knotc, 'a1', 'a2'
)
mock_execute.assert_called_once_with('knotc', 'a1', 'a2')
@mock.patch('designate.backend.agent_backend.impl_knot2.execute')
def test_execute_knotc_raising_exception(self, mock_execute):
mock_execute.side_effect = processutils.ProcessExecutionError
self.assertRaises(
exceptions.Backend,
self.backend._execute_knotc, 'a1', 'a2'
)
mock_execute.assert_called_once_with('knotc', 'a1', 'a2')

View File

@ -1,141 +0,0 @@
# Copyright 2016 Cloudbase Solutions Srl
# All Rights Reserved.
#
# Author: Alin Balutoiu <abalutoiu@cloudbasesolutions.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 unittest import mock
from os_win import constants
from os_win import exceptions as os_win_exc
from os_win import utilsfactory
from designate.backend.agent_backend import impl_msdns
from designate import exceptions
import designate.tests
from designate.tests.unit.agent import backends
class MSDNSAgentBackendTestCase(designate.tests.TestCase):
@mock.patch.object(utilsfactory, 'get_dnsutils')
def setUp(self, mock_get_dnsutils):
super(MSDNSAgentBackendTestCase, self).setUp()
self.zone_name = 'example.com'
self.CONF.set_override('masters', ['127.0.0.1:5354'], 'service:agent')
self.backend = impl_msdns.MSDNSBackend('foo')
self.backend._dnsutils = mock.MagicMock()
def test_start_backend(self):
self.backend.start()
def test_stop_backend(self):
self.backend.stop()
def test_init(self):
self.assertEqual(['127.0.0.1'], self.backend._masters)
@mock.patch.object(utilsfactory, 'get_dnsutils')
def test_init_no_masters(self, mock_get_dnsutils):
self.CONF.set_override('masters', [], 'service:agent')
self.assertRaisesRegex(
exceptions.Backend,
'Missing agent AXFR masters',
impl_msdns.MSDNSBackend, 'foo'
)
def test_find_zone_serial(self):
serial = self.backend.find_zone_serial(self.zone_name)
expected_serial = self.backend._dnsutils.get_zone_serial.return_value
self.assertEqual(expected_serial, serial)
self.backend._dnsutils.get_zone_serial.assert_called_once_with(
self.zone_name
)
def test_find_zone_serial_error(self):
self.backend._dnsutils.get_zone_serial.side_effect = (
os_win_exc.DNSZoneNotFound(zone_name=self.zone_name))
serial = self.backend.find_zone_serial(self.zone_name)
self.assertIsNone(serial)
self.backend._dnsutils.get_zone_serial.assert_called_once_with(
self.zone_name
)
def test_create_zone(self):
zone = backends.create_dnspy_zone(self.zone_name)
self.backend.create_zone(zone)
self.backend._dnsutils.zone_create.assert_called_once_with(
zone_name=self.zone_name,
zone_type=constants.DNS_ZONE_TYPE_SECONDARY,
ds_integrated=False,
ip_addrs=self.backend._masters
)
def test_create_zone_already_existing_diff(self):
zone = backends.create_dnspy_zone(self.zone_name)
self.backend._dnsutils.zone_create.side_effect = (
os_win_exc.DNSZoneAlreadyExists(zone_name=self.zone_name))
self.assertRaises(
os_win_exc.DNSZoneAlreadyExists,
self.backend.create_zone, zone
)
self.backend._dnsutils.get_zone_properties.assert_called_once_with(
self.zone_name
)
def test_create_zone_already_existing_identical(self):
zone = backends.create_dnspy_zone(self.zone_name)
self.backend._dnsutils.zone_create.side_effect = (
os_win_exc.DNSZoneAlreadyExists(zone_name=self.zone_name)
)
mock_zone_properties = {
'zone_type': constants.DNS_ZONE_TYPE_SECONDARY,
'ds_integrated': False,
'master_servers': self.backend._masters
}
self.backend._dnsutils.get_zone_properties.return_value = (
mock_zone_properties
)
self.backend.create_zone(zone)
self.backend._dnsutils.get_zone_properties.assert_called_once_with(
self.zone_name
)
def test_update_zone(self):
zone = backends.create_dnspy_zone(self.zone_name)
self.backend.update_zone(zone)
self.backend._dnsutils.zone_update.assert_called_once_with(
self.zone_name
)
def test_delete_zone(self):
self.backend.delete_zone(self.zone_name)
self.backend._dnsutils.zone_delete.assert_called_once_with(
self.zone_name
)

View File

@ -1,209 +0,0 @@
# Copyright 2014 Rackspace Inc.
#
# Author: Tim Simmons <tim.simmons@rackspace.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import binascii
from unittest import mock
import dns
import dns.resolver
import designate
from designate.agent import handler
from designate.backend import private_codes
import designate.tests
class AgentRequestHandlerTest(designate.tests.TestCase):
def setUp(self):
super(AgentRequestHandlerTest, self).setUp()
self.CONF.set_override('allow_notify', ['0.0.0.0'], 'service:agent')
self.CONF.set_override('backend_driver', 'fake', 'service:agent')
self.CONF.set_override('transfer_source', '1.2.3.4', 'service:agent')
self.handler = handler.RequestHandler()
self.addr = ['0.0.0.0', 5558]
# TODO(johnsom) Remove this after the agents framework is removed or
# the protocol has been updated to not use an unassigned opcode(14).
dns.opcode.Opcode = private_codes.OpcodeWith14
def test_init(self):
self.CONF.set_override('masters', ['192.0.2.1', '192.0.2.2'],
'service:agent')
hndlr = handler.RequestHandler()
self.assertEqual(
[
{'host': '192.0.2.1', 'port': 53},
{'host': '192.0.2.2', 'port': 53}
],
hndlr.masters
)
@mock.patch.object(dns.resolver.Resolver, 'query')
@mock.patch('designate.dnsutils.do_axfr')
def test_receive_notify(self, mock_doaxfr, mock_query):
"""
Get a NOTIFY and ensure the response is right,
and an AXFR is triggered
"""
payload = ('1a7220000001000000000000076578616d706c6503636f6d000006'
'0001')
# expected response is NOERROR, other fields are
# opcode NOTIFY
# rcode NOERROR
# flags QR AA
# ;QUESTION
# example.com. IN SOA
# ;ANSWER
# ;AUTHORITY
# ;ADDITIONAL
expected_response = (b'1a72a4000001000000000000076578616d706c6503'
b'636f6d0000060001')
request = dns.message.from_wire(binascii.a2b_hex(payload))
request.environ = {'addr': ['0.0.0.0', 1234]}
response = next(self.handler(request)).to_wire()
self.assertEqual(expected_response, binascii.b2a_hex(response))
def test_receive_notify_bad_notifier(self):
payload = '243520000001000000000000076578616d706c6503636f6d0000060001'
# expected response is REFUSED, other fields are
# opcode NOTIFY
# rcode REFUSED
# flags QR
# ;QUESTION
# example.com. IN SOA
# ;ANSWER
# ;AUTHORITY
# ;ADDITIONAL
expected_response = (b'2435a0050001000000000000076578616d706c6503636f'
b'6d0000060001')
request = dns.message.from_wire(binascii.a2b_hex(payload))
# Bad 'requester'
request.environ = {'addr': ['6.6.6.6', 1234]}
response = next(self.handler(request)).to_wire()
self.assertEqual(expected_response, binascii.b2a_hex(response))
@mock.patch.object(dns.resolver.Resolver, 'query')
@mock.patch('designate.dnsutils.do_axfr')
def test_receive_create(self, mock_doaxfr, mock_query):
payload = '735d70000001000000000000076578616d706c6503636f6d00ff02ff00'
# Expected NOERROR other fields are
# opcode 14
# rcode NOERROR
# flags QR AA
# ;QUESTION
# example.com. CLASS65280 TYPE65282
# ;ANSWER
# ;AUTHORITY
# ;ADDITIONAL
expected_response = (b'735df4000001000000000000076578616d706c6503636f'
b'6d00ff02ff00')
request = dns.message.from_wire(binascii.a2b_hex(payload))
request.environ = {'addr': ['0.0.0.0', 1234]}
with mock.patch.object(
designate.backend.agent_backend.impl_fake.FakeBackend,
'find_zone_serial', return_value=None):
response = next(self.handler(request)).to_wire()
self.assertEqual(expected_response, binascii.b2a_hex(response))
def test_receive_create_bad_notifier(self):
payload = '8dfd70000001000000000000076578616d706c6503636f6d00ff02ff00'
# expected response is REFUSED, other fields are
# opcode 14
# rcode REFUSED
# flags QR
# ;QUESTION
# example.com. CLASS65280 TYPE65282
# ;ANSWER
# ;AUTHORITY
# ;ADDITIONAL
expected_response = (b'8dfdf0050001000000000000076578616d706c6503636f'
b'6d00ff02ff00')
request = dns.message.from_wire(binascii.a2b_hex(payload))
# Bad 'requester'
request.environ = {'addr': ['6.6.6.6', 1234]}
response = next(self.handler(request)).to_wire()
self.assertEqual(binascii.b2a_hex(response), expected_response)
@mock.patch('designate.utils.execute')
def test_receive_delete(self, mock_execute):
payload = '3b9970000001000000000000076578616d706c6503636f6d00ff03ff00'
# Expected NOERROR other fields are
# opcode 14
# rcode NOERROR
# flags QR AA
# ;QUESTION
# example.com. CLASS65280 TYPE65283
# ;ANSWER
# ;AUTHORITY
# ;ADDITIONAL
expected_response = (b'3b99f4000001000000000000076578616d706c6503636f'
b'6d00ff03ff00')
request = dns.message.from_wire(binascii.a2b_hex(payload))
request.environ = {'addr': ['0.0.0.0', 1234]}
response = next(self.handler(request)).to_wire()
self.assertEqual(expected_response, binascii.b2a_hex(response))
def test_receive_delete_bad_notifier(self):
payload = 'e6da70000001000000000000076578616d706c6503636f6d00ff03ff00'
# expected response is REFUSED, other fields are
# opcode 14
# rcode REFUSED
# flags QR
# ;QUESTION
# example.com. CLASS65280 TYPE65283
# ;ANSWER
# ;AUTHORITY
# ;ADDITIONAL
expected_response = (b'e6daf0050001000000000000076578616d706c6503636f'
b'6d00ff03ff00')
request = dns.message.from_wire(binascii.a2b_hex(payload))
# Bad 'requester'
request.environ = {'addr': ['6.6.6.6', 1234]}
response = next(self.handler(request)).to_wire()
self.assertEqual(expected_response, binascii.b2a_hex(response))
@mock.patch.object(dns.resolver.Resolver, 'query')
@mock.patch.object(designate.dnsutils, 'do_axfr')
def test_transfer_source(self, mock_doaxfr, mock_query):
payload = '735d70000001000000000000076578616d706c6503636f6d00ff02ff00'
# Expected NOERROR other fields are
# opcode 14
# rcode NOERROR
# flags QR AA
# ;QUESTION
# example.com. CLASS65280 TYPE65282
# ;ANSWER
# ;AUTHORITY
# ;ADDITIONAL
expected_response = (b'735df4000001000000000000076578616d706c6503636f'
b'6d00ff02ff00')
request = dns.message.from_wire(binascii.a2b_hex(payload))
request.environ = {'addr': ['0.0.0.0', 1234]}
with mock.patch.object(
designate.backend.agent_backend.impl_fake.FakeBackend,
'find_zone_serial', return_value=None):
response = next(self.handler(request)).to_wire()
mock_doaxfr.assert_called_with(
'example.com.', [], source='1.2.3.4'
)
self.assertEqual(expected_response, binascii.b2a_hex(response))

View File

@ -1,78 +0,0 @@
# Copyright 2014 Rackspace Inc.
#
# Author: Tim Simmons <tim.simmons@rackspace.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from unittest import mock
from designate.agent import service
from designate.backend import agent_backend
from designate.backend.agent_backend import impl_fake
from designate import dnsmiddleware
import designate.tests
from designate.tests import fixtures
from designate import utils
class AgentServiceTest(designate.tests.TestCase):
def setUp(self):
super(AgentServiceTest, self).setUp()
self.stdlog = fixtures.StandardLogging()
self.useFixture(self.stdlog)
self.CONF.set_override('listen', ['0.0.0.0:0'], 'service:agent')
self.CONF.set_override('notify_delay', 0, 'service:agent')
self.service = service.Service()
self.service.dns_service._start = mock.Mock()
def test_service_start(self):
self.service.start()
self.assertTrue(self.service.dns_service._start.called)
def test_service_stop(self):
self.service.dns_service.stop = mock.Mock()
self.service.backend.stop = mock.Mock()
self.service.stop()
self.assertTrue(self.service.dns_service.stop.called)
self.assertTrue(self.service.backend.stop.called)
self.assertIn('Stopping agent service', self.stdlog.logger.output)
def test_service_name(self):
self.assertEqual('agent', self.service.service_name)
def test_get_backend(self):
backend = agent_backend.get_backend('fake', agent_service=self.service)
self.assertIsInstance(backend, impl_fake.FakeBackend)
@mock.patch.object(utils, 'cache_result')
def test_get_dns_application(self, mock_cache_result):
self.assertIsInstance(
self.service.dns_application,
dnsmiddleware.SerializationMiddleware
)
@mock.patch.object(utils, 'cache_result')
def test_get_dns_application_with_notify_delay(self, mock_cache_result):
self.service = service.Service()
self.CONF.set_override('notify_delay', 1.0, 'service:agent')
self.assertIsInstance(
self.service.dns_application,
dnsmiddleware.SerializationMiddleware
)

View File

@ -1,185 +0,0 @@
# Author: Federico Ceratto <federico.ceratto@hpe.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 unittest import mock
import dns
import dns.query
import dns.rdataclass
import dns.rdatatype
from oslo_config import cfg
from oslo_config import fixture as cfg_fixture
import oslotest.base
import designate.backend.agent as agent
import designate.backend.private_codes as pcodes
from designate import context
from designate import dnsutils
from designate import exceptions
from designate import objects
from designate.tests.unit import RoObject
CONF = cfg.CONF
class AgentBackendTestCase(oslotest.base.BaseTestCase):
def setUp(self):
super(AgentBackendTestCase, self).setUp()
self.useFixture(cfg_fixture.Config(CONF))
self.context = mock.Mock()
self.admin_context = mock.Mock()
mock.patch.object(
context.DesignateContext, 'get_admin_context',
return_value=self.admin_context).start()
CONF.set_override('poll_timeout', 1, 'service:worker')
CONF.set_override('poll_retry_interval', 4, 'service:worker')
CONF.set_override('poll_max_retries', 5, 'service:worker')
CONF.set_override('poll_delay', 6, 'service:worker')
self.zone = objects.Zone(
id='e2bed4dc-9d01-11e4-89d3-123b93f75cba',
name='example.com.',
email='example@example.com',
)
self.target = {
'id': '4588652b-50e7-46b9-b688-a9bad40a873e',
'type': 'agent',
'masters': [],
'options': [
{'key': 'host', 'value': 2},
{'key': 'port', 'value': 3},
],
}
self.backend = agent.AgentPoolBackend(
objects.PoolTarget.from_dict(self.target)
)
def test_create_zone(self):
self.backend._make_and_send_dns_message = mock.Mock(
return_value=1
)
out = self.backend.create_zone(self.context, self.zone)
self.backend._make_and_send_dns_message.assert_called_with(
self.zone.name, 1, 14, pcodes.CREATE, pcodes.SUCCESS, 2, 3)
self.assertIsNone(out)
def test_create_zone_exception(self):
self.backend._make_and_send_dns_message = mock.Mock(
return_value=None
)
self.assertRaisesRegex(
exceptions.Backend, 'Failed create_zone()',
self.backend.create_zone, self.context, self.zone,
)
self.backend._make_and_send_dns_message.assert_called_with(
self.zone.name, 1, 14, pcodes.CREATE, pcodes.SUCCESS, 2, 3)
def test_update_zone(self):
self.assertIsNone(self.backend.update_zone(self.context, self.zone))
def test_delete_zone(self):
self.backend._make_and_send_dns_message = mock.Mock(
return_value=(1, 2))
out = self.backend.delete_zone(self.context, self.zone)
self.backend._make_and_send_dns_message.assert_called_with(
self.zone.name, 1, 14, pcodes.DELETE, pcodes.SUCCESS, 2, 3)
self.assertIsNone(out)
def test_delete_zone_exception(self):
self.backend._make_and_send_dns_message = mock.Mock(
return_value=None
)
self.assertRaisesRegex(
exceptions.Backend, 'Failed delete_zone()',
self.backend.delete_zone, self.context, self.zone,
)
self.backend._make_and_send_dns_message.assert_called_with(
self.zone.name, 1, 14, pcodes.DELETE, pcodes.SUCCESS, 2, 3)
@mock.patch.object(dnsutils, 'send_dns_message')
def test_make_and_send_dns_message_timeout(self, mock_send_dns_message):
self.backend._make_dns_message = mock.Mock(return_value='')
mock_send_dns_message.side_effect = dns.exception.Timeout()
self.assertIsNone(
self.backend._make_and_send_dns_message('h', 123, 1, 2, 3, 4, 5)
)
@mock.patch.object(dnsutils, 'send_dns_message')
def test_make_and_send_dns_message_bad_response(self,
mock_send_dns_message):
self.backend._make_dns_message = mock.Mock(return_value='')
mock_send_dns_message.side_effect = dns.query.BadResponse()
self.assertIsNone(
self.backend._make_and_send_dns_message('h', 123, 1, 2, 3, 4, 5)
)
@mock.patch.object(dnsutils, 'send_dns_message')
def test_make_and_send_dns_message_missing_AA_flags(self,
mock_send_dns_message):
self.backend._make_dns_message = mock.Mock(return_value='')
response = RoObject(
rcode=mock.Mock(return_value=dns.rcode.NOERROR),
# rcode is NOERROR but (flags & dns.flags.AA) gives 0
flags=0,
)
mock_send_dns_message.return_value = response
self.assertIsNone(
self.backend._make_and_send_dns_message('h', 123, 1, 2, 3, 4, 5)
)
@mock.patch.object(dnsutils, 'send_dns_message')
def test_make_and_send_dns_message_error_flags(self,
mock_send_dns_message):
self.backend._make_dns_message = mock.Mock(return_value='')
response = RoObject(
rcode=mock.Mock(return_value=dns.rcode.NOERROR),
# rcode is NOERROR but flags are not NOERROR
flags=123,
ednsflags=321
)
mock_send_dns_message.return_value = response
self.assertIsNone(
self.backend._make_and_send_dns_message('h', 123, 1, 2, 3, 4, 5)
)
@mock.patch.object(dnsutils, 'send_dns_message')
def test_make_and_send_dns_message(self, mock_send_dns_message):
self.backend._make_dns_message = mock.Mock(return_value='')
response = RoObject(
rcode=mock.Mock(return_value=dns.rcode.NOERROR),
flags=agent.dns.flags.AA,
ednsflags=321
)
mock_send_dns_message.return_value = response
self.assertEqual(
response,
self.backend._make_and_send_dns_message('h', 123, 1, 2, 3, 4, 5)
)

View File

@ -16,7 +16,6 @@ from oslo_config import cfg
from oslo_config import fixture as cfg_fixture
import oslotest.base
from designate.cmd import agent
from designate.cmd import api
from designate.cmd import central
from designate.cmd import mdns
@ -37,20 +36,6 @@ class CmdTestCase(oslotest.base.BaseTestCase):
super(CmdTestCase, self).setUp()
self.useFixture(cfg_fixture.Config(CONF))
@mock.patch('designate.agent.service.Service')
def test_agent(self, mock_service, mock_read_config, mock_log_setup,
mock_heartbeat, mock_serve, mock_wait):
CONF.set_override('workers', 1, 'service:agent')
agent.main()
mock_read_config.assert_called_with('designate', mock.ANY)
mock_log_setup.assert_called_with(mock.ANY, 'designate')
mock_service.assert_called_with()
mock_heartbeat.assert_called()
mock_serve.assert_called_with(mock.ANY, workers=1)
mock_wait.assert_called_with()
@mock.patch('designate.api.service.Service')
def test_api(self, mock_service, mock_read_config, mock_log_setup,
mock_heartbeat, mock_serve, mock_wait):

View File

@ -1,103 +0,0 @@
# Configure the agent backend
# Enable with:
# DESIGNATE_BACKEND_DRIVER=agent
# DESIGNATE_AGENT_BACKEND_DRIVER=<an agent backend>
# 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_AGENT_XTRACE=$(set +o | grep xtrace)
set +o xtrace
# Get agent backend configuration
# -------------------------------
if [[ -r $DESIGNATE_PLUGINS/backend-agent-$DESIGNATE_AGENT_BACKEND_DRIVER ]]; then
# Load plugin
source $DESIGNATE_PLUGINS/backend-agent-$DESIGNATE_AGENT_BACKEND_DRIVER
fi
# Entry Points
# ------------
# install_designate_backend - install any external requirements
function install_designate_backend {
# Install the Agent Backend
install_designate_agent_backend
}
# configure_designate_backend - make configuration changes, including those to other services
function configure_designate_backend {
# Generate Designate pool.yaml file
sudo tee $DESIGNATE_CONF_DIR/pools.yaml > /dev/null <<EOF
---
- name: default
description: DevStack Agent Pool
attributes: {}
ns_records:
- hostname: $DESIGNATE_DEFAULT_NS_RECORD
priority: 1
nameservers:
- host: $(ipv6_unquote $DESIGNATE_SERVICE_HOST)
port: $DESIGNATE_SERVICE_PORT_DNS
targets:
- type: agent
description: Agent Instance
masters:
- host: $(ipv6_unquote $DESIGNATE_SERVICE_HOST)
port: $DESIGNATE_SERVICE_PORT_MDNS
options:
host: $(ipv6_unquote $DESIGNATE_SERVICE_HOST)
port: $DESIGNATE_SERVICE_PORT_AGENT
EOF
# Configure Agent Settings
iniset $DESIGNATE_CONF service:agent backend_driver $DESIGNATE_AGENT_BACKEND_DRIVER
iniset $DESIGNATE_CONF service:agent host $(ipv6_unquote $DESIGNATE_SERVICE_HOST)
iniset $DESIGNATE_CONF service:agent port $DESIGNATE_SERVICE_PORT_AGENT
iniset $DESIGNATE_CONF service:agent masters "$DESIGNATE_SERVICE_HOST:$DESIGNATE_SERVICE_PORT_MDNS"
# Configure the Agent Backend
configure_designate_agent_backend
}
# init_designate_backend - initialize databases, etc.
function init_designate_backend {
# Init the Agent Backend
init_designate_agent_backend
}
# start_designate_backend - start any external services
function start_designate_backend {
# Start the Agent Backend
start_designate_agent_backend
}
# stop_designate_backend - stop any external services
function stop_designate_backend {
# Stop the Agent Backend
stop_designate_agent_backend
}
# cleanup_designate_backend - remove transient data and cache
function cleanup_designate_backend {
# Cleanup the Agent Backend
cleanup_designate_agent_backend
}
# Restore xtrace
$DP_AGENT_XTRACE

View File

@ -1,55 +0,0 @@
# Configure the fake agent backend
# Enable with:
# DESIGNATE_BACKEND_DRIVER=agent
# DESIGNATE_AGENT_BACKEND_DRIVER=fake
# install_designate_agent_backend - install any external requirements
# configure_designate_agent_backend - make configuration changes, including those to other services
# init_designate_agent_backend - initialize databases, etc.
# start_designate_agent_backend - start any external services
# stop_designate_agent_backend - stop any external services
# cleanup_designate_agent_backend - remove transient data and cache
# Save trace setting
DP_AGENT_FAKE_XTRACE=$(set +o | grep xtrace)
set +o xtrace
# Defaults
# --------
# Entry Points
# ------------
# install_designate_agent_backend - install any external requirements
function install_designate_agent_backend {
:
}
# configure_designate_agent_backend - make configuration changes, including those to other services
function configure_designate_agent_backend {
:
}
# init_designate_agent_backend - initialize databases, etc.
function init_designate_agent_backend {
:
}
# start_designate_agent_backend - start any external services
function start_designate_agent_backend {
:
}
# stop_designate_agent_backend - stop any external services
function stop_designate_agent_backend {
:
}
# cleanup_designate_agent_backend - remove transient data and cache
function cleanup_designate_agent_backend {
:
}
# Restore xtrace
$DP_AGENT_FAKE_XTRACE

View File

@ -1,130 +0,0 @@
# Configure the Knot2 agent backend for Devstack
# Enable this pluging by adding these line to local.conf:
#
# DESIGNATE_BACKEND_DRIVER=agent
# DESIGNATE_AGENT_BACKEND_DRIVER=knot2
# install_designate_agent_backend - install any external requirements
# configure_designate_agent_backend - make configuration changes, including those to other services
# init_designate_agent_backend - initialize databases, etc.
# start_designate_agent_backend - start any external services
# stop_designate_agent_backend - stop any external services
# cleanup_designate_agent_backend - remove transient data and cache
# Save trace setting
DP_AGENT_KNOT_XTRACE=$(set +o | grep xtrace)
set +o xtrace
# Defaults
# --------
KNOT_SERVICE_NAME=knot
KNOT_CFG_DIR=/etc/knot
KNOT_VAR_DIR=/var/lib/knot
KNOT_USER=knot
KNOT_GROUP=knot
if is_fedora; then
echo "only Ubuntu is supported right now"
fi
# Entry Points
# ------------
# install_designate_agent_backend - install any external requirements
function install_designate_agent_backend {
if is_ubuntu; then
# https://github.com/oerdnj/deb.sury.org/issues/56
LC_ALL=C.UTF-8 sudo add-apt-repository --yes ppa:cz.nic-labs/knot-dns
sudo apt-get update
echo "---- available knot package ---"
sudo apt-cache show knot
echo "---- installing knot ---"
sudo apt-get install -y knot
else
echo "only Ubuntu is supported right now"
exit 1
fi
}
# configure_designate_agent_backend - make configuration changes, including those to other services
function configure_designate_agent_backend {
# [re]create the config database
stop_service knot
sudo sh -c "rm /var/lib/knot/*zone /var/lib/knot/*/*.mdb -f"
sudo knotc conf-init -v
# Create /etc/default/knot
cat <<EOF | sudo tee /etc/default/knot
# Created by $0 on $(date)
KNOTD_ARGS="-C /var/lib/knot/confdb"
EOF
# Apply this workaround for bug
# https://gitlab.labs.nic.cz/labs/knot/issues/455
sudo sh -c "cd /etc/default/ && test -f knotd || ln -s knot knotd"
start_service knot
sleep 1
# Ensure the confdb is present
sudo test -f /var/lib/knot/confdb/data.mdb
# Create the configuration
MINIDNS_IPADDR=$(ipv6_unquote $DESIGNATE_SERVICE_HOST)
sudo knotc conf-begin
sudo knotc conf-set server.listen $(ipv6_unquote $DESIGNATE_SERVICE_HOST)@$DESIGNATE_SERVICE_PORT_DNS
sudo knotc conf-set remote[minidns]
sudo knotc conf-set remote[minidns].address $(ipv6_unquote $DESIGNATE_SERVICE_HOST)@$DESIGNATE_SERVICE_PORT_MDNS
sudo knotc conf-set template[default]
sudo knotc conf-set template[default].master minidns
sudo knotc conf-set template[default].acl acl_minidns
sudo knotc conf-set template[default].semantic-checks on
# Create localdomain as a workaround for
# https://gitlab.labs.nic.cz/labs/knot/issues/457
sudo knotc conf-set zone[localdomain]
sudo knotc conf-set log.any info
sudo knotc conf-set log.target syslog
sudo knotc conf-set acl[acl_minidns]
sudo knotc conf-set acl[acl_minidns].address $DESIGNATE_SERVICE_HOST
sudo knotc conf-set acl[acl_minidns].action notify
echo "--------------"
sudo knotc conf-diff
echo "--------------"
sudo knotc conf-commit
sudo knotc conf-check
# Ensure the zone survives a restart
sleep 1
sudo service knot restart
sleep 1
sudo knotc zone-status localdomain
echo "Testing Knot: this should return the daemon version"
dig @$(ipv6_unquote $DESIGNATE_SERVICE_HOST) -p$DESIGNATE_SERVICE_PORT_DNS version.server CH TXT
}
# init_designate_agent_backend - initialize databases, etc.
function init_designate_agent_backend {
:
}
# start_designate_agent_backend - start any external services
function start_designate_agent_backend {
start_service knot
}
# stop_designate_agent_backend - stop any external services
function stop_designate_agent_backend {
stop_service knot
}
# cleanup_designate_agent_backend - remove transient data and cache
function cleanup_designate_agent_backend {
:
}
# Restore xtrace
$DP_AGENT_KNOT_XTRACE

View File

@ -1,116 +0,0 @@
# Configure the agent backend
# Enable this pluging by adding these line to local.conf:
#
# DESIGNATE_BACKEND_DRIVER=agent
# DESIGNATE_AGENT_BACKEND_DRIVER=msdns
# Dependencies:
# ``functions`` file
# ``designate`` configuration
# install_designate_agent_backend - install any external requirements
# configure_designate_agent_backend - make configuration changes, including those to other services
# init_designate_agent_backend - initialize databases, etc.
# start_designate_agent_backend - start any external services
# stop_designate_agent_backend - stop any external services
# cleanup_designate_agent_backend - remove transient data and cache
# Save trace setting
DP_AGENT_MSDNS_XTRACE=$(set +o | grep xtrace)
set +o xtrace
# Defaults
# --------
DESIGNATE_MSDNS_MASTERS=${DESIGNATE_MSDNS_MASTERS:-"$DESIGNATE_SERVICE_HOST:$DESIGNATE_SERVICE_PORT_MDNS"}
DESIGNATE_MSDNS_HOST_IP=${DESIGNATE_MSDNS_HOST_IP:-}
DESIGNATE_MSDNS_HOST_PORT=${DESIGNATE_MSDNS_HOST_PORT:-}
# Sanity Checks
# -------------
if [ -z "$DESIGNATE_MSDNS_MASTERS" ]; then
die $LINENO "You must configure DESIGNATE_MSDNS_MASTERS"
fi
if [ -z "$DESIGNATE_MSDNS_HOST_IP" ]; then
die $LINENO "You must configure DESIGNATE_MSDNS_HOST_IP with the IP of the MS DNS host"
fi
if [ -z "$DESIGNATE_MSDNS_HOST_PORT" ]; then
die $LINENO "You must configure DESIGNATE_MSDNS_HOST_PORT with the PORT of the MS DNS host"
fi
if [ "$DESIGNATE_SERVICE_PORT_MDNS" != "53" ]; then
die $LINENO "Microsoft DNS requires DESIGNATE_SERVICE_PORT_MDNS to be set to '53'"
fi
# Entry Points
# ------------
# install_designate_agent_backend - install any external requirements
function install_designate_agent_backend {
:
}
# configure_designate_agent_backend - make configuration changes, including those to other services
function configure_designate_agent_backend {
# Generate Designate pool.yaml file
sudo tee $DESIGNATE_CONF_DIR/pools.yaml > /dev/null <<EOF
---
- name: default
description: DevStack MSDNS Pool
attributes: {}
ns_records:
- hostname: $DESIGNATE_DEFAULT_NS_RECORD
priority: 1
nameservers:
- host: $DESIGNATE_MSDNS_HOST_IP
port: $DESIGNATE_MSDNS_HOST_PORT
targets:
- type: agent
description: MSDNS Agent Instance
masters:
- host: $(ipv6_unquote $DESIGNATE_SERVICE_HOST)
port: $DESIGNATE_SERVICE_PORT_MDNS
options:
host: $DESIGNATE_MSDNS_HOST_IP
port: $DESIGNATE_MSDNS_HOST_PORT
EOF
echo "# Sample Config for Windows Agent service" | sudo tee ${DESIGNATE_CONF}.win
echo "# This file should be copied to the Windows DNS server and used as the Designate config file" | sudo tee -a ${DESIGNATE_CONF}.win
# Configure Agent Settings
iniset ${DESIGNATE_CONF}.win service:agent backend_driver $DESIGNATE_AGENT_BACKEND_DRIVER
iniset ${DESIGNATE_CONF}.win service:agent host $DESIGNATE_MSDNS_HOST_IP
iniset ${DESIGNATE_CONF}.win service:agent port $DESIGNATE_MSDNS_HOST_PORT
iniset ${DESIGNATE_CONF}.win service:agent masters "$DESIGNATE_MSDNS_MASTERS"
}
# init_designate_agent_backend - initialize databases, etc.
function init_designate_agent_backend {
:
}
# start_designate_agent_backend - start any external services
function start_designate_agent_backend {
:
}
# stop_designate_agent_backend - stop any external services
function stop_designate_agent_backend {
:
}
# cleanup_designate_agent_backend - remove transient data and cache
function cleanup_designate_agent_backend {
:
}
# Restore xtrace
$DP_AGENT_MSDNS_XTRACE

View File

@ -58,9 +58,6 @@ function configure_designate {
iniset $DESIGNATE_CONF coordination backend_url $DESIGNATE_COORDINATION_URL
fi
# Agent Configuration
iniset $DESIGNATE_CONF service:agent workers $API_WORKERS
# API Configuration
sudo cp $DESIGNATE_DIR/etc/designate/api-paste.ini $DESIGNATE_APIPASTE_CONF
iniset $DESIGNATE_CONF service:api enabled_extensions_v2 $DESIGNATE_ENABLED_EXTENSIONS_V2
@ -278,7 +275,6 @@ function start_designate {
run_process designate-central "$DESIGNATE_BIN_DIR/designate-central --config-file $DESIGNATE_CONF"
run_process designate-mdns "$DESIGNATE_BIN_DIR/designate-mdns --config-file $DESIGNATE_CONF"
run_process designate-agent "$DESIGNATE_BIN_DIR/designate-agent --config-file $DESIGNATE_CONF"
run_process designate-sink "$DESIGNATE_BIN_DIR/designate-sink --config-file $DESIGNATE_CONF"
run_process designate-worker "$DESIGNATE_BIN_DIR/designate-worker --config-file $DESIGNATE_CONF"
@ -314,7 +310,6 @@ function stop_designate {
stop_process designate-central
stop_process designate-mdns
stop_process designate-agent
stop_process designate-sink
stop_process designate-worker
stop_process designate-producer
@ -324,12 +319,7 @@ function stop_designate {
# This is the main for plugin.sh
if is_service_enabled designate; then
# Sanify check for agent backend
# ------------------------------
if ! is_service_enabled designate-agent && [ "$DESIGNATE_BACKEND_DRIVER" == "agent" ]; then
die $LINENO "To use the agent backend, you must enable the designate-agent service"
fi
if [[ "$1" == "stack" && "$2" == "install" ]]; then
echo_summary "Installing Designate client"
install_designateclient

View File

@ -1,6 +1,5 @@
# Default options
DESIGNATE_BACKEND_DRIVER=${DESIGNATE_BACKEND_DRIVER:=bind9}
DESIGNATE_AGENT_BACKEND_DRIVER=${DESIGNATE_AGENT_BACKEND_DRIVER:-"fake"}
DESIGNATE_POOL_ID=${DESIGNATE_POOL_ID:-794ccc2c-d751-44fe-b57f-8894c9f5c842}
DESIGNATE_DEFAULT_NS_RECORD=${DESIGNATE_DEFAULT_NS_RECORD:-ns1.devstack.org.}
DESIGNATE_NOTIFICATION_DRIVER=${DESIGNATE_NOTIFICATION_DRIVER:-messagingv2}
@ -35,7 +34,6 @@ DESIGNATE_SERVICE_PROTOCOL=${DESIGNATE_SERVICE_PROTOCOL:-$SERVICE_PROTOCOL}
DESIGNATE_SERVICE_HOST=${DESIGNATE_SERVICE_HOST:-$SERVICE_HOST}
DESIGNATE_SERVICE_PORT_DNS=${DESIGNATE_SERVICE_PORT_DNS:-53}
DESIGNATE_SERVICE_PORT_MDNS=${DESIGNATE_SERVICE_PORT_MDNS:-5354}
DESIGNATE_SERVICE_PORT_AGENT=${DESIGNATE_SERVICE_PORT_AGENT:-5358}
DESIGNATE_DIR=$DEST/designate
# Default directories
@ -82,5 +80,4 @@ enable_service designate-api
enable_service designate-worker
enable_service designate-producer
enable_service designate-mdns
enable_service designate-agent
enable_service designate-sink

View File

@ -4,8 +4,8 @@ register_db_to_save designate
devstack_localrc base enable_plugin designate https://opendev.org/openstack/designate
devstack_localrc target enable_plugin designate https://opendev.org/openstack/designate
devstack_localrc base enable_service designate-api designate-central designate-producer designate-worker designate-mdns designate-agent designate-sink designate
devstack_localrc target enable_service designate-api designate-central designate-producer designate-worker designate-mdns designate-agent designate-sink designate
devstack_localrc base enable_service designate-api designate-central designate-producer designate-worker designate-mdns designate-sink designate
devstack_localrc target enable_service designate-api designate-central designate-producer designate-worker designate-mdns designate-sink designate
BASE_RUN_SMOKE=False
TARGET_RUN_SMOKE=False

View File

@ -19,12 +19,11 @@ set -o xtrace
stop_process designate-central
stop_process designate-api
stop_process designate-mdns
stop_process designate-agent
stop_process designate-sink
stop_process designate-worker
stop_process designate-producer
# sanity check that service is actually down
ensure_services_stopped designate-api designate-central designate-mdns designate-agent designate-sink designate-worker designate-producer
ensure_services_stopped designate-api designate-central designate-mdns designate-sink designate-worker designate-producer

View File

@ -89,7 +89,6 @@ run_process "designate-api" "$(which uwsgi) --procname-prefix designate-api --in
run_process designate-producer "$DESIGNATE_BIN_DIR/designate-producer --config-file $DESIGNATE_CONF"
run_process designate-worker "$DESIGNATE_BIN_DIR/designate-worker --config-file $DESIGNATE_CONF"
run_process designate-mdns "$DESIGNATE_BIN_DIR/designate-mdns --config-file $DESIGNATE_CONF"
run_process designate-agent "$DESIGNATE_BIN_DIR/designate-agent --config-file $DESIGNATE_CONF"
run_process designate-sink "$DESIGNATE_BIN_DIR/designate-sink --config-file $DESIGNATE_CONF"
restart_apache_server
@ -103,7 +102,7 @@ if ! timeout $SERVICE_TIMEOUT sh -c "while ! wget --no-proxy -q -O- $DESIGNATE_S
fi
# Don't succeed unless the service come up
ensure_services_started designate-api designate-central designate-producer designate-worker designate-mdns designate-agent designate-sink
ensure_services_started designate-api designate-central designate-producer designate-worker designate-mdns designate-sink
set +o xtrace
echo "*********************************************************************"

View File

@ -30,7 +30,6 @@ from sphinx.util import logging
from sphinx.util.osutil import copyfile
from designate.backend.base import Backend
from designate.backend.agent_backend.base import AgentBackend
LOG = logging.getLogger(__name__)
@ -128,8 +127,6 @@ class SupportMatrixDirective(rst.Directive):
except config_parser.NoOptionError:
if cfg.get("backends.%s" % item, "type") == "xfr":
backend = Backend.get_driver(name[0])
elif cfg.get("backends.%s" % item, "type") == "agent":
backend = AgentBackend.get_driver(name[0])
status = backend.__backend_status__
if len(name) == 1:

View File

@ -1,45 +0,0 @@
..
Copyright 2016 Hewlett Packard Enterprise Development Company LP
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.
Agent Backend
=============
This page documents using the various Agent backends, and it's accompanying
service, `designate-agent`. This backend uses an extension of the DNS protocol
itself to send management requests to the remote agent processes, where the
requests will be actioned.
The `rpc` traffic between designate and the `agent` is both unauthenticated and
unencrypted. Do not run this traffic over unsecured networks.
Designate Configuration
-----------------------
For each designate-agent running, add a target to the pools.yaml configuration
file, using the following template:
.. literalinclude:: sample_yaml_snippets/agent.yaml
:language: yaml
Then update the designate pools database using the ``designate-manage pool``
command - see :ref:`designate_manage_pool` for further details on the
``designate-manage pool`` command:
.. code-block:: console
$ designate-manage pool update
.. TODO: Document how to configure the agent service itself, and the available
agent backends.

View File

@ -1,134 +0,0 @@
..
Copyright 2016 Hewlett Packard Enterprise Development Company LP
Author: Federico Ceratto <federico.ceratto@hpe.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.
Djbdns Agent backend
********************
Djbdns User documentation
=========================
This page documents the Agent backend for `djbdns <https://cr.yp.to/djbdns.html>`_.
The agent runs on the same host as the `tinydns <https://cr.yp.to/djbdns/tinydns.html>`_ resolver.
It receives DNS messages from Mini DNS using private DNS OPCODEs
and classes and creates or deletes zones in the data.cdb file using
`axfr-get <https://cr.yp.to/djbdns/axfr-get.html>`_ and
`tinydns-data <https://cr.yp.to/djbdns/tinydns-data.html>`_
Setting up Djbdns on Ubuntu Trusty
------------------------------------
Assuming no DNS resolver is already installed, run as root:
.. code-block:: bash
set -u
datadir=/var/lib/djbdns
ug_name=djbdns
tinydns_ipaddr=127.0.0.1
[[ -d $datadir ]] && echo "$datadir already exists" && exit 1
set -e
apt-get update
apt-get install dbndns daemontools
if ! getent passwd $ug_name >/dev/null; then
adduser --quiet --system --group --no-create-home --home /nonexistent $ug_name
fi
tinydns-conf $ug_name $ug_name $datadir $tinydns_ipaddr
cd $datadir/root
tinydns-data data
chown -Rv $ug_name:$ug_name $datadir
Setup the a Systemd service or, alternatively, an initfile to start TinyDNS.
In the contrib/djbdns directory there are example files for both.
.. code-block:: bash
systemctl daemon-reload
service tinydns start
service tinydns status
If needed, create the rootwrap filters, as root:
.. code-block:: bash
cat > /etc/designate/rootwrap.d/djbdns.filters <<EOF
# cmd-name: filter-name, raw-command, user, args
[Filters]
tcpclient: CommandFilter, /usr/bin/tcpclient, root
axfr-get: CommandFilter, /usr/bin/axfr-get, root
EOF
# Check the filter:
sudo /usr/local/bin/designate-rootwrap /etc/designate/rootwrap.conf tcpclient -h
sudo /usr/local/bin/designate-rootwrap /etc/designate/rootwrap.conf axfr-get -h
Configure the "service.agent" and "backend.agent.djbdns"
sections in /etc/designate/designate.conf
Look in designate.conf.example for examples.
Create an agent pool:
.. code-block:: bash
# Fetch the existing pool(s) if needed or start from scratch
designate-manage pool generate_file --file /tmp/pool.yaml
# Edit the file (see below) and reload it as:
designate-manage pool update --file /tmp/pool.yaml
The "targets" section in pool.yaml should look like:
.. code-block:: ini
targets:
- description: gdnsd agent
masters:
- host: <MiniDNS IP addr>
port: 5354
options: {}
options:
- host: <Agent IP addr>
port: 5358
type: agent
Testing
^^^^^^^
Create new zones and records. Monitor the agent logfile and the contents of the
TinyDNS datadir. The data.cdb file should be receiving updates.
.. code-block:: bash
openstack zone create --email example@example.org example.org.
openstack recordset create example.org. --type A foo --records 1.2.3.4
dig example.org @<tinydns_ipaddr> SOA
dig foo.example.org @<tinydns_ipaddr> A
Developer documentation
=======================
Devstack testbed
----------------
Follow "Setting up Djbdns on Ubuntu Trusty"
Configure Tinydns to do AXFR from MiniDNS on 192.168.121.131

View File

@ -1,120 +0,0 @@
..
Copyright 2016 Hewlett Packard Enterprise Development Company LP
Author: Federico Ceratto <federico.ceratto@hpe.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.
gdnsd Agent backend
*******************
User documentation
==================
This page documents the Agent backend for `gdnsd <http://gdnsd.org/>`_.
The agent runs on the same host as the resolver. It receives DNS messages
from Mini DNS using private DNS OPCODEs and classes and
creates/updates/deletes zones on gdnsd using zone files under
the gdnsd configuration directory.
The backend supports gdnsd from version 2.0
`gdnsd documentation <https://github.com/gdnsd/gdnsd/wiki>`_
Setting up gdnsd on Ubuntu Vivid
--------------------------------
Run as root:
.. code-block:: bash
apt-get update
apt-get install gdnsd
Configuring gdnsd
-----------------
Assuming gdnsd has been freshly installed on the system, run as root:
.. code-block:: bash
# Monitor syslog during the next steps
tail -f /var/log/syslog
# config check should be successful
/usr/sbin/gdnsd checkconf
# Start the daemon if needed
service gdnsd status
service gdnsd start
# gdnsd should be listening on TCP and UDP ports
netstat -lnptu | grep '/gdnsd'
# Test the daemon: it should respond with "gdnsd"
dig @127.0.0.1 CH TXT +short
Configure the "service.agent" and "backend.agent.gdnsd" sections
in /etc/designate/designate.conf
Look in designate.conf.example for more complete examples
.. code-block:: ini
[service:agent]
backend_driver = gdnsd
# Place here the MiniDNS ipaddr and port (not the agent itself)
masters = 192.168.27.100:5354
[backend:agent:gdnsd]
#gdnsd_cmd_name = gdnsd
#confdir_path = /etc/gdnsd
#query_destination = 127.0.0.1
Ensure that the "zones" directory under "confdir_path" (default /etc/gdnsd)
is readable and writable by the system user running the Designate Agent
Create an agent pool:
.. code-block:: bash
# Fetch the existing pool(s) if needed
designate-manage pool generate_file --file /tmp/pool.yaml
# Edit the file (see below) and reload it as:
designate-manage pool update --file /tmp/pool.yaml
The "targets" section in pool.yaml should look like:
.. code-block:: ini
targets:
- description: gdnsd agent
masters:
- host: <MiniDNS IP addr>
port: 5354
options: {}
options:
- host: <Agent IP addr>
port: 5358
type: agent
Start the Designate Agent. You should see log messages similar to:
.. code-block:: bash
2016-05-03 15:13:38.193 INFO designate.backend.agent_backend.impl_gdnsd [-] gdnsd command: 'gdnsd'
2016-05-03 15:13:38.193 INFO designate.backend.agent_backend.impl_gdnsd [-] gdnsd conf directory: '/etc/gdnsd'
2016-05-03 15:13:38.194 INFO designate.backend.agent_backend.impl_gdnsd [-] Resolvers: ['127.0.0.1']

View File

@ -1,189 +0,0 @@
..
Copyright 2016 Hewlett Packard Enterprise Development Company LP
Author: Federico Ceratto <federico.ceratto@hpe.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.
Knot DNS 2 Agent backend
************************
Knot DNS 2 User documentation
=============================
This page documents the Agent backend for `Knot DNS <https://www.knot-dns.cz/>`_.
The agent runs on the same host as the resolver. It receives DNS messages from
Mini DNS using private DNS OPCODEs and classes and creates or deletes zones
on Knot using the knotc tool. It also instructs Knot to request AXFR
from MiniDNS when a zone is created or updated.
Support matrix:
* 2.0 and older: not supported
* 2.2.0: `affected by a bug <https://gitlab.labs.nic.cz/labs/knot/issues/460>`_
`Knot DNS documentation <https://www.knot-dns.cz/documentation/>`_
Configuring Knot DNS
--------------------
Assuming Knot has been freshly installed on the system, run as root:
.. code-block:: bash
# Monitor syslog during the next steps
tail -f /var/log/syslog
# Start the daemon, ensure it's running
service knot start
netstat -npltu | grep knotd
# Create the config database
knotc conf-init
# Edit /etc/default/knot
# Set the variable:
# KNOTD_ARGS="-C /var/lib/knot/confdb"
# Restart
service knot restart
# Check if the deamon is still running from the conf file in /etc/knot/
ps axuw | grep knotd
# if so, apply this workaround for bug
# https://gitlab.labs.nic.cz/labs/knot/issues/455
( cd /etc/default/ && ln -s knot knotd )
service knot restart
ps axuw | grep knotd
# Ensure the confdb is present
test -f /var/lib/knot/confdb/data.mdb && echo OK
# Create the configuration
# Populate the variable with the MiniDNS ipaddr:
MINIDNS_IPADDR=
knotc conf-begin
knotc conf-set server.listen 0.0.0.0@53
# To listen on IPv6 as well, also run this:
# knotc conf-set server.listen '::@53'
knotc conf-set remote[minidns]
knotc conf-set remote[minidns].address $MINIDNS_IPADDR@5354
knotc conf-set template[default]
knotc conf-set template[default].master minidns
knotc conf-set template[default].acl acl_minidns
knotc conf-set template[default].semantic-checks on
knotc conf-set zone[example.com]
knotc conf-set log.any info
knotc conf-set log.target syslog
knotc conf-set acl[acl_minidns]
knotc conf-set acl[acl_minidns].address $MINIDNS_IPADDR
knotc conf-set acl[acl_minidns].action notify
# Review the changes and commit
knotc conf-diff
knotc conf-commit
# Optionally check and back up the conf
knotc conf-check
knotc conf-export knot.conf.bak && cat knot.conf.bak
# Ensure the zone survives a restart
service knot restart
knotc zone-status example.com
# Test Knot: this should return the version
dig @127.0.0.1 version.server CH TXT
If needed, create a rootwrap filter, as root:
.. code-block:: bash
cat > /etc/designate/rootwrap.d/knot2.filters <<EOF
# cmd-name: filter-name, raw-command, user, args
[Filters]
knotc: CommandFilter, /usr/sbin/knotc, root
EOF
# Check the filter:
sudo /usr/local/bin/designate-rootwrap /etc/designate/rootwrap.conf knotc status
Configure the "service.agent" and "backend.agent.knot2" sections
in /etc/designate/designate.conf
Look in designate.conf.example for examples
Create an agent pool:
.. code-block:: bash
# Fetch the existing pool(s) if needed or start from scratch
designate-manage pool generate_file --file /tmp/pool.yaml
# Edit the file (see below) and reload it as:
designate-manage pool update --file /tmp/pool.yaml
The "targets" section in pool.yaml should look like:
.. code-block:: ini
targets:
- description: knot2 agent
masters:
- host: <MiniDNS IP addr>
port: 5354
options: {}
options:
- host: <Agent IP addr>
port: 5358
type: agent
Developer documentation
=======================
Devstack testbed
----------------
Follow "Setting up Knot DNS on Ubuntu Trusty"
Configure Knot to slave from MiniDNS on 192.168.121.131
Knotd configuration example (sudo knotc conf-export <filename>):
.. code-block:: yaml
# Configuration export (Knot DNS 2.1.1)
server:
listen: "0.0.0.0@53"
log:
- target: "syslog"
any: "debug"
acl:
- id: "acl_minidns"
address: [ "192.168.121.131" ]
action: [ "notify" ]
remote:
- id: "minidns"
address: "192.168.121.131@5354"
template:
- id: "default"
master: "minidns"
acl: "acl_minidns"
semantic-checks: "on"

View File

@ -1,137 +0,0 @@
..
Copyright 2016 Cloudbase Solutions Srl
Author: Alin Balutoiu <abalutoiu@cloudbasesolutions.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.
MSDNS Agent Backend
*******************
MSDNS User Documentation
========================
This page documents using the MSDNS Agent backend.
The agent runs on the Windows host where the Microsoft DNS Server feature
is installed. It receives DNS messages from Mini DNS using private
DNS OPCODEs and classes and creates or deletes zones using WMI calls.
It also instructs MSDNS to request AXFR from MiniDNS when a zone is created
or updated.
`Microsoft DNS documentation for managing DNS zones
<https://msdn.microsoft.com/en-us/library/windows/desktop/ms682757.aspx>`_
Setting up the Microsoft DNS server on Windows Server
-----------------------------------------------------
The DNS Server role can be installed on the system by following the
documentation available here:
`How to install the DNS Server role
<https://technet.microsoft.com/en-us/library/cc725925.aspx>`_
Configuring MSDNS
-----------------
Assuming the DNS Server role has been installed on the system, follow the
next steps to complete the configuration.
These steps are for the Windows host which will run the designate agent.
Make sure that Python 2.7 or Python 3.4 is installed on the system already.
To install Designate, clone the repository from https://github.com/openstack/designate
and do a pip install. Example:
.. code-block:: console
git clone https://github.com/openstack/designate
pip install .\\designate
After that, we need to configure the Designate Agent.
Inside the github repository, there is a folder named "etc/designate"
which can be used as default configuration.
Copy the folder somewhere else, for this example we will copy it to
C:\\etc\\designate
Inside the configuration folder, make a copy of designate.conf.sample
and rename the copy to designate.conf
Example:
.. code-block:: console
copy C:\\etc\\designate\\designate.conf.sample C:\\etc\\designate\\designate.conf
Configure the "service.agent" and "backend.agent.msdns" sections in
C:\\etc\\designate\\designate.conf
Look in C:\\etc\\designate\\designate.conf.example for more complete examples.
.. code-block:: ini
[service:agent]
backend_driver = msdns
# Place here the MiniDNS ipaddr and port (no the agent itself)
masters = <MiniDNS IP addr>:53
Ensure that "policy_file" under the [default] section is set:
.. code-block:: ini
policy_file = C:\\etc\\designate\\policy.yaml
Start the designate agent using
(Python 2.7 was installed in the default location C:\\Python27):
.. code-block:: console
C:\\Python27\\Scripts\\designate-agent.exe --config-file 'C:\\etc\\designate\\designate.conf'
You should see log messages similar to:
.. code-block:: console
2016-06-22 02:00:47.177 3436 INFO designate.backend.agent_backend.impl_msdns [-] Started msdns backend
2016-06-22 02:00:47.177 3436 INFO designate.service [-] _handle_tcp thread started
2016-06-22 02:00:47.177 3436 INFO designate.service [-] _handle_udp thread started
The following steps are for the system running the Designate controller.
Make sure to set the mDNS port to 53 in the ``[service:mdns]`` section.
MS DNS does not support Masters that are on any port other than 53.
Create an agent pool:
.. code-block:: bash
# Fetch the existing pool(s) if needed or start from scratch
designate-manage pool generate_file --file /tmp/pool.yaml
# Edit the file (see below) and reload it as:
designate-manage pool update --file /tmp/pool.yaml
The "targets" section in pool.yaml should look like:
.. code-block:: ini
targets:
- description: Microsoft DNS agent
masters:
- host: <MiniDNS IP addr>
port: 53
options: {}
options:
- host: <Agent IP addr>
port: 5358
type: agent

View File

@ -1,15 +0,0 @@
targets:
- type: agent
description: Agent Server 1
# List out the designate-mdns servers from which Agent servers should
# request zone transfers (AXFRs) from.
masters:
- host: 192.0.2.1
port: 5354
# Agent Configuration options, this should be this targets
# designate-agent service's host and port.
options:
host: 192.0.2.2
port: 5358

View File

@ -72,10 +72,6 @@ DynDNS or even another Designate instance. In this situation, you will
typically have a single target with a set of nameservers to test that
meet your requirements.
Yet another example is when using a Designate agent. In this scenario
your agent instances are the targets and the nameservers the agent
updates would be checked for the correct information.
Managing Pools
==============

View File

@ -53,14 +53,6 @@ backend-impl-akamai_v2=Akamai DNS v2
backend-impl-infoblox-xfr=Infoblox (XFR)
backend-impl-nsd4=NSD4
backend-impl-ns1=NS1 DNS
backend-impl-agent=Agent
backend-impl-bind9-agent=Bind9 (Agent)
backend-impl-denominator=Denominator
backend-impl-knot2-agent=Knot2 (Agent)
backend-impl-djbdns-agent=Djbdns (Agent)
backend-impl-gdnsd-agent=Gdnsd (Agent)
backend-impl-msdns-agent=Microsoft DNS (Agent)
[backends.backend-impl-bind9]
docs=bind9_backend_docs
@ -87,36 +79,12 @@ docs=ns1_backend_docs
status=untested
config=backends/sample_yaml_snippets/ns1.yaml
[backends.backend-impl-agent]
[backends.backend-impl-bind9-agent]
type=agent
[backends.backend-impl-knot2-agent]
type=agent
status=experimental
[backends.backend-impl-djbdns-agent]
type=agent
status=experimental
[backends.backend-impl-gdnsd-agent]
type=agent
status=experimental
[backends.backend-impl-infoblox-xfr]
status=untested
maintainers=Infoblox OpenStack Team <openstack-maintainer@infoblox.com>
[backends.backend-impl-nsd4]
[backends.backend-impl-denominator]
type=agent
[backends.backend-impl-msdns-agent]
type=agent
status=untested
[grades]
valid-grades=integrated,master-compatible,release-compatible,untested,failing,known-broken,experimental,deprecated,end-of-life

View File

@ -45,9 +45,6 @@ The default values are:
| Component | Protocol | Port |
| (header rows optional) | | numbers |
+========================+============+==========+
| Agent | TCP | 5358 |
+ +------------+----------+
| | UDP | 5358 |
+------------------------+------------+----------+
| API | TCP | 9001 |
+------------------------+------------+----------+

View File

@ -223,14 +223,6 @@ GMR Example
==== Configuration ====
========================================================================
backend:agent:bind9:
query-destination = 127.0.0.1
rndc-config-file = None
rndc-host = 127.0.0.1
rndc-key-file = None
rndc-port = 953
zone-file-path = /opt/stack/data/designate/zones
backend:bind9:
masters =
127.0.0.1:5354

View File

@ -67,43 +67,3 @@ Backend PowerDNS 4
:members:
:undoc-members:
:show-inheritance:
Agent Backend KnotDNS
=====================
.. automodule:: designate.backend.agent_backend.impl_knot2
:members:
:special-members:
:private-members:
:undoc-members:
:show-inheritance:
Agent Backend gdnsd
===================
.. automodule:: designate.backend.agent_backend.impl_gdnsd
:members:
:special-members:
:private-members:
:undoc-members:
:show-inheritance:
Agent Backend Djbdns
====================
.. automodule:: designate.backend.agent_backend.impl_djbdns
:members:
:special-members:
:private-members:
:undoc-members:
:show-inheritance:
Agent Backend MSDNS
====================
.. automodule:: designate.backend.agent_backend.impl_msdns
:members:
:special-members:
:private-members:
:undoc-members:
:show-inheritance:

View File

@ -30,14 +30,6 @@ The DNS service consists of the following components:
the customer facing DNS Servers. Can also pull in DNS information about
DNS Zones hosted outside of the Designate infrastructure
``designate-agent`` component
A small python daemon that can be used for a limited sub set of DNS Servers
Some DNS Servers requrire commands be run locally, and to do this we use
this component.
.. note:: The majority of the DNS service installs will not need this
component.
``Customer Facing DNS Servers``
Serves DNS requests to end users. They are orchestreated by the
``designate-worker``, and the supported list is maintained

View File

@ -24,7 +24,6 @@ Verify operation of the DNS service.
../usr/bin/python /usr/bin/designate-mdns --config-file /etc/designate/designate.conf
../usr/bin/python /usr/bin/designate-central --config-file /etc/designate/designate.conf
../usr/bin/python /usr/bin/designate-agent --config-file /etc/designate/designate.conf
../usr/bin/python /usr/bin/designate-api --config-file /etc/designate/designate.conf
../usr/bin/python /usr/bin/designate-worker --config-file /etc/designate/designate.conf
../usr/bin/python /usr/bin/designate-producer --config-file /etc/designate/designate.conf

View File

@ -0,0 +1,6 @@
---
prelude: >
Designate-agent removal is complete in this version of designate.
upgrade:
- |
The agent and all agent backends has been removed and can no longer be used.

View File

@ -36,7 +36,6 @@ data_files =
oslo.config.opts =
designate.conf = designate.conf.opts:list_opts
oslo.config.opts.defaults =
designate.conf = designate.common.config:set_defaults
@ -53,12 +52,10 @@ console_scripts =
designate-manage = designate.cmd.manage:main
designate-mdns = designate.cmd.mdns:main
designate-sink = designate.cmd.sink:main
designate-agent = designate.cmd.agent:main
designate-worker = designate.cmd.worker:main
designate-producer = designate.cmd.producer:main
designate-status = designate.cmd.status:main
designate.api.admin.extensions =
reports = designate.api.admin.controllers.extensions.reports:ReportsController
quotas = designate.api.admin.controllers.extensions.quotas:QuotasController
@ -78,18 +75,8 @@ designate.backend =
nsd4 = designate.backend.impl_nsd4:NSD4Backend
infoblox = designate.backend.impl_infoblox:InfobloxBackend
fake = designate.backend.impl_fake:FakeBackend
agent = designate.backend.agent:AgentPoolBackend
ns1 = designate.backend.impl_ns1:NS1Backend
designate.backend.agent_backend =
bind9 = designate.backend.agent_backend.impl_bind9:Bind9Backend
knot2 = designate.backend.agent_backend.impl_knot2:Knot2Backend
djbdns = designate.backend.agent_backend.impl_djbdns:DjbdnsBackend
denominator = designate.backend.agent_backend.impl_denominator:DenominatorBackend
fake = designate.backend.agent_backend.impl_fake:FakeBackend
gdnsd = designate.backend.agent_backend.impl_gdnsd:GdnsdBackend
msdns = designate.backend.agent_backend.impl_msdns:MSDNSBackend
designate.network_api =
fake = designate.network_api.fake:FakeNetworkAPI
neutron = designate.network_api.neutron:NeutronNetworkAPI