Browse Source

[1938] make sure zonemgr doesn't treat unmatched notify as an error.

it can happen if the server is a primary of the zone; notauth case is
now handled in auth.
JINMEI Tatuya 12 years ago
parent
commit
48054d5a0c
2 changed files with 71 additions and 14 deletions
  1. 44 7
      src/bin/zonemgr/tests/zonemgr_test.py
  2. 27 7
      src/bin/zonemgr/zonemgr.py.in

+ 44 - 7
src/bin/zonemgr/tests/zonemgr_test.py

@@ -314,7 +314,8 @@ class TestZonemgrRefresh(unittest.TestCase):
         sqlite3_ds.get_zone_soa = old_get_zone_soa
 
     def test_zone_handle_notify(self):
-        self.zone_refresh.zone_handle_notify(ZONE_NAME_CLASS1_IN,"127.0.0.1")
+        self.assertTrue(self.zone_refresh.zone_handle_notify(
+                ZONE_NAME_CLASS1_IN, "127.0.0.1"))
         notify_master = self.zone_refresh.\
             _zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["notify_master"]
         self.assertEqual("127.0.0.1", notify_master)
@@ -322,12 +323,13 @@ class TestZonemgrRefresh(unittest.TestCase):
             _zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["next_refresh_time"]
         current_time = time.time()
         self.assertTrue(zone_timeout <= current_time)
-        self.assertRaises(ZonemgrException,
-                          self.zone_refresh.zone_handle_notify,
-                          ZONE_NAME_CLASS3_CH, "127.0.0.1")
-        self.assertRaises(ZonemgrException,
-                          self.zone_refresh.zone_handle_notify,
-                          ZONE_NAME_CLASS3_IN, "127.0.0.1")
+
+        # If the specified zone does not in the configured secondary list,
+        # it should return False.
+        self.assertFalse(self.zone_refresh.zone_handle_notify(
+                ZONE_NAME_CLASS3_CH, "127.0.0.1"))
+        self.assertFalse(self.zone_refresh.zone_handle_notify(
+                ZONE_NAME_CLASS3_IN, "127.0.0.1"))
 
     def test_zone_refresh_success(self):
         soa_rdata = 'a.example.net. root.example.net. 2009073106 1800 900 2419200 21600'
@@ -611,6 +613,19 @@ class TestZonemgrRefresh(unittest.TestCase):
                           config, self.cc_session)
 
 class MyZonemgr(Zonemgr):
+    class DummySocket:
+        """This dummy class simply steal send() to record any transmitted data.
+
+        """
+        def __init__(self):
+            self.sent_data = []
+
+        def send(self, data):
+            self.sent_data.append(data)
+
+    class DummyLock:
+        def __enter__(self): pass
+        def __exit__(self, type, value, traceback): pass
 
     def __init__(self):
         self._db_file = TEST_SQLITE3_DBFILE
@@ -625,6 +640,8 @@ class MyZonemgr(Zonemgr):
                     "reload_jitter" : 0.75,
                     "secondary_zones": []
                     }
+        self._lock = self.DummyLock()
+        self._master_socket = self.DummySocket()
 
     def _start_zone_refresh_timer(self):
         pass
@@ -707,6 +724,26 @@ class TestZonemgr(unittest.TestCase):
         self.zonemgr.run()
         self.assertTrue(self.zonemgr._module_cc.stopped)
 
+    def test_command_handler_notify(self):
+        """Check the result of NOTIFY command."""
+        self.zonemgr._zone_refresh = MyZonemgrRefresh()
+
+        # On successful case, the other thread will be notified via
+        # _master_socket.
+        self.zonemgr._zone_refresh.zone_handle_notify = lambda x, y: True
+        self.zonemgr.command_handler("notify", {"zone_name": "example.",
+                                                "zone_class": "IN",
+                                                "master": "192.0.2.1"})
+        self.assertEqual([b" "], self.zonemgr._master_socket.sent_data)
+
+        # If the specified is not found in the secondary list, it doesn't
+        # bother to wake the thread (sent_data shouldn't change)
+        self.zonemgr._zone_refresh.zone_handle_notify = lambda x, y: False
+        self.zonemgr.command_handler("notify", {"zone_name": "example.",
+                                                "zone_class": "IN",
+                                                "master": "192.0.2.1"})
+        self.assertEqual([b" "], self.zonemgr._master_socket.sent_data)
+
 if __name__== "__main__":
     isc.log.resetUnitTestRootLogger()
     unittest.main()

+ 27 - 7
src/bin/zonemgr/zonemgr.py.in

@@ -191,14 +191,31 @@ class ZonemgrRefresh:
         self._set_zone_retry_timer(zone_name_class)
 
     def zone_handle_notify(self, zone_name_class, master):
-        """Handle zone notify"""
-        if (self._zone_not_exist(zone_name_class)):
+        """Handle an incomding NOTIFY message via the Auth module.
+
+        It returns True if the specified zone matches one of the locally
+        configured list of secondary zones; otherwise returns False.
+        In the latter case it assumes the server is a primary (master) of the
+        zone; the Auth module should have rejected the case where it's not
+        even authoritative for the zone.
+
+        Note: to be more robust and less independent from other module's
+        behavior, it's probably safer to check the authority condition here,
+        too.  But right now it uses SQLite3 specific API (to be deprecated),
+        so we rather rely on Auth.
+
+        Parameters:
+        zone_name_class (Name, RRClass): the notified zone name and class.
+        master (str): textual address of the NOTIFY sender.
+
+        """
+        if self._zone_not_exist(zone_name_class):
             logger.error(ZONEMGR_UNKNOWN_ZONE_NOTIFIED, zone_name_class[0],
                          zone_name_class[1], master)
-            raise ZonemgrException("[b10-zonemgr] Notified zone (%s, %s) "
-                                   "doesn't belong to zonemgr" % zone_name_class)
+            return False
         self._set_zone_notifier_master(zone_name_class, master)
         self._set_zone_notify_timer(zone_name_class)
+        return True
 
     def zonemgr_reload_zone(self, zone_name_class):
         """ Reload a zone."""
@@ -633,9 +650,12 @@ class Zonemgr:
             logger.debug(DBG_ZONEMGR_COMMAND, ZONEMGR_RECEIVE_NOTIFY,
                          zone_name_class[0], zone_name_class[1])
             with self._lock:
-                self._zone_refresh.zone_handle_notify(zone_name_class, master)
-            # Send notification to zonemgr timer thread
-            self._master_socket.send(b" ")# make self._slave_socket readble
+                need_refresh = self._zone_refresh.zone_handle_notify(
+                    zone_name_class, master)
+            if need_refresh:
+                # Send notification to zonemgr timer thread by making
+                # self._slave_socket readble.
+                self._master_socket.send(b" ")
 
         elif command == notify_out.ZONE_NEW_DATA_READY_CMD:
             """ Handle xfrin success command"""