dhcp6_test.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. # copyright (C) 2011,2012 Internet Systems Consortium.
  2. #
  3. # Permission to use, copy, modify, and distribute this software for any
  4. # purpose with or without fee is hereby granted, provided that the above
  5. # copyright notice and this permission notice appear in all copies.
  6. #
  7. # THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
  8. # DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
  9. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
  10. # INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
  11. # INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
  12. # FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
  13. # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
  14. # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. from bind10_src import ProcessInfo, parse_args, dump_pid, unlink_pid_file, _BASETIME
  16. import unittest
  17. import sys
  18. import os
  19. import signal
  20. import socket
  21. from isc.net.addr import IPAddr
  22. import time
  23. import isc
  24. import fcntl
  25. class TestDhcpv6Daemon(unittest.TestCase):
  26. def setUp(self):
  27. # don't redirect stdout/stderr here as we want to print out things
  28. # during the test
  29. pass
  30. def tearDown(self):
  31. pass
  32. def runCommand(self, params, wait=1):
  33. """
  34. This method runs a command and returns a touple: (returncode, stdout, stderr)
  35. """
  36. ## @todo: Convert this into generic method and reuse it in dhcp4 and dhcp6
  37. print("Running command: %s" % (" ".join(params)))
  38. # redirect stdout to a pipe so we can check that our
  39. # process spawning is doing the right thing with stdout
  40. self.stdout_old = os.dup(sys.stdout.fileno())
  41. self.stdout_pipes = os.pipe()
  42. os.dup2(self.stdout_pipes[1], sys.stdout.fileno())
  43. os.close(self.stdout_pipes[1])
  44. # do the same trick for stderr:
  45. self.stderr_old = os.dup(sys.stderr.fileno())
  46. self.stderr_pipes = os.pipe()
  47. os.dup2(self.stderr_pipes[1], sys.stderr.fileno())
  48. os.close(self.stderr_pipes[1])
  49. # note that we use dup2() to restore the original stdout
  50. # to the main program ASAP in each test... this prevents
  51. # hangs reading from the child process (as the pipe is only
  52. # open in the child), and also insures nice pretty output
  53. pi = ProcessInfo('Test Process', params)
  54. pi.spawn()
  55. time.sleep(wait)
  56. os.dup2(self.stdout_old, sys.stdout.fileno())
  57. os.dup2(self.stderr_old, sys.stderr.fileno())
  58. self.assertNotEqual(pi.process, None)
  59. self.assertTrue(type(pi.pid) is int)
  60. # Set non-blocking read on pipes. Process may not print anything
  61. # on specific output and the we would hang without this.
  62. fd = self.stdout_pipes[0]
  63. fl = fcntl.fcntl(fd, fcntl.F_GETFL)
  64. fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
  65. fd = self.stderr_pipes[0]
  66. fl = fcntl.fcntl(fd, fcntl.F_GETFL)
  67. fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
  68. # There's potential problem if b10-dhcp4 prints out more
  69. # than 4k of text
  70. try:
  71. output = os.read(self.stdout_pipes[0], 4096)
  72. except OSError:
  73. print("No data available from stdout")
  74. output = ""
  75. # read can return None. Make sure we have a string
  76. if (output is None):
  77. output = ""
  78. try:
  79. error = os.read(self.stderr_pipes[0], 4096)
  80. except OSError:
  81. print("No data available on stderr")
  82. error = ""
  83. # read can return None. Make sure we have a string
  84. if (error is None):
  85. error = ""
  86. try:
  87. if (not pi.process.poll()):
  88. # let's be nice at first...
  89. pi.process.terminate()
  90. except OSError:
  91. print("Ignoring failed kill attempt. Process is dead already.")
  92. # call this to get returncode, process should be dead by now
  93. rc = pi.process.wait()
  94. # Clean up our stdout/stderr munging.
  95. os.dup2(self.stdout_old, sys.stdout.fileno())
  96. os.close(self.stdout_pipes[0])
  97. os.dup2(self.stderr_old, sys.stderr.fileno())
  98. os.close(self.stderr_pipes[0])
  99. print ("Process finished, return code=%d, stdout=%d bytes, stderr=%d bytes"
  100. % (rc, len(output), len(error)) )
  101. return (rc, output, error)
  102. def test_alive(self):
  103. """
  104. Simple test. Checks that b10-dhcp6 can be started and prints out info
  105. about starting DHCPv6 operation.
  106. """
  107. print("Note: Purpose of some of the tests is to check if DHCPv6 server can be started,")
  108. print(" not that is can bind sockets correctly. Please ignore binding errors.")
  109. (returncode, output, error) = self.runCommand(["../b10-dhcp6", "-v"])
  110. self.assertEqual( str(output).count("b10-dhcp6: Initiating DHCPv6 server operation."), 1)
  111. def test_portnumber_0(self):
  112. print("Check that specifying port number 0 is not allowed.")
  113. (returncode, output, error) = self.runCommand(['../b10-dhcp6', '-p', '0'])
  114. # When invalid port number is specified, return code must not be success
  115. self.assertTrue(returncode != 0)
  116. # Check that there is an error message about invalid port number printed on stderr
  117. self.assertEqual( str(error).count("Failed to parse port number"), 1)
  118. def test_portnumber_missing(self):
  119. print("Check that -p option requires a parameter.")
  120. (returncode, output, error) = self.runCommand(['../b10-dhcp6', '-p'])
  121. # When invalid port number is specified, return code must not be success
  122. self.assertTrue(returncode != 0)
  123. # Check that there is an error message about invalid port number printed on stderr
  124. self.assertEqual( str(error).count("option requires an argument"), 1)
  125. def test_portnumber_invalid1(self):
  126. print("Check that -p option is check against bogus port number (999999).")
  127. (returncode, output, error) = self.runCommand(['../b10-dhcp6', '-p','999999'])
  128. # When invalid port number is specified, return code must not be success
  129. self.assertTrue(returncode != 0)
  130. # Check that there is an error message about invalid port number printed on stderr
  131. self.assertEqual( str(error).count("Failed to parse port number"), 1)
  132. def test_portnumber_invalid2(self):
  133. print("Check that -p option is check against bogus port number (123garbage).")
  134. (returncode, output, error) = self.runCommand(['../b10-dhcp6', '-p','123garbage'])
  135. # When invalid port number is specified, return code must not be success
  136. self.assertTrue(returncode != 0)
  137. # Check that there is an error message about invalid port number printed on stderr
  138. self.assertEqual( str(error).count("Failed to parse port number"), 1)
  139. def test_portnumber_nonroot(self):
  140. print("Check that specifying unprivileged port number will work.")
  141. (returncode, output, error) = self.runCommand(['../b10-dhcp6', '-s', '-p', '10547'])
  142. # When invalid port number is specified, return code must not be success
  143. # TODO: Temporarily commented out as socket binding on systems that do not have
  144. # interface detection implemented currently fails.
  145. # self.assertTrue(returncode == 0)
  146. self.assertEqual( str(output).count("opening sockets on port 10547"), 1)
  147. def test_skip_msgq(self):
  148. print("Check that connection to BIND10 msgq can be disabled.")
  149. (returncode, output, error) = self.runCommand(['../b10-dhcp6', '-s', '-p', '10547'])
  150. # When invalid port number is specified, return code must not be success
  151. # TODO: Temporarily commented out as socket binding on systems that do not have
  152. # interface detection implemented currently fails.
  153. # self.assertTrue(returncode == 0)
  154. self.assertEqual( str(output).count("Skipping connection to the BIND10 msgq."), 1)
  155. if __name__ == '__main__':
  156. unittest.main()