]> git.ipfire.org Git - oddments/fireinfo.git/blame - fireinfo/system.py
Bump version 0.13.1.
[oddments/fireinfo.git] / fireinfo / system.py
CommitLineData
b45f0e98
MT
1#!/usr/bin/python
2
31a30328 3import hashlib
b45f0e98
MT
4import json
5import os
6import string
7
3f70e7fd
MT
8import _fireinfo
9
b45f0e98
MT
10import cpu
11import device
715ba5ac 12import hypervisor
b45f0e98 13
0c5ef738
MT
14PROFILE_VERSION = 0
15
45b74ad5 16SYS_CLASS_DMI = "/sys/class/dmi/id"
73014efe 17SECRET_ID_FILE = "/etc/fireinfo-id"
45b74ad5 18
7eadbfba
MT
19INVALID_ID_STRINGS = (
20 "OEM", "O.E.M.", "o.e.m.",
21 "N/A", "n/a",
74ce4c8d 22 "12345", "54321", "202020",
ed491340 23 "Chassis", "chassis",
7eadbfba
MT
24)
25
32aeec73
MT
26class Singleton(type):
27 def __init__(cls, name, bases, dict):
28 super(Singleton, cls).__init__(name, bases, dict)
29 cls.instance = None
30
31 def __call__(cls, *args, **kw):
32 if cls.instance is None:
33 cls.instance = super(Singleton, cls).__call__(*args, **kw)
34
35 return cls.instance
36
37
45b74ad5
MT
38def read_from_file(filename):
39 """
40 Read all data from filename.
41 """
42 if not os.path.exists(filename):
43 return
44
45 try:
46 with open(filename) as f:
47 return f.read().strip()
48 except IOError:
49 pass
50
b45f0e98 51class System(object):
32aeec73 52 __metaclass__ = Singleton
b45f0e98
MT
53
54 def __init__(self):
55 # find all devices
56 self.devices = []
57 self.scan()
58 self.cpu = cpu.CPU()
715ba5ac
MT
59 self.hypervisor = hypervisor.Hypervisor()
60
b45f0e98 61 def profile(self):
0c5ef738
MT
62 p = {}
63 p["system"] = {
45b74ad5
MT
64 # System information
65 "model" : self.model,
66 "vendor" : self.vendor,
67
0c5ef738
MT
68 # Indicator if the system is running in a
69 # virtual environment.
d8614fc3 70 "virtual" : self.virtual,
b45f0e98 71
0c5ef738
MT
72 # System language
73 "language" : self.language,
74
75 # Release information
76 "release" : self.release,
77 "kernel_release" : self.kernel_release,
78
79 "memory" : self.memory,
80 "root_size" : self.root_size,
81 }
82
83 p["devices"] = []
b45f0e98 84 for device in self.devices:
97679775 85 d = {
b45f0e98
MT
86 "subsystem" : device.subsystem.lower(),
87 "vendor" : device.vendor.lower(),
88 "model" : device.model.lower(),
7923840b
MT
89 "deviceclass" : device.deviceclass,
90 "driver" : device.driver,
97679775
MT
91 }
92
93 # PCI devices provide subsystem information, USB don't.
94 if d["subsystem"] == "pci":
95 d["sub_model"] = device.sub_model
96 d["sub_vendor"] = device.sub_vendor
97
98 p["devices"].append(d)
99
b45f0e98 100 p["cpu"] = {
0c5ef738 101 "arch" : self.arch,
b45f0e98
MT
102 "vendor" : self.cpu.vendor,
103 "model" : self.cpu.model,
eecf2eea 104 "model_string" : self.cpu.model_string,
b45f0e98
MT
105 "stepping" : self.cpu.stepping,
106 "flags" : self.cpu.flags,
107 "bogomips" : self.cpu.bogomips,
108 "speed" : self.cpu.speed,
b45f0e98
MT
109 "family" : self.cpu.family,
110 "count" : self.cpu.count
111 }
715ba5ac
MT
112
113 # Only append hypervisor information if we are virtualized.
114 if self.virtual:
115 p["hypervisor"] = {
116 "type" : self.hypervisor.type,
117 "vendor" : self.hypervisor.vendor,
118 }
119
0c5ef738
MT
120 return {
121 # Profile version
122 "profile_version" : PROFILE_VERSION,
123
124 # Identification and authorization codes
125 "public_id" : self.public_id,
126 "private_id" : self.private_id,
127
1beb8e1c
MT
128 # XXX just for beta
129 "secret_id" : self._unique_id,
130
0c5ef738
MT
131 # Actual profile data
132 "profile" : p,
133 }
b45f0e98
MT
134
135
136 @property
137 def arch(self):
138 return os.uname()[4]
139
140 @property
141 def public_id(self):
31a30328
MT
142 """
143 This returns a globally (hopefully) ID to identify the host
144 later (by request) in the database.
145 """
73014efe 146 public_id = self.secret_id
3d10b6d9
MT
147 if not public_id:
148 return "0" * 40
149
150 return hashlib.sha1(public_id).hexdigest()
151
b45f0e98
MT
152 @property
153 def private_id(self):
31a30328
MT
154 """
155 The private ID is built out of the _unique_id and used to
156 permit a host to do changes on the database.
157
158 No one could ever guess this without access to the host.
159 """
5e2ba24e 160 private_id = ""
73014efe 161 for i in reversed(self.secret_id):
5e2ba24e
MT
162 private_id += i
163
3d10b6d9
MT
164 if not private_id:
165 return "0" * 40
166
5e2ba24e 167 return hashlib.sha1(private_id).hexdigest()
31a30328 168
73014efe
MT
169 @property
170 def secret_id(self):
171 """
172 Read a "secret" ID from a file if available
173 or calculate it from the hardware.
174 """
175 if os.path.exists(SECRET_ID_FILE):
176 return read_from_file(SECRET_ID_FILE)
177
178 return hashlib.sha1(self._unique_id).hexdigest()
179
31a30328
MT
180 @property
181 def _unique_id(self):
182 """
183 This is a helper ID which is generated out of some hardware information
184 that is considered to be constant over a PC's lifetime.
185
186 None of the data here is ever sent to the server.
187 """
ec65e266 188 ids = []
3d10b6d9
MT
189
190 # Virtual machines (for example) and some boards have a UUID
191 # which is globally unique.
192 for file in ("product_uuid", "product_serial", "chassis_serial"):
7eadbfba 193 id = read_from_file(os.path.join(SYS_CLASS_DMI, file))
c0ef2eb7 194 ids.append(id)
3d10b6d9 195
7eadbfba
MT
196 # Sort out all bogous or invalid strings from the list.
197 _ids = []
198 for id in ids:
88b0ded5
MT
199 if id is None:
200 continue
201
7eadbfba 202 for i in INVALID_ID_STRINGS:
c0ef2eb7
MT
203 if i in id:
204 id = None
7eadbfba
MT
205 break
206
207 if id:
208 _ids.append(id)
209
210 ids = _ids
211
17dc2486
MT
212 # Use serial number from root disk (if available) and if
213 # no other ID was found, yet.
214 if not ids:
215 root_disk_serial = self.root_disk_serial
4ea06541 216 if root_disk_serial and not root_disk_serial.startswith("QM000"):
17dc2486 217 ids.append(root_disk_serial)
121f9f20 218
f05eac0d
MT
219 # As last resort, we use the UUID from pakfire.
220 if not ids:
221 id = read_from_file("/opt/pakfire/db/uuid")
222 ids.append(id)
223
ec65e266 224 return "#".join(ids)
31a30328 225
b45f0e98
MT
226 @property
227 def language(self):
b3ea53a7
MT
228 # Return "unknown" if settings file does not exist.
229 filename = "/var/ipfire/main/settings"
230 if not os.path.exists(filename):
231 return "unknown"
232
233 with open(filename, "r") as f:
b45f0e98
MT
234 for line in f.readlines():
235 key, val = line.split("=", 1)
236 if key=="LANGUAGE":
237 return val.strip()
238
239 @property
240 def release(self):
770c8841 241 return read_from_file("/etc/system-release")
b56bde73
SP
242
243 @property
244 def bios_vendor(self):
770c8841 245 return read_from_file("/sys/class/dmi/id/bios_vendor")
b56bde73 246
45b74ad5
MT
247 @property
248 def vendor(self):
249 ret = None
240fefea 250 for file in ("sys_vendor", "board_vendor", "chassis_vendor",):
45b74ad5
MT
251 ret = read_from_file(os.path.join(SYS_CLASS_DMI, file))
252 if ret:
253 break
254
255 return ret
256
257 @property
258 def model(self):
259 ret = None
84efbcb4 260 for file in ("product_name", "board_model", "chassis_model",):
45b74ad5
MT
261 ret = read_from_file(os.path.join(SYS_CLASS_DMI, file))
262 if ret:
263 break
264
265 return ret
266
b45f0e98
MT
267 @property
268 def memory(self):
269 with open("/proc/meminfo", "r") as f:
270 firstline = f.readline().strip()
2ced5413 271 return int(firstline.split()[1])
b45f0e98
MT
272
273 @property
0c5ef738 274 def kernel_release(self):
b45f0e98
MT
275 return os.uname()[2]
276
277 @property
278 def root_disk(self):
279 with open("/etc/mtab", "r") as f:
280 dev, mountpoint, rest = f.readline().split(" ",2)
281 if mountpoint == "/":
282 dev = dev[5:]
283 # cut off all digits at end of string
284 while dev[-1] in string.digits:
285 dev = dev[:-1]
286 return dev
287
288 @property
289 def root_size(self):
290 path="/sys/block/%s/size" %self.root_disk
291 if not os.path.exists(path):
292 return
293 with open(path, "r") as f:
294 return int(f.readline())*512/1024
3f70e7fd
MT
295
296 @property
297 def root_disk_serial(self):
e658b1a0
MT
298 serial = _fireinfo.get_harddisk_serial("/dev/%s" % self.root_disk)
299
300 if serial:
301 # Strip all spaces
302 return serial.strip()
303
b45f0e98
MT
304 def scan(self):
305 toscan = (("/sys/bus/pci/devices", device.PCIDevice),
306 ("/sys/bus/usb/devices", device.USBDevice))
307 for path, cls in toscan:
94700953
MT
308 if not os.path.exists(path):
309 continue
310
b45f0e98
MT
311 dirlist = os.listdir(path)
312 for dir in dirlist:
313 self.devices.append(cls(os.path.join(path, dir)))
d8614fc3
MT
314
315 @property
316 def virtual(self):
317 """
318 Say if the host is running in a virtual environment.
319 """
715ba5ac 320 return self.hypervisor.virtual
b45f0e98
MT
321
322
323
324if __name__ == "__main__":
325 s=System()
326 print s.arch
327 print s.language
328 print s.release
b56bde73 329 print s.bios_vendor
b45f0e98
MT
330 print s.memory
331 print s.kernel
332 print s.root_disk
333 print s.root_size
334 print "------------\n", s.devices, "\n------------\n"
65891720 335 print json.dumps(s.profile(), sort_keys=True, indent=4)