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
 	libdns++: updated the internal implementation of the
 	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()
 
     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):
         '''Update config data. TODO. Do error check'''
@@ -982,10 +982,16 @@ class XfroutServer:
         elif cmd == notify_out.ZONE_NEW_DATA_READY_CMD:
             zone_name = args.get('zone_name')
             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)
-                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:
                 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
           }
         ]
+        },
+        { "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();
 
     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);
     RRsetPtr rrset_ptr = *iter;
 
     // The TTL should equal to the TTL of SOA record
     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
     Message msg_example_com_soa(Message::PARSE);
@@ -68,16 +71,22 @@ TEST_F(NegativeCacheTest, testNXDOMAIN){
 
     msg_example_com_soa.makeResponse();
     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);
     rrset_ptr = *iter;
 
     // The TTL should equal to the TTL of SOA record in answer section
     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
     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
     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
     EXPECT_TRUE(msg_nxdomain2.getRRCount(Message::SECTION_ANSWER) == 0);
     // 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);
     messageFromFile(msg_example_com_soa2, "message_example_com_soa.wire");
     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);
     rrset_ptr = *iter;
     const RRTTL& soa_ttl2 = rrset_ptr->getTTL();
     // 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){
@@ -166,15 +178,16 @@ TEST_F(NegativeCacheTest, testNoerrorNodata){
     msg_nodata.makeResponse();
 
     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);
     RRsetPtr rrset_ptr = *iter;
 
     // The TTL should equal to the TTL of SOA record
     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
     Message msg_example_com_soa(Message::PARSE);
@@ -183,21 +196,26 @@ TEST_F(NegativeCacheTest, testNoerrorNodata){
 
     msg_example_com_soa.makeResponse();
     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);
     rrset_ptr = *iter;
 
     // The TTL should equal to the TTL of SOA record in answer section
     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
     Message msg_nodata2(Message::PARSE);
     messageFromFile(msg_nodata2, "message_nodata_with_soa.wire");
     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));
 
@@ -209,9 +227,11 @@ TEST_F(NegativeCacheTest, testNoerrorNodata){
     iter = msg_nodata2.beginSection(Message::SECTION_AUTHORITY);
     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();
-    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){

+ 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
 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_TRY_NUM = 5
 _EVENT_NONE = 0
@@ -164,17 +164,19 @@ class NotifyOut:
         the only interface for class NotifyOut which can be called
         by other object.
           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] != '.':
             zone_name += '.'
 
         zone_id = (zone_name, zone_class)
         if zone_id not in self._notify_infos:
-            return
+            return False
 
         # Has no slave servers, skip it.
         if (len(self._notify_infos[zone_id].notify_slaves) <= 0):
-            return
+            return True
 
         with self._lock:
             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)
                 if not self._nonblock_event.isSet():
                     self._nonblock_event.set()
+        return True
 
     def _dispatcher(self, started_event):
         started_event.set() # Let the master know we are alive already
@@ -250,7 +253,9 @@ class NotifyOut:
         self._thread.join()
 
         # Clean up
+        self._write_sock.close()
         self._write_sock = None
+        self._read_sock.close()
         self._read_sock = 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
 
         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.assertEqual(self._notify.notify_num, 1)
         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._notifying_zones[1], ('example.com.', 'IN'))
 
         # notify_num is equal to MAX_NOTIFY_NUM, append it to waiting_zones list.
         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.
         self.assertFalse(self._notify._nonblock_event.isSet())
         self.assertEqual(self._notify.notify_num, 2)
         self.assertEqual(1, len(self._notify._waiting_zones))
 
         # 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(self._notify._waiting_zones[1], ('example.net.', 'IN'))
 
         # 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))
 
         # 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(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(2, len(self._notify._waiting_zones))
 
@@ -185,6 +195,11 @@ class TestNotifyOut(unittest.TestCase):
         # Now make one socket be readable
         self._notify._notify_infos[('example.net.', '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._write_sock.send(SOCK_DATA)
         replied_zones, timeout_zones = self._notify._wait_for_notify_reply()