]>
Commit | Line | Data |
---|---|---|
66862195 MT |
1 | #!/usr/bin/python |
2 | ||
3 | from __future__ import division | |
4 | ||
5 | import datetime | |
6 | import hwdata | |
e400e37d | 7 | import iso3166 |
66862195 MT |
8 | import logging |
9 | import re | |
10 | ||
e400e37d | 11 | import database |
66862195 MT |
12 | import util |
13 | from misc import Object | |
14 | ||
15 | N_ = lambda x: x | |
16 | ||
17 | CPU_VENDORS = { | |
18 | "AMDisbetter!" : "AMD", | |
19 | "AuthenticAMD" : "AMD", | |
20 | "CentaurHauls" : "VIA", | |
21 | "CyrixInstead" : "Cyrix", | |
22 | "GenuineIntel" : "Intel", | |
23 | "TransmetaCPU" : "Transmeta", | |
24 | "GenuineTMx86" : "Transmeta", | |
25 | "Geode by NSC" : "NSC", | |
26 | "NexGenDriven" : "NexGen", | |
27 | "RiseRiseRise" : "Rise", | |
28 | "SiS SiS SiS" : "SiS", | |
29 | "SiS SiS SiS " : "SiS", | |
30 | "UMC UMC UMC " : "UMC", | |
31 | "VIA VIA VIA " : "VIA", | |
32 | "Vortex86 SoC" : "Vortex86", | |
33 | } | |
34 | ||
35 | CPU_STRINGS = ( | |
05bd7298 MT |
36 | ### AMD ### |
37 | # APU | |
3c855735 | 38 | (r"AMD (Sempron)\(tm\) (\d+) APU with Radeon\(tm\) R\d+", r"AMD \1 \2 APU"), |
c85ebbc4 | 39 | (r"AMD ([\w\-]+) APU with Radeon\(tm\) HD Graphics", r"AMD \1 APU"), |
74638712 | 40 | (r"AMD ([\w\-]+) Radeon R\d+, \d+ Compute Cores \d+C\+\d+G", r"AMD \1 APU"), |
05bd7298 MT |
41 | # Athlon |
42 | (r"AMD Athlon.* II X2 ([a-z0-9]+).*", r"AMD Athlon X2 \1"), | |
43 | (r"AMD Athlon\(tm\) 64 Processor (\w+)", r"AMD Athlon64 \1"), | |
44 | (r"AMD Athlon\(tm\) 64 X2 Dual Core Processor (\w+)", r"AMD Athlon64 X2 \1"), | |
66862195 MT |
45 | (r"(AMD Athlon).*(XP).*", r"\1 \2"), |
46 | (r"(AMD Phenom).* ([0-9]+) .*", r"\1 \2"), | |
47 | (r"(AMD Phenom).*", r"\1"), | |
48 | (r"(AMD Sempron).*", r"\1"), | |
05bd7298 MT |
49 | # Geode |
50 | (r"Geode\(TM\) Integrated Processor by AMD PCS", r"AMD Geode"), | |
66862195 | 51 | (r"(Geode).*", r"\1"), |
74638712 MT |
52 | # Mobile |
53 | (r"Mobile AMD (Athlon|Sempron)\(tm\) Processor (\d+\+?)", r"AMD \1-M \2"), | |
66862195 MT |
54 | |
55 | # Intel | |
56 | (r"Intel\(R\) (Atom|Celeron).*CPU\s*([A-Z0-9]+) .*", r"Intel \1 \2"), | |
57 | (r"(Intel).*(Celeron).*", r"\1 \2"), | |
05bd7298 MT |
58 | (r"Intel\(R\)? Core\(TM\)?2 Duo *CPU .* ([A-Z0-9]+) .*", r"Intel C2D \1"), |
59 | (r"Intel\(R\)? Core\(TM\)?2 Duo CPU (\w+)", r"Intel C2D \1"), | |
60 | (r"Intel\(R\)? Core\(TM\)?2 CPU .* ([A-Z0-9]+) .*", r"Intel C2 \1"), | |
61 | (r"Intel\(R\)? Core\(TM\)?2 Quad *CPU .* ([A-Z0-9]+) .*", r"Intel C2Q \1"), | |
62 | (r"Intel\(R\)? Core\(TM\)? (i[753]\-\w+) CPU", r"Intel Core \1"), | |
63 | (r"Intel\(R\)? Xeon\(R\)? CPU (\w+) (0|v\d+)", r"Intel Xeon \1 \2"), | |
64 | (r"Intel\(R\)? Xeon\(R\)? CPU\s+(\w+)", r"Intel Xeon \1"), | |
66862195 MT |
65 | (r"(Intel).*(Xeon).*", r"\1 \2"), |
66 | (r"Intel.* Pentium.* (D|4) .*", r"Intel Pentium \1"), | |
67 | (r"Intel.* Pentium.* Dual .* ([A-Z0-9]+) .*", r"Intel Pentium Dual \1"), | |
68 | (r"Pentium.* Dual-Core .* ([A-Z0-9]+) .*", r"Intel Pentium Dual \1"), | |
69 | (r"(Pentium I{2,3}).*", r"Intel \1"), | |
70 | (r"(Celeron \(Coppermine\))", r"Intel Celeron"), | |
71 | ||
05bd7298 MT |
72 | # NSC |
73 | (r"Geode\(TM\) Integrated Processor by National Semi", r"NSC Geode"), | |
74 | ||
66862195 MT |
75 | # VIA |
76 | (r"(VIA \w*).*", r"\1"), | |
77 | ||
78 | # Qemu | |
79 | (r"QEMU Virtual CPU version .*", r"QEMU CPU"), | |
80 | ||
81 | # ARM | |
82 | (r"Feroceon .*", r"ARM Feroceon"), | |
83 | ) | |
84 | ||
85 | IGNORED_DEVICES = ["usb",] | |
86 | ||
87 | class ProfileDict(object): | |
88 | def __init__(self, data): | |
89 | self._data = data | |
90 | ||
91 | ||
92 | class ProfileNetwork(ProfileDict): | |
93 | def __eq__(self, other): | |
95ae21d1 MT |
94 | if other is None: |
95 | return False | |
96 | ||
66862195 MT |
97 | if not self.has_red == other.has_red: |
98 | return False | |
99 | ||
100 | if not self.has_green == other.has_green: | |
101 | return False | |
102 | ||
103 | if not self.has_orange == other.has_orange: | |
104 | return False | |
105 | ||
106 | if not self.has_blue == other.has_blue: | |
107 | return False | |
108 | ||
109 | return True | |
110 | ||
111 | def __iter__(self): | |
112 | ret = [] | |
113 | ||
114 | for zone in ("red", "green", "orange", "blue"): | |
115 | if self.has_zone(zone): | |
116 | ret.append(zone) | |
117 | ||
118 | return iter(ret) | |
119 | ||
120 | def has_zone(self, name): | |
121 | return self._data.get("has_%s" % name) | |
122 | ||
123 | @property | |
124 | def has_red(self): | |
125 | return self._data.get("has_red", False) | |
126 | ||
127 | @property | |
128 | def has_green(self): | |
129 | return self._data.get("has_green", False) | |
130 | ||
131 | @property | |
132 | def has_orange(self): | |
133 | return self._data.get("has_orange", False) | |
134 | ||
135 | @property | |
136 | def has_blue(self): | |
137 | return self._data.get("has_blue", False) | |
138 | ||
139 | ||
140 | class Processor(Object): | |
141 | def __init__(self, backend, id, data=None, clock_speed=None, bogomips=None): | |
142 | Object.__init__(self, backend) | |
143 | ||
144 | self.id = id | |
145 | self.__data = data | |
146 | self.__clock_speed = clock_speed | |
147 | self.__bogomips = bogomips | |
148 | ||
149 | def __str__(self): | |
150 | s = [] | |
151 | ||
152 | if not self.model_string.startswith(self.vendor): | |
153 | s.append(self.vendor) | |
154 | s.append("-") | |
155 | ||
156 | s.append(self.model_string) | |
157 | ||
158 | if self.core_count > 1: | |
159 | s.append("x%s" % self.core_count) | |
160 | ||
161 | return " ".join(s) | |
162 | ||
163 | @property | |
164 | def data(self): | |
165 | if self.__data is None: | |
166 | self.__data = self.db.get("SELECT * FROM fireinfo_processors \ | |
167 | WHERE id = %s", self.id) | |
168 | ||
169 | return self.__data | |
170 | ||
171 | @property | |
172 | def vendor(self): | |
173 | try: | |
174 | return CPU_VENDORS[self.data.vendor] | |
175 | except KeyError: | |
176 | return self.data.vendor | |
177 | ||
574ce4e6 MT |
178 | @property |
179 | def family(self): | |
180 | return self.data.family | |
181 | ||
66862195 MT |
182 | @property |
183 | def model(self): | |
184 | return self.data.model | |
185 | ||
574ce4e6 MT |
186 | @property |
187 | def stepping(self): | |
188 | return self.data.stepping | |
189 | ||
66862195 MT |
190 | @property |
191 | def model_string(self): | |
192 | s = self.data.model_string.split() | |
193 | ||
194 | return " ".join((e for e in s if e)) | |
195 | ||
196 | @property | |
197 | def flags(self): | |
198 | return self.data.flags | |
199 | ||
200 | def has_flag(self, flag): | |
201 | return flag in self.flags | |
202 | ||
203 | def uses_ht(self): | |
8137d982 | 204 | if self.vendor == "Intel" and self.family == 6 and self.model in (15, 55, 76, 77): |
574ce4e6 MT |
205 | return False |
206 | ||
66862195 MT |
207 | return self.has_flag("ht") |
208 | ||
209 | @property | |
210 | def core_count(self): | |
211 | return self.data.core_count | |
212 | ||
213 | @property | |
214 | def count(self): | |
215 | if self.uses_ht(): | |
216 | return self.core_count // 2 | |
217 | ||
218 | return self.core_count | |
219 | ||
220 | @property | |
221 | def clock_speed(self): | |
222 | return self.__clock_speed | |
223 | ||
224 | def format_clock_speed(self): | |
225 | if not self.clock_speed: | |
226 | return | |
227 | ||
228 | if self.clock_speed < 1000: | |
229 | return "%dMHz" % self.clock_speed | |
230 | ||
231 | return "%.2fGHz" % round(self.clock_speed / 1000, 2) | |
232 | ||
233 | @property | |
234 | def bogomips(self): | |
235 | return self.__bogomips | |
236 | ||
237 | @property | |
238 | def capabilities(self): | |
239 | caps = [ | |
240 | ("64bit", self.has_flag("lm")), | |
241 | ("aes", self.has_flag("aes")), | |
242 | ("nx", self.has_flag("nx")), | |
243 | ("pae", self.has_flag("pae") or self.has_flag("lpae")), | |
244 | ("rdrand", self.has_flag("rdrand")), | |
245 | ] | |
246 | ||
247 | # If the system is already running in a virtual environment, | |
248 | # we cannot correctly detect if the CPU supports svm or vmx | |
249 | if self.has_flag("hypervisor"): | |
250 | caps.append(("virt", None)) | |
251 | else: | |
252 | caps.append(("virt", self.has_flag("vmx") or self.has_flag("svm"))) | |
253 | ||
254 | return caps | |
255 | ||
256 | def format_model(self): | |
257 | s = self.model_string | |
05bd7298 MT |
258 | |
259 | # Remove everything after the @: Intel(R) Core(TM) i7-3770 CPU @ 3.40GHz | |
260 | s, sep, rest = s.partition("@") | |
261 | ||
66862195 MT |
262 | for pattern, repl in CPU_STRINGS: |
263 | if re.match(pattern, s) is None: | |
264 | continue | |
05bd7298 MT |
265 | |
266 | s = re.sub(pattern, repl, s) | |
267 | break | |
66862195 MT |
268 | |
269 | # Otherwise remove the symbols | |
05bd7298 | 270 | for i in ("C", "R", "TM", "tm"): |
66862195 MT |
271 | s = s.replace("(%s)" % i, "") |
272 | ||
05bd7298 MT |
273 | # Replace too long strings with shorter ones |
274 | pairs = ( | |
275 | ("Quad-Core Processor", ""), | |
276 | ("Dual-Core Processor", ""), | |
277 | ("Processor", "CPU"), | |
278 | ("processor", "CPU"), | |
279 | ) | |
280 | for k, v in pairs: | |
281 | s = s.replace(k, v) | |
282 | ||
283 | # Remove too many spaces | |
284 | s = " ".join((e for e in s.split() if e)) | |
285 | ||
66862195 MT |
286 | return s |
287 | ||
288 | @property | |
289 | def friendly_string(self): | |
290 | s = [] | |
291 | ||
292 | model = self.format_model() | |
293 | s.append(model) | |
294 | ||
295 | clock_speed = self.format_clock_speed() | |
296 | if clock_speed: | |
297 | s.append("@ %s" % clock_speed) | |
298 | ||
299 | if self.count > 1: | |
300 | s.append("x%s" % self.count) | |
301 | ||
302 | return " ".join(s) | |
303 | ||
304 | ||
305 | class Device(Object): | |
306 | classid2name = { | |
307 | "pci" : { | |
308 | "00" : N_("Unclassified"), | |
309 | "01" : N_("Mass storage"), | |
310 | "02" : N_("Network"), | |
311 | "03" : N_("Display"), | |
312 | "04" : N_("Multimedia"), | |
313 | "05" : N_("Memory controller"), | |
314 | "06" : N_("Bridge"), | |
315 | "07" : N_("Communication"), | |
316 | "08" : N_("Generic system peripheral"), | |
317 | "09" : N_("Input device"), | |
318 | "0a" : N_("Docking station"), | |
319 | "0b" : N_("Processor"), | |
320 | "0c" : N_("Serial bus"), | |
321 | "0d" : N_("Wireless"), | |
322 | "0e" : N_("Intelligent controller"), | |
323 | "0f" : N_("Satellite communications controller"), | |
324 | "10" : N_("Encryption"), | |
325 | "11" : N_("Signal processing controller"), | |
326 | "ff" : N_("Unassigned class"), | |
327 | }, | |
328 | ||
329 | "usb" : { | |
330 | "00" : N_("Unclassified"), | |
331 | "01" : N_("Multimedia"), | |
332 | "02" : N_("Communication"), | |
333 | "03" : N_("Input device"), | |
334 | "05" : N_("Generic system peripheral"), | |
335 | "06" : N_("Image"), | |
336 | "07" : N_("Printer"), | |
337 | "08" : N_("Mass storage"), | |
338 | "09" : N_("Hub"), | |
339 | "0a" : N_("Communication"), | |
340 | "0b" : N_("Smart card"), | |
341 | "0d" : N_("Encryption"), | |
342 | "0e" : N_("Display"), | |
343 | "0f" : N_("Personal Healthcare"), | |
344 | "dc" : N_("Diagnostic Device"), | |
345 | "e0" : N_("Wireless"), | |
346 | "ef" : N_("Unclassified"), | |
347 | "fe" : N_("Unclassified"), | |
348 | "ff" : N_("Unclassified"), | |
349 | } | |
350 | } | |
351 | ||
352 | def __init__(self, backend, id, data=None): | |
353 | Object.__init__(self, backend) | |
354 | ||
355 | self.id = id | |
356 | self.__data = data | |
357 | ||
358 | def __repr__(self): | |
359 | return "<%s vendor=%s model=%s>" % (self.__class__.__name__, | |
360 | self.vendor_string, self.model_string) | |
361 | ||
362 | def __cmp__(self, other): | |
363 | if self.id and self.id == other.id: | |
364 | return 0 | |
365 | ||
366 | return \ | |
367 | cmp(self.subsystem, other.subsystem) or \ | |
368 | cmp(self.vendor_string, other.vendor_string) or \ | |
369 | cmp(self.vendor, other.vendor) or \ | |
370 | cmp(self.model_string, other.model_string) or \ | |
371 | cmp(self.model, other.model) or \ | |
372 | cmp(self.driver, other.driver) | |
373 | ||
374 | @property | |
375 | def data(self): | |
376 | if self.__data is None: | |
377 | assert self.id | |
378 | ||
379 | self.__data = self.db.get("SELECT * FROM fireinfo_devices \ | |
380 | WHERE id = %s", self.id) | |
381 | ||
382 | return self.__data | |
383 | ||
384 | def is_showable(self): | |
385 | if self.driver in IGNORED_DEVICES: | |
386 | return False | |
387 | ||
388 | if self.driver in ("pcieport", "hub"): | |
389 | return False | |
390 | ||
391 | return True | |
392 | ||
393 | @property | |
394 | def subsystem(self): | |
395 | return self.data.subsystem | |
396 | ||
397 | @property | |
398 | def model(self): | |
399 | return self.data.model | |
400 | ||
401 | @property | |
402 | def model_string(self): | |
403 | return self.fireinfo.get_model_string(self.subsystem, | |
404 | self.vendor, self.model) | |
405 | ||
406 | @property | |
407 | def vendor(self): | |
408 | return self.data.vendor | |
409 | ||
410 | @property | |
411 | def vendor_string(self): | |
412 | return self.fireinfo.get_vendor_string(self.subsystem, self.vendor) | |
413 | ||
414 | @property | |
415 | def driver(self): | |
416 | return self.data.driver | |
417 | ||
418 | @property | |
419 | def cls(self): | |
420 | classid = self.data.deviceclass | |
421 | ||
422 | if self.subsystem == "pci": | |
423 | classid = classid[:-4] | |
424 | if len(classid) == 1: | |
425 | classid = "0%s" % classid | |
426 | ||
427 | elif self.subsystem == "usb" and classid: | |
428 | classid = classid.split("/")[0] | |
429 | classid = "%02x" % int(classid) | |
430 | ||
431 | try: | |
432 | return self.classid2name[self.subsystem][classid] | |
433 | except KeyError: | |
434 | return "N/A" | |
435 | ||
436 | @property | |
437 | def percentage(self): | |
438 | return self.data.get("percentage", None) | |
439 | ||
440 | ||
441 | class Profile(Object): | |
442 | def __init__(self, backend, id, data=None): | |
443 | Object.__init__(self, backend) | |
444 | ||
445 | self.id = id | |
446 | self.__data = data | |
447 | ||
448 | def __repr__(self): | |
449 | return "<%s %s>" % (self.__class__.__name__, self.public_id) | |
450 | ||
451 | def __cmp__(self, other): | |
452 | return cmp(self.id, other.id) | |
453 | ||
454 | def is_showable(self): | |
455 | if self.arch_id: | |
456 | return True | |
457 | ||
458 | return False | |
459 | ||
460 | @property | |
461 | def data(self): | |
462 | if self.__data is None: | |
463 | self.__data = self.db.get("SELECT * FROM fireinfo_profiles \ | |
464 | WHERE id = %s", self.id) | |
465 | ||
466 | return self.__data | |
467 | ||
468 | @property | |
469 | def public_id(self): | |
470 | return self.data.public_id | |
471 | ||
472 | @property | |
473 | def private_id(self): | |
474 | raise NotImplementedError | |
475 | ||
476 | @property | |
477 | def time_created(self): | |
478 | return self.data.time_created | |
479 | ||
480 | @property | |
481 | def time_updated(self): | |
482 | return self.data.time_updated | |
483 | ||
484 | def updated(self, profile_parser=None, location=None, when=None): | |
485 | valid = self.settings.get_int("fireinfo_profile_days_valid", 14) | |
486 | ||
487 | self.db.execute("UPDATE fireinfo_profiles \ | |
488 | SET \ | |
489 | time_updated = then_or_now(%s), \ | |
490 | time_valid = then_or_now(%s) + INTERVAL '%s days', \ | |
491 | updates = updates + 1 \ | |
492 | WHERE id = %s", when, when, valid, self.id) | |
493 | ||
494 | if profile_parser: | |
495 | self.set_processor_speeds( | |
496 | profile_parser.processor_clock_speed, | |
497 | profile_parser.processor_bogomips, | |
498 | ) | |
499 | ||
500 | if location: | |
501 | self.set_location(location) | |
502 | ||
f5e42730 MT |
503 | self.log_profile_update() |
504 | ||
505 | def log_profile_update(self): | |
506 | # Log that an update was performed for this profile id | |
507 | self.db.execute("INSERT INTO fireinfo_profiles_log(public_id) \ | |
508 | VALUES(%s)", self.public_id) | |
509 | ||
66862195 MT |
510 | def expired(self, when=None): |
511 | self.db.execute("UPDATE fireinfo_profiles \ | |
512 | SET time_valid = then_or_now(%s) WHERE id = %s", when, self.id) | |
513 | ||
514 | def parse(self, parser): | |
515 | # Processor | |
516 | self.processor = parser.processor | |
517 | self.set_processor_speeds(parser.processor_clock_speed, parser.processor_bogomips) | |
518 | ||
519 | # All devices | |
520 | self.devices = parser.devices | |
521 | ||
522 | # System | |
523 | self.system_id = parser.system_id | |
524 | ||
525 | # Memory | |
526 | self.memory = parser.memory | |
527 | ||
528 | # Storage | |
529 | self.storage = parser.storage | |
530 | ||
531 | # Kernel | |
532 | self.kernel_id = parser.kernel_id | |
533 | ||
534 | # Arch | |
535 | self.arch_id = parser.arch_id | |
536 | ||
537 | # Release | |
538 | self.release_id = parser.release_id | |
539 | ||
540 | # Language | |
541 | self.language = parser.language | |
542 | ||
543 | # Virtual | |
544 | if parser.virtual: | |
545 | self.hypervisor_id = parser.hypervisor_id | |
546 | ||
547 | # Network | |
548 | self.network = parser.network | |
549 | ||
550 | # Location | |
551 | ||
552 | def get_location(self): | |
553 | if not hasattr(self, "_location"): | |
554 | res = self.db.get("SELECT location FROM fireinfo_profiles_locations \ | |
555 | WHERE profile_id = %s", self.id) | |
556 | ||
557 | if res: | |
558 | self._location = res.location | |
559 | else: | |
560 | self._location = None | |
561 | ||
562 | return self._location | |
563 | ||
564 | def set_location(self, location): | |
565 | if self.location == location: | |
566 | return | |
567 | ||
568 | self.db.execute("DELETE FROM fireinfo_profiles_locations \ | |
569 | WHERE profile_id = %s", self.id) | |
570 | self.db.execute("INSERT INTO fireinfo_profiles_locations(profile_id, location) \ | |
571 | VALUES(%s, %s)", self.id, location) | |
572 | ||
573 | self._location = location | |
574 | ||
575 | location = property(get_location, set_location) | |
576 | ||
577 | @property | |
578 | def location_string(self): | |
579 | return self.geoip.get_country_name(self.location) | |
580 | ||
581 | # Devices | |
582 | ||
583 | @property | |
584 | def device_ids(self): | |
585 | if not hasattr(self, "_device_ids"): | |
586 | res = self.db.query("SELECT device_id FROM fireinfo_profiles_devices \ | |
587 | WHERE profile_id = %s", self.id) | |
588 | ||
589 | self._device_ids = sorted([r.device_id for r in res]) | |
590 | ||
591 | return self._device_ids | |
592 | ||
593 | def get_devices(self): | |
594 | if not hasattr(self, "_devices"): | |
595 | res = self.db.query("SELECT * FROM fireinfo_devices \ | |
596 | LEFT JOIN fireinfo_profiles_devices ON \ | |
597 | fireinfo_devices.id = fireinfo_profiles_devices.device_id \ | |
598 | WHERE fireinfo_profiles_devices.profile_id = %s", self.id) | |
599 | ||
600 | self._devices = [] | |
601 | for row in res: | |
602 | device = Device(self.backend, row.id, row) | |
603 | self._devices.append(device) | |
604 | ||
605 | return self._devices | |
606 | ||
607 | def set_devices(self, devices): | |
608 | device_ids = [d.id for d in devices] | |
609 | ||
610 | self.db.execute("DELETE FROM fireinfo_profiles_devices WHERE profile_id = %s", self.id) | |
611 | self.db.executemany("INSERT INTO fireinfo_profiles_devices(profile_id, device_id) \ | |
612 | VALUES(%s, %s)", ((self.id, d) for d in device_ids)) | |
613 | ||
614 | self._devices = devices | |
615 | self._device_ids = device_ids | |
616 | ||
617 | devices = property(get_devices, set_devices) | |
618 | ||
4238ef83 | 619 | def count_device(self, subsystem, vendor, model): |
66862195 MT |
620 | counter = 0 |
621 | ||
622 | for dev in self.devices: | |
4238ef83 | 623 | if dev.subsystem == subsystem and dev.vendor == vendor and dev.model == model: |
66862195 MT |
624 | counter += 1 |
625 | ||
626 | return counter | |
627 | ||
628 | # System | |
629 | ||
630 | def get_system_id(self): | |
631 | if not hasattr(self, "_system_id"): | |
632 | res = self.db.get("SELECT system_id AS id FROM fireinfo_profiles_systems \ | |
633 | WHERE profile_id = %s", self.id) | |
634 | ||
635 | if res: | |
636 | self._system_id = res.id | |
637 | else: | |
638 | self._system_id = None | |
639 | ||
640 | return self._system_id | |
641 | ||
642 | def set_system_id(self, system_id): | |
643 | self.db.execute("DELETE FROM fireinfo_profiles_systems WHERE profile_id = %s", self.id) | |
644 | ||
645 | if system_id: | |
646 | self.db.execute("INSERT INTO fireinfo_profiles_systems(profile_id, system_id) \ | |
647 | VALUES(%s, %s)", self.id, system_id) | |
648 | ||
649 | self._system_id = None | |
650 | if hasattr(self, "_system"): | |
651 | del self._system | |
652 | ||
653 | system_id = property(get_system_id, set_system_id) | |
654 | ||
655 | @property | |
656 | def system(self): | |
657 | if not hasattr(self, "_system"): | |
658 | res = self.db.get("SELECT fireinfo_systems.vendor AS vendor, fireinfo_systems.model AS model \ | |
659 | FROM fireinfo_profiles_systems \ | |
660 | LEFT JOIN fireinfo_systems ON fireinfo_profiles_systems.system_id = fireinfo_systems.id \ | |
661 | WHERE fireinfo_profiles_systems.profile_id = %s", self.id) | |
662 | ||
663 | if res: | |
664 | self._system = (res.vendor, res.model) | |
665 | else: | |
666 | self._system = (None, None) | |
667 | ||
668 | return self._system | |
669 | ||
670 | @property | |
671 | def system_vendor(self): | |
672 | try: | |
673 | v, m = self.system | |
674 | return v | |
675 | except TypeError: | |
676 | pass | |
677 | ||
678 | @property | |
679 | def system_model(self): | |
680 | try: | |
681 | v, m = self.system | |
682 | return m | |
683 | except TypeError: | |
684 | pass | |
685 | ||
686 | @property | |
687 | def appliance_id(self): | |
688 | if not hasattr(self, "_appliance_id"): | |
689 | appliances = ( | |
50b5392d | 690 | ("fountainnetworks-duo-box", self._appliance_test_fountainnetworks_duo_box), |
4238ef83 | 691 | ("fountainnetworks-prime", self._appliance_test_fountainnetworks_prime), |
82793e74 | 692 | ("lightningwirelabs-eco-plus", self._appliance_test_lightningwirelabs_eco_plus), |
66862195 MT |
693 | ("lightningwirelabs-eco", self._appliance_test_lightningwirelabs_eco), |
694 | ) | |
695 | ||
696 | self._appliance_id = None | |
697 | for name, test_func in appliances: | |
698 | if not test_func(): | |
699 | continue | |
700 | ||
701 | self._appliance_id = name | |
702 | break | |
703 | ||
704 | return self._appliance_id | |
705 | ||
706 | @property | |
707 | def appliance(self): | |
50b5392d MT |
708 | if self.appliance_id == "fountainnetworks-duo-box": |
709 | return "Fountain Networks - IPFire Duo Box" | |
710 | ||
711 | elif self.appliance_id == "fountainnetworks-prime": | |
4238ef83 MT |
712 | return "Fountain Networks - IPFire Prime Box" |
713 | ||
82793e74 MT |
714 | elif self.appliance_id == "lightningwirelabs-eco-plus": |
715 | return "Lightning Wire Labs - IPFire Eco Plus Appliance" | |
716 | ||
4238ef83 MT |
717 | elif self.appliance_id == "lightningwirelabs-eco": |
718 | return "Lightning Wire Labs - IPFire Eco Appliance" | |
719 | ||
50b5392d MT |
720 | def _appliance_test_fountainnetworks_duo_box(self): |
721 | if not self.processor.vendor == "Intel": | |
722 | return False | |
723 | ||
724 | if not self.processor.model_string == "Intel(R) Celeron(R) 2957U @ 1.40GHz": | |
725 | return False | |
726 | ||
727 | if not self.count_device("pci", "10ec", "8168") == 2: | |
728 | return False | |
729 | ||
730 | # WiFi module | |
731 | if self.count_device("usb", "148f", "5572") < 1: | |
732 | return False | |
733 | ||
734 | return True | |
735 | ||
4238ef83 | 736 | def _appliance_test_fountainnetworks_prime(self): |
9fd4f970 | 737 | if not self.system in (("SECO", None), ("SECO", "0949")): |
4238ef83 MT |
738 | return False |
739 | ||
740 | # Must have a wireless device | |
741 | if self.count_device("usb", "148f", "5572") < 1: | |
742 | return False | |
743 | ||
744 | return True | |
66862195 MT |
745 | |
746 | def _appliance_test_lightningwirelabs_eco(self): | |
747 | if not self.system == ("MSI", "MS-9877"): | |
748 | return False | |
749 | ||
750 | # Must have four Intel network adapters | |
4238ef83 | 751 | network_adapters_count = self.count_device("pci", "8086", "10d3") |
66862195 MT |
752 | if not network_adapters_count == 4: |
753 | return False | |
754 | ||
66862195 MT |
755 | return True |
756 | ||
82793e74 MT |
757 | def _appliance_test_lightningwirelabs_eco_plus(self): |
758 | if not self.system_vendor == "ASUS": | |
759 | return False | |
760 | ||
761 | if not self.system_model.startswith("P9A-I/2550"): | |
762 | return False | |
763 | ||
764 | # Must have four Intel network adapters | |
765 | network_adapters_count = self.count_device("pci", "8086", "1f41") | |
766 | if not network_adapters_count == 4: | |
767 | return False | |
768 | ||
769 | return True | |
770 | ||
66862195 MT |
771 | # Processors |
772 | ||
773 | @property | |
774 | def processor_id(self): | |
775 | if hasattr(self, "_processor"): | |
776 | return self._processor.id | |
777 | ||
778 | if not hasattr(self, "_processor_id"): | |
779 | res = self.db.get("SELECT processor_id FROM fireinfo_profiles_processors \ | |
780 | WHERE profile_id = %s", self.id) | |
781 | ||
782 | if res: | |
783 | self._processor_id = res.processor_id | |
784 | else: | |
785 | self._processor_id = None | |
786 | ||
787 | return self._processor_id | |
788 | ||
789 | def get_processor(self): | |
790 | if not self.processor_id: | |
791 | return | |
792 | ||
793 | if not hasattr(self, "_processor"): | |
794 | res = self.db.get("SELECT * FROM fireinfo_profiles_processors \ | |
795 | WHERE profile_id = %s", self.id) | |
796 | ||
797 | if res: | |
798 | self._processor = self.fireinfo.get_processor_by_id(res.processor_id, | |
799 | clock_speed=res.clock_speed, bogomips=res.bogomips) | |
800 | else: | |
801 | self._processor = None | |
802 | ||
803 | return self._processor | |
804 | ||
805 | def set_processor(self, processor): | |
806 | self.db.execute("DELETE FROM fireinfo_profiles_processors \ | |
807 | WHERE profile_id = %s", self.id) | |
808 | ||
809 | if processor: | |
810 | self.db.execute("INSERT INTO fireinfo_profiles_processors(profile_id, processor_id) \ | |
811 | VALUES(%s, %s)", self.id, processor.id) | |
812 | ||
813 | self._processor = processor | |
814 | ||
815 | processor = property(get_processor, set_processor) | |
816 | ||
817 | def set_processor_speeds(self, clock_speed, bogomips): | |
818 | self.db.execute("UPDATE fireinfo_profiles_processors \ | |
819 | SET clock_speed = %s, bogomips = %s WHERE profile_id = %s", | |
820 | clock_speed, bogomips, self.id) | |
821 | ||
822 | # Compat | |
823 | @property | |
824 | def cpu(self): | |
825 | return self.processor | |
826 | ||
827 | # Memory | |
828 | ||
829 | def get_memory(self): | |
830 | if not hasattr(self, "_memory"): | |
831 | res = self.db.get("SELECT amount FROM fireinfo_profiles_memory \ | |
832 | WHERE profile_id = %s", self.id) | |
833 | ||
834 | if res: | |
835 | self._memory = res.amount * 1024 | |
836 | else: | |
837 | self._memory = None | |
838 | ||
839 | return self._memory | |
840 | ||
841 | def set_memory(self, amount): | |
842 | if self.memory == amount: | |
843 | return | |
844 | ||
845 | amount /= 1024 | |
846 | ||
847 | self.db.execute("DELETE FROM fireinfo_profiles_memory WHERE profile_id = %s", self.id) | |
848 | if amount: | |
849 | self.db.execute("INSERT INTO fireinfo_profiles_memory(profile_id, amount) \ | |
850 | VALUES(%s, %s)", self.id, amount) | |
851 | ||
852 | self._memory = amount * 1024 | |
853 | ||
854 | memory = property(get_memory, set_memory) | |
855 | ||
856 | @property | |
857 | def friendly_memory(self): | |
a0c9a14e | 858 | return util.format_size(self.memory or 0) |
66862195 MT |
859 | |
860 | # Storage | |
861 | ||
862 | def get_storage(self): | |
863 | if not hasattr(self, "_storage"): | |
864 | res = self.db.get("SELECT amount FROM fireinfo_profiles_storage \ | |
865 | WHERE profile_id = %s", self.id) | |
866 | ||
867 | if res: | |
868 | self._storage = res.amount * 1024 | |
869 | else: | |
870 | self._storage = None | |
871 | ||
872 | return self._storage | |
873 | ||
874 | def set_storage(self, amount): | |
875 | if self.storage == amount: | |
876 | return | |
877 | ||
878 | amount /= 1024 | |
879 | ||
880 | self.db.execute("DELETE FROM fireinfo_profiles_storage WHERE profile_id = %s", self.id) | |
881 | if amount: | |
882 | self.db.execute("INSERT INTO fireinfo_profiles_storage(profile_id, amount) \ | |
883 | VALUES(%s, %s)", self.id, amount) | |
884 | ||
885 | self._storage = amount * 1024 | |
886 | ||
887 | storage = property(get_storage, set_storage) | |
888 | ||
889 | @property | |
890 | def friendly_storage(self): | |
891 | return util.format_size(self.storage) | |
892 | ||
893 | # Kernel | |
894 | ||
895 | def get_kernel_id(self): | |
896 | if not hasattr(self, "_kernel_id"): | |
897 | res = self.db.get("SELECT fireinfo_profiles_kernels.kernel_id AS id FROM fireinfo_profiles \ | |
898 | LEFT JOIN fireinfo_profiles_kernels ON fireinfo_profiles.id = fireinfo_profiles_kernels.profile_id \ | |
899 | WHERE fireinfo_profiles.id = %s", self.id) | |
900 | ||
901 | if res: | |
902 | self._kernel_id = res.id | |
903 | else: | |
904 | self._kernel_id = None | |
905 | ||
906 | return self._kernel_id | |
907 | ||
908 | def set_kernel_id(self, kernel_id): | |
909 | if self.kernel_id == kernel_id: | |
910 | return | |
911 | ||
912 | self.db.execute("DELETE FROM fireinfo_profiles_kernels WHERE profile_id = %s", self.id) | |
913 | if kernel_id: | |
914 | self.db.execute("INSERT INTO fireinfo_profiles_kernels(profile_id, kernel_id) \ | |
915 | VALUES(%s, %s)", self.id, kernel_id) | |
916 | ||
917 | self._kernel_id = kernel_id | |
918 | if hasattr(self, "_kernel"): | |
919 | del self._kernel | |
920 | ||
921 | kernel_id = property(get_kernel_id, set_kernel_id) | |
922 | ||
923 | @property | |
924 | def kernel(self): | |
925 | if not hasattr(self, "_kernel"): | |
926 | res = self.db.get("SELECT fireinfo_kernels.name AS name FROM fireinfo_profiles \ | |
927 | LEFT JOIN fireinfo_profiles_kernels ON fireinfo_profiles.id = fireinfo_profiles_kernels.profile_id \ | |
928 | LEFT JOIN fireinfo_kernels ON fireinfo_kernels.id = fireinfo_profiles_kernels.kernel_id \ | |
929 | WHERE fireinfo_profiles.id = %s", self.id) | |
930 | ||
931 | if res: | |
932 | self._kernel = res.name | |
933 | else: | |
934 | self._kernel = None | |
935 | ||
936 | return self._kernel | |
937 | ||
938 | # Arch | |
939 | ||
940 | def get_arch_id(self): | |
941 | if not hasattr(self, "_arch_id"): | |
942 | res = self.db.get("SELECT fireinfo_profiles_arches.arch_id AS id FROM fireinfo_profiles \ | |
943 | LEFT JOIN fireinfo_profiles_arches ON fireinfo_profiles.id = fireinfo_profiles_arches.profile_id \ | |
944 | WHERE fireinfo_profiles.id = %s", self.id) | |
945 | ||
946 | if res: | |
947 | self._arch_id = res.id | |
948 | else: | |
949 | self._arch_id = None | |
950 | ||
951 | return self._arch_id | |
952 | ||
953 | def set_arch_id(self, arch_id): | |
954 | if self.arch_id == arch_id: | |
955 | return | |
956 | ||
957 | self.db.execute("DELETE FROM fireinfo_profiles_arches WHERE profile_id = %s", self.id) | |
958 | if arch_id: | |
959 | self.db.execute("INSERT INTO fireinfo_profiles_arches(profile_id, arch_id) \ | |
960 | VALUES(%s, %s)", self.id, arch_id) | |
961 | ||
962 | self._arch_id = None | |
963 | if hasattr(self, "_arch"): | |
964 | del self._arch | |
965 | ||
966 | arch_id = property(get_arch_id, set_arch_id) | |
967 | ||
968 | @property | |
969 | def arch(self): | |
970 | if not hasattr(self, "_arch"): | |
971 | res = self.db.get("SELECT fireinfo_arches.name AS name FROM fireinfo_profiles \ | |
972 | LEFT JOIN fireinfo_profiles_arches ON fireinfo_profiles.id = fireinfo_profiles_arches.profile_id \ | |
973 | LEFT JOIN fireinfo_arches ON fireinfo_arches.id = fireinfo_profiles_arches.arch_id \ | |
974 | WHERE fireinfo_profiles.id = %s", self.id) | |
975 | ||
976 | if res: | |
977 | self._arch = res.name | |
978 | else: | |
979 | self._arch = None | |
980 | ||
981 | return self._arch | |
982 | ||
983 | # Release | |
984 | ||
985 | def get_release_id(self): | |
986 | if not hasattr(self, "_release_id"): | |
987 | res = self.db.get("SELECT fireinfo_profiles_releases.release_id AS id FROM fireinfo_profiles \ | |
988 | LEFT JOIN fireinfo_profiles_releases ON fireinfo_profiles.id = fireinfo_profiles_releases.profile_id \ | |
989 | WHERE fireinfo_profiles.id = %s", self.id) | |
990 | ||
991 | if res: | |
992 | self._release_id = res.id | |
993 | else: | |
994 | self._release_id = None | |
995 | ||
996 | return self._release_id | |
997 | ||
998 | def set_release_id(self, release_id): | |
999 | if self.release_id == release_id: | |
1000 | return | |
1001 | ||
1002 | self.db.execute("DELETE FROM fireinfo_profiles_releases WHERE profile_id = %s", self.id) | |
1003 | if release_id: | |
1004 | self.db.execute("INSERT INTO fireinfo_profiles_releases(profile_id, release_id) \ | |
1005 | VALUES(%s, %s)", self.id, release_id) | |
1006 | ||
1007 | self._release_id = release_id | |
1008 | if hasattr(self, "_release"): | |
1009 | del self._release | |
1010 | ||
1011 | release_id = property(get_release_id, set_release_id) | |
1012 | ||
1013 | @property | |
1014 | def release(self): | |
1015 | if not hasattr(self, "_release"): | |
1016 | res = self.db.get("SELECT fireinfo_releases.name AS name FROM fireinfo_profiles \ | |
1017 | LEFT JOIN fireinfo_profiles_releases ON fireinfo_profiles.id = fireinfo_profiles_releases.profile_id \ | |
1018 | LEFT JOIN fireinfo_releases ON fireinfo_profiles_releases.release_id = fireinfo_releases.id \ | |
1019 | WHERE fireinfo_profiles.id = %s", self.id) | |
1020 | ||
1021 | if res: | |
1022 | self._release = self._format_release(res.name) | |
1023 | else: | |
1024 | self._release = None | |
1025 | ||
1026 | return self._release | |
1027 | ||
1028 | @staticmethod | |
1029 | def _format_release(r): | |
1030 | if not r: | |
1031 | return r | |
1032 | ||
1033 | # Remove the development header | |
1034 | r = r.replace("Development Build: ", "") | |
1035 | ||
1036 | pairs = ( | |
1037 | ("-beta", " - Beta "), | |
1038 | ("-rc", " - Release Candidate "), | |
1039 | ("rc", "Release Candidate "), | |
1040 | ("core", "Core Update "), | |
1041 | ("beta", "Beta "), | |
1042 | ) | |
1043 | ||
1044 | for k, v in pairs: | |
1045 | r = r.replace(k, v) | |
1046 | ||
1047 | return r | |
1048 | ||
05bd7298 MT |
1049 | @property |
1050 | def release_short(self): | |
1051 | pairs = ( | |
1052 | (r"Release Candidate (\d+)", r"RC\1"), | |
1053 | ) | |
1054 | ||
1055 | s = self.release | |
1056 | for pattern, repl in pairs: | |
1057 | if re.search(pattern, s) is None: | |
1058 | continue | |
1059 | ||
1060 | s = re.sub(pattern, repl, s) | |
1061 | ||
1062 | return s | |
1063 | ||
66862195 MT |
1064 | # Virtual |
1065 | ||
1066 | @property | |
1067 | def virtual(self): | |
1068 | if not hasattr(self, "_virtual"): | |
1069 | res = self.db.get("SELECT 1 FROM fireinfo_profiles_virtual \ | |
1070 | WHERE profile_id = %s", self.id) | |
1071 | ||
1072 | if res: | |
1073 | self._virtual = True | |
1074 | else: | |
1075 | self._virtual = False | |
1076 | ||
1077 | return self._virtual | |
1078 | ||
1079 | def get_hypervisor_id(self): | |
1080 | if not hasattr(self, "_hypervisor_id"): | |
1081 | res = self.db.get("SELECT fireinfo_profiles_virtual.hypervisor_id AS id FROM fireinfo_profiles \ | |
1082 | LEFT JOIN fireinfo_profiles_virtual ON fireinfo_profiles.id = fireinfo_profiles_virtual.profile_id \ | |
1083 | WHERE fireinfo_profiles.id = %s", self.id) | |
1084 | ||
1085 | if res: | |
1086 | self._hypervisor_id = res.id | |
1087 | else: | |
1088 | self._hypervisor_id = None | |
1089 | ||
1090 | return self._hypervisor_id | |
1091 | ||
1092 | def set_hypervisor_id(self, hypervisor_id): | |
1093 | self.db.execute("DELETE FROM fireinfo_profiles_virtual WHERE profile_id = %s", self.id) | |
1094 | self.db.execute("INSERT INTO fireinfo_profiles_virtual(profile_id, hypervisor_id) \ | |
1095 | VALUES(%s, %s)", self.id, hypervisor_id) | |
1096 | ||
1097 | self._hypervisor_id = hypervisor_id | |
1098 | ||
1099 | hypervisor_id = property(get_hypervisor_id, set_hypervisor_id) | |
1100 | ||
1101 | @property | |
1102 | def hypervisor(self): | |
1103 | if not hasattr(self, "_hypervisor"): | |
1104 | res = self.db.get("SELECT fireinfo_hypervisors.name AS hypervisor FROM fireinfo_profiles \ | |
1105 | LEFT JOIN fireinfo_profiles_virtual ON fireinfo_profiles.id = fireinfo_profiles_virtual.profile_id \ | |
1106 | LEFT JOIN fireinfo_hypervisors ON fireinfo_profiles_virtual.hypervisor_id = fireinfo_hypervisors.id \ | |
1107 | WHERE fireinfo_profiles.id = %s", self.id) | |
1108 | ||
1109 | if res: | |
1110 | self._hypervisor = res.hypervisor | |
1111 | else: | |
1112 | self._hypervisor = None | |
1113 | ||
1114 | return self._hypervisor | |
1115 | ||
1116 | # Language | |
1117 | ||
1118 | def get_language(self): | |
1119 | if not hasattr(self, "_language"): | |
1120 | res = self.db.get("SELECT language FROM fireinfo_profiles_languages \ | |
1121 | WHERE profile_id = %s", self.id) | |
1122 | ||
1123 | if res: | |
1124 | self._language = res.language | |
1125 | else: | |
1126 | self._language = None | |
1127 | ||
1128 | return self._language | |
1129 | ||
1130 | def set_language(self, language): | |
1131 | self.db.execute("DELETE FROM fireinfo_profiles_languages WHERE profile_id = %s", self.id) | |
1132 | ||
1133 | if language: | |
1134 | self.db.execute("INSERT INTO fireinfo_profiles_languages(profile_id, language) \ | |
1135 | VALUES(%s, %s)", self.id, language) | |
1136 | ||
1137 | self._language = language | |
1138 | ||
1139 | language = property(get_language, set_language) | |
1140 | ||
1141 | # Network | |
1142 | ||
1143 | def get_network(self): | |
1144 | if not hasattr(self, "_network"): | |
1145 | res = self.db.get("SELECT * FROM fireinfo_profiles_networks \ | |
1146 | WHERE profile_id = %s", self.id) | |
1147 | ||
1148 | if not res: | |
1149 | res = {} | |
1150 | ||
1151 | self._network = ProfileNetwork(res) | |
1152 | ||
1153 | return self._network | |
1154 | ||
1155 | def set_network(self, network): | |
1156 | self.db.execute("DELETE FROM fireinfo_profiles_networks WHERE profile_id = %s", self.id) | |
1157 | ||
1158 | if network: | |
1159 | self.db.execute("INSERT INTO fireinfo_profiles_networks(profile_id, \ | |
1160 | has_red, has_green, has_orange, has_blue) VALUES(%s, %s, %s, %s, %s)", | |
1161 | self.id, network.has_red, network.has_green, network.has_orange, network.has_blue) | |
1162 | ||
1163 | self._network = network | |
1164 | ||
1165 | network = property(get_network, set_network) | |
1166 | ||
1167 | ||
1168 | class ProfileData(Object): | |
1169 | def __init__(self, backend, id, data=None, profile=None): | |
1170 | Object.__init__(self, backend) | |
1171 | ||
1172 | self.id = id | |
1173 | self._data = data | |
1174 | self._profile = profile | |
1175 | ||
1176 | @property | |
1177 | def data(self): | |
1178 | if self._data is None: | |
1179 | self._data = self.db.get("SELECT * FROM fireinfo_profile_data \ | |
1180 | WHERE id = %s", self.id) | |
1181 | ||
1182 | return self._data | |
1183 | ||
1184 | @property | |
1185 | def profile(self): | |
1186 | if not self._profile: | |
1187 | self._profile = self.fireinfo.get_profile_by_id(self.profile_id) | |
1188 | ||
1189 | return self._profile | |
1190 | ||
1191 | @property | |
1192 | def profile_id(self): | |
1193 | return self.data.profile_id | |
1194 | ||
1195 | ||
1196 | class ProfileParserError(Exception): | |
1197 | pass | |
1198 | ||
1199 | ||
1200 | class ProfileParser(Object): | |
1201 | __device_args = ( | |
1202 | "subsystem", | |
1203 | "vendor", | |
1204 | "model", | |
1205 | "sub_vendor", | |
1206 | "sub_model", | |
1207 | "driver", | |
1208 | "deviceclass", | |
1209 | ) | |
1210 | ||
1211 | __processor_args = ( | |
1212 | "vendor", | |
1213 | "model_string", | |
1214 | "family", | |
1215 | "model", | |
1216 | "stepping", | |
1217 | "core_count", | |
1218 | "flags", | |
1219 | ) | |
1220 | ||
1221 | __processor_args_mandatory = ( | |
1222 | "vendor", | |
1223 | "model_string", | |
1224 | ) | |
1225 | ||
1226 | def __init__(self, backend, public_id, blob=None): | |
1227 | Object.__init__(self, backend) | |
1228 | ||
1229 | self.public_id = public_id | |
1230 | self.private_id = None | |
1231 | self.devices = [] | |
1232 | self.processor = None | |
1233 | self.processor_clock_speed = None | |
1234 | self.processor_bogomips = None | |
1235 | self.system_id = None | |
1236 | self.memory = None | |
1237 | self.storage = None | |
1238 | self.kernel = None | |
1239 | self.kernel_id = None | |
1240 | self.arch = None | |
1241 | self.arch_id = None | |
1242 | self.release = None | |
1243 | self.release_id = None | |
1244 | self.language = None | |
1245 | self.virtual = None | |
1246 | self.hypervisor_id = None | |
1247 | self.network = None | |
1248 | ||
1249 | self.__parse_blob(blob) | |
1250 | ||
1251 | def equals(self, other): | |
1252 | if not self.processor_id == other.processor_id: | |
1253 | return False | |
1254 | ||
1255 | if not self.device_ids == other.device_ids: | |
1256 | return False | |
1257 | ||
1258 | if not self.system_id == other.system_id: | |
1259 | return False | |
1260 | ||
1261 | if not self.memory == other.memory: | |
1262 | return False | |
1263 | ||
1264 | if not self.storage == other.storage: | |
1265 | return False | |
1266 | ||
1267 | if not self.kernel_id == other.kernel_id: | |
1268 | return False | |
1269 | ||
1270 | if not self.arch_id == other.arch_id: | |
1271 | return False | |
1272 | ||
1273 | if not self.release_id == other.release_id: | |
1274 | return False | |
1275 | ||
1276 | if not self.language == other.language: | |
1277 | return False | |
1278 | ||
1279 | if not self.virtual == other.virtual: | |
1280 | return False | |
1281 | ||
1282 | if other.virtual: | |
1283 | if not self.hypervisor_id == other.hypervisor_id: | |
1284 | return False | |
1285 | ||
1286 | if not self.network == other.network: | |
1287 | return False | |
1288 | ||
1289 | return True | |
1290 | ||
1291 | def __parse_blob(self, blob): | |
1292 | _profile = blob.get("profile", {}) | |
1293 | self.private_id = blob.get("private_id") | |
1294 | ||
1295 | # Do not try to parse an empty profile | |
1296 | if not _profile: | |
1297 | return | |
1298 | ||
1299 | # Processor | |
1300 | _processor = _profile.get("cpu", {}) | |
1301 | self.__parse_processor(_processor) | |
1302 | ||
1303 | # Find devices | |
1304 | _devices = _profile.get("devices", []) | |
1305 | self.__parse_devices(_devices) | |
1306 | ||
1307 | # System | |
1308 | _system = _profile.get("system") | |
1309 | if _system: | |
1310 | self.__parse_system(_system) | |
1311 | ||
1312 | # Memory (convert to bytes) | |
1313 | memory = _system.get("memory", None) | |
1314 | if memory: | |
1315 | self.memory = memory * 1024 | |
1316 | ||
1317 | # Storage size (convert to bytes) | |
1318 | storage = _system.get("root_size", None) | |
1319 | if storage: | |
1320 | self.storage = storage * 1024 | |
1321 | ||
1322 | # Kernel | |
1323 | kernel = _system.get("kernel_release", None) | |
1324 | if kernel: | |
1325 | self.__parse_kernel(kernel) | |
1326 | ||
1327 | # Release | |
1328 | release = _system.get("release", None) | |
1329 | if release: | |
1330 | self.__parse_release(release) | |
1331 | ||
1332 | # Language | |
1333 | language = _system.get("language", None) | |
1334 | if language: | |
1335 | self.__parse_language(language) | |
1336 | ||
1337 | # Virtual | |
1338 | self.virtual = _system.get("virtual", False) | |
1339 | if self.virtual: | |
1340 | hypervisor = _profile.get("hypervisor") | |
1341 | self.__parse_hypervisor(hypervisor) | |
1342 | ||
1343 | # Network | |
1344 | _network = _profile.get("network") | |
1345 | if _network: | |
1346 | self.__parse_network(_network) | |
1347 | ||
1348 | @property | |
1349 | def device_ids(self): | |
1350 | return sorted([d.id for d in self.devices]) | |
1351 | ||
1352 | def __parse_devices(self, _devices): | |
1353 | self.devices = [] | |
1354 | ||
1355 | for _device in _devices: | |
1356 | args = {} | |
1357 | for arg in self.__device_args: | |
1358 | args[arg] = _device.get(arg, None) | |
1359 | ||
95ae21d1 MT |
1360 | # Skip if the subsystem is not set |
1361 | if not args.get("subsystem", None): | |
1362 | continue | |
1363 | ||
66862195 MT |
1364 | # Find the device or create a new one. |
1365 | device = self.fireinfo.get_device(**args) | |
1366 | if not device: | |
1367 | device = self.fireinfo.create_device(**args) | |
1368 | ||
1369 | self.devices.append(device) | |
1370 | ||
1371 | def __parse_system(self, system): | |
1372 | vendor = system.get("vendor", None) | |
1373 | if not vendor: | |
1374 | vendor = None | |
1375 | ||
1376 | model = system.get("model", None) | |
1377 | if not model: | |
1378 | model = None | |
1379 | ||
1380 | self.system_id = self.fireinfo.get_system(vendor, model) | |
1381 | if not self.system_id: | |
1382 | self.system_id = self.fireinfo.create_system(vendor, model) | |
1383 | ||
1384 | @property | |
1385 | def processor_id(self): | |
1386 | if not self.processor: | |
1387 | return | |
1388 | ||
1389 | return self.processor.id | |
1390 | ||
1391 | def __parse_processor(self, _processor): | |
1392 | args = {} | |
1393 | for arg in self.__processor_args: | |
1394 | if arg == "core_count": | |
1395 | _arg = "count" | |
1396 | else: | |
1397 | _arg = arg | |
1398 | ||
1399 | args[arg] = _processor.get(_arg, None) | |
1400 | ||
1401 | for arg in self.__processor_args_mandatory: | |
1402 | if not args.get(arg, None): | |
1403 | raise ProfileParserError, "Mandatory argument missing: %s" % arg | |
1404 | ||
1405 | self.processor = self.fireinfo.get_processor(**args) | |
1406 | if not self.processor: | |
1407 | self.processor = self.fireinfo.create_processor(**args) | |
1408 | ||
1409 | self.processor_clock_speed = _processor.get("speed", None) | |
1410 | self.processor_bogomips = _processor.get("bogomips", None) | |
1411 | ||
1412 | arch = _processor.get("arch", None) | |
1413 | if arch: | |
1414 | self.__parse_arch(arch) | |
1415 | ||
1416 | def __parse_kernel(self, kernel): | |
1417 | self.kernel_id = self.fireinfo.get_kernel(kernel) | |
1418 | if not self.kernel_id: | |
1419 | self.kernel_id = self.fireinfo.create_kernel(kernel) | |
1420 | assert self.kernel_id | |
1421 | ||
1422 | self.kernel = kernel | |
1423 | ||
1424 | def __parse_arch(self, arch): | |
1425 | self.arch_id = self.fireinfo.get_arch(arch) | |
1426 | if not self.arch_id: | |
1427 | self.arch_id = self.fireinfo.create_arch(arch) | |
1428 | ||
1429 | self.arch = arch | |
1430 | ||
1431 | def __parse_release(self, release): | |
1432 | # Remove the arch bit | |
1433 | if release: | |
1434 | r = [e for e in release.split() if e] | |
9bd46813 | 1435 | for s in ("(x86_64)", "(i586)", "(armv5tel)"): |
66862195 MT |
1436 | try: |
1437 | r.remove(s) | |
1438 | break | |
1439 | except ValueError: | |
1440 | pass | |
1441 | ||
1442 | release = " ".join(r) | |
1443 | ||
1444 | self.release_id = self.fireinfo.get_release(release) | |
1445 | if not self.release_id: | |
1446 | self.release_id = self.fireinfo.create_release(release) | |
1447 | assert self.release_id | |
1448 | ||
1449 | self.release = release | |
1450 | ||
1451 | def __parse_language(self, language): | |
2c0d3e50 MT |
1452 | self.language = language |
1453 | self.language, delim, rest = self.language.partition(".") | |
1454 | self.language, delim, rest = self.language.partition("_") | |
66862195 MT |
1455 | |
1456 | def __parse_hypervisor(self, hypervisor): | |
1457 | vendor = hypervisor.get("vendor", "other") | |
1458 | ||
1459 | if vendor in ("other", "unknown"): | |
1460 | self.hypervisor_id = None | |
1461 | return | |
1462 | ||
1463 | self.hypervisor_id = self.fireinfo.get_hypervisor(vendor) | |
1464 | if not self.hypervisor_id: | |
1465 | self.hypervisor_id = self.fireinfo.create_hypervisor(vendor) | |
1466 | ||
1467 | def __parse_network(self, network): | |
1468 | self.network = ProfileNetwork({ | |
1469 | "has_red" : network.get("red", False), | |
1470 | "has_green" : network.get("green", False), | |
1471 | "has_orange" : network.get("orange", False), | |
1472 | "has_blue" : network.get("blue", False), | |
1473 | }) | |
1474 | ||
1475 | ||
1476 | class Fireinfo(Object): | |
1477 | def get_profile_count(self, when=None): | |
1478 | res = self.db.get("SELECT COUNT(*) AS count FROM fireinfo_profiles \ | |
1479 | WHERE then_or_now(%s) BETWEEN time_created AND time_valid", when) | |
1480 | ||
1481 | if res: | |
1482 | return res.count | |
1483 | ||
de14b297 MT |
1484 | def get_total_updates_count(self, when=None): |
1485 | res = self.db.get("SELECT COUNT(*) + SUM(updates) AS count \ | |
1486 | FROM fireinfo_profiles WHERE time_created <= then_or_now(%s)", when) | |
66862195 MT |
1487 | |
1488 | if res: | |
1489 | return res.count | |
1490 | ||
1491 | # Parser | |
1492 | ||
1493 | def parse_profile(self, public_id, blob): | |
1494 | return ProfileParser(self.backend, public_id, blob) | |
1495 | ||
1496 | # Profiles | |
1497 | ||
1498 | def profile_exists(self, public_id): | |
1499 | res = self.db.get("SELECT id FROM fireinfo_profiles \ | |
1500 | WHERE public_id = %s LIMIT 1", public_id) | |
1501 | ||
1502 | if res: | |
1503 | return True | |
1504 | ||
1505 | return False | |
1506 | ||
f5e42730 | 1507 | def profile_rate_limit_active(self, public_id, when=None): |
f5e42730 | 1508 | res = self.db.get("SELECT COUNT(*) AS count FROM fireinfo_profiles_log \ |
191087b6 MT |
1509 | WHERE public_id = %s AND ts >= then_or_now(%s) - INTERVAL '60 minutes'", |
1510 | public_id, when) | |
f5e42730 MT |
1511 | |
1512 | if res and res.count >= 10: | |
1513 | return True | |
66862195 | 1514 | |
f5e42730 MT |
1515 | return False |
1516 | ||
1517 | def is_private_id_change_permitted(self, public_id, private_id, when=None): | |
6c748b46 MT |
1518 | # Check if a profile exists with a different private id that is still valid |
1519 | res = self.db.get("SELECT 1 FROM fireinfo_profiles \ | |
1520 | WHERE public_id = %s AND NOT private_id = %s \ | |
1521 | AND time_valid >= then_or_now(%s) LIMIT 1", public_id, private_id, when) | |
1522 | ||
1523 | if res: | |
1524 | return False | |
1525 | ||
1526 | return True | |
66862195 MT |
1527 | |
1528 | def get_profile(self, public_id, private_id=None, when=None): | |
1529 | res = self.db.get("SELECT * FROM fireinfo_profiles \ | |
1530 | WHERE public_id = %s AND \ | |
1531 | (CASE WHEN %s IS NULL THEN TRUE ELSE private_id = %s END) AND \ | |
46f3e814 MT |
1532 | then_or_now(%s) BETWEEN time_created AND time_valid \ |
1533 | ORDER BY time_updated DESC LIMIT 1", | |
1534 | public_id, private_id, private_id, when) | |
66862195 MT |
1535 | |
1536 | if res: | |
1537 | return Profile(self.backend, res.id, res) | |
1538 | ||
d7bebd28 MT |
1539 | def get_profile_with_data(self, public_id, when=None): |
1540 | res = self.db.get("WITH profiles AS (SELECT fireinfo_profiles_with_data_at(%s) AS id) \ | |
1541 | SELECT * FROM profiles JOIN fireinfo_profiles ON profiles.id = fireinfo_profiles.id \ | |
1542 | WHERE public_id = %s ORDER BY time_updated DESC LIMIT 1", when, public_id) | |
1543 | ||
1544 | if res: | |
1545 | return Profile(self.backend, res.id, res) | |
1546 | ||
66862195 MT |
1547 | def get_profiles(self, public_id): |
1548 | res = self.db.query("SELECT * FROM fireinfo_profiles \ | |
1549 | WHERE public_id = %s ORDER BY time_created DESC", public_id) | |
1550 | ||
1551 | profiles = [] | |
1552 | for row in res: | |
1553 | profile = Profile(self.backend, row.id, row) | |
1554 | profiles.append(profile) | |
1555 | ||
1556 | return profiles | |
1557 | ||
1558 | def create_profile(self, public_id, private_id, when=None): | |
1559 | valid = self.settings.get_int("fireinfo_profile_days_valid", 14) | |
1560 | ||
1561 | res = self.db.get("INSERT INTO fireinfo_profiles(public_id, private_id, \ | |
1562 | time_created, time_updated, time_valid) VALUES(%s, %s, then_or_now(%s), \ | |
1563 | then_or_now(%s), then_or_now(%s) + INTERVAL '%s days') RETURNING id", | |
1564 | public_id, private_id, when, when, when, valid) | |
1565 | ||
1566 | if res: | |
f5e42730 MT |
1567 | p = Profile(self.backend, res.id) |
1568 | p.log_profile_update() | |
1569 | ||
1570 | return p | |
66862195 MT |
1571 | |
1572 | # Devices | |
1573 | ||
1574 | def create_device(self, subsystem, vendor, model, sub_vendor=None, sub_model=None, | |
1575 | driver=None, deviceclass=None): | |
1576 | res = self.db.get("INSERT INTO fireinfo_devices(subsystem, vendor, model, \ | |
1577 | sub_vendor, sub_model, driver, deviceclass) VALUES(%s, %s, %s, %s, %s, %s, %s) \ | |
1578 | RETURNING id", subsystem, vendor, model, sub_vendor, sub_model, driver, deviceclass) | |
1579 | ||
1580 | if res: | |
1581 | return Device(self.backend, res.id) | |
1582 | ||
1583 | def get_device(self, subsystem, vendor, model, sub_vendor=None, sub_model=None, | |
1584 | driver=None, deviceclass=None): | |
1585 | res = self.db.get("SELECT * FROM fireinfo_devices \ | |
1586 | WHERE subsystem = %s AND vendor = %s AND model = %s \ | |
1587 | AND sub_vendor IS NOT DISTINCT FROM %s \ | |
1588 | AND sub_model IS NOT DISTINCT FROM %s \ | |
1589 | AND driver IS NOT DISTINCT FROM %s \ | |
1590 | AND deviceclass IS NOT DISTINCT FROM %s \ | |
1591 | LIMIT 1", subsystem, vendor, model, sub_vendor, | |
1592 | sub_model, driver, deviceclass) | |
1593 | ||
1594 | if res: | |
1595 | return Device(self.backend, res.id, res) | |
1596 | ||
1597 | # System | |
1598 | ||
1599 | def create_system(self, vendor, model): | |
1600 | res = self.db.get("INSERT INTO fireinfo_systems(vendor, model) \ | |
1601 | VALUES(%s, %s) RETURNING id", vendor, model) | |
1602 | ||
1603 | if res: | |
1604 | return res.id | |
1605 | ||
1606 | def get_system(self, vendor, model): | |
1607 | res = self.db.get("SELECT id FROM fireinfo_systems WHERE vendor IS NOT DISTINCT FROM %s \ | |
1608 | AND model IS NOT DISTINCT FROM %s LIMIT 1", vendor, model) | |
1609 | ||
1610 | if res: | |
1611 | return res.id | |
1612 | ||
1613 | # Processors | |
1614 | ||
1615 | def create_processor(self, vendor, model_string, family, model, stepping, core_count, flags=None): | |
1616 | res = self.db.get("INSERT INTO fireinfo_processors(vendor, model_string, \ | |
1617 | family, model, stepping, core_count, flags) VALUES(%s, %s, %s, %s, %s, %s, %s) \ | |
1618 | RETURNING id", vendor, model_string, family, model, stepping, core_count, flags) | |
1619 | ||
1620 | if res: | |
1621 | return Processor(self.backend, res.id) | |
1622 | ||
1623 | def get_processor_by_id(self, processor_id, **kwargs): | |
1624 | res = self.db.get("SELECT * FROM fireinfo_processors \ | |
1625 | WHERE id = %s", processor_id) | |
1626 | ||
1627 | if res: | |
1628 | return Processor(self.backend, res.id, data=res, **kwargs) | |
1629 | ||
1630 | def get_processor(self, vendor, model_string, family, model, stepping, core_count, flags=None): | |
1631 | if flags is None: | |
1632 | flags = [] | |
1633 | ||
1634 | res = self.db.get("SELECT * FROM fireinfo_processors \ | |
1635 | WHERE vendor = %s AND model_string = %s \ | |
1636 | AND family IS NOT DISTINCT FROM %s AND model IS NOT DISTINCT FROM %s \ | |
1637 | AND stepping IS NOT DISTINCT FROM %s AND core_count = %s \ | |
1638 | AND flags <@ %s AND flags @> %s", vendor, model_string, family, model, | |
1639 | stepping, core_count, flags, flags) | |
1640 | ||
1641 | if res: | |
1642 | return Processor(self.backend, res.id, res) | |
1643 | ||
1644 | # Kernel | |
1645 | ||
1646 | def create_kernel(self, kernel): | |
1647 | res = self.db.get("INSERT INTO fireinfo_kernels(name) VALUES(%s) \ | |
1648 | RETURNING id", kernel) | |
1649 | ||
1650 | if res: | |
1651 | return res.id | |
1652 | ||
1653 | def get_kernel(self, kernel): | |
1654 | res = self.db.get("SELECT id FROM fireinfo_kernels WHERE name = %s", kernel) | |
1655 | ||
1656 | if res: | |
1657 | return res.id | |
1658 | ||
1659 | # Arch | |
1660 | ||
1661 | def create_arch(self, arch): | |
1662 | res = self.db.get("INSERT INTO fireinfo_arches(name) VALUES(%s) \ | |
1663 | RETURNING id", arch) | |
1664 | ||
1665 | if res: | |
1666 | return res.id | |
1667 | ||
1668 | def get_arch(self, arch): | |
1669 | res = self.db.get("SELECT id FROM fireinfo_arches WHERE name = %s", arch) | |
1670 | ||
1671 | if res: | |
1672 | return res.id | |
1673 | ||
1674 | # Release | |
1675 | ||
1676 | def create_release(self, release): | |
1677 | res = self.db.get("INSERT INTO fireinfo_releases(name) VALUES(%s) \ | |
1678 | RETURNING id", release) | |
1679 | ||
1680 | if res: | |
1681 | return res.id | |
1682 | ||
1683 | def get_release(self, release): | |
1684 | res = self.db.get("SELECT id FROM fireinfo_releases WHERE name = %s", release) | |
1685 | ||
1686 | if res: | |
1687 | return res.id | |
1688 | ||
dd3a5446 MT |
1689 | def get_release_penetration(self, release, when=None): |
1690 | res = self.db.get("WITH profiles AS (SELECT fireinfo_profiles_with_data_at(%s) AS id) \ | |
1691 | SELECT COUNT(*)::float / (SELECT COUNT(*) FROM profiles) AS penetration FROM profiles \ | |
1692 | LEFT JOIN fireinfo_profiles_releases ON profiles.id = fireinfo_profiles_releases.profile_id \ | |
1693 | WHERE fireinfo_profiles_releases.release_id = %s", when, release.fireinfo_id) | |
1694 | ||
1695 | if res: | |
1696 | return res.penetration | |
1697 | ||
e400e37d MT |
1698 | def get_random_country_penetration(self): |
1699 | res = self.db.get("SELECT * FROM fireinfo_country_percentages \ | |
1700 | ORDER BY RANDOM() LIMIT 1") | |
1701 | ||
1702 | if res: | |
1703 | return database.Row({ | |
1704 | "country" : iso3166.countries.get(res.location), | |
1705 | "percentage" : res.count, | |
1706 | }) | |
1707 | ||
66862195 MT |
1708 | # Hypervisor |
1709 | ||
1710 | def create_hypervisor(self, hypervisor): | |
1711 | res = self.db.get("INSERT INTO fireinfo_hypervisors(name) VALUES(%s) \ | |
1712 | RETURNING id", hypervisor) | |
1713 | ||
1714 | if res: | |
1715 | return res.id | |
1716 | ||
1717 | def get_hypervisor(self, hypervisor): | |
1718 | res = self.db.get("SELECT id FROM fireinfo_hypervisors WHERE name = %s", | |
1719 | hypervisor) | |
1720 | ||
1721 | if res: | |
1722 | return res.id | |
1723 | ||
1724 | # Handle profile | |
1725 | ||
1726 | def handle_profile(self, *args, **kwargs): | |
1727 | self.db.execute("START TRANSACTION") | |
1728 | ||
1729 | # Wrap all the handling of the profile in a huge transaction. | |
1730 | try: | |
1731 | self._handle_profile(*args, **kwargs) | |
1732 | ||
1733 | except: | |
1734 | self.db.execute("ROLLBACK") | |
1735 | raise | |
1736 | ||
1737 | else: | |
1738 | self.db.execute("COMMIT") | |
1739 | ||
1740 | def _handle_profile(self, public_id, profile_blob, location=None, when=None): | |
1741 | private_id = profile_blob.get("private_id", None) | |
1742 | assert private_id | |
1743 | ||
1744 | # Check if the profile already exists in the database. | |
1745 | profile = self.fireinfo.get_profile(public_id, private_id=private_id, when=when) | |
1746 | ||
1747 | # Check if the update can actually be updated | |
f5e42730 MT |
1748 | if profile and self.fireinfo.profile_rate_limit_active(public_id, when=when): |
1749 | logging.warning("There were too many updates for this profile in the last hour: %s" % public_id) | |
1750 | return | |
1751 | ||
1752 | elif not self.is_private_id_change_permitted(public_id, private_id, when=when): | |
1753 | logging.warning("Changing private id is not permitted for profile: %s" % public_id) | |
66862195 MT |
1754 | return |
1755 | ||
1756 | # Parse the profile | |
1757 | profile_parser = self.parse_profile(public_id, profile_blob) | |
1758 | ||
1759 | # If a profile exists, check if it matches and if so, just update the | |
1760 | # timestamp. | |
1761 | if profile: | |
1762 | # Check if the profile has changed. If so, update the data. | |
1763 | if profile_parser.equals(profile): | |
1764 | profile.updated(profile_parser, location=location, when=when) | |
1765 | return | |
1766 | ||
1767 | # If it does not match, we assume that it is expired and | |
1768 | # create a new profile. | |
1769 | profile.expired(when=when) | |
1770 | ||
1771 | # Replace the old profile with a new one | |
1772 | profile = self.fireinfo.create_profile(public_id, private_id, when=when) | |
1773 | profile.parse(profile_parser) | |
1774 | ||
1775 | if location: | |
1776 | profile.set_location(location) | |
1777 | ||
1778 | return profile | |
1779 | ||
1780 | # Data outputs | |
1781 | ||
1782 | def get_random_profile(self, when=None): | |
1783 | # Check if the architecture exists so that we pick a profile with some data | |
1784 | res = self.db.get("SELECT public_id FROM fireinfo_profiles \ | |
1785 | LEFT JOIN fireinfo_profiles_arches ON fireinfo_profiles.id = fireinfo_profiles_arches.profile_id \ | |
1786 | WHERE fireinfo_profiles_arches.profile_id IS NOT NULL \ | |
1787 | AND then_or_now(%s) BETWEEN time_created AND time_valid ORDER BY RANDOM() LIMIT 1", when) | |
1788 | ||
1789 | if res: | |
1790 | return res.public_id | |
1791 | ||
1792 | def get_active_profiles(self, when=None): | |
1793 | res = self.db.get("WITH profiles AS (SELECT fireinfo_profiles_at(%s) AS id) \ | |
1794 | SELECT COUNT(*) AS with_data, (SELECT COUNT(*) FROM profiles) AS count FROM profiles \ | |
1795 | LEFT JOIN fireinfo_profiles_releases ON profiles.id = fireinfo_profiles_releases.profile_id \ | |
1796 | WHERE fireinfo_profiles_releases.profile_id IS NOT NULL", when) | |
1797 | ||
1798 | if res: | |
1799 | return res.with_data, res.count | |
1800 | ||
1801 | def get_archive_size(self, when=None): | |
1802 | res = self.db.get("SELECT COUNT(*) AS count FROM fireinfo_profiles \ | |
1803 | WHERE time_created <= then_or_now(%s)", when) | |
1804 | ||
1805 | if res: | |
1806 | return res.count | |
1807 | ||
93adf65a | 1808 | def get_geo_location_map(self, when=None, minimum_percentage=1): |
66862195 MT |
1809 | res = self.db.query("WITH profiles AS (SELECT fireinfo_profiles_at(%s) AS id) \ |
1810 | SELECT location, COUNT(location)::float / (SELECT COUNT(*) FROM profiles) AS count FROM profiles \ | |
1811 | LEFT JOIN fireinfo_profiles_locations ON profiles.id = fireinfo_profiles_locations.profile_id \ | |
93adf65a MT |
1812 | WHERE fireinfo_profiles_locations.location IS NOT NULL GROUP BY location \ |
1813 | HAVING COUNT(location)::float / (SELECT COUNT(*) FROM profiles) >= %s ORDER BY count DESC", | |
1814 | when, minimum_percentage) | |
66862195 MT |
1815 | |
1816 | return ((r.location, r.count) for r in res) | |
1817 | ||
1818 | def get_language_map(self, when=None): | |
b3f9bc4c | 1819 | res = self.db.query("WITH profiles AS (SELECT fireinfo_profiles_with_data_at(%s) AS id) \ |
66862195 MT |
1820 | SELECT language, COUNT(language)::float / (SELECT COUNT(*) FROM profiles) AS count FROM profiles \ |
1821 | LEFT JOIN fireinfo_profiles_languages ON profiles.id = fireinfo_profiles_languages.profile_id \ | |
1822 | WHERE fireinfo_profiles_languages.language IS NOT NULL GROUP BY language ORDER BY count DESC", when) | |
1823 | ||
1824 | return ((r.language, r.count) for r in res) | |
1825 | ||
1826 | @property | |
1827 | def cpu_vendors(self): | |
1828 | res = self.db.query("SELECT DISTINCT vendor FROM fireinfo_processors ORDER BY vendor") | |
1829 | ||
1830 | return (CPU_VENDORS.get(r.vendor, r.vendor) for r in res) | |
1831 | ||
1832 | def get_cpu_vendors_map(self, when=None): | |
1833 | res = self.db.query("WITH profiles AS (SELECT fireinfo_profiles_with_data_at(%s) AS id) \ | |
1834 | SELECT vendor, COUNT(vendor)::float / (SELECT COUNT(*) FROM profiles) AS count FROM profiles \ | |
1835 | LEFT JOIN fireinfo_profiles_processors ON profiles.id = fireinfo_profiles_processors.profile_id \ | |
1836 | LEFT JOIN fireinfo_processors ON fireinfo_profiles_processors.processor_id = fireinfo_processors.id \ | |
1837 | WHERE NOT fireinfo_profiles_processors.processor_id IS NULL GROUP BY vendor ORDER BY count DESC", when) | |
1838 | ||
1839 | return ((CPU_VENDORS.get(r.vendor, r.vendor), r.count) for r in res) | |
1840 | ||
1841 | def get_cpu_clock_speeds(self, when=None): | |
1842 | res = self.db.get("WITH profiles AS (SELECT fireinfo_profiles_with_data_at(%s) AS id) \ | |
1843 | SELECT AVG(fireinfo_profiles_processors.clock_speed) AS avg, \ | |
1844 | STDDEV(fireinfo_profiles_processors.clock_speed) AS stddev, \ | |
1845 | MIN(fireinfo_profiles_processors.clock_speed) AS min, \ | |
1846 | MAX(fireinfo_profiles_processors.clock_speed) AS max FROM profiles \ | |
1847 | LEFT JOIN fireinfo_profiles_processors ON profiles.id = fireinfo_profiles_processors.profile_id \ | |
1848 | WHERE NOT fireinfo_profiles_processors.processor_id IS NULL \ | |
5516a25b MT |
1849 | AND fireinfo_profiles_processors.clock_speed > 0 \ |
1850 | AND fireinfo_profiles_processors.clock_speed < fireinfo_profiles_processors.bogomips \ | |
1851 | AND fireinfo_profiles_processors.bogomips <= %s", when, 10000) | |
66862195 MT |
1852 | |
1853 | if res: | |
1854 | return (res.avg or 0, res.stddev or 0, res.min or 0, res.max or 0) | |
1855 | ||
1856 | def get_cpus_with_platform_and_flag(self, platform, flag, when=None): | |
1857 | res = self.db.get("WITH profiles AS (SELECT fireinfo_profiles_with_data_at(%s) AS id), \ | |
1858 | processors AS (SELECT fireinfo_processors.id AS id, fireinfo_processors.flags AS flags FROM profiles \ | |
1859 | LEFT JOIN fireinfo_profiles_processors ON profiles.id = fireinfo_profiles_processors.profile_id \ | |
1860 | LEFT JOIN fireinfo_processors ON fireinfo_profiles_processors.processor_id = fireinfo_processors.id \ | |
1861 | LEFT JOIN fireinfo_profiles_arches ON profiles.id = fireinfo_profiles_arches.profile_id \ | |
1862 | LEFT JOIN fireinfo_arches ON fireinfo_profiles_arches.arch_id = fireinfo_arches.id \ | |
1863 | WHERE NOT fireinfo_profiles_processors.processor_id IS NULL \ | |
1864 | AND fireinfo_arches.platform = %s AND NOT 'hypervisor' = ANY(fireinfo_processors.flags)) \ | |
1865 | SELECT (COUNT(*)::float / (SELECT NULLIF(COUNT(*), 0) FROM processors)) AS count FROM processors \ | |
1866 | WHERE %s = ANY(processors.flags)", when, platform, flag) | |
1867 | ||
1868 | return res.count or 0 | |
1869 | ||
1870 | def get_common_cpu_flags_by_platform(self, platform, when=None): | |
1871 | if platform == "arm": | |
1872 | flags = ( | |
1873 | "lpae", "neon", "thumb", "thumb2", "thumbee", "vfpv3", "vfpv4", | |
1874 | ) | |
1875 | elif platform == "x86": | |
1876 | flags = ( | |
1877 | "aes", "avx", "avx2", "lm", "mmx", "mmxext", "nx", "pae", | |
1878 | "pni", "popcnt", "sse", "sse2", "rdrand", "ssse3", "sse4a", | |
5602477f | 1879 | "sse4_1", "sse4_2", "sha", "pclmulqdq", "rdseed", |
66862195 MT |
1880 | ) |
1881 | else: | |
1882 | return | |
1883 | ||
1884 | ret = [] | |
1885 | for flag in flags: | |
1886 | ret.append((flag, self.get_cpus_with_platform_and_flag(platform, flag, when=when))) | |
1887 | ||
1888 | # Add virtual CPU flag "virt" for virtualization support | |
1889 | if platform == "x86": | |
1890 | ret.append(("virt", | |
1891 | self.get_cpus_with_platform_and_flag(platform, "vmx", when=when) + \ | |
1892 | self.get_cpus_with_platform_and_flag(platform, "svm", when=when))) | |
1893 | ||
1894 | return sorted(ret, key=lambda x: x[1], reverse=True) | |
1895 | ||
1896 | def get_common_memory_amounts(self, when=None): | |
1897 | amounts = (128, 256, 512, 1024, 2 * 1024, 4 * 1024, 8 * 1024, 16 * 1024, 32 * 1024, 64 * 1024) | |
1898 | ||
1899 | ret = [] | |
1900 | for amount in amounts: | |
1901 | ret.append((amount, self.get_memory_amount(amount * 1024 * 0.95, when=when))) | |
1902 | ||
1903 | return ret | |
1904 | ||
1905 | def get_memory_amount(self, greather_than, when=None): | |
1906 | res = self.db.get("WITH profiles AS (SELECT fireinfo_profiles_with_data_at(%s) AS id) \ | |
1907 | SELECT COUNT(*)::float / (SELECT COUNT(*) FROM profiles \ | |
1908 | LEFT JOIN fireinfo_profiles_memory ON profiles.id = fireinfo_profiles_memory.profile_id \ | |
1909 | WHERE NOT fireinfo_profiles_memory.amount IS NULL) AS percentage FROM profiles \ | |
1910 | LEFT JOIN fireinfo_profiles_memory ON profiles.id = fireinfo_profiles_memory.profile_id \ | |
1911 | WHERE fireinfo_profiles_memory.amount >= %s", when, greather_than) | |
1912 | ||
1913 | if res: | |
1914 | return res.percentage | |
1915 | ||
1916 | def get_memory_amounts(self, when=None): | |
1917 | res = self.db.get("WITH profiles AS (SELECT fireinfo_profiles_with_data_at(%s) AS id) \ | |
1918 | SELECT AVG(fireinfo_profiles_memory.amount) AS avg, \ | |
1919 | STDDEV(fireinfo_profiles_memory.amount) AS stddev, \ | |
1920 | MIN(fireinfo_profiles_memory.amount) AS min, \ | |
1921 | MAX(fireinfo_profiles_memory.amount) AS max FROM profiles \ | |
1922 | LEFT JOIN fireinfo_profiles_memory ON profiles.id = fireinfo_profiles_memory.profile_id", when) | |
1923 | ||
1924 | if res: | |
1925 | return (res.avg or 0, res.stddev or 0, res.min or 0, res.max or 0) | |
1926 | ||
1927 | def get_arch_map(self, when=None): | |
1928 | res = self.db.query("WITH profiles AS (SELECT fireinfo_profiles_with_data_at(%s) AS id) \ | |
1929 | SELECT fireinfo_arches.name AS arch, COUNT(*)::float / (SELECT COUNT(*) FROM profiles) AS count \ | |
1930 | FROM profiles \ | |
1931 | LEFT JOIN fireinfo_profiles_arches ON profiles.id = fireinfo_profiles_arches.profile_id \ | |
1932 | LEFT JOIN fireinfo_arches ON fireinfo_profiles_arches.arch_id = fireinfo_arches.id \ | |
1933 | WHERE NOT fireinfo_profiles_arches.profile_id IS NULL \ | |
1934 | GROUP BY fireinfo_arches.id ORDER BY count DESC", when) | |
1935 | ||
1936 | return ((r.arch, r.count) for r in res) | |
1937 | ||
1938 | # Virtual | |
1939 | ||
1940 | def get_hypervisor_map(self, when=None): | |
1941 | res = self.db.query("WITH profiles AS (SELECT fireinfo_profiles_with_data_at(%s) AS id), \ | |
1942 | virtual_profiles AS (SELECT profiles.id AS profile_id, fireinfo_profiles_virtual.hypervisor_id FROM profiles \ | |
1943 | LEFT JOIN fireinfo_profiles_virtual ON profiles.id = fireinfo_profiles_virtual.profile_id \ | |
1944 | WHERE fireinfo_profiles_virtual.profile_id IS NOT NULL) \ | |
1945 | SELECT COALESCE(fireinfo_hypervisors.name, %s) AS name, \ | |
1946 | COUNT(*)::float / (SELECT COUNT(*) FROM virtual_profiles) AS count FROM virtual_profiles \ | |
1947 | LEFT JOIN fireinfo_hypervisors ON virtual_profiles.hypervisor_id = fireinfo_hypervisors.id \ | |
1948 | GROUP BY fireinfo_hypervisors.name ORDER BY count DESC", when, "unknown") | |
1949 | ||
1950 | return ((r.name, r.count) for r in res) | |
1951 | ||
1952 | def get_virtual_ratio(self, when=None): | |
1953 | res = self.db.get("WITH profiles AS (SELECT fireinfo_profiles_with_data_at(%s) AS id) \ | |
1954 | SELECT COUNT(*)::float / (SELECT COUNT(*) FROM profiles) AS count FROM profiles \ | |
1955 | LEFT JOIN fireinfo_profiles_virtual ON profiles.id = fireinfo_profiles_virtual.profile_id \ | |
1956 | WHERE fireinfo_profiles_virtual.profile_id IS NOT NULL", when) | |
1957 | ||
1958 | if res: | |
1959 | return res.count | |
1960 | ||
1961 | # Releases | |
1962 | ||
1963 | def get_releases_map(self, when=None): | |
1964 | res = self.db.query("WITH profiles AS (SELECT fireinfo_profiles_with_data_at(%s) AS id) \ | |
1965 | SELECT fireinfo_releases.name, COUNT(*)::float / (SELECT COUNT(*) FROM profiles) AS count FROM profiles \ | |
1966 | LEFT JOIN fireinfo_profiles_releases ON profiles.id = fireinfo_profiles_releases.profile_id \ | |
1967 | LEFT JOIN fireinfo_releases ON fireinfo_profiles_releases.release_id = fireinfo_releases.id \ | |
1968 | GROUP BY fireinfo_releases.name ORDER BY count DESC", when) | |
1969 | ||
1970 | return ((r.name, r.count) for r in res) | |
1971 | ||
1972 | def get_kernels_map(self, when=None): | |
1973 | res = self.db.query("WITH profiles AS (SELECT fireinfo_profiles_with_data_at(%s) AS id) \ | |
1974 | SELECT fireinfo_kernels.name, COUNT(*)::float / (SELECT COUNT(*) FROM profiles) AS count FROM profiles \ | |
1975 | LEFT JOIN fireinfo_profiles_kernels ON profiles.id = fireinfo_profiles_kernels.profile_id \ | |
1976 | LEFT JOIN fireinfo_kernels ON fireinfo_profiles_kernels.kernel_id = fireinfo_kernels.id \ | |
1977 | GROUP BY fireinfo_kernels.name ORDER BY count DESC", when) | |
1978 | ||
1979 | return ((r.name, r.count) for r in res) | |
1980 | ||
1981 | def _process_devices(self, devices): | |
1982 | result = [] | |
1983 | ||
1984 | for dev in devices: | |
1985 | dev = Device(self.backend, dev.get("id", None), dev) | |
1986 | result.append(dev) | |
1987 | ||
1988 | return result | |
1989 | ||
1990 | def get_driver_map(self, driver, when=None): | |
1991 | res = self.db.query("WITH profiles AS (SELECT fireinfo_profiles_with_data_at(%s) AS id), \ | |
1992 | devices AS (SELECT * FROM profiles \ | |
1993 | LEFT JOIN fireinfo_profiles_devices ON profiles.id = fireinfo_profiles_devices.profile_id \ | |
1994 | LEFT JOIN fireinfo_devices ON fireinfo_profiles_devices.device_id = fireinfo_devices.id \ | |
1995 | WHERE driver = %s) \ | |
1996 | SELECT subsystem, model, vendor, driver, deviceclass, \ | |
1997 | COUNT(*)::float / (SELECT COUNT(*) FROM devices) AS percentage FROM devices \ | |
1998 | GROUP BY subsystem, model, vendor, driver, deviceclass \ | |
1999 | ORDER BY percentage DESC", when, driver) | |
2000 | ||
2001 | return self._process_devices(res) | |
2002 | ||
2003 | subsystem2class = { | |
2004 | "pci" : hwdata.PCI(), | |
2005 | "usb" : hwdata.USB(), | |
2006 | } | |
2007 | ||
2008 | def get_vendor_string(self, subsystem, vendor_id): | |
2009 | try: | |
2010 | cls = self.subsystem2class[subsystem] | |
2011 | except KeyError: | |
2012 | return | |
2013 | ||
2014 | return cls.get_vendor(vendor_id) | |
2015 | ||
2016 | def get_model_string(self, subsystem, vendor_id, model_id): | |
2017 | try: | |
2018 | cls = self.subsystem2class[subsystem] | |
2019 | except KeyError: | |
2020 | return | |
2021 | ||
2022 | return cls.get_device(vendor_id, model_id) | |
2023 | ||
2024 | def get_vendor_list(self, when=None): | |
2025 | res = self.db.query("WITH profiles AS (SELECT fireinfo_profiles_with_data_at(%s) AS id) \ | |
2026 | SELECT DISTINCT fireinfo_devices.subsystem AS subsystem, fireinfo_devices.vendor AS vendor FROM profiles \ | |
2027 | LEFT JOIN fireinfo_profiles_devices ON profiles.id = fireinfo_profiles_devices.profile_id \ | |
2028 | LEFT JOIN fireinfo_devices ON fireinfo_profiles_devices.device_id = fireinfo_devices.id \ | |
2029 | WHERE NOT fireinfo_devices.driver = ANY(%s)", when, IGNORED_DEVICES) | |
2030 | ||
2031 | vendors = {} | |
2032 | for row in res: | |
2033 | vendor = self.get_vendor_string(row.subsystem, row.vendor) | |
2034 | ||
2035 | # Drop if vendor could not be determined | |
2036 | if vendor is None: | |
2037 | continue | |
2038 | ||
2039 | try: | |
2040 | vendors[vendor].append((row.subsystem, row.vendor)) | |
2041 | except KeyError: | |
2042 | vendors[vendor] = [(row.subsystem, row.vendor)] | |
2043 | ||
2044 | vendors = vendors.items() | |
2045 | return sorted(vendors) | |
2046 | ||
2047 | def get_devices_by_vendor(self, subsystem, vendor, when=None): | |
2048 | res = self.db.query("WITH profiles AS (SELECT fireinfo_profiles_with_data_at(%s) AS id), \ | |
2049 | devices AS (SELECT * FROM profiles \ | |
2050 | LEFT JOIN fireinfo_profiles_devices ON profiles.id = fireinfo_profiles_devices.profile_id \ | |
2051 | LEFT JOIN fireinfo_devices ON fireinfo_profiles_devices.device_id = fireinfo_devices.id \ | |
2052 | WHERE NOT fireinfo_devices.driver = ANY(%s)), \ | |
2053 | vendor_devices AS (SELECT * FROM devices WHERE devices.subsystem = %s AND devices.vendor = %s) \ | |
2054 | SELECT subsystem, model, vendor, driver, deviceclass FROM vendor_devices \ | |
2055 | GROUP BY subsystem, model, vendor, driver, deviceclass", when, IGNORED_DEVICES, subsystem, vendor) | |
2056 | ||
2057 | return self._process_devices(res) | |
2058 | ||
2059 | def get_device_percentage(self, subsystem, vendor, model, when=None): | |
2060 | res = self.db.get("WITH profiles AS (SELECT fireinfo_profiles_with_data_at(%s) AS id), \ | |
2061 | devices AS (SELECT * FROM profiles \ | |
2062 | LEFT JOIN fireinfo_profiles_devices ON profiles.id = fireinfo_profiles_devices.profile_id \ | |
2063 | LEFT JOIN fireinfo_devices ON fireinfo_profiles_devices.device_id = fireinfo_devices.id) \ | |
2064 | SELECT COUNT(*)::float / (SELECT COUNT(*) FROM devices) AS percentage FROM devices \ | |
2065 | WHERE devices.subsystem = %s AND devices.vendor = %s AND devices.model = %s", | |
2066 | when, subsystem, vendor, model) | |
2067 | ||
2068 | if res: | |
2069 | return res.percentage | |
2070 | ||
2071 | def get_device_in_profile(self, subsystem, vendor, model, limit=10, when=None): | |
2072 | res = self.db.query("WITH profiles AS (SELECT fireinfo_profiles_with_data_at(%s) AS id), \ | |
2073 | profiles_with_device AS (SELECT DISTINCT fireinfo_profiles.public_id FROM profiles \ | |
2074 | LEFT JOIN fireinfo_profiles ON profiles.id = fireinfo_profiles.id \ | |
2075 | LEFT JOIN fireinfo_profiles_devices ON profiles.id = fireinfo_profiles_devices.profile_id \ | |
2076 | LEFT JOIN fireinfo_devices ON fireinfo_profiles_devices.device_id = fireinfo_devices.id \ | |
2077 | WHERE fireinfo_devices.subsystem = %s AND fireinfo_devices.vendor = %s \ | |
2078 | AND fireinfo_devices.model = %s) \ | |
2079 | SELECT * FROM profiles_with_device ORDER BY RANDOM() LIMIT %s", | |
2080 | when, subsystem, vendor, model, limit) | |
2081 | ||
2082 | return (r.public_id for r in res) | |
2083 | ||
2084 | def get_network_zones_map(self, when=None): | |
2085 | res = self.db.get("WITH profiles AS (SELECT fireinfo_profiles_with_data_at(%s) AS id) \ | |
2086 | SELECT COUNT(NULLIF(has_red, FALSE))::float / (SELECT COUNT(*) FROM profiles) AS has_red, \ | |
2087 | COUNT(NULLIF(has_green, FALSE))::float / (SELECT COUNT(*) FROM profiles) AS has_green, \ | |
2088 | COUNT(NULLIF(has_orange, FALSE))::float / (SELECT COUNT(*) FROM profiles) AS has_orange, \ | |
2089 | COUNT(NULLIF(has_blue, FALSE))::float / (SELECT COUNT(*) FROM profiles) AS has_blue FROM profiles \ | |
2090 | LEFT JOIN fireinfo_profiles_networks ON profiles.id = fireinfo_profiles_networks.profile_id \ | |
2091 | WHERE fireinfo_profiles_networks.profile_id IS NOT NULL", when) | |
2092 | ||
2093 | return res |