]>
Commit | Line | Data |
---|---|---|
b45f0e98 MT |
1 | #!/usr/bin/python |
2 | ||
31a30328 | 3 | import hashlib |
b45f0e98 MT |
4 | import json |
5 | import os | |
6 | import string | |
7 | ||
8 | import cpu | |
9 | import device | |
715ba5ac | 10 | import hypervisor |
b45f0e98 | 11 | |
0c5ef738 MT |
12 | PROFILE_VERSION = 0 |
13 | ||
45b74ad5 MT |
14 | SYS_CLASS_DMI = "/sys/class/dmi/id" |
15 | ||
32aeec73 MT |
16 | class 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 |
28 | def 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 | 41 | class 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 | ||
267 | if __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) |