]> git.ipfire.org Git - pakfire.git/blame - python/pakfire/system.py
A new try to get the parallelism straight.
[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
28from i18n import _
29
30class System(object):
31 """
32 Class that grants access to several information about
33 the system this software is running on.
34 """
35 @property
36 def hostname(self):
a579a34f
MT
37 hn = socket.gethostname()
38
39 # If a host has got no domain part, we add one.
40 if not "." in hn:
41 hn = "%s.localdomain" % hn
42
43 return hn
c62d93f1 44
790a44cc
MT
45 @property
46 def native_arch(self):
47 """
48 Return the native architecture of the host we
49 are running on.
50 """
51 return os.uname()[4]
52
c62d93f1
MT
53 @property
54 def arch(self):
55 """
56 Return the architecture of the host we are running on.
57 """
b0b48ca3 58 if self.supported_arches and not self.native_arch in self.supported_arches:
790a44cc
MT
59 return self.supported_arches[0]
60
61 return self.native_arch
c62d93f1
MT
62
63 @property
64 def supported_arches(self):
65 """
66 Check what architectures can be built on this host.
67 """
68 host_can_build = {
69 # Host arch : Can build these arches.
70
71 # x86
72 "x86_64" : ["x86_64", "i686",],
73 "i686" : ["i686",],
74
75 # ARM
76 "armv5tel" : ["armv5tel",],
77 "armv5tejl" : ["armv5tel",],
f711a054 78 "armv6l" : ["armv5tel",],
790a44cc 79 "armv7l" : ["armv7hl", "armv5tel",],
c62d93f1
MT
80 "armv7hl" : ["armv7hl", "armv5tel",],
81 }
82
83 try:
790a44cc 84 return host_can_build[self.native_arch]
c62d93f1
MT
85 except KeyError:
86 return []
87
88 def host_supports_arch(self, arch):
89 """
90 Check if this host can build for the target architecture "arch".
91 """
92 return arch in self.supported_arches
93
94 @property
95 def cpu_count(self):
96 """
97 Count the number of CPU cores.
98 """
99 return multiprocessing.cpu_count()
100
101 @property
102 def cpu_model(self):
103 # Determine CPU model
104 cpuinfo = {}
105 with open("/proc/cpuinfo") as f:
106 for line in f.readlines():
107 # Break at an empty line, because all information after that
108 # is redundant.
109 if not line:
110 break
111
112 try:
113 key, value = line.split(":")
114 except:
115 pass # Skip invalid lines
116
117 key, value = key.strip(), value.strip()
118 cpuinfo[key] = value
119
120 ret = None
121 if self.arch.startswith("arm"):
122 try:
123 ret = "%(Hardware)s - %(Processor)s" % cpuinfo
124 except KeyError:
125 pass
126 else:
127 ret = cpuinfo.get("model name", None)
128
129 # Remove too many spaces.
130 ret = " ".join(ret.split())
131
132 return ret or _("Could not be determined")
133
134 @property
135 def memory(self):
136 # Determine memory size
137 memory = 0
138 with open("/proc/meminfo") as f:
139 line = f.readline()
140
141 try:
142 a, b, c = line.split()
143 except:
144 pass
145 else:
146 memory = int(b) * 1024
147
148 return memory
149
2ad59b6e
MT
150 def get_mountpoint(self, path):
151 return Mountpoint(path)
152
cae35096
MT
153 @property
154 def parallelism(self):
155 """
156 Calculates how many processes should be run
157 simulatneously when compiling.
158 """
cae35096
MT
159 # Count the number of online CPU cores.
160 cpucount = os.sysconf("SC_NPROCESSORS_CONF")
161
369ac3b3 162 return cpucount * 2
cae35096 163
c62d93f1
MT
164
165# Create an instance of this class to only keep it once in memory.
166system = System()
8fe16710
MT
167
168class Mountpoints(object):
2ad59b6e 169 def __init__(self, root="/"):
8fe16710
MT
170 self._mountpoints = []
171
172 # Scan for all mountpoints on the system.
173 self._scan(root)
174
175 def __iter__(self):
176 return iter(self._mountpoints)
177
178 def _scan(self, root):
179 # Get the real path of root.
180 root = os.path.realpath(root)
181
182 # If root is not equal to /, we are in a chroot and
183 # our root must be a mountpoint to count files.
184 if not root == "/":
2ad59b6e 185 mp = Mountpoint("/", root=root)
8fe16710
MT
186 self._mountpoints.append(mp)
187
188 f = open("/proc/mounts")
189
190 for line in f.readlines():
191 line = line.split()
192
193 # The mountpoint is the second argument.
194 mountpoint = line[1]
195
196 # Skip all mountpoints that are not in our root directory.
197 if not mountpoint.startswith(root):
198 continue
199
200 mountpoint = os.path.relpath(mountpoint, root)
201 if mountpoint == ".":
202 mountpoint = "/"
203 else:
204 mountpoint = os.path.join("/", mountpoint)
205
2ad59b6e 206 mp = Mountpoint(mountpoint, root=root)
8fe16710
MT
207
208 if not mp in self._mountpoints:
209 self._mountpoints.append(mp)
210
211 f.close()
212
213 # Sort all mountpoints for better searching.
214 self._mountpoints.sort()
215
216 def add_pkg(self, pkg):
217 for file in pkg.filelist:
218 self.add(file)
219
220 def rem_pkg(self, pkg):
221 for file in pkg.filelist:
222 self.rem(file)
223
224 def add(self, file):
225 for mp in reversed(self._mountpoints):
226 # Check if the file is located on this mountpoint.
227 if not file.name.startswith(mp.path):
228 continue
229
230 # Add file to this mountpoint.
231 mp.add(file)
232 break
233
234 def rem(self, file):
235 for mp in reversed(self._mountpoints):
236 # Check if the file is located on this mountpoint.
237 if not file.name.startswith(mp.path):
238 continue
239
240 # Remove file from this mountpoint.
241 mp.rem(file)
242 break
243
244
245class Mountpoint(object):
2ad59b6e 246 def __init__(self, path, root="/"):
8fe16710
MT
247 self.path = path
248 self.root = root
249
250 # Cache the statvfs call of the mountpoint.
251 self.__stat = None
252
253 # Save the amount of data that is used or freed.
254 self.disk_usage = 0
255
256 def __cmp__(self, other):
257 return cmp(self.fullpath, other.fullpath)
258
259 @property
260 def fullpath(self):
261 path = self.path
262 while path.startswith("/"):
263 path = path[1:]
264
265 return os.path.join(self.root, path)
266
267 @property
268 def stat(self):
269 if self.__stat is None:
270 # Find the next mountpoint, because we cannot
271 # statvfs any path in the FS.
272 path = os.path.realpath(self.fullpath)
273
274 # Walk to root until we find a mountpoint.
275 while not os.path.ismount(path):
276 path = os.path.dirname(path)
277
278 # See what we can get.
279 self.__stat = os.statvfs(path)
280
281 return self.__stat
282
283 @property
284 def free(self):
285 return self.stat.f_bavail * self.stat.f_bsize
286
287 @property
288 def space_needed(self):
289 if self.disk_usage > 0:
290 return self.disk_usage
291
292 return 0
293
294 @property
295 def space_left(self):
296 return self.free - self.space_needed
297
298 def add(self, file):
299 assert file.name.startswith(self.path)
300
301 # Round filesize to 4k blocks.
302 block_size = 4096
303
304 blocks = file.size // block_size
305 if file.size % block_size:
306 blocks += 1
307
308 self.disk_usage += blocks * block_size
309
310 def rem(self, file):
311 assert file.name.startswith(self.path)
312
313 self.disk_usage += file.size
c62d93f1
MT
314
315
316if __name__ == "__main__":
317 print "Hostname", system.hostname
318 print "Arch", system.arch
319 print "Supported arches", system.supported_arches
320
321 print "CPU Model", system.cpu_model
322 print "CPU count", system.cpu_count
323 print "Memory", system.memory