BIND 10 master, updated. 92cdf7773ea4631eda41a15b5b5b21a4926ff391 Merge branch 'trac1503'

BIND 10 source code commits bind10-changes at lists.isc.org
Thu May 31 13:35:08 UTC 2012


The branch, master has been updated
       via  92cdf7773ea4631eda41a15b5b5b21a4926ff391 (commit)
       via  e60af9fa16a6094d2204f27c40a648fae313bdae (commit)
       via  b35d3159e812712e47d59db80ae0115e58cd5c82 (commit)
       via  948f12089ccfd8e77c568568ed78153dd926c646 (commit)
       via  848be350a53e835929a536d295d0fc3ee12a2376 (commit)
       via  1b5e0412294dcf0bc499c4fd288db0f6d88c4ba5 (commit)
       via  9cffd7d9f5dc6c9c2909f9569c2f4c78aa71bdbd (commit)
       via  2be06486e504e522f9ef39586fe7fb5947c33872 (commit)
      from  98c723ce92f98e2982f61fbbb4c12b74c4aed87d (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 92cdf7773ea4631eda41a15b5b5b21a4926ff391
Merge: 98c723c e60af9f
Author: Tomek Mrugalski <tomasz at isc.org>
Date:   Thu May 31 15:34:56 2012 +0200

    Merge branch 'trac1503'
    
    Conflicts:
    	ChangeLog
    	src/bin/dhcp6/tests/dhcp6_test.py

-----------------------------------------------------------------------

Summary of changes:
 ChangeLog                         |    6 ++
 src/bin/dhcp4/main.cc             |   25 ++++--
 src/bin/dhcp4/tests/Makefile.am   |   17 +++-
 src/bin/dhcp4/tests/dhcp4_test.py |  170 +++++++++++++++++++++++++++++++++++++
 src/bin/dhcp6/dhcp6_srv.cc        |    2 +-
 src/bin/dhcp6/main.cc             |   23 +++--
 src/bin/dhcp6/tests/Makefile.am   |    5 +-
 src/bin/dhcp6/tests/dhcp6_test.py |  159 +++++++++++++++++++++++++++-------
 8 files changed, 358 insertions(+), 49 deletions(-)
 create mode 100644 src/bin/dhcp4/tests/dhcp4_test.py

-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index 5b9c9d1..c849b55 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+442.	[func]		tomek
+	b10-dhcp4, b10-dhcp6: Both DHCP servers now accept -p parameter
+	that can be used to specify listening port number. This capability
+	is useful only for testing purposes.
+	(Trac #1503, git e60af9fa16a6094d2204f27c40a648fae313bdae)
+
 441.	[func]		tomek
 	libdhcp++: Stub interface detection (support for interfaces.txt
 	file) was removed.
diff --git a/src/bin/dhcp4/main.cc b/src/bin/dhcp4/main.cc
index ee40295..f3fd8ad 100644
--- a/src/bin/dhcp4/main.cc
+++ b/src/bin/dhcp4/main.cc
@@ -37,6 +37,7 @@
 
 #include <dhcp4/spec_config.h>
 #include <dhcp4/dhcp4_srv.h>
+#include <dhcp/dhcp4.h>
 
 using namespace std;
 using namespace isc::util;
@@ -53,33 +54,45 @@ usage() {
     cerr << "Usage:  b10-dhcp4 [-v]"
          << endl;
     cerr << "\t-v: verbose output" << endl;
-    exit(1);
+    cerr << "\t-p number: specify non-standard port number 1-65535 (useful for testing only)" << endl;
+    exit(EXIT_FAILURE);
 }
 } // end of anonymous namespace
 
 int
 main(int argc, char* argv[]) {
     int ch;
+    int port_number = DHCP4_SERVER_PORT; // The default. any other values are
+                                         // useful for testing only.
 
-    while ((ch = getopt(argc, argv, ":v")) != -1) {
+    while ((ch = getopt(argc, argv, "vp:")) != -1) {
         switch (ch) {
         case 'v':
             verbose_mode = true;
             isc::log::denabled = true;
             break;
+        case 'p':
+            port_number = strtol(optarg, NULL, 10);
+            if (port_number == 0) {
+                cerr << "Failed to parse port number: [" << optarg
+                     << "], 1-65535 allowed." << endl;
+                usage();
+            }
+            break;
         case ':':
         default:
             usage();
         }
     }
 
-    cout << "My pid=" << getpid() << endl;
+    cout << "My pid=" << getpid() << ", binding to port " << port_number
+         << ", verbose " << (verbose_mode?"yes":"no") << endl;
 
     if (argc - optind > 0) {
         usage();
     }
 
-    int ret = 0;
+    int ret = EXIT_SUCCESS;
 
     // TODO remainder of auth to dhcp4 code copy. We need to enable this in
     //      dhcp4 eventually
@@ -99,13 +112,13 @@ main(int argc, char* argv[]) {
 
         cout << "[b10-dhcp4] Initiating DHCPv4 server operation." << endl;
 
-        Dhcpv4Srv* srv = new Dhcpv4Srv();
+        Dhcpv4Srv* srv = new Dhcpv4Srv(port_number);
 
         srv->run();
 
     } catch (const std::exception& ex) {
         cerr << "[b10-dhcp4] Server failed: " << ex.what() << endl;
-        ret = 1;
+        ret = EXIT_FAILURE;
     }
 
     return (ret);
diff --git a/src/bin/dhcp4/tests/Makefile.am b/src/bin/dhcp4/tests/Makefile.am
index b956aee..a327e47 100644
--- a/src/bin/dhcp4/tests/Makefile.am
+++ b/src/bin/dhcp4/tests/Makefile.am
@@ -1,12 +1,25 @@
 PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
 
-# If necessary (rare cases), explicitly specify paths to dynamic libraries
-# required by loadable python modules.
+PYTESTS = dhcp4_test.py
+EXTRA_DIST = $(PYTESTS)
+
+# Explicitly specify paths to dynamic libraries required by loadable python
+# modules. That is required on Mac OS systems. Otherwise we will get exception
+# about python not being able to load liblog library.
 LIBRARY_PATH_PLACEHOLDER =
 if SET_ENV_LIBRARY_PATH
 LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
 endif
 
+# test using command-line arguments, so use check-local target instead of TESTS
+check-local:
+	for pytest in $(PYTESTS) ; do \
+	echo Running test: $$pytest ; \
+	PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_srcdir)/src/bin:$(abs_top_builddir)/src/bin/bind10:$(abs_top_builddir)/src/lib/util/io/.libs \
+	$(LIBRARY_PATH_PLACEHOLDER) \
+		$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
+	done
+
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS += -I$(top_builddir)/src/bin # for generated spec_config.h header
 AM_CPPFLAGS += -I$(top_srcdir)/src/bin
diff --git a/src/bin/dhcp4/tests/dhcp4_test.py b/src/bin/dhcp4/tests/dhcp4_test.py
new file mode 100644
index 0000000..18d23ff
--- /dev/null
+++ b/src/bin/dhcp4/tests/dhcp4_test.py
@@ -0,0 +1,170 @@
+# Copyright (C) 2012 Internet Systems Consortium.
+#
+# 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.
+
+from bind10_src import ProcessInfo, parse_args, dump_pid, unlink_pid_file, _BASETIME
+
+import unittest
+import sys
+import os
+import signal
+import socket
+from isc.net.addr import IPAddr
+import time
+import isc
+import fcntl
+
+class TestDhcpv4Daemon(unittest.TestCase):
+    def setUp(self):
+        # don't redirect stdout/stderr here as we want to print out things
+        # during the test
+        pass
+
+    def tearDown(self):
+        pass
+
+    def runDhcp4(self, params, wait=1):
+        """
+        This method runs dhcp4 and returns a touple: (returncode, stdout, stderr)
+        """
+        ## @todo: Convert this into generic method and reuse it in dhcp6
+
+        print("Running command: %s" % (" ".join(params)))
+
+        # redirect stdout to a pipe so we can check that our
+        # process spawning is doing the right thing with stdout
+        self.stdout_old = os.dup(sys.stdout.fileno())
+        self.stdout_pipes = os.pipe()
+        os.dup2(self.stdout_pipes[1], sys.stdout.fileno())
+        os.close(self.stdout_pipes[1])
+
+        # do the same trick for stderr:
+        self.stderr_old = os.dup(sys.stderr.fileno())
+        self.stderr_pipes = os.pipe()
+        os.dup2(self.stderr_pipes[1], sys.stderr.fileno())
+        os.close(self.stderr_pipes[1])
+
+        # note that we use dup2() to restore the original stdout
+        # to the main program ASAP in each test... this prevents
+        # hangs reading from the child process (as the pipe is only
+        # open in the child), and also insures nice pretty output
+
+        pi = ProcessInfo('Test Process', params)
+        pi.spawn()
+        time.sleep(wait)
+        os.dup2(self.stdout_old, sys.stdout.fileno())
+        os.dup2(self.stderr_old, sys.stderr.fileno())
+        self.assertNotEqual(pi.process, None)
+        self.assertTrue(type(pi.pid) is int)
+
+        # Set non-blocking read on pipes. Process may not print anything
+        # on specific output and the we would hang without this.
+        fd = self.stdout_pipes[0]
+        fl = fcntl.fcntl(fd, fcntl.F_GETFL)
+        fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
+
+        fd = self.stderr_pipes[0]
+        fl = fcntl.fcntl(fd, fcntl.F_GETFL)
+        fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
+
+        # There's potential problem if b10-dhcp4 prints out more
+        # than 4k of text
+        try:
+            output = os.read(self.stdout_pipes[0], 4096)
+        except OSError:
+            print("No data available from stdout")
+            output = ""
+
+        # read can return None. Make sure we have a string
+        if (output is None):
+            output = ""
+
+        try:
+            error = os.read(self.stderr_pipes[0], 4096)
+        except OSError:
+            print("No data available on stderr")
+            error = ""
+
+        # read can return None. Make sure we have a string
+        if (error is None):
+            error = ""
+
+
+        try:
+            if (not pi.process.poll()):
+                # let's be nice at first...
+                pi.process.terminate()
+        except OSError:
+            print("Ignoring failed kill attempt. Process is dead already.")
+
+        # call this to get returncode, process should be dead by now
+        rc = pi.process.wait()
+
+        # Clean up our stdout/stderr munging.
+        os.dup2(self.stdout_old, sys.stdout.fileno())
+        os.close(self.stdout_pipes[0])
+
+        os.dup2(self.stderr_old, sys.stderr.fileno())
+        os.close(self.stderr_pipes[0])
+
+        print ("Process finished, return code=%d, stdout=%d bytes, stderr=%d bytes"
+               % (rc, len(output), len(error)) )
+
+        return (rc, output, error)
+
+    def test_alive(self):
+        print("Note: Purpose of some of the tests is to check if DHCPv4 server can be started,")
+        print("      not that is can bind sockets correctly. Please ignore binding errors.")
+
+        (returncode, output, error) = self.runDhcp4(["../b10-dhcp4", "-v"])
+
+        self.assertEqual( str(output).count("[b10-dhcp4] Initiating DHCPv4 server operation."), 1)
+
+    def test_portnumber_0(self):
+        print("Check that specifying port number 0 is not allowed.")
+
+        (returncode, output, error) = self.runDhcp4(['../b10-dhcp4', '-p', '0'])
+
+        # When invalid port number is specified, return code must not be success
+        self.assertTrue(returncode != 0)
+
+        # Check that there is an error message about invalid port number printed on stderr
+        self.assertEqual( str(error).count("Failed to parse port number"), 1)
+
+    def test_portnumber_missing(self):
+        print("Check that -p option requires a parameter.")
+
+        (returncode, output, error) = self.runDhcp4(['../b10-dhcp4', '-p'])
+
+        # When invalid port number is specified, return code must not be success
+        self.assertTrue(returncode != 0)
+
+        # Check that there is an error message about invalid port number printed on stderr
+        self.assertEqual( str(error).count("option requires an argument"), 1)
+
+    def test_portnumber_nonroot(self):
+        print("Check that specifying unprivileged port number will work.")
+
+        (returncode, output, error) = self.runDhcp4(['../b10-dhcp4', '-p', '10057'])
+
+        # When invalid port number is specified, return code must not be success
+        # TODO: Temporarily commented out as socket binding on systems that do not have
+        #       interface detection implemented currently fails.
+        # self.assertTrue(returncode == 0)
+
+        # Check that there is an error message about invalid port number printed on stderr
+        self.assertEqual( str(output).count("opening sockets on port 10057"), 1)
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index 4497e22..293e600 100644
--- a/src/bin/dhcp6/dhcp6_srv.cc
+++ b/src/bin/dhcp6/dhcp6_srv.cc
@@ -40,7 +40,7 @@ const uint32_t HARDCODED_VALID_LIFETIME = 7200; // in seconds
 const std::string HARDCODED_DNS_SERVER = "2001:db8:1::1";
 
 Dhcpv6Srv::Dhcpv6Srv(uint16_t port) {
-    cout << "Initialization" << endl;
+    cout << "Initialization: opening sockets on port " << port << endl;
 
     // first call to instance() will create IfaceMgr (it's a singleton)
     // it may throw something if things go wrong
diff --git a/src/bin/dhcp6/main.cc b/src/bin/dhcp6/main.cc
index 5323811..62c0d20 100644
--- a/src/bin/dhcp6/main.cc
+++ b/src/bin/dhcp6/main.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2009-2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2012  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -53,20 +53,31 @@ usage() {
     cerr << "Usage:  b10-dhcp6 [-v]"
          << endl;
     cerr << "\t-v: verbose output" << endl;
-    exit(1);
+    cerr << "\t-p number: specify non-standard port number 1-65535 (useful for testing only)" << endl;
+    exit(EXIT_FAILURE);
 }
 } // end of anonymous namespace
 
 int
 main(int argc, char* argv[]) {
     int ch;
+    int port_number = DHCP6_SERVER_PORT; // The default. Any other values are
+                                         // useful for testing only.
 
-    while ((ch = getopt(argc, argv, ":v")) != -1) {
+    while ((ch = getopt(argc, argv, "vp:")) != -1) {
         switch (ch) {
         case 'v':
             verbose_mode = true;
             isc::log::denabled = true;
             break;
+        case 'p':
+            port_number = strtol(optarg, NULL, 10);
+            if (port_number == 0) {
+                cerr << "Failed to parse port number: [" << optarg
+                     << "], 1-65535 allowed." << endl;
+                usage();
+            }
+            break;
         case ':':
         default:
             usage();
@@ -79,7 +90,7 @@ main(int argc, char* argv[]) {
         usage();
     }
 
-    int ret = 0;
+    int ret = EXIT_SUCCESS;
 
     // TODO remainder of auth to dhcp6 code copy. We need to enable this in
     //      dhcp6 eventually
@@ -99,13 +110,13 @@ main(int argc, char* argv[]) {
 
         cout << "[b10-dhcp6] Initiating DHCPv6 operation." << endl;
 
-        Dhcpv6Srv* srv = new Dhcpv6Srv();
+        Dhcpv6Srv* srv = new Dhcpv6Srv(port_number);
 
         srv->run();
 
     } catch (const std::exception& ex) {
         cerr << "[b10-dhcp6] Server failed: " << ex.what() << endl;
-        ret = 1;
+        ret = EXIT_FAILURE;
     }
 
     return (ret);
diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am
index d216f53..a1a11a0 100644
--- a/src/bin/dhcp6/tests/Makefile.am
+++ b/src/bin/dhcp6/tests/Makefile.am
@@ -2,8 +2,9 @@ PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
 PYTESTS = dhcp6_test.py
 EXTRA_DIST = $(PYTESTS)
 
-# If necessary (rare cases), explicitly specify paths to dynamic libraries
-# required by loadable python modules.
+# Explicitly specify paths to dynamic libraries required by loadable python
+# modules. That is required on Mac OS systems. Otherwise we will get exception
+# about python not being able to load liblog library.
 LIBRARY_PATH_PLACEHOLDER =
 if SET_ENV_LIBRARY_PATH
 LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
diff --git a/src/bin/dhcp6/tests/dhcp6_test.py b/src/bin/dhcp6/tests/dhcp6_test.py
index 85732a1..cf04f60 100644
--- a/src/bin/dhcp6/tests/dhcp6_test.py
+++ b/src/bin/dhcp6/tests/dhcp6_test.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2011 Internet Systems Consortium.
+# Copyright (C) 2011,2012 Internet Systems Consortium.
 #
 # Permission to use, copy, modify, and distribute this software for any
 # purpose with or without fee is hereby granted, provided that the above
@@ -23,55 +23,150 @@ import socket
 from isc.net.addr import IPAddr
 import time
 import isc
+import fcntl
 
 class TestDhcpv6Daemon(unittest.TestCase):
     def setUp(self):
+        # don't redirect stdout/stderr here as we want to print out things
+        # during the test
+        pass
+
+    def tearDown(self):
+        pass
+
+    def runCommand(self, params, wait=1):
+        """
+        This method runs a command and returns a touple: (returncode, stdout, stderr)
+        """
+        ## @todo: Convert this into generic method and reuse it in dhcp4 and dhcp6
+
+        print("Running command: %s" % (" ".join(params)))
+
+        # redirect stdout to a pipe so we can check that our
+        # process spawning is doing the right thing with stdout
+        self.stdout_old = os.dup(sys.stdout.fileno())
+        self.stdout_pipes = os.pipe()
+        os.dup2(self.stdout_pipes[1], sys.stdout.fileno())
+        os.close(self.stdout_pipes[1])
+
+        # do the same trick for stderr:
+        self.stderr_old = os.dup(sys.stderr.fileno())
+        self.stderr_pipes = os.pipe()
+        os.dup2(self.stderr_pipes[1], sys.stderr.fileno())
+        os.close(self.stderr_pipes[1])
 
-        # Let's print this out before we redirect out stdout.
-        print("Please ignore any socket errors. Purpose of this test is to")
-        print("verify that DHCPv6 process could be started, not that socket")
-        print("could be bound. Binding fails when run as non-root user.")
-
-        # Redirect stdout to a pipe so we can check that our
-        # process spawning is doing the right thing with stdout.
-        self.old_stdout = os.dup(sys.stdout.fileno())
-        self.pipes = os.pipe()
-        os.dup2(self.pipes[1], sys.stdout.fileno())
-        os.close(self.pipes[1])
         # note that we use dup2() to restore the original stdout
         # to the main program ASAP in each test... this prevents
         # hangs reading from the child process (as the pipe is only
         # open in the child), and also insures nice pretty output
 
-    def tearDown(self):
-        # clean up our stdout munging
-        os.dup2(self.old_stdout, sys.stdout.fileno())
-        os.close(self.pipes[0])
+        pi = ProcessInfo('Test Process', params)
+        pi.spawn()
+        time.sleep(wait)
+        os.dup2(self.stdout_old, sys.stdout.fileno())
+        os.dup2(self.stderr_old, sys.stderr.fileno())
+        self.assertNotEqual(pi.process, None)
+        self.assertTrue(type(pi.pid) is int)
+
+        # Set non-blocking read on pipes. Process may not print anything
+        # on specific output and the we would hang without this.
+        fd = self.stdout_pipes[0]
+        fl = fcntl.fcntl(fd, fcntl.F_GETFL)
+        fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
+
+        fd = self.stderr_pipes[0]
+        fl = fcntl.fcntl(fd, fcntl.F_GETFL)
+        fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
+
+        # There's potential problem if b10-dhcp4 prints out more
+        # than 4k of text
+        try:
+            output = os.read(self.stdout_pipes[0], 4096)
+        except OSError:
+            print("No data available from stdout")
+            output = ""
+
+        # read can return None. Make sure we have a string
+        if (output is None):
+            output = ""
+
+        try:
+            error = os.read(self.stderr_pipes[0], 4096)
+        except OSError:
+            print("No data available on stderr")
+            error = ""
+
+        # read can return None. Make sure we have a string
+        if (error is None):
+            error = ""
+
+        try:
+            if (not pi.process.poll()):
+                # let's be nice at first...
+                pi.process.terminate()
+        except OSError:
+            print("Ignoring failed kill attempt. Process is dead already.")
+
+        # call this to get returncode, process should be dead by now
+        rc = pi.process.wait()
+
+        # Clean up our stdout/stderr munging.
+        os.dup2(self.stdout_old, sys.stdout.fileno())
+        os.close(self.stdout_pipes[0])
+
+        os.dup2(self.stderr_old, sys.stderr.fileno())
+        os.close(self.stderr_pipes[0])
+
+        print ("Process finished, return code=%d, stdout=%d bytes, stderr=%d bytes"
+               % (rc, len(output), len(error)) )
+
+        return (rc, output, error)
 
     def test_alive(self):
         """
         Simple test. Checks that b10-dhcp6 can be started and prints out info 
         about starting DHCPv6 operation.
         """
-        pi = ProcessInfo('Test Process', [ '../b10-dhcp6' , '-v' ])
-        pi.spawn()
-        time.sleep(1)
-        os.dup2(self.old_stdout, sys.stdout.fileno())
-        self.assertNotEqual(pi.process, None)
-        self.assertTrue(type(pi.pid) is int)
-        output = os.read(self.pipes[0], 4096)
+        print("Note: Purpose of some of the tests is to check if DHCPv6 server can be started,")
+        print("      not that is can bind sockets correctly. Please ignore binding errors.")
+        (returncode, output, error) = self.runCommand(["../b10-dhcp6", "-v"])
+
         self.assertEqual( str(output).count("[b10-dhcp6] Initiating DHCPv6 operation."), 1)
 
-        # kill this process
-        # XXX: b10-dhcp6 is too dumb to understand 'shutdown' command for now,
-        #      so let's just kill the bastard
+    def test_portnumber_0(self):
+        print("Check that specifying port number 0 is not allowed.")
 
-        # TODO: Ignore errors for now. This test will be more thorough once ticket #1503
-        # (passing port number to b10-dhcp6 daemon) is implemented.
-        try:
-            os.kill(pi.pid, signal.SIGTERM)
-        except OSError:
-            print("Ignoring failed kill attempt. Process is dead already.")
+        (returncode, output, error) = self.runCommand(['../b10-dhcp6', '-p', '0'])
+
+        # When invalid port number is specified, return code must not be success
+        self.assertTrue(returncode != 0)
+
+        # Check that there is an error message about invalid port number printed on stderr
+        self.assertEqual( str(error).count("Failed to parse port number"), 1)
+
+    def test_portnumber_missing(self):
+        print("Check that -p option requires a parameter.")
+
+        (returncode, output, error) = self.runCommand(['../b10-dhcp6', '-p'])
+
+        # When invalid port number is specified, return code must not be success
+        self.assertTrue(returncode != 0)
+
+        # Check that there is an error message about invalid port number printed on stderr
+        self.assertEqual( str(error).count("option requires an argument"), 1)
+
+    def test_portnumber_nonroot(self):
+        print("Check that specifying unprivileged port number will work.")
+
+        (returncode, output, error) = self.runCommand(['../b10-dhcp6', '-p', '10057'])
+
+        # When invalid port number is specified, return code must not be success
+        # TODO: Temporarily commented out as socket binding on systems that do not have
+        #       interface detection implemented currently fails.
+        # self.assertTrue(returncode == 0)
+
+        # Check that there is a message on stdout about opening proper port
+        self.assertEqual( str(output).count("opening sockets on port 10057"), 1)
 
 if __name__ == '__main__':
     unittest.main()



More information about the bind10-changes mailing list