]>
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 | ||
81 | return m | |
82 | ||
83 | ||
84 | class AnalyticsNetworkOverviewHandler(AnalyticsNetworkBaseHandler): | |
85 | url = r"/analytics/network" | |
86 | title = N_("Network") | |
87 | ||
88 | def get(self): | |
89 | self.render("base.html") | |
90 | ||
91 | ||
92 | class AnalyticsNetworkConnectionsBaseHandler(AnalyticsBaseHandler): | |
93 | @property | |
94 | def menu(self): | |
95 | _ = self.locale.translate | |
96 | ||
97 | m = ui.menu.Menu(self, _("Connections")) | |
98 | ||
99 | # Back... | |
100 | m.add_handler(AnalyticsNetworkOverviewHandler, title=_("Back...")) | |
101 | m.add_divider() | |
102 | ||
103 | # Overview | |
104 | m.add_handler(AnalyticsNetworkConnectionsOverviewHandler, title=_("Overview")) | |
105 | m.add_divider() | |
106 | ||
107 | # Protocol States | |
108 | s = m.add_submenu(_("Procotol States")) | |
109 | s.add_handler(AnalyticsNetworkConnectionsStatesTCPHandler) | |
110 | s.add_handler(AnalyticsNetworkConnectionsStatesSCTPHandler) | |
111 | s.add_handler(AnalyticsNetworkConnectionsStatesDCCPHandler) | |
112 | ||
113 | return m | |
114 | ||
115 | ||
116 | class AnalyticsNetworkConnectionsOverviewHandler(AnalyticsNetworkConnectionsBaseHandler): | |
117 | url = r"/analytics/network/connections" | |
118 | title = N_("Connections") | |
119 | ||
120 | def get(self): | |
121 | _ = self.locale.translate | |
122 | ||
123 | graphs = [ | |
124 | ui.graphs.Graph(self, "conntrack-layer3-protocols"), | |
125 | ui.graphs.Graph(self, "conntrack-layer4-protocols"), | |
126 | ] | |
127 | ||
128 | self.render_graphs(graphs) | |
129 | ||
130 | ||
131 | class AnalyticsNetworkConnectionsStatesTCPHandler(AnalyticsNetworkConnectionsBaseHandler): | |
132 | url = r"/analytics/network/connections/states/tcp" | |
133 | title = N_("TCP") | |
134 | ||
135 | def get(self): | |
136 | _ = self.locale.translate | |
137 | ||
138 | graphs = [ | |
139 | ui.graphs.Graph(self, "conntrack-protocol-states", "tcp"), | |
140 | ] | |
141 | ||
142 | self.render_graphs(graphs) | |
143 | ||
144 | ||
145 | class AnalyticsNetworkConnectionsStatesSCTPHandler(AnalyticsNetworkConnectionsBaseHandler): | |
146 | url = r"/analytics/network/connections/states/sctp" | |
147 | title = N_("SCTP") | |
148 | ||
149 | def get(self): | |
150 | _ = self.locale.translate | |
151 | ||
152 | graphs = [ | |
153 | ui.graphs.Graph(self, "conntrack-protocol-states", "sctp"), | |
154 | ] | |
155 | ||
156 | self.render_graphs(graphs) | |
157 | ||
158 | ||
159 | class AnalyticsNetworkConnectionsStatesDCCPHandler(AnalyticsNetworkConnectionsBaseHandler): | |
160 | url = r"/analytics/network/connections/states/dccp" | |
161 | title = N_("DCCP") | |
162 | ||
163 | def get(self): | |
164 | _ = self.locale.translate | |
165 | ||
166 | graphs = [ | |
167 | ui.graphs.Graph(self, "conntrack-protocol-states", "dccp"), | |
168 | ] | |
169 | ||
170 | self.render_graphs(graphs) | |
171 | ||
172 | ||
a6144752 MT |
173 | class AnalyticsSystemBaseHandler(AnalyticsBaseHandler): |
174 | @property | |
175 | def menu(self): | |
176 | _ = self.locale.translate | |
177 | ||
178 | m = ui.menu.Menu(self, _("System")) | |
179 | ||
180 | # Back... | |
181 | m.add_handler(AnalyticsOverviewHandler, title=_("Back...")) | |
182 | m.add_divider() | |
183 | ||
184 | # Overview | |
185 | m.add_handler(AnalyticsSystemOverviewHandler, title=_("Overview")) | |
186 | m.add_divider() | |
187 | ||
03f518c7 | 188 | # Most interesting items |
a6144752 MT |
189 | m.add_handler(AnalyticsSystemProcessorsHandler) |
190 | m.add_handler(AnalyticsSystemMemoryHandler) | |
03f518c7 MT |
191 | m.add_handler(AnalyticsSystemTemperaturesHandler) |
192 | ||
193 | # Others | |
194 | s = m.add_submenu(_("More")) | |
195 | s.add_handler(AnalyticsSystemEntropyHandler) | |
a6144752 MT |
196 | |
197 | return m | |
198 | ||
199 | ||
200 | class AnalyticsSystemOverviewHandler(AnalyticsSystemBaseHandler): | |
201 | url = r"/analytics/system" | |
202 | title = N_("System") | |
203 | ||
03f518c7 MT |
204 | def get(self): |
205 | self.render("base.html") | |
206 | ||
a6144752 MT |
207 | |
208 | class AnalyticsSystemProcessorsHandler(AnalyticsSystemBaseHandler): | |
209 | url = r"/analytics/system/processors" | |
210 | title = N_("Processors") | |
211 | ||
03f518c7 MT |
212 | def get(self): |
213 | _ = self.locale.translate | |
214 | ||
215 | graphs = [ | |
216 | ui.graphs.Graph(self, "processor"), | |
217 | ui.graphs.Graph(self, "processor-temperature"), | |
218 | ui.graphs.Graph(self, "cpufreq"), | |
219 | ui.graphs.Graph(self, "loadavg"), | |
220 | ] | |
221 | ||
222 | self.render_graphs(graphs) | |
223 | ||
a6144752 MT |
224 | |
225 | class AnalyticsSystemMemoryHandler(AnalyticsSystemBaseHandler): | |
226 | url = r"/analytics/system/memory" | |
227 | title = N_("Memory") | |
228 | ||
03f518c7 MT |
229 | def get(self): |
230 | _ = self.locale.translate | |
231 | ||
232 | graphs = [ | |
233 | ui.graphs.Graph(self, "memory"), | |
234 | ] | |
235 | ||
236 | self.render_graphs(graphs) | |
237 | ||
238 | ||
239 | class AnalyticsSystemTemperaturesHandler(AnalyticsSystemBaseHandler): | |
240 | url = r"/analytics/system/temperatures" | |
241 | title = N_("Temperatures") | |
242 | ||
243 | def get(self): | |
244 | _ = self.locale.translate | |
245 | ||
246 | graphs = [ | |
247 | ui.graphs.Graph(self, "sensors-temperature"), | |
248 | ui.graphs.Graph(self, "processor-temperature"), | |
249 | ] | |
250 | ||
251 | self.render_graphs(graphs) | |
252 | ||
253 | ||
254 | class AnalyticsSystemEntropyHandler(AnalyticsSystemBaseHandler): | |
255 | url = r"/analytics/system/entropy" | |
256 | title = N_("Entropy") | |
257 | ||
258 | def get(self): | |
259 | _ = self.locale.translate | |
260 | ||
261 | graphs = [ | |
262 | ui.graphs.Graph(self, "entropy"), | |
263 | ] | |
264 | ||
265 | self.render_graphs(graphs) | |
266 | ||
21a66327 MT |
267 | |
268 | class GraphExportHandler(base.BaseHandler): | |
269 | VALID_INTERVALS = ("hour", "day", "month", "week", "year") | |
270 | DEFAULT_INTERVAL = "day" | |
271 | ||
272 | SUPPORTED_FORMATS = ("pdf", "png", "svg") | |
273 | ||
03f518c7 | 274 | url = r"/graph/([\w\-]+)(?:/([\w\d\.]+))?\.(%s)" % "|".join(SUPPORTED_FORMATS) |
21a66327 MT |
275 | |
276 | def get(self, template_name, object_id, format): | |
277 | # Get the requested dimensions of the image | |
278 | height = self.get_argument_int("height", None) | |
279 | width = self.get_argument_int("width", None) | |
280 | ||
281 | # Get the requested interval | |
282 | interval = self.get_argument("interval", self.DEFAULT_INTERVAL) | |
283 | if interval and not interval in self.VALID_INTERVALS: | |
284 | raise tornado.web.HTTPError(400, _("Invalid interval: %s") % interval) | |
285 | ||
03f518c7 MT |
286 | # Create the graph object |
287 | g = ui.graphs.Graph(self, template_name, object_id=object_id) | |
288 | ||
21a66327 | 289 | # Generate the graph image |
03f518c7 MT |
290 | image = g.generate_graph(format=format.upper(), interval=interval, |
291 | height=height, width=width) | |
21a66327 MT |
292 | |
293 | # Set the HTTP headers | |
294 | self._make_headers(format, template_name, object_id) | |
295 | ||
296 | # Deliver the content | |
03f518c7 | 297 | self.finish(image.get("image")) |
21a66327 MT |
298 | |
299 | def _make_headers(self, extension, template_name, object_id): | |
300 | # Determine the mime type | |
301 | mimetype = mimetypes.types_map.get(".%s" % extension, "application/octet-stream") | |
302 | self.set_header("Content-Type", mimetype) | |
303 | ||
304 | # Add the timestamp when this graph was generated | |
305 | now = datetime.datetime.now() | |
306 | ||
307 | # Put together the filename (for downloads) | |
308 | filename = [self.backend.system.hostname, template_name, object_id, now.isoformat()] | |
309 | filename = "%s.%s" % ("-".join((e for e in filename if e)), extension) | |
310 | ||
311 | if extension == "pdf": | |
312 | self.set_header("Content-Disposition", "attachment; filename=%s" % filename) | |
313 | else: | |
314 | self.set_header("Content-Disposition", "inline; filename=%s" % filename) | |
03f518c7 MT |
315 | |
316 | ||
317 | class GraphHandler(base.BaseHandler): | |
318 | url = r"/graph/([\w\-]+)(?:/([\w\d\.]+))?" | |
319 | ||
320 | def get(self, template, object_id): | |
321 | graph = ui.graphs.Graph(self, template, object_id=object_id) | |
322 | ||
323 | self.render("graph.html", graph=graph) |