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, traceback=traceback.format_exc() ) 111 finally: 112 try: 113 sys.path.remove(self.package) 114 except ValueError: 115 # It's already been removed 116 pass
117 118 pb.setUnjellyableForClass(PluginLoader, PluginLoader) 119
120 -def _coreModPaths(walker, package):
121 "generates modPath strings for the modules in a core directory" 122 for absolutePath, dirname, filenames in walker.walk(package): 123 if absolutePath == package: 124 modPathBase = [] 125 elif absolutePath.startswith(package): 126 modPathBase = absolutePath[len(package)+1:].split(os.path.sep) 127 else: 128 log.debug('absolutePath must start with package: ' 129 'absolutePath=%s, package=%s', absolutePath, package) 130 continue 131 for filename in filenames: 132 if filename.endswith(".py") \ 133 and filename[0] not in ('.', "_") \ 134 and '#' not in filename \ 135 and filename not in ('CollectorPlugin.py', 'DataMaps.py'): 136 yield '.'.join(modPathBase + [filename[:-3]])
137
138 -class OsWalker(object):
139
140 - def walk(self, package):
141 return os.walk(package)
142
143 -class CoreImporter(pb.Copyable, pb.RemoteCopy):
144
145 - def importPlugin(self, package, modPath):
146 # Load the plugins package using its path as the name to 147 # avoid conflicts. slashes in the name are OK when using 148 # the imp module. 149 plugin_pkg = imp.find_module('.', [package]) 150 imp.load_module(package, *plugin_pkg) 151 # Import the module, using the plugins package 152 # 153 # Equivalent to, for example: 154 # from mypackage.zenoss.snmp import DeviceMap 155 # 156 clsname = modPath.split('.')[-1] 157 mod = __import__(package + '.' + modPath, 158 globals(), 159 locals(), 160 [clsname]) 161 # get the class 162 return getattr(mod, clsname)
163 164 pb.setUnjellyableForClass(CoreImporter, CoreImporter) 165
166 -class PackImporter(pb.Copyable, pb.RemoteCopy):
167
168 - def importPlugin(self, package, modPath):
169 # ZenPack plugins are specified absolutely; we can import 170 # them using the old method 171 return importClass(modPath)
172 173 pb.setUnjellyableForClass(PackImporter, PackImporter) 174
175 -class BaseLoaderFactory(object):
176
177 - def __init__(self, walker):
178 self.walker = walker
179
180 - def genLoaders(self, package, lastModName):
181 for coreModPath in _coreModPaths(self.walker, package): 182 yield self._createLoader(package, coreModPath, lastModName)
183
184 -class CoreLoaderFactory(BaseLoaderFactory):
185
186 - def _createLoader(self, package, coreModPath, lastModName):
187 return PluginLoader(package, coreModPath, lastModName, CoreImporter())
188
189 -class PackLoaderFactory(BaseLoaderFactory):
190
191 - def __init__(self, walker, modPathPrefix):
192 BaseLoaderFactory.__init__(self, walker) 193 self.modPathPrefix = modPathPrefix
194
195 - def _createLoader(self, package, coreModPath, lastModName):
196 packModPath = '%s.%s' % (self.modPathPrefix, coreModPath) 197 return PluginLoader(package, packModPath, lastModName, PackImporter())
198
199 -class PluginManager(object):
200 """ 201 Manages plugin modules. Finds plugins and returns PluginLoader instances. 202 Keeps a cache of previously loaded plugins. 203 """ 204
205 - def __init__(self, lastModName, packPath, productsPaths):
206 """ 207 Adds PluginLoaders for plugins in productsPaths to the pluginLoaders 208 dictionary. 209 210 lastModName - the directory name where the plugins are found. this name 211 is appended to the following paths 212 packPath - path to the directory that holds the plugin modules inside 213 a zenpack. this path is relative to the zenpack root 214 productsPaths - list of paths to directories that hold plugin 215 modules. these paths are relative to $ZENHOME/Products 216 217 a 'path', as used here, is a tuple of directory names 218 """ 219 self.pluginLoaders = {} # PluginLoaders by module path 220 self.loadedZenpacks = [] # zenpacks that have been processed 221 self.lastModName = lastModName 222 self.packPath = packPath 223 for path in productsPaths: 224 package = zenPath(*('Products',) + path + (lastModName,)) 225 self._addPluginLoaders(CoreLoaderFactory(OsWalker()), package)
226
227 - def getPluginLoader(self, packs, modPath):
228 """ 229 Get the PluginLoader for a specific plugin. 230 231 packs - list of installed zenpacks (ZenPack instances) 232 modPath - the module path of the plugin 233 """ 234 if modPath not in self.pluginLoaders: 235 self.getPluginLoaders(packs) 236 if modPath in self.pluginLoaders: 237 return self.pluginLoaders[modPath]
238
239 - def getPluginLoaders(self, packs):
240 """ 241 Add the PluginLoaders for the packs to the pluginLoaders dictionary. 242 Return the values of that dictionary. 243 244 packs - list of installed zenpacks (ZenPack instances) 245 """ 246 try: 247 for pack in packs: 248 if pack.moduleName() not in self.loadedZenpacks: 249 self.loadedZenpacks.append(pack.moduleName()) 250 modPathPrefix = '.'.join((pack.moduleName(),) + 251 self.packPath + (self.lastModName,)) 252 factory = PackLoaderFactory(OsWalker(), modPathPrefix) 253 package = pack.path(*self.packPath + (self.lastModName,)) 254 self._addPluginLoaders(factory, package) 255 except: 256 log.error('Could not load plugins from ZenPacks.' 257 ' One of the ZenPacks is missing or broken.') 258 import traceback 259 log.debug(traceback.format_exc()) 260 return self.pluginLoaders.values()
261
262 - def _addPluginLoaders(self, loaderFactory, package):
263 log.debug("Loading collector plugins from: %s", package) 264 try: 265 loaders = loaderFactory.genLoaders(package, self.lastModName) 266 for loader in loaders: 267 self.pluginLoaders[loader.modPath] = loader 268 except: 269 log.error('Could not load plugins from %s', package) 270 import traceback 271 log.debug(traceback.format_exc())
272
273 -class ModelingManager(object):
274 """ 275 this class is not intended to be instantiated. instead it is a place to 276 hold a singleton instance of PluginManager without having them call the 277 constructor when this module is imported. 278 """ 279 280 instance = None 281 282 @classmethod
283 - def getInstance(cls):
284 if cls.instance is None: 285 cls.instance = PluginManager( 286 lastModName='plugins', 287 packPath=('modeler',), 288 productsPaths=[('DataCollector',), 289 ('ZenWin', 'modeler',)]) 290 return cls.instance
291
292 -class MonitoringManager(object):
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
302 - def getInstance(cls):
303 if cls.instance is None: 304 cls.instance = PluginManager( 305 lastModName='parsers', 306 packPath=(), 307 productsPaths=[('ZenRRD',)]) 308 return cls.instance
309
310 -def _loadPlugins(pluginManager, dmd):
311 return pluginManager.getPluginLoaders(dmd.ZenPackManager.packs())
312
313 -def loadPlugins(dmd):
314 "Get PluginLoaders for all the modeling plugins" 315 return _loadPlugins(ModelingManager.getInstance(), dmd)
316
317 -def loadParserPlugins(dmd):
318 "Get PluginLoaders for all the modeling plugins" 319 return _loadPlugins(MonitoringManager.getInstance(), dmd)
320
321 -def getParserLoader(dmd, modPath):
322 "Get a PluginLoader for the given monitoring plugin's module path" 323 return MonitoringManager.getInstance().getPluginLoader( 324 dmd.ZenPackManager.packs(), modPath)
325