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