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

Source Code for Module Products.DataCollector.Plugins

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