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