summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlin Balutoiu <abalutoiu@cloudbasesolutions.com>2016-06-22 09:34:28 +0300
committerAlin Balutoiu <abalutoiu@cloudbasesolutions.com>2016-08-22 12:19:54 +0300
commit95f451dc8a07a0434210be5629490f150bf2962e (patch)
tree0d696bc42949ed670451d49e6ad94e5c476e56f5
parentf64d3a358a147818a9d0432be879af0b8d2dd414 (diff)
downloaddesignate-95f451dc8a07a0434210be5629490f150bf2962e.tar.gz
Add support for Microsoft DNS Server backend
Currently, there is no support for Microsoft DNS in designate. This patch addresses this issue by adding support for Microsoft DNS Server using an agent backend in designate. Change-Id: I8db1906e17e5fb20fa6f3e5d1f13b2d701f0c032 Implements: blueprint msdns-backend-support Depends-On: I029747555a58e0a8e362b65e6c0c470cf2774e42
-rw-r--r--designate/backend/agent_backend/impl_msdns.py113
-rw-r--r--designate/tests/unit/test_agent/test_backends/test_msdns.py122
-rw-r--r--devstack/designate_plugins/backend-agent-msdns116
-rw-r--r--doc/source/backends/msdns_agent.rst134
-rw-r--r--doc/source/sourcedoc/backend.rst10
-rw-r--r--doc/source/support-matrix.ini5
-rw-r--r--etc/designate/designate.conf.sample3
-rw-r--r--requirements.txt1
-rw-r--r--setup.cfg1
9 files changed, 505 insertions, 0 deletions
diff --git a/designate/backend/agent_backend/impl_msdns.py b/designate/backend/agent_backend/impl_msdns.py
new file mode 100644
index 00000000..949342fa
--- /dev/null
+++ b/designate/backend/agent_backend/impl_msdns.py
@@ -0,0 +1,113 @@
+# Copyright 2016 Cloudbase Solutions Srl
+# All Rights Reserved.
+#
+# Author: Alin Balutoiu <abalutoiu@cloudbasesolutions.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from oslo_config import cfg
+from oslo_log import log as logging
+from os_win import utilsfactory
+from os_win import constants
+from os_win import exceptions as os_win_exc
+
+from designate.backend.agent_backend import base
+from designate import exceptions
+from designate.i18n import _LI
+
+
+LOG = logging.getLogger(__name__)
+CFG_GROUP = 'backend:agent:msdns'
+
+GROUP = cfg.OptGroup(
+ name=CFG_GROUP,
+ title="Configuration for Microsoft DNS Server"
+)
+OPTS = []
+
+
+class MSDNSBackend(base.AgentBackend):
+
+ __plugin_name__ = 'msdns'
+ __backend_status__ = 'experimental'
+
+ def __init__(self, agent_service):
+ """Configure the backend"""
+ super(MSDNSBackend, self).__init__(agent_service)
+
+ 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(_LI("AXFR masters: %r"), self._masters)
+
+ @classmethod
+ def get_cfg_opts(cls):
+ return [(GROUP, OPTS)]
+
+ def start(self):
+ """Start the backend"""
+ LOG.info(_LI("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)
+ 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)
+ 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/tests/unit/test_agent/test_backends/test_msdns.py b/designate/tests/unit/test_agent/test_backends/test_msdns.py
new file mode 100644
index 00000000..ed798e5e
--- /dev/null
+++ b/designate/tests/unit/test_agent/test_backends/test_msdns.py
@@ -0,0 +1,122 @@
+# Copyright 2016 Cloudbase Solutions Srl
+# All Rights Reserved.
+#
+# Author: Alin Balutoiu <abalutoiu@cloudbasesolutions.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""
+ Unit-test the MSDNS agent backend
+"""
+
+import dns
+import mock
+from os_win import constants
+from os_win import exceptions as os_win_exc
+
+from designate.backend.agent_backend import impl_msdns
+from designate.tests import TestCase
+
+
+class MSDNSAgentBackendUnitTestCase(TestCase):
+
+ _FAKE_ZONE_NAME = 'example.com'
+
+ def setUp(self):
+ super(MSDNSAgentBackendUnitTestCase, self).setUp()
+ self.CONF.set_override('masters', ('127.0.0.1:5354',), 'service:agent')
+
+ patcher = mock.patch('os_win.utilsfactory.get_dnsutils')
+ self._dnsutils = patcher.start().return_value
+ self.addCleanup(patcher.stop)
+ self.backend = impl_msdns.MSDNSBackend('foo')
+
+ 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(['127.0.0.1'], self.backend._masters)
+ self.assertEqual(self._dnsutils, self.backend._dnsutils)
+
+ def test_find_zone_serial(self):
+ serial = self.backend.find_zone_serial(self._FAKE_ZONE_NAME)
+
+ expected_serial = self._dnsutils.get_zone_serial.return_value
+ self.assertEqual(expected_serial, serial)
+ self._dnsutils.get_zone_serial.assert_called_once_with(
+ self._FAKE_ZONE_NAME)
+
+ def test_find_zone_serial_error(self):
+ self._dnsutils.get_zone_serial.side_effect = (
+ os_win_exc.DNSZoneNotFound(zone_name=self._FAKE_ZONE_NAME))
+
+ serial = self.backend.find_zone_serial(self._FAKE_ZONE_NAME)
+
+ self.assertIsNone(serial)
+ self._dnsutils.get_zone_serial.assert_called_once_with(
+ self._FAKE_ZONE_NAME)
+
+ def test_create_zone(self):
+ zone = self._create_dnspy_zone(self._FAKE_ZONE_NAME)
+
+ self.backend.create_zone(zone)
+
+ self._dnsutils.zone_create.assert_called_once_with(
+ zone_name=self._FAKE_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 = self._create_dnspy_zone(self._FAKE_ZONE_NAME)
+ self._dnsutils.zone_create.side_effect = (
+ os_win_exc.DNSZoneAlreadyExists(zone_name=self._FAKE_ZONE_NAME))
+
+ self.assertRaises(os_win_exc.DNSZoneAlreadyExists,
+ self.backend.create_zone,
+ zone)
+ self._dnsutils.get_zone_properties.assert_called_once_with(
+ self._FAKE_ZONE_NAME)
+
+ def test_create_zone_already_existing_identical(self):
+ zone = self._create_dnspy_zone(self._FAKE_ZONE_NAME)
+ self._dnsutils.zone_create.side_effect = (
+ os_win_exc.DNSZoneAlreadyExists(zone_name=self._FAKE_ZONE_NAME))
+ mock_zone_properties = {'zone_type': constants.DNS_ZONE_TYPE_SECONDARY,
+ 'ds_integrated': False,
+ 'master_servers': self.backend._masters}
+ self._dnsutils.get_zone_properties.return_value = mock_zone_properties
+
+ self.backend.create_zone(zone)
+
+ self._dnsutils.get_zone_properties.assert_called_once_with(
+ self._FAKE_ZONE_NAME)
+
+ def test_update_zone(self):
+ zone = self._create_dnspy_zone(self._FAKE_ZONE_NAME)
+
+ self.backend.update_zone(zone)
+
+ self._dnsutils.zone_update.assert_called_once_with(
+ self._FAKE_ZONE_NAME)
+
+ def test_delete_zone(self):
+ self.backend.delete_zone(self._FAKE_ZONE_NAME)
+
+ self._dnsutils.zone_delete.assert_called_once_with(
+ self._FAKE_ZONE_NAME)
diff --git a/devstack/designate_plugins/backend-agent-msdns b/devstack/designate_plugins/backend-agent-msdns
new file mode 100644
index 00000000..1d459cef
--- /dev/null
+++ b/devstack/designate_plugins/backend-agent-msdns
@@ -0,0 +1,116 @@
+# Configure the agent backend
+
+# Enable this pluging by adding these line to local.conf:
+#
+# DESIGNATE_BACKEND_DRIVER=agent
+# DESIGNATE_AGENT_BACKEND_DRIVER=msdns
+
+# Dependencies:
+# ``functions`` file
+# ``designate`` configuration
+
+# install_designate_agent_backend - install any external requirements
+# configure_designate_agent_backend - make configuration changes, including those to other services
+# init_designate_agent_backend - initialize databases, etc.
+# start_designate_agent_backend - start any external services
+# stop_designate_agent_backend - stop any external services
+# cleanup_designate_agent_backend - remove transient data and cache
+
+# Save trace setting
+DP_AGENT_MSDNS_XTRACE=$(set +o | grep xtrace)
+set +o xtrace
+
+# Defaults
+# --------
+
+DESIGNATE_MSDNS_MASTERS=${DESIGNATE_MSDNS_MASTERS:-"$DESIGNATE_SERVICE_HOST:$DESIGNATE_SERVICE_PORT_MDNS"}
+DESIGNATE_MSDNS_HOST_IP=${DESIGNATE_MSDNS_HOST_IP:-}
+DESIGNATE_MSDNS_HOST_PORT=${DESIGNATE_MSDNS_HOST_PORT:-}
+
+# Sanity Checks
+# -------------
+if [ -z "$DESIGNATE_MSDNS_MASTERS" ]; then
+ die $LINENO "You must configure DESIGNATE_MSDNS_MASTERS"
+fi
+
+if [ -z "$DESIGNATE_MSDNS_HOST_IP" ]; then
+ die $LINENO "You must configure DESIGNATE_MSDNS_HOST_IP with the IP of the MS DNS host"
+fi
+
+if [ -z "$DESIGNATE_MSDNS_HOST_PORT" ]; then
+ die $LINENO "You must configure DESIGNATE_MSDNS_HOST_PORT with the PORT of the MS DNS host"
+fi
+
+if [ "$DESIGNATE_SERVICE_PORT_MDNS" != "53" ]; then
+ die $LINENO "Microsoft DNS requires DESIGNATE_SERVICE_PORT_MDNS to be set to '53'"
+fi
+
+# Entry Points
+# ------------
+
+# install_designate_agent_backend - install any external requirements
+function install_designate_agent_backend {
+ :
+}
+
+# configure_designate_agent_backend - make configuration changes, including those to other services
+function configure_designate_agent_backend {
+ # Generate Designate pool.yaml file
+ sudo tee $DESIGNATE_CONF_DIR/pools.yaml > /dev/null <<EOF
+---
+- name: default
+ description: DevStack MSDNS Pool
+ attributes: {}
+
+ ns_records:
+ - hostname: $DESIGNATE_DEFAULT_NS_RECORD
+ priority: 1
+
+ nameservers:
+ - host: $DESIGNATE_MSDNS_HOST_IP
+ port: $DESIGNATE_MSDNS_HOST_PORT
+
+ targets:
+ - type: agent
+ description: MSDNS Agent Instance
+
+ masters:
+ - host: $DESIGNATE_SERVICE_HOST
+ port: $DESIGNATE_SERVICE_PORT_MDNS
+
+ options:
+ host: $DESIGNATE_MSDNS_HOST_IP
+ port: $DESIGNATE_MSDNS_HOST_PORT
+EOF
+
+ echo "# Sample Config for Windows Agent service" | sudo tee ${DESIGNATE_CONF}.win
+ echo "# This file should be copied to the Windows DNS server and used as the Designate config file" | sudo tee -a ${DESIGNATE_CONF}.win
+ # Configure Agent Settings
+ iniset ${DESIGNATE_CONF}.win service:agent backend_driver $DESIGNATE_AGENT_BACKEND_DRIVER
+ iniset ${DESIGNATE_CONF}.win service:agent host $DESIGNATE_MSDNS_HOST_IP
+ iniset ${DESIGNATE_CONF}.win service:agent port $DESIGNATE_MSDNS_HOST_PORT
+ iniset ${DESIGNATE_CONF}.win service:agent masters "$DESIGNATE_MSDNS_MASTERS"
+}
+
+# init_designate_agent_backend - initialize databases, etc.
+function init_designate_agent_backend {
+ :
+}
+
+# start_designate_agent_backend - start any external services
+function start_designate_agent_backend {
+ :
+}
+
+# stop_designate_agent_backend - stop any external services
+function stop_designate_agent_backend {
+ :
+}
+
+# cleanup_designate_agent_backend - remove transient data and cache
+function cleanup_designate_agent_backend {
+ :
+}
+
+# Restore xtrace
+$DP_AGENT_MSDNS_XTRACE
diff --git a/doc/source/backends/msdns_agent.rst b/doc/source/backends/msdns_agent.rst
new file mode 100644
index 00000000..3f2976e6
--- /dev/null
+++ b/doc/source/backends/msdns_agent.rst
@@ -0,0 +1,134 @@
+..
+ Copyright 2016 Cloudbase Solutions Srl
+
+ Author: Alin Balutoiu <abalutoiu@cloudbasesolutions.com>
+
+ Licensed under the Apache License, Version 2.0 (the "License"); you may
+ not use this file except in compliance with the License. You may obtain
+ a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ License for the specific language governing permissions and limitations
+ under the License.
+
+MSDNS Agent Backend
+*******************
+
+MSDNS User Documentation
+========================
+
+This page documents using the MSDNS Agent backend.
+
+The agent runs on the Windows host where the Microsoft DNS Server feature
+is installed. It receives DNS messages from Mini DNS using private
+DNS OPCODEs and classes and creates or deletes zones using WMI calls.
+
+It also instructs MSDNS to request AXFR from MiniDNS when a zone is created
+or updated.
+
+`Microsoft DNS documentation for managing DNS zones
+<https://msdn.microsoft.com/en-us/library/windows/desktop/ms682757.aspx>`_
+
+Setting up the Microsoft DNS server on Windows Server
+-----------------------------------------------------
+
+The DNS Server role can be installed on the system by following the
+documentation available here:
+`How to install the DNS Server role
+<https://technet.microsoft.com/en-us/library/cc725925.aspx>`_
+
+Configuring MSDNS
+-----------------
+
+Assuming the DNS Server role has been installed on the system, follow the
+next steps to complete the configuration.
+
+These steps are for the Windows host which will run the designate agent.
+Make sure that Python 2.7 or Python 3.4 is installed on the system already.
+
+To install Designate, clone the repository from https://github.com/openstack/designate
+and do a pip install. Example:
+
+.. code-block:: powershell
+
+ 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:: powershell
+
+ copy C:\\etc\\designate\\designate.conf.sample C:\\etc\\designate\\designate.conf
+
+
+Configure the "service.agent" and "backend.agent.msdns" sections in
+C:\\etc\\designate\\designate.conf
+
+Look in C:\\etc\\designate\\designate.conf.example for more complete examples.
+
+.. code-block:: ini
+
+ [service:agent]
+ backend_driver = msdns
+ # Place here the MiniDNS ipaddr and port (no the agent itself)
+ masters = <MiniDNS IP addr>:53
+
+Ensure that "policy_file" under the [default] section is set:
+
+.. code-block:: ini
+
+ policy_file = C:\\etc\\designate\\policy.json
+
+Start the designate agent using (Python 2.7 was installed in the default location C:\\Python27):
+
+.. code-block:: powershell
+
+ C:\\Python27\\Scripts\\designate-agent.exe --config-file 'C:\\etc\\designate\\designate.conf'
+
+You should see log messages similar to:
+
+.. code-block:: powershell
+
+ 2016-06-22 02:00:47.177 3436 INFO designate.backend.agent_backend.impl_msdns [-] Started msdns backend
+ 2016-06-22 02:00:47.177 3436 INFO designate.service [-] _handle_tcp thread started
+ 2016-06-22 02:00:47.177 3436 INFO designate.service [-] _handle_udp thread started
+
+
+The following steps are for the system running the Designate controller.
+
+Make sure to set the mDNS port to 53 in the ``[service:mdns]`` section.
+MS DNS does not support Masters that are on any port other than 53.
+
+Create an agent pool:
+
+.. code-block:: bash
+
+ # Fetch the existing pool(s) if needed or start from scratch
+ designate-manage pool generate_file --file /tmp/pool.yaml
+ # Edit the file (see below) and reload it as:
+ designate-manage pool update --file /tmp/pool.yaml
+
+The "targets" section in pool.yaml should look like:
+
+.. code-block:: ini
+
+ targets:
+ - description: Microsoft DNS agent
+ masters:
+ - host: <MiniDNS IP addr>
+ port: 53
+ options: {}
+ options:
+ - host: <Agent IP addr>
+ port: 5358
+ type: agent
diff --git a/doc/source/sourcedoc/backend.rst b/doc/source/sourcedoc/backend.rst
index 4c6594cf..abdc9df1 100644
--- a/doc/source/sourcedoc/backend.rst
+++ b/doc/source/sourcedoc/backend.rst
@@ -106,3 +106,13 @@ Agent Backend Djbdns
: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/support-matrix.ini b/doc/source/support-matrix.ini
index da5c3fa9..92c7466b 100644
--- a/doc/source/support-matrix.ini
+++ b/doc/source/support-matrix.ini
@@ -56,6 +56,8 @@ 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]
@@ -95,6 +97,9 @@ maintainers=Infoblox OpenStack Team <openstack-maintainer@infoblox.com>
[backends.backend-impl-denominator]
type=agent
+[backends.backend-impl-msdns-agent]
+type=agent
+
[grades]
valid-grades=integrated,master-compatible,release-compatible,untested,failing,known-broken,experimental
diff --git a/etc/designate/designate.conf.sample b/etc/designate/designate.conf.sample
index 6870f8fc..7ad60f15 100644
--- a/etc/designate/designate.conf.sample
+++ b/etc/designate/designate.conf.sample
@@ -492,6 +492,9 @@ debug = False
#confdir_path = /etc/gdnsd
#query_destination = 127.0.0.1
+[backend:agent:msdns]
+#query_destination = 127.0.0.1
+
########################
## Library Configuration
########################
diff --git a/requirements.txt b/requirements.txt
index 722fdd24..96031f99 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -47,3 +47,4 @@ Werkzeug>=0.7 # BSD License
python-memcached>=1.56 # PSF
tooz>=1.28.0 # Apache-2.0
debtcollector>=1.2.0 # Apache-2.0
+os-win>=0.2.3 # Apache-2.0
diff --git a/setup.cfg b/setup.cfg
index c1db2076..cd7512f5 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -98,6 +98,7 @@ designate.backend.agent_backend =
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