]> git.ipfire.org Git - ipfire.org.git/blob - webapp/handlers_stasy.py
Remove obsolete pakfire CGI scripts.
[ipfire.org.git] / webapp / handlers_stasy.py
1 #!/usr/bin/python
2
3 from __future__ import division
4
5 import datetime
6 import hwdata
7 import ipaddr
8 import logging
9 import re
10 import simplejson
11 import tornado.web
12
13 import backend
14
15 from handlers_base import *
16
17 class StasyBaseHandler(BaseHandler):
18 @property
19 def stasy(self):
20 return backend.Stasy()
21
22 def format_size(self, s):
23 units = ("K", "M", "G", "T")
24 unit = 0
25
26 while s >= 1024 and unit < len(units):
27 s /= 1024
28 unit += 1
29
30 return "%.1f%s" % (s, units[unit])
31
32 def cut_string(self, s, limit=15):
33 if len(s) > limit:
34 s = s[:limit] + "..."
35
36 return s
37
38 def render(self, *args, **kwargs):
39 kwargs.update({
40 "cut_string" : self.cut_string,
41 "format_size" : self.format_size,
42 })
43
44 return BaseHandler.render(self, *args, **kwargs)
45
46
47 MIN_PROFILE_VERSION = 0
48 MAX_PROFILE_VERSION = 0
49
50 class Profile(dict):
51 def __getattr__(self, key):
52 try:
53 return self[key]
54 except KeyError:
55 raise AttributeError, key
56
57 def __setattr__(self, key, val):
58 self[key] = val
59
60
61 class StasyProfileSendHandler(StasyBaseHandler):
62 def check_xsrf_cookie(self):
63 # This cookie is not required here.
64 pass
65
66 @property
67 def archives(self):
68 return self.stasy.archives
69
70 @property
71 def profiles(self):
72 return self.stasy.profiles
73
74 def prepare(self):
75 # Create an empty profile.
76 self.profile = Profile()
77
78 def __check_attributes(self, profile):
79 """
80 Check for attributes that must be provided,
81 """
82
83 attributes = (
84 "private_id",
85 "profile_version",
86 "public_id",
87 "updated",
88 )
89 for attr in attributes:
90 if not profile.has_key(attr):
91 raise tornado.web.HTTPError(400, "Profile lacks '%s' attribute: %s" % (attr, profile))
92
93 def __check_valid_ids(self, profile):
94 """
95 Check if IDs contain valid data.
96 """
97
98 for id in ("public_id", "private_id"):
99 if re.match(r"^([a-f0-9]{40})$", "%s" % profile[id]) is None:
100 raise tornado.web.HTTPError(400, "ID '%s' has wrong format: %s" % (id, profile))
101
102 def __check_equal_ids(self, profile):
103 """
104 Check if public_id and private_id are equal.
105 """
106
107 if profile.public_id == profile.private_id:
108 raise tornado.web.HTTPError(400, "Public and private IDs are equal: %s" % profile)
109
110 def __check_matching_ids(self, profile):
111 """
112 Check if a profile with the given public_id is already in the
113 database. If so we need to check if the private_id matches.
114 """
115 p = self.profiles.find_one({ "public_id" : profile["public_id"]})
116 if not p:
117 return
118
119 p = Profile(p)
120 if p.private_id != profile.private_id:
121 raise tornado.web.HTTPError(400, "Mismatch of private_id: %s" % profile)
122
123 def __check_profile_version(self, profile):
124 """
125 Check if this version of the server software does support the
126 received profile.
127 """
128 version = profile.profile_version
129
130 if version < MIN_PROFILE_VERSION or version > MAX_PROFILE_VERSION:
131 raise tornado.web.HTTPError(400,
132 "Profile version is not supported: %s" % version)
133
134 def check_profile(self):
135 """
136 This method checks if the blob is sane.
137 """
138
139 checks = (
140 self.__check_attributes,
141 self.__check_valid_ids,
142 self.__check_equal_ids,
143 self.__check_profile_version,
144 # These checks require at least one database query and should be done
145 # at last.
146 self.__check_matching_ids,
147 )
148
149 for check in checks:
150 check(self.profile)
151
152 # If we got here, everything is okay and we can go on...
153
154 # The GET method is only allowed in debugging mode.
155 def get(self, public_id):
156 if not self.application.settings["debug"]:
157 return tornado.web.HTTPError(405)
158
159 return self.post(public_id)
160
161 def post(self, public_id):
162 profile = self.get_argument("profile", None)
163
164 # Send "400 bad request" if no profile was provided
165 if not profile:
166 raise tornado.web.HTTPError(400, "No profile received.")
167
168 # Try to decode the profile.
169 try:
170 self.profile.update(simplejson.loads(profile))
171 except simplejson.decoder.JSONDecodeError, e:
172 raise tornado.web.HTTPError(400, "Profile could not be decoded: %s" % e)
173
174 # Create a shortcut and overwrite public_id from query string
175 profile = self.profile
176 profile.public_id = public_id
177
178 # Add timestamp to the profile
179 profile.updated = datetime.datetime.utcnow()
180
181 # Check if profile contains proper data.
182 self.check_profile()
183
184 # Get GeoIP information if address is not defined in rfc1918
185 remote_ips = self.request.remote_ip.split(", ")
186 for remote_ip in remote_ips:
187 try:
188 addr = ipaddr.IPAddress(remote_ip)
189 except ValueError:
190 # Skip invalid IP addresses.
191 continue
192
193 # Check if the given IP address is from a
194 # private network.
195 if addr.is_private:
196 continue
197
198 profile.geoip = self.geoip.get_all(remote_ip)
199 break
200
201 # Move previous profiles to archive and keep only the latest one
202 # in profiles. This will make full table lookups faster.
203 self.stasy.move_profiles({ "public_id" : profile.public_id })
204
205 # Write profile to database
206 id = self.profiles.save(profile)
207
208 self.write("Your profile was successfully saved to the database.")
209 self.finish()
210
211 logging.debug("Saved profile: %s" % profile)
212
213 def on_finish(self):
214 logging.debug("Starting automatic cleanup...")
215
216 # Remove all profiles that were not updated since 4 weeks.
217 not_updated_since = datetime.datetime.utcnow() - \
218 datetime.timedelta(weeks=4)
219
220 self.stasy.move_profiles({ "updated" : { "$lt" : not_updated_since }})
221
222
223 class StasyIndexHandler(StasyBaseHandler):
224 def _profile_not_found(self, profile_id):
225 self.set_status(404)
226 self.render("stasy-profile-notfound.html", profile_id=profile_id)
227
228 def get(self):
229 self.render("stasy-index.html")
230
231 def post(self):
232 profile_id = self.get_argument("profile_id", None)
233 if not profile_id:
234 raise tornado.web.HTTPError(400, "No profile ID was given.")
235
236 if not self.stasy.profile_exists(profile_id):
237 self._profile_not_found(profile_id)
238 return
239
240 self.redirect("/profile/%s" % profile_id)
241
242
243 class StasyProfileDetailHandler(StasyIndexHandler):
244 def get(self, profile_id):
245 profile = self.stasy.get_profile(profile_id)
246 if not profile:
247 self._profile_not_found(profile_id)
248 return
249
250 self.render("stasy-profile-detail.html", profile=profile)
251
252
253 class StasyStatsHandler(StasyBaseHandler):
254 def get(self):
255 self.render("stasy-stats.html")
256
257
258 class StasyStatsCPUHandler(StasyBaseHandler):
259 def get(self):
260 return self.render("stasy-stats-cpus.html",
261 cpu_vendors=self.stasy.cpu_vendors_map,
262 average_speed=self.stasy.cpu_speed_average,
263 cpu_speeds=self.stasy.cpu_speed_map,
264 cpu_cores=self.stasy.get_cpu_cores_map(),
265 bogomips=self.stasy.get_cpu_bogomips_accumulated())
266
267
268 class StasyStatsCPUFlagsHandler(StasyBaseHandler):
269 def get(self):
270 kwargs = {}
271
272 flags = (
273 ("lm", "lm"),
274 ("pae", "pae"),
275 ("virt", ("vmx", "svm")),
276 )
277
278 for name, flag in flags:
279 kwargs["cpus_" + name] = self.stasy.get_cpu_flag_map(flag)
280
281 return self.render("stasy-stats-cpu-flags.html", **kwargs)
282
283 class StasyStatsMemoryHandler(StasyBaseHandler):
284 def get(self):
285 return self.render("stasy-stats-memory.html",
286 average_memory=self.stasy.memory_average,
287 memory=self.stasy.get_memory_map())
288
289
290 class StasyStatsOSesHandler(StasyBaseHandler):
291 def get(self):
292 return self.render("stasy-stats-oses.html",
293 arches=self.stasy.arch_map,
294 kernels=self.stasy.kernel_map,
295 releases=self.stasy.release_map)
296
297
298 class StasyStatsVirtualHandler(StasyBaseHandler):
299 def get(self):
300 return self.render("stasy-stats-virtual.html",
301 hypervisor_vendors = self.stasy.hypervisor_map,
302 is_virtual = self.stasy.virtual_map)
303
304 class StasyStatsGeoHandler(StasyBaseHandler):
305 def get(self):
306 return self.render("stasy-stats-geo.html",
307 languages = self.stasy.get_language_map(),
308 geo_locations = self.stasy.get_geo_location_map())
309
310
311 class StasyStatsNetworkHandler(StasyBaseHandler):
312 def get(self):
313 return self.render("stasy-stats-network.html",
314 network_zones=self.stasy.get_network_zones_map())
315
316
317 class StasyStatsVendorDetail(StasyBaseHandler):
318 def get(self, bus, vendor_id):
319 # XXX some way ugly
320 bus2cls = {
321 "pci" : hwdata.PCI,
322 "usb" : hwdata.USB
323 }
324 cls = bus2cls[bus.lower()]
325 vendor_name = cls().get_vendor(vendor_id)
326
327 # Get a list of all models we know from this vendor
328 models = self.stasy.get_models_by_vendor(bus, vendor_id)
329
330 self.render("stasy-vendor-detail.html",
331 vendor_name=vendor_name, models=models)
332
333
334 class StasyStatsModelDetail(StasyBaseHandler):
335 def get(self, bus, vendor_id, model_id):
336 bus2cls = {
337 "pci" : hwdata.PCI,
338 "usb" : hwdata.USB
339 }
340
341 cls = bus2cls[bus.lower()]
342
343 vendor_name = cls().get_vendor(vendor_id)
344 model_name = cls().get_device(vendor_id, model_id)
345
346 percentage = \
347 self.stasy.get_device_percentage(bus, vendor_id, model_id) * 100
348
349 self.render("stasy-model-detail.html",
350 vendor_id=vendor_id,
351 vendor_name=vendor_name,
352 model_id=model_id,
353 model_name=model_name,
354 percentage=percentage,
355 bus=bus.lower())
356
357
358 class AdminFireinfoStatsHandler(StasyBaseHandler):
359 def get(self):
360 _ = self.locale.translate
361
362 data = {}
363
364 data["profiles_count"], data["profiles_count_all"] = \
365 self.stasy.get_profile_ratio()
366
367 data["archives_count"] = self.stasy.get_archives_count()
368
369 # updated since 24h
370 #since = { "hours" : 24 }
371 #updates = self.stasy.get_updates_by_release_since(since)
372 #updates[_("All versions")] = self.stasy.get_updated_since(since).count()
373 #data["updated_since_24h"] = updates
374
375 self.render("stasy-stats-admin.html", **data)
376