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