}
CPU_STRINGS = (
- # AMD
+ ### AMD ###
+ # APU
+ (r"AMD (Sempron)\(tm\) (\d+) APU with Radeon\(tm\) R\d+", r"AMD \1 \2 APU"),
+ (r"AMD ([\w\-]+) APU with Radeon\(tm\) HD Graphics", r"AMD \1 APU"),
+ (r"AMD ([\w\-]+) Radeon R\d+, \d+ Compute Cores \d+C\+\d+G", r"AMD \1 APU"),
+ # Athlon
+ (r"AMD Athlon.* II X2 ([a-z0-9]+).*", r"AMD Athlon X2 \1"),
+ (r"AMD Athlon\(tm\) 64 Processor (\w+)", r"AMD Athlon64 \1"),
+ (r"AMD Athlon\(tm\) 64 X2 Dual Core Processor (\w+)", r"AMD Athlon64 X2 \1"),
(r"(AMD Athlon).*(XP).*", r"\1 \2"),
(r"(AMD Phenom).* ([0-9]+) .*", r"\1 \2"),
(r"(AMD Phenom).*", r"\1"),
(r"(AMD Sempron).*", r"\1"),
- (r"AMD Athlon.* II X2 ([a-z0-9]+).*", r"AMD Athlon X2 \1"),
+ # Geode
+ (r"Geode\(TM\) Integrated Processor by AMD PCS", r"AMD Geode"),
(r"(Geode).*", r"\1"),
+ # Mobile
+ (r"Mobile AMD (Athlon|Sempron)\(tm\) Processor (\d+\+?)", r"AMD \1-M \2"),
# Intel
(r"Intel\(R\) (Atom|Celeron).*CPU\s*([A-Z0-9]+) .*", r"Intel \1 \2"),
(r"(Intel).*(Celeron).*", r"\1 \2"),
- (r"Intel.* Core.*2 Duo *CPU .* ([A-Z0-9]+) .*", r"Intel C2D \1"),
- (r"Intel.* Core.*2 CPU .* ([A-Z0-9]+) .*", r"Intel C2 \1"),
- (r"Intel.* Core.*2 Quad *CPU .* ([A-Z0-9]+) .*", r"Intel C2Q \1"),
- (r"Intel.* Xeon.* CPU .* ([A-Z0-9]+) .*", r"Intel Xeon \1"),
+ (r"Intel\(R\)? Core\(TM\)?2 Duo *CPU .* ([A-Z0-9]+) .*", r"Intel C2D \1"),
+ (r"Intel\(R\)? Core\(TM\)?2 Duo CPU (\w+)", r"Intel C2D \1"),
+ (r"Intel\(R\)? Core\(TM\)?2 CPU .* ([A-Z0-9]+) .*", r"Intel C2 \1"),
+ (r"Intel\(R\)? Core\(TM\)?2 Quad *CPU .* ([A-Z0-9]+) .*", r"Intel C2Q \1"),
+ (r"Intel\(R\)? Core\(TM\)? (i[753]\-\w+) CPU", r"Intel Core \1"),
+ (r"Intel\(R\)? Xeon\(R\)? CPU (\w+) (0|v\d+)", r"Intel Xeon \1 \2"),
+ (r"Intel\(R\)? Xeon\(R\)? CPU\s+(\w+)", r"Intel Xeon \1"),
(r"(Intel).*(Xeon).*", r"\1 \2"),
(r"Intel.* Pentium.* (D|4) .*", r"Intel Pentium \1"),
(r"Intel.* Pentium.* Dual .* ([A-Z0-9]+) .*", r"Intel Pentium Dual \1"),
(r"(Pentium I{2,3}).*", r"Intel \1"),
(r"(Celeron \(Coppermine\))", r"Intel Celeron"),
+ # NSC
+ (r"Geode\(TM\) Integrated Processor by National Semi", r"NSC Geode"),
+
# VIA
(r"(VIA \w*).*", r"\1"),
class ProfileNetwork(ProfileDict):
def __eq__(self, other):
+ if other is None:
+ return False
+
if not self.has_red == other.has_red:
return False
except KeyError:
return self.data.vendor
+ @property
+ def family(self):
+ return self.data.family
+
@property
def model(self):
return self.data.model
+ @property
+ def stepping(self):
+ return self.data.stepping
+
@property
def model_string(self):
s = self.data.model_string.split()
return flag in self.flags
def uses_ht(self):
+ if self.family == 6 and self.model in (55, 77):
+ return False
+
return self.has_flag("ht")
@property
def format_model(self):
s = self.model_string
+
+ # Remove everything after the @: Intel(R) Core(TM) i7-3770 CPU @ 3.40GHz
+ s, sep, rest = s.partition("@")
+
for pattern, repl in CPU_STRINGS:
if re.match(pattern, s) is None:
continue
- return re.sub(pattern, repl, s)
+
+ s = re.sub(pattern, repl, s)
+ break
# Otherwise remove the symbols
- for i in ("C", "R", "TM"):
+ for i in ("C", "R", "TM", "tm"):
s = s.replace("(%s)" % i, "")
+ # Replace too long strings with shorter ones
+ pairs = (
+ ("Quad-Core Processor", ""),
+ ("Dual-Core Processor", ""),
+ ("Processor", "CPU"),
+ ("processor", "CPU"),
+ )
+ for k, v in pairs:
+ s = s.replace(k, v)
+
+ # Remove too many spaces
+ s = " ".join((e for e in s.split() if e))
+
return s
@property
if location:
self.set_location(location)
+ self.log_profile_update()
+
+ def log_profile_update(self):
+ # Log that an update was performed for this profile id
+ self.db.execute("INSERT INTO fireinfo_profiles_log(public_id) \
+ VALUES(%s)", self.public_id)
+
def expired(self, when=None):
self.db.execute("UPDATE fireinfo_profiles \
SET time_valid = then_or_now(%s) WHERE id = %s", when, self.id)
devices = property(get_devices, set_devices)
- def count_device(self, vendor, model):
+ def count_device(self, subsystem, vendor, model):
counter = 0
for dev in self.devices:
- if dev.vendor == vendor and dev.model == model:
+ if dev.subsystem == subsystem and dev.vendor == vendor and dev.model == model:
counter += 1
return counter
def appliance_id(self):
if not hasattr(self, "_appliance_id"):
appliances = (
+ ("fountainnetworks-prime", self._appliance_test_fountainnetworks_prime),
+ ("lightningwirelabs-eco-plus", self._appliance_test_lightningwirelabs_eco_plus),
("lightningwirelabs-eco", self._appliance_test_lightningwirelabs_eco),
)
@property
def appliance(self):
- if self.appliance_id == "lightningwirelabs-eco":
- return "Lightning Wire Labs IPFire Eco Appliance"
+ if self.appliance_id == "fountainnetworks-prime":
+ return "Fountain Networks - IPFire Prime Box"
+
+ elif self.appliance_id == "lightningwirelabs-eco-plus":
+ return "Lightning Wire Labs - IPFire Eco Plus Appliance"
+
+ elif self.appliance_id == "lightningwirelabs-eco":
+ return "Lightning Wire Labs - IPFire Eco Appliance"
+
+ def _appliance_test_fountainnetworks_prime(self):
+ if not self.system in (("SECO", None), ("SECO", "0949")):
+ return False
+
+ # Must have a wireless device
+ if self.count_device("usb", "148f", "5572") < 1:
+ return False
+
+ return True
def _appliance_test_lightningwirelabs_eco(self):
if not self.system == ("MSI", "MS-9877"):
return False
# Must have four Intel network adapters
- network_adapters_count = self.count_device("8086", "10d3")
+ network_adapters_count = self.count_device("pci", "8086", "10d3")
if not network_adapters_count == 4:
return False
- # 4GB of memory
- if not self.memory >= 4230823936 * 0.95:
+ return True
+
+ def _appliance_test_lightningwirelabs_eco_plus(self):
+ if not self.system_vendor == "ASUS":
+ return False
+
+ if not self.system_model.startswith("P9A-I/2550"):
+ return False
+
+ # Must have four Intel network adapters
+ network_adapters_count = self.count_device("pci", "8086", "1f41")
+ if not network_adapters_count == 4:
return False
return True
@property
def friendly_memory(self):
- return util.format_size(self.memory)
+ return util.format_size(self.memory or 0)
# Storage
return r
+ @property
+ def release_short(self):
+ pairs = (
+ (r"Release Candidate (\d+)", r"RC\1"),
+ )
+
+ s = self.release
+ for pattern, repl in pairs:
+ if re.search(pattern, s) is None:
+ continue
+
+ s = re.sub(pattern, repl, s)
+
+ return s
+
# Virtual
@property
for arg in self.__device_args:
args[arg] = _device.get(arg, None)
+ # Skip if the subsystem is not set
+ if not args.get("subsystem", None):
+ continue
+
# Find the device or create a new one.
device = self.fireinfo.get_device(**args)
if not device:
# Remove the arch bit
if release:
r = [e for e in release.split() if e]
- for s in ("(i586)", "(armv5tel)"):
+ for s in ("(x86_64)", "(i586)", "(armv5tel)"):
try:
r.remove(s)
break
self.release = release
def __parse_language(self, language):
- self.language, delim, rest = language.partition(".")
- self.language, delim, rest = language.partition("_")
+ self.language = language
+ self.language, delim, rest = self.language.partition(".")
+ self.language, delim, rest = self.language.partition("_")
def __parse_hypervisor(self, hypervisor):
vendor = hypervisor.get("vendor", "other")
if res:
return res.count
- def get_archives_count(self):
- res = self.db.get("SELECT COUNT(*) AS count FROM fireinfo_profiles")
+ def get_total_updates_count(self, when=None):
+ res = self.db.get("SELECT COUNT(*) + SUM(updates) AS count \
+ FROM fireinfo_profiles WHERE time_created <= then_or_now(%s)", when)
if res:
return res.count
return False
- def can_update_profile(self, public_id, private_id, when=None):
- res = self.db.get("SELECT 1 FROM fireinfo_profiles \
- WHERE public_id = %s AND private_id = %s \
- AND time_updated + INTERVAL '60 minutes' <= then_or_now(%s) \
- AND time_valid >= then_or_now(%s) ORDER BY time_updated DESC LIMIT 1",
- public_id, private_id, when, when)
+ def profile_rate_limit_active(self, public_id, when=None):
+ # Remove all outdated entries
+ self.db.execute("DELETE FROM fireinfo_profiles_log \
+ WHERE ts <= then_or_now(%s) - INTERVAL '60 minutes'", when)
- if res:
+ res = self.db.get("SELECT COUNT(*) AS count FROM fireinfo_profiles_log \
+ WHERE public_id = %s", public_id)
+
+ if res and res.count >= 10:
return True
return False
+ def is_private_id_change_permitted(self, public_id, private_id, when=None):
+ # Check if a profile exists with a different private id that is still valid
+ res = self.db.get("SELECT 1 FROM fireinfo_profiles \
+ WHERE public_id = %s AND NOT private_id = %s \
+ AND time_valid >= then_or_now(%s) LIMIT 1", public_id, private_id, when)
+
+ if res:
+ return False
+
+ return True
+
def get_profile(self, public_id, private_id=None, when=None):
res = self.db.get("SELECT * FROM fireinfo_profiles \
WHERE public_id = %s AND \
(CASE WHEN %s IS NULL THEN TRUE ELSE private_id = %s END) AND \
- (CASE WHEN %s IS NULL THEN TRUE ELSE \
- then_or_now(%s) BETWEEN time_created AND time_valid END) \
- ORDER BY time_updated DESC LIMIT 1", public_id,
- private_id, private_id, when, when)
+ then_or_now(%s) BETWEEN time_created AND time_valid \
+ ORDER BY time_updated DESC LIMIT 1",
+ public_id, private_id, private_id, when)
+
+ if res:
+ return Profile(self.backend, res.id, res)
+
+ def get_profile_with_data(self, public_id, when=None):
+ res = self.db.get("WITH profiles AS (SELECT fireinfo_profiles_with_data_at(%s) AS id) \
+ SELECT * FROM profiles JOIN fireinfo_profiles ON profiles.id = fireinfo_profiles.id \
+ WHERE public_id = %s ORDER BY time_updated DESC LIMIT 1", when, public_id)
if res:
return Profile(self.backend, res.id, res)
public_id, private_id, when, when, when, valid)
if res:
- return Profile(self.backend, res.id)
+ p = Profile(self.backend, res.id)
+ p.log_profile_update()
+
+ return p
# Devices
profile = self.fireinfo.get_profile(public_id, private_id=private_id, when=when)
# Check if the update can actually be updated
- if profile and not self.fireinfo.can_update_profile(public_id, private_id, when=when):
- logging.warning("Profile not updated because private ID does not"
- " match or last update is too recent: %s" % public_id)
+ if profile and self.fireinfo.profile_rate_limit_active(public_id, when=when):
+ logging.warning("There were too many updates for this profile in the last hour: %s" % public_id)
+ return
+
+ elif not self.is_private_id_change_permitted(public_id, private_id, when=when):
+ logging.warning("Changing private id is not permitted for profile: %s" % public_id)
return
# Parse the profile
return ((r.location, r.count) for r in res)
def get_language_map(self, when=None):
- res = self.db.query("WITH profiles AS (SELECT fireinfo_profiles_at(%s) AS id) \
+ res = self.db.query("WITH profiles AS (SELECT fireinfo_profiles_with_data_at(%s) AS id) \
SELECT language, COUNT(language)::float / (SELECT COUNT(*) FROM profiles) AS count FROM profiles \
LEFT JOIN fireinfo_profiles_languages ON profiles.id = fireinfo_profiles_languages.profile_id \
WHERE fireinfo_profiles_languages.language IS NOT NULL GROUP BY language ORDER BY count DESC", when)
MAX(fireinfo_profiles_processors.clock_speed) AS max FROM profiles \
LEFT JOIN fireinfo_profiles_processors ON profiles.id = fireinfo_profiles_processors.profile_id \
WHERE NOT fireinfo_profiles_processors.processor_id IS NULL \
- AND fireinfo_profiles_processors.clock_speed > 0", when)
+ AND fireinfo_profiles_processors.clock_speed > 0 \
+ AND fireinfo_profiles_processors.clock_speed < fireinfo_profiles_processors.bogomips \
+ AND fireinfo_profiles_processors.bogomips <= %s", when, 10000)
if res:
return (res.avg or 0, res.stddev or 0, res.min or 0, res.max or 0)