1
2
3
4
5
6
7
8
9
10
11
12
13 __doc__="""AliasPlugin
14
15 In order to more easily create reports, there is now a base class (AliasPlugin)
16 that plugins can subclass and provide minimal information. The plugin is
17 meant to be run from an rpt file.
18 """
19 import Globals
20
21 from Products.ZenUtils.ZenTales import talesEval, talesEvalStr
22 from Products.ZenModel.RRDDataPoint import getDataPointsByAliases
23 from Products.ZenModel.RRDDataPointAlias import EVAL_KEY
24 from Products.ZenReports import Utils, Utilization
25
27 """
28 Represents a column in a report row. Returns a value when given the
29 context represented by the row. For example, a brain-dead report
30 might list the paths of all the devices in the system. A Column object
31 that represents the path column would know how to return the path given
32 the device.
33 """
34 - def __init__(self, columnName, columnHandler=None):
35 """
36 @param columnName: the name of the column
37 @param columnHandler: optional object or method that knows
38 how to take the row context and return
39 the column value
40 """
41 self._columnName = columnName
42 self._columnHandler = columnHandler
43
45 return self._columnName
46
47 - def getValue(self, device, component=None, extra=None ):
48 """
49 @param device: the device represented by this row
50 @param component: the component represented by this row (if present)
51 @param extra: extra context passed to the columnHandler that will
52 generate the column value
53 """
54 value = None
55 if self._columnHandler is not None:
56 value = self._columnHandler( device, component, extra )
57 return value
58
60 """
61 @return the alias that this column uses (if any)
62 """
63
64
65
66 return getattr( self._columnHandler, 'aliasName', None )
67
69 """
70 Generates the alias RPN formula with the entity as the context, then
71 retrieves the RRD value of the datapoint with the RPN formula
72 """
73 if alias:
74 summary['extraRpn'] = alias.evaluate( entity )
75 return entity.getRRDValue( datapoint.id, **summary )
76
77
79 """
80 A handler that return RRD data for the value given the row context
81 """
83 """
84 @param aliasName: the alias or datapoint name to fetch
85 @param columnHandler: optional columnHandler method or object
86 for post-processing of the RRD data
87 """
88 self.aliasName = aliasName
89
90 - def __call__( self, device, component=None, extra=None ):
91 """
92 @param device: the device represented by this row
93 @param component: the component represented by this row (if present)
94 @param extra: extra context passed to the columnHandler that will
95 generate the column value
96 """
97
98 summary=extra['summary']
99
100
101
102
103 aliasDatapointPairs=extra['aliasDatapointPairs']
104 if aliasDatapointPairs is None or len( aliasDatapointPairs ) == 0:
105 return None
106
107 value = None
108
109 perfObject = component or device
110 deviceTemplates = perfObject.getRRDTemplates()
111 for alias, datapoint in aliasDatapointPairs:
112 template = datapoint.datasource().rrdTemplate()
113
114
115
116 if template in deviceTemplates:
117 value = _fetchValueWithAlias( perfObject, datapoint,
118 alias, summary )
119
120 if value is not None:
121 break
122
123 return value
124
125
127 """
128 A column handler accepts row context (like a device, component, or extra
129 information) and returns the column value. This class specifically
130 executes a python expression that can use the row context.
131
132 The row context includes the device object ('device') and, if available,
133 the component object ('component'). It also includes report information
134 such as the start date ('start') and end date ('end') of the report.
135 """
136 - def __init__(self, talesExpression, extraContext={}):
137 """
138 @param talesExpression: A python expression that can use the context
139 """
140 self._talesExpression = 'python:%s' % talesExpression
141 self._extraContext = extraContext
142
143 - def __call__(self, device, component=None, extra=None, value=None ):
149
150
152 """
153 A base class for performance report plugins that use aliases to
154 choose datapoints
155 """
162
164 """
165 Return the mapping of aliases to column names. This should be one
166 to one. This is unimplemented in the base class.
167
168 This is meant to be overridden.
169 """
170 raise Exception( 'Unimplemented: Only subclasses of AliasPlugin' +
171 ' should be instantiated directly' )
172
174 """
175 Return columns that will be evaluated in order and have access to
176 the previous column values. TalesColumnHandlers will have the previous
177 column values for this row in their context.
178
179 For example, if one column named 'cpuIdle' evaluates to .36,
180 a composite column named 'cpuUtilized' can be created with
181 a TalesColumnHandler with the expression '1 - cpuIdle'.
182
183 NOTE: These cannot be RRD columns (or rather, RRD columns will fail
184 because no datapoints will be passed to them)
185
186 This is meant to be overridden (if needed).
187 """
188 return []
189
191 """
192 If the rows in the report represent components, this is how to
193 get from a device to the appropriate components.
194
195 This is meant to be overridden for component reports.
196 """
197 return None
198
199
200 - def _createRecord(self, device, component=None,
201 columnDatapointsMap={}, summary={} ):
202 """
203 Creates a record for the given row context
204 (that is, the device and/or component)
205
206 @param device: the device for this row
207 @param component: the (optional) component for this row
208 @param columnDatapointsMap: a dict of Column objects to
209 alias-datapoint pairs. The first
210 datapoint that gives a value for
211 the context will be used.
212 @param summary: a dict of report parameters like start date,
213 end date, and rrd summary function
214 @rtype L{Utils.Record}
215 """
216 def localGetValue( device, component, extra ):
217 try:
218 val=column.getValue( device, component,
219 extra=extra )
220 except TypeError:
221 val=None
222 except NameError:
223 val=None
224 return val
225 columnValueMap = {}
226 for column, aliasDatapointPairs in columnDatapointsMap.iteritems():
227 columnName = column.getColumnName()
228 extra=dict( aliasDatapointPairs=aliasDatapointPairs,
229 summary=summary )
230 value = localGetValue( device, component, extra )
231 columnValueMap[columnName] = value
232
233
234
235 extra=dict( summary=summary )
236 extra.update( columnValueMap )
237 for column in self.getCompositeColumns():
238 columnName = column.getColumnName()
239 value = localGetValue( device, component, extra )
240 columnValueMap[columnName]=value
241 extra.update( { columnName : value } )
242
243 return Utils.Record(device=device, component=component,
244 **columnValueMap)
245
247 """
248 Create a map of columns->alias/datapoint pairs. For each
249 column we need both the datapoint/alias-- the datapoint to
250 retrieve the rrd data and the alias to execute the alias
251 formula to transform that data (to get the correct units).
252
253 Non-perf columns will be mapped to None
254 """
255 def getAliasName( column ):
256 return column.getAliasName()
257
258
259 columns = self.getColumns()
260 nonAliasColumns = filter( lambda x: getAliasName( x ) is None,
261 columns )
262 aliasColumns = filter( lambda x: getAliasName( x ) is not None,
263 columns )
264
265
266 aliasColumnMap = dict( zip( map( getAliasName, aliasColumns ),
267 aliasColumns ) )
268
269 columnDatapointsMap = {}
270
271
272
273
274 for column in columns:
275 columnDatapointsMap[column] = []
276
277
278
279 aliasDatapointPairs = getDataPointsByAliases( dmd,
280 aliasColumnMap.keys() )
281 for alias, datapoint in aliasDatapointPairs:
282 if alias:
283 column = aliasColumnMap[ alias.id ]
284 else:
285
286
287
288 column = aliasColumnMap[ datapoint.id ]
289
290 columnDatapointsMap[column].append( (alias,datapoint) )
291
292 return columnDatapointsMap
293
294 - def run(self, dmd, args):
295 """
296 Generate the report using the columns and aliases
297
298 @param dmd the dmd context to access the context objects
299 @param args the report args from the ui
300
301 @rtype a list of L{Utils.Record}s
302 """
303
304 summary = Utilization.getSummaryArgs(dmd, args)
305
306
307
308 columnDatapointsMap = self._mapColumnsToDatapoints( dmd )
309
310
311 if args.get('deviceClass', '/') == '/':
312 return []
313
314 componentPath = self.getComponentPath()
315
316
317
318 report = []
319 for device in Utilization.filteredDevices(dmd, args):
320 if componentPath is None:
321
322 record = self._createRecord( device, None,
323 columnDatapointsMap, summary )
324 report.append(record)
325
326 else:
327 components = self._getComponents( device,
328 componentPath )
329 for component in components:
330 record = self._createRecord(device, component,
331 columnDatapointsMap, summary)
332 report.append(record)
333
334 return report
335