diff --git a/contrib/djbdns/tinydns.init b/contrib/djbdns/tinydns.init deleted file mode 100755 index 7bcbb4773..000000000 --- a/contrib/djbdns/tinydns.init +++ /dev/null @@ -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 diff --git a/contrib/djbdns/tinydns.service b/contrib/djbdns/tinydns.service deleted file mode 100644 index 2fcf9d2a6..000000000 --- a/contrib/djbdns/tinydns.service +++ /dev/null @@ -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 diff --git a/designate/agent/__init__.py b/designate/agent/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/designate/agent/handler.py b/designate/agent/handler.py deleted file mode 100644 index 638831c84..000000000 --- a/designate/agent/handler.py +++ /dev/null @@ -1,258 +0,0 @@ -# Copyright 2014 Rackspace Inc. -# -# Author: Tim Simmons -# -# 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 diff --git a/designate/agent/service.py b/designate/agent/service.py deleted file mode 100644 index ccd63d1ba..000000000 --- a/designate/agent/service.py +++ /dev/null @@ -1,86 +0,0 @@ -# Copyright 2014 Rackspace Inc. -# -# Author: Tim Simmons -# -# 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 diff --git a/designate/backend/agent.py b/designate/backend/agent.py deleted file mode 100644 index d4675ec4b..000000000 --- a/designate/backend/agent.py +++ /dev/null @@ -1,173 +0,0 @@ -# Copyright 2014 Rackspace Inc. -# -# Author: Tim Simmons -# -# 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 diff --git a/designate/backend/agent_backend/__init__.py b/designate/backend/agent_backend/__init__.py deleted file mode 100644 index fd27b83d9..000000000 --- a/designate/backend/agent_backend/__init__.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright 2014 Rackspace Inc. -# -# Author: Tim Simmons -# -# 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) diff --git a/designate/backend/agent_backend/base.py b/designate/backend/agent_backend/base.py deleted file mode 100644 index 19d2dd509..000000000 --- a/designate/backend/agent_backend/base.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright 2014 Rackspace Inc. -# -# Author: Tim Simmons -# -# 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""" diff --git a/designate/backend/agent_backend/impl_bind9.py b/designate/backend/agent_backend/impl_bind9.py deleted file mode 100644 index f752ca07c..000000000 --- a/designate/backend/agent_backend/impl_bind9.py +++ /dev/null @@ -1,137 +0,0 @@ -# Copyright 2014 Rackspace Inc. -# -# Author: Tim Simmons -# -# 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) diff --git a/designate/backend/agent_backend/impl_denominator.py b/designate/backend/agent_backend/impl_denominator.py deleted file mode 100644 index 8b06036ea..000000000 --- a/designate/backend/agent_backend/impl_denominator.py +++ /dev/null @@ -1,242 +0,0 @@ -# Copyright 2015 Dyn Inc. -# -# Author: Yasha Bubnov -# -# 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 diff --git a/designate/backend/agent_backend/impl_djbdns.py b/designate/backend/agent_backend/impl_djbdns.py deleted file mode 100755 index c43b68bb5..000000000 --- a/designate/backend/agent_backend/impl_djbdns.py +++ /dev/null @@ -1,333 +0,0 @@ -# Copyright 2016 Hewlett Packard Enterprise Development Company LP -# -# Author: Federico Ceratto -# -# 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() diff --git a/designate/backend/agent_backend/impl_fake.py b/designate/backend/agent_backend/impl_fake.py deleted file mode 100644 index 7eaee6860..000000000 --- a/designate/backend/agent_backend/impl_fake.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright 2014 Rackspace Inc. -# -# Author: Tim Simmons -# -# 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) diff --git a/designate/backend/agent_backend/impl_gdnsd.py b/designate/backend/agent_backend/impl_gdnsd.py deleted file mode 100644 index 8a4215c2a..000000000 --- a/designate/backend/agent_backend/impl_gdnsd.py +++ /dev/null @@ -1,240 +0,0 @@ -# Copyright 2016 Hewlett Packard Enterprise Development Company LP -# -# Author: Federico Ceratto -# -# 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 diff --git a/designate/backend/agent_backend/impl_knot2.py b/designate/backend/agent_backend/impl_knot2.py deleted file mode 100755 index 9bbf0d44b..000000000 --- a/designate/backend/agent_backend/impl_knot2.py +++ /dev/null @@ -1,215 +0,0 @@ -# Copyright 2016 Hewlett Packard Enterprise Development Company LP -# -# Author: Federico Ceratto -# -# 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') diff --git a/designate/backend/agent_backend/impl_msdns.py b/designate/backend/agent_backend/impl_msdns.py deleted file mode 100644 index ae397931e..000000000 --- a/designate/backend/agent_backend/impl_msdns.py +++ /dev/null @@ -1,111 +0,0 @@ -# Copyright 2016 Cloudbase Solutions Srl -# All Rights Reserved. -# -# Author: Alin Balutoiu -# -# 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) diff --git a/designate/backend/private_codes.py b/designate/backend/private_codes.py deleted file mode 100644 index 8f88acb10..000000000 --- a/designate/backend/private_codes.py +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright 2016 Hewlett Packard Enterprise Development Company LP -# -# Author: Federico Ceratto -# -# 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 diff --git a/designate/cmd/agent.py b/designate/cmd/agent.py deleted file mode 100644 index a24001290..000000000 --- a/designate/cmd/agent.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright 2014 Rackspace Inc. -# -# Author: Tim Simmons -# -# 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() diff --git a/designate/conf/__init__.py b/designate/conf/__init__.py index c7acd7f55..570266b80 100644 --- a/designate/conf/__init__.py +++ b/designate/conf/__init__.py @@ -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) diff --git a/designate/conf/agent.py b/designate/conf/agent.py deleted file mode 100644 index faa846ff0..000000000 --- a/designate/conf/agent.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright 2014 Rackspace Inc. -# -# Author: Tim Simmons -# -# 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 - } diff --git a/designate/conf/bind9.py b/designate/conf/bind9.py deleted file mode 100644 index 6d6b0f2a2..000000000 --- a/designate/conf/bind9.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright 2014 Rackspace Inc. -# -# Author: Tim Simmons -# -# 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, - } diff --git a/designate/conf/denominator.py b/designate/conf/denominator.py deleted file mode 100644 index d7a8c66c2..000000000 --- a/designate/conf/denominator.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright 2015 Dyn Inc. -# -# Author: Yasha Bubnov -# -# 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, - } diff --git a/designate/conf/djbdns.py b/designate/conf/djbdns.py deleted file mode 100644 index f00c479c2..000000000 --- a/designate/conf/djbdns.py +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright 2016 Hewlett Packard Enterprise Development Company LP -# -# Author: Federico Ceratto -# -# 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, - } diff --git a/designate/conf/gdnsd.py b/designate/conf/gdnsd.py deleted file mode 100644 index 31615abf1..000000000 --- a/designate/conf/gdnsd.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright 2016 Hewlett Packard Enterprise Development Company LP -# -# Author: Federico Ceratto -# -# 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, - } diff --git a/designate/conf/knot2.py b/designate/conf/knot2.py deleted file mode 100644 index 739b3c5d7..000000000 --- a/designate/conf/knot2.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright 2016 Hewlett Packard Enterprise Development Company LP -# -# Author: Federico Ceratto -# -# 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, - } diff --git a/designate/conf/msdns.py b/designate/conf/msdns.py deleted file mode 100644 index 4d8002c87..000000000 --- a/designate/conf/msdns.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright 2016 Cloudbase Solutions Srl -# All Rights Reserved. -# -# Author: Alin Balutoiu -# -# 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, - } diff --git a/designate/dnsmiddleware.py b/designate/dnsmiddleware.py index ec7e101f4..a4666b020 100644 --- a/designate/dnsmiddleware.py +++ b/designate/dnsmiddleware.py @@ -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,) diff --git a/designate/tests/test_dnsutils.py b/designate/tests/test_dnsutils.py index df1d848b9..46b6b7fef 100644 --- a/designate/tests/test_dnsutils.py +++ b/designate/tests/test_dnsutils.py @@ -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,)) diff --git a/designate/tests/unit/agent/__init__.py b/designate/tests/unit/agent/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/designate/tests/unit/agent/backends/__init__.py b/designate/tests/unit/agent/backends/__init__.py deleted file mode 100644 index 499fc938d..000000000 --- a/designate/tests/unit/agent/backends/__init__.py +++ /dev/null @@ -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 - ) diff --git a/designate/tests/unit/agent/backends/test_bind9.py b/designate/tests/unit/agent/backends/test_bind9.py deleted file mode 100644 index 40f0ea9ee..000000000 --- a/designate/tests/unit/agent/backends/test_bind9.py +++ /dev/null @@ -1,131 +0,0 @@ -# Copyright 2014 Rackspace Inc. -# -# Author: Tim Simmons -# -# 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"; };' - ) diff --git a/designate/tests/unit/agent/backends/test_denominator.py b/designate/tests/unit/agent/backends/test_denominator.py deleted file mode 100644 index f74e00f69..000000000 --- a/designate/tests/unit/agent/backends/test_denominator.py +++ /dev/null @@ -1,168 +0,0 @@ -# Copyright 2015 Dyn Inc. -# -# Author: Yasha Bubnov -# -# 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'], {} - ) diff --git a/designate/tests/unit/agent/backends/test_djbdns.py b/designate/tests/unit/agent/backends/test_djbdns.py deleted file mode 100644 index 518f71e71..000000000 --- a/designate/tests/unit/agent/backends/test_djbdns.py +++ /dev/null @@ -1,125 +0,0 @@ -# Copyright 2016 Hewlett Packard Enterprise Development Company LP -# -# Author: Federico Ceratto -# -# 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' - ) diff --git a/designate/tests/unit/agent/backends/test_fake.py b/designate/tests/unit/agent/backends/test_fake.py deleted file mode 100644 index 7cbe15a3d..000000000 --- a/designate/tests/unit/agent/backends/test_fake.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright 2014 Rackspace Inc. -# -# Author: Tim Simmons -# -# 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.') diff --git a/designate/tests/unit/agent/backends/test_gdnsd.py b/designate/tests/unit/agent/backends/test_gdnsd.py deleted file mode 100644 index d790bfefa..000000000 --- a/designate/tests/unit/agent/backends/test_gdnsd.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright 2016 Hewlett Packard Enterprise Development Company LP -# -# Author: Federico Ceratto -# -# 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' - ) diff --git a/designate/tests/unit/agent/backends/test_knot2.py b/designate/tests/unit/agent/backends/test_knot2.py deleted file mode 100644 index b8bbb7a53..000000000 --- a/designate/tests/unit/agent/backends/test_knot2.py +++ /dev/null @@ -1,226 +0,0 @@ -# Copyright 2016 Hewlett Packard Enterprise Development Company LP -# -# Author: Federico Ceratto -# -# 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') diff --git a/designate/tests/unit/agent/backends/test_msdns.py b/designate/tests/unit/agent/backends/test_msdns.py deleted file mode 100644 index 36f0fde55..000000000 --- a/designate/tests/unit/agent/backends/test_msdns.py +++ /dev/null @@ -1,141 +0,0 @@ -# Copyright 2016 Cloudbase Solutions Srl -# All Rights Reserved. -# -# Author: Alin Balutoiu -# -# 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 - ) diff --git a/designate/tests/unit/agent/test_handler.py b/designate/tests/unit/agent/test_handler.py deleted file mode 100644 index 4e552f850..000000000 --- a/designate/tests/unit/agent/test_handler.py +++ /dev/null @@ -1,209 +0,0 @@ -# Copyright 2014 Rackspace Inc. -# -# Author: Tim Simmons -# -# 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)) diff --git a/designate/tests/unit/agent/test_service.py b/designate/tests/unit/agent/test_service.py deleted file mode 100644 index 1f65de88c..000000000 --- a/designate/tests/unit/agent/test_service.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright 2014 Rackspace Inc. -# -# Author: Tim Simmons -# -# 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 - ) diff --git a/designate/tests/unit/backend/test_agent.py b/designate/tests/unit/backend/test_agent.py deleted file mode 100644 index 031bd5056..000000000 --- a/designate/tests/unit/backend/test_agent.py +++ /dev/null @@ -1,185 +0,0 @@ -# Author: Federico Ceratto -# -# 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) - ) diff --git a/designate/tests/unit/cmd/test_cmd.py b/designate/tests/unit/cmd/test_cmd.py index 88a2a17ea..b6e4beb19 100644 --- a/designate/tests/unit/cmd/test_cmd.py +++ b/designate/tests/unit/cmd/test_cmd.py @@ -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): diff --git a/devstack/designate_plugins/backend-agent b/devstack/designate_plugins/backend-agent deleted file mode 100644 index dd883d50e..000000000 --- a/devstack/designate_plugins/backend-agent +++ /dev/null @@ -1,103 +0,0 @@ -# Configure the agent backend - -# Enable with: -# DESIGNATE_BACKEND_DRIVER=agent -# DESIGNATE_AGENT_BACKEND_DRIVER= - -# 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 < /dev/null < - - 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 `_. - -The agent runs on the same host as the `tinydns `_ 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 `_ and -`tinydns-data `_ - -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 < - port: 5354 - options: {} - options: - - host: - 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 @ SOA - dig foo.example.org @ A - -Developer documentation -======================= - -Devstack testbed ----------------- - -Follow "Setting up Djbdns on Ubuntu Trusty" - -Configure Tinydns to do AXFR from MiniDNS on 192.168.121.131 diff --git a/doc/source/admin/backends/gdnsd_agent.rst b/doc/source/admin/backends/gdnsd_agent.rst deleted file mode 100644 index 9d9c2fe2d..000000000 --- a/doc/source/admin/backends/gdnsd_agent.rst +++ /dev/null @@ -1,120 +0,0 @@ -.. - Copyright 2016 Hewlett Packard Enterprise Development Company LP - - Author: Federico Ceratto - - 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 `_. - -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 `_ - -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: - port: 5354 - options: {} - options: - - host: - 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'] - diff --git a/doc/source/admin/backends/knot2_agent.rst b/doc/source/admin/backends/knot2_agent.rst deleted file mode 100644 index 7390f9c52..000000000 --- a/doc/source/admin/backends/knot2_agent.rst +++ /dev/null @@ -1,189 +0,0 @@ -.. - Copyright 2016 Hewlett Packard Enterprise Development Company LP - - Author: Federico Ceratto - - 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 `_. - -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 `_ - - -`Knot DNS 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 < - port: 5354 - options: {} - options: - - host: - 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 ): - -.. 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" diff --git a/doc/source/admin/backends/msdns_agent.rst b/doc/source/admin/backends/msdns_agent.rst deleted file mode 100644 index a0bfdd7f5..000000000 --- a/doc/source/admin/backends/msdns_agent.rst +++ /dev/null @@ -1,137 +0,0 @@ -.. - Copyright 2016 Cloudbase Solutions Srl - - Author: Alin Balutoiu - - 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 -`_ - -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 -`_ - -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 = :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: - port: 53 - options: {} - options: - - host: - port: 5358 - type: agent diff --git a/doc/source/admin/backends/sample_yaml_snippets/agent.yaml b/doc/source/admin/backends/sample_yaml_snippets/agent.yaml deleted file mode 100644 index 35709824c..000000000 --- a/doc/source/admin/backends/sample_yaml_snippets/agent.yaml +++ /dev/null @@ -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 diff --git a/doc/source/admin/pools.rst b/doc/source/admin/pools.rst index 1cb849ac6..c9af58cb6 100644 --- a/doc/source/admin/pools.rst +++ b/doc/source/admin/pools.rst @@ -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 ============== diff --git a/doc/source/admin/support-matrix.ini b/doc/source/admin/support-matrix.ini index 7e72d0e19..48290b08d 100644 --- a/doc/source/admin/support-matrix.ini +++ b/doc/source/admin/support-matrix.ini @@ -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 [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 diff --git a/doc/source/admin/troubleshooting.rst b/doc/source/admin/troubleshooting.rst index a91b849f0..5721b2be5 100644 --- a/doc/source/admin/troubleshooting.rst +++ b/doc/source/admin/troubleshooting.rst @@ -45,9 +45,6 @@ The default values are: | Component | Protocol | Port | | (header rows optional) | | numbers | +========================+============+==========+ -| Agent | TCP | 5358 | -+ +------------+----------+ -| | UDP | 5358 | +------------------------+------------+----------+ | API | TCP | 9001 | +------------------------+------------+----------+ diff --git a/doc/source/contributor/gmr.rst b/doc/source/contributor/gmr.rst index 9f95faab2..471db29eb 100644 --- a/doc/source/contributor/gmr.rst +++ b/doc/source/contributor/gmr.rst @@ -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 diff --git a/doc/source/contributor/sourcedoc/backend.rst b/doc/source/contributor/sourcedoc/backend.rst index 51b98cafd..15bec0156 100644 --- a/doc/source/contributor/sourcedoc/backend.rst +++ b/doc/source/contributor/sourcedoc/backend.rst @@ -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: diff --git a/doc/source/install/get_started.rst b/doc/source/install/get_started.rst index 9e3c0c1c3..9715b3171 100644 --- a/doc/source/install/get_started.rst +++ b/doc/source/install/get_started.rst @@ -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 diff --git a/doc/source/install/verify.rst b/doc/source/install/verify.rst index d612f4c2f..9aa328ec9 100644 --- a/doc/source/install/verify.rst +++ b/doc/source/install/verify.rst @@ -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 diff --git a/releasenotes/notes/remove-deprecated-agent-1ef95b8608e2a41b.yaml b/releasenotes/notes/remove-deprecated-agent-1ef95b8608e2a41b.yaml new file mode 100644 index 000000000..bd8827403 --- /dev/null +++ b/releasenotes/notes/remove-deprecated-agent-1ef95b8608e2a41b.yaml @@ -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. diff --git a/setup.cfg b/setup.cfg index 7fd36eaf2..b73d120c7 100644 --- a/setup.cfg +++ b/setup.cfg @@ -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