]>
Commit | Line | Data |
---|---|---|
66862195 MT |
1 | #!/usr/bin/python |
2 | ||
66862195 | 3 | import datetime |
66862195 MT |
4 | import logging |
5 | import re | |
a95c2f97 | 6 | import json |
66862195 MT |
7 | import tornado.web |
8 | ||
a95c2f97 | 9 | from .. import fireinfo |
66862195 | 10 | |
124a8404 | 11 | from . import base |
96c9bb79 | 12 | from . import ui_modules |
66862195 | 13 | |
96c9bb79 | 14 | class BaseHandler(base.BaseHandler): |
66862195 MT |
15 | @property |
16 | def when(self): | |
17 | return self.get_argument_date("when", None) | |
18 | ||
19 | ||
20 | MIN_PROFILE_VERSION = 0 | |
21 | MAX_PROFILE_VERSION = 0 | |
22 | ||
23 | class Profile(dict): | |
24 | def __getattr__(self, key): | |
25 | try: | |
26 | return self[key] | |
27 | except KeyError: | |
11347e46 | 28 | raise AttributeError(key) |
66862195 MT |
29 | |
30 | def __setattr__(self, key, val): | |
31 | self[key] = val | |
32 | ||
33 | ||
96c9bb79 | 34 | class ProfileSendHandler(BaseHandler): |
66862195 MT |
35 | def check_xsrf_cookie(self): |
36 | # This cookie is not required here. | |
37 | pass | |
38 | ||
39 | def prepare(self): | |
40 | # Create an empty profile. | |
41 | self.profile = Profile() | |
42 | ||
43 | def __check_attributes(self, profile): | |
44 | """ | |
45 | Check for attributes that must be provided, | |
46 | """ | |
47 | attributes = ( | |
48 | "private_id", | |
49 | "profile_version", | |
50 | "public_id", | |
51 | ) | |
52 | for attr in attributes: | |
11347e46 | 53 | if attr not in profile: |
66862195 MT |
54 | raise tornado.web.HTTPError(400, "Profile lacks '%s' attribute: %s" % (attr, profile)) |
55 | ||
56 | def __check_valid_ids(self, profile): | |
57 | """ | |
58 | Check if IDs contain valid data. | |
59 | """ | |
60 | for id in ("public_id", "private_id"): | |
61 | if re.match(r"^([a-f0-9]{40})$", "%s" % profile[id]) is None: | |
62 | raise tornado.web.HTTPError(400, "ID '%s' has wrong format: %s" % (id, profile)) | |
63 | ||
64 | def __check_equal_ids(self, profile): | |
65 | """ | |
66 | Check if public_id and private_id are equal. | |
67 | """ | |
68 | if profile.public_id == profile.private_id: | |
69 | raise tornado.web.HTTPError(400, "Public and private IDs are equal: %s" % profile) | |
70 | ||
71 | def __check_matching_ids(self, profile): | |
72 | """ | |
73 | Check if a profile with the given public_id is already in the | |
74 | database. If so we need to check if the private_id matches. | |
75 | """ | |
76 | p = self.profiles.find_one({ "public_id" : profile["public_id"]}) | |
77 | if not p: | |
78 | return | |
79 | ||
80 | p = Profile(p) | |
81 | if p.private_id != profile.private_id: | |
82 | raise tornado.web.HTTPError(400, "Mismatch of private_id: %s" % profile) | |
83 | ||
84 | def __check_profile_version(self, profile): | |
85 | """ | |
86 | Check if this version of the server software does support the | |
87 | received profile. | |
88 | """ | |
89 | version = profile.profile_version | |
90 | ||
91 | if version < MIN_PROFILE_VERSION or version > MAX_PROFILE_VERSION: | |
92 | raise tornado.web.HTTPError(400, | |
93 | "Profile version is not supported: %s" % version) | |
94 | ||
95 | def check_profile_blob(self, profile): | |
96 | """ | |
97 | This method checks if the blob is sane. | |
98 | """ | |
99 | checks = ( | |
100 | self.__check_attributes, | |
101 | self.__check_valid_ids, | |
102 | self.__check_equal_ids, | |
103 | self.__check_profile_version, | |
104 | # These checks require at least one database query and should be done | |
105 | # at last. | |
106 | self.__check_matching_ids, | |
107 | ) | |
108 | ||
109 | for check in checks: | |
110 | check(profile) | |
111 | ||
112 | # If we got here, everything is okay and we can go on... | |
113 | ||
114 | def get_profile_blob(self): | |
115 | profile = self.get_argument("profile", None) | |
116 | ||
117 | # Send "400 bad request" if no profile was provided | |
118 | if not profile: | |
119 | raise tornado.web.HTTPError(400, "No profile received") | |
120 | ||
121 | # Try to decode the profile. | |
122 | try: | |
a95c2f97 | 123 | return json.loads(profile) |
11347e46 | 124 | except json.decoder.JSONDecodeError as e: |
66862195 MT |
125 | raise tornado.web.HTTPError(400, "Profile could not be decoded: %s" % e) |
126 | ||
127 | # The GET method is only allowed in debugging mode. | |
128 | def get(self, public_id): | |
129 | if not self.application.settings["debug"]: | |
fe947d95 | 130 | raise tornado.web.HTTPError(405) |
66862195 MT |
131 | |
132 | return self.post(public_id) | |
133 | ||
134 | def post(self, public_id): | |
135 | profile_blob = self.get_profile_blob() | |
136 | #self.check_profile_blob(profile_blob) | |
137 | ||
138 | # Get location | |
139 | location = self.get_remote_location() | |
140 | if location: | |
141 | location = location.country | |
142 | ||
143 | # Handle the profile. | |
a69e87a1 MT |
144 | with self.db.transaction(): |
145 | try: | |
146 | self.fireinfo.handle_profile(public_id, profile_blob, location=location) | |
66862195 | 147 | |
240e9253 MT |
148 | except fireinfo.ProfileParserError as e: |
149 | raise tornado.web.HTTPError(400, "Could not parse profile: %s" % e) | |
66862195 MT |
150 | |
151 | self.finish("Your profile was successfully saved to the database.") | |
152 | ||
153 | ||
96c9bb79 | 154 | class IndexHandler(BaseHandler): |
66862195 | 155 | def get(self): |
c4099434 | 156 | data = { |
11ee2139 MT |
157 | # Release |
158 | "latest_release" : self.backend.releases.get_latest(), | |
159 | ||
84604476 | 160 | # Hardware |
5a34028b MT |
161 | "arches" : self.fireinfo.get_arch_map(when=self.when), |
162 | "cpu_vendors" : self.fireinfo.get_cpu_vendors_map(when=self.when), | |
84604476 MT |
163 | "memory_avg" : self.backend.fireinfo.get_average_memory_amount(when=self.when), |
164 | ||
11ee2139 MT |
165 | # Virtualization |
166 | "hypervisors" : self.fireinfo.get_hypervisor_map(when=self.when), | |
167 | "virtual_ratio" : self.fireinfo.get_virtual_ratio(when=self.when), | |
574a88c7 MT |
168 | |
169 | # Location | |
170 | "locations" : self.fireinfo.get_geo_location_map(when=self.when), | |
c4099434 MT |
171 | } |
172 | ||
8eec8811 MT |
173 | # Cache for 1h |
174 | self.set_expires(3600) | |
175 | ||
c4099434 | 176 | self.render("fireinfo/index.html", **data) |
66862195 | 177 | |
66862195 | 178 | |
1e3b2aad MT |
179 | class DriverDetail(BaseHandler): |
180 | def get(self, driver): | |
8eec8811 MT |
181 | # Cache for 1h |
182 | self.set_expires(3600) | |
183 | ||
1e3b2aad MT |
184 | self.render("fireinfo/driver.html", driver=driver, |
185 | driver_map=self.fireinfo.get_driver_map(driver, when=self.when)) | |
186 | ||
187 | ||
b84b407f | 188 | class ProfileHandler(BaseHandler): |
66862195 MT |
189 | def get(self, profile_id): |
190 | profile = self.fireinfo.get_profile(profile_id, when=self.when) | |
191 | ||
192 | if not profile or not profile.is_showable(): | |
b84b407f | 193 | raise tornado.web.HTTPError(404) |
66862195 | 194 | |
8eec8811 MT |
195 | # Cache for 1h |
196 | self.set_expires(3600) | |
197 | ||
b84b407f | 198 | self.render("fireinfo/profile.html", profile=profile) |
66862195 MT |
199 | |
200 | ||
96c9bb79 | 201 | class RandomProfileHandler(BaseHandler): |
66862195 MT |
202 | def get(self): |
203 | profile_id = self.fireinfo.get_random_profile(when=self.when) | |
204 | if profile_id is None: | |
205 | raise tornado.web.HTTPError(404) | |
206 | ||
207 | self.redirect("/profile/%s" % profile_id) | |
208 | ||
209 | ||
ed2e3c1f MT |
210 | class ReleasesHandler(BaseHandler): |
211 | def get(self): | |
212 | data = { | |
213 | "releases" : self.fireinfo.get_releases_map(when=self.when), | |
214 | "kernels" : self.fireinfo.get_kernels_map(when=self.when), | |
215 | } | |
216 | ||
217 | # Cache for 1h | |
218 | self.set_expires(3600) | |
219 | ||
220 | return self.render("fireinfo/releases.html", **data) | |
221 | ||
222 | ||
19518d6e | 223 | class ProcessorsHandler(BaseHandler): |
66862195 | 224 | def get(self): |
19518d6e | 225 | flags = {} |
66862195 | 226 | |
19518d6e MT |
227 | for platform in ("arm", "x86"): |
228 | flags[platform] = \ | |
229 | self.fireinfo.get_common_cpu_flags_by_platform(platform, when=self.when) | |
66862195 | 230 | |
19518d6e MT |
231 | # Cache for 1h |
232 | self.set_expires(3600) | |
66862195 | 233 | |
19518d6e | 234 | return self.render("fireinfo/processors.html", flags=flags) |
66862195 MT |
235 | |
236 | ||
8ab37e0b MT |
237 | class VendorsHandler(BaseHandler): |
238 | def get(self): | |
239 | vendors = self.fireinfo.get_vendor_list(when=self.when) | |
240 | ||
8eec8811 MT |
241 | # Cache for 1h |
242 | self.set_expires(3600) | |
243 | ||
8ab37e0b MT |
244 | self.render("fireinfo/vendors.html", vendors=vendors) |
245 | ||
246 | ||
247 | class VendorHandler(BaseHandler): | |
66862195 | 248 | def get(self, subsystem, vendor_id): |
8ab37e0b MT |
249 | devices = self.fireinfo.get_devices_by_vendor(subsystem, vendor_id, when=self.when) |
250 | if not devices: | |
251 | raise tornado.web.HTTPError(404) | |
66862195 MT |
252 | |
253 | vendor_name = self.fireinfo.get_vendor_string(subsystem, vendor_id) | |
254 | ||
8eec8811 MT |
255 | # Cache for 1h |
256 | self.set_expires(3600) | |
257 | ||
851a00bc | 258 | self.render("fireinfo/vendor.html", vendor_name=vendor_name, devices=devices) |
66862195 MT |
259 | |
260 | ||
96c9bb79 | 261 | class DeviceTableModule(ui_modules.UIModule): |
3697181e | 262 | def render(self, devices, show_group=True, embedded=False): |
96c9bb79 | 263 | return self.render_string("fireinfo/modules/table-devices.html", |
3697181e | 264 | devices=devices, show_group=show_group, embedded=embedded) |
96c9bb79 MT |
265 | |
266 | ||
267 | class DeviceAndGroupsTableModule(ui_modules.UIModule): | |
268 | def render(self, devices): | |
269 | _ = self.locale.translate | |
270 | ||
271 | groups = {} | |
272 | ||
273 | for device in devices: | |
274 | if device.cls not in groups: | |
275 | groups[device.cls] = [] | |
276 | ||
277 | groups[device.cls].append(device) | |
278 | ||
279 | # Sort all devices | |
280 | for key in list(groups.keys()): | |
281 | groups[key].sort() | |
282 | ||
283 | # Order the groups by their name | |
284 | groups = list(groups.items()) | |
285 | groups.sort() | |
286 | ||
287 | return self.render_string("fireinfo/modules/table-devices-and-groups.html", | |
288 | groups=groups) | |
dc96f754 MT |
289 | |
290 | ||
291 | class AdminIndexHandler(BaseHandler): | |
292 | @tornado.web.authenticated | |
293 | def prepare(self): | |
294 | if not self.current_user.is_staff(): | |
295 | raise tornado.web.HTTPError(401) | |
296 | ||
297 | @tornado.web.authenticated | |
298 | def get(self): | |
dc838425 | 299 | with_data, total = self.backend.fireinfo.get_active_profiles() |
dc96f754 | 300 | |
dc838425 | 301 | self.render("fireinfo/admin.html", with_data=with_data, total=total) |