]> git.ipfire.org Git - pakfire.git/blame - python/pakfire/system.py
PBS: Send amount of free disk space to server.
[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):
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.
148system = System()
8fe16710
MT
149
150class 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
227class 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
298if __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