Parcourir la source

[2856] Add test for "load" command to builder

Mukund Sivaraman il y a 11 ans
Parent
commit
37753c0dc2

+ 1 - 0
configure.ac

@@ -1279,6 +1279,7 @@ AC_CONFIG_FILES([Makefile
                  src/lib/python/isc/ddns/tests/Makefile
                  src/lib/python/isc/memmgr/Makefile
                  src/lib/python/isc/memmgr/tests/Makefile
+                 src/lib/python/isc/memmgr/tests/testdata/Makefile
                  src/lib/python/isc/xfrin/Makefile
                  src/lib/python/isc/xfrin/tests/Makefile
                  src/lib/python/isc/server_common/Makefile

+ 14 - 8
src/lib/python/isc/memmgr/builder.py

@@ -13,6 +13,7 @@
 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
+import json
 from isc.datasrc import ConfigurableClientList
 from isc.memmgr.datasrc_info import SegmentInfo
 
@@ -90,9 +91,10 @@ class MemorySegmentBuilder:
 
         clist = dsrc_info.clients_map[rrclass]
         sgmt_info = dsrc_info.segment_info_map[(rrclass, dsrc_name)]
+        params = json.dumps(sgmt_info.get_reset_param(SegmentInfo.WRITER))
         clist.reset_memory_segment(dsrc_name,
-                                   ConfigurableClientList.READ_ONLY,
-                                   sgmt_info.get_reset_param(SegmentInfo.WRITER))
+                                   ConfigurableClientList.READ_WRITE,
+                                   params)
 
         if zone_name is not None:
             zones = [(None, zone_name)]
@@ -100,9 +102,13 @@ class MemorySegmentBuilder:
             zones = clist.get_zone_table_accessor(dsrc_name, True)
 
         for _, zone_name in zones:
-            cache_load_error = (zone_name is None) # install empty zone initially
-            writer = clist.get_cached_zone_writer(zone_name, catch_load_error,
-                                                  dsrc_name)
+            catch_load_error = (zone_name is None) # install empty zone initially
+            result, writer = clist.get_cached_zone_writer(zone_name, catch_load_error,
+                                                          dsrc_name)
+            if result != ConfigurableClientList.CACHE_STATUS_ZONE_SUCCESS:
+                # FIXME: log the error
+                continue
+
             try:
                 error = writer.load()
                 if error is not None:
@@ -119,7 +125,7 @@ class MemorySegmentBuilder:
         # public API to just clear the segment)
         clist.reset_memory_segment(dsrc_name,
                                    ConfigurableClientList.READ_ONLY,
-                                   sgmt_info.get_reset_param(SegmentInfo.WRITER))
+                                   params)
 
         self._response_queue.append(('load-completed', dsrc_info, rrclass,
                                      dsrc_name))
@@ -154,8 +160,8 @@ class MemorySegmentBuilder:
                         # See the comments for __handle_load() for
                         # details of the tuple passed to the "load"
                         # command.
-                        self.__handle_load(command_tuple[1], command_tuple[2],
-                                           command_tuple[3], command_tuple[4])
+                        _, zone_name, dsrc_info, rrclass, dsrc_name = command_tuple
+                        self.__handle_load(zone_name, dsrc_info, rrclass, dsrc_name)
                     elif command == 'shutdown':
                         self.__handle_shutdown()
                         # When the shutdown command is received, we do

+ 2 - 0
src/lib/python/isc/memmgr/tests/Makefile.am

@@ -1,3 +1,4 @@
+SUBDIRS = testdata
 PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
 PYTESTS = builder_tests.py datasrc_info_tests.py
 EXTRA_DIST = $(PYTESTS)
@@ -26,6 +27,7 @@ endif
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
 	$(LIBRARY_PATH_PLACEHOLDER) \
+	TESTDATA_PATH=$(abs_srcdir)/testdata \
 	TESTDATA_WRITE_PATH=$(builddir) \
 	B10_FROM_BUILD=$(abs_top_builddir) \
 	HAVE_SHARED_MEMORY=$(HAVE_SHARED_MEMORY) \

+ 113 - 2
src/lib/python/isc/memmgr/tests/builder_tests.py

@@ -14,12 +14,28 @@
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 import unittest
+import os
 import socket
 import select
 import threading
 
 import isc.log
+from isc.dns import *
+import isc.datasrc
 from isc.memmgr.builder import *
+from isc.server_common.datasrc_clients_mgr import DataSrcClientsMgr
+from isc.memmgr.datasrc_info import *
+
+TESTDATA_PATH = os.environ['TESTDATA_PATH'] + os.sep
+
+# Defined for easier tests with DataSrcClientsMgr.reconfigure(), which
+# only needs get_value() method
+class MockConfigData:
+    def __init__(self, data):
+        self.__data = data
+
+    def get_value(self, identifier):
+        return self.__data[identifier], False
 
 class TestMemorySegmentBuilder(unittest.TestCase):
     def _create_builder_thread(self):
@@ -29,7 +45,8 @@ class TestMemorySegmentBuilder(unittest.TestCase):
         self._builder_command_queue = []
         self._builder_response_queue = []
 
