Commit 454a8f66 authored by Jerry's avatar Jerry
Browse files

merge trac215(zone manager) and trac289(notify-out)



git-svn-id: svn://bind10.isc.org/svn/bind10/trunk@2736 e5f2f494-b856-4b98-b285-d166d9295462
parents de31fac6 7b7bee95
87. [func] zhanglikun
lib/python/isc/notifyout: Add the feature of notify-out, when
zone axfr/ixfr finishing, the server will notify its slaves.
(Trac #289, svn r2735)
86. [func] jerry
bin/zonemgr: Added zone manager module. The zone manager is one
of the co-operating processes of BIND10, which keeps track of
timers and other information necessary for BIND10 to act as a
slave. (Trac #215, svn r2735)
85. [build]* jinmei
Build programs using dynamic link by default. A new configure
option --enable-static-link is provided to force static link for
......
......@@ -411,6 +411,8 @@ AC_CONFIG_FILES([Makefile
src/bin/xfrin/tests/Makefile
src/bin/xfrout/Makefile
src/bin/xfrout/tests/Makefile
src/bin/zonemgr/Makefile
src/bin/zonemgr/tests/Makefile
src/bin/usermgr/Makefile
src/lib/Makefile
src/lib/bench/Makefile
......@@ -427,6 +429,8 @@ AC_CONFIG_FILES([Makefile
src/lib/python/isc/config/tests/Makefile
src/lib/python/isc/log/Makefile
src/lib/python/isc/log/tests/Makefile
src/lib/python/isc/notify/Makefile
src/lib/python/isc/notify/tests/Makefile
src/lib/config/Makefile
src/lib/config/tests/Makefile
src/lib/dns/Makefile
......@@ -453,6 +457,10 @@ AC_OUTPUT([src/bin/cfgmgr/b10-cfgmgr.py
src/bin/xfrout/xfrout.spec.pre
src/bin/xfrout/tests/xfrout_test
src/bin/xfrout/run_b10-xfrout.sh
src/bin/zonemgr/zonemgr.py
src/bin/zonemgr/zonemgr.spec.pre
src/bin/zonemgr/tests/zonemgr_test
src/bin/zonemgr/run_b10-zonemgr.sh
src/bin/bind10/bind10.py
src/bin/bind10/tests/bind10_test
src/bin/bind10/run_bind10.sh
......@@ -474,6 +482,7 @@ AC_OUTPUT([src/bin/cfgmgr/b10-cfgmgr.py
src/lib/python/isc/config/tests/config_test
src/lib/python/isc/cc/tests/cc_test
src/lib/python/isc/log/tests/log_test
src/lib/python/isc/notify/tests/notify_out_test
src/lib/dns/gen-rdatacode.py
src/lib/python/bind10_config.py
src/lib/dns/tests/testdata/gen-wiredata.py
......@@ -482,10 +491,12 @@ AC_OUTPUT([src/bin/cfgmgr/b10-cfgmgr.py
chmod +x src/bin/cmdctl/run_b10-cmdctl.sh
chmod +x src/bin/xfrin/run_b10-xfrin.sh
chmod +x src/bin/xfrout/run_b10-xfrout.sh
chmod +x src/bin/zonemgr/run_b10-zonemgr.sh
chmod +x src/bin/bind10/run_bind10.sh
chmod +x src/bin/cmdctl/tests/cmdctl_test
chmod +x src/bin/xfrin/tests/xfrin_test
chmod +x src/bin/xfrout/tests/xfrout_test
chmod +x src/bin/zonemgr/tests/zonemgr_test
chmod +x src/bin/bindctl/tests/bindctl_test
chmod +x src/bin/bindctl/run_bindctl.sh
chmod +x src/bin/loadzone/run_loadzone.sh
......
SUBDIRS = bind10 bindctl cfgmgr loadzone msgq host cmdctl auth xfrin xfrout usermgr
SUBDIRS = bind10 bindctl cfgmgr loadzone msgq host cmdctl auth xfrin xfrout usermgr zonemgr
......@@ -327,7 +327,6 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message, Message& message,
return (true);
}
bool
AuthSrvImpl::processAxfrQuery(const IOMessage& io_message, Message& message,
MessageRenderer& response_renderer)
......@@ -423,7 +422,7 @@ AuthSrvImpl::processNotify(const IOMessage& io_message, Message& message,
static const string command_template_start =
"{\"command\": [\"notify\", {\"zone_name\" : \"";
static const string command_template_master = "\", \"master\" : \"";
static const string command_template_rrclass = "\", \"rrclass\" : \"";
static const string command_template_rrclass = "\", \"zone_class\" : \"";
static const string command_template_end = "\"}]}";
try {
......@@ -433,7 +432,7 @@ AuthSrvImpl::processNotify(const IOMessage& io_message, Message& message,
command_template_rrclass + question->getClass().toText() +
command_template_end);
const unsigned int seq =
xfrin_session_->group_sendmsg(notify_command, "Xfrin",
xfrin_session_->group_sendmsg(notify_command, "Zonemgr",
"*", "*");
ElementPtr env, answer, parsed_answer;
xfrin_session_->group_recvmsg(env, answer, false, seq);
......@@ -441,14 +440,14 @@ AuthSrvImpl::processNotify(const IOMessage& io_message, Message& message,
parsed_answer = parseAnswer(rcode, answer);
if (rcode != 0) {
if (verbose_mode_) {
cerr << "[b10-auth] failed to notify Xfrin: "
cerr << "[b10-auth] failed to notify Zonemgr: "
<< parsed_answer->str() << endl;
}
return (false);
}
} catch (const Exception& ex) {
if (verbose_mode_) {
cerr << "[b10-auth] failed to notify Xfrin: " << ex.what() << endl;
cerr << "[b10-auth] failed to notify Zonemgr: " << ex.what() << endl;
}
return (false);
}
......
......@@ -398,6 +398,26 @@ class BoB:
sys.stdout.write("[bind10] Started b10-xfrin (PID %d)\n" %
xfrind.pid)
# start b10-zonemgr
zonemgr_args = ['b10-zonemgr']
if self.verbose:
sys.stdout.write("[bind10] Starting b10-zonemgr\n")
zonemgr_args += ['-v']
try:
zonemgr = ProcessInfo("b10-zonemgr", zonemgr_args,
c_channel_env)
except Exception as e:
c_channel.process.kill()
bind_cfgd.process.kill()
xfrout.process.kill()
auth.process.kill()
xfrind.process.kill()
return "Unable to start b10-zonemgr; " + str(e)
self.processes[zonemgr.pid] = zonemgr
if self.verbose:
sys.stdout.write("[bind10] Started b10-zonemgr(PID %d)\n" %
zonemgr.pid)
# start the b10-cmdctl
# XXX: we hardcode port 8080
cmdctl_args = ['b10-cmdctl']
......@@ -413,6 +433,7 @@ class BoB:
xfrout.process.kill()
auth.process.kill()
xfrind.process.kill()
zonemgr.process.kill()
return "Unable to start b10-cmdctl; " + str(e)
self.processes[cmd_ctrld.pid] = cmd_ctrld
if self.verbose:
......@@ -431,6 +452,7 @@ class BoB:
self.cc_session.group_sendmsg(cmd, "Boss", "Auth")
self.cc_session.group_sendmsg(cmd, "Boss", "Xfrout")
self.cc_session.group_sendmsg(cmd, "Boss", "Xfrin")
self.cc_session.group_sendmsg(cmd, "Boss", "Zonemgr")
def stop_process(self, process):
"""Stop the given process, friendly-like."""
......
......@@ -20,7 +20,7 @@ export PYTHON_EXEC
BIND10_PATH=@abs_top_builddir@/src/bin/bind10
PATH=@abs_top_builddir@/src/bin/msgq:@abs_top_builddir@/src/bin/auth:@abs_top_builddir@/src/bin/cfgmgr:@abs_top_builddir@/src/bin/cmdctl:@abs_top_builddir@/src/bin/xfrin:@abs_top_builddir@/src/bin/xfrout:$PATH
PATH=@abs_top_builddir@/src/bin/msgq:@abs_top_builddir@/src/bin/auth:@abs_top_builddir@/src/bin/cfgmgr:@abs_top_builddir@/src/bin/cmdctl:@abs_top_builddir@/src/bin/xfrin:@abs_top_builddir@/src/bin/xfrout:@abs_top_builddir@/src/bin/zonemgr:$PATH
export PATH
PYTHONPATH=@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/xfr/.libs
......
......@@ -63,6 +63,9 @@ class MockXfrin(Xfrin):
def _cc_setup(self):
pass
def _get_db_file(self):
pass
def _cc_check_command(self):
self._shutdown_event.set()
......@@ -408,11 +411,16 @@ class TestXfrin(unittest.TestCase):
def tearDown(self):
self.xfr.shutdown()
def _do_parse(self):
return self.xfr._parse_cmd_params(self.args)
def _do_parse_zone_name_class(self):
return self.xfr._parse_zone_name_and_class(self.args)
def _do_parse_master_port(self):
return self.xfr._parse_master_and_port(self.args)
def test_parse_cmd_params(self):
name, rrclass, master_addrinfo, db_file = self._do_parse()
name, rrclass = self._do_parse_zone_name_class()
master_addrinfo = self._do_parse_master_port()
db_file = self.args.get('db_file')
self.assertEqual(master_addrinfo[4][1], int(TEST_MASTER_PORT))
self.assertEqual(name, TEST_ZONE_NAME)
self.assertEqual(rrclass, TEST_RRCLASS)
......@@ -421,49 +429,50 @@ class TestXfrin(unittest.TestCase):
def test_parse_cmd_params_default_port(self):
del self.args['port']
master_addrinfo = self._do_parse()[2]
master_addrinfo = self._do_parse_master_port()
self.assertEqual(master_addrinfo[4][1], 53)
def test_parse_cmd_params_ip6master(self):
self.args['master'] = TEST_MASTER_IPV6_ADDRESS
master_addrinfo = self._do_parse()[2]
master_addrinfo = self._do_parse_master_port()
self.assertEqual(master_addrinfo[4][0], TEST_MASTER_IPV6_ADDRESS)
def test_parse_cmd_params_chclass(self):
self.args['rrclass'] = 'CH'
self.assertEqual(self._do_parse()[1], RRClass.CH())
self.args['zone_class'] = 'CH'
self.assertEqual(self._do_parse_zone_name_class()[1], RRClass.CH())
def test_parse_cmd_params_bogusclass(self):
self.args['rrclass'] = 'XXX'
self.assertRaises(XfrinException, self._do_parse)
self.args['zone_class'] = 'XXX'
self.assertRaises(XfrinException, self._do_parse_zone_name_class)
def test_parse_cmd_params_nozone(self):
# zone name is mandatory.
del self.args['zone_name']
self.assertRaises(XfrinException, self._do_parse)
self.assertRaises(XfrinException, self._do_parse_zone_name_class)
def test_parse_cmd_params_nomaster(self):
# master address is mandatory.
del self.args['master']
self.assertRaises(XfrinException, self._do_parse)
master_addrinfo = self._do_parse_master_port()
self.assertEqual(master_addrinfo[4][0], DEFAULT_MASTER)
def test_parse_cmd_params_bad_ip4(self):
self.args['master'] = '3.3.3.3.3'
self.assertRaises(XfrinException, self._do_parse)
self.assertRaises(XfrinException, self._do_parse_master_port)
def test_parse_cmd_params_bad_ip6(self):
self.args['master'] = '1::1::1'
self.assertRaises(XfrinException, self._do_parse)
self.assertRaises(XfrinException, self._do_parse_master_port)
def test_parse_cmd_params_bad_port(self):
self.args['port'] = '-1'
self.assertRaises(XfrinException, self._do_parse)
self.assertRaises(XfrinException, self._do_parse_master_port)
self.args['port'] = '65536'
self.assertRaises(XfrinException, self._do_parse)
self.assertRaises(XfrinException, self._do_parse_master_port)
self.args['port'] = 'http'
self.assertRaises(XfrinException, self._do_parse)
self.assertRaises(XfrinException, self._do_parse_master_port)
def test_command_handler_shutdown(self):
self.assertEqual(self.xfr.command_handler("shutdown",
......@@ -518,11 +527,32 @@ class TestXfrin(unittest.TestCase):
self.args['master'] = TEST_MASTER_IPV6_ADDRESS
# ...but right now we disable the feature due to security concerns.
self.assertEqual(self.xfr.command_handler("notify",
self.args)['result'][0], 1)
self.args)['result'][0], 0)
def test_command_handler_unknown(self):
self.assertEqual(self.xfr.command_handler("xxx", None)['result'][0], 1)
def test_command_handler_transfers_in(self):
self.assertEqual(self.xfr.config_handler({})['result'][0], 0)
self.assertEqual(self.xfr.config_handler({'transfers_in': 3})['result'][0], 0)
self.assertEqual(self.xfr._max_transfers_in, 3)
def test_command_handler_masters(self):
master_info = {'master_addr': '1.1.1.1', 'master_port':53}
self.assertEqual(self.xfr.config_handler(master_info)['result'][0], 0)
master_info = {'master_addr': '1111.1.1.1', 'master_port':53 }
self.assertEqual(self.xfr.config_handler(master_info)['result'][0], 1)
master_info = {'master_addr': '2.2.2.2', 'master_port':530000 }
self.assertEqual(self.xfr.config_handler(master_info)['result'][0], 1)
master_info = {'master_addr': '2.2.2.2', 'master_port':53 }
self.xfr.config_handler(master_info)
self.assertEqual(self.xfr._master_addr, '2.2.2.2')
self.assertEqual(self.xfr._master_port, 53)
def raise_interrupt():
raise KeyboardInterrupt()
......
......@@ -28,6 +28,7 @@ import socket
import random
from optparse import OptionParser, OptionValueError
from isc.config.ccsession import *
from isc.notify import notify_out
try:
from libdns_python import *
except ImportError as e:
......@@ -49,13 +50,17 @@ else:
SPECFILE_LOCATION = SPECFILE_PATH + "/xfrin.spec"
AUTH_SPECFILE_LOCATION = AUTH_SPECFILE_PATH + "/auth.spec"
XFROUT_MODULE_NAME = 'Xfrout'
ZONE_MANAGER_MODULE_NAME = 'Zonemgr'
REFRESH_FROM_ZONEMGR = 'refresh_from_zonemgr'
ZONE_XFRIN_FAILED = 'zone_xfrin_failed'
__version__ = 'BIND10'
# define xfrin rcode
XFRIN_OK = 0
XFRIN_FAIL = 1
DEFAULT_MASTER_PORT = '53'
DEFAULT_MASTER = '127.0.0.1'
def log_error(msg):
sys.stderr.write("[b10-xfrin] %s\n" % str(msg))
......@@ -316,14 +321,15 @@ class XfrinConnection(asyncore.dispatcher):
sys.stdout.write('[b10-xfrin] %s\n' % str(msg))
def process_xfrin(xfrin_recorder, zone_name, rrclass, db_file,
def process_xfrin(server, xfrin_recorder, zone_name, rrclass, db_file,
shutdown_event, master_addrinfo, check_soa, verbose):
xfrin_recorder.increment(zone_name)
sock_map = {}
conn = XfrinConnection(sock_map, zone_name, rrclass, db_file,
shutdown_event, master_addrinfo, verbose)
if conn.connect_to_master():
conn.do_xfrin(check_soa)
ret = conn.do_xfrin(check_soa)
server.publish_xfrin_news(zone_name, rrclass, ret)
xfrin_recorder.decrement(zone_name)
......@@ -358,32 +364,54 @@ class XfrinRecorder:
class Xfrin:
def __init__(self, verbose = False):
self._cc_setup()
self._max_transfers_in = 10
#TODO, this is the temp way to set the zone's master.
self._master_addr = DEFAULT_MASTER
self._master_port = DEFAULT_MASTER_PORT
self._cc_setup()
self.recorder = XfrinRecorder()
self._shutdown_event = threading.Event()
self._verbose = verbose
def _cc_setup(self):
'''
This method is used only as part of initialization, but is implemented
separately for convenience of unit tests; by letting the test code override
this method we can test most of this class without requiring a command channel.
'''
self._cc = isc.config.ModuleCCSession(SPECFILE_LOCATION,
'''This method is used only as part of initialization, but is
implemented separately for convenience of unit tests; by letting
the test code override this method we can test most of this class
without requiring a command channel.'''
# Create one session for sending command to other modules, because the
# listening session will block the send operation.
self._send_cc_session = isc.cc.Session()
self._module_cc = isc.config.ModuleCCSession(SPECFILE_LOCATION,
self.config_handler,
self.command_handler)
self._cc.start()
self._module_cc.start()
config_data = self._module_cc.get_full_config()
self._max_transfers_in = config_data.get("transfers_in")
self._master_addr = config_data.get('master_addr') or self._master_addr
self._master_port = config_data.get('master_port') or self._master_port
def _cc_check_command(self):
'''
This is a straightforward wrapper for cc.check_command, but provided as
a separate method for the convenience of unit tests.
'''
self._cc.check_command()
'''This is a straightforward wrapper for cc.check_command,
but provided as a separate method for the convenience
of unit tests.'''
self._module_cc.check_command()
def config_handler(self, new_config):
# TODO, process new config data
self._max_transfers_in = new_config.get("transfers_in") or self._max_transfers_in
if ('master_addr' in new_config) or ('master_port' in new_config):
# Check if the new master is valid, there should be library for check it.
# and user should change the port and address together.
try:
addr = new_config.get('master_addr') or self._master_addr
port = new_config.get('master_port') or self._master_port
check_addr_port(addr, port)
self._master_addr = addr
self._master_port = port
except:
errmsg = "bad format for zone's master: " + str(new_config)
log_error(errmsg)
return create_answer(1, errmsg)
return create_answer(0)
def shutdown(self):
......@@ -397,95 +425,97 @@ a separate method for the convenience of unit tests.
continue
th.join()
def command_handler(self, command, args):
answer = create_answer(0)
try:
if command == 'shutdown':
self._shutdown_event.set()
elif command == 'retransfer' or command == 'refresh':
(zone_name, rrclass,
master_addr, db_file) = self._parse_cmd_params(args)
ret = self.xfrin_start(zone_name, rrclass, db_file,
elif command == 'notify' or command == REFRESH_FROM_ZONEMGR:
# Xfrin receives the refresh/notify command from zone manager.
# notify command maybe has the parameters which
# specify the notifyfrom address and port, according the RFC1996, zone
# transfer should starts first from the notifyfrom, but now, let 'TODO' it.
(zone_name, rrclass) = self._parse_zone_name_and_class(args)
(master_addr) = check_addr_port(self._master_addr, self._master_port)
ret = self.xfrin_start(zone_name,
rrclass,
self._get_db_file(),
master_addr,
False if command == 'retransfer' else True)
True)
answer = create_answer(ret[0], ret[1])
elif command == 'notify':
# This is the temporary implementation for notify.
# actually the notfiy command should be sent to the
# Zone Manager module. Being temporary, we separate this case
# from refresh/retransfer while we could (and should otherwise)
# share the code.
(zone_name, rrclass,
master_addr, db_file) = self._parse_cmd_params(args)
# XXX: master_addr is the sender of the notify message.
# It's very dangerous to naively trust it as the source of
# subsequent zone transfer; any remote node can easily exploit
# it to mount zone poisoning or DoS attacks. We should
# locally identify the appropriate set of master servers.
# For now, we disable the code below.
master_is_valid = False
if master_is_valid:
ret = self.xfrin_start(zone_name, rrclass, db_file,
master_addr, True)
else:
errmsg = 'Failed to validate the master address ('
errmsg += args['master'] + '), ignoring notify'
ret = [1, errmsg]
elif command == 'retransfer' or command == 'refresh':
# Xfrin receives the retransfer/refresh from cmdctl(sent by bindctl).
# If the command has specified master address, do transfer from the
# master address, or else do transfer from the configured masters.
(zone_name, rrclass) = self._parse_zone_name_and_class(args)
master_addr = self._parse_master_and_port(args)
db_file = args.get('db_file') or self._get_db_file()
ret = self.xfrin_start(zone_name,
rrclass,
db_file,
master_addr,
(False if command == 'retransfer' else True))
answer = create_answer(ret[0], ret[1])
else:
answer = create_answer(1, 'unknown command: ' + command)
except XfrinException as err:
log_error('error happened for command: %s, %s' % (command, str(err)) )
answer = create_answer(1, str(err))
return answer
def _parse_cmd_params(self, args):
def _parse_zone_name_and_class(self, args):
zone_name = args.get('zone_name')
if not zone_name:
raise XfrinException('zone name should be provided')
rrclass = args.get('rrclass')
rrclass = args.get('zone_class')
if not rrclass:
# The default RR class is IN. We should fix this so that
# the class is always passed in the command arg (where we specify
# the default)
rrclass = RRClass.IN()
else:
try:
rrclass = RRClass(rrclass)
except InvalidRRClass as e:
raise XfrinException('invalid RRClass: ' + rrclass)
master = args.get('master')
if not master:
raise XfrinException('master address should be provided')
port_str = args.get('port')
if not port_str:
port_str = DEFAULT_MASTER_PORT
master_addrinfo = check_addr_port(master, port_str)
db_file = args.get('db_file')
if not db_file:
#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._cc.add_remote_config(AUTH_SPECFILE_LOCATION)
db_file, is_default = self._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._cc.remove_remote_config(AUTH_SPECFILE_LOCATION)
return (zone_name, rrclass, master_addrinfo, db_file)
return zone_name, rrclass
def _parse_master_and_port(self, args):
port = args.get('port') or self._master_port
master = args.get('master') or self._master_addr
return check_addr_port(master, 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")
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
def publish_xfrin_news(self, zone_name, zone_class, xfr_result):
'''Send command to xfrout/zone manager module.
If xfrin has finished successfully for one zone, tell the good
news(command: zone_new_data_ready) to zone manager and xfrout.
if xfrin failed, just tell the bad news to zone manager, so that
it can reset the refresh timer for that zone. '''
param = {'zone_name': zone_name, 'zone_class': zone_class.to_text()}
if xfr_result == XFRIN_OK:
msg = create_command(notify_out.ZONE_NEW_DATA_READY_CMD, param)
self._send_cc_session.group_sendmsg(msg, XFROUT_MODULE_NAME)
self._send_cc_session.group_sendmsg(msg, ZONE_MANAGER_MODULE_NAME)
else:
msg = create_command(ZONE_XFRIN_FAILED, param)
self._send_cc_session.group_sendmsg(msg, ZONE_MANAGER_MODULE_NAME)
def startup(self):
while not self._shutdown_event.is_set():
......@@ -504,7 +534,8 @@ a separate method for the convenience of unit tests.
return (1, 'zone xfrin is in progress')
xfrin_thread = threading.Thread(target = process_xfrin,
args = (self.recorder,
args = (self,
self.recorder,
zone_name, rrclass,
db_file,
self._shutdown_event,
......
......@@ -8,6 +8,17 @@
"item_type": "integer",
"item_optional": false,
"item_default": 10
},
{
"item_name": "master_addr",
"item_type": "string",
"item_optional": false,
"item_default": ""
},
{ "item_name": "master_port",
"item_type": "integer",
"item_optional": false,
"item_default": 53
}
],
"commands": [
......@@ -20,10 +31,16 @@
"item_optional": false,
"item_default": ""
},
{
"item_name": "zone_class",
"item_type": "string",
"item_optional": true,
"item_default": "IN"
},
{
"item_name": "master",
"item_type": "string",
"item_optional": false,
"item_optional": true,
"item_default": ""
},
{
......
......@@ -28,6 +28,7 @@ import os
from isc.config.ccsession import *
from isc.log.log import *
from isc.cc import SessionError
from isc.notify import notify_out
import socket
import select
import errno
......@@ -303,7 +304,7 @@ class UnixSockServer(ThreadingUnixStreamServer):
self._log = log
self.update_config_data(config_data)
self._cc = cc
def finish_request(self, request, client_address):
'''Finish one request by instantiating RequestHandlerClass.'''
self.RequestHandlerClass(request, client_address, self, self._log)
......@@ -415,16 +416,25 @@ class XfroutServer:
self._config_data.get('log_severity'), self._config_data.get('log_versions'),
self._config_data.get('log_max_bytes'), True)
self._start_xfr_query_listener()
self._start_notifier()
def _start_xfr_query_listener(self):
'''Start a new thread to accept xfr query. '''