]>
git.ipfire.org Git - people/shoehn/ipfire.org.git/blob - www/webapp/backend/stasy.py
3 from __future__
import division
11 from misc
import Singleton
13 DATABASE_HOST
= ["irma.ipfire.org", "madeye.ipfire.org"]
14 DATABASE_NAME
= "stasy"
16 CPU_SPEED_CONSTRAINTS
= (0, 500, 1000, 1500, 2000, 2500, 3000, 3500)
17 MEMORY_CONSTRAINTS
= (0, 128, 256, 512, 1024, 2048, 4096, 8192, 16384)
21 (r
"(AMD Athlon).*(XP).*", r
"\1 \2"),
22 (r
"(AMD Phenom).* ([0-9]+) .*", r
"\1 \2"),
23 (r
"(AMD Phenom).*", r
"\1"),
24 (r
"(AMD Sempron).*", r
"\1"),
25 (r
"AMD Athlon.* II X2 ([a-z0-9]+).*", r
"AMD Athlon X2 \1"),
26 (r
"(Geode).*", r
"\1"),
29 (r
"Intel.*(Atom|Celeron).*CPU\s*([A-Z0-9]+) .*", r
"Intel \1 \2"),
30 (r
"(Intel).*(Celeron).*", r
"\1 \2"),
31 (r
"Intel.* Core.*2 Duo CPU .* ([A-Z0-9]+) .*", r
"Intel C2D \1"),
32 (r
"Intel.* Core.*2 CPU .* ([A-Z0-9]+) .*", r
"Intel C2 \1"),
33 (r
"Intel.* Core.*2 Quad CPU .* ([A-Z0-9]+) .*", r
"Intel C2Q \1"),
34 (r
"Intel.* Xeon.* CPU .* ([A-Z0-9]+) .*", r
"Intel Xeon \1"),
35 (r
"(Intel).*(Xeon).*", r
"\1 \2"),
36 (r
"Intel.* Pentium.* (D|4) .*", r
"Intel Pentium \1"),
37 (r
"Intel.* Pentium.* Dual .* ([A-Z0-9]+) .*", r
"Intel Pentium Dual \1"),
38 (r
"Pentium.* Dual-Core .* ([A-Z0-9]+) .*", r
"Intel Pentium Dual \1"),
39 (r
"(Pentium I{2,3}).*", r
"Intel \1"),
40 (r
"(Celeron \(Coppermine\))", r
"Intel Celeron"),
43 (r
"(VIA \w*).*", r
"\1"),
46 class ProfileDict(object):
47 def __init__(self
, data
):
51 class ProfileCPU(ProfileDict
):
54 return self
._data
.get("arch")
58 return self
._data
.get("vendor")
62 return self
._data
.get("speed")
65 def friendly_speed(self
):
67 return "%dMHz" % self
.speed
69 return "%.1fGHz" % round(self
.speed
/ 1000, 1)
73 return self
._data
.get("bogomips")
77 return self
._data
.get("model")
80 def model_string(self
):
81 return self
._data
.get("model_string")
85 return self
._data
.get("flags")
89 return self
._data
.get("count")
92 def capable_64bit(self
):
93 return "lm" in self
.flags
96 def capable_pae(self
):
97 return "pae" in self
.flags
100 def capable_virt(self
):
101 return "vmx" in self
.flags
or "svm" in self
.flags
104 def friendly_vendor(self
):
105 s
= self
.model_string
106 for pattern
, repl
in CPU_STRINGS
:
107 if re
.match(pattern
, s
) is None:
109 return re
.sub(pattern
, repl
, s
)
114 def friendly_string(self
):
115 return "%s @ %s" % (self
.friendly_vendor
, self
.friendly_speed
)
118 class ProfileHypervisor(ProfileDict
):
120 return "<%s %s-%s>" % (self
.__class
__.__name
__, self
.vendor
, self
.type)
124 return self
._data
.get("vendor")
128 return self
._data
.get("type")
131 class ProfileNetwork(ProfileDict
):
132 def has_zone(self
, name
):
133 return self
._data
.get(name
)
137 return self
._data
.get("green")
141 return self
._data
.get("red")
145 return self
._data
.get("blue")
149 return self
._data
.get("orange")
152 class ProfileDevice(ProfileDict
):
160 "00" : "Unclassified",
161 "01" : "Mass storage",
165 "05" : "Memory controller",
167 "07" : "Communication",
168 "08" : "Generic system peripheral",
169 "09" : "Input device",
170 "0a" : "Docking station",
174 "0e" : "Intelligent controller",
175 "0f" : "Satellite communications controller",
177 "11" : "Signal processing controller",
178 "ff" : "Unassigned class",
182 "00" : "Unclassified",
184 "02" : "Communication",
185 "03" : "Input device",
186 "05" : "Generic system peripheral",
189 "08" : "Mass storage",
191 "0a" : "Communication",
195 "0f" : "Personal Healthcare",
196 "dc" : "Diagnostic Device",
198 "ef" : "Unclassified",
199 "fe" : "Unclassified",
200 "ff" : "Unclassified",
204 def __cmp__(self
, other
):
205 return cmp(self
.vendor
, other
.vendor
) or \
206 cmp(self
.model
, other
.model
) or \
207 cmp(self
.driver
, other
.driver
)
211 return self
._data
.get("model")
214 def model_string(self
):
215 cls
= self
.subsystem2class
[self
.subsystem
]
217 return cls().get_device(self
.vendor
, self
.model
)
221 return self
._data
.get("subsystem")
225 return self
._data
.get("vendor")
228 def vendor_string(self
):
229 cls
= self
.subsystem2class
[self
.subsystem
]
231 return cls().get_vendor(self
.vendor
)
235 return self
._data
.get("driver")
239 classid
= self
._data
.get("deviceclass")
241 if self
.subsystem
== "pci":
242 classid
= classid
[:-4]
243 if len(classid
) == 1:
244 classid
= "0%s" % classid
246 elif self
.subsystem
== "usb" and classid
:
247 classid
= classid
.split("/")[0]
248 classid
= "%02x" % int(classid
)
251 return self
.classid2name
[self
.subsystem
][classid
]
256 class Profile(ProfileDict
):
257 def __init__(self
, profile_blob
):
258 ProfileDict
.__init
__(self
, profile_blob
.get("profile"))
260 self
.public_id
= profile_blob
.get("public_id")
261 self
.updated
= profile_blob
.get("updated")
262 self
._geoip
= profile_blob
.get("geoip", None)
265 return "<%s %s>" % (self
.__class
__.__name
__, self
.public_id
)
269 return ProfileCPU(self
._data
.get("cpu"))
274 for d
in self
._data
.get("devices"):
277 if d
.driver
in ("pcieport", "usb", "hub"):
285 def hypervisor(self
):
287 return ProfileHypervisor(self
._data
.get("hypervisor"))
291 return self
.system
.get("virtual")
295 return self
._data
.get("system")
299 return self
.system
.get("release")
303 return self
.system
.get("kernel_release")
307 return self
.system
.get("memory")
310 def friendly_memory(self
):
311 units
= ("k", "M", "G", "T")
319 return "%d%s" % (round(mem
, 0), units
[i
])
323 return self
.system
.get("root_size") or 0
326 def friendly_root_size(self
):
327 units
= ("k", "M", "G", "T")
329 size
= self
.root_size
338 return "%d%s" % (round(size
, 0), units
[i
])
342 return self
.system
.get("vendor")
346 return self
.system
.get("model")
349 def country_code(self
):
351 return self
._geoip
["country_code"].lower()
357 network
= self
._data
.get("network", None)
359 return ProfileNetwork(network
)
363 __metaclass__
= Singleton
366 # Initialize database connection
367 self
._conn
= pymongo
.Connection(DATABASE_HOST
)
368 self
._db
= self
._conn
[DATABASE_NAME
]
370 def get_profile_count(self
):
371 # XXX need to implement something to get profiles updated since
374 # All distinct profiles (based on public_id)
375 # XXX possibly bad performance
376 return len(self
._db
.profiles
.distinct("public_id"))
378 def get_archives_count(self
):
379 return self
._db
.archives
.count()
381 #def _get_profile_cursor(self, public_id):
382 # c = self._db.profiles.find({ "public_id" : public_id })
383 # c.sort("updated", pymongo.ASCENDING)
387 def profile_exists(self
, public_id
):
388 return self
.query({ "public_id" : public_id
}).count() >= 1
390 def get_profile(self
, public_id
):
392 # XXX should only find one object in the end
393 for p
in self
.query({ "public_id" : public_id
}):
398 def get_profiles(self
):
399 # XXX needs nicer database query
401 for p
in self
._db
.profiles
.find():
402 if not p
.get("public_id") in profiles
:
403 profiles
.append(p
.get("public_id"))
407 def query(self
, query
, archives
=False, no_virt
=False, all
=False):
408 db
= self
._db
.profiles
411 db
= self
.db
.archives
414 # XXX cannot use the index?
415 query
.update({ "profile" : { "$exists" : True }})
418 query
.update({ "profile.system.virtual" : False })
420 logging
.debug("Executing query: %s" % query
)
422 return db
.find(query
)
425 def secret_ids(self
):
426 return self
._db
.profiles
.distinct("secret_id")
430 return self
.query({}, no_virt
=True).distinct("profile.cpu")
433 def cpu_vendors(self
):
434 return self
.query({}, no_virt
=True).distinct("profile.cpu.vendor")
437 def cpu_vendors_map(self
):
440 for vendor
in self
.cpu_vendors
:
443 "profile.cpu.vendor" : vendor
444 }, no_virt
=True).count()
449 def cpu_speed_average(self
):
452 all
= self
.query({}, no_virt
=True)
454 # XXX ugly. needs to be done by group()
456 if not m
.has_key("profile"):
458 speed
+= m
.get("profile").get("cpu").get("speed")
460 return (speed
/ all
.count())
463 def cpu_speed_map(self
):
466 for i
in range(len(CPU_SPEED_CONSTRAINTS
) - 1):
467 min, max = CPU_SPEED_CONSTRAINTS
[i
:i
+2]
469 cpu_speeds
[min, max] = \
471 "profile.cpu.speed" : {
472 "$gte" : min, "$lt" : max
474 }, no_virt
=True).count()
478 def get_memory_map(self
):
481 for i
in range(len(MEMORY_CONSTRAINTS
) - 1):
482 min, max = MEMORY_CONSTRAINTS
[i
:i
+2]
486 { "profile.system.memory" : {
487 "$gte" : min * 1024, "$lt" : max * 1024
489 }, no_virt
=True).count()
494 def memory_average(self
):
497 all
= self
.query({}, no_virt
=True)
499 # XXX ugly. needs to be done by group()
501 if not m
.has_key("profile"):
503 memory
+= int(m
.get("profile").get("system").get("memory"))
505 return (memory
/ all
.count()) / 1024
508 def hypervisor_vendors(self
):
509 return self
.query({}).distinct("profile.hypervisor.vendor")
512 def hypervisor_map(self
):
515 for hypervisor
in self
.hypervisor_vendors
:
516 hypervisors
[hypervisor
] = \
518 "profile.hypervisor.vendor" : hypervisor
524 def hypervisor_models(self
):
525 return self
.query({}).distinct("profile.hypervisor.model")
528 def virtual_map(self
):
534 for k
in virtual
.keys():
536 self
.query({ "profile.system.virtual": k
}).count()
542 return self
.query({}).distinct("profile.system.language")
544 def get_language_map(self
):
547 for language
in self
.languages
:
548 languages
[language
] = \
550 "profile.system.language" : language
557 return self
.query({}).distinct("profile.system.vendor")
560 def vendor_map(self
):
563 for vendor
in self
.vendors
:
566 "profile.system.vendor" : vendor
573 return self
.query({}).distinct("profile.system.model")
579 for model
in self
.models
:
582 "profile.system.model" : model
589 return self
.query({}).distinct("profile.cpu.arch")
595 for arch
in self
.arches
:
598 "profile.cpu.arch" : arch
605 return self
.query({}).distinct("profile.system.kernel_release")
608 def kernel_map(self
):
611 for kernel
in self
.kernels
:
614 "profile.system.kernel_release" : kernel
621 return self
.query({}).distinct("profile.system.release")
624 def release_map(self
):
627 for release
in self
.releases
:
628 releases
[release
] = \
630 "profile.system.release" : release
635 def get_device_percentage(self
, bus
, vendor_id
, model_id
):
636 profiles_with_device
= self
.query({
637 "profile.devices.subsystem" : bus
,
638 "profile.devices.vendor" : vendor_id
,
639 "profile.devices.model" : model_id
,
642 profiles_all
= self
.query({})
644 if not profiles_all
.count():
647 return profiles_with_device
.count() / profiles_all
.count()
649 def get_cpu_flag_map(self
, flags
):
650 # XXX needs a cleanup
652 _flags
= { True : 0 }
654 if type(flags
) == type("a"):
660 "profile.cpu.flags" : flag
,
661 }, no_virt
=True).count()
663 _flags
[False] = self
.query({}, no_virt
=True).count() - _flags
[True]
668 def geo_locations(self
):
669 return [code
.lower() for code
in self
.query({}, all
=True).distinct("geoip.country_code")]
671 def get_geo_location_map(self
):
675 for geo_location
in self
.geo_locations
:
676 geo_locations
[geo_location
] = \
678 "geoip.country_code" : geo_location
.upper()
681 count
+= geo_locations
[geo_location
]
683 for geo_location
in geo_locations
.keys():
684 geo_locations
[geo_location
] /= count
688 def get_models_by_vendor(self
, subsystem
, vendor_id
):
691 profiles_all
= self
.query({})
693 for profile
in profiles_all
:
694 if not profile
.has_key("profile"):
697 profile
= Profile(profile
)
699 for device
in profile
.devices
:
700 if not device
.vendor
== vendor_id
:
703 if not device
in devices
:
704 devices
.append(device
)
708 def get_network_zones_map(self
):
709 zones
= { "green" : 0, "blue" : 0, "orange" : 0, "red" : 0 }
711 all
= self
.query({ "profile.network" : { "$exists" : True }})
713 for zone
in zones
.keys():
714 zones
[zone
] = self
.query({
715 "profile.network.%s" % zone
: True,
716 }).count() / all
.count()
720 def get_profile_ratio(self
):
721 return (self
.query({}).count(), self
.get_profile_count())
723 def get_updated_since(self
, since
, _query
={}):
724 since
= datetime
.datetime
.utcnow() - datetime
.timedelta(**since
)
726 query
= { "updated" : { "$gte" : since
}}
729 return self
.query(query
)
731 def get_updates_by_release_since(self
, since
):
734 for release
in self
.releases
:
735 updates
[release
] = self
.get_updated_since(since
,
736 { "profile.system.release" : release
}).count()
741 if __name__
== "__main__":