1
2
3
4
5
6
7
8
9
10
11 """
12 Load modeling and monitoring plugins from standard locations and from
13 ZenPacks. Most of the entry into this module is via the three functions
14 defined at the very bottom of the file. Those functions use the singleton
15 PluginManager objects to load the plugins.
16
17 Classes -
18 PluginImportError - an exception type
19 PluginLoader - jellyable object that has all the information neccessary
20 to dynamically import the plugin module and instantiate the
21 class that shares the module's name
22 CoreImporter - jellyable object that is injected into a PluginLoader.
23 handles importing of plugins found inside Products
24 PackImporter - same as above but for zenpack plugins
25 BaseLoaderFactory - base class for the two loader factories
26 CoreLoaderFactory - generates the PluginLoaders for core plugins
27 PackLoaderFactory - generates the PluginLoaders for zenpack plugins
28 PluginManager - there is one singleton instance of this class for modeling
29 plugins and another for monitoring plugins
30
31 Note that modPath uses a different convention for core versus zenpack plugins.
32
33 core: zenoss.cmd.uname
34 zenpack: ZenPacks.zenoss.AixMonitor.modeler.plugins.zenoss.cmd.uname
35
36 """
37
38 from Products.ZenUtils.Utils import importClass, zenPath
39 import sys
40 import os
41 import exceptions
42 import imp
43 from twisted.spread import pb
44 import logging
45 log = logging.getLogger('zen.Plugins')
48 """
49 Capture extra data from plugin exceptions
50 """
51
52 - def __init__(self, plugin='', traceback='' ):
53 """
54 Initializer
55
56 @param plugin: plugin name
57 @type plugin: string
58 @param traceback: traceback from an exception
59 @type traceback: traceback object
60 """
61 self.plugin = plugin
62 self.traceback = traceback
63
64 self.args = traceback
65
67 """
68 Class to load plugins
69 """
70
71 - def __init__(self, package, modPath, lastModName, importer):
72 """
73 package - '/'-separated absolute path to the root of the plugins
74 modules
75 modPath - '.'-spearated module path. for core plugins, it is rooted
76 at the package. for zenpack plugins, it starts with
77 'ZenPacks'
78 lastModName - name of the last module in modPath that is not part of
79 of the plugin name
80 importer - object with an importPlugin method used to import the
81 plugin. the implementation of the import method differs
82 between core and zenpack plugins
83 """
84 self.package = package
85 self.modPath = modPath
86 self.pluginName = modPath.split(lastModName + '.')[-1]
87 self.importer = importer
88
90 """
91 Load and compile the code contained in the given plugin
92 """
93 try:
94 try:
95
96
97 sys.path.insert(0, self.package)
98 pluginClass = self.importer.importPlugin(self.package,
99 self.modPath)
100 return pluginClass()
101 except (SystemExit, KeyboardInterrupt):
102 raise
103 except:
104 import traceback
105 log.debug(traceback.format_exc())
106 raise PluginImportError(
107 plugin=self.modPath,
108 traceback=traceback.format_exc().splitlines())
109 finally:
110 try:
111 sys.path.remove(self.package)
112 except ValueError:
113
114 pass
115
116 pb.setUnjellyableForClass(PluginLoader, PluginLoader)
119 "generates modPath strings for the modules in a core directory"
120 for absolutePath, dirname, filenames in walker.walk(package):
121 if absolutePath == package:
122 modPathBase = []
123 elif absolutePath.startswith(package):
124 modPathBase = absolutePath[len(package)+1:].split(os.path.sep)
125 else:
126 log.debug('absolutePath must start with package: '
127 'absolutePath=%s, package=%s', absolutePath, package)
128 continue
129 for filename in filenames:
130 if filename.endswith(".py") \
131 and filename[0] not in ('.', "_") \
132 and '#' not in filename \
133 and filename not in ('CollectorPlugin.py', 'DataMaps.py'):
134 yield '.'.join(modPathBase + [filename[:-3]])
135
137
138 - def walk(self, package):
139 return os.walk(package)
140
142
144 fp = None
145
146
147
148 parts = modPath.split('.')
149 path = package
150 try:
151 for partNo in range(1,len(parts)+1):
152 part = parts[partNo-1]
153 fp, path, description = imp.find_module(part,[path])
154 modSubPath = '.'.join(parts[:partNo])
155 mod = imp.load_module(modSubPath, fp, path, description)
156 finally:
157 if fp:
158 fp.close()
159 return mod
160
162 parts = modPath.split('.')
163
164 clsname = parts[-1]
165 mod = self.importModule(package, modPath)
166 return getattr(mod, clsname)
167
168 pb.setUnjellyableForClass(CoreImporter, CoreImporter)
171
173 modulePath = modPath
174 try:
175 classname = modulePath.split(".")[-1]
176 try:
177 __import__(modulePath, globals(), locals(), classname)
178 mod = sys.modules[modulePath]
179 except (ValueError, ImportError, KeyError), ex:
180 raise ex
181
182 return mod
183 except AttributeError:
184 raise ImportError("Failed while importing module %s" % (
185 modulePath))
186
191
192 pb.setUnjellyableForClass(PackImporter, PackImporter)
202
207
209
210 - def __init__(self, walker, modPathPrefix):
213
215 packModPath = '%s.%s' % (self.modPathPrefix, coreModPath)
216 return PluginLoader(package, packModPath, lastModName, PackImporter())
217
219 """
220 Manages plugin modules. Finds plugins and returns PluginLoader instances.
221 Keeps a cache of previously loaded plugins.
222 """
223
224 - def __init__(self, lastModName, packPath, productsPaths):
225 """
226 Adds PluginLoaders for plugins in productsPaths to the pluginLoaders
227 dictionary.
228
229 lastModName - the directory name where the plugins are found. this name
230 is appended to the following paths
231 packPath - path to the directory that holds the plugin modules inside
232 a zenpack. this path is relative to the zenpack root
233 productsPaths - list of paths to directories that hold plugin
234 modules. these paths are relative to $ZENHOME/Products
235
236 a 'path', as used here, is a tuple of directory names
237 """
238 self.pluginLoaders = {}
239 self.loadedZenpacks = []
240 self.lastModName = lastModName
241 self.packPath = packPath
242 for path in productsPaths:
243 package = zenPath(*('Products',) + path + (lastModName,))
244 self._addPluginLoaders(CoreLoaderFactory(OsWalker()), package)
245
247 """
248 Get the PluginLoader for a specific plugin.
249
250 packs - list of installed zenpacks (ZenPack instances)
251 modPath - the module path of the plugin
252 """
253 if modPath not in self.pluginLoaders:
254 self.getPluginLoaders(packs)
255 if modPath in self.pluginLoaders:
256 return self.pluginLoaders[modPath]
257
259 """
260 Add the PluginLoaders for the packs to the pluginLoaders dictionary.
261 Return the values of that dictionary.
262
263 packs - list of installed zenpacks (ZenPack instances)
264 """
265 try:
266 for pack in packs:
267 if pack.moduleName() not in self.loadedZenpacks:
268 self.loadedZenpacks.append(pack.moduleName())
269 modPathPrefix = '.'.join((pack.moduleName(),) +
270 self.packPath + (self.lastModName,))
271 factory = PackLoaderFactory(OsWalker(), modPathPrefix)
272 package = pack.path(*self.packPath + (self.lastModName,))
273 self._addPluginLoaders(factory, package)
274 except:
275 log.error('Could not load plugins from ZenPacks.'
276 ' One of the ZenPacks is missing or broken.')
277 import traceback
278 log.debug(traceback.format_exc())
279 return self.pluginLoaders.values()
280
282 log.debug("Loading collector plugins from: %s", package)
283 try:
284 loaders = loaderFactory.genLoaders(package, self.lastModName)
285 for loader in loaders:
286 self.pluginLoaders[loader.modPath] = loader
287 except:
288 log.error('Could not load plugins from %s', package)
289 import traceback
290 log.debug(traceback.format_exc())
291
293 """
294 this class is not intended to be instantiated. instead it is a place to
295 hold a singleton instance of PluginManager without having them call the
296 constructor when this module is imported.
297 """
298
299 instance = None
300
301 @classmethod
309
311 """
312 this class is not intended to be instantiated. instead it is a place to
313 hold a singleton instance of PluginManager without having them call the
314 constructor when this module is imported.
315 """
316
317 instance = None
318
319 @classmethod
327
330
334
338
343