diff --git a/src/bin/xfrin/tests/xfrin_test.py b/src/bin/xfrin/tests/xfrin_test.py index 071b001797..62174eee50 100644 --- a/src/bin/xfrin/tests/xfrin_test.py +++ b/src/bin/xfrin/tests/xfrin_test.py @@ -2577,6 +2577,76 @@ class TestXfrin(unittest.TestCase): self.common_ixfr_setup('refresh', False) self.assertEqual(RRType.AXFR(), self.xfr.xfrin_started_request_type) + def test_memory_zones(self): + # Configuration snippet containing 2 memory datasources, + # one for IN and one for CH. Both contain a zone 'example.com' + # the IN ds also contains a zone example2.com, and a zone example3.com, + # which is of file type 'text' (and hence, should be ignored) + config = { 'datasources': [ + { 'type': 'memory', + 'class': 'IN', + 'zones': [ + { 'origin': 'example.com', + 'filetype': 'sqlite3' }, + { 'origin': 'example2.com', + 'filetype': 'sqlite3' }, + { 'origin': 'example3.com', + 'filetype': 'text' } + ] + }, + { 'type': 'memory', + 'class': 'CH', + 'zones': [ + { 'origin': 'example.com', + 'filetype': 'sqlite3' } + ] + } + ] } + + self.assertFalse(self.xfr._is_memory_zone("example.com", "IN")) + self.assertFalse(self.xfr._is_memory_zone("example2.com", "IN")) + self.assertFalse(self.xfr._is_memory_zone("example3.com", "IN")) + self.assertFalse(self.xfr._is_memory_zone("example.com", "CH")) + + # add them all + self.xfr._set_memory_zones(config, None) + self.assertTrue(self.xfr._is_memory_zone("example.com", "IN")) + self.assertTrue(self.xfr._is_memory_zone("example2.com", "IN")) + self.assertFalse(self.xfr._is_memory_zone("example3.com", "IN")) + self.assertTrue(self.xfr._is_memory_zone("example.com", "CH")) + + # Remove the CH data source from the config snippet, and update + del config['datasources'][1] + self.xfr._set_memory_zones(config, None) + self.assertTrue(self.xfr._is_memory_zone("example.com", "IN")) + self.assertTrue(self.xfr._is_memory_zone("example2.com", "IN")) + self.assertFalse(self.xfr._is_memory_zone("example3.com", "IN")) + self.assertFalse(self.xfr._is_memory_zone("example.com", "CH")) + + # Remove example2.com from the datasource, and update + del config['datasources'][0]['zones'][1] + self.xfr._set_memory_zones(config, None) + self.assertTrue(self.xfr._is_memory_zone("example.com", "IN")) + self.assertFalse(self.xfr._is_memory_zone("example2.com", "IN")) + self.assertFalse(self.xfr._is_memory_zone("example3.com", "IN")) + self.assertFalse(self.xfr._is_memory_zone("example.com", "CH")) + + # If 'datasources' is not in the config update list (i.e. its config + # has not changed), no difference should be found + self.xfr._set_memory_zones({}, None) + self.assertTrue(self.xfr._is_memory_zone("example.com", "IN")) + self.assertFalse(self.xfr._is_memory_zone("example2.com", "IN")) + self.assertFalse(self.xfr._is_memory_zone("example3.com", "IN")) + self.assertFalse(self.xfr._is_memory_zone("example.com", "CH")) + + # If datasources list becomes empty, everything should be removed + config['datasources'][0]['zones'] = [] + self.xfr._set_memory_zones(config, None) + self.assertFalse(self.xfr._is_memory_zone("example.com", "IN")) + self.assertFalse(self.xfr._is_memory_zone("example2.com", "IN")) + self.assertFalse(self.xfr._is_memory_zone("example3.com", "IN")) + self.assertFalse(self.xfr._is_memory_zone("example.com", "CH")) + def raise_interrupt(): raise KeyboardInterrupt() diff --git a/src/bin/xfrin/xfrin.py.in b/src/bin/xfrin/xfrin.py.in index be9b7c73a2..db66f2c850 100755 --- a/src/bin/xfrin/xfrin.py.in +++ b/src/bin/xfrin/xfrin.py.in @@ -1246,10 +1246,11 @@ class Xfrin: def __init__(self): self._max_transfers_in = 10 self._zones = {} - # keep a separate list so we can more easily update either for now - # these are the zones that are served by the in-memory datasource, - # but transfers are handled by sqlite3 - self._memory_zones = {} + # This is a set of (zone/class) tuples (both as strings), + # representing the in-memory zones maintaned by Xfrin. It + # is used to trigger Auth/in-memory so that it reloads + # zones when they have been transfered in + self._memory_zones = set() self._cc_setup() self.recorder = XfrinRecorder() self._shutdown_event = threading.Event() @@ -1316,10 +1317,48 @@ class Xfrin: return create_answer(0) - def auth_config_handler(self, new_config, foo): + def auth_config_handler(self, new_config, config_data): # Config handler for changes in Auth configuration - print("[XX] Hi. XFRIN HERE. AUTH CONFIG UPDATE") self._set_db_file() + self._set_memory_zones(new_config, config_data) + + def _clear_memory_zones(self): + """Clears the memory_zones set; called before processing the + changed list of memory datasource zones that have file type + sqlite3""" + self._memory_zones.clear() + + def _is_memory_zone(self, zone_name_str, zone_class_str): + """Returns true if the given zone/class combination is configured + in the in-memory datasource of the Auth process with file type + 'sqlite3'. + """ + return (zone_name_str, zone_class_str) in self._memory_zones + + def _add_memory_zone(self, zone_name_str, zone_class_str): + """Add the given zone and class to the internal list of zones + in memory datasources with file type 'sqlite3'""" + self._memory_zones.add((zone_name_str, zone_class_str)) + + def _set_memory_zones(self, new_config, config_data): + """Part of the auth_config_handler function, keeps an internal set + of zones in the datasources config subset that have 'sqlite3' as + their file type""" + if "datasources" in new_config: + self._clear_memory_zones() + for datasource in new_config["datasources"]: + if "class" in datasource: + ds_class = datasource["class"] + else: + # Get the default + ds_class =\ + config_data.get_default_value("datasources/class") + if datasource["type"] == "memory": + zones = [] + for zone in datasource["zones"]: + if zone["filetype"] == "sqlite3": + zone_name = zone["origin"] + self._add_memory_zone(zone_name, ds_class) def shutdown(self): ''' shutdown the xfrin process. the thread which is doing xfrin should be