Browse Source

[1978] have ddns notify auth of an update if necessary.

JINMEI Tatuya 13 years ago
parent
commit
81239a632d
2 changed files with 68 additions and 6 deletions
  1. 9 0
      src/bin/ddns/ddns.py.in
  2. 59 6
      src/bin/ddns/tests/ddns_test.py

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

@@ -31,6 +31,7 @@ import isc.util.cio.socketsession
 from isc.notify.notify_out import ZONE_NEW_DATA_READY_CMD
 from isc.notify.notify_out import ZONE_NEW_DATA_READY_CMD
 import isc.server_common.tsig_keyring
 import isc.server_common.tsig_keyring
 from isc.datasrc import DataSourceClient
 from isc.datasrc import DataSourceClient
+from isc.server_common.auth_command import auth_loadzone_command
 import select
 import select
 import errno
 import errno
 
 
@@ -82,6 +83,7 @@ isc.util.process.rename()
 
 
 # Cooperating modules
 # Cooperating modules
 XFROUT_MODULE_NAME = 'Xfrout'
 XFROUT_MODULE_NAME = 'Xfrout'
+AUTH_MODULE_NAME = 'Auth'
 
 
 class DDNSConfigError(Exception):
 class DDNSConfigError(Exception):
     '''An exception indicating an error in updating ddns configuration.
     '''An exception indicating an error in updating ddns configuration.
@@ -380,6 +382,7 @@ class DDNSServer:
         ret = self.__send_response(sock, self.__response_renderer.get_data(),
         ret = self.__send_response(sock, self.__response_renderer.get_data(),
                                    remote_addr)
                                    remote_addr)
         if result == isc.ddns.session.UPDATE_SUCCESS:
         if result == isc.ddns.session.UPDATE_SUCCESS:
+            self.__notify_auth(zname, zclass)
             self.__notify_xfrout(zname, zclass)
             self.__notify_xfrout(zname, zclass)
         return ret
         return ret
 
 
@@ -409,6 +412,12 @@ class DDNSServer:
 
 
         return True
         return True
 
 
+    def __notify_auth(self, zname, zclass):
+        '''Notify auth of the update, if necessary.'''
+        msg = auth_loadzone_command(self._cc, zname, zclass)
+        if msg is not None:
+            self.__notify_update(AUTH_MODULE_NAME, msg, zname, zclass)
+
     def __notify_xfrout(self, zname, zclass):
     def __notify_xfrout(self, zname, zclass):
         '''Notify xfrout of the update.'''
         '''Notify xfrout of the update.'''
         param = {'zone_name': zname.to_text(), 'zone_class': zclass.to_text()}
         param = {'zone_name': zname.to_text(), 'zone_class': zclass.to_text()}

+ 59 - 6
src/bin/ddns/tests/ddns_test.py

@@ -161,6 +161,8 @@ class MyCCSession(isc.config.ConfigData):
         self._stopped = False
         self._stopped = False
         # Used as the return value of get_remote_config_value.  Customizable.
         # Used as the return value of get_remote_config_value.  Customizable.
         self.auth_db_file = READ_ZONE_DB_FILE
         self.auth_db_file = READ_ZONE_DB_FILE
+        # Used as the return value of get_remote_config_value.  Customizable.
+        self.auth_datasources = None
         # faked cc channel, providing group_send/recvmsg itself.  The following
         # faked cc channel, providing group_send/recvmsg itself.  The following
         # attributes are for inspection/customization in tests.
         # attributes are for inspection/customization in tests.
         self._session = self
         self._session = self
@@ -190,6 +192,11 @@ class MyCCSession(isc.config.ConfigData):
     def get_remote_config_value(self, module_name, item):
     def get_remote_config_value(self, module_name, item):
         if module_name == "Auth" and item == "database_file":
         if module_name == "Auth" and item == "database_file":
             return self.auth_db_file, False
             return self.auth_db_file, False
+        if module_name == "Auth" and item == "datasources":
+            if self.auth_datasources is None:
+                return [], True # default
+            else:
+                return self.auth_datasources, False
 
 
     def group_sendmsg(self, msg, group):
     def group_sendmsg(self, msg, group):
         # remember the passed parameter, and return dummy sequence
         # remember the passed parameter, and return dummy sequence
@@ -745,20 +752,42 @@ class TestDDNSSession(unittest.TestCase):
         num_rrsets = len(self.__req_message.get_section(SECTION_PREREQUISITE))
         num_rrsets = len(self.__req_message.get_section(SECTION_PREREQUISITE))
         self.assertEqual(2, num_rrsets)
         self.assertEqual(2, num_rrsets)
 
 
-    def check_session_msg(self, result, expect_recv=1):
+    def check_session_msg(self, result, expect_recv=1, notify_auth=False):
         '''Check post update communication with other modules.'''
         '''Check post update communication with other modules.'''
         # iff the update succeeds, b10-ddns should tell interested other
         # iff the update succeeds, b10-ddns should tell interested other
