]> git.ipfire.org Git - ipfire.org.git/blame - src/web/fireinfo.py
fireinfo: Show total amount of profiles in database
[ipfire.org.git] / src / web / fireinfo.py
CommitLineData
66862195
MT
1#!/usr/bin/python
2
66862195 3import datetime
66862195
MT
4import logging
5import re
a95c2f97 6import json
66862195
MT
7import tornado.web
8
a95c2f97 9from .. import fireinfo
66862195 10
124a8404 11from . import base
96c9bb79 12from . import ui_modules
66862195 13
96c9bb79 14class BaseHandler(base.BaseHandler):
66862195
MT
15 @property
16 def when(self):
17 return self.get_argument_date("when", None)
18
19
20MIN_PROFILE_VERSION = 0
21MAX_PROFILE_VERSION = 0
22
23class 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 34class 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 154class 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
179class 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 188class 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 201class 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
210class 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 223class 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
237class 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
247class 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 261class 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
267class 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
291class 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):
299 count = self.backend.fireinfo.get_profile_count()
300
301 self.render("fireinfo/admin.html", count=count)