Commit c029db4f authored by Likun Zhang's avatar Likun Zhang
Browse files

Refactor the code of xfrin and add config item 'masters' to xfrin, make sure...

Refactor the code of xfrin and add config item 'masters' to xfrin, make sure when xfrin get the notify command from zone manager, the zone transfer will be triggered.

git-svn-id: svn://bind10.isc.org/svn/bind10/branches/trac289@2628 e5f2f494-b856-4b98-b285-d166d9295462
parent 447bebe7
......@@ -362,18 +362,19 @@ 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._masters = {}
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.
'''
'''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()
......@@ -381,16 +382,32 @@ this method we can test most of this class without requiring a command channel.
self.config_handler,
self.command_handler)
self._module_cc.start()
config_data = self._module_cc.get_full_config()
self._max_transfers_in = config_data.get("transfers_in")
self._masters = config_data.get('masters')
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.
'''
'''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
new_master = new_config.get('masters')
if new_master:
# Check if the new master is valid, there should be library for check it.
# and user should change the port and address together.
try:
new_master['address'] = new_master.get('address') or self._masters.get('address')
new_master['port'] = new_master.get('port') or self._masters.get('port')
check_addr_port(new_master.get('address'), new_master.get('port'))
self._masters = new_master
except:
errmsg = "bad format for zone's master: " + str(new_master)
log_error(errmsg)
return create_answer(1, errmsg)
return create_answer(0)
def shutdown(self):
......@@ -404,67 +421,32 @@ 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,
master_addr,
False if command == 'retransfer' else 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 == 'notify' or \
command == 'retransfer' or \
command == 'refresh':
# Xfrin maybe receives the refresh/notify command from zone manager, or
# 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.
# 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, master_addr, db_file) = self._parse_cmd_params(args)
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:
answer = create_answer(1, str(err))
return answer
def publish_xfrin_news(self, zone_name, 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}
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 _parse_cmd_params(self, args):
zone_name = args.get('zone_name')
if not zone_name:
......@@ -472,9 +454,6 @@ a separate method for the convenience of unit tests.
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:
......@@ -482,15 +461,19 @@ a separate method for the convenience of unit tests.
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)
master = args.get('master')
if not master:
if len(self._masters) > 0:
master = self._masters.get('address')
port_str = self._masters.get('port')
else:
raise XfrinException("zone's master should be provided")
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
......@@ -509,6 +492,21 @@ a separate method for the convenience of unit tests.
return (zone_name, rrclass, master_addrinfo, db_file)
def publish_xfrin_news(self, zone_name, 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}
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():
self._cc_check_command()
......
......@@ -8,7 +8,25 @@
"item_type": "integer",
"item_optional": false,
"item_default": 10
}
},
{
"item_name": "masters",
"item_type": "map",
"item_optional": true,
"item_default": {},
"map_item_spec": [
{ "item_name": "address",
"item_type": "string",
"item_optional": false,
"item_default": ""
},
{ "item_name": "port",
"item_type": "integer",
"item_optional": false,
"item_default": 53
}
]
}
],
"commands": [
{
......@@ -29,7 +47,7 @@
{
"item_name": "master",
"item_type": "string",
"item_optional": false,
"item_optional": true,
"item_default": ""
},
{
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment