ccsession.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. # Copyright (C) 2010,2011 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. """
  16. A mock-up module of isc.cc.session
  17. *** NOTE ***
  18. It is only for testing stats_httpd module and not reusable for
  19. external module.
  20. """
  21. import json
  22. import os
  23. from isc.cc.session import Session
  24. COMMAND_CONFIG_UPDATE = "config_update"
  25. def parse_answer(msg):
  26. assert 'result' in msg
  27. try:
  28. return msg['result'][0], msg['result'][1]
  29. except IndexError:
  30. return msg['result'][0], None
  31. def create_answer(rcode, arg = None):
  32. if arg is None:
  33. return { 'result': [ rcode ] }
  34. else:
  35. return { 'result': [ rcode, arg ] }
  36. def parse_command(msg):
  37. assert 'command' in msg
  38. try:
  39. return msg['command'][0], msg['command'][1]
  40. except IndexError:
  41. return msg['command'][0], None
  42. def create_command(command_name, params = None):
  43. if params is None:
  44. return {"command": [command_name]}
  45. else:
  46. return {"command": [command_name, params]}
  47. def module_spec_from_file(spec_file, check = True):
  48. try:
  49. file = open(spec_file)
  50. json_str = file.read()
  51. module_spec = json.loads(json_str)
  52. file.close()
  53. return ModuleSpec(module_spec['module_spec'], check)
  54. except IOError as ioe:
  55. raise ModuleSpecError("JSON read error: " + str(ioe))
  56. except ValueError as ve:
  57. raise ModuleSpecError("JSON parse error: " + str(ve))
  58. except KeyError as err:
  59. raise ModuleSpecError("Data definition has no module_spec element")
  60. class ModuleSpecError(Exception):
  61. pass
  62. class ModuleSpec:
  63. def __init__(self, module_spec, check = True):
  64. self._module_spec = module_spec
  65. def get_config_spec(self):
  66. return self._module_spec['config_data']
  67. def get_commands_spec(self):
  68. return self._module_spec['commands']
  69. def get_module_name(self):
  70. return self._module_spec['module_name']
  71. class ModuleCCSessionError(Exception):
  72. pass
  73. class DataNotFoundError(Exception):
  74. pass
  75. class ConfigData:
  76. def __init__(self, specification):
  77. self.specification = specification
  78. def get_value(self, identifier):
  79. """Returns a tuple where the first item is the value at the
  80. given identifier, and the second item is absolutely False
  81. even if the value is an unset default or not. Raises an
  82. DataNotFoundError if the identifier is not found in the
  83. specification file.
  84. *** NOTE ***
  85. There are some differences from the original method. This
  86. method never handles local settings like the original
  87. method. But these different behaviors aren't so big issues
  88. for a mock-up method of stats_httpd because stats_httpd
  89. calls this method at only first."""
  90. for config_map in self.get_module_spec().get_config_spec():
  91. if config_map['item_name'] == identifier:
  92. if 'item_default' in config_map:
  93. return config_map['item_default'], False
  94. raise DataNotFoundError("item_name %s is not found in the specfile" % identifier)
  95. def get_module_spec(self):
  96. return self.specification
  97. class ModuleCCSession(ConfigData):
  98. def __init__(self, spec_file_name, config_handler, command_handler, cc_session = None):
  99. module_spec = module_spec_from_file(spec_file_name)
  100. ConfigData.__init__(self, module_spec)
  101. self._module_name = module_spec.get_module_name()
  102. self.set_config_handler(config_handler)
  103. self.set_command_handler(command_handler)
  104. if not cc_session:
  105. self._session = Session(verbose=True)
  106. else:
  107. self._session = cc_session
  108. def start(self):
  109. pass
  110. def close(self):
  111. self._session.close()
  112. def check_command(self, nonblock=True):
  113. msg, env = self._session.group_recvmsg(nonblock)
  114. if not msg or 'result' in msg:
  115. return
  116. cmd, arg = parse_command(msg)
  117. answer = None
  118. if cmd == COMMAND_CONFIG_UPDATE and self._config_handler:
  119. answer = self._config_handler(arg)
  120. elif env['group'] == self._module_name and self._command_handler:
  121. answer = self._command_handler(cmd, arg)
  122. if answer:
  123. self._session.group_reply(env, answer)
  124. def set_config_handler(self, config_handler):
  125. self._config_handler = config_handler
  126. # should we run this right now since we've changed the handler?
  127. def set_command_handler(self, command_handler):
  128. self._command_handler = command_handler
  129. def get_module_spec(self):
  130. return self.specification
  131. def get_socket(self):
  132. return self._session._socket