loader.py 72 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091
  1. # -*- coding: utf-8 -*-
  2. """
  3. The Salt loader is the core to Salt's plugin system, the loader scans
  4. directories for python loadable code and organizes the code into the
  5. plugin interfaces used by Salt.
  6. """
  7. # Import python libs
  8. from __future__ import absolute_import, print_function, unicode_literals
  9. import functools
  10. import inspect
  11. import logging
  12. import os
  13. import re
  14. import sys
  15. import tempfile
  16. import threading
  17. import time
  18. import traceback
  19. import types
  20. from collections.abc import MutableMapping
  21. from zipimport import zipimporter
  22. # Import salt libs
  23. import salt.config
  24. import salt.defaults.events
  25. import salt.defaults.exitcodes
  26. import salt.syspaths
  27. import salt.utils.args
  28. import salt.utils.context
  29. import salt.utils.data
  30. import salt.utils.dictupdate
  31. import salt.utils.event
  32. import salt.utils.files
  33. import salt.utils.lazy
  34. import salt.utils.odict
  35. import salt.utils.platform
  36. import salt.utils.stringutils
  37. import salt.utils.versions
  38. from salt.exceptions import LoaderError
  39. # Import 3rd-party libs
  40. from salt.ext import six
  41. from salt.ext.six.moves import reload_module
  42. from salt.template import check_render_pipe_str
  43. from salt.utils.decorators import Depends
  44. if sys.version_info[:2] >= (3, 5):
  45. import importlib.machinery # pylint: disable=no-name-in-module,import-error
  46. import importlib.util # pylint: disable=no-name-in-module,import-error
  47. USE_IMPORTLIB = True
  48. else:
  49. import imp
  50. USE_IMPORTLIB = False
  51. try:
  52. import pkg_resources
  53. HAS_PKG_RESOURCES = True
  54. except ImportError:
  55. HAS_PKG_RESOURCES = False
  56. log = logging.getLogger(__name__)
  57. SALT_BASE_PATH = os.path.abspath(salt.syspaths.INSTALL_DIR)
  58. LOADED_BASE_NAME = "salt.loaded"
  59. if USE_IMPORTLIB:
  60. # pylint: disable=no-member
  61. MODULE_KIND_SOURCE = 1
  62. MODULE_KIND_COMPILED = 2
  63. MODULE_KIND_EXTENSION = 3
  64. MODULE_KIND_PKG_DIRECTORY = 5
  65. SUFFIXES = []
  66. for suffix in importlib.machinery.EXTENSION_SUFFIXES:
  67. SUFFIXES.append((suffix, "rb", MODULE_KIND_EXTENSION))
  68. for suffix in importlib.machinery.SOURCE_SUFFIXES:
  69. SUFFIXES.append((suffix, "rb", MODULE_KIND_SOURCE))
  70. for suffix in importlib.machinery.BYTECODE_SUFFIXES:
  71. SUFFIXES.append((suffix, "rb", MODULE_KIND_COMPILED))
  72. MODULE_KIND_MAP = {
  73. MODULE_KIND_SOURCE: importlib.machinery.SourceFileLoader,
  74. MODULE_KIND_COMPILED: importlib.machinery.SourcelessFileLoader,
  75. MODULE_KIND_EXTENSION: importlib.machinery.ExtensionFileLoader,
  76. }
  77. # pylint: enable=no-member
  78. else:
  79. SUFFIXES = imp.get_suffixes()
  80. PY3_PRE_EXT = re.compile(
  81. r"\.cpython-{0}{1}(\.opt-[1-9])?".format(*sys.version_info[:2])
  82. )
  83. # Because on the cloud drivers we do `from salt.cloud.libcloudfuncs import *`
  84. # which simplifies code readability, it adds some unsupported functions into
  85. # the driver's module scope.
  86. # We list un-supported functions here. These will be removed from the loaded.
  87. # TODO: remove the need for this cross-module code. Maybe use NotImplemented
  88. LIBCLOUD_FUNCS_NOT_SUPPORTED = (
  89. "parallels.avail_sizes",
  90. "parallels.avail_locations",
  91. "proxmox.avail_sizes",
  92. )
  93. # Will be set to pyximport module at runtime if cython is enabled in config.
  94. pyximport = None
  95. def static_loader(
  96. opts,
  97. ext_type,
  98. tag,
  99. pack=None,
  100. int_type=None,
  101. ext_dirs=True,
  102. ext_type_dirs=None,
  103. base_path=None,
  104. filter_name=None,
  105. ):
  106. funcs = LazyLoader(
  107. _module_dirs(
  108. opts, ext_type, tag, int_type, ext_dirs, ext_type_dirs, base_path,
  109. ),
  110. opts,
  111. tag=tag,
  112. pack=pack,
  113. )
  114. ret = {}
  115. funcs._load_all()
  116. if filter_name:
  117. funcs = FilterDictWrapper(funcs, filter_name)
  118. for key in funcs:
  119. ret[key] = funcs[key]
  120. return ret
  121. def _format_entrypoint_target(ep):
  122. """
  123. Makes a string describing the target of an EntryPoint object.
  124. Base strongly on EntryPoint.__str__().
  125. """
  126. s = ep.module_name
  127. if ep.attrs:
  128. s += ":" + ".".join(ep.attrs)
  129. return s
  130. def _module_dirs(
  131. opts,
  132. ext_type,
  133. tag=None,
  134. int_type=None,
  135. ext_dirs=True,
  136. ext_type_dirs=None,
  137. base_path=None,
  138. ):
  139. if tag is None:
  140. tag = ext_type
  141. sys_types = os.path.join(base_path or SALT_BASE_PATH, int_type or ext_type)
  142. ext_types = os.path.join(opts["extension_modules"], ext_type)
  143. ext_type_types = []
  144. if ext_dirs:
  145. if ext_type_dirs is None:
  146. ext_type_dirs = "{0}_dirs".format(tag)
  147. if ext_type_dirs in opts:
  148. ext_type_types.extend(opts[ext_type_dirs])
  149. if HAS_PKG_RESOURCES and ext_type_dirs:
  150. for entry_point in pkg_resources.iter_entry_points(
  151. "salt.loader", ext_type_dirs
  152. ):
  153. try:
  154. loaded_entry_point = entry_point.load()
  155. for path in loaded_entry_point():
  156. ext_type_types.append(path)
  157. except Exception as exc: # pylint: disable=broad-except
  158. log.error(
  159. "Error getting module directories from %s: %s",
  160. _format_entrypoint_target(entry_point),
  161. exc,
  162. )
  163. log.debug(
  164. "Full backtrace for module directories error", exc_info=True
  165. )
  166. cli_module_dirs = []
  167. # The dirs can be any module dir, or a in-tree _{ext_type} dir
  168. for _dir in opts.get("module_dirs", []):
  169. # Prepend to the list to match cli argument ordering
  170. maybe_dir = os.path.join(_dir, ext_type)
  171. if os.path.isdir(maybe_dir):
  172. cli_module_dirs.insert(0, maybe_dir)
  173. continue
  174. maybe_dir = os.path.join(_dir, "_{0}".format(ext_type))
  175. if os.path.isdir(maybe_dir):
  176. cli_module_dirs.insert(0, maybe_dir)
  177. return cli_module_dirs + ext_type_types + [ext_types, sys_types]
  178. def minion_mods(
  179. opts,
  180. context=None,
  181. utils=None,
  182. whitelist=None,
  183. initial_load=False,
  184. loaded_base_name=None,
  185. notify=False,
  186. static_modules=None,
  187. proxy=None,
  188. ):
  189. """
  190. Load execution modules
  191. Returns a dictionary of execution modules appropriate for the current
  192. system by evaluating the __virtual__() function in each module.
  193. :param dict opts: The Salt options dictionary
  194. :param dict context: A Salt context that should be made present inside
  195. generated modules in __context__
  196. :param dict utils: Utility functions which should be made available to
  197. Salt modules in __utils__. See `utils_dirs` in
  198. salt.config for additional information about
  199. configuration.
  200. :param list whitelist: A list of modules which should be whitelisted.
  201. :param bool initial_load: Deprecated flag! Unused.
  202. :param str loaded_base_name: A string marker for the loaded base name.
  203. :param bool notify: Flag indicating that an event should be fired upon
  204. completion of module loading.
  205. .. code-block:: python
  206. import salt.config
  207. import salt.loader
  208. __opts__ = salt.config.minion_config('/etc/salt/minion')
  209. __grains__ = salt.loader.grains(__opts__)
  210. __opts__['grains'] = __grains__
  211. __utils__ = salt.loader.utils(__opts__)
  212. __salt__ = salt.loader.minion_mods(__opts__, utils=__utils__)
  213. __salt__['test.ping']()
  214. """
  215. # TODO Publish documentation for module whitelisting
  216. if not whitelist:
  217. whitelist = opts.get("whitelist_modules", None)
  218. ret = LazyLoader(
  219. _module_dirs(opts, "modules", "module"),
  220. opts,
  221. tag="module",
  222. pack={"__context__": context, "__utils__": utils, "__proxy__": proxy},
  223. whitelist=whitelist,
  224. loaded_base_name=loaded_base_name,
  225. static_modules=static_modules,
  226. extra_module_dirs=utils.module_dirs if utils else None,
  227. )
  228. ret.pack["__salt__"] = ret
  229. # Load any provider overrides from the configuration file providers option
  230. # Note: Providers can be pkg, service, user or group - not to be confused
  231. # with cloud providers.
  232. providers = opts.get("providers", False)
  233. if providers and isinstance(providers, dict):
  234. for mod in providers:
  235. # sometimes providers opts is not to diverge modules but
  236. # for other configuration
  237. try:
  238. funcs = raw_mod(opts, providers[mod], ret)
  239. except TypeError:
  240. break
  241. else:
  242. if funcs:
  243. for func in funcs:
  244. f_key = "{0}{1}".format(mod, func[func.rindex(".") :])
  245. ret[f_key] = funcs[func]
  246. if notify:
  247. with salt.utils.event.get_event("minion", opts=opts, listen=False) as evt:
  248. evt.fire_event(
  249. {"complete": True}, tag=salt.defaults.events.MINION_MOD_REFRESH_COMPLETE
  250. )
  251. return ret
  252. def raw_mod(opts, name, functions, mod="modules"):
  253. """
  254. Returns a single module loaded raw and bypassing the __virtual__ function
  255. .. code-block:: python
  256. import salt.config
  257. import salt.loader
  258. __opts__ = salt.config.minion_config('/etc/salt/minion')
  259. testmod = salt.loader.raw_mod(__opts__, 'test', None)
  260. testmod['test.ping']()
  261. """
  262. loader = LazyLoader(
  263. _module_dirs(opts, mod, "module"),
  264. opts,
  265. tag="rawmodule",
  266. virtual_enable=False,
  267. pack={"__salt__": functions},
  268. )
  269. # if we don't have the module, return an empty dict
  270. if name not in loader.file_mapping:
  271. return {}
  272. loader._load_module(name) # load a single module (the one passed in)
  273. return dict(loader._dict) # return a copy of *just* the funcs for `name`
  274. def metaproxy(opts):
  275. """
  276. Return functions used in the meta proxy
  277. """
  278. return LazyLoader(_module_dirs(opts, "metaproxy"), opts, tag="metaproxy")
  279. def matchers(opts):
  280. """
  281. Return the matcher services plugins
  282. """
  283. return LazyLoader(_module_dirs(opts, "matchers"), opts, tag="matchers")
  284. def engines(opts, functions, runners, utils, proxy=None):
  285. """
  286. Return the master services plugins
  287. """
  288. pack = {
  289. "__salt__": functions,
  290. "__runners__": runners,
  291. "__proxy__": proxy,
  292. "__utils__": utils,
  293. }
  294. return LazyLoader(
  295. _module_dirs(opts, "engines"),
  296. opts,
  297. tag="engines",
  298. pack=pack,
  299. extra_module_dirs=utils.module_dirs if utils else None,
  300. )
  301. def proxy(opts, functions=None, returners=None, whitelist=None, utils=None):
  302. """
  303. Returns the proxy module for this salt-proxy-minion
  304. """
  305. ret = LazyLoader(
  306. _module_dirs(opts, "proxy"),
  307. opts,
  308. tag="proxy",
  309. pack={"__salt__": functions, "__ret__": returners, "__utils__": utils},
  310. extra_module_dirs=utils.module_dirs if utils else None,
  311. )
  312. ret.pack["__proxy__"] = ret
  313. return ret
  314. def returners(opts, functions, whitelist=None, context=None, proxy=None):
  315. """
  316. Returns the returner modules
  317. """
  318. return LazyLoader(
  319. _module_dirs(opts, "returners", "returner"),
  320. opts,
  321. tag="returner",
  322. whitelist=whitelist,
  323. pack={"__salt__": functions, "__context__": context, "__proxy__": proxy or {}},
  324. )
  325. def utils(opts, whitelist=None, context=None, proxy=proxy):
  326. """
  327. Returns the utility modules
  328. """
  329. return LazyLoader(
  330. _module_dirs(opts, "utils", ext_type_dirs="utils_dirs"),
  331. opts,
  332. tag="utils",
  333. whitelist=whitelist,
  334. pack={"__context__": context, "__proxy__": proxy or {}},
  335. )
  336. def pillars(opts, functions, context=None):
  337. """
  338. Returns the pillars modules
  339. """
  340. _utils = utils(opts)
  341. ret = LazyLoader(
  342. _module_dirs(opts, "pillar"),
  343. opts,
  344. tag="pillar",
  345. pack={"__salt__": functions, "__context__": context, "__utils__": _utils},
  346. extra_module_dirs=_utils.module_dirs,
  347. )
  348. ret.pack["__ext_pillar__"] = ret
  349. return FilterDictWrapper(ret, ".ext_pillar")
  350. def tops(opts):
  351. """
  352. Returns the tops modules
  353. """
  354. if "master_tops" not in opts:
  355. return {}
  356. whitelist = list(opts["master_tops"].keys())
  357. ret = LazyLoader(
  358. _module_dirs(opts, "tops", "top"), opts, tag="top", whitelist=whitelist,
  359. )
  360. return FilterDictWrapper(ret, ".top")
  361. def wheels(opts, whitelist=None, context=None):
  362. """
  363. Returns the wheels modules
  364. """
  365. if context is None:
  366. context = {}
  367. return LazyLoader(
  368. _module_dirs(opts, "wheel"),
  369. opts,
  370. tag="wheel",
  371. whitelist=whitelist,
  372. pack={"__context__": context},
  373. )
  374. def outputters(opts):
  375. """
  376. Returns the outputters modules
  377. :param dict opts: The Salt options dictionary
  378. :returns: LazyLoader instance, with only outputters present in the keyspace
  379. """
  380. ret = LazyLoader(
  381. _module_dirs(opts, "output", ext_type_dirs="outputter_dirs"),
  382. opts,
  383. tag="output",
  384. )
  385. wrapped_ret = FilterDictWrapper(ret, ".output")
  386. # TODO: this name seems terrible... __salt__ should always be execution mods
  387. ret.pack["__salt__"] = wrapped_ret
  388. return wrapped_ret
  389. def serializers(opts):
  390. """
  391. Returns the serializers modules
  392. :param dict opts: The Salt options dictionary
  393. :returns: LazyLoader instance, with only serializers present in the keyspace
  394. """
  395. return LazyLoader(_module_dirs(opts, "serializers"), opts, tag="serializers",)
  396. def eauth_tokens(opts):
  397. """
  398. Returns the tokens modules
  399. :param dict opts: The Salt options dictionary
  400. :returns: LazyLoader instance, with only token backends present in the keyspace
  401. """
  402. return LazyLoader(_module_dirs(opts, "tokens"), opts, tag="tokens",)
  403. def auth(opts, whitelist=None):
  404. """
  405. Returns the auth modules
  406. :param dict opts: The Salt options dictionary
  407. :returns: LazyLoader
  408. """
  409. return LazyLoader(
  410. _module_dirs(opts, "auth"),
  411. opts,
  412. tag="auth",
  413. whitelist=whitelist,
  414. pack={"__salt__": minion_mods(opts)},
  415. )
  416. def fileserver(opts, backends):
  417. """
  418. Returns the file server modules
  419. """
  420. _utils = utils(opts)
  421. if backends is not None:
  422. if not isinstance(backends, list):
  423. backends = [backends]
  424. # Make sure that the VCS backends work either with git or gitfs, hg or
  425. # hgfs, etc.
  426. vcs_re = re.compile("^(git|svn|hg)")
  427. fs_re = re.compile("fs$")
  428. vcs = []
  429. non_vcs = []
  430. for back in [fs_re.sub("", x) for x in backends]:
  431. if vcs_re.match(back):
  432. vcs.extend((back, back + "fs"))
  433. else:
  434. non_vcs.append(back)
  435. backends = vcs + non_vcs
  436. return LazyLoader(
  437. _module_dirs(opts, "fileserver"),
  438. opts,
  439. tag="fileserver",
  440. whitelist=backends,
  441. pack={"__utils__": _utils},
  442. extra_module_dirs=_utils.module_dirs,
  443. )
  444. def roster(opts, runner=None, utils=None, whitelist=None):
  445. """
  446. Returns the roster modules
  447. """
  448. return LazyLoader(
  449. _module_dirs(opts, "roster"),
  450. opts,
  451. tag="roster",
  452. whitelist=whitelist,
  453. pack={"__runner__": runner, "__utils__": utils},
  454. extra_module_dirs=utils.module_dirs if utils else None,
  455. )
  456. def thorium(opts, functions, runners):
  457. """
  458. Load the thorium runtime modules
  459. """
  460. pack = {"__salt__": functions, "__runner__": runners, "__context__": {}}
  461. ret = LazyLoader(_module_dirs(opts, "thorium"), opts, tag="thorium", pack=pack)
  462. ret.pack["__thorium__"] = ret
  463. return ret
  464. def states(
  465. opts, functions, utils, serializers, whitelist=None, proxy=None, context=None
  466. ):
  467. """
  468. Returns the state modules
  469. :param dict opts: The Salt options dictionary
  470. :param dict functions: A dictionary of minion modules, with module names as
  471. keys and funcs as values.
  472. .. code-block:: python
  473. import salt.config
  474. import salt.loader
  475. __opts__ = salt.config.minion_config('/etc/salt/minion')
  476. statemods = salt.loader.states(__opts__, None, None)
  477. """
  478. if context is None:
  479. context = {}
  480. ret = LazyLoader(
  481. _module_dirs(opts, "states"),
  482. opts,
  483. tag="states",
  484. pack={"__salt__": functions, "__proxy__": proxy or {}},
  485. whitelist=whitelist,
  486. extra_module_dirs=utils.module_dirs if utils else None,
  487. )
  488. ret.pack["__states__"] = ret
  489. ret.pack["__utils__"] = utils
  490. ret.pack["__serializers__"] = serializers
  491. ret.pack["__context__"] = context
  492. return ret
  493. def beacons(opts, functions, context=None, proxy=None):
  494. """
  495. Load the beacon modules
  496. :param dict opts: The Salt options dictionary
  497. :param dict functions: A dictionary of minion modules, with module names as
  498. keys and funcs as values.
  499. """
  500. return LazyLoader(
  501. _module_dirs(opts, "beacons"),
  502. opts,
  503. tag="beacons",
  504. pack={"__context__": context, "__salt__": functions, "__proxy__": proxy or {}},
  505. virtual_funcs=[],
  506. )
  507. def log_handlers(opts):
  508. """
  509. Returns the custom logging handler modules
  510. :param dict opts: The Salt options dictionary
  511. """
  512. ret = LazyLoader(
  513. _module_dirs(
  514. opts,
  515. "log_handlers",
  516. int_type="handlers",
  517. base_path=os.path.join(SALT_BASE_PATH, "log"),
  518. ),
  519. opts,
  520. tag="log_handlers",
  521. )
  522. return FilterDictWrapper(ret, ".setup_handlers")
  523. def ssh_wrapper(opts, functions=None, context=None):
  524. """
  525. Returns the custom logging handler modules
  526. """
  527. return LazyLoader(
  528. _module_dirs(
  529. opts,
  530. "wrapper",
  531. base_path=os.path.join(SALT_BASE_PATH, os.path.join("client", "ssh")),
  532. ),
  533. opts,
  534. tag="wrapper",
  535. pack={
  536. "__salt__": functions,
  537. "__grains__": opts.get("grains", {}),
  538. "__pillar__": opts.get("pillar", {}),
  539. "__context__": context,
  540. },
  541. )
  542. def render(opts, functions, states=None, proxy=None, context=None):
  543. """
  544. Returns the render modules
  545. """
  546. if context is None:
  547. context = {}
  548. pack = {
  549. "__salt__": functions,
  550. "__grains__": opts.get("grains", {}),
  551. "__context__": context,
  552. }
  553. if states:
  554. pack["__states__"] = states
  555. pack["__proxy__"] = proxy or {}
  556. ret = LazyLoader(
  557. _module_dirs(opts, "renderers", "render", ext_type_dirs="render_dirs",),
  558. opts,
  559. tag="render",
  560. pack=pack,
  561. )
  562. rend = FilterDictWrapper(ret, ".render")
  563. if not check_render_pipe_str(
  564. opts["renderer"], rend, opts["renderer_blacklist"], opts["renderer_whitelist"]
  565. ):
  566. err = (
  567. "The renderer {0} is unavailable, this error is often because "
  568. "the needed software is unavailable".format(opts["renderer"])
  569. )
  570. log.critical(err)
  571. raise LoaderError(err)
  572. return rend
  573. def grain_funcs(opts, proxy=None):
  574. """
  575. Returns the grain functions
  576. .. code-block:: python
  577. import salt.config
  578. import salt.loader
  579. __opts__ = salt.config.minion_config('/etc/salt/minion')
  580. grainfuncs = salt.loader.grain_funcs(__opts__)
  581. """
  582. _utils = utils(opts, proxy=proxy)
  583. ret = LazyLoader(
  584. _module_dirs(opts, "grains", "grain", ext_type_dirs="grains_dirs",),
  585. opts,
  586. tag="grains",
  587. extra_module_dirs=_utils.module_dirs,
  588. )
  589. ret.pack["__utils__"] = _utils
  590. return ret
  591. def _format_cached_grains(cached_grains):
  592. """
  593. Returns cached grains with fixed types, like tuples.
  594. """
  595. if cached_grains.get("osrelease_info"):
  596. osrelease_info = cached_grains["osrelease_info"]
  597. if isinstance(osrelease_info, list):
  598. cached_grains["osrelease_info"] = tuple(osrelease_info)
  599. return cached_grains
  600. def _load_cached_grains(opts, cfn):
  601. """
  602. Returns the grains cached in cfn, or None if the cache is too old or is
  603. corrupted.
  604. """
  605. if not os.path.isfile(cfn):
  606. log.debug("Grains cache file does not exist.")
  607. return None
  608. grains_cache_age = int(time.time() - os.path.getmtime(cfn))
  609. if grains_cache_age > opts.get("grains_cache_expiration", 300):
  610. log.debug(
  611. "Grains cache last modified %s seconds ago and cache "
  612. "expiration is set to %s. Grains cache expired. "
  613. "Refreshing.",
  614. grains_cache_age,
  615. opts.get("grains_cache_expiration", 300),
  616. )
  617. return None
  618. if opts.get("refresh_grains_cache", False):
  619. log.debug("refresh_grains_cache requested, Refreshing.")
  620. return None
  621. log.debug("Retrieving grains from cache")
  622. try:
  623. serial = salt.payload.Serial(opts)
  624. with salt.utils.files.fopen(cfn, "rb") as fp_:
  625. cached_grains = salt.utils.data.decode(
  626. serial.load(fp_), preserve_tuples=True
  627. )
  628. if not cached_grains:
  629. log.debug("Cached grains are empty, cache might be corrupted. Refreshing.")
  630. return None
  631. return _format_cached_grains(cached_grains)
  632. except (IOError, OSError):
  633. return None
  634. def grains(opts, force_refresh=False, proxy=None):
  635. """
  636. Return the functions for the dynamic grains and the values for the static
  637. grains.
  638. Since grains are computed early in the startup process, grains functions
  639. do not have __salt__ or __proxy__ available. At proxy-minion startup,
  640. this function is called with the proxymodule LazyLoader object so grains
  641. functions can communicate with their controlled device.
  642. .. code-block:: python
  643. import salt.config
  644. import salt.loader
  645. __opts__ = salt.config.minion_config('/etc/salt/minion')
  646. __grains__ = salt.loader.grains(__opts__)
  647. print __grains__['id']
  648. """
  649. # Need to re-import salt.config, somehow it got lost when a minion is starting
  650. import salt.config
  651. # if we have no grains, lets try loading from disk (TODO: move to decorator?)
  652. cfn = os.path.join(opts["cachedir"], "grains.cache.p")
  653. if not force_refresh and opts.get("grains_cache", False):
  654. cached_grains = _load_cached_grains(opts, cfn)
  655. if cached_grains:
  656. return cached_grains
  657. else:
  658. log.debug("Grains refresh requested. Refreshing grains.")
  659. if opts.get("skip_grains", False):
  660. return {}
  661. grains_deep_merge = opts.get("grains_deep_merge", False) is True
  662. if "conf_file" in opts:
  663. pre_opts = {}
  664. pre_opts.update(
  665. salt.config.load_config(
  666. opts["conf_file"],
  667. "SALT_MINION_CONFIG",
  668. salt.config.DEFAULT_MINION_OPTS["conf_file"],
  669. )
  670. )
  671. default_include = pre_opts.get("default_include", opts["default_include"])
  672. include = pre_opts.get("include", [])
  673. pre_opts.update(
  674. salt.config.include_config(
  675. default_include, opts["conf_file"], verbose=False
  676. )
  677. )
  678. pre_opts.update(
  679. salt.config.include_config(include, opts["conf_file"], verbose=True)
  680. )
  681. if "grains" in pre_opts:
  682. opts["grains"] = pre_opts["grains"]
  683. else:
  684. opts["grains"] = {}
  685. else:
  686. opts["grains"] = {}
  687. grains_data = {}
  688. blist = opts.get("grains_blacklist", [])
  689. funcs = grain_funcs(opts, proxy=proxy)
  690. if force_refresh: # if we refresh, lets reload grain modules
  691. funcs.clear()
  692. # Run core grains
  693. for key in funcs:
  694. if not key.startswith("core."):
  695. continue
  696. log.trace("Loading %s grain", key)
  697. ret = funcs[key]()
  698. if not isinstance(ret, dict):
  699. continue
  700. if blist:
  701. for key in list(ret):
  702. for block in blist:
  703. if salt.utils.stringutils.expr_match(key, block):
  704. del ret[key]
  705. log.trace("Filtering %s grain", key)
  706. if not ret:
  707. continue
  708. if grains_deep_merge:
  709. salt.utils.dictupdate.update(grains_data, ret)
  710. else:
  711. grains_data.update(ret)
  712. # Run the rest of the grains
  713. for key in funcs:
  714. if key.startswith("core.") or key == "_errors":
  715. continue
  716. try:
  717. # Grains are loaded too early to take advantage of the injected
  718. # __proxy__ variable. Pass an instance of that LazyLoader
  719. # here instead to grains functions if the grains functions take
  720. # one parameter. Then the grains can have access to the
  721. # proxymodule for retrieving information from the connected
  722. # device.
  723. log.trace("Loading %s grain", key)
  724. parameters = salt.utils.args.get_function_argspec(funcs[key]).args
  725. kwargs = {}
  726. if "proxy" in parameters:
  727. kwargs["proxy"] = proxy
  728. if "grains" in parameters:
  729. kwargs["grains"] = grains_data
  730. ret = funcs[key](**kwargs)
  731. except Exception: # pylint: disable=broad-except
  732. if salt.utils.platform.is_proxy():
  733. log.info(
  734. "The following CRITICAL message may not be an error; the proxy may not be completely established yet."
  735. )
  736. log.critical(
  737. "Failed to load grains defined in grain file %s in "
  738. "function %s, error:\n",
  739. key,
  740. funcs[key],
  741. exc_info=True,
  742. )
  743. continue
  744. if not isinstance(ret, dict):
  745. continue
  746. if blist:
  747. for key in list(ret):
  748. for block in blist:
  749. if salt.utils.stringutils.expr_match(key, block):
  750. del ret[key]
  751. log.trace("Filtering %s grain", key)
  752. if not ret:
  753. continue
  754. if grains_deep_merge:
  755. salt.utils.dictupdate.update(grains_data, ret)
  756. else:
  757. grains_data.update(ret)
  758. if opts.get("proxy_merge_grains_in_module", True) and proxy:
  759. try:
  760. proxytype = proxy.opts["proxy"]["proxytype"]
  761. if proxytype + ".grains" in proxy:
  762. if (
  763. proxytype + ".initialized" in proxy
  764. and proxy[proxytype + ".initialized"]()
  765. ):
  766. try:
  767. proxytype = proxy.opts["proxy"]["proxytype"]
  768. ret = proxy[proxytype + ".grains"]()
  769. if grains_deep_merge:
  770. salt.utils.dictupdate.update(grains_data, ret)
  771. else:
  772. grains_data.update(ret)
  773. except Exception: # pylint: disable=broad-except
  774. log.critical(
  775. "Failed to run proxy's grains function!", exc_info=True
  776. )
  777. except KeyError:
  778. pass
  779. grains_data.update(opts["grains"])
  780. # Write cache if enabled
  781. if opts.get("grains_cache", False):
  782. with salt.utils.files.set_umask(0o077):
  783. try:
  784. if salt.utils.platform.is_windows():
  785. # Late import
  786. import salt.modules.cmdmod
  787. # Make sure cache file isn't read-only
  788. salt.modules.cmdmod._run_quiet('attrib -R "{0}"'.format(cfn))
  789. with salt.utils.files.fopen(cfn, "w+b") as fp_:
  790. try:
  791. serial = salt.payload.Serial(opts)
  792. serial.dump(grains_data, fp_)
  793. except TypeError as e:
  794. log.error("Failed to serialize grains cache: %s", e)
  795. raise # re-throw for cleanup
  796. except Exception as e: # pylint: disable=broad-except
  797. log.error("Unable to write to grains cache file %s: %s", cfn, e)
  798. # Based on the original exception, the file may or may not have been
  799. # created. If it was, we will remove it now, as the exception means
  800. # the serialized data is not to be trusted, no matter what the
  801. # exception is.
  802. if os.path.isfile(cfn):
  803. os.unlink(cfn)
  804. if grains_deep_merge:
  805. salt.utils.dictupdate.update(grains_data, opts["grains"])
  806. else:
  807. grains_data.update(opts["grains"])
  808. return salt.utils.data.decode(grains_data, preserve_tuples=True)
  809. # TODO: get rid of? Does anyone use this? You should use raw() instead
  810. def call(fun, **kwargs):
  811. """
  812. Directly call a function inside a loader directory
  813. """
  814. args = kwargs.get("args", [])
  815. dirs = kwargs.get("dirs", [])
  816. funcs = LazyLoader(
  817. [os.path.join(SALT_BASE_PATH, "modules")] + dirs,
  818. None,
  819. tag="modules",
  820. virtual_enable=False,
  821. )
  822. return funcs[fun](*args)
  823. def runner(opts, utils=None, context=None, whitelist=None):
  824. """
  825. Directly call a function inside a loader directory
  826. """
  827. if utils is None:
  828. utils = {}
  829. if context is None:
  830. context = {}
  831. ret = LazyLoader(
  832. _module_dirs(opts, "runners", "runner", ext_type_dirs="runner_dirs"),
  833. opts,
  834. tag="runners",
  835. pack={"__utils__": utils, "__context__": context},
  836. whitelist=whitelist,
  837. extra_module_dirs=utils.module_dirs if utils else None,
  838. )
  839. # TODO: change from __salt__ to something else, we overload __salt__ too much
  840. ret.pack["__salt__"] = ret
  841. return ret
  842. def queues(opts):
  843. """
  844. Directly call a function inside a loader directory
  845. """
  846. return LazyLoader(
  847. _module_dirs(opts, "queues", "queue", ext_type_dirs="queue_dirs"),
  848. opts,
  849. tag="queues",
  850. )
  851. def sdb(opts, functions=None, whitelist=None, utils=None):
  852. """
  853. Make a very small database call
  854. """
  855. if utils is None:
  856. utils = {}
  857. return LazyLoader(
  858. _module_dirs(opts, "sdb"),
  859. opts,
  860. tag="sdb",
  861. pack={
  862. "__sdb__": functions,
  863. "__opts__": opts,
  864. "__utils__": utils,
  865. "__salt__": minion_mods(opts, utils=utils),
  866. },
  867. whitelist=whitelist,
  868. extra_module_dirs=utils.module_dirs if utils else None,
  869. )
  870. def pkgdb(opts):
  871. """
  872. Return modules for SPM's package database
  873. .. versionadded:: 2015.8.0
  874. """
  875. return LazyLoader(
  876. _module_dirs(opts, "pkgdb", base_path=os.path.join(SALT_BASE_PATH, "spm")),
  877. opts,
  878. tag="pkgdb",
  879. )
  880. def pkgfiles(opts):
  881. """
  882. Return modules for SPM's file handling
  883. .. versionadded:: 2015.8.0
  884. """
  885. return LazyLoader(
  886. _module_dirs(opts, "pkgfiles", base_path=os.path.join(SALT_BASE_PATH, "spm")),
  887. opts,
  888. tag="pkgfiles",
  889. )
  890. def clouds(opts):
  891. """
  892. Return the cloud functions
  893. """
  894. _utils = salt.loader.utils(opts)
  895. # Let's bring __active_provider_name__, defaulting to None, to all cloud
  896. # drivers. This will get temporarily updated/overridden with a context
  897. # manager when needed.
  898. functions = LazyLoader(
  899. _module_dirs(
  900. opts,
  901. "clouds",
  902. "cloud",
  903. base_path=os.path.join(SALT_BASE_PATH, "cloud"),
  904. int_type="clouds",
  905. ),
  906. opts,
  907. tag="clouds",
  908. pack={"__utils__": _utils, "__active_provider_name__": None},
  909. extra_module_dirs=_utils.module_dirs,
  910. )
  911. for funcname in LIBCLOUD_FUNCS_NOT_SUPPORTED:
  912. log.trace(
  913. "'%s' has been marked as not supported. Removing from the "
  914. "list of supported cloud functions",
  915. funcname,
  916. )
  917. functions.pop(funcname, None)
  918. return functions
  919. def netapi(opts):
  920. """
  921. Return the network api functions
  922. """
  923. return LazyLoader(_module_dirs(opts, "netapi"), opts, tag="netapi",)
  924. def executors(opts, functions=None, context=None, proxy=None):
  925. """
  926. Returns the executor modules
  927. """
  928. executors = LazyLoader(
  929. _module_dirs(opts, "executors", "executor"),
  930. opts,
  931. tag="executor",
  932. pack={
  933. "__salt__": functions,
  934. "__context__": context or {},
  935. "__proxy__": proxy or {},
  936. },
  937. )
  938. executors.pack["__executors__"] = executors
  939. return executors
  940. def cache(opts, serial):
  941. """
  942. Returns the returner modules
  943. """
  944. return LazyLoader(
  945. _module_dirs(opts, "cache", "cache"),
  946. opts,
  947. tag="cache",
  948. pack={"__opts__": opts, "__context__": {"serial": serial}},
  949. )
  950. def _generate_module(name):
  951. if name in sys.modules:
  952. return
  953. code = "'''Salt loaded {0} parent module'''".format(name.split(".")[-1])
  954. # ModuleType can't accept a unicode type on PY2
  955. module = types.ModuleType(str(name)) # future lint: disable=blacklisted-function
  956. exec(code, module.__dict__)
  957. sys.modules[name] = module
  958. def _mod_type(module_path):
  959. if module_path.startswith(SALT_BASE_PATH):
  960. return "int"
  961. return "ext"
  962. # TODO: move somewhere else?
  963. class FilterDictWrapper(MutableMapping):
  964. """
  965. Create a dict which wraps another dict with a specific key suffix on get
  966. This is to replace "filter_load"
  967. """
  968. def __init__(self, d, suffix):
  969. self._dict = d
  970. self.suffix = suffix
  971. def __setitem__(self, key, val):
  972. self._dict[key] = val
  973. def __delitem__(self, key):
  974. del self._dict[key]
  975. def __getitem__(self, key):
  976. return self._dict[key + self.suffix]
  977. def __len__(self):
  978. return len(self._dict)
  979. def __iter__(self):
  980. for key in self._dict:
  981. if key.endswith(self.suffix):
  982. yield key.replace(self.suffix, "")
  983. class LazyLoader(salt.utils.lazy.LazyDict):
  984. """
  985. A pseduo-dictionary which has a set of keys which are the
  986. name of the module and function, delimited by a dot. When
  987. the value of the key is accessed, the function is then loaded
  988. from disk and into memory.
  989. .. note::
  990. Iterating over keys will cause all modules to be loaded.
  991. :param list module_dirs: A list of directories on disk to search for modules
  992. :param dict opts: The salt options dictionary.
  993. :param str tag: The tag for the type of module to load
  994. :param func mod_type_check: A function which can be used to verify files
  995. :param dict pack: A dictionary of function to be packed into modules as they are loaded
  996. :param list whitelist: A list of modules to whitelist
  997. :param bool virtual_enable: Whether or not to respect the __virtual__ function when loading modules.
  998. :param str virtual_funcs: The name of additional functions in the module to call to verify its functionality.
  999. If not true, the module will not load.
  1000. :param list extra_module_dirs: A list of directories that will be able to import from
  1001. :returns: A LazyLoader object which functions as a dictionary. Keys are 'module.function' and values
  1002. are function references themselves which are loaded on-demand.
  1003. # TODO:
  1004. - move modules_max_memory into here
  1005. - singletons (per tag)
  1006. """
  1007. mod_dict_class = salt.utils.odict.OrderedDict
  1008. def __init__(
  1009. self,
  1010. module_dirs,
  1011. opts=None,
  1012. tag="module",
  1013. loaded_base_name=None,
  1014. mod_type_check=None,
  1015. pack=None,
  1016. whitelist=None,
  1017. virtual_enable=True,
  1018. static_modules=None,
  1019. proxy=None,
  1020. virtual_funcs=None,
  1021. extra_module_dirs=None,
  1022. ): # pylint: disable=W0231
  1023. """
  1024. In pack, if any of the values are None they will be replaced with an
  1025. empty context-specific dict
  1026. """
  1027. self.inject_globals = {}
  1028. self.pack = {} if pack is None else pack
  1029. if opts is None:
  1030. opts = {}
  1031. threadsafety = not opts.get("multiprocessing")
  1032. self.context_dict = salt.utils.context.ContextDict(threadsafe=threadsafety)
  1033. self.opts = self.__prep_mod_opts(opts)
  1034. self.module_dirs = module_dirs
  1035. self.tag = tag
  1036. self.loaded_base_name = loaded_base_name or LOADED_BASE_NAME
  1037. self.mod_type_check = mod_type_check or _mod_type
  1038. if "__context__" not in self.pack:
  1039. self.pack["__context__"] = None
  1040. for k, v in six.iteritems(self.pack):
  1041. if v is None: # if the value of a pack is None, lets make an empty dict
  1042. self.context_dict.setdefault(k, {})
  1043. self.pack[k] = salt.utils.context.NamespacedDictWrapper(
  1044. self.context_dict, k
  1045. )
  1046. self.whitelist = whitelist
  1047. self.virtual_enable = virtual_enable
  1048. self.initial_load = True
  1049. # names of modules that we don't have (errors, __virtual__, etc.)
  1050. self.missing_modules = {} # mapping of name -> error
  1051. self.loaded_modules = {} # mapping of module_name -> dict_of_functions
  1052. self.loaded_files = set() # TODO: just remove them from file_mapping?
  1053. self.static_modules = static_modules if static_modules else []
  1054. if virtual_funcs is None:
  1055. virtual_funcs = []
  1056. self.virtual_funcs = virtual_funcs
  1057. self.extra_module_dirs = extra_module_dirs if extra_module_dirs else []
  1058. self._clean_module_dirs = []
  1059. self.disabled = set(
  1060. self.opts.get(
  1061. "disable_{0}{1}".format(self.tag, "" if self.tag[-1] == "s" else "s"),
  1062. [],
  1063. )
  1064. )
  1065. # A map of suffix to description for imp
  1066. self.suffix_map = {}
  1067. # A list to determine precedence of extensions
  1068. # Prefer packages (directories) over modules (single files)!
  1069. self.suffix_order = [""]
  1070. for (suffix, mode, kind) in SUFFIXES:
  1071. self.suffix_map[suffix] = (suffix, mode, kind)
  1072. self.suffix_order.append(suffix)
  1073. self._lock = threading.RLock()
  1074. with self._lock:
  1075. self._refresh_file_mapping()
  1076. super(LazyLoader, self).__init__() # late init the lazy loader
  1077. # create all of the import namespaces
  1078. _generate_module("{0}.int".format(self.loaded_base_name))
  1079. _generate_module("{0}.int.{1}".format(self.loaded_base_name, tag))
  1080. _generate_module("{0}.ext".format(self.loaded_base_name))
  1081. _generate_module("{0}.ext.{1}".format(self.loaded_base_name, tag))
  1082. def __getitem__(self, item):
  1083. """
  1084. Override the __getitem__ in order to decorate the returned function if we need
  1085. to last-minute inject globals
  1086. """
  1087. func = super(LazyLoader, self).__getitem__(item)
  1088. if self.inject_globals:
  1089. return global_injector_decorator(self.inject_globals)(func)
  1090. else:
  1091. return func
  1092. def __getattr__(self, mod_name):
  1093. """
  1094. Allow for "direct" attribute access-- this allows jinja templates to
  1095. access things like `salt.test.ping()`
  1096. """
  1097. if mod_name in ("__getstate__", "__setstate__"):
  1098. return object.__getattribute__(self, mod_name)
  1099. # if we have an attribute named that, lets return it.
  1100. try:
  1101. return object.__getattr__(self, mod_name) # pylint: disable=no-member
  1102. except AttributeError:
  1103. pass
  1104. # otherwise we assume its jinja template access
  1105. if mod_name not in self.loaded_modules and not self.loaded:
  1106. for name in self._iter_files(mod_name):
  1107. if name in self.loaded_files:
  1108. continue
  1109. # if we got what we wanted, we are done
  1110. if self._load_module(name) and mod_name in self.loaded_modules:
  1111. break
  1112. if mod_name in self.loaded_modules:
  1113. return self.loaded_modules[mod_name]
  1114. else:
  1115. raise AttributeError(mod_name)
  1116. def missing_fun_string(self, function_name):
  1117. """
  1118. Return the error string for a missing function.
  1119. This can range from "not available' to "__virtual__" returned False
  1120. """
  1121. mod_name = function_name.split(".")[0]
  1122. if mod_name in self.loaded_modules:
  1123. return "'{0}' is not available.".format(function_name)
  1124. else:
  1125. try:
  1126. reason = self.missing_modules[mod_name]
  1127. except KeyError:
  1128. return "'{0}' is not available.".format(function_name)
  1129. else:
  1130. if reason is not None:
  1131. return "'{0}' __virtual__ returned False: {1}".format(
  1132. mod_name, reason
  1133. )
  1134. else:
  1135. return "'{0}' __virtual__ returned False".format(mod_name)
  1136. def _refresh_file_mapping(self):
  1137. """
  1138. refresh the mapping of the FS on disk
  1139. """
  1140. # map of suffix to description for imp
  1141. if self.opts.get("cython_enable", True) is True:
  1142. try:
  1143. global pyximport
  1144. pyximport = __import__("pyximport") # pylint: disable=import-error
  1145. pyximport.install()
  1146. # add to suffix_map so file_mapping will pick it up
  1147. self.suffix_map[".pyx"] = tuple()
  1148. except ImportError:
  1149. log.info(
  1150. "Cython is enabled in the options but not present "
  1151. "in the system path. Skipping Cython modules."
  1152. )
  1153. # Allow for zipimport of modules
  1154. if self.opts.get("enable_zip_modules", True) is True:
  1155. self.suffix_map[".zip"] = tuple()
  1156. # allow for module dirs
  1157. if USE_IMPORTLIB:
  1158. self.suffix_map[""] = ("", "", MODULE_KIND_PKG_DIRECTORY)
  1159. else:
  1160. self.suffix_map[""] = ("", "", imp.PKG_DIRECTORY)
  1161. # create mapping of filename (without suffix) to (path, suffix)
  1162. # The files are added in order of priority, so order *must* be retained.
  1163. self.file_mapping = salt.utils.odict.OrderedDict()
  1164. opt_match = []
  1165. def _replace_pre_ext(obj):
  1166. """
  1167. Hack so we can get the optimization level that we replaced (if
  1168. any) out of the re.sub call below. We use a list here because
  1169. it is a persistent data structure that we will be able to
  1170. access after re.sub is called.
  1171. """
  1172. opt_match.append(obj)
  1173. return ""
  1174. for mod_dir in self.module_dirs:
  1175. try:
  1176. # Make sure we have a sorted listdir in order to have
  1177. # expectable override results
  1178. files = sorted(x for x in os.listdir(mod_dir) if x != "__pycache__")
  1179. except OSError:
  1180. continue # Next mod_dir
  1181. if six.PY3:
  1182. try:
  1183. pycache_files = [
  1184. os.path.join("__pycache__", x)
  1185. for x in sorted(
  1186. os.listdir(os.path.join(mod_dir, "__pycache__"))
  1187. )
  1188. ]
  1189. except OSError:
  1190. pass
  1191. else:
  1192. files.extend(pycache_files)
  1193. for filename in files:
  1194. try:
  1195. dirname, basename = os.path.split(filename)
  1196. if basename.startswith("_"):
  1197. # skip private modules
  1198. # log messages omitted for obviousness
  1199. continue # Next filename
  1200. f_noext, ext = os.path.splitext(basename)
  1201. if six.PY3:
  1202. f_noext = PY3_PRE_EXT.sub(_replace_pre_ext, f_noext)
  1203. try:
  1204. opt_level = int(opt_match.pop().group(1).rsplit("-", 1)[-1])
  1205. except (AttributeError, IndexError, ValueError):
  1206. # No regex match or no optimization level matched
  1207. opt_level = 0
  1208. try:
  1209. opt_index = self.opts["optimization_order"].index(opt_level)
  1210. except KeyError:
  1211. log.trace(
  1212. "Disallowed optimization level %d for module "
  1213. "name '%s', skipping. Add %d to the "
  1214. "'optimization_order' config option if you "
  1215. "do not want to ignore this optimization "
  1216. "level.",
  1217. opt_level,
  1218. f_noext,
  1219. opt_level,
  1220. )
  1221. continue
  1222. else:
  1223. # Optimization level not reflected in filename on PY2
  1224. opt_index = 0
  1225. # make sure it is a suffix we support
  1226. if ext not in self.suffix_map:
  1227. continue # Next filename
  1228. if f_noext in self.disabled:
  1229. log.trace(
  1230. "Skipping %s, it is disabled by configuration", filename
  1231. )
  1232. continue # Next filename
  1233. fpath = os.path.join(mod_dir, filename)
  1234. # if its a directory, lets allow us to load that
  1235. if ext == "":
  1236. # is there something __init__?
  1237. subfiles = os.listdir(fpath)
  1238. for suffix in self.suffix_order:
  1239. if "" == suffix:
  1240. continue # Next suffix (__init__ must have a suffix)
  1241. init_file = "__init__{0}".format(suffix)
  1242. if init_file in subfiles:
  1243. break
  1244. else:
  1245. continue # Next filename
  1246. try:
  1247. curr_ext = self.file_mapping[f_noext][1]
  1248. curr_opt_index = self.file_mapping[f_noext][2]
  1249. except KeyError:
  1250. pass
  1251. else:
  1252. if "" in (curr_ext, ext) and curr_ext != ext:
  1253. log.error(
  1254. "Module/package collision: '%s' and '%s'",
  1255. fpath,
  1256. self.file_mapping[f_noext][0],
  1257. )
  1258. if six.PY3 and ext == ".pyc" and curr_ext == ".pyc":
  1259. # Check the optimization level
  1260. if opt_index >= curr_opt_index:
  1261. # Module name match, but a higher-priority
  1262. # optimization level was already matched, skipping.
  1263. continue
  1264. elif not curr_ext or self.suffix_order.index(
  1265. ext
  1266. ) >= self.suffix_order.index(curr_ext):
  1267. # Match found but a higher-priorty match already
  1268. # exists, so skip this.
  1269. continue
  1270. if six.PY3 and not dirname and ext == ".pyc":
  1271. # On Python 3, we should only load .pyc files from the
  1272. # __pycache__ subdirectory (i.e. when dirname is not an
  1273. # empty string).
  1274. continue
  1275. # Made it this far - add it
  1276. self.file_mapping[f_noext] = (fpath, ext, opt_index)
  1277. except OSError:
  1278. continue
  1279. for smod in self.static_modules:
  1280. f_noext = smod.split(".")[-1]
  1281. self.file_mapping[f_noext] = (smod, ".o", 0)
  1282. def clear(self):
  1283. """
  1284. Clear the dict
  1285. """
  1286. with self._lock:
  1287. super(LazyLoader, self).clear() # clear the lazy loader
  1288. self.loaded_files = set()
  1289. self.missing_modules = {}
  1290. self.loaded_modules = {}
  1291. # if we have been loaded before, lets clear the file mapping since
  1292. # we obviously want a re-do
  1293. if hasattr(self, "opts"):
  1294. self._refresh_file_mapping()
  1295. self.initial_load = False
  1296. def __prep_mod_opts(self, opts):
  1297. """
  1298. Strip out of the opts any logger instance
  1299. """
  1300. if "__grains__" not in self.pack:
  1301. self.context_dict["grains"] = opts.get("grains", {})
  1302. self.pack["__grains__"] = salt.utils.context.NamespacedDictWrapper(
  1303. self.context_dict, "grains"
  1304. )
  1305. if "__pillar__" not in self.pack:
  1306. self.context_dict["pillar"] = opts.get("pillar", {})
  1307. self.pack["__pillar__"] = salt.utils.context.NamespacedDictWrapper(
  1308. self.context_dict, "pillar"
  1309. )
  1310. mod_opts = {}
  1311. for key, val in list(opts.items()):
  1312. if key == "logger":
  1313. continue
  1314. mod_opts[key] = val
  1315. return mod_opts
  1316. def _iter_files(self, mod_name):
  1317. """
  1318. Iterate over all file_mapping files in order of closeness to mod_name
  1319. """
  1320. # do we have an exact match?
  1321. if mod_name in self.file_mapping:
  1322. yield mod_name
  1323. # do we have a partial match?
  1324. for k in self.file_mapping:
  1325. if mod_name in k:
  1326. yield k
  1327. # anyone else? Bueller?
  1328. for k in self.file_mapping:
  1329. if mod_name not in k:
  1330. yield k
  1331. def _reload_submodules(self, mod):
  1332. submodules = (
  1333. getattr(mod, sname)
  1334. for sname in dir(mod)
  1335. if isinstance(getattr(mod, sname), mod.__class__)
  1336. )
  1337. # reload only custom "sub"modules
  1338. for submodule in submodules:
  1339. # it is a submodule if the name is in a namespace under mod
  1340. if submodule.__name__.startswith(mod.__name__ + "."):
  1341. reload_module(submodule)
  1342. self._reload_submodules(submodule)
  1343. def __populate_sys_path(self):
  1344. for directory in self.extra_module_dirs:
  1345. if directory not in sys.path:
  1346. sys.path.append(directory)
  1347. self._clean_module_dirs.append(directory)
  1348. def __clean_sys_path(self):
  1349. invalidate_path_importer_cache = False
  1350. for directory in self._clean_module_dirs:
  1351. if directory in sys.path:
  1352. sys.path.remove(directory)
  1353. invalidate_path_importer_cache = True
  1354. self._clean_module_dirs = []
  1355. # Be sure that sys.path_importer_cache do not contains any
  1356. # invalid FileFinder references
  1357. if USE_IMPORTLIB:
  1358. importlib.invalidate_caches()
  1359. # Because we are mangling with importlib, we can find from
  1360. # time to time an invalidation issue with
  1361. # sys.path_importer_cache, that requires the removal of
  1362. # FileFinder that remain None for the extra_module_dirs
  1363. if invalidate_path_importer_cache:
  1364. for directory in self.extra_module_dirs:
  1365. if (
  1366. directory in sys.path_importer_cache
  1367. and sys.path_importer_cache[directory] is None
  1368. ):
  1369. del sys.path_importer_cache[directory]
  1370. def _load_module(self, name):
  1371. mod = None
  1372. fpath, suffix = self.file_mapping[name][:2]
  1373. self.loaded_files.add(name)
  1374. fpath_dirname = os.path.dirname(fpath)
  1375. try:
  1376. self.__populate_sys_path()
  1377. sys.path.append(fpath_dirname)
  1378. if suffix == ".pyx":
  1379. mod = pyximport.load_module(name, fpath, tempfile.gettempdir())
  1380. elif suffix == ".o":
  1381. top_mod = __import__(fpath, globals(), locals(), [])
  1382. comps = fpath.split(".")
  1383. if len(comps) < 2:
  1384. mod = top_mod
  1385. else:
  1386. mod = top_mod
  1387. for subname in comps[1:]:
  1388. mod = getattr(mod, subname)
  1389. elif suffix == ".zip":
  1390. mod = zipimporter(fpath).load_module(name)
  1391. else:
  1392. desc = self.suffix_map[suffix]
  1393. # if it is a directory, we don't open a file
  1394. try:
  1395. mod_namespace = ".".join(
  1396. (
  1397. self.loaded_base_name,
  1398. self.mod_type_check(fpath),
  1399. self.tag,
  1400. name,
  1401. )
  1402. )
  1403. except TypeError:
  1404. mod_namespace = "{0}.{1}.{2}.{3}".format(
  1405. self.loaded_base_name,
  1406. self.mod_type_check(fpath),
  1407. self.tag,
  1408. name,
  1409. )
  1410. if suffix == "":
  1411. if USE_IMPORTLIB:
  1412. # pylint: disable=no-member
  1413. # Package directory, look for __init__
  1414. loader_details = [
  1415. (
  1416. importlib.machinery.SourceFileLoader,
  1417. importlib.machinery.SOURCE_SUFFIXES,
  1418. ),
  1419. (
  1420. importlib.machinery.SourcelessFileLoader,
  1421. importlib.machinery.BYTECODE_SUFFIXES,
  1422. ),
  1423. (
  1424. importlib.machinery.ExtensionFileLoader,
  1425. importlib.machinery.EXTENSION_SUFFIXES,
  1426. ),
  1427. ]
  1428. file_finder = importlib.machinery.FileFinder(
  1429. fpath_dirname, *loader_details
  1430. )
  1431. spec = file_finder.find_spec(mod_namespace)
  1432. if spec is None:
  1433. raise ImportError()
  1434. # TODO: Get rid of load_module in favor of
  1435. # exec_module below. load_module is deprecated, but
  1436. # loading using exec_module has been causing odd things
  1437. # with the magic dunders we pack into the loaded
  1438. # modules, most notably with salt-ssh's __opts__.
  1439. mod = spec.loader.load_module()
  1440. # mod = importlib.util.module_from_spec(spec)
  1441. # spec.loader.exec_module(mod)
  1442. # pylint: enable=no-member
  1443. sys.modules[mod_namespace] = mod
  1444. else:
  1445. mod = imp.load_module(mod_namespace, None, fpath, desc)
  1446. # reload all submodules if necessary
  1447. if not self.initial_load:
  1448. self._reload_submodules(mod)
  1449. else:
  1450. if USE_IMPORTLIB:
  1451. # pylint: disable=no-member
  1452. loader = MODULE_KIND_MAP[desc[2]](mod_namespace, fpath)
  1453. spec = importlib.util.spec_from_file_location(
  1454. mod_namespace, fpath, loader=loader
  1455. )
  1456. if spec is None:
  1457. raise ImportError()
  1458. # TODO: Get rid of load_module in favor of
  1459. # exec_module below. load_module is deprecated, but
  1460. # loading using exec_module has been causing odd things
  1461. # with the magic dunders we pack into the loaded
  1462. # modules, most notably with salt-ssh's __opts__.
  1463. mod = spec.loader.load_module()
  1464. # mod = importlib.util.module_from_spec(spec)
  1465. # spec.loader.exec_module(mod)
  1466. # pylint: enable=no-member
  1467. sys.modules[mod_namespace] = mod
  1468. else:
  1469. with salt.utils.files.fopen(fpath, desc[1]) as fn_:
  1470. mod = imp.load_module(mod_namespace, fn_, fpath, desc)
  1471. except IOError:
  1472. raise
  1473. except ImportError as exc:
  1474. if "magic number" in six.text_type(exc):
  1475. error_msg = "Failed to import {0} {1}. Bad magic number. If migrating from Python2 to Python3, remove all .pyc files and try again.".format(
  1476. self.tag, name
  1477. )
  1478. log.warning(error_msg)
  1479. self.missing_modules[name] = error_msg
  1480. log.debug("Failed to import %s %s:\n", self.tag, name, exc_info=True)
  1481. self.missing_modules[name] = exc
  1482. return False
  1483. except Exception as error: # pylint: disable=broad-except
  1484. log.error(
  1485. "Failed to import %s %s, this is due most likely to a "
  1486. "syntax error:\n",
  1487. self.tag,
  1488. name,
  1489. exc_info=True,
  1490. )
  1491. self.missing_modules[name] = error
  1492. return False
  1493. except SystemExit as error:
  1494. try:
  1495. fn_, _, caller, _ = traceback.extract_tb(sys.exc_info()[2])[-1]
  1496. except Exception: # pylint: disable=broad-except
  1497. pass
  1498. else:
  1499. tgt_fn = os.path.join("salt", "utils", "process.py")
  1500. if fn_.endswith(tgt_fn) and "_handle_signals" in caller:
  1501. # Race conditon, SIGTERM or SIGINT received while loader
  1502. # was in process of loading a module. Call sys.exit to
  1503. # ensure that the process is killed.
  1504. sys.exit(salt.defaults.exitcodes.EX_OK)
  1505. log.error(
  1506. "Failed to import %s %s as the module called exit()\n",
  1507. self.tag,
  1508. name,
  1509. exc_info=True,
  1510. )
  1511. self.missing_modules[name] = error
  1512. return False
  1513. finally:
  1514. sys.path.remove(fpath_dirname)
  1515. self.__clean_sys_path()
  1516. if hasattr(mod, "__opts__"):
  1517. mod.__opts__.update(self.opts)
  1518. else:
  1519. mod.__opts__ = self.opts
  1520. # pack whatever other globals we were asked to
  1521. for p_name, p_value in six.iteritems(self.pack):
  1522. setattr(mod, p_name, p_value)
  1523. module_name = mod.__name__.rsplit(".", 1)[-1]
  1524. # Call a module's initialization method if it exists
  1525. module_init = getattr(mod, "__init__", None)
  1526. if inspect.isfunction(module_init):
  1527. try:
  1528. module_init(self.opts)
  1529. except TypeError as e:
  1530. log.error(e)
  1531. except Exception: # pylint: disable=broad-except
  1532. err_string = "__init__ failed"
  1533. log.debug(
  1534. "Error loading %s.%s: %s",
  1535. self.tag,
  1536. module_name,
  1537. err_string,
  1538. exc_info=True,
  1539. )
  1540. self.missing_modules[module_name] = err_string
  1541. self.missing_modules[name] = err_string
  1542. return False
  1543. # if virtual modules are enabled, we need to look for the
  1544. # __virtual__() function inside that module and run it.
  1545. if self.virtual_enable:
  1546. virtual_funcs_to_process = ["__virtual__"] + self.virtual_funcs
  1547. for virtual_func in virtual_funcs_to_process:
  1548. (
  1549. virtual_ret,
  1550. module_name,
  1551. virtual_err,
  1552. virtual_aliases,
  1553. ) = self._process_virtual(mod, module_name, virtual_func)
  1554. if virtual_err is not None:
  1555. log.trace(
  1556. "Error loading %s.%s: %s", self.tag, module_name, virtual_err
  1557. )
  1558. # if _process_virtual returned a non-True value then we are
  1559. # supposed to not process this module
  1560. if virtual_ret is not True and module_name not in self.missing_modules:
  1561. # If a module has information about why it could not be loaded, record it
  1562. self.missing_modules[module_name] = virtual_err
  1563. self.missing_modules[name] = virtual_err
  1564. return False
  1565. else:
  1566. virtual_aliases = ()
  1567. # If this is a proxy minion then MOST modules cannot work. Therefore, require that
  1568. # any module that does work with salt-proxy-minion define __proxyenabled__ as a list
  1569. # containing the names of the proxy types that the module supports.
  1570. #
  1571. # Render modules and state modules are OK though
  1572. if "proxy" in self.opts:
  1573. if self.tag in ["grains", "proxy"]:
  1574. if not hasattr(mod, "__proxyenabled__") or (
  1575. self.opts["proxy"]["proxytype"] not in mod.__proxyenabled__
  1576. and "*" not in mod.__proxyenabled__
  1577. ):
  1578. err_string = "not a proxy_minion enabled module"
  1579. self.missing_modules[module_name] = err_string
  1580. self.missing_modules[name] = err_string
  1581. return False
  1582. if getattr(mod, "__load__", False) is not False:
  1583. log.info(
  1584. "The functions from module '%s' are being loaded from the "
  1585. "provided __load__ attribute",
  1586. module_name,
  1587. )
  1588. # If we had another module by the same virtual name, we should put any
  1589. # new functions under the existing dictionary.
  1590. mod_names = [module_name] + list(virtual_aliases)
  1591. mod_dict = dict(
  1592. ((x, self.loaded_modules.get(x, self.mod_dict_class())) for x in mod_names)
  1593. )
  1594. for attr in getattr(mod, "__load__", dir(mod)):
  1595. if attr.startswith("_"):
  1596. # private functions are skipped
  1597. continue
  1598. func = getattr(mod, attr)
  1599. if not inspect.isfunction(func) and not isinstance(func, functools.partial):
  1600. # Not a function!? Skip it!!!
  1601. continue
  1602. # Let's get the function name.
  1603. # If the module has the __func_alias__ attribute, it must be a
  1604. # dictionary mapping in the form of(key -> value):
  1605. # <real-func-name> -> <desired-func-name>
  1606. #
  1607. # It default's of course to the found callable attribute name
  1608. # if no alias is defined.
  1609. funcname = getattr(mod, "__func_alias__", {}).get(attr, attr)
  1610. for tgt_mod in mod_names:
  1611. try:
  1612. full_funcname = ".".join((tgt_mod, funcname))
  1613. except TypeError:
  1614. full_funcname = "{0}.{1}".format(tgt_mod, funcname)
  1615. # Save many references for lookups
  1616. # Careful not to overwrite existing (higher priority) functions
  1617. if full_funcname not in self._dict:
  1618. self._dict[full_funcname] = func
  1619. if funcname not in mod_dict[tgt_mod]:
  1620. setattr(mod_dict[tgt_mod], funcname, func)
  1621. mod_dict[tgt_mod][funcname] = func
  1622. self._apply_outputter(func, mod)
  1623. # enforce depends
  1624. try:
  1625. Depends.enforce_dependencies(self._dict, self.tag, name)
  1626. except RuntimeError as exc:
  1627. log.info(
  1628. "Depends.enforce_dependencies() failed for the following " "reason: %s",
  1629. exc,
  1630. )
  1631. for tgt_mod in mod_names:
  1632. self.loaded_modules[tgt_mod] = mod_dict[tgt_mod]
  1633. return True
  1634. def _load(self, key):
  1635. """
  1636. Load a single item if you have it
  1637. """
  1638. # if the key doesn't have a '.' then it isn't valid for this mod dict
  1639. if not isinstance(key, six.string_types):
  1640. raise KeyError("The key must be a string.")
  1641. if "." not in key:
  1642. raise KeyError("The key '{0}' should contain a '.'".format(key))
  1643. mod_name, _ = key.split(".", 1)
  1644. with self._lock:
  1645. # It is possible that the key is in the dictionary after
  1646. # acquiring the lock due to another thread loading it.
  1647. if mod_name in self.missing_modules or key in self._dict:
  1648. return True
  1649. # if the modulename isn't in the whitelist, don't bother
  1650. if self.whitelist and mod_name not in self.whitelist:
  1651. log.error(
  1652. "Failed to load function %s because its module (%s) is "
  1653. "not in the whitelist: %s",
  1654. key,
  1655. mod_name,
  1656. self.whitelist,
  1657. )
  1658. raise KeyError(key)
  1659. def _inner_load(mod_name):
  1660. for name in self._iter_files(mod_name):
  1661. if name in self.loaded_files:
  1662. continue
  1663. # if we got what we wanted, we are done
  1664. if self._load_module(name) and key in self._dict:
  1665. return True
  1666. return False
  1667. # try to load the module
  1668. ret = None
  1669. reloaded = False
  1670. # re-scan up to once, IOErrors or a failed load cause re-scans of the
  1671. # filesystem
  1672. while True:
  1673. try:
  1674. ret = _inner_load(mod_name)
  1675. if not reloaded and ret is not True:
  1676. self._refresh_file_mapping()
  1677. reloaded = True
  1678. continue
  1679. break
  1680. except IOError:
  1681. if not reloaded:
  1682. self._refresh_file_mapping()
  1683. reloaded = True
  1684. continue
  1685. return ret
  1686. def _load_all(self):
  1687. """
  1688. Load all of them
  1689. """
  1690. with self._lock:
  1691. for name in self.file_mapping:
  1692. if name in self.loaded_files or name in self.missing_modules:
  1693. continue
  1694. self._load_module(name)
  1695. self.loaded = True
  1696. def reload_modules(self):
  1697. with self._lock:
  1698. self.loaded_files = set()
  1699. self._load_all()
  1700. def _apply_outputter(self, func, mod):
  1701. """
  1702. Apply the __outputter__ variable to the functions
  1703. """
  1704. if hasattr(mod, "__outputter__"):
  1705. outp = mod.__outputter__
  1706. if func.__name__ in outp:
  1707. func.__outputter__ = outp[func.__name__]
  1708. def _process_virtual(self, mod, module_name, virtual_func="__virtual__"):
  1709. """
  1710. Given a loaded module and its default name determine its virtual name
  1711. This function returns a tuple. The first value will be either True or
  1712. False and will indicate if the module should be loaded or not (i.e. if
  1713. it threw and exception while processing its __virtual__ function). The
  1714. second value is the determined virtual name, which may be the same as
  1715. the value provided.
  1716. The default name can be calculated as follows::
  1717. module_name = mod.__name__.rsplit('.', 1)[-1]
  1718. """
  1719. # The __virtual__ function will return either a True or False value.
  1720. # If it returns a True value it can also set a module level attribute
  1721. # named __virtualname__ with the name that the module should be
  1722. # referred to as.
  1723. #
  1724. # This allows us to have things like the pkg module working on all
  1725. # platforms under the name 'pkg'. It also allows for modules like
  1726. # augeas_cfg to be referred to as 'augeas', which would otherwise have
  1727. # namespace collisions. And finally it allows modules to return False
  1728. # if they are not intended to run on the given platform or are missing
  1729. # dependencies.
  1730. virtual_aliases = getattr(mod, "__virtual_aliases__", tuple())
  1731. try:
  1732. error_reason = None
  1733. if hasattr(mod, "__virtual__") and inspect.isfunction(mod.__virtual__):
  1734. try:
  1735. start = time.time()
  1736. virtual = getattr(mod, virtual_func)()
  1737. if isinstance(virtual, tuple):
  1738. error_reason = virtual[1]
  1739. virtual = virtual[0]
  1740. if self.opts.get("virtual_timer", False):
  1741. end = time.time() - start
  1742. msg = "Virtual function took {0} seconds for {1}".format(
  1743. end, module_name
  1744. )
  1745. log.warning(msg)
  1746. except Exception as exc: # pylint: disable=broad-except
  1747. error_reason = (
  1748. "Exception raised when processing __virtual__ function"
  1749. " for {0}. Module will not be loaded: {1}".format(
  1750. mod.__name__, exc
  1751. )
  1752. )
  1753. log.error(error_reason, exc_info_on_loglevel=logging.DEBUG)
  1754. virtual = None
  1755. # Get the module's virtual name
  1756. virtualname = getattr(mod, "__virtualname__", virtual)
  1757. if not virtual:
  1758. # if __virtual__() evaluates to False then the module
  1759. # wasn't meant for this platform or it's not supposed to
  1760. # load for some other reason.
  1761. # Some modules might accidentally return None and are
  1762. # improperly loaded
  1763. if virtual is None:
  1764. log.warning(
  1765. "%s.__virtual__() is wrongly returning `None`. "
  1766. "It should either return `True`, `False` or a new "
  1767. "name. If you're the developer of the module "
  1768. "'%s', please fix this.",
  1769. mod.__name__,
  1770. module_name,
  1771. )
  1772. return (False, module_name, error_reason, virtual_aliases)
  1773. # At this point, __virtual__ did not return a
  1774. # boolean value, let's check for deprecated usage
  1775. # or module renames
  1776. if virtual is not True and module_name != virtual:
  1777. # The module is renaming itself. Updating the module name
  1778. # with the new name
  1779. log.trace("Loaded %s as virtual %s", module_name, virtual)
  1780. if virtualname != virtual:
  1781. # The __virtualname__ attribute does not match what's
  1782. # being returned by the __virtual__() function. This
  1783. # should be considered an error.
  1784. log.error(
  1785. "The module '%s' is showing some bad usage. Its "
  1786. "__virtualname__ attribute is set to '%s' yet the "
  1787. "__virtual__() function is returning '%s'. These "
  1788. "values should match!",
  1789. mod.__name__,
  1790. virtualname,
  1791. virtual,
  1792. )
  1793. module_name = virtualname
  1794. # If the __virtual__ function returns True and __virtualname__
  1795. # is set then use it
  1796. elif virtual is True and virtualname != module_name:
  1797. if virtualname is not True:
  1798. module_name = virtualname
  1799. except KeyError:
  1800. # Key errors come out of the virtual function when passing
  1801. # in incomplete grains sets, these can be safely ignored
  1802. # and logged to debug, still, it includes the traceback to
  1803. # help debugging.
  1804. log.debug("KeyError when loading %s", module_name, exc_info=True)
  1805. except Exception: # pylint: disable=broad-except
  1806. # If the module throws an exception during __virtual__()
  1807. # then log the information and continue to the next.
  1808. log.error(
  1809. "Failed to read the virtual function for %s: %s",
  1810. self.tag,
  1811. module_name,
  1812. exc_info=True,
  1813. )
  1814. return (False, module_name, error_reason, virtual_aliases)
  1815. return (True, module_name, None, virtual_aliases)
  1816. def global_injector_decorator(inject_globals):
  1817. """
  1818. Decorator used by the LazyLoader to inject globals into a function at
  1819. execute time.
  1820. globals
  1821. Dictionary with global variables to inject
  1822. """
  1823. def inner_decorator(f):
  1824. @functools.wraps(f)
  1825. def wrapper(*args, **kwargs):
  1826. with salt.utils.context.func_globals_inject(f, **inject_globals):
  1827. return f(*args, **kwargs)
  1828. return wrapper
  1829. return inner_decorator