2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-30 21:45:37 +00:00

[master] Merge branch 'trac1789'

This commit is contained in:
Jelte Jansen
2012-04-13 11:20:51 +02:00
2 changed files with 218 additions and 13 deletions

View File

@@ -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()

View File

@@ -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.