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