-        # modules the information about the update zone in the form of
+        # modules the information about the update zone.  Possible modules
+        # are xfrout and auth: for xfrout, the message format should be:
         # {'command': ['notify', {'zone_name': <updated_zone_name>,
         # {'command': ['notify', {'zone_name': <updated_zone_name>,
         #                         'zone_class', <updated_zone_class>}]}
         #                         'zone_class', <updated_zone_class>}]}
+        # for auth, it should be:
+        # {'command': ['loadzone', {'origin': <updated_zone_name>,
+        #                           'class', <updated_zone_class>,
+        #                           'datasrc', <datasrc type, should be
+        #                                       "memory" in practice>}]}
         # and expect an answer by calling group_recvmsg().
         # and expect an answer by calling group_recvmsg().
         #
         #
         # expect_recv indicates the expected number of calls to
         # expect_recv indicates the expected number of calls to
-        # group_recvmsg(), which is normally 1, but can be 0 if send fails.
+        # group_recvmsg(), which is normally 1, but can be 0 if send fails;
+        # if the message is to be sent
         if result == UPDATE_SUCCESS:
         if result == UPDATE_SUCCESS:
-            self.assertEqual(1, len(self.__cc_session._sent_msg))
+            expected_sentmsg = 2 if notify_auth else 1
+            self.assertEqual(expected_sentmsg,
+                             len(self.__cc_session._sent_msg))
             self.assertEqual(expect_recv, self.__cc_session._recvmsg_called)
             self.assertEqual(expect_recv, self.__cc_session._recvmsg_called)
-            sent_msg, sent_group = self.__cc_session._sent_msg[0]
+            msg_cnt = 0
+            if notify_auth:
+                sent_msg, sent_group = self.__cc_session._sent_msg[msg_cnt]
+                sent_cmd = sent_msg['command']
+                self.assertEqual('Auth', sent_group)
+                self.assertEqual('loadzone', sent_cmd[0])
+                self.assertEqual(3, len(sent_cmd[1]))
+                self.assertEqual(TEST_ZONE_NAME.to_text(),
+                                 sent_cmd[1]['origin'])
+                self.assertEqual(TEST_RRCLASS.to_text(),
+                                 sent_cmd[1]['class'])
+                self.assertEqual('memory', sent_cmd[1]['datasrc'])
+                msg_cnt += 1
+            sent_msg, sent_group = self.__cc_session._sent_msg[msg_cnt]
             sent_cmd = sent_msg['command']
             sent_cmd = sent_msg['command']
             self.assertEqual('Xfrout', sent_group)
             self.assertEqual('Xfrout', sent_group)
             self.assertEqual('notify', sent_cmd[0])
             self.assertEqual('notify', sent_cmd[0])
@@ -815,8 +844,32 @@ class TestDDNSSession(unittest.TestCase):
         self.__cc_session._sendmsg_exception = RuntimeError('unexpected')
         self.__cc_session._sendmsg_exception = RuntimeError('unexpected')
         self.assertRaises(RuntimeError, self.check_session)
         self.assertRaises(RuntimeError, self.check_session)
 
 
+    def test_session_msg_for_auth(self):
+        '''Test post update communication with other modules including Auth.'''
+        # Let the CC session return in-memory config with sqlite3 backend.
+        # (The default case was covered by other tests.)
+        self.__cc_session.auth_datasources = \
+            [{'type': 'memory', 'class': 'IN', 'zones': [
+                    {'origin': TEST_ZONE_NAME_STR, 'filetype': 'sqlite3'}]}]
+        self.check_session()
+        self.check_session_msg(UPDATE_SUCCESS, expect_recv=2, notify_auth=True)
+
+        # Let sendmsg() raise an exception.  The first exception shouldn't
+        # stop sending the second message.  There's just no recv calls.
+        self.__cc_session.clear_msg()
+        self.__cc_session._sendmsg_exception = SessionError('send error')
+        self.check_session()
+        self.check_session_msg(UPDATE_SUCCESS, expect_recv=0, notify_auth=True)
+
+        # Likewise, in the case recvmsg() raises (and there should be recv
+        # calls in this case)
+        self.__cc_session.clear_msg()
+        self.__cc_session._recvmsg_exception = SessionError('recv error')
+        self.check_session()
+        self.check_session_msg(UPDATE_SUCCESS, expect_recv=2, notify_auth=True)
+
     def test_session_with_config(self):
     def test_session_with_config(self):
-        '''Check a session with more relistic config setups
+        '''Check a session with more realistic config setups.
 
 
         We don't have to explore various cases in detail in this test.
         We don't have to explore various cases in detail in this test.
         We're just checking if the expected configured objects are passed
         We're just checking if the expected configured objects are passed