2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-31 22:15:23 +00:00

Merge branch 'trac1414'

This commit is contained in:
Jelte Jansen
2011-12-16 10:05:09 +01:00
3 changed files with 125 additions and 63 deletions

View File

@@ -20,6 +20,7 @@ endif
for pytest in $(PYTESTS) ; do \ for pytest in $(PYTESTS) ; do \
echo Running test: $$pytest ; \ echo Running test: $$pytest ; \
$(LIBRARY_PATH_PLACEHOLDER) \ $(LIBRARY_PATH_PLACEHOLDER) \
B10_FROM_BUILD=$(abs_top_builddir) \
PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/zonemgr:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/xfr/.libs \ PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/zonemgr:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/xfr/.libs \
$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
done done

View File

@@ -48,28 +48,16 @@ class MySession():
def group_recvmsg(self, nonblock, seq): def group_recvmsg(self, nonblock, seq):
return None, None return None, None
class FakeConfig: class FakeCCSession(isc.config.ConfigData):
def __init__(self): def __init__(self):
self.zone_list = [] module_spec = isc.config.module_spec_from_file(SPECFILE_LOCATION)
self.set_zone_list_from_name_classes([ZONE_NAME_CLASS1_IN, ConfigData.__init__(self, module_spec)
ZONE_NAME_CLASS2_CH])
def set_zone_list_from_name_classes(self, zones): def get_remote_config_value(self, module_name, identifier):
self.zone_list = map(lambda nc: {"name": nc[0], "class": nc[1]}, zones) if module_name == "Auth" and identifier == "database_file":
def get(self, name): return "initdb.file", False
if name == 'lowerbound_refresh':
return LOWERBOUND_REFRESH
elif name == 'lowerbound_retry':
return LOWERBOUND_RETRY
elif name == 'max_transfer_timeout':
return MAX_TRANSFER_TIMEOUT
elif name == 'refresh_jitter':
return REFRESH_JITTER
elif name == 'reload_jitter':
return RELOAD_JITTER
elif name == 'secondary_zones':
return self.zone_list
else: else:
raise ValueError('Uknown config option') return "unknown", False
class MyZonemgrRefresh(ZonemgrRefresh): class MyZonemgrRefresh(ZonemgrRefresh):
def __init__(self): def __init__(self):
@@ -92,7 +80,7 @@ class MyZonemgrRefresh(ZonemgrRefresh):
sqlite3_ds.get_zone_soa = get_zone_soa sqlite3_ds.get_zone_soa = get_zone_soa
ZonemgrRefresh.__init__(self, MySession(), "initdb.file", ZonemgrRefresh.__init__(self, MySession(), "initdb.file",
self._slave_socket, FakeConfig()) self._slave_socket, FakeCCSession())
current_time = time.time() current_time = time.time()
self._zonemgr_refresh_info = { self._zonemgr_refresh_info = {
('example.net.', 'IN'): { ('example.net.', 'IN'): {
@@ -112,6 +100,7 @@ class TestZonemgrRefresh(unittest.TestCase):
self.stderr_backup = sys.stderr self.stderr_backup = sys.stderr
sys.stderr = open(os.devnull, 'w') sys.stderr = open(os.devnull, 'w')
self.zone_refresh = MyZonemgrRefresh() self.zone_refresh = MyZonemgrRefresh()
self.cc_session = FakeCCSession()
def test_random_jitter(self): def test_random_jitter(self):
max = 100025.120 max = 100025.120
@@ -458,7 +447,23 @@ class TestZonemgrRefresh(unittest.TestCase):
"secondary_zones": [ { "name": "example.net.", "secondary_zones": [ { "name": "example.net.",
"class": "IN" } ] "class": "IN" } ]
} }
self.zone_refresh.update_config_data(config_data) self.zone_refresh.update_config_data(config_data, self.cc_session)
self.assertTrue(("example.net.", "IN") in
self.zone_refresh._zonemgr_refresh_info)
# make sure it does fail if we don't provide a name
config_data = {
"secondary_zones": [ { "class": "IN" } ]
}
self.assertRaises(ZonemgrException,
self.zone_refresh.update_config_data,
config_data, self.cc_session)
# But not if we don't provide a class
config_data = {
"secondary_zones": [ { "name": "example.net." } ]
}
self.zone_refresh.update_config_data(config_data, self.cc_session)
self.assertTrue(("example.net.", "IN") in self.assertTrue(("example.net.", "IN") in
self.zone_refresh._zonemgr_refresh_info) self.zone_refresh._zonemgr_refresh_info)
@@ -471,7 +476,7 @@ class TestZonemgrRefresh(unittest.TestCase):
"reload_jitter" : 0.75, "reload_jitter" : 0.75,
"secondary_zones": [] "secondary_zones": []
} }
self.zone_refresh.update_config_data(config_data) self.zone_refresh.update_config_data(config_data, self.cc_session)
self.assertEqual(60, self.zone_refresh._lowerbound_refresh) self.assertEqual(60, self.zone_refresh._lowerbound_refresh)
self.assertEqual(30, self.zone_refresh._lowerbound_retry) self.assertEqual(30, self.zone_refresh._lowerbound_retry)
self.assertEqual(19800, self.zone_refresh._max_transfer_timeout) self.assertEqual(19800, self.zone_refresh._max_transfer_timeout)
@@ -482,7 +487,7 @@ class TestZonemgrRefresh(unittest.TestCase):
config_data = { config_data = {
"reload_jitter" : 0.35, "reload_jitter" : 0.35,
} }
self.zone_refresh.update_config_data(config_data) self.zone_refresh.update_config_data(config_data, self.cc_session)
self.assertEqual(60, self.zone_refresh._lowerbound_refresh) self.assertEqual(60, self.zone_refresh._lowerbound_refresh)
self.assertEqual(30, self.zone_refresh._lowerbound_retry) self.assertEqual(30, self.zone_refresh._lowerbound_retry)
self.assertEqual(19800, self.zone_refresh._max_transfer_timeout) self.assertEqual(19800, self.zone_refresh._max_transfer_timeout)
@@ -500,7 +505,7 @@ class TestZonemgrRefresh(unittest.TestCase):
"secondary_zones": [ { "name": "doesnotexist", "secondary_zones": [ { "name": "doesnotexist",
"class": "IN" } ] "class": "IN" } ]
} }
self.zone_refresh.update_config_data(config_data) self.zone_refresh.update_config_data(config_data, self.cc_session)
name_class = ("doesnotexist.", "IN") name_class = ("doesnotexist.", "IN")
self.assertTrue(self.zone_refresh._zonemgr_refresh_info[name_class]["zone_soa_rdata"] self.assertTrue(self.zone_refresh._zonemgr_refresh_info[name_class]["zone_soa_rdata"]
is None) is None)
@@ -520,7 +525,7 @@ class TestZonemgrRefresh(unittest.TestCase):
"reload_jitter" : 0.75, "reload_jitter" : 0.75,
"secondary_zones": [] "secondary_zones": []
} }
self.zone_refresh.update_config_data(config_data) self.zone_refresh.update_config_data(config_data, self.cc_session)
self.assertEqual(60, self.zone_refresh._lowerbound_refresh) self.assertEqual(60, self.zone_refresh._lowerbound_refresh)
self.assertEqual(30, self.zone_refresh._lowerbound_retry) self.assertEqual(30, self.zone_refresh._lowerbound_retry)
self.assertEqual(19800, self.zone_refresh._max_transfer_timeout) self.assertEqual(19800, self.zone_refresh._max_transfer_timeout)
@@ -536,46 +541,68 @@ class TestZonemgrRefresh(unittest.TestCase):
self.assertFalse(listener.is_alive()) self.assertFalse(listener.is_alive())
def test_secondary_zones(self): def test_secondary_zones(self):
def zone_list_from_name_classes(zones):
return map(lambda nc: {"name": nc[0], "class": nc[1]}, zones)
"""Test that we can modify the list of secondary zones""" """Test that we can modify the list of secondary zones"""
config = FakeConfig() config = self.cc_session.get_full_config()
config.zone_list = [] config['secondary_zones'] = []
# First, remove everything # First, remove everything
self.zone_refresh.update_config_data(config) self.zone_refresh.update_config_data(config, self.cc_session)
self.assertEqual(self.zone_refresh._zonemgr_refresh_info, {}) self.assertEqual(self.zone_refresh._zonemgr_refresh_info, {})
# Put something in # Put something in
config.set_zone_list_from_name_classes([ZONE_NAME_CLASS1_IN]) config['secondary_zones'] = \
self.zone_refresh.update_config_data(config) zone_list_from_name_classes([ZONE_NAME_CLASS1_IN])
self.zone_refresh.update_config_data(config, self.cc_session)
self.assertTrue(("example.net.", "IN") in self.assertTrue(("example.net.", "IN") in
self.zone_refresh._zonemgr_refresh_info) self.zone_refresh._zonemgr_refresh_info)
# This one does not exist # Reset the data, set to use a different class, and make sure
config.set_zone_list_from_name_classes(["example.net", "CH"]) # it does not get set to IN
self.zone_refresh.update_config_data(config) config['secondary_zones'] = \
self.assertFalse(("example.net.", "CH") in zone_list_from_name_classes([ZONE_NAME_CLASS1_CH])
self.zone_refresh._zonemgr_refresh_info) self.zone_refresh.update_config_data(config, self.cc_session)
# Simply skip loading soa for the zone, the other configs should be updated successful
self.assertFalse(("example.net.", "IN") in self.assertFalse(("example.net.", "IN") in
self.zone_refresh._zonemgr_refresh_info) self.zone_refresh._zonemgr_refresh_info)
# Make sure it works even when we "accidentally" forget the final dot # Make sure it works even when we "accidentally" forget the final dot
config.set_zone_list_from_name_classes([("example.net", "IN")]) config['secondary_zones'] = \
self.zone_refresh.update_config_data(config) zone_list_from_name_classes([("example.net", "IN")])
self.zone_refresh.update_config_data(config, self.cc_session)
self.assertTrue(("example.net.", "IN") in self.assertTrue(("example.net.", "IN") in
self.zone_refresh._zonemgr_refresh_info) self.zone_refresh._zonemgr_refresh_info)
# and with case-insensitive checking
config['secondary_zones'] = \
zone_list_from_name_classes([("Example.NeT.", "in")])
self.zone_refresh.update_config_data(config, self.cc_session)
self.assertTrue(("example.net.", "IN") in
self.zone_refresh._zonemgr_refresh_info)
# Try some bad names
config['secondary_zones'] = \
zone_list_from_name_classes([("example..net", "IN")])
self.assertRaises(ZonemgrException,
self.zone_refresh.update_config_data,
config, self.cc_session)
config['secondary_zones'] = \
zone_list_from_name_classes([("", "IN")])
self.assertRaises(ZonemgrException,
self.zone_refresh.update_config_data,
config, self.cc_session)
# Try a bad class
config['secondary_zones'] = \
zone_list_from_name_classes([("example.net", "BADCLASS")])
self.assertRaises(ZonemgrException,
self.zone_refresh.update_config_data,
config, self.cc_session)
config['secondary_zones'] = \
zone_list_from_name_classes([("example.net", "")])
self.assertRaises(ZonemgrException,
self.zone_refresh.update_config_data,
config, self.cc_session)
def tearDown(self): def tearDown(self):
sys.stderr= self.stderr_backup sys.stderr= self.stderr_backup
class MyCCSession():
def __init__(self):
pass
def get_remote_config_value(self, module_name, identifier):
if module_name == "Auth" and identifier == "database_file":
return "initdb.file", False
else:
return "unknown", False
class MyZonemgr(Zonemgr): class MyZonemgr(Zonemgr):
def __init__(self): def __init__(self):
@@ -583,7 +610,7 @@ class MyZonemgr(Zonemgr):
self._zone_refresh = None self._zone_refresh = None
self._shutdown_event = threading.Event() self._shutdown_event = threading.Event()
self._cc = MySession() self._cc = MySession()
self._module_cc = MyCCSession() self._module_cc = FakeCCSession()
self._config_data = { self._config_data = {
"lowerbound_refresh" : 10, "lowerbound_refresh" : 10,
"lowerbound_retry" : 5, "lowerbound_retry" : 5,
@@ -622,7 +649,7 @@ class TestZonemgr(unittest.TestCase):
self.assertEqual(0.5, self.zonemgr._config_data.get("refresh_jitter")) self.assertEqual(0.5, self.zonemgr._config_data.get("refresh_jitter"))
# The zone doesn't exist in database, simply skip loading soa for it and log an warning # The zone doesn't exist in database, simply skip loading soa for it and log an warning
self.zonemgr._zone_refresh = ZonemgrRefresh(None, "initdb.file", None, self.zonemgr._zone_refresh = ZonemgrRefresh(None, "initdb.file", None,
config_data1) FakeCCSession())
config_data1["secondary_zones"] = [{"name": "nonexistent.example", config_data1["secondary_zones"] = [{"name": "nonexistent.example",
"class": "IN"}] "class": "IN"}]
self.assertEqual(self.zonemgr.config_handler(config_data1), self.assertEqual(self.zonemgr.config_handler(config_data1),
@@ -660,4 +687,5 @@ class TestZonemgr(unittest.TestCase):
pass pass
if __name__== "__main__": if __name__== "__main__":
isc.log.resetUnitTestRootLogger()
unittest.main() unittest.main()

View File

@@ -28,6 +28,7 @@ import os
import time import time
import signal import signal
import isc import isc
import isc.dns
import random import random
import threading import threading
import select import select
@@ -98,7 +99,7 @@ class ZonemgrRefresh:
can be stopped by calling shutdown() in another thread. can be stopped by calling shutdown() in another thread.
""" """
def __init__(self, cc, db_file, slave_socket, config_data): def __init__(self, cc, db_file, slave_socket, module_cc_session):
self._cc = cc self._cc = cc
self._check_sock = slave_socket self._check_sock = slave_socket
self._db_file = db_file self._db_file = db_file
@@ -108,7 +109,8 @@ class ZonemgrRefresh:
self._max_transfer_timeout = None self._max_transfer_timeout = None
self._refresh_jitter = None self._refresh_jitter = None
self._reload_jitter = None self._reload_jitter = None
self.update_config_data(config_data) self.update_config_data(module_cc_session.get_full_config(),
module_cc_session)
self._running = False self._running = False
def _random_jitter(self, max, jitter): def _random_jitter(self, max, jitter):
@@ -424,7 +426,7 @@ class ZonemgrRefresh:
self._read_sock = None self._read_sock = None
self._write_sock = None self._write_sock = None
def update_config_data(self, new_config): def update_config_data(self, new_config, module_cc_session):
""" update ZonemgrRefresh config """ """ update ZonemgrRefresh config """
# Get a new value, but only if it is defined (commonly used below) # Get a new value, but only if it is defined (commonly used below)
# We don't use "value or default", because if value would be # We don't use "value or default", because if value would be
@@ -456,11 +458,42 @@ class ZonemgrRefresh:
if secondary_zones is not None: if secondary_zones is not None:
# Add new zones # Add new zones
for secondary_zone in new_config.get('secondary_zones'): for secondary_zone in new_config.get('secondary_zones'):
if 'name' not in secondary_zone:
raise ZonemgrException("Secondary zone specified "
"without a name")
name = secondary_zone['name'] name = secondary_zone['name']
# Be tolerant to sclerotic users who forget the final dot
if name[-1] != '.': # Convert to Name and back (both to check and to normalize)
name = name + '.' try:
name_class = (name, secondary_zone['class']) name = isc.dns.Name(name, True).to_text()
# Name() can raise a number of different exceptions, just
# catch 'em all.
except Exception as isce:
raise ZonemgrException("Bad zone name '" + name +
"': " + str(isce))
# Currently we use an explicit get_default_value call
# in case the class hasn't been set. Alternatively, we
# could use
# module_cc_session.get_value('secondary_zones[INDEX]/class')
# To get either the value that was set, or the default if
# it wasn't set.
# But the real solution would be to make new_config a type
# that contains default values itself
# (then this entire method can be simplified a lot, and we
# wouldn't need direct access to the ccsession object)
if 'class' in secondary_zone:
rr_class = secondary_zone['class']
else:
rr_class = module_cc_session.get_default_value(
'secondary_zones/class')
# Convert rr_class to and from RRClass to check its value
try:
name_class = (name, isc.dns.RRClass(rr_class).to_text())
except isc.dns.InvalidRRClass:
raise ZonemgrException("Bad RR class '" +
rr_class +
"' for zone " + name)
required[name_class] = True required[name_class] = True
# Add it only if it isn't there already # Add it only if it isn't there already
if not name_class in self._zonemgr_refresh_info: if not name_class in self._zonemgr_refresh_info:
@@ -485,7 +518,7 @@ class Zonemgr:
self._db_file = self.get_db_file() self._db_file = self.get_db_file()
# Create socket pair for communicating between main thread and zonemgr timer thread # Create socket pair for communicating between main thread and zonemgr timer thread
self._master_socket, self._slave_socket = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM) self._master_socket, self._slave_socket = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM)
self._zone_refresh = ZonemgrRefresh(self._cc, self._db_file, self._slave_socket, self._config_data) self._zone_refresh = ZonemgrRefresh(self._cc, self._db_file, self._slave_socket, self._module_cc)
self._zone_refresh.run_timer() self._zone_refresh.run_timer()
self._lock = threading.Lock() self._lock = threading.Lock()
@@ -540,7 +573,7 @@ class Zonemgr:
self._config_data_check(complete) self._config_data_check(complete)
if self._zone_refresh is not None: if self._zone_refresh is not None:
try: try:
self._zone_refresh.update_config_data(complete) self._zone_refresh.update_config_data(complete, self._module_cc)
except Exception as e: except Exception as e:
answer = create_answer(1, str(e)) answer = create_answer(1, str(e))
ok = False ok = False