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

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