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

Source Code for Module Products.ZenRRD.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  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   
26 -def loadargs(obj, args):
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
66 -def prefixid(idprefix, id):
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
82 -def rootid(idprefix, id):
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
98 -def walkupconfig(context, name):
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
121 -def templateNames(context):
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
139 -def getRRDView(context, name):
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
153 -def getRRDTargetType(context, name):
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
167 -def getRRDDataSource(context, name):
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
181 -class rpnStack(object):
182 183 NAN = 1e5000 - 1e5000 184 INF = 1e5000 185
186 - def __init__(self, value):
187 self.stack = [float(value)]
188
189 - def sanitizePop(self):
190 x = self.stack.pop() 191 return 0 if x != x else x
192
193 - def process(self, count, proc):
194 args = [] 195 stack = self.stack 196 for i in range(count): 197 args.append(stack.pop()) 198 stack.append(proc(*args))
199 200 # Note: 0 does not pollute. Sorry.
201 - def polluteProcess(self, count, condition, proc):
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
213 - def dynamicProcess(self, proc):
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
219 - def isSpecial(self, x):
220 return math.isinf(x) or x != x
221
222 - def lt(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 le(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 gt(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 ge(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 eq(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 ne(self):
243 self.polluteProcess(2, self.isSpecial, 244 lambda p, y, x: 1.0 if not(p) and x != y else 0.0)
245
246 - def un(self):
247 self.process(1, lambda x: 1.0 if x != x else 0.0)
248
249 - def isInf(self):
250 self.process(1, lambda x: 1.0 if math.isinf(x) else 0.0)
251
252 - def if_(self):
253 self.process(3, lambda y, x, c: x if c != 0.0 else y)
254
255 - def min_(self):
256 self.polluteProcess(2, 257 lambda x: x != x, 258 lambda p, y, x: p if p else min(x, y) 259 )
260
261 - def max_(self):
262 self.polluteProcess(2, 263 lambda x: x != x, 264 lambda p, y, x: p if p else max(x, y) 265 )
266
267 - def limit(self):
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
274 - def mul(self):
275 self.process(2, lambda y, x: x * y)
276
277 - def div(self):
278 self.process(2, lambda y, x: self.NAN if y == 0 else x / y)
279
280 - def add(self):
281 self.process(2, lambda y, x: x + y)
282
283 - def sub(self):
284 self.process(2, lambda y, x: x - y)
285
286 - def mod(self):
287 self.process(2, lambda y, x: self.NAN if y == 0 else math.fmod(x, y))
288
289 - def addNaN(self):
290 x = self.sanitizePop() 291 y = self.sanitizePop() 292 self.stack.append(x + y)
293
294 - def sin(self):
295 self.process(1, lambda x: math.sin(x))
296
297 - def cos(self):
298 self.process(1, lambda x: math.cos(x))
299
300 - def log(self):
301 self.process(1, lambda x: math.log(x))
302
303 - def exp(self):
304 self.process(1, lambda x: math.exp(x))
305
306 - def sqrt(self):
307 self.process(1, lambda x: math.sqrt(x))
308
309 - def atan(self):
310 self.process(1, lambda x: math.atan(x))
311
312 - def atan2(self):
313 self.process(2, lambda y, x: math.atan2(x, y))
314
315 - def floor(self):
316 self.process(1, lambda x: math.floor(x))
317
318 - def ceil(self):
319 self.process(1, lambda x: math.ceil(x))
320
321 - def deg2rad(self):
322 self.process(1, lambda x: math.radians(x))
323
324 - def rad2deg(self):
325 self.process(1, lambda x: math.degrees(x))
326
327 - def abs(self):
328 self.process(1, lambda x: abs(x))
329
330 - def sort(self):
331 self.dynamicProcess(lambda a: a.sort() or a)
332
333 - def rev(self):
334 self.dynamicProcess(lambda a: a.reverse() or a)
335
336 - def avg(self):
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
346 - def unkn(self):
347 self.stack.append(self.NAN)
348
349 - def inf(self):
350 self.stack.append(self.INF)
351
352 - def neginf(self):
353 self.stack.append(-self.INF)
354
355 - def time_(self):
356 self.stack.append(time.time())
357
358 - def dup(self):
359 stack = self.stack 360 stack.append(stack[-1])
361
362 - def pop(self):
363 self.stack.pop()
364
365 - def exc(self):
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
417 - def result(self):
418 return self.stack.pop()
419 420 421 # This is ONLY for the doctests of rpneval. If you need to do serious floating 422 # point nearness checking, consult someone who knows IEEE-754 in detail. This 423 # one blows up, sometimes subtlely.
424 -def close(x, y):
425 return (abs(x - y) / y) < 1e-15
426 427
428 -def rpneval(value, rpn):
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