Browse Source

[master] Merge branch 'trac1451'

Conflicts:
	src/bin/Makefile.am
Jelte Jansen 13 years ago
parent
commit
b0d0bf39fb

+ 3 - 0
configure.ac

@@ -890,6 +890,8 @@ AC_CONFIG_FILES([Makefile
                  src/bin/auth/Makefile
                  src/bin/auth/tests/Makefile
                  src/bin/auth/benchmarks/Makefile
+                 src/bin/ddns/Makefile
+                 src/bin/ddns/tests/Makefile
                  src/bin/dhcp6/Makefile
                  src/bin/dhcp6/tests/Makefile
 		 src/bin/dhcp4/Makefile
@@ -1002,6 +1004,7 @@ AC_OUTPUT([doc/version.ent
            src/bin/cmdctl/run_b10-cmdctl.sh
            src/bin/cmdctl/tests/cmdctl_test
            src/bin/cmdctl/cmdctl.spec.pre
+           src/bin/ddns/ddns.py
            src/bin/xfrin/tests/xfrin_test
            src/bin/xfrin/xfrin.py
            src/bin/xfrin/run_b10-xfrin.sh

+ 2 - 2
src/bin/Makefile.am

@@ -1,4 +1,4 @@
-SUBDIRS = bind10 bindctl cfgmgr loadzone msgq host cmdctl auth xfrin xfrout \
-	usermgr zonemgr stats tests resolver sockcreator dhcp6 dhcp4
+SUBDIRS = bind10 bindctl cfgmgr ddns loadzone msgq host cmdctl auth xfrin \
+	xfrout usermgr zonemgr stats tests resolver sockcreator dhcp4 dhcp6
 
 check-recursive: all-recursive

+ 1 - 1
src/bin/bind10/run_bind10.sh.in

@@ -20,7 +20,7 @@ export PYTHON_EXEC
 
 BIND10_PATH=@abs_top_builddir@/src/bin/bind10
 
-PATH=@abs_top_builddir@/src/bin/msgq:@abs_top_builddir@/src/bin/auth:@abs_top_builddir@/src/bin/resolver:@abs_top_builddir@/src/bin/cfgmgr:@abs_top_builddir@/src/bin/cmdctl:@abs_top_builddir@/src/bin/stats:@abs_top_builddir@/src/bin/xfrin:@abs_top_builddir@/src/bin/xfrout:@abs_top_builddir@/src/bin/zonemgr:@abs_top_builddir@/src/bin/dhcp6:@abs_top_builddir@/src/bin/sockcreator:$PATH
+PATH=@abs_top_builddir@/src/bin/msgq:@abs_top_builddir@/src/bin/auth:@abs_top_builddir@/src/bin/resolver:@abs_top_builddir@/src/bin/cfgmgr:@abs_top_builddir@/src/bin/cmdctl:@abs_top_builddir@/src/bin/stats:@abs_top_builddir@/src/bin/xfrin:@abs_top_builddir@/src/bin/xfrout:@abs_top_builddir@/src/bin/zonemgr:@abs_top_builddir@/src/bin/ddns:@abs_top_builddir@/src/bin/dhcp6:@abs_top_builddir@/src/bin/sockcreator:$PATH
 export PATH
 
 PYTHONPATH=@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/xfr/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/python/isc/config:@abs_top_builddir@/src/lib/python/isc/acl/.libs:@abs_top_builddir@/src/lib/python/isc/datasrc/.libs

+ 42 - 0
src/bin/ddns/Makefile.am

@@ -0,0 +1,42 @@
+SUBDIRS = . tests
+
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+
+pkglibexec_SCRIPTS = b10-ddns
+
+b10_ddnsdir = $(pkgdatadir)
+b10_ddns_DATA = ddns.spec
+
+nodist_pylogmessage_PYTHON = $(PYTHON_LOGMSGPKG_DIR)/work/ddns_messages.py
+pylogmessagedir = $(pyexecdir)/isc/log_messages/
+
+CLEANFILES = b10-ddns ddns.pyc
+CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/ddns_messages.py
+CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/ddns_messages.pyc
+
+EXTRA_DIST =  ddns_messages.mes ddns.spec
+man_MANS = b10-ddns.8
+EXTRA_DIST += $(man_MANS) b10-ddns.xml
+
+if ENABLE_MAN
+
+b10-ddns.8: b10-ddns.xml
+	xsltproc --novalid --xinclude --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $(srcdir)/b10-ddns.xml
+
+endif
+
+# Define rule to build logging source files from message file
+$(PYTHON_LOGMSGPKG_DIR)/work/ddns_messages.py : ddns_messages.mes
+	$(top_builddir)/src/lib/log/compiler/message \
+	-d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/ddns_messages.mes
+
+# this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
+b10-ddns: ddns.py $(PYTHON_LOGMSGPKG_DIR)/work/ddns_messages.py
+	$(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \
+	       -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" ddns.py >$@
+	chmod a+x $@
+
+CLEANDIRS = __pycache__
+
+clean-local:
+	rm -rf $(CLEANDIRS)

+ 97 - 0
src/bin/ddns/b10-ddns.8

@@ -0,0 +1,97 @@
+'\" t
+.\"     Title: b10-ddns
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
+.\"      Date: December 9, 2011
+.\"    Manual: BIND10
+.\"    Source: BIND10
+.\"  Language: English
+.\"
+.TH "B10\-DDNS" "8" "December 9, 2011" "BIND10" "BIND10"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+b10-ddns \- Dynamic DNS update service
+.SH "SYNOPSIS"
+.HP \w'\fBb10\-ddns\fR\ 'u
+\fBb10\-ddns\fR [\fB\-v\fR | \fB\-\-verbose\fR]
+.SH "DESCRIPTION"
+.PP
+The
+\fBb10\-ddns\fR
+daemon provides the BIND 10 Dynamic Update (DDNS) service, as specified in RFC 2136\&. Normally it is started by the
+\fBbind10\fR(8)
+boss process\&. When the
+\fBb10\-auth\fR
+DNS server receives a DDNS update,
+\fBb10\-ddns\fR
+updates the zone in the BIND 10 zone data store\&.
+.PP
+This daemon communicates with BIND 10 over a
+\fBb10-msgq\fR(8)
+C\-Channel connection\&. If this connection is not established,
+\fBb10\-ddns\fR
+will exit\&.
+.PP
+
+\fBb10\-ddns\fR
+receives its configurations from
+\fBb10-cfgmgr\fR(8)\&.
+.SH "ARGUMENTS"
+.PP
+The arguments are as follows:
+.PP
+\fB\-v\fR, \fB\-\-verbose\fR
+.RS 4
+This value is ignored at this moment, but is provided for compatibility with the bind10 Boss process
+.RE
+.SH "CONFIGURATION AND COMMANDS"
+.PP
+The configurable settings are:
+.PP
+
+\fIzones\fR
+The zones option is a named set of zones that can be updated with DDNS\&. Each entry has one element called update_acl, which is is a list of access control rules that define update permissions\&. By default this is empty; DDNS must be explicitely enabled per zone\&.
+.PP
+The module commands are:
+.PP
+
+\fBshutdown\fR
+Exits
+\fBb10\-ddns\fR\&. (Note that the BIND 10 boss process will restart this service\&.)
+.SH "SEE ALSO"
+.PP
+
+\fBb10-auth\fR(8),
+\fBb10-cfgmgr\fR(8),
+\fBb10-msgq\fR(8),
+\fBb10-xfrin\fR(8),
+\fBb10-xfrout\fR(8),
+\fBbind10\fR(8),
+BIND 10 Guide\&.
+.SH "HISTORY"
+.PP
+The
+\fBb10\-ddns\fR
+daemon was first implemented in December 2011 for the ISC BIND 10 project\&.
+.SH "COPYRIGHT"
+.br
+Copyright \(co 2011 Internet Systems Consortium, Inc. ("ISC")
+.br

+ 161 - 0
src/bin/ddns/b10-ddns.xml

@@ -0,0 +1,161 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+               "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
+	       [<!ENTITY mdash "&#8212;">]>
+<!--
+ - Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+ -
+ - Permission to use, copy, modify, and/or distribute this software for any
+ - purpose with or without fee is hereby granted, provided that the above
+ - copyright notice and this permission notice appear in all copies.
+ -
+ - THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ - AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ - INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ - LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ - OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ - PERFORMANCE OF THIS SOFTWARE.
+-->
+
+<refentry>
+
+  <refentryinfo>
+    <date>December 9, 2011</date>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>b10-ddns</refentrytitle>
+    <manvolnum>8</manvolnum>
+    <refmiscinfo>BIND10</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>b10-ddns</refname>
+    <refpurpose>Dynamic DNS update service</refpurpose>
+  </refnamediv>
+
+  <docinfo>
+    <copyright>
+      <year>2011</year>
+      <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
+    </copyright>
+  </docinfo>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>b10-ddns</command>
+        <group choice="opt">
+          <arg choice="plain"><option>-v</option></arg>
+          <arg choice="plain"><option>--verbose</option></arg>
+        </group>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>DESCRIPTION</title>
+    <para>The <command>b10-ddns</command> daemon provides the BIND 10
+      Dynamic Update (DDNS) service, as specified in RFC 2136.
+      Normally it is started by the
+      <citerefentry><refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+      boss process.
+      When the <command>b10-auth</command> DNS server receives
+      a DDNS update, <command>b10-ddns</command> updates the zone
+      in the BIND 10 zone data store.
+    </para>
+
+    <para>
+      This daemon communicates with BIND 10 over a
+      <citerefentry><refentrytitle>b10-msgq</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+      C-Channel connection.  If this connection is not established,
+      <command>b10-ddns</command> will exit.
+    </para>
+
+    <para>
+     <command>b10-ddns</command> receives its configurations from
+<citerefentry><refentrytitle>b10-cfgmgr</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>ARGUMENTS</title>
+
+    <para>The arguments are as follows:</para>
+
+    <variablelist>
+
+      <varlistentry>
+        <term>
+          <option>-v</option>,
+          <option>--verbose</option>
+        </term>
+        <listitem>
+          <para>
+            This value is ignored at this moment, but is provided for
+            compatibility with the bind10 Boss process
+          </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>CONFIGURATION AND COMMANDS</title>
+    <para>
+      The configurable settings are:
+    </para>
+    <para>
+      <varname>zones</varname>
+      The zones option is a named set of zones that can be updated with
+      DDNS. Each entry has one element called update_acl, which is
+      a list of access control rules that define update permissions.
+      By default this is empty; DDNS must be explicitely enabled per zone.
+    </para>
+
+    <para>
+      The module commands are:
+    </para>
+    <para>
+      <command>shutdown</command> Exits <command>b10-ddns</command>.
+      (Note that the BIND 10 boss process will restart this service.)
+    </para>
+
+  </refsect1>
+
+
+  <refsect1>
+    <title>SEE ALSO</title>
+    <para>
+      <citerefentry>
+        <refentrytitle>b10-auth</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+        <refentrytitle>b10-cfgmgr</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+        <refentrytitle>b10-msgq</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+        <refentrytitle>b10-xfrin</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+        <refentrytitle>b10-xfrout</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+        <refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citetitle>BIND 10 Guide</citetitle>.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>HISTORY</title>
+    <para>
+      The <command>b10-ddns</command> daemon was first implemented
+      in December 2011 for the ISC BIND 10 project.
+    </para>
+  </refsect1>
+</refentry><!--
+ - Local variables:
+ - mode: sgml
+ - End:
+-->

+ 209 - 0
src/bin/ddns/ddns.py.in

@@ -0,0 +1,209 @@
+#!@PYTHON@
+
+# Copyright (C) 2011  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+
+import sys; sys.path.append ('@@PYTHONPATH@@')
+import isc
+import bind10_config
+from isc.dns import *
+from isc.config.ccsession import *
+from isc.cc import SessionError, SessionTimeout
+import isc.util.process
+
+from isc.log_messages.ddns_messages import *
+
+from optparse import OptionParser, OptionValueError
+import os
+import signal
+
+isc.log.init("b10-ddns")
+logger = isc.log.Logger("ddns")
+
+DATA_PATH = bind10_config.DATA_PATH
+if "B10_FROM_SOURCE" in os.environ:
+    DATA_PATH = os.environ['B10_FROM_SOURCE'] + "/src/bin/ddns"
+SPECFILE_LOCATION = DATA_PATH + "/ddns.spec"
+
+
+isc.util.process.rename()
+
+class DDNSConfigError(Exception):
+    '''An exception indicating an error in updating ddns configuration.
+
+    This exception is raised when the ddns process encounters an error in
+    handling configuration updates.  Not all syntax error can be caught
+    at the module-CC layer, so ddns needs to (explicitly or implicitly)
+    validate the given configuration data itself.  When it finds an error
+    it raises this exception (either directly or by converting an exception
+    from other modules) as a unified error in configuration.
+    '''
+    pass
+
+class DDNSSessionError(Exception):
+    '''An exception raised for some unexpected events during a ddns session.
+    '''
+    pass
+
+class DDNSSession:
+    '''Class to handle one DDNS update'''
+
+    def __init__(self):
+        '''Initialize a DDNS Session'''
+        pass
+
+class DDNSServer:
+    def __init__(self, cc_session=None):
+        '''
+        Initialize the DDNS Server.
+        This sets up a ModuleCCSession for the BIND 10 system.
+        Parameters:
+        cc_session: If None (default), a new ModuleCCSession will be set up.
+                    If specified, the given session will be used. This is
+                    mainly used for testing.
+        '''
+        if cc_session is not None:
+            self._cc = cc_session
+        else:
+            self._cc = isc.config.ModuleCCSession(SPECFILE_LOCATION,
+                                                  self.config_handler,
+                                                  self.command_handler)
+
+        self._config_data = self._cc.get_full_config()
+        self._cc.start()
+        self._shutdown = False
+
+    def config_handler(self, new_config):
+        '''Update config data.'''
+        answer = create_answer(0)
+        return answer
+
+    def command_handler(self, cmd, args):
+        '''
+        Handle a CC session command, as sent from bindctl or other
+        BIND 10 modules.
+        '''
+        if cmd == "shutdown":
+            logger.info(DDNS_RECEIVED_SHUTDOWN_COMMAND)
+            self.trigger_shutdown()
+            answer = create_answer(0)
+        else:
+            answer = create_answer(1, "Unknown command: " + str(cmd))
+        return answer
+
+    def trigger_shutdown(self):
+        '''Initiate a shutdown sequence.
+
+        This method is expected to be called in various ways including
+        in the middle of a signal handler, and is designed to be as simple
+        as possible to minimize side effects.  Actual shutdown will take
+        place in a normal control flow.
+
+        '''
+        logger.info(DDNS_SHUTDOWN)
+        self._shutdown = True
+
+    def shutdown_cleanup(self):
+        '''
+        Perform any cleanup that is necessary when shutting down the server.
+        Do NOT call this to initialize shutdown, use trigger_shutdown().
+
+        Currently, it does nothing, but cleanup routines are expected.
+        '''
+        pass
+
+    def run(self):
+        '''
+        Get and process all commands sent from cfgmgr or other modules.
+        This loops waiting for events until self.shutdown() has been called.
+        '''
+        logger.info(DDNS_RUNNING)
+        while not self._shutdown:
+            # We do not catch any exceptions here right now, but this would
+            # be a good place to catch any exceptions that b10-ddns can
+            # recover from. We currently have no exception hierarchy to
+            # make such a distinction easily, but once we do, this would
+            # be the place to catch.
+            self._cc.check_command(False)
+        self.shutdown_cleanup()
+        logger.info(DDNS_STOPPED)
+
+def create_signal_handler(ddns_server):
+    '''
+    This creates a signal_handler for use in set_signal_handler, which
+    shuts down the given DDNSServer (or any object that has a shutdown()
+    method)
+    '''
+    def signal_handler(signal, frame):
+        '''
+        Handler for process signals. Since only signals to shut down are sent
+        here, the actual signal is not checked and the server is simply shut
+        down.
+        '''
+        ddns_server.trigger_shutdown()
+    return signal_handler
+
+def set_signal_handler(signal_handler):
+    '''
+    Sets the signal handler(s).
+    '''
+    signal.signal(signal.SIGTERM, signal_handler)
+    signal.signal(signal.SIGINT, signal_handler)
+
+def set_cmd_options(parser):
+    '''
+    Helper function to set command-line options
+    '''
+    parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
+            help="display more about what is going on")
+
+def main(ddns_server=None):
+    '''
+    The main function.
+    Parameters:
+    ddns_server: If None (default), a DDNSServer object is initialized.
+                 If specified, the given DDNSServer will be used. This is
+                 mainly used for testing.
+    cc_session: If None (default), a new ModuleCCSession will be set up.
+                If specified, the given session will be used. This is
+                mainly used for testing.
+    '''
+    try:
+        parser = OptionParser()
+        set_cmd_options(parser)
+        (options, args) = parser.parse_args()
+        if options.verbose:
+            print("[b10-ddns] Warning: -v verbose option is ignored at this point.")
+
+        if ddns_server is None:
+            ddns_server = DDNSServer()
+        set_signal_handler(create_signal_handler(ddns_server))
+        ddns_server.run()
+    except KeyboardInterrupt:
+        logger.info(DDNS_STOPPED_BY_KEYBOARD)
+    except SessionError as e:
+        logger.error(DDNS_CC_SESSION_ERROR, str(e))
+    except ModuleCCSessionError as e:
+        logger.error(DDNS_MODULECC_SESSION_ERROR, str(e))
+    except DDNSConfigError as e:
+        logger.error(DDNS_CONFIG_ERROR, str(e))
+    except SessionTimeout as e:
+        logger.error(DDNS_CC_SESSION_TIMEOUT_ERROR)
+    except Exception as e:
+        logger.error(DDNS_UNCAUGHT_EXCEPTION, type(e).__name__, str(e))
+
+if '__main__' == __name__:
+    main()

+ 42 - 0
src/bin/ddns/ddns.spec

@@ -0,0 +1,42 @@
+{
+  "module_spec": {
+    "module_name": "DDNS",
+    "config_data": [
+      {
+        "item_name": "zones",
+        "item_type": "named_set",
+        "item_optional": false,
+        "item_default": {},
+        "named_set_item_spec": {
+          "item_name": "entry",
+          "item_type": "map",
+          "item_optional": true,
+          "item_default": {
+            "update_acl": [{"action": "ACCEPT", "from": "127.0.0.1"},
+                           {"action": "ACCEPT", "from": "::1"}]
+          },
+          "map_item_spec": [
+            {
+              "item_name": "update_acl",
+              "item_type": "list",
+              "item_optional": false,
+              "list_item_spec": {
+                "item_name": "acl_element",
+                "item_type": "any",
+                "item_optional": true
+              }
+            }
+          ]
+        }
+      }
+    ],
+    "commands": [
+      {
+        "command_name": "shutdown",
+        "command_description": "Shut down DDNS",
+        "command_args": []
+      }
+    ]
+  }
+}
+

+ 66 - 0
src/bin/ddns/ddns_messages.mes

@@ -0,0 +1,66 @@
+# Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# No namespace declaration - these constants go in the global namespace
+# of the ddns messages python module.
+
+# When you add a message to this file, it is a good idea to run
+# <topsrcdir>/tools/reorder_message_file.py to make sure the
+# messages are in the correct order.
+
+% DDNS_CC_SESSION_ERROR error reading from cc channel: %1
+There was a problem reading from the command and control channel. The
+most likely cause is that the msgq process is not running.
+
+% DDNS_CC_SESSION_TIMEOUT_ERROR timeout waiting for cc response
+There was a problem reading a response from another module over the
+command and control channel. The most likely cause is that the
+configuration manager b10-cfgmgr is not running.
+
+% DDNS_CONFIG_ERROR error found in configuration data: %1
+The ddns process encountered an error when installing the configuration at
+startup time.  Details of the error are included in the log message.
+
+% DDNS_MODULECC_SESSION_ERROR error encountered by configuration/command module: %1
+There was a problem in the lower level module handling configuration and
+control commands.  This could happen for various reasons, but the most likely
+cause is that the configuration database contains a syntax error and ddns
+failed to start at initialization.  A detailed error message from the module
+will also be displayed.
+
+% DDNS_RECEIVED_SHUTDOWN_COMMAND shutdown command received
+The ddns process received a shutdown command from the command channel
+and will now shut down.
+
+% DDNS_RUNNING ddns server is running and listening for updates
+The ddns process has successfully started and is now ready to receive commands
+and updates.
+
+% DDNS_SHUTDOWN ddns server shutting down
+The ddns process is shutting down. It will no longer listen for new commands
+or updates. Any command or update that is being addressed at this moment will
+be completed, after which the process will exit.
+
+% DDNS_STOPPED ddns server has stopped
+The ddns process has successfully stopped and is no longer listening for or
+handling commands or updates, and will now exit.
+
+% DDNS_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down
+There was a keyboard interrupt signal to stop the ddns process. The
+process will now shut down.
+
+% DDNS_UNCAUGHT_EXCEPTION uncaught exception of type %1: %2
+The b10-ddns process encountered an uncaught exception and will now shut
+down. This is indicative of a programming error and should not happen under
+normal circumstances. The exception type and message are printed.

File diff suppressed because it is too large
+ 28 - 0
src/bin/ddns/tests/Makefile.am


+ 142 - 0
src/bin/ddns/tests/ddns_test.py

@@ -0,0 +1,142 @@
+# Copyright (C) 2011  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+'''Tests for the DDNS module'''
+
+import unittest
+import isc
+import ddns
+import isc.config
+
+class MyCCSession(isc.config.ConfigData):
+    '''Fake session with minimal interface compliance'''
+    def __init__(self):
+        module_spec = isc.config.module_spec_from_file(
+            ddns.SPECFILE_LOCATION)
+        isc.config.ConfigData.__init__(self, module_spec)
+        self._started = False
+
+    def start(self):
+        '''Called by DDNSServer initialization, but not used in tests'''
+        self._started = True
+
+class MyDDNSServer():
+    '''Fake DDNS server used to test the main() function'''
+    def __init__(self):
+        self.reset()
+
+    def run(self):
+        '''
+        Fake the run() method of the DDNS server. This will set
+        self._run_called to True.
+        If self._exception is not None, this is raised as an exception
+        '''
+        self.run_called = True
+        if self._exception is not None:
+            self.exception_raised = True
+            raise self._exception
+
+    def set_exception(self, exception):
+        '''Set an exception to be raised when run() is called'''
+        self._exception = exception
+
+    def reset(self):
+        '''(Re)set to initial values'''
+        self.run_called = False
+        self.exception_raised = False
+        self._exception = None
+
+class TestDDNSServer(unittest.TestCase):
+    def setUp(self):
+        cc_session = MyCCSession()
+        self.assertFalse(cc_session._started)
+        self.ddns_server = ddns.DDNSServer(cc_session)
+        self.assertTrue(cc_session._started)
+
+    def test_config_handler(self):
+        # Config handler does not do anything yet, but should at least
+        # return 'ok' for now.
+        new_config = {}
+        answer = self.ddns_server.config_handler(new_config)
+        self.assertEqual((0, None), isc.config.parse_answer(answer))
+
+    def test_shutdown_command(self):
+        '''Test whether the shutdown command works'''
+        self.assertFalse(self.ddns_server._shutdown)
+        answer = self.ddns_server.command_handler('shutdown', None)
+        self.assertEqual((0, None), isc.config.parse_answer(answer))
+        self.assertTrue(self.ddns_server._shutdown)
+
+    def test_command_handler(self):
+        '''Test some commands.'''
+        # this command should not exist
+        answer = self.ddns_server.command_handler('bad_command', None)
+        self.assertEqual((1, 'Unknown command: bad_command'),
+                         isc.config.parse_answer(answer))
+
+    def test_signal_handler(self):
+        '''Test whether signal_handler calls shutdown()'''
+        signal_handler = ddns.create_signal_handler(self.ddns_server)
+        self.assertFalse(self.ddns_server._shutdown)
+        signal_handler(None, None)
+        self.assertTrue(self.ddns_server._shutdown)
+
+class TestMain(unittest.TestCase):
+    def setUp(self):
+        self._server = MyDDNSServer()
+
+    def test_main(self):
+        self.assertFalse(self._server.run_called)
+        ddns.main(self._server)
+        self.assertTrue(self._server.run_called)
+
+    def check_exception(self, ex):
+        '''Common test sequence to see if the given exception is caused.
+        '''
+        # Should technically not be necessary, but reset server to be sure
+        self._server.reset()
+        self.assertFalse(self._server.exception_raised)
+        self._server.set_exception(ex)
+        ddns.main(self._server)
+        self.assertTrue(self._server.exception_raised)
+
+    def test_exceptions(self):
+        '''
+        Test whether exceptions are caught in main()
+        These exceptions should not bubble up.
+        '''
+        self._server.set_exception(KeyboardInterrupt())
+        self.assertFalse(self._server.exception_raised)
+        ddns.main(self._server)
+        self.assertTrue(self._server.exception_raised)
+
+        self.check_exception(isc.cc.SessionError("error"))
+        self.check_exception(isc.config.ModuleCCSessionError("error"))
+        self.check_exception(ddns.DDNSConfigError("error"))
+        self.check_exception(isc.cc.SessionTimeout("error"))
+        self.check_exception(Exception("error"))
+
+        # Add one that is not a subclass of Exception, and hence not
+        # caught. Misuse BaseException for that.
+        self._server.reset()
+        self.assertFalse(self._server.exception_raised)
+        self._server.set_exception(BaseException("error"))
+        self.assertRaises(BaseException, ddns.main, self._server)
+        self.assertTrue(self._server.exception_raised)
+        
+
+if __name__== "__main__":
+    isc.log.resetUnitTestRootLogger()
+    unittest.main()

+ 2 - 0
src/lib/python/isc/log_messages/Makefile.am

@@ -3,6 +3,7 @@ SUBDIRS = work
 EXTRA_DIST = __init__.py
 EXTRA_DIST += bind10_messages.py
 EXTRA_DIST += cmdctl_messages.py
+EXTRA_DIST += ddns_messages.py
 EXTRA_DIST += stats_messages.py
 EXTRA_DIST += stats_httpd_messages.py
 EXTRA_DIST += xfrin_messages.py
@@ -16,6 +17,7 @@ EXTRA_DIST += libxfrin_messages.py
 CLEANFILES = __init__.pyc
 CLEANFILES += bind10_messages.pyc
 CLEANFILES += cmdctl_messages.pyc
+CLEANFILES += ddns_messages.pyc
 CLEANFILES += stats_messages.pyc
 CLEANFILES += stats_httpd_messages.pyc
 CLEANFILES += xfrin_messages.pyc

+ 1 - 0
src/lib/python/isc/log_messages/ddns_messages.py

@@ -0,0 +1 @@
+from work.ddns_messages import *

+ 1 - 1
tests/lettuce/setup_intree_bind10.sh.in

@@ -20,7 +20,7 @@ export PYTHON_EXEC
 
 BIND10_PATH=@abs_top_builddir@/src/bin/bind10
 
-PATH=@abs_top_builddir@/src/bin/bind10:@abs_top_builddir@/src/bin/bindctl:@abs_top_builddir@/src/bin/msgq:@abs_top_builddir@/src/bin/auth:@abs_top_builddir@/src/bin/resolver:@abs_top_builddir@/src/bin/cfgmgr:@abs_top_builddir@/src/bin/cmdctl:@abs_top_builddir@/src/bin/stats:@abs_top_builddir@/src/bin/xfrin:@abs_top_builddir@/src/bin/xfrout:@abs_top_builddir@/src/bin/zonemgr:@abs_top_builddir@/src/bin/dhcp6:@abs_top_builddir@/src/bin/sockcreator:$PATH
+PATH=@abs_top_builddir@/src/bin/bind10:@abs_top_builddir@/src/bin/bindctl:@abs_top_builddir@/src/bin/msgq:@abs_top_builddir@/src/bin/auth:@abs_top_builddir@/src/bin/resolver:@abs_top_builddir@/src/bin/cfgmgr:@abs_top_builddir@/src/bin/cmdctl:@abs_top_builddir@/src/bin/stats:@abs_top_builddir@/src/bin/xfrin:@abs_top_builddir@/src/bin/xfrout:@abs_top_builddir@/src/bin/zonemgr:@abs_top_builddir@/src/bin/ddns:@abs_top_builddir@/src/bin/dhcp6:@abs_top_builddir@/src/bin/sockcreator:$PATH
 export PATH
 
 PYTHONPATH=@abs_top_builddir@/src/bin:@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/xfr/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/python/isc/config:@abs_top_builddir@/src/lib/python/isc/acl/.libs:@abs_top_builddir@/src/lib/python/isc/datasrc/.libs