]> git.ipfire.org Git - oddments/fireinfo.git/blame - fireinfo/system.py
Ignore any string with Chassis.
[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
128 # Actual profile data
129 "profile" : p,
130 }
b45f0e98
MT
131
132
133 @property
134 def arch(self):
135 return os.uname()[4]
136
137 @property
138 def public_id(self):
31a30328
MT
139 """
140 This returns a globally (hopefully) ID to identify the host
141 later (by request) in the database.
142 """
73014efe 143 public_id = self.secret_id
3d10b6d9
MT
144 if not public_id:
145 return "0" * 40
146
147 return hashlib.sha1(public_id).hexdigest()
148
b45f0e98
MT
149 @property
150 def private_id(self):
31a30328
MT
151 """
152 The private ID is built out of the _unique_id and used to
153 permit a host to do changes on the database.
154
155 No one could ever guess this without access to the host.
156 """
5e2ba24e 157 private_id = ""
73014efe 158 for i in reversed(self.secret_id):
5e2ba24e
MT
159 private_id += i
160
3d10b6d9
MT
161 if not private_id:
162 return "0" * 40
163
5e2ba24e 164 return hashlib.sha1(private_id).hexdigest()
31a30328 165
73014efe
MT
166 @property
167 def secret_id(self):
168 """
169 Read a "secret" ID from a file if available
170 or calculate it from the hardware.
171 """
172 if os.path.exists(SECRET_ID_FILE):
173 return read_from_file(SECRET_ID_FILE)
174
175 return hashlib.sha1(self._unique_id).hexdigest()
176
31a30328
MT
177 @property
178 def _unique_id(self):
179 """
180 This is a helper ID which is generated out of some hardware information
181 that is considered to be constant over a PC's lifetime.
182
183 None of the data here is ever sent to the server.
184 """
ec65e266 185 ids = []
3d10b6d9
MT
186
187 # Virtual machines (for example) and some boards have a UUID
188 # which is globally unique.
189 for file in ("product_uuid", "product_serial", "chassis_serial"):
7eadbfba 190 id = read_from_file(os.path.join(SYS_CLASS_DMI, file))
c0ef2eb7 191 ids.append(id)
3d10b6d9 192
3f70e7fd
MT
193 # Use serial number from root disk (if available)
194 root_disk_serial = self.root_disk_serial
195 if root_disk_serial:
196 ids.append(root_disk_serial)
197
7eadbfba
MT
198 # Sort out all bogous or invalid strings from the list.
199 _ids = []
200 for id in ids:
88b0ded5
MT
201 if id is None:
202 continue
203
7eadbfba 204 for i in INVALID_ID_STRINGS:
c0ef2eb7
MT
205 if i in id:
206 id = None
7eadbfba
MT
207 break
208
209 if id:
210 _ids.append(id)
211
212 ids = _ids
213
f05eac0d
MT
214 # As last resort, we use the UUID from pakfire.
215 if not ids:
216 id = read_from_file("/opt/pakfire/db/uuid")
217 ids.append(id)
218
ec65e266 219 return "#".join(ids)
31a30328 220
b45f0e98
MT
221 @property
222 def language(self):
b3ea53a7
MT
223 # Return "unknown" if settings file does not exist.
224 filename = "/var/ipfire/main/settings"
225 if not os.path.exists(filename):
226 return "unknown"
227
228 with open(filename, "r") as f:
b45f0e98
MT
229 for line in f.readlines():
230 key, val = line.split("=", 1)
231 if key=="LANGUAGE":
232 return val.strip()
233
234 @property
235 def release(self):
770c8841 236 return read_from_file("/etc/system-release")
b56bde73
SP
237
238 @property
239 def bios_vendor(self):
770c8841 240 return read_from_file("/sys/class/dmi/id/bios_vendor")
b56bde73 241
45b74ad5
MT
242 @property
243 def vendor(self):
244 ret = None
245 for file in ("chassis_vendor", "board_vendor", "sys_vendor",):
246 ret = read_from_file(os.path.join(SYS_CLASS_DMI, file))
247 if ret:
248 break
249
250 return ret
251
252 @property
253 def model(self):
254 ret = None
255 for file in ("chassis_model", "board_model", "product_name",):
256 ret = read_from_file(os.path.join(SYS_CLASS_DMI, file))
257 if ret:
258 break
259
260 return ret
261
b45f0e98
MT
262 @property
263 def memory(self):
264 with open("/proc/meminfo", "r") as f:
265 firstline = f.readline().strip()
2ced5413 266 return int(firstline.split()[1])
b45f0e98
MT
267
268 @property
0c5ef738 269 def kernel_release(self):
b45f0e98
MT
270 return os.uname()[2]
271
272 @property
273 def root_disk(self):
274 with open("/etc/mtab", "r") as f:
275 dev, mountpoint, rest = f.readline().split(" ",2)
276 if mountpoint == "/":
277 dev = dev[5:]
278 # cut off all digits at end of string
279 while dev[-1] in string.digits:
280 dev = dev[:-1]
281 return dev
282
283 @property
284 def root_size(self):
285 path="/sys/block/%s/size" %self.root_disk
286 if not os.path.exists(path):
287 return
288 with open(path, "r") as f:
289 return int(f.readline())*512/1024
3f70e7fd
MT
290
291 @property
292 def root_disk_serial(self):
e658b1a0
MT
293 serial = _fireinfo.get_harddisk_serial("/dev/%s" % self.root_disk)
294
295 if serial:
296 # Strip all spaces
297 return serial.strip()
298
b45f0e98
MT
299 def scan(self):
300 toscan = (("/sys/bus/pci/devices", device.PCIDevice),
301 ("/sys/bus/usb/devices", device.USBDevice))
302 for path, cls in toscan:
303 dirlist = os.listdir(path)
304 for dir in dirlist:
305 self.devices.append(cls(os.path.join(path, dir)))
d8614fc3
MT
306
307 @property
308 def virtual(self):
309 """
310 Say if the host is running in a virtual environment.
311 """
715ba5ac 312 return self.hypervisor.virtual
b45f0e98
MT
313
314
315
316if __name__ == "__main__":
317 s=System()
318 print s.arch
319 print s.language
320 print s.release
b56bde73 321 print s.bios_vendor
b45f0e98
MT
322 print s.memory
323 print s.kernel
324 print s.root_disk
325 print s.root_size
326 print "------------\n", s.devices, "\n------------\n"
65891720 327 print json.dumps(s.profile(), sort_keys=True, indent=4)