40 slope_mode = True, |
40 slope_mode = True, |
41 ) |
41 ) |
42 |
42 |
43 def overview_opts () : |
43 def overview_opts () : |
44 """ |
44 """ |
45 Common options for the overview graph |
45 Graph statements for a single-source overview graph |
46 """ |
46 """ |
47 |
47 |
48 return dict( |
48 return dict( |
49 width = 600, |
49 width = 600, |
50 height = 50, |
50 height = 50, |
51 ), [ |
51 ), [ |
52 "CDEF:all=in,out,+", |
52 "CDEF:all=in0,out0,+", |
53 |
53 |
54 "VDEF:max=all,MAXIMUM", |
54 "VDEF:max=all,MAXIMUM", |
55 "VDEF:avg=all,AVERAGE", |
55 "VDEF:avg=all,AVERAGE", |
56 "VDEF:min=all,MINIMUM", |
56 "VDEF:min=all,MINIMUM", |
57 |
57 |
58 "LINE1:in#0000FF:In", |
58 "LINE1:in0#0000FF:In", |
59 "LINE1:out#00CC00:Out", |
59 "LINE1:out0#00CC00:Out", |
60 |
60 |
61 "GPRINT:max:%6.2lf %Sbps max", |
61 "GPRINT:max:%6.2lf %Sbps max", |
62 "GPRINT:avg:%6.2lf %Sbps avg", |
62 "GPRINT:avg:%6.2lf %Sbps avg", |
63 "GPRINT:min:%6.2lf %Sbps min\\l", |
63 "GPRINT:min:%6.2lf %Sbps min\\l", |
64 ] |
64 ] |
65 |
65 |
66 def detail_opts () : |
66 def detail_opts () : |
67 """ |
67 """ |
68 Common options for the detail graph |
68 Common options for a single-source detail graph |
69 """ |
69 """ |
70 |
70 |
71 return dict( |
71 return dict( |
72 # dimensions |
72 # dimensions |
73 width = 600, |
73 width = 600, |
74 height = 200, |
74 height = 200, |
75 ), [ |
75 ), [ |
76 # values |
76 # values |
77 'VDEF:in_max=in,MAXIMUM', |
77 'VDEF:in_max=in0,MAXIMUM', |
78 'VDEF:in_avg=in,AVERAGE', |
78 'VDEF:in_avg=in0,AVERAGE', |
79 'VDEF:in_min=in,MINIMUM', |
79 'VDEF:in_min=in0,MINIMUM', |
80 'VDEF:in_cur=in,LAST', |
80 'VDEF:in_cur=in0,LAST', |
81 'VDEF:out_max=out,MAXIMUM', |
81 'VDEF:out_max=out0,MAXIMUM', |
82 'VDEF:out_avg=out,AVERAGE', |
82 'VDEF:out_avg=out0,AVERAGE', |
83 'VDEF:out_min=out,MINIMUM', |
83 'VDEF:out_min=out0,MINIMUM', |
84 'VDEF:out_cur=out,LAST', |
84 'VDEF:out_cur=out0,LAST', |
85 |
85 |
86 # legend/graph |
86 # legend/graph |
87 "COMMENT:%4s" % "", |
87 "COMMENT:%4s" % "", |
88 "COMMENT:%11s" % "Maximum", |
88 "COMMENT:%11s" % "Maximum", |
89 "COMMENT:%11s" % "Average", |
89 "COMMENT:%11s" % "Average", |
90 "COMMENT:%11s" % "Minimum", |
90 "COMMENT:%11s" % "Minimum", |
91 "COMMENT:%11s\\l" % "Current", |
91 "COMMENT:%11s\\l" % "Current", |
92 |
92 |
93 "LINE1:in#0000FF:%4s" % "In", |
93 "LINE1:in0#0000FF:%4s" % "In", |
94 'GPRINT:in_max:%6.2lf %Sbps', |
94 'GPRINT:in_max:%6.2lf %Sbps', |
95 'GPRINT:in_avg:%6.2lf %Sbps', |
95 'GPRINT:in_avg:%6.2lf %Sbps', |
96 'GPRINT:in_min:%6.2lf %Sbps', |
96 'GPRINT:in_min:%6.2lf %Sbps', |
97 'GPRINT:in_cur:%6.2lf %Sbps\\l', |
97 'GPRINT:in_cur:%6.2lf %Sbps\\l', |
98 |
98 |
99 "LINE1:out#00CC00:%4s" % "Out", |
99 "LINE1:out0#00CC00:%4s" % "Out", |
100 'GPRINT:out_max:%6.2lf %Sbps', |
100 'GPRINT:out_max:%6.2lf %Sbps', |
101 'GPRINT:out_avg:%6.2lf %Sbps', |
101 'GPRINT:out_avg:%6.2lf %Sbps', |
102 'GPRINT:out_min:%6.2lf %Sbps', |
102 'GPRINT:out_min:%6.2lf %Sbps', |
103 'GPRINT:out_cur:%6.2lf %Sbps\\l', |
103 'GPRINT:out_cur:%6.2lf %Sbps\\l', |
104 |
104 |
105 # mark |
105 # mark |
106 "COMMENT:Generated %s\\r" % timestamp().replace(':', '\\:'), |
106 "COMMENT:Generated %s\\r" % timestamp().replace(':', '\\:'), |
107 ] |
107 ] |
108 |
108 |
|
109 # set of line colors used for a multi-source graph, in rgb hex form (no prefix) |
|
110 MULTI_COLORS = ( |
|
111 '00cc00', |
|
112 '0000ff', |
|
113 'cc0000', |
|
114 ) |
|
115 |
|
116 def multi_graph_source_defs (idx, name, color) : |
|
117 """ |
|
118 Render graph and legend summary for a single source in a multi-source graph. |
|
119 """ |
|
120 |
|
121 params = dict( |
|
122 idx = idx, name = name, color = color |
|
123 ) |
|
124 |
|
125 return [stmt.format(**params) for stmt in ( |
|
126 "CDEF:all{idx}=in{idx},out{idx},+", |
|
127 |
|
128 "VDEF:max{idx}=all{idx},MAXIMUM", |
|
129 "VDEF:avg{idx}=all{idx},AVERAGE", |
|
130 "VDEF:cur{idx}=all{idx},LAST", |
|
131 |
|
132 "LINE1:all{idx}#{color}: ", |
|
133 |
|
134 "GPRINT:max{idx}:%6.2lf %Sbps", |
|
135 "GPRINT:avg{idx}:%6.2lf %Sbps", |
|
136 "GPRINT:cur{idx}:%6.2lf %Sbps", |
|
137 "COMMENT:\t{name}\\l", |
|
138 )] |
|
139 |
|
140 |
|
141 def multi_graph (sources) : |
|
142 """ |
|
143 Graph definition for a multi-source overview graph. |
|
144 |
|
145 Uses combined in/out totals, giving a single line per source. |
|
146 """ |
|
147 |
|
148 # defs for each source |
|
149 sources_defs = [multi_graph_source_defs(idx, name, color) for (idx, name), color in zip(sources, MULTI_COLORS)] |
|
150 |
|
151 return dict( |
|
152 # dimensions |
|
153 width = 600, |
|
154 height = 200, |
|
155 |
|
156 ), [ |
|
157 # legend header |
|
158 "COMMENT: ", |
|
159 "COMMENT:%11s" % "Maximum", |
|
160 "COMMENT:%11s" % "Average", |
|
161 "COMMENT:%11s" % "Current", |
|
162 "COMMENT:\t%s\\l" % "", |
|
163 |
|
164 ] + [ |
|
165 # each source |
|
166 stmt for defs in sources_defs for stmt in defs |
|
167 ] |
|
168 |
109 STYLE_DEFS = { |
169 STYLE_DEFS = { |
110 'overview': overview_opts, |
170 'overview': overview_opts, |
111 'detail': detail_opts, |
171 'detail': detail_opts, |
112 } |
172 } |
113 |
173 |
|
174 def hourly_opts (title) : |
|
175 |
|
176 return dict( |
|
177 # labels |
|
178 x_grid = None, |
|
179 |
|
180 # general info |
|
181 title = "Hourly %s" % (title, ), |
|
182 |
|
183 # interval |
|
184 start = "-1h", |
|
185 ) |
|
186 |
114 def daily_opts (title) : |
187 def daily_opts (title) : |
115 """ |
188 """ |
116 Common options for the 'daily' view |
189 Common options for the 'daily' view |
117 """ |
190 """ |
118 |
191 |
151 start = "-1y", |
224 start = "-1y", |
152 ) |
225 ) |
153 |
226 |
154 |
227 |
155 INTERVAL_DEFS = { |
228 INTERVAL_DEFS = { |
|
229 'hourly': hourly_opts, |
156 'daily': daily_opts, |
230 'daily': daily_opts, |
157 'weekly': weekly_opts, |
231 'weekly': weekly_opts, |
158 'yearly': yearly_opts, |
232 'yearly': yearly_opts, |
159 } |
233 } |
160 |
234 |
161 def mrtg_data (rrd_path) : |
235 def data_defs (idx, rrd, ds_in, ds_out, bytes=True, cf='AVERAGE') : |
162 """ |
236 """ |
163 Data sources for network in/out from an MRTG rrd |
237 Generate the DEF/CDEF statements for the in{idx}/out{idx} data sources for the given RRD and DS names. |
164 """ |
238 |
165 |
239 If bytes is given, convert the value into bits. |
166 return [ |
240 """ |
|
241 |
|
242 params = dict( |
|
243 ds_in=ds_in, ds_out=ds_out, cf=cf, |
|
244 idx=idx, rrd=rrd, |
|
245 ) |
|
246 |
|
247 if bytes : |
167 # data sources, bytes/s |
248 # data sources, bytes/s |
168 r'DEF:in0=%s:ds0:AVERAGE' % rrd_path, |
249 yield 'DEF:in_raw{idx}={rrd}:{ds_in}:{cf}'.format(**params) |
169 r'DEF:out0=%s:ds1:AVERAGE' % rrd_path, |
250 yield 'DEF:out_raw{idx}={rrd}:{ds_out}:{cf}'.format(**params) |
170 |
251 |
171 # data, bits/s |
252 # data, bits/s |
172 'CDEF:in=in0,8,*', |
253 yield 'CDEF:in{idx}=in_raw{idx},8,*'.format(**params) |
173 'CDEF:out=out0,8,*', |
254 yield 'CDEF:out{idx}=out_raw{idx},8,*'.format(**params) |
174 |
255 |
175 ] |
256 else : |
176 |
257 # data sources, bits/s |
177 def collectd_data (rrd_path) : |
258 yield 'DEF:in{idx}={rrd}:{ds_in}:{cf}'.format(**params) |
|
259 yield 'DEF:out{idx}={rrd}:{ds_out}:{cf}'.format(**params) |
|
260 |
|
261 |
|
262 def mrtg_data (idx, rrd) : |
|
263 """ |
|
264 Generate the in{idx}/out{idx} data sources fro the given MRTG rrd. |
|
265 """ |
|
266 |
|
267 return data_defs(idx, rrd, 'ds0', 'ds1') |
|
268 |
|
269 def collectd_data (idx, rrd) : |
178 """ |
270 """ |
179 Data sources for if_octets from a collectd rrd |
271 Data sources for if_octets from a collectd rrd |
180 """ |
272 """ |
181 |
273 |
182 return [ |
274 return data_defs(idx, rrd, 'rx', 'tx') |
183 # data sources, bytes/s |
275 |
184 r'DEF:in0=%s:rx:AVERAGE' % rrd_path, |
276 def pmacct_data (idx, rrd) : |
185 r'DEF:out0=%s:tx:AVERAGE' % rrd_path, |
277 """ |
186 |
278 Data sources for in/out bytes from a pmacct rrd |
187 # data, bits/s |
279 """ |
188 'CDEF:in=in0,8,*', |
280 |
189 'CDEF:out=out0,8,*', |
281 return data_defs(idx, rrd, 'in', 'out') |
190 |
282 |
191 ] |
283 def graph_single (style, interval, title, data_func, rrd_path, out_path) : |
192 |
284 """ |
193 def _graph (style, interval, title, data_func, rrd_path, out_path) : |
285 Render graph. |
|
286 |
|
287 Returns a (width, height, print_lines, graph_file) tuple. |
|
288 """ |
|
289 |
194 style_opts, style_vars = STYLE_DEFS[style]() |
290 style_opts, style_vars = STYLE_DEFS[style]() |
195 interval_opts = INTERVAL_DEFS[interval](title) |
291 interval_opts = INTERVAL_DEFS[interval](title) |
196 |
292 |
197 opts = rrd.merge_opts(common_opts(), style_opts, interval_opts) |
293 opts = rrd.merge_opts(common_opts(), style_opts, interval_opts) |
198 data = data_func(rrd_path) + style_vars |
294 data = list(data_func(0, rrd_path)) + style_vars |
199 |
295 |
200 return rrd.graph(out_path, *data, **opts) |
296 return rrd.graph(out_path, *data, **opts) |
201 |
297 |
|
298 def graph_multi (interval, title, rrd_list, data_func, out) : |
|
299 """ |
|
300 Render a multi-source graph. |
|
301 |
|
302 interval - the name of the time interval to use |
|
303 title - graph title |
|
304 rrd_list - sequence of (rrd, name) tuples for each source to draw |
|
305 data_func - the data source definition to use |
|
306 out - output path for graph |
|
307 """ |
|
308 |
|
309 # data sources |
|
310 data_defs = [stmt for idx, (path, name) in enumerate(rrd_list) for stmt in data_func(idx, path)] |
|
311 |
|
312 # options |
|
313 graph_opts, graph_defs = multi_graph([(idx, name) for idx, (path, name) in enumerate(rrd_list)]) |
|
314 interval_opts = INTERVAL_DEFS[interval](title) |
|
315 |
|
316 # combine |
|
317 opts = rrd.merge_opts(common_opts(), graph_opts, interval_opts) |
|
318 defs = data_defs + graph_defs |
|
319 |
|
320 # graph |
|
321 return rrd.graph(out, *defs, **opts) |
202 |
322 |
203 def mrtg (style, interval, title, rrd_path, out_path) : |
323 def mrtg (style, interval, title, rrd_path, out_path) : |
204 return _graph(style, interval, title, mrtg_data, rrd_path, out_path) |
324 return _graph(style, interval, title, mrtg_data, rrd_path, out_path) |
205 |
325 |
206 def collectd_ifoctets (style, interval, title, rrd_path, out_path) : |
326 def collectd_ifoctets (style, interval, title, rrd_path, out_path) : |
207 return _graph(style, interval, title, collectd_data, rrd_path, out_path) |
327 return _graph(style, interval, title, collectd_data, rrd_path, out_path) |
208 |
328 |
|
329 def pmacct_bytes (style, interval, title, rrd_path, out_path) : |
|
330 return _graph(style, interval, title, pmacct_data, rrd_path, out_path) |
|
331 |
|
332 |