blob: 7ddd411e57e3b7175e455fada4f8212e8b2f15bb [file] [log] [blame]
mblighc86b0b42006-07-28 17:35:28 +00001"""Convenience functions for use by tests or whomever.
2"""
3
mblighff88e4e2006-05-25 19:34:52 +00004import os,os.path,shutil,urllib,sys,signal,commands,pickle
apwbc2867d2006-04-06 18:21:16 +00005from error import *
mblighcdf02a42006-04-23 02:22:49 +00006import re
mblighf4c35322006-03-13 01:01:10 +00007
8def grep(pattern, file):
mblighc86b0b42006-07-28 17:35:28 +00009 """This is mainly to fix the return code inversion from grep"""
mbligha975fb62006-04-22 19:56:25 +000010 return not system('grep "' + pattern + '" "' + file + '"')
mblighf4c35322006-03-13 01:01:10 +000011
12
mblighc86b0b42006-07-28 17:35:28 +000013def difflist(list1, list2):
14 """returns items in list2 that are not in list1"""
mblighf4c35322006-03-13 01:01:10 +000015 diff = [];
16 for x in list2:
17 if x not in list1:
18 diff.append(x)
19 return diff
20
mblighc86b0b42006-07-28 17:35:28 +000021
mblighf4c35322006-03-13 01:01:10 +000022def cat_file_to_cmd(file, command):
mblighc86b0b42006-07-28 17:35:28 +000023 """equivalent to 'cat file | command' but knows to use zcat or bzcat if appropriate"""
mbligh712cd142006-04-22 18:57:50 +000024 if not os.path.isfile(file):
25 raise NameError, 'invalid file %s to cat to command %s' % file, command
mblighf4c35322006-03-13 01:01:10 +000026 if file.endswith('.bz2'):
mbligha975fb62006-04-22 19:56:25 +000027 system('bzcat ' + file + ' | ' + command)
mbligh3d914912006-04-22 17:37:19 +000028 elif (file.endswith('.gz') or file.endswith('.tgz')):
mbligha975fb62006-04-22 19:56:25 +000029 system('zcat ' + file + ' | ' + command)
mblighf4c35322006-03-13 01:01:10 +000030 else:
mbligha975fb62006-04-22 19:56:25 +000031 system('cat ' + file + ' | ' + command)
mblighf4c35322006-03-13 01:01:10 +000032
mblighc86b0b42006-07-28 17:35:28 +000033
mbligh712cd142006-04-22 18:57:50 +000034def extract_tarball_to_dir(tarball, dir):
mblighc86b0b42006-07-28 17:35:28 +000035 """
36 Extract a tarball to a specified directory name instead of whatever
37 the top level of a tarball is - useful for versioned directory names, etc
38 """
mbligh712cd142006-04-22 18:57:50 +000039 if os.path.exists(dir):
40 raise NameError, 'target %s already exists' % dir
41 pwd = os.getcwd()
42 os.chdir(os.path.dirname(os.path.abspath(dir)))
43 newdir = extract_tarball(tarball)
44 os.rename(newdir, dir)
45 os.chdir(pwd)
46
47
mbligh712cd142006-04-22 18:57:50 +000048def extract_tarball(tarball):
mblighc86b0b42006-07-28 17:35:28 +000049 """Returns the first found newly created directory by the tarball extraction"""
mbligh712cd142006-04-22 18:57:50 +000050 oldlist = os.listdir('.')
51 cat_file_to_cmd(tarball, 'tar xf -')
52 newlist = os.listdir('.')
53 newfiles = difflist(oldlist, newlist) # what is new dir ?
54 new_dir = None
55 for newfile in newfiles:
56 if (os.path.isdir(newfile)):
57 return newfile
58 raise NameError, "extracting tarball produced no dir"
59
mblighcdf02a42006-04-23 02:22:49 +000060
mbligh72905562006-05-25 01:30:49 +000061def update_version(srcdir, new_version, install):
mblighc86b0b42006-07-28 17:35:28 +000062 """Make sure srcdir is version new_version
63
64 If not, delete it and install() the new version
65 """
mbligh72905562006-05-25 01:30:49 +000066 versionfile = srcdir + '/.version'
mblighff88e4e2006-05-25 19:34:52 +000067 if os.path.exists(srcdir):
68 if os.path.exists(versionfile):
69 old_version = pickle.load(open(versionfile, 'r'))
70 if (old_version != new_version):
71 system('rm -rf ' + srcdir)
72 else:
mbligh72905562006-05-25 01:30:49 +000073 system('rm -rf ' + srcdir)
74 if not os.path.exists(srcdir):
75 install()
76 if os.path.exists(srcdir):
77 pickle.dump(new_version, open(versionfile, 'w'))
78
79
mblighea30c8a2006-04-22 22:24:25 +000080def is_url(path):
mblighc86b0b42006-07-28 17:35:28 +000081 """true if path is a url
82 """
83 # should cope with other url types here, but we only handle http and ftp
mblighea30c8a2006-04-22 22:24:25 +000084 if (path.startswith('http://')) or (path.startswith('ftp://')):
mblighea30c8a2006-04-22 22:24:25 +000085 return 1
86 return 0
87
mblighcdf02a42006-04-23 02:22:49 +000088
mblighf4c35322006-03-13 01:01:10 +000089def get_file(src, dest):
mblighc86b0b42006-07-28 17:35:28 +000090 """get a file, either from url or local"""
mbligh31186612006-04-22 21:55:56 +000091 if (src == dest): # no-op here allows clean overrides in tests
92 return
mblighea30c8a2006-04-22 22:24:25 +000093 if (is_url(src)):
mblighf4c35322006-03-13 01:01:10 +000094 print 'PWD: ' + os.getcwd()
95 print 'Fetching \n\t', src, '\n\t->', dest
96 try:
97 urllib.urlretrieve(src, dest)
98 except IOError:
99 sys.stderr.write("Unable to retrieve %s (to %s)\n" % (src, dest))
100 sys.exit(1)
101 return dest
mblighf4c35322006-03-13 01:01:10 +0000102 shutil.copyfile(src, dest)
103 return dest
104
mblighea30c8a2006-04-22 22:24:25 +0000105
mbligh82641862006-04-23 06:21:36 +0000106def unmap_url(srcdir, src, destdir = '.'):
mblighea30c8a2006-04-22 22:24:25 +0000107 if is_url(src):
108 dest = destdir + '/' + os.path.basename(src)
109 get_file(src, dest)
110 return dest
mbligh82641862006-04-23 06:21:36 +0000111 else:
112 return srcdir + '/' + src
mblighea30c8a2006-04-22 22:24:25 +0000113
114
mblighf4c35322006-03-13 01:01:10 +0000115def basename(path):
116 i = path.rfind('/');
117 return path[i+1:]
118
119
120def force_copy(src, dest):
mblighc86b0b42006-07-28 17:35:28 +0000121 """Replace dest with a new copy of src, even if it exists"""
mblighf4c35322006-03-13 01:01:10 +0000122 if os.path.isfile(dest):
123 os.remove(dest)
124 return shutil.copyfile(src, dest)
125
126
mblighcdf02a42006-04-23 02:22:49 +0000127def file_contains_pattern(file, pattern):
mblighc86b0b42006-07-28 17:35:28 +0000128 """Return true if file contains the specified egrep pattern"""
mblighcdf02a42006-04-23 02:22:49 +0000129 if not os.path.isfile(file):
130 raise NameError, 'file %s does not exist' % file
131 return not system('egrep -q ' + pattern + ' ' + file, ignorestatus = 1)
132
133
134def list_grep(list, pattern):
mblighc86b0b42006-07-28 17:35:28 +0000135 """True if any item in list matches the specified pattern."""
mblighcdf02a42006-04-23 02:22:49 +0000136 compiled = re.compile(pattern)
137 for line in list:
138 match = compiled.search(line)
139 if (match):
140 return 1
141 return 0
142
143
mblighf49d5cf2006-04-23 02:24:42 +0000144def get_vmlinux():
mblighc86b0b42006-07-28 17:35:28 +0000145 """Return the full path to vmlinux
146
147 Ahem. This is crap. Pray harder. Bad Martin.
148 """
mbligh67b5ece2006-04-23 02:53:12 +0000149 vmlinux = '/boot/vmlinux'
150 if not os.path.isfile(vmlinux):
151 raise NameError, 'Cannot find vmlinux'
152 return vmlinux
mblighf49d5cf2006-04-23 02:24:42 +0000153
154
155def get_systemmap():
mblighc86b0b42006-07-28 17:35:28 +0000156 """Return the full path to System.map
157
158 Ahem. This is crap. Pray harder. Bad Martin.
159 """
mbligh67b5ece2006-04-23 02:53:12 +0000160 map = '/boot/System.map'
161 if not os.path.isfile(map):
162 raise NameError, 'Cannot find System.map'
163 return map
164
165
166def get_modules_dir():
mblighc86b0b42006-07-28 17:35:28 +0000167 """Return the modules dir for the running kernel version"""
mbligh67b5ece2006-04-23 02:53:12 +0000168 kernel_version = system_output('uname -r')
169 return '/lib/modules/%s/kernel' % kernel_version
mblighf49d5cf2006-04-23 02:24:42 +0000170
171
mbligh5970cf02006-08-06 15:39:22 +0000172def get_cpu_arch():
mblighc86b0b42006-07-28 17:35:28 +0000173 """Work out which CPU architecture we're running on"""
mblighcdf02a42006-04-23 02:22:49 +0000174 f = open('/proc/cpuinfo', 'r')
175 cpuinfo = f.readlines()
176 f.close()
177 if list_grep(cpuinfo, '^cpu.*(RS64|POWER3|Broadband Engine)'):
178 return 'power'
179 elif list_grep(cpuinfo, '^cpu.*POWER4'):
180 return 'power4'
181 elif list_grep(cpuinfo, '^cpu.*POWER5'):
182 return 'power5'
183 elif list_grep(cpuinfo, '^cpu.*POWER6'):
184 return 'power6'
185 elif list_grep(cpuinfo, '^cpu.*PPC970'):
186 return 'power970'
187 elif list_grep(cpuinfo, 'Opteron'):
188 return 'x86_64'
189 elif list_grep(cpuinfo, 'GenuineIntel') and list_grep(cpuinfo, '48 bits virtual'):
mblighf4c35322006-03-13 01:01:10 +0000190 return 'x86_64'
191 else:
192 return 'i386'
193
194
mbligh5970cf02006-08-06 15:39:22 +0000195def get_kernel_arch():
196 """Work out the current kernel architecture (as a kernel arch)"""
197 arch = os.popen('uname -m').read().rstrip()
198 if ((arch == 'i586') or (arch == 'i686')):
199 return 'i386'
mblighcdf02a42006-04-23 02:22:49 +0000200 else:
201 return arch
202
203
mblighf4c35322006-03-13 01:01:10 +0000204def kernelexpand(kernel):
205 # if not (kernel.startswith('http://') or kernel.startswith('ftp://') or os.path.isfile(kernel)):
206 if kernel.find('/'):
207 w, r = os.popen2('./kernelexpand ' + kernel)
208
209 kernel = r.readline().strip()
210 r.close()
211 w.close()
212 return kernel
213
214
215def count_cpus():
mblighc86b0b42006-07-28 17:35:28 +0000216 """number of CPUs in the local machine according to /proc/cpuinfo"""
mblighf4c35322006-03-13 01:01:10 +0000217 f = file('/proc/cpuinfo', 'r')
218 cpus = 0
219 for line in f.readlines():
220 if line.startswith('processor'):
221 cpus += 1
222 return cpus
223
mbligha975fb62006-04-22 19:56:25 +0000224def system(cmd, ignorestatus = 0):
mblighc86b0b42006-07-28 17:35:28 +0000225 """os.system replacement
226
227 We have our own definition of system here, as the stock os.system doesn't
228 correctly handle sigpipe
229 (ie things like "yes | head" will hang because yes doesn't get the SIGPIPE).
230
231 Also the stock os.system didn't raise errors based on exit status, this
232 version does unless you explicitly specify ignorestatus=1
233 """
mblighf4c35322006-03-13 01:01:10 +0000234 signal.signal(signal.SIGPIPE, signal.SIG_DFL)
235 try:
apwbc2867d2006-04-06 18:21:16 +0000236 status = os.system(cmd)
mblighf4c35322006-03-13 01:01:10 +0000237 finally:
238 signal.signal(signal.SIGPIPE, signal.SIG_IGN)
mbligha975fb62006-04-22 19:56:25 +0000239
mbligh67b5ece2006-04-23 02:53:12 +0000240 if ((status != 0) and not ignorestatus):
apwbc2867d2006-04-06 18:21:16 +0000241 raise CmdError(cmd, status)
mbligha975fb62006-04-22 19:56:25 +0000242 return status
mbligh3d914912006-04-22 17:37:19 +0000243
244
mbligh67b5ece2006-04-23 02:53:12 +0000245def system_output(command, ignorestatus = 0):
mblighc86b0b42006-07-28 17:35:28 +0000246 """Run command and return its output
247
248 ignorestatus
249 whether to raise a CmdError if command has a nonzero exit status
250 """
mbligh67b5ece2006-04-23 02:53:12 +0000251 (result, data) = commands.getstatusoutput(command)
252 if ((result != 0) and not ignorestatus):
253 raise CmdError, 'command failed: ' + command
254 return data
255
256
mblighf4c35322006-03-13 01:01:10 +0000257def where_art_thy_filehandles():
mblighc86b0b42006-07-28 17:35:28 +0000258 """Dump the current list of filehandles"""
mblighf4c35322006-03-13 01:01:10 +0000259 os.system("ls -l /proc/%d/fd >> /dev/tty" % os.getpid())
260
261
262def print_to_tty(string):
mblighc86b0b42006-07-28 17:35:28 +0000263 """Output string straight to the tty"""
mblighf4c35322006-03-13 01:01:10 +0000264 os.system("echo " + string + " >> /dev/tty")
265
266
mblighb8a14e32006-05-06 00:17:35 +0000267def dump_object(object):
mblighc86b0b42006-07-28 17:35:28 +0000268 """Dump an object's attributes and methods
269
270 kind of like dir()
271 """
mblighb8a14e32006-05-06 00:17:35 +0000272 for item in object.__dict__.iteritems():
273 print item
274 try:
275 (key,value) = item
276 dump_object(value)
277 except:
278 continue
279
280
mbligh4b089662006-06-14 22:34:58 +0000281def environ(env_key):
mblighc86b0b42006-07-28 17:35:28 +0000282 """return the requested environment variable, or '' if unset"""
mbligh4b089662006-06-14 22:34:58 +0000283 if (os.environ.has_key(env_key)):
284 return os.environ(env_key)
285 else:
286 return ''
287
288
289def prepend_path(newpath, oldpath):
mblighc86b0b42006-07-28 17:35:28 +0000290 """prepend newpath to oldpath"""
mbligh4b089662006-06-14 22:34:58 +0000291 if (oldpath):
292 return newpath + ':' + oldpath
293 else:
294 return newpath
295
296
297def append_path(oldpath, newpath):
mblighc86b0b42006-07-28 17:35:28 +0000298 """append newpath to oldpath"""
mbligh4b089662006-06-14 22:34:58 +0000299 if (oldpath):
300 return oldpath + ':' + newpath
301 else:
302 return newpath
303
304
mblighf4c35322006-03-13 01:01:10 +0000305class fd_stack:
mblighc86b0b42006-07-28 17:35:28 +0000306 """a stack of fd redirects
307
308 Redirects cause existing fd's to be pushed on the stack; restore()
309 causes the current set of redirects to be popped, restoring the previous
310 filehandle destinations.
311
312 Note that we need to redirect both the sys.stdout type descriptor
313 (which print, etc use) and the low level OS numbered descriptor
314 which os.system() etc use.
315 """
mblighf4c35322006-03-13 01:01:10 +0000316
317 def __init__(self, fd, filehandle):
318 self.fd = fd # eg 1
319 self.filehandle = filehandle # eg sys.stdout
320 self.stack = [(fd, filehandle)]
321
322
323 def redirect(self, filename):
mblighc86b0b42006-07-28 17:35:28 +0000324 """Redirect output to the specified file
325
326 Overwrites the previous contents, if any.
327 """
mblighf4c35322006-03-13 01:01:10 +0000328 fdcopy = os.dup(self.fd)
329 self.stack.append( (fdcopy, self.filehandle) )
330 # self.filehandle = file(filename, 'w')
331 if (os.path.isfile(filename)):
332 newfd = os.open(filename, os.O_WRONLY)
333 else:
334 newfd = os.open(filename, os.O_WRONLY | os.O_CREAT)
335 os.dup2(newfd, self.fd)
336 os.close(newfd)
337 self.filehandle = os.fdopen(self.fd, 'w')
338
339
340 def tee_redirect(self, filename):
mblighc86b0b42006-07-28 17:35:28 +0000341 """Tee output to the specified file
342
343 Overwrites the previous contents, if any.
344 """
mblighf4c35322006-03-13 01:01:10 +0000345 print_to_tty("tee_redirect to " + filename)
346 where_art_thy_filehandles()
347 fdcopy = os.dup(self.fd)
348 self.stack.append( (fdcopy, self.filehandle) )
349 r, w = os.pipe()
350 pid = os.fork()
351 if pid: # parent
352 os.close(r)
353 os.dup2(w, self.fd)
354 os.close(w)
355 else: # child
356 os.close(w)
357 os.dup2(r, 0)
358 os.dup2(2, 1)
359 os.execlp('tee', 'tee', filename)
360 self.filehandle = os.fdopen(self.fd, 'w')
361 where_art_thy_filehandles()
362 print_to_tty("done tee_redirect to " + filename)
363
364
365 def restore(self):
mblighc86b0b42006-07-28 17:35:28 +0000366 """unredirect one level"""
mblighcf315332006-04-02 19:58:30 +0000367 # print_to_tty("ENTERING RESTORE %d" % self.fd)
mblighf4c35322006-03-13 01:01:10 +0000368 # where_art_thy_filehandles()
369 (old_fd, old_filehandle) = self.stack.pop()
370 # print_to_tty("old_fd %d" % old_fd)
371 # print_to_tty("self.fd %d" % self.fd)
372 self.filehandle.close() # seems to close old_fd as well.
373 # where_art_thy_filehandles()
374 os.dup2(old_fd, self.fd)
375 # print_to_tty("CLOSING FD %d" % old_fd)
376 os.close(old_fd)
377 # where_art_thy_filehandles()
378 self.filehandle = old_filehandle
379 # where_art_thy_filehandles()
380 # print_to_tty("EXIT RESTORE %d" % self.fd)