#!/usr/bin/python
-
+import hashlib
import json
import os
import string
+import _fireinfo
+
import cpu
import device
+import hypervisor
+
+PROFILE_VERSION = 0
+
+SYS_CLASS_DMI = "/sys/class/dmi/id"
+SECRET_ID_FILE = "/etc/fireinfo-id"
+
+class Singleton(type):
+ def __init__(cls, name, bases, dict):
+ super(Singleton, cls).__init__(name, bases, dict)
+ cls.instance = None
+
+ def __call__(cls, *args, **kw):
+ if cls.instance is None:
+ cls.instance = super(Singleton, cls).__call__(*args, **kw)
+
+ return cls.instance
+
+
+def read_from_file(filename):
+ """
+ Read all data from filename.
+ """
+ if not os.path.exists(filename):
+ return
+
+ try:
+ with open(filename) as f:
+ return f.read().strip()
+ except IOError:
+ pass
class System(object):
+ __metaclass__ = Singleton
def __init__(self):
# find all devices
self.devices = []
self.scan()
self.cpu = cpu.CPU()
-
+ self.hypervisor = hypervisor.Hypervisor()
+
def profile(self):
- p = {
- "public_id" : self.public_id,
- "private_id" : self.private_id,
+ p = {}
+ p["system"] = {
+ # System information
+ "model" : self.model,
+ "vendor" : self.vendor,
+
+ # Indicator if the system is running in a
+ # virtual environment.
+ "virtual" : self.virtual,
- "arch" : self.arch,
+ # System language
"language" : self.language,
+
+ # Release information
"release" : self.release,
+ "kernel_release" : self.kernel_release,
+
"memory" : self.memory,
- "kernel" : self.kernel,
"root_size" : self.root_size,
- "devices" : [],
-
- }
-
+ }
+
+ p["devices"] = []
for device in self.devices:
- p["devices"].append({
+ d = {
"subsystem" : device.subsystem.lower(),
"vendor" : device.vendor.lower(),
"model" : device.model.lower(),
- "deviceclass" : device.deviceclass
- })
-
+ "deviceclass" : device.deviceclass,
+ "driver" : device.driver,
+ }
+
+ # PCI devices provide subsystem information, USB don't.
+ if d["subsystem"] == "pci":
+ d["sub_model"] = device.sub_model
+ d["sub_vendor"] = device.sub_vendor
+
+ p["devices"].append(d)
+
p["cpu"] = {
+ "arch" : self.arch,
"vendor" : self.cpu.vendor,
"model" : self.cpu.model,
+ "model_string" : self.cpu.model_string,
"stepping" : self.cpu.stepping,
"flags" : self.cpu.flags,
"bogomips" : self.cpu.bogomips,
"speed" : self.cpu.speed,
- "modes" : self.cpu.modes,
- "hypervisor" : self.cpu.hypervisor,
- "virtype" : self.cpu.virtype,
"family" : self.cpu.family,
"count" : self.cpu.count
}
-
- return json.dumps(p)
+
+ # Only append hypervisor information if we are virtualized.
+ if self.virtual:
+ p["hypervisor"] = {
+ "type" : self.hypervisor.type,
+ "vendor" : self.hypervisor.vendor,
+ }
+
+ return {
+ # Profile version
+ "profile_version" : PROFILE_VERSION,
+
+ # Identification and authorization codes
+ "public_id" : self.public_id,
+ "private_id" : self.private_id,
+
+ # Actual profile data
+ "profile" : p,
+ }
@property
@property
def public_id(self):
- return "0"*40
-
+ """
+ This returns a globally (hopefully) ID to identify the host
+ later (by request) in the database.
+ """
+ public_id = self.secret_id
+ if not public_id:
+ return "0" * 40
+
+ return hashlib.sha1(public_id).hexdigest()
+
@property
def private_id(self):
- return "1"*40
-
+ """
+ The private ID is built out of the _unique_id and used to
+ permit a host to do changes on the database.
+
+ No one could ever guess this without access to the host.
+ """
+ private_id = ""
+ for i in reversed(self.secret_id):
+ private_id += i
+
+ if not private_id:
+ return "0" * 40
+
+ return hashlib.sha1(private_id).hexdigest()
+
+ @property
+ def secret_id(self):
+ """
+ Read a "secret" ID from a file if available
+ or calculate it from the hardware.
+ """
+ if os.path.exists(SECRET_ID_FILE):
+ return read_from_file(SECRET_ID_FILE)
+
+ return hashlib.sha1(self._unique_id).hexdigest()
+
+ @property
+ def _unique_id(self):
+ """
+ This is a helper ID which is generated out of some hardware information
+ that is considered to be constant over a PC's lifetime.
+
+ None of the data here is ever sent to the server.
+ """
+ ids = []
+
+ # Virtual machines (for example) and some boards have a UUID
+ # which is globally unique.
+ for file in ("product_uuid", "product_serial", "chassis_serial"):
+ id = read_from_file(os.path.join(SYS_CLASS_DMI, file)) or ""
+ ids.append(id)
+
+ # Use serial number from root disk (if available)
+ root_disk_serial = self.root_disk_serial
+ if root_disk_serial:
+ ids.append(root_disk_serial)
+
+ # As last resort, we use the UUID from pakfire.
+ if not ids:
+ id = read_from_file("/opt/pakfire/db/uuid") or ""
+ ids.append(id)
+
+ return "#".join(ids)
+
@property
def language(self):
- with open("/var/ipfire/main/settings", "r") as f:
+ # Return "unknown" if settings file does not exist.
+ filename = "/var/ipfire/main/settings"
+ if not os.path.exists(filename):
+ return "unknown"
+
+ with open(filename, "r") as f:
for line in f.readlines():
key, val = line.split("=", 1)
if key=="LANGUAGE":
@property
def release(self):
- with open("/etc/system-release", "r") as f:
- return f.read().strip()
-
+ return read_from_file("/etc/system-release")
+
+ @property
+ def bios_vendor(self):
+ return read_from_file("/sys/class/dmi/id/bios_vendor")
+
+ @property
+ def vendor(self):
+ ret = None
+ for file in ("chassis_vendor", "board_vendor", "sys_vendor",):
+ ret = read_from_file(os.path.join(SYS_CLASS_DMI, file))
+ if ret:
+ break
+
+ return ret
+
+ @property
+ def model(self):
+ ret = None
+ for file in ("chassis_model", "board_model", "product_name",):
+ ret = read_from_file(os.path.join(SYS_CLASS_DMI, file))
+ if ret:
+ break
+
+ return ret
+
@property
def memory(self):
with open("/proc/meminfo", "r") as f:
firstline = f.readline().strip()
- return firstline.split()[1]
+ return int(firstline.split()[1])
@property
- def kernel(self):
+ def kernel_release(self):
return os.uname()[2]
@property
return
with open(path, "r") as f:
return int(f.readline())*512/1024
-
+
+ @property
+ def root_disk_serial(self):
+ serial = _fireinfo.get_harddisk_serial("/dev/%s" % self.root_disk)
+
+ if serial:
+ # Strip all spaces
+ return serial.strip()
+
def scan(self):
toscan = (("/sys/bus/pci/devices", device.PCIDevice),
("/sys/bus/usb/devices", device.USBDevice))
dirlist = os.listdir(path)
for dir in dirlist:
self.devices.append(cls(os.path.join(path, dir)))
-
-
+
+ @property
+ def virtual(self):
+ """
+ Say if the host is running in a virtual environment.
+ """
+ return self.hypervisor.virtual
print s.arch
print s.language
print s.release
+ print s.bios_vendor
print s.memory
print s.kernel
print s.root_disk
print s.root_size
print "------------\n", s.devices, "\n------------\n"
- print s.profile()
-
- import urllib2
- import urllib
- r = urllib2.Request("http://192.168.10.101:9001/send/%s" %s.public_id, data = urllib.urlencode({"profile" : s.profile()}))
- urllib2.urlopen(r)
\ No newline at end of file
+ print json.dumps(s.profile(), sort_keys=True, indent=4)