-        self._builder_cv = threading.Condition()
+        self._builder_lock = threading.Lock()
+        self._builder_cv = threading.Condition(lock=self._builder_lock)
 
         self._builder = MemorySegmentBuilder(self._builder_sock,
                                              self._builder_cv,
@@ -95,7 +112,7 @@ class TestMemorySegmentBuilder(unittest.TestCase):
 
         self._builder_thread.start()
 
-        # Now that the builder thread is running, send it the shutdown
+        # Now that the builder thread is running, send it the "shutdown"
         # command. The thread should exit its main loop and be joinable.
         with self._builder_cv:
             self._builder_command_queue.append(('shutdown',))
@@ -115,6 +132,100 @@ class TestMemorySegmentBuilder(unittest.TestCase):
         self.assertEqual(len(self._builder_command_queue), 0)
         self.assertEqual(len(self._builder_response_queue), 0)
 
+    @unittest.skipIf(os.environ['HAVE_SHARED_MEMORY'] != 'yes',
+                     'shared memory is not available')
+    def test_load(self):
+        """
+        Test "load" command.
+        """
+
+        mapped_file_dir = os.environ['TESTDATA_WRITE_PATH']
+        mgr_config = {'mapped_file_dir': mapped_file_dir}
+
+        cfg_data = MockConfigData(
+            {"classes":
+                 {"IN": [{"type": "MasterFiles",
+                          "params": { "example.com": TESTDATA_PATH + "example.com.zone" },
+                          "cache-enable": True,
+                          "cache-type": "mapped"}]
+                  }
+             })
+        cmgr = DataSrcClientsMgr(use_cache=True)
+        cmgr.reconfigure({}, cfg_data)
+
+        genid, clients_map = cmgr.get_clients_map()
+        datasrc_info = DataSrcInfo(genid, clients_map, mgr_config)
+
+        self.assertEqual(1, datasrc_info.gen_id)
+        self.assertEqual(clients_map, datasrc_info.clients_map)
+        self.assertEqual(1, len(datasrc_info.segment_info_map))
+        sgmt_info = datasrc_info.segment_info_map[(RRClass.IN, 'MasterFiles')]
+        self.assertIsNone(sgmt_info.get_reset_param(SegmentInfo.READER))
+        self.assertIsNotNone(sgmt_info.get_reset_param(SegmentInfo.WRITER))
+
+        self._builder_thread.start()
+
+        # Now that the builder thread is running, send it the "load"
+        # command. We should be notified when the load operation is
+        # complete.
+        with self._builder_cv:
+            self._builder_command_queue.append(('load',
+                                                isc.dns.Name("example.com"),
+                                                datasrc_info, RRClass.IN,
+                                                'MasterFiles'))
+            self._builder_cv.notify_all()
+
+        # Wait 60 seconds to receive a notification on the socket from
+        # the builder.
+        (reads, _, _) = select.select([self._master_sock], [], [], 60)
+        self.assertTrue(self._master_sock in reads)
+
+        # Reading 1 byte should not block us here, especially as the
+        # socket is ready to read. It's a hack, but this is just a
+        # testcase.
+        got = self._master_sock.recv(1)
+        self.assertEqual(got, b'x')
+
+        with self._builder_lock:
+            # The command queue must be cleared, and the response queue
+            # must contain a response that a bad command was sent. The
+            # thread is no longer running, so we can use the queues
+            # without a lock.
+            self.assertEqual(len(self._builder_command_queue), 0)
+            self.assertEqual(len(self._builder_response_queue), 1)
+
+            response = self._builder_response_queue[0]
+            self.assertTrue(isinstance(response, tuple))
+            self.assertTupleEqual(response, ('load-completed', datasrc_info,
+                                             RRClass.IN, 'MasterFiles'))
+            del self._builder_response_queue[:]
+
+        # Now try looking for some loaded data
+        clist = datasrc_info.clients_map[RRClass.IN]
+        dsrc, finder, exact = clist.find(isc.dns.Name("example.com"))
+        self.assertIsNotNone(dsrc)
+        self.assertTrue(isinstance(dsrc, isc.datasrc.DataSourceClient))
+        self.assertIsNotNone(finder)
+        self.assertTrue(isinstance(finder, isc.datasrc.ZoneFinder))
+        self.assertTrue(exact)
+
+        # Send the builder thread the "shutdown" command. The thread
+        # should exit its main loop and be joinable.
+        with self._builder_cv:
+            self._builder_command_queue.append(('shutdown',))
+            self._builder_cv.notify_all()
+
+        # Wait 5 seconds at most for the main loop of the builder to
+        # exit.
+        self._builder_thread.join(5)
+        self.assertFalse(self._builder_thread.isAlive())
+
+        # The command queue must be cleared, and the response queue must
+        # be untouched (we don't use it in this test). The thread is no
+        # longer running, so we can use the queues without a lock.
+        self.assertEqual(len(self._builder_command_queue), 0)
+        self.assertEqual(len(self._builder_response_queue), 0)
+
 if __name__ == "__main__":
     isc.log.init("bind10-test")
     isc.log.resetUnitTestRootLogger()

+ 2 - 0
src/lib/python/isc/memmgr/tests/testdata/Makefile.am

@@ -0,0 +1,2 @@
+EXTRA_DIST = \
+	example.com.zone

+ 8 - 0
src/lib/python/isc/memmgr/tests/testdata/example.com.zone

@@ -0,0 +1,8 @@
+example.com.         1000  IN  SOA a.dns.example.com. mail.example.com. 1 1 1 1 1
+example.com.         1000  IN  NS  a.dns.example.com.
+example.com.         1000  IN  NS  b.dns.example.com.
+example.com.         1000  IN  NS  c.dns.example.com.
+a.dns.example.com.   1000  IN  A    1.1.1.1
+b.dns.example.com.   1000  IN  A    3.3.3.3
+b.dns.example.com.   1000  IN  AAAA 4:4::4:4
+b.dns.example.com.   1000  IN  AAAA 5:5::5:5