暂无描述
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

scripts.py 8.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. import argparse
  2. import sys
  3. from lodel.logger import logger
  4. from lodel.exceptions import LodelException, LodelExceptions, LodelFatalError, DataNoneValid, FieldValidationError
  5. ## @defgroup lodel2_script Administration scripts
  6. # @ingroup lodel2_plugins
  7. ## @package lodel.plugin.scripts Lodel2 utility for writting administration scripts
  8. # @ingroup lodel2_plugins
  9. # @ingroup lodel2_script
  10. ## @brief Stores registered scripts
  11. # @todo store it in MetaLodelScript
  12. __registered_scripts = dict()
  13. ## @brief LodelScript metaclass that allows to "catch" child class declaration
  14. # @ingroup lodel2_script
  15. # @ingroup lodel2_plugins
  16. #
  17. # Automatic action registration on child class declaration
  18. class MetaLodelScript(type):
  19. ##
  20. # @param name str : action's name
  21. # @param bases list
  22. # @param attrs list
  23. def __init__(self, name, bases, attrs):
  24. # Here we can store all child classes of LodelScript
  25. super().__init__(name, bases, attrs)
  26. if len(bases) == 1 and bases[0] == object:
  27. return
  28. self.__register_script(name)
  29. #_action initialization
  30. if self._action is None:
  31. logger.warning("%s._action is None. Trying to use class name as \
  32. action identifier" % name)
  33. self._action = name
  34. self._action = self._action.lower()
  35. if self._description is None:
  36. self._description = self._default_description()
  37. self._parser = argparse.ArgumentParser(
  38. prog = self._prog_name(),
  39. description = self._description)
  40. self.argparser_config(self._parser)
  41. ## @brief Handles script registration
  42. # @note Script list is maitained in lodel.plugin.admin_script.__registered_scripts
  43. # @param name str
  44. def __register_script(self, name):
  45. if self._action is None:
  46. logger.warning("%s._action is None. Trying to use class name as \
  47. action identifier" % name)
  48. self._action = name
  49. self._action = self._action.lower()
  50. script_registration(self._action, self)
  51. def __str__(self):
  52. return '%s : %s' % (self._action, self._description)
  53. ## @brief Class designed to facilitate custom script writting
  54. # @ingroup lodel2_plugins
  55. # @ingroup lodel2_script
  56. class LodelScript(object, metaclass=MetaLodelScript):
  57. ## @brief A string to identify the action
  58. _action = None
  59. ## @brief Script descripiton (argparse argument)
  60. _description = None
  61. ## @brief argparse.ArgumentParser instance
  62. _parser = None
  63. ## @brief No instanciation
  64. def __init__(self):
  65. raise NotImplementedError("Static class")
  66. ## @brief Virtual method. Designed to initialize arguement parser.
  67. # @param parser ArgumentParser : Child class argument parser instance
  68. # @return MUST return the argument parser (NOT SURE ABOUT THAT !! Maybe it works by reference)
  69. # @throw LodelScriptError in case it is not implemented in the child class
  70. @classmethod
  71. def argparser_config(cls, parser):
  72. raise LodelScriptError("LodelScript.argparser_config() is a pure \
  73. virtual method! MUST be implemented by ALL child classes")
  74. ## @brief Virtual method. Runs the script
  75. # @param args list
  76. # @return None or an integer that will be the script return code
  77. # @throw LodelScriptError in case it is not implemented in the child class
  78. @classmethod
  79. def run(cls, args):
  80. raise LodelScriptError("LodelScript.run() is a pure virtual method. \
  81. MUST be implemented by ALL child classes")
  82. ## @brief Executes a script
  83. #
  84. # Called by main_run()
  85. #
  86. # Handles argument parsing and then call LodelScript.run()
  87. @classmethod
  88. def _run(cls):
  89. args = cls._parser.parse_args()
  90. return cls.run(args)
  91. ## @brief Append action name to the prog name
  92. # @note See argparse.ArgumentParser() prog argument
  93. # @return str
  94. @classmethod
  95. def _prog_name(cls):
  96. return '%s %s' % (sys.argv[0], cls._action)
  97. ## @brief Return the default description for an action
  98. # @return str
  99. @classmethod
  100. def _default_description(cls):
  101. return "Lodel2 script : %s" % cls._action
  102. ## @brief handles the help message of an action
  103. # @param msg str
  104. # @param return_code int : the default return code is 1
  105. # @param exit_after bool : default value is True, so that there is an exit after the message printing
  106. @classmethod
  107. def help_exit(cls,msg = None, return_code = 1, exit_after = True):
  108. if not (msg is None):
  109. print(msg, file=sys.stderr)
  110. cls._parser.print_help()
  111. if exit_after:
  112. exit(1)
  113. ## @brief Registers the script class for an action
  114. # @param action_name str
  115. # @param cls LodelScript
  116. def script_registration(action_name, cls):
  117. __registered_scripts[action_name] = cls
  118. logger.info("New script registered : %s" % action_name)
  119. ## @brief Returns a list containing all available actions
  120. # @return list
  121. def _available_actions():
  122. return [ act for act in __registered_scripts ]
  123. ## @brief Returns default runner's argument parser
  124. # @param ArgumentParser
  125. def _default_parser():
  126. action_list = _available_actions()
  127. if len(action_list) > 0:
  128. action_list = ', '.join(sorted(action_list))
  129. else:
  130. action_list = 'NO SCRIPT FOUND !'
  131. parser = argparse.ArgumentParser(description = "Lodel2 script runner")
  132. parser.add_argument('-L', '--list-actions', action='store_true',
  133. default=False, help="List available actions")
  134. parser.add_argument('action', metavar="ACTION", type=str,
  135. help="One of the following actions : %s" % action_list, nargs='?')
  136. parser.add_argument('option', metavar="OPTIONS", type=str, nargs='*',
  137. help="Action options. Use %s ACTION -h to have help on a specific \
  138. action" % sys.argv[0])
  139. return parser
  140. ## @brief Main function of lodel_admin.py script
  141. #
  142. # This function take care to run the good plugins and clean sys.argv from
  143. # action name before running script
  144. #
  145. # @return DO NOT RETURN BUT exit() ONCE SCRIPT EXECUTED !!
  146. def main_run():
  147. default_parser = _default_parser()
  148. if len(sys.argv) == 1:
  149. default_parser.print_help()
  150. exit(1)
  151. #preparing sys.argv (deleting action)
  152. action = sys.argv[1].lower()
  153. if action not in __registered_scripts:
  154. #Trying to parse argument with default parser
  155. args = default_parser.parse_args()
  156. if args.list_actions:
  157. print("Available actions :")
  158. for sname in sorted(__registered_scripts.keys()):
  159. print("\t- %s" % __registered_scripts[sname])
  160. exit(0)
  161. print("Unknow action '%s'\n" % action, file=sys.stderr)
  162. default_parser.print_help()
  163. exit(1)
  164. #OK action is known, preparing argv to pass it to the action script
  165. del(sys.argv[1])
  166. script = __registered_scripts[action]
  167. ret = script._run()
  168. ret = 0 if ret is None else ret
  169. exit(ret)
  170. ## @page lodel2_script_doc Lodel2 scripting
  171. # @ingroup lodel2_script
  172. #
  173. # @section lodel2_script_adm Lodel2 instance administration scripts
  174. #
  175. # In Lodel2, it is possible to administrate instances using either Makefiles
  176. # or lodel_admin.py script ( see @ref lodel2_instance_admin ).
  177. #
  178. # The lodel_admin.py script takes an action as first argument. Each action
  179. # corresponds to a sub-script with its own options etc. To get a list
  180. # of all available action run <code>python3 lodel_admin.py -L</code>.
  181. #
  182. # @section lodel2_script_action lodel_admin.py actions
  183. #
  184. # Action implementation is done by class inheritance. To create a new action,
  185. # one has to write a @ref lodel.plugin.scripts.LodelScript "LodelScript"
  186. # derived class ( see @ref lodel.plugin.core_scripts "core_scripts.py" file
  187. # as example )
  188. #
  189. # @subsection lodel2_script_inheritance LodelScript inheritance
  190. #
  191. # In order to implement properly a new action you have to write a new
  192. # @ref lodel.plugin.scripts.LodelScript "LodelScript" derivated class.
  193. # Some methods and attributes are mandatory to write a fully functionnal
  194. # derivated class. Here is a list :
  195. #
  196. # - mandatory methods
  197. # - @ref plugin.scripts.LodelScript.argparser_config() "argparser_config()" :
  198. #(classmethod) initialize argparse.Parser
  199. # - @ref plugin.scripts.LodelScript.run() "run()" : (classmethod) contains the
  200. #code that runs to perform the action
  201. # - mandatory attributes
  202. # - @ref plugin.scripts.LodelScript::_action "_action" : (class attribute)
  203. #stores action name
  204. # - @ref plugin.scripts.LodelScript::_description "_description" : (class
  205. #attribute) sotres a short action description
  206. #
  207. # @note On script's action registration : once child class is written you only
  208. # need to import it to trigger script's action registration (see
  209. # @ref plugin.scripts.MetaLodelScript )
  210. #