1
2
3
4
5
6
7
8
9
10
11
12
13
14 __doc__="""utils
15
16 RRD utility functions
17 """
18
19 from sets import Set
20 from Acquisition import aq_chain
21
22 from Exceptions import RRDObjectNotFound, TooManyArgs
23 import math
24 import time
25
27 """
28 Load data into a RRD Object
29
30 @param obj: RRD object
31 @type obj: RRD object
32 @param args: arguments
33 @type args: list of strings
34 """
35 import string
36 arglen = len(args)
37 if arglen > len(obj._properties):
38 raise TooManyArgs( "Too many args" )
39 i = 0
40 for arg in args:
41 if i > arglen: break
42 arg = arg.strip()
43 att = obj._properties[i]['id']
44 try:
45 if arg != '':
46 if obj._properties[i]['type'] == 'lines':
47 value = map(string.strip, arg.split(','))
48 att = '_' + att
49 elif obj._properties[i]['type'] == 'int':
50 value = int(arg)
51 elif obj._properties[i]['type'] == 'long':
52 value = long(arg)
53 elif obj._properties[i]['type'] == 'float':
54 value = float(arg)
55 elif obj._properties[i]['type'] == 'boolean':
56 value = eval(arg)
57 else:
58 value = arg
59 if value is not None: setattr(obj,att,value)
60 except:
61 print "att = %s value = %s" % (att, arg)
62 raise
63 i += 1
64
65
67 """
68 See if prefix needs to be added to id
69
70 @param idprefix: prefix
71 @type idprefix: string
72 @param id: identifier
73 @type id: string
74 @return: add the prefix with a '-' in between
75 @rtype: string
76 """
77 if id.find(idprefix) != 0:
78 id = idprefix + '-' + id
79 return id
80
81
83 """
84 See if prefix needs to be removed from id
85
86 @param idprefix: prefix
87 @type idprefix: string
88 @param id: identifier
89 @type id: string
90 @return: remove the prefix with a '-' in between or return None
91 @rtype: string or None
92 """
93 if idprefix[-1] != '-': idprefix += '-'
94 if id.find(idprefix) == 0:
95 return id[len(idprefix):]
96
97
99 """
100 Given a Zope context, try to find the rrdconfig object
101 for the name.
102 Raises RRDObjectNotFound if not found.
103
104 @param context: Zope context
105 @type context: Zope context object
106 @param name: RRDView name
107 @type name: string
108 @return: rrdconfig object or None
109 @rtype: rrdconfig object
110 """
111 if not name: return
112 while 1:
113 if hasattr(context, 'rrdconfig') and hasattr(context.rrdconfig, name):
114 return getattr(context.rrdconfig, name)
115 context = context.aq_parent
116 if context.id == 'dmd':
117 raise RRDObjectNotFound( "Object %s not found in context %s" % \
118 (name, context.getPrimaryUrlPath()))
119
120
122 """
123 Return template names in the given context
124
125 @param context: Zope context
126 @type context: Zope context object
127 @return: names of the templates
128 @rtype: set of strings
129 """
130 names = Set()
131 for obj in aq_chain(context):
132 rrdconfig = getattr(obj, 'rrdconfig', None)
133 if rrdconfig:
134 names = names.union(rrdconfig.objectIds(spec='RRDTargetType'))
135 return names
136
137
138
140 """
141 Lookup an RRDView based on its name
142
143 @param context: Zope context
144 @type context: Zope context object
145 @param name: RRDView name
146 @type name: string
147 @return: rrdconfig object or None
148 @rtype: rrdconfig object
149 """
150 return walkupconfig(context, 'RRDView-'+name)
151
152
154 """
155 Lookup an rrdtargettype based on its name
156
157 @param context: Zope context
158 @type context: Zope context object
159 @param name: RRDView name
160 @type name: string
161 @return: rrdconfig object or None
162 @rtype: rrdconfig object
163 """
164 return walkupconfig(context, 'RRDTargetType-'+name)
165
166
168 """
169 Lookup an rrddatasource based on its name
170
171 @param context: Zope context
172 @type context: Zope context object
173 @param name: RRDView name
174 @type name: string
175 @return: rrdconfig object or None
176 @rtype: rrdconfig object
177 """
178 return walkupconfig(context, 'RRDDataSource-'+name)
179
180
182
183 NAN = 1e5000 - 1e5000
184 INF = 1e5000
185
187 self.stack = [float(value)]
188
190 x = self.stack.pop()
191 return 0 if x != x else x
192
194 args = []
195 stack = self.stack
196 for i in range(count):
197 args.append(stack.pop())
198 stack.append(proc(*args))
199
200
202 stack = self.stack
203 args = []
204 polluted = False
205 for i in range(count):
206 x = stack.pop()
207 if condition(x):
208 polluted = x
209 args.append(x)
210 args.insert(0, polluted)
211 stack.append(proc(*args))
212
214 count = int(self.stack.pop())
215 args = self.stack[-count:]
216 self.stack = self.stack[0:-count]
217 self.stack.extend(proc(args))
218
220 return math.isinf(x) or x != x
221
225
229
233
237
241
245
247 self.process(1, lambda x: 1.0 if x != x else 0.0)
248
250 self.process(1, lambda x: 1.0 if math.isinf(x) else 0.0)
251
253 self.process(3, lambda y, x, c: x if c != 0.0 else y)
254
256 self.polluteProcess(2,
257 lambda x: x != x,
258 lambda p, y, x: p if p else min(x, y)
259 )
260
262 self.polluteProcess(2,
263 lambda x: x != x,
264 lambda p, y, x: p if p else max(x, y)
265 )
266
268 self.polluteProcess(3,
269 self.isSpecial,
270 lambda p, y, x, v: v if not(p) and
271 (x <= v and v <= y or x >= v and v >= y) else self.NAN
272 )
273
275 self.process(2, lambda y, x: x * y)
276
278 self.process(2, lambda y, x: self.NAN if y == 0 else x / y)
279
281 self.process(2, lambda y, x: x + y)
282
284 self.process(2, lambda y, x: x - y)
285
287 self.process(2, lambda y, x: self.NAN if y == 0 else math.fmod(x, y))
288
293
296
299
302
305
308
311
314
317
320
322 self.process(1, lambda x: math.radians(x))
323
325 self.process(1, lambda x: math.degrees(x))
326
329
332
335
337 def average(a):
338 total = count = 0
339 for x in a:
340 if not(math.isnan(x)):
341 count += 1
342 total += x
343 return [total / count] if count > 0 else [self.NAN]
344 self.dynamicProcess(average)
345
347 self.stack.append(self.NAN)
348
350 self.stack.append(self.INF)
351
353 self.stack.append(-self.INF)
354
357
359 stack = self.stack
360 stack.append(stack[-1])
361
364
366 stack = self.stack
367 stack[-1], stack[-2] = stack[-2], stack[-1]
368 opcodes = {
369 'LT': lt,
370 'LE': le,
371 'GT': gt,
372 'GE': ge,
373 'EQ': eq,
374 'NE': ne,
375 'UN': un,
376 'ISINF': isInf,
377 'IF': if_,
378 'MIN': min_,
379 'MAX': max_,
380 'LIMIT': limit,
381 '+': add,
382 '-': sub,
383 '*': mul,
384 '/': div,
385 '%': mod,
386 'ADDNAN': addNaN,
387 'SIN': sin,
388 'COS': cos,
389 'LOG': log,
390 'EXP': exp,
391 'SQRT': sqrt,
392 'ATAN': atan,
393 'ATAN2': atan2,
394 'FLOOR': floor,
395 'CEIL': ceil,
396 'DEG2RAD': deg2rad,
397 'RAD2DEG': rad2deg,
398 'ABS': abs,
399 'SORT': sort,
400 'REV': rev,
401 'AVG': avg,
402 'UNKN': unkn,
403 'INF': inf,
404 'NEGINF': neginf,
405 'TIME': time_,
406 'DUP': dup,
407 'POP': pop,
408 'EXC': exc,
409 }
410
411 - def step(self, op):
412 if op in self.opcodes:
413 self.opcodes[op](self)
414 else:
415 self.stack.append(float(op))
416
418 return self.stack.pop()
419
420
421
422
423
425 return (abs(x - y) / y) < 1e-15
426
427
429 """
430 Simulate RPN evaluation as per
431 http://oss.oetiker.ch/rrdtool/doc/rrdgraph_rpn.en.html
432 Note: because we only have one value, we won't support the entire API.
433 >>> rpneval(2, '2,*')
434 4.0
435 >>> rpneval(7, '2,3,*,*')
436 42.0
437 >>> close(rpneval(19, '9,5,/,*,32,+'), 66.2)
438 True
439 >>> rpneval(1, '*')
440 -1.0
441 >>> rpneval(2, '-8,-')
442 10.0
443 >>> rpneval(3, '2,%')
444 1.0
445 >>> rpneval(1e5000 - 1e5000, 'UN')
446 1.0
447 >>> rpneval(70, '71,LT')
448 1.0
449 >>> rpneval(69, '69,LT')
450 0.0
451 >>> rpneval(68, 'inf,LT')
452 0.0
453 >>> rpneval(67, '67,LE')
454 1.0
455 >>> rpneval(66, '0,LE')
456 0.0
457 >>> rpneval(65, 'inf,LE')
458 0.0
459 >>> rpneval(64, '60,GT')
460 1.0
461 >>> rpneval(63, '63,GT')
462 0.0
463 >>> rpneval(63, 'neginf,GT')
464 0.0
465 >>> rpneval(61, '100,GE')
466 0.0
467 >>> rpneval(60, '60,GE')
468 1.0
469 >>> rpneval(59, 'neginf,GE')
470 0.0
471 >>> rpneval(58, '137,EQ')
472 0.0
473 >>> rpneval(57, '57,EQ')
474 1.0
475 >>> rpneval(56, 'inf,EQ')
476 0.0
477 >>> rpneval(55, '55,NE')
478 0.0
479 >>> rpneval(-1e5000, 'neginf,EQ')
480 0.0
481 >>> rpneval(1e5000 - 1e5000, 'unkn,EQ')
482 0.0
483 >>> rpneval(1e5000 - 1e5000, 'unkn,NE')
484 0.0
485 >>> rpneval(1e5000, 'inf,NE')
486 0.0
487 >>> rpneval(51, '51,NE')
488 0.0
489 >>> rpneval(50, ' 42 , NE ')
490 1.0
491 >>> rpneval(49, 'UN')
492 0.0
493 >>> rpneval(-1e5000, 'isINF')
494 1.0
495 >>> rpneval(1e5000, 'IsInF')
496 1.0
497 >>> rpneval(46, 'ISINF')
498 0.0
499 >>> rpneval(0, '1,2,if')
500 2.0
501 >>> rpneval(44, '1,2,if')
502 1.0
503 >>> rpneval(1e5000, '1,2,IF')
504 1.0
505 >>> rpneval(1e5000 - 1e5000, '1,2,iF')
506 1.0
507 >>> rpneval(41, '5,min')
508 5.0
509 >>> rpneval(40, 'neginf,min') == -1e5000
510 True
511 >>> rpneval(39, 'unkn,min')
512 nan
513 >>> rpneval(38, 'neginf,max')
514 38.0
515 >>> rpneval(37, 'inf,max') == 1e5000
516 True
517 >>> math.isnan(rpneval(36, 'unkn,max'))
518 True
519 >>> math.isnan(rpneval(35, '30,neginf,limit'))
520 True
521 >>> math.isnan(rpneval(34, '30,30.5,limit'))
522 True
523 >>> rpneval(33, '30,35,limit')
524 33.0
525 >>> rpneval(32, '464,+')
526 496.0
527 >>> rpneval(31, '5,-')
528 26.0
529 >>> rpneval(37, '18,*')
530 666.0
531 >>> close(rpneval(29, '5,/'), 5.8)
532 True
533 >>> math.isnan(rpneval(28, '0,/'))
534 True
535 >>> rpneval(27, '11,%')
536 5.0
537 >>> math.isnan(rpneval(26, '0,%'))
538 True
539 >>> rpneval(25, '0,0,/,addnan')
540 25.0
541 >>> close(rpneval(math.pi / 6, 'sin'), 0.5)
542 True
543 >>> close(rpneval(math.pi / 3, 'cos'), 0.5)
544 True
545 >>> rpneval(math.e, 'log') == 1
546 True
547 >>> rpneval(1, 'exp') == math.e
548 True
549 >>> rpneval(1.44, 'sqrt')
550 1.2
551 >>> rpneval(1, 'atan') == math.pi / 4
552 True
553 >>> rpneval(1, '0,atan2') == math.pi / 2
554 True
555 >>> rpneval(17.9, 'floor')
556 17.0
557 >>> rpneval(16.3, 'ceil')
558 17.0
559 >>> rpneval(15, 'deg2rad') == 15 * math.pi / 180
560 True
561 >>> rpneval(14, 'rad2deg') == 14 * 180 / math.pi
562 True
563 >>> rpneval(-13,'abs')
564 13.0
565 >>> rpneval(12, '5,7,3,sort,-,-')
566 10.0
567 >>> rpneval(11, '3,4,3,rev,-,+')
568 -4.0
569 >>> rpneval(10, '5,4,2,4,avg')
570 5.25
571 >>> rpneval(9, 'unkn')
572 nan
573 >>> rpneval(8, 'inf')
574 inf
575 >>> rpneval(7, 'neginf')
576 -inf
577 >>> rpneval(6, 'time') != 6
578 True
579 >>> rpneval(5, 'dup,-')
580 0.0
581 >>> rpneval(2, 'pop')
582 -1.0
583 >>> rpneval(4, '5,exc,-')
584 1.0
585 >>> rpneval(None, '2,*')
586 None
587 """
588 if value is None: return value
589 rpnOps = [op.strip().upper() for op in rpn.split(',')]
590 stack = rpnStack(value)
591 try:
592 for op in rpnOps:
593 stack.step(op)
594 return stack.result()
595 except IndexError:
596 return -1.0
597