1
2
3
4
5
6
7
8
9
10
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 from decimal import Decimal
33 from sets import Set
34 import asyncore
35 log = logging.getLogger("zen.Utils")
36
37 from popen2 import Popen4
38
39 from Acquisition import aq_base
40 from zExceptions import NotFound
41 from AccessControl import getSecurityManager
42 from AccessControl import Unauthorized
43 from AccessControl.ZopeGuards import guarded_getattr
44 from Acquisition import aq_inner, aq_parent
45 from ZServer.HTTPServer import zhttp_channel
46
47 from Products.ZenUtils.Exceptions import ZenPathError, ZentinelException
48 from Products.ZenUtils.json import json as _json, unjson
49
75
76
78 """
79 Setup logging to log to a browser using a request object.
80
81 @param stream: IO stream
82 @type stream: stream class
83 @return: logging handler
84 @rtype: logging handler
85 """
86 handler = logging.StreamHandler(stream)
87 handler.setFormatter(HtmlFormatter())
88 rlog = logging.getLogger()
89 rlog.addHandler(handler)
90 rlog.setLevel(logging.ERROR)
91 zlog = logging.getLogger("zen")
92 zlog.setLevel(logging.INFO)
93 return handler
94
95
97 """
98 Clear our web logger.
99
100 @param handler: logging handler
101 @type handler: logging handler
102 """
103 rlog = logging.getLogger()
104 rlog.removeHandler(handler)
105
106
108 """
109 Convert a number to its human-readable form. ie: 4GB, 4MB, etc.
110
111 >>> convToUnits() # Don't do this!
112 '0.0B'
113 >>> convToUnits(None) # Don't do this!
114 ''
115 >>> convToUnits(123456789)
116 '117.7MB'
117 >>> convToUnits(123456789, 1000, "Hz")
118 '123.5MHz'
119
120 @param number: base number
121 @type number: number
122 @param divby: divisor to use to convert to appropriate prefix
123 @type divby: number
124 @param unitstr: base unit of the number
125 @type unitstr: string
126 @return: number with appropriate units
127 @rtype: string
128 """
129 units = map(lambda x:x + unitstr, ('','K','M','G','T','P'))
130 try:
131 numb = float(number)
132 except:
133 return ''
134
135 sign = 1
136 if numb < 0:
137 numb = abs(numb)
138 sign = -1
139 for unit in units:
140 if numb < divby: break
141 numb /= divby
142 return "%.1f%s" % (numb * sign, unit)
143
144
146 """
147 Walk a series of to one rels collecting collectname into collect
148
149 @param obj: object inside of Zope
150 @type obj: object
151 @param toonerel: a to-one relationship object
152 @type toonerel: toonerel object
153 @param collect: object list
154 @type collect: list
155 @param collectname: name inside of the to-one relation object
156 @type collectname: string
157 @return: list of objects
158 @rtype: list
159 """
160
161 value = getattr(aq_base(obj), collectname, None)
162 if value:
163 collect.append(value)
164 rel = getattr(aq_base(obj), toonerel, None)
165 if callable(rel):
166 nobj = rel()
167 if nobj:
168 return travAndColl(nobj, toonerel, collect, collectname)
169 return collect
170
171
173 """
174 Get a Zope object by its path (e.g. '/Devices/Server/Linux').
175 Mostly a stripdown of unrestrictedTraverse method from Zope 2.8.8.
176
177 @param base: base part of a path
178 @type base: string
179 @param path: path to an object inside of the DMD
180 @type path: string
181 @param restricted: flag indicated whether to use securityManager
182 @type restricted: integer
183 @return: object pointed to by the path
184 @rtype: object
185 """
186 if not path:
187 return base
188
189 _getattr = getattr
190 _none = None
191 marker = object()
192
193 if isinstance(path, str):
194
195 path = path.split('/')
196 else:
197 path = list(path)
198
199 REQUEST = {'TraversalRequestNameStack': path}
200 path.reverse()
201 path_pop=path.pop
202
203 if len(path) > 1 and not path[0]:
204
205 path.pop(0)
206
207 if restricted:
208 securityManager = getSecurityManager()
209 else:
210 securityManager = _none
211
212 if not path[-1]:
213
214 path_pop()
215 base = base.getPhysicalRoot()
216 if (restricted
217 and not securityManager.validate(None, None, None, base)):
218 raise Unauthorized( base )
219
220 obj = base
221 while path:
222 name = path_pop()
223
224 if name[0] == '_':
225
226 raise NotFound( name )
227
228 if name == '..':
229 next = aq_parent(obj)
230 if next is not _none:
231 if restricted and not securityManager.validate(
232 obj, obj,name, next):
233 raise Unauthorized( name )
234 obj = next
235 continue
236
237 bobo_traverse = _getattr(obj, '__bobo_traverse__', _none)
238 if bobo_traverse is not _none:
239 next = bobo_traverse(REQUEST, name)
240 if restricted:
241 if aq_base(next) is not next:
242
243
244 container = aq_parent(aq_inner(next))
245 elif _getattr(next, 'im_self', _none) is not _none:
246
247
248 container = next.im_self
249 elif _getattr(aq_base(obj), name, marker) == next:
250
251
252 container = obj
253 else:
254
255 container = _none
256 try:
257 validated = securityManager.validate(
258 obj, container, name, next)
259 except Unauthorized:
260
261
262
263
264
265 validated = 0
266 if container is _none and \
267 guarded_getattr(obj, name, marker) is next:
268 validated = 1
269 if not validated:
270 raise Unauthorized( name )
271 else:
272 if restricted:
273 next = guarded_getattr(obj, name, marker)
274 else:
275 next = _getattr(obj, name, marker)
276
277
278
279
280
281
282
283
284 if next is marker:
285 try:
286 next=obj[name]
287 except AttributeError:
288
289
290 raise NotFound( name )
291 if restricted and not securityManager.validate(
292 obj, obj, _none, next):
293 raise Unauthorized( name )
294 obj = next
295 return obj
296
297
298
300 """
301 Perform issubclass using class name as string
302
303 @param myclass: generic object
304 @type myclass: object
305 @param className: name of a class
306 @type className: string
307 @return: the value 1 if found or None
308 @rtype: integer or None
309 """
310 if myclass.__name__ == className:
311 return 1
312 for mycl in myclass.__bases__:
313 if checkClass(mycl, className):
314 return 1
315
316
318 """
319 look in sys.modules for our class
320
321 @param productName: object in Products
322 @type productName: string
323 @param classname: class name
324 @type classname: string
325 @return: object at the classname in Products
326 @rtype: object or None
327 """
328 if sys.modules.has_key(productName):
329 mod = sys.modules[productName]
330
331 elif sys.modules.has_key("Products."+productName):
332 mod = sys.modules["Products."+productName]
333
334 else:
335 return None
336
337 if not classname:
338 classname = productName.split('.')[-1]
339
340 return getattr(mod,classname)
341
342
344 """
345 Import a class from the module given.
346
347 @param modulePath: path to module in sys.modules
348 @type modulePath: string
349 @param classname: name of a class
350 @type classname: string
351 @return: the class in the module
352 @rtype: class
353 """
354 try:
355 if not classname: classname = modulePath.split(".")[-1]
356 try:
357 __import__(modulePath, globals(), locals(), classname)
358 mod = sys.modules[modulePath]
359 except (ValueError, ImportError, KeyError), ex:
360 raise ex
361
362 return getattr(mod, classname)
363 except AttributeError:
364 raise ImportError("Failed while importing class %s from module %s" % (
365 classname, modulePath))
366
367
369 """
370 Take the trailing \x00 off the end of a string
371
372 @param unitstr: sample string
373 @type unitstr: string
374 @return: cleaned string
375 @rtype: string
376 """
377 if type(value) in types.StringTypes:
378 value = value.split('\0')[0]
379 return value
380
381
382 -def getSubObjects(base, filter=None, descend=None, retobjs=None):
383 """
384 Do a depth-first search looking for objects that the function filter
385 returns as True. If descend is passed it will check to see if we
386 should keep going down or not
387
388 @param base: base object to start search
389 @type base: object
390 @param filter: filter to apply to each object to determine if it gets added to the returned list
391 @type filter: function or None
392 @param descend: function to apply to each object to determine whether or not to continue searching
393 @type descend: function or None
394 @param retobjs: list of objects found
395 @type retobjs: list
396 @return: list of objects found
397 @rtype: list
398 """
399 if not retobjs: retobjs = []
400 for obj in base.objectValues():
401 if not filter or filter(obj):
402 retobjs.append(obj)
403 if not descend or descend(obj):
404 retobjs = getSubObjects(obj, filter, descend, retobjs)
405 return retobjs
406
407
409 """
410 Do a depth-first search looking for objects that the function filter
411 returns as True. If descend is passed it will check to see if we
412 should keep going down or not.
413
414 This is a Python iterable.
415
416 @param base: base object to start search
417 @type base: object
418 @param filter: filter to apply to each object to determine if it gets added to the returned list
419 @type filter: function or None
420 @param descend: function to apply to each object to determine whether or not to continue searching
421 @type descend: function or None
422 @param memo: dictionary of objects found (unused)
423 @type memo: dictionary
424 @return: list of objects found
425 @rtype: list
426 """
427 from Products.ZenRelations.RelationshipManager \
428 import RelationshipManager
429 if base.meta_type == "To One Relationship":
430 objs = [base.obj]
431 else:
432 objs = base.objectValues()
433 for obj in objs:
434 if (isinstance(obj, RelationshipManager) and
435 not obj.getPrimaryDmdId().startswith(base.getPrimaryDmdId())):
436 continue
437 if not filter or filter(obj):
438 yield obj
439 if not descend or descend(obj):
440 for x in getSubObjectsMemo(obj, filter, descend, memo):
441 yield x
442
443
475
476 def filter(obj):
477 """
478 Filter function to decide whether it's an object we
479 want to know about or not.
480
481 @param obj: object
482 @type obj: object
483 @return: True if we want to keep it
484 @rtype: boolean
485 """
486 return isinstance(obj, ZenModelRM) and obj.id != "dmd"
487
488 return getSubObjectsMemo(base, filter=filter, descend=descend)
489
490
492 """
493 Split a zen path and clean up any blanks or bogus spaces in it
494
495 @param pathstring: a path inside of ZENHOME
496 @type pathstring: string
497 @return: a path
498 @rtype: string
499 """
500 path = pathstring.split("/")
501 path = filter(lambda x: x, path)
502 path = map(lambda x: x.strip(), path)
503 return path
504
505
506
508 """
509 Build a zenpath in its string form
510
511 @param pathstring: a path
512 @type pathstring: string
513 @return: a path
514 @rtype: string
515 """
516 return "/" + "/".join(pathar)
517
518
520 """
521 Create a hierarchy object from its path we use relpath to skip down
522 any missing relations in the path and factory is the constructor for
523 this object.
524
525 @param root: root from which to start
526 @type root: object
527 @param name: path to object
528 @type name: string
529 @param factory: factory object to create
530 @type factory: factory object
531 @param relpath: relationship within which we will recurse as objects are created, if any
532 @type relpath: object
533 @param llog: unused
534 @type llog: object
535 @return: root object of a hierarchy
536 @rtype: object
537 """
538 unused(llog)
539 rootName = root.id
540 for id in zenpathsplit(name):
541 if id == rootName: continue
542 if id == relpath or getattr(aq_base(root), relpath, False):
543 root = getattr(root, relpath)
544 if not getattr(aq_base(root), id, False):
545 if id == relpath:
546 raise AttributeError("relpath %s not found" % relpath)
547 log.debug("Creating object with id %s in object %s",id,root.getId())
548 newobj = factory(id)
549 root._setObject(id, newobj)
550 root = getattr(root, id)
551
552 return root
553
554
556 """
557 Return an object using its path relations are optional in the path.
558
559 @param root: root from which to start
560 @type root: object
561 @param name: path to object
562 @type name: string
563 @param relpath: relationship within which we will recurse as objects are created, if any
564 @type relpath: object
565 @return: root object of a hierarchy
566 @rtype: object
567 """
568 for id in zenpathsplit(name):
569 if id == relpath or getattr(aq_base(root), relpath, False):
570 root = getattr(root, relpath)
571 if not getattr(root, id, False):
572 raise ZenPathError("Path %s id %s not found on object %s" %
573 (name, id, root.getPrimaryId()))
574 root = getattr(root, id, None)
575
576 return root
577
578
579
581 """
582 Add the username and password to a url in the form
583 http://username:password@host/path
584
585 @param username: username
586 @type username: string
587 @param password: password
588 @type password: string
589 @param url: base URL to add username/password info
590 @type url: string
591 @return: URL with auth information incorporated
592 @rtype: string
593 """
594 urlar = url.split('/')
595 if not username or not password or urlar[2].find('@') > -1:
596 return url
597 urlar[2] = "%s:%s@%s" % (username, password, urlar[2])
598 return "/".join(urlar)
599
600
601
603 """
604 Make an id with valid url characters. Subs [^a-zA-Z0-9-_,.$\(\) ]
605 with subchar. If id then starts with subchar it is removed.
606
607 @param id: user-supplied id
608 @type id: string
609 @return: valid id
610 @rtype: string
611 """
612 _prepId = re.compile(r'[^a-zA-Z0-9-_,.$\(\) ]').sub
613 _cleanend = re.compile(r"%s+$" % subchar).sub
614 if id is None:
615 raise ValueError('Ids can not be None')
616 if type(id) not in types.StringTypes:
617 id = str(id)
618 id = _prepId(subchar, id)
619 while id.startswith(subchar):
620 if len(id) > 1: id = id[1:]
621 else: id = "-"
622 id = _cleanend("",id)
623 id = id.strip()
624 return str(id)
625
626
627 -def sendEmail(emsg, host, port=25, usetls=0, usr='', pwd=''):
628 """
629 Send an email. Return a tuple:
630 (sucess, message) where sucess is True or False.
631
632 @param emsg: message to send
633 @type emsg: string
634 @param host: name of e-mail server
635 @type host: string
636 @param port: port number to communicate to the e-mail server
637 @type port: integer
638 @param usetls: boolean-type integer to specify whether to use TLS
639 @type usetls: integer
640 @param usr: username for TLS
641 @type usr: string
642 @param pwd: password for TLS
643 @type pwd: string
644 @return: (sucess, message) where sucess is True or False.
645 @rtype: tuple
646 """
647 import smtplib
648 fromaddr = emsg['From']
649 toaddr = emsg['To'].split(', ')
650 try:
651 server = smtplib.SMTP(host, port)
652 if usetls:
653 server.ehlo()
654 server.starttls()
655 server.ehlo()
656 if len(usr): server.login(usr, pwd)
657 server.sendmail(fromaddr, toaddr, emsg.as_string())
658
659
660 try: server.quit()
661 except: pass
662 except (smtplib.SMTPException, socket.error):
663 result = (False, '%s - %s' % tuple(sys.exc_info()[:2]))
664 else:
665 result = (True, '')
666 return result
667
668
669 -def sendPage(recipient, msg, pageCommand):
670 """
671 Send a page. Return a tuple: (success, message) where
672 sucess is True or False.
673
674 @param recipient: name to where a page should be sent
675 @type recipient: string
676 @param msg: message to send
677 @type msg: string
678 @param pageCommand: command that will send a page
679 @type pageCommand: string
680 @return: (sucess, message) where sucess is True or False.
681 @rtype: tuple
682 """
683 import subprocess
684 env = dict(os.environ)
685 env["RECIPIENT"] = recipient
686 p = subprocess.Popen(pageCommand,
687 stdin=subprocess.PIPE,
688 stdout=subprocess.PIPE,
689 shell=True,
690 env=env)
691 p.stdin.write(msg)
692 p.stdin.close()
693 response = p.stdout.read()
694 return (not p.wait(), response)
695
696
698 """
699 Convert a string using the decoding found in zCollectorDecoding
700
701 @param context: Zope object
702 @type context: object
703 @param value: input string
704 @type value: string
705 @return: converted string
706 @rtype: string
707 """
708 if type(value) == type(''):
709 decoding = getattr(context, 'zCollectorDecoding', 'latin-1')
710 value = value.decode(decoding)
711 return value
712
713
715 """
716 Test to see if an IP should not be included in the network map.
717 Uses the zLocalIpAddresses to decide.
718
719 @param context: Zope object
720 @type context: object
721 @param ip: IP address
722 @type ip: string
723 @return: regular expression match or None (if not found)
724 @rtype: re match object
725 """
726 return re.search(getattr(context, 'zLocalIpAddresses', '^$'), ip)
727
729 """
730 Test to see if an interface should not be included in the network map.
731 Uses the zLocalInterfaceNames to decide.
732
733 @param context: Zope object
734 @type context: object
735 @param intname: network interface name
736 @type intname: string
737 @return: regular expression match or None (if not found)
738 @rtype: re match object
739 """
740 return re.search(getattr(context, 'zLocalInterfaceNames', '^$'), intname)
741
742
744 """
745 Check to see if any of an object's base classes
746 are in a list of class names. Like isinstance(),
747 but without requiring a class to compare against.
748
749 @param obj: object
750 @type obj: object
751 @param classnames: class names
752 @type classnames: list of strings
753 @return: result of the comparison
754 @rtype: boolean
755 """
756 finalnames = Set()
757 x = [obj.__class__]
758 while x:
759 thisclass = x.pop()
760 x.extend(thisclass.__bases__)
761 finalnames.add(thisclass.__name__)
762 return bool( Set(classnames).intersection(finalnames) )
763
764
765 -def resequence(context, objects, seqmap, origseq, REQUEST):
766 """
767 Resequence a seqmap
768
769 @param context: Zope object
770 @type context: object
771 @param objects: objects
772 @type objects: list
773 @param seqmap: sequence map
774 @type seqmap: list
775 @param origseq: sequence map
776 @type origseq: list
777 @param REQUEST: Zope REQUEST object
778 @type REQUEST: Zope REQUEST object
779 @return:
780 @rtype: string
781 """
782 if seqmap and origseq:
783 try:
784 origseq = tuple([long(s) for s in origseq])
785 seqmap = tuple([float(s) for s in seqmap])
786 except ValueError:
787 origseq = ()
788 seqmap = ()
789 orig = dict([(o.sequence, o) for o in objects])
790 if origseq:
791 for oldSeq, newSeq in zip(origseq, seqmap):
792 orig[oldSeq].sequence = newSeq
793 def sort(x):
794 """
795 @param x: unordered sequence items
796 @type x: list
797 @return: ordered sequence items
798 @rtype: list
799 """
800 x = list(x)
801 x.sort(lambda a, b: cmp(a.sequence, b.sequence))
802 return x
803
804 for i, obj in enumerate(sort(objects)):
805 obj.sequence = i
806
807 if REQUEST:
808 return context.callZenScreen(REQUEST)
809
810
812 """
813 Prune out objects
814
815 @param dmd: Device Management Database
816 @type dmd: DMD object
817 """
818 ps = dmd.getPhysicalRoot().zport.portal_skins
819 layers = ps._objects
820 layers = filter(lambda x:getattr(ps, x['id'], False), layers)
821 ps._objects = tuple(layers)
822
823
825 """
826 Convert edges to an XML file
827
828 @param edges: edges
829 @type edges: list
830 @return: XML-formatted string
831 @rtype: string
832 """
833 nodet = '<Node id="%s" prop="%s" icon="%s" color="%s"/>'
834 edget = '<Edge fromID="%s" toID="%s"/>'
835 xmlels = ['<Start name="%s" url="%s"/>' % start]
836 nodeels = []
837 edgeels = []
838 for a, b in edges:
839 node1 = nodet % (a[0], a[0], a[1], a[2])
840 node2 = nodet % (b[0], b[0], b[1], b[2])
841 edge1 = edget % (a[0], b[0])
842 if node1 not in nodeels: nodeels.append(node1)
843 if node2 not in nodeels: nodeels.append(node2)
844 if edge1 not in edgeels: edgeels.append(edge1)
845
846 xmlels.extend(nodeels)
847 xmlels.extend(edgeels)
848 xmldoc = "<graph>%s</graph>" % ''.join(list(xmlels))
849
850 return xmldoc
851
852
854 """
855 Joins paths in a saner manner than os.path.join()
856
857 @param base_path: base path to assume everything is rooted from
858 @type base_path: string
859 @param *args: path components starting from $ZENHOME
860 @type *args: strings
861 @return: sanitized path
862 @rtype: string
863 """
864 path = base_path
865 if args:
866
867
868
869
870
871
872
873 base = args[0]
874 if base.startswith( base_path ):
875 path_args = [ base ] + [a.strip('/') for a in args[1:] if a != '' ]
876 else:
877 path_args = [a.strip('/') for a in args if a != '' ]
878
879
880 if len(path_args) > 0:
881
882 pathological_case = os.path.join( *path_args )
883 if pathological_case.startswith( base_path ):
884 pass
885
886 elif not base.startswith( base_path ):
887 path_args.insert( 0, base_path )
888
889
890
891 path = os.path.join( *path_args )
892
893
894 return path.rstrip('/')
895
896
898 """
899 Return a path relative to $ZENHOME specified by joining args. The path
900 is not guaranteed to exist on the filesystem.
901
902 >>> import os
903 >>> zenHome = os.environ['ZENHOME']
904 >>> zenPath() == zenHome
905 True
906 >>> zenPath( '' ) == zenHome
907 True
908 >>> zenPath('Products') == os.path.join(zenHome, 'Products')
909 True
910 >>> zenPath('/Products/') == zenPath('Products')
911 True
912 >>>
913 >>> zenPath('Products', 'foo') == zenPath('Products/foo')
914 True
915
916 # NB: The following is *NOT* true for os.path.join()
917 >>> zenPath('/Products', '/foo') == zenPath('Products/foo')
918 True
919 >>> zenPath(zenPath('Products')) == zenPath('Products')
920 True
921 >>> zenPath(zenPath('Products'), 'orange', 'blue' ) == zenPath('Products', 'orange', 'blue' )
922 True
923
924 # Pathological case
925 # NB: need to expand out the array returned by split()
926 >>> zenPath() == zenPath( *'/'.split(zenPath()) )
927 True
928
929 @param *args: path components starting from $ZENHOME
930 @type *args: strings
931 @todo: determine what the correct behaviour should be if $ZENHOME is a symlink!
932 """
933 zenhome = os.environ.get( 'ZENHOME', '' )
934
935 path = sane_pathjoin( zenhome, *args )
936
937
938
939 if not os.path.exists(path):
940 brPath = os.path.realpath(os.path.join(zenhome, '..', 'common'))
941 testPath = sane_pathjoin(brPath, *args)
942 if(os.path.exists(testPath)):
943 path = testPath
944 return path
945
946
948 """
949 Similar to zenPath() except that this constructs a path based on
950 ZOPEHOME rather than ZENHOME. This is useful on the appliance.
951 If ZOPEHOME is not defined or is empty then return ''.
952 NOTE: A non-empty return value does not guarantee that the path exists,
953 just that ZOPEHOME is defined.
954
955 >>> import os
956 >>> zopeHome = os.environ.setdefault('ZOPEHOME', '/something')
957 >>> zopePath('bin') == os.path.join(zopeHome, 'bin')
958 True
959 >>> zopePath(zopePath('bin')) == zopePath('bin')
960 True
961
962 @param *args: path components starting from $ZOPEHOME
963 @type *args: strings
964 """
965 zopehome = os.environ.get('ZOPEHOME', '')
966 return sane_pathjoin( zopehome, *args )
967
968
970 """
971 Search for the given file in a list of possible locations. Return
972 either the full path to the file or '' if the file was not found.
973
974 >>> len(binPath('zenoss')) > 0
975 True
976 >>> len(binPath('zeoup.py')) > 0
977 True
978 >>> len(binPath('check_http')) > 0
979 True
980 >>> binPath('Idontexistreally') == ''
981 True
982
983 @param fileName: name of executable
984 @type fileName: string
985 @return: path to file or '' if not found
986 @rtype: string
987 """
988
989
990
991 for path in (zenPath(d, fileName) for d in (
992 'bin', 'libexec', '../common/bin', '../common/libexec')):
993 if os.path.isfile(path):
994 return path
995 path = zopePath('bin', fileName)
996 if os.path.isfile(path):
997 return path
998 return ''
999
1000
1002 """
1003 IE puts the POST content in one place in the REQUEST object, and Firefox in
1004 another. Thus we need to try both.
1005
1006 @param REQUEST: Zope REQUEST object
1007 @type REQUEST: Zope REQUEST object
1008 @return: POST content
1009 @rtype: string
1010 """
1011 try:
1012 try:
1013
1014 result = REQUEST._file.read()
1015 except:
1016
1017 result = REQUEST.form.keys()[0]
1018 except: result = ''
1019 return result
1020
1021
1023 """
1024 A no-op function useful for shutting up pychecker
1025
1026 @param *args: arbitrary arguments
1027 @type *args: objects
1028 @return: count of the objects
1029 @rtype: integer
1030 """
1031 return len(args)
1032
1033
1035 """
1036 Did we receive a XML-RPC call?
1037
1038 @param REQUEST: Zope REQUEST object
1039 @type REQUEST: Zope REQUEST object
1040 @return: True if REQUEST is an XML-RPC call
1041 @rtype: boolean
1042 """
1043 if REQUEST and REQUEST['CONTENT_TYPE'].find('xml') > -1:
1044 return True
1045 else:
1046 return False
1047
1048
1050 """
1051 Extract out the 2nd outermost table
1052
1053 @param context: Zope object
1054 @type context: Zope object
1055 @param REQUEST: Zope REQUEST object
1056 @type REQUEST: Zope REQUEST object
1057 @return: response
1058 @rtype: string
1059 """
1060 response = REQUEST.RESPONSE
1061 dlh = context.discoverLoggingHeader()
1062 idx = dlh.rindex("</table>")
1063 dlh = dlh[:idx]
1064 idx = dlh.rindex("</table>")
1065 dlh = dlh[:idx]
1066 response.write(str(dlh[:idx]))
1067
1068 return setWebLoggingStream(response)
1069
1070
1072 """
1073 Execute the command and return the output
1074
1075 @param cmd: command to execute
1076 @type cmd: string
1077 @param REQUEST: Zope REQUEST object
1078 @type REQUEST: Zope REQUEST object
1079 @return: result of executing the command
1080 @rtype: string
1081 """
1082 xmlrpc = isXmlRpc(REQUEST)
1083 result = 0
1084 try:
1085 if REQUEST:
1086 response = REQUEST.RESPONSE
1087 else:
1088 response = sys.stdout
1089 log.info('Executing command: %s' % ' '.join(cmd))
1090 f = Popen4(cmd)
1091 while 1:
1092 s = f.fromchild.readline()
1093 if not s:
1094 break
1095 elif response:
1096 response.write(s)
1097 response.flush()
1098 else:
1099 log.info(s)
1100 except (SystemExit, KeyboardInterrupt):
1101 if xmlrpc: return 1
1102 raise
1103 except ZentinelException, e:
1104 if xmlrpc: return 1
1105 log.critical(e)
1106 except:
1107 if xmlrpc: return 1
1108 raise
1109 else:
1110 result = f.wait()
1111 result = int(hex(result)[:-2], 16)
1112 return result
1113
1114
1116 """
1117 Compare (cmp()) a + b's IP addresses
1118 These addresses may contain subnet mask info.
1119
1120 @param a: IP address
1121 @type a: string
1122 @param b: IP address
1123 @type b: string
1124 @return: result of cmp(a.ip,b.ip)
1125 @rtype: boolean
1126 """
1127
1128 if not a: a = "0.0.0.0"
1129 if not b: b = "0.0.0.0"
1130
1131
1132 a, b = map(lambda x:x.rsplit("/")[0], (a, b))
1133 return cmp(*map(socket.inet_aton, (a, b)))
1134
1135
1137 """
1138 Convert negative 32-bit values into the 2's complement unsigned value
1139
1140 >>> str(unsigned(-1))
1141 '4294967295'
1142 >>> unsigned(1)
1143 1L
1144 >>> unsigned(1e6)
1145 1000000L
1146 >>> unsigned(1e10)
1147 10000000000L
1148
1149 @param v: number
1150 @type v: negative 32-bit number
1151 @return: 2's complement unsigned value
1152 @rtype: unsigned int
1153 """
1154 v = long(v)
1155 if v < 0:
1156 import ctypes
1157 return int(ctypes.c_uint32(v).value)
1158 return v
1159
1160
1162 """
1163 Execute cmd in the shell and send the output to writefunc.
1164
1165 @param cmd: command to execute
1166 @type cmd: string
1167 @param writefunc: output function
1168 @type writefunc: function
1169 @param timeout: maxium number of seconds to wait for the command to execute
1170 @type timeout: number
1171 """
1172 child = popen2.Popen4(cmd)
1173 flags = fcntl.fcntl(child.fromchild, fcntl.F_GETFL)
1174 fcntl.fcntl(child.fromchild, fcntl.F_SETFL, flags | os.O_NDELAY)
1175 pollPeriod = 1
1176 endtime = time.time() + timeout
1177 firstPass = True
1178 while time.time() < endtime and (
1179 firstPass or child.poll()==-1):
1180 firstPass = False
1181 r,w,e = select.select([child.fromchild],[],[],pollPeriod)
1182 if r:
1183 t = child.fromchild.read()
1184 if t:
1185 writefunc(t)
1186 if child.poll()==-1:
1187 writefunc('Command timed out')
1188 import signal
1189 os.kill(child.pid, signal.SIGKILL)
1190
1191
1193 """
1194 A decorator to patch the decorated function into the given class.
1195
1196 >>> @monkeypatch('Products.ZenModel.DataRoot.DataRoot')
1197 ... def do_nothing_at_all(self):
1198 ... print "I do nothing at all."
1199 ...
1200 >>> from Products.ZenModel.DataRoot import DataRoot
1201 >>> hasattr(DataRoot, 'do_nothing_at_all')
1202 True
1203 >>> DataRoot('dummy').do_nothing_at_all()
1204 I do nothing at all.
1205
1206
1207 @param target: class
1208 @type target: class object
1209 @return: decorator function return
1210 @rtype: function
1211 """
1212 if isinstance(target, basestring):
1213 mod, klass = target.rsplit('.', 1)
1214 target = importClass(mod, klass)
1215 def patcher(func):
1216 setattr(target, func.__name__, func)
1217 return func
1218 return patcher
1219
1220
1222 """
1223 Decorator that serializes the return value of the decorated function as
1224 JSON.
1225
1226 Use of the C{ZenUtils.Utils.json} decorator is deprecated. Please import
1227 from C{ZenUtils.json}.
1228
1229 >>> @json
1230 ... def f():
1231 ... return (dict(a=1L), u"123", 123)
1232 ...
1233 >>> print f()
1234 [{"a": 1}, "123", 123]
1235
1236 @param f: class
1237 @type f: class object
1238 @return: decorator function return
1239 @rtype: function
1240 @deprecated: import from Products.ZenWidgets.json
1241 """
1242 warnings.warn("Use of the ZenUtils.Utils.json decorator is deprecated. "
1243 "Please import from Products.ZenUtils.json",
1244 DeprecationWarning)
1245 return _json(f)
1246
1248 """
1249 Decorator to pass in request.form information as arguments to a method.
1250
1251 These are intended to decorate methods of BrowserViews.
1252
1253 @param f: class
1254 @type f: class object
1255 @return: decorator function return
1256 @rtype: function
1257 """
1258 def inner(self, *args, **kwargs):
1259 """
1260 Inner portion of the decorator
1261
1262 @param *args: arguments
1263 @type *args: possible list
1264 @param **kwargs: keyword arguments
1265 @type **kwargs: possible list
1266 @return: decorator function return
1267 @rtype: function
1268 """
1269 if self.request.REQUEST_METHOD=='POST':
1270 content = extractPostContent(self.request)
1271 try:
1272 args += (unjson(content),)
1273 except ValueError:
1274 kwargs.update(self.request.form)
1275 else:
1276 kwargs.update(self.request.form)
1277
1278 if kwargs.has_key('-C'): del kwargs['-C']
1279
1280 if kwargs.has_key('_dc'): del kwargs['_dc']
1281 return f(self, *args, **kwargs)
1282
1283 return inner
1284
1285
1287 """
1288 Metaclass that ensures only a single instance of a class is ever created.
1289
1290 This is accomplished by storing the first instance created as an attribute
1291 of the class itself, then checking that attribute for later constructor
1292 calls.
1293 """
1295 super(Singleton, cls).__init__(*args, **kwargs)
1296 cls._singleton_instance = None
1297
1299 if cls._singleton_instance is None:
1300 cls._singleton_instance = super(
1301 Singleton, cls).__call__(*args, **kwargs)
1302 return cls._singleton_instance
1303
1304
1306 """
1307 Convert some number of seconds into a human-readable string.
1308
1309 @param t: The number of seconds to convert
1310 @type t: int
1311 @param precision: The maximum number of time units to include.
1312 @type t: int
1313 @rtype: str
1314
1315 >>> readable_time(1)
1316 '1 second'
1317 >>> readable_time(60)
1318 '1 minute'
1319 >>> readable_time(60*60*3+12)
1320 '3 hours'
1321 >>> readable_time(60*60*3+12, 2)
1322 '3 hours 12 seconds'
1323
1324 """
1325 names = ('year', 'month', 'week', 'day', 'hour', 'minute', 'second')
1326 mults = (60*60*24*365, 60*60*24*30, 60*60*24*7, 60*60*24, 60*60, 60, 1)
1327 result = []
1328 remaining = abs(seconds)
1329 for name, div in zip(names, mults):
1330 num = Decimal(str(math.floor(remaining/div)))
1331 remaining -= int(num)*div
1332 num = int(num)
1333 if num:
1334 result.append('%d %s%s' %(num, name, num>1 and 's' or ''))
1335 if len(result)==precision:
1336 break
1337 return ' '.join(result)
1338
1339
1341 """
1342 Return a human-readable string describing time relative to C{cmptime}
1343 (defaulted to now).
1344
1345 @param t: The time to convert, in seconds since the epoch.
1346 @type t: int
1347 @param precision: The maximum number of time units to include.
1348 @type t: int
1349 @param cmptime: The time from which to compute the difference, in seconds
1350 since the epoch
1351 @type cmptime: int
1352 @rtype: str
1353
1354 >>> relative_time(time.time() - 60*10)
1355 '10 minutes ago'
1356 >>> relative_time(time.time() - 60*10-3, precision=2)
1357 '10 minutes 3 seconds ago'
1358 >>> relative_time(time.time() - 60*60*24*10, precision=2)
1359 '1 week 3 days ago'
1360 >>> relative_time(time.time() - 60*60*24*365-1, precision=2)
1361 '1 year 1 second ago'
1362 >>> relative_time(time.time() + 1 + 60*60*24*7*2) # Add 1 for rounding
1363 'in 2 weeks'
1364
1365 """
1366 if cmptime is None:
1367 cmptime = time.time()
1368 seconds = Decimal(str(t - cmptime))
1369 result = readable_time(seconds, precision)
1370 if seconds < 0:
1371 result += ' ago'
1372 else:
1373 result = 'in ' + result
1374 return result
1375
1376
1378 """
1379 Check to see if the TCP connection to the browser is still open.
1380
1381 This might be used to interrupt an infinite while loop, which would
1382 preclude the thread from being destroyed even though the connection has
1383 been closed.
1384 """
1385 creation_time = request.environ['channel.creation_time']
1386 for cnxn in asyncore.socket_map.values():
1387 if (isinstance(cnxn, zhttp_channel) and
1388 cnxn.creation_time==creation_time):
1389 return True
1390 return False
1391
1392
1393 EXIT_CODE_MAPPING = {
1394 0:'Success',
1395 1:'General error',
1396 2:'Misuse of shell builtins',
1397 126:'Command invoked cannot execute, permissions problem or command is not an executable',
1398 127:'Command not found',
1399 128:'Invalid argument to exit, exit takes only integers in the range 0-255',
1400 130:'Fatal error signal: 2, Command terminated by Control-C'
1401 }
1402
1404 """
1405 Return a nice exit message that corresponds to the given exit status code
1406
1407 @param exitCode: process exit code
1408 @type exitCode: integer
1409 @return: human-readable version of the exit code
1410 @rtype: string
1411 """
1412 if exitCode in EXIT_CODE_MAPPING.keys():
1413 return EXIT_CODE_MAPPING[exitCode]
1414 elif exitCode >= 255:
1415 return 'Exit status out of range, exit takes only integer arguments in the range 0-255'
1416 elif exitCode > 128:
1417 return 'Fatal error signal: %s' % (exitCode-128)
1418 return 'Unknown error code: %s' % exitCode
1419