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