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