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

Source Code for Module Products.ZenModel.GraphDefinition

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