blob: 71f9f04acfe6fe1c148604e45f59602e74439778 [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 *
mbligh4e75b0d2006-08-29 15:22:44 +00006import re,string
mblighf4c35322006-03-13 01:01:10 +00007
8def grep(pattern, file):
mbligh7bdbfbd2006-09-30 16:47:01 +00009 """
10 This is mainly to fix the return code inversion from grep
11 Also handles compressed files.
12
13 returns 1 if the pattern is present in the file, 0 if not.
14 """
15 command = 'grep "%s" > /dev/null' % pattern
16 ret = cat_file_to_cmd(file, command, ignorestatus = 1)
17 return not ret
mblighf4c35322006-03-13 01:01:10 +000018
19
mblighc86b0b42006-07-28 17:35:28 +000020def difflist(list1, list2):
21 """returns items in list2 that are not in list1"""
mblighf4c35322006-03-13 01:01:10 +000022 diff = [];
23 for x in list2:
24 if x not in list1:
25 diff.append(x)
26 return diff
27
mblighc86b0b42006-07-28 17:35:28 +000028
mbligh7bdbfbd2006-09-30 16:47:01 +000029def cat_file_to_cmd(file, command, ignorestatus = 0):
30 """
31 equivalent to 'cat file | command' but knows to use
32 zcat or bzcat if appropriate
33 """
mbligh712cd142006-04-22 18:57:50 +000034 if not os.path.isfile(file):
35 raise NameError, 'invalid file %s to cat to command %s' % file, command
mblighf4c35322006-03-13 01:01:10 +000036 if file.endswith('.bz2'):
mbligh7bdbfbd2006-09-30 16:47:01 +000037 return system('bzcat ' + file + ' | ' + command, ignorestatus)
mbligh3d914912006-04-22 17:37:19 +000038 elif (file.endswith('.gz') or file.endswith('.tgz')):
mbligh7bdbfbd2006-09-30 16:47:01 +000039 return system('zcat ' + file + ' | ' + command, ignorestatus)
mblighf4c35322006-03-13 01:01:10 +000040 else:
mbligh7bdbfbd2006-09-30 16:47:01 +000041 return system('cat ' + file + ' | ' + command, ignorestatus)
mblighf4c35322006-03-13 01:01:10 +000042
mblighc86b0b42006-07-28 17:35:28 +000043
mbligh712cd142006-04-22 18:57:50 +000044def extract_tarball_to_dir(tarball, dir):
mblighc86b0b42006-07-28 17:35:28 +000045 """
46 Extract a tarball to a specified directory name instead of whatever
47 the top level of a tarball is - useful for versioned directory names, etc
48 """
mbligh712cd142006-04-22 18:57:50 +000049 if os.path.exists(dir):
50 raise NameError, 'target %s already exists' % dir
51 pwd = os.getcwd()
52 os.chdir(os.path.dirname(os.path.abspath(dir)))
53 newdir = extract_tarball(tarball)
54 os.rename(newdir, dir)
55 os.chdir(pwd)
56
57
mbligh712cd142006-04-22 18:57:50 +000058def extract_tarball(tarball):
mblighc86b0b42006-07-28 17:35:28 +000059 """Returns the first found newly created directory by the tarball extraction"""
mbligh712cd142006-04-22 18:57:50 +000060 oldlist = os.listdir('.')
61 cat_file_to_cmd(tarball, 'tar xf -')
62 newlist = os.listdir('.')
63 newfiles = difflist(oldlist, newlist) # what is new dir ?
64 new_dir = None
65 for newfile in newfiles:
66 if (os.path.isdir(newfile)):
67 return newfile
68 raise NameError, "extracting tarball produced no dir"
69
mblighcdf02a42006-04-23 02:22:49 +000070
mbligh78e17022006-09-13 19:58:12 +000071def update_version(srcdir, new_version, install, *args, **dargs):
mblighc86b0b42006-07-28 17:35:28 +000072 """Make sure srcdir is version new_version
73
74 If not, delete it and install() the new version
75 """
mbligh72905562006-05-25 01:30:49 +000076 versionfile = srcdir + '/.version'
mblighff88e4e2006-05-25 19:34:52 +000077 if os.path.exists(srcdir):
78 if os.path.exists(versionfile):
79 old_version = pickle.load(open(versionfile, 'r'))
80 if (old_version != new_version):
81 system('rm -rf ' + srcdir)
82 else:
mbligh72905562006-05-25 01:30:49 +000083 system('rm -rf ' + srcdir)
84 if not os.path.exists(srcdir):
mbligh78e17022006-09-13 19:58:12 +000085 install(*args, **dargs)
mbligh72905562006-05-25 01:30:49 +000086 if os.path.exists(srcdir):
87 pickle.dump(new_version, open(versionfile, 'w'))
88
89
mblighea30c8a2006-04-22 22:24:25 +000090def is_url(path):
mblighc86b0b42006-07-28 17:35:28 +000091 """true if path is a url
92 """
93 # should cope with other url types here, but we only handle http and ftp
mblighea30c8a2006-04-22 22:24:25 +000094 if (path.startswith('http://')) or (path.startswith('ftp://')):
mblighea30c8a2006-04-22 22:24:25 +000095 return 1
96 return 0
97
mblighcdf02a42006-04-23 02:22:49 +000098
mblighf4c35322006-03-13 01:01:10 +000099def get_file(src, dest):
mblighc86b0b42006-07-28 17:35:28 +0000100 """get a file, either from url or local"""
mbligh31186612006-04-22 21:55:56 +0000101 if (src == dest): # no-op here allows clean overrides in tests
102 return
mblighea30c8a2006-04-22 22:24:25 +0000103 if (is_url(src)):
mblighf4c35322006-03-13 01:01:10 +0000104 print 'PWD: ' + os.getcwd()
105 print 'Fetching \n\t', src, '\n\t->', dest
106 try:
107 urllib.urlretrieve(src, dest)
108 except IOError:
109 sys.stderr.write("Unable to retrieve %s (to %s)\n" % (src, dest))
110 sys.exit(1)
111 return dest
mblighf4c35322006-03-13 01:01:10 +0000112 shutil.copyfile(src, dest)
113 return dest
114
mblighea30c8a2006-04-22 22:24:25 +0000115
mbligh82641862006-04-23 06:21:36 +0000116def unmap_url(srcdir, src, destdir = '.'):
mblighea30c8a2006-04-22 22:24:25 +0000117 if is_url(src):
118 dest = destdir + '/' + os.path.basename(src)
119 get_file(src, dest)
120 return dest
mbligh82641862006-04-23 06:21:36 +0000121 else:
122 return srcdir + '/' + src
mblighea30c8a2006-04-22 22:24:25 +0000123
124
mblighf4c35322006-03-13 01:01:10 +0000125def basename(path):
126 i = path.rfind('/');
127 return path[i+1:]
128
129
130def force_copy(src, dest):
mblighc86b0b42006-07-28 17:35:28 +0000131 """Replace dest with a new copy of src, even if it exists"""
mblighf4c35322006-03-13 01:01:10 +0000132 if os.path.isfile(dest):
133 os.remove(dest)
134 return shutil.copyfile(src, dest)
135
136
mblighcdf02a42006-04-23 02:22:49 +0000137def file_contains_pattern(file, pattern):
mblighc86b0b42006-07-28 17:35:28 +0000138 """Return true if file contains the specified egrep pattern"""
mblighcdf02a42006-04-23 02:22:49 +0000139 if not os.path.isfile(file):
140 raise NameError, 'file %s does not exist' % file
141 return not system('egrep -q ' + pattern + ' ' + file, ignorestatus = 1)
142
143
144def list_grep(list, pattern):
mblighc86b0b42006-07-28 17:35:28 +0000145 """True if any item in list matches the specified pattern."""
mblighcdf02a42006-04-23 02:22:49 +0000146 compiled = re.compile(pattern)
147 for line in list:
148 match = compiled.search(line)
149 if (match):
150 return 1
151 return 0
152
153
mblighf49d5cf2006-04-23 02:24:42 +0000154def get_vmlinux():
mblighc86b0b42006-07-28 17:35:28 +0000155 """Return the full path to vmlinux
156
157 Ahem. This is crap. Pray harder. Bad Martin.
158 """
mbligh67b5ece2006-04-23 02:53:12 +0000159 vmlinux = '/boot/vmlinux'
160 if not os.path.isfile(vmlinux):
161 raise NameError, 'Cannot find vmlinux'
162 return vmlinux
mblighf49d5cf2006-04-23 02:24:42 +0000163
164
165def get_systemmap():
mblighc86b0b42006-07-28 17:35:28 +0000166 """Return the full path to System.map
167
168 Ahem. This is crap. Pray harder. Bad Martin.
169 """
mbligh67b5ece2006-04-23 02:53:12 +0000170 map = '/boot/System.map'
171 if not os.path.isfile(map):
172 raise NameError, 'Cannot find System.map'
173 return map
174
175
176def get_modules_dir():
mblighc86b0b42006-07-28 17:35:28 +0000177 """Return the modules dir for the running kernel version"""
mbligh67b5ece2006-04-23 02:53:12 +0000178 kernel_version = system_output('uname -r')
179 return '/lib/modules/%s/kernel' % kernel_version
mblighf49d5cf2006-04-23 02:24:42 +0000180
181
mbligh5970cf02006-08-06 15:39:22 +0000182def get_cpu_arch():
mblighc86b0b42006-07-28 17:35:28 +0000183 """Work out which CPU architecture we're running on"""
mblighcdf02a42006-04-23 02:22:49 +0000184 f = open('/proc/cpuinfo', 'r')
185 cpuinfo = f.readlines()
186 f.close()
187 if list_grep(cpuinfo, '^cpu.*(RS64|POWER3|Broadband Engine)'):
188 return 'power'
189 elif list_grep(cpuinfo, '^cpu.*POWER4'):
190 return 'power4'
191 elif list_grep(cpuinfo, '^cpu.*POWER5'):
192 return 'power5'
193 elif list_grep(cpuinfo, '^cpu.*POWER6'):
194 return 'power6'
195 elif list_grep(cpuinfo, '^cpu.*PPC970'):
196 return 'power970'
197 elif list_grep(cpuinfo, 'Opteron'):
198 return 'x86_64'
199 elif list_grep(cpuinfo, 'GenuineIntel') and list_grep(cpuinfo, '48 bits virtual'):
mblighf4c35322006-03-13 01:01:10 +0000200 return 'x86_64'
201 else:
202 return 'i386'
203
204
mbligh5970cf02006-08-06 15:39:22 +0000205def get_kernel_arch():
206 """Work out the current kernel architecture (as a kernel arch)"""
207 arch = os.popen('uname -m').read().rstrip()
208 if ((arch == 'i586') or (arch == 'i686')):
209 return 'i386'
mblighcdf02a42006-04-23 02:22:49 +0000210 else:
211 return arch
212
213
mblighf4c35322006-03-13 01:01:10 +0000214def kernelexpand(kernel):
215 # if not (kernel.startswith('http://') or kernel.startswith('ftp://') or os.path.isfile(kernel)):
mbligh5629af02006-09-15 01:54:23 +0000216 if kernel.find('/') < 0: # contains no path.
mbligh534015f2006-09-15 03:28:56 +0000217 autodir = os.environ['AUTODIR']
218 kernelexpand = os.path.join(autodir, 'tools/kernelexpand')
mbligh5629af02006-09-15 01:54:23 +0000219 w, r = os.popen2(kernelexpand + ' ' + kernel)
mblighf4c35322006-03-13 01:01:10 +0000220
221 kernel = r.readline().strip()
222 r.close()
223 w.close()
mbligh534015f2006-09-15 03:28:56 +0000224 return kernel.split()
mblighf4c35322006-03-13 01:01:10 +0000225
226
227def count_cpus():
mblighc86b0b42006-07-28 17:35:28 +0000228 """number of CPUs in the local machine according to /proc/cpuinfo"""
mblighf4c35322006-03-13 01:01:10 +0000229 f = file('/proc/cpuinfo', 'r')
230 cpus = 0
231 for line in f.readlines():
232 if line.startswith('processor'):
233 cpus += 1
234 return cpus
235
mbligha975fb62006-04-22 19:56:25 +0000236def system(cmd, ignorestatus = 0):
mblighc86b0b42006-07-28 17:35:28 +0000237 """os.system replacement
238
239 We have our own definition of system here, as the stock os.system doesn't
240 correctly handle sigpipe
241 (ie things like "yes | head" will hang because yes doesn't get the SIGPIPE).
242
243 Also the stock os.system didn't raise errors based on exit status, this
244 version does unless you explicitly specify ignorestatus=1
245 """
mblighf4c35322006-03-13 01:01:10 +0000246 signal.signal(signal.SIGPIPE, signal.SIG_DFL)
247 try:
apwbc2867d2006-04-06 18:21:16 +0000248 status = os.system(cmd)
mblighf4c35322006-03-13 01:01:10 +0000249 finally:
250 signal.signal(signal.SIGPIPE, signal.SIG_IGN)
mbligha975fb62006-04-22 19:56:25 +0000251
mbligh67b5ece2006-04-23 02:53:12 +0000252 if ((status != 0) and not ignorestatus):
apwbc2867d2006-04-06 18:21:16 +0000253 raise CmdError(cmd, status)
mbligha975fb62006-04-22 19:56:25 +0000254 return status
mbligh3d914912006-04-22 17:37:19 +0000255
256
mbligh67b5ece2006-04-23 02:53:12 +0000257def system_output(command, ignorestatus = 0):
mblighc86b0b42006-07-28 17:35:28 +0000258 """Run command and return its output
259
260 ignorestatus
261 whether to raise a CmdError if command has a nonzero exit status
262 """
mbligh67b5ece2006-04-23 02:53:12 +0000263 (result, data) = commands.getstatusoutput(command)
264 if ((result != 0) and not ignorestatus):
265 raise CmdError, 'command failed: ' + command
266 return data
267
268
mblighf4c35322006-03-13 01:01:10 +0000269def where_art_thy_filehandles():
mblighc86b0b42006-07-28 17:35:28 +0000270 """Dump the current list of filehandles"""
mblighf4c35322006-03-13 01:01:10 +0000271 os.system("ls -l /proc/%d/fd >> /dev/tty" % os.getpid())
272
273
274def print_to_tty(string):
mblighc86b0b42006-07-28 17:35:28 +0000275 """Output string straight to the tty"""
mblighf4c35322006-03-13 01:01:10 +0000276 os.system("echo " + string + " >> /dev/tty")
277
278
mblighb8a14e32006-05-06 00:17:35 +0000279def dump_object(object):
mblighc86b0b42006-07-28 17:35:28 +0000280 """Dump an object's attributes and methods
281
282 kind of like dir()
283 """
mblighb8a14e32006-05-06 00:17:35 +0000284 for item in object.__dict__.iteritems():
285 print item
286 try:
287 (key,value) = item
288 dump_object(value)
289 except:
290 continue
291
292
mbligh4b089662006-06-14 22:34:58 +0000293def environ(env_key):
mblighc86b0b42006-07-28 17:35:28 +0000294 """return the requested environment variable, or '' if unset"""
mbligh4b089662006-06-14 22:34:58 +0000295 if (os.environ.has_key(env_key)):
296 return os.environ(env_key)
297 else:
298 return ''
299
300
301def prepend_path(newpath, oldpath):
mblighc86b0b42006-07-28 17:35:28 +0000302 """prepend newpath to oldpath"""
mbligh4b089662006-06-14 22:34:58 +0000303 if (oldpath):
304 return newpath + ':' + oldpath
305 else:
306 return newpath
307
308
309def append_path(oldpath, newpath):
mblighc86b0b42006-07-28 17:35:28 +0000310 """append newpath to oldpath"""
mbligh4b089662006-06-14 22:34:58 +0000311 if (oldpath):
312 return oldpath + ':' + newpath
313 else:
314 return newpath
315
316
mbligh4e75b0d2006-08-29 15:22:44 +0000317def avgtime_print(dir):
318 """ Calculate some benchmarking statistics.
319 Input is a directory containing a file called 'time'.
320 File contains one-per-line results of /usr/bin/time.
321 Output is average Elapsed, User, and System time in seconds,
322 and average CPU percentage.
323 """
324 f = open(dir + "/time")
325 user = system = elapsed = cpu = count = 0
326 r = re.compile('([\d\.]*)user ([\d\.]*)system (\d*):([\d\.]*)elapsed (\d*)%CPU')
327 for line in f.readlines():
328 try:
329 s = r.match(line);
mblighe1ee2672006-08-29 16:27:15 +0000330 user += float(s.group(1))
331 system += float(s.group(2))
332 elapsed += (float(s.group(3)) * 60) + float(s.group(4))
333 cpu += float(s.group(5))
mbligh4e75b0d2006-08-29 15:22:44 +0000334 count += 1
335 except:
336 raise ValueError("badly formatted times")
337
338 f.close()
339 return "Elapsed: %0.2fs User: %0.2fs System: %0.2fs CPU: %0.0f%%" % \
340 (elapsed/count, user/count, system/count, cpu/count)
341
342
mblighf06db0f2006-09-30 17:08:43 +0000343def running_config():
344 """
345 Return path of config file of the currently running kernel
346 """
347 for config in ('/proc/config.gz', \
348 '/boot/config-%s' % system_output('uname -r') ):
349 if os.path.isfile(config):
350 return config
351 return None
352
353
mblighf4c35322006-03-13 01:01:10 +0000354class fd_stack:
mblighc86b0b42006-07-28 17:35:28 +0000355 """a stack of fd redirects
356
357 Redirects cause existing fd's to be pushed on the stack; restore()
358 causes the current set of redirects to be popped, restoring the previous
359 filehandle destinations.
360
361 Note that we need to redirect both the sys.stdout type descriptor
362 (which print, etc use) and the low level OS numbered descriptor
363 which os.system() etc use.
364 """
mblighf4c35322006-03-13 01:01:10 +0000365
366 def __init__(self, fd, filehandle):
367 self.fd = fd # eg 1
368 self.filehandle = filehandle # eg sys.stdout
369 self.stack = [(fd, filehandle)]
370
371
mbligh78e17022006-09-13 19:58:12 +0000372 def update_handle(self, new):
373 if (self.filehandle == sys.stdout):
374 sys.stdout = new
375 if (self.filehandle == sys.stderr):
376 sys.stderr = new
377 self.filehandle = new
378
mblighf4c35322006-03-13 01:01:10 +0000379 def redirect(self, filename):
mblighc86b0b42006-07-28 17:35:28 +0000380 """Redirect output to the specified file
381
382 Overwrites the previous contents, if any.
383 """
mbligh78e17022006-09-13 19:58:12 +0000384 self.filehandle.flush()
mblighf4c35322006-03-13 01:01:10 +0000385 fdcopy = os.dup(self.fd)
mblighd7341d52006-09-13 20:02:34 +0000386 self.stack.append( (fdcopy, self.filehandle, 0) )
mblighf4c35322006-03-13 01:01:10 +0000387 # self.filehandle = file(filename, 'w')
388 if (os.path.isfile(filename)):
389 newfd = os.open(filename, os.O_WRONLY)
390 else:
391 newfd = os.open(filename, os.O_WRONLY | os.O_CREAT)
392 os.dup2(newfd, self.fd)
393 os.close(newfd)
mbligh78e17022006-09-13 19:58:12 +0000394 self.update_handle(os.fdopen(self.fd, 'w'))
mblighf4c35322006-03-13 01:01:10 +0000395
396
397 def tee_redirect(self, filename):
mblighc86b0b42006-07-28 17:35:28 +0000398 """Tee output to the specified file
399
400 Overwrites the previous contents, if any.
401 """
mbligh78e17022006-09-13 19:58:12 +0000402 self.filehandle.flush()
mbligh51566342006-08-29 04:55:40 +0000403 #print_to_tty("tee_redirect to " + filename)
404 #where_art_thy_filehandles()
mblighf4c35322006-03-13 01:01:10 +0000405 fdcopy = os.dup(self.fd)
mblighf4c35322006-03-13 01:01:10 +0000406 r, w = os.pipe()
407 pid = os.fork()
408 if pid: # parent
409 os.close(r)
410 os.dup2(w, self.fd)
411 os.close(w)
412 else: # child
413 os.close(w)
414 os.dup2(r, 0)
mbligh78e17022006-09-13 19:58:12 +0000415 os.dup2(fdcopy, 1)
416 os.close(r)
417 os.close(fdcopy)
mblighf4c35322006-03-13 01:01:10 +0000418 os.execlp('tee', 'tee', filename)
mblighd7341d52006-09-13 20:02:34 +0000419 self.stack.append( (fdcopy, self.filehandle, pid) )
mbligh78e17022006-09-13 19:58:12 +0000420 self.update_handle(os.fdopen(self.fd, 'w'))
mbligh51566342006-08-29 04:55:40 +0000421 #where_art_thy_filehandles()
422 #print_to_tty("done tee_redirect to " + filename)
mblighf4c35322006-03-13 01:01:10 +0000423
424
425 def restore(self):
mblighc86b0b42006-07-28 17:35:28 +0000426 """unredirect one level"""
mbligh78e17022006-09-13 19:58:12 +0000427 self.filehandle.flush()
mblighcf315332006-04-02 19:58:30 +0000428 # print_to_tty("ENTERING RESTORE %d" % self.fd)
mblighf4c35322006-03-13 01:01:10 +0000429 # where_art_thy_filehandles()
mblighd7341d52006-09-13 20:02:34 +0000430 (old_fd, old_filehandle, pid) = self.stack.pop()
mblighf4c35322006-03-13 01:01:10 +0000431 # print_to_tty("old_fd %d" % old_fd)
432 # print_to_tty("self.fd %d" % self.fd)
433 self.filehandle.close() # seems to close old_fd as well.
mblighd7341d52006-09-13 20:02:34 +0000434 if pid:
435 os.waitpid(pid, 0)
mblighf4c35322006-03-13 01:01:10 +0000436 # where_art_thy_filehandles()
437 os.dup2(old_fd, self.fd)
438 # print_to_tty("CLOSING FD %d" % old_fd)
439 os.close(old_fd)
440 # where_art_thy_filehandles()
mbligh78e17022006-09-13 19:58:12 +0000441 self.update_handle(old_filehandle)
mblighf4c35322006-03-13 01:01:10 +0000442 # where_art_thy_filehandles()
443 # print_to_tty("EXIT RESTORE %d" % self.fd)