author | Tero Marttila <terom@fixme.fi> |
Tue, 25 Jan 2011 01:28:06 +0200 | |
changeset 32 | 47e977c23ba2 |
parent 24 | 29a523db66a8 |
permissions | -rw-r--r-- |
22 | 1 |
""" |
2 |
Dynamic frontend |
|
3 |
""" |
|
4 |
||
5 |
import werkzeug |
|
6 |
from werkzeug import exceptions |
|
7 |
from werkzeug import Request, Response |
|
8 |
from werkzeug.routing import Map, Rule |
|
9 |
||
32
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
10 |
from rrdweb import html, graph, backend |
22 | 11 |
|
12 |
import os, os.path |
|
13 |
import errno |
|
14 |
import logging |
|
15 |
||
16 |
||
17 |
# logging |
|
18 |
log = logging.getLogger('rrdweb.wsgi') |
|
19 |
||
20 |
||
21 |
class WSGIApp (object) : |
|
32
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
22 |
def __init__ (self, rrdpath, tplpath, imgpath, rrdgraph=graph.pmacct_bytes) : |
22 | 23 |
""" |
24 |
Configure |
|
25 |
||
26 |
rrdpath - path to directory containing *.rrd files |
|
27 |
tplpath - path to HTML templates |
|
28 |
imgpath - path to generated PNG images. Must be writeable |
|
32
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
29 |
|
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
30 |
rrdgraph - the graph.*_data function for rendering the graphs. |
22 | 31 |
""" |
32 |
||
33 |
self.rrdpath = os.path.abspath(rrdpath) |
|
34 |
self.templates = html.BaseFormatter(tplpath) |
|
32
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
35 |
self.imgpath = os.path.abspath(imgpath) |
22 | 36 |
|
32
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
37 |
self.rrd_graph_func = rrdgraph |
22 | 38 |
|
39 |
||
40 |
# wrap to use werkzeug's Request/Response |
|
41 |
@Request.application |
|
42 |
def __call__ (self, req) : |
|
43 |
""" |
|
44 |
Main WSGI entry point |
|
45 |
""" |
|
46 |
||
47 |
try : |
|
48 |
response = self.request(req) |
|
49 |
||
50 |
except exceptions.HTTPException, e : |
|
51 |
# format as response |
|
52 |
return e.get_response(req.environ) |
|
53 |
||
54 |
else : |
|
55 |
# a-ok |
|
56 |
return response |
|
57 |
||
58 |
||
59 |
def request (self, req) : |
|
60 |
""" |
|
61 |
Wrapped request handler |
|
62 |
""" |
|
63 |
||
64 |
||
65 |
# map URLs against this request |
|
66 |
urls = self.URLS.bind_to_environ(req) |
|
67 |
||
68 |
# lookup URL against endpoint and dict of matched values from URL |
|
69 |
endpoint, args = urls.match() |
|
70 |
||
71 |
def build_url (method, **args) : |
|
72 |
""" |
|
73 |
Small wrapper around Werkzeug's routing.MapAdapter.build to suit our puroses |
|
74 |
""" |
|
75 |
||
76 |
return urls.build(method.im_func, args) |
|
77 |
||
78 |
# invoke |
|
79 |
# XXX: non-methods? |
|
80 |
response = endpoint(self, req, build_url, **args) |
|
81 |
||
82 |
return response |
|
83 |
||
32
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
84 |
def fs_path (self, name) : |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
85 |
""" |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
86 |
Lookup and return the full filesystem path to the given relative RRD file/dir. |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
87 |
|
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
88 |
The given name must be a relative path (no leading /). |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
89 |
|
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
90 |
Raises NotFound for invalid paths. |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
91 |
""" |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
92 |
|
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
93 |
# full path |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
94 |
path = os.path.normpath(os.path.join(self.rrdpath, name)) |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
95 |
|
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
96 |
# check inside base path |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
97 |
if not path.startswith(self.rrdpath) : |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
98 |
# not found |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
99 |
raise exceptions.NotFound(name) |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
100 |
|
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
101 |
# ok |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
102 |
return path |
22 | 103 |
|
104 |
def scan_dir (self, dir) : |
|
105 |
""" |
|
106 |
Scan for RRD files and subdirectories directly underneath the given path. |
|
107 |
||
108 |
Returns a ([subdir_name], [rrd_name]) tuple, with the sorted lists of subdirs and rrds. |
|
109 |
""" |
|
110 |
||
111 |
# we need to do this procedurally, because we collect two lists :( |
|
112 |
subdirs = [] |
|
113 |
rrds = [] |
|
114 |
||
115 |
log.debug("Scanning dir %s", dir) |
|
116 |
||
117 |
for name in os.listdir(dir) : |
|
118 |
# skip hidden files |
|
119 |
if name.startswith('.') : |
|
120 |
continue |
|
121 |
||
122 |
# path to file |
|
123 |
path = os.path.join(dir, name) |
|
124 |
||
125 |
# possible extesion |
|
126 |
basename, extname = os.path.splitext(name) |
|
127 |
||
128 |
log.debug("\tname=%s - %s", name, extname) |
|
129 |
||
130 |
# collect subdirs |
|
131 |
if os.path.isdir(path) : |
|
132 |
subdirs.append(name) |
|
133 |
||
134 |
# log.debug("\tsubdir: %s", name) |
|
135 |
||
136 |
# collect .rrd's |
|
137 |
elif extname == '.rrd' : |
|
138 |
# without the .rrd |
|
139 |
rrds.append(basename) |
|
140 |
||
141 |
# log.debug("\trrd: %s", basename) |
|
142 |
||
143 |
# return sorted lists |
|
144 |
subdirs.sort() |
|
145 |
rrds.sort() |
|
146 |
||
147 |
return subdirs, rrds |
|
148 |
||
149 |
def fmt_page (self, breadcrumb, content) : |
|
150 |
""" |
|
151 |
Render page with master layout as HTML. |
|
152 |
||
153 |
breadcrumb - breadcrumb nav as HTML |
|
154 |
content - main content as HTML |
|
155 |
""" |
|
156 |
||
157 |
log.debug("content = %r", content) |
|
158 |
||
159 |
return self.templates.render('layout', |
|
160 |
title = "MRTG", # XXX: some context |
|
161 |
breadcrumb = breadcrumb, |
|
162 |
content = content |
|
163 |
) |
|
164 |
||
165 |
def fmt_breadcrumb_segment (self, url, node, name) : |
|
166 |
""" |
|
167 |
Render the breadcrumb link for the given node as HTML. |
|
168 |
""" |
|
169 |
||
170 |
# real path |
|
171 |
path = self.fs_path(node) |
|
172 |
||
173 |
if os.path.isdir(path) : |
|
174 |
# index |
|
175 |
return dict( |
|
176 |
url = url(self.index, dir=node), |
|
177 |
name = name + '/', |
|
178 |
) |
|
179 |
||
180 |
else : |
|
181 |
# XXX: assume .rrd |
|
182 |
||
183 |
return dict( |
|
184 |
url = url(self.target, rrd=node), |
|
185 |
name = name, |
|
186 |
) |
|
187 |
||
188 |
def fmt_breadcrumb_segments (self, url, segments) : |
|
189 |
""" |
|
190 |
Render a sequence of segments for each of the nodes in the given list of segments. |
|
191 |
""" |
|
192 |
||
193 |
# root node |
|
194 |
yield dict( |
|
195 |
url = url(self.index), |
|
196 |
name = "MRTG" |
|
197 |
) |
|
198 |
||
199 |
path = '' |
|
200 |
||
201 |
for segment in segments : |
|
202 |
# cumulative path |
|
203 |
path = os.path.join(path, segment) |
|
204 |
||
205 |
# format the induvidual node |
|
206 |
yield self.fmt_breadcrumb_segment(url, path, segment) |
|
207 |
||
208 |
||
209 |
def fmt_breadcrumb (self, url, node) : |
|
210 |
""" |
|
211 |
Render the breadcrumb for the given node's path as HTML. |
|
212 |
""" |
|
213 |
||
214 |
# split path to node into segments |
|
215 |
segments = node.split('/') |
|
216 |
||
217 |
log.debug("%r -> %r", node, segments) |
|
218 |
||
219 |
# join segments together as hrefs |
|
220 |
return " » ".join( |
|
221 |
'<a href="%(url)s">%(name)s</a>' % html for html in self.fmt_breadcrumb_segments(url, segments) |
|
222 |
) |
|
223 |
||
224 |
||
225 |
def fmt_overview (self, url, dir, subdirs, rrds) : |
|
226 |
""" |
|
227 |
Render overview page listing given RRDs to HTML. |
|
228 |
""" |
|
229 |
||
230 |
if not dir : |
|
231 |
dir = '/' |
|
232 |
||
233 |
log.debug("Overview for %r with %d subdirs and %d rrds", dir, len(subdirs), len(rrds)) |
|
234 |
||
235 |
return self.templates.render('overview', |
|
236 |
dir = dir, |
|
237 |
overview_subdirs = '\n'.join( |
|
238 |
self.fmt_overview_subdir(url, subdir, os.path.join(dir, subdir)) for subdir in subdirs |
|
239 |
), |
|
240 |
overview_graphs = '\n'.join( |
|
24 | 241 |
self.fmt_overview_target(url, os.path.join(dir, rrd), idx) for idx, rrd in enumerate(rrds) |
22 | 242 |
), |
243 |
) |
|
244 |
||
245 |
||
246 |
def fmt_overview_subdir (self, url, subdir, dir) : |
|
247 |
""" |
|
248 |
Render overview item for given subdir to HTML. |
|
249 |
""" |
|
250 |
||
251 |
return self.templates.render('overview-subdir', |
|
252 |
dir_url = url(self.index, dir=dir), |
|
253 |
dir_name = subdir, |
|
254 |
) |
|
255 |
||
256 |
||
24 | 257 |
def fmt_overview_target (self, url, rrd, idx) : |
22 | 258 |
""" |
259 |
Render overview item for given target to HTML. |
|
260 |
""" |
|
261 |
||
262 |
return self.templates.render('overview-target', |
|
24 | 263 |
oddeven = 'odd' if idx % 2 else 'even', |
22 | 264 |
title = self.rrd_title(rrd), |
265 |
target_url = url(self.target, rrd=rrd), |
|
266 |
daily_overview_img = url(self.graph, rrd=rrd, style='overview'), |
|
267 |
) |
|
268 |
||
269 |
||
270 |
def fmt_target (self, url, rrd) : |
|
271 |
""" |
|
272 |
Render target overview page to HTML. |
|
273 |
""" |
|
274 |
||
275 |
return self.templates.render('target', |
|
276 |
title = self.rrd_title(rrd), |
|
32
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
277 |
hourly_img = url(self.graph, rrd=rrd, style='detail', interval='hourly'), |
22 | 278 |
daily_img = url(self.graph, rrd=rrd, style='detail', interval='daily'), |
279 |
weekly_img = url(self.graph, rrd=rrd, style='detail', interval='weekly'), |
|
280 |
yearly_img = url(self.graph, rrd=rrd, style='detail', interval='yearly'), |
|
281 |
) |
|
282 |
||
283 |
||
284 |
def rrd_path (self, rrd) : |
|
285 |
""" |
|
286 |
Lookup and return the full filesystem path to the given RRD name. |
|
287 |
""" |
|
288 |
||
289 |
# real path |
|
290 |
path = self.fs_path(rrd + '.rrd') |
|
291 |
||
292 |
# found as file? |
|
293 |
if not os.path.isfile(path) : |
|
294 |
raise exceptions.NotFound("No such RRD file: %s" % (rrd, )) |
|
295 |
||
296 |
return path |
|
297 |
||
298 |
def rrd_title (self, rrd) : |
|
299 |
""" |
|
300 |
Generate a neat human-readable title from the given RRD name. |
|
301 |
""" |
|
302 |
||
303 |
# XXX: path components... |
|
304 |
return " » ".join(rrd.split('/')) |
|
305 |
||
306 |
def render_graph (self, rrd, style, interval, png_path) : |
|
307 |
""" |
|
32
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
308 |
Render the given graph for the given RRD to the given path, returning the opened file object. |
22 | 309 |
""" |
310 |
||
311 |
rrd_path = self.rrd_path(rrd) |
|
312 |
||
313 |
# title |
|
314 |
# this is » |
|
315 |
title = " / ".join(rrd.split('/')) |
|
316 |
||
317 |
log.debug("%s -> %s", rrd_path, png_path) |
|
318 |
||
32
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
319 |
# generate using the variant function given |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
320 |
w, h, report, png_file = graph.graph_single(style, interval, title, self.rrd_graph_func, rrd_path, png_path) |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
321 |
|
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
322 |
# the open'd .tmp file |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
323 |
return png_file |
22 | 324 |
|
325 |
def rrd_graph (self, rrd, style, interval, flush=False) : |
|
326 |
""" |
|
327 |
Return an open file object representing the given graph's PNG image. |
|
328 |
||
329 |
This is returned directly from cache, if a fresh copy is available. The cached copy is compared against |
|
330 |
the source RRD file. |
|
331 |
""" |
|
332 |
||
333 |
# real path to .rrd |
|
334 |
rrd_path = self.rrd_path(rrd) |
|
335 |
||
336 |
# path to cached img |
|
337 |
img_path = os.path.join(self.imgpath, style, interval, rrd) + '.png' |
|
338 |
||
339 |
# this should always exist.. |
|
340 |
rrd_stat = os.stat(rrd_path) |
|
341 |
||
342 |
try : |
|
343 |
# this may not exist |
|
344 |
img_stat = os.stat(img_path) |
|
345 |
||
346 |
except OSError, e : |
|
347 |
if e.errno == errno.ENOENT : |
|
348 |
# doesn't exist |
|
349 |
img_stat = None |
|
350 |
||
351 |
else : |
|
352 |
# can't handle |
|
353 |
raise |
|
354 |
||
355 |
# check freshness |
|
356 |
if flush or img_stat is None or rrd_stat.st_mtime > img_stat.st_mtime : |
|
357 |
# generate containing dir if missiong |
|
358 |
dir_path = os.path.dirname(img_path) |
|
359 |
||
360 |
if not os.path.isdir(dir_path) : |
|
361 |
log.warn("makedirs %s", dir_path) |
|
362 |
||
363 |
os.makedirs(dir_path) |
|
364 |
||
365 |
# re-generate to tmp file |
|
366 |
tmp_path = os.path.join(self.imgpath, style, interval, rrd) + '.tmp' |
|
32
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
367 |
|
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
368 |
# open and write the graph image |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
369 |
img_file = self.render_graph(rrd, style, interval, tmp_path) |
22 | 370 |
|
371 |
# replace .png with .tmp (semi-atomic, but atomic enough..) |
|
32
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
372 |
# XXX: probably not portable to windows, what with img_file |
22 | 373 |
os.rename(tmp_path, img_path) |
32
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
374 |
|
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
375 |
else : |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
376 |
# use existing file |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
377 |
img_file = open(img_path, 'rb') |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
378 |
|
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
379 |
# return the now-fresh .png and return that |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
380 |
return img_file |
22 | 381 |
|
382 |
### Request handlers |
|
383 |
def index (self, req, url, dir = '') : |
|
384 |
""" |
|
385 |
Directory overview |
|
386 |
||
387 |
dir - (optional) relative path to subdir from base rrdpath |
|
388 |
""" |
|
32
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
389 |
|
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
390 |
# lookup |
22 | 391 |
path = self.fs_path(dir) |
392 |
||
393 |
# scan |
|
394 |
subdirs, rrds = self.scan_dir(path) |
|
395 |
||
396 |
# render |
|
397 |
html = self.fmt_page( |
|
398 |
self.fmt_breadcrumb(url, dir), |
|
399 |
self.fmt_overview(url, dir, subdirs, rrds) |
|
400 |
) |
|
401 |
||
402 |
return Response(html, mimetype='text/html') |
|
403 |
||
404 |
||
405 |
def target (self, req, url, rrd) : |
|
406 |
""" |
|
407 |
Target overview |
|
408 |
||
409 |
""" |
|
410 |
||
411 |
# verify existance |
|
412 |
path = self.rrd_path(rrd) |
|
413 |
||
414 |
# render |
|
415 |
html = self.fmt_page( |
|
416 |
self.fmt_breadcrumb(url, rrd), |
|
417 |
self.fmt_target(url, rrd) |
|
418 |
) |
|
419 |
||
420 |
return Response(html, mimetype='text/html') |
|
421 |
||
422 |
||
423 |
STYLES = graph.STYLE_DEFS.keys() |
|
424 |
INTERVALS = graph.INTERVAL_DEFS.keys() |
|
425 |
||
426 |
def graph (self, req, url, rrd, style, interval) : |
|
427 |
""" |
|
428 |
Target graph |
|
429 |
""" |
|
430 |
||
431 |
# validate style/interval |
|
432 |
if style not in self.STYLES or interval not in self.INTERVALS : |
|
433 |
raise exceptions.BadRequest("Invalid style/interval") |
|
434 |
||
435 |
# flush if asked to by ?flush |
|
436 |
flush = ('flush' in req.args) |
|
437 |
||
438 |
# render |
|
439 |
png = self.rrd_graph(rrd, style, interval, flush=flush) |
|
440 |
||
441 |
# construct wrapper for response file, using either werkzeug's own wrapper, or the one provided by the WSGI server |
|
442 |
response_file = werkzeug.wrap_file(req.environ, png) |
|
443 |
||
444 |
# respond with file wrapper |
|
445 |
return Response(response_file, mimetype='image/png', direct_passthrough=True) |
|
32
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
446 |
|
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
447 |
def graph_top (self, req, url, dir = '', count=5) : |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
448 |
""" |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
449 |
Show top N hosts by peak/average. |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
450 |
""" |
22 | 451 |
|
32
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
452 |
# find |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
453 |
path = self.fs_path(dir) |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
454 |
|
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
455 |
# scan |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
456 |
subdirs, rrds = self.scan_dir(path) |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
457 |
|
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
458 |
# get top N hosts |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
459 |
hosts = backend.calc_top_hosts(path, rrds, 'in', 'out', count=count) |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
460 |
|
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
461 |
# draw graph with hosts |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
462 |
w, h, data, img = graph.graph_multi('hourly', |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
463 |
"Top %d hosts" % count, |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
464 |
[('%s/%s.rrd' % (path, name), name) for name in hosts], |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
465 |
self.rrd_graph_func, |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
466 |
None # to tmpfile |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
467 |
) |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
468 |
|
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
469 |
# wrap file output |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
470 |
response_file = werkzeug.wrap_file(req.environ, img) |
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
471 |
|
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
472 |
return Response(response_file, mimetype='image/png', direct_passthrough=True) |
22 | 473 |
|
474 |
# map URLs to various methods |
|
475 |
# XXX: this uses the method object as the endpoint, which is a bit silly, since it's not bound and we need to pass |
|
476 |
# in self explicitly.. |
|
477 |
URLS = Map(( |
|
478 |
Rule('/', endpoint=index, defaults=dict(dir = '')), |
|
32
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
479 |
Rule('/top.png', endpoint=graph_top, defaults=dict(dir = '')), |
22 | 480 |
Rule('/<path:dir>/', endpoint=index), |
32
47e977c23ba2
implement rendering of pmacct rrd graphs, and a dir/top.png feature
Tero Marttila <terom@fixme.fi>
parents:
24
diff
changeset
|
481 |
Rule('/<path:dir>/top.png', endpoint=graph_top), |
22 | 482 |
Rule('/<path:rrd>.rrd', endpoint=target), |
483 |
Rule('/<path:rrd>.rrd/<string:style>.png', endpoint=graph, defaults=dict(interval = 'daily')), |
|
484 |
Rule('/<path:rrd>.rrd/<string:style>/<string:interval>.png', endpoint=graph), |
|
485 |
)) |
|
486 |
||
487 |