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
This commit is contained in:
Alin Balutoiu 2016-06-22 09:34:28 +03:00
parent f64d3a358a
commit 95f451dc8a
9 changed files with 505 additions and 0 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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
########################

View File

@ -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

View File

@ -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