Browse Source

[1454] Loop to listen

Michal 'vorner' Vaner 13 years ago
parent
commit
ba9038e23c
2 changed files with 68 additions and 2 deletions
  1. 14 1
      src/bin/ddns/ddns.py.in
  2. 54 1
      src/bin/ddns/tests/ddns_test.py

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

@@ -23,6 +23,7 @@ from isc.dns import *
 from isc.config.ccsession import *
 from isc.cc import SessionError, SessionTimeout
 import isc.util.process
+import select
 
 from isc.log_messages.ddns_messages import *
 
@@ -131,13 +132,25 @@ class DDNSServer:
         This loops waiting for events until self.shutdown() has been called.
         '''
         logger.info(DDNS_RUNNING)
+        cc_fileno = self._cc.get_socket().fileno()
+        listen_fileno = self._listen_socket.fileno()
         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)
+
+            # TODO: Catch select exceptions, like EINTR
+            (reads, writes, exceptions) = \
+                select.select([cc_fileno, listen_fileno], [], [])
+            for fileno in reads:
+                if fileno == cc_fileno:
+                    self._cc.check_command(False)
+                elif fileno == listen_fileno:
+                    self.accept()
+                else:
+                    pass # TODO
         self.shutdown_cleanup()
         logger.info(DDNS_STOPPED)
 

+ 54 - 1
src/bin/ddns/tests/ddns_test.py

@@ -19,6 +19,16 @@ import unittest
 import isc
 import ddns
 import isc.config
+import select
+
+class FakeSocket:
+    """
+    A fake socket. It only provides a file number.
+    """
+    def __init__(self, fileno):
+        self.__fileno = fileno
+    def fileno(self):
+        return self.__fileno
 
 class MyCCSession(isc.config.ConfigData):
     '''Fake session with minimal interface compliance'''
@@ -32,6 +42,12 @@ class MyCCSession(isc.config.ConfigData):
         '''Called by DDNSServer initialization, but not used in tests'''
         self._started = True
 
+    def get_socket(self):
+        """
+        Used to get the file number for select.
+        """
+        return FakeSocket(1)
+
 class MyDDNSServer():
     '''Fake DDNS server used to test the main() function'''
     def __init__(self):
@@ -64,6 +80,9 @@ class TestDDNSServer(unittest.TestCase):
         self.assertFalse(cc_session._started)
         self.ddns_server = ddns.DDNSServer(cc_session)
         self.assertTrue(cc_session._started)
+        self.__select_expected = None
+        self.__select_answer = None
+        self.__hook_called = False
 
     def test_config_handler(self):
         # Config handler does not do anything yet, but should at least
@@ -93,6 +112,41 @@ class TestDDNSServer(unittest.TestCase):
         signal_handler(None, None)
         self.assertTrue(self.ddns_server._shutdown)
 
+    def __select(self, reads, writes, exceptions, timeout=None):
+        """
+        A fake select. It checks it was called with the correct parameters and
+        returns a preset answer.
+        """
+        self.assertEqual(self.__select_expected, (reads, writes, exceptions,
+                                                  timeout))
+        answer = self.__select_answer
+        self.__select_answer = None
+        self.ddns_server._shutdown = True
+        return answer
+
+    def __hook(self):
+        """
+        A hook that can be installed to any unary function and see if it was
+        really called.
+        """
+        self.__hook_called = True
+
+    def test_accept_called(self):
+        """
+        Test we call the accept function when a new connection comes.
+        """
+        self.ddns_server._listen_socket = FakeSocket(2)
+        ddns.select.select = self.__select
+        self.ddns_server.accept = self.__hook
+        self.__select_expected = ([1, 2], [], [], None)
+        self.__select_answer = ([2], [], [])
+        self.ddns_server.run()
+        self.assertTrue(self.ddns_server._shutdown)
+        # The answer got used
+        self.assertIsNone(self.__select_answer)
+        self.assertTrue(self.__hook_called)
+        ddns.select.select = select.select
+
 class TestMain(unittest.TestCase):
     def setUp(self):
         self._server = MyDDNSServer()
@@ -135,7 +189,6 @@ class TestMain(unittest.TestCase):
         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()