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
153 clsname = parts[-1]
154 path = package
155 try:
156 for partNo in range(1,len(parts)+1):
157 part = parts[partNo-1]
158 fp, path, description = imp.find_module(part,[path])
159 modSubPath = '.'.join(parts[:partNo])
160 mod = imp.load_module(modSubPath, fp, path, description)
161 finally:
162 if fp:
163 fp.close()
164 return getattr(mod, clsname)
165
166 pb.setUnjellyableForClass(CoreImporter, CoreImporter)
174
175 pb.setUnjellyableForClass(PackImporter, PackImporter)
185
190
192
193 - def __init__(self, walker, modPathPrefix):
196
198 packModPath = '%s.%s' % (self.modPathPrefix, coreModPath)
199 return PluginLoader(package, packModPath, lastModName, PackImporter())
200
202 """
203 Manages plugin modules. Finds plugins and returns PluginLoader instances.
204 Keeps a cache of previously loaded plugins.
205 """
206
207 - def __init__(self, lastModName, packPath, productsPaths):
208 """
209 Adds PluginLoaders for plugins in productsPaths to the pluginLoaders
210 dictionary.
211
212 lastModName - the directory name where the plugins are found. this name
213 is appended to the following paths
214 packPath - path to the directory that holds the plugin modules inside
215 a zenpack. this path is relative to the zenpack root
216 productsPaths - list of paths to directories that hold plugin
217 modules. these paths are relative to $ZENHOME/Products
218
219 a 'path', as used here, is a tuple of directory names
220 """
221 self.pluginLoaders = {}
222 self.loadedZenpacks = []
223 self.lastModName = lastModName
224 self.packPath = packPath
225 for path in productsPaths:
226 package = zenPath(*('Products',) + path + (lastModName,))
227 self._addPluginLoaders(CoreLoaderFactory(OsWalker()), package)
228
230 """
231 Get the PluginLoader for a specific plugin.
232
233 packs - list of installed zenpacks (ZenPack instances)
234 modPath - the module path of the plugin
235 """
236 if modPath not in self.pluginLoaders:
237 self.getPluginLoaders(packs)
238 if modPath in self.pluginLoaders:
239 return self.pluginLoaders[modPath]
240
242 """
243 Add the PluginLoaders for the packs to the pluginLoaders dictionary.
244 Return the values of that dictionary.
245
246 packs - list of installed zenpacks (ZenPack instances)
247 """
248 try:
249 for pack in packs:
250 if pack.moduleName() not in self.loadedZenpacks:
251 self.loadedZenpacks.append(pack.moduleName())
252 modPathPrefix = '.'.join((pack.moduleName(),) +
253 self.packPath + (self.lastModName,))
254 factory = PackLoaderFactory(OsWalker(), modPathPrefix)
255 package = pack.path(*self.packPath + (self.lastModName,))
256 self._addPluginLoaders(factory, package)
257 except:
258 log.error('Could not load plugins from ZenPacks.'
259 ' One of the ZenPacks is missing or broken.')
260 import traceback
261 log.debug(traceback.format_exc())
262 return self.pluginLoaders.values()
263
265 log.debug("Loading collector plugins from: %s", package)
266 try:
267 loaders = loaderFactory.genLoaders(package, self.lastModName)
268 for loader in loaders:
269 self.pluginLoaders[loader.modPath] = loader
270 except:
271 log.error('Could not load plugins from %s', package)
272 import traceback
273 log.debug(traceback.format_exc())
274
276 """
277 this class is not intended to be instantiated. instead it is a place to
278 hold a singleton instance of PluginManager without having them call the
279 constructor when this module is imported.
280 """
281
282 instance = None
283
284 @classmethod
286 if cls.instance is None:
287 cls.instance = PluginManager(
288 lastModName='plugins',
289 packPath=('modeler',),
290 productsPaths=[('DataCollector',),
291 ('ZenWin', 'modeler',)])
292 return cls.instance
293
295 """
296 this class is not intended to be instantiated. instead it is a place to
297 hold a singleton instance of PluginManager without having them call the
298 constructor when this module is imported.
299 """
300
301 instance = None
302
303 @classmethod
311
314
318
322
327