blob: 23f3bf898638b2bd9d4683a194ef2850afdcf20b [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 = '.'):
mbligh52508632006-09-30 18:29:55 +0000117 """
118 Receives either a path to a local file or a URL.
119 returns either the path to the local file, or the fetched URL
120
121 unmap_url('/usr/src', 'foo.tar', '/tmp')
122 = '/usr/src/foo.tar'
123 unmap_url('/usr/src', 'http://site/file', '/tmp')
124 = '/tmp/file'
125 (after retrieving it)
126 """
mblighea30c8a2006-04-22 22:24:25 +0000127 if is_url(src):
128 dest = destdir + '/' + os.path.basename(src)
129 get_file(src, dest)
130 return dest
mbligh82641862006-04-23 06:21:36 +0000131 else:
132 return srcdir + '/' + src
mblighea30c8a2006-04-22 22:24:25 +0000133
134
mblighf4c35322006-03-13 01:01:10 +0000135def basename(path):
136 i = path.rfind('/');
137 return path[i+1:]
138
139
140def force_copy(src, dest):
mblighc86b0b42006-07-28 17:35:28 +0000141 """Replace dest with a new copy of src, even if it exists"""
mblighf4c35322006-03-13 01:01:10 +0000142 if os.path.isfile(dest):
143 os.remove(dest)
mbligh1e8858e2006-11-24 22:18:35 +0000144 if os.path.isdir(dest):
145 dest = os.path.join(dest, os.path.basename(src))
mblighf4c35322006-03-13 01:01:10 +0000146 return shutil.copyfile(src, dest)
147
148
mblighfdbcaec2006-10-01 23:28:57 +0000149def force_link(src, dest):
150 """Link src to dest, overwriting it if it exists"""
151 return system("ln -sf %s %s" % (src, dest))
152
153
mblighcdf02a42006-04-23 02:22:49 +0000154def file_contains_pattern(file, pattern):
mblighc86b0b42006-07-28 17:35:28 +0000155 """Return true if file contains the specified egrep pattern"""
mblighcdf02a42006-04-23 02:22:49 +0000156 if not os.path.isfile(file):
157 raise NameError, 'file %s does not exist' % file
mbligh5c1e26a2006-10-04 14:46:33 +0000158 return not system('egrep -q "' + pattern + '" ' + file, ignorestatus = 1)
mblighcdf02a42006-04-23 02:22:49 +0000159
160
161def list_grep(list, pattern):
mblighc86b0b42006-07-28 17:35:28 +0000162 """True if any item in list matches the specified pattern."""
mblighcdf02a42006-04-23 02:22:49 +0000163 compiled = re.compile(pattern)
164 for line in list:
165 match = compiled.search(line)
166 if (match):
167 return 1
168 return 0
169
mbligh42b81ca2006-09-30 22:10:01 +0000170def get_os_vendor():
171 """Try to guess what's the os vendor
172 """
173 issue = '/etc/issue'
174
175 if not os.path.isfile(issue):
176 return 'Unknown'
177
178 if file_contains_pattern(issue, 'Red Hat'):
179 return 'Red Hat'
180 elif file_contains_pattern(issue, 'Fedora Core'):
181 return 'Fedora Core'
182 elif file_contains_pattern(issue, 'SUSE'):
183 return 'SUSE'
184 elif file_contains_pattern(issue, 'Ubuntu'):
185 return 'Ubuntu'
186 else:
187 return 'Unknown'
188
mblighcdf02a42006-04-23 02:22:49 +0000189
mblighf49d5cf2006-04-23 02:24:42 +0000190def get_vmlinux():
mblighc86b0b42006-07-28 17:35:28 +0000191 """Return the full path to vmlinux
192
193 Ahem. This is crap. Pray harder. Bad Martin.
194 """
mblighad849ee2006-10-02 17:53:18 +0000195 vmlinux = '/boot/vmlinux-%s' % system_output('uname -r')
mbligh662a2a22006-10-01 17:01:14 +0000196 if os.path.isfile(vmlinux):
197 return vmlinux
mbligh17dbf052006-10-12 04:46:10 +0000198 vmlinux = '/lib/modules/%s/build/vmlinux' % system_output('uname -r')
199 if os.path.isfile(vmlinux):
200 return vmlinux
mbligh662a2a22006-10-01 17:01:14 +0000201 return None
mblighf49d5cf2006-04-23 02:24:42 +0000202
203
204def get_systemmap():
mblighc86b0b42006-07-28 17:35:28 +0000205 """Return the full path to System.map
206
207 Ahem. This is crap. Pray harder. Bad Martin.
208 """
mblighad849ee2006-10-02 17:53:18 +0000209 map = '/boot/System.map-%s' % system_output('uname -r')
mbligh662a2a22006-10-01 17:01:14 +0000210 if os.path.isfile(map):
211 return map
mbligh17dbf052006-10-12 04:46:10 +0000212 map = '/lib/modules/%s/build/System.map' % system_output('uname -r')
213 if os.path.isfile(map):
214 return map
mbligh662a2a22006-10-01 17:01:14 +0000215 return None
mbligh67b5ece2006-04-23 02:53:12 +0000216
217
218def get_modules_dir():
mblighc86b0b42006-07-28 17:35:28 +0000219 """Return the modules dir for the running kernel version"""
mbligh67b5ece2006-04-23 02:53:12 +0000220 kernel_version = system_output('uname -r')
221 return '/lib/modules/%s/kernel' % kernel_version
mblighf49d5cf2006-04-23 02:24:42 +0000222
223
mbligh5970cf02006-08-06 15:39:22 +0000224def get_cpu_arch():
mblighc86b0b42006-07-28 17:35:28 +0000225 """Work out which CPU architecture we're running on"""
mblighcdf02a42006-04-23 02:22:49 +0000226 f = open('/proc/cpuinfo', 'r')
227 cpuinfo = f.readlines()
228 f.close()
229 if list_grep(cpuinfo, '^cpu.*(RS64|POWER3|Broadband Engine)'):
230 return 'power'
231 elif list_grep(cpuinfo, '^cpu.*POWER4'):
232 return 'power4'
233 elif list_grep(cpuinfo, '^cpu.*POWER5'):
234 return 'power5'
235 elif list_grep(cpuinfo, '^cpu.*POWER6'):
236 return 'power6'
237 elif list_grep(cpuinfo, '^cpu.*PPC970'):
238 return 'power970'
239 elif list_grep(cpuinfo, 'Opteron'):
240 return 'x86_64'
241 elif list_grep(cpuinfo, 'GenuineIntel') and list_grep(cpuinfo, '48 bits virtual'):
mblighf4c35322006-03-13 01:01:10 +0000242 return 'x86_64'
243 else:
244 return 'i386'
245
246
mbligh548f29a2006-10-17 04:55:12 +0000247def get_current_kernel_arch():
248 """Get the machine architecture, now just a wrap of 'uname -m'."""
249 return os.popen('uname -m').read().rstrip()
mblighcdf02a42006-04-23 02:22:49 +0000250
251
mblighfdbcaec2006-10-01 23:28:57 +0000252def get_file_arch(filename):
253 # -L means follow symlinks
254 file_data = system_output('file -L ' + filename)
255 if file_data.count('80386'):
256 return 'i386'
257 return None
258
259
mbligh548f29a2006-10-17 04:55:12 +0000260def kernelexpand(kernel, args=None):
mblighf4c35322006-03-13 01:01:10 +0000261 # if not (kernel.startswith('http://') or kernel.startswith('ftp://') or os.path.isfile(kernel)):
mbligh5629af02006-09-15 01:54:23 +0000262 if kernel.find('/') < 0: # contains no path.
mbligh534015f2006-09-15 03:28:56 +0000263 autodir = os.environ['AUTODIR']
264 kernelexpand = os.path.join(autodir, 'tools/kernelexpand')
mbligh548f29a2006-10-17 04:55:12 +0000265 if args:
266 kernelexpand += ' ' + args
mbligh5629af02006-09-15 01:54:23 +0000267 w, r = os.popen2(kernelexpand + ' ' + kernel)
mblighf4c35322006-03-13 01:01:10 +0000268
269 kernel = r.readline().strip()
270 r.close()
271 w.close()
mbligh534015f2006-09-15 03:28:56 +0000272 return kernel.split()
mblighf4c35322006-03-13 01:01:10 +0000273
274
275def count_cpus():
mblighc86b0b42006-07-28 17:35:28 +0000276 """number of CPUs in the local machine according to /proc/cpuinfo"""
mblighf4c35322006-03-13 01:01:10 +0000277 f = file('/proc/cpuinfo', 'r')
278 cpus = 0
279 for line in f.readlines():
280 if line.startswith('processor'):
281 cpus += 1
282 return cpus
283
mblighe7a170f2006-12-05 07:48:18 +0000284
285# Returns total memory in kb
286def memtotal():
287 memtotal = system_output('grep MemTotal /proc/meminfo')
288 return int(re.search(r'\d+', memtotal).group(0))
289
290
mbligha975fb62006-04-22 19:56:25 +0000291def system(cmd, ignorestatus = 0):
mblighc86b0b42006-07-28 17:35:28 +0000292 """os.system replacement
293
294 We have our own definition of system here, as the stock os.system doesn't
295 correctly handle sigpipe
296 (ie things like "yes | head" will hang because yes doesn't get the SIGPIPE).
297
298 Also the stock os.system didn't raise errors based on exit status, this
299 version does unless you explicitly specify ignorestatus=1
300 """
mblighf4c35322006-03-13 01:01:10 +0000301 signal.signal(signal.SIGPIPE, signal.SIG_DFL)
302 try:
apwbc2867d2006-04-06 18:21:16 +0000303 status = os.system(cmd)
mblighf4c35322006-03-13 01:01:10 +0000304 finally:
305 signal.signal(signal.SIGPIPE, signal.SIG_IGN)
mbligha975fb62006-04-22 19:56:25 +0000306
mbligh67b5ece2006-04-23 02:53:12 +0000307 if ((status != 0) and not ignorestatus):
apwbc2867d2006-04-06 18:21:16 +0000308 raise CmdError(cmd, status)
mbligha975fb62006-04-22 19:56:25 +0000309 return status
mbligh3d914912006-04-22 17:37:19 +0000310
311
mbligh67b5ece2006-04-23 02:53:12 +0000312def system_output(command, ignorestatus = 0):
mblighc86b0b42006-07-28 17:35:28 +0000313 """Run command and return its output
314
315 ignorestatus
316 whether to raise a CmdError if command has a nonzero exit status
317 """
mbligh67b5ece2006-04-23 02:53:12 +0000318 (result, data) = commands.getstatusoutput(command)
319 if ((result != 0) and not ignorestatus):
320 raise CmdError, 'command failed: ' + command
321 return data
322
323
mblighf4c35322006-03-13 01:01:10 +0000324def where_art_thy_filehandles():
mblighc86b0b42006-07-28 17:35:28 +0000325 """Dump the current list of filehandles"""
mblighf4c35322006-03-13 01:01:10 +0000326 os.system("ls -l /proc/%d/fd >> /dev/tty" % os.getpid())
327
328
329def print_to_tty(string):
mblighc86b0b42006-07-28 17:35:28 +0000330 """Output string straight to the tty"""
mblighf4c35322006-03-13 01:01:10 +0000331 os.system("echo " + string + " >> /dev/tty")
332
333
mblighb8a14e32006-05-06 00:17:35 +0000334def dump_object(object):
mblighc86b0b42006-07-28 17:35:28 +0000335 """Dump an object's attributes and methods
336
337 kind of like dir()
338 """
mblighb8a14e32006-05-06 00:17:35 +0000339 for item in object.__dict__.iteritems():
340 print item
341 try:
342 (key,value) = item
343 dump_object(value)
344 except:
345 continue
346
347
mbligh4b089662006-06-14 22:34:58 +0000348def environ(env_key):
mblighc86b0b42006-07-28 17:35:28 +0000349 """return the requested environment variable, or '' if unset"""
mbligh4b089662006-06-14 22:34:58 +0000350 if (os.environ.has_key(env_key)):
mblighd931a582006-10-01 00:30:12 +0000351 return os.environ[env_key]
mbligh4b089662006-06-14 22:34:58 +0000352 else:
353 return ''
354
355
356def prepend_path(newpath, oldpath):
mblighc86b0b42006-07-28 17:35:28 +0000357 """prepend newpath to oldpath"""
mbligh4b089662006-06-14 22:34:58 +0000358 if (oldpath):
359 return newpath + ':' + oldpath
360 else:
361 return newpath
362
363
364def append_path(oldpath, newpath):
mblighc86b0b42006-07-28 17:35:28 +0000365 """append newpath to oldpath"""
mbligh4b089662006-06-14 22:34:58 +0000366 if (oldpath):
367 return oldpath + ':' + newpath
368 else:
369 return newpath
370
371
mbligh4e75b0d2006-08-29 15:22:44 +0000372def avgtime_print(dir):
373 """ Calculate some benchmarking statistics.
374 Input is a directory containing a file called 'time'.
375 File contains one-per-line results of /usr/bin/time.
376 Output is average Elapsed, User, and System time in seconds,
377 and average CPU percentage.
378 """
379 f = open(dir + "/time")
380 user = system = elapsed = cpu = count = 0
381 r = re.compile('([\d\.]*)user ([\d\.]*)system (\d*):([\d\.]*)elapsed (\d*)%CPU')
382 for line in f.readlines():
383 try:
384 s = r.match(line);
mblighe1ee2672006-08-29 16:27:15 +0000385 user += float(s.group(1))
386 system += float(s.group(2))
387 elapsed += (float(s.group(3)) * 60) + float(s.group(4))
388 cpu += float(s.group(5))
mbligh4e75b0d2006-08-29 15:22:44 +0000389 count += 1
390 except:
391 raise ValueError("badly formatted times")
392
393 f.close()
394 return "Elapsed: %0.2fs User: %0.2fs System: %0.2fs CPU: %0.0f%%" % \
395 (elapsed/count, user/count, system/count, cpu/count)
396
397
mblighf06db0f2006-09-30 17:08:43 +0000398def running_config():
399 """
400 Return path of config file of the currently running kernel
401 """
402 for config in ('/proc/config.gz', \
403 '/boot/config-%s' % system_output('uname -r') ):
404 if os.path.isfile(config):
405 return config
406 return None
mbligh9ec8acc2006-10-05 06:52:33 +0000407
408
409def cpu_online_map():
410 """
411 Check out the available cpu online map
412 """
413 cpus = []
414 for line in open('/proc/cpuinfo', 'r').readlines():
415 if line.startswith('processor'):
416 cpus.append(line.split()[2]) # grab cpu number
417 return cpus
mbligh663e4f62006-10-11 05:03:40 +0000418
419
420def check_glibc_ver(ver):
421 glibc_ver = commands.getoutput('ldd --version').splitlines()[0]
mblighcabfdaf2006-10-11 14:07:48 +0000422 glibc_ver = re.search(r'(\d+\.\d+(\.\d+)?)', glibc_ver).group()
mblighea97ab82006-10-13 20:18:01 +0000423 if glibc_ver.split('.') < ver.split('.'):
424 raise "Glibc is too old (%s). Glibc >= %s is needed." % \
425 (glibc_ver, ver)
mbligh663e4f62006-10-11 05:03:40 +0000426
mblighea97ab82006-10-13 20:18:01 +0000427
428def check_python_version():
429 py_version = (sys.version).split(' ')[0]
430 version = py_version.split('.')[0:2]
431 if [int(x) for x in version] < [2, 4]:
apw1f22fe62006-11-28 10:03:51 +0000432 for new in ('/usr/bin/python2.4', '/usr/local/bin/python2.4'):
433 if os.path.exists(new):
434 sys.argv.insert(0, new)
435 os.execv(sys.argv[0], sys.argv)
mblighea97ab82006-10-13 20:18:01 +0000436 raise "Python 2.4 or newer is needed"