| 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 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
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
65
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
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
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
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
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
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
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
221 return [gi.getDescription() for gi in self.graphPoints()]
222
223
228
229
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
| Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0beta1 on Thu Oct 25 16:28:42 2007 | http://epydoc.sourceforge.net |