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

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