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

# Source Code for Module Products.ZenUtils.Utils

       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 or (at your
       8  # option) any later version as published by the Free Software Foundation.
       9  #
      10  # For complete information please visit: http://www.zenoss.com/oss/
      11  #
      12  ###########################################################################
      13
      14  __doc__="""Utils
      15
      16  General utility functions module
      17
      18  """
      19
      20  import sys
      21  import select
      22  import popen2
      23  import fcntl
      24  import time
      25  import os
      26  import types
      27  import logging
      28  import re
      29  import socket
      30  import warnings
      31  import math
      32  import contextlib
      33  from decimal import Decimal
      34  from sets import Set
      35  import asyncore
      36  import copy
      37  log = logging.getLogger("zen.Utils")
      38
      39  from popen2 import Popen4
      40
      41  from Acquisition import aq_base
      42  from zExceptions import NotFound
      43  from AccessControl import getSecurityManager
      44  from AccessControl import Unauthorized
      45  from AccessControl.ZopeGuards import guarded_getattr
      46  from Acquisition import aq_inner, aq_parent
      47  from ZServer.HTTPServer import zhttp_channel
      48
      49  from Products.ZenUtils.Exceptions import ZenPathError, ZentinelException
      50  from Products.ZenUtils.jsonutils import unjson
      51
      52
      53  DEFAULT_SOCKET_TIMEOUT = 30
      54
      55
      56 -class HtmlFormatter(logging.Formatter):
      57      """
      58      Formatter for the logging class
      59      """
      60
      61 -    def __init__(self):
      62          logging.Formatter.__init__(self,
      63          """<tr class="loggingRow">
      64          <td>%(asctime)s</td> <td>%(levelname)s</td>
      65          <td>%(name)s</td> <td>%(message)s</td>
      66          </tr>
      67          """,
      68          "%Y-%m-%d %H:%M:%S")
      69
      70 -    def formatException(self, exc_info):
      71          """
      72          Format a Python exception
      73
      74          @param exc_info: Python exception containing a description of what went wrong
      75          @type exc_info: Python exception class
      76          @return: formatted exception
      77          @rtype: string
      78          """
      79          exc = logging.Formatter.formatException(self,exc_info)
      80          return """<tr class="tablevalues"><td colspan="4">%s</td></tr>""" % exc
      81
      82
      83 -def setWebLoggingStream(stream):
      84      """
      85      Setup logging to log to a browser using a request object.
      86
      87      @param stream: IO stream
      88      @type stream: stream class
      89      @return: logging handler
      90      @rtype: logging handler
      91      """
      92      handler = logging.StreamHandler(stream)
      93      handler.setFormatter(HtmlFormatter())
      94      rlog = logging.getLogger()
      95      rlog.addHandler(handler)
      96      rlog.setLevel(logging.ERROR)
      97      zlog = logging.getLogger("zen")
      98      zlog.setLevel(logging.INFO)
      99      return handler
     100
     101
     102 -def clearWebLoggingStream(handler):
     103      """
     104      Clear our web logger.
     105
     106      @param handler: logging handler
     107      @type handler: logging handler
     108      """
     109      rlog = logging.getLogger()
     110      rlog.removeHandler(handler)
     111
     112
     113 -def convToUnits(number=0, divby=1024.0, unitstr="B"):
     114      """
     115      Convert a number to its human-readable form. ie: 4GB, 4MB, etc.
     116
     117          >>> convToUnits() # Don't do this!
     118          '0.0B'
     119          >>> convToUnits(None) # Don't do this!
     120          ''
     121          >>> convToUnits(123456789)
     122          '117.7MB'
     123          >>> convToUnits(123456789, 1000, "Hz")
     124          '123.5MHz'
     125
     126      @param number: base number
     127      @type number: number
     128      @param divby: divisor to use to convert to appropriate prefix
     129      @type divby: number
     130      @param unitstr: base unit of the number
     131      @type unitstr: string
     132      @return: number with appropriate units
     133      @rtype: string
     134      """
     135      units = map(lambda x:x + unitstr, ('','K','M','G','T','P'))
     136      try:
     137          numb = float(number)
     138      except:
     139          return ''
     140
     141      sign = 1
     142      if numb < 0:
     143          numb = abs(numb)
     144          sign = -1
     145      for unit in units:
     146          if numb < divby: break
     147          numb /= divby
     148      return "%.1f%s" % (numb * sign, unit)
     149
     150
     151 -def travAndColl(obj, toonerel, collect, collectname):
     152      """
     153      Walk a series of to one rels collecting collectname into collect
     154
     155      @param obj: object inside of Zope
     156      @type obj: object
     157      @param toonerel: a to-one relationship object
     158      @type toonerel: toonerel object
     159      @param collect: object list
     160      @type collect: list
     161      @param collectname: name inside of the to-one relation object
     162      @type collectname: string
     163      @return: list of objects
     164      @rtype: list
     165      """
     166      #from Acquisition import aq_base
     167      value = getattr(aq_base(obj), collectname, None)
     168      if value:
     169          collect.append(value)
     170      rel = getattr(aq_base(obj), toonerel, None)
     171      if callable(rel):
     172          nobj = rel()
     173          if nobj:
     174              return travAndColl(nobj, toonerel, collect, collectname)
     175      return collect
     176
     177
     178 -def getObjByPath(base, path, restricted=0):
     179      """
     180      Get a Zope object by its path (e.g. '/Devices/Server/Linux').
     181      Mostly a stripdown of unrestrictedTraverse method from Zope 2.8.8.
     182
     183      @param base: base part of a path
     184      @type base: string
     185      @param path: path to an object inside of the DMD
     186      @type path: string
     187      @param restricted: flag indicated whether to use securityManager
     188      @type restricted: integer
     189      @return: object pointed to by the path
     190      @rtype: object
     191      """
     192      if not path:
     193          return base
     194
     195      _getattr = getattr
     196      _none = None
     197      marker = object()
     198
     199      if isinstance(path, str):
     200          # Unicode paths are not allowed
     201          path = path.split('/')
     202      else:
     203          path = list(path)
     204
     205      REQUEST = {'TraversalRequestNameStack': path}
     206      path.reverse()
     207      path_pop=path.pop
     208
     209      if len(path) > 1 and not path[0]:
     210          # Remove trailing slash
     211          path.pop(0)
     212
     213      if restricted:
     214          securityManager = getSecurityManager()
     215      else:
     216          securityManager = _none
     217
     218      if not path[-1]:
     219          # If the path starts with an empty string, go to the root first.
     220          path_pop()
     221          base = base.getPhysicalRoot()
     222          if (restricted
     223              and not securityManager.validate(None, None, None, base)):
     224              raise Unauthorized( base )
     225
     226      obj = base
     227      while path:
     228          name = path_pop()
     229
     230          if name[0] == '_':
     231              # Never allowed in a URL.
     232              raise NotFound( name )
     233
     234          if name == '..':
     235              next = aq_parent(obj)
     236              if next is not _none:
     237                  if restricted and not securityManager.validate(
     238                      obj, obj,name, next):
     239                      raise Unauthorized( name )
     240                  obj = next
     241                  continue
     242
     243          bobo_traverse = _getattr(obj, '__bobo_traverse__', _none)
     244          if bobo_traverse is not _none:
     245              next = bobo_traverse(REQUEST, name)
     246              if restricted:
     247                  if aq_base(next) is not next:
     248                      # The object is wrapped, so the acquisition
     249                      # context is the container.
     250                      container = aq_parent(aq_inner(next))
     251                  elif _getattr(next, 'im_self', _none) is not _none:
     252                      # Bound method, the bound instance
     253                      # is the container
     254                      container = next.im_self
     255                  elif _getattr(aq_base(obj), name, marker) == next:
     256                      # Unwrapped direct attribute of the object so
     257                      # object is the container
     258                      container = obj
     259                  else:
     260                      # Can't determine container
     261                      container = _none
     262                  try:
     263                      validated = securityManager.validate(
     264                                             obj, container, name, next)
     265                  except Unauthorized:
     266                      # If next is a simple unwrapped property, it's
     267                      # parentage is indeterminate, but it may have been
     268                      # acquired safely.  In this case validate will
     269                      # raise an error, and we can explicitly check that
     270                      # our value was acquired safely.
     271                      validated = 0
     272                      if container is _none and \
     273                             guarded_getattr(obj, name, marker) is next:
     274                          validated = 1
     275                  if not validated:
     276                      raise Unauthorized( name )
     277          else:
     278              if restricted:
     279                  next = guarded_getattr(obj, name, marker)
     280              else:
     281                  next = _getattr(obj, name, marker)
     282                  ## Below this is a change from the standard traverse from zope
     283                  ## it allows a path to use acquisition which is not what
     284                  ## we want.  Our version will fail if one element of the
     285                  ## path doesn't exist. -EAD
     286                  #if hasattr(aq_base(obj), name):
     287                  #    next = _getattr(obj, name, marker)
     288                  #else:
     289                  #    raise NotFound, name
     290              if next is marker:
     291                  try:
     292                      next=obj[name]
     293                  except AttributeError:
     294                      # Raise NotFound for easier debugging
     295                      # instead of AttributeError: __getitem__
     296                      raise NotFound( name )
     297                  if restricted and not securityManager.validate(
     298                      obj, obj, _none, next):
     299                      raise Unauthorized( name )
     300          obj = next
     301      return obj
     302
     303
     304
     305 -def checkClass(myclass, className):
     306      """
     307      Perform issubclass using class name as string
     308
     309      @param myclass: generic object
     310      @type myclass: object
     311      @param className: name of a class
     312      @type className: string
     313      @return: the value 1 if found or None
     314      @rtype: integer or None
     315      """
     316      if myclass.__name__ == className:
     317          return 1
     318      for mycl in myclass.__bases__:
     319          if checkClass(mycl, className):
     320              return 1
     321
     322
     323 -def lookupClass(productName, classname=None):
     324      """
     325      look in sys.modules for our class
     326
     327      @param productName: object in Products
     328      @type productName: string
     329      @param classname: class name
     330      @type classname: string
     331      @return: object at the classname in Products
     332      @rtype: object or None
     333      """
     334      if productName in sys.modules:
     335         mod = sys.modules[productName]
     336
     337      elif "Products."+productName in sys.modules:
     338         mod = sys.modules["Products."+productName]
     339
     340      else:
     341         return None
     342
     343      if not classname:
     344         classname = productName.split('.')[-1]
     345
     346      return getattr(mod,classname)
     347
     348
     349 -def importClass(modulePath, classname=""):
     350      """
     351      Import a class from the module given.
     352
     353      @param modulePath: path to module in sys.modules
     354      @type modulePath: string
     355      @param classname: name of a class
     356      @type classname: string
     357      @return: the class in the module
     358      @rtype: class
     359      """
     360      try:
     361          if not classname: classname = modulePath.split(".")[-1]
     362          try:
     363              __import__(modulePath, globals(), locals(), classname)
     364              mod = sys.modules[modulePath]
     365          except (ValueError, ImportError, KeyError), ex:
     366              raise ex
     367
     368          return getattr(mod, classname)
     369      except AttributeError:
     370          raise ImportError("Failed while importing class %s from module %s" % (
     371                              classname, modulePath))
     372
     373
     374 -def cleanstring(value):
     375      """
     376      Take the trailing \x00 off the end of a string
     377
     378      @param unitstr: sample string
     379      @type unitstr: string
     380      @return: cleaned string
     381      @rtype: string
     382      """
     383      if isinstance(value, basestring) and value.endswith('\0'):
     384          value = value[:-1]
     385      return value
     386
     387
     388 -def getSubObjects(base, filter=None, descend=None, retobjs=None):
     389      """
     390      Do a depth-first search looking for objects that the function filter
     391      returns as True. If descend is passed it will check to see if we
     392      should keep going down or not
     393
     394      @param base: base object to start search
     395      @type base: object
     396      @param filter: filter to apply to each object to determine if it gets added to the returned list
     397      @type filter: function or None
     398      @param descend: function to apply to each object to determine whether or not to continue searching
     399      @type descend: function or None
     400      @param retobjs: list of objects found
     401      @type retobjs: list
     402      @return: list of objects found
     403      @rtype: list
     404      """
     405      if not retobjs: retobjs = []
     406      for obj in base.objectValues():
     407          if not filter or filter(obj):
     408              retobjs.append(obj)
     409          if not descend or descend(obj):
     410              retobjs = getSubObjects(obj, filter, descend, retobjs)
     411      return retobjs
     412
     413
     414 -def getSubObjectsMemo(base, filter=None, descend=None, memo={}):
     415      """
     416      Do a depth-first search looking for objects that the function filter
     417      returns as True. If descend is passed it will check to see if we
     418      should keep going down or not.
     419
     420      This is a Python iterable.
     421
     422      @param base: base object to start search
     423      @type base: object
     424      @param filter: filter to apply to each object to determine if it gets added to the returned list
     425      @type filter: function or None
     426      @param descend: function to apply to each object to determine whether or not to continue searching
     427      @type descend: function or None
     428      @param memo: dictionary of objects found (unused)
     429      @type memo: dictionary
     430      @return: list of objects found
     431      @rtype: list
     432      """
     433      from Products.ZenRelations.RelationshipManager \
     434          import RelationshipManager
     435      if base.meta_type == "To One Relationship":
     436          objs = [base.obj]
     437      else:
     438          objs = base.objectValues()
     439      for obj in objs:
     440          if (isinstance(obj, RelationshipManager) and
     441              not obj.getPrimaryDmdId().startswith(base.getPrimaryDmdId())):
     442              continue
     443          if not filter or filter(obj):
     444              yield obj
     445          if not descend or descend(obj):
     446              for x in getSubObjectsMemo(obj, filter, descend, memo):
     447                  yield x
     448
     449
     450 -def getAllConfmonObjects(base):
     451      """
     452      Get all ZenModelRM objects in database
     453
     454      @param base: base object to start searching
     455      @type base: object
     456      @return: list of objects
     457      @rtype: list
     458      """
     459      from Products.ZenModel.ZenModelRM import ZenModelRM
     460      from Products.ZenModel.ZenModelBase import ZenModelBase
     461      from Products.ZenRelations.ToManyContRelationship \
     462          import ToManyContRelationship
     463      from Products.ZenRelations.ToManyRelationship \
     464          import ToManyRelationship
     465      from Products.ZenRelations.ToOneRelationship \
     466          import ToOneRelationship
     467
     468      def descend(obj):
     469          """
     470          Function to determine whether or not to continue searching
     471          @param obj: object
     472          @type obj: object
     473          @return: True if we want to keep searching
     474          @rtype: boolean
     475          """
     476          return (
     477                  isinstance(obj, ZenModelBase) or
     478                  isinstance(obj, ToManyContRelationship) or
     479                  isinstance(obj, ToManyRelationship) or
     480                  isinstance(obj, ToOneRelationship))
     481
     482      def filter(obj):
     483          """
     484          Filter function to decide whether it's an object we
     485          want to know about or not.
     486
     487          @param obj: object
     488          @type obj: object
     489          @return: True if we want to keep it
     490          @rtype: boolean
     491          """
     492          return isinstance(obj, ZenModelRM) and obj.id != "dmd"
     493
     494      return getSubObjectsMemo(base, filter=filter, descend=descend)
     495
     496
     497 -def zenpathsplit(pathstring):
     498      """
     499      Split a zen path and clean up any blanks or bogus spaces in it
     500
     501      @param pathstring: a path inside of ZENHOME
     502      @type pathstring: string
     503      @return: a path
     504      @rtype: string
     505      """
     506      path = pathstring.split("/")
     507      path = filter(lambda x: x, path)
     508      path = map(lambda x: x.strip(), path)
     509      return path
     510
     511
     512
     513 -def zenpathjoin(pathar):
     514      """
     515      Build a zenpath in its string form
     516
     517      @param pathstring: a path
     518      @type pathstring: string
     519      @return: a path
     520      @rtype: string
     521      """
     522      return "/" + "/".join(pathar)
     523
     524
     525 -def createHierarchyObj(root, name, factory, relpath="", llog=None):
     526      """
     527      Create a hierarchy object from its path we use relpath to skip down
     528      any missing relations in the path and factory is the constructor for
     529      this object.
     530
     531      @param root: root from which to start
     532      @type root: object
     533      @param name: path to object
     534      @type name: string
     535      @param factory: factory object to create
     536      @type factory: factory object
     537      @param relpath: relationship within which we will recurse as objects are created, if any
     538      @type relpath: object
     539      @param llog: unused
     540      @type llog: object
     541      @return: root object of a hierarchy
     542      @rtype: object
     543      """
     544      unused(llog)
     545      rootName = root.id
     546      for id in zenpathsplit(name):
     547          if id == rootName: continue
     548          if id == relpath or getattr(aq_base(root), relpath, False):
     549              root = getattr(root, relpath)
     550          if not getattr(aq_base(root), id, False):
     551              if id == relpath:
     552                  raise AttributeError("relpath %s not found" % relpath)
     553              log.debug("Creating object with id %s in object %s",id,root.getId())
     554              newobj = factory(id)
     555              root._setObject(id, newobj)
     556          root = getattr(root, id)
     557
     558      return root
     559
     560
     561 -def getHierarchyObj(root, name, relpath=None):
     562      """
     563      Return an object using its path relations are optional in the path.
     564
     565      @param root: root from which to start
     566      @type root: object
     567      @param name: path to object
     568      @type name: string
     569      @param relpath: relationship within which we will recurse as objects are created, if any
     570      @type relpath: object
     571      @return: root object of a hierarchy
     572      @rtype: object
     573      """
     574      for id in zenpathsplit(name):
     575          if id == relpath or getattr(aq_base(root), relpath, False):
     576              root = getattr(root, relpath)
     577          if not getattr(root, id, False):
     578              raise ZenPathError("Path %s id %s not found on object %s" %
     579                                  (name, id, root.getPrimaryId()))
     580          root = getattr(root, id, None)
     581
     582      return root
     583
     584
     585
     586 -def basicAuthUrl(username, password, url):
     587      """
     588      Add the username and password to a url in the form
     589      http://username:password@host/path
     590
     591      @param username: username
     592      @type username: string
     593      @param password: password
     594      @type password: string
     595      @param url: base URL to add username/password info
     596      @type url: string
     597      @return: URL with auth information incorporated
     598      @rtype: string
     599      """
     600      urlar = url.split('/')
     601      if not username or not password or urlar[2].find('@') > -1:
     602          return url
     603      urlar[2] = "%s:%s@%s" % (username, password, urlar[2])
     604      return "/".join(urlar)
     605
     606
     607
     608 -def prepId(id, subchar='_'):
     609      """
     610      Make an id with valid url characters. Subs [^a-zA-Z0-9-_,.$\(\) ]
     611      with subchar.  If id then starts with subchar it is removed.
     612
     613      @param id: user-supplied id
     614      @type id: string
     615      @return: valid id
     616      @rtype: string
     617      """
     618      _prepId = re.compile(r'[^a-zA-Z0-9-_,.$\(\) ]').sub
     619      _cleanend = re.compile(r"%s+$" % subchar).sub
     620      if id is None:
     621          raise ValueError('Ids can not be None')
     622      if not isinstance(id, basestring):
     623          id = str(id)
     624      id = _prepId(subchar, id)
     625      while id.startswith(subchar):
     626          if len(id) > 1: id = id[1:]
     627          else: id = "-"
     628      id = _cleanend("",id)
     629      id = id.strip()
     630      return str(id)
     631
     632
     633 -def sendEmail(emsg, host, port=25, usetls=0, usr='', pwd=''):
     634      """
     635      Send an email.  Return a tuple:
     636      (sucess, message) where sucess is True or False.
     637
     638      @param emsg: message to send
     639      @type emsg: email.MIMEText
     640      @param host: name of e-mail server
     641      @type host: string
     642      @param port: port number to communicate to the e-mail server
     643      @type port: integer
     644      @param usetls: boolean-type integer to specify whether to use TLS
     645      @type usetls: integer
     646      @param usr: username for TLS
     647      @type usr: string
     648      @param pwd: password for TLS
     649      @type pwd: string
     650      @return: (sucess, message) where sucess is True or False.
     651      @rtype: tuple
     652      """
     653      import smtplib
     654      socket.setdefaulttimeout(DEFAULT_SOCKET_TIMEOUT)
     655      fromaddr = emsg['From']
     656      toaddr = map(lambda x: x.strip(), emsg['To'].split(','))
     657      try:
     658          server = smtplib.SMTP(host, port)
     659          if usetls:
     660              server.ehlo()
     661              server.starttls()
     662              server.ehlo()
     663          if len(usr): server.login(usr, pwd)
     664          server.sendmail(fromaddr, toaddr, emsg.as_string())
     665          # Need to catch the quit because some servers using TLS throw an
     666          # EOF error on quit, so the email gets sent over and over
     667          try: server.quit()
     668          except: pass
     669      except (smtplib.SMTPException, socket.error, socket.timeout):
     670          result = (False, '%s - %s' % tuple(sys.exc_info()[:2]))
     671      else:
     672          result = (True, '')
     673      return result
     674
     675
     676  from twisted.internet.protocol import ProcessProtocol
     677 -class SendPageProtocol(ProcessProtocol):
     678      out = ''
     679      err = ''
     680      code = None
     681
     682 -    def __init__(self, msg):
     683          self.msg = msg
     684          self.out = ''
     685          self.err = ''
     686
     687 -    def connectionMade(self):
     688          self.transport.write(self.msg)
     689          self.transport.closeStdin()
     690
     691 -    def outReceived(self, data):
     692          self.out += data
     693
     694 -    def errReceived(self, data):
     695          self.err += data
     696
     697 -    def processEnded(self, reason):
     698          self.code = reason.value.exitCode
     699
     700
     701 -def sendPage(recipient, msg, pageCommand, deferred=False):
     702      """
     703      Send a page.  Return a tuple: (success, message) where
     704      sucess is True or False.
     705
     706      @param recipient: name to where a page should be sent
     707      @type recipient: string
     708      @param msg: message to send
     709      @type msg: string
     710      @param pageCommand: command that will send a page
     711      @type pageCommand: string
     712      @return: (sucess, message) where sucess is True or False.
     713      @rtype: tuple
     714      """
     715      import subprocess
     716      env = dict(os.environ)
     717      env["RECIPIENT"] = recipient
     718      msg = str(msg)
     719      if deferred:
     720          from twisted.internet import reactor
     721          protocol = SendPageProtocol(msg)
     722          d = reactor.spawnProcess(
     723              protocol, '/bin/sh', ('/bin/sh', '-c', pageCommand), env)
     724
     725          # Bad practice to block on a deferred. This is done because our call
     726          # chain is not asynchronous and we need to behave like a blocking call.
     727          while protocol.code is None:
     728              reactor.iterate(0.1)
     729
     730          return (not protocol.code, protocol.out)
     731      else:
     732          p = subprocess.Popen(pageCommand,
     733                               stdin=subprocess.PIPE,
     734                               stdout=subprocess.PIPE,
     735                               shell=True,
     736                               env=env)
     737          p.stdin.write(msg)
     738          p.stdin.close()
     739          response = p.stdout.read()
     740      return (not p.wait(), response)
     741
     742
     743 -def zdecode(context, value):
     744      """
     745      Convert a string using the decoding found in zCollectorDecoding
     746
     747      @param context: Zope object
     748      @type context: object
     749      @param value: input string
     750      @type value: string
     751      @return: converted string
     752      @rtype: string
     753      """
     754      if isinstance(value, str):
     755          decoding = getattr(context, 'zCollectorDecoding', 'latin-1')
     756          value = value.decode(decoding)
     757      return value
     758
     759
     760 -def localIpCheck(context, ip):
     761      """
     762      Test to see if an IP should not be included in the network map.
     763      Uses the zLocalIpAddresses to decide.
     764
     765      @param context: Zope object
     766      @type context: object
     767      @param ip: IP address
     768      @type ip: string
     769      @return: regular expression match or None (if not found)
     770      @rtype: re match object
     771      """
     772      return re.search(getattr(context, 'zLocalIpAddresses', '^$'), ip)
     773
     774 -def localInterfaceCheck(context, intname):
     775      """
     776      Test to see if an interface should not be included in the network map.
     777      Uses the zLocalInterfaceNames to decide.
     778
     779      @param context: Zope object
     780      @type context: object
     781      @param intname: network interface name
     782      @type intname: string
     783      @return: regular expression match or None (if not found)
     784      @rtype: re match object
     785      """
     786      return re.search(getattr(context, 'zLocalInterfaceNames', '^$'), intname)
     787
     788
     789 -def cmpClassNames(obj, classnames):
     790      """
     791      Check to see if any of an object's base classes
     792      are in a list of class names. Like isinstance(),
     793      but without requiring a class to compare against.
     794
     795      @param obj: object
     796      @type obj: object
     797      @param classnames: class names
     798      @type classnames: list of strings
     799      @return: result of the comparison
     800      @rtype: boolean
     801      """
     802      finalnames = Set()
     803      x = [obj.__class__]
     804      while x:
     805          thisclass = x.pop()
     806          x.extend(thisclass.__bases__)
     807          finalnames.add(thisclass.__name__)
     808      return bool( Set(classnames).intersection(finalnames) )
     809
     810
     811 -def resequence(context, objects, seqmap, origseq, REQUEST):
     812      """
     813      Resequence a seqmap
     814
     815      @param context: Zope object
     816      @type context: object
     817      @param objects: objects
     818      @type objects: list
     819      @param seqmap: sequence map
     820      @type seqmap: list
     821      @param origseq: sequence map
     822      @type origseq: list
     823      @param REQUEST: Zope REQUEST object
     824      @type REQUEST: Zope REQUEST object
     825      @return:
     826      @rtype: string
     827      """
     828      if seqmap and origseq:
     829          try:
     830              origseq = tuple(long(s) for s in origseq)
     831              seqmap = tuple(float(s) for s in seqmap)
     832          except ValueError:
     833              origseq = ()
     834              seqmap = ()
     835          orig = dict((o.sequence, o) for o in objects)
     836          if origseq:
     837              for oldSeq, newSeq in zip(origseq, seqmap):
     838                  orig[oldSeq].sequence = newSeq
     839      def sort(x):
     840          """
     841          @param x: unordered sequence items
     842          @type x: list
     843          @return: ordered sequence items
     844          @rtype: list
     845          """
     846          x = list(x)
     847          x.sort(lambda a, b: cmp(a.sequence, b.sequence))
     848          return x
     849
     850      for i, obj in enumerate(sort(objects)):
     851          obj.sequence = i
     852
     853      if REQUEST:
     854          return context.callZenScreen(REQUEST)
     855
     856
     857 -def cleanupSkins(dmd):
     858      """
     859      Prune out objects
     860
     861      @param dmd: Device Management Database
     862      @type dmd: DMD object
     863      """
     864      ps = dmd.getPhysicalRoot().zport.portal_skins
     865      layers = ps._objects
     866      layers = filter(lambda x:getattr(ps, x['id'], False), layers)
     867      ps._objects = tuple(layers)
     868
     869
     870 -def edgesToXML(edges, start=()):
     871      """
     872      Convert edges to an XML file
     873
     874      @param edges: edges
     875      @type edges: list
     876      @return: XML-formatted string
     877      @rtype: string
     878      """
     879      nodet = '<Node id="%s" prop="%s" icon="%s" color="%s"/>'
     880      edget = '<Edge fromID="%s" toID="%s"/>'
     881      xmlels = ['<Start name="%s" url="%s"/>' % start]
     882      nodeels = []
     883      edgeels = []
     884      for a, b in edges:
     885          node1 = nodet % (a[0], a[0], a[1], a[2])
     886          node2 = nodet % (b[0], b[0], b[1], b[2])
     887          edge1 = edget % (a[0], b[0])
     888          if node1 not in nodeels: nodeels.append(node1)
     889          if node2 not in nodeels: nodeels.append(node2)
     890          if edge1 not in edgeels: edgeels.append(edge1)
     891
     892      xmlels.extend(nodeels)
     893      xmlels.extend(edgeels)
     894      xmldoc = "<graph>%s</graph>" % ''.join(list(xmlels))
     895
     896      return xmldoc
     897
     898
     899 -def sane_pathjoin(base_path, *args ):
     900      """
     901      Joins paths in a saner manner than os.path.join()
     902
     903      @param base_path: base path to assume everything is rooted from
     904      @type base_path: string
     905      @param *args: path components starting from $ZENHOME
     906      @type *args: strings
     907      @return: sanitized path
     908      @rtype: string
     909      """
     910      path = base_path
     911      if args:
     912          # Hugely bizarre (but documented!) behaviour with os.path.join()
     913          # >>> import os.path
     914          # >>> os.path.join( '/blue', 'green' )
     915          # '/blue/green'
     916          # >>> os.path.join( '/blue', '/green' )
     917          # '/green'
     918          # Work around the brain damage...
     919          base = args[0]
     920          if base.startswith( base_path ):
     921              path_args = [ base ] + [a.strip('/') for a in args[1:] if a != '' ]
     922          else:
     923              path_args = [a.strip('/') for a in args if a != '' ]
     924
     925          # Empty strings get thrown out so we may not have anything
     926          if len(path_args) > 0:
     927              # What if the user splits up base_path and passes it in?
     928              pathological_case = os.path.join( *path_args )
     929              if pathological_case.startswith( base_path ):
     930                  pass
     931
     932              elif not base.startswith( base_path ):
     933                  path_args.insert( 0, base_path )
     934
     935              # Note: passing in a list to os.path.join() returns a list,
     936              #       again completely unlike string join()
     937              path = os.path.join( *path_args )
     938
     939      # os.path.join( '/blue', '' ) returns '/blue/' -- egads!
     940      return path.rstrip('/')
     941
     942
     943 -def zenPath(*args):
     944      """
     945      Return a path relative to $ZENHOME specified by joining args.  The path
     946      is not guaranteed to exist on the filesystem.
     947
     948      >>> import os
     949      >>> zenHome = os.environ['ZENHOME']
     950      >>> zenPath() == zenHome
     951      True
     952      >>> zenPath( '' ) == zenHome
     953      True
     954      >>> zenPath('Products') == os.path.join(zenHome, 'Products')
     955      True
     956      >>> zenPath('/Products/') == zenPath('Products')
     957      True
     958      >>>
     959      >>> zenPath('Products', 'foo') == zenPath('Products/foo')
     960      True
     961
     962      # NB: The following is *NOT* true for os.path.join()
     963      >>> zenPath('/Products', '/foo') == zenPath('Products/foo')
     964      True
     965      >>> zenPath(zenPath('Products')) == zenPath('Products')
     966      True
     967      >>> zenPath(zenPath('Products'), 'orange', 'blue' ) == zenPath('Products', 'orange', 'blue' )
     968      True
     969
     970      # Pathological case
     971      # NB: need to expand out the array returned by split()
     972      >>> zenPath() == zenPath( *'/'.split(zenPath()) )
     973      True
     974
     975      @param *args: path components starting from $ZENHOME
     976      @type *args: strings
     977      @todo: determine what the correct behaviour should be if $ZENHOME is a symlink!
     978      """
     979      zenhome = os.environ.get( 'ZENHOME', '' )
     980
     981      path = sane_pathjoin( zenhome, *args )
     982
     983      #test if ZENHOME based path exists and if not try bitrock-style path.
     984      #if neither exists return the ZENHOME-based path
     985      if not os.path.exists(path):
     986          brPath = os.path.realpath(os.path.join(zenhome, '..', 'common'))
     987          testPath = sane_pathjoin(brPath, *args)
     988          if(os.path.exists(testPath)):
     989              path = testPath
     990      return path
     991
     992
     993 -def zopePath(*args):
     994      """
     995      Similar to zenPath() except that this constructs a path based on
     996      ZOPEHOME rather than ZENHOME.  This is useful on the appliance.
     997      If ZOPEHOME is not defined or is empty then return ''.
     998      NOTE: A non-empty return value does not guarantee that the path exists,
     999      just that ZOPEHOME is defined.
    1000
    1001      >>> import os
    1002      >>> zopeHome = os.environ.setdefault('ZOPEHOME', '/something')
    1003      >>> zopePath('bin') == os.path.join(zopeHome, 'bin')
    1004      True
    1005      >>> zopePath(zopePath('bin')) == zopePath('bin')
    1006      True
    1007
    1008      @param *args: path components starting from $ZOPEHOME
    1009      @type *args: strings
    1010      """
    1011      zopehome = os.environ.get('ZOPEHOME', '')
    1012      return sane_pathjoin( zopehome, *args )
    1013
    1014
    1015 -def binPath(fileName):
    1016      """
    1017      Search for the given file in a list of possible locations.  Return
    1018      either the full path to the file or '' if the file was not found.
    1019
    1020      >>> len(binPath('zenoss')) > 0
    1021      True
    1022      >>> len(binPath('zeoup.py')) > 0 # This doesn't exist in Zope 2.12
    1023      False
    1024      >>> len(binPath('check_http')) > 0
    1025      True
    1026      >>> binPath('Idontexistreally') == ''
    1027      True
    1028
    1029      @param fileName: name of executable
    1030      @type fileName: string
    1031      @return: path to file or '' if not found
    1032      @rtype: string
    1033      """
    1034      # bin and libexec are the usual suspect locations.
    1035      # ../common/bin and ../common/libexec are additional options for bitrock
    1036      # $ZOPEHOME/bin is an additional option for appliance
    1037      for path in (zenPath(d, fileName) for d in [
    1038                  'bin', 'libexec', '../common/bin', '../common/libexec'] +
    1039                  os.environ.get('PATH','').split(':')
    1040                   ):
    1041          if os.path.isfile(path):
    1042              return path
    1043      path = zopePath('bin', fileName)
    1044      if os.path.isfile(path):
    1045          return path
    1046      return ''
    1047
    1048
    1049 -def extractPostContent(REQUEST):
    1050      """
    1051      IE puts the POST content in one place in the REQUEST object, and Firefox in
    1052      another. Thus we need to try both.
    1053
    1054      @param REQUEST: Zope REQUEST object
    1055      @type REQUEST: Zope REQUEST object
    1056      @return: POST content
    1057      @rtype: string
    1058      """
    1059      try:
    1060          try:
    1061              # Firefox
    1062              result = REQUEST._file.read()
    1063          except:
    1064              # IE
    1065              result = REQUEST.form.keys()[0]
    1066      except: result = ''
    1067      return result
    1068
    1069
    1070 -def unused(*args):
    1071      """
    1072      A no-op function useful for shutting up pychecker
    1073
    1074      @param *args: arbitrary arguments
    1075      @type *args: objects
    1076      @return: count of the objects
    1077      @rtype: integer
    1078      """
    1079      return len(args)
    1080
    1081
    1082 -def isXmlRpc(REQUEST):
    1083      """
    1084      Did we receive a XML-RPC call?
    1085
    1086      @param REQUEST: Zope REQUEST object
    1087      @type REQUEST: Zope REQUEST object
    1088      @return: True if REQUEST is an XML-RPC call
    1089      @rtype: boolean
    1090      """
    1091      if REQUEST and REQUEST['CONTENT_TYPE'].find('xml') > -1:
    1092          return True
    1093      else:
    1094          return False
    1095
    1096
    1097 -def setupLoggingHeader(context, REQUEST):
    1098      """
    1099      Extract out the 2nd outermost table
    1100
    1101      @param context: Zope object
    1102      @type context: Zope object
    1103      @param REQUEST: Zope REQUEST object
    1104      @type REQUEST: Zope REQUEST object
    1105      @return: response
    1106      @rtype: string
    1107      """
    1108      response = REQUEST.RESPONSE
    1109      dlh = context.discoverLoggingHeader()
    1110      idx = dlh.rindex("</table>")
    1111      dlh = dlh[:idx]
    1112      idx = dlh.rindex("</table>")
    1113      dlh = dlh[:idx]
    1114      response.write(str(dlh[:idx]))
    1115
    1116      return setWebLoggingStream(response)
    1117
    1118
    1119 -def executeCommand(cmd, REQUEST, write=None):
    1120      """
    1121      Execute the command and return the output
    1122
    1123      @param cmd: command to execute
    1124      @type cmd: string
    1125      @param REQUEST: Zope REQUEST object
    1126      @type REQUEST: Zope REQUEST object
    1127      @return: result of executing the command
    1128      @rtype: string
    1129      """
    1130      xmlrpc = isXmlRpc(REQUEST)
    1131      result = 0
    1132      try:
    1133          if REQUEST:
    1134              response = REQUEST.RESPONSE
    1135          else:
    1136              response = sys.stdout
    1137          if write is None:
    1138              def _write(s):
    1139                  response.write(s)
    1140                  response.flush()
    1141              write = _write
    1142          log.info('Executing command: %s' % ' '.join(cmd))
    1143          f = Popen4(cmd)
    1144          while 1:
    1145              s = f.fromchild.readline()
    1146              if not s:
    1147                  break
    1148              elif write:
    1149                  write(s)
    1150              else:
    1151                  log.info(s)
    1152      except (SystemExit, KeyboardInterrupt):
    1153          if xmlrpc: return 1
    1154          raise
    1155      except ZentinelException, e:
    1156          if xmlrpc: return 1
    1157          log.critical(e)
    1158      except:
    1159          if xmlrpc: return 1
    1160          raise
    1161      else:
    1162          result = f.wait()
    1163          result = int(hex(result)[:-2], 16)
    1164      return result
    1165
    1166
    1167 -def ipsort(a, b):
    1168      """
    1169      Compare (cmp()) a + b's IP addresses
    1170      These addresses may contain subnet mask info.
    1171
    1172      @param a: IP address
    1173      @type a: string
    1174      @param b: IP address
    1175      @type b: string
    1176      @return: result of cmp(a.ip,b.ip)
    1177      @rtype: boolean
    1178      """
    1179      # Use 0.0.0.0 instead of blank string
    1180      if not a: a = "0.0.0.0"
    1181      if not b: b = "0.0.0.0"
    1182
    1183      # Strip off netmasks
    1184      a, b = map(lambda x:x.rsplit("/")[0], (a, b))
    1185      return cmp(*map(socket.inet_aton, (a, b)))
    1186
    1187
    1188 -def unsigned(v):
    1189      """
    1190      Convert negative 32-bit values into the 2's complement unsigned value
    1191
    1192      >>> str(unsigned(-1))
    1193      '4294967295'
    1194      >>> unsigned(1)
    1195      1L
    1196      >>> unsigned(1e6)
    1197      1000000L
    1198      >>> unsigned(1e10)
    1199      10000000000L
    1200
    1201      @param v: number
    1202      @type v: negative 32-bit number
    1203      @return: 2's complement unsigned value
    1204      @rtype: unsigned int
    1205      """
    1206      v = long(v)
    1207      if v < 0:
    1208          import ctypes
    1209          return int(ctypes.c_uint32(v).value)
    1210      return v
    1211
    1212
    1213 -def nanToNone(value):
    1214      try:
    1215          if math.isnan(value):
    1216              return None
    1217      except TypeError:
    1218          pass
    1219      return value
    1220
    1221
    1222 -def executeStreamCommand(cmd, writefunc, timeout=30):
    1223      """
    1224      Execute cmd in the shell and send the output to writefunc.
    1225
    1226      @param cmd: command to execute
    1227      @type cmd: string
    1228      @param writefunc: output function
    1229      @type writefunc: function
    1230      @param timeout: maxium number of seconds to wait for the command to execute
    1231      @type timeout: number
    1232      """
    1233      child = popen2.Popen4(cmd)
    1234      flags = fcntl.fcntl(child.fromchild, fcntl.F_GETFL)
    1235      fcntl.fcntl(child.fromchild, fcntl.F_SETFL, flags | os.O_NDELAY)
    1236      pollPeriod = 1
    1237      endtime = time.time() + timeout
    1238      firstPass = True
    1239      while time.time() < endtime and (
    1240          firstPass or child.poll()==-1):
    1241          firstPass = False
    1242          r,w,e = select.select([child.fromchild],[],[],pollPeriod)
    1243          if r:
    1244              t = child.fromchild.read()
    1245              if t:
    1246                  writefunc(t)
    1247      if child.poll()==-1:
    1248          writefunc('Command timed out')
    1249          import signal
    1250          os.kill(child.pid, signal.SIGKILL)
    1251
    1252
    1253 -def monkeypatch(target):
    1254      """
    1255      A decorator to patch the decorated function into the given class.
    1256
    1257          >>> @monkeypatch('Products.ZenModel.DataRoot.DataRoot')
    1258          ... def do_nothing_at_all(self):
    1259          ...     print "I do nothing at all."
    1260          ...
    1261          >>> from Products.ZenModel.DataRoot import DataRoot
    1262          >>> hasattr(DataRoot, 'do_nothing_at_all')
    1263          True
    1264          >>> DataRoot('dummy').do_nothing_at_all()
    1265          I do nothing at all.
    1266
    1267      You can also call the original within the new method
    1268      using a special variable available only locally.
    1269
    1270          >>> @monkeypatch('Products.ZenModel.DataRoot.DataRoot')
    1271          ... def getProductName(self):
    1272          ...     print "Doing something additional."
    1273          ...     return 'core' or original(self)
    1274          ...
    1275          >>> from Products.ZenModel.DataRoot import DataRoot
    1276          >>> DataRoot('dummy').getProductName()
    1277          Doing something additional.
    1278          'core'
    1279
    1280      You can also stack monkeypatches.
    1281
    1282          ### @monkeypatch('Products.ZenModel.System.System')
    1283          ... @monkeypatch('Products.ZenModel.DeviceGroup.DeviceGroup')
    1284          ... @monkeypatch('Products.ZenModel.Location.Location')
    1285          ... def foo(self):
    1286          ...     print "bar!"
    1287          ...
    1288          ### dmd.Systems.foo()
    1289          bar!
    1290          ### dmd.Groups.foo()
    1291          bar!
    1292          ### dmd.Locations.foo()
    1293          bar!
    1294
    1295      @param target: class
    1296      @type target: class object
    1297      @return: decorator function return
    1298      @rtype: function
    1299      """
    1300      if isinstance(target, basestring):
    1301          mod, klass = target.rsplit('.', 1)
    1302          target = importClass(mod, klass)
    1303      def patcher(func):
    1304          original = getattr(target, func.__name__, None)
    1305          if original is None:
    1306              setattr(target, func.__name__, func)
    1307              return func
    1308
    1309          new_globals = copy.copy(func.func_globals)
    1310          new_globals['original'] = original
    1311          new_func = types.FunctionType(func.func_code,
    1312                                        globals=new_globals,
    1313                                        name=func.func_name,
    1314                                        argdefs=func.func_defaults,
    1315                                        closure=func.func_closure)
    1316          setattr(target, func.__name__, new_func)
    1317          return func
    1318      return patcher
    1319
    1320 -def nocache(f):
    1321      """
    1322      Decorator to set headers which force browser to not cache request
    1323
    1324      This is intended to decorate methods of BrowserViews.
    1325
    1326      @param f: class
    1327      @type f: class object
    1328      @return: decorator function return
    1329      @rtype: function
    1330      """
    1331      def inner(self, *args, **kwargs):
    1332          """
    1333          Inner portion of the decorator
    1334
    1335          @param *args: arguments
    1336          @type *args: possible list
    1337          @param **kwargs: keyword arguments
    1338          @type **kwargs: possible list
    1339          @return: decorator function return
    1340          @rtype: function
    1341          """
    1342          self.request.response.setHeader('Cache-Control', 'no-cache, must-revalidate')
    1343          self.request.response.setHeader('Pragma', 'no-cache')
    1344          self.request.response.setHeader('Expires', 'Sat, 13 May 2006 18:02:00 GMT')
    1345          # Get rid of kw used to prevent browser caching
    1346          kwargs.pop('_dc', None)
    1347          return f(self, *args, **kwargs)
    1348
    1349      return inner
    1350
    1351 -def formreq(f):
    1352      """
    1353      Decorator to pass in request.form information as arguments to a method.
    1354
    1355      These are intended to decorate methods of BrowserViews.
    1356
    1357      @param f: class
    1358      @type f: class object
    1359      @return: decorator function return
    1360      @rtype: function
    1361      """
    1362      def inner(self, *args, **kwargs):
    1363          """
    1364          Inner portion of the decorator
    1365
    1366          @param *args: arguments
    1367          @type *args: possible list
    1368          @param **kwargs: keyword arguments
    1369          @type **kwargs: possible list
    1370          @return: decorator function return
    1371          @rtype: function
    1372          """
    1373          if self.request.REQUEST_METHOD=='POST':
    1374              content = extractPostContent(self.request)
    1375              try:
    1376                  args += (unjson(content),)
    1377              except ValueError:
    1378                  kwargs.update(self.request.form)
    1379          else:
    1380              kwargs.update(self.request.form)
    1381          # Get rid of useless Zope thing that appears when no querystring
    1382          kwargs.pop('-C', None)
    1383          # Get rid of kw used to prevent browser caching
    1384          kwargs.pop('_dc', None)
    1385          return f(self, *args, **kwargs)
    1386
    1387      return inner
    1388
    1389
    1390 -class Singleton(type):
    1391      """
    1392      Metaclass that ensures only a single instance of a class is ever created.
    1393
    1394      This is accomplished by storing the first instance created as an attribute
    1395      of the class itself, then checking that attribute for later constructor
    1396      calls.
    1397      """
    1398 -    def __init__(cls, *args, **kwargs):
    1399          super(Singleton, cls).__init__(*args, **kwargs)
    1400          cls._singleton_instance = None
    1401
    1402 -    def __call__(cls, *args, **kwargs):
    1403          if cls._singleton_instance is None:
    1404              cls._singleton_instance = super(
    1405                  Singleton, cls).__call__(*args, **kwargs)
    1406          return cls._singleton_instance
    1407
    1408
    1409 -def readable_time(seconds, precision=1):
    1410      """
    1411      Convert some number of seconds into a human-readable string.
    1412
    1413      @param t: The number of seconds to convert
    1414      @type t: int
    1415      @param precision: The maximum number of time units to include.
    1416      @type t: int
    1417      @rtype: str
    1418
    1419          >>> readable_time(None)
    1420          '0 seconds'
    1421          >>> readable_time(0)
    1422          '0 seconds'
    1423          >>> readable_time(0.12)
    1424          '0 seconds'
    1425          >>> readable_time(1)
    1426          '1 second'
    1427          >>> readable_time(1.5)
    1428          '1 second'
    1429          >>> readable_time(60)
    1430          '1 minute'
    1431          >>> readable_time(60*60*3+12)
    1432          '3 hours'
    1433          >>> readable_time(60*60*3+12, 2)
    1434          '3 hours 12 seconds'
    1435
    1436      """
    1437      if seconds is None:
    1438          return '0 seconds'
    1439      remaining = abs(seconds)
    1440      if remaining < 1:
    1441          return '0 seconds'
    1442
    1443      names = ('year', 'month', 'week', 'day', 'hour', 'minute', 'second')
    1444      mults = (60*60*24*365, 60*60*24*30, 60*60*24*7, 60*60*24, 60*60, 60, 1)
    1445      result = []
    1446      for name, div in zip(names, mults):
    1447          num = Decimal(str(math.floor(remaining/div)))
    1448          remaining -= int(num)*div
    1449          num = int(num)
    1450          if num:
    1451              result.append('%d %s%s' %(num, name, num>1 and 's' or ''))
    1452          if len(result)==precision:
    1453              break
    1454      return ' '.join(result)
    1455
    1456
    1457 -def relative_time(t, precision=1, cmptime=None):
    1458      """
    1459      Return a human-readable string describing time relative to C{cmptime}
    1460      (defaulted to now).
    1461
    1462      @param t: The time to convert, in seconds since the epoch.
    1463      @type t: int
    1464      @param precision: The maximum number of time units to include.
    1465      @type t: int
    1466      @param cmptime: The time from which to compute the difference, in seconds
    1467      since the epoch
    1468      @type cmptime: int
    1469      @rtype: str
    1470
    1471          >>> relative_time(time.time() - 60*10)
    1472          '10 minutes ago'
    1473          >>> relative_time(time.time() - 60*10-3, precision=2)
    1474          '10 minutes 3 seconds ago'
    1475          >>> relative_time(time.time() - 60*60*24*10, precision=2)
    1476          '1 week 3 days ago'
    1477          >>> relative_time(time.time() - 60*60*24*365-1, precision=2)
    1478          '1 year 1 second ago'
    1479          >>> relative_time(time.time() + 1 + 60*60*24*7*2) # Add 1 for rounding
    1480          'in 2 weeks'
    1481
    1482      """
    1483      if cmptime is None:
    1484          cmptime = time.time()
    1485      seconds = Decimal(str(t - cmptime))
    1486      result = readable_time(seconds, precision)
    1487      if seconds < 0:
    1488          result += ' ago'
    1489      else:
    1490          result = 'in ' + result
    1491      return result
    1492
    1493
    1494 -def is_browser_connection_open(request):
    1495      """
    1496      Check to see if the TCP connection to the browser is still open.
    1497
    1498      This might be used to interrupt an infinite while loop, which would
    1499      preclude the thread from being destroyed even though the connection has
    1500      been closed.
    1501      """
    1502      creation_time = request.environ['channel.creation_time']
    1503      for cnxn in asyncore.socket_map.values():
    1504          if (isinstance(cnxn, zhttp_channel) and
    1505              cnxn.creation_time==creation_time):
    1506              return True
    1507      return False
    1508
    1509
    1510  EXIT_CODE_MAPPING = {
    1511      0:'Success',
    1512      1:'General error',
    1513      2:'Misuse of shell builtins',
    1514      126:'Command invoked cannot execute, permissions problem or command is not an executable',
    1515      127:'Command not found',
    1516      128:'Invalid argument to exit, exit takes only integers in the range 0-255',
    1517      130:'Fatal error signal: 2, Command terminated by Control-C'
    1518  }
    1519
    1520 -def getExitMessage(exitCode):
    1521      """
    1522      Return a nice exit message that corresponds to the given exit status code
    1523
    1524      @param exitCode: process exit code
    1525      @type exitCode: integer
    1526      @return: human-readable version of the exit code
    1527      @rtype: string
    1528      """
    1529      if exitCode in EXIT_CODE_MAPPING.keys():
    1530          return EXIT_CODE_MAPPING[exitCode]
    1531      elif exitCode >= 255:
    1532          return 'Exit status out of range, exit takes only integer arguments in the range 0-255'
    1533      elif exitCode > 128:
    1534          return 'Fatal error signal: %s' % (exitCode-128)
    1535      return 'Unknown error code: %s' % exitCode
    1536
    1537
    1538 -def set_context(ob):
    1539      """
    1540      Wrap an object in a REQUEST context.
    1541      """
    1542      from ZPublisher.HTTPRequest import HTTPRequest
    1543      from ZPublisher.HTTPResponse import HTTPResponse
    1544      from ZPublisher.BaseRequest import RequestContainer
    1545      resp = HTTPResponse(stdout=None)
    1546      env = {
    1547          'SERVER_NAME':'localhost',
    1548          'SERVER_PORT':'8080',
    1549          'REQUEST_METHOD':'GET'
    1550          }
    1551      req = HTTPRequest(None, env, resp)
    1552      return ob.__of__(RequestContainer(REQUEST = req))
    1553
    1554 -def dumpCallbacks(deferred):
    1555      """
    1556      Dump the callback chain of a Twisted Deferred object. The chain will be
    1557      displayed on standard output.
    1558
    1559      @param deferred: the twisted Deferred object to dump
    1560      @type deferred: a Deferred object
    1561      """
    1562      callbacks = deferred.callbacks
    1563      print "%-39s %-39s" % ("Callbacks", "Errbacks")
    1564      print "%-39s %-39s" % ("-" * 39, "-" * 39)
    1565      for cbs in callbacks:
    1566          callback = cbs[0][0]
    1567          callbackName = "%s.%s" % (callback.__module__, callback.func_name)
    1568          errback = cbs[1][0]
    1569          errbackName = "%s.%s" % (errback.__module__, errback.func_name)
    1570          print "%-39.39s %-39.39s" % (callbackName, errbackName)
    1571
    1572
    1573 -def getObjectsFromCatalog(catalog, query=None, log=None):
    1574      """
    1575      Generator that can be used to load all objects of out a catalog and skip
    1576      any objects that are no longer able to be loaded.
    1577      """
    1578
    1579      for brain in catalog(query):
    1580          try:
    1581              ob = brain.getObject()
    1582              yield ob
    1583          except (NotFound, KeyError, AttributeError):
    1584              if log:
    1585                  log.warn("Stale %s record: %s", catalog.id, brain.getPath())
    1586
    1587
    1588 -def load_config_override(file, package=None, execute=True):
    1589      """Load an additional ZCML file into the context, overriding others.
    1590
    1591      Use with extreme care.
    1592      """
    1593      from zope.configuration import xmlconfig
    1594      from Products.Five.zcml import _context
    1595      xmlconfig.includeOverrides(_context, file, package=package)
    1596      if execute:
    1597          _context.execute_actions()
    1598
    1599
    1600 -def rrd_daemon_running():
    1601      sockfile = zenPath('var', 'rrdcached.sock')
    1602      if os.path.exists(sockfile):
    1603          return sockfile
    1604
    1605  @contextlib.contextmanager
    1606 -def get_temp_dir():
    1607      import tempfile
    1608      import shutil
    1609
    1610      dirname = tempfile.mkdtemp()
    1611      try:
    1612          yield dirname
    1613      finally:
    1614          shutil.rmtree(dirname)
    1615
    1616 -def getDefaultZopeUrl():
    1617      """
    1618      Returns the default Zope URL.
    1619      """
    1620      return 'http://%s:%d' % (socket.getfqdn(), 8080)
    1621