Browse Source

[1451] addressed review comments

Jelte Jansen 13 years ago
parent
commit
ae488cc48d

+ 1 - 1
src/bin/ddns/b10-ddns.8

@@ -68,7 +68,7 @@ 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_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 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

+ 2 - 2
src/bin/ddns/b10-ddns.xml

@@ -106,8 +106,8 @@
     <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_acls, which itself
-      is a list of ACLs that define update permissions.
+      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.
     </para>
 

+ 26 - 14
src/bin/ddns/ddns.py.in

@@ -63,15 +63,6 @@ class DDNSSession:
 
     def __init__(self):
         '''Initialize a DDNS Session'''
-        self._handle()
-
-    def _handle(self):
-        '''
-        Handle a DDNS update.
-        This should be called from the initializer, and should contain the
-        logic for doing the checks, handling the update, and responding with
-        the result.
-        '''
         pass
 
 class DDNSServer:
@@ -120,6 +111,7 @@ class DDNSServer:
         so the main loop in run() stops.
         '''
         self._shutdown = True
+        logger.info(DDNS_SHUTDOWN)
 
     def run(self):
         '''
@@ -128,7 +120,13 @@ class DDNSServer:
         '''
         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)
+        logger.info(DDNS_STOPPED)
 
 def create_signal_handler(ddns_server):
     '''
@@ -142,8 +140,7 @@ def create_signal_handler(ddns_server):
         here, the actual signal is not checked and the server is simply shut
         down.
         '''
-        if ddns_server is not None:
-            ddns_server.shutdown()
+        ddns_server.shutdown()
     return signal_handler
 
 def set_signal_handler(signal_handler):
@@ -160,7 +157,17 @@ def set_cmd_options(parser):
     parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
             help="display more about what is going on")
 
-if '__main__' == __name__:
+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)
@@ -168,8 +175,8 @@ if '__main__' == __name__:
         if options.verbose:
             print("[b10-ddns] Warning: -v verbose option is ignored at this point.")
 
-        ddns_server = DDNSServer()
-        logger.debug(10, DDNS_STOPPED_BY_KEYBOARD)
+        if ddns_server is None:
+            ddns_server = DDNSServer()
         set_signal_handler(create_signal_handler(ddns_server))
         ddns_server.run()
     except KeyboardInterrupt:
@@ -182,3 +189,8 @@ if '__main__' == __name__:
         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()

+ 3 - 3
src/bin/ddns/ddns.spec

@@ -12,12 +12,12 @@
           "item_type": "map",
           "item_optional": true,
           "item_default": {
-            "update_acls": [{"action": "ACCEPT", "from": "127.0.0.1"},
-                            {"action": "ACCEPT", "from": "::1"}]
+            "update_acl": [{"action": "ACCEPT", "from": "127.0.0.1"},
+                           {"action": "ACCEPT", "from": "::1"}]
           },
           "map_item_spec": [
             {
-              "item_name": "update_acls",
+              "item_name": "update_acl",
               "item_type": "list",
               "item_optional": false,
               "list_item_spec": {

+ 23 - 5
src/bin/ddns/ddns_messages.mes

@@ -15,9 +15,13 @@
 # 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 daemon is not running.
+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
@@ -36,13 +40,27 @@ 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 daemon received a shutdown command from the command channel
+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 daemon has successfully started and is now ready to receive commands
+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 daemon. The
-daemon will now shut 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.

+ 0 - 27
src/bin/ddns/tests/ddns_test.in

@@ -1,27 +0,0 @@
-#! /bin/sh
-
-# 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.
-
-PYTHON_EXEC=${PYTHON_EXEC:-@PYTHON@}
-export PYTHON_EXEC
-
-TEST_PATH=@abs_top_srcdir@/src/bin/ddns/tests
-PYTHONPATH=@abs_top_srcdir@/src/bin/ddns:@abs_top_srcdir@/src/lib/python
-export PYTHONPATH
-
-cd ${TEST_PATH}
-exec ${PYTHON_EXEC} -O ddns_test.py $*
-

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

@@ -32,6 +32,32 @@ class MyCCSession(isc.config.ConfigData):
         '''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()
@@ -67,6 +93,65 @@ class TestDDNSServer(unittest.TestCase):
         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 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)
+
+        # Should technically not be necessary, but reset server to be sure
+        self._server.reset()
+        self.assertFalse(self._server.exception_raised)
+        self._server.set_exception(isc.cc.SessionError("error"))
+        ddns.main(self._server)
+        self.assertTrue(self._server.exception_raised)
+
+        self._server.reset()
+        self.assertFalse(self._server.exception_raised)
+        self._server.set_exception(isc.config.ModuleCCSessionError("error"))
+        ddns.main(self._server)
+        self.assertTrue(self._server.exception_raised)
+
+        self._server.reset()
+        self.assertFalse(self._server.exception_raised)
+        self._server.set_exception(ddns.DDNSConfigError("error"))
+        ddns.main(self._server)
+        self.assertTrue(self._server.exception_raised)
+
+        self._server.reset()
+        self.assertFalse(self._server.exception_raised)
+        self._server.set_exception(isc.cc.SessionTimeout("error"))
+        ddns.main(self._server)
+        self.assertTrue(self._server.exception_raised)
+
+        self._server.reset()
+        self.assertFalse(self._server.exception_raised)
+        self._server.set_exception(Exception("error"))
+        ddns.main(self._server)
+        self.assertTrue(self._server.exception_raised)
+
+        # 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()