1# Lint as: python2, python3 2# Copyright 2016 The Chromium OS Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6from __future__ import absolute_import 7from __future__ import division 8from __future__ import print_function 9 10import logging 11import math 12import six 13import sys 14import time 15 16import common 17from autotest_lib.client.common_lib import error 18from autotest_lib.client.common_lib import global_config 19from autotest_lib.client.common_lib import hosts 20from autotest_lib.client.common_lib import utils 21from autotest_lib.client.common_lib.cros import dev_server 22from autotest_lib.client.common_lib.cros import retry 23from autotest_lib.client.common_lib.cros import tpm_utils 24from autotest_lib.server import afe_utils 25from autotest_lib.server import crashcollect 26from autotest_lib.server.cros import provisioner 27from autotest_lib.server.cros.dynamic_suite import tools 28from autotest_lib.server.cros.dynamic_suite import constants as ds_constants 29from autotest_lib.server.cros.servo.keyboard import servo_keyboard_flasher 30from autotest_lib.server.cros.repair import mac_address_helper 31from autotest_lib.server.hosts import cros_constants 32from autotest_lib.server.hosts import cros_firmware 33from autotest_lib.server.hosts import repair_utils 34from autotest_lib.site_utils.admin_audit import verifiers as audit_verify 35from autotest_lib.site_utils.admin_audit import constants as audit_const 36from autotest_lib.site_utils.admin_audit import battery_validator 37from six.moves import range 38 39try: 40 from autotest_lib.utils.frozen_chromite.lib import metrics 41except ImportError: 42 metrics = utils.metrics_mock 43 44from autotest_lib.utils.frozen_chromite.lib import timeout_util 45 46DEFAULT_SERVO_RESET_TRIGGER = ( 47 'ping', 48 'ssh', 49 'stop_start_ui', 50 'power', 51) 52 53 54# _DEV_MODE_ALLOW_POOLS - The set of pools that are allowed to be 55# in dev mode (usually, those should be unmanaged devices) 56# 57_DEV_MODE_ALLOWED_POOLS = set( 58 global_config.global_config.get_config_value( 59 'CROS', 60 'pools_dev_mode_allowed', 61 type=str, 62 default='', 63 allow_blank=True).split(',')) 64 65# Setting to suppress dev mode check; primarily used for moblab where all 66# DUT's are in dev mode. 67_DEV_MODE_ALWAYS_ALLOWED = global_config.global_config.get_config_value( 68 'CROS', 69 'dev_mode_allowed', 70 type=bool, 71 default=False) 72 73# Triggers for the 'provision', 'powerwash', and 'usb' repair actions. 74# These are also used as dependencies in the `CrosHost` repair 75# sequence, as follows: 76# 77# provision: 78# - triggers: _CROS_PROVISION_TRIGGERS 79# - depends on: _CROS_USB_TRIGGERS + _CROS_POWERWASH_TRIGGERS 80# 81# powerwash: 82# - triggers: _CROS_POWERWASH_TRIGGERS + _CROS_PROVISION_TRIGGERS 83# - depends on: _CROS_USB_TRIGGERS 84# 85# usb: 86# - triggers: _CROS_USB_TRIGGERS + _CROS_POWERWASH_TRIGGERS + 87# _CROS_PROVISION_TRIGGERS 88# - depends on: _CROS_USB_DEPENDENCIES 89# 90# N.B. AC power detection depends on software on the DUT, and there 91# have been bugs where detection failed even though the DUT really 92# did have power. So, we make the 'power' verifier a trigger for 93# reinstall repair actions, too. 94# 95# TODO(jrbarnette): provision repair can't fix all problems reported by 96# the 'cros' verifier; it's listed as an provision trigger as a 97# simplification. The ultimate fix is to split the 'cros' verifier 98# into smaller individual verifiers. 99_CROS_PROVISION_TRIGGERS = ( 100 'power', 101 'rwfw', 102 'fwstatus', 103 'python', 104 'hwid', 105 'cros', 106 'dev_default_boot', 107) 108_CROS_POWERWASH_TRIGGERS = ('tpm', 'good_provision', 'ext4',) 109_CROS_USB_TRIGGERS = ( 110 'ping', 111 'ssh', 112 'writable', 113) 114_JETSTREAM_USB_TRIGGERS = ( 115 'ping', 116 'ssh', 117 'writable', 118) 119_CROS_FIRMWARE_TRIGGERS = ( 120 'ping', 121 'ssh', 122) 123_CROS_AC_TRIGGERS = ( 124 'ping', 125 'power', 126) 127_CROS_USB_DEPENDENCIES = ('usb_drive', ) 128 129 130class ACPowerVerifier(hosts.Verifier): 131 """Check for AC power and battery charging state.""" 132 133 # Battery discharging state in power_supply_info file. 134 BATTERY_DISCHARGING = 'Discharging' 135 # Power controller can discharge battery any time till 90% for any model. 136 # Setting level to 90% in case we have wearout of it. 137 BATTERY_DISCHARGE_MIN = 90 138 139 @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 140 def verify(self, host): 141 # pylint: disable=missing-docstring 142 info = self._load_info(host) 143 self._validate_ac_plugged(info) 144 self._validate_battery(host, info) 145 146 def _load_info(self, host): 147 try: 148 info = host.get_power_supply_info() 149 except error.AutoservRunError: 150 raise hosts.AutoservVerifyError( 151 'Failed to get power supply info') 152 return info 153 154 def _validate_ac_plugged(self, info): 155 # Validate that DUT is plugged to the AC. 156 try: 157 if info['Line Power']['online'] != 'yes': 158 raise hosts.AutoservVerifyError( 159 'AC power is not plugged in') 160 except KeyError: 161 raise hosts.AutoservVerifyError( 162 'Cannot determine AC power status') 163 164 def _validate_battery(self, host, info): 165 host_info = host.host_info_store.get() 166 if host_info.get_label_value('power') == 'battery': 167 if 'Battery' not in info: 168 data = {'host': host.hostname, 'model': host_info.model} 169 metrics.Counter('chromeos/autotest/battery_not_detected' 170 ).increment(fields=data) 171 logging.info('Battery is not presented but expected!' 172 ' Probably hardware issue.') 173 174 try: 175 charging_state = info['Battery']['state'] 176 battery_level = float(info['Battery']['percentage']) 177 178 # Collect info to determine which battery level is better to call 179 # as MIN_BATTERY_LEVEL for DUTs in the lab. 180 if battery_level < cros_constants.MIN_BATTERY_LEVEL: 181 level_by_10 = int(math.floor(battery_level / 10.0)) * 10 182 metrics_data = { 183 'host': host.hostname, 184 'level': level_by_10, 185 'mode': charging_state 186 } 187 metrics.Counter('chromeos/autotest/battery/state2').increment( 188 fields=metrics_data) 189 190 if (charging_state == self.BATTERY_DISCHARGING 191 and battery_level < self.BATTERY_DISCHARGE_MIN): 192 logging.debug('Try to fix discharging state of the battery. ' 193 'Possible that a test left wrong state.') 194 # Here is the chance that battery is discharging because 195 # of some test did not clean up the state. 196 # We are going to try to fix it by set charging to normal. 197 host.run('ectool chargecontrol normal', ignore_status=True) 198 # wait to change state. 199 time.sleep(10) 200 info = self._load_info(host) 201 charging_state = info['Battery']['state'] 202 fixed = charging_state != self.BATTERY_DISCHARGING 203 # TODO (@otabek) remove metrics after research 204 logging.debug('Fixed battery discharge mode.') 205 metrics_data = { 206 'model': host.host_info_store.get().model, 207 'fixed': fixed 208 } 209 metrics.Counter( 210 'chromeos/autotest/repair/chargecontrol_fixed' 211 ).increment(fields=metrics_data) 212 213 if (battery_level < cros_constants.MIN_BATTERY_LEVEL 214 and charging_state == self.BATTERY_DISCHARGING): 215 # TODO(@xianuowang) remove metrics here once we have device 216 # health profile to collect history of DUT's metrics. 217 metrics_data = {'host': host.hostname, 218 'board': host.host_info_store.get().board} 219 metrics.Counter( 220 'chromeos/autotest/repair/verifier/power').increment( 221 fields=metrics_data) 222 raise hosts.AutoservVerifyError( 223 'Battery is in discharging state and current level' 224 ' is less than %s%%' % 225 cros_constants.MIN_BATTERY_LEVEL) 226 except (KeyError, ValueError): 227 logging.warning('Cannot determine battery state -' 228 ' skipping check.') 229 230 @property 231 def description(self): 232 # pylint: disable=missing-docstring 233 return 'The DUT is plugged in to AC power and battery is charging' 234 235 236class ProvisioningLabelsVerifier(hosts.Verifier): 237 """Confirm that current ChromeOS image on the host is matches 238 to provision labels. 239 240 Some tests behavior may changed DUT image while they don't update 241 provision-cros_version or provisioning-job_repo_url labels, which could 242 cause the next test run on the same host gets an unexpected data and 243 yields false positive test result. 244 """ 245 246 @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 247 def verify(self, host): 248 self._verify_cros_version(host) 249 self._verify_job_repo_url(host) 250 251 def _verify_cros_version(self, host): 252 """Verify that cros-version match version on the host.""" 253 label_match = True 254 try: 255 label_match = host.verify_cros_version_label() 256 except Exception as e: 257 # We don't want fail this verifier for any errors that other 258 # than a actual version mismatch, as that can make debugging 259 # more challenge. 260 logging.warning( 261 'Unexpected error during verify cros version on %s; %s', 262 host.hostname, e) 263 264 if not label_match: 265 raise hosts.AutoservVerifyError('ChromeOS image on the host' 266 ' does not match to cros-version' 267 ' label.') 268 269 def _verify_job_repo_url(self, host): 270 """Verify that job_repo_url match version on the host.""" 271 info = host.host_info_store.get() 272 job_repo_url = info.attributes.get(ds_constants.JOB_REPO_URL, '') 273 if not job_repo_url: 274 logging.debug('job_repo_url is empty. Skip check.') 275 return 276 os_from_host = host.get_release_builder_path() 277 if not os_from_host in job_repo_url: 278 raise hosts.AutoservVerifyError('ChromeOS image on the host' 279 ' does not match to job_repo_url' 280 ' label.') 281 282 @property 283 def description(self): 284 # pylint: disable=missing-docstring 285 return 'ChromeOS image on host matches cros_version label' 286 287 288class WritableVerifier(hosts.Verifier): 289 """ 290 Confirm the stateful file systems are writable. 291 292 The standard linux response to certain unexpected file system errors 293 (including hardware errors in block devices) is to change the file 294 system status to read-only. This checks that that hasn't happened. 295 296 The test covers the two file systems that need to be writable for 297 critical operations like AU: 298 * The (unencrypted) stateful system which includes 299 /mnt/stateful_partition. 300 * The encrypted stateful partition, which includes /var. 301 302 The test doesn't check various bind mounts; those are expected to 303 fail the same way as their underlying main mounts. Whether the 304 Linux kernel can guarantee that is untested... 305 """ 306 307 # N.B. Order matters here: Encrypted stateful is loop-mounted from 308 # a file in unencrypted stateful, so we don't test for errors in 309 # encrypted stateful if unencrypted fails. 310 _TEST_DIRECTORIES = ['/mnt/stateful_partition', '/var/tmp'] 311 312 @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 313 def verify(self, host): 314 # pylint: disable=missing-docstring 315 # This deliberately stops looking after the first error. 316 # See above for the details. 317 for testdir in self._TEST_DIRECTORIES: 318 if not host.is_file_system_writable([testdir]): 319 msg = 'Can\'t create a file in %s' % testdir 320 raise hosts.AutoservVerifyError(msg) 321 322 @property 323 def description(self): 324 # pylint: disable=missing-docstring 325 return 'The stateful filesystems are writable' 326 327 328class EXT4fsErrorVerifier(hosts.Verifier): 329 """ 330 Confirm we have not seen critical file system kernel errors. 331 """ 332 333 @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 334 def verify(self, host): 335 # pylint: disable=missing-docstring 336 # grep for stateful FS errors of the type "EXT4-fs error (device sda1):" 337 command = ("dmesg | grep -E \"EXT4-fs error \(device " 338 "$(cut -d ' ' -f 5,9 /proc/$$/mountinfo | " 339 "grep -e '^/mnt/stateful_partition ' | " 340 "cut -d ' ' -f 2 | cut -d '/' -f 3)\):\"") 341 output = host.run(command=command, ignore_status=True).stdout 342 if output: 343 sample = output.splitlines()[0] 344 message = 'Saw file system error: %s' % sample 345 raise hosts.AutoservVerifyError(message) 346 # Check for other critical FS errors. 347 command = 'dmesg | grep "This should not happen!! Data will be lost"' 348 output = host.run(command=command, ignore_status=True).stdout 349 if output: 350 message = 'Saw file system error: Data will be lost' 351 raise hosts.AutoservVerifyError(message) 352 else: 353 logging.error('Could not determine stateful mount.') 354 355 @property 356 def description(self): 357 # pylint: disable=missing-docstring 358 return 'Did not find critical file system errors' 359 360 361class UpdateSuccessVerifier(hosts.Verifier): 362 """ 363 Checks that the DUT successfully finished its last provision job. 364 365 At the start of any update (e.g. for a Provision job), the code 366 creates a marker file named `PROVISION_FAILED`. The file is located 367 in a part of the stateful partition that will be removed if an 368 update finishes successfully. Thus, the presence of the file 369 indicates that a prior update failed. 370 371 The verifier tests for the existence of the marker file and fails if 372 it still exists. 373 """ 374 375 @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 376 def verify(self, host): 377 # pylint: disable=missing-docstring 378 result = host.run('test -f %s' % provisioner.PROVISION_FAILED, 379 ignore_status=True) 380 if result.exit_status == 0: 381 raise hosts.AutoservVerifyError( 382 'Last provision on this DUT failed') 383 384 @property 385 def description(self): 386 # pylint: disable=missing-docstring 387 return 'The most recent provision attempt on this DUT succeeded' 388 389 390class TPMStatusVerifier(hosts.Verifier): 391 """Verify that the host's TPM is in a good state.""" 392 393 @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 394 def verify(self, host): 395 # pylint: disable=missing-docstring 396 if _is_virtual_machine(host): 397 # We do not forward host TPM / emulated TPM to qemu VMs, so skip 398 # this verification step. 399 logging.debug('Skipped verification %s on VM', self) 400 return 401 402 try: 403 status = TpmStatus(host) 404 except hosts.AutoservVerifyError: 405 logging.info('Cannot determine the Cryptohome valid status - ' 406 'skipping check.') 407 return 408 try: 409 if not status['is_enabled']: 410 raise hosts.AutoservVerifyError( 411 'TPM is not enabled -- Hardware is not working.') 412 if status['is_owned'] and not status['is_srk_default_auth']: 413 raise hosts.AutoservVerifyError('Cannot load the TPM SRK') 414 except KeyError: 415 logging.info('Cannot determine the TPM valid status - ' 416 'skipping check.') 417 418 @property 419 def description(self): 420 # pylint: disable=missing-docstring 421 return 'The host\'s TPM is available and working' 422 423 424class PythonVerifier(hosts.Verifier): 425 """Confirm the presence of a working Python interpreter.""" 426 427 @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 428 def verify(self, host): 429 # pylint: disable=missing-docstring 430 result = host.run('python -c "import json"', 431 ignore_status=True) 432 if result.exit_status != 0: 433 message = 'The python interpreter is broken' 434 if result.exit_status == 127: 435 search = host.run('which python', ignore_status=True) 436 if search.exit_status != 0 or not search.stdout: 437 message = ('Python is missing; may be caused by ' 438 'powerwash') 439 raise hosts.AutoservVerifyError(message) 440 441 @property 442 def description(self): 443 # pylint: disable=missing-docstring 444 return 'Python on the host is installed and working' 445 446 447class DevModeVerifier(hosts.Verifier): 448 """Verify that the host is not in dev mode.""" 449 450 @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 451 def verify(self, host): 452 # pylint: disable=missing-docstring 453 # Some pools are allowed to be in dev mode 454 info = host.host_info_store.get() 455 if (_DEV_MODE_ALWAYS_ALLOWED or 456 bool(info.pools & _DEV_MODE_ALLOWED_POOLS)): 457 return 458 459 result = host.run('crossystem devsw_boot', ignore_status=True).stdout 460 if result != '0': 461 raise hosts.AutoservVerifyError('The host is in dev mode') 462 463 @property 464 def description(self): 465 # pylint: disable=missing-docstring 466 return 'The host should not be in dev mode' 467 468 469class DevDefaultBootVerifier(hosts.Verifier): 470 """Verify that the host is set to boot the internal disk by default.""" 471 472 @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 473 def verify(self, host): 474 # pylint: disable=missing-docstring 475 result = host.run('crossystem dev_default_boot', ignore_status=True) 476 default_boot = result.stdout.strip() 477 if default_boot != 'disk': 478 raise hosts.AutoservVerifyError( 479 'The host has incorrect dev_default_boot value: %r' 480 % default_boot) 481 482 @property 483 def description(self): 484 # pylint: disable=missing-docstring 485 return 'The host should have dev_default_boot=disk' 486 487 488class HWIDVerifier(hosts.Verifier): 489 """Verify that the host has HWID & serial number.""" 490 491 @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 492 def verify(self, host): 493 # pylint: disable=missing-docstring 494 info = host.host_info_store.get() 495 if not info.board or not info.model: 496 # if board or model missed in host_info file then it is empty 497 # skip verifier 498 return 499 info_hwid = info.attributes.get('HWID') 500 info_serial_number = info.attributes.get('serial_number') 501 502 if not info_hwid or not info_serial_number: 503 logging.info('Missing HWID or/and SerialNumber.' 504 ' Probably device was not deployed properly.' 505 ' Marking DUT for need re-deployment.') 506 host.set_device_repair_state( 507 cros_constants.DEVICE_STATE_NEEDS_DEPLOY) 508 return 509 510 host_hwid = host.run('crossystem hwid', ignore_status=True).stdout 511 host_serial_number = self._get_serial_number(host, info_serial_number) 512 if not host_hwid or not host_serial_number: 513 raise hosts.AutoservVerifyError( 514 'Failed to get HWID & Serial Number for host %s' % 515 host.hostname) 516 517 if host_hwid != info_hwid: 518 # We not fail verifier as it not critical for majority tests. 519 metrics.Counter('chromeos/autotest/repair/hwid_change').increment( 520 fields={ 521 'host': host.hostname, 522 'board': info.board or '' 523 }) 524 logging.info( 525 'HWID changed to: %s required manual work' 526 ' to fix it.', host_hwid) 527 528 if host_serial_number and host_serial_number != info_serial_number: 529 logging.info( 530 'The SerialNumber mismatch detected %s != %s.' 531 ' Probably attempt to replace DUT without deployment.' 532 ' Marking DUT for need re-deployment.', info_serial_number, 533 host_serial_number) 534 host.set_device_repair_state( 535 cros_constants.DEVICE_STATE_NEEDS_DEPLOY) 536 537 def _get_serial_number(self, host, serial_number): 538 """Read serial_number from VPD. 539 540 If VPD does not have any value for serial_number then it will 541 try to restore from host_info. 542 543 @param host CrosHost 544 @param serial_number Serial-number from host-info 545 """ 546 req = host.run('vpd -g serial_number', ignore_status=True) 547 # serial_number not found in the VPD info 548 if not req.stdout and req.exit_status == 3 and serial_number: 549 logging.debug('Cannot find serial_number from VPD.') 550 # check if vpd working fine without error 551 l1 = host.run('vpd -l', ignore_status=True) 552 l2 = host.run('vpd -l |grep "\"serial_number\"="', 553 ignore_status=True) 554 if l1.exit_status == 0 and l2.exit_status == 1: 555 logging.info('Start restoring serial_number:%s for VPD.', 556 serial_number) 557 # update serial_number for VPD 558 cmd = 'vpd -s serial_number=%s' 559 host.run(cmd % serial_number, ignore_status=True) 560 host.run('dump_vpd_log --force', ignore_status=True) 561 # reading from VPD to see what we updated 562 req = host.run('vpd -g serial_number', ignore_status=True) 563 return req.stdout 564 565 def _is_applicable(self, host): 566 if host.is_satlab(): 567 logging.info('Not critical for Satlab. Skipping') 568 return False 569 return True 570 571 @property 572 def description(self): 573 # pylint: disable=missing-docstring 574 return 'The host should have valid HWID and Serial Number' 575 576 577class EnrollmentStateVerifier(hosts.Verifier): 578 """Verify that the device's enrollment state is clean. 579 580 There are two "flags" that generate 3 possible enrollment states here. 581 Flag 1 - The presence of install attributes file in 582 /home/.shadow/install_attributes.pb 583 584 Flag 2 - The value of "check_enrollment" from VPD. Can be obtained by 585 reading the cache file in 586 /mnt/stateful_partition/unencrypted/cache/vpd/full-v2.txt 587 588 The states: 589 State 1 - Device is enrolled, means flag 1 is true and in 590 flag 2 check_enrollment=1 591 State 2 - Device is consumer owned, means flag 1 is true and in 592 flag 2 check_enrollment=0 593 State 3 - Device is enrolled and has been powerwashed, means flag 1 is 594 false. If the value in flag 2 is check_enrollment=1 then the 595 device will perform forced re-enrollment check and depending 596 on the response from the server might force the device to enroll 597 again. If the value is check_enrollment=0, then device can be 598 used like a new device. 599 600 We consider state 1, and first scenario(check_enrollment=1) of state 3 601 as unacceptable state here as they may interfere with normal tests. 602 """ 603 604 VPD_CACHE = '/mnt/stateful_partition/unencrypted/cache/vpd/full-v2.txt' 605 606 @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 607 def verify(self, host): 608 # pylint: disable=missing-docstring 609 if self._get_enrollment_state(host): 610 raise hosts.AutoservNonCriticalVerifyError('The device is enrolled,' 611 ' it may interfere with' 612 ' some tests.') 613 614 def _get_enrollment_state(self, host): 615 logging.debug('checking enrollment state from VPD cache...') 616 response = host.run('grep "check_enrollment" %s' % self.VPD_CACHE, 617 ignore_status=True) 618 if response.exit_status == 0: 619 result = response.stdout.strip() 620 logging.info('Enrollment state in VPD cache: %s', result) 621 return result == '"check_enrollment"="1"' 622 623 logging.error('Unexpected error occured during verify enrollment state' 624 ' in VPD cache, skipping verify process.') 625 return False 626 627 def _is_applicable(self, host): 628 info = host.host_info_store.get() 629 # if os type is missing from host_info, then we assume it's cros. 630 return getattr(info, 'os', 'cros') in ('', 'cros') 631 632 @property 633 def description(self): 634 # pylint: disable=missing-docstring 635 return 'The enrollment state is clean on the host' 636 637 638class FirmwareTpmVerifier(hosts.Verifier): 639 """Verifier that firmware tpm info is correct. 640 641 For dev-signed firmware, tpm_fwver and tpm_kernver reported from 642 crossystem should always be 0x10001. Firmware update on DUTs with 643 incorrect tmp_fwver or tpm_kernver may fail due to firmware 644 rollback protection. 645 """ 646 # A list of field we want check from crossystem and expected value. 647 CHECK_LIST = [ 648 ('tpm_fwver', '0x00010001'), 649 ('tpm_kernver', '0x00010001'), 650 ] 651 652 @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 653 def verify(self, host): 654 # pylint: disable=missing-docstring 655 for field, expected_value in self.CHECK_LIST: 656 result = host.run('crossystem %s' % field, ignore_status=True) 657 if result.exit_status != 0: 658 raise hosts.AutoservNonCriticalVerifyError( 659 'Unable to get %s from crossystem.' % field) 660 if result.stdout != expected_value: 661 raise hosts.AutoservNonCriticalVerifyError( 662 'Unexpected %s value: %s, expected: %s. This error' 663 ' may cause firmware provision fail due to the' 664 ' rollback protection.' % 665 (field, result.stdout, expected_value)) 666 667 def _is_applicable(self, host): 668 return cros_firmware._is_firmware_testing_device(host) 669 670 @property 671 def description(self): 672 # pylint: disable=missing-docstring 673 return 'Firmware tpm info is correct in crossystem.' 674 675 676class JetstreamTpmVerifier(hosts.Verifier): 677 """Verify that Jetstream TPM is in a good state.""" 678 679 @retry.retry(error.AutoservError, timeout_min=2, delay_sec=10) 680 @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 681 def verify(self, host): 682 # pylint: disable=missing-docstring 683 try: 684 status = TpmStatus(host) 685 if not status.tpm_enabled: 686 raise hosts.AutoservVerifyError('TPM is not enabled') 687 if not status.tpm_owned: 688 raise hosts.AutoservVerifyError('TPM is not owned') 689 if not status.tpm_can_load_srk: 690 raise hosts.AutoservVerifyError('TPM cannot load SRK') 691 if not status.tpm_can_load_srk_pubkey: 692 raise hosts.AutoservVerifyError('TPM cannot load SRK pubkey') 693 694 # Check that the TPM is fully initialized. The output of this 695 # command is line-oriented property/value pairs. 696 result = host.run('cryptohome --action=tpm_status') 697 if 'TPM Ready: true' not in result.stdout: 698 raise hosts.AutoservVerifyError('TPM is not ready') 699 except error.AutoservRunError: 700 raise hosts.AutoservVerifyError( 701 'Could not determine TPM status') 702 703 @property 704 def description(self): 705 # pylint: disable=missing-docstring 706 return 'Jetstream TPM state check' 707 708 709class JetstreamAttestationVerifier(hosts.Verifier): 710 """Verify that Jetstream attestation client has a certificate.""" 711 712 @retry.retry(error.AutoservError, timeout_min=2, delay_sec=10) 713 @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 714 def verify(self, host): 715 # pylint: disable=missing-docstring 716 try: 717 # This output is in text protobuf format. 718 result = host.run('cryptohome --action=tpm_more_status') 719 if 'attestation_prepared: true' not in result.stdout: 720 raise hosts.AutoservVerifyError( 721 'Attestation has not been prepared') 722 723 result = host.run('cryptohome --action=tpm_attestation_get_ek') 724 if 'EK Certificate' not in result.stdout: 725 raise hosts.AutoservVerifyError( 726 'Endorsement certificate not found') 727 except error.AutoservRunError: 728 raise hosts.AutoservVerifyError( 729 'Unable to fetch endorsement certificate') 730 731 @property 732 def description(self): 733 # pylint: disable=missing-docstring 734 return 'Jetstream attestation endorsement check' 735 736 737class JetstreamServicesVerifier(hosts.Verifier): 738 """Verify that Jetstream services are running.""" 739 740 # Retry for b/62576902 741 @retry.retry(error.AutoservError, timeout_min=1, delay_sec=10) 742 @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 743 def verify(self, host): 744 # pylint: disable=missing-docstring 745 try: 746 host.run('pgrep ap-controller') 747 except error.AutoservRunError: 748 raise hosts.AutoservVerifyError( 749 'ap-controller process is not running') 750 751 @property 752 def description(self): 753 # pylint: disable=missing-docstring 754 return 'Jetstream services must be running' 755 756 757class StopStartUIVerifier(hosts.Verifier): 758 """Verify that command 'stop ui' won't crash the DUT. 759 760 We run 'stop ui' in AU and provision. We found some bad images broke 761 this command and then broke all the provision of all following test. We add 762 this verifier to ensure it works and will trigger reimaging to a good 763 version if it fails. 764 """ 765 766 @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 767 def verify(self, host): 768 try: 769 host.run('stop ui && start ui', ignore_status=True, timeout=45) 770 except error.AutoservSSHTimeout: 771 raise hosts.AutoservVerifyError( 772 "Got timeout when stop ui/start ui. DUT might crash.") 773 774 @property 775 def description(self): 776 return 'The DUT image works fine when stop ui/start ui.' 777 778 779class GscToolPresentVerifier(hosts.Verifier): 780 """Verify that GSC tool is functional. 781 782 If board/model expected to have GSC tool but it does not have it then need 783 to re-image the host to recover it. 784 If host-info has label 'cr50' then we expect to have GSC tool on the host. 785 """ 786 787 VERIFY_GSC_CMD = 'gsctool -a -f' 788 789 @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 790 def verify(self, host): 791 r = host.run(self.VERIFY_GSC_CMD, ignore_status=True, timeout=10) 792 if r.exit_status != 0: 793 raise hosts.AutoservNonCriticalVerifyError( 794 "GSC tool issue detected.") 795 logging.debug('GSC tool is functional.') 796 797 def _is_applicable(self, host): 798 host_info = host.host_info_store.get() 799 if host_info.get_label_value('cr50'): 800 return True 801 logging.info('GSC is not on the host.') 802 return False 803 804 @property 805 def description(self): 806 return 'Verify GSC tool is functional.' 807 808 809class ServoUSBDriveVerifier(hosts.Verifier): 810 """Verify that USB drive on Servo is good to use. 811 812 Check if USB drive is detected on servo and verified on servohost and 813 USB is not marked for replacement. 814 """ 815 816 @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 817 def verify(self, host): 818 # pylint: disable=missing-docstring 819 usb_dev = '' 820 try: 821 usb_dev = host._servo_host._probe_and_validate_usb_dev() 822 except hosts.AutoservRepairError as e: 823 # We USB drive not detected by servod 824 logging.debug('(Not critical) %s', e) 825 host_info = host.host_info_store.get() 826 if not usb_dev: 827 host_info.set_version_label(audit_const.SERVO_USB_STATE_PREFIX, 828 audit_const.HW_STATE_NOT_DETECTED) 829 host.host_info_store.commit(host_info) 830 raise hosts.AutoservNonCriticalVerifyError( 831 'USB-drive is not detected or bad') 832 833 # Check if USB-drive marked for replacement. 834 usb_state = host_info.get_label_value( 835 audit_const.SERVO_USB_STATE_PREFIX) 836 if usb_state and usb_state == audit_const.HW_STATE_NEED_REPLACEMENT: 837 # Allow to use USB-key marked for replacement. 838 # Goal to collect metrics to see if DUT still can recovered 839 return 840 # TODO(otabek): restory when fix crbug.com/1164408 841 # raise hosts.AutoservNonCriticalVerifyError( 842 # 'USB-drive marked for replacement') 843 844 # The USB-drive detected and was not mark for replacement. 845 # Set as normal for future audit. 846 host_info.set_version_label(audit_const.SERVO_USB_STATE_PREFIX, 847 audit_const.HW_STATE_NORMAL) 848 host.host_info_store.commit(host_info) 849 850 def _is_applicable(self, host): 851 if host.servo: 852 return True 853 return False 854 855 @property 856 def description(self): 857 return 'Ensure USB drive on Servo is in good state.' 858 859 860class DUTStorageVerifier(hosts.Verifier): 861 """Verify that main storage on DUT is good to use. 862 863 Check if DUT drive is providing good SMART stats which not showing any 864 issues on it. The verifier can mark DUT for replacement if SMART stats 865 show outworn data. 866 """ 867 868 @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 869 def verify(self, host): 870 # pylint: disable=missing-docstring 871 verifier = audit_verify.VerifyDutStorage(host) 872 verifier.verify(set_label=True, run_badblocks='NOT') 873 state = verifier.get_state() or audit_const.HW_STATE_UNKNOWN 874 if not state: 875 raise hosts.AutoservNonCriticalVerifyError( 876 'DUT storage did not detected or state cannot extracted.') 877 if state == audit_const.HW_STATE_NEED_REPLACEMENT: 878 logging.info('Detected issue with storage on the DUT.') 879 host.set_device_needs_replacement() 880 881 @property 882 def description(self): 883 return 'Ensure DUT storage SMART information is in good state.' 884 885 886class AuditBattery(hosts.Verifier): 887 """Verify that battery on DUT is good to use. 888 889 Check if DUT drive is providing good SMART stats which not showing any 890 issues on it. The verifier can mark DUT for replacement if SMART stats 891 show outworn data. 892 """ 893 894 @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 895 def verify(self, host): 896 # pylint: disable=missing-docstring 897 state = None 898 try: 899 state = self._get_validator(host).validate() 900 except Exception as e: 901 # We do not want stop main process if it fail. 902 logging.debug('(Not critical) %s', e) 903 if not state: 904 raise hosts.AutoservNonCriticalVerifyError( 905 'DUT battery did not detected or state cannot extracted.') 906 if state == audit_const.HW_STATE_NEED_REPLACEMENT: 907 logging.info('Detected issue with storage on the DUT.') 908 host.set_device_needs_replacement() 909 910 def _is_applicable(self, host): 911 return self._get_validator(host).is_battery_expected() 912 913 def _get_validator(self, host): 914 if not getattr(self, '_validator', None): 915 self._validator = battery_validator.BatteryValidator(host) 916 return self._validator 917 918 @property 919 def description(self): 920 return 'Ensure DUT battery is in good state.' 921 922 923class ServoKeyboardMapVerifier(hosts.Verifier): 924 """Not critical verify to flash servo keyboard for the host. 925 926 Check if host support servo keyboard and update if firmware is not present. 927 """ 928 929 @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 930 def verify(self, host): 931 try: 932 flasher = servo_keyboard_flasher.ServoKeyboardMapFlasher() 933 if flasher.is_image_supported(host): 934 flasher.update(host) 935 except Exception as e: 936 logging.debug('(Not critical) %s', e) 937 raise hosts.AutoservNonCriticalVerifyError( 938 'Fail to verify/update servo keyboard map on the host.') 939 940 def _is_applicable(self, host): 941 if host.servo: 942 return True 943 return False 944 945 @property 946 def description(self): 947 return 'Verify and update servo keyboard map.' 948 949 950class ServoMacAddressVerifier(hosts.Verifier): 951 """Not critical verify to cache NIC mac address for the host on servo. 952 953 Servo_v4 plugged to the DUT and providing NIC for that. We caching mac 954 address on servod side for better debugging. 955 """ 956 957 @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 958 def verify(self, host): 959 try: 960 helper = mac_address_helper.MacAddressHelper() 961 helper.update_if_needed(host) 962 except Exception as e: 963 logging.debug('(Not critical) %s', e) 964 raise hosts.AutoservNonCriticalVerifyError( 965 'Fail to verify/update servo NIC mac address for host.') 966 967 def _is_applicable(self, host): 968 if host.servo: 969 return True 970 return False 971 972 @property 973 def description(self): 974 return 'Verify and update cached NIC mac address.' 975 976 977class _ResetRepairAction(hosts.RepairAction): 978 """Common handling for repair actions that reset a DUT.""" 979 980 def _collect_logs(self, host): 981 """Collect logs from a successfully repaired DUT.""" 982 dirname = 'after_%s' % self.tag 983 local_log_dir = crashcollect.get_crashinfo_dir(host, dirname) 984 # Collect crash info. 985 crashcollect.get_crashinfo(host, None) 986 987 def _check_reset_success(self, host): 988 """Check whether reset succeeded, and gather logs if possible.""" 989 # Waiting to boot device after repair action. 990 if host.wait_up(host.BOOT_TIMEOUT): 991 if host.get_verifier_state('ssh') == hosts.VERIFY_SUCCESS: 992 logging.debug( 993 'Skip collection logs due DUT was sshable before') 994 return 995 try: 996 # Collect logs once we regain ssh access before 997 # clobbering them. 998 self._collect_logs(host) 999 except Exception: 1000 # If the DUT is up, we want to declare success, even if 1001 # log gathering fails for some reason. So, if there's 1002 # a failure, just log it and move on. 1003 logging.exception('Non-critical failure in log ' 1004 'collection during %s.', 1005 self.tag) 1006 return 1007 raise hosts.AutoservRepairError( 1008 'Host %s is offline after %s.' % (host.hostname, self.tag), 1009 'failed_to_boot_after_' + self.tag) 1010 1011 1012class ServoSysRqRepair(_ResetRepairAction): 1013 """ 1014 Repair a Chrome device by sending a system request to the kernel. 1015 1016 Sending 3 times the Alt+VolUp+x key combination (aka sysrq-x) 1017 will ask the kernel to panic itself and reboot while conserving 1018 the kernel logs in console ramoops. 1019 """ 1020 1021 @timeout_util.TimeoutDecorator(cros_constants.REPAIR_TIMEOUT_SEC) 1022 def repair(self, host): 1023 # pylint: disable=missing-docstring 1024 repair_utils.require_servo(host, ignore_state=True) 1025 # Press 3 times Alt+VolUp+X 1026 # no checking DUT health between each press as 1027 # killing Chrome is not really likely to fix the DUT SSH. 1028 for _ in range(3): 1029 try: 1030 host.servo.sysrq_x() 1031 except error.TestFail as ex: 1032 raise hosts.AutoservRepairError( 1033 'cannot press sysrq-x: %s.' % str(ex), 1034 'cannot_press_sysrq_x') 1035 # less than 5 seconds between presses. 1036 time.sleep(2.0) 1037 self._check_reset_success(host) 1038 1039 @property 1040 def description(self): 1041 # pylint: disable=missing-docstring 1042 return 'Reset the DUT via keyboard sysrq-x' 1043 1044 1045class ServoResetRepair(_ResetRepairAction): 1046 """Repair a Chrome device by resetting it with servo.""" 1047 1048 @timeout_util.TimeoutDecorator(cros_constants.REPAIR_TIMEOUT_SEC) 1049 def repair(self, host): 1050 # pylint: disable=missing-docstring 1051 repair_utils.require_servo(host, ignore_state=True) 1052 host.servo.get_power_state_controller().reset() 1053 self._check_reset_success(host) 1054 1055 def _is_applicable(self, host): 1056 if host.servo: 1057 return True 1058 return False 1059 1060 @property 1061 def description(self): 1062 # pylint: disable=missing-docstring 1063 return 'Reset the DUT via servo' 1064 1065 1066class ServoCr50RebootRepair(_ResetRepairAction): 1067 """ 1068 Repair a Chrome device by resetting cr50 by servo. 1069 1070 Reset cr50 which is ec+ccd reset. 1071 """ 1072 1073 @timeout_util.TimeoutDecorator(cros_constants.REPAIR_TIMEOUT_SEC) 1074 def repair(self, host): 1075 # pylint: disable=missing-docstring 1076 try: 1077 host.servo.get_power_state_controller().cr50_reset() 1078 self._check_reset_success(host) 1079 finally: 1080 # cr50 reset will clear some some init like `ccd testlab open` 1081 # so we want to re-initialize servo after cr50 reset if the main 1082 # device uses cr50 console commands. 1083 if host.servo.main_device_uses_gsc_drv(): 1084 host.servo.initialize_dut() 1085 1086 def _is_applicable(self, host): 1087 if host.servo: 1088 if host.servo.has_control('cr50_reboot'): 1089 return True 1090 return False 1091 1092 @property 1093 def description(self): 1094 # pylint: disable=missing-docstring 1095 return 'Reset(cr50) the DUT via servo' 1096 1097 1098class DevDefaultBootRepair(hosts.RepairAction): 1099 """Repair a CrOS target by setting dev_default_boot to 'disk'""" 1100 1101 @timeout_util.TimeoutDecorator(cros_constants.SHORT_REPAIR_TIMEOUT_SEC) 1102 def repair(self, host): 1103 # pylint: disable=missing-docstring 1104 host.run('crossystem dev_default_boot=disk', ignore_status=True) 1105 1106 @property 1107 def description(self): 1108 # pylint: disable=missing-docstring 1109 return "Set dev_default_boot to 'disk'" 1110 1111 1112class CrosRebootRepair(repair_utils.RebootRepair): 1113 """Repair a CrOS target by clearing dev mode and rebooting it.""" 1114 1115 @timeout_util.TimeoutDecorator(cros_constants.REPAIR_TIMEOUT_SEC) 1116 def repair(self, host): 1117 # pylint: disable=missing-docstring 1118 # N.B. We need to reboot regardless of whether clearing 1119 # dev_mode succeeds or fails. 1120 host.run('/usr/share/vboot/bin/set_gbb_flags.sh 0', 1121 ignore_status=True) 1122 host.run('crossystem disable_dev_request=1', 1123 ignore_status=True) 1124 super(CrosRebootRepair, self).repair(host) 1125 1126 @property 1127 def description(self): 1128 # pylint: disable=missing-docstring 1129 return 'Reset GBB flags and Reboot the host' 1130 1131 1132class ProvisioningLabelsRepair(hosts.RepairAction): 1133 """Repair issue with provisioning labels for the host. 1134 1135 The repair is doing simple clean up of labels as next provisioning will 1136 re-generate required fields. 1137 """ 1138 1139 @timeout_util.TimeoutDecorator(cros_constants.SHORT_REPAIR_TIMEOUT_SEC) 1140 def repair(self, host): 1141 afe_utils.clean_provision_labels(host) 1142 1143 @property 1144 def description(self): 1145 # pylint: disable=missing-docstring 1146 return 'Cleanup provisioning labels for the host' 1147 1148 1149class EnrollmentCleanupRepair(hosts.RepairAction): 1150 """Cleanup enrollment state on ChromeOS device""" 1151 1152 @timeout_util.TimeoutDecorator(cros_constants.REPAIR_TIMEOUT_SEC) 1153 def repair(self, host): 1154 # Reset VPD enrollment state. 1155 host.run('/usr/sbin/update_rw_vpd check_enrollment 0') 1156 1157 # Clear TPM Owner state. 1158 tpm_utils.ClearTPMOwnerRequest(host, wait_for_ready=True, 1159 timeout=host.BOOT_TIMEOUT) 1160 1161 def _is_applicable(self, host): 1162 info = host.host_info_store.get() 1163 # if os type is missing from host_info, then we assume it's cros. 1164 return getattr(info, 'os', 'cros') in ('', 'cros') 1165 1166 @property 1167 def description(self): 1168 # pylint: disable=missing-docstring 1169 return 'Cleanup enrollment state and reboot the host' 1170 1171 1172class ProvisionRepair(hosts.RepairAction): 1173 """ 1174 Repair by re-installing a test image using quick provision. 1175 1176 Try to install the DUT's designated "stable test image" using the 1177 standard procedure for installing a new test image via quick provision. 1178 """ 1179 1180 @timeout_util.TimeoutDecorator(cros_constants.LONG_REPAIR_TIMEOUT_SEC) 1181 def repair(self, host): 1182 # pylint: disable=missing-docstring 1183 image_name = host.get_cros_repair_image_name() 1184 logging.info('Staging build for provision: %s', image_name) 1185 devserver = dev_server.ImageServer.resolve(image_name, host.hostname) 1186 devserver.trigger_download(image_name, synchronous=False) 1187 update_url = tools.image_url_pattern() % ( 1188 devserver.url(), image_name) 1189 afe_utils.machine_install_and_update_labels(host, update_url) 1190 1191 @property 1192 def description(self): 1193 # pylint: disable=missing-docstring 1194 return 'Re-install the stable build on the host' 1195 1196 1197class PowerWashRepair(ProvisionRepair): 1198 """ 1199 Powerwash the DUT, then re-install using quick provision. 1200 1201 Powerwash the DUT, then attempt to re-install a stable test image as 1202 for `ProvisionRepair`. 1203 """ 1204 1205 @timeout_util.TimeoutDecorator(cros_constants.LONG_REPAIR_TIMEOUT_SEC) 1206 def repair(self, host): 1207 # pylint: disable=missing-docstring 1208 host.run('echo "fast safe" > ' 1209 '/mnt/stateful_partition/factory_install_reset') 1210 host.reboot(timeout=host.POWERWASH_BOOT_TIMEOUT, wait=True) 1211 super(PowerWashRepair, self).repair(host) 1212 1213 @property 1214 def description(self): 1215 # pylint: disable=missing-docstring 1216 return 'Powerwash and then re-install the stable build on the host' 1217 1218 1219class ServoInstallRepair(hosts.RepairAction): 1220 """ 1221 Reinstall a test image from USB using servo. 1222 1223 Use servo to re-install the DUT's designated "stable test image" 1224 from servo-attached USB storage. 1225 """ 1226 1227 # Timeout value for this repair action is specially configured as we need 1228 # stage image to usb drive, install chromeos image. 1229 @timeout_util.TimeoutDecorator(60 * 60) 1230 def repair(self, host): 1231 self.boot_in_recovery = False 1232 # pylint: disable=missing-docstring 1233 repair_utils.require_servo(host, ignore_state=True) 1234 image_name = host.get_cros_repair_image_name() 1235 image_name_on_usb = host._servo_host.validate_image_usbkey() 1236 if image_name_on_usb == image_name: 1237 logging.info( 1238 'Required image %s is already on usbkey,' 1239 ' skipping download.', image_name) 1240 need_update_image = False 1241 else: 1242 logging.info('Required image is not on usbkey.') 1243 need_update_image = True 1244 1245 # Verify if we want to force re-image the USB. 1246 if not need_update_image and host.health_profile: 1247 repair_failed_count = host.health_profile.get_repair_fail_count() 1248 # try to re-image USB when previous attempt failed 1249 if (repair_failed_count > 0 and 1250 (repair_failed_count == 1 or repair_failed_count % 10 == 0)): 1251 logging.info( 1252 'Required re-download image to usbkey as' 1253 ' a previous repair failed. Fail count: %s', 1254 repair_failed_count) 1255 need_update_image = True 1256 1257 update_url = None 1258 if need_update_image: 1259 logging.info('Staging image: %s on caching server.', image_name) 1260 _, update_url = host.stage_image_for_servo() 1261 afe_utils.clean_provision_labels(host) 1262 # Start process to install new image from USB 1263 need_snk = host.require_snk_mode_in_recovery() 1264 1265 host.servo.get_power_state_controller().power_off() 1266 if update_url: 1267 try: 1268 host.install_image_to_servo_usb(image_url=update_url) 1269 except Exception as e: 1270 # Format USB-storage as incorrect download image can cause 1271 # false believe that image downloaded. 1272 self._format_usb_storage(host) 1273 # Powering DUT on as if leave it in off mode can cause issue 1274 # with detecting ccd_cr50 on the board. 1275 host.servo.get_power_state_controller().power_on() 1276 six.reraise(error.AutotestError, str(e), sys.exc_info()[2]) 1277 else: 1278 # Give the DUT some time to power_off if we skip 1279 # download image to usb. (crbug.com/982993) 1280 time.sleep(10) 1281 1282 host.boot_in_recovery_mode(need_snk=need_snk) 1283 # Note that device successful booted from USB 1284 # That mean fw RO is good. 1285 self.boot_in_recovery = True 1286 host.run_install_image(install_timeout=host.ADMIN_INSTALL_TIMEOUT * 2, 1287 need_snk=need_snk, 1288 is_repair=True) 1289 afe_utils.add_provision_labels(host, host.VERSION_PREFIX, image_name) 1290 # Collect info which USB-key used for successful re-image. 1291 host_info = host.host_info_store.get() 1292 if host_info: 1293 usb_state = host_info.get_label_value( 1294 audit_const.SERVO_USB_STATE_PREFIX) 1295 metrics_data = {'host': host.hostname, 'usb_state': usb_state} 1296 metrics.Counter('chromeos/autotest/usbkey_install_success' 1297 ).increment(fields=metrics_data) 1298 1299 def _format_usb_storage(self, host): 1300 """Format USB-storage connected to servo.""" 1301 try: 1302 # Format USB-storage to prevent corrupted image to be 1303 # counted as good image. 1304 usb_path = host.servo.probe_host_usb_dev() 1305 logging.info('Formating %s', usb_path) 1306 cmd = 'mkfs.ext4 -F %s' % usb_path 1307 host._servo_host.run(cmd, ignore_status=True) 1308 except Exception as e: 1309 logging.info('(Not critical) fail to format USB-storage: %s', e) 1310 1311 @property 1312 def description(self): 1313 # pylint: disable=missing-docstring 1314 return 'Reinstall from USB using servo' 1315 1316 1317class ServoResetAfterUSBRepair(_ResetRepairAction): 1318 """Repair a host by resetting it with servo. 1319 1320 This is follow up action for cases when device fail to boot as part of 1321 USB-install. The repair will be applicable only if device was successful 1322 booted from USB-key. 1323 """ 1324 1325 @timeout_util.TimeoutDecorator(cros_constants.REPAIR_TIMEOUT_SEC) 1326 def repair(self, host): 1327 # pylint: disable=missing-docstring 1328 host.servo.get_power_state_controller().reset() 1329 self._check_reset_success(host) 1330 1331 def _is_applicable(self, host): 1332 if not host.servo: 1333 return False 1334 if host.is_marked_for_replacement(): 1335 logging.debug('The device marked for replacement.' 1336 ' Skip the action.') 1337 return False 1338 usb_install = host.get_repair_strategy_node('usb') 1339 if not usb_install: 1340 logging.debug('Strategy node not found! Skip repair action.') 1341 return False 1342 if not getattr(usb_install, 'boot_in_recovery', False): 1343 logging.debug('Device did not boot in recovery mode.' 1344 ' Skip repair action.') 1345 return False 1346 return True 1347 1348 @property 1349 def description(self): 1350 # pylint: disable=missing-docstring 1351 return 'Reset the DUT via servo after USB-install' 1352 1353 1354class RecoverFwAfterUSBRepair(_ResetRepairAction): 1355 """Recover FW on the host when host can boot in recovery mode. 1356 1357 This is follow up action for cases when device fail to boot as part of 1358 USB-install but successful booted in recovery mode. 1359 1360 If host can boot in recovery mode but fail boot in default mode then 1361 probably we have corrupted firmware. The repair try to recover firmware 1362 on the host by booting from USB-key. 1363 """ 1364 1365 # Command to update firmware located on host 1366 _FW_UPDATE_CMD = 'chromeos-firmwareupdate --mode=recovery' 1367 1368 @timeout_util.TimeoutDecorator(cros_constants.LONG_REPAIR_TIMEOUT_SEC) 1369 def repair(self, host): 1370 # pylint: disable=missing-docstring 1371 # Switch USB_key to servo to wake up it as sometimes it can show 1372 # USB-key direction to DUT but it is not yet seeing by DUT. 1373 host.servo.switch_usbkey('host') 1374 time.sleep(host.servo.USB_DETECTION_DELAY) 1375 # Power off the DUT as in this case the host will boot 1376 # in recovery mode with higher chance. 1377 host.servo.get_power_state_controller().power_off() 1378 # Give the DUT some time to power_off if we skip 1379 # download image to usb. (crbug.com/982993) 1380 time.sleep(10) 1381 1382 # Boot host in recovery mode as it is working and verified 1383 # by another repair action. 1384 need_snk = host.require_snk_mode_in_recovery() 1385 try: 1386 host.boot_in_recovery_mode(need_snk=need_snk) 1387 logging.debug('Host booted in recovery mode') 1388 1389 result = host.run(self._FW_UPDATE_CMD, ignore_status=True) 1390 if result.exit_status != 0: 1391 logging.error('chromeos-firmwareupdate failed: %s', 1392 result.stdout.strip()) 1393 host.halt() 1394 finally: 1395 # We need reset the DUT no matter success or not, 1396 # as we don't want leave the DUT in boot from usb state. 1397 # N.B. The Servo API requires that we use power_on() here 1398 # for two reasons: 1399 # 1) After turning on a DUT in recovery mode, you must turn 1400 # it off and then on with power_on() once more to 1401 # disable recovery mode (this is a Parrot specific 1402 # requirement). 1403 # 2) After power_off(), the only way to turn on is with 1404 # power_on() (this is a Storm specific requirement). 1405 logging.debug('Power cycling DUT through servo.') 1406 host.servo.get_power_state_controller().power_off() 1407 host.servo.switch_usbkey('off') 1408 if need_snk: 1409 # Attempt to restore servo_v4 role to 'src' mode. 1410 host.servo.set_servo_v4_role('src') 1411 # Use cold-reset instead 'on' to increase the chance to boot DUT 1412 host.servo.get_power_state_controller().reset() 1413 self._check_reset_success(host) 1414 1415 def _is_applicable(self, host): 1416 if not host.servo: 1417 return False 1418 if host.is_marked_for_replacement(): 1419 logging.debug('The device marked for replacement.' 1420 ' Skip the action.') 1421 return False 1422 usb_install = host.get_repair_strategy_node('usb') 1423 if not usb_install: 1424 logging.debug('Strategy node not found! Skip repair action.') 1425 return False 1426 if not getattr(usb_install, 'boot_in_recovery', False): 1427 logging.debug('Device did not boot in recovery mode.' 1428 ' Skip repair action.') 1429 return False 1430 dhp = host.health_profile 1431 if not dhp: 1432 logging.info('Device health profile is not available, cannot' 1433 ' determine if firmware repair is needed.') 1434 return False 1435 if dhp.get_failed_repair_action(self.tag) > 2: 1436 logging.info('Firmware recovery has been attempted and failed 3' 1437 ' times, no need to retry.') 1438 return False 1439 return True 1440 1441 @property 1442 def description(self): 1443 # pylint: disable=missing-docstring 1444 return 'Recover FW on the host after USB-install' 1445 1446 1447class RecoverACPowerRepair(_ResetRepairAction): 1448 """Recover AC detection if AC is not detected. 1449 1450 The fix based on toggle PD negotiating on EC level of DUT. 1451 Repair works only for the DUT which has EC and battery. 1452 """ 1453 1454 @timeout_util.TimeoutDecorator(cros_constants.REPAIR_TIMEOUT_SEC) 1455 def repair(self, host): 1456 # pylint: disable=missing-docstring 1457 repair_utils.require_servo(host, ignore_state=True) 1458 # Verify that EC is available and we can interact with that. 1459 # Do not put it in '_is_applicable' to avoid extra DUT reset. 1460 try: 1461 host.servo.get_ec_board() 1462 except Exception as e: 1463 logging.debug('(Not critical) %s', e) 1464 # if EC is off it will fail to execute any EC command 1465 # to wake it up we do cold-reboot then we will have active ec 1466 # connection for ~30 seconds 1467 host.servo.get_power_state_controller().reset() 1468 try: 1469 if host.servo.get('battery_is_charging'): 1470 # device is changing. 1471 return 1472 except Exception as e: 1473 logging.debug('(Not critical) %s', e) 1474 raise hosts.AutoservRepairError( 1475 'Fail to read battery metrics from EC') 1476 # Simple off-on not always working stable in all cases as source-sink 1477 # not working too in another cases. To cover more cases here we do 1478 # both toggle to recover PD negotiation. 1479 # Source/sink switching CC lines to make DUT work as supplying or 1480 # consuming power (between Rp and Rd). 1481 self._set_pd_dualrole(host, 'off') 1482 self._set_pd_dualrole(host, 'on') 1483 self._set_pd_dualrole(host, 'source') 1484 self._set_pd_dualrole(host, 'sink') 1485 # wait to reinitialize PD negotiation and charge a little bit 1486 time.sleep(120) 1487 # Recommended to reset EC after manipulation with PD 1488 host.servo.get_power_state_controller().reset() 1489 # Verify if repair well done. 1490 if not host.servo.get('battery_is_charging'): 1491 raise hosts.AutoservRepairError( 1492 'Fail recovery AC detection fo the DUT.', 1493 'failed_recover_usb_pd_ac') 1494 self._check_reset_success(host) 1495 1496 def _set_pd_dualrole(self, host, role): 1497 host.servo.set_nocheck('ec_uart_flush', 'off') 1498 host.servo.set_nocheck('ec_uart_cmd', 'pd dualrole %s' % role) 1499 host.servo.set_nocheck('ec_uart_flush', 'on') 1500 time.sleep(1) 1501 1502 def _is_applicable(self, host): 1503 if not host._servo_host.is_ec_supported(): 1504 logging.info('The board not support EC') 1505 return False 1506 host_info = host.host_info_store.get() 1507 if host_info.get_label_value('power') != 'battery': 1508 logging.info('The board does not have battery') 1509 return False 1510 return True 1511 1512 @property 1513 def description(self): 1514 # pylint: disable=missing-docstring 1515 return 'Recovery AC of DUT' 1516 1517 1518class JetstreamTpmRepair(hosts.RepairAction): 1519 """Repair by resetting TPM and rebooting.""" 1520 1521 @timeout_util.TimeoutDecorator(cros_constants.REPAIR_TIMEOUT_SEC) 1522 def repair(self, host): 1523 # pylint: disable=missing-docstring 1524 host.run('rm -f /var/cache/ap/setup-network', ignore_status=True) 1525 host.run('rm -f /home/chronos/.oobe_completed', ignore_status=True) 1526 host.run('rm -f /home/.shadow/.can_attempt_ownership', 1527 ignore_status=True) 1528 host.run('crossystem clear_tpm_owner_request=1', ignore_status=True) 1529 host.reboot() 1530 1531 @property 1532 def description(self): 1533 # pylint: disable=missing-docstring 1534 return 'Reset TPM and reboot' 1535 1536 1537class JetstreamServiceRepair(hosts.RepairAction): 1538 """Repair by restarting Jetstream services.""" 1539 1540 @timeout_util.TimeoutDecorator(cros_constants.REPAIR_TIMEOUT_SEC) 1541 def repair(self, host): 1542 # pylint: disable=missing-docstring 1543 host.cleanup_services() 1544 1545 @property 1546 def description(self): 1547 # pylint: disable=missing-docstring 1548 return 'Restart Jetstream services' 1549 1550 1551def _cros_verify_dag(): 1552 """Return the verification DAG for a `CrosHost`.""" 1553 return _cros_verify_base_dag() + _cros_verify_extended_dag() 1554 1555 1556def _cros_verify_base_dag(): 1557 """Return the base verification DAG for a `CrosHost`.""" 1558 FirmwareStatusVerifier = cros_firmware.FirmwareStatusVerifier 1559 FirmwareVersionVerifier = cros_firmware.FirmwareVersionVerifier 1560 verify_dag = ( 1561 (repair_utils.PingVerifier, 'ping', ()), 1562 (repair_utils.SshVerifier, 'ssh', ('ping', )), 1563 (ServoUSBDriveVerifier, 'usb_drive', ()), 1564 (DevDefaultBootVerifier, 'dev_default_boot', ('ssh', )), 1565 (DevModeVerifier, 'devmode', ('ssh', )), 1566 (EnrollmentStateVerifier, 'enrollment_state', ('ssh', )), 1567 (HWIDVerifier, 'hwid', ('ssh', )), 1568 (ACPowerVerifier, 'power', ('ssh', )), 1569 (EXT4fsErrorVerifier, 'ext4', ('ssh', )), 1570 (WritableVerifier, 'writable', ('ssh', )), 1571 (TPMStatusVerifier, 'tpm', ('ssh', )), 1572 (UpdateSuccessVerifier, 'good_provision', ('ssh', )), 1573 (FirmwareTpmVerifier, 'faft_tpm', ('ssh', )), 1574 (FirmwareStatusVerifier, 'fwstatus', ('ssh', )), 1575 (FirmwareVersionVerifier, 'rwfw', ('ssh', )), 1576 (PythonVerifier, 'python', ('ssh', )), 1577 (repair_utils.LegacyHostVerifier, 'cros', ('ssh', )), 1578 (ProvisioningLabelsVerifier, 'provisioning_labels', ('ssh', )), 1579 ) 1580 return verify_dag 1581 1582 1583def _cros_verify_extended_dag(): 1584 """Return the extended verification DAG for a `CrosHost`.""" 1585 return ( 1586 (StopStartUIVerifier, 'stop_start_ui', ('ssh', )), 1587 (DUTStorageVerifier, 'storage', ('ssh', )), 1588 (AuditBattery, 'audit_battery', ()), 1589 (GscToolPresentVerifier, 'dut_gsctool', ('ssh', )), 1590 (ServoKeyboardMapVerifier, 'dut_servo_keyboard', ('ssh', )), 1591 (ServoMacAddressVerifier, 'dut_servo_macaddr', ('ssh', )), 1592 ) 1593 1594 1595def _cros_basic_repair_actions( 1596 servo_reset_trigger=DEFAULT_SERVO_RESET_TRIGGER 1597): 1598 """Return the basic repair actions for a `CrosHost` 1599 1600 @param servo_reset_trigger: sequence of verifiers that trigger servo reset 1601 and servo cr50 reboot repair. 1602 """ 1603 repair_actions = ( 1604 # RPM cycling must precede Servo reset: if the DUT has a dead 1605 # battery, we need to reattach AC power before we reset via servo. 1606 (repair_utils.RPMCycleRepair, 'rpm', (), ( 1607 'ping', 1608 'ssh', 1609 'power', 1610 )), 1611 (ServoResetRepair, 'servoreset', (), servo_reset_trigger), 1612 (ServoCr50RebootRepair, 'cr50_reset', (), servo_reset_trigger), 1613 (ServoSysRqRepair, 'sysrq', (), ( 1614 'ping', 1615 'ssh', 1616 )), 1617 (ProvisioningLabelsRepair, 'provisioning_labels_repair', ('ssh', ), 1618 ('provisioning_labels', )), 1619 1620 # N.B. FaftFirmwareRepair can't fix a 'good_provision' failure 1621 # directly, because it doesn't remove the flag file that triggers 1622 # the failure. We include it as a repair trigger because it's 1623 # possible the the last update failed because of the firmware, 1624 # and we want the repair steps below to be able to trust the 1625 # firmware. 1626 (cros_firmware.FaftFirmwareRepair, 'faft_firmware_repair', (), ( 1627 'ping', 1628 'ssh', 1629 'fwstatus', 1630 'good_provision', 1631 )), 1632 (DevDefaultBootRepair, 'set_default_boot', ('ssh', ), 1633 ('dev_default_boot', )), 1634 (CrosRebootRepair, 'reboot', ('ssh', ), ( 1635 'devmode', 1636 'writable', 1637 )), 1638 (EnrollmentCleanupRepair, 'cleanup_enrollment', ('ssh', ), 1639 ('enrollment_state', )), 1640 ) 1641 return repair_actions 1642 1643 1644def _cros_extended_repair_actions(provision_triggers=_CROS_PROVISION_TRIGGERS, 1645 powerwash_triggers=_CROS_POWERWASH_TRIGGERS, 1646 usb_triggers=_CROS_USB_TRIGGERS, 1647 usb_dependencies=_CROS_USB_DEPENDENCIES): 1648 """Return the extended repair actions for a `CrosHost`""" 1649 1650 # The dependencies and triggers for the 'provision', 'powerwash', and 'usb' 1651 # repair actions stack up: Each one is able to repair progressively 1652 # more verifiers than the one before. The 'triggers' lists specify 1653 # the progression. 1654 1655 repair_actions = ( 1656 (ProvisionRepair, 'provision', usb_triggers + powerwash_triggers, 1657 provision_triggers), 1658 (PowerWashRepair, 'powerwash', usb_triggers, 1659 powerwash_triggers + provision_triggers), 1660 ( 1661 ServoInstallRepair, 1662 'usb', 1663 usb_dependencies, 1664 # faft_tpm is a trigger of usb repair action but should not be 1665 # dependence of provision and powerwash repair action, due to 1666 # restriction of current structure, we hardcode it here instead 1667 # of put it into _CROS_USB_TRIGGERS. TODO(xianuowang@) refactor 1668 # the logic to create action/verifier DAG for different host 1669 # type after we decouple infra from test autotest repo. 1670 usb_triggers + powerwash_triggers + provision_triggers + 1671 ('faft_tpm', )), 1672 ) 1673 return repair_actions 1674 1675 1676def _cros_repair_actions(): 1677 """Return the repair actions for a `CrosHost`.""" 1678 1679 servo_reset_trigger = DEFAULT_SERVO_RESET_TRIGGER 1680 firmware_triggers = _CROS_FIRMWARE_TRIGGERS 1681 ac_triggers = _CROS_AC_TRIGGERS 1682 usb_dependencies = _CROS_USB_DEPENDENCIES 1683 provision_triggers = _CROS_PROVISION_TRIGGERS + ( 1684 'stop_start_ui', 1685 'dut_gsctool', 1686 ) 1687 powerwash_triggers = _CROS_POWERWASH_TRIGGERS 1688 usb_triggers = _CROS_USB_TRIGGERS 1689 1690 repair_actions = ( 1691 # RPM cycling must precede Servo reset: if the DUT has a dead 1692 # battery, we need to reattach AC power before we reset via servo. 1693 (repair_utils.RPMCycleRepair, 'rpm', (), ( 1694 'ping', 1695 'ssh', 1696 'power', 1697 )), 1698 (ServoResetRepair, 'servoreset', (), servo_reset_trigger), 1699 (ServoCr50RebootRepair, 'cr50_reset', (), servo_reset_trigger), 1700 (ServoSysRqRepair, 'sysrq', (), ( 1701 'ping', 1702 'ssh', 1703 )), 1704 (ProvisioningLabelsRepair, 'provisioning_labels_repair', ('ssh', ), 1705 ('provisioning_labels', )), 1706 1707 # N.B. FaftFirmwareRepair can't fix a 'good_provision' failure 1708 # directly, because it doesn't remove the flag file that triggers 1709 # the failure. We include it as a repair trigger because it's 1710 # possible the the last update failed because of the firmware, 1711 # and we want the repair steps below to be able to trust the 1712 # firmware. 1713 (cros_firmware.FaftFirmwareRepair, 'faft_firmware_repair', (), ( 1714 'ping', 1715 'ssh', 1716 'fwstatus', 1717 'good_provision', 1718 )), 1719 (DevDefaultBootRepair, 'set_default_boot', ('ssh', ), 1720 ('dev_default_boot', )), 1721 (CrosRebootRepair, 'reboot', ('ssh', ), ( 1722 'devmode', 1723 'writable', 1724 )), 1725 (EnrollmentCleanupRepair, 'cleanup_enrollment', ('ssh', ), 1726 ('enrollment_state', )), 1727 (cros_firmware.GeneralFirmwareRepair, 'general_firmware', 1728 usb_dependencies, firmware_triggers), 1729 (RecoverACPowerRepair, 'ac_recover', (), ac_triggers), 1730 (ProvisionRepair, 'provision', usb_triggers + powerwash_triggers, 1731 provision_triggers), 1732 (PowerWashRepair, 'powerwash', usb_triggers, 1733 powerwash_triggers + provision_triggers), 1734 ( 1735 ServoInstallRepair, 1736 'usb', 1737 usb_dependencies, 1738 # faft_tpm is a trigger of usb repair action but should 1739 # not be dependence of provision and powerwash repair 1740 # action, due to restriction of current structure, we 1741 # hardcode it here instead of put it into 1742 # _CROS_USB_TRIGGERS. TODO(xianuowang@) refactor the logic 1743 # to create action/verifier DAG for different host type 1744 # after we decouple infra from test autotest repo. 1745 usb_triggers + powerwash_triggers + provision_triggers + 1746 ('faft_tpm', )), 1747 (ServoResetAfterUSBRepair, 'servo_reset_after_usb', 1748 (usb_dependencies), ( 1749 'ping', 1750 'ssh', 1751 )), 1752 (RecoverFwAfterUSBRepair, 'recover_fw_after_usb', 1753 (usb_dependencies), ( 1754 'ping', 1755 'ssh', 1756 )), 1757 ) 1758 return repair_actions 1759 1760 1761def create_cros_repair_strategy(): 1762 """Return a `RepairStrategy` for a `CrosHost`.""" 1763 verify_dag = _cros_verify_dag() 1764 repair_actions = _cros_repair_actions() 1765 return hosts.RepairStrategy(verify_dag, repair_actions, 'cros') 1766 1767 1768def _moblab_verify_dag(): 1769 """Return the verification DAG for a `MoblabHost`.""" 1770 verify_dag = ( 1771 (repair_utils.SshVerifier, 'ssh', ()), 1772 (ACPowerVerifier, 'power', ('ssh',)), 1773 (PythonVerifier, 'python', ('ssh',)), 1774 (repair_utils.LegacyHostVerifier, 'cros', ('ssh',)), 1775 ) 1776 return verify_dag 1777 1778 1779def _moblab_repair_actions(): 1780 """Return the repair actions for a `MoblabHost`.""" 1781 repair_actions = ( 1782 (repair_utils.RPMCycleRepair, 'rpm', (), ('ssh', 'power',)), 1783 (ProvisionRepair, 'provision', ('ssh',), ('power', 'python', 'cros')), 1784 ) 1785 return repair_actions 1786 1787 1788def create_moblab_repair_strategy(): 1789 """ 1790 Return a `RepairStrategy` for a `MoblabHost`. 1791 1792 Moblab is a subset of the CrOS verify and repair. Several pieces 1793 are removed because they're not expected to be meaningful. Some 1794 others are removed for more specific reasons: 1795 1796 'tpm': Moblab DUTs don't run the tests that matter to this 1797 verifier. TODO(jrbarnette) This assertion is unproven. 1798 1799 'good_provision': This verifier can't pass, because the Moblab provision 1800 procedure doesn't properly delete the PROVISION_FAILED file. 1801 TODO(jrbarnette) We should refactor ChromiumOSProvisioner so 1802 that it can be different for Moblab. 1803 1804 'firmware': Moblab DUTs shouldn't be in FAFT pools, so we don't try 1805 this. 1806 1807 'powerwash': Powerwash on Moblab causes trouble with deleting the 1808 DHCP leases file, so we skip it. 1809 """ 1810 verify_dag = _moblab_verify_dag() 1811 repair_actions = _moblab_repair_actions() 1812 return hosts.RepairStrategy(verify_dag, repair_actions, 'moblab') 1813 1814 1815def _jetstream_repair_actions(): 1816 """Return the repair actions for a `JetstreamHost`.""" 1817 provision_triggers = _CROS_PROVISION_TRIGGERS 1818 jetstream_tpm_triggers = ('jetstream_tpm', 'jetstream_attestation') 1819 jetstream_service_triggers = (jetstream_tpm_triggers + 1820 ('jetstream_services',)) 1821 base_actions = _cros_basic_repair_actions(servo_reset_trigger=( 1822 'ping', 1823 'ssh', 1824 )) 1825 custom_actions = ( 1826 (JetstreamTpmRepair, 'jetstream_tpm_repair', 1827 _JETSTREAM_USB_TRIGGERS + _CROS_POWERWASH_TRIGGERS, 1828 provision_triggers + jetstream_tpm_triggers), 1829 (JetstreamServiceRepair, 'jetstream_service_repair', 1830 _JETSTREAM_USB_TRIGGERS + _CROS_POWERWASH_TRIGGERS + 1831 ('jetstream_tpm', 'jetstream_attestation'), 1832 provision_triggers + jetstream_service_triggers), 1833 ) 1834 extend_actions = _cros_extended_repair_actions( 1835 provision_triggers=provision_triggers + jetstream_service_triggers, 1836 usb_triggers=_JETSTREAM_USB_TRIGGERS) 1837 return base_actions + custom_actions + extend_actions 1838 1839 1840def _jetstream_verify_dag(): 1841 """Return the verification DAG for a `JetstreamHost`.""" 1842 verify_dag = _cros_verify_base_dag() + ( 1843 (JetstreamTpmVerifier, 'jetstream_tpm', ('ssh',)), 1844 (JetstreamAttestationVerifier, 'jetstream_attestation', ('ssh',)), 1845 (JetstreamServicesVerifier, 'jetstream_services', ('ssh',)), 1846 ) 1847 return verify_dag 1848 1849 1850def create_jetstream_repair_strategy(): 1851 """ 1852 Return a `RepairStrategy` for a `JetstreamHost`. 1853 1854 The Jetstream repair strategy is based on the CrOS verify and repair, 1855 but adds the JetstreamServicesVerifier. 1856 """ 1857 verify_dag = _jetstream_verify_dag() 1858 repair_actions = _jetstream_repair_actions() 1859 return hosts.RepairStrategy(verify_dag, repair_actions, 'jetstream') 1860 1861 1862# TODO(pprabhu) Move this to a better place. I have no idea what that place 1863# would be. 1864def _is_virtual_machine(host): 1865 """Determine whether the given |host| is a virtual machine. 1866 1867 @param host: a hosts.Host object. 1868 @returns True if the host is a virtual machine, False otherwise. 1869 """ 1870 output = host.run('cat /proc/cpuinfo | grep "model name"', 1871 ignore_status=True) 1872 return (output.exit_status == 0 and output.stdout and 1873 'qemu' in output.stdout.lower()) 1874 1875 1876class TpmStatus(dict): 1877 """Wrapper for getting cryptohome status from a host.""" 1878 1879 def __init__(self, host): 1880 super(TpmStatus, self).__init__() 1881 self.update(_get_tpm_status(host)) 1882 1883 @property 1884 def tpm_enabled(self): 1885 # pylint: disable=missing-docstring 1886 return self.get('is_enabled') == True 1887 1888 @property 1889 def tpm_owned(self): 1890 # pylint: disable=missing-docstring 1891 return self.get('is_owned') == True 1892 1893 @property 1894 def tpm_can_load_srk(self): 1895 # pylint: disable=missing-docstring 1896 return self.tpm_owned and self.get('is_srk_default_auth') == True 1897 1898 @property 1899 def tpm_can_load_srk_pubkey(self): 1900 # pylint: disable=missing-docstring 1901 return self.tpm_owned and self.get('is_srk_default_auth') == True 1902 1903 1904def _get_tpm_status(host): 1905 """Returns a dictionary containing the TPM status. 1906 1907 @param host: a hosts.Host object. 1908 @returns A dictionary containing the TPM status. 1909 @raises AutoservVerifyError: if the output could not be parsed or the TPM 1910 status is missing. 1911 @raises hosts.AutoservRunError: if the cryptohome command failed. 1912 """ 1913 try: 1914 output = host.run( 1915 'tpm_manager_client status --nonsensitive').stdout.strip() 1916 lines = output.split('\n')[1:-1] 1917 status = {} 1918 for item in lines: 1919 item = item.split(':') 1920 if not item[0]: 1921 continue 1922 if len(item) == 1: 1923 item.append('') 1924 item = [x.strip() for x in item] 1925 item[1] = True if item[1] == 'true' else item[1] 1926 item[1] = False if item[1] == 'false' else item[1] 1927 status[item[0]] = item[1] 1928 if status['status'] != 'STATUS_SUCCESS': 1929 raise hosts.AutoservVerifyError('TPM status is missing') 1930 return status 1931 except ValueError: 1932 raise hosts.AutoservVerifyError('Unable to parse cryptohome status') 1933