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

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