]> git.ipfire.org Git - people/stevee/pakfire.git/blame - python/pakfire/system.py
system: bogomips is spelled differently on ARM.
[people/stevee/pakfire.git] / python / pakfire / system.py
CommitLineData
8fe16710
MT
1#!/usr/bin/python
2###############################################################################
3# #
4# Pakfire - The IPFire package management system #
5# Copyright (C) 2011 Pakfire development team #
6# #
7# This program is free software: you can redistribute it and/or modify #
8# it under the terms of the GNU General Public License as published by #
9# the Free Software Foundation, either version 3 of the License, or #
10# (at your option) any later version. #
11# #
12# This program is distributed in the hope that it will be useful, #
13# but WITHOUT ANY WARRANTY; without even the implied warranty of #
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
15# GNU General Public License for more details. #
16# #
17# You should have received a copy of the GNU General Public License #
18# along with this program. If not, see <http://www.gnu.org/licenses/>. #
19# #
20###############################################################################
21
22from __future__ import division
23
c62d93f1 24import multiprocessing
8fe16710 25import os
c62d93f1
MT
26import socket
27
aa14071d
MT
28import distro
29
c62d93f1
MT
30from i18n import _
31
32class System(object):
33 """
34 Class that grants access to several information about
35 the system this software is running on.
36 """
37 @property
38 def hostname(self):
a579a34f
MT
39 hn = socket.gethostname()
40
41 # If a host has got no domain part, we add one.
42 if not "." in hn:
43 hn = "%s.localdomain" % hn
44
45 return hn
c62d93f1 46
aa14071d
MT
47 @property
48 def distro(self):
49 if not hasattr(self, "_distro"):
50 self._distro = distro.Distribution()
51
52 return self._distro
53
790a44cc
MT
54 @property
55 def native_arch(self):
56 """
57 Return the native architecture of the host we
58 are running on.
59 """
60 return os.uname()[4]
61
c62d93f1
MT
62 @property
63 def arch(self):
64 """
65 Return the architecture of the host we are running on.
66 """
b0b48ca3 67 if self.supported_arches and not self.native_arch in self.supported_arches:
790a44cc
MT
68 return self.supported_arches[0]
69
70 return self.native_arch
c62d93f1
MT
71
72 @property
73 def supported_arches(self):
74 """
75 Check what architectures can be built on this host.
76 """
77 host_can_build = {
78 # Host arch : Can build these arches.
79
80 # x86
81 "x86_64" : ["x86_64", "i686",],
82 "i686" : ["i686",],
83
84 # ARM
85 "armv5tel" : ["armv5tel",],
86 "armv5tejl" : ["armv5tel",],
f711a054 87 "armv6l" : ["armv5tel",],
790a44cc 88 "armv7l" : ["armv7hl", "armv5tel",],
c62d93f1
MT
89 "armv7hl" : ["armv7hl", "armv5tel",],
90 }
91
92 try:
790a44cc 93 return host_can_build[self.native_arch]
c62d93f1
MT
94 except KeyError:
95 return []
96
97 def host_supports_arch(self, arch):
98 """
99 Check if this host can build for the target architecture "arch".
100 """
101 return arch in self.supported_arches
102
103 @property
104 def cpu_count(self):
105 """
106 Count the number of CPU cores.
107 """
108 return multiprocessing.cpu_count()
109
aa14071d
MT
110 def parse_cpuinfo(self):
111 ret = {}
112
c62d93f1
MT
113 with open("/proc/cpuinfo") as f:
114 for line in f.readlines():
c62d93f1 115 try:
aa14071d
MT
116 # Split the lines by colons.
117 a, b = line.split(":")
118
119 # Strip whitespace.
120 a = a.strip()
121 b = b.strip()
122
123 ret[a] = b
c62d93f1 124 except:
aa14071d 125 pass
c62d93f1 126
aa14071d
MT
127 return ret
128
129 @property
130 def cpu_model(self):
131 cpuinfo = self.parse_cpuinfo()
c62d93f1
MT
132
133 ret = None
134 if self.arch.startswith("arm"):
135 try:
136 ret = "%(Hardware)s - %(Processor)s" % cpuinfo
137 except KeyError:
138 pass
139 else:
140 ret = cpuinfo.get("model name", None)
141
142 # Remove too many spaces.
143 ret = " ".join(ret.split())
144
145 return ret or _("Could not be determined")
146
147 @property
aa14071d
MT
148 def cpu_bogomips(self):
149 cpuinfo = self.parse_cpuinfo()
150
726c2908
MT
151 for key in ("bogomips", "BogoMIPS"):
152 bogomips = cpuinfo.get(key, None)
153
154 if bogomips is None:
155 continue
156
aa14071d 157 return float(bogomips) * self.cpu_count
aa14071d
MT
158
159 def get_loadavg(self):
160 return os.getloadavg()
161
162 @property
163 def loadavg1(self):
164 return self.get_loadavg()[0]
165
166 @property
167 def loadavg5(self):
168 return self.get_loadavg()[1]
169
170 @property
171 def loadavg15(self):
172 return self.get_loadavg()[2]
173
174 def has_overload(self):
175 """
176 Checks, if the load average is not too high.
177
178 On this is to be decided if a new job is taken.
179 """
180 # If there are more than 2 processes in the process queue per CPU
181 # core we will assume that the system has heavy load and to not request
182 # a new job.
183 return self.loadavg5 >= self.cpu_count * 2
184
185 def parse_meminfo(self):
186 ret = {}
187
c62d93f1 188 with open("/proc/meminfo") as f:
aa14071d
MT
189 for line in f.readlines():
190 try:
191 a, b, c = line.split()
c62d93f1 192
aa14071d
MT
193 a = a.strip()
194 a = a.replace(":", "")
195 b = int(b)
196
197 ret[a] = b * 1024
198 except:
199 pass
200
201 return ret
202
203 @property
204 def memory_total(self):
205 meminfo = self.parse_meminfo()
206
207 return meminfo.get("MemTotal", None)
208
209 # For compatibility
210 memory = memory_total
211
212 @property
213 def memory_free(self):
214 meminfo = self.parse_meminfo()
215
216 return meminfo.get("MemFree", None)
217
218 @property
219 def swap_total(self):
220 meminfo = self.parse_meminfo()
221
222 return meminfo.get("SwapTotal", None)
223
224 @property
225 def swap_free(self):
226 meminfo = self.parse_meminfo()
c62d93f1 227
aa14071d 228 return meminfo.get("SwapFree", None)
c62d93f1 229
2ad59b6e
MT
230 def get_mountpoint(self, path):
231 return Mountpoint(path)
232
cae35096
MT
233 @property
234 def parallelism(self):
235 """
236 Calculates how many processes should be run
237 simulatneously when compiling.
238 """
879abfa1 239 # Check how many processes would fit into the
f7a632ab
MT
240 # memory when each process takes up to 128MB.
241 multiplicator = self.memory / (128 * 1024 * 1024)
879abfa1
MT
242 multiplicator = round(multiplicator)
243
cae35096 244 # Count the number of online CPU cores.
f7a632ab
MT
245 cpucount = os.sysconf("SC_NPROCESSORS_CONF") * 2
246 cpucount += 1
cae35096 247
f7a632ab 248 return min(multiplicator, cpucount)
cae35096 249
c62d93f1
MT
250
251# Create an instance of this class to only keep it once in memory.
252system = System()
8fe16710
MT
253
254class Mountpoints(object):
2ad59b6e 255 def __init__(self, root="/"):
8fe16710
MT
256 self._mountpoints = []
257
258 # Scan for all mountpoints on the system.
259 self._scan(root)
260
261 def __iter__(self):
262 return iter(self._mountpoints)
263
264 def _scan(self, root):
265 # Get the real path of root.
266 root = os.path.realpath(root)
267
268 # If root is not equal to /, we are in a chroot and
269 # our root must be a mountpoint to count files.
270 if not root == "/":
2ad59b6e 271 mp = Mountpoint("/", root=root)
8fe16710
MT
272 self._mountpoints.append(mp)
273
274 f = open("/proc/mounts")
275
276 for line in f.readlines():
277 line = line.split()
278
279 # The mountpoint is the second argument.
280 mountpoint = line[1]
281
282 # Skip all mountpoints that are not in our root directory.
283 if not mountpoint.startswith(root):
284 continue
285
286 mountpoint = os.path.relpath(mountpoint, root)
287 if mountpoint == ".":
288 mountpoint = "/"
289 else:
290 mountpoint = os.path.join("/", mountpoint)
291
2ad59b6e 292 mp = Mountpoint(mountpoint, root=root)
8fe16710
MT
293
294 if not mp in self._mountpoints:
295 self._mountpoints.append(mp)
296
297 f.close()
298
299 # Sort all mountpoints for better searching.
300 self._mountpoints.sort()
301
302 def add_pkg(self, pkg):
303 for file in pkg.filelist:
304 self.add(file)
305
306 def rem_pkg(self, pkg):
307 for file in pkg.filelist:
308 self.rem(file)
309
310 def add(self, file):
311 for mp in reversed(self._mountpoints):
312 # Check if the file is located on this mountpoint.
313 if not file.name.startswith(mp.path):
314 continue
315
316 # Add file to this mountpoint.
317 mp.add(file)
318 break
319
320 def rem(self, file):
321 for mp in reversed(self._mountpoints):
322 # Check if the file is located on this mountpoint.
323 if not file.name.startswith(mp.path):
324 continue
325
326 # Remove file from this mountpoint.
327 mp.rem(file)
328 break
329
330
331class Mountpoint(object):
2ad59b6e 332 def __init__(self, path, root="/"):
8fe16710
MT
333 self.path = path
334 self.root = root
335
336 # Cache the statvfs call of the mountpoint.
337 self.__stat = None
338
339 # Save the amount of data that is used or freed.
340 self.disk_usage = 0
341
342 def __cmp__(self, other):
343 return cmp(self.fullpath, other.fullpath)
344
345 @property
346 def fullpath(self):
347 path = self.path
348 while path.startswith("/"):
349 path = path[1:]
350
351 return os.path.join(self.root, path)
352
353 @property
354 def stat(self):
355 if self.__stat is None:
356 # Find the next mountpoint, because we cannot
357 # statvfs any path in the FS.
358 path = os.path.realpath(self.fullpath)
359
360 # Walk to root until we find a mountpoint.
361 while not os.path.ismount(path):
362 path = os.path.dirname(path)
363
364 # See what we can get.
365 self.__stat = os.statvfs(path)
366
367 return self.__stat
368
369 @property
370 def free(self):
371 return self.stat.f_bavail * self.stat.f_bsize
372
373 @property
374 def space_needed(self):
375 if self.disk_usage > 0:
376 return self.disk_usage
377
378 return 0
379
380 @property
381 def space_left(self):
382 return self.free - self.space_needed
383
384 def add(self, file):
385 assert file.name.startswith(self.path)
386
387 # Round filesize to 4k blocks.
388 block_size = 4096
389
390 blocks = file.size // block_size
391 if file.size % block_size:
392 blocks += 1
393
394 self.disk_usage += blocks * block_size
395
396 def rem(self, file):
397 assert file.name.startswith(self.path)
398
399 self.disk_usage += file.size
c62d93f1
MT
400
401
402if __name__ == "__main__":
403 print "Hostname", system.hostname
404 print "Arch", system.arch
405 print "Supported arches", system.supported_arches
406
407 print "CPU Model", system.cpu_model
408 print "CPU count", system.cpu_count
409 print "Memory", system.memory