blob: 79b4d15bf49390cca937e6e80caeb8f5782e2038 [file] [log] [blame]
Allen Li2c32d6b2017-02-03 15:28:10 -08001# 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
mblighaece77e2009-01-21 19:06:52 +00005"""
6Convenience functions for use by tests or whomever.
mblighaece77e2009-01-21 19:06:52 +00007"""
Allen Li2c32d6b2017-02-03 15:28:10 -08008
9# pylint: disable=missing-docstring
10
11import commands
12import fnmatch
13import glob
14import json
15import logging
16import math
17import multiprocessing
mblighcc038642009-02-05 20:15:52 +000018import os
Allen Li2c32d6b2017-02-03 15:28:10 -080019import 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
mblighaece77e2009-01-21 19:06:52 +000031
32from autotest_lib.client.common_lib.utils import *
Allen Li2c32d6b2017-02-03 15:28:10 -080033
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 = {
Harry Panc9897aa2017-07-31 20:17:52 +0800371 '06_4C': 'Airmont',
Allen Li2c32d6b2017-02-03 15:28:10 -0800372 '06_1C': 'Atom',
373 '06_26': 'Atom',
Harry Panc9897aa2017-07-31 20:17:52 +0800374 '06_27': 'Atom',
375 '06_35': 'Atom',
Allen Li2c32d6b2017-02-03 15:28:10 -0800376 '06_36': 'Atom',
Allen Li2c32d6b2017-02-03 15:28:10 -0800377 '06_3D': 'Broadwell',
Harry Panc9897aa2017-07-31 20:17:52 +0800378 '06_47': 'Broadwell',
Harry Pan45cb0e02017-08-22 22:19:59 +0800379 '06_4F': 'Broadwell',
380 '06_56': 'Broadwell',
Allen Li2c32d6b2017-02-03 15:28:10 -0800381 '06_0D': 'Dothan',
Harry Panc9897aa2017-07-31 20:17:52 +0800382 '06_5C': 'Goldmont',
Allen Li2c32d6b2017-02-03 15:28:10 -0800383 '06_3C': 'Haswell',
Allen Li2c32d6b2017-02-03 15:28:10 -0800384 '06_45': 'Haswell',
385 '06_46': 'Haswell',
Harry Panc9897aa2017-07-31 20:17:52 +0800386 '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',
Allen Li2c32d6b2017-02-03 15:28:10 -0800391 '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',
Allen Li2c32d6b2017-02-03 15:28:10 -0800399 '0F_03': 'Prescott',
400 '0F_04': 'Prescott',
401 '0F_06': 'Presler',
Harry Panc9897aa2017-07-31 20:17:52 +0800402 '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',
Allen Li2c32d6b2017-02-03 15:28:10 -0800412 '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
Gwendal Grignou876e6912017-06-21 12:17:36 -0700830_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]+')
Allen Li2c32d6b2017-02-03 15:28:10 -0800833
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
Po-Hsien Wangc02992f2017-08-10 11:18:46 -07001101_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')
Allen Li2c32d6b2017-02-03 15:28:10 -08001106_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
Achuith Bhandarkare3adb0a2017-08-10 14:15:57 -07001201 port. if it is 0, look at DevToolsActivePort for the ephemeral port.
Allen Li2c32d6b2017-02-03 15:28:10 -08001202 """
1203 _, command = get_oldest_by_name('chrome')
1204 matches = re.search('--remote-debugging-port=([0-9]+)', command)
Achuith Bhandarkare3adb0a2017-08-10 14:15:57 -07001205 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())
Allen Li2c32d6b2017-02-03 15:28:10 -08001212
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.
Allen Li5ed7e632017-02-03 16:31:33 -08001282 utils.TimeoutError: if processes still exist after timeout_sec.
Allen Li2c32d6b2017-02-03 15:28:10 -08001283 """
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',
Brian Norrisbb745382017-06-30 11:49:27 -07002105 'beltino', 'panther', 'stumpy', 'panther', 'tricky', 'zako', 'veyron_rialto'
Allen Li2c32d6b2017-02-03 15:28:10 -08002106]
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
Allen Li2c32d6b2017-02-03 15:28:10 -08002205def graphics_platform():
2206 """
2207 Return a string identifying the graphics platform,
2208 e.g. 'glx' or 'x11_egl' or 'gbm'
2209 """
Po-Hsien Wangdab8e182017-05-03 10:25:41 -07002210 return 'null'
Allen Li2c32d6b2017-02-03 15:28:10 -08002211
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
Allen Li2c32d6b2017-02-03 15:28:10 -08002221def 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.