]> git.ipfire.org Git - pakfire.git/blame - python/pakfire/system.py
Update pakfire-daemon:
[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():
aa14071d
MT
115 # Only parse the first block.
116 if line == "\n":
c62d93f1
MT
117 break
118
119 try:
aa14071d
MT
120 # Split the lines by colons.
121 a, b = line.split(":")
122
123 # Strip whitespace.
124 a = a.strip()
125 b = b.strip()
126
127 ret[a] = b
c62d93f1 128 except:
aa14071d 129 pass
c62d93f1 130
aa14071d
MT
131 return ret
132
133 @property
134 def cpu_model(self):
135 cpuinfo = self.parse_cpuinfo()
c62d93f1
MT
136
137 ret = None
138 if self.arch.startswith("arm"):
139 try:
140 ret = "%(Hardware)s - %(Processor)s" % cpuinfo
141 except KeyError:
142 pass
143 else:
144 ret = cpuinfo.get("model name", None)
145
146 # Remove too many spaces.
147 ret = " ".join(ret.split())
148
149 return ret or _("Could not be determined")
150
151 @property
aa14071d
MT
152 def cpu_bogomips(self):
153 cpuinfo = self.parse_cpuinfo()
154
155 bogomips = cpuinfo.get("bogomips", None)
156 try:
157 return float(bogomips) * self.cpu_count
158 except:
159 pass
160
161 def get_loadavg(self):
162 return os.getloadavg()
163
164 @property
165 def loadavg1(self):
166 return self.get_loadavg()[0]
167
168 @property
169 def loadavg5(self):
170 return self.get_loadavg()[1]
171
172 @property
173 def loadavg15(self):
174 return self.get_loadavg()[2]
175
176 def has_overload(self):
177 """
178 Checks, if the load average is not too high.
179
180 On this is to be decided if a new job is taken.
181 """
182 # If there are more than 2 processes in the process queue per CPU
183 # core we will assume that the system has heavy load and to not request
184 # a new job.
185 return self.loadavg5 >= self.cpu_count * 2
186
187 def parse_meminfo(self):
188 ret = {}
189
c62d93f1 190 with open("/proc/meminfo") as f:
aa14071d
MT
191 for line in f.readlines():
192 try:
193 a, b, c = line.split()
c62d93f1 194
aa14071d
MT
195 a = a.strip()
196 a = a.replace(":", "")
197 b = int(b)
198
199 ret[a] = b * 1024
200 except:
201 pass
202
203 return ret
204
205 @property
206 def memory_total(self):
207 meminfo = self.parse_meminfo()
208
209 return meminfo.get("MemTotal", None)
210
211 # For compatibility
212 memory = memory_total
213
214 @property
215 def memory_free(self):
216 meminfo = self.parse_meminfo()
217
218 return meminfo.get("MemFree", None)
219
220 @property
221 def swap_total(self):
222 meminfo = self.parse_meminfo()
223
224 return meminfo.get("SwapTotal", None)
225
226 @property
227 def swap_free(self):
228 meminfo = self.parse_meminfo()
c62d93f1 229
aa14071d 230 return meminfo.get("SwapFree", None)
c62d93f1 231
2ad59b6e
MT
232 def get_mountpoint(self, path):
233 return Mountpoint(path)
234
cae35096
MT
235 @property
236 def parallelism(self):
237 """
238 Calculates how many processes should be run
239 simulatneously when compiling.
240 """
879abfa1 241 # Check how many processes would fit into the
f7a632ab
MT
242 # memory when each process takes up to 128MB.
243 multiplicator = self.memory / (128 * 1024 * 1024)
879abfa1
MT
244 multiplicator = round(multiplicator)
245
cae35096 246 # Count the number of online CPU cores.
f7a632ab
MT
247 cpucount = os.sysconf("SC_NPROCESSORS_CONF") * 2
248 cpucount += 1
cae35096 249
f7a632ab 250 return min(multiplicator, cpucount)
cae35096 251
c62d93f1
MT
252
253# Create an instance of this class to only keep it once in memory.
254system = System()
8fe16710
MT
255
256class Mountpoints(object):
2ad59b6e 257 def __init__(self, root="/"):
8fe16710
MT
258 self._mountpoints = []
259
260 # Scan for all mountpoints on the system.
261 self._scan(root)
262
263 def __iter__(self):
264 return iter(self._mountpoints)
265
266 def _scan(self, root):
267 # Get the real path of root.
268 root = os.path.realpath(root)
269
270 # If root is not equal to /, we are in a chroot and
271 # our root must be a mountpoint to count files.
272 if not root == "/":
2ad59b6e 273 mp = Mountpoint("/", root=root)
8fe16710
MT
274 self._mountpoints.append(mp)
275
276 f = open("/proc/mounts")
277
278 for line in f.readlines():
279 line = line.split()
280
281 # The mountpoint is the second argument.
282 mountpoint = line[1]
283
284 # Skip all mountpoints that are not in our root directory.
285 if not mountpoint.startswith(root):
286 continue
287
288 mountpoint = os.path.relpath(mountpoint, root)
289 if mountpoint == ".":
290 mountpoint = "/"
291 else:
292 mountpoint = os.path.join("/", mountpoint)
293
2ad59b6e 294 mp = Mountpoint(mountpoint, root=root)
8fe16710
MT
295
296 if not mp in self._mountpoints:
297 self._mountpoints.append(mp)
298
299 f.close()
300
301 # Sort all mountpoints for better searching.
302 self._mountpoints.sort()
303
304 def add_pkg(self, pkg):
305 for file in pkg.filelist:
306 self.add(file)
307
308 def rem_pkg(self, pkg):
309 for file in pkg.filelist:
310 self.rem(file)
311
312 def add(self, file):
313 for mp in reversed(self._mountpoints):
314 # Check if the file is located on this mountpoint.
315 if not file.name.startswith(mp.path):
316 continue
317
318 # Add file to this mountpoint.
319 mp.add(file)
320 break
321
322 def rem(self, file):
323 for mp in reversed(self._mountpoints):
324 # Check if the file is located on this mountpoint.
325 if not file.name.startswith(mp.path):
326 continue
327
328 # Remove file from this mountpoint.
329 mp.rem(file)
330 break
331
332
333class Mountpoint(object):
2ad59b6e 334 def __init__(self, path, root="/"):
8fe16710
MT
335 self.path = path
336 self.root = root
337
338 # Cache the statvfs call of the mountpoint.
339 self.__stat = None
340
341 # Save the amount of data that is used or freed.
342 self.disk_usage = 0
343
344 def __cmp__(self, other):
345 return cmp(self.fullpath, other.fullpath)
346
347 @property
348 def fullpath(self):
349 path = self.path
350 while path.startswith("/"):
351 path = path[1:]
352
353 return os.path.join(self.root, path)
354
355 @property
356 def stat(self):
357 if self.__stat is None:
358 # Find the next mountpoint, because we cannot
359 # statvfs any path in the FS.
360 path = os.path.realpath(self.fullpath)
361
362 # Walk to root until we find a mountpoint.
363 while not os.path.ismount(path):
364 path = os.path.dirname(path)
365
366 # See what we can get.
367 self.__stat = os.statvfs(path)
368
369 return self.__stat
370
371 @property
372 def free(self):
373 return self.stat.f_bavail * self.stat.f_bsize
374
375 @property
376 def space_needed(self):
377 if self.disk_usage > 0:
378 return self.disk_usage
379
380 return 0
381
382 @property
383 def space_left(self):
384 return self.free - self.space_needed
385
386 def add(self, file):
387 assert file.name.startswith(self.path)
388
389 # Round filesize to 4k blocks.
390 block_size = 4096
391
392 blocks = file.size // block_size
393 if file.size % block_size:
394 blocks += 1
395
396 self.disk_usage += blocks * block_size
397
398 def rem(self, file):
399 assert file.name.startswith(self.path)
400
401 self.disk_usage += file.size
c62d93f1
MT
402
403
404if __name__ == "__main__":
405 print "Hostname", system.hostname
406 print "Arch", system.arch
407 print "Supported arches", system.supported_arches
408
409 print "CPU Model", system.cpu_model
410 print "CPU count", system.cpu_count
411 print "Memory", system.memory