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 7.9KB

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