3 from __future__
import division
13 from misc
import Object
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",
38 (r
"AMD (Sempron)\(tm\) (\d+) APU with Radeon\(tm\) R\d+", r
"AMD \1 \2 APU"),
39 (r
"AMD ([\w\-]+) APU with Radeon\(tm\) HD Graphics", r
"AMD \1 APU"),
40 (r
"AMD ([\w\-]+) Radeon R\d+, \d+ Compute Cores \d+C\+\d+G", r
"AMD \1 APU"),
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"),
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"),
50 (r
"Geode\(TM\) Integrated Processor by AMD PCS", r
"AMD Geode"),
51 (r
"(Geode).*", r
"\1"),
53 (r
"Mobile AMD (Athlon|Sempron)\(tm\) Processor (\d+\+?)", r
"AMD \1-M \2"),
56 (r
"Intel\(R\) (Atom|Celeron).*CPU\s*([A-Z0-9]+) .*", r
"Intel \1 \2"),
57 (r
"(Intel).*(Celeron).*", r
"\1 \2"),
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"),
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"),
73 (r
"Geode\(TM\) Integrated Processor by National Semi", r
"NSC Geode"),
76 (r
"(VIA \w*).*", r
"\1"),
79 (r
"QEMU Virtual CPU version .*", r
"QEMU CPU"),
82 (r
"Feroceon .*", r
"ARM Feroceon"),
85 IGNORED_DEVICES
= ["usb",]
87 class ProfileDict(object):
88 def __init__(self
, data
):
92 class ProfileNetwork(ProfileDict
):
93 def __eq__(self
, other
):
97 if not self
.has_red
== other
.has_red
:
100 if not self
.has_green
== other
.has_green
:
103 if not self
.has_orange
== other
.has_orange
:
106 if not self
.has_blue
== other
.has_blue
:
114 for zone
in ("red", "green", "orange", "blue"):
115 if self
.has_zone(zone
):
120 def has_zone(self
, name
):
121 return self
._data
.get("has_%s" % name
)
125 return self
._data
.get("has_red", False)
129 return self
._data
.get("has_green", False)
132 def has_orange(self
):
133 return self
._data
.get("has_orange", False)
137 return self
._data
.get("has_blue", False)
140 class Processor(Object
):
141 def __init__(self
, backend
, id, data
=None, clock_speed
=None, bogomips
=None):
142 Object
.__init
__(self
, backend
)
146 self
.__clock
_speed
= clock_speed
147 self
.__bogomips
= bogomips
152 if not self
.model_string
.startswith(self
.vendor
):
153 s
.append(self
.vendor
)
156 s
.append(self
.model_string
)
158 if self
.core_count
> 1:
159 s
.append("x%s" % self
.core_count
)
165 if self
.__data
is None:
166 self
.__data
= self
.db
.get("SELECT * FROM fireinfo_processors \
167 WHERE id = %s", self
.id)
174 return CPU_VENDORS
[self
.data
.vendor
]
176 return self
.data
.vendor
180 return self
.data
.family
184 return self
.data
.model
188 return self
.data
.stepping
191 def model_string(self
):
192 s
= self
.data
.model_string
.split()
194 return " ".join((e
for e
in s
if e
))
198 return self
.data
.flags
200 def has_flag(self
, flag
):
201 return flag
in self
.flags
204 if self
.vendor
== "Intel" and self
.family
== 6 and self
.model
in (15, 55, 76, 77):
207 return self
.has_flag("ht")
210 def core_count(self
):
211 return self
.data
.core_count
216 return self
.core_count
// 2
218 return self
.core_count
221 def clock_speed(self
):
222 return self
.__clock
_speed
224 def format_clock_speed(self
):
225 if not self
.clock_speed
:
228 if self
.clock_speed
< 1000:
229 return "%dMHz" % self
.clock_speed
231 return "%.2fGHz" % round(self
.clock_speed
/ 1000, 2)
235 return self
.__bogomips
238 def capabilities(self
):
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")),
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))
252 caps
.append(("virt", self
.has_flag("vmx") or self
.has_flag("svm")))
256 def format_model(self
):
257 s
= self
.model_string
259 # Remove everything after the @: Intel(R) Core(TM) i7-3770 CPU @ 3.40GHz
260 s
, sep
, rest
= s
.partition("@")
262 for pattern
, repl
in CPU_STRINGS
:
263 if re
.match(pattern
, s
) is None:
266 s
= re
.sub(pattern
, repl
, s
)
269 # Otherwise remove the symbols
270 for i
in ("C", "R", "TM", "tm"):
271 s
= s
.replace("(%s)" % i
, "")
273 # Replace too long strings with shorter ones
275 ("Quad-Core Processor", ""),
276 ("Dual-Core Processor", ""),
277 ("Processor", "CPU"),
278 ("processor", "CPU"),
283 # Remove too many spaces
284 s
= " ".join((e
for e
in s
.split() if e
))
289 def friendly_string(self
):
292 model
= self
.format_model()
295 clock_speed
= self
.format_clock_speed()
297 s
.append("@ %s" % clock_speed
)
300 s
.append("x%s" % self
.count
)
305 class Device(Object
):
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"),
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"),
330 "00" : N_("Unclassified"),
331 "01" : N_("Multimedia"),
332 "02" : N_("Communication"),
333 "03" : N_("Input device"),
334 "05" : N_("Generic system peripheral"),
336 "07" : N_("Printer"),
337 "08" : N_("Mass storage"),
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"),
352 def __init__(self
, backend
, id, data
=None):
353 Object
.__init
__(self
, backend
)
359 return "<%s vendor=%s model=%s>" % (self
.__class
__.__name
__,
360 self
.vendor_string
, self
.model_string
)
362 def __cmp__(self
, other
):
363 if self
.id and self
.id == other
.id:
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
)
376 if self
.__data
is None:
379 self
.__data
= self
.db
.get("SELECT * FROM fireinfo_devices \
380 WHERE id = %s", self
.id)
384 def is_showable(self
):
385 if self
.driver
in IGNORED_DEVICES
:
388 if self
.driver
in ("pcieport", "hub"):
395 return self
.data
.subsystem
399 return self
.data
.model
402 def model_string(self
):
403 return self
.fireinfo
.get_model_string(self
.subsystem
,
404 self
.vendor
, self
.model
)
408 return self
.data
.vendor
411 def vendor_string(self
):
412 return self
.fireinfo
.get_vendor_string(self
.subsystem
, self
.vendor
)
416 return self
.data
.driver
420 classid
= self
.data
.deviceclass
422 if self
.subsystem
== "pci":
423 classid
= classid
[:-4]
424 if len(classid
) == 1:
425 classid
= "0%s" % classid
427 elif self
.subsystem
== "usb" and classid
:
428 classid
= classid
.split("/")[0]
429 classid
= "%02x" % int(classid
)
432 return self
.classid2name
[self
.subsystem
][classid
]
437 def percentage(self
):
438 return self
.data
.get("percentage", None)
441 class Profile(Object
):
442 def __init__(self
, backend
, id, data
=None):
443 Object
.__init
__(self
, backend
)
449 return "<%s %s>" % (self
.__class
__.__name
__, self
.public_id
)
451 def __cmp__(self
, other
):
452 return cmp(self
.id, other
.id)
454 def is_showable(self
):
462 if self
.__data
is None:
463 self
.__data
= self
.db
.get("SELECT * FROM fireinfo_profiles \
464 WHERE id = %s", self
.id)
470 return self
.data
.public_id
473 def private_id(self
):
474 raise NotImplementedError
477 def time_created(self
):
478 return self
.data
.time_created
481 def time_updated(self
):
482 return self
.data
.time_updated
484 def updated(self
, profile_parser
=None, location
=None, when
=None):
485 valid
= self
.settings
.get_int("fireinfo_profile_days_valid", 14)
487 self
.db
.execute("UPDATE fireinfo_profiles \
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)
495 self
.set_processor_speeds(
496 profile_parser
.processor_clock_speed
,
497 profile_parser
.processor_bogomips
,
501 self
.set_location(location
)
503 self
.log_profile_update()
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
)
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)
514 def parse(self
, parser
):
516 self
.processor
= parser
.processor
517 self
.set_processor_speeds(parser
.processor_clock_speed
, parser
.processor_bogomips
)
520 self
.devices
= parser
.devices
523 self
.system_id
= parser
.system_id
526 self
.memory
= parser
.memory
529 self
.storage
= parser
.storage
532 self
.kernel_id
= parser
.kernel_id
535 self
.arch_id
= parser
.arch_id
538 self
.release_id
= parser
.release_id
541 self
.language
= parser
.language
545 self
.hypervisor_id
= parser
.hypervisor_id
548 self
.network
= parser
.network
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)
558 self
._location
= res
.location
560 self
._location
= None
562 return self
._location
564 def set_location(self
, location
):
565 if self
.location
== location
:
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
)
573 self
._location
= location
575 location
= property(get_location
, set_location
)
578 def location_string(self
):
579 return self
.geoip
.get_country_name(self
.location
)
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)
589 self
._device
_ids
= sorted([r
.device_id
for r
in res
])
591 return self
._device
_ids
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)
602 device
= Device(self
.backend
, row
.id, row
)
603 self
._devices
.append(device
)
607 def set_devices(self
, devices
):
608 device_ids
= [d
.id for d
in devices
]
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
))
614 self
._devices
= devices
615 self
._device
_ids
= device_ids
617 devices
= property(get_devices
, set_devices
)
619 def count_device(self
, subsystem
, vendor
, model
):
622 for dev
in self
.devices
:
623 if dev
.subsystem
== subsystem
and dev
.vendor
== vendor
and dev
.model
== model
:
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)
636 self
._system
_id
= res
.id
638 self
._system
_id
= None
640 return self
._system
_id
642 def set_system_id(self
, system_id
):
643 self
.db
.execute("DELETE FROM fireinfo_profiles_systems WHERE profile_id = %s", self
.id)
646 self
.db
.execute("INSERT INTO fireinfo_profiles_systems(profile_id, system_id) \
647 VALUES(%s, %s)", self
.id, system_id
)
649 self
._system
_id
= None
650 if hasattr(self
, "_system"):
653 system_id
= property(get_system_id
, set_system_id
)
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)
664 self
._system
= (res
.vendor
, res
.model
)
666 self
._system
= (None, None)
671 def system_vendor(self
):
679 def system_model(self
):
687 def appliance_id(self
):
688 if not hasattr(self
, "_appliance_id"):
690 ("fountainnetworks-duo-box", self
._appliance
_test
_fountainnetworks
_duo
_box
),
691 ("fountainnetworks-prime", self
._appliance
_test
_fountainnetworks
_prime
),
692 ("lightningwirelabs-eco-plus", self
._appliance
_test
_lightningwirelabs
_eco
_plus
),
693 ("lightningwirelabs-eco", self
._appliance
_test
_lightningwirelabs
_eco
),
696 self
._appliance
_id
= None
697 for name
, test_func
in appliances
:
701 self
._appliance
_id
= name
704 return self
._appliance
_id
708 if self
.appliance_id
== "fountainnetworks-duo-box":
709 return "Fountain Networks - IPFire Duo Box"
711 elif self
.appliance_id
== "fountainnetworks-prime":
712 return "Fountain Networks - IPFire Prime Box"
714 elif self
.appliance_id
== "lightningwirelabs-eco-plus":
715 return "Lightning Wire Labs - IPFire Eco Plus Appliance"
717 elif self
.appliance_id
== "lightningwirelabs-eco":
718 return "Lightning Wire Labs - IPFire Eco Appliance"
720 def _appliance_test_fountainnetworks_duo_box(self
):
721 if not self
.processor
.vendor
== "Intel":
724 if not self
.processor
.model_string
== "Intel(R) Celeron(R) 2957U @ 1.40GHz":
727 if not self
.count_device("pci", "10ec", "8168") == 2:
731 if self
.count_device("usb", "148f", "5572") < 1:
736 def _appliance_test_fountainnetworks_prime(self
):
737 if not self
.system
in (("SECO", None), ("SECO", "0949")):
740 # Must have a wireless device
741 if self
.count_device("usb", "148f", "5572") < 1:
746 def _appliance_test_lightningwirelabs_eco(self
):
747 if not self
.system
== ("MSI", "MS-9877"):
750 # Must have four Intel network adapters
751 network_adapters_count
= self
.count_device("pci", "8086", "10d3")
752 if not network_adapters_count
== 4:
757 def _appliance_test_lightningwirelabs_eco_plus(self
):
758 if not self
.system_vendor
== "ASUS":
761 if not self
.system_model
.startswith("P9A-I/2550"):
764 # Must have four Intel network adapters
765 network_adapters_count
= self
.count_device("pci", "8086", "1f41")
766 if not network_adapters_count
== 4:
774 def processor_id(self
):
775 if hasattr(self
, "_processor"):
776 return self
._processor
.id
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)
783 self
._processor
_id
= res
.processor_id
785 self
._processor
_id
= None
787 return self
._processor
_id
789 def get_processor(self
):
790 if not self
.processor_id
:
793 if not hasattr(self
, "_processor"):
794 res
= self
.db
.get("SELECT * FROM fireinfo_profiles_processors \
795 WHERE profile_id = %s", self
.id)
798 self
._processor
= self
.fireinfo
.get_processor_by_id(res
.processor_id
,
799 clock_speed
=res
.clock_speed
, bogomips
=res
.bogomips
)
801 self
._processor
= None
803 return self
._processor
805 def set_processor(self
, processor
):
806 self
.db
.execute("DELETE FROM fireinfo_profiles_processors \
807 WHERE profile_id = %s", self
.id)
810 self
.db
.execute("INSERT INTO fireinfo_profiles_processors(profile_id, processor_id) \
811 VALUES(%s, %s)", self
.id, processor
.id)
813 self
._processor
= processor
815 processor
= property(get_processor
, set_processor
)
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)
825 return self
.processor
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)
835 self
._memory
= res
.amount
* 1024
841 def set_memory(self
, amount
):
842 if self
.memory
== amount
:
847 self
.db
.execute("DELETE FROM fireinfo_profiles_memory WHERE profile_id = %s", self
.id)
849 self
.db
.execute("INSERT INTO fireinfo_profiles_memory(profile_id, amount) \
850 VALUES(%s, %s)", self
.id, amount
)
852 self
._memory
= amount
* 1024
854 memory
= property(get_memory
, set_memory
)
857 def friendly_memory(self
):
858 return util
.format_size(self
.memory
or 0)
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)
868 self
._storage
= res
.amount
* 1024
874 def set_storage(self
, amount
):
875 if self
.storage
== amount
:
880 self
.db
.execute("DELETE FROM fireinfo_profiles_storage WHERE profile_id = %s", self
.id)
882 self
.db
.execute("INSERT INTO fireinfo_profiles_storage(profile_id, amount) \
883 VALUES(%s, %s)", self
.id, amount
)
885 self
._storage
= amount
* 1024
887 storage
= property(get_storage
, set_storage
)
890 def friendly_storage(self
):
891 return util
.format_size(self
.storage
)
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)
902 self
._kernel
_id
= res
.id
904 self
._kernel
_id
= None
906 return self
._kernel
_id
908 def set_kernel_id(self
, kernel_id
):
909 if self
.kernel_id
== kernel_id
:
912 self
.db
.execute("DELETE FROM fireinfo_profiles_kernels WHERE profile_id = %s", self
.id)
914 self
.db
.execute("INSERT INTO fireinfo_profiles_kernels(profile_id, kernel_id) \
915 VALUES(%s, %s)", self
.id, kernel_id
)
917 self
._kernel
_id
= kernel_id
918 if hasattr(self
, "_kernel"):
921 kernel_id
= property(get_kernel_id
, set_kernel_id
)
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)
932 self
._kernel
= res
.name
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)
947 self
._arch
_id
= res
.id
953 def set_arch_id(self
, arch_id
):
954 if self
.arch_id
== arch_id
:
957 self
.db
.execute("DELETE FROM fireinfo_profiles_arches WHERE profile_id = %s", self
.id)
959 self
.db
.execute("INSERT INTO fireinfo_profiles_arches(profile_id, arch_id) \
960 VALUES(%s, %s)", self
.id, arch_id
)
963 if hasattr(self
, "_arch"):
966 arch_id
= property(get_arch_id
, set_arch_id
)
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)
977 self
._arch
= res
.name
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)
992 self
._release
_id
= res
.id
994 self
._release
_id
= None
996 return self
._release
_id
998 def set_release_id(self
, release_id
):
999 if self
.release_id
== release_id
:
1002 self
.db
.execute("DELETE FROM fireinfo_profiles_releases WHERE profile_id = %s", self
.id)
1004 self
.db
.execute("INSERT INTO fireinfo_profiles_releases(profile_id, release_id) \
1005 VALUES(%s, %s)", self
.id, release_id
)
1007 self
._release
_id
= release_id
1008 if hasattr(self
, "_release"):
1011 release_id
= property(get_release_id
, set_release_id
)
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)
1022 self
._release
= self
._format
_release
(res
.name
)
1024 self
._release
= None
1026 return self
._release
1029 def _format_release(r
):
1033 # Remove the development header
1034 r
= r
.replace("Development Build: ", "")
1037 ("-beta", " - Beta "),
1038 ("-rc", " - Release Candidate "),
1039 ("rc", "Release Candidate "),
1040 ("core", "Core Update "),
1050 def release_short(self
):
1052 (r
"Release Candidate (\d+)", r
"RC\1"),
1056 for pattern
, repl
in pairs
:
1057 if re
.search(pattern
, s
) is None:
1060 s
= re
.sub(pattern
, repl
, s
)
1068 if not hasattr(self
, "_virtual"):
1069 res
= self
.db
.get("SELECT 1 FROM fireinfo_profiles_virtual \
1070 WHERE profile_id = %s", self
.id)
1073 self
._virtual
= True
1075 self
._virtual
= False
1077 return self
._virtual
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)
1086 self
._hypervisor
_id
= res
.id
1088 self
._hypervisor
_id
= None
1090 return self
._hypervisor
_id
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
)
1097 self
._hypervisor
_id
= hypervisor_id
1099 hypervisor_id
= property(get_hypervisor_id
, set_hypervisor_id
)
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)
1110 self
._hypervisor
= res
.hypervisor
1112 self
._hypervisor
= None
1114 return self
._hypervisor
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)
1124 self
._language
= res
.language
1126 self
._language
= None
1128 return self
._language
1130 def set_language(self
, language
):
1131 self
.db
.execute("DELETE FROM fireinfo_profiles_languages WHERE profile_id = %s", self
.id)
1134 self
.db
.execute("INSERT INTO fireinfo_profiles_languages(profile_id, language) \
1135 VALUES(%s, %s)", self
.id, language
)
1137 self
._language
= language
1139 language
= property(get_language
, set_language
)
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)
1151 self
._network
= ProfileNetwork(res
)
1153 return self
._network
1155 def set_network(self
, network
):
1156 self
.db
.execute("DELETE FROM fireinfo_profiles_networks WHERE profile_id = %s", self
.id)
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
)
1163 self
._network
= network
1165 network
= property(get_network
, set_network
)
1168 class ProfileData(Object
):
1169 def __init__(self
, backend
, id, data
=None, profile
=None):
1170 Object
.__init
__(self
, backend
)
1174 self
._profile
= profile
1178 if self
._data
is None:
1179 self
._data
= self
.db
.get("SELECT * FROM fireinfo_profile_data \
1180 WHERE id = %s", self
.id)
1186 if not self
._profile
:
1187 self
._profile
= self
.fireinfo
.get_profile_by_id(self
.profile_id
)
1189 return self
._profile
1192 def profile_id(self
):
1193 return self
.data
.profile_id
1196 class ProfileParserError(Exception):
1200 class ProfileParser(Object
):
1211 __processor_args
= (
1221 __processor_args_mandatory
= (
1226 def __init__(self
, backend
, public_id
, blob
=None):
1227 Object
.__init
__(self
, backend
)
1229 self
.public_id
= public_id
1230 self
.private_id
= None
1232 self
.processor
= None
1233 self
.processor_clock_speed
= None
1234 self
.processor_bogomips
= None
1235 self
.system_id
= None
1239 self
.kernel_id
= None
1243 self
.release_id
= None
1244 self
.language
= None
1246 self
.hypervisor_id
= None
1249 self
.__parse
_blob
(blob
)
1251 def equals(self
, other
):
1252 if not self
.processor_id
== other
.processor_id
:
1255 if not self
.device_ids
== other
.device_ids
:
1258 if not self
.system_id
== other
.system_id
:
1261 if not self
.memory
== other
.memory
:
1264 if not self
.storage
== other
.storage
:
1267 if not self
.kernel_id
== other
.kernel_id
:
1270 if not self
.arch_id
== other
.arch_id
:
1273 if not self
.release_id
== other
.release_id
:
1276 if not self
.language
== other
.language
:
1279 if not self
.virtual
== other
.virtual
:
1283 if not self
.hypervisor_id
== other
.hypervisor_id
:
1286 if not self
.network
== other
.network
:
1291 def __parse_blob(self
, blob
):
1292 _profile
= blob
.get("profile", {})
1293 self
.private_id
= blob
.get("private_id")
1295 # Do not try to parse an empty profile
1300 _processor
= _profile
.get("cpu", {})
1301 self
.__parse
_processor
(_processor
)
1304 _devices
= _profile
.get("devices", [])
1305 self
.__parse
_devices
(_devices
)
1308 _system
= _profile
.get("system")
1310 self
.__parse
_system
(_system
)
1312 # Memory (convert to bytes)
1313 memory
= _system
.get("memory", None)
1315 self
.memory
= memory
* 1024
1317 # Storage size (convert to bytes)
1318 storage
= _system
.get("root_size", None)
1320 self
.storage
= storage
* 1024
1323 kernel
= _system
.get("kernel_release", None)
1325 self
.__parse
_kernel
(kernel
)
1328 release
= _system
.get("release", None)
1330 self
.__parse
_release
(release
)
1333 language
= _system
.get("language", None)
1335 self
.__parse
_language
(language
)
1338 self
.virtual
= _system
.get("virtual", False)
1340 hypervisor
= _profile
.get("hypervisor")
1341 self
.__parse
_hypervisor
(hypervisor
)
1344 _network
= _profile
.get("network")
1346 self
.__parse
_network
(_network
)
1349 def device_ids(self
):
1350 return sorted([d
.id for d
in self
.devices
])
1352 def __parse_devices(self
, _devices
):
1355 for _device
in _devices
:
1357 for arg
in self
.__device
_args
:
1358 args
[arg
] = _device
.get(arg
, None)
1360 # Skip if the subsystem is not set
1361 if not args
.get("subsystem", None):
1364 # Find the device or create a new one.
1365 device
= self
.fireinfo
.get_device(**args
)
1367 device
= self
.fireinfo
.create_device(**args
)
1369 self
.devices
.append(device
)
1371 def __parse_system(self
, system
):
1372 vendor
= system
.get("vendor", None)
1376 model
= system
.get("model", None)
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
)
1385 def processor_id(self
):
1386 if not self
.processor
:
1389 return self
.processor
.id
1391 def __parse_processor(self
, _processor
):
1393 for arg
in self
.__processor
_args
:
1394 if arg
== "core_count":
1399 args
[arg
] = _processor
.get(_arg
, None)
1401 for arg
in self
.__processor
_args
_mandatory
:
1402 if not args
.get(arg
, None):
1403 raise ProfileParserError
, "Mandatory argument missing: %s" % arg
1405 self
.processor
= self
.fireinfo
.get_processor(**args
)
1406 if not self
.processor
:
1407 self
.processor
= self
.fireinfo
.create_processor(**args
)
1409 self
.processor_clock_speed
= _processor
.get("speed", None)
1410 self
.processor_bogomips
= _processor
.get("bogomips", None)
1412 arch
= _processor
.get("arch", None)
1414 self
.__parse
_arch
(arch
)
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
1422 self
.kernel
= kernel
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
)
1431 def __parse_release(self
, release
):
1432 # Remove the arch bit
1434 r
= [e
for e
in release
.split() if e
]
1435 for s
in ("(x86_64)", "(i586)", "(armv5tel)"):
1442 release
= " ".join(r
)
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
1449 self
.release
= release
1451 def __parse_language(self
, language
):
1452 self
.language
= language
1453 self
.language
, delim
, rest
= self
.language
.partition(".")
1454 self
.language
, delim
, rest
= self
.language
.partition("_")
1456 def __parse_hypervisor(self
, hypervisor
):
1457 vendor
= hypervisor
.get("vendor", "other")
1459 if vendor
in ("other", "unknown"):
1460 self
.hypervisor_id
= None
1463 self
.hypervisor_id
= self
.fireinfo
.get_hypervisor(vendor
)
1464 if not self
.hypervisor_id
:
1465 self
.hypervisor_id
= self
.fireinfo
.create_hypervisor(vendor
)
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),
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
)
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
)
1493 def parse_profile(self
, public_id
, blob
):
1494 return ProfileParser(self
.backend
, public_id
, blob
)
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
)
1507 def profile_rate_limit_active(self
, public_id
, when
=None):
1508 res
= self
.db
.get("SELECT COUNT(*) AS count FROM fireinfo_profiles_log \
1509 WHERE public_id = %s AND ts >= then_or_now(%s) - INTERVAL '60 minutes'",
1512 if res
and res
.count
>= 10:
1517 def is_private_id_change_permitted(self
, public_id
, private_id
, when
=None):
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
)
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 \
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
)
1537 return Profile(self
.backend
, res
.id, res
)
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
)
1545 return Profile(self
.backend
, res
.id, res
)
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
)
1553 profile
= Profile(self
.backend
, row
.id, row
)
1554 profiles
.append(profile
)
1558 def create_profile(self
, public_id
, private_id
, when
=None):
1559 valid
= self
.settings
.get_int("fireinfo_profile_days_valid", 14)
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
)
1567 p
= Profile(self
.backend
, res
.id)
1568 p
.log_profile_update()
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
)
1581 return Device(self
.backend
, res
.id)
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
)
1595 return Device(self
.backend
, res
.id, res
)
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
)
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
)
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
)
1621 return Processor(self
.backend
, res
.id)
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
)
1628 return Processor(self
.backend
, res
.id, data
=res
, **kwargs
)
1630 def get_processor(self
, vendor
, model_string
, family
, model
, stepping
, core_count
, flags
=None):
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
)
1642 return Processor(self
.backend
, res
.id, res
)
1646 def create_kernel(self
, kernel
):
1647 res
= self
.db
.get("INSERT INTO fireinfo_kernels(name) VALUES(%s) \
1648 RETURNING id", kernel
)
1653 def get_kernel(self
, kernel
):
1654 res
= self
.db
.get("SELECT id FROM fireinfo_kernels WHERE name = %s", kernel
)
1661 def create_arch(self
, arch
):
1662 res
= self
.db
.get("INSERT INTO fireinfo_arches(name) VALUES(%s) \
1663 RETURNING id", arch
)
1668 def get_arch(self
, arch
):
1669 res
= self
.db
.get("SELECT id FROM fireinfo_arches WHERE name = %s", arch
)
1676 def create_release(self
, release
):
1677 res
= self
.db
.get("INSERT INTO fireinfo_releases(name) VALUES(%s) \
1678 RETURNING id", release
)
1683 def get_release(self
, release
):
1684 res
= self
.db
.get("SELECT id FROM fireinfo_releases WHERE name = %s", release
)
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
)
1696 return res
.penetration
1698 def get_random_country_penetration(self
):
1699 res
= self
.db
.get("SELECT * FROM fireinfo_country_percentages \
1700 ORDER BY RANDOM() LIMIT 1")
1703 return database
.Row({
1704 "country" : iso3166
.countries
.get(res
.location
),
1705 "percentage" : res
.count
,
1710 def create_hypervisor(self
, hypervisor
):
1711 res
= self
.db
.get("INSERT INTO fireinfo_hypervisors(name) VALUES(%s) \
1712 RETURNING id", hypervisor
)
1717 def get_hypervisor(self
, hypervisor
):
1718 res
= self
.db
.get("SELECT id FROM fireinfo_hypervisors WHERE name = %s",
1726 def handle_profile(self
, *args
, **kwargs
):
1727 self
.db
.execute("START TRANSACTION")
1729 # Wrap all the handling of the profile in a huge transaction.
1731 self
._handle
_profile
(*args
, **kwargs
)
1734 self
.db
.execute("ROLLBACK")
1738 self
.db
.execute("COMMIT")
1740 def _handle_profile(self
, public_id
, profile_blob
, location
=None, when
=None):
1741 private_id
= profile_blob
.get("private_id", None)
1744 # Check if the profile already exists in the database.
1745 profile
= self
.fireinfo
.get_profile(public_id
, private_id
=private_id
, when
=when
)
1747 # Check if the update can actually be updated
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
)
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
)
1757 profile_parser
= self
.parse_profile(public_id
, profile_blob
)
1759 # If a profile exists, check if it matches and if so, just update the
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
)
1767 # If it does not match, we assume that it is expired and
1768 # create a new profile.
1769 profile
.expired(when
=when
)
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
)
1776 profile
.set_location(location
)
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
)
1790 return res
.public_id
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
)
1799 return res
.with_data
, res
.count
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
)
1808 def get_geo_location_map(self
, when
=None, minimum_percentage
=1):
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 \
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
)
1816 return ((r
.location
, r
.count
) for r
in res
)
1818 def get_language_map(self
, when
=None):
1819 res
= self
.db
.query("WITH profiles AS (SELECT fireinfo_profiles_with_data_at(%s) AS id) \
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
)
1824 return ((r
.language
, r
.count
) for r
in res
)
1827 def cpu_vendors(self
):
1828 res
= self
.db
.query("SELECT DISTINCT vendor FROM fireinfo_processors ORDER BY vendor")
1830 return (CPU_VENDORS
.get(r
.vendor
, r
.vendor
) for r
in res
)
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
)
1839 return ((CPU_VENDORS
.get(r
.vendor
, r
.vendor
), r
.count
) for r
in res
)
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 \
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)
1854 return (res
.avg
or 0, res
.stddev
or 0, res
.min or 0, res
.max or 0)
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
)
1868 return res
.count
or 0
1870 def get_common_cpu_flags_by_platform(self
, platform
, when
=None):
1871 if platform
== "arm":
1873 "lpae", "neon", "thumb", "thumb2", "thumbee", "vfpv3", "vfpv4",
1875 elif platform
== "x86":
1877 "aes", "avx", "avx2", "lm", "mmx", "mmxext", "nx", "pae",
1878 "pni", "popcnt", "sse", "sse2", "rdrand", "ssse3", "sse4a",
1879 "sse4_1", "sse4_2", "sha", "pclmulqdq", "rdseed",
1886 ret
.append((flag
, self
.get_cpus_with_platform_and_flag(platform
, flag
, when
=when
)))
1888 # Add virtual CPU flag "virt" for virtualization support
1889 if platform
== "x86":
1891 self
.get_cpus_with_platform_and_flag(platform
, "vmx", when
=when
) + \
1892 self
.get_cpus_with_platform_and_flag(platform
, "svm", when
=when
)))
1894 return sorted(ret
, key
=lambda x
: x
[1], reverse
=True)
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)
1900 for amount
in amounts
:
1901 ret
.append((amount
, self
.get_memory_amount(amount
* 1024 * 0.95, when
=when
)))
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
)
1914 return res
.percentage
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
)
1925 return (res
.avg
or 0, res
.stddev
or 0, res
.min or 0, res
.max or 0)
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 \
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
)
1936 return ((r
.arch
, r
.count
) for r
in res
)
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")
1950 return ((r
.name
, r
.count
) for r
in res
)
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
)
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
)
1970 return ((r
.name
, r
.count
) for r
in res
)
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
)
1979 return ((r
.name
, r
.count
) for r
in res
)
1981 def _process_devices(self
, devices
):
1985 dev
= Device(self
.backend
, dev
.get("id", None), dev
)
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
)
2001 return self
._process
_devices
(res
)
2004 "pci" : hwdata
.PCI(),
2005 "usb" : hwdata
.USB(),
2008 def get_vendor_string(self
, subsystem
, vendor_id
):
2010 cls
= self
.subsystem2class
[subsystem
]
2014 return cls
.get_vendor(vendor_id
)
2016 def get_model_string(self
, subsystem
, vendor_id
, model_id
):
2018 cls
= self
.subsystem2class
[subsystem
]
2022 return cls
.get_device(vendor_id
, model_id
)
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
)
2033 vendor
= self
.get_vendor_string(row
.subsystem
, row
.vendor
)
2035 # Drop if vendor could not be determined
2040 vendors
[vendor
].append((row
.subsystem
, row
.vendor
))
2042 vendors
[vendor
] = [(row
.subsystem
, row
.vendor
)]
2044 vendors
= vendors
.items()
2045 return sorted(vendors
)
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
)
2057 return self
._process
_devices
(res
)
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
)
2069 return res
.percentage
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
)
2082 return (r
.public_id
for r
in res
)
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
)