]>
Commit | Line | Data |
---|---|---|
66862195 MT |
1 | #!/usr/bin/python |
2 | ||
3 | from __future__ import division | |
4 | ||
5 | import datetime | |
6 | import hwdata | |
7 | import logging | |
8 | import re | |
a95c2f97 | 9 | import json |
66862195 MT |
10 | import tornado.web |
11 | ||
a95c2f97 | 12 | from .. import fireinfo |
66862195 MT |
13 | |
14 | from handlers_base import * | |
15 | ||
16 | class FireinfoBaseHandler(BaseHandler): | |
17 | def cut_string(self, s, limit=15): | |
18 | if len(s) > limit: | |
19 | s = s[:limit] + "..." | |
20 | ||
21 | return s | |
22 | ||
23 | def render(self, *args, **kwargs): | |
24 | kwargs.update({ | |
25 | "cut_string" : self.cut_string, | |
26 | }) | |
27 | ||
28 | return BaseHandler.render(self, *args, **kwargs) | |
29 | ||
30 | @property | |
31 | def when(self): | |
32 | return self.get_argument_date("when", None) | |
33 | ||
34 | ||
35 | MIN_PROFILE_VERSION = 0 | |
36 | MAX_PROFILE_VERSION = 0 | |
37 | ||
38 | class Profile(dict): | |
39 | def __getattr__(self, key): | |
40 | try: | |
41 | return self[key] | |
42 | except KeyError: | |
43 | raise AttributeError, key | |
44 | ||
45 | def __setattr__(self, key, val): | |
46 | self[key] = val | |
47 | ||
48 | ||
49 | class FireinfoProfileSendHandler(FireinfoBaseHandler): | |
50 | def check_xsrf_cookie(self): | |
51 | # This cookie is not required here. | |
52 | pass | |
53 | ||
54 | def prepare(self): | |
55 | # Create an empty profile. | |
56 | self.profile = Profile() | |
57 | ||
58 | def __check_attributes(self, profile): | |
59 | """ | |
60 | Check for attributes that must be provided, | |
61 | """ | |
62 | attributes = ( | |
63 | "private_id", | |
64 | "profile_version", | |
65 | "public_id", | |
66 | ) | |
67 | for attr in attributes: | |
68 | if not profile.has_key(attr): | |
69 | raise tornado.web.HTTPError(400, "Profile lacks '%s' attribute: %s" % (attr, profile)) | |
70 | ||
71 | def __check_valid_ids(self, profile): | |
72 | """ | |
73 | Check if IDs contain valid data. | |
74 | """ | |
75 | for id in ("public_id", "private_id"): | |
76 | if re.match(r"^([a-f0-9]{40})$", "%s" % profile[id]) is None: | |
77 | raise tornado.web.HTTPError(400, "ID '%s' has wrong format: %s" % (id, profile)) | |
78 | ||
79 | def __check_equal_ids(self, profile): | |
80 | """ | |
81 | Check if public_id and private_id are equal. | |
82 | """ | |
83 | if profile.public_id == profile.private_id: | |
84 | raise tornado.web.HTTPError(400, "Public and private IDs are equal: %s" % profile) | |
85 | ||
86 | def __check_matching_ids(self, profile): | |
87 | """ | |
88 | Check if a profile with the given public_id is already in the | |
89 | database. If so we need to check if the private_id matches. | |
90 | """ | |
91 | p = self.profiles.find_one({ "public_id" : profile["public_id"]}) | |
92 | if not p: | |
93 | return | |
94 | ||
95 | p = Profile(p) | |
96 | if p.private_id != profile.private_id: | |
97 | raise tornado.web.HTTPError(400, "Mismatch of private_id: %s" % profile) | |
98 | ||
99 | def __check_profile_version(self, profile): | |
100 | """ | |
101 | Check if this version of the server software does support the | |
102 | received profile. | |
103 | """ | |
104 | version = profile.profile_version | |
105 | ||
106 | if version < MIN_PROFILE_VERSION or version > MAX_PROFILE_VERSION: | |
107 | raise tornado.web.HTTPError(400, | |
108 | "Profile version is not supported: %s" % version) | |
109 | ||
110 | def check_profile_blob(self, profile): | |
111 | """ | |
112 | This method checks if the blob is sane. | |
113 | """ | |
114 | checks = ( | |
115 | self.__check_attributes, | |
116 | self.__check_valid_ids, | |
117 | self.__check_equal_ids, | |
118 | self.__check_profile_version, | |
119 | # These checks require at least one database query and should be done | |
120 | # at last. | |
121 | self.__check_matching_ids, | |
122 | ) | |
123 | ||
124 | for check in checks: | |
125 | check(profile) | |
126 | ||
127 | # If we got here, everything is okay and we can go on... | |
128 | ||
129 | def get_profile_blob(self): | |
130 | profile = self.get_argument("profile", None) | |
131 | ||
132 | # Send "400 bad request" if no profile was provided | |
133 | if not profile: | |
134 | raise tornado.web.HTTPError(400, "No profile received") | |
135 | ||
136 | # Try to decode the profile. | |
137 | try: | |
a95c2f97 MT |
138 | return json.loads(profile) |
139 | except json.decoder.JSONDecodeError, e: | |
66862195 MT |
140 | raise tornado.web.HTTPError(400, "Profile could not be decoded: %s" % e) |
141 | ||
142 | # The GET method is only allowed in debugging mode. | |
143 | def get(self, public_id): | |
144 | if not self.application.settings["debug"]: | |
fe947d95 | 145 | raise tornado.web.HTTPError(405) |
66862195 MT |
146 | |
147 | return self.post(public_id) | |
148 | ||
149 | def post(self, public_id): | |
150 | profile_blob = self.get_profile_blob() | |
151 | #self.check_profile_blob(profile_blob) | |
152 | ||
153 | # Get location | |
154 | location = self.get_remote_location() | |
155 | if location: | |
156 | location = location.country | |
157 | ||
158 | # Handle the profile. | |
f726def6 MT |
159 | with self.db.transaction(): |
160 | try: | |
161 | self.fireinfo.handle_profile(public_id, profile_blob, location=location) | |
66862195 | 162 | |
a95c2f97 | 163 | except fireinfo.ProfileParserError: |
f726def6 | 164 | raise tornado.web.HTTPError(400) |
66862195 MT |
165 | |
166 | self.finish("Your profile was successfully saved to the database.") | |
167 | ||
168 | ||
169 | class FireinfoIndexHandler(FireinfoBaseHandler): | |
170 | def _profile_not_found(self, profile_id): | |
171 | self.set_status(404) | |
172 | self.render("fireinfo/profile-notfound.html", profile_id=profile_id) | |
173 | ||
174 | def get(self): | |
175 | self.render("fireinfo/index.html") | |
176 | ||
177 | def post(self): | |
178 | profile_id = self.get_argument("profile_id", None) | |
179 | if not profile_id: | |
180 | raise tornado.web.HTTPError(400, "No profile ID was given.") | |
181 | ||
182 | if not self.fireinfo.profile_exists(profile_id): | |
183 | self._profile_not_found(profile_id) | |
184 | return | |
185 | ||
186 | self.redirect("/profile/%s" % profile_id) | |
187 | ||
188 | ||
189 | class FireinfoProfileDetailHandler(FireinfoIndexHandler): | |
190 | def get(self, profile_id): | |
191 | profile = self.fireinfo.get_profile(profile_id, when=self.when) | |
192 | ||
193 | if not profile or not profile.is_showable(): | |
194 | self._profile_not_found(profile_id) | |
195 | return | |
196 | ||
197 | self.render("fireinfo/profile-detail.html", profile=profile) | |
198 | ||
199 | ||
200 | class FireinfoRandomProfileHandler(FireinfoBaseHandler): | |
201 | def get(self): | |
202 | profile_id = self.fireinfo.get_random_profile(when=self.when) | |
203 | if profile_id is None: | |
204 | raise tornado.web.HTTPError(404) | |
205 | ||
206 | self.redirect("/profile/%s" % profile_id) | |
207 | ||
208 | ||
209 | class FireinfoDeviceDriverDetail(FireinfoBaseHandler): | |
210 | def get(self, driver): | |
211 | self.render("fireinfo/driver.html", driver=driver, | |
212 | driver_map=self.fireinfo.get_driver_map(driver, when=self.when)) | |
213 | ||
214 | ||
215 | class FireinfoDeviceVendorsHandler(FireinfoBaseHandler): | |
216 | def get(self): | |
217 | vendors = self.fireinfo.get_vendor_list(when=self.when) | |
218 | ||
219 | self.render("fireinfo/vendors.html", vendors=vendors) | |
220 | ||
221 | ||
222 | class FireinfoStatsHandler(FireinfoBaseHandler): | |
223 | def get(self): | |
224 | self.render("fireinfo/stats.html") | |
225 | ||
226 | ||
227 | class FireinfoStatsProcessorsHandler(FireinfoBaseHandler): | |
228 | def get(self): | |
229 | avg, stddev, min, max = self.fireinfo.get_cpu_clock_speeds(when=self.when) | |
230 | arch_map = self.fireinfo.get_arch_map(when=self.when) | |
231 | ||
232 | data = { | |
233 | "arch_map" : arch_map, | |
234 | "cpu_vendors" : self.fireinfo.get_cpu_vendors_map(when=self.when), | |
235 | "clock_speed_avg" : avg, | |
236 | "clock_speed_stddev" : stddev, | |
237 | "clock_speed_min" : min, | |
238 | "clock_speed_max" : max, | |
239 | } | |
240 | ||
241 | return self.render("fireinfo/stats-cpus.html", **data) | |
242 | ||
243 | ||
244 | class FireinfoStatsProcessorDetailHandler(FireinfoBaseHandler): | |
245 | def get(self, platform): | |
246 | assert platform in ("arm", "x86") | |
247 | ||
248 | flags = self.fireinfo.get_common_cpu_flags_by_platform(platform, when=self.when) | |
249 | ||
250 | return self.render("fireinfo/stats-cpus-detail.html", | |
251 | platform=platform, flags=flags) | |
252 | ||
253 | ||
254 | class FireinfoStatsMemoryHandler(FireinfoBaseHandler): | |
255 | def get(self): | |
256 | avg, stddev, min, max = self.fireinfo.get_memory_amounts(when=self.when) | |
257 | amounts = self.fireinfo.get_common_memory_amounts(when=self.when) | |
258 | ||
259 | data = { | |
260 | "mem_avg" : avg, | |
261 | "mem_stddev" : stddev, | |
262 | "mem_min" : min, | |
263 | "mem_max" : max, | |
264 | "amounts" : amounts, | |
265 | } | |
266 | ||
267 | return self.render("fireinfo/stats-memory.html", **data) | |
268 | ||
269 | ||
270 | class FireinfoStatsReleasesHandler(FireinfoBaseHandler): | |
271 | def get(self): | |
272 | data = { | |
273 | "releases" : self.fireinfo.get_releases_map(when=self.when), | |
274 | "kernels" : self.fireinfo.get_kernels_map(when=self.when), | |
275 | } | |
276 | return self.render("fireinfo/stats-oses.html", **data) | |
277 | ||
278 | ||
279 | class FireinfoStatsVirtualHandler(FireinfoBaseHandler): | |
280 | def get(self): | |
281 | data = { | |
282 | "hypervisors" : self.fireinfo.get_hypervisor_map(when=self.when), | |
283 | "virtual" : self.fireinfo.get_virtual_ratio(when=self.when), | |
284 | } | |
285 | ||
286 | return self.render("fireinfo/stats-virtual.html", **data) | |
287 | ||
288 | ||
289 | class FireinfoStatsGeoHandler(FireinfoBaseHandler): | |
290 | def get(self): | |
291 | return self.render("fireinfo/stats-geo.html", | |
292 | geo_locations = self.fireinfo.get_geo_location_map(when=self.when)) | |
293 | ||
294 | ||
295 | class FireinfoStatsLanguagesHandler(FireinfoBaseHandler): | |
296 | def get(self): | |
297 | return self.render("fireinfo/stats-languages.html", | |
298 | languages = self.fireinfo.get_language_map(when=self.when)) | |
299 | ||
300 | ||
301 | class FireinfoStatsNetworkingHandler(FireinfoBaseHandler): | |
302 | def get(self): | |
303 | network=self.fireinfo.get_network_zones_map(when=self.when) | |
304 | ||
305 | return self.render("fireinfo/stats-network.html", network=network) | |
306 | ||
307 | ||
308 | class FireinfoDeviceVendorHandler(FireinfoBaseHandler): | |
309 | def get(self, subsystem, vendor_id): | |
310 | devices = self.fireinfo.get_devices_by_vendor(subsystem, vendor_id, | |
311 | when=self.when) | |
312 | ||
313 | vendor_name = self.fireinfo.get_vendor_string(subsystem, vendor_id) | |
314 | ||
315 | self.render("fireinfo/vendor-detail.html", vendor_name=vendor_name, | |
316 | devices=devices) | |
317 | ||
318 | ||
319 | class FireinfoDeviceVendorCompatHandler(FireinfoBaseHandler): | |
320 | def get(self, subsystem, vendor_id): | |
321 | self.redirect("/device/%s/%s" % (subsystem, vendor_id)) | |
322 | ||
323 | ||
324 | class FireinfoDeviceModelHandler(FireinfoBaseHandler): | |
325 | def get(self, subsystem, vendor, model): | |
326 | percentage = self.fireinfo.get_device_percentage(subsystem, vendor, | |
327 | model, when=self.when) | |
328 | percentage *= 100 | |
329 | ||
330 | profiles = self.fireinfo.get_device_in_profile(subsystem, vendor, | |
331 | model, when=self.when) | |
332 | ||
333 | vendor_name = self.fireinfo.get_vendor_string(subsystem, vendor) | |
334 | model_name = self.fireinfo.get_model_string(subsystem, vendor, model) | |
335 | ||
336 | self.render("fireinfo/model-detail.html", profiles=profiles, | |
337 | vendor_id=vendor, vendor_name=vendor_name, | |
338 | model_id=model, model_name=model_name, | |
339 | percentage=percentage, subsystem=subsystem) | |
340 | ||
341 | ||
342 | class FireinfoDeviceModelCompatHandler(FireinfoBaseHandler): | |
343 | def get(self, subsystem, vendor_id, model_id): | |
344 | self.redirect("/device/%s/%s/%s" % (subsystem, vendor_id, model_id)) | |
345 | ||
346 | ||
347 | class AdminFireinfoHandler(FireinfoBaseHandler): | |
348 | def get(self): | |
349 | profiles_with_data, profiles_all = self.fireinfo.get_active_profiles(when=self.when) | |
350 | ||
351 | data = { | |
352 | "archive_size" : self.fireinfo.get_archive_size(when=self.when), | |
de14b297 | 353 | "total_updates" : self.fireinfo.get_total_updates_count(when=self.when), |
66862195 MT |
354 | "profiles_with_data" : profiles_with_data, |
355 | "profiles_all" : profiles_all, | |
356 | } | |
357 | ||
358 | self.render("fireinfo/stats-admin.html", **data) |