diff --git a/src/bin/ddns/b10-ddns.8 b/src/bin/ddns/b10-ddns.8 index f0fbf0191c..d1e05d9f3d 100644 --- a/src/bin/ddns/b10-ddns.8 +++ b/src/bin/ddns/b10-ddns.8 @@ -31,12 +31,12 @@ b10-ddns \- Dynamic DNS update service .SH "SYNOPSIS" .HP \w'\fBb10\-ddns\fR\ 'u -\fBb10\-ddns\fR [\fB\-v\fR] [\fB\-\-verbose\fR] +\fBb10\-ddns\fR [\fB\-v\fR | \fB\-\-verbose\fR] .SH "DESCRIPTION" .PP The \fBb10\-ddns\fR -daemon provides the BIND 10 DDNS update service\&. Normally it is started by the +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 @@ -54,13 +54,21 @@ will exit\&. \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 -\fITODO\fR -TODO +\fIzones\fR +The zones option is a named set of zones that can be updated with DDNS\&. Each entry has one element called update_acls, which itself is a list of ACLs that define update permissions\&. By default this is empty; DDNS must be explicitely enabled per zone\&. .PP The module commands are: .PP diff --git a/src/bin/ddns/b10-ddns.xml b/src/bin/ddns/b10-ddns.xml index efb7667209..73bdbc24d7 100644 --- a/src/bin/ddns/b10-ddns.xml +++ b/src/bin/ddns/b10-ddns.xml @@ -44,15 +44,17 @@ b10-ddns - - + + + + DESCRIPTION The b10-ddns daemon provides the BIND 10 - DDNS update service. + Dynamic Update (DDNS) service, as specified in RFC 2136. Normally it is started by the bind108 boss process. @@ -74,17 +76,41 @@ + + ARGUMENTS + + The arguments are as follows: + + + + + + , + + + + + This value is ignored at this moment, but is provided for + compatibility with the bind10 Boss process + + + + + + CONFIGURATION AND COMMANDS The configurable settings are: - TODO - TODO + zones + The zones option is a named set of zones that can be updated with + DDNS. Each entry has one element called update_acls, which itself + is a list of ACLs that define update permissions. + By default this is empty; DDNS must be explicitely enabled per zone. - The module commands are: diff --git a/src/bin/ddns/ddns.py.in b/src/bin/ddns/ddns.py.in index 97c57c8dff..104d845d82 100755 --- a/src/bin/ddns/ddns.py.in +++ b/src/bin/ddns/ddns.py.in @@ -35,7 +35,7 @@ logger = isc.log.Logger("ddns") DATA_PATH = bind10_config.DATA_PATH if "B10_FROM_BUILD" in os.environ: - DATA_PATH = DATA_PATH + "/src/bin/ddns" + DATA_PATH = os.environ['B10_FROM_BUILD'] + "/src/bin/ddns" SPECFILE_LOCATION = DATA_PATH + "/ddns.spec" @@ -75,14 +75,22 @@ class DDNSSession: pass class DDNSServer: - def __init__(self): + 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. ''' - self._cc = isc.config.ModuleCCSession(SPECFILE_LOCATION, - self.config_handler, - self.command_handler) + 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 @@ -102,7 +110,7 @@ class DDNSServer: self.shutdown() answer = create_answer(0) else: - answer = create_answer(1, "Unknown command:" + str(cmd)) + answer = create_answer(1, "Unknown command: " + str(cmd)) return answer def shutdown(self): @@ -122,18 +130,23 @@ class DDNSServer: while not self._shutdown: self._cc.check_command(False) -ddns_server = None - -def signal_handler(signal, frame): +def create_signal_handler(ddns_server): ''' - 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. + 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) ''' - if ddns_server is not None: - ddns_server.shutdown() + 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. + ''' + if ddns_server is not None: + ddns_server.shutdown() + return signal_handler -def set_signal_handler(): +def set_signal_handler(signal_handler): ''' Sets the signal handler(s). ''' @@ -152,13 +165,15 @@ if '__main__' == __name__: parser = OptionParser() set_cmd_options(parser) (options, args) = parser.parse_args() - VERBOSE_MODE = options.verbose + if options.verbose: + print("[b10-ddns] Warning: -v verbose option is ignored at this point.") ddns_server = DDNSServer() - set_signal_handler() + logger.debug(10, DDNS_STOPPED_BY_KEYBOARD) + set_signal_handler(create_signal_handler(ddns_server)) ddns_server.run() except KeyboardInterrupt: - logger.INFO(DDNS_STOPPED_BY_KEYBOARD) + logger.info(DDNS_STOPPED_BY_KEYBOARD) except SessionError as e: logger.error(DDNS_CC_SESSION_ERROR, str(e)) except ModuleCCSessionError as e: @@ -167,7 +182,3 @@ if '__main__' == __name__: logger.error(DDNS_CONFIG_ERROR, str(e)) except SessionTimeout as e: logger.error(DDNS_CC_SESSION_TIMEOUT_ERROR) - - if ddns_server: - ddns_server.shutdown() - diff --git a/src/bin/ddns/tests/ddns_test.py b/src/bin/ddns/tests/ddns_test.py index 141944ba7c..e18a990a6d 100755 --- a/src/bin/ddns/tests/ddns_test.py +++ b/src/bin/ddns/tests/ddns_test.py @@ -17,10 +17,55 @@ import unittest import isc +import ddns +import isc.config -class TestInitialization(unittest.TestCase): - def test_noop(self): - self.assertTrue(True) +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 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) if __name__== "__main__": isc.log.resetUnitTestRootLogger()