utils.py revision c02992f0e6fe58c6b57975f201100ceb3d71a7a5
1# Copyright 2017 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5""" 6Convenience functions for use by tests or whomever. 7""" 8 9# pylint: disable=missing-docstring 10 11import commands 12import fnmatch 13import glob 14import json 15import logging 16import math 17import multiprocessing 18import os 19import pickle 20import platform 21import re 22import shutil 23import signal 24import tempfile 25import time 26import uuid 27 28from autotest_lib.client.common_lib import error 29from autotest_lib.client.common_lib import magic 30from autotest_lib.client.common_lib import utils 31 32from autotest_lib.client.common_lib.utils import * 33 34 35def grep(pattern, file): 36 """ 37 This is mainly to fix the return code inversion from grep 38 Also handles compressed files. 39 40 returns 1 if the pattern is present in the file, 0 if not. 41 """ 42 command = 'grep "%s" > /dev/null' % pattern 43 ret = cat_file_to_cmd(file, command, ignore_status=True) 44 return not ret 45 46 47def difflist(list1, list2): 48 """returns items in list2 that are not in list1""" 49 diff = []; 50 for x in list2: 51 if x not in list1: 52 diff.append(x) 53 return diff 54 55 56def cat_file_to_cmd(file, command, ignore_status=0, return_output=False): 57 """ 58 equivalent to 'cat file | command' but knows to use 59 zcat or bzcat if appropriate 60 """ 61 if not os.path.isfile(file): 62 raise NameError('invalid file %s to cat to command %s' 63 % (file, command)) 64 65 if return_output: 66 run_cmd = utils.system_output 67 else: 68 run_cmd = utils.system 69 70 if magic.guess_type(file) == 'application/x-bzip2': 71 cat = 'bzcat' 72 elif magic.guess_type(file) == 'application/x-gzip': 73 cat = 'zcat' 74 else: 75 cat = 'cat' 76 return run_cmd('%s %s | %s' % (cat, file, command), 77 ignore_status=ignore_status) 78 79 80def extract_tarball_to_dir(tarball, dir): 81 """ 82 Extract a tarball to a specified directory name instead of whatever 83 the top level of a tarball is - useful for versioned directory names, etc 84 """ 85 if os.path.exists(dir): 86 if os.path.isdir(dir): 87 shutil.rmtree(dir) 88 else: 89 os.remove(dir) 90 pwd = os.getcwd() 91 os.chdir(os.path.dirname(os.path.abspath(dir))) 92 newdir = extract_tarball(tarball) 93 os.rename(newdir, dir) 94 os.chdir(pwd) 95 96 97def extract_tarball(tarball): 98 """Returns the directory extracted by the tarball.""" 99 extracted = cat_file_to_cmd(tarball, 'tar xvf - 2>/dev/null', 100 return_output=True).splitlines() 101 102 dir = None 103 104 for line in extracted: 105 if line.startswith('./'): 106 line = line[2:] 107 if not line or line == '.': 108 continue 109 topdir = line.split('/')[0] 110 if os.path.isdir(topdir): 111 if dir: 112 assert(dir == topdir) 113 else: 114 dir = topdir 115 if dir: 116 return dir 117 else: 118 raise NameError('extracting tarball produced no dir') 119 120 121def unmap_url_cache(cachedir, url, expected_hash, method="md5"): 122 """ 123 Downloads a file from a URL to a cache directory. If the file is already 124 at the expected position and has the expected hash, let's not download it 125 again. 126 127 @param cachedir: Directory that might hold a copy of the file we want to 128 download. 129 @param url: URL for the file we want to download. 130 @param expected_hash: Hash string that we expect the file downloaded to 131 have. 132 @param method: Method used to calculate the hash string (md5, sha1). 133 """ 134 # Let's convert cachedir to a canonical path, if it's not already 135 cachedir = os.path.realpath(cachedir) 136 if not os.path.isdir(cachedir): 137 try: 138 os.makedirs(cachedir) 139 except: 140 raise ValueError('Could not create cache directory %s' % cachedir) 141 file_from_url = os.path.basename(url) 142 file_local_path = os.path.join(cachedir, file_from_url) 143 144 file_hash = None 145 failure_counter = 0 146 while not file_hash == expected_hash: 147 if os.path.isfile(file_local_path): 148 file_hash = hash_file(file_local_path, method) 149 if file_hash == expected_hash: 150 # File is already at the expected position and ready to go 151 src = file_from_url 152 else: 153 # Let's download the package again, it's corrupted... 154 logging.error("Seems that file %s is corrupted, trying to " 155 "download it again", file_from_url) 156 src = url 157 failure_counter += 1 158 else: 159 # File is not there, let's download it 160 src = url 161 if failure_counter > 1: 162 raise EnvironmentError("Consistently failed to download the " 163 "package %s. Aborting further download " 164 "attempts. This might mean either the " 165 "network connection has problems or the " 166 "expected hash string that was determined " 167 "for this file is wrong", file_from_url) 168 file_path = utils.unmap_url(cachedir, src, cachedir) 169 170 return file_path 171 172 173def force_copy(src, dest): 174 """Replace dest with a new copy of src, even if it exists""" 175 if os.path.isfile(dest): 176 os.remove(dest) 177 if os.path.isdir(dest): 178 dest = os.path.join(dest, os.path.basename(src)) 179 shutil.copyfile(src, dest) 180 return dest 181 182 183def force_link(src, dest): 184 """Link src to dest, overwriting it if it exists""" 185 return utils.system("ln -sf %s %s" % (src, dest)) 186 187 188def file_contains_pattern(file, pattern): 189 """Return true if file contains the specified egrep pattern""" 190 if not os.path.isfile(file): 191 raise NameError('file %s does not exist' % file) 192 return not utils.system('egrep -q "' + pattern + '" ' + file, 193 ignore_status=True) 194 195 196def list_grep(list, pattern): 197 """True if any item in list matches the specified pattern.""" 198 compiled = re.compile(pattern) 199 for line in list: 200 match = compiled.search(line) 201 if (match): 202 return 1 203 return 0 204 205 206def get_os_vendor(): 207 """Try to guess what's the os vendor 208 """ 209 if os.path.isfile('/etc/SuSE-release'): 210 return 'SUSE' 211 212 issue = '/etc/issue' 213 214 if not os.path.isfile(issue): 215 return 'Unknown' 216 217 if file_contains_pattern(issue, 'Red Hat'): 218 return 'Red Hat' 219 elif file_contains_pattern(issue, 'Fedora'): 220 return 'Fedora Core' 221 elif file_contains_pattern(issue, 'SUSE'): 222 return 'SUSE' 223 elif file_contains_pattern(issue, 'Ubuntu'): 224 return 'Ubuntu' 225 elif file_contains_pattern(issue, 'Debian'): 226 return 'Debian' 227 else: 228 return 'Unknown' 229 230 231def get_cc(): 232 try: 233 return os.environ['CC'] 234 except KeyError: 235 return 'gcc' 236 237 238def get_vmlinux(): 239 """Return the full path to vmlinux 240 241 Ahem. This is crap. Pray harder. Bad Martin. 242 """ 243 vmlinux = '/boot/vmlinux-%s' % utils.system_output('uname -r') 244 if os.path.isfile(vmlinux): 245 return vmlinux 246 vmlinux = '/lib/modules/%s/build/vmlinux' % utils.system_output('uname -r') 247 if os.path.isfile(vmlinux): 248 return vmlinux 249 return None 250 251 252def get_systemmap(): 253 """Return the full path to System.map 254 255 Ahem. This is crap. Pray harder. Bad Martin. 256 """ 257 map = '/boot/System.map-%s' % utils.system_output('uname -r') 258 if os.path.isfile(map): 259 return map 260 map = '/lib/modules/%s/build/System.map' % utils.system_output('uname -r') 261 if os.path.isfile(map): 262 return map 263 return None 264 265 266def get_modules_dir(): 267 """Return the modules dir for the running kernel version""" 268 kernel_version = utils.system_output('uname -r') 269 return '/lib/modules/%s/kernel' % kernel_version 270 271 272_CPUINFO_RE = re.compile(r'^(?P<key>[^\t]*)\t*: ?(?P<value>.*)$') 273 274 275def get_cpuinfo(): 276 """Read /proc/cpuinfo and convert to a list of dicts.""" 277 cpuinfo = [] 278 with open('/proc/cpuinfo', 'r') as f: 279 cpu = {} 280 for line in f: 281 line = line.strip() 282 if not line: 283 cpuinfo.append(cpu) 284 cpu = {} 285 continue 286 match = _CPUINFO_RE.match(line) 287 cpu[match.group('key')] = match.group('value') 288 if cpu: 289 # cpuinfo usually ends in a blank line, so this shouldn't happen. 290 cpuinfo.append(cpu) 291 return cpuinfo 292 293 294def get_cpu_arch(): 295 """Work out which CPU architecture we're running on""" 296 f = open('/proc/cpuinfo', 'r') 297 cpuinfo = f.readlines() 298 f.close() 299 if list_grep(cpuinfo, '^cpu.*(RS64|POWER3|Broadband Engine)'): 300 return 'power' 301 elif list_grep(cpuinfo, '^cpu.*POWER4'): 302 return 'power4' 303 elif list_grep(cpuinfo, '^cpu.*POWER5'): 304 return 'power5' 305 elif list_grep(cpuinfo, '^cpu.*POWER6'): 306 return 'power6' 307 elif list_grep(cpuinfo, '^cpu.*POWER7'): 308 return 'power7' 309 elif list_grep(cpuinfo, '^cpu.*PPC970'): 310 return 'power970' 311 elif list_grep(cpuinfo, 'ARM'): 312 return 'arm' 313 elif list_grep(cpuinfo, '^flags.*:.* lm .*'): 314 return 'x86_64' 315 elif list_grep(cpuinfo, 'CPU.*implementer.*0x41'): 316 return 'arm' 317 else: 318 return 'i386' 319 320 321def get_arm_soc_family_from_devicetree(): 322 """ 323 Work out which ARM SoC we're running on based on the 'compatible' property 324 of the base node of devicetree, if it exists. 325 """ 326 devicetree_compatible = '/sys/firmware/devicetree/base/compatible' 327 if not os.path.isfile(devicetree_compatible): 328 return None 329 f = open(devicetree_compatible, 'r') 330 compatible = f.readlines() 331 f.close() 332 if list_grep(compatible, 'rk3399'): 333 return 'rockchip' 334 elif list_grep(compatible, 'mt8173'): 335 return 'mediatek' 336 return None 337 338 339def get_arm_soc_family(): 340 """Work out which ARM SoC we're running on""" 341 family = get_arm_soc_family_from_devicetree() 342 if family is not None: 343 return family 344 345 f = open('/proc/cpuinfo', 'r') 346 cpuinfo = f.readlines() 347 f.close() 348 if list_grep(cpuinfo, 'EXYNOS5'): 349 return 'exynos5' 350 elif list_grep(cpuinfo, 'Tegra'): 351 return 'tegra' 352 elif list_grep(cpuinfo, 'Rockchip'): 353 return 'rockchip' 354 return 'arm' 355 356 357def get_cpu_soc_family(): 358 """Like get_cpu_arch, but for ARM, returns the SoC family name""" 359 f = open('/proc/cpuinfo', 'r') 360 cpuinfo = f.readlines() 361 f.close() 362 family = get_cpu_arch() 363 if family == 'arm': 364 family = get_arm_soc_family() 365 if list_grep(cpuinfo, '^vendor_id.*:.*AMD'): 366 family = 'amd' 367 return family 368 369 370INTEL_UARCH_TABLE = { 371 '06_4C': 'Airmont', 372 '06_1C': 'Atom', 373 '06_26': 'Atom', 374 '06_27': 'Atom', 375 '06_35': 'Atom', 376 '06_36': 'Atom', 377 '06_3D': 'Broadwell', 378 '06_47': 'Broadwell', 379 '06_4F': 'Broadwell', 380 '06_56': 'Broadwell', 381 '06_0D': 'Dothan', 382 '06_5C': 'Goldmont', 383 '06_3C': 'Haswell', 384 '06_45': 'Haswell', 385 '06_46': 'Haswell', 386 '06_3F': 'Haswell-E', 387 '06_3A': 'Ivy Bridge', 388 '06_3E': 'Ivy Bridge-E', 389 '06_8E': 'Kaby Lake', 390 '06_9E': 'Kaby Lake', 391 '06_0F': 'Merom', 392 '06_16': 'Merom', 393 '06_17': 'Nehalem', 394 '06_1A': 'Nehalem', 395 '06_1D': 'Nehalem', 396 '06_1E': 'Nehalem', 397 '06_1F': 'Nehalem', 398 '06_2E': 'Nehalem', 399 '0F_03': 'Prescott', 400 '0F_04': 'Prescott', 401 '0F_06': 'Presler', 402 '06_2A': 'Sandy Bridge', 403 '06_2D': 'Sandy Bridge', 404 '06_37': 'Silvermont', 405 '06_4A': 'Silvermont', 406 '06_4D': 'Silvermont', 407 '06_5A': 'Silvermont', 408 '06_5D': 'Silvermont', 409 '06_4E': 'Skylake', 410 '06_5E': 'Skylake', 411 '06_55': 'Skylake', 412 '06_25': 'Westmere', 413 '06_2C': 'Westmere', 414 '06_2F': 'Westmere', 415} 416 417 418def get_intel_cpu_uarch(numeric=False): 419 """Return the Intel microarchitecture we're running on, or None. 420 421 Returns None if this is not an Intel CPU. Returns the family and model as 422 underscore-separated hex (per Intel manual convention) if the uarch is not 423 known, or if numeric is True. 424 """ 425 if not get_current_kernel_arch().startswith('x86'): 426 return None 427 cpuinfo = get_cpuinfo()[0] 428 if cpuinfo['vendor_id'] != 'GenuineIntel': 429 return None 430 family_model = '%02X_%02X' % (int(cpuinfo['cpu family']), 431 int(cpuinfo['model'])) 432 if numeric: 433 return family_model 434 return INTEL_UARCH_TABLE.get(family_model, family_model) 435 436 437def get_current_kernel_arch(): 438 """Get the machine architecture, now just a wrap of 'uname -m'.""" 439 return os.popen('uname -m').read().rstrip() 440 441 442def get_file_arch(filename): 443 # -L means follow symlinks 444 file_data = utils.system_output('file -L ' + filename) 445 if file_data.count('80386'): 446 return 'i386' 447 return None 448 449 450def count_cpus(): 451 """number of CPUs in the local machine according to /proc/cpuinfo""" 452 try: 453 return multiprocessing.cpu_count() 454 except Exception: 455 logging.exception('can not get cpu count from' 456 ' multiprocessing.cpu_count()') 457 cpuinfo = get_cpuinfo() 458 # Returns at least one cpu. Check comment #1 in crosbug.com/p/9582. 459 return len(cpuinfo) or 1 460 461 462def cpu_online_map(): 463 """ 464 Check out the available cpu online map 465 """ 466 cpuinfo = get_cpuinfo() 467 cpus = [] 468 for cpu in cpuinfo: 469 cpus.append(cpu['processor']) # grab cpu number 470 return cpus 471 472 473def get_cpu_family(): 474 cpuinfo = get_cpuinfo()[0] 475 return int(cpuinfo['cpu_family']) 476 477 478def get_cpu_vendor(): 479 cpuinfo = get_cpuinfo() 480 vendors = [cpu['vendor_id'] for cpu in cpuinfo] 481 for v in vendors[1:]: 482 if v != vendors[0]: 483 raise error.TestError('multiple cpu vendors found: ' + str(vendors)) 484 return vendors[0] 485 486 487def probe_cpus(): 488 """ 489 This routine returns a list of cpu devices found under 490 /sys/devices/system/cpu. 491 """ 492 cmd = 'find /sys/devices/system/cpu/ -maxdepth 1 -type d -name cpu*' 493 return utils.system_output(cmd).splitlines() 494 495 496# Returns total memory in kb 497def read_from_meminfo(key): 498 meminfo = utils.system_output('grep %s /proc/meminfo' % key) 499 return int(re.search(r'\d+', meminfo).group(0)) 500 501 502def memtotal(): 503 return read_from_meminfo('MemTotal') 504 505 506def freememtotal(): 507 return read_from_meminfo('MemFree') 508 509def usable_memtotal(): 510 # Reserved 5% for OS use 511 return int(read_from_meminfo('MemFree') * 0.95) 512 513 514def rounded_memtotal(): 515 # Get total of all physical mem, in kbytes 516 usable_kbytes = memtotal() 517 # usable_kbytes is system's usable DRAM in kbytes, 518 # as reported by memtotal() from device /proc/meminfo memtotal 519 # after Linux deducts 1.5% to 5.1% for system table overhead 520 # Undo the unknown actual deduction by rounding up 521 # to next small multiple of a big power-of-two 522 # eg 12GB - 5.1% gets rounded back up to 12GB 523 mindeduct = 0.015 # 1.5 percent 524 maxdeduct = 0.055 # 5.5 percent 525 # deduction range 1.5% .. 5.5% supports physical mem sizes 526 # 6GB .. 12GB in steps of .5GB 527 # 12GB .. 24GB in steps of 1 GB 528 # 24GB .. 48GB in steps of 2 GB ... 529 # Finer granularity in physical mem sizes would require 530 # tighter spread between min and max possible deductions 531 532 # increase mem size by at least min deduction, without rounding 533 min_kbytes = int(usable_kbytes / (1.0 - mindeduct)) 534 # increase mem size further by 2**n rounding, by 0..roundKb or more 535 round_kbytes = int(usable_kbytes / (1.0 - maxdeduct)) - min_kbytes 536 # find least binary roundup 2**n that covers worst-cast roundKb 537 mod2n = 1 << int(math.ceil(math.log(round_kbytes, 2))) 538 # have round_kbytes <= mod2n < round_kbytes*2 539 # round min_kbytes up to next multiple of mod2n 540 phys_kbytes = min_kbytes + mod2n - 1 541 phys_kbytes = phys_kbytes - (phys_kbytes % mod2n) # clear low bits 542 return phys_kbytes 543 544 545def sysctl(key, value=None): 546 """Generic implementation of sysctl, to read and write. 547 548 @param key: A location under /proc/sys 549 @param value: If not None, a value to write into the sysctl. 550 551 @return The single-line sysctl value as a string. 552 """ 553 path = '/proc/sys/%s' % key 554 if value is not None: 555 utils.write_one_line(path, str(value)) 556 return utils.read_one_line(path) 557 558 559def sysctl_kernel(key, value=None): 560 """(Very) partial implementation of sysctl, for kernel params""" 561 if value is not None: 562 # write 563 utils.write_one_line('/proc/sys/kernel/%s' % key, str(value)) 564 else: 565 # read 566 out = utils.read_one_line('/proc/sys/kernel/%s' % key) 567 return int(re.search(r'\d+', out).group(0)) 568 569 570def _convert_exit_status(sts): 571 if os.WIFSIGNALED(sts): 572 return -os.WTERMSIG(sts) 573 elif os.WIFEXITED(sts): 574 return os.WEXITSTATUS(sts) 575 else: 576 # impossible? 577 raise RuntimeError("Unknown exit status %d!" % sts) 578 579 580def where_art_thy_filehandles(): 581 """Dump the current list of filehandles""" 582 os.system("ls -l /proc/%d/fd >> /dev/tty" % os.getpid()) 583 584 585def print_to_tty(string): 586 """Output string straight to the tty""" 587 open('/dev/tty', 'w').write(string + '\n') 588 589 590def dump_object(object): 591 """Dump an object's attributes and methods 592 593 kind of like dir() 594 """ 595 for item in object.__dict__.iteritems(): 596 print item 597 try: 598 (key, value) = item 599 dump_object(value) 600 except: 601 continue 602 603 604def environ(env_key): 605 """return the requested environment variable, or '' if unset""" 606 if (os.environ.has_key(env_key)): 607 return os.environ[env_key] 608 else: 609 return '' 610 611 612def prepend_path(newpath, oldpath): 613 """prepend newpath to oldpath""" 614 if (oldpath): 615 return newpath + ':' + oldpath 616 else: 617 return newpath 618 619 620def append_path(oldpath, newpath): 621 """append newpath to oldpath""" 622 if (oldpath): 623 return oldpath + ':' + newpath 624 else: 625 return newpath 626 627 628_TIME_OUTPUT_RE = re.compile( 629 r'([\d\.]*)user ([\d\.]*)system ' 630 r'(\d*):([\d\.]*)elapsed (\d*)%CPU') 631 632 633def avgtime_print(dir): 634 """ Calculate some benchmarking statistics. 635 Input is a directory containing a file called 'time'. 636 File contains one-per-line results of /usr/bin/time. 637 Output is average Elapsed, User, and System time in seconds, 638 and average CPU percentage. 639 """ 640 user = system = elapsed = cpu = count = 0 641 with open(dir + "/time") as f: 642 for line in f: 643 try: 644 m = _TIME_OUTPUT_RE.match(line); 645 user += float(m.group(1)) 646 system += float(m.group(2)) 647 elapsed += (float(m.group(3)) * 60) + float(m.group(4)) 648 cpu += float(m.group(5)) 649 count += 1 650 except: 651 raise ValueError("badly formatted times") 652 653 return "Elapsed: %0.2fs User: %0.2fs System: %0.2fs CPU: %0.0f%%" % \ 654 (elapsed / count, user / count, system / count, cpu / count) 655 656 657def to_seconds(time_string): 658 """Converts a string in M+:SS.SS format to S+.SS""" 659 elts = time_string.split(':') 660 if len(elts) == 1: 661 return time_string 662 return str(int(elts[0]) * 60 + float(elts[1])) 663 664 665_TIME_OUTPUT_RE_2 = re.compile(r'(.*?)user (.*?)system (.*?)elapsed') 666 667 668def extract_all_time_results(results_string): 669 """Extract user, system, and elapsed times into a list of tuples""" 670 results = [] 671 for result in _TIME_OUTPUT_RE_2.findall(results_string): 672 results.append(tuple([to_seconds(elt) for elt in result])) 673 return results 674 675 676def running_config(): 677 """ 678 Return path of config file of the currently running kernel 679 """ 680 version = utils.system_output('uname -r') 681 for config in ('/proc/config.gz', \ 682 '/boot/config-%s' % version, 683 '/lib/modules/%s/build/.config' % version): 684 if os.path.isfile(config): 685 return config 686 return None 687 688 689def check_for_kernel_feature(feature): 690 config = running_config() 691 692 if not config: 693 raise TypeError("Can't find kernel config file") 694 695 if magic.guess_type(config) == 'application/x-gzip': 696 grep = 'zgrep' 697 else: 698 grep = 'grep' 699 grep += ' ^CONFIG_%s= %s' % (feature, config) 700 701 if not utils.system_output(grep, ignore_status=True): 702 raise ValueError("Kernel doesn't have a %s feature" % (feature)) 703 704 705def check_glibc_ver(ver): 706 glibc_ver = commands.getoutput('ldd --version').splitlines()[0] 707 glibc_ver = re.search(r'(\d+\.\d+(\.\d+)?)', glibc_ver).group() 708 if utils.compare_versions(glibc_ver, ver) == -1: 709 raise error.TestError("Glibc too old (%s). Glibc >= %s is needed." % 710 (glibc_ver, ver)) 711 712def check_kernel_ver(ver): 713 kernel_ver = utils.system_output('uname -r') 714 kv_tmp = re.split(r'[-]', kernel_ver)[0:3] 715 # In compare_versions, if v1 < v2, return value == -1 716 if utils.compare_versions(kv_tmp[0], ver) == -1: 717 raise error.TestError("Kernel too old (%s). Kernel > %s is needed." % 718 (kernel_ver, ver)) 719 720 721def human_format(number): 722 # Convert number to kilo / mega / giga format. 723 if number < 1024: 724 return "%d" % number 725 kilo = float(number) / 1024.0 726 if kilo < 1024: 727 return "%.2fk" % kilo 728 meg = kilo / 1024.0 729 if meg < 1024: 730 return "%.2fM" % meg 731 gig = meg / 1024.0 732 return "%.2fG" % gig 733 734 735def numa_nodes(): 736 node_paths = glob.glob('/sys/devices/system/node/node*') 737 nodes = [int(re.sub(r'.*node(\d+)', r'\1', x)) for x in node_paths] 738 return (sorted(nodes)) 739 740 741def node_size(): 742 nodes = max(len(numa_nodes()), 1) 743 return ((memtotal() * 1024) / nodes) 744 745 746def pickle_load(filename): 747 return pickle.load(open(filename, 'r')) 748 749 750# Return the kernel version and build timestamp. 751def running_os_release(): 752 return os.uname()[2:4] 753 754 755def running_os_ident(): 756 (version, timestamp) = running_os_release() 757 return version + '::' + timestamp 758 759 760def running_os_full_version(): 761 (version, timestamp) = running_os_release() 762 return version 763 764 765# much like find . -name 'pattern' 766def locate(pattern, root=os.getcwd()): 767 for path, dirs, files in os.walk(root): 768 for f in files: 769 if fnmatch.fnmatch(f, pattern): 770 yield os.path.abspath(os.path.join(path, f)) 771 772 773def freespace(path): 774 """Return the disk free space, in bytes""" 775 s = os.statvfs(path) 776 return s.f_bavail * s.f_bsize 777 778 779def disk_block_size(path): 780 """Return the disk block size, in bytes""" 781 return os.statvfs(path).f_bsize 782 783 784_DISK_PARTITION_3_RE = re.compile(r'^(/dev/hd[a-z]+)3', re.M) 785 786def get_disks(): 787 df_output = utils.system_output('df') 788 return _DISK_PARTITION_3_RE.findall(df_output) 789 790 791def get_disk_size(disk_name): 792 """ 793 Return size of disk in byte. Return 0 in Error Case 794 795 @param disk_name: disk name to find size 796 """ 797 device = os.path.basename(disk_name) 798 for line in file('/proc/partitions'): 799 try: 800 _, _, blocks, name = re.split(r' +', line.strip()) 801 except ValueError: 802 continue 803 if name == device: 804 return 1024 * int(blocks) 805 return 0 806 807 808def get_disk_size_gb(disk_name): 809 """ 810 Return size of disk in GB (10^9). Return 0 in Error Case 811 812 @param disk_name: disk name to find size 813 """ 814 return int(get_disk_size(disk_name) / (10.0 ** 9) + 0.5) 815 816 817def get_disk_model(disk_name): 818 """ 819 Return model name for internal storage device 820 821 @param disk_name: disk name to find model 822 """ 823 cmd1 = 'udevadm info --query=property --name=%s' % disk_name 824 cmd2 = 'grep -E "ID_(NAME|MODEL)="' 825 cmd3 = 'cut -f 2 -d"="' 826 cmd = ' | '.join([cmd1, cmd2, cmd3]) 827 return utils.system_output(cmd) 828 829 830_DISK_DEV_RE = re.compile(r'/dev/sd[a-z]|' 831 r'/dev/mmcblk[0-9]+|' 832 r'/dev/nvme[0-9]+n[0-9]+') 833 834 835def get_disk_from_filename(filename): 836 """ 837 Return the disk device the filename is on. 838 If the file is on tmpfs or other special file systems, 839 return None. 840 841 @param filename: name of file, full path. 842 """ 843 844 if not os.path.exists(filename): 845 raise error.TestError('file %s missing' % filename) 846 847 if filename[0] != '/': 848 raise error.TestError('This code works only with full path') 849 850 m = _DISK_DEV_RE.match(filename) 851 while not m: 852 if filename[0] != '/': 853 return None 854 if filename == '/dev/root': 855 cmd = 'rootdev -d -s' 856 elif filename.startswith('/dev/mapper'): 857 cmd = 'dmsetup table "%s"' % os.path.basename(filename) 858 dmsetup_output = utils.system_output(cmd).split(' ') 859 if dmsetup_output[2] == 'verity': 860 maj_min = dmsetup_output[4] 861 elif dmsetup_output[2] == 'crypt': 862 maj_min = dmsetup_output[6] 863 cmd = 'realpath "/dev/block/%s"' % maj_min 864 elif filename.startswith('/dev/loop'): 865 cmd = 'losetup -O BACK-FILE "%s" | tail -1' % filename 866 else: 867 cmd = 'df "%s" | tail -1 | cut -f 1 -d" "' % filename 868 filename = utils.system_output(cmd) 869 m = _DISK_DEV_RE.match(filename) 870 return m.group(0) 871 872 873def get_disk_firmware_version(disk_name): 874 """ 875 Return firmware version for internal storage device. (empty string for eMMC) 876 877 @param disk_name: disk name to find model 878 """ 879 cmd1 = 'udevadm info --query=property --name=%s' % disk_name 880 cmd2 = 'grep -E "ID_REVISION="' 881 cmd3 = 'cut -f 2 -d"="' 882 cmd = ' | '.join([cmd1, cmd2, cmd3]) 883 return utils.system_output(cmd) 884 885 886def is_disk_scsi(disk_name): 887 """ 888 Return true if disk is a scsi device, return false otherwise 889 890 @param disk_name: disk name check 891 """ 892 return re.match('/dev/sd[a-z]+', disk_name) 893 894 895def is_disk_harddisk(disk_name): 896 """ 897 Return true if disk is a harddisk, return false otherwise 898 899 @param disk_name: disk name check 900 """ 901 cmd1 = 'udevadm info --query=property --name=%s' % disk_name 902 cmd2 = 'grep -E "ID_ATA_ROTATION_RATE_RPM="' 903 cmd3 = 'cut -f 2 -d"="' 904 cmd = ' | '.join([cmd1, cmd2, cmd3]) 905 906 rtt = utils.system_output(cmd) 907 908 # eMMC will not have this field; rtt == '' 909 # SSD will have zero rotation rate; rtt == '0' 910 # For harddisk rtt > 0 911 return rtt and int(rtt) > 0 912 913 914def verify_hdparm_feature(disk_name, feature): 915 """ 916 Check for feature support for SCSI disk using hdparm 917 918 @param disk_name: target disk 919 @param feature: hdparm output string of the feature 920 """ 921 cmd = 'hdparm -I %s | grep -q "%s"' % (disk_name, feature) 922 ret = utils.system(cmd, ignore_status=True) 923 if ret == 0: 924 return True 925 elif ret == 1: 926 return False 927 else: 928 raise error.TestFail('Error running command %s' % cmd) 929 930 931def get_storage_error_msg(disk_name, reason): 932 """ 933 Get Error message for storage test which include disk model. 934 and also include the firmware version for the SCSI disk 935 936 @param disk_name: target disk 937 @param reason: Reason of the error. 938 """ 939 940 msg = reason 941 942 model = get_disk_model(disk_name) 943 msg += ' Disk model: %s' % model 944 945 if is_disk_scsi(disk_name): 946 fw = get_disk_firmware_version(disk_name) 947 msg += ' firmware: %s' % fw 948 949 return msg 950 951 952def load_module(module_name, params=None): 953 # Checks if a module has already been loaded 954 if module_is_loaded(module_name): 955 return False 956 957 cmd = '/sbin/modprobe ' + module_name 958 if params: 959 cmd += ' ' + params 960 utils.system(cmd) 961 return True 962 963 964def unload_module(module_name): 965 """ 966 Removes a module. Handles dependencies. If even then it's not possible 967 to remove one of the modules, it will trhow an error.CmdError exception. 968 969 @param module_name: Name of the module we want to remove. 970 """ 971 l_raw = utils.system_output("/bin/lsmod").splitlines() 972 lsmod = [x for x in l_raw if x.split()[0] == module_name] 973 if len(lsmod) > 0: 974 line_parts = lsmod[0].split() 975 if len(line_parts) == 4: 976 submodules = line_parts[3].split(",") 977 for submodule in submodules: 978 unload_module(submodule) 979 utils.system("/sbin/modprobe -r %s" % module_name) 980 logging.info("Module %s unloaded", module_name) 981 else: 982 logging.info("Module %s is already unloaded", module_name) 983 984 985def module_is_loaded(module_name): 986 module_name = module_name.replace('-', '_') 987 modules = utils.system_output('/bin/lsmod').splitlines() 988 for module in modules: 989 if module.startswith(module_name) and module[len(module_name)] == ' ': 990 return True 991 return False 992 993 994def get_loaded_modules(): 995 lsmod_output = utils.system_output('/bin/lsmod').splitlines()[1:] 996 return [line.split(None, 1)[0] for line in lsmod_output] 997 998 999def get_huge_page_size(): 1000 output = utils.system_output('grep Hugepagesize /proc/meminfo') 1001 return int(output.split()[1]) # Assumes units always in kB. :( 1002 1003 1004def get_num_huge_pages(): 1005 raw_hugepages = utils.system_output('/sbin/sysctl vm.nr_hugepages') 1006 return int(raw_hugepages.split()[2]) 1007 1008 1009def set_num_huge_pages(num): 1010 utils.system('/sbin/sysctl vm.nr_hugepages=%d' % num) 1011 1012 1013def ping_default_gateway(): 1014 """Ping the default gateway.""" 1015 1016 network = open('/etc/sysconfig/network') 1017 m = re.search('GATEWAY=(\S+)', network.read()) 1018 1019 if m: 1020 gw = m.group(1) 1021 cmd = 'ping %s -c 5 > /dev/null' % gw 1022 return utils.system(cmd, ignore_status=True) 1023 1024 raise error.TestError('Unable to find default gateway') 1025 1026 1027def drop_caches(): 1028 """Writes back all dirty pages to disk and clears all the caches.""" 1029 utils.system("sync") 1030 # We ignore failures here as this will fail on 2.6.11 kernels. 1031 utils.system("echo 3 > /proc/sys/vm/drop_caches", ignore_status=True) 1032 1033 1034def process_is_alive(name_pattern): 1035 """ 1036 'pgrep name' misses all python processes and also long process names. 1037 'pgrep -f name' gets all shell commands with name in args. 1038 So look only for command whose initial pathname ends with name. 1039 Name itself is an egrep pattern, so it can use | etc for variations. 1040 """ 1041 return utils.system("pgrep -f '^([^ /]*/)*(%s)([ ]|$)'" % name_pattern, 1042 ignore_status=True) == 0 1043 1044 1045def get_hwclock_seconds(utc=True): 1046 """ 1047 Return the hardware clock in seconds as a floating point value. 1048 Use Coordinated Universal Time if utc is True, local time otherwise. 1049 Raise a ValueError if unable to read the hardware clock. 1050 """ 1051 cmd = '/sbin/hwclock --debug' 1052 if utc: 1053 cmd += ' --utc' 1054 hwclock_output = utils.system_output(cmd, ignore_status=True) 1055 match = re.search(r'= ([0-9]+) seconds since .+ (-?[0-9.]+) seconds$', 1056 hwclock_output, re.DOTALL) 1057 if match: 1058 seconds = int(match.group(1)) + float(match.group(2)) 1059 logging.debug('hwclock seconds = %f', seconds) 1060 return seconds 1061 1062 raise ValueError('Unable to read the hardware clock -- ' + 1063 hwclock_output) 1064 1065 1066def set_wake_alarm(alarm_time): 1067 """ 1068 Set the hardware RTC-based wake alarm to 'alarm_time'. 1069 """ 1070 utils.write_one_line('/sys/class/rtc/rtc0/wakealarm', str(alarm_time)) 1071 1072 1073def set_power_state(state): 1074 """ 1075 Set the system power state to 'state'. 1076 """ 1077 utils.write_one_line('/sys/power/state', state) 1078 1079 1080def standby(): 1081 """ 1082 Power-on suspend (S1) 1083 """ 1084 set_power_state('standby') 1085 1086 1087def suspend_to_ram(): 1088 """ 1089 Suspend the system to RAM (S3) 1090 """ 1091 set_power_state('mem') 1092 1093 1094def suspend_to_disk(): 1095 """ 1096 Suspend the system to disk (S4) 1097 """ 1098 set_power_state('disk') 1099 1100 1101_AUTOTEST_CLIENT_PATH = os.path.join(os.path.dirname(__file__), '..') 1102_AMD_PCI_IDS_FILE_PATH = os.path.join(_AUTOTEST_CLIENT_PATH, 1103 'bin/amd_pci_ids.json') 1104_INTEL_PCI_IDS_FILE_PATH = os.path.join(_AUTOTEST_CLIENT_PATH, 1105 'bin/intel_pci_ids.json') 1106_UI_USE_FLAGS_FILE_PATH = '/etc/ui_use_flags.txt' 1107 1108# Command to check if a package is installed. If the package is not installed 1109# the command shall fail. 1110_CHECK_PACKAGE_INSTALLED_COMMAND =( 1111 "dpkg-query -W -f='${Status}\n' %s | head -n1 | awk '{print $3;}' | " 1112 "grep -q '^installed$'") 1113 1114pciid_to_amd_architecture = {} 1115pciid_to_intel_architecture = {} 1116 1117class Crossystem(object): 1118 """A wrapper for the crossystem utility.""" 1119 1120 def __init__(self, client): 1121 self.cros_system_data = {} 1122 self._client = client 1123 1124 def init(self): 1125 self.cros_system_data = {} 1126 (_, fname) = tempfile.mkstemp() 1127 f = open(fname, 'w') 1128 self._client.run('crossystem', stdout_tee=f) 1129 f.close() 1130 text = utils.read_file(fname) 1131 for line in text.splitlines(): 1132 assignment_string = line.split('#')[0] 1133 if not assignment_string.count('='): 1134 continue 1135 (name, value) = assignment_string.split('=', 1) 1136 self.cros_system_data[name.strip()] = value.strip() 1137 os.remove(fname) 1138 1139 def __getattr__(self, name): 1140 """ 1141 Retrieve a crosssystem attribute. 1142 1143 The call crossystemobject.name() will return the crossystem reported 1144 string. 1145 """ 1146 return lambda: self.cros_system_data[name] 1147 1148 1149def get_oldest_pid_by_name(name): 1150 """ 1151 Return the oldest pid of a process whose name perfectly matches |name|. 1152 1153 name is an egrep expression, which will be matched against the entire name 1154 of processes on the system. For example: 1155 1156 get_oldest_pid_by_name('chrome') 1157 1158 on a system running 1159 8600 ? 00:00:04 chrome 1160 8601 ? 00:00:00 chrome 1161 8602 ? 00:00:00 chrome-sandbox 1162 1163 would return 8600, as that's the oldest process that matches. 1164 chrome-sandbox would not be matched. 1165 1166 Arguments: 1167 name: egrep expression to match. Will be anchored at the beginning and 1168 end of the match string. 1169 1170 Returns: 1171 pid as an integer, or None if one cannot be found. 1172 1173 Raises: 1174 ValueError if pgrep returns something odd. 1175 """ 1176 str_pid = utils.system_output('pgrep -o ^%s$' % name, 1177 ignore_status=True).rstrip() 1178 if str_pid: 1179 return int(str_pid) 1180 1181 1182def get_oldest_by_name(name): 1183 """Return pid and command line of oldest process whose name matches |name|. 1184 1185 @param name: egrep expression to match desired process name. 1186 @return: A tuple of (pid, command_line) of the oldest process whose name 1187 matches |name|. 1188 1189 """ 1190 pid = get_oldest_pid_by_name(name) 1191 if pid: 1192 command_line = utils.system_output('ps -p %i -o command=' % pid, 1193 ignore_status=True).rstrip() 1194 return (pid, command_line) 1195 1196 1197def get_chrome_remote_debugging_port(): 1198 """Returns remote debugging port for Chrome. 1199 1200 Parse chrome process's command line argument to get the remote debugging 1201 port. if it is 0, look at DevToolsActivePort for the ephemeral port. 1202 """ 1203 _, command = get_oldest_by_name('chrome') 1204 matches = re.search('--remote-debugging-port=([0-9]+)', command) 1205 if not matches: 1206 return 0 1207 port = int(matches.group(1)) 1208 if port: 1209 return port 1210 with open('/home/chronos/DevToolsActivePort') as f: 1211 return int(f.readline().rstrip()) 1212 1213 1214def get_process_list(name, command_line=None): 1215 """ 1216 Return the list of pid for matching process |name command_line|. 1217 1218 on a system running 1219 31475 ? 0:06 /opt/google/chrome/chrome --allow-webui-compositing - 1220 31478 ? 0:00 /opt/google/chrome/chrome-sandbox /opt/google/chrome/ 1221 31485 ? 0:00 /opt/google/chrome/chrome --type=zygote --log-level=1 1222 31532 ? 1:05 /opt/google/chrome/chrome --type=renderer 1223 1224 get_process_list('chrome') 1225 would return ['31475', '31485', '31532'] 1226 1227 get_process_list('chrome', '--type=renderer') 1228 would return ['31532'] 1229 1230 Arguments: 1231 name: process name to search for. If command_line is provided, name is 1232 matched against full command line. If command_line is not provided, 1233 name is only matched against the process name. 1234 command line: when command line is passed, the full process command line 1235 is used for matching. 1236 1237 Returns: 1238 list of PIDs of the matching processes. 1239 1240 """ 1241 # TODO(rohitbm) crbug.com/268861 1242 flag = '-x' if not command_line else '-f' 1243 name = '\'%s.*%s\'' % (name, command_line) if command_line else name 1244 str_pid = utils.system_output('pgrep %s %s' % (flag, name), 1245 ignore_status=True).rstrip() 1246 return str_pid.split() 1247 1248 1249def nuke_process_by_name(name, with_prejudice=False): 1250 """Tell the oldest process specified by name to exit. 1251 1252 Arguments: 1253 name: process name specifier, as understood by pgrep. 1254 with_prejudice: if True, don't allow for graceful exit. 1255 1256 Raises: 1257 error.AutoservPidAlreadyDeadError: no existing process matches name. 1258 """ 1259 try: 1260 pid = get_oldest_pid_by_name(name) 1261 except Exception as e: 1262 logging.error(e) 1263 return 1264 if pid is None: 1265 raise error.AutoservPidAlreadyDeadError('No process matching %s.' % 1266 name) 1267 if with_prejudice: 1268 utils.nuke_pid(pid, [signal.SIGKILL]) 1269 else: 1270 utils.nuke_pid(pid) 1271 1272 1273def ensure_processes_are_dead_by_name(name, timeout_sec=10): 1274 """Terminate all processes specified by name and ensure they're gone. 1275 1276 Arguments: 1277 name: process name specifier, as understood by pgrep. 1278 timeout_sec: maximum number of seconds to wait for processes to die. 1279 1280 Raises: 1281 error.AutoservPidAlreadyDeadError: no existing process matches name. 1282 utils.TimeoutError: if processes still exist after timeout_sec. 1283 """ 1284 1285 def list_and_kill_processes(name): 1286 process_list = get_process_list(name) 1287 try: 1288 for pid in [int(str_pid) for str_pid in process_list]: 1289 utils.nuke_pid(pid) 1290 except error.AutoservPidAlreadyDeadError: 1291 pass 1292 return process_list 1293 1294 utils.poll_for_condition(lambda: list_and_kill_processes(name) == [], 1295 timeout=timeout_sec) 1296 1297 1298def is_virtual_machine(): 1299 return 'QEMU' in platform.processor() 1300 1301 1302def save_vm_state(checkpoint): 1303 """Saves the current state of the virtual machine. 1304 1305 This function is a NOOP if the test is not running under a virtual machine 1306 with the USB serial port redirected. 1307 1308 Arguments: 1309 checkpoint - Name used to identify this state 1310 1311 Returns: 1312 None 1313 """ 1314 # The QEMU monitor has been redirected to the guest serial port located at 1315 # /dev/ttyUSB0. To save the state of the VM, we just send the 'savevm' 1316 # command to the serial port. 1317 if is_virtual_machine() and os.path.exists('/dev/ttyUSB0'): 1318 logging.info('Saving VM state "%s"', checkpoint) 1319 serial = open('/dev/ttyUSB0', 'w') 1320 serial.write('savevm %s\r\n' % checkpoint) 1321 logging.info('Done saving VM state "%s"', checkpoint) 1322 1323 1324def check_raw_dmesg(dmesg, message_level, whitelist): 1325 """Checks dmesg for unexpected warnings. 1326 1327 This function parses dmesg for message with message_level <= message_level 1328 which do not appear in the whitelist. 1329 1330 Arguments: 1331 dmesg - string containing raw dmesg buffer 1332 message_level - minimum message priority to check 1333 whitelist - messages to ignore 1334 1335 Returns: 1336 List of unexpected warnings 1337 """ 1338 whitelist_re = re.compile(r'(%s)' % '|'.join(whitelist)) 1339 unexpected = [] 1340 for line in dmesg.splitlines(): 1341 if int(line[1]) <= message_level: 1342 stripped_line = line.split('] ', 1)[1] 1343 if whitelist_re.search(stripped_line): 1344 continue 1345 unexpected.append(stripped_line) 1346 return unexpected 1347 1348 1349def verify_mesg_set(mesg, regex, whitelist): 1350 """Verifies that the exact set of messages are present in a text. 1351 1352 This function finds all strings in the text matching a certain regex, and 1353 then verifies that all expected strings are present in the set, and no 1354 unexpected strings are there. 1355 1356 Arguments: 1357 mesg - the mutiline text to be scanned 1358 regex - regular expression to match 1359 whitelist - messages to find in the output, a list of strings 1360 (potentially regexes) to look for in the filtered output. All these 1361 strings must be there, and no other strings should be present in the 1362 filtered output. 1363 1364 Returns: 1365 string of inconsistent findings (i.e. an empty string on success). 1366 """ 1367 1368 rv = [] 1369 1370 missing_strings = [] 1371 present_strings = [] 1372 for line in mesg.splitlines(): 1373 if not re.search(r'%s' % regex, line): 1374 continue 1375 present_strings.append(line.split('] ', 1)[1]) 1376 1377 for string in whitelist: 1378 for present_string in list(present_strings): 1379 if re.search(r'^%s$' % string, present_string): 1380 present_strings.remove(present_string) 1381 break 1382 else: 1383 missing_strings.append(string) 1384 1385 if present_strings: 1386 rv.append('unexpected strings:') 1387 rv.extend(present_strings) 1388 if missing_strings: 1389 rv.append('missing strings:') 1390 rv.extend(missing_strings) 1391 1392 return '\n'.join(rv) 1393 1394 1395def target_is_pie(): 1396 """Returns whether the toolchain produces a PIE (position independent 1397 executable) by default. 1398 1399 Arguments: 1400 None 1401 1402 Returns: 1403 True if the target toolchain produces a PIE by default. 1404 False otherwise. 1405 """ 1406 1407 command = 'echo | ${CC} -E -dD -P - | grep -i pie' 1408 result = utils.system_output(command, 1409 retain_output=True, 1410 ignore_status=True) 1411 if re.search('#define __PIE__', result): 1412 return True 1413 else: 1414 return False 1415 1416 1417def target_is_x86(): 1418 """Returns whether the toolchain produces an x86 object 1419 1420 Arguments: 1421 None 1422 1423 Returns: 1424 True if the target toolchain produces an x86 object 1425 False otherwise. 1426 """ 1427 1428 command = 'echo | ${CC} -E -dD -P - | grep -i 86' 1429 result = utils.system_output(command, 1430 retain_output=True, 1431 ignore_status=True) 1432 if re.search('__i386__', result) or re.search('__x86_64__', result): 1433 return True 1434 else: 1435 return False 1436 1437 1438def mounts(): 1439 ret = [] 1440 for line in file('/proc/mounts'): 1441 m = re.match( 1442 r'(?P<src>\S+) (?P<dest>\S+) (?P<type>\S+) (?P<opts>\S+).*', line) 1443 if m: 1444 ret.append(m.groupdict()) 1445 return ret 1446 1447 1448def is_mountpoint(path): 1449 return path in [m['dest'] for m in mounts()] 1450 1451 1452def require_mountpoint(path): 1453 """ 1454 Raises an exception if path is not a mountpoint. 1455 """ 1456 if not is_mountpoint(path): 1457 raise error.TestFail('Path not mounted: "%s"' % path) 1458 1459 1460def random_username(): 1461 return str(uuid.uuid4()) + '@example.com' 1462 1463 1464def get_signin_credentials(filepath): 1465 """Returns user_id, password tuple from credentials file at filepath. 1466 1467 File must have one line of the format user_id:password 1468 1469 @param filepath: path of credentials file. 1470 @return user_id, password tuple. 1471 """ 1472 user_id, password = None, None 1473 if os.path.isfile(filepath): 1474 with open(filepath) as f: 1475 user_id, password = f.read().rstrip().split(':') 1476 return user_id, password 1477 1478 1479def parse_cmd_output(command, run_method=utils.run): 1480 """Runs a command on a host object to retrieve host attributes. 1481 1482 The command should output to stdout in the format of: 1483 <key> = <value> # <optional_comment> 1484 1485 1486 @param command: Command to execute on the host. 1487 @param run_method: Function to use to execute the command. Defaults to 1488 utils.run so that the command will be executed locally. 1489 Can be replace with a host.run call so that it will 1490 execute on a DUT or external machine. Method must accept 1491 a command argument, stdout_tee and stderr_tee args and 1492 return a result object with a string attribute stdout 1493 which will be parsed. 1494 1495 @returns a dictionary mapping host attributes to their values. 1496 """ 1497 result = {} 1498 # Suppresses stdout so that the files are not printed to the logs. 1499 cmd_result = run_method(command, stdout_tee=None, stderr_tee=None) 1500 for line in cmd_result.stdout.splitlines(): 1501 # Lines are of the format "<key> = <value> # <comment>" 1502 key_value = re.match(r'^\s*(?P<key>[^ ]+)\s*=\s*(?P<value>[^ ' 1503 r']+)(?:\s*#.*)?$', line) 1504 if key_value: 1505 result[key_value.group('key')] = key_value.group('value') 1506 return result 1507 1508 1509def set_from_keyval_output(out, delimiter=' '): 1510 """Parse delimiter-separated key-val output into a set of tuples. 1511 1512 Output is expected to be multiline text output from a command. 1513 Stuffs the key-vals into tuples in a set to be later compared. 1514 1515 e.g. deactivated 0 1516 disableForceClear 0 1517 ==> set(('deactivated', '0'), ('disableForceClear', '0')) 1518 1519 @param out: multiple lines of space-separated key-val pairs. 1520 @param delimiter: character that separates key from val. Usually a 1521 space but may be '=' or something else. 1522 @return set of key-val tuples. 1523 """ 1524 results = set() 1525 kv_match_re = re.compile('([^ ]+)%s(.*)' % delimiter) 1526 for linecr in out.splitlines(): 1527 match = kv_match_re.match(linecr.strip()) 1528 if match: 1529 results.add((match.group(1), match.group(2))) 1530 return results 1531 1532 1533def get_cpu_usage(): 1534 """Returns machine's CPU usage. 1535 1536 This function uses /proc/stat to identify CPU usage. 1537 Returns: 1538 A dictionary with 'user', 'nice', 'system' and 'idle' values. 1539 Sample dictionary: 1540 { 1541 'user': 254544, 1542 'nice': 9, 1543 'system': 254768, 1544 'idle': 2859878, 1545 } 1546 """ 1547 proc_stat = open('/proc/stat') 1548 cpu_usage_str = proc_stat.readline().split() 1549 proc_stat.close() 1550 return { 1551 'user': int(cpu_usage_str[1]), 1552 'nice': int(cpu_usage_str[2]), 1553 'system': int(cpu_usage_str[3]), 1554 'idle': int(cpu_usage_str[4]) 1555 } 1556 1557 1558def compute_active_cpu_time(cpu_usage_start, cpu_usage_end): 1559 """Computes the fraction of CPU time spent non-idling. 1560 1561 This function should be invoked using before/after values from calls to 1562 get_cpu_usage(). 1563 """ 1564 time_active_end = ( 1565 cpu_usage_end['user'] + cpu_usage_end['nice'] + cpu_usage_end['system']) 1566 time_active_start = (cpu_usage_start['user'] + cpu_usage_start['nice'] + 1567 cpu_usage_start['system']) 1568 total_time_end = (cpu_usage_end['user'] + cpu_usage_end['nice'] + 1569 cpu_usage_end['system'] + cpu_usage_end['idle']) 1570 total_time_start = (cpu_usage_start['user'] + cpu_usage_start['nice'] + 1571 cpu_usage_start['system'] + cpu_usage_start['idle']) 1572 return ((float(time_active_end) - time_active_start) / 1573 (total_time_end - total_time_start)) 1574 1575 1576def is_pgo_mode(): 1577 return 'USE_PGO' in os.environ 1578 1579 1580def wait_for_idle_cpu(timeout, utilization): 1581 """Waits for the CPU to become idle (< utilization). 1582 1583 Args: 1584 timeout: The longest time in seconds to wait before throwing an error. 1585 utilization: The CPU usage below which the system should be considered 1586 idle (between 0 and 1.0 independent of cores/hyperthreads). 1587 """ 1588 time_passed = 0.0 1589 fraction_active_time = 1.0 1590 sleep_time = 1 1591 logging.info('Starting to wait up to %.1fs for idle CPU...', timeout) 1592 while fraction_active_time >= utilization: 1593 cpu_usage_start = get_cpu_usage() 1594 # Split timeout interval into not too many chunks to limit log spew. 1595 # Start at 1 second, increase exponentially 1596 time.sleep(sleep_time) 1597 time_passed += sleep_time 1598 sleep_time = min(16.0, 2.0 * sleep_time) 1599 cpu_usage_end = get_cpu_usage() 1600 fraction_active_time = \ 1601 compute_active_cpu_time(cpu_usage_start, cpu_usage_end) 1602 logging.info('After waiting %.1fs CPU utilization is %.3f.', 1603 time_passed, fraction_active_time) 1604 if time_passed > timeout: 1605 logging.warning('CPU did not become idle.') 1606 log_process_activity() 1607 # crosbug.com/37389 1608 if is_pgo_mode(): 1609 logging.info('Still continuing because we are in PGO mode.') 1610 return True 1611 1612 return False 1613 logging.info('Wait for idle CPU took %.1fs (utilization = %.3f).', 1614 time_passed, fraction_active_time) 1615 return True 1616 1617 1618def log_process_activity(): 1619 """Logs the output of top. 1620 1621 Useful to debug performance tests and to find runaway processes. 1622 """ 1623 logging.info('Logging current process activity using top and ps.') 1624 cmd = 'top -b -n1 -c' 1625 output = utils.run(cmd) 1626 logging.info(output) 1627 output = utils.run('ps axl') 1628 logging.info(output) 1629 1630 1631def wait_for_cool_machine(): 1632 """ 1633 A simple heuristic to wait for a machine to cool. 1634 The code looks a bit 'magic', but we don't know ambient temperature 1635 nor machine characteristics and still would like to return the caller 1636 a machine that cooled down as much as reasonably possible. 1637 """ 1638 temperature = get_current_temperature_max() 1639 # We got here with a cold machine, return immediately. This should be the 1640 # most common case. 1641 if temperature < 50: 1642 return True 1643 logging.info('Got a hot machine of %dC. Sleeping 1 minute.', temperature) 1644 # A modest wait should cool the machine. 1645 time.sleep(60.0) 1646 temperature = get_current_temperature_max() 1647 # Atoms idle below 60 and everyone else should be even lower. 1648 if temperature < 62: 1649 return True 1650 # This should be rare. 1651 logging.info('Did not cool down (%dC). Sleeping 2 minutes.', temperature) 1652 time.sleep(120.0) 1653 temperature = get_current_temperature_max() 1654 # A temperature over 65'C doesn't give us much headroom to the critical 1655 # temperatures that start at 85'C (and PerfControl as of today will fail at 1656 # critical - 10'C). 1657 if temperature < 65: 1658 return True 1659 logging.warning('Did not cool down (%dC), giving up.', temperature) 1660 log_process_activity() 1661 return False 1662 1663 1664# System paths for machine performance state. 1665_CPUINFO = '/proc/cpuinfo' 1666_DIRTY_WRITEBACK_CENTISECS = '/proc/sys/vm/dirty_writeback_centisecs' 1667_KERNEL_MAX = '/sys/devices/system/cpu/kernel_max' 1668_MEMINFO = '/proc/meminfo' 1669_TEMP_SENSOR_RE = 'Reading temperature...([0-9]*)' 1670 1671 1672def _get_line_from_file(path, line): 1673 """ 1674 line can be an integer or 1675 line can be a string that matches the beginning of the line 1676 """ 1677 with open(path) as f: 1678 if isinstance(line, int): 1679 l = f.readline() 1680 for _ in range(0, line): 1681 l = f.readline() 1682 return l 1683 else: 1684 for l in f: 1685 if l.startswith(line): 1686 return l 1687 return None 1688 1689 1690def _get_match_from_file(path, line, prefix, postfix): 1691 """ 1692 Matches line in path and returns string between first prefix and postfix. 1693 """ 1694 match = _get_line_from_file(path, line) 1695 # Strip everything from front of line including prefix. 1696 if prefix: 1697 match = re.split(prefix, match)[1] 1698 # Strip everything from back of string including first occurence of postfix. 1699 if postfix: 1700 match = re.split(postfix, match)[0] 1701 return match 1702 1703 1704def _get_float_from_file(path, line, prefix, postfix): 1705 match = _get_match_from_file(path, line, prefix, postfix) 1706 return float(match) 1707 1708 1709def _get_int_from_file(path, line, prefix, postfix): 1710 match = _get_match_from_file(path, line, prefix, postfix) 1711 return int(match) 1712 1713 1714def _get_hex_from_file(path, line, prefix, postfix): 1715 match = _get_match_from_file(path, line, prefix, postfix) 1716 return int(match, 16) 1717 1718 1719# The paths don't change. Avoid running find all the time. 1720_hwmon_paths = None 1721 1722def _get_hwmon_paths(file_pattern): 1723 """ 1724 Returns a list of paths to the temperature sensors. 1725 """ 1726 # Some systems like daisy_spring only have the virtual hwmon. 1727 # And other systems like rambi only have coretemp.0. See crbug.com/360249. 1728 # /sys/class/hwmon/hwmon*/ 1729 # /sys/devices/virtual/hwmon/hwmon*/ 1730 # /sys/devices/platform/coretemp.0/ 1731 if not _hwmon_paths: 1732 cmd = 'find /sys/ -name "' + file_pattern + '"' 1733 _hwon_paths = utils.run(cmd, verbose=False).stdout.splitlines() 1734 return _hwon_paths 1735 1736 1737def get_temperature_critical(): 1738 """ 1739 Returns temperature at which we will see some throttling in the system. 1740 """ 1741 min_temperature = 1000.0 1742 paths = _get_hwmon_paths('temp*_crit') 1743 for path in paths: 1744 temperature = _get_float_from_file(path, 0, None, None) * 0.001 1745 # Today typical for Intel is 98'C to 105'C while ARM is 85'C. Clamp to 1746 # the lowest known value. 1747 if (min_temperature < 60.0) or min_temperature > 150.0: 1748 logging.warning('Critical temperature of %.1fC was reset to 85.0C.', 1749 min_temperature) 1750 min_temperature = 85.0 1751 1752 min_temperature = min(temperature, min_temperature) 1753 return min_temperature 1754 1755 1756def get_temperature_input_max(): 1757 """ 1758 Returns the maximum currently observed temperature. 1759 """ 1760 max_temperature = -1000.0 1761 paths = _get_hwmon_paths('temp*_input') 1762 for path in paths: 1763 temperature = _get_float_from_file(path, 0, None, None) * 0.001 1764 max_temperature = max(temperature, max_temperature) 1765 return max_temperature 1766 1767 1768def get_thermal_zone_temperatures(): 1769 """ 1770 Returns the maximum currently observered temperature in thermal_zones. 1771 """ 1772 temperatures = [] 1773 for path in glob.glob('/sys/class/thermal/thermal_zone*/temp'): 1774 try: 1775 temperatures.append( 1776 _get_float_from_file(path, 0, None, None) * 0.001) 1777 except IOError: 1778 # Some devices (e.g. Veyron) may have reserved thermal zones that 1779 # are not active. Trying to read the temperature value would cause a 1780 # EINVAL IO error. 1781 continue 1782 return temperatures 1783 1784 1785def get_ec_temperatures(): 1786 """ 1787 Uses ectool to return a list of all sensor temperatures in Celsius. 1788 """ 1789 temperatures = [] 1790 try: 1791 full_cmd = 'ectool temps all' 1792 lines = utils.run(full_cmd, verbose=False).stdout.splitlines() 1793 for line in lines: 1794 temperature = int(line.split(': ')[1]) - 273 1795 temperatures.append(temperature) 1796 except Exception: 1797 logging.warning('Unable to read temperature sensors using ectool.') 1798 for temperature in temperatures: 1799 # Sanity check for real world values. 1800 assert ((temperature > 10.0) and 1801 (temperature < 150.0)), ('Unreasonable temperature %.1fC.' % 1802 temperature) 1803 1804 return temperatures 1805 1806 1807def get_current_temperature_max(): 1808 """ 1809 Returns the highest reported board temperature (all sensors) in Celsius. 1810 """ 1811 temperature = max([get_temperature_input_max()] + 1812 get_thermal_zone_temperatures() + 1813 get_ec_temperatures()) 1814 # Sanity check for real world values. 1815 assert ((temperature > 10.0) and 1816 (temperature < 150.0)), ('Unreasonable temperature %.1fC.' % 1817 temperature) 1818 return temperature 1819 1820 1821def get_cpu_cache_size(): 1822 """ 1823 Returns the last level CPU cache size in kBytes. 1824 """ 1825 cache_size = _get_int_from_file(_CPUINFO, 'cache size', ': ', ' KB') 1826 # Sanity check. 1827 assert cache_size >= 64, 'Unreasonably small cache.' 1828 return cache_size 1829 1830 1831def get_cpu_model_frequency(): 1832 """ 1833 Returns the model frequency from the CPU model name on Intel only. This 1834 might be redundant with get_cpu_max_frequency. Unit is Hz. 1835 """ 1836 frequency = _get_float_from_file(_CPUINFO, 'model name', ' @ ', 'GHz') 1837 return 1.e9 * frequency 1838 1839 1840def get_cpu_max_frequency(): 1841 """ 1842 Returns the largest of the max CPU core frequencies. The unit is Hz. 1843 """ 1844 max_frequency = -1 1845 paths = _get_cpufreq_paths('cpuinfo_max_freq') 1846 for path in paths: 1847 # Convert from kHz to Hz. 1848 frequency = 1000 * _get_float_from_file(path, 0, None, None) 1849 max_frequency = max(frequency, max_frequency) 1850 # Sanity check. 1851 assert max_frequency > 1e8, 'Unreasonably low CPU frequency.' 1852 return max_frequency 1853 1854 1855def get_cpu_min_frequency(): 1856 """ 1857 Returns the smallest of the minimum CPU core frequencies. 1858 """ 1859 min_frequency = 1e20 1860 paths = _get_cpufreq_paths('cpuinfo_min_freq') 1861 for path in paths: 1862 frequency = _get_float_from_file(path, 0, None, None) 1863 min_frequency = min(frequency, min_frequency) 1864 # Sanity check. 1865 assert min_frequency > 1e8, 'Unreasonably low CPU frequency.' 1866 return min_frequency 1867 1868 1869def get_cpu_model(): 1870 """ 1871 Returns the CPU model. 1872 Only works on Intel. 1873 """ 1874 cpu_model = _get_int_from_file(_CPUINFO, 'model\t', ': ', None) 1875 return cpu_model 1876 1877 1878def get_cpu_family(): 1879 """ 1880 Returns the CPU family. 1881 Only works on Intel. 1882 """ 1883 cpu_family = _get_int_from_file(_CPUINFO, 'cpu family\t', ': ', None) 1884 return cpu_family 1885 1886 1887def get_board_property(key): 1888 """ 1889 Get a specific property from /etc/lsb-release. 1890 1891 @param key: board property to return value for 1892 1893 @return the value or '' if not present 1894 """ 1895 with open('/etc/lsb-release') as f: 1896 pattern = '%s=(.*)' % key 1897 pat = re.search(pattern, f.read()) 1898 if pat: 1899 return pat.group(1) 1900 return '' 1901 1902 1903def get_board(): 1904 """ 1905 Get the ChromeOS release board name from /etc/lsb-release. 1906 """ 1907 return get_board_property('BOARD') 1908 1909 1910def get_board_type(): 1911 """ 1912 Get the ChromeOS board type from /etc/lsb-release. 1913 1914 @return device type. 1915 """ 1916 return get_board_property('DEVICETYPE') 1917 1918 1919def get_board_with_frequency_and_memory(): 1920 """ 1921 Returns a board name modified with CPU frequency and memory size to 1922 differentiate between different board variants. For instance 1923 link -> link_1.8GHz_4GB. 1924 """ 1925 board_name = get_board() 1926 if is_virtual_machine(): 1927 board = '%s_VM' % board_name 1928 else: 1929 # Rounded to nearest GB and GHz. 1930 memory = int(round(get_mem_total() / 1024.0)) 1931 # Convert frequency to GHz with 1 digit accuracy after the 1932 # decimal point. 1933 frequency = int(round(get_cpu_max_frequency() * 1e-8)) * 0.1 1934 board = '%s_%1.1fGHz_%dGB' % (board_name, frequency, memory) 1935 return board 1936 1937 1938def get_mem_total(): 1939 """ 1940 Returns the total memory available in the system in MBytes. 1941 """ 1942 mem_total = _get_float_from_file(_MEMINFO, 'MemTotal:', 'MemTotal:', ' kB') 1943 # Sanity check, all Chromebooks have at least 1GB of memory. 1944 assert mem_total > 256 * 1024, 'Unreasonable amount of memory.' 1945 return mem_total / 1024 1946 1947 1948def get_mem_free(): 1949 """ 1950 Returns the currently free memory in the system in MBytes. 1951 """ 1952 mem_free = _get_float_from_file(_MEMINFO, 'MemFree:', 'MemFree:', ' kB') 1953 return mem_free / 1024 1954 1955 1956def get_kernel_max(): 1957 """ 1958 Returns content of kernel_max. 1959 """ 1960 kernel_max = _get_int_from_file(_KERNEL_MAX, 0, None, None) 1961 # Sanity check. 1962 assert ((kernel_max > 0) and (kernel_max < 257)), 'Unreasonable kernel_max.' 1963 return kernel_max 1964 1965 1966def set_high_performance_mode(): 1967 """ 1968 Sets the kernel governor mode to the highest setting. 1969 Returns previous governor state. 1970 """ 1971 original_governors = get_scaling_governor_states() 1972 set_scaling_governors('performance') 1973 return original_governors 1974 1975 1976def set_scaling_governors(value): 1977 """ 1978 Sets all scaling governor to string value. 1979 Sample values: 'performance', 'interactive', 'ondemand', 'powersave'. 1980 """ 1981 paths = _get_cpufreq_paths('scaling_governor') 1982 for path in paths: 1983 cmd = 'echo %s > %s' % (value, path) 1984 logging.info('Writing scaling governor mode \'%s\' -> %s', value, path) 1985 # On Tegra CPUs can be dynamically enabled/disabled. Ignore failures. 1986 utils.system(cmd, ignore_status=True) 1987 1988 1989def _get_cpufreq_paths(filename): 1990 """ 1991 Returns a list of paths to the governors. 1992 """ 1993 cmd = 'ls /sys/devices/system/cpu/cpu*/cpufreq/' + filename 1994 paths = utils.run(cmd, verbose=False).stdout.splitlines() 1995 return paths 1996 1997 1998def get_scaling_governor_states(): 1999 """ 2000 Returns a list of (performance governor path, current state) tuples. 2001 """ 2002 paths = _get_cpufreq_paths('scaling_governor') 2003 path_value_list = [] 2004 for path in paths: 2005 value = _get_line_from_file(path, 0) 2006 path_value_list.append((path, value)) 2007 return path_value_list 2008 2009 2010def restore_scaling_governor_states(path_value_list): 2011 """ 2012 Restores governor states. Inverse operation to get_scaling_governor_states. 2013 """ 2014 for (path, value) in path_value_list: 2015 cmd = 'echo %s > %s' % (value.rstrip('\n'), path) 2016 # On Tegra CPUs can be dynamically enabled/disabled. Ignore failures. 2017 utils.system(cmd, ignore_status=True) 2018 2019 2020def get_dirty_writeback_centisecs(): 2021 """ 2022 Reads /proc/sys/vm/dirty_writeback_centisecs. 2023 """ 2024 time = _get_int_from_file(_DIRTY_WRITEBACK_CENTISECS, 0, None, None) 2025 return time 2026 2027 2028def set_dirty_writeback_centisecs(time=60000): 2029 """ 2030 In hundredths of a second, this is how often pdflush wakes up to write data 2031 to disk. The default wakes up the two (or more) active threads every five 2032 seconds. The ChromeOS default is 10 minutes. 2033 2034 We use this to set as low as 1 second to flush error messages in system 2035 logs earlier to disk. 2036 """ 2037 # Flush buffers first to make this function synchronous. 2038 utils.system('sync') 2039 if time >= 0: 2040 cmd = 'echo %d > %s' % (time, _DIRTY_WRITEBACK_CENTISECS) 2041 utils.system(cmd) 2042 2043 2044def wflinfo_cmd(): 2045 """ 2046 Returns a wflinfo command appropriate to the current graphics platform/api. 2047 """ 2048 return 'wflinfo -p %s -a %s' % (graphics_platform(), graphics_api()) 2049 2050 2051def has_mali(): 2052 """ @return: True if system has a Mali GPU enabled.""" 2053 return os.path.exists('/dev/mali0') 2054 2055def get_gpu_family(): 2056 """Returns the GPU family name.""" 2057 global pciid_to_amd_architecture 2058 global pciid_to_intel_architecture 2059 2060 socfamily = get_cpu_soc_family() 2061 if socfamily == 'exynos5' or socfamily == 'rockchip' or has_mali(): 2062 cmd = wflinfo_cmd() 2063 wflinfo = utils.system_output(cmd, 2064 retain_output=True, 2065 ignore_status=False) 2066 version = re.findall(r'OpenGL renderer string: ' 2067 r'Mali-T([0-9]+)', wflinfo) 2068 if version: 2069 return 'mali-t%s' % version[0] 2070 return 'mali-unrecognized' 2071 if socfamily == 'tegra': 2072 return 'tegra' 2073 if os.path.exists('/sys/kernel/debug/pvr'): 2074 return 'rogue' 2075 2076 pci_vga_device = utils.run("lspci | grep VGA").stdout.rstrip('\n') 2077 bus_device_function = pci_vga_device.partition(' ')[0] 2078 pci_path = '/sys/bus/pci/devices/0000:' + bus_device_function + '/device' 2079 2080 if not os.path.exists(pci_path): 2081 raise error.TestError('PCI device 0000:' + bus_device_function + ' not found') 2082 2083 device_id = utils.read_one_line(pci_path).lower() 2084 2085 if "Advanced Micro Devices" in pci_vga_device: 2086 if not pciid_to_amd_architecture: 2087 with open(_AMD_PCI_IDS_FILE_PATH, 'r') as in_f: 2088 pciid_to_amd_architecture = json.load(in_f) 2089 2090 return pciid_to_amd_architecture[device_id] 2091 2092 if "Intel Corporation" in pci_vga_device: 2093 # Only load Intel PCI ID file once and only if necessary. 2094 if not pciid_to_intel_architecture: 2095 with open(_INTEL_PCI_IDS_FILE_PATH, 'r') as in_f: 2096 pciid_to_intel_architecture = json.load(in_f) 2097 2098 return pciid_to_intel_architecture[device_id] 2099 2100# TODO(ihf): Consider using /etc/lsb-release DEVICETYPE != CHROMEBOOK/CHROMEBASE 2101# for sanity check, but usage seems a bit inconsistent. See 2102# src/third_party/chromiumos-overlay/eclass/appid.eclass 2103_BOARDS_WITHOUT_MONITOR = [ 2104 'anglar', 'mccloud', 'monroe', 'ninja', 'rikku', 'guado', 'jecht', 'tidus', 2105 'beltino', 'panther', 'stumpy', 'panther', 'tricky', 'zako', 'veyron_rialto' 2106] 2107 2108 2109def has_no_monitor(): 2110 """Returns whether a machine doesn't have a built-in monitor.""" 2111 board_name = get_board() 2112 if board_name in _BOARDS_WITHOUT_MONITOR: 2113 return True 2114 2115 return False 2116 2117 2118def get_fixed_dst_drive(): 2119 """ 2120 Return device name for internal disk. 2121 Example: return /dev/sda for falco booted from usb 2122 """ 2123 cmd = ' '.join(['. /usr/sbin/write_gpt.sh;', 2124 '. /usr/share/misc/chromeos-common.sh;', 2125 'load_base_vars;', 2126 'get_fixed_dst_drive']) 2127 return utils.system_output(cmd) 2128 2129 2130def get_root_device(): 2131 """ 2132 Return root device. 2133 Will return correct disk device even system boot from /dev/dm-0 2134 Example: return /dev/sdb for falco booted from usb 2135 """ 2136 return utils.system_output('rootdev -s -d') 2137 2138 2139def get_root_partition(): 2140 """ 2141 Return current root partition 2142 Example: return /dev/sdb3 for falco booted from usb 2143 """ 2144 return utils.system_output('rootdev -s') 2145 2146 2147def get_free_root_partition(root_part=None): 2148 """ 2149 Return currently unused root partion 2150 Example: return /dev/sdb5 for falco booted from usb 2151 2152 @param root_part: cuurent root partition 2153 """ 2154 spare_root_map = {'3': '5', '5': '3'} 2155 if not root_part: 2156 root_part = get_root_partition() 2157 return root_part[:-1] + spare_root_map[root_part[-1]] 2158 2159 2160def get_kernel_partition(root_part=None): 2161 """ 2162 Return current kernel partition 2163 Example: return /dev/sda2 for falco booted from usb 2164 2165 @param root_part: current root partition 2166 """ 2167 if not root_part: 2168 root_part = get_root_partition() 2169 current_kernel_map = {'3': '2', '5': '4'} 2170 return root_part[:-1] + current_kernel_map[root_part[-1]] 2171 2172 2173def get_free_kernel_partition(root_part=None): 2174 """ 2175 return currently unused kernel partition 2176 Example: return /dev/sda4 for falco booted from usb 2177 2178 @param root_part: current root partition 2179 """ 2180 kernel_part = get_kernel_partition(root_part) 2181 spare_kernel_map = {'2': '4', '4': '2'} 2182 return kernel_part[:-1] + spare_kernel_map[kernel_part[-1]] 2183 2184 2185def is_booted_from_internal_disk(): 2186 """Return True if boot from internal disk. False, otherwise.""" 2187 return get_root_device() == get_fixed_dst_drive() 2188 2189 2190def get_ui_use_flags(): 2191 """Parses the USE flags as listed in /etc/ui_use_flags.txt. 2192 2193 @return: A list of flag strings found in the ui use flags file. 2194 """ 2195 flags = [] 2196 for flag in utils.read_file(_UI_USE_FLAGS_FILE_PATH).splitlines(): 2197 # Removes everything after the '#'. 2198 flag_before_comment = flag.split('#')[0].strip() 2199 if len(flag_before_comment) != 0: 2200 flags.append(flag_before_comment) 2201 2202 return flags 2203 2204 2205def graphics_platform(): 2206 """ 2207 Return a string identifying the graphics platform, 2208 e.g. 'glx' or 'x11_egl' or 'gbm' 2209 """ 2210 return 'null' 2211 2212 2213def graphics_api(): 2214 """Return a string identifying the graphics api, e.g. gl or gles2.""" 2215 use_flags = get_ui_use_flags() 2216 if 'opengles' in use_flags: 2217 return 'gles2' 2218 return 'gl' 2219 2220 2221def is_vm(): 2222 """Check if the process is running in a virtual machine. 2223 2224 @return: True if the process is running in a virtual machine, otherwise 2225 return False. 2226 """ 2227 try: 2228 virt = utils.run('sudo -n virt-what').stdout.strip() 2229 logging.debug('virt-what output: %s', virt) 2230 return bool(virt) 2231 except error.CmdError: 2232 logging.warn('Package virt-what is not installed, default to assume ' 2233 'it is not a virtual machine.') 2234 return False 2235 2236 2237def is_package_installed(package): 2238 """Check if a package is installed already. 2239 2240 @return: True if the package is already installed, otherwise return False. 2241 """ 2242 try: 2243 utils.run(_CHECK_PACKAGE_INSTALLED_COMMAND % package) 2244 return True 2245 except error.CmdError: 2246 logging.warn('Package %s is not installed.', package) 2247 return False 2248 2249 2250def is_python_package_installed(package): 2251 """Check if a Python package is installed already. 2252 2253 @return: True if the package is already installed, otherwise return False. 2254 """ 2255 try: 2256 __import__(package) 2257 return True 2258 except ImportError: 2259 logging.warn('Python package %s is not installed.', package) 2260 return False 2261 2262 2263def run_sql_cmd(server, user, password, command, database=''): 2264 """Run the given sql command against the specified database. 2265 2266 @param server: Hostname or IP address of the MySQL server. 2267 @param user: User name to log in the MySQL server. 2268 @param password: Password to log in the MySQL server. 2269 @param command: SQL command to run. 2270 @param database: Name of the database to run the command. Default to empty 2271 for command that does not require specifying database. 2272 2273 @return: The stdout of the command line. 2274 """ 2275 cmd = ('mysql -u%s -p%s --host %s %s -e "%s"' % 2276 (user, password, server, database, command)) 2277 # Set verbose to False so the command line won't be logged, as it includes 2278 # database credential. 2279