bind10_server.py.in 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. # Copyright (C) 2013 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. import errno
  16. import os
  17. import select
  18. import signal
  19. import isc.log
  20. import isc.config
  21. from isc.server_common.logger import logger
  22. from isc.log_messages.server_common_messages import *
  23. class BIND10ServerFatal(Exception):
  24. """Exception raised when the server program encounters a fatal error."""
  25. pass
  26. class BIND10Server:
  27. """A mixin class for common BIND 10 server implementations.
  28. It takes care of common initialization such as setting up a module CC
  29. session, and running main event loop. It also handles the "shutdown"
  30. command for its normal behavior. If a specific server class wants to
  31. handle this command differently or if it does not support the command,
  32. it should override the _command_handler method.
  33. Specific modules can define module-specific class inheriting this class,
  34. instantiate it, and call run() with the module name.
  35. Methods to be implemented in the actual class:
  36. _config_handler: config handler method as specified in ModuleCCSession.
  37. must be exception free; errors should be signaled by
  38. the return value.
  39. _mod_command_handler: can be optionally defined to handle
  40. module-specific commands. should conform to
  41. command handlers as specified in ModuleCCSession.
  42. must be exception free; errors should be signaled
  43. by the return value.
  44. """
  45. # Will be set to True when the server should stop and shut down.
  46. # Can be read via accessor method 'shutdown', mainly for testing.
  47. __shutdown = False
  48. # ModuleCCSession used in the server. Defined as 'protectd' so tests
  49. # can refer to it directly; others should access it via the
  50. # 'mod_ccsession' accessor.
  51. _mod_cc = None
  52. # Will be set in run(). Define a tentative value so other methods can
  53. # be tested directly.
  54. __module_name = ''
  55. # Basically constant, but allow tests to override it.
  56. _select_fn = select.select
  57. @property
  58. def shutdown(self):
  59. return self.__shutdown
  60. @property
  61. def mod_ccsession(self):
  62. return self._mod_cc
  63. def _setup_ccsession(self):
  64. """Create and start module CC session.
  65. This is essentially private, but allows tests to override it.
  66. """
  67. self._mod_cc = isc.config.ModuleCCSession(
  68. self._get_specfile_location(), self._config_handler,
  69. self._command_handler)
  70. self._mod_cc.start()
  71. def _get_specfile_location(self):
  72. """Return the path to the module spec file following common convetion.
  73. This method generates the path commonly used by most BIND 10 modules,
  74. determined by a well known prefix and the module name.
  75. A specific module can override this method if it uses a different
  76. path for the spec file.
  77. """
  78. if 'B10_FROM_SOURCE' in os.environ:
  79. specfile_path = os.environ['B10_FROM_SOURCE'] + '/src/bin/' + \
  80. self.__module_name
  81. else:
  82. specfile_path = '${datarootdir}/bind10'\
  83. .replace('${datarootdir}', '${prefix}/share')\
  84. .replace('${prefix}', '/Users/jinmei/opt')
  85. return specfile_path + '/' + self.__module_name + '.spec'
  86. def _trigger_shutdown(self):
  87. """Initiate a shutdown sequence.
  88. This method is expected to be called in various ways including
  89. in the middle of a signal handler, and is designed to be as simple
  90. as possible to minimize side effects. Actual shutdown will take
  91. place in a normal control flow.
  92. This method is defined as 'protected'. User classes can use it
  93. to shut down the server.
  94. """
  95. self.__shutdown = True
  96. def _run_internal(self):
  97. """Main event loop.
  98. This method is essentially private, but allows tests to override it.
  99. """
  100. logger.info(PYSERVER_COMMON_SERVER_STARTED, self.__module_name)
  101. cc_fileno = self._mod_cc.get_socket().fileno()
  102. while not self.__shutdown:
  103. try:
  104. (reads, _, _) = self._select_fn([cc_fileno], [], [])
  105. except select.error as ex:
  106. # ignore intterruption by signal; regard other select errors
  107. # fatal.
  108. if ex.args[0] == errno.EINTR:
  109. continue
  110. else:
  111. raise
  112. for fileno in reads:
  113. if fileno == cc_fileno:
  114. # this shouldn't raise an exception (if it does, we'll
  115. # propagate it)
  116. self._mod_cc.check_command(True)
  117. self._mod_cc.send_stopping()
  118. def _command_handler(self, cmd, args):
  119. logger.debug(logger.DBGLVL_TRACE_BASIC, PYSERVER_COMMON_COMMAND,
  120. self.__module_name, cmd)
  121. if cmd == 'shutdown':
  122. self._trigger_shutdown()
  123. answer = isc.config.create_answer(0)
  124. else:
  125. answer = self._mod_command_handler(cmd, args)
  126. return answer
  127. def _mod_command_handler(self, cmd, args):
  128. """The default implementation of the module specific command handler"""
  129. return isc.config.create_answer(1, "Unknown command: " + str(cmd))
  130. def run(self, module_name):
  131. """Start the server and let it run until it's told to stop.
  132. Usually this must be the first method of this class that is called
  133. from its user.
  134. Parameter:
  135. module_name (str): the Python module name for the actual server
  136. implementation. Often identical to the directory name in which
  137. the implementation files are placed.
  138. Returns: values expected to be used as program's exit code.
  139. 0: server has run and finished successfully.
  140. 1: some error happens
  141. """
  142. try:
  143. self.__module_name = module_name
  144. shutdown_sighandler = \
  145. lambda signal, frame: self._trigger_shutdown()
  146. signal.signal(signal.SIGTERM, shutdown_sighandler)
  147. signal.signal(signal.SIGINT, shutdown_sighandler)
  148. self._setup_ccsession()
  149. self._run_internal()
  150. logger.info(PYSERVER_COMMON_SERVER_STOPPED, self.__module_name)
  151. return 0
  152. except BIND10ServerFatal as ex:
  153. logger.error(PYSERVER_COMMON_SERVER_FATAL, self.__module_name,
  154. ex)
  155. except Exception as ex:
  156. logger.error(PYSERVER_COMMON_UNCAUGHT_EXCEPTION, type(ex).__name__,
  157. ex)
  158. return 1