Commit 8cc8f4c0 authored by JINMEI Tatuya's avatar JINMEI Tatuya
Browse files

[1165] added new configuration data: "zone_config". While the name is

generic, the intent is to use it for transfer ACL per zone.  The notion of
per-zone configuration should eventually be implemented in a more generic
way (not specific to xfrout).
parent 842fc917
......@@ -636,17 +636,17 @@ class TestUnixSockServer(unittest.TestCase):
socket.AI_NUMERICHOST)[0][4])
self.assertEqual(isc.acl.acl.ACCEPT, self.unix._acl.execute(context))
def check_loaded_ACL(self):
def check_loaded_ACL(self, acl):
context = isc.acl.dns.RequestContext(socket.getaddrinfo("127.0.0.1",
1234, 0, socket.SOCK_DGRAM,
socket.IPPROTO_UDP,
socket.AI_NUMERICHOST)[0][4])
self.assertEqual(isc.acl.acl.ACCEPT, self.unix._acl.execute(context))
self.assertEqual(isc.acl.acl.ACCEPT, acl.execute(context))
context = isc.acl.dns.RequestContext(socket.getaddrinfo("192.0.2.1",
1234, 0, socket.SOCK_DGRAM,
socket.IPPROTO_UDP,
socket.AI_NUMERICHOST)[0][4])
self.assertEqual(isc.acl.acl.REJECT, self.unix._acl.execute(context))
self.assertEqual(isc.acl.acl.REJECT, acl.execute(context))
def test_update_config_data(self):
self.check_default_ACL()
......@@ -673,12 +673,77 @@ class TestUnixSockServer(unittest.TestCase):
# Load the ACL
self.unix.update_config_data({'query_acl': [{'from': '127.0.0.1',
'action': 'ACCEPT'}]})
self.check_loaded_ACL()
self.check_loaded_ACL(self.unix._acl)
# Pass a wrong data there and check it does not replace the old one
self.assertRaises(isc.acl.acl.LoaderError,
self.unix.update_config_data,
{'query_acl': ['Something bad']})
self.check_loaded_ACL()
self.check_loaded_ACL(self.unix._acl)
def test_zone_config_data(self):
# By default, there's no specific zone config
self.assertEqual({}, self.unix._zone_config)
# Adding config for a specific zone. The config is empty unless
# explicitly specified.
self.unix.update_config_data({'zone_config':
[{'origin': 'example.com',
'class': 'IN'}]})
self.assertEqual({}, self.unix._zone_config[('IN', 'example.com.')])
# zone class can be omitted
self.unix.update_config_data({'zone_config':
[{'origin': 'example.com'}]})
self.assertEqual({}, self.unix._zone_config[('IN', 'example.com.')])
# zone class, name are stored in the "normalized" form. class
# strings are upper cased, names are down cased.
self.unix.update_config_data({'zone_config':
[{'origin': 'EXAMPLE.com'}]})
self.assertEqual({}, self.unix._zone_config[('IN', 'example.com.')])
# invalid zone class, name will result in exceptions
self.assertRaises(EmptyLabel,
self.unix.update_config_data,
{'zone_config': [{'origin': 'bad..example'}]})
self.assertRaises(InvalidRRClass,
self.unix.update_config_data,
{'zone_config': [{'origin': 'example.com',
'class': 'badclass'}]})
# Configuring a couple of more zones
self.unix.update_config_data({'zone_config':
[{'origin': 'example.com'},
{'origin': 'example.com',
'class': 'CH'},
{'origin': 'example.org'}]})
self.assertEqual({}, self.unix._zone_config[('IN', 'example.com.')])
self.assertEqual({}, self.unix._zone_config[('CH', 'example.com.')])
self.assertEqual({}, self.unix._zone_config[('IN', 'example.org.')])
# Duplicate data: should be rejected with an exception
self.assertRaises(ValueError,
self.unix.update_config_data,
{'zone_config': [{'origin': 'example.com'},
{'origin': 'example.org'},
{'origin': 'example.com'}]})
def test_zone_config_data_with_acl(self):
# Similar to the previous test, but with transfer_acl config
self.unix.update_config_data({'zone_config':
[{'origin': 'example.com',
'transfer_acl':
[{'from': '127.0.0.1',
'action': 'ACCEPT'}]}]})
acl = self.unix._zone_config[('IN', 'example.com.')]['transfer_acl']
self.check_loaded_ACL(acl)
# invalid ACL syntax will be rejected with exception
self.assertRaises(isc.acl.acl.LoaderError,
self.unix.update_config_data,
{'zone_config': [{'origin': 'example.com',
'transfer_acl':
[{'action': 'BADACTION'}]}]})
def test_get_db_file(self):
self.assertEqual(self.unix.get_db_file(), "initdb.file")
......
......@@ -86,6 +86,9 @@ TSIG_SIGN_EVERY_NTH = 96
XFROUT_MAX_MESSAGE_SIZE = 65535
# In practice, RR class is almost always fixed, so if and when we allow
# it to be configured, it's convenient to make it optional.
DEFAULT_RRCLASS = RRClass('IN')
def get_rrset_len(rrset):
"""Returns the wire length of the given RRset"""
......@@ -401,10 +404,11 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
def _common_init(self):
self._lock = threading.Lock()
self._transfers_counter = 0
# This default value will probably get overwritten by the (same)
# default value from the spec file. This is here just to make
# sure and to make the default value in tests consistent.
# These default values will probably get overwritten by the (same)
# default value from the spec file. These are here just to make
# sure and to make the default values in tests consistent.
self._acl = REQUEST_LOADER.load('[{"action": "ACCEPT"}]')
self._zone_config = {}
def _receive_query_message(self, sock):
''' receive request message from sock'''
......@@ -551,16 +555,49 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
pass
def update_config_data(self, new_config):
'''Apply the new config setting of xfrout module. '''
'''Apply the new config setting of xfrout module.
Note: this method does not provide strong exception guarantee;
if an exception is raised in the middle of parsing and building the
given config data, the incomplete set of new configuration will
remain. This should be fixed.
'''
logger.info(XFROUT_NEW_CONFIG)
if 'query_acl' in new_config:
self._acl = REQUEST_LOADER.load(new_config['query_acl'])
if 'zone_config' in new_config:
self._zone_config = \
self.__create_zone_config(new_config.get('zone_config'))
self._lock.acquire()
self._max_transfers_out = new_config.get('transfers_out')
self.set_tsig_key_ring(new_config.get('tsig_key_ring'))
self._lock.release()
logger.info(XFROUT_NEW_CONFIG_DONE)
def __create_zone_config(self, zone_config_list):
new_config = {}
for zconf in zone_config_list:
# convert the class, origin (name) pair. First build pydnspp
# object to reject invalid input.
if 'class' in zconf:
zclass = RRClass(zconf['class'])
else:
zclass = DEFAULT_RRCLASS
zorigin = Name(zconf['origin'], True)
config_key = (zclass.to_text(), zorigin.to_text())
# reject duplicate config
if config_key in new_config:
raise ValueError('Duplicaet zone_config for ' +
str(zorigin) + '/' + str(zclass))
# create a new config entry, build any given (and known) config
new_config[config_key] = {}
if 'transfer_acl' in zconf:
new_config[config_key]['transfer_acl'] = \
REQUEST_LOADER.load(zconf['transfer_acl'])
return new_config
def set_tsig_key_ring(self, key_list):
"""Set the tsig_key_ring , given a TSIG key string list representation. """
......
Supports Markdown
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