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