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