BIND 10 trac800, updated. c62810c526d75363ed4d668bbdb6b21a5a294a7b [trac800] Makefile fixes
BIND 10 source code commits
bind10-changes at lists.isc.org
Mon Jul 25 16:39:06 UTC 2011
The branch, trac800 has been updated
via c62810c526d75363ed4d668bbdb6b21a5a294a7b (commit)
via 0710846d8d7a38079b9570aeec9abfb94341af79 (commit)
via 9517f61cb8ad4f8074b5e6e33c663ca9ed581908 (commit)
from 3da7e8747dcea9b45c8bc4c17b946be7d5ff9576 (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 c62810c526d75363ed4d668bbdb6b21a5a294a7b
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date: Mon Jul 25 18:38:53 2011 +0200
[trac800] Makefile fixes
commit 0710846d8d7a38079b9570aeec9abfb94341af79
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date: Mon Jul 25 18:15:43 2011 +0200
[trac800] Fix tests depending on boss
Why do they include boss?
commit 9517f61cb8ad4f8074b5e6e33c663ca9ed581908
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date: Mon Jul 25 18:02:06 2011 +0200
[trac800] Actually starting the creator
-----------------------------------------------------------------------
Summary of changes:
configure.ac | 1 +
src/bin/bind10/bind10_messages.mes | 9 +++++
src/bin/bind10/bind10_src.py.in | 34 +++++++++++++++++++-
src/bin/bind10/sockcreator.py | 31 ++++++++++++++++++
src/bin/bind10/tests/Makefile.am | 1 -
src/bin/bind10/tests/bind10_test.py.in | 8 +++++
...{sockcreator_test.py => sockcreator_test.py.in} | 3 ++
src/bin/dhcp6/tests/Makefile.am | 2 +-
src/bin/dhcp6/tests/dhcp6_test.py | 2 +-
9 files changed, 87 insertions(+), 4 deletions(-)
rename src/bin/bind10/tests/{sockcreator_test.py => sockcreator_test.py.in} (98%)
-----------------------------------------------------------------------
diff --git a/configure.ac b/configure.ac
index 9ee5348..a554db1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -905,6 +905,7 @@ AC_OUTPUT([doc/version.ent
src/bin/bind10/bind10_src.py
src/bin/bind10/run_bind10.sh
src/bin/bind10/tests/bind10_test.py
+ src/bin/bind10/tests/sockcreator_test.py
src/bin/bindctl/run_bindctl.sh
src/bin/bindctl/bindctl_main.py
src/bin/bindctl/tests/bindctl_test
diff --git a/src/bin/bind10/bind10_messages.mes b/src/bin/bind10/bind10_messages.mes
index 392b6c7..a596147 100644
--- a/src/bin/bind10/bind10_messages.mes
+++ b/src/bin/bind10/bind10_messages.mes
@@ -189,3 +189,12 @@ the given file number.
Either sending or receiving data from the socket creator failed with the given
error. The creator probably crashed or some serious OS-level problem happened,
as the communication happens only on local host.
+
+% BIND10_SOCKCREATOR_CRASHED the socket creator crashed
+The socket creator terminated unexpectadly. It is not possible to restart it
+(because the boss already gave up root privileges), so the system is going
+to terminate.
+
+% BIND10_SOCKCREATOR_KILL killing the socket creator
+The socket creator is being terminated the aggressive way, by sending it
+sigkill. This should not happen usually.
diff --git a/src/bin/bind10/bind10_src.py.in b/src/bin/bind10/bind10_src.py.in
index a624383..bbb17a2 100755
--- a/src/bin/bind10/bind10_src.py.in
+++ b/src/bin/bind10/bind10_src.py.in
@@ -67,6 +67,7 @@ import isc.util.process
import isc.net.parse
import isc.log
from bind10_messages import *
+import bind10.sockcreator
isc.log.init("b10-boss")
logger = isc.log.Logger("boss")
@@ -248,6 +249,7 @@ class BoB:
self.config_filename = config_filename
self.cmdctl_port = cmdctl_port
self.brittle = brittle
+ self.sockcreator = None
def config_handler(self, new_config):
# If this is initial update, don't do anything now, leave it to startup
@@ -333,6 +335,20 @@ class BoB:
"Unknown command")
return answer
+ def start_creator(self):
+ self.curproc = 'b10-sockcreator'
+ self.sockcreator = bind10.sockcreator.Creator("@@LIBEXECDIR@@:" +
+ os.environ['PATH'])
+
+ def stop_creator(self, kill=False):
+ if self.sockcreator is None:
+ return
+ if kill:
+ self.sockcreator.kill()
+ else:
+ self.sockcreator.terminate()
+ self.sockcreator = None
+
def kill_started_processes(self):
"""
Called as part of the exception handling when a process fails to
@@ -341,6 +357,8 @@ class BoB:
"""
logger.info(BIND10_KILLING_ALL_PROCESSES)
+ self.stop_creator(True)
+
for pid in self.processes:
logger.info(BIND10_KILL_PROCESS, self.processes[pid].name)
self.processes[pid].process.kill()
@@ -571,6 +589,11 @@ class BoB:
Starts up all the processes. Any exception generated during the
starting of the processes is handled by the caller.
"""
+ # The socket creator first, as it is the only thing that needs root
+ self.start_creator()
+ # TODO: Once everything uses the socket creator, we can drop root
+ # privileges right now
+
c_channel_env = self.c_channel_env
self.start_msgq(c_channel_env)
self.start_cfgmgr(c_channel_env)
@@ -660,6 +683,8 @@ class BoB:
self.cc_session.group_sendmsg(cmd, "Zonemgr", "Zonemgr")
self.cc_session.group_sendmsg(cmd, "Stats", "Stats")
self.cc_session.group_sendmsg(cmd, "StatsHttpd", "StatsHttpd")
+ # Terminate the creator last
+ self.stop_creator()
def stop_process(self, process, recipient):
"""
@@ -746,7 +771,14 @@ class BoB:
# XXX: should be impossible to get any other error here
raise
if pid == 0: break
- if pid in self.processes:
+ if self.sockcreator is not None and self.sockcreator.pid() == pid:
+ # This is the socket creator, started and terminated
+ # differently. This can't be restarted.
+ if self.runnable:
+ logger.fatal(BIND10_SOCKCREATOR_CRASHED)
+ self.sockcreator = None
+ self.runnable = False
+ elif pid in self.processes:
# One of the processes we know about. Get information on it.
proc_info = self.processes.pop(pid)
proc_info.restart_schedule.set_run_stop_time()
diff --git a/src/bin/bind10/sockcreator.py b/src/bin/bind10/sockcreator.py
index 0bf750f..baccc95 100644
--- a/src/bin/bind10/sockcreator.py
+++ b/src/bin/bind10/sockcreator.py
@@ -16,6 +16,7 @@
import socket
import struct
import os
+import subprocess
from bind10_messages import *
from libutil_io_python import recv_fd
@@ -191,3 +192,33 @@ class WrappedSocket:
Read the file descriptor from the socket.
"""
return recv_fd(self.fileno())
+
+# FIXME: Any idea how to test this? Starting an external process doesn't sound
+# OK
+class Creator(Parser):
+ """
+ This starts the socket creator and allows asking for the sockets.
+ """
+ def __init__(self, path):
+ (local, remote) = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM)
+ # Popen does not like, for some reason, having the same socket for
+ # stdin as well as stdout, so we dup it before passing it there.
+ remote2 = socket.fromfd(remote.fileno(), socket.AF_UNIX,
+ socket.SOCK_STREAM)
+ env = os.environ
+ env['PATH'] = path
+ self.__process = subprocess.Popen(['b10-sockcreator'], env=env,
+ stdin=remote.fileno(),
+ stdout=remote2.fileno())
+ remote.close()
+ remote2.close()
+ Parser.__init__(self, WrappedSocket(local))
+
+ def pid(self):
+ return self.__process.pid
+
+ def kill(self):
+ logger.warn(BIND10_SOCKCREATOR_KILL)
+ if self.__process is not None:
+ self.__process.kill()
+ self.__process = None
diff --git a/src/bin/bind10/tests/Makefile.am b/src/bin/bind10/tests/Makefile.am
index 1cbd841..4a40ec8 100644
--- a/src/bin/bind10/tests/Makefile.am
+++ b/src/bin/bind10/tests/Makefile.am
@@ -2,7 +2,6 @@ PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
#PYTESTS = args_test.py bind10_test.py
# NOTE: this has a generated test found in the builddir
PYTESTS = bind10_test.py sockcreator_test.py
-EXTRA_DIST = $(PYTESTS)
# If necessary (rare cases), explicitly specify paths to dynamic libraries
# required by loadable python modules.
diff --git a/src/bin/bind10/tests/bind10_test.py.in b/src/bin/bind10/tests/bind10_test.py.in
index 6b87133..077190c 100644
--- a/src/bin/bind10/tests/bind10_test.py.in
+++ b/src/bin/bind10/tests/bind10_test.py.in
@@ -193,6 +193,13 @@ class MockBob(BoB):
self.cmdctl = False
self.c_channel_env = {}
self.processes = { }
+ self.creator = False
+
+ def start_creator(self):
+ self.creator = True
+
+ def stop_creator(self, kill=False):
+ self.creator = False
def read_bind10_config(self):
# Configuration options are set directly
@@ -337,6 +344,7 @@ class TestStartStopProcessesBob(unittest.TestCase):
self.assertEqual(bob.msgq, core)
self.assertEqual(bob.cfgmgr, core)
self.assertEqual(bob.ccsession, core)
+ self.assertEqual(bob.creator, core)
self.assertEqual(bob.auth, auth)
self.assertEqual(bob.resolver, resolver)
self.assertEqual(bob.xfrout, auth)
diff --git a/src/bin/bind10/tests/sockcreator_test.py b/src/bin/bind10/tests/sockcreator_test.py
deleted file mode 100644
index c863034..0000000
--- a/src/bin/bind10/tests/sockcreator_test.py
+++ /dev/null
@@ -1,312 +0,0 @@
-# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-#
-# Permission to use, copy, modify, and distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
-# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
-# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
-# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
-# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
-# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
-# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-"""
-Tests for the bind10.sockcreator module.
-"""
-
-import unittest
-import struct
-import socket
-from isc.net.addr import IPAddr
-import isc.log
-from libutil_io_python import send_fd
-from bind10.sockcreator import Parser, CreatorError, WrappedSocket
-
-class FakeCreator:
- """
- Class emulating the socket to the socket creator. It can be given expected
- data to receive (and check) and responses to give to the Parser class
- during testing.
- """
-
- class InvalidPlan(Exception):
- """
- Raised when someone wants to recv when sending is planned or vice
- versa.
- """
- pass
-
- class InvalidData(Exception):
- """
- Raises when the data passed to sendall are not the same as expected.
- """
- pass
-
- def __init__(self, plan):
- """
- Create the object. The plan variable contains list of expected actions,
- in form:
-
- [('r', 'Data to return from recv'), ('s', 'Data expected on sendall'),
- , ('d', 'File descriptor number to return from read_sock'), ('e',
- None), ...]
-
- It modifies the array as it goes.
- """
- self.__plan = plan
-
- def __get_plan(self, expected):
- if len(self.__plan) == 0:
- raise InvalidPlan('Nothing more planned')
- (kind, data) = self.__plan[0]
- if kind == 'e':
- self.__plan.pop(0)
- raise socket.error('False socket error')
- if kind != expected:
- raise InvalidPlan('Planned ' + kind + ', but ' + expected +
- 'requested')
- return data
-
- def recv(self, maxsize):
- """
- Emulate recv. Returs maxsize bytes from the current recv plan. If
- there are data left from previous recv call, it is used first.
-
- If no recv is planned, raises InvalidPlan.
- """
- data = self.__get_plan('r')
- result, rest = data[:maxsize], data[maxsize:]
- if len(rest) > 0:
- self.__plan[0] = ('r', rest)
- else:
- self.__plan.pop(0)
- return result
-
- def read_fd(self):
- """
- Emulate the reading of file descriptor. Returns one from a plan.
-
- It raises InvalidPlan if no socket is planned now.
- """
- fd = self.__get_plan('f')
- self.__plan.pop(0)
- return fd
-
- def sendall(self, data):
- """
- Checks that the data passed are correct according to plan. It raises
- InvalidData if the data differs or InvalidPlan when sendall is not
- expected.
- """
- planned = self.__get_plan('s')
- dlen = len(data)
- prefix, rest = planned[:dlen], planned[dlen:]
- if prefix != data:
- raise InvalidData('Expected "' + str(prefix)+ '", got "' +
- str(data) + '"')
- if len(rest) > 0:
- self.__plan[0] = ('s', rest)
- else:
- self.__plan.pop(0)
-
- def all_used(self):
- """
- Returns if the whole plan was consumed.
- """
- return len(self.__plan) == 0
-
-class ParserTests(unittest.TestCase):
- """
- Testcases for the Parser class.
- """
- def __terminate(self):
- creator = FakeCreator([('s', b'T'), ('r', b'')])
- parser = Parser(creator)
- self.assertEqual(None, parser.terminate())
- self.assertTrue(creator.all_used())
- return parser
-
- def test_terminate(self):
- """
- Test if the command to terminate is correct and it waits for reading the
- EOF.
- """
- self.__terminate()
-
- def test_terminate_error1(self):
- """
- Test it reports an exception when there's error terminating the creator.
- This one raises an error when receiving the EOF.
- """
- creator = FakeCreator([('s', b'T'), ('e', None)])
- parser = Parser(creator)
- with self.assertRaises(CreatorError) as cm:
- parser.terminate()
- self.assertTrue(cm.exception.fatal)
- self.assertEqual(None, cm.exception.errno)
-
- def test_terminate_error2(self):
- """
- Test it reports an exception when there's error terminating the creator.
- This one raises an error when sending data.
- """
- creator = FakeCreator([('e', None)])
- parser = Parser(creator)
- with self.assertRaises(CreatorError) as cm:
- parser.terminate()
- self.assertTrue(cm.exception.fatal)
- self.assertEqual(None, cm.exception.errno)
-
- def test_terminate_twice(self):
- """
- Test we can't terminate twice.
- """
- parser = self.__terminate()
- with self.assertRaises(CreatorError) as cm:
- parser.terminate()
- self.assertTrue(cm.exception.fatal)
- self.assertEqual(None, cm.exception.errno)
-
- def test_terminate_error3(self):
- """
- Test it reports an exception when there's error terminating the creator.
- This one sends data when it should have terminated.
- """
- creator = FakeCreator([('s', b'T'), ('r', b'Extra data')])
- parser = Parser(creator)
- with self.assertRaises(CreatorError) as cm:
- parser.terminate()
- self.assertTrue(cm.exception.fatal)
- self.assertEqual(None, cm.exception.errno)
-
- def test_crash(self):
- """
- Tests that the parser correctly raises exception when it crashes
- unexpectedly.
- """
- creator = FakeCreator([('s', b'SU4\0\0\0\0\0\0'), ('r', b'')])
- parser = Parser(creator)
- with self.assertRaises(CreatorError) as cm:
- parser.get_socket(IPAddr('0.0.0.0'), 0, 'UDP')
- self.assertTrue(creator.all_used())
- # Is the exception correct?
- self.assertTrue(cm.exception.fatal)
- self.assertEqual(None, cm.exception.errno)
-
- def test_error(self):
- """
- Tests that the parser correctly raises non-fatal exception when
- the socket can not be created.
- """
- # We split the int to see if it can cope with data coming in
- # different packets
- intpart = struct.pack('@i', 42)
- creator = FakeCreator([('s', b'SU4\0\0\0\0\0\0'), ('r', b'ES' +
- intpart[:1]), ('r', intpart[1:])])
- parser = Parser(creator)
- with self.assertRaises(CreatorError) as cm:
- parser.get_socket(IPAddr('0.0.0.0'), 0, 'UDP')
- self.assertTrue(creator.all_used())
- # Is the exception correct?
- self.assertFalse(cm.exception.fatal)
- self.assertEqual(42, cm.exception.errno)
-
- def __error(self, plan):
- creator = FakeCreator(plan)
- parser = Parser(creator)
- with self.assertRaises(CreatorError) as cm:
- parser.get_socket(IPAddr('0.0.0.0'), 0, socket.SOCK_DGRAM)
- self.assertTrue(creator.all_used())
- self.assertTrue(cm.exception.fatal)
-
- def test_error_send(self):
- self.__error([('e', None)])
-
- def test_error_recv(self):
- self.__error([('s', b'SU4\0\0\0\0\0\0'), ('e', None)])
-
- def test_error_read_fd(self):
- self.__error([('s', b'SU4\0\0\0\0\0\0'), ('r', b'S'), ('e', None)])
-
- def __create(self, addr, socktype, encoded):
- creator = FakeCreator([('s', b'S' + encoded), ('r', b'S'), ('f', 42)])
- parser = Parser(creator)
- self.assertEqual(42, parser.get_socket(IPAddr(addr), 42, socktype))
-
- def test_create1(self):
- self.__create('192.0.2.0', 'UDP', b'U4\0\x2A\xC0\0\x02\0')
-
- def test_create2(self):
- self.__create('2001:db8::', socket.SOCK_STREAM,
- b'T6\0\x2A\x20\x01\x0d\xb8\0\0\0\0\0\0\0\0\0\0\0\0')
-
- def test_create_terminated(self):
- """
- Test we can't request sockets after it was terminated.
- """
- parser = self.__terminate()
- with self.assertRaises(CreatorError) as cm:
- parser.get_socket(IPAddr('0.0.0.0'), 0, 'UDP')
- self.assertTrue(cm.exception.fatal)
- self.assertEqual(None, cm.exception.errno)
-
- def test_invalid_socktype(self):
- """
- Test invalid socket type is rejected
- """
- self.assertRaises(ValueError, Parser(FakeCreator([])).get_socket,
- IPAddr('0.0.0.0'), 42, 'RAW')
-
- def test_invalid_family(self):
- """
- Test it rejects invalid address family.
- """
- # Note: this produces a bad logger output, since this address
- # can not be converted to string, so the original message with
- # placeholders is output. This should not happen in practice, so
- # it is harmless.
- addr = IPAddr('0.0.0.0')
- addr.family = 42
- self.assertRaises(ValueError, Parser(FakeCreator([])).get_socket,
- addr, 42, socket.SOCK_DGRAM)
-
-class WrapTests(unittest.TestCase):
- """
- Tests for the wrap_socket function.
- """
- def test_wrap(self):
- # We construct two pairs of socket. The receiving side of one pair will
- # be wrapped. Then we send one of the other pair through this pair and
- # check the received one can be used as a socket
-
- # The transport socket
- (t1, t2) = socket.socketpair()
- # The payload socket
- (p1, p2) = socket.socketpair()
-
- t2 = WrappedSocket(t2)
-
- # Transfer the descriptor
- send_fd(t1.fileno(), p1.fileno())
- p1 = socket.fromfd(t2.read_fd(), socket.AF_UNIX, socket.SOCK_STREAM)
-
- # Now, pass some data trough the socket
- p1.send(b'A')
- data = p2.recv(1)
- self.assertEqual(b'A', data)
-
- # Test the wrapping didn't hurt the socket's usual methods
- t1.send(b'B')
- data = t2.recv(1)
- self.assertEqual(b'B', data)
- t2.send(b'C')
- data = t1.recv(1)
- self.assertEqual(b'C', data)
-
-if __name__ == '__main__':
- isc.log.init("bind10") # FIXME Should this be needed?
- isc.log.resetUnitTestRootLogger()
- unittest.main()
diff --git a/src/bin/bind10/tests/sockcreator_test.py.in b/src/bin/bind10/tests/sockcreator_test.py.in
new file mode 100644
index 0000000..7fb522f
--- /dev/null
+++ b/src/bin/bind10/tests/sockcreator_test.py.in
@@ -0,0 +1,315 @@
+# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+# This test file is generated .py.in -> .py just to be in the build dir,
+# same as the rest of the tests. Saves a lot of stuff in makefile.
+
+"""
+Tests for the bind10.sockcreator module.
+"""
+
+import unittest
+import struct
+import socket
+from isc.net.addr import IPAddr
+import isc.log
+from libutil_io_python import send_fd
+from bind10.sockcreator import Parser, CreatorError, WrappedSocket
+
+class FakeCreator:
+ """
+ Class emulating the socket to the socket creator. It can be given expected
+ data to receive (and check) and responses to give to the Parser class
+ during testing.
+ """
+
+ class InvalidPlan(Exception):
+ """
+ Raised when someone wants to recv when sending is planned or vice
+ versa.
+ """
+ pass
+
+ class InvalidData(Exception):
+ """
+ Raises when the data passed to sendall are not the same as expected.
+ """
+ pass
+
+ def __init__(self, plan):
+ """
+ Create the object. The plan variable contains list of expected actions,
+ in form:
+
+ [('r', 'Data to return from recv'), ('s', 'Data expected on sendall'),
+ , ('d', 'File descriptor number to return from read_sock'), ('e',
+ None), ...]
+
+ It modifies the array as it goes.
+ """
+ self.__plan = plan
+
+ def __get_plan(self, expected):
+ if len(self.__plan) == 0:
+ raise InvalidPlan('Nothing more planned')
+ (kind, data) = self.__plan[0]
+ if kind == 'e':
+ self.__plan.pop(0)
+ raise socket.error('False socket error')
+ if kind != expected:
+ raise InvalidPlan('Planned ' + kind + ', but ' + expected +
+ 'requested')
+ return data
+
+ def recv(self, maxsize):
+ """
+ Emulate recv. Returs maxsize bytes from the current recv plan. If
+ there are data left from previous recv call, it is used first.
+
+ If no recv is planned, raises InvalidPlan.
+ """
+ data = self.__get_plan('r')
+ result, rest = data[:maxsize], data[maxsize:]
+ if len(rest) > 0:
+ self.__plan[0] = ('r', rest)
+ else:
+ self.__plan.pop(0)
+ return result
+
+ def read_fd(self):
+ """
+ Emulate the reading of file descriptor. Returns one from a plan.
+
+ It raises InvalidPlan if no socket is planned now.
+ """
+ fd = self.__get_plan('f')
+ self.__plan.pop(0)
+ return fd
+
+ def sendall(self, data):
+ """
+ Checks that the data passed are correct according to plan. It raises
+ InvalidData if the data differs or InvalidPlan when sendall is not
+ expected.
+ """
+ planned = self.__get_plan('s')
+ dlen = len(data)
+ prefix, rest = planned[:dlen], planned[dlen:]
+ if prefix != data:
+ raise InvalidData('Expected "' + str(prefix)+ '", got "' +
+ str(data) + '"')
+ if len(rest) > 0:
+ self.__plan[0] = ('s', rest)
+ else:
+ self.__plan.pop(0)
+
+ def all_used(self):
+ """
+ Returns if the whole plan was consumed.
+ """
+ return len(self.__plan) == 0
+
+class ParserTests(unittest.TestCase):
+ """
+ Testcases for the Parser class.
+ """
+ def __terminate(self):
+ creator = FakeCreator([('s', b'T'), ('r', b'')])
+ parser = Parser(creator)
+ self.assertEqual(None, parser.terminate())
+ self.assertTrue(creator.all_used())
+ return parser
+
+ def test_terminate(self):
+ """
+ Test if the command to terminate is correct and it waits for reading the
+ EOF.
+ """
+ self.__terminate()
+
+ def test_terminate_error1(self):
+ """
+ Test it reports an exception when there's error terminating the creator.
+ This one raises an error when receiving the EOF.
+ """
+ creator = FakeCreator([('s', b'T'), ('e', None)])
+ parser = Parser(creator)
+ with self.assertRaises(CreatorError) as cm:
+ parser.terminate()
+ self.assertTrue(cm.exception.fatal)
+ self.assertEqual(None, cm.exception.errno)
+
+ def test_terminate_error2(self):
+ """
+ Test it reports an exception when there's error terminating the creator.
+ This one raises an error when sending data.
+ """
+ creator = FakeCreator([('e', None)])
+ parser = Parser(creator)
+ with self.assertRaises(CreatorError) as cm:
+ parser.terminate()
+ self.assertTrue(cm.exception.fatal)
+ self.assertEqual(None, cm.exception.errno)
+
+ def test_terminate_twice(self):
+ """
+ Test we can't terminate twice.
+ """
+ parser = self.__terminate()
+ with self.assertRaises(CreatorError) as cm:
+ parser.terminate()
+ self.assertTrue(cm.exception.fatal)
+ self.assertEqual(None, cm.exception.errno)
+
+ def test_terminate_error3(self):
+ """
+ Test it reports an exception when there's error terminating the creator.
+ This one sends data when it should have terminated.
+ """
+ creator = FakeCreator([('s', b'T'), ('r', b'Extra data')])
+ parser = Parser(creator)
+ with self.assertRaises(CreatorError) as cm:
+ parser.terminate()
+ self.assertTrue(cm.exception.fatal)
+ self.assertEqual(None, cm.exception.errno)
+
+ def test_crash(self):
+ """
+ Tests that the parser correctly raises exception when it crashes
+ unexpectedly.
+ """
+ creator = FakeCreator([('s', b'SU4\0\0\0\0\0\0'), ('r', b'')])
+ parser = Parser(creator)
+ with self.assertRaises(CreatorError) as cm:
+ parser.get_socket(IPAddr('0.0.0.0'), 0, 'UDP')
+ self.assertTrue(creator.all_used())
+ # Is the exception correct?
+ self.assertTrue(cm.exception.fatal)
+ self.assertEqual(None, cm.exception.errno)
+
+ def test_error(self):
+ """
+ Tests that the parser correctly raises non-fatal exception when
+ the socket can not be created.
+ """
+ # We split the int to see if it can cope with data coming in
+ # different packets
+ intpart = struct.pack('@i', 42)
+ creator = FakeCreator([('s', b'SU4\0\0\0\0\0\0'), ('r', b'ES' +
+ intpart[:1]), ('r', intpart[1:])])
+ parser = Parser(creator)
+ with self.assertRaises(CreatorError) as cm:
+ parser.get_socket(IPAddr('0.0.0.0'), 0, 'UDP')
+ self.assertTrue(creator.all_used())
+ # Is the exception correct?
+ self.assertFalse(cm.exception.fatal)
+ self.assertEqual(42, cm.exception.errno)
+
+ def __error(self, plan):
+ creator = FakeCreator(plan)
+ parser = Parser(creator)
+ with self.assertRaises(CreatorError) as cm:
+ parser.get_socket(IPAddr('0.0.0.0'), 0, socket.SOCK_DGRAM)
+ self.assertTrue(creator.all_used())
+ self.assertTrue(cm.exception.fatal)
+
+ def test_error_send(self):
+ self.__error([('e', None)])
+
+ def test_error_recv(self):
+ self.__error([('s', b'SU4\0\0\0\0\0\0'), ('e', None)])
+
+ def test_error_read_fd(self):
+ self.__error([('s', b'SU4\0\0\0\0\0\0'), ('r', b'S'), ('e', None)])
+
+ def __create(self, addr, socktype, encoded):
+ creator = FakeCreator([('s', b'S' + encoded), ('r', b'S'), ('f', 42)])
+ parser = Parser(creator)
+ self.assertEqual(42, parser.get_socket(IPAddr(addr), 42, socktype))
+
+ def test_create1(self):
+ self.__create('192.0.2.0', 'UDP', b'U4\0\x2A\xC0\0\x02\0')
+
+ def test_create2(self):
+ self.__create('2001:db8::', socket.SOCK_STREAM,
+ b'T6\0\x2A\x20\x01\x0d\xb8\0\0\0\0\0\0\0\0\0\0\0\0')
+
+ def test_create_terminated(self):
+ """
+ Test we can't request sockets after it was terminated.
+ """
+ parser = self.__terminate()
+ with self.assertRaises(CreatorError) as cm:
+ parser.get_socket(IPAddr('0.0.0.0'), 0, 'UDP')
+ self.assertTrue(cm.exception.fatal)
+ self.assertEqual(None, cm.exception.errno)
+
+ def test_invalid_socktype(self):
+ """
+ Test invalid socket type is rejected
+ """
+ self.assertRaises(ValueError, Parser(FakeCreator([])).get_socket,
+ IPAddr('0.0.0.0'), 42, 'RAW')
+
+ def test_invalid_family(self):
+ """
+ Test it rejects invalid address family.
+ """
+ # Note: this produces a bad logger output, since this address
+ # can not be converted to string, so the original message with
+ # placeholders is output. This should not happen in practice, so
+ # it is harmless.
+ addr = IPAddr('0.0.0.0')
+ addr.family = 42
+ self.assertRaises(ValueError, Parser(FakeCreator([])).get_socket,
+ addr, 42, socket.SOCK_DGRAM)
+
+class WrapTests(unittest.TestCase):
+ """
+ Tests for the wrap_socket function.
+ """
+ def test_wrap(self):
+ # We construct two pairs of socket. The receiving side of one pair will
+ # be wrapped. Then we send one of the other pair through this pair and
+ # check the received one can be used as a socket
+
+ # The transport socket
+ (t1, t2) = socket.socketpair()
+ # The payload socket
+ (p1, p2) = socket.socketpair()
+
+ t2 = WrappedSocket(t2)
+
+ # Transfer the descriptor
+ send_fd(t1.fileno(), p1.fileno())
+ p1 = socket.fromfd(t2.read_fd(), socket.AF_UNIX, socket.SOCK_STREAM)
+
+ # Now, pass some data trough the socket
+ p1.send(b'A')
+ data = p2.recv(1)
+ self.assertEqual(b'A', data)
+
+ # Test the wrapping didn't hurt the socket's usual methods
+ t1.send(b'B')
+ data = t2.recv(1)
+ self.assertEqual(b'B', data)
+ t2.send(b'C')
+ data = t1.recv(1)
+ self.assertEqual(b'C', data)
+
+if __name__ == '__main__':
+ isc.log.init("bind10") # FIXME Should this be needed?
+ isc.log.resetUnitTestRootLogger()
+ unittest.main()
diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am
index a35284f..219dcff 100644
--- a/src/bin/dhcp6/tests/Makefile.am
+++ b/src/bin/dhcp6/tests/Makefile.am
@@ -15,7 +15,7 @@ endif
check-local:
for pytest in $(PYTESTS) ; do \
echo Running test: $$pytest ; \
- env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/bin/bind10 \
+ env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_srcdir)/src/bin:$(abs_top_builddir)/src/bin/bind10:$(abs_top_builddir)/src/lib/util/io/.libs \
$(LIBRARY_PATH_PLACEHOLDER) \
BIND10_MSGQ_SOCKET_FILE=$(abs_top_builddir)/msgq_socket \
$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
diff --git a/src/bin/dhcp6/tests/dhcp6_test.py b/src/bin/dhcp6/tests/dhcp6_test.py
index 61ec009..5ae1f5e 100644
--- a/src/bin/dhcp6/tests/dhcp6_test.py
+++ b/src/bin/dhcp6/tests/dhcp6_test.py
@@ -13,7 +13,7 @@
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-from bind10 import ProcessInfo, parse_args, dump_pid, unlink_pid_file, _BASETIME
+from bind10_src import ProcessInfo, parse_args, dump_pid, unlink_pid_file, _BASETIME
import unittest
import sys
More information about the bind10-changes
mailing list