No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

scripts.py 8.7KB

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