BIND 10 master, updated. 5dcf1dadd19d9b251fa6dbefefbc98f16fb62c66 [master] Merge branch 'trac1789'
BIND 10 source code commits
bind10-changes at lists.isc.org
Fri Apr 13 09:42:49 UTC 2012
The branch, master has been updated
via 5dcf1dadd19d9b251fa6dbefefbc98f16fb62c66 (commit)
via 56338ac70f174880ff1ca73bda0afe73dad2e7d4 (commit)
via 7ce82914391a42fde56946b284f386bf0f3fe169 (commit)
via 94077743ff440dfbcfd4a723f3fd676acbfbefe4 (commit)
via 7a0dc75cee48aceeb218147331ddc81a05376358 (commit)
via 97389bad9bf82cd32329407557aed4ea669401f4 (commit)
from 7c75154f52853de902bfd7c880b6e16fe3a79c0f (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 5dcf1dadd19d9b251fa6dbefefbc98f16fb62c66
Merge: 7c75154f52853de902bfd7c880b6e16fe3a79c0f 56338ac70f174880ff1ca73bda0afe73dad2e7d4
Author: Jelte Jansen <jelte at isc.org>
Date: Fri Apr 13 11:20:51 2012 +0200
[master] Merge branch 'trac1789'
-----------------------------------------------------------------------
Summary of changes:
src/bin/xfrin/tests/xfrin_test.py | 131 +++++++++++++++++++++++++++++++++++++
src/bin/xfrin/xfrin.py.in | 100 ++++++++++++++++++++++++----
2 files changed, 218 insertions(+), 13 deletions(-)
-----------------------------------------------------------------------
diff --git a/src/bin/xfrin/tests/xfrin_test.py b/src/bin/xfrin/tests/xfrin_test.py
index a5c92ab..b88d6a9 100644
--- a/src/bin/xfrin/tests/xfrin_test.py
+++ b/src/bin/xfrin/tests/xfrin_test.py
@@ -139,6 +139,9 @@ class MockCC(MockModuleCCSession):
if identifier == "zones/use_ixfr":
return False
+ def remove_remote_config(self, module_name):
+ pass
+
class MockDataSourceClient():
'''A simple mock data source client.
@@ -2574,6 +2577,134 @@ class TestXfrin(unittest.TestCase):
self.common_ixfr_setup('refresh', False)
self.assertEqual(RRType.AXFR(), self.xfr.xfrin_started_request_type)
+class TextXfrinMemoryZones(unittest.TestCase):
+ def setUp(self):
+ self.xfr = MockXfrin()
+ # 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)
+ self.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' }
+ ]
+ }
+ ] }
+
+ def test_updates(self):
+ 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(self.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 self.config snippet, and update
+ del self.config['datasources'][1]
+ self.xfr._set_memory_zones(self.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 self.config['datasources'][0]['zones'][1]
+ self.xfr._set_memory_zones(self.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 self.config update list (i.e. its
+ # self.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
+ self.config['datasources'][0]['zones'] = []
+ self.xfr._set_memory_zones(self.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 test_normalization(self):
+ self.xfr._set_memory_zones(self.config, None)
+ # make sure it is case insensitive, root-dot-insensitive,
+ # and supports CLASSXXX notation
+ self.assertTrue(self.xfr._is_memory_zone("EXAMPLE.com", "IN"))
+ self.assertTrue(self.xfr._is_memory_zone("example.com", "in"))
+ self.assertTrue(self.xfr._is_memory_zone("example2.com.", "IN"))
+ self.assertTrue(self.xfr._is_memory_zone("example.com", "CLASS3"))
+
+ def test_bad_name(self):
+ # First set it to some config
+ self.xfr._set_memory_zones(self.config, None)
+
+ # Error checking; bad owner name should result in no changes
+ self.config['datasources'][1]['zones'][0]['origin'] = ".."
+ self.xfr._set_memory_zones(self.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"))
+
+ def test_bad_class(self):
+ # First set it to some config
+ self.xfr._set_memory_zones(self.config, None)
+
+ # Error checking; bad owner name should result in no changes
+ self.config['datasources'][1]['class'] = "Foo"
+ self.xfr._set_memory_zones(self.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"))
+
+ def test_no_filetype(self):
+ # omitting the filetype should leave that zone out, but not
+ # the rest
+ del self.config['datasources'][1]['zones'][0]['filetype']
+ self.xfr._set_memory_zones(self.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"))
+
+ def test_class_filetype(self):
+ # omitting the class should have it default to what is in the
+ # specfile for Auth.
+ AuthConfigData = isc.config.config_data.ConfigData(
+ isc.config.module_spec_from_file(xfrin.AUTH_SPECFILE_LOCATION))
+ del self.config['datasources'][0]['class']
+ self.xfr._set_memory_zones(self.config, AuthConfigData)
+ 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"))
+
def raise_interrupt():
raise KeyboardInterrupt()
diff --git a/src/bin/xfrin/xfrin.py.in b/src/bin/xfrin/xfrin.py.in
index 863c5b9..a60ad7f 100755
--- a/src/bin/xfrin/xfrin.py.in
+++ b/src/bin/xfrin/xfrin.py.in
@@ -1246,6 +1246,11 @@ class Xfrin:
def __init__(self):
self._max_transfers_in = 10
self._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()
@@ -1264,6 +1269,8 @@ class Xfrin:
self._module_cc.start()
config_data = self._module_cc.get_full_config()
self.config_handler(config_data)
+ self._module_cc.add_remote_config(AUTH_SPECFILE_LOCATION,
+ self._auth_config_handler)
def _cc_check_command(self):
'''This is a straightforward wrapper for cc.check_command,
@@ -1310,10 +1317,78 @@ class Xfrin:
return create_answer(0)
+ def _auth_config_handler(self, new_config, config_data):
+ # Config handler for changes in Auth configuration
+ 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'.
+ Note: this method is not thread-safe. We are considering
+ changing the threaded model here, but if we do not, take
+ care in accessing and updating the memory zone set (or add
+ locks)
+ """
+ # Normalize them first, if either conversion fails, return false
+ # (they won't be in the set anyway)
+ try:
+ zone_name_str = Name(zone_name_str).to_text().lower()
+ zone_class_str = RRClass(zone_class_str).to_text()
+ except Exception:
+ return False
+ return (zone_name_str, zone_class_str) in self._memory_zones
+
+ 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.
+ Note: this method is not thread-safe. We are considering
+ changing the threaded model here, but if we do not, take
+ care in accessing and updating the memory zone set (or add
+ locks)
+ """
+ # walk through the data and collect the memory zones
+ # If this causes any exception, assume we were passed bad data
+ # and keep the original set
+ new_memory_zones = set()
+ try:
+ if "datasources" in new_config:
+ for datasource in new_config["datasources"]:
+ if "class" in datasource:
+ ds_class = RRClass(datasource["class"])
+ else:
+ # Get the default
+ ds_class = RRClass(config_data.get_default_value(
+ "datasources/class"))
+ if datasource["type"] == "memory":
+ for zone in datasource["zones"]:
+ if "filetype" in zone and \
+ zone["filetype"] == "sqlite3":
+ zone_name = Name(zone["origin"])
+ zone_name_str = zone_name.to_text().lower()
+ new_memory_zones.add((zone_name_str,
+ ds_class.to_text()))
+ # Ok, we can use the data, update our list
+ self._memory_zones = new_memory_zones
+ except Exception:
+ # Something is wrong with the data. If this data even reached us,
+ # we cannot do more than assume the real module has logged and
+ # reported an error. Keep the old set.
+ return
+
def shutdown(self):
''' shutdown the xfrin process. the thread which is doing xfrin should be
terminated.
'''
+ self._module_cc.remove_remote_config(AUTH_SPECFILE_LOCATION)
self._module_cc.send_stopping()
self._shutdown_event.set()
main_thread = threading.currentThread()
@@ -1446,20 +1521,19 @@ class Xfrin:
return (addr.family, socket.SOCK_STREAM, (str(addr), port))
def _get_db_file(self):
- #TODO, the db file path should be got in auth server's configuration
- # if we need access to this configuration more often, we
- # should add it on start, and not remove it here
- # (or, if we have writable ds, we might not need this in
- # the first place)
- self._module_cc.add_remote_config(AUTH_SPECFILE_LOCATION)
- db_file, is_default = self._module_cc.get_remote_config_value("Auth", "database_file")
+ return self._db_file
+
+ def _set_db_file(self):
+ db_file, is_default =\
+ self._module_cc.get_remote_config_value("Auth", "database_file")
if is_default and "B10_FROM_BUILD" in os.environ:
- # this too should be unnecessary, but currently the
- # 'from build' override isn't stored in the config
- # (and we don't have writable datasources yet)
- db_file = os.environ["B10_FROM_BUILD"] + os.sep + "bind10_zones.sqlite3"
- self._module_cc.remove_remote_config(AUTH_SPECFILE_LOCATION)
- return db_file
+ # override the local database setting if it is default and we
+ # are running from the source tree
+ # This should be hidden inside the data source library and/or
+ # done as a configuration, and this special case should be gone).
+ db_file = os.environ["B10_FROM_BUILD"] + os.sep +\
+ "bind10_zones.sqlite3"
+ self._db_file = db_file
def publish_xfrin_news(self, zone_name, zone_class, xfr_result):
'''Send command to xfrout/zone manager module.
More information about the bind10-changes
mailing list