Browse Source

Merge branch 'master' of ssh://git.bind10.isc.org/var/bind10/git/bind10

Xie Jiagui 13 years ago
parent
commit
1e62bf975b

+ 5 - 0
ChangeLog

@@ -1,3 +1,8 @@
+402.	[func]		jelte
+	b10-xfrout now has a visible command to send out notifies for
+	a given zone, callable from bindctl. Xfrout notify <zone> [class]
+	(Trac #1321, git 0bb258f8610620191d75cfd5d2308b6fc558c280)
+
 401.	[func]*		jinmei
 401.	[func]*		jinmei
 	libdns++: updated the internal implementation of the
 	libdns++: updated the internal implementation of the
 	MessageRenderer class.  This is mostly a transparent change, but
 	MessageRenderer class.  This is mostly a transparent change, but

+ 10 - 4
src/bin/xfrout/xfrout.py.in

@@ -926,7 +926,7 @@ class XfroutServer:
         self._notifier.dispatcher()
         self._notifier.dispatcher()
 
 
     def send_notify(self, zone_name, zone_class):
     def send_notify(self, zone_name, zone_class):
-        self._notifier.send_notify(zone_name, zone_class)
+        return self._notifier.send_notify(zone_name, zone_class)
 
 
     def config_handler(self, new_config):
     def config_handler(self, new_config):
         '''Update config data. TODO. Do error check'''
         '''Update config data. TODO. Do error check'''
@@ -982,10 +982,16 @@ class XfroutServer:
         elif cmd == notify_out.ZONE_NEW_DATA_READY_CMD:
         elif cmd == notify_out.ZONE_NEW_DATA_READY_CMD:
             zone_name = args.get('zone_name')
             zone_name = args.get('zone_name')
             zone_class = args.get('zone_class')
             zone_class = args.get('zone_class')
-            if zone_name and zone_class:
+            if not zone_class:
+                zone_class = str(RRClass.IN())
+            if zone_name:
                 logger.info(XFROUT_NOTIFY_COMMAND, zone_name, zone_class)
                 logger.info(XFROUT_NOTIFY_COMMAND, zone_name, zone_class)
-                self.send_notify(zone_name, zone_class)
-                answer = create_answer(0)
+                if self.send_notify(zone_name, zone_class):
+                    answer = create_answer(0)
+                else:
+                    zonestr = notify_out.format_zone_str(Name(zone_name),
+                                                         zone_class)
+                    answer = create_answer(1, "Unknown zone: " + zonestr)
             else:
             else:
                 answer = create_answer(1, "Bad command parameter:" + str(args))
                 answer = create_answer(1, "Bad command parameter:" + str(args))
 
 

+ 13 - 0
src/bin/xfrout/xfrout.spec.pre.in

@@ -71,6 +71,19 @@
             "item_optional": true
             "item_optional": true
           }
           }
         ]
         ]
+        },
+        { "command_name": "notify",
+          "command_description": "Send notifies for zone",
+          "command_args": [
+          { "item_name": "zone_name",
+            "item_type": "string",
+            "item_optional": false,
+            "item_default": "" },
+          { "item_name": "zone_class",
+            "item_type": "string",
+            "item_optional": true,
+            "item_default": "IN"
+          } ]
         }
         }
       ]
       ]
   }
   }

+ 36 - 16
src/lib/cache/tests/negative_cache_unittest.cc

@@ -52,14 +52,17 @@ TEST_F(NegativeCacheTest, testNXDOMAIN){
     msg_nxdomain.makeResponse();
     msg_nxdomain.makeResponse();
 
 
     Name non_exist_qname("nonexist.example.com.");
     Name non_exist_qname("nonexist.example.com.");
-    EXPECT_TRUE(cache->lookup(non_exist_qname, RRType::A(), RRClass::IN(), msg_nxdomain));
+    EXPECT_TRUE(cache->lookup(non_exist_qname, RRType::A(), RRClass::IN(),
+                msg_nxdomain));
 
 
     RRsetIterator iter = msg_nxdomain.beginSection(Message::SECTION_AUTHORITY);
     RRsetIterator iter = msg_nxdomain.beginSection(Message::SECTION_AUTHORITY);
     RRsetPtr rrset_ptr = *iter;
     RRsetPtr rrset_ptr = *iter;
 
 
     // The TTL should equal to the TTL of SOA record
     // The TTL should equal to the TTL of SOA record
     const RRTTL& nxdomain_ttl1 = rrset_ptr->getTTL();
     const RRTTL& nxdomain_ttl1 = rrset_ptr->getTTL();
-    EXPECT_EQ(nxdomain_ttl1.getValue(), 86400);
+    // May have already crossed seconds boundary
+    EXPECT_GE(nxdomain_ttl1.getValue(), 86399);
+    EXPECT_LE(nxdomain_ttl1.getValue(), 86400);
 
 
     // SOA response for example.com
     // SOA response for example.com
     Message msg_example_com_soa(Message::PARSE);
     Message msg_example_com_soa(Message::PARSE);
@@ -68,16 +71,22 @@ TEST_F(NegativeCacheTest, testNXDOMAIN){
 
 
     msg_example_com_soa.makeResponse();
     msg_example_com_soa.makeResponse();
     Name soa_qname("example.com.");
     Name soa_qname("example.com.");
-    EXPECT_TRUE(cache->lookup(soa_qname, RRType::SOA(), RRClass::IN(), msg_example_com_soa));
+    EXPECT_TRUE(cache->lookup(soa_qname, RRType::SOA(), RRClass::IN(),
+                msg_example_com_soa));
 
 
     iter = msg_example_com_soa.beginSection(Message::SECTION_ANSWER);
     iter = msg_example_com_soa.beginSection(Message::SECTION_ANSWER);
     rrset_ptr = *iter;
     rrset_ptr = *iter;
 
 
     // The TTL should equal to the TTL of SOA record in answer section
     // The TTL should equal to the TTL of SOA record in answer section
     const RRTTL& soa_ttl = rrset_ptr->getTTL();
     const RRTTL& soa_ttl = rrset_ptr->getTTL();
-    EXPECT_EQ(soa_ttl.getValue(), 172800);
+    // May have already crossed seconds boundary
+    EXPECT_GE(soa_ttl.getValue(), 172799);
+    EXPECT_LE(soa_ttl.getValue(), 172800);
 
 
-    sleep(1);
+    // Sleep for 2 seconds. 2 seconds to make sure the final range check
+    // does not overlap with the original ones (in which case this test
+    // would erroneously pass if the ttl value is not changed)
+    sleep(2);
 
 
     // Query nonexist.example.com again
     // Query nonexist.example.com again
     Message msg_nxdomain2(Message::PARSE);
     Message msg_nxdomain2(Message::PARSE);
@@ -90,7 +99,8 @@ TEST_F(NegativeCacheTest, testNXDOMAIN){
 
 
     // The TTL should equal to the TTL of negative response SOA record
     // The TTL should equal to the TTL of negative response SOA record
     const RRTTL& nxdomain_ttl2 = rrset_ptr->getTTL();
     const RRTTL& nxdomain_ttl2 = rrset_ptr->getTTL();
-    EXPECT_TRUE(86398 <= nxdomain_ttl2.getValue() && nxdomain_ttl2.getValue() <= 86399);
+    EXPECT_GE(nxdomain_ttl2.getValue(), 86397);
+    EXPECT_LE(nxdomain_ttl2.getValue(), 86398);
     // No RRset in ANSWER section
     // No RRset in ANSWER section
     EXPECT_TRUE(msg_nxdomain2.getRRCount(Message::SECTION_ANSWER) == 0);
     EXPECT_TRUE(msg_nxdomain2.getRRCount(Message::SECTION_ANSWER) == 0);
     // Check that only one SOA record exist in AUTHORITY section
     // Check that only one SOA record exist in AUTHORITY section
@@ -103,13 +113,15 @@ TEST_F(NegativeCacheTest, testNXDOMAIN){
     Message msg_example_com_soa2(Message::PARSE);
     Message msg_example_com_soa2(Message::PARSE);
     messageFromFile(msg_example_com_soa2, "message_example_com_soa.wire");
     messageFromFile(msg_example_com_soa2, "message_example_com_soa.wire");
     msg_example_com_soa2.makeResponse();
     msg_example_com_soa2.makeResponse();
-    EXPECT_TRUE(cache->lookup(soa_qname, RRType::SOA(), RRClass::IN(), msg_example_com_soa2));
+    EXPECT_TRUE(cache->lookup(soa_qname, RRType::SOA(), RRClass::IN(),
+                              msg_example_com_soa2));
 
 
     iter = msg_example_com_soa2.beginSection(Message::SECTION_ANSWER);
     iter = msg_example_com_soa2.beginSection(Message::SECTION_ANSWER);
     rrset_ptr = *iter;
     rrset_ptr = *iter;
     const RRTTL& soa_ttl2 = rrset_ptr->getTTL();
     const RRTTL& soa_ttl2 = rrset_ptr->getTTL();
     // The TTL should equal to the TTL of SOA record in answer section
     // The TTL should equal to the TTL of SOA record in answer section
-    EXPECT_TRUE(172798 <= soa_ttl2.getValue() && soa_ttl2.getValue() <= 172799);
+    EXPECT_GE(soa_ttl2.getValue(), 172797);
+    EXPECT_LE(soa_ttl2.getValue(), 172798);
 }
 }
 
 
 TEST_F(NegativeCacheTest, testNXDOMAINWithoutSOA){
 TEST_F(NegativeCacheTest, testNXDOMAINWithoutSOA){
@@ -166,15 +178,16 @@ TEST_F(NegativeCacheTest, testNoerrorNodata){
     msg_nodata.makeResponse();
     msg_nodata.makeResponse();
 
 
     Name example_dot_com("example.com.");
     Name example_dot_com("example.com.");
-    EXPECT_TRUE(cache->lookup(example_dot_com, RRType::MX(), RRClass::IN(), msg_nodata));
+    EXPECT_TRUE(cache->lookup(example_dot_com, RRType::MX(), RRClass::IN(),
+                msg_nodata));
 
 
     RRsetIterator iter = msg_nodata.beginSection(Message::SECTION_AUTHORITY);
     RRsetIterator iter = msg_nodata.beginSection(Message::SECTION_AUTHORITY);
     RRsetPtr rrset_ptr = *iter;
     RRsetPtr rrset_ptr = *iter;
 
 
     // The TTL should equal to the TTL of SOA record
     // The TTL should equal to the TTL of SOA record
     const RRTTL& nodata_ttl1 = rrset_ptr->getTTL();
     const RRTTL& nodata_ttl1 = rrset_ptr->getTTL();
-    EXPECT_EQ(nodata_ttl1.getValue(), 86400);
-
+    EXPECT_GE(nodata_ttl1.getValue(), 86399);
+    EXPECT_LE(nodata_ttl1.getValue(), 86400);
 
 
     // Normal SOA response for example.com
     // Normal SOA response for example.com
     Message msg_example_com_soa(Message::PARSE);
     Message msg_example_com_soa(Message::PARSE);
@@ -183,21 +196,26 @@ TEST_F(NegativeCacheTest, testNoerrorNodata){
 
 
     msg_example_com_soa.makeResponse();
     msg_example_com_soa.makeResponse();
     Name soa_qname("example.com.");
     Name soa_qname("example.com.");
-    EXPECT_TRUE(cache->lookup(soa_qname, RRType::SOA(), RRClass::IN(), msg_example_com_soa));
+    EXPECT_TRUE(cache->lookup(soa_qname, RRType::SOA(), RRClass::IN(),
+                msg_example_com_soa));
 
 
     iter = msg_example_com_soa.beginSection(Message::SECTION_ANSWER);
     iter = msg_example_com_soa.beginSection(Message::SECTION_ANSWER);
     rrset_ptr = *iter;
     rrset_ptr = *iter;
 
 
     // The TTL should equal to the TTL of SOA record in answer section
     // The TTL should equal to the TTL of SOA record in answer section
     const RRTTL& soa_ttl = rrset_ptr->getTTL();
     const RRTTL& soa_ttl = rrset_ptr->getTTL();
-    EXPECT_EQ(soa_ttl.getValue(), 172800);
+    EXPECT_GE(soa_ttl.getValue(), 172799);
+    EXPECT_LE(soa_ttl.getValue(), 172800);
 
 
     // Query MX record of example.com again
     // Query MX record of example.com again
     Message msg_nodata2(Message::PARSE);
     Message msg_nodata2(Message::PARSE);
     messageFromFile(msg_nodata2, "message_nodata_with_soa.wire");
     messageFromFile(msg_nodata2, "message_nodata_with_soa.wire");
     msg_nodata2.makeResponse();
     msg_nodata2.makeResponse();
 
 
-    sleep(1);
+    // Sleep for 2 seconds. 2 seconds to make sure the final range check
+    // does not overlap with the original ones (in which case this test
+    // would erroneously pass if the ttl value is not changed)
+    sleep(2);
 
 
     EXPECT_TRUE(cache->lookup(example_dot_com, RRType::MX(), RRClass::IN(), msg_nodata2));
     EXPECT_TRUE(cache->lookup(example_dot_com, RRType::MX(), RRClass::IN(), msg_nodata2));
 
 
@@ -209,9 +227,11 @@ TEST_F(NegativeCacheTest, testNoerrorNodata){
     iter = msg_nodata2.beginSection(Message::SECTION_AUTHORITY);
     iter = msg_nodata2.beginSection(Message::SECTION_AUTHORITY);
     rrset_ptr = *iter;
     rrset_ptr = *iter;
 
 
-    // The TTL should equal to the TTL of negative response SOA record and counted down
+    // The TTL should equal to the TTL of negative response SOA record
+    // and counted down
     const RRTTL& nodata_ttl2 = rrset_ptr->getTTL();
     const RRTTL& nodata_ttl2 = rrset_ptr->getTTL();
-    EXPECT_TRUE(86398 <= nodata_ttl2.getValue() && nodata_ttl2.getValue() <= 86399);
+    EXPECT_GE(nodata_ttl2.getValue(), 86397);
+    EXPECT_LE(nodata_ttl2.getValue(), 86398);
 }
 }
 
 
 TEST_F(NegativeCacheTest, testReferralResponse){
 TEST_F(NegativeCacheTest, testReferralResponse){

+ 9 - 4
src/lib/python/isc/notify/notify_out.py

@@ -34,7 +34,7 @@ logger = isc.log.Logger("notify_out")
 # initialized yet. see trac ticket #1103
 # initialized yet. see trac ticket #1103
 from isc.dns import *
 from isc.dns import *
 
 
-ZONE_NEW_DATA_READY_CMD = 'zone_new_data_ready'
+ZONE_NEW_DATA_READY_CMD = 'notify'
 _MAX_NOTIFY_NUM = 30
 _MAX_NOTIFY_NUM = 30
 _MAX_NOTIFY_TRY_NUM = 5
 _MAX_NOTIFY_TRY_NUM = 5
 _EVENT_NONE = 0
 _EVENT_NONE = 0
@@ -164,17 +164,19 @@ class NotifyOut:
         the only interface for class NotifyOut which can be called
         the only interface for class NotifyOut which can be called
         by other object.
         by other object.
           Internally, the function only set the zone's notify-reply
           Internally, the function only set the zone's notify-reply
-        timeout to now, then notify message will be sent out. '''
+        timeout to now, then notify message will be sent out.
+        Returns False if the zone/class is not known, True if it is
+        (even if there are no slaves)'''
         if zone_name[len(zone_name) - 1] != '.':
         if zone_name[len(zone_name) - 1] != '.':
             zone_name += '.'
             zone_name += '.'
 
 
         zone_id = (zone_name, zone_class)
         zone_id = (zone_name, zone_class)
         if zone_id not in self._notify_infos:
         if zone_id not in self._notify_infos:
-            return
+            return False
 
 
         # Has no slave servers, skip it.
         # Has no slave servers, skip it.
         if (len(self._notify_infos[zone_id].notify_slaves) <= 0):
         if (len(self._notify_infos[zone_id].notify_slaves) <= 0):
-            return
+            return True
 
 
         with self._lock:
         with self._lock:
             if (self.notify_num >= _MAX_NOTIFY_NUM) or (zone_id in self._notifying_zones):
             if (self.notify_num >= _MAX_NOTIFY_NUM) or (zone_id in self._notifying_zones):
@@ -186,6 +188,7 @@ class NotifyOut:
                 self._notifying_zones.append(zone_id)
                 self._notifying_zones.append(zone_id)
                 if not self._nonblock_event.isSet():
                 if not self._nonblock_event.isSet():
                     self._nonblock_event.set()
                     self._nonblock_event.set()
+        return True
 
 
     def _dispatcher(self, started_event):
     def _dispatcher(self, started_event):
         started_event.set() # Let the master know we are alive already
         started_event.set() # Let the master know we are alive already
@@ -250,7 +253,9 @@ class NotifyOut:
         self._thread.join()
         self._thread.join()
 
 
         # Clean up
         # Clean up
+        self._write_sock.close()
         self._write_sock = None
         self._write_sock = None
+        self._read_sock.close()
         self._read_sock = None
         self._read_sock = None
         self._thread = None
         self._thread = None
 
 

+ 22 - 7
src/lib/python/isc/notify/tests/notify_out_test.py

@@ -114,38 +114,48 @@ class TestNotifyOut(unittest.TestCase):
         notify_out._MAX_NOTIFY_NUM = 2
         notify_out._MAX_NOTIFY_NUM = 2
 
 
         self._notify._nonblock_event.clear()
         self._notify._nonblock_event.clear()
-        self._notify.send_notify('example.net')
+        self.assertTrue(self._notify.send_notify('example.net'))
         self.assertTrue(self._notify._nonblock_event.isSet())
         self.assertTrue(self._notify._nonblock_event.isSet())
         self.assertEqual(self._notify.notify_num, 1)
         self.assertEqual(self._notify.notify_num, 1)
         self.assertEqual(self._notify._notifying_zones[0], ('example.net.', 'IN'))
         self.assertEqual(self._notify._notifying_zones[0], ('example.net.', 'IN'))
 
 
-        self._notify.send_notify('example.com')
+        self.assertTrue(self._notify.send_notify('example.com'))
         self.assertEqual(self._notify.notify_num, 2)
         self.assertEqual(self._notify.notify_num, 2)
         self.assertEqual(self._notify._notifying_zones[1], ('example.com.', 'IN'))
         self.assertEqual(self._notify._notifying_zones[1], ('example.com.', 'IN'))
 
 
         # notify_num is equal to MAX_NOTIFY_NUM, append it to waiting_zones list.
         # notify_num is equal to MAX_NOTIFY_NUM, append it to waiting_zones list.
         self._notify._nonblock_event.clear()
         self._notify._nonblock_event.clear()
-        self._notify.send_notify('example.com', 'CH')
+        self.assertTrue(self._notify.send_notify('example.com', 'CH'))
         # add waiting zones won't set nonblock_event.
         # add waiting zones won't set nonblock_event.
         self.assertFalse(self._notify._nonblock_event.isSet())
         self.assertFalse(self._notify._nonblock_event.isSet())
         self.assertEqual(self._notify.notify_num, 2)
         self.assertEqual(self._notify.notify_num, 2)
         self.assertEqual(1, len(self._notify._waiting_zones))
         self.assertEqual(1, len(self._notify._waiting_zones))
 
 
         # zone_id is already in notifying_zones list, append it to waiting_zones list.
         # zone_id is already in notifying_zones list, append it to waiting_zones list.
-        self._notify.send_notify('example.net')
+        self.assertTrue(self._notify.send_notify('example.net'))
         self.assertEqual(2, len(self._notify._waiting_zones))
         self.assertEqual(2, len(self._notify._waiting_zones))
         self.assertEqual(self._notify._waiting_zones[1], ('example.net.', 'IN'))
         self.assertEqual(self._notify._waiting_zones[1], ('example.net.', 'IN'))
 
 
         # zone_id is already in waiting_zones list, skip it.
         # zone_id is already in waiting_zones list, skip it.
-        self._notify.send_notify('example.net')
+        self.assertTrue(self._notify.send_notify('example.net'))
         self.assertEqual(2, len(self._notify._waiting_zones))
         self.assertEqual(2, len(self._notify._waiting_zones))
 
 
         # has no slave masters, skip it.
         # has no slave masters, skip it.
-        self._notify.send_notify('example.org.', 'CH')
+        self.assertTrue(self._notify.send_notify('example.org.', 'CH'))
         self.assertEqual(self._notify.notify_num, 2)
         self.assertEqual(self._notify.notify_num, 2)
         self.assertEqual(2, len(self._notify._waiting_zones))
         self.assertEqual(2, len(self._notify._waiting_zones))
 
 
-        self._notify.send_notify('example.org.')
+        self.assertTrue(self._notify.send_notify('example.org.'))
+        self.assertEqual(self._notify.notify_num, 2)
+        self.assertEqual(2, len(self._notify._waiting_zones))
+
+        # zone does not exist, should return False, and no change in other
+        # values
+        self.assertFalse(self._notify.send_notify('does.not.exist.'))
+        self.assertEqual(self._notify.notify_num, 2)
+        self.assertEqual(2, len(self._notify._waiting_zones))
+
+        self.assertFalse(self._notify.send_notify('example.net.', 'CH'))
         self.assertEqual(self._notify.notify_num, 2)
         self.assertEqual(self._notify.notify_num, 2)
         self.assertEqual(2, len(self._notify._waiting_zones))
         self.assertEqual(2, len(self._notify._waiting_zones))
 
 
@@ -185,6 +195,11 @@ class TestNotifyOut(unittest.TestCase):
         # Now make one socket be readable
         # Now make one socket be readable
         self._notify._notify_infos[('example.net.', 'IN')].notify_timeout = time.time() + 10
         self._notify._notify_infos[('example.net.', 'IN')].notify_timeout = time.time() + 10
         self._notify._notify_infos[('example.com.', 'IN')].notify_timeout = time.time() + 10
         self._notify._notify_infos[('example.com.', 'IN')].notify_timeout = time.time() + 10
+
+        if self._notify._read_sock is not None:
+            self._notify._read_sock.close()
+        if self._notify._write_sock is not None:
+            self._notify._write_sock.close()
         self._notify._read_sock, self._notify._write_sock = socket.socketpair()
         self._notify._read_sock, self._notify._write_sock = socket.socketpair()
         self._notify._write_sock.send(SOCK_DATA)
         self._notify._write_sock.send(SOCK_DATA)
         replied_zones, timeout_zones = self._notify._wait_for_notify_reply()
         replied_zones, timeout_zones = self._notify._wait_for_notify_reply()