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