BIND 10 trac1429, updated. 20a6000a9f69476797477ca7af5fd83b8e236909 [1429] The drop_socket command
BIND 10 source code commits
bind10-changes at lists.isc.org
Wed Nov 30 12:18:48 UTC 2011
The branch, trac1429 has been updated
via 20a6000a9f69476797477ca7af5fd83b8e236909 (commit)
via 1485c897a9e2c71ed2a33c8972c116a5f7e8e078 (commit)
via b7ac17da5405582098e98ed22bf122fe87658923 (commit)
via b8d14d2e45ee719e4e33adbecddafb4ae3aa4df1 (commit)
via 567260cde6e21499ad4bf47789d538a929df5552 (commit)
from b092df6f17e5d8f8f07e726fc4006e346417d49f (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 20a6000a9f69476797477ca7af5fd83b8e236909
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date: Tue Nov 29 19:29:37 2011 +0100
[1429] The drop_socket command
commit 1485c897a9e2c71ed2a33c8972c116a5f7e8e078
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date: Tue Nov 29 19:14:36 2011 +0100
[1429] Error handling in _get_socket
commit b7ac17da5405582098e98ed22bf122fe87658923
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date: Tue Nov 29 18:49:13 2011 +0100
[1429] _get_socket without error checking
commit b8d14d2e45ee719e4e33adbecddafb4ae3aa4df1
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date: Tue Nov 29 17:56:11 2011 +0100
[1429] The command_handler handles get_socket
commit 567260cde6e21499ad4bf47789d538a929df5552
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date: Tue Nov 29 15:18:01 2011 +0100
[1429] The socket_request_handler
-----------------------------------------------------------------------
Summary of changes:
src/bin/bind10/bind10_src.py.in | 56 +++++++++-
src/bin/bind10/tests/bind10_test.py.in | 191 ++++++++++++++++++++++++++++++++
2 files changed, 246 insertions(+), 1 deletions(-)
-----------------------------------------------------------------------
diff --git a/src/bin/bind10/bind10_src.py.in b/src/bin/bind10/bind10_src.py.in
index a780843..ae59904 100755
--- a/src/bin/bind10/bind10_src.py.in
+++ b/src/bin/bind10/bind10_src.py.in
@@ -73,6 +73,7 @@ from isc.log_messages.bind10_messages import *
import isc.bind10.component
import isc.bind10.special_component
import isc.bind10.socket_cache
+import libutil_io_python
isc.log.init("b10-boss")
logger = isc.log.Logger("boss")
@@ -243,6 +244,8 @@ class BoB:
if self.verbose:
logger.set_severity("DEBUG", 99)
self._socket_cache = None
+ # TODO: To be filled in by #1428
+ self._socket_path = None
def __propagate_component_config(self, config):
comps = dict(config)
@@ -317,6 +320,18 @@ class BoB:
elif command == "show_processes":
answer = isc.config.ccsession. \
create_answer(0, self.get_processes())
+ elif command == "get_socket":
+ answer = self._get_socket(args)
+ elif command == "drop_socket":
+ if "token" not in args:
+ answer = isc.config.ccsession. \
+ create_answer(1, "Missing token parameter")
+ else:
+ try:
+ self._socket_cache.drop_socket(args["token"])
+ answer = isc.config.ccsession.create_answer(0)
+ except Exception as e:
+ answer = isc.config.ccsession.create_answer(1, str(e))
else:
answer = isc.config.ccsession.create_answer(1,
"Unknown command")
@@ -814,13 +829,51 @@ class BoB:
return next_restart_time
+ def _get_socket(self, args):
+ """
+ Implementation of the get_socket CC command. It asks the cache
+ to provide the token and sends the information back.
+ """
+ try:
+ try:
+ addr = isc.net.parse.addr_parse(args['address'])
+ port = isc.net.parse.port_parse(args['port'])
+ protocol = args['protocol']
+ if protocol not in ['UDP', 'TCP']:
+ raise ValueError("Protocol must be either UDP or TCP")
+ share_mode = args['share_mode']
+ if share_mode not in ['ANY', 'SAMEAPP', 'NO']:
+ raise ValueError("Share mode must be one of ANY, SAMEAPP" +
+ "or NO")
+ share_name = args['share_name']
+ except KeyError as ke:
+ return \
+ isc.config.ccsession.create_answer(1,
+ "Missing parameter " +
+ str(ke))
+
+ token = self._socket_cache.get_token(protocol, addr, port,
+ share_mode, share_name)
+ return isc.config.ccsession.create_answer(0, {
+ 'token': token,
+ 'path': self._socket_path
+ })
+ except Exception as e:
+ return isc.config.ccsession.create_answer(1, str(e))
+
def socket_request_handler(self, token, unix_socket):
"""
This function handles a token that comes over a unix_domain socket.
The function looks into the _socket_cache and sends the socket
identified by the tocken back over the unix_socket.
"""
- pass
+ try:
+ fd = self._socket_cache.get_socket(token, unix_socket.fileno())
+ unix_socket.sendall("1\n")
+ libutil_io_python.send_fd(unix_socket.fileno(), fd)
+ except Exception as e:
+ # TODO Log
+ unix_socket.sendall("0\n")
def socket_consumer_dead(self, unix_socket):
"""
@@ -828,6 +881,7 @@ class BoB:
sockets sent to it are to be considered closed. This function signals
so to the _socket_cache.
"""
+ # TODO Log
self._socket_cache.drop_application(unix_socket.fileno())
def insert_creator(self, creator):
diff --git a/src/bin/bind10/tests/bind10_test.py.in b/src/bin/bind10/tests/bind10_test.py.in
index 41b430b..bc2ab7a 100644
--- a/src/bin/bind10/tests/bind10_test.py.in
+++ b/src/bin/bind10/tests/bind10_test.py.in
@@ -14,6 +14,7 @@
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
from bind10_src import ProcessInfo, BoB, parse_args, dump_pid, unlink_pid_file, _BASETIME
+import bind10_src
# XXX: environment tests are currently disabled, due to the preprocessor
# setup that we have now complicating the environment
@@ -112,20 +113,48 @@ class TestCacheCommands(unittest.TestCase):
# Fake the cache here so we can pretend it is us and hijack the
# calls to its methods.
self.__boss._socket_cache = self
+ self.__boss._socket_path = '/socket/path'
+ self.__raise_exception = None
+ self.__socket_args = {
+ "port": 53,
+ "address": "0.0.0.0",
+ "protocol": "UDP",
+ "share_mode": "ANY",
+ "share_name": "app"
+ }
# What was and wasn't called.
self.__drop_app_called = None
+ self.__get_socket_called = None
+ self.__send_fd_called = None
+ self.__get_token_called = None
+ self.__drop_socket_called = None
+ bind10_src.libutil_io_python.send_fd = self.__send_fd
+
+ def __send_fd(self, to, socket):
+ """
+ A function to hook the send_fd in the bind10_src.
+ """
+ self.__send_fd_called = (to, socket)
class FalseSocket:
"""
A socket where we can fake methods we need instead of having a real
socket.
"""
+ def __init__(self):
+ self.send = ""
def fileno(self):
"""
The file number. Used for identifying the remote application.
"""
return 42
+ def sendall(self, data):
+ """
+ Adds data to the self.send.
+ """
+ self.send += data
+
def drop_application(self, application):
"""
Part of pretending to be the cache. Logs the parameter to
@@ -140,6 +169,147 @@ class TestCacheCommands(unittest.TestCase):
self.__boss.socket_consumer_dead(self.FalseSocket())
self.assertEqual(42, self.__drop_app_called)
+ def get_socket(self, token, application):
+ """
+ Part of pretending to be the cache. If there's anything in
+ __raise_exception, it is raised. Otherwise, the call is logged
+ into __get_socket_called and a number is returned.
+ """
+ if self.__raise_exception is not None:
+ raise self.__raise_exception
+ self.__get_socket_called = (token, application)
+ return 13
+
+ def test_request_handler(self):
+ """
+ Test that a request for socket is forwarded and the socket is sent
+ back, if it returns a socket.
+ """
+ socket = self.FalseSocket()
+ # An exception from the cache
+ self.__raise_exception = ValueError("Test value error")
+ self.__boss.socket_request_handler("token", socket)
+ # It was called, but it threw, so it is not noted here
+ self.assertIsNone(self.__get_socket_called)
+ self.assertEqual("0\n", socket.send)
+ # It should not have sent any socket.
+ self.assertIsNone(self.__send_fd_called)
+ # Now prepare a valid scenario
+ self.__raise_exception = None
+ socket.send = ""
+ self.__boss.socket_request_handler("token", socket)
+ self.assertEqual("1\n", socket.send)
+ self.assertEqual((42, 13), self.__send_fd_called)
+ self.assertEqual(("token", 42), self.__get_socket_called)
+
+ def get_token(self, protocol, address, port, share_mode, share_name):
+ """
+ Part of pretending to be the cache. If there's anything in
+ __raise_exception, it is raised. Otherwise, the parameters are
+ logged into __get_token_called and a token is returned.
+ """
+ if self.__raise_exception is not None:
+ raise self.__raise_exception
+ self.__get_token_called = (protocol, address, port, share_mode,
+ share_name)
+ return "token"
+
+ def test_get_socket_ok(self):
+ """
+ Test the successful scenario of getting a socket.
+ """
+ result = self.__boss._get_socket(self.__socket_args)
+ [code, answer] = result['result']
+ self.assertEqual(0, code)
+ self.assertEqual({
+ 'token': 'token',
+ 'path': '/socket/path'
+ }, answer)
+ addr = self.__get_token_called[1]
+ self.assertIsInstance(addr, IPAddr)
+ self.assertEqual("0.0.0.0", str(addr))
+ self.assertEqual(("UDP", addr, 53, "ANY", "app"),
+ self.__get_token_called)
+
+ def test_get_socket_error(self):
+ """
+ Test that bad inputs are handled correctly, etc.
+ """
+ def check_code(code, args):
+ """
+ Pass the args there and check if it returs success or not.
+
+ The rest is not tested, as it is already checked in the
+ test_get_socket_ok.
+ """
+ [rcode, ranswer] = self.__boss._get_socket(args)['result']
+ self.assertEqual(code, rcode)
+ if code == 1:
+ # This should be an error message. The exact formatting
+ # is unknown, but we check it is string at last
+ self.assertIsInstance(ranswer, str)
+ def mod_args(name, value):
+ """
+ Override a parameter in the args.
+ """
+ result = dict(self.__socket_args)
+ result[name] = value
+ return result
+
+ # Port too large
+ check_code(1, mod_args('port', 65536))
+ # Not numeric address
+ check_code(1, mod_args('address', 'example.org.'))
+ # Some bad values of enum-like params
+ check_code(1, mod_args('protocol', 'BAD PROTO'))
+ check_code(1, mod_args('share_mode', 'BAD SHARE'))
+ # Check missing parameters
+ for param in self.__socket_args.keys():
+ args = dict(self.__socket_args)
+ del args[param]
+ check_code(1, args)
+ # These are OK values for the enum-like parameters
+ # The ones from test_get_socket_ok are not tested here
+ check_code(0, mod_args('protocol', 'TCP'))
+ check_code(0, mod_args('share_mode', 'SAMEAPP'))
+ check_code(0, mod_args('share_mode', 'NO'))
+ # If an exception is raised from within the cache, it is converted
+ # to an error, not propagated
+ self.__raise_exception = Exception("Test exception")
+ check_code(1, self.__socket_args)
+
+ def drop_socket(self, token):
+ """
+ Part of pretending to be the cache. If there's anything in
+ __raise_exception, it is raised. Otherwise, the parameter is stored
+ in __drop_socket_called.
+ """
+ if self.__raise_exception is not None:
+ raise self.__raise_exception
+ self.__drop_socket_called = token
+
+ def test_drop_socket(self):
+ """
+ Check the drop_socket command. It should directly call the method
+ on the cache. Exceptions should be translated to error messages.
+ """
+ # This should be OK and just propagated to the call.
+ self.assertEqual({"result": [0]},
+ self.__boss.command_handler("drop_socket",
+ {"token": "token"}))
+ self.assertEqual("token", self.__drop_socket_called)
+ self.__drop_socket_called = None
+ # Missing parameter
+ self.assertEqual({"result": [1, "Missing token parameter"]},
+ self.__boss.command_handler("drop_socket", {}))
+ self.assertIsNone(self.__drop_socket_called)
+ # An exception is raised from within the cache
+ self.__raise_exception = ValueError("Test error")
+ self.assertEqual({"result": [1, "Test error"]},
+ self.__boss.command_handler("drop_socket",
+ {"token": "token"}))
+
+
class TestBoB(unittest.TestCase):
def test_init(self):
bob = BoB()
@@ -241,6 +411,27 @@ class TestBoB(unittest.TestCase):
self.assertEqual(bob.command_handler("__UNKNOWN__", None),
isc.config.ccsession.create_answer(1, "Unknown command"))
+ # Fake the _get_socket, which is complicated and tested elsewhere
+ # We just want to pass the parameters in and let it create a response
+ def get_socket(args):
+ return isc.config.ccsession.create_answer(0, args)
+
+ bob._get_socket = get_socket
+ args = {
+ "port": 53,
+ "address": "0.0.0.0",
+ "protocol": "UDP",
+ "share_mode": "ANY",
+ "share_name": "app"
+ }
+ # Test it just returns whatever it got. The real function doesn't
+ # work like this, but we don't want the command_handler to touch it
+ # at all and this is the easiest way to check.
+ self.assertEqual({'result': [0, args]},
+ bob.command_handler("get_socket", args))
+ # The drop_socket is not tested here, but in TestCacheCommands.
+ # It needs the cache mocks to be in place and they are there.
+
# Class for testing the BoB without actually starting processes.
# This is used for testing the start/stop components routines and
# the BoB commands.
More information about the bind10-changes
mailing list