Package Products :: Package ZenRRD :: Module utils
[hide private]
[frames] | no frames]

Source Code for Module Products.ZenRRD.utils

  1  ############################################################################## 
  2  #  
  3  # Copyright (C) Zenoss, Inc. 2007, all rights reserved. 
  4  #  
  5  # This content is made available according to terms specified in 
  6  # License.zenoss under the directory where your Zenoss product is installed. 
  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   
22 -def loadargs(obj, args):
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
62 -def prefixid(idprefix, id):
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
78 -def rootid(idprefix, id):
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
94 -def walkupconfig(context, name):
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
117 -def templateNames(context):
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
135 -def getRRDView(context, name):
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
149 -def getRRDTargetType(context, name):
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
163 -def getRRDDataSource(context, name):
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
177 -class rpnStack(object):
178 179 NAN = 1e5000 - 1e5000 180 INF = 1e5000 181
182 - def __init__(self, value):
183 self.stack = [float(value)]
184
185 - def sanitizePop(self):
186 x = self.stack.pop() 187 return 0 if x != x else x
188
189 - def process(self, count, proc):
190 args = [] 191 stack = self.stack 192 for i in range(count): 193 args.append(stack.pop()) 194 stack.append(proc(*args))
195 196 # Note: 0 does not pollute. Sorry.
197 - def polluteProcess(self, count, condition, proc):
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
209 - def dynamicProcess(self, proc):
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
215 - def isSpecial(self, x):
216 return math.isinf(x) or x != x
217
218 - def lt(self):
219 self.polluteProcess(2, self.isSpecial, 220 lambda p, y, x: 1.0 if not(p) and x < y else 0.0)
221
222 - def le(self):
223 self.polluteProcess(2, self.isSpecial, 224 lambda p, y, x: 1.0 if not(p) and x <= y else 0.0)
225
226 - def gt(self):
227 self.polluteProcess(2, self.isSpecial, 228 lambda p, y, x: 1.0 if not(p) and x > y else 0.0)
229
230 - def ge(self):
231 self.polluteProcess(2, self.isSpecial, 232 lambda p, y, x: 1.0 if not(p) and x >= y else 0.0)
233
234 - def eq(self):
235 self.polluteProcess(2, self.isSpecial, 236 lambda p, y, x: 1.0 if not(p) and x == y else 0.0)
237
238 - def ne(self):
239 self.polluteProcess(2, self.isSpecial, 240 lambda p, y, x: 1.0 if not(p) and x != y else 0.0)
241
242 - def un(self):
243 self.process(1, lambda x: 1.0 if x != x else 0.0)
244
245 - def isInf(self):
246 self.process(1, lambda x: 1.0 if math.isinf(x) else 0.0)
247
248 - def if_(self):
249 self.process(3, lambda y, x, c: x if c != 0.0 else y)
250
251 - def min_(self):
252 self.polluteProcess(2, 253 lambda x: x != x, 254 lambda p, y, x: p if p else min(x, y) 255 )
256
257 - def max_(self):
258 self.polluteProcess(2, 259 lambda x: x != x, 260 lambda p, y, x: p if p else max(x, y) 261 )
262
263 - def limit(self):
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
270 - def mul(self):
271 self.process(2, lambda y, x: x * y)
272
273 - def div(self):
274 self.process(2, lambda y, x: self.NAN if y == 0 else x / y)
275
276 - def add(self):
277 self.process(2, lambda y, x: x + y)
278
279 - def sub(self):
280 self.process(2, lambda y, x: x - y)
281
282 - def mod(self):
283 self.process(2, lambda y, x: self.NAN if y == 0 else math.fmod(x, y))
284
285 - def addNaN(self):
286 x = self.sanitizePop() 287 y = self.sanitizePop() 288 self.stack.append(x + y)
289
290 - def sin(self):
291 self.process(1, lambda x: math.sin(x))
292
293 - def cos(self):
294 self.process(1, lambda x: math.cos(x))
295
296 - def log(self):
297 self.process(1, lambda x: math.log(x))
298
299 - def exp(self):
300 self.process(1, lambda x: math.exp(x))
301
302 - def sqrt(self):
303 self.process(1, lambda x: math.sqrt(x))
304
305 - def atan(self):
306 self.process(1, lambda x: math.atan(x))
307
308 - def atan2(self):
309 self.process(2, lambda y, x: math.atan2(x, y))
310
311 - def floor(self):
312 self.process(1, lambda x: math.floor(x))
313
314 - def ceil(self):
315 self.process(1, lambda x: math.ceil(x))
316
317 - def deg2rad(self):
318 self.process(1, lambda x: math.radians(x))
319
320 - def rad2deg(self):
321 self.process(1, lambda x: math.degrees(x))
322
323 - def abs(self):
324 self.process(1, lambda x: abs(x))
325
326 - def sort(self):
327 # uses None returned by sort to do in-place sort 328 self.dynamicProcess(lambda a: a.sort() or a)
329
330 - def rev(self):
331 # uses None returned by reverse to do in-place reverse 332 self.dynamicProcess(lambda a: a.reverse() or a)
333
334 - def avg(self):
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
344 - def unkn(self):
345 self.stack.append(self.NAN)
346
347 - def inf(self):
348 self.stack.append(self.INF)
349
350 - def neginf(self):
351 self.stack.append(-self.INF)
352
353 - def time_(self):
354 self.stack.append(time.time())
355
356 - def dup(self):
357 stack = self.stack 358 stack.append(stack[-1])
359
360 - def pop(self):
361 self.stack.pop()
362
363 - def exc(self):
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):
410 if op in self.opcodes: 411 self.opcodes[op](self) 412 else: 413 self.stack.append(float(op))
414
415 - def result(self):
416 return self.stack.pop()
417 418 419 # This is ONLY for the doctests of rpneval. If you need to do serious floating 420 # point nearness checking, consult someone who knows IEEE-754 in detail. This 421 # one blows up, sometimes subtlely.
422 -def close(x, y):
423 return (abs(x - y) / y) < 1e-15
424 425
426 -def rpneval(value, rpn):
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