| Trees | Indices | Help |
|
|---|
|
|
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
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
68
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
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
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
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
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
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
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
211 return [gi.getDescription() for gi in self.graphPoints()]
212
213
218
219
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
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
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')
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
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')
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')
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')
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
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
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
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
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
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
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
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
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
| Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1.1812 on Mon Jul 30 17:11:16 2012 | http://epydoc.sourceforge.net |