Add gdnsd backend
Change-Id: Iee4227134b1d6c506e66085dcd417fe60e460ffb
This commit is contained in:
parent
6395765b82
commit
4e7c65c312
245
designate/backend/agent_backend/impl_gdnsd.py
Normal file
245
designate/backend/agent_backend/impl_gdnsd.py
Normal file
@ -0,0 +1,245 @@
|
||||
# Copyright 2016 Hewlett Packard Enterprise Development Company LP
|
||||
#
|
||||
# Author: Federico Ceratto <federico.ceratto@hpe.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
backend.agent_backend.impl_gdnsd
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
gdnsd agent backend
|
||||
|
||||
Create, update, delete zones locally on a gdnsd resolver using the
|
||||
gdnsd utility.
|
||||
|
||||
Supported Knot versions: >= 2.1, < 3
|
||||
|
||||
`User documentation <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 os
|
||||
import tempfile
|
||||
import string
|
||||
|
||||
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 import utils
|
||||
from designate import exceptions
|
||||
from designate.backend.agent_backend import base
|
||||
from designate.i18n import _LI
|
||||
from designate.i18n import _LE
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CFG_GROUP = 'backend:agent:gdnsd'
|
||||
# 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(_LE("Unhandled exception %s"), e.message, exc_info=True)
|
||||
raise exceptions.Backend(e.message)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
class GdnsdBackend(base.AgentBackend):
|
||||
__plugin_name__ = 'gdnsd'
|
||||
__backend_status__ = 'experimental'
|
||||
|
||||
@classmethod
|
||||
def get_cfg_opts(cls):
|
||||
group = cfg.OptGroup(
|
||||
name=CFG_GROUP, title="Configuration for gdnsd backend"
|
||||
)
|
||||
opts = [
|
||||
cfg.StrOpt('gdnsd-cmd-name',
|
||||
help='gdnsd executable path or rootwrap command name',
|
||||
default=GDNSD_DEFAULT_PATH),
|
||||
cfg.StrOpt('confdir-path',
|
||||
help='gdnsd configuration directory path',
|
||||
default=CONFDIR_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(GdnsdBackend, self).__init__(*a, **kw)
|
||||
|
||||
self._gdnsd_cmd_name = cfg.CONF[CFG_GROUP].gdnsd_cmd_name
|
||||
LOG.info(_LI("gdnsd command: %r"), self._gdnsd_cmd_name)
|
||||
self._confdir_path = cfg.CONF[CFG_GROUP].confdir_path
|
||||
self._zonedir_path = os.path.join(self._confdir_path, 'zones')
|
||||
LOG.info(_LI("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].query_destination]
|
||||
LOG.info(_LI("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(_LI("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].gdnsd_cmd_name,
|
||||
'-D', '-x', 'checkconf', '-c', self._confdir_path,
|
||||
run_as_root=False,
|
||||
)
|
||||
except ProcessExecutionError as e:
|
||||
LOG.error(_LE("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().rstrip('.')
|
||||
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 os.errno.ENOENT == e.errno:
|
||||
LOG.info(_LI("Zone datafile %s was already deleted"), zone_fn)
|
||||
return
|
||||
raise
|
156
designate/tests/test_agent/test_backends/test_gdnsd.py
Normal file
156
designate/tests/test_agent/test_backends/test_gdnsd.py
Normal file
@ -0,0 +1,156 @@
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Functional-test the Gdnsd 2 agent backend
|
||||
|
||||
These tests rely on creating directories and files and running
|
||||
gdnsd.
|
||||
gdnsd must be installed
|
||||
"""
|
||||
|
||||
from textwrap import dedent as de
|
||||
import glob
|
||||
import os
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
from oslo_concurrency.processutils import ProcessExecutionError
|
||||
from oslo_config import cfg
|
||||
from oslo_config import fixture as cfg_fixture
|
||||
import dns
|
||||
import fixtures
|
||||
import mock
|
||||
import testtools
|
||||
|
||||
from designate import exceptions
|
||||
from designate.backend.agent_backend.impl_gdnsd import CFG_GROUP
|
||||
from designate.backend.agent_backend.impl_gdnsd import GdnsdBackend
|
||||
from designate.tests import TestCase
|
||||
|
||||
TMPFS_DIR = "/dev/shm"
|
||||
ROOT_TMP_DIR = TMPFS_DIR if os.path.isdir(TMPFS_DIR) else "/tmp"
|
||||
GDNSD_BIN_PATH = "/usr/sbin/gdnsd"
|
||||
GDNSD_NOT_AVAILABLE = not os.path.isfile(GDNSD_BIN_PATH)
|
||||
|
||||
ZONE_TPL = """
|
||||
$ORIGIN %(name)s
|
||||
%(name)s 3600 IN SOA ns.%(name)s email.%(name)s. 1421777854 3600 600 86400 3600
|
||||
%(name)s 3600 IN NS ns.%(name)s
|
||||
ns 300 IN A 127.0.0.1
|
||||
""" # noqa
|
||||
|
||||
|
||||
class GdnsdAgentBackendTestCase(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(GdnsdAgentBackendTestCase, self).setUp()
|
||||
self.conf_dir_path = tempfile.mkdtemp(dir=ROOT_TMP_DIR)
|
||||
self.zones_dir_path = os.path.join(self.conf_dir_path, 'zones')
|
||||
os.mkdir(self.zones_dir_path)
|
||||
self.CONF = self.useFixture(cfg_fixture.Config(cfg.CONF)).conf
|
||||
cfg.CONF.set_override('confdir_path', self.conf_dir_path,
|
||||
CFG_GROUP, enforce_type=True)
|
||||
cfg.CONF.set_override('gdnsd_cmd_name', GDNSD_BIN_PATH,
|
||||
CFG_GROUP, enforce_type=True)
|
||||
|
||||
self.backend = GdnsdBackend('foo')
|
||||
|
||||
def tearDown(self):
|
||||
super(GdnsdAgentBackendTestCase, self).tearDown()
|
||||
for zone_fn in glob.glob(os.path.join(self.zones_dir_path, "*.org")):
|
||||
os.remove(zone_fn)
|
||||
os.rmdir(self.zones_dir_path)
|
||||
os.rmdir(self.conf_dir_path)
|
||||
|
||||
def _patch_ob(self, *a, **kw):
|
||||
self.useFixture(fixtures.MockPatchObject(*a, **kw))
|
||||
|
||||
def _create_dnspy_zone(self, name):
|
||||
name = name.rstrip('.')
|
||||
zone_text = ZONE_TPL % {'name': name}
|
||||
return dns.zone.from_text(zone_text, check_origin=False)
|
||||
|
||||
def _create_dnspy_zone_with_records(self, name):
|
||||
zone_text = (
|
||||
'$ORIGIN %(name)s\n'
|
||||
'@ 3600 IN SOA %(ns)s email.%(name)s 1421777854 3600 600 86400 3600\n' # noqa
|
||||
' 3600 IN NS %(ns)s\n'
|
||||
' 1800 IN A 173.194.123.30\n'
|
||||
' 1800 IN A 173.194.123.31\n'
|
||||
's 2400 IN AAAA 2001:db8:cafe::1\n'
|
||||
's 2400 IN AAAA 2001:db8:cafe::2\n'
|
||||
% {'name': name, 'ns': 'ns1.example.net.'}
|
||||
)
|
||||
return dns.zone.from_text(zone_text, check_origin=False)
|
||||
|
||||
@mock.patch('designate.utils.execute', return_value=("", ""))
|
||||
def test_start(self, *mocks):
|
||||
self.backend.start()
|
||||
|
||||
@mock.patch('designate.utils.execute', side_effect=ProcessExecutionError)
|
||||
def test_exec_error(self, *mocks):
|
||||
with testtools.ExpectedException(exceptions.Backend):
|
||||
self.backend._check_conf()
|
||||
|
||||
def test_create_zone(self, *mocks):
|
||||
zone = self._create_dnspy_zone('example.org')
|
||||
self.backend.create_zone(zone)
|
||||
|
||||
zone_fn = os.path.join(self.zones_dir_path, "example.org")
|
||||
expected = de("""\
|
||||
ns 300 IN A 127.0.0.1
|
||||
@ 3600 IN SOA ns email.example.org. 1421777854 3600 600 86400 3600
|
||||
@ 3600 IN NS ns
|
||||
""")
|
||||
with open(zone_fn) as f:
|
||||
self.assertEqual(expected, f.read())
|
||||
|
||||
@unittest.skipIf(GDNSD_NOT_AVAILABLE, "gdnsd binary not installed")
|
||||
def test_create_zone_and_check(self):
|
||||
zone = self._create_dnspy_zone('example.org')
|
||||
self.backend.create_zone(zone)
|
||||
self.backend._check_conf()
|
||||
|
||||
def test_update_zone(self):
|
||||
zone = self._create_dnspy_zone_with_records('example.org')
|
||||
self.backend.update_zone(zone)
|
||||
|
||||
zone_fn = os.path.join(self.zones_dir_path, "example.org")
|
||||
expected = de("""\
|
||||
@ 3600 IN SOA ns1.example.net. email 1421777854 3600 600 86400 3600
|
||||
@ 3600 IN NS ns1.example.net.
|
||||
@ 1800 IN A 173.194.123.30
|
||||
@ 1800 IN A 173.194.123.31
|
||||
s 2400 IN AAAA 2001:db8:cafe::1
|
||||
s 2400 IN AAAA 2001:db8:cafe::2
|
||||
""") # noqa
|
||||
with open(zone_fn) as f:
|
||||
self.assertEqual(expected, f.read())
|
||||
|
||||
@unittest.skipIf(GDNSD_NOT_AVAILABLE, "gdnsd binary not installed")
|
||||
def test_update_zone_and_check(self):
|
||||
zone = self._create_dnspy_zone_with_records('example.org')
|
||||
self.backend.update_zone(zone)
|
||||
self.backend._check_conf()
|
||||
|
||||
def test_delete_zone(self):
|
||||
foo_fn = os.path.join(self.zones_dir_path, 'foo')
|
||||
with open(foo_fn, 'w') as f:
|
||||
f.write("42")
|
||||
self.backend.delete_zone('foo')
|
||||
self.assertFalse(os.path.isfile(foo_fn))
|
||||
self.backend.delete_zone('foo')
|
85
designate/tests/unit/test_agent/test_backends/test_gdnsd.py
Normal file
85
designate/tests/unit/test_agent/test_backends/test_gdnsd.py
Normal file
@ -0,0 +1,85 @@
|
||||
# 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 Gdnsd 2 agent backend
|
||||
|
||||
These tests do not rely on creating directories and files or running
|
||||
executables from the gdnsd suite
|
||||
"""
|
||||
|
||||
import dns.zone
|
||||
import fixtures
|
||||
import mock
|
||||
|
||||
from designate.backend.agent_backend.impl_gdnsd import GdnsdBackend
|
||||
from designate.tests import TestCase
|
||||
import designate.backend.agent_backend.impl_gdnsd # noqa
|
||||
|
||||
|
||||
class GdnsdAgentBackendUnitTestCase(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(GdnsdAgentBackendUnitTestCase, self).setUp()
|
||||
self.useFixture(fixtures.MockPatchObject(
|
||||
GdnsdBackend, '_check_dirs'
|
||||
))
|
||||
self.backend = GdnsdBackend('foo')
|
||||
self.useFixture(fixtures.MockPatchObject(self.backend._resolver,
|
||||
'query'))
|
||||
|
||||
def tearDown(self):
|
||||
super(GdnsdAgentBackendUnitTestCase, self).tearDown()
|
||||
|
||||
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 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):
|
||||
fn = self.backend._generate_zone_filename("A/bc-d_e.f")
|
||||
self.assertEqual("a@bc-d_e.f", fn)
|
||||
|
||||
def test_find_zone_serial(self):
|
||||
class Data(object):
|
||||
serial = 3
|
||||
|
||||
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.query.side_effect = RuntimeError('foo')
|
||||
|
||||
serial = self.backend.find_zone_serial('example.com')
|
||||
self.assertEqual(None, 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")
|
@ -87,6 +87,16 @@ Agent Backend KnotDNS
|
||||
: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
|
||||
====================
|
||||
|
||||
|
115
doc/source/backends/gdnsd_agent.rst
Normal file
115
doc/source/backends/gdnsd_agent.rst
Normal file
@ -0,0 +1,115 @@
|
||||
..
|
||||
Copyright 2016 Hewlett Packard Enterprise Development Company LP
|
||||
|
||||
Author: Federico Ceratto <federico.ceratto@hpe.com>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
not use this file except in compliance with the License. You may obtain
|
||||
a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
gdnsd Agent backend
|
||||
*******************
|
||||
|
||||
|
||||
User documentation
|
||||
==================
|
||||
|
||||
This page documents the Agent backend for `gdnsd <http://gdnsd.org/>`_.
|
||||
|
||||
The agent runs on the same host as the resolver. It receives DNS messages from Mini DNS using private DNS OPCODEs and classes and creates/updates/deletes zones on gdnsd using zone files under the gdnsd configuration directory.
|
||||
|
||||
The backend supports gdnsd from version 2.0
|
||||
|
||||
`gdnsd documentation <https://github.com/gdnsd/gdnsd/wiki>`_
|
||||
|
||||
Setting up gdnsd on Ubuntu Vivid
|
||||
--------------------------------
|
||||
|
||||
Run as root:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
apt-get update
|
||||
apt-get install gdnsd
|
||||
|
||||
Configuring gdnsd
|
||||
-----------------
|
||||
|
||||
Assuming gdnsd has been freshly installed on the system, run as root:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Monitor syslog during the next steps
|
||||
tail -f /var/log/syslog
|
||||
|
||||
# config check should be successful
|
||||
/usr/sbin/gdnsd checkconf
|
||||
|
||||
# Start the daemon if needed
|
||||
service gdnsd status
|
||||
service gdnsd start
|
||||
|
||||
# gdnsd should be listening on TCP and UDP ports
|
||||
netstat -lnptu | grep '/gdnsd'
|
||||
|
||||
# Test the daemon: it should respond with "gdnsd"
|
||||
dig @127.0.0.1 CH TXT +short
|
||||
|
||||
Configure the "service.agent" and "backend.agent.gdnsd" sections in /etc/designate/designate.conf
|
||||
|
||||
Look in designate.conf.example for more complete examples
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[service:agent]
|
||||
backend_driver = gdnsd
|
||||
# Place here the MiniDNS ipaddr and port (not the agent itself)
|
||||
masters = 192.168.27.100:5354
|
||||
|
||||
[backend:agent:gdnsd]
|
||||
#gdnsd_cmd_name = gdnsd
|
||||
#confdir_path = /etc/gdnsd
|
||||
#query_destination = 127.0.0.1
|
||||
|
||||
Ensure that the "zones" directory under "confdir_path" (default /etc/gdnsd) is readable and writable by the system user running the Designate Agent
|
||||
|
||||
Create an agent pool:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Fetch the existing pool(s) if needed
|
||||
designate-manage pool generate_file --file /tmp/pool.yaml
|
||||
# Edit the file (see below) and reload it as:
|
||||
designate-manage pool update --file /tmp/pool.yaml
|
||||
|
||||
The "targets" section in pool.yaml should look like:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
targets:
|
||||
- description: gdnsd agent
|
||||
masters:
|
||||
- host: <MiniDNS IP addr>
|
||||
port: 5354
|
||||
options: {}
|
||||
options:
|
||||
- host: <Agent IP addr>
|
||||
port: 5358
|
||||
type: agent
|
||||
|
||||
Start the Designate Agent. You should see log messages similar to:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
2016-05-03 15:13:38.193 INFO designate.backend.agent_backend.impl_gdnsd [-] gdnsd command: 'gdnsd'
|
||||
2016-05-03 15:13:38.193 INFO designate.backend.agent_backend.impl_gdnsd [-] gdnsd conf directory: '/etc/gdnsd'
|
||||
2016-05-03 15:13:38.194 INFO designate.backend.agent_backend.impl_gdnsd [-] Resolvers: ['127.0.0.1']
|
||||
|
@ -55,7 +55,7 @@ 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)
|
||||
|
||||
[backends.backend-impl-bind9]
|
||||
|
||||
@ -83,6 +83,9 @@ type=agent
|
||||
[backends.backend-impl-djbdns-agent]
|
||||
type=agent
|
||||
|
||||
[backends.backend-impl-gdnsd-agent]
|
||||
type=agent
|
||||
|
||||
[backends.backend-impl-infoblox-xfr]
|
||||
status=release-compatible
|
||||
maintainers=Infoblox OpenStack Team <openstack-maintainer@infoblox.com>
|
||||
|
@ -479,6 +479,11 @@ debug = False
|
||||
#name = dynect
|
||||
#config_file = /etc/denominator.conf
|
||||
|
||||
[backend:agent:gdnsd]
|
||||
#gdnsd_cmd_name = gdnsd
|
||||
#confdir_path = /etc/gdnsd
|
||||
#query_destination = 127.0.0.1
|
||||
|
||||
########################
|
||||
## Library Configuration
|
||||
########################
|
||||
|
@ -95,6 +95,7 @@ designate.backend.agent_backend =
|
||||
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
|
||||
|
||||
designate.network_api =
|
||||
fake = designate.network_api.fake:FakeNetworkAPI
|
||||
|
Loading…
x
Reference in New Issue
Block a user