base_utils.py revision 77e0121a0d2a878433b96f4757a44db541530c3c
1""" 2DO NOT import this file directly - import client/bin/utils.py, 3which will mix this in 4 5Convenience functions for use by tests or whomever. 6 7Note that this file is mixed in by utils.py - note very carefully the 8precedence order defined there 9""" 10import os, shutil, commands, pickle, glob 11import math, re, fnmatch, logging, multiprocessing 12from autotest_lib.client.common_lib import error, utils, magic 13 14 15def grep(pattern, file): 16 """ 17 This is mainly to fix the return code inversion from grep 18 Also handles compressed files. 19 20 returns 1 if the pattern is present in the file, 0 if not. 21 """ 22 command = 'grep "%s" > /dev/null' % pattern 23 ret = cat_file_to_cmd(file, command, ignore_status=True) 24 return not ret 25 26 27def difflist(list1, list2): 28 """returns items in list2 that are not in list1""" 29 diff = []; 30 for x in list2: 31 if x not in list1: 32 diff.append(x) 33 return diff 34 35 36def cat_file_to_cmd(file, command, ignore_status=0, return_output=False): 37 """ 38 equivalent to 'cat file | command' but knows to use 39 zcat or bzcat if appropriate 40 """ 41 if not os.path.isfile(file): 42 raise NameError('invalid file %s to cat to command %s' 43 % (file, command)) 44 45 if return_output: 46 run_cmd = utils.system_output 47 else: 48 run_cmd = utils.system 49 50 if magic.guess_type(file) == 'application/x-bzip2': 51 cat = 'bzcat' 52 elif magic.guess_type(file) == 'application/x-gzip': 53 cat = 'zcat' 54 else: 55 cat = 'cat' 56 return run_cmd('%s %s | %s' % (cat, file, command), 57 ignore_status=ignore_status) 58 59 60def extract_tarball_to_dir(tarball, dir): 61 """ 62 Extract a tarball to a specified directory name instead of whatever 63 the top level of a tarball is - useful for versioned directory names, etc 64 """ 65 if os.path.exists(dir): 66 if os.path.isdir(dir): 67 shutil.rmtree(dir) 68 else: 69 os.remove(dir) 70 pwd = os.getcwd() 71 os.chdir(os.path.dirname(os.path.abspath(dir))) 72 newdir = extract_tarball(tarball) 73 os.rename(newdir, dir) 74 os.chdir(pwd) 75 76 77def extract_tarball(tarball): 78 """Returns the directory extracted by the tarball.""" 79 extracted = cat_file_to_cmd(tarball, 'tar xvf - 2>/dev/null', 80 return_output=True).splitlines() 81 82 dir = None 83 84 for line in extracted: 85 if line.startswith('./'): 86 line = line[2:] 87 if not line or line == '.': 88 continue 89 topdir = line.split('/')[0] 90 if os.path.isdir(topdir): 91 if dir: 92 assert(dir == topdir) 93 else: 94 dir = topdir 95 if dir: 96 return dir 97 else: 98 raise NameError('extracting tarball produced no dir') 99 100 101def hash_file(filename, size=None, method="md5"): 102 """ 103 Calculate the hash of filename. 104 If size is not None, limit to first size bytes. 105 Throw exception if something is wrong with filename. 106 Can be also implemented with bash one-liner (assuming size%1024==0): 107 dd if=filename bs=1024 count=size/1024 | sha1sum - 108 109 @param filename: Path of the file that will have its hash calculated. 110 @param method: Method used to calculate the hash. Supported methods: 111 * md5 112 * sha1 113 @returns: Hash of the file, if something goes wrong, return None. 114 """ 115 chunksize = 4096 116 fsize = os.path.getsize(filename) 117 118 if not size or size > fsize: 119 size = fsize 120 f = open(filename, 'rb') 121 122 try: 123 hash = utils.hash(method) 124 except ValueError: 125 logging.error("Unknown hash type %s, returning None", method) 126 127 while size > 0: 128 if chunksize > size: 129 chunksize = size 130 data = f.read(chunksize) 131 if len(data) == 0: 132 logging.debug("Nothing left to read but size=%d", size) 133 break 134 hash.update(data) 135 size -= len(data) 136 f.close() 137 return hash.hexdigest() 138 139 140def unmap_url_cache(cachedir, url, expected_hash, method="md5"): 141 """ 142 Downloads a file from a URL to a cache directory. If the file is already 143 at the expected position and has the expected hash, let's not download it 144 again. 145 146 @param cachedir: Directory that might hold a copy of the file we want to 147 download. 148 @param url: URL for the file we want to download. 149 @param expected_hash: Hash string that we expect the file downloaded to 150 have. 151 @param method: Method used to calculate the hash string (md5, sha1). 152 """ 153 # Let's convert cachedir to a canonical path, if it's not already 154 cachedir = os.path.realpath(cachedir) 155 if not os.path.isdir(cachedir): 156 try: 157 os.makedirs(cachedir) 158 except: 159 raise ValueError('Could not create cache directory %s' % cachedir) 160 file_from_url = os.path.basename(url) 161 file_local_path = os.path.join(cachedir, file_from_url) 162 163 file_hash = None 164 failure_counter = 0 165 while not file_hash == expected_hash: 166 if os.path.isfile(file_local_path): 167 file_hash = hash_file(file_local_path, method) 168 if file_hash == expected_hash: 169 # File is already at the expected position and ready to go 170 src = file_from_url 171 else: 172 # Let's download the package again, it's corrupted... 173 logging.error("Seems that file %s is corrupted, trying to " 174 "download it again", file_from_url) 175 src = url 176 failure_counter += 1 177 else: 178 # File is not there, let's download it 179 src = url 180 if failure_counter > 1: 181 raise EnvironmentError("Consistently failed to download the " 182 "package %s. Aborting further download " 183 "attempts. This might mean either the " 184 "network connection has problems or the " 185 "expected hash string that was determined " 186 "for this file is wrong", file_from_url) 187 file_path = utils.unmap_url(cachedir, src, cachedir) 188 189 return file_path 190 191 192def force_copy(src, dest): 193 """Replace dest with a new copy of src, even if it exists""" 194 if os.path.isfile(dest): 195 os.remove(dest) 196 if os.path.isdir(dest): 197 dest = os.path.join(dest, os.path.basename(src)) 198 shutil.copyfile(src, dest) 199 return dest 200 201 202def force_link(src, dest): 203 """Link src to dest, overwriting it if it exists""" 204 return utils.system("ln -sf %s %s" % (src, dest)) 205 206 207def file_contains_pattern(file, pattern): 208 """Return true if file contains the specified egrep pattern""" 209 if not os.path.isfile(file): 210 raise NameError('file %s does not exist' % file) 211 return not utils.system('egrep -q "' + pattern + '" ' + file, ignore_status=True) 212 213 214def list_grep(list, pattern): 215 """True if any item in list matches the specified pattern.""" 216 compiled = re.compile(pattern) 217 for line in list: 218 match = compiled.search(line) 219 if (match): 220 return 1 221 return 0 222 223 224def get_os_vendor(): 225 """Try to guess what's the os vendor 226 """ 227 if os.path.isfile('/etc/SuSE-release'): 228 return 'SUSE' 229 230 issue = '/etc/issue' 231 232 if not os.path.isfile(issue): 233 return 'Unknown' 234 235 if file_contains_pattern(issue, 'Red Hat'): 236 return 'Red Hat' 237 elif file_contains_pattern(issue, 'Fedora'): 238 return 'Fedora Core' 239 elif file_contains_pattern(issue, 'SUSE'): 240 return 'SUSE' 241 elif file_contains_pattern(issue, 'Ubuntu'): 242 return 'Ubuntu' 243 elif file_contains_pattern(issue, 'Debian'): 244 return 'Debian' 245 else: 246 return 'Unknown' 247 248 249def get_cc(): 250 try: 251 return os.environ['CC'] 252 except KeyError: 253 return 'gcc' 254 255 256def get_vmlinux(): 257 """Return the full path to vmlinux 258 259 Ahem. This is crap. Pray harder. Bad Martin. 260 """ 261 vmlinux = '/boot/vmlinux-%s' % utils.system_output('uname -r') 262 if os.path.isfile(vmlinux): 263 return vmlinux 264 vmlinux = '/lib/modules/%s/build/vmlinux' % utils.system_output('uname -r') 265 if os.path.isfile(vmlinux): 266 return vmlinux 267 return None 268 269 270def get_systemmap(): 271 """Return the full path to System.map 272 273 Ahem. This is crap. Pray harder. Bad Martin. 274 """ 275 map = '/boot/System.map-%s' % utils.system_output('uname -r') 276 if os.path.isfile(map): 277 return map 278 map = '/lib/modules/%s/build/System.map' % utils.system_output('uname -r') 279 if os.path.isfile(map): 280 return map 281 return None 282 283 284def get_modules_dir(): 285 """Return the modules dir for the running kernel version""" 286 kernel_version = utils.system_output('uname -r') 287 return '/lib/modules/%s/kernel' % kernel_version 288 289 290_CPUINFO_RE = re.compile(r'^(?P<key>[^\t]*)\t*: ?(?P<value>.*)$') 291 292 293def get_cpuinfo(): 294 """Read /proc/cpuinfo and convert to a list of dicts.""" 295 cpuinfo = [] 296 with open('/proc/cpuinfo', 'r') as f: 297 cpu = {} 298 for line in f: 299 line = line.strip() 300 if not line: 301 cpuinfo.append(cpu) 302 cpu = {} 303 continue 304 match = _CPUINFO_RE.match(line) 305 cpu[match.group('key')] = match.group('value') 306 if cpu: 307 # cpuinfo usually ends in a blank line, so this shouldn't happen. 308 cpuinfo.append(cpu) 309 return cpuinfo 310 311 312def get_cpu_arch(): 313 """Work out which CPU architecture we're running on""" 314 f = open('/proc/cpuinfo', 'r') 315 cpuinfo = f.readlines() 316 f.close() 317 if list_grep(cpuinfo, '^cpu.*(RS64|POWER3|Broadband Engine)'): 318 return 'power' 319 elif list_grep(cpuinfo, '^cpu.*POWER4'): 320 return 'power4' 321 elif list_grep(cpuinfo, '^cpu.*POWER5'): 322 return 'power5' 323 elif list_grep(cpuinfo, '^cpu.*POWER6'): 324 return 'power6' 325 elif list_grep(cpuinfo, '^cpu.*POWER7'): 326 return 'power7' 327 elif list_grep(cpuinfo, '^cpu.*PPC970'): 328 return 'power970' 329 elif list_grep(cpuinfo, 'ARM'): 330 return 'arm' 331 elif list_grep(cpuinfo, '^flags.*:.* lm .*'): 332 return 'x86_64' 333 else: 334 return 'i386' 335 336 337def get_arm_soc_family(): 338 """Work out which ARM SoC we're running on""" 339 f = open('/proc/cpuinfo', 'r') 340 cpuinfo = f.readlines() 341 f.close() 342 if list_grep(cpuinfo, 'EXYNOS5'): 343 return 'exynos5' 344 elif list_grep(cpuinfo, 'Tegra'): 345 return 'tegra' 346 elif list_grep(cpuinfo, 'Rockchip'): 347 return 'rockchip' 348 return 'arm' 349 350 351def get_cpu_soc_family(): 352 """Like get_cpu_arch, but for ARM, returns the SoC family name""" 353 family = get_cpu_arch() 354 if family == 'arm': 355 family = get_arm_soc_family() 356 return family 357 358 359INTEL_UARCH_TABLE = { 360 '06_36': 'Atom', 361 '06_26': 'Atom', 362 '06_1C': 'Atom', 363 '06_3D': 'Broadwell', 364 '06_3F': 'Haswell', 365 '06_3C': 'Haswell', 366 '06_46': 'Haswell', 367 '06_45': 'Haswell', 368 '06_3E': 'IvyBridge', 369 '06_3A': 'IvyBridge', 370 '06_2D': 'SandyBridge', 371 '06_2A': 'SandyBridge', 372 '06_2F': 'Westmere', 373 '06_2C': 'Westmere', 374 '06_25': 'Westmere', 375 '06_2E': 'Nehalem', 376 '06_1F': 'Nehalem', 377 '06_1E': 'Nehalem', 378 '06_1D': 'Nehalem', 379 '06_1A': 'Nehalem', 380 '06_17': 'Nehalem', 381 '06_16': 'Merom', 382 '06_0F': 'Merom', 383 '0F_06': 'Presler', 384 '0F_04': 'Prescott', 385 '0F_03': 'Prescott', 386 '06_0D': 'Dothan', 387} 388 389 390def get_intel_cpu_uarch(numeric=False): 391 """Return the Intel microarchitecture we're running on, or None. 392 393 Returns None if this is not an Intel CPU. Returns the family and model as 394 underscore-separated hex (per Intel manual convention) if the uarch is not 395 known, or if numeric is True. 396 """ 397 if not get_current_kernel_arch().startswith('x86'): 398 return None 399 cpuinfo = get_cpuinfo()[0] 400 if cpuinfo['vendor_id'] != 'GenuineIntel': 401 return None 402 family_model = '%02X_%02X' % (int(cpuinfo['cpu family']), 403 int(cpuinfo['model'])) 404 if numeric: 405 return family_model 406 return INTEL_UARCH_TABLE.get(family_model, family_model) 407 408 409def get_current_kernel_arch(): 410 """Get the machine architecture, now just a wrap of 'uname -m'.""" 411 return os.popen('uname -m').read().rstrip() 412 413 414def get_file_arch(filename): 415 # -L means follow symlinks 416 file_data = utils.system_output('file -L ' + filename) 417 if file_data.count('80386'): 418 return 'i386' 419 return None 420 421 422def count_cpus(): 423 """number of CPUs in the local machine according to /proc/cpuinfo""" 424 try: 425 return multiprocessing.cpu_count() 426 except Exception as e: 427 logging.exception('can not get cpu count from' 428 ' multiprocessing.cpu_count()') 429 cpuinfo = get_cpuinfo() 430 # Returns at least one cpu. Check comment #1 in crosbug.com/p/9582. 431 return len(cpuinfo) or 1 432 433 434def cpu_online_map(): 435 """ 436 Check out the available cpu online map 437 """ 438 cpuinfo = get_cpuinfo() 439 cpus = [] 440 for cpu in cpuinfo: 441 cpus.append(cpu['processor']) # grab cpu number 442 return cpus 443 444 445def get_cpu_family(): 446 cpuinfo = get_cpuinfo()[0] 447 return int(cpuinfo['cpu_family']) 448 449 450def get_cpu_vendor(): 451 cpuinfo = get_cpuinfo() 452 vendors = [cpu['vendor_id'] for cpu in cpuinfo] 453 for v in vendors[1:]: 454 if v != vendors[0]: 455 raise error.TestError('multiple cpu vendors found: ' + str(vendors)) 456 return vendors[0] 457 458 459def probe_cpus(): 460 """ 461 This routine returns a list of cpu devices found under 462 /sys/devices/system/cpu. 463 """ 464 cmd = 'find /sys/devices/system/cpu/ -maxdepth 1 -type d -name cpu*' 465 return utils.system_output(cmd).splitlines() 466 467 468# Returns total memory in kb 469def read_from_meminfo(key): 470 meminfo = utils.system_output('grep %s /proc/meminfo' % key) 471 return int(re.search(r'\d+', meminfo).group(0)) 472 473 474def memtotal(): 475 return read_from_meminfo('MemTotal') 476 477 478def freememtotal(): 479 return read_from_meminfo('MemFree') 480 481def usable_memtotal(): 482 # Reserved 5% for OS use 483 return int(read_from_meminfo('MemFree') * 0.95) 484 485 486def rounded_memtotal(): 487 # Get total of all physical mem, in kbytes 488 usable_kbytes = memtotal() 489 # usable_kbytes is system's usable DRAM in kbytes, 490 # as reported by memtotal() from device /proc/meminfo memtotal 491 # after Linux deducts 1.5% to 5.1% for system table overhead 492 # Undo the unknown actual deduction by rounding up 493 # to next small multiple of a big power-of-two 494 # eg 12GB - 5.1% gets rounded back up to 12GB 495 mindeduct = 0.015 # 1.5 percent 496 maxdeduct = 0.055 # 5.5 percent 497 # deduction range 1.5% .. 5.5% supports physical mem sizes 498 # 6GB .. 12GB in steps of .5GB 499 # 12GB .. 24GB in steps of 1 GB 500 # 24GB .. 48GB in steps of 2 GB ... 501 # Finer granularity in physical mem sizes would require 502 # tighter spread between min and max possible deductions 503 504 # increase mem size by at least min deduction, without rounding 505 min_kbytes = int(usable_kbytes / (1.0 - mindeduct)) 506 # increase mem size further by 2**n rounding, by 0..roundKb or more 507 round_kbytes = int(usable_kbytes / (1.0 - maxdeduct)) - min_kbytes 508 # find least binary roundup 2**n that covers worst-cast roundKb 509 mod2n = 1 << int(math.ceil(math.log(round_kbytes, 2))) 510 # have round_kbytes <= mod2n < round_kbytes*2 511 # round min_kbytes up to next multiple of mod2n 512 phys_kbytes = min_kbytes + mod2n - 1 513 phys_kbytes = phys_kbytes - (phys_kbytes % mod2n) # clear low bits 514 return phys_kbytes 515 516 517def sysctl(key, value=None): 518 """Generic implementation of sysctl, to read and write. 519 520 @param key: A location under /proc/sys 521 @param value: If not None, a value to write into the sysctl. 522 523 @return The single-line sysctl value as a string. 524 """ 525 path = '/proc/sys/%s' % key 526 if value is not None: 527 utils.write_one_line(path, str(value)) 528 return utils.read_one_line(path) 529 530 531def sysctl_kernel(key, value=None): 532 """(Very) partial implementation of sysctl, for kernel params""" 533 if value is not None: 534 # write 535 utils.write_one_line('/proc/sys/kernel/%s' % key, str(value)) 536 else: 537 # read 538 out = utils.read_one_line('/proc/sys/kernel/%s' % key) 539 return int(re.search(r'\d+', out).group(0)) 540 541 542def _convert_exit_status(sts): 543 if os.WIFSIGNALED(sts): 544 return -os.WTERMSIG(sts) 545 elif os.WIFEXITED(sts): 546 return os.WEXITSTATUS(sts) 547 else: 548 # impossible? 549 raise RuntimeError("Unknown exit status %d!" % sts) 550 551 552def where_art_thy_filehandles(): 553 """Dump the current list of filehandles""" 554 os.system("ls -l /proc/%d/fd >> /dev/tty" % os.getpid()) 555 556 557def print_to_tty(string): 558 """Output string straight to the tty""" 559 open('/dev/tty', 'w').write(string + '\n') 560 561 562def dump_object(object): 563 """Dump an object's attributes and methods 564 565 kind of like dir() 566 """ 567 for item in object.__dict__.iteritems(): 568 print item 569 try: 570 (key, value) = item 571 dump_object(value) 572 except: 573 continue 574 575 576def environ(env_key): 577 """return the requested environment variable, or '' if unset""" 578 if (os.environ.has_key(env_key)): 579 return os.environ[env_key] 580 else: 581 return '' 582 583 584def prepend_path(newpath, oldpath): 585 """prepend newpath to oldpath""" 586 if (oldpath): 587 return newpath + ':' + oldpath 588 else: 589 return newpath 590 591 592def append_path(oldpath, newpath): 593 """append newpath to oldpath""" 594 if (oldpath): 595 return oldpath + ':' + newpath 596 else: 597 return newpath 598 599 600_TIME_OUTPUT_RE = re.compile( 601 r'([\d\.]*)user ([\d\.]*)system ' 602 r'(\d*):([\d\.]*)elapsed (\d*)%CPU') 603 604 605def avgtime_print(dir): 606 """ Calculate some benchmarking statistics. 607 Input is a directory containing a file called 'time'. 608 File contains one-per-line results of /usr/bin/time. 609 Output is average Elapsed, User, and System time in seconds, 610 and average CPU percentage. 611 """ 612 user = system = elapsed = cpu = count = 0 613 with open(dir + "/time") as f: 614 for line in f: 615 try: 616 m = _TIME_OUTPUT_RE.match(line); 617 user += float(m.group(1)) 618 system += float(m.group(2)) 619 elapsed += (float(m.group(3)) * 60) + float(m.group(4)) 620 cpu += float(m.group(5)) 621 count += 1 622 except: 623 raise ValueError("badly formatted times") 624 625 return "Elapsed: %0.2fs User: %0.2fs System: %0.2fs CPU: %0.0f%%" % \ 626 (elapsed / count, user / count, system / count, cpu / count) 627 628 629def to_seconds(time_string): 630 """Converts a string in M+:SS.SS format to S+.SS""" 631 elts = time_string.split(':') 632 if len(elts) == 1: 633 return time_string 634 return str(int(elts[0]) * 60 + float(elts[1])) 635 636 637_TIME_OUTPUT_RE_2 = re.compile(r'(.*?)user (.*?)system (.*?)elapsed') 638 639 640def extract_all_time_results(results_string): 641 """Extract user, system, and elapsed times into a list of tuples""" 642 results = [] 643 for result in _TIME_OUTPUT_RE_2.findall(results_string): 644 results.append(tuple([to_seconds(elt) for elt in result])) 645 return results 646 647 648def running_config(): 649 """ 650 Return path of config file of the currently running kernel 651 """ 652 version = utils.system_output('uname -r') 653 for config in ('/proc/config.gz', \ 654 '/boot/config-%s' % version, 655 '/lib/modules/%s/build/.config' % version): 656 if os.path.isfile(config): 657 return config 658 return None 659 660 661def check_for_kernel_feature(feature): 662 config = running_config() 663 664 if not config: 665 raise TypeError("Can't find kernel config file") 666 667 if magic.guess_type(config) == 'application/x-gzip': 668 grep = 'zgrep' 669 else: 670 grep = 'grep' 671 grep += ' ^CONFIG_%s= %s' % (feature, config) 672 673 if not utils.system_output(grep, ignore_status=True): 674 raise ValueError("Kernel doesn't have a %s feature" % (feature)) 675 676 677def check_glibc_ver(ver): 678 glibc_ver = commands.getoutput('ldd --version').splitlines()[0] 679 glibc_ver = re.search(r'(\d+\.\d+(\.\d+)?)', glibc_ver).group() 680 if utils.compare_versions(glibc_ver, ver) == -1: 681 raise error.TestError("Glibc too old (%s). Glibc >= %s is needed." % 682 (glibc_ver, ver)) 683 684def check_kernel_ver(ver): 685 kernel_ver = utils.system_output('uname -r') 686 kv_tmp = re.split(r'[-]', kernel_ver)[0:3] 687 # In compare_versions, if v1 < v2, return value == -1 688 if utils.compare_versions(kv_tmp[0], ver) == -1: 689 raise error.TestError("Kernel too old (%s). Kernel > %s is needed." % 690 (kernel_ver, ver)) 691 692 693def human_format(number): 694 # Convert number to kilo / mega / giga format. 695 if number < 1024: 696 return "%d" % number 697 kilo = float(number) / 1024.0 698 if kilo < 1024: 699 return "%.2fk" % kilo 700 meg = kilo / 1024.0 701 if meg < 1024: 702 return "%.2fM" % meg 703 gig = meg / 1024.0 704 return "%.2fG" % gig 705 706 707def numa_nodes(): 708 node_paths = glob.glob('/sys/devices/system/node/node*') 709 nodes = [int(re.sub(r'.*node(\d+)', r'\1', x)) for x in node_paths] 710 return (sorted(nodes)) 711 712 713def node_size(): 714 nodes = max(len(numa_nodes()), 1) 715 return ((memtotal() * 1024) / nodes) 716 717 718def pickle_load(filename): 719 return pickle.load(open(filename, 'r')) 720 721 722# Return the kernel version and build timestamp. 723def running_os_release(): 724 return os.uname()[2:4] 725 726 727def running_os_ident(): 728 (version, timestamp) = running_os_release() 729 return version + '::' + timestamp 730 731 732def running_os_full_version(): 733 (version, timestamp) = running_os_release() 734 return version 735 736 737# much like find . -name 'pattern' 738def locate(pattern, root=os.getcwd()): 739 for path, dirs, files in os.walk(root): 740 for f in files: 741 if fnmatch.fnmatch(f, pattern): 742 yield os.path.abspath(os.path.join(path, f)) 743 744 745def freespace(path): 746 """Return the disk free space, in bytes""" 747 s = os.statvfs(path) 748 return s.f_bavail * s.f_bsize 749 750 751def disk_block_size(path): 752 """Return the disk block size, in bytes""" 753 return os.statvfs(path).f_bsize 754 755 756_DISK_PARTITION_3_RE = re.compile(r'^(/dev/hd[a-z]+)3', re.M) 757 758def get_disks(): 759 df_output = utils.system_output('df') 760 return _DISK_PARTITION_3_RE.findall(df_output) 761 762 763def get_disk_size(disk_name): 764 """ 765 Return size of disk in byte. Return 0 in Error Case 766 767 @param disk_name: disk name to find size 768 """ 769 device = os.path.basename(disk_name) 770 for line in file('/proc/partitions'): 771 try: 772 _, _, blocks, name = re.split(r' +', line.strip()) 773 except ValueError: 774 continue 775 if name == device: 776 return 1024 * int(blocks) 777 return 0 778 779 780def get_disk_size_gb(disk_name): 781 """ 782 Return size of disk in GB (10^9). Return 0 in Error Case 783 784 @param disk_name: disk name to find size 785 """ 786 return int(get_disk_size(disk_name) / (10.0 ** 9) + 0.5) 787 788 789def get_disk_model(disk_name): 790 """ 791 Return model name for internal storage device 792 793 @param disk_name: disk name to find model 794 """ 795 cmd1 = 'udevadm info --query=property --name=%s' % disk_name 796 cmd2 = 'grep -E "ID_(NAME|MODEL)="' 797 cmd3 = 'cut -f 2 -d"="' 798 cmd = ' | '.join([cmd1, cmd2, cmd3]) 799 return utils.system_output(cmd) 800 801 802_DISK_DEV_RE = re.compile(r'/dev/sd[a-z]|/dev/mmcblk[0-9]*') 803 804 805def get_disk_from_filename(filename): 806 """ 807 Return the disk device the filename is on. 808 If the file is on tmpfs or other special file systems, 809 return None. 810 811 @param filename: name of file, full path. 812 """ 813 814 if not os.path.exists(filename): 815 raise error.TestError('file %s missing' % filename) 816 817 if filename[0] != '/': 818 raise error.TestError('This code works only with full path') 819 820 m = _DISK_DEV_RE.match(filename) 821 while not m: 822 if filename[0] != '/': 823 return None 824 if filename == '/dev/root': 825 cmd = 'rootdev -d -s' 826 elif filename.startswith('/dev/mapper'): 827 cmd = 'dmsetup table "%s"' % os.path.basename(filename) 828 dmsetup_output = utils.system_output(cmd).split(' ') 829 if dmsetup_output[2] == 'verity': 830 maj_min = dmsetup_output[4] 831 elif dmsetup_output[2] == 'crypt': 832 maj_min = dmsetup_output[6] 833 cmd = 'realpath "/dev/block/%s"' % maj_min 834 elif filename.startswith('/dev/loop'): 835 cmd = 'losetup -O BACK-FILE "%s" | tail -1' % filename 836 else: 837 cmd = 'df "%s" | tail -1 | cut -f 1 -d" "' % filename 838 filename = utils.system_output(cmd) 839 m = _DISK_DEV_RE.match(filename) 840 return m.group(0) 841 842 843def get_disk_firmware_version(disk_name): 844 """ 845 Return firmware version for internal storage device. (empty string for eMMC) 846 847 @param disk_name: disk name to find model 848 """ 849 cmd1 = 'udevadm info --query=property --name=%s' % disk_name 850 cmd2 = 'grep -E "ID_REVISION="' 851 cmd3 = 'cut -f 2 -d"="' 852 cmd = ' | '.join([cmd1, cmd2, cmd3]) 853 return utils.system_output(cmd) 854 855 856def is_disk_scsi(disk_name): 857 """ 858 Return true if disk is a scsi device, return false otherwise 859 860 @param disk_name: disk name check 861 """ 862 return re.match('/dev/sd[a-z]+', disk_name) 863 864 865def is_disk_harddisk(disk_name): 866 """ 867 Return true if disk is a harddisk, return false otherwise 868 869 @param disk_name: disk name check 870 """ 871 cmd1 = 'udevadm info --query=property --name=%s' % disk_name 872 cmd2 = 'grep -E "ID_ATA_ROTATION_RATE_RPM="' 873 cmd3 = 'cut -f 2 -d"="' 874 cmd = ' | '.join([cmd1, cmd2, cmd3]) 875 876 rtt = utils.system_output(cmd) 877 878 # eMMC will not have this field; rtt == '' 879 # SSD will have zero rotation rate; rtt == '0' 880 # For harddisk rtt > 0 881 return rtt and int(rtt) > 0 882 883 884def verify_hdparm_feature(disk_name, feature): 885 """ 886 Check for feature support for SCSI disk using hdparm 887 888 @param disk_name: target disk 889 @param feature: hdparm output string of the feature 890 """ 891 cmd = 'hdparm -I %s | grep -q "%s"' % (disk_name, feature) 892 ret = utils.system(cmd, ignore_status=True) 893 if ret == 0: 894 return True 895 elif ret == 1: 896 return False 897 else: 898 raise error.TestFail('Error running command %s' % cmd) 899 900 901def get_storage_error_msg(disk_name, reason): 902 """ 903 Get Error message for storage test which include disk model. 904 and also include the firmware version for the SCSI disk 905 906 @param disk_name: target disk 907 @param reason: Reason of the error. 908 """ 909 910 msg = reason 911 912 model = get_disk_model(disk_name) 913 msg += ' Disk model: %s' % model 914 915 if is_disk_scsi(disk_name): 916 fw = get_disk_firmware_version(disk_name) 917 msg += ' firmware: %s' % fw 918 919 return msg 920 921 922def load_module(module_name): 923 # Checks if a module has already been loaded 924 if module_is_loaded(module_name): 925 return False 926 927 utils.system('/sbin/modprobe ' + module_name) 928 return True 929 930 931def unload_module(module_name): 932 """ 933 Removes a module. Handles dependencies. If even then it's not possible 934 to remove one of the modules, it will trhow an error.CmdError exception. 935 936 @param module_name: Name of the module we want to remove. 937 """ 938 l_raw = utils.system_output("/bin/lsmod").splitlines() 939 lsmod = [x for x in l_raw if x.split()[0] == module_name] 940 if len(lsmod) > 0: 941 line_parts = lsmod[0].split() 942 if len(line_parts) == 4: 943 submodules = line_parts[3].split(",") 944 for submodule in submodules: 945 unload_module(submodule) 946 utils.system("/sbin/modprobe -r %s" % module_name) 947 logging.info("Module %s unloaded", module_name) 948 else: 949 logging.info("Module %s is already unloaded", module_name) 950 951 952def module_is_loaded(module_name): 953 module_name = module_name.replace('-', '_') 954 modules = utils.system_output('/bin/lsmod').splitlines() 955 for module in modules: 956 if module.startswith(module_name) and module[len(module_name)] == ' ': 957 return True 958 return False 959 960 961def get_loaded_modules(): 962 lsmod_output = utils.system_output('/bin/lsmod').splitlines()[1:] 963 return [line.split(None, 1)[0] for line in lsmod_output] 964 965 966def get_huge_page_size(): 967 output = utils.system_output('grep Hugepagesize /proc/meminfo') 968 return int(output.split()[1]) # Assumes units always in kB. :( 969 970 971def get_num_huge_pages(): 972 raw_hugepages = utils.system_output('/sbin/sysctl vm.nr_hugepages') 973 return int(raw_hugepages.split()[2]) 974 975 976def set_num_huge_pages(num): 977 utils.system('/sbin/sysctl vm.nr_hugepages=%d' % num) 978 979 980def ping_default_gateway(): 981 """Ping the default gateway.""" 982 983 network = open('/etc/sysconfig/network') 984 m = re.search('GATEWAY=(\S+)', network.read()) 985 986 if m: 987 gw = m.group(1) 988 cmd = 'ping %s -c 5 > /dev/null' % gw 989 return utils.system(cmd, ignore_status=True) 990 991 raise error.TestError('Unable to find default gateway') 992 993 994def drop_caches(): 995 """Writes back all dirty pages to disk and clears all the caches.""" 996 utils.system("sync") 997 # We ignore failures here as this will fail on 2.6.11 kernels. 998 utils.system("echo 3 > /proc/sys/vm/drop_caches", ignore_status=True) 999 1000 1001def process_is_alive(name_pattern): 1002 """ 1003 'pgrep name' misses all python processes and also long process names. 1004 'pgrep -f name' gets all shell commands with name in args. 1005 So look only for command whose initial pathname ends with name. 1006 Name itself is an egrep pattern, so it can use | etc for variations. 1007 """ 1008 return utils.system("pgrep -f '^([^ /]*/)*(%s)([ ]|$)'" % name_pattern, 1009 ignore_status=True) == 0 1010 1011 1012def get_hwclock_seconds(utc=True): 1013 """ 1014 Return the hardware clock in seconds as a floating point value. 1015 Use Coordinated Universal Time if utc is True, local time otherwise. 1016 Raise a ValueError if unable to read the hardware clock. 1017 """ 1018 cmd = '/sbin/hwclock --debug' 1019 if utc: 1020 cmd += ' --utc' 1021 hwclock_output = utils.system_output(cmd, ignore_status=True) 1022 match = re.search(r'= ([0-9]+) seconds since .+ (-?[0-9.]+) seconds$', 1023 hwclock_output, re.DOTALL) 1024 if match: 1025 seconds = int(match.group(1)) + float(match.group(2)) 1026 logging.debug('hwclock seconds = %f', seconds) 1027 return seconds 1028 1029 raise ValueError('Unable to read the hardware clock -- ' + 1030 hwclock_output) 1031 1032 1033def set_wake_alarm(alarm_time): 1034 """ 1035 Set the hardware RTC-based wake alarm to 'alarm_time'. 1036 """ 1037 utils.write_one_line('/sys/class/rtc/rtc0/wakealarm', str(alarm_time)) 1038 1039 1040def set_power_state(state): 1041 """ 1042 Set the system power state to 'state'. 1043 """ 1044 utils.write_one_line('/sys/power/state', state) 1045 1046 1047def standby(): 1048 """ 1049 Power-on suspend (S1) 1050 """ 1051 set_power_state('standby') 1052 1053 1054def suspend_to_ram(): 1055 """ 1056 Suspend the system to RAM (S3) 1057 """ 1058 set_power_state('mem') 1059 1060 1061def suspend_to_disk(): 1062 """ 1063 Suspend the system to disk (S4) 1064 """ 1065 set_power_state('disk') 1066