| Trees | Indices | Help |
|
|---|
|
|
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 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
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
69
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
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
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
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
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
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
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
225 return [gi.getDescription() for gi in self.graphPoints()]
226
227
232
233
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
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
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')
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')
318 ''' Create new graph points
319 The migrate script graphDefinitions and friends depends on the first
320 element in newGps being the DataPointGraphPoint when only one
321 name is passed in dpNames.
322 '''
323 from DataPointGraphPoint import DataPointGraphPoint
324 newGps = []
325 for dpName in dpNames:
326 dpId = dpName.split('_', 1)[-1]
327 gp = self.createGraphPoint(DataPointGraphPoint, dpId)
328 gp.dpName = dpName
329 newGps.append(gp)
330 if includeThresholds:
331 for dpName in dpNames:
332 newGps += self.addThresholdsForDataPoint(dpName)
333 if REQUEST:
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 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 messaging.IMessageSender(self).sendToBrowser(
373 'Graph Points Added',
374 '%s graph point%s were added.' % (len(newGps),
375 len(newGps) > 1 and 's' or '')
376 )
377 return self.callZenScreen(REQUEST)
378 return newGps
379
380
381 security.declareProtected(ZEN_MANAGE_DMD, 'manage_deleteGraphPoints')
383 ''' Deleted given graphpoints
384 '''
385 num = 0
386 for id in ids:
387 if getattr(self.graphPoints, id, False):
388 num += 1
389 self.graphPoints._delObject(id)
390 self.manage_resequenceGraphPoints()
391 if REQUEST:
392 messaging.IMessageSender(self).sendToBrowser(
393 'Graph Points Deleted',
394 '%s graph point%s were deleted.' % (num,
395 num > 1 and 's' or '')
396 )
397 return self.callZenScreen(REQUEST)
398
399
400 security.declareProtected(ZEN_MANAGE_DMD, 'manage_resequenceGraphPoints')
402 """Reorder the sequence of the GraphPoints.
403 """
404 from Products.ZenUtils.Utils import resequence
405 return resequence(self, self.graphPoints(),
406 seqmap, origseq, REQUEST)
407
408
410 ''' Return a list of (value, name) tuples for the list of datapoints
411 which the user selects from to create new graphpoints.
412 '''
413 return [(dp.name(), dp.name())
414 for dp in self.rrdTemplate.getRRDDataPoints()]
415
416
418 ''' Return a list of (value, name) tuples for the list of thresholds
419 which the user selects from to create new graphpoints.
420 '''
421 return [(t.id, t.id) for t in self.rrdTemplate.thresholds()]
422
423
424 ## Graphing Support
425
426
427 - def getGraphCmds(self, context, rrdDir, multiid=-1, upToPoint=None,
428 includeSetup=True, includeThresholds=True,
429 prefix='', cmds=None, idxOffset=0):
430 """build the graph opts for a single rrdfile"""
431 from Products.ZenUtils.ZenTales import talesEval
432 if not cmds:
433 cmds = []
434 if includeSetup:
435 cmds += self.graphsetup()
436
437 # Have to draw thresholds before data so that thresholds won't
438 # obscure data (especially if threshold uses TICK)
439 if includeThresholds:
440 threshGps = [gp for gp in self.getThresholdGraphPoints()
441 if upToPoint is None or gp.sequence < upToPoint]
442 if threshGps:
443 for index, gp in enumerate(threshGps):
444 try:
445 cmds = gp.getGraphCmds(cmds, context, rrdDir,
446 self.hasSummary, index+idxOffset,
447 multiid, prefix)
448 except (KeyError, NameError), e:
449 cmds.append('COMMENT: UNKOWN VALUE IN '
450 'GRAPHPOINT %s\: %s' % (gp.id, str(e)))
451 gpList = [gp for gp in self.getGraphPoints(includeThresholds=False)
452 if upToPoint is None or gp.sequence < upToPoint]
453 for index, gp in enumerate(gpList):
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 GRAPHPOINT '
460 '%s\: %s' % (gp.id, str(e)))
461 if self.custom and includeSetup \
462 and not upToPoint:
463 try:
464 res = talesEval("string:"+str(self.custom), context)
465 except (KeyError, NameError), e:
466 res = 'COMMENT:UNKNOWN VALUE IN CUSTOM COMMANDS\: %s' % str(e)
467 res = [l for l in res.split('\n') if l.strip()]
468 cmds.extend(res)
469 #if self.hasSummary:
470 # cmds = self.addSummary(cmds)
471
472 return cmds
473
474
476 ''' Return list of rrd variable names that are defined by DEF, CDEF
477 or VDEF statements in the rrd commands. If upToPoint is not None then
478 only consider statements generated by graphoints where
479 sequence < upToPoint
480 '''
481 cmds = self.getFakeGraphCmds(upToPoint=upToPoint)
482 names = [line[line.find(':')+1:line.find('=')]
483 for line in cmds.split('\n')
484 if line[:line.find(':')] in ('DEF', 'CDEF', 'VDEF')]
485 return names
486
487
489 ''' Used to display the graph commands (or a reasonable likeness)
490 to the user
491 '''
492 context = FakeContext('Context')
493 cmds = self.getGraphCmds(context, context.rrdPath(), upToPoint=upToPoint)
494 cmds = '\n'.join(cmds)
495 return cmds
496
497
499 """Setup global graph parameters.
500 """
501 gopts = ['-F', '-E']
502 if self.height:
503 gopts.append('--height=%d' % self.height)
504 if self.width:
505 gopts.append('--width=%d' % self.width)
506 if self.log:
507 gopts.append('--logarithmic')
508 if self.maxy > -1:
509 gopts.append('--upper-limit=%d' % self.maxy)
510 gopts.append('--rigid')
511 if self.miny > -1:
512 gopts.append('--lower-limit=%d' % self.miny)
513 gopts.append('--rigid')
514 # Always include a vertical label so that multiple graphs on page
515 # align correctly.
516 gopts.append('--vertical-label=%s' % (self.units or ' '))
517 if self.units == 'percentage':
518 if not self.maxy > -1:
519 gopts.append('--upper-limit=100')
520 if not self.miny > -1:
521 gopts.append('--lower-limit=0')
522 if self.base:
523 gopts.append('--base=1024')
524 gopts = [str(o) for o in gopts]
525 return gopts
526
527
529 ''' Return a list of DataPointGraphPoints that use the given dpName
530 '''
531 from DataPointGraphPoint import DataPointGraphPoint
532 return [gp for gp in self.graphPoints()
533 if isinstance(gp, DataPointGraphPoint)
534 and gp.dpName == dpName]
535
536
538 '''
539 Get a list of all unique datapoint names
540 '''
541 from sets import Set
542 dpNames = Set()
543 for t in self.dmd.Devices.getAllRRDTemplates():
544 for ds in t.datasources():
545 # If we have a broken datasource (likely from a missing zenpack)
546 # then don't try to parse datapoints, you can't.
547 if hasattr(ds, 'datapoints'):
548 for dp in ds.datapoints():
549 dpNames.add(dp.name())
550 if limit and len(dpNames) >= limit:
551 break
552 dpNames = list(dpNames)
553 dpNames.sort()
554 return dpNames
555
556
558 '''
559 Get a list of all unique threshold names
560 '''
561 from sets import Set
562 names = Set()
563 for t in self.dmd.Devices.getAllRRDTemplates():
564 for thresh in t.thresholds():
565 names.add(thresh.id)
566 if len(names) >= limit:
567 break
568 if len(names) >= limit:
569 break
570 names = list(names)
571 names.sort()
572 return names
573
574
575 InitializeClass(GraphDefinition)
576
| Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0beta1 on Thu May 7 11:46:20 2009 | http://epydoc.sourceforge.net |