Package Products :: Package DataCollector :: Module Plugins
[hide private]
[frames] | no frames]

Source Code for Module Products.DataCollector.Plugins

  1  ########################################################################### 
  2  # 
  3  # This program is part of Zenoss Core, an open source monitoring platform. 
  4  # Copyright (C) 2007-2009, Zenoss Inc. 
  5  # 
  6  # This program is free software; you can redistribute it and/or modify it 
  7  # under the terms of the GNU General Public License version 2 or (at your 
  8  # option) any later version as published by the Free Software Foundation. 
  9  # 
 10  # For complete information please visit: http://www.zenoss.com/oss/ 
 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') 
49 50 -class PluginImportError(exceptions.ImportError):
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 # The following is needed for zendisc 67 self.args = traceback
68
69 -class PluginLoader(pb.Copyable, pb.RemoteCopy):
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
92 - def create(self):
93 """ 94 Load and compile the code contained in the given plugin 95 """ 96 try: 97 try: 98 # Modify sys.path (some plugins depend on this to import other 99 # modules from the plugins root) 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 # It's already been removed 117 pass
118 119 pb.setUnjellyableForClass(PluginLoader, PluginLoader)
120 121 -def _coreModPaths(walker, package):
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
139 -class OsWalker(object):
140
141 - def walk(self, package):
142 return os.walk(package)
143
144 -class CoreImporter(pb.Copyable, pb.RemoteCopy):
145
146 - def importModule(self, package, modPath):
147 fp = None 148 # Load the plugins package using its path as the name to 149 # avoid conflicts. slashes in the name are OK when using 150 # the imp module. 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
164 - def importPlugin(self, package, modPath):
165 parts = modPath.split('.') 166 # class name is same as module name 167 clsname = parts[-1] 168 mod = self.importModule(package, modPath) 169 return getattr(mod, clsname)
170 171 pb.setUnjellyableForClass(CoreImporter, CoreImporter)
172 173 -class PackImporter(pb.Copyable, pb.RemoteCopy):
174
175 - def importModule(self, package, modPath):
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
190 - def importPlugin(self, package, modPath):
191 # ZenPack plugins are specified absolutely; we can import 192 # them using the old method 193 return importClass(modPath)
194 195 pb.setUnjellyableForClass(PackImporter, PackImporter)
196 197 -class BaseLoaderFactory(object):
198
199 - def __init__(self, walker):
200 self.walker = walker
201
202 - def genLoaders(self, package, lastModName):
203 for coreModPath in _coreModPaths(self.walker, package): 204 yield self._createLoader(package, coreModPath, lastModName)
205
206 -class CoreLoaderFactory(BaseLoaderFactory):
207
208 - def _createLoader(self, package, coreModPath, lastModName):
209 return PluginLoader(package, coreModPath, lastModName, CoreImporter())
210
211 -class PackLoaderFactory(BaseLoaderFactory):
212
213 - def __init__(self, walker, modPathPrefix):
214 BaseLoaderFactory.__init__(self, walker) 215 self.modPathPrefix = modPathPrefix
216
217 - def _createLoader(self, package, coreModPath, lastModName):
218 packModPath = '%s.%s' % (self.modPathPrefix, coreModPath) 219 return PluginLoader(package, packModPath, lastModName, PackImporter())
220
221 -class PluginManager(object):
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 = {} # PluginLoaders by module path 242 self.loadedZenpacks = [] # zenpacks that have been processed 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
249 - def getPluginLoader(self, packs, modPath):
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
261 - def getPluginLoaders(self, packs):
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
284 - def _addPluginLoaders(self, loaderFactory, package):
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
295 -class ModelingManager(object):
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
305 - def getInstance(cls):
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
314 -class MonitoringManager(object):
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
324 - def getInstance(cls):
325 if cls.instance is None: 326 cls.instance = PluginManager( 327 lastModName='parsers', 328 packPath=(), 329 productsPaths=[('ZenRRD',)]) 330 return cls.instance
331
332 -def _loadPlugins(pluginManager, dmd):
333 return pluginManager.getPluginLoaders(dmd.ZenPackManager.packs())
334
335 -def loadPlugins(dmd):
336 "Get PluginLoaders for all the modeling plugins" 337 return _loadPlugins(ModelingManager.getInstance(), dmd)
338
339 -def loadParserPlugins(dmd):
340 "Get PluginLoaders for all the modeling plugins" 341 return _loadPlugins(MonitoringManager.getInstance(), dmd)
342
343 -def getParserLoader(dmd, modPath):
344 "Get a PluginLoader for the given monitoring plugin's module path" 345 return MonitoringManager.getInstance().getPluginLoader( 346 dmd.ZenPackManager.packs(), modPath)
347