Package Products :: Package ZenUtils :: Module zenpack |
[hide private]
[frames] | no frames]

# Source Code for Module Products.ZenUtils.zenpack

      1  ###########################################################################
      2  #
      3  # This program is part of Zenoss Core, an open source monitoring platform.
      4  # Copyright (C) 2007, 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  __doc__ = "Manage ZenPacks"
     14
     15  import Globals
     16  from Products.ZenModel.ZenPack import ZenPack, ZenPackException, \
     17                                          ZenPackNeedMigrateException
     18  from Products.ZenUtils.ZenScriptBase import ZenScriptBase
     19  from Products.ZenUtils.Utils import cleanupSkins, zenPath
     20  import transaction
     21  from zipfile import ZipFile
     22  from StringIO import StringIO
     23  import ConfigParser
     24  import Products.ZenModel.ZenPackLoader as ZPL
     25  from Products.ZenModel.ZenPackLoader import CONFIG_FILE, CONFIG_SECTION_ABOUT
     26  import os, sys
     27  import ZenPackCmd as EggPackCmd
     28
     29
     30 -def RemoveZenPack(dmd, packName, log=None,
     31                          skipDepsCheck=False, leaveObjects=True,
     32                          deleteFiles=True):
     33      if log:
     34          log.debug('Removing Pack "%s"' % packName)
     35      if not skipDepsCheck:
     36          for pack in dmd.ZenPackManager.packs():
     37              if packName in pack.requires:
     38                  raise ZenPackException('Pack %s depends on pack %s, '
     39                                          'not removing' % (pack.id, packName))
     40      zp = None
     41      try:
     42          zp = dmd.ZenPackManager.packs._getOb(packName)
     43      except AttributeError, ex:
     44          # Pack not in zeo, might still exist in filesystem
     45          if log:
     46              log.debug('No ZenPack named %s in zeo' % packName)
     47      if zp:
     48          try:
     49              # In 2.2 we added an additional parameter to the remove method.
     50              # Any ZenPack subclasses that haven't been updated to take the new
     51              # parameter will throw a TypeError.
     52              # The newer version of zenoss-supplied ZenPacks monkey patch
     53              # older installed versions during an upgrade so that the remove
     54              # accepts the leaveObjects method.
     55              zp.remove(dmd, leaveObjects=True)
     56          except TypeError:
     57              zp.remove(dmd)
     58          dmd.ZenPackManager.packs._delObject(packName)
     59      root = zenPath('Products', packName)
     60      if deleteFiles:
     61          if log:
     62              log.debug('Removing %s' % root)
     63          recurse = ""
     64          if os.path.isdir(root):
     65              recurse = "r"
     66          os.system('rm -%sf %s' % (recurse, root))
     67      cleanupSkins(dmd)
     68      return True
     69
     70
     71 -class ZenPackCmd(ZenScriptBase):
     72      "Manage ZenPacks"
     73
     74
     75 -    def run(self):
     76          "Execute the user's request"
     77          import Products
     78          Products.Five.zcml.load_config('event.zcml', Products.Five)
     79
     80          if self.args:
     81              print "Require one of --install, --remove or --list flags."
     82              self.parser.print_help()
     83              return
     84
     85          if self.options.installPackName:
     86              eggInstall = (self.options.installPackName.lower().endswith('.egg')
     87                  or os.path.exists(os.path.join(self.options.installPackName,
     88                                                  'setup.py')))
     89
     90          # files-only just lays the files down and doesn't "install"
     91          # them into zeo
     92          class ZPProxy:
     93              def __init__(self, zpId):
     94                  self.id = zpId
     95              def path(self, *parts):
     96                  return zenPath('Products', self.id, *parts)
     97          if self.options.installPackName and self.options.filesOnly:
     98              if eggInstall:
     99                  return  EggPackCmd.InstallZenPack(None,
    100                              self.options.installPackName,
    101                                  filesOnly=True)
    102              packName = self.extract(self.options.installPackName)
    103              proxy = ZPProxy(packName)
    104              for loader in (ZPL.ZPLDaemons(), ZPL.ZPLBin(), ZPL.ZPLLibExec()):
    105                  loader.load(proxy, None)
    106              return
    107          if self.options.removePackName and self.options.filesOnly:
    108              # Remove files-only is not yet supported for egg zenpacks
    109              # todo
    110              proxy = ZPProxy(self.options.removePackName)
    111              for loader in (ZPL.ZPLDaemons(), ZPL.ZPLBin(), ZPL.ZPLLibExec()):
    112                  loader.unload(proxy, None)
    113              os.system('rm -rf %s' % zenPath('Products',
    114                                              self.options.removePackName))
    115              return
    116
    117
    118
    119          self.connect()
    120
    121          if not getattr(self.dmd, 'ZenPackManager', None):
    122              raise ZenPackNeedMigrateException('Your Zenoss database appears'
    123                  ' to be out of date. Try running zenmigrate to update.')
    124
    125          if self.options.installPackName:
    126              if eggInstall:
    127                  return EggPackCmd.InstallEggAndZenPack(
    128                      self.dmd,
    129                      self.options.installPackName,
    130                      link=self.options.link,
    131                      filesOnly=False,
    132                      previousVersion= self.options.previousVersion)
    133              if not self.preInstallCheck():
    134                  self.stop('%s not installed' % self.options.installPackName)
    135              if os.path.isfile(self.options.installPackName):
    136                  packName = self.extract(self.options.installPackName)
    137              elif os.path.isdir(self.options.installPackName):
    138                  if self.options.link:
    139                      packName = self.linkDir(self.options.installPackName)
    140                  else:
    141                      packName = self.copyDir(self.options.installPackName)
    142              else:
    143                  self.stop('%s does not appear to be a valid file or directory.'
    144                            % self.options.installPackName)
    145              # We want to make sure all zenpacks have a skins directory and that it
    146              # is registered. The zip file may not contain a skins directory, so we
    147              # create one here if need be.  The directory should be registered
    148              # by the zenpack's __init__.py and the skin should be registered
    149              # by ZPLSkins loader.
    150              skinsSubdir = zenPath('Products', packName, 'skins', packName)
    151              if not os.path.exists(skinsSubdir):
    152                  os.makedirs(skinsSubdir, 0750)
    153              self.install(packName)
    154
    155  #        elif self.options.fetch:
    156  #            return EggPackCmd.FetchAndInstallZenPack(self.dmd,
    157  #                                self.options.fetch, self.options.fetchVers)
    158          elif self.options.removePackName:
    159              pack = self.dmd.ZenPackManager.packs._getOb(
    160                                          self.options.removePackName, None)
    161              if not pack:
    162                  raise ZenPackException('ZenPack %s is not installed.' %
    163                                          self.options.removePackName)
    164              if pack.isEggPack():
    165                  return EggPackCmd.RemoveZenPack(self.dmd,
    166                                                  self.options.removePackName)
    167              RemoveZenPack(self.dmd, self.options.removePackName, self.log)
    168
    169          elif self.options.list:
    170              for zpId in self.dmd.ZenPackManager.packs.objectIds():
    171                  try:
    172                      zp = self.dmd.ZenPackManager.packs._getOb(zpId, None)
    173                  except AttributeError:
    174                      zp = None
    175                  if not zp:
    176                      desc = 'broken'
    177                  elif zp.isEggPack():
    178                      desc = zp.eggPath()
    179                  else:
    180                      desc = zp.path()
    181                  print('%s (%s)' % (zpId,  desc))
    182
    183          transaction.commit()
    184
    185
    186 -    def preInstallCheck(self):
    187          ''' Check that prerequisite zenpacks are installed.
    188          Return True if no prereqs specified or if they are present.
    189          False otherwise.
    190          '''
    191          if os.path.isfile(self.options.installPackName):
    192              zf = ZipFile(self.options.installPackName)
    193              for name in zf.namelist():
    194                  if name.endswith == '/%s' % CONFIG_FILE:
    195                      sio = StringIO(zf.read(name))
    196              else:
    197                  return True
    198          else:
    199              name = os.path.join(self.options.installPackName, CONFIG_FILE)
    200              if os.path.isfile(name):
    201                  fp = open(name)
    202                  sio = StringIO(fp.read())
    203                  fp.close()
    204              else:
    205                  return True
    206
    207          parser = ConfigParser.SafeConfigParser()
    208          parser.readfp(sio, name)
    209          if parser.has_section(CONFIG_SECTION_ABOUT) \
    210              and parser.has_option(CONFIG_SECTION_ABOUT, 'requires'):
    211              requires = eval(parser.get(CONFIG_SECTION_ABOUT, 'requires'))
    212              if not isinstance(requires, list):
    213                  requires = [zp.strip() for zp in requires.split(',')]
    214              missing = [zp for zp in requires
    215                      if zp not in self.dataroot.ZenPackManager.packs.objectIds()]
    216              if missing:
    217                  self.log.error('ZenPack %s was not installed because'
    218                                  % self.options.installPackName
    219                                  + ' it requires the following ZenPack(s): %s'
    220                                  % ', '.join(missing))
    221                  return False
    222          return True
    223
    224
    225 -    def install(self, packName):
    226          zp = None
    227          try:
    228              zp = self.dmd.ZenPackManager.packs._getOb(packName)
    229              self.log.info('Upgrading %s' % packName)
    230              zp.upgrade(self.app)
    231          except AttributeError:
    232              try:
    233                  module =  __import__('Products.' + packName, globals(), {}, [''])
    234                  zp = module.ZenPack(packName)
    235              except (ImportError, AttributeError), ex:
    236                  self.log.debug("Unable to find custom ZenPack (%s), "
    237                                 "defaulting to generic ZenPack",
    238                                 ex)
    239                  zp = ZenPack(packName)
    240              self.dmd.ZenPackManager.packs._setObject(packName, zp)
    241              zp = self.dmd.ZenPackManager.packs._getOb(packName)
    242              zp.install(self.app)
    243          if zp:
    244              for required in zp.requires:
    245                  try:
    246                      self.dmd.ZenPackManager.packs._getOb(required)
    247                  except:
    248                      self.log.error("Pack %s requires pack %s: not installing",
    249                                     packName, required)
    250                      return
    251          transaction.commit()
    252
    253 -    def extract(self, fname):
    254          "Unpack a ZenPack, and return the name"
    255          if not os.path.isfile(fname):
    256              self.stop('Unable to open file "%s"' % fname)
    257          zf = ZipFile(fname)
    258          name = zf.namelist()[0]
    259          packName = name.split('/')[0]
    260          self.log.debug('Extracting ZenPack "%s"' % packName)
    261          for name in zf.namelist():
    262              fullname = zenPath('Products', name)
    263              self.log.debug('Extracting %s' % name)
    264              if name.find('/.svn') > -1: continue
    265              if name.endswith('~'): continue
    266              if name.endswith('/'):
    267                  if not os.path.exists(fullname):
    268                      os.makedirs(fullname, 0750)
    269              else:
    270                  base = os.path.dirname(fullname)
    271                  if not os.path.isdir(base):
    272                      os.makedirs(base, 0750)
    273                  file(fullname, 'wb').write(zf.read(name))
    274          return packName
    275
    276
    277 -    def copyDir(self, srcDir):
    278          '''Copy an unzipped zenpack to the appropriate location.
    279          Return the name.
    280          '''
    281          # Normalize srcDir to not end with slash
    282          if srcDir.endswith('/'):
    283              srcDir = srcDir[:-1]
    284
    285          if not os.path.isdir(srcDir):
    286              self.stop('Specified directory does not appear to exist: %s' %
    287                          srcDir)
    288
    289          # Determine name of pack and it's destination directory
    290          packName = os.path.split(srcDir)[1]
    291          root = zenPath('Products', packName)
    292
    293          # Continue without copying if the srcDir is already in Products
    294          if os.path.exists(root) and os.path.samefile(root, srcDir):
    295              self.log.debug('Directory already in %s, not copying.',
    296                             zenPath('Products'))
    297              return packName
    298
    299          # Copy the source dir over to Products
    300          self.log.debug('Copying %s' % packName)
    301          result = os.system('cp -r %s %s' % (srcDir, zenPath('Products')))
    302          if result == -1:
    303              self.stop('Error copying %s to %s' % (srcDir, zenPath('Products')))
    304
    305          return packName
    306
    307
    308 -    def linkDir(self, srcDir):
    309          '''Symlink the srcDir into Products
    310          Return the name.
    311          '''
    312          # Normalize srcDir to not end with slash
    313          if srcDir.endswith('/'):
    314              srcDir = srcDir[:-1]
    315
    316          # Need absolute path for links
    317          srcDir = os.path.abspath(srcDir)
    318
    319          if not os.path.isdir(srcDir):
    320              self.stop('Specified directory does not appear to exist: %s' %
    321                          srcDir)
    322
    323          # Determine name of pack and it's destination directory
    324          packName = os.path.split(srcDir)[1]
    325          root = zenPath('Products', packName)
    326
    327          # Continue without copying if the srcDir is already in Products
    328          if os.path.exists(root) and os.path.samefile(root, srcDir):
    329              self.log.debug('Directory already in %s, not copying.',
    330                             zenPath('Products'))
    331              return packName
    332
    333          targetdir = zenPath("Products", packName)
    334          cmd = 'test -d %s && rm -rf %s' % (targetdir, targetdir)
    335          os.system(cmd)
    336          cmd = 'ln -s %s %s' % (srcDir, zenPath("Products"))
    337          os.system(cmd)
    338
    339          return packName
    340
    341
    342 -    def stop(self, why):
    343          self.log.error("zenpack stopped: %s", why)
    344          sys.exit(1)
    345
    346
    347 -    def buildOptions(self):
    348          self.parser.add_option('--install',
    349                                 dest='installPackName',
    350                                 default=None,
    351                                 help="Path to the ZenPack to install.")
    352  #        self.parser.add_option('--fetch',
    353  #                               dest='fetch',
    354  #                               default=None,
    355  #                               help='Name of ZenPack to retrieve from '
    356  #                                    'Zenoss.net and install.')
    357  #        self.parser.add_option('--fetchVers',
    358  #                               dest='fetchVers',
    359  #                               default=None,
    360  #                               help='Use with --fetch to specify a version'
    361  #                                    ' for the ZenPack to download and install.')
    362          self.parser.add_option('--remove',
    363                                 dest='removePackName',
    364                                 default=None,
    365                                 help="Name of the ZenPack to remove.")
    366          self.parser.add_option('--list',
    367                                 dest='list',
    368                                 action="store_true",
    369                                 default=False,
    370                                 help='List installed ZenPacks')
    371          self.parser.add_option('--link',
    372                                 dest='link',
    373                                 action="store_true",
    374                                 default=False,
    375                                 help="Install the ZenPack in place, without "
    376                                          "copying into $ZENHOME/ZenPacks.")
    377          self.parser.add_option('--files-only',
    378                                 dest='filesOnly',
    379                                 action="store_true",
    380                                 default=False,
    381                                 help='Install the ZenPack files onto the '
    382                                          'filesystem, but do not install the '
    383                                          'ZenPack into Zenoss.')
    384          self.parser.add_option('--previousversion',
    385                                 dest='previousVersion',
    386                                 default=None,
    387                                 help="Previous version of the zenpack;"
    388                                      ' used during upgrades')
    389
    390          ZenScriptBase.buildOptions(self)
    391
    392  if __name__ == '__main__':
    393      try:
    394          zp = ZenPackCmd()
    395          zp.run()
    396      except Exception, e:
    397          sys.stderr.write('ERROR: zenpack command failed. Reason: %s: %s\n' %
    398                  (e.__class__.__name__, e))
    399          sys.exit(-1)
    400