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 as published by 
  8  # 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 importPlugin(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 # class name is same as module name 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)
167 168 -class PackImporter(pb.Copyable, pb.RemoteCopy):
169
170 - def importPlugin(self, package, modPath):
171 # ZenPack plugins are specified absolutely; we can import 172 # them using the old method 173 return importClass(modPath)
174 175 pb.setUnjellyableForClass(PackImporter, PackImporter)
176 177 -class BaseLoaderFactory(object):
178
179 - def __init__(self, walker):
180 self.walker = walker
181
182 - def genLoaders(self, package, lastModName):
183 for coreModPath in _coreModPaths(self.walker, package): 184 yield self._createLoader(package, coreModPath, lastModName)
185
186 -class CoreLoaderFactory(BaseLoaderFactory):
187
188 - def _createLoader(self, package, coreModPath, lastModName):
189 return PluginLoader(package, coreModPath, lastModName, CoreImporter())
190
191 -class PackLoaderFactory(BaseLoaderFactory):
192
193 - def __init__(self, walker, modPathPrefix):
194 BaseLoaderFactory.__init__(self, walker) 195 self.modPathPrefix = modPathPrefix
196
197 - def _createLoader(self, package, coreModPath, lastModName):
198 packModPath = '%s.%s' % (self.modPathPrefix, coreModPath) 199 return PluginLoader(package, packModPath, lastModName, PackImporter())
200
201 -class PluginManager(object):
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 = {} # PluginLoaders by module path 222 self.loadedZenpacks = [] # zenpacks that have been processed 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
229 - def getPluginLoader(self, packs, modPath):
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
241 - def getPluginLoaders(self, packs):
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
264 - def _addPluginLoaders(self, loaderFactory, package):
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
275 -class ModelingManager(object):
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
285 - def getInstance(cls):
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
294 -class MonitoringManager(object):
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
304 - def getInstance(cls):
305 if cls.instance is None: 306 cls.instance = PluginManager( 307 lastModName='parsers', 308 packPath=(), 309 productsPaths=[('ZenRRD',)]) 310 return cls.instance
311
312 -def _loadPlugins(pluginManager, dmd):
313 return pluginManager.getPluginLoaders(dmd.ZenPackManager.packs())
314
315 -def loadPlugins(dmd):
316 "Get PluginLoaders for all the modeling plugins" 317 return _loadPlugins(ModelingManager.getInstance(), dmd)
318
319 -def loadParserPlugins(dmd):
320 "Get PluginLoaders for all the modeling plugins" 321 return _loadPlugins(MonitoringManager.getInstance(), dmd)
322
323 -def getParserLoader(dmd, modPath):
324 "Get a PluginLoader for the given monitoring plugin's module path" 325 return MonitoringManager.getInstance().getPluginLoader( 326 dmd.ZenPackManager.packs(), modPath)
327