]>
Commit | Line | Data |
---|---|---|
21a66327 MT |
1 | #!/usr/bin/python3 |
2 | ############################################################################### | |
3 | # # | |
4 | # Westferry - The IPFire web user interface # | |
5 | # Copyright (C) 2015 IPFire development team # | |
6 | # # | |
7 | # This program is free software: you can redistribute it and/or modify # | |
8 | # it under the terms of the GNU General Public License as published by # | |
9 | # the Free Software Foundation, either version 3 of the License, or # | |
10 | # (at your option) any later version. # | |
11 | # # | |
12 | # This program is distributed in the hope that it will be useful, # | |
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of # | |
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # | |
15 | # GNU General Public License for more details. # | |
16 | # # | |
17 | # You should have received a copy of the GNU General Public License # | |
18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. # | |
19 | # # | |
20 | ############################################################################### | |
21 | ||
22 | import datetime | |
23 | import collecty.client | |
24 | import mimetypes | |
25 | import tornado.web | |
26 | ||
27 | from . import base | |
a6144752 MT |
28 | from .. import ui |
29 | ||
3fc6d806 | 30 | from ..i18n import N_ |
a6144752 MT |
31 | |
32 | class AnalyticsBaseHandler(base.BaseHandler): | |
33 | @property | |
34 | def menu(self): | |
35 | _ = self.locale.translate | |
36 | ||
37 | m = ui.menu.Menu(self, _("Analytics")) | |
38 | ||
39 | # Overview | |
40 | m.add_handler(AnalyticsOverviewHandler) | |
41 | m.add_divider() | |
42 | ||
30806514 MT |
43 | # Network |
44 | m.add_handler(AnalyticsNetworkOverviewHandler) | |
45 | ||
a6144752 MT |
46 | # System |
47 | m.add_handler(AnalyticsSystemOverviewHandler) | |
48 | ||
49 | return m | |
50 | ||
03f518c7 MT |
51 | def render_graphs(self, graphs): |
52 | self.render("graphs.html", graphs=graphs) | |
53 | ||
a6144752 MT |
54 | |
55 | class AnalyticsOverviewHandler(AnalyticsBaseHandler): | |
56 | url = r"/analytics" | |
57 | title = N_("Overview") | |
58 | ||
59 | def get(self): | |
60 | self.render("base.html") | |
61 | ||
62 | ||
30806514 MT |
63 | class AnalyticsNetworkBaseHandler(AnalyticsBaseHandler): |
64 | @property | |
65 | def menu(self): | |
66 | _ = self.locale.translate | |
67 | ||
68 | m = ui.menu.Menu(self, _("Network")) | |
69 | ||
70 | # Back... | |
71 | m.add_handler(AnalyticsOverviewHandler, title=_("Back...")) | |
72 | m.add_divider() | |
73 | ||
74 | # Overview | |
75 | m.add_handler(AnalyticsNetworkOverviewHandler, title=_("Overview")) | |
76 | m.add_divider() | |
77 | ||
78 | # Connections | |
79 | m.add_handler(AnalyticsNetworkConnectionsOverviewHandler) | |
80 | ||
cbc61195 MT |
81 | # Fragmentation |
82 | m.add_handler(AnalyticsNetworkIPFragmentationHandler) | |
83 | ||
30806514 MT |
84 | return m |
85 | ||
86 | ||
87 | class AnalyticsNetworkOverviewHandler(AnalyticsNetworkBaseHandler): | |
88 | url = r"/analytics/network" | |
89 | title = N_("Network") | |
90 | ||
91 | def get(self): | |
92 | self.render("base.html") | |
93 | ||
94 | ||
95 | class AnalyticsNetworkConnectionsBaseHandler(AnalyticsBaseHandler): | |
96 | @property | |
97 | def menu(self): | |
98 | _ = self.locale.translate | |
99 | ||
100 | m = ui.menu.Menu(self, _("Connections")) | |
101 | ||
102 | # Back... | |
103 | m.add_handler(AnalyticsNetworkOverviewHandler, title=_("Back...")) | |
104 | m.add_divider() | |
105 | ||
106 | # Overview | |
107 | m.add_handler(AnalyticsNetworkConnectionsOverviewHandler, title=_("Overview")) | |
108 | m.add_divider() | |
109 | ||
110 | # Protocol States | |
111 | s = m.add_submenu(_("Procotol States")) | |
112 | s.add_handler(AnalyticsNetworkConnectionsStatesTCPHandler) | |
113 | s.add_handler(AnalyticsNetworkConnectionsStatesSCTPHandler) | |
114 | s.add_handler(AnalyticsNetworkConnectionsStatesDCCPHandler) | |
115 | ||
116 | return m | |
117 | ||
118 | ||
119 | class AnalyticsNetworkConnectionsOverviewHandler(AnalyticsNetworkConnectionsBaseHandler): | |
120 | url = r"/analytics/network/connections" | |
121 | title = N_("Connections") | |
122 | ||
123 | def get(self): | |
124 | _ = self.locale.translate | |
125 | ||
126 | graphs = [ | |
127 | ui.graphs.Graph(self, "conntrack-layer3-protocols"), | |
128 | ui.graphs.Graph(self, "conntrack-layer4-protocols"), | |
129 | ] | |
130 | ||
131 | self.render_graphs(graphs) | |
132 | ||
133 | ||
134 | class AnalyticsNetworkConnectionsStatesTCPHandler(AnalyticsNetworkConnectionsBaseHandler): | |
135 | url = r"/analytics/network/connections/states/tcp" | |
136 | title = N_("TCP") | |
137 | ||
138 | def get(self): | |
139 | _ = self.locale.translate | |
140 | ||
141 | graphs = [ | |
142 | ui.graphs.Graph(self, "conntrack-protocol-states", "tcp"), | |
143 | ] | |
144 | ||
145 | self.render_graphs(graphs) | |
146 | ||
147 | ||
148 | class AnalyticsNetworkConnectionsStatesSCTPHandler(AnalyticsNetworkConnectionsBaseHandler): | |
149 | url = r"/analytics/network/connections/states/sctp" | |
150 | title = N_("SCTP") | |
151 | ||
152 | def get(self): | |
153 | _ = self.locale.translate | |
154 | ||
155 | graphs = [ | |
156 | ui.graphs.Graph(self, "conntrack-protocol-states", "sctp"), | |
157 | ] | |
158 | ||
159 | self.render_graphs(graphs) | |
160 | ||
161 | ||
162 | class AnalyticsNetworkConnectionsStatesDCCPHandler(AnalyticsNetworkConnectionsBaseHandler): | |
163 | url = r"/analytics/network/connections/states/dccp" | |
164 | title = N_("DCCP") | |
165 | ||
166 | def get(self): | |
167 | _ = self.locale.translate | |
168 | ||
169 | graphs = [ | |
170 | ui.graphs.Graph(self, "conntrack-protocol-states", "dccp"), | |
171 | ] | |
172 | ||
173 | self.render_graphs(graphs) | |
174 | ||
175 | ||
cbc61195 MT |
176 | class AnalyticsNetworkIPFragmentationHandler(AnalyticsNetworkBaseHandler): |
177 | url = r"/analytics/network/fragmentation" | |
178 | title = N_("Fragmentation") | |
179 | ||
180 | def get(self): | |
181 | _ = self.locale.translate | |
182 | ||
183 | graphs = [ | |
184 | ui.graphs.Graph(self, "ipv6-fragmentation"), | |
185 | ui.graphs.Graph(self, "ipv4-fragmentation"), | |
186 | ] | |
187 | ||
188 | self.render_graphs(graphs) | |
189 | ||
190 | ||
a6144752 MT |
191 | class AnalyticsSystemBaseHandler(AnalyticsBaseHandler): |
192 | @property | |
193 | def menu(self): | |
194 | _ = self.locale.translate | |
195 | ||
196 | m = ui.menu.Menu(self, _("System")) | |
197 | ||
198 | # Back... | |
199 | m.add_handler(AnalyticsOverviewHandler, title=_("Back...")) | |
200 | m.add_divider() | |
201 | ||
202 | # Overview | |
203 | m.add_handler(AnalyticsSystemOverviewHandler, title=_("Overview")) | |
204 | m.add_divider() | |
205 | ||
03f518c7 | 206 | # Most interesting items |
a6144752 MT |
207 | m.add_handler(AnalyticsSystemProcessorsHandler) |
208 | m.add_handler(AnalyticsSystemMemoryHandler) | |
03f518c7 MT |
209 | m.add_handler(AnalyticsSystemTemperaturesHandler) |
210 | ||
211 | # Others | |
212 | s = m.add_submenu(_("More")) | |
213 | s.add_handler(AnalyticsSystemEntropyHandler) | |
0da4e21c | 214 | s.add_handler(AnalyticsSystemContextSwitchesHandler) |
f88d0ae2 | 215 | s.add_handler(AnalyticsSystemInterruptsHandler) |
a6144752 MT |
216 | |
217 | return m | |
218 | ||
219 | ||
220 | class AnalyticsSystemOverviewHandler(AnalyticsSystemBaseHandler): | |
221 | url = r"/analytics/system" | |
222 | title = N_("System") | |
223 | ||
03f518c7 MT |
224 | def get(self): |
225 | self.render("base.html") | |
226 | ||
a6144752 MT |
227 | |
228 | class AnalyticsSystemProcessorsHandler(AnalyticsSystemBaseHandler): | |
229 | url = r"/analytics/system/processors" | |
230 | title = N_("Processors") | |
231 | ||
03f518c7 MT |
232 | def get(self): |
233 | _ = self.locale.translate | |
234 | ||
235 | graphs = [ | |
236 | ui.graphs.Graph(self, "processor"), | |
237 | ui.graphs.Graph(self, "processor-temperature"), | |
238 | ui.graphs.Graph(self, "cpufreq"), | |
239 | ui.graphs.Graph(self, "loadavg"), | |
240 | ] | |
241 | ||
242 | self.render_graphs(graphs) | |
243 | ||
a6144752 MT |
244 | |
245 | class AnalyticsSystemMemoryHandler(AnalyticsSystemBaseHandler): | |
246 | url = r"/analytics/system/memory" | |
247 | title = N_("Memory") | |
248 | ||
03f518c7 MT |
249 | def get(self): |
250 | _ = self.locale.translate | |
251 | ||
252 | graphs = [ | |
253 | ui.graphs.Graph(self, "memory"), | |
254 | ] | |
255 | ||
256 | self.render_graphs(graphs) | |
257 | ||
258 | ||
259 | class AnalyticsSystemTemperaturesHandler(AnalyticsSystemBaseHandler): | |
260 | url = r"/analytics/system/temperatures" | |
261 | title = N_("Temperatures") | |
262 | ||
263 | def get(self): | |
264 | _ = self.locale.translate | |
265 | ||
266 | graphs = [ | |
267 | ui.graphs.Graph(self, "sensors-temperature"), | |
268 | ui.graphs.Graph(self, "processor-temperature"), | |
269 | ] | |
270 | ||
271 | self.render_graphs(graphs) | |
272 | ||
273 | ||
274 | class AnalyticsSystemEntropyHandler(AnalyticsSystemBaseHandler): | |
275 | url = r"/analytics/system/entropy" | |
276 | title = N_("Entropy") | |
277 | ||
278 | def get(self): | |
279 | _ = self.locale.translate | |
280 | ||
281 | graphs = [ | |
282 | ui.graphs.Graph(self, "entropy"), | |
283 | ] | |
284 | ||
285 | self.render_graphs(graphs) | |
286 | ||
21a66327 | 287 | |
0da4e21c MT |
288 | class AnalyticsSystemContextSwitchesHandler(AnalyticsSystemBaseHandler): |
289 | url = r"/analytics/system/context-switches" | |
290 | title = N_("Context Switches") | |
291 | ||
292 | def get(self): | |
293 | _ = self.locale.translate | |
294 | ||
295 | graphs = [ | |
296 | ui.graphs.Graph(self, "context-switches"), | |
297 | ] | |
298 | ||
299 | self.render_graphs(graphs) | |
300 | ||
301 | ||
f88d0ae2 MT |
302 | class AnalyticsSystemInterruptsHandler(AnalyticsSystemBaseHandler): |
303 | url = r"/analytics/system/interrupts" | |
304 | title = N_("Interrupts") | |
305 | ||
306 | def get(self): | |
307 | _ = self.locale.translate | |
308 | ||
309 | graphs = [ | |
310 | ui.graphs.Graph(self, "system-interrupts"), | |
311 | ] | |
312 | ||
313 | self.render_graphs(graphs) | |
314 | ||
315 | ||
21a66327 MT |
316 | class GraphExportHandler(base.BaseHandler): |
317 | VALID_INTERVALS = ("hour", "day", "month", "week", "year") | |
318 | DEFAULT_INTERVAL = "day" | |
319 | ||
320 | SUPPORTED_FORMATS = ("pdf", "png", "svg") | |
321 | ||
0ac359f3 | 322 | url = r"/graph(/thumbnail)?/([\w\-]+)(?:/([\w\d\.]+))?\.(%s)" % "|".join(SUPPORTED_FORMATS) |
21a66327 | 323 | |
0ac359f3 | 324 | def get(self, thumbnail, template_name, object_id, format): |
b7561c87 MT |
325 | _ = self.locale.translate |
326 | ||
21a66327 MT |
327 | # Get the requested dimensions of the image |
328 | height = self.get_argument_int("height", None) | |
329 | width = self.get_argument_int("width", None) | |
330 | ||
331 | # Get the requested interval | |
332 | interval = self.get_argument("interval", self.DEFAULT_INTERVAL) | |
333 | if interval and not interval in self.VALID_INTERVALS: | |
334 | raise tornado.web.HTTPError(400, _("Invalid interval: %s") % interval) | |
335 | ||
03f518c7 MT |
336 | # Create the graph object |
337 | g = ui.graphs.Graph(self, template_name, object_id=object_id) | |
338 | ||
21a66327 | 339 | # Generate the graph image |
cbc61195 MT |
340 | kwargs = { |
341 | "format" : format.upper(), | |
342 | "interval" : interval, | |
343 | "height" : height, | |
344 | "width" : width, | |
0ac359f3 | 345 | "thumbnail" : bool(thumbnail), |
cbc61195 MT |
346 | |
347 | # Include the title in the PDF exports | |
348 | "with_title" : format == "pdf", | |
349 | } | |
350 | image = g.generate_graph(**kwargs) | |
21a66327 MT |
351 | |
352 | # Set the HTTP headers | |
353 | self._make_headers(format, template_name, object_id) | |
354 | ||
355 | # Deliver the content | |
03f518c7 | 356 | self.finish(image.get("image")) |
21a66327 MT |
357 | |
358 | def _make_headers(self, extension, template_name, object_id): | |
359 | # Determine the mime type | |
360 | mimetype = mimetypes.types_map.get(".%s" % extension, "application/octet-stream") | |
361 | self.set_header("Content-Type", mimetype) | |
362 | ||
363 | # Add the timestamp when this graph was generated | |
364 | now = datetime.datetime.now() | |
365 | ||
366 | # Put together the filename (for downloads) | |
367 | filename = [self.backend.system.hostname, template_name, object_id, now.isoformat()] | |
368 | filename = "%s.%s" % ("-".join((e for e in filename if e)), extension) | |
369 | ||
370 | if extension == "pdf": | |
371 | self.set_header("Content-Disposition", "attachment; filename=%s" % filename) | |
372 | else: | |
373 | self.set_header("Content-Disposition", "inline; filename=%s" % filename) | |
03f518c7 MT |
374 | |
375 | ||
376 | class GraphHandler(base.BaseHandler): | |
377 | url = r"/graph/([\w\-]+)(?:/([\w\d\.]+))?" | |
378 | ||
379 | def get(self, template, object_id): | |
380 | graph = ui.graphs.Graph(self, template, object_id=object_id) | |
381 | ||
382 | self.render("graph.html", graph=graph) |