Package Products :: Package ZenModel :: Module GraphDefinition
[hide private]
[frames] | no frames]

Source Code for Module Products.ZenModel.GraphDefinition

  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__="""GraphDefinition 
 15   
 16  GraphDefinition defines the global options for a graph. 
 17  """ 
 18   
 19  import sys 
 20  from sets import Set 
 21  import string 
 22   
 23  from Products.ZenRelations.RelSchema import * 
 24  from Products.ZenModel.ZenossSecurity import ZEN_MANAGE_DMD 
 25  from Globals import InitializeClass 
 26  from AccessControl import ClassSecurityInfo, Permissions 
 27  from ZenModelRM import ZenModelRM 
 28  from Products.ZenWidgets import messaging 
 29  from ZenPackable import ZenPackable 
 30  import logging 
 31  log = logging.getLogger("zen.Device") 
 32  from Acquisition import aq_base 
 33   
 34   
35 -def manage_addGraphDefinition(context, id, REQUEST = None):
36 """ 37 This is here so that Zope will let us copy/paste/rename graph points. 38 """ 39 if REQUEST: 40 messaging.IMessageSender(self).sendToBrowser( 41 'Unsupported', 42 'That operation is not supported.', 43 priority=messaging.WARNING 44 ) 45 context.callZenScreen(REQUEST)
46
47 -class FakeContext:
48 isFake = True
49 - def __init__(self, name):
50 self.id = name
51 - def __getattr__(self, name):
52 return FakeContext(name)
53 - def __call__(self, *unused, **ignored):
54 return self
55 - def __getitem__(self, key):
56 return FakeContext(key)
57 - def __str__(self):
58 return self.id
59 - def __repr__(self):
60 return self.id
61 - def device(self):
62 return self
63 - def __nonzero__(self):
64 return True
65 - def rrdPath(self):
66 return 'rrdPath'
67 - def getRRDTemplates(self):
68 return []
69
70 -class GraphDefinition(ZenModelRM, ZenPackable):
71 ''' 72 ''' 73 74 meta_type = 'GraphDefinition' 75 76 height = 100 77 width = 500 78 units = "" 79 log = False 80 base = False 81 #summary = True 82 miny = -1 83 maxy = -1 84 custom = "" 85 hasSummary = True 86 sequence = 0 87 88 _properties = ( 89 {'id':'height', 'type':'int', 'mode':'w'}, 90 {'id':'width', 'type':'int', 'mode':'w'}, 91 {'id':'units', 'type':'string', 'mode':'w'}, 92 {'id':'log', 'type':'boolean', 'mode':'w'}, 93 {'id':'base', 'type':'boolean', 'mode':'w'}, 94 #{'id':'summary', 'type':'boolean', 'mode':'w'}, 95 {'id':'miny', 'type':'int', 'mode':'w'}, 96 {'id':'maxy', 'type':'int', 'mode':'w'}, 97 {'id':'custom', 'type':'text', 'mode':'w'}, 98 {'id':'hasSummary', 'type':'boolean', 'mode':'w'}, 99 {'id':'sequence', 'type':'long', 'mode':'w'}, 100 ) 101 102 _relations = ( 103 ("rrdTemplate", 104 ToOne(ToManyCont,"Products.ZenModel.RRDTemplate", "graphDefs")), 105 ('report', 106 ToOne(ToManyCont, 'Products.ZenModel.MultiGraphReport', 'graphDefs')), 107 ('graphPoints', 108 ToManyCont(ToOne, 'Products.ZenModel.GraphPoint', 'graphDef')), 109 # Remove this relationship after version 2.1 110 ('reportClass', 111 ToOne(ToManyCont, 'Products.ZenModel.MultiGraphReportClass', 'graphDefs')), 112 ) 113 114 115 # Screen action bindings (and tab definitions) 116 factory_type_information = ( 117 { 118 'immediate_view' : 'editGraphDefinition', 119 'actions' : 120 ( 121 { 'id' : 'edit' 122 , 'name' : 'Graph Definition' 123 , 'action' : 'editGraphDefinition' 124 , 'permissions' : ( Permissions.view, ) 125 }, 126 { 'id' : 'editCustom' 127 , 'name' : 'Graph Custom Definition' 128 , 'action' : 'editCustGraphDefinition' 129 , 'permissions' : ( Permissions.view, ) 130 }, 131 { 'id' : 'viewCommands' 132 , 'name' : 'Graph Commands' 133 , 'action' : 'viewGraphCommands' 134 , 'permissions' : ( Permissions.view, ) 135 }, 136 ) 137 }, 138 ) 139 140 security = ClassSecurityInfo() 141 142 ## Basic stuff 143
144 - def getGraphPoints(self, includeThresholds=True):
145 ''' Return ordered list of graph points 146 ''' 147 def cmpGraphPoints(a, b): 148 try: 149 a = int(a.sequence) 150 except ValueError: 151 a = sys.maxint 152 try: 153 b = int(b.sequence) 154 except ValueError: 155 b = sys.maxint 156 return cmp(a, b)
157 gps = [gp for gp in self.graphPoints() 158 if includeThresholds or not gp.isThreshold] 159 gps.sort(cmpGraphPoints) 160 return gps
161 162
163 - def getThresholdGraphPoints(self):
164 ''' Get ordered list of threshold graph points 165 ''' 166 gps = [gp for gp in self.getGraphPoints() if gp.isThreshold] 167 return gps
168 169
170 - def isThresholdGraphed(self, threshId):
171 ''' Return true if there is a thresholdgraphpoint with threshId=threshid 172 ''' 173 for gp in self.getThresholdGraphPoints(): 174 if gp.threshId == threshId: 175 return True 176 return False
177 178
179 - def isDataPointGraphed(self, dpName):
180 ''' Return true if there is at least one graphpoint with a dsName 181 equal to dpName. 182 ''' 183 from DataPointGraphPoint import DataPointGraphPoint 184 for gp in self.getGraphPoints(includeThresholds=False): 185 if isinstance(gp, DataPointGraphPoint): 186 if gp.dpName == dpName: 187 return True 188 return False
189 190 191 ## GUI Support 192 193
194 - def breadCrumbs(self, terminator='dmd'):
195 """Return the breadcrumb links for this object add ActionRules list. 196 [('url','id'), ...] 197 """ 198 if self.rrdTemplate(): 199 from RRDTemplate import crumbspath 200 crumbs = super(GraphDefinition, self).breadCrumbs(terminator) 201 return crumbspath(self.rrdTemplate(), crumbs, -2) 202 return ZenModelRM.breadCrumbs(self, terminator)
203 204
205 - def checkValidId(self, id, prep_id = False):
206 """Checks a valid id 207 """ 208 # RRD docs say that limit on vnames is 255 characters and that 209 # A-Za-z0-9_ are the valid characters. Zenoss reserves - for it's own 210 # use. Limiting to 200 instead just to leave room for whatever. 211 # http://oss.oetiker.ch/rrdtool/doc/rrdgraph_data.en.html 212 if len(id) > 200: 213 return 'GraphPoint names can not be longer than 200 characters.' 214 allowed = Set(list(string.ascii_letters) 215 + list(string.digits) 216 + ['_']) 217 attempted = Set(list(id)) 218 if not attempted.issubset(allowed): 219 return 'Only letters, digits and underscores are allowed' + \ 220 ' in GraphPoint names.' 221 return ZenModelRM.checkValidId(self, id, prep_id)
222 223
224 - def getGraphPointDescriptions(self):
225 return [gi.getDescription() for gi in self.graphPoints()]
226 227
228 - def getGraphPointsNames(self):
229 ''' Return list of graph point ids 230 ''' 231 return [gp.id for gp in self.getGraphPoints()]
232 233
234 - def getGraphPointNamesString(self):
235 """ 236 Return a string that lists the names of the graphpoints used in this 237 graph definition. If this graph definition has a perf template then 238 note in the string which graphpoints are broken (in that they refer 239 to nonexistent datapoints.) 240 """ 241 names = [] 242 for gp in self.getGraphPoints(): 243 if hasattr(aq_base(gp), 'isBroken') and gp.isBroken(): 244 names.append('%s(<span style="color: red">missing</span>)' % 245 gp.id) 246 else: 247 names.append(gp.id) 248 return ', '.join(names)
249 250
251 - def getGraphPointOptions(self):
252 ''' Used by dialog_addGraphPoint to construct the list of 253 available graphpoint types. 254 ''' 255 return (('DefGraphPoint', 'DEF'), 256 ('VdefGraphPoint', 'VDEF'), 257 ('CdefGraphPoint', 'CDEF'), 258 ('PrintGraphPoint', 'PRINT'), 259 ('GprintGraphPoint', 'GPRINT'), 260 ('CommentGraphPoint', 'COMMENT'), 261 ('VruleGraphPoint', 'VRULE'), 262 ('HruleGraphPoint', 'HRULE'), 263 ('LineGraphPoint', 'LINE'), 264 ('AreaGraphPoint', 'AREA'), 265 ('TickGraphPoint', 'TICK'), 266 ('ShiftGraphPoint', 'SHIFT'))
267 268
269 - def createGraphPoint(self, cls, newId):
270 ''' Create the graphpoint with the given id or something similar 271 and add to self.graphPoints 272 ''' 273 def getUniqueId(container, base): 274 ids = container.objectIds() 275 new = base 276 i = 2 277 while new in ids: 278 new = '%s%s' % (base, i) 279 i += 1 280 return new
281 newId = getUniqueId(self.graphPoints, newId) 282 gp = cls(newId) 283 # Set sequence 284 if gp.isThreshold: 285 gp.sequence = -1 286 else: 287 gp.sequence = len(self.graphPoints()) 288 # Set legend for graph points on multigraph reports 289 if self.report() and hasattr(gp, 'legend'): 290 # For MultiGraphReports we use a fancier legend 291 # to differentiate when you have multiple devices/graphpoints 292 # on a single graph 293 gp.legend = gp.DEFAULT_MULTIGRAPH_LEGEND 294 self.graphPoints._setObject(gp.id, gp) 295 gp = self.graphPoints._getOb(gp.id) 296 if gp.sequence == -1: 297 self.manage_resequenceGraphPoints() 298 return gp 299 300 301 security.declareProtected(ZEN_MANAGE_DMD, 'manage_addCustomGraphPoint')
302 - def manage_addCustomGraphPoint(self, new_id, flavor, REQUEST=None):
303 ''' Create a new graphpoint of the given class and id 304 ''' 305 exec 'import %s' % flavor 306 cls = eval('%s.%s' % (flavor, flavor)) 307 gp = self.createGraphPoint(cls, new_id) 308 if REQUEST: 309 url = '%s/graphPoints/%s' % (self.getPrimaryUrlPath(), gp.id) 310 REQUEST['RESPONSE'].redirect(url) 311 return self.callZenScreen(REQUEST) 312 return gp
313 314 315 security.declareProtected(ZEN_MANAGE_DMD, 'manage_addDataPointGraphPoints')
316 - def manage_addDataPointGraphPoints(self, dpNames=None, 317 includeThresholds=False, 318 REQUEST=None):
319 ''' Create new graph points 320 The migrate script graphDefinitions and friends depends on the first 321 element in newGps being the DataPointGraphPoint when only one 322 name is passed in dpNames. 323 ''' 324 if not dpNames: 325 if REQUEST: 326 messaging.IMessageSender(self).sendToBrowser( 327 'Error', 328 'No graph points were selected.', 329 priority=messaging.WARNING 330 ) 331 return self.callZenScreen(REQUEST) 332 else: 333 from DataPointGraphPoint import DataPointGraphPoint 334 newGps = [] 335 for dpName in dpNames: 336 dpId = dpName.split('_', 1)[-1] 337 gp = self.createGraphPoint(DataPointGraphPoint, dpId) 338 gp.dpName = dpName 339 newGps.append(gp) 340 if includeThresholds: 341 for dpName in dpNames: 342 newGps += self.addThresholdsForDataPoint(dpName) 343 if REQUEST: 344 messaging.IMessageSender(self).sendToBrowser( 345 'Graph Points Added', 346 '%s graph point%s were added.' % (len(newGps), 347 len(newGps) > 1 and 's' or '') 348 ) 349 return self.callZenScreen(REQUEST) 350 return newGps
351 352
353 - def addThresholdsForDataPoint(self, dpName):
354 ''' Make sure that Threshold graph points exist for all thresholds 355 that use the given dpName. 356 Return a list of all graphpoints created by this call. 357 ''' 358 from ThresholdGraphPoint import ThresholdGraphPoint 359 newGps = [] 360 for thresh in self.rrdTemplate().thresholds(): 361 if thresh.canGraph(self) \ 362 and dpName in thresh.dsnames \ 363 and not self.isThresholdGraphed(thresh.id): 364 gp = self.createGraphPoint(ThresholdGraphPoint, thresh.id) 365 gp.threshId = thresh.id 366 newGps.append(gp) 367 return newGps
368 369 370 security.declareProtected(ZEN_MANAGE_DMD, 'manage_addThresholdGraphPoints')
371 - def manage_addThresholdGraphPoints(self, threshNames, REQUEST=None):
372 ''' Create new graph points 373 ''' 374 from ThresholdGraphPoint import ThresholdGraphPoint 375 newGps = [] 376 for threshName in threshNames: 377 #thresh = getattr(self.rrdTemplate.thresholds, threshName) 378 gp = self.createGraphPoint(ThresholdGraphPoint, threshName) 379 gp.threshId = threshName 380 newGps.append(gp) 381 if REQUEST: 382 messaging.IMessageSender(self).sendToBrowser( 383 'Graph Points Added', 384 '%s graph point%s were added.' % (len(newGps), 385 len(newGps) > 1 and 's' or '') 386 ) 387 return self.callZenScreen(REQUEST) 388 return newGps
389 390 391 security.declareProtected(ZEN_MANAGE_DMD, 'manage_deleteGraphPoints')
392 - def manage_deleteGraphPoints(self, ids=(), REQUEST=None):
393 ''' Deleted given graphpoints 394 ''' 395 num = 0 396 for id in ids: 397 if getattr(self.graphPoints, id, False): 398 num += 1 399 self.graphPoints._delObject(id) 400 self.manage_resequenceGraphPoints() 401 if REQUEST: 402 messaging.IMessageSender(self).sendToBrowser( 403 'Graph Points Deleted', 404 '%s graph point%s were deleted.' % (num, 405 num > 1 and 's' or '') 406 ) 407 return self.callZenScreen(REQUEST)
408 409 410 security.declareProtected(ZEN_MANAGE_DMD, 'manage_resequenceGraphPoints')
411 - def manage_resequenceGraphPoints(self, seqmap=(), origseq=(), REQUEST=None):
412 """Reorder the sequence of the GraphPoints. 413 """ 414 from Products.ZenUtils.Utils import resequence 415 return resequence(self, self.graphPoints(), 416 seqmap, origseq, REQUEST)
417 418
419 - def getDataPointOptions(self):
420 ''' Return a list of (value, name) tuples for the list of datapoints 421 which the user selects from to create new graphpoints. 422 ''' 423 return [(dp.name(), dp.name()) 424 for dp in self.rrdTemplate.getRRDDataPoints()]
425 426
427 - def getThresholdOptions(self):
428 ''' Return a list of (value, name) tuples for the list of thresholds 429 which the user selects from to create new graphpoints. 430 ''' 431 return [(t.id, t.id) for t in self.rrdTemplate.thresholds()]
432 433 434 ## Graphing Support 435 436
437 - def getGraphCmds(self, context, rrdDir, multiid=-1, upToPoint=None, 438 includeSetup=True, includeThresholds=True, 439 prefix='', cmds=None, idxOffset=0):
440 """build the graph opts for a single rrdfile""" 441 from Products.ZenUtils.ZenTales import talesEval 442 if not cmds: 443 cmds = [] 444 if includeSetup: 445 cmds += self.graphsetup() 446 447 # Have to draw thresholds before data so that thresholds won't 448 # obscure data (especially if threshold uses TICK) 449 if includeThresholds: 450 threshGps = [gp for gp in self.getThresholdGraphPoints() 451 if upToPoint is None or gp.sequence < upToPoint] 452 if threshGps: 453 for index, gp in enumerate(threshGps): 454 try: 455 cmds = gp.getGraphCmds(cmds, context, rrdDir, 456 self.hasSummary, index+idxOffset, 457 multiid, prefix) 458 except (KeyError, NameError), e: 459 cmds.append('COMMENT: UNKNOWN VALUE IN ' 460 'GRAPHPOINT %s\: %s' % (gp.id, str(e))) 461 gpList = [gp for gp in self.getGraphPoints(includeThresholds=False) 462 if upToPoint is None or gp.sequence < upToPoint] 463 for index, gp in enumerate(gpList): 464 try: 465 cmds = gp.getGraphCmds(cmds, context, rrdDir, 466 self.hasSummary, index+idxOffset, 467 multiid, prefix) 468 except (KeyError, NameError), e: 469 cmds.append('COMMENT: UNKNOWN VALUE IN GRAPHPOINT ' 470 '%s\: %s' % (gp.id, str(e))) 471 if self.custom and includeSetup \ 472 and not upToPoint: 473 try: 474 res = talesEval("string:"+str(self.custom), context) 475 except (KeyError, NameError), e: 476 res = 'COMMENT:UNKNOWN VALUE IN CUSTOM COMMANDS\: %s' % str(e) 477 res = [l for l in res.split('\n') if l.strip()] 478 cmds.extend(res) 479 #if self.hasSummary: 480 # cmds = self.addSummary(cmds) 481 482 return cmds
483 484
485 - def getRRDVariables(self, upToPoint=None):
486 ''' Return list of rrd variable names that are defined by DEF, CDEF 487 or VDEF statements in the rrd commands. If upToPoint is not None then 488 only consider statements generated by graphoints where 489 sequence < upToPoint 490 ''' 491 cmds = self.getFakeGraphCmds(upToPoint=upToPoint) 492 names = [line[line.find(':')+1:line.find('=')] 493 for line in cmds.split('\n') 494 if line[:line.find(':')] in ('DEF', 'CDEF', 'VDEF')] 495 return names
496 497
498 - def getFakeGraphCmds(self, upToPoint=None):
499 ''' Used to display the graph commands (or a reasonable likeness) 500 to the user 501 ''' 502 context = FakeContext('Context') 503 cmds = self.getGraphCmds(context, context.rrdPath(), upToPoint=upToPoint) 504 cmds = '\n'.join(cmds) 505 return cmds
506 507
508 - def graphsetup(self):
509 """Setup global graph parameters. 510 """ 511 gopts = ['-F', '-E'] 512 if self.height: 513 gopts.append('--height=%d' % int(self.height)) 514 if self.width: 515 gopts.append('--width=%d' % int(self.width)) 516 if self.log: 517 gopts.append('--logarithmic') 518 if self.maxy > -1: 519 gopts.append('--upper-limit=%d' % int(self.maxy)) 520 gopts.append('--rigid') 521 if self.miny > -1: 522 gopts.append('--lower-limit=%d' % int(self.miny)) 523 gopts.append('--rigid') 524 # Always include a vertical label so that multiple graphs on page 525 # align correctly. 526 gopts.append('--vertical-label=%s' % (self.units or ' ')) 527 if self.units == 'percentage': 528 if not self.maxy > -1: 529 gopts.append('--upper-limit=100') 530 if not self.miny > -1: 531 gopts.append('--lower-limit=0') 532 if self.base: 533 gopts.append('--base=1024') 534 gopts = [str(o) for o in gopts] 535 return gopts
536 537
538 - def getDataPointGraphPoints(self, dpName):
539 ''' Return a list of DataPointGraphPoints that use the given dpName 540 ''' 541 from DataPointGraphPoint import DataPointGraphPoint 542 return [gp for gp in self.graphPoints() 543 if isinstance(gp, DataPointGraphPoint) 544 and gp.dpName == dpName]
545 546
547 - def getUniqueDpNames(self, limit=None):
548 ''' 549 Get a list of all unique datapoint names 550 ''' 551 from sets import Set 552 dpNames = Set() 553 for t in self.dmd.Devices.getAllRRDTemplates(): 554 for ds in t.datasources(): 555 # If we have a broken datasource (likely from a missing zenpack) 556 # then don't try to parse datapoints, you can't. 557 if hasattr(ds, 'datapoints'): 558 for dp in ds.datapoints(): 559 dpNames.add(dp.name()) 560 if limit and len(dpNames) >= limit: 561 break 562 dpNames = list(dpNames) 563 dpNames.sort() 564 return dpNames
565 566
567 - def getUniqueThresholdNames(self, limit=100):
568 ''' 569 Get a list of all unique threshold names 570 ''' 571 from sets import Set 572 names = Set() 573 for t in self.dmd.Devices.getAllRRDTemplates(): 574 for thresh in t.thresholds(): 575 names.add(thresh.id) 576 if len(names) >= limit: 577 break 578 if len(names) >= limit: 579 break 580 names = list(names) 581 names.sort() 582 return names
583 584 585 InitializeClass(GraphDefinition) 586