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