bind10_server.py.in 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  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. _setup_module: can be optionally defined for module-specific
  45. initialization. This is called after the module CC
  46. session has started, and can be used for registering
  47. interest on remote modules, etc. If it raises an
  48. exception, the server will be immediately stopped.
  49. Parameter: None, Return: None
  50. _shutdown_module: can be optionally defined for module-specific
  51. finalization. This is called right before the
  52. module CC session is stopped. If it raises an
  53. exception, the server will be immediately
  54. stopped.
  55. Parameter: None, Return: None
  56. """
  57. # Will be set to True when the server should stop and shut down.
  58. # Can be read via accessor method 'shutdown', mainly for testing.
  59. __shutdown = False
  60. # ModuleCCSession used in the server. Defined as 'protectd' so tests
  61. # can refer to it directly; others should access it via the
  62. # 'mod_ccsession' accessor.
  63. _mod_cc = None
  64. # Will be set in run(). Define a tentative value so other methods can
  65. # be tested directly.
  66. __module_name = ''
  67. # Basically constant, but allow tests to override it.
  68. _select_fn = select.select
  69. def __init__(self):
  70. self._read_callbacks = {}
  71. self._write_callbacks = {}
  72. self._error_callbacks = {}
  73. @property
  74. def shutdown(self):
  75. return self.__shutdown
  76. @property
  77. def mod_ccsession(self):
  78. return self._mod_cc
  79. def _setup_ccsession(self):
  80. """Create and start module CC session.
  81. This is essentially private, but allows tests to override it.
  82. """
  83. self._mod_cc = isc.config.ModuleCCSession(
  84. self._get_specfile_location(), self._config_handler,
  85. self._command_handler)
  86. self._mod_cc.start()
  87. def _get_specfile_location(self):
  88. """Return the path to the module spec file following common convetion.
  89. This method generates the path commonly used by most BIND 10 modules,
  90. determined by a well known prefix and the module name.
  91. A specific module can override this method if it uses a different
  92. path for the spec file.
  93. """
  94. # First check if it's running under an 'in-source' environment,
  95. # then try commonly used paths and file names. If found, use it.
  96. for ev in ['B10_FROM_SOURCE', 'B10_FROM_BUILD']:
  97. if ev in os.environ:
  98. specfile = os.environ[ev] + '/src/bin/' + self.__module_name +\
  99. '/' + self.__module_name + '.spec'
  100. if os.path.exists(specfile):
  101. return specfile
  102. # Otherwise, just use the installed path, whether or not it really
  103. # exists; leave error handling to the caller.
  104. specfile_path = '${datarootdir}/bind10'\
  105. .replace('${datarootdir}', '${prefix}/share')\
  106. .replace('${prefix}', '/Users/jinmei/opt')
  107. return specfile_path + '/' + self.__module_name + '.spec'
  108. def _trigger_shutdown(self):
  109. """Initiate a shutdown sequence.
  110. This method is expected to be called in various ways including
  111. in the middle of a signal handler, and is designed to be as simple
  112. as possible to minimize side effects. Actual shutdown will take
  113. place in a normal control flow.
  114. This method is defined as 'protected'. User classes can use it
  115. to shut down the server.
  116. """
  117. self.__shutdown = True
  118. def _run_internal(self):
  119. """Main event loop.
  120. This method is essentially private, but allows tests to override it.
  121. """
  122. logger.info(PYSERVER_COMMON_SERVER_STARTED, self.__module_name)
  123. cc_fileno = self._mod_cc.get_socket().fileno()
  124. while not self.__shutdown:
  125. try:
  126. read_fds = list(self._read_callbacks.keys())
  127. read_fds.append(cc_fileno)
  128. write_fds = list(self._write_callbacks.keys())
  129. error_fds = list(self._error_callbacks.keys())
  130. (reads, writes, errors) = \
  131. self._select_fn(read_fds, write_fds, error_fds)
  132. except select.error as ex:
  133. # ignore intterruption by signal; regard other select errors
  134. # fatal.
  135. if ex.args[0] == errno.EINTR:
  136. continue
  137. else:
  138. raise
  139. for fileno in reads:
  140. if fileno in self._read_callbacks:
  141. for callback in self._read_callbacks[fileno]:
  142. callback()
  143. for fileno in writes:
  144. if fileno in self._write_callbacks:
  145. for callback in self._write_callbacks[fileno]:
  146. callback()
  147. for fileno in errors:
  148. if fileno in self._error_callbacks:
  149. for callback in self._error_callbacks[fileno]:
  150. callback()
  151. if cc_fileno in reads:
  152. # this shouldn't raise an exception (if it does, we'll
  153. # propagate it)
  154. self._mod_cc.check_command(True)
  155. self._shutdown_module()
  156. self._mod_cc.send_stopping()
  157. def _command_handler(self, cmd, args):
  158. logger.debug(logger.DBGLVL_TRACE_BASIC, PYSERVER_COMMON_COMMAND,
  159. self.__module_name, cmd)
  160. if cmd == 'shutdown':
  161. self._trigger_shutdown()
  162. answer = isc.config.create_answer(0)
  163. else:
  164. answer = self._mod_command_handler(cmd, args)
  165. return answer
  166. def _mod_command_handler(self, cmd, args):
  167. """The default implementation of the module specific command handler"""
  168. return isc.config.create_answer(1, "Unknown command: " + str(cmd))
  169. def _setup_module(self):
  170. """The default implementation of the module specific initialization"""
  171. pass
  172. def _shutdown_module(self):
  173. """The default implementation of the module specific finalization"""
  174. pass
  175. def watch_fileno(self, fileno, rcallback=None, wcallback=None, \
  176. xcallback=None):
  177. """Register the fileno for the internal select() call.
  178. *callback's are callable objects which would be called when
  179. read, write, error events occur on the specified fileno.
  180. """
  181. if rcallback is not None:
  182. if fileno in self._read_callbacks:
  183. self._read_callbacks[fileno].append(rcallback)
  184. else:
  185. self._read_callbacks[fileno] = [rcallback]
  186. if wcallback is not None:
  187. if fileno in self._write_callbacks:
  188. self._write_callbacks[fileno].append(wcallback)
  189. else:
  190. self._write_callbacks[fileno] = [wcallback]
  191. if xcallback is not None:
  192. if fileno in self._error_callbacks:
  193. self._error_callbacks[fileno].append(xcallback)
  194. else:
  195. self._error_callbacks[fileno] = [xcallback]
  196. def run(self, module_name):
  197. """Start the server and let it run until it's told to stop.
  198. Usually this must be the first method of this class that is called
  199. from its user.
  200. Parameter:
  201. module_name (str): the Python module name for the actual server
  202. implementation. Often identical to the directory name in which
  203. the implementation files are placed.
  204. Returns: values expected to be used as program's exit code.
  205. 0: server has run and finished successfully.
  206. 1: some error happens
  207. """
  208. try:
  209. self.__module_name = module_name
  210. shutdown_sighandler = \
  211. lambda signal, frame: self._trigger_shutdown()
  212. signal.signal(signal.SIGTERM, shutdown_sighandler)
  213. signal.signal(signal.SIGINT, shutdown_sighandler)
  214. self._setup_ccsession()
  215. self._setup_module()
  216. self._run_internal()
  217. logger.info(PYSERVER_COMMON_SERVER_STOPPED, self.__module_name)
  218. return 0
  219. except BIND10ServerFatal as ex:
  220. logger.error(PYSERVER_COMMON_SERVER_FATAL, self.__module_name,
  221. ex)
  222. except Exception as ex:
  223. logger.error(PYSERVER_COMMON_UNCAUGHT_EXCEPTION, type(ex).__name__,
  224. ex)
  225. return 1