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.

core_scripts.py 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. import operator
  2. import shutil
  3. import tempfile
  4. import os
  5. import os.path
  6. import argparse
  7. from lodel.context import LodelContext
  8. LodelContext.expose_modules(globals(), {
  9. 'lodel.plugin.scripts': 'lodel_script',
  10. 'lodel.logger': 'logger'})
  11. # @package lodel.plugin.core_scripts
  12. #@brief Lodel2 internal scripts declaration
  13. #@ingroup lodel2_plugins
  14. #@ingroup lodel2_script
  15. ## @brief Implement lodel_admin.py list-plugins action
  16. #@ingroup lodel2_plugins
  17. #@ingroup lodel2_script
  18. #
  19. class ListPlugins(lodel_script.LodelScript):
  20. _action = 'plugins-list'
  21. _description = "List all installed plugins"
  22. ## @brief Set available arguments for the script lodel_admin with action plugin-list
  23. # @param parser : a parser (see argparse python module)
  24. @classmethod
  25. def argparser_config(cls, parser):
  26. parser.add_argument('-v', '--verbose',
  27. help="Display more informations on installed plugins",
  28. action='store_true')
  29. parser.add_argument('-c', '--csv',
  30. help="Format output in CSV format",
  31. action='store_true')
  32. ## @brief Display the list of plugins according to given arguments
  33. # @param args : grabbed from argv of command line
  34. @classmethod
  35. def run(cls, args):
  36. import lodel.plugin.plugins
  37. from lodel.plugin.plugins import Plugin
  38. if args.verbose:
  39. #_discover does not return duplicated names
  40. tmp_plist = Plugin._discover(lodel.plugin.plugins.PLUGINS_PATH)
  41. plist = []
  42. # ordering the list by plugin's name
  43. for pname in sorted(set([d['name'] for d in tmp_plist])):
  44. for pinfos in tmp_plist:
  45. if pinfos['name'] == pname:
  46. plist.append(pinfos)
  47. else:
  48. # Retrieve the dict with the list of plugins
  49. pdict = Plugin.discover()
  50. # casting to a list ordered by names
  51. plist = []
  52. for pname in sorted(pdict.keys()):
  53. plist.append(pdict[pname])
  54. if args.csv:
  55. if args.verbose:
  56. res = "name,version,path\n"
  57. fmt = "%s,%s,%s\n"
  58. else:
  59. res = "name,version\n"
  60. fmt = "%s,%s\n"
  61. else:
  62. res = "Installed plugins list :\n"
  63. if args.verbose:
  64. fmt = "\t- %s(%s) in %s\n"
  65. else:
  66. fmt = "\t- %s(%s)\n"
  67. for pinfos in plist:
  68. if args.verbose:
  69. res += fmt % (
  70. pinfos['name'], pinfos['version'], pinfos['path'])
  71. else:
  72. res += fmt % (pinfos['name'], pinfos['version'])
  73. print(res)
  74. ## @brief Handle install & uninstall of lodel plugins
  75. class PluginManager(lodel_script.LodelScript):
  76. _action = 'plugins'
  77. _description = "Install/Uninstall plugins"
  78. ## @brief Set parser's available arguments for lodel_admin.py with action plugins
  79. # @param parser : a parser (see argparse python module)
  80. @classmethod
  81. def argparser_config(cls, parser):
  82. parser.add_argument('-u', '--uninstall',
  83. help="Uninstall specified plugin",
  84. action='store_true')
  85. parser.add_argument('-c', '--clean',
  86. help="Uninstall duplicated plugins with smallest version number",
  87. action="store_true")
  88. parser.add_argument('-n', '--plugin-name', nargs='*',
  89. default=list(),
  90. help="Indicate a plugin name to uninstall",
  91. type=str)
  92. parser.add_argument('-a', '--archive', nargs='*',
  93. default=list(),
  94. help="(NOT IMPLEMENTED) Specify a tarball containing a plugin \
  95. to install",
  96. type=str)
  97. parser.add_argument('-d', '--directory', nargs='*',
  98. default=list(),
  99. help="Specify a plugin by its directory",
  100. type=str)
  101. parser.add_argument('-f', '--force', action="store_true",
  102. help="Force plugin directory deletion in case of check errors")
  103. ## @brief Install, uninstall or clean a plugin according to the option given
  104. # @param args : grabbed from argv of command line
  105. @classmethod
  106. def run(cls, args):
  107. if args.clean:
  108. if args.uninstall or len(args.directory) > 0 \
  109. or len(args.archive) > 0:
  110. raise RuntimeError("When using -c --clean option you can \
  111. only use option -n --name to clean plugins with specified names")
  112. return cls.clean(args)
  113. if args.uninstall:
  114. return cls.uninstall(args)
  115. return cls.install(args)
  116. ## @brief Install plugins
  117. # @param args : grabbed from argv of command line
  118. @classmethod
  119. def install(cls, args):
  120. import lodel.plugin.plugins
  121. from lodel.plugin.plugins import Plugin
  122. from lodel.plugin.exceptions import PluginError
  123. # We can't install a plugin with just its name, we have to know where
  124. # it is
  125. if len(args.plugin_name) > 0:
  126. raise RuntimeError("Unable to install a plugin from its name !\
  127. We do not know where to find it...")
  128. plist = Plugin.discover()
  129. errors = dict()
  130. # For now we do not handle archive for plugins
  131. if len(args.archive) > 0:
  132. raise NotImplementedError("Not supported yet")
  133. plugins_infos = {}
  134. for cur_dir in args.directory:
  135. # Check that the directories obtained correspond to plugins
  136. try:
  137. res = Plugin.dir_is_plugin(cur_dir, assert_in_package=False)
  138. if res is False:
  139. errors[cur_dir] = PluginError("Not a plugin")
  140. else:
  141. plugins_infos[res['name']] = res
  142. except Exception as e:
  143. errors[cur_dir] = e
  144. # Abording because of previous errors
  145. if len(errors) > 0:
  146. msg = "Abording installation because of following errors :\n"
  147. for path, expt in errors.items():
  148. msg += ("\t- For path '%s' : %s\n" % (path, expt))
  149. raise RuntimeError(msg)
  150. # No errors continuing to install
  151. for pname, pinfos in plugins_infos.items():
  152. if pname in plist:
  153. # Found an installed plugin with the same name
  154. # Checking both versions
  155. if plist[pname]['version'] == pinfos['version']:
  156. errors[pinfos['path']] = 'Abording installation of %s \
  157. found in %s because it seems to be allready installed in %s' % (
  158. pname, pinfos['path'], plist[pname]['path'])
  159. continue
  160. if plist[pname]['version'] > pinfos['version']:
  161. errors[pinfos['path']] = 'Abording installation of %s \
  162. found in %s because the same plugins with a greater version seems to be \
  163. installed in %s' % (pname, pinfos['path'], plist[pname]['path'])
  164. continue
  165. logger.info("Found a plugin with the same name but with an \
  166. inferior version. Continuing to install")
  167. # Checking that we can safely copy our plugin
  168. dst_path = os.path.join(lodel.plugin.plugins.PLUGINS_PATH,
  169. os.path.basename(os.path.dirname(pinfos['path'])))
  170. orig_path = dst_path
  171. if os.path.isdir(dst_path):
  172. dst_path = tempfile.mkdtemp(
  173. prefix=os.path.basename(dst_path) + '_',
  174. dir=lodel.plugin.plugins.PLUGINS_PATH)
  175. logger.warning("A plugin already exists in %s. Installing \
  176. in %s" % (orig_path, dst_path))
  177. shutil.rmtree(dst_path)
  178. # Install the plugin
  179. shutil.copytree(pinfos['path'], dst_path, symlinks=False)
  180. print("%s(%s) installed in %s" % (
  181. pname, pinfos['version'], dst_path))
  182. if len(errors) > 0:
  183. msg = "Following errors occurs during installation process :\n"
  184. for path, error_msg in errors.items():
  185. msg += "\t- For '%s' : %s" % (path, error_msg)
  186. print(msg)
  187. ## @brief Uninstall plugins
  188. # @param args : grabbed from argv of command line
  189. # @todo Does nothing for now : delete is commented
  190. @classmethod
  191. def uninstall(cls, args):
  192. import lodel.plugin.plugins
  193. from lodel.plugin.plugins import Plugin
  194. if len(args.archive) > 0:
  195. raise RuntimeError("Cannot uninstall plugin using -f --file \
  196. options. Use -d --directory instead")
  197. to_delete = dict() # will contain all pathes of plugins to delete
  198. errors = dict()
  199. # Uninstall by pathes
  200. if len(args.directory) > 0:
  201. # processing & checking -d --directory arguments
  202. for path in args.directory:
  203. apath = os.path.abspath(path)
  204. # We assume plugins are in lodel/plugins
  205. if not apath.startswith(lodel.plugins.PLUGINS_PATH):
  206. errors[path] = "Not a subdir of %s"
  207. errors[path] %= lodel.plugins.PLUGINS_PATH
  208. continue
  209. try:
  210. pinfos = Plugin.dir_is_plugin(apath)
  211. except Exception as e:
  212. if not args.force:
  213. errors[path] = e
  214. continue
  215. to_delete[path] = pinfos
  216. # Uninstall by plugin's names
  217. # We retrieve the path of the plugin from its name
  218. if len(args.plugin_name) > 0:
  219. # Processing -n --plugin-name arguments
  220. plist = Plugin._discover(lodel.plugins.PLUGINS_PATH)
  221. for pinfos in plist:
  222. if pinfos['name'] in args.plugin_name:
  223. to_delete[pinfos['path']] = pinfos
  224. # Manage errors and exit if there is no force option
  225. if len(errors) > 0:
  226. msg = "Following errors detected before begining deletions :\n"
  227. for path, errmsg in errors.items():
  228. msg += "\t- For %s : %s" % (path, errmsg)
  229. print(msg)
  230. if not args.force:
  231. exit(1)
  232. print("Begining deletion :")
  233. for path, pinfos in to_delete.items():
  234. # shutil.rmtree(path)
  235. print("rm -R %s" % path)
  236. print("\t%s(%s) in %s deleted" % (
  237. pinfos['name'], pinfos['version'], pinfos['path']))
  238. ## @brief Clean plugins by removing plugins with same names \
  239. # The last version is kept
  240. # @param args : grabbed from argv of command line
  241. @classmethod
  242. def clean(cls, args):
  243. import lodel.plugin.plugins
  244. from lodel.plugin.plugins import Plugin
  245. if len(args.archive) > 0:
  246. raise RuntimeError("Cannot specify plugins to uninstall using \
  247. -f --file option. You have to use -d --directory or -n --name")
  248. if len(args.plugin_name) > 0:
  249. names = args.plugin_name
  250. else:
  251. names = list(Plugin.discover().keys())
  252. #_discover do not remove duplicated names
  253. full_list = Plugin._discover(lodel.plugins.PLUGINS_PATH)
  254. # Casting into a dict with list of plugins infos
  255. pdict = dict()
  256. for pinfos in full_list:
  257. if pinfos['name'] in names:
  258. if pinfos['name'] in pdict:
  259. pdict[pinfos['name']].append(pinfos)
  260. else:
  261. pdict[pinfos['name']] = [pinfos]
  262. to_clean = list()
  263. clean_count = 0
  264. for pname, pinfos_l in pdict.items():
  265. if len(pinfos_l) > 1:
  266. # There are some plugins to clean
  267. tmp_l = sorted(pinfos_l, key=lambda item: item['version'])
  268. to_clean += tmp_l[:-1]
  269. msg = "Found %s(%s). Cleaning " % (
  270. pname, tmp_l[-1]['version'])
  271. for pinfos in to_clean:
  272. clean_count += 1
  273. str_info = '%s(%s)' % (pname, pinfos['version'])
  274. msg += "%s, " % (str_info)
  275. shutil.rmtree(pinfos['path'])
  276. print(msg)
  277. if clean_count > 0:
  278. print("%d plugins were uninstalled" % clean_count)
  279. else:
  280. print("Already clean")
  281. ## @brief Implements lodel_admin.py **hooks-list** action
  282. #@ingroup lodel2_script
  283. #@ingroup lodel2_hooks
  284. class ListHooks(lodel_script.LodelScript):
  285. _action = 'hooks-list'
  286. _description = 'Generate a list of registered hooks once instance started'
  287. @classmethod
  288. def argparser_config(cls, parser):
  289. pass
  290. ## @brief Display the list of hooks registered
  291. @classmethod
  292. def run(cls, args):
  293. import loader
  294. loader.start()
  295. from lodel.plugin.hooks import LodelHook
  296. hlist = LodelHook.hook_list()
  297. print("Registered hooks : ")
  298. for name in sorted(hlist.keys()):
  299. print("\t- %s is registered by :" % name)
  300. for hfun, priority in hlist[name]:
  301. msg = "\t\t- {modname}.{funname} with priority : {priority}"
  302. print(msg.format(
  303. modname=hfun.__module__,
  304. funname=hfun.__name__,
  305. priority=priority))
  306. print("\n")