blob: 79698e005a1d2906d7469838c872ee42b3e4c75f [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):
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
mbligh78e17022006-09-13 19:58:12 +000061def update_version(srcdir, new_version, install, *args, **dargs):
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):
mbligh78e17022006-09-13 19:58:12 +000075 install(*args, **dargs)
mbligh72905562006-05-25 01:30:49 +000076 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)):
mbligh5629af02006-09-15 01:54:23 +0000206 if kernel.find('/') < 0: # contains no path.
mbligh534015f2006-09-15 03:28:56 +0000207 autodir = os.environ['AUTODIR']
208 kernelexpand = os.path.join(autodir, 'tools/kernelexpand')
mbligh5629af02006-09-15 01:54:23 +0000209 w, r = os.popen2(kernelexpand + ' ' + kernel)
mblighf4c35322006-03-13 01:01:10 +0000210
211 kernel = r.readline().strip()
212 r.close()
213 w.close()
mbligh534015f2006-09-15 03:28:56 +0000214 return kernel.split()
mblighf4c35322006-03-13 01:01:10 +0000215
216
217def count_cpus():
mblighc86b0b42006-07-28 17:35:28 +0000218 """number of CPUs in the local machine according to /proc/cpuinfo"""
mblighf4c35322006-03-13 01:01:10 +0000219 f = file('/proc/cpuinfo', 'r')
220 cpus = 0
221 for line in f.readlines():
222 if line.startswith('processor'):
223 cpus += 1
224 return cpus
225
mbligha975fb62006-04-22 19:56:25 +0000226def system(cmd, ignorestatus = 0):
mblighc86b0b42006-07-28 17:35:28 +0000227 """os.system replacement
228
229 We have our own definition of system here, as the stock os.system doesn't
230 correctly handle sigpipe
231 (ie things like "yes | head" will hang because yes doesn't get the SIGPIPE).
232
233 Also the stock os.system didn't raise errors based on exit status, this
234 version does unless you explicitly specify ignorestatus=1
235 """
mblighf4c35322006-03-13 01:01:10 +0000236 signal.signal(signal.SIGPIPE, signal.SIG_DFL)
237 try:
apwbc2867d2006-04-06 18:21:16 +0000238 status = os.system(cmd)
mblighf4c35322006-03-13 01:01:10 +0000239 finally:
240 signal.signal(signal.SIGPIPE, signal.SIG_IGN)
mbligha975fb62006-04-22 19:56:25 +0000241
mbligh67b5ece2006-04-23 02:53:12 +0000242 if ((status != 0) and not ignorestatus):
apwbc2867d2006-04-06 18:21:16 +0000243 raise CmdError(cmd, status)
mbligha975fb62006-04-22 19:56:25 +0000244 return status
mbligh3d914912006-04-22 17:37:19 +0000245
246
mbligh67b5ece2006-04-23 02:53:12 +0000247def system_output(command, ignorestatus = 0):
mblighc86b0b42006-07-28 17:35:28 +0000248 """Run command and return its output
249
250 ignorestatus
251 whether to raise a CmdError if command has a nonzero exit status
252 """
mbligh67b5ece2006-04-23 02:53:12 +0000253 (result, data) = commands.getstatusoutput(command)
254 if ((result != 0) and not ignorestatus):
255 raise CmdError, 'command failed: ' + command
256 return data
257
258
mblighf4c35322006-03-13 01:01:10 +0000259def where_art_thy_filehandles():
mblighc86b0b42006-07-28 17:35:28 +0000260 """Dump the current list of filehandles"""
mblighf4c35322006-03-13 01:01:10 +0000261 os.system("ls -l /proc/%d/fd >> /dev/tty" % os.getpid())
262
263
264def print_to_tty(string):
mblighc86b0b42006-07-28 17:35:28 +0000265 """Output string straight to the tty"""
mblighf4c35322006-03-13 01:01:10 +0000266 os.system("echo " + string + " >> /dev/tty")
267
268
mblighb8a14e32006-05-06 00:17:35 +0000269def dump_object(object):
mblighc86b0b42006-07-28 17:35:28 +0000270 """Dump an object's attributes and methods
271
272 kind of like dir()
273 """
mblighb8a14e32006-05-06 00:17:35 +0000274 for item in object.__dict__.iteritems():
275 print item
276 try:
277 (key,value) = item
278 dump_object(value)
279 except:
280 continue
281
282
mbligh4b089662006-06-14 22:34:58 +0000283def environ(env_key):
mblighc86b0b42006-07-28 17:35:28 +0000284 """return the requested environment variable, or '' if unset"""
mbligh4b089662006-06-14 22:34:58 +0000285 if (os.environ.has_key(env_key)):
286 return os.environ(env_key)
287 else:
288 return ''
289
290
291def prepend_path(newpath, oldpath):
mblighc86b0b42006-07-28 17:35:28 +0000292 """prepend newpath to oldpath"""
mbligh4b089662006-06-14 22:34:58 +0000293 if (oldpath):
294 return newpath + ':' + oldpath
295 else:
296 return newpath
297
298
299def append_path(oldpath, newpath):
mblighc86b0b42006-07-28 17:35:28 +0000300 """append newpath to oldpath"""
mbligh4b089662006-06-14 22:34:58 +0000301 if (oldpath):
302 return oldpath + ':' + newpath
303 else:
304 return newpath
305
306
mbligh4e75b0d2006-08-29 15:22:44 +0000307def avgtime_print(dir):
308 """ Calculate some benchmarking statistics.
309 Input is a directory containing a file called 'time'.
310 File contains one-per-line results of /usr/bin/time.
311 Output is average Elapsed, User, and System time in seconds,
312 and average CPU percentage.
313 """
314 f = open(dir + "/time")
315 user = system = elapsed = cpu = count = 0
316 r = re.compile('([\d\.]*)user ([\d\.]*)system (\d*):([\d\.]*)elapsed (\d*)%CPU')
317 for line in f.readlines():
318 try:
319 s = r.match(line);
mblighe1ee2672006-08-29 16:27:15 +0000320 user += float(s.group(1))
321 system += float(s.group(2))
322 elapsed += (float(s.group(3)) * 60) + float(s.group(4))
323 cpu += float(s.group(5))
mbligh4e75b0d2006-08-29 15:22:44 +0000324 count += 1
325 except:
326 raise ValueError("badly formatted times")
327
328 f.close()
329 return "Elapsed: %0.2fs User: %0.2fs System: %0.2fs CPU: %0.0f%%" % \
330 (elapsed/count, user/count, system/count, cpu/count)
331
332
mblighf4c35322006-03-13 01:01:10 +0000333class fd_stack:
mblighc86b0b42006-07-28 17:35:28 +0000334 """a stack of fd redirects
335
336 Redirects cause existing fd's to be pushed on the stack; restore()
337 causes the current set of redirects to be popped, restoring the previous
338 filehandle destinations.
339
340 Note that we need to redirect both the sys.stdout type descriptor
341 (which print, etc use) and the low level OS numbered descriptor
342 which os.system() etc use.
343 """
mblighf4c35322006-03-13 01:01:10 +0000344
345 def __init__(self, fd, filehandle):
346 self.fd = fd # eg 1
347 self.filehandle = filehandle # eg sys.stdout
348 self.stack = [(fd, filehandle)]
349
350
mbligh78e17022006-09-13 19:58:12 +0000351 def update_handle(self, new):
352 if (self.filehandle == sys.stdout):
353 sys.stdout = new
354 if (self.filehandle == sys.stderr):
355 sys.stderr = new
356 self.filehandle = new
357
mblighf4c35322006-03-13 01:01:10 +0000358 def redirect(self, filename):
mblighc86b0b42006-07-28 17:35:28 +0000359 """Redirect output to the specified file
360
361 Overwrites the previous contents, if any.
362 """
mbligh78e17022006-09-13 19:58:12 +0000363 self.filehandle.flush()
mblighf4c35322006-03-13 01:01:10 +0000364 fdcopy = os.dup(self.fd)
mblighd7341d52006-09-13 20:02:34 +0000365 self.stack.append( (fdcopy, self.filehandle, 0) )
mblighf4c35322006-03-13 01:01:10 +0000366 # self.filehandle = file(filename, 'w')
367 if (os.path.isfile(filename)):
368 newfd = os.open(filename, os.O_WRONLY)
369 else:
370 newfd = os.open(filename, os.O_WRONLY | os.O_CREAT)
371 os.dup2(newfd, self.fd)
372 os.close(newfd)
mbligh78e17022006-09-13 19:58:12 +0000373 self.update_handle(os.fdopen(self.fd, 'w'))
mblighf4c35322006-03-13 01:01:10 +0000374
375
376 def tee_redirect(self, filename):
mblighc86b0b42006-07-28 17:35:28 +0000377 """Tee output to the specified file
378
379 Overwrites the previous contents, if any.
380 """
mbligh78e17022006-09-13 19:58:12 +0000381 self.filehandle.flush()
mbligh51566342006-08-29 04:55:40 +0000382 #print_to_tty("tee_redirect to " + filename)
383 #where_art_thy_filehandles()
mblighf4c35322006-03-13 01:01:10 +0000384 fdcopy = os.dup(self.fd)
mblighf4c35322006-03-13 01:01:10 +0000385 r, w = os.pipe()
386 pid = os.fork()
387 if pid: # parent
388 os.close(r)
389 os.dup2(w, self.fd)
390 os.close(w)
391 else: # child
392 os.close(w)
393 os.dup2(r, 0)
mbligh78e17022006-09-13 19:58:12 +0000394 os.dup2(fdcopy, 1)
395 os.close(r)
396 os.close(fdcopy)
mblighf4c35322006-03-13 01:01:10 +0000397 os.execlp('tee', 'tee', filename)
mblighd7341d52006-09-13 20:02:34 +0000398 self.stack.append( (fdcopy, self.filehandle, pid) )
mbligh78e17022006-09-13 19:58:12 +0000399 self.update_handle(os.fdopen(self.fd, 'w'))
mbligh51566342006-08-29 04:55:40 +0000400 #where_art_thy_filehandles()
401 #print_to_tty("done tee_redirect to " + filename)
mblighf4c35322006-03-13 01:01:10 +0000402
403
404 def restore(self):
mblighc86b0b42006-07-28 17:35:28 +0000405 """unredirect one level"""
mbligh78e17022006-09-13 19:58:12 +0000406 self.filehandle.flush()
mblighcf315332006-04-02 19:58:30 +0000407 # print_to_tty("ENTERING RESTORE %d" % self.fd)
mblighf4c35322006-03-13 01:01:10 +0000408 # where_art_thy_filehandles()
mblighd7341d52006-09-13 20:02:34 +0000409 (old_fd, old_filehandle, pid) = self.stack.pop()
mblighf4c35322006-03-13 01:01:10 +0000410 # print_to_tty("old_fd %d" % old_fd)
411 # print_to_tty("self.fd %d" % self.fd)
412 self.filehandle.close() # seems to close old_fd as well.
mblighd7341d52006-09-13 20:02:34 +0000413 if pid:
414 os.waitpid(pid, 0)
mblighf4c35322006-03-13 01:01:10 +0000415 # where_art_thy_filehandles()
416 os.dup2(old_fd, self.fd)
417 # print_to_tty("CLOSING FD %d" % old_fd)
418 os.close(old_fd)
419 # where_art_thy_filehandles()
mbligh78e17022006-09-13 19:58:12 +0000420 self.update_handle(old_filehandle)
mblighf4c35322006-03-13 01:01:10 +0000421 # where_art_thy_filehandles()
422 # print_to_tty("EXIT RESTORE %d" % self.fd)