]>
Commit | Line | Data |
---|---|---|
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 | ||
22 | from __future__ import division | |
23 | ||
c62d93f1 | 24 | import multiprocessing |
8fe16710 | 25 | import os |
c62d93f1 MT |
26 | import socket |
27 | ||
28 | from i18n import _ | |
29 | ||
30 | class 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. | |
166 | system = System() | |
8fe16710 MT |
167 | |
168 | class 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 | ||
245 | class 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 | ||
316 | if __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 |