Add Knot DNS backend
Change-Id: I885c003afc2496ba4aa9631568be02e7d4654e52
This commit is contained in:
parent
e23eb9a030
commit
5d328f07aa
@ -47,7 +47,7 @@ OPTS = [
|
||||
cfg.ListOpt('masters', default=[],
|
||||
help='List of masters for the Agent, format ip:port'),
|
||||
cfg.StrOpt('backend-driver', default='bind9',
|
||||
help='The backend driver to use'),
|
||||
help='The backend driver to use: bind9 or knot2'),
|
||||
cfg.StrOpt('transfer-source',
|
||||
help='An IP address to be used to fetch zones transferred in'),
|
||||
cfg.FloatOpt('notify-delay', default=0.0,
|
||||
|
216
designate/backend/agent_backend/impl_knot2.py
Normal file
216
designate/backend/agent_backend/impl_knot2.py
Normal file
@ -0,0 +1,216 @@
|
||||
# Copyright 2016 Hewlett Packard Enterprise Development Company LP
|
||||
#
|
||||
# Author: Federico Ceratto <federico.ceratto@hpe.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
backend.agent_backend.impl_knot2
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Knot DNS agent backend
|
||||
|
||||
Create, update, delete zones locally on a Knot DNS resolver using the
|
||||
knotc utility.
|
||||
|
||||
Supported Knot versions: >= 2.1, < 3
|
||||
|
||||
`User documentation <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]
|
||||
"""
|
||||
|
||||
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 import exceptions
|
||||
from designate.backend.agent_backend import base
|
||||
from designate.i18n import _LI
|
||||
from designate.i18n import _LE
|
||||
from designate.utils import execute
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CFG_GROUP = 'backend:agent:knot2'
|
||||
# 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'
|
||||
|
||||
@classmethod
|
||||
def get_cfg_opts(cls):
|
||||
group = cfg.OptGroup(
|
||||
name='backend:agent:knot2', title="Configuration for Knot2 backend"
|
||||
)
|
||||
opts = [
|
||||
cfg.StrOpt('knotc-cmd-name',
|
||||
help='knotc executable path or rootwrap command name',
|
||||
default=KNOTC_DEFAULT_PATH),
|
||||
cfg.StrOpt('query-destination', default='127.0.0.1',
|
||||
help='Host to query when finding zones')
|
||||
]
|
||||
return [(group, opts)]
|
||||
|
||||
def __init__(self, *a, **kw):
|
||||
"""Configure the backend"""
|
||||
super(Knot2Backend, self).__init__(*a, **kw)
|
||||
|
||||
self._knotc_cmd_name = cfg.CONF[CFG_GROUP].knotc_cmd_name
|
||||
|
||||
def start(self):
|
||||
"""Start the backend"""
|
||||
LOG.info(_LI("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(_LI("Ignoring error: %r"), out)
|
||||
else:
|
||||
raise ProcessExecutionError(stdout=out, stderr=err)
|
||||
|
||||
except ProcessExecutionError as e:
|
||||
LOG.error(_LE("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(_LI("Zone change aborted: %r"), e)
|
||||
raise e
|
||||
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(_LE("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 as e:
|
||||
LOG.error(_LE("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().rstrip('.')
|
||||
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()
|
||||
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')
|
@ -329,7 +329,9 @@ def dnspythonrecord_to_recordset(rname, rdataset):
|
||||
|
||||
def do_axfr(zone_name, servers, timeout=None, source=None):
|
||||
"""
|
||||
Performs an AXFR for a given zone name
|
||||
Requests an AXFR for a given zone name and process the response
|
||||
|
||||
:returns: Zone instance from dnspython
|
||||
"""
|
||||
random.shuffle(servers)
|
||||
timeout = timeout or cfg.CONF["service:mdns"].xfr_timeout
|
||||
|
0
designate/tests/unit/test_agent/__init__.py
Normal file
0
designate/tests/unit/test_agent/__init__.py
Normal file
200
designate/tests/unit/test_agent/test_backends/test_knot2.py
Normal file
200
designate/tests/unit/test_agent/test_backends/test_knot2.py
Normal file
@ -0,0 +1,200 @@
|
||||
# Copyright 2016 Hewlett Packard Enterprise Development Company LP
|
||||
#
|
||||
# Author: Federico Ceratto <federico.ceratto@hpe.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Unit-test the Knot 2 agent backend
|
||||
knotc is not being executed
|
||||
"""
|
||||
|
||||
from mock import call
|
||||
from oslo_concurrency.processutils import ProcessExecutionError
|
||||
import dns.zone
|
||||
import fixtures
|
||||
import mock
|
||||
|
||||
from designate import exceptions
|
||||
from designate.backend.agent_backend.impl_knot2 import Knot2Backend
|
||||
from designate.tests import TestCase
|
||||
import designate.backend.agent_backend.impl_knot2 # noqa
|
||||
|
||||
|
||||
class Knot2AgentBackendBasicUnitTestCase(TestCase):
|
||||
|
||||
def test_init(self):
|
||||
kb = Knot2Backend('foo')
|
||||
self.assertEqual('knotc', kb._knotc_cmd_name)
|
||||
|
||||
@mock.patch('designate.backend.agent_backend.impl_knot2.execute')
|
||||
def test__execute_knotc_ok(self, mock_exe):
|
||||
mock_exe.return_value = ('OK', '')
|
||||
kb = Knot2Backend('foo')
|
||||
kb._execute_knotc('a1', 'a2')
|
||||
mock_exe.assert_called_with('knotc', 'a1', 'a2')
|
||||
self.assertEqual(1, mock_exe.call_count)
|
||||
|
||||
@mock.patch('designate.backend.agent_backend.impl_knot2.execute')
|
||||
def test__execute_knotc_expected_error(self, mock_exe):
|
||||
mock_exe.return_value = ('xyz', '')
|
||||
kb = Knot2Backend('foo')
|
||||
kb._execute_knotc('a1', 'a2', expected_error='xyz')
|
||||
mock_exe.assert_called_once_with('knotc', 'a1', 'a2')
|
||||
|
||||
@mock.patch('designate.backend.agent_backend.impl_knot2.execute')
|
||||
def test__execute_knotc_expected_output(self, mock_exe):
|
||||
mock_exe.return_value = ('xyz', '')
|
||||
kb = Knot2Backend('foo')
|
||||
kb._execute_knotc('a1', 'a2', expected_output='xyz')
|
||||
mock_exe.assert_called_once_with('knotc', 'a1', 'a2')
|
||||
|
||||
@mock.patch('designate.backend.agent_backend.impl_knot2.execute')
|
||||
def test__execute_knotc_with_error(self, mock_exe):
|
||||
mock_exe.return_value = ('xyz', '')
|
||||
kb = Knot2Backend('foo')
|
||||
self.assertRaises(
|
||||
exceptions.Backend,
|
||||
kb._execute_knotc, 'a1', 'a2')
|
||||
mock_exe.assert_called_once_with('knotc', 'a1', 'a2')
|
||||
|
||||
@mock.patch('designate.backend.agent_backend.impl_knot2.execute')
|
||||
def test__execute_knotc_raising_exception(self, mock_exe):
|
||||
mock_exe.side_effect = ProcessExecutionError
|
||||
kb = Knot2Backend('foo')
|
||||
self.assertRaises(
|
||||
exceptions.Backend,
|
||||
kb._execute_knotc, 'a1', 'a2')
|
||||
mock_exe.assert_called_once_with('knotc', 'a1', 'a2')
|
||||
|
||||
|
||||
class Knot2AgentBackendUnitTestCase(TestCase):
|
||||
|
||||
def _create_dnspy_zone(self, 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') % {'name': name, 'ns': 'ns1.designate.com'}
|
||||
|
||||
return dns.zone.from_text(zone_text, check_origin=False)
|
||||
|
||||
def setUp(self):
|
||||
super(Knot2AgentBackendUnitTestCase, self).setUp()
|
||||
self.kb = Knot2Backend('foo')
|
||||
self.patch_ob(self.kb, '_execute_knotc')
|
||||
|
||||
def tearDown(self):
|
||||
super(Knot2AgentBackendUnitTestCase, self).tearDown()
|
||||
|
||||
def patch_ob(self, *a, **kw):
|
||||
self.useFixture(fixtures.MockPatchObject(*a, **kw))
|
||||
|
||||
def test_create_zone(self, *mocks):
|
||||
zone = self._create_dnspy_zone('example.org')
|
||||
self.kb.create_zone(zone)
|
||||
self.kb._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, *mocks):
|
||||
self.kb._execute_knotc.return_value = 'duplicate identifier'
|
||||
zone = self._create_dnspy_zone('example.org')
|
||||
self.kb.create_zone(zone)
|
||||
self.kb._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.kb._start_minidns_to_knot_axfr('foo')
|
||||
self.kb._execute_knotc.assert_called_with('zone-refresh', 'foo')
|
||||
|
||||
@mock.patch('oslo_concurrency.lockutils.lock')
|
||||
def test__modify_zone(self, *mocks):
|
||||
self.kb._modify_zone('blah', 'bar')
|
||||
self.assertEqual(3, self.kb._execute_knotc.call_count)
|
||||
self.kb._execute_knotc.assert_called_with('conf-commit')
|
||||
|
||||
@mock.patch('oslo_concurrency.lockutils.lock')
|
||||
def test__modify_zone_exception(self, *mocks):
|
||||
# Raise an exception during the second call to _execute_knotc
|
||||
self.kb._execute_knotc.side_effect = [None, exceptions.Backend, None]
|
||||
self.assertRaises(
|
||||
exceptions.Backend,
|
||||
self.kb._modify_zone, 'blah', 'bar')
|
||||
self.assertEqual(3, self.kb._execute_knotc.call_count)
|
||||
self.kb._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_exe):
|
||||
mock_exe.return_value = "[example.com.] type: slave | serial: 20 | " \
|
||||
"next-event: idle | auto-dnssec: disabled]", ""
|
||||
serial = self.kb.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_exe):
|
||||
mock_exe.side_effect = ProcessExecutionError(
|
||||
"error: [example.com.] (no such zone found)")
|
||||
serial = self.kb.find_zone_serial('example.com')
|
||||
self.assertEqual(None, serial)
|
||||
|
||||
@mock.patch('designate.backend.agent_backend.impl_knot2.execute')
|
||||
def test_find_zone_serial_unexpected_output(self, mock_exe):
|
||||
mock_exe.return_value = "bogus output", ""
|
||||
self.assertRaises(
|
||||
exceptions.Backend,
|
||||
self.kb.find_zone_serial, 'example.com')
|
||||
|
||||
@mock.patch('designate.backend.agent_backend.impl_knot2.execute')
|
||||
def test_find_zone_serial_error(self, mock_exe):
|
||||
mock_exe.side_effect = ProcessExecutionError("blah")
|
||||
self.assertRaises(
|
||||
exceptions.Backend,
|
||||
self.kb.find_zone_serial, 'example.com')
|
||||
|
||||
def test_update_zone(self):
|
||||
zone = self._create_dnspy_zone('example.org')
|
||||
self.kb.update_zone(zone)
|
||||
self.kb._execute_knotc.assert_called_once_with(
|
||||
'zone-refresh', 'example.org')
|
||||
|
||||
def test_delete_zone(self):
|
||||
self.kb.delete_zone('example.org')
|
||||
self.kb._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.kb._execute_knotc.return_value = 'duplicate identifier'
|
||||
self.kb.delete_zone('example.org')
|
||||
self.kb._execute_knotc.assert_has_calls([
|
||||
call('conf-begin'),
|
||||
call('conf-unset', 'zone[example.org]',
|
||||
expected_error='invalid identifier'),
|
||||
call('conf-commit'),
|
||||
])
|
130
devstack/designate_plugins/backend-agent-knot2
Normal file
130
devstack/designate_plugins/backend-agent-knot2
Normal file
@ -0,0 +1,130 @@
|
||||
# Configure the Knot2 agent backend for Devstack
|
||||
|
||||
# Enable this pluging by adding these line to local.conf:
|
||||
#
|
||||
# DESIGNATE_BACKEND_DRIVER=agent
|
||||
# DESIGNATE_AGENT_BACKEND_DRIVER=knot2
|
||||
|
||||
# install_designate_agent_backend - install any external requirements
|
||||
# configure_designate_agent_backend - make configuration changes, including those to other services
|
||||
# init_designate_agent_backend - initialize databases, etc.
|
||||
# start_designate_agent_backend - start any external services
|
||||
# stop_designate_agent_backend - stop any external services
|
||||
# cleanup_designate_agent_backend - remove transient data and cache
|
||||
|
||||
# Save trace setting
|
||||
DP_AGENT_KNOT_XTRACE=$(set +o | grep xtrace)
|
||||
set +o xtrace
|
||||
|
||||
# Defaults
|
||||
# --------
|
||||
KNOT_SERVICE_NAME=knot
|
||||
KNOT_CFG_DIR=/etc/knot
|
||||
KNOT_VAR_DIR=/var/lib/knot
|
||||
KNOT_USER=knot
|
||||
KNOT_GROUP=knot
|
||||
|
||||
if is_fedora; then
|
||||
echo "only Ubuntu is supported right now"
|
||||
fi
|
||||
|
||||
# Entry Points
|
||||
# ------------
|
||||
|
||||
# install_designate_agent_backend - install any external requirements
|
||||
function install_designate_agent_backend {
|
||||
if is_ubuntu; then
|
||||
# https://github.com/oerdnj/deb.sury.org/issues/56
|
||||
LC_ALL=C.UTF-8 sudo add-apt-repository --yes ppa:cz.nic-labs/knot-dns
|
||||
sudo apt-get update
|
||||
echo "---- available knot package ---"
|
||||
sudo apt-cache show knot
|
||||
echo "---- installing knot ---"
|
||||
sudo apt-get install -y knot
|
||||
else
|
||||
echo "only Ubuntu is supported right now"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# configure_designate_agent_backend - make configuration changes, including those to other services
|
||||
function configure_designate_agent_backend {
|
||||
|
||||
# [re]create the config database
|
||||
stop_service knot
|
||||
sudo sh -c "rm /var/lib/knot/*zone /var/lib/knot/*/*.mdb -f"
|
||||
sudo knotc conf-init -v
|
||||
|
||||
# Create /etc/default/knot
|
||||
cat <<EOF | sudo tee /etc/default/knot
|
||||
# Created by $0 on $(date)
|
||||
KNOTD_ARGS="-C /var/lib/knot/confdb"
|
||||
EOF
|
||||
|
||||
# Apply this workaround for bug
|
||||
# https://gitlab.labs.nic.cz/labs/knot/issues/455
|
||||
sudo sh -c "cd /etc/default/ && test -f knotd || ln -s knot knotd"
|
||||
|
||||
start_service knot
|
||||
sleep 1
|
||||
|
||||
# Ensure the confdb is present
|
||||
sudo test -f /var/lib/knot/confdb/data.mdb
|
||||
|
||||
# Create the configuration
|
||||
MINIDNS_IPADDR=$DESIGNATE_SERVICE_HOST
|
||||
|
||||
sudo knotc conf-begin
|
||||
sudo knotc conf-set server.listen $DESIGNATE_SERVICE_HOST@$DESIGNATE_SERVICE_PORT_DNS
|
||||
sudo knotc conf-set remote[minidns]
|
||||
sudo knotc conf-set remote[minidns].address $DESIGNATE_SERVICE_HOST@$DESIGNATE_SERVICE_PORT_MDNS
|
||||
sudo knotc conf-set template[default]
|
||||
sudo knotc conf-set template[default].master minidns
|
||||
sudo knotc conf-set template[default].acl acl_minidns
|
||||
sudo knotc conf-set template[default].semantic-checks on
|
||||
# Create localdomain as a workaround for
|
||||
# https://gitlab.labs.nic.cz/labs/knot/issues/457
|
||||
sudo knotc conf-set zone[localdomain]
|
||||
sudo knotc conf-set log.any info
|
||||
sudo knotc conf-set log.target syslog
|
||||
sudo knotc conf-set acl[acl_minidns]
|
||||
sudo knotc conf-set acl[acl_minidns].address $DESIGNATE_SERVICE_HOST
|
||||
sudo knotc conf-set acl[acl_minidns].action notify
|
||||
echo "--------------"
|
||||
sudo knotc conf-diff
|
||||
echo "--------------"
|
||||
sudo knotc conf-commit
|
||||
sudo knotc conf-check
|
||||
|
||||
# Ensure the zone survives a restart
|
||||
sleep 1
|
||||
sudo service knot restart
|
||||
sleep 1
|
||||
sudo knotc zone-status localdomain
|
||||
|
||||
echo "Testing Knot: this should return the daemon version"
|
||||
dig @$DESIGNATE_SERVICE_HOST -p$DESIGNATE_SERVICE_PORT_DNS version.server CH TXT
|
||||
}
|
||||
|
||||
# init_designate_agent_backend - initialize databases, etc.
|
||||
function init_designate_agent_backend {
|
||||
:
|
||||
}
|
||||
|
||||
# start_designate_agent_backend - start any external services
|
||||
function start_designate_agent_backend {
|
||||
start_service knot
|
||||
}
|
||||
|
||||
# stop_designate_agent_backend - stop any external services
|
||||
function stop_designate_agent_backend {
|
||||
stop_service knot
|
||||
}
|
||||
|
||||
# cleanup_designate_agent_backend - remove transient data and cache
|
||||
function cleanup_designate_agent_backend {
|
||||
:
|
||||
}
|
||||
|
||||
# Restore xtrace
|
||||
$DP_AGENT_KNOT_XTRACE
|
@ -76,3 +76,14 @@ Backend Powerdns
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
Agent Backend KnotDNS
|
||||
=====================
|
||||
|
||||
.. automodule:: designate.backend.agent_backend.impl_knot2
|
||||
:members:
|
||||
:special-members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
|
201
doc/source/backends/knot2_agent.rst
Normal file
201
doc/source/backends/knot2_agent.rst
Normal file
@ -0,0 +1,201 @@
|
||||
..
|
||||
Copyright 2016 Hewlett Packard Enterprise Development Company LP
|
||||
|
||||
Author: Federico Ceratto <federico.ceratto@hpe.com>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
not use this file except in compliance with the License. You may obtain
|
||||
a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
Knot DNS 2 Agent backend
|
||||
************************
|
||||
|
||||
|
||||
User documentation
|
||||
==================
|
||||
|
||||
This page documents the Agent backend for `Knot DNS <https://www.knot-dns.cz/>`_.
|
||||
|
||||
The agent runs on the same host as the resolver. It receives DNS messages from Mini DNS using private DNS OPCODEs and classes and creates or deletes zones on Knot using the knotc tool.
|
||||
It also instructs Knot to request AXFR from MiniDNS when a zone is created or updated.
|
||||
|
||||
Support matrix:
|
||||
|
||||
* 2.0 and older: not supported
|
||||
* 2.1.1: supported, tested
|
||||
* 2.2.0: `affected by a bug <https://gitlab.labs.nic.cz/labs/knot/issues/460>`_
|
||||
|
||||
|
||||
`Knot DNS 2.x documentation <https://www.knot-dns.cz/docs/2.x/singlehtml/>`_
|
||||
|
||||
Setting up Knot DNS on Ubuntu Trusty
|
||||
------------------------------------
|
||||
|
||||
Knot DNS 2.1 is not part of Ubuntu Trusty. You can ues the CZ.NIC PPA with:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
service pdns stop
|
||||
add-apt-repository ppa:cz.nic-labs/knot-dns
|
||||
apt-get update
|
||||
apt-get install knot
|
||||
|
||||
|
||||
Configuring Knot DNS
|
||||
--------------------
|
||||
|
||||
Assuming Knot has been freshly installed on the system, run as root:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Monitor syslog during the next steps
|
||||
tail -f /var/log/syslog
|
||||
|
||||
# Start the daemon, ensure it's running
|
||||
service knot start
|
||||
netstat -npltu | grep knotd
|
||||
|
||||
# Create the config database
|
||||
knotc conf-init
|
||||
|
||||
# Edit /etc/default/knot
|
||||
# Set the variable:
|
||||
# KNOTD_ARGS="-C /var/lib/knot/confdb"
|
||||
|
||||
# Restart
|
||||
service knot restart
|
||||
|
||||
# Check if the deamon is still running from the conf file in /etc/knot/
|
||||
ps axuw | grep knotd
|
||||
|
||||
# if so, apply this workaround for bug
|
||||
# https://gitlab.labs.nic.cz/labs/knot/issues/455
|
||||
( cd /etc/default/ && ln -s knot knotd )
|
||||
service knot restart
|
||||
ps axuw | grep knotd
|
||||
|
||||
# Ensure the confdb is present
|
||||
test -f /var/lib/knot/confdb/data.mdb && echo OK
|
||||
|
||||
# Create the configuration
|
||||
# Populate the variable with the MiniDNS ipaddr:
|
||||
MINIDNS_IPADDR=
|
||||
|
||||
knotc conf-begin
|
||||
knotc conf-set server.listen 0.0.0.0@53
|
||||
# To listen on IPv6 as well, also run this:
|
||||
# knotc conf-set server.listen '::@53'
|
||||
knotc conf-set remote[minidns]
|
||||
knotc conf-set remote[minidns].address $MINIDNS_IPADDR@5354
|
||||
knotc conf-set template[default]
|
||||
knotc conf-set template[default].master minidns
|
||||
knotc conf-set template[default].acl acl_minidns
|
||||
knotc conf-set template[default].semantic-checks on
|
||||
knotc conf-set zone[example.com]
|
||||
knotc conf-set log.any info
|
||||
knotc conf-set log.target syslog
|
||||
knotc conf-set acl[acl_minidns]
|
||||
knotc conf-set acl[acl_minidns].address $MINIDNS_IPADDR
|
||||
knotc conf-set acl[acl_minidns].action notify
|
||||
# Review the changes and commit
|
||||
knotc conf-diff
|
||||
knotc conf-commit
|
||||
|
||||
# Optionally check and back up the conf
|
||||
knotc conf-check
|
||||
knotc conf-export knot.conf.bak && cat knot.conf.bak
|
||||
|
||||
# Ensure the zone survives a restart
|
||||
service knot restart
|
||||
knotc zone-status example.com
|
||||
|
||||
# Test Knot: this should return the version
|
||||
dig @127.0.0.1 version.server CH TXT
|
||||
|
||||
If needed, create a rootwrap filter, as root:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
cat > /etc/designate/rootwrap.d/knot2.filters <<EOF
|
||||
# cmd-name: filter-name, raw-command, user, args
|
||||
[Filters]
|
||||
knotc: CommandFilter, /usr/sbin/knotc, root
|
||||
EOF
|
||||
|
||||
# Check the filter:
|
||||
sudo /usr/local/bin/designate-rootwrap /etc/designate/rootwrap.conf knotc status
|
||||
|
||||
Configure the "service.agent" and "backend.agent.knot2" sections in /etc/designate/designate.conf
|
||||
|
||||
Look in designate.conf.example for examples
|
||||
|
||||
Create an agent pool:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Fetch the existing pool(s) if needed or start from scratch
|
||||
designate-manage pool generate_file --file /tmp/pool.yaml
|
||||
# Edit the file (see below) and reload it as:
|
||||
designate-manage pool update --file /tmp/pool.yaml
|
||||
|
||||
The "targets" section in pool.yaml should look like:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
targets:
|
||||
- description: knot2 agent
|
||||
masters:
|
||||
- host: <MiniDNS IP addr>
|
||||
port: 5354
|
||||
options: {}
|
||||
options:
|
||||
- host: <Agent IP addr>
|
||||
port: 5358
|
||||
type: agent
|
||||
|
||||
Developer documentation
|
||||
=======================
|
||||
|
||||
Devstack testbed
|
||||
----------------
|
||||
|
||||
Follow "Setting up Knot DNS on Ubuntu Trusty"
|
||||
|
||||
Configure Knot to slave from MiniDNS on 192.168.121.131
|
||||
|
||||
Knotd configuration example (sudo knotc conf-export <filename>):
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
# Configuration export (Knot DNS 2.1.1)
|
||||
|
||||
server:
|
||||
listen: "0.0.0.0@53"
|
||||
|
||||
log:
|
||||
- target: "syslog"
|
||||
any: "debug"
|
||||
|
||||
acl:
|
||||
- id: "acl_minidns"
|
||||
address: [ "192.168.121.131" ]
|
||||
action: [ "notify" ]
|
||||
|
||||
remote:
|
||||
- id: "minidns"
|
||||
address: "192.168.121.131@5354"
|
||||
|
||||
template:
|
||||
- id: "default"
|
||||
master: "minidns"
|
||||
acl: "acl_minidns"
|
||||
semantic-checks: "on"
|
||||
|
@ -47,13 +47,14 @@ backend-impl-bind9=Bind9
|
||||
backend-impl-powerdns-mysql=Power DNS (MySQL)
|
||||
backend-impl-designate=Designate to Designate
|
||||
backend-impl-dynect=DynECT
|
||||
backend-impl-dynect=DynECT
|
||||
backend-impl-akamai=Akamai eDNS
|
||||
backend-impl-infoblox-xfr=Infoblox (XFR)
|
||||
backend-impl-nsd4=NSD4
|
||||
backend-impl-agent=Agent
|
||||
backend-impl-bind9-agent=Bind9 (Agent)
|
||||
backend-impl-denominator=Denominator
|
||||
backend-impl-knot2-agent=Knot2 (Agent)
|
||||
|
||||
|
||||
[backends.backend-impl-bind9]
|
||||
|
||||
@ -75,6 +76,9 @@ maintainers=HP DNSaaS Team <dnsaas@hp.com>
|
||||
[backends.backend-impl-bind9-agent]
|
||||
type=agent
|
||||
|
||||
[backends.backend-impl-knot2-agent]
|
||||
type=agent
|
||||
|
||||
[backends.backend-impl-infoblox-xfr]
|
||||
status=release-compatible
|
||||
maintainers=Infoblox OpenStack Team <openstack-maintainer@infoblox.com>
|
||||
|
@ -256,13 +256,19 @@ debug = False
|
||||
#-----------------------
|
||||
# Agent Service
|
||||
#-----------------------
|
||||
# The agent runs on the resolver hosts
|
||||
[service:agent]
|
||||
#workers = None
|
||||
#listen = 0.0.0.0:5358
|
||||
#tcp_backlog = 100
|
||||
#allow_notify = 127.0.0.1
|
||||
|
||||
# MiniDNS IP address and port
|
||||
#masters = 127.0.0.1:5354
|
||||
|
||||
# Set to "fake", "bind9" or "knot2"
|
||||
#backend_driver = fake
|
||||
|
||||
#transfer_source = None
|
||||
#notify_delay = 0
|
||||
|
||||
@ -441,9 +447,12 @@ debug = False
|
||||
#format = '%(hostname)s.%(project)s.%(domain)s'
|
||||
#format = '%(hostname)s.%(domain)s'
|
||||
|
||||
#############################
|
||||
##############################
|
||||
## Agent Backend Configuration
|
||||
#############################
|
||||
##############################
|
||||
|
||||
# Set backend_driver in the [service:agent] section
|
||||
|
||||
[backend:agent:bind9]
|
||||
#rndc_host = 127.0.0.1
|
||||
#rndc_port = 953
|
||||
@ -451,7 +460,12 @@ debug = False
|
||||
#rndc_key_file = /etc/rndc.key
|
||||
#zone_file_path = $state_path/zones
|
||||
#query_destination = 127.0.0.1
|
||||
#
|
||||
|
||||
[backend:agent:knot2]
|
||||
# knotc command name when rootwrap is used. Location of the knotc executable
|
||||
# on the resolver host if rootwrap is not used
|
||||
#knotc_cmd_name = /usr/sbin/knotc
|
||||
|
||||
[backend:agent:denominator]
|
||||
#name = dynect
|
||||
#config_file = /etc/denominator.conf
|
||||
|
3
etc/designate/rootwrap.d/knot2.filters
Normal file
3
etc/designate/rootwrap.d/knot2.filters
Normal file
@ -0,0 +1,3 @@
|
||||
# cmd-name: filter-name, raw-command, user, args
|
||||
[Filters]
|
||||
knotc: CommandFilter, /usr/sbin/knotc, root
|
@ -0,0 +1,3 @@
|
||||
---
|
||||
features:
|
||||
- An experimental agent backend to support Knot DNS 2
|
@ -91,6 +91,7 @@ designate.backend =
|
||||
|
||||
designate.backend.agent_backend =
|
||||
bind9 = designate.backend.agent_backend.impl_bind9:Bind9Backend
|
||||
knot2 = designate.backend.agent_backend.impl_knot2:Knot2Backend
|
||||
denominator = designate.backend.agent_backend.impl_denominator:DenominatorBackend
|
||||
fake = designate.backend.agent_backend.impl_fake:FakeBackend
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user