# pretend it just succeeds
pass
+class MockDataSrcClientsMgr():
+ def __init__(self):
+ # Default faked result of get_client_list, customizable by tests
+ self.found_datasrc_client_list = self
+
+ # Default faked result of find(), customizable by tests
+ self.found_datasrc_client = MockDataSourceClient()
+
+ def get_client_list(self, rrclass):
+ return self.found_datasrc_client_list
+
+ def find(self, zone_name, want_exact_match, want_finder):
+ """Pretending find method on the object returned by get_clinet_list"""
+ if issubclass(type(self.found_datasrc_client), Exception):
+ raise self.found_datasrc_client
+ return self.found_datasrc_client, None, None
+
class MockXfrin(Xfrin):
# This is a class attribute of a callable object that specifies a non
# default behavior triggered in _cc_check_command(). Specific test methods
# redirect output
self.stderr_backup = sys.stderr
sys.stderr = open(os.devnull, 'w')
+ self.__orig_DataSrcClientsMgr = xfrin.DataSrcClientsMgr
+ xfrin.DataSrcClientsMgr = MockDataSrcClientsMgr
+
self.xfr = MockXfrin()
self.args = {}
self.args['zone_name'] = TEST_ZONE_NAME_STR
self.args['tsig_key'] = ''
def tearDown(self):
+ xfrin.DataSrcClientsMgr = self.__orig_DataSrcClientsMgr
self.assertFalse(self.xfr._module_cc.stopped);
self.xfr.shutdown()
self.assertTrue(self.xfr._module_cc.stopped);
def test_command_handler_retransfer(self):
self.assertEqual(self.xfr.command_handler("retransfer",
self.args)['result'][0], 0)
- self.assertEqual(self.args['master'], self.xfr.xfrin_started_master_addr)
- self.assertEqual(int(self.args['port']), self.xfr.xfrin_started_master_port)
- # By default we use AXFR (for now)
+ self.assertEqual(self.args['master'],
+ self.xfr.xfrin_started_master_addr)
+ self.assertEqual(int(self.args['port']),
+ self.xfr.xfrin_started_master_port)
+ # retransfer always uses AXFR
self.assertEqual(ZoneInfo.REQUEST_IXFR_DISABLED,
self.xfr.xfrin_started_request_ixfr)
# sys.modules is global, so we must recover it
sys.modules['pydnspp'] = dns_module
+ def test_command_handler_retransfer_datasrc_error(self):
+ # Failure cases due to various errors at the data source (config/data)
+ # level
+
+ # No data source client list for the RR class
+ self.xfr._datasrc_clients_mgr.found_datasrc_client_list = None
+ self.assertEqual(1, self.xfr.command_handler("retransfer",
+ self.args)['result'][0])
+
+ # No data source client for the zone name
+ self.xfr._datasrc_clients_mgr.found_datasrc_client_list = \
+ self.xfr._datasrc_clients_mgr # restore the original
+ self.xfr._datasrc_clients_mgr.found_datasrc_client = None
+ self.assertEqual(1, self.xfr.command_handler("retransfer",
+ self.args)['result'][0])
+
+ # list.find() raises an exception
+ self.xfr._datasrc_clients_mgr.found_datasrc_client = \
+ isc.datasrc.Error('test exception')
+ self.assertEqual(1, self.xfr.command_handler("retransfer",
+ self.args)['result'][0])
+
+ # datasrc.find() raises an exception
+ class RaisingkDataSourceClient(MockDataSourceClient):
+ def find_zone(self, zone_name):
+ raise isc.datasrc.Error('test exception')
+ self.xfr._datasrc_clients_mgr.found_datasrc_client = \
+ RaisingkDataSourceClient()
+ self.assertEqual(1, self.xfr.command_handler("retransfer",
+ self.args)['result'][0])
+
def test_command_handler_refresh(self):
# at this level, refresh is no different than retransfer.
# just confirm the successful case with a different family of address.
from isc.xfrin.diff import Diff
from isc.server_common.auth_command import auth_loadzone_command
from isc.server_common.tsig_keyring import init_keyring, get_keyring
+from isc.server_common.datasrc_clients_mgr import DataSrcClientsMgr, ConfigError
from isc.log_messages.xfrin_messages import *
from isc.dns import *
self.recorder = XfrinRecorder()
self._shutdown_event = threading.Event()
self._counters = Counters(SPECFILE_LOCATION)
+ # This is essentially private, but we allow tests to customize it.
+ self._datasrc_clients_mgr = DataSrcClientsMgr()
# Initial configuration
self._cc_setup()
old_max_transfers_in = self._max_transfers_in
old_zones = self._zones
- self._max_transfers_in = new_config.get("transfers_in") or self._max_transfers_in
+ self._max_transfers_in = \
+ new_config.get("transfers_in") or self._max_transfers_in
if 'zones' in new_config:
self._clear_zone_info()
self._set_db_file()
def _datasrc_config_handler(self, new_config, config_data):
- pass
+ self._datasrc_clients_mgr.reconfigure(new_config)
def shutdown(self):
''' shutdown the xfrin process. the thread which is doing xfrin should be
if self.recorder.xfrin_in_progress(zone_name):
return (1, 'zone xfrin is in progress')
- # Create a data source client used in this XFR session. Right now we
- # still assume an sqlite3-based data source, and use both the old and
- # new data source APIs. We also need to use a mock client for tests.
- # For a temporary workaround to deal with these situations, we skip the
- # creation when the given file is none (the test case). Eventually
- # this code will be much cleaner.
+ # Identify the data source to which the zone content is transferred,
+ # and get the current zone SOA from the data source (if available).
datasrc_client = None
- if db_file is not None:
- # temporary hardcoded sqlite initialization. Once we decide on
- # the config specification, we need to update this (TODO)
- # this may depend on #1207, or any follow-up ticket created for
- # #1207
- datasrc_type = "sqlite3"
- datasrc_config = "{ \"database_file\": \"" + db_file + "\"}"
- datasrc_client = DataSourceClient(datasrc_type, datasrc_config)
-
- # Get the current zone SOA (if available).
- zone_soa = _get_zone_soa(datasrc_client, zone_name, rrclass)
+ clist = self._datasrc_clients_mgr.get_client_list(rrclass)
+ if clist is None:
+ return (1, 'no data source is configured for class %s' % rrclass)
+
+ try:
+ datasrc_client = clist.find(zone_name, True, False)[0]
+ if datasrc_client is None: # can happen, so log it separately.
+ logger.error(XFRIN_NO_DATASRC,
+ format_zone_str(zone_name, rrclass))
+ return (1, 'no data source for transferring %s' %
+ format_zone_str(zone_name, rrclass))
+ zone_soa = _get_zone_soa(datasrc_client, zone_name, rrclass)
+ except isc.datasrc.Error as ex: # rare case error, convert (so logged)
+ raise XfrinException('unexpected failure in datasrc module: ' +
+ str(ex))
xfrin_thread = threading.Thread(target=process_xfrin,
args=(self, self.recorder,
"""
# datasrc_client should never be None in production case (only tests could
# specify None)
+ # TBD: this CAN NOW BE CLEANED UP
if datasrc_client is None:
return None
is ignored. This may indicate that the configuration for the master is wrong,
that a wrong machine is sending notifies, or that fake notifies are being sent.
+% XFRIN_NO_DATASRC no data source for transferring %1
+The xfrin daemon received a command that would trigger a transfer,
+but could not find a data source where the specified zone belong.
+There can be several reasons for this error: it may be a simple
+misspelling in the xfrin or zonemgr configuration, or in the user
+supplied parameter if it is triggered by an external command (such as
+from bindctl). Another possibility is that this is the initial transfer
+for newly setup secondary zone. In this case at least an initial empty zone
+must be created in one of configured data sources. This can be done by
+the -e option of b10-loadzone.
+
% XFRIN_RECEIVED_COMMAND received command: %1
The xfrin daemon received a command on the command channel.