1*9c5db199SXin Li# Lint as: python2, python3 2*9c5db199SXin Li# Copyright 2016 The Chromium OS Authors. All rights reserved. 3*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be 4*9c5db199SXin Li# found in the LICENSE file. 5*9c5db199SXin Li 6*9c5db199SXin Lifrom __future__ import absolute_import 7*9c5db199SXin Lifrom __future__ import division 8*9c5db199SXin Lifrom __future__ import print_function 9*9c5db199SXin Li 10*9c5db199SXin Liimport functools 11*9c5db199SXin Liimport logging 12*9c5db199SXin Liimport math 13*9c5db199SXin Liimport sys 14*9c5db199SXin Liimport time 15*9c5db199SXin Li 16*9c5db199SXin Liimport common 17*9c5db199SXin Lifrom autotest_lib.client.common_lib import error 18*9c5db199SXin Lifrom autotest_lib.client.common_lib import hosts 19*9c5db199SXin Lifrom autotest_lib.client.common_lib import utils 20*9c5db199SXin Lifrom autotest_lib.server.cros.servo import servo 21*9c5db199SXin Lifrom autotest_lib.server.hosts import cros_constants 22*9c5db199SXin Lifrom autotest_lib.server.hosts import repair_utils 23*9c5db199SXin Lifrom autotest_lib.server.hosts import servo_constants 24*9c5db199SXin Lifrom autotest_lib.server.cros.servo.topology import servo_topology 25*9c5db199SXin Lifrom autotest_lib.site_utils.admin_audit import servo_updater 26*9c5db199SXin Liimport six 27*9c5db199SXin Li 28*9c5db199SXin Litry: 29*9c5db199SXin Li from autotest_lib.utils.frozen_chromite.lib import metrics 30*9c5db199SXin Liexcept ImportError: 31*9c5db199SXin Li metrics = utils.metrics_mock 32*9c5db199SXin Li 33*9c5db199SXin Lifrom autotest_lib.utils.frozen_chromite.lib import timeout_util 34*9c5db199SXin Li 35*9c5db199SXin Lidef ignore_exception_for_non_cros_host(func): 36*9c5db199SXin Li """ 37*9c5db199SXin Li Decorator to ignore ControlUnavailableError if servo host is not cros host. 38*9c5db199SXin Li When using test_that command on a workstation, this enables usage of 39*9c5db199SXin Li additional servo devices such as servo micro and Sweetberry. This shall not 40*9c5db199SXin Li change any lab behavior. 41*9c5db199SXin Li """ 42*9c5db199SXin Li @functools.wraps(func) 43*9c5db199SXin Li def wrapper(self, host): 44*9c5db199SXin Li """ 45*9c5db199SXin Li Wrapper around func. 46*9c5db199SXin Li """ 47*9c5db199SXin Li try: 48*9c5db199SXin Li func(self, host) 49*9c5db199SXin Li except servo.ControlUnavailableError as e: 50*9c5db199SXin Li if host.is_cros_host(): 51*9c5db199SXin Li raise 52*9c5db199SXin Li logging.warning("Servo host is not cros host, ignore %s: %s", 53*9c5db199SXin Li type(e).__name__, e) 54*9c5db199SXin Li return wrapper 55*9c5db199SXin Li 56*9c5db199SXin Li 57*9c5db199SXin Liclass _UpdateVerifier(hosts.Verifier): 58*9c5db199SXin Li """ 59*9c5db199SXin Li Verifier to trigger a servo host update, if necessary. 60*9c5db199SXin Li 61*9c5db199SXin Li The verifier works only for servo_v3. 62*9c5db199SXin Li The operation doesn't wait for the update to complete and is 63*9c5db199SXin Li considered a success whether or not the servo is currently 64*9c5db199SXin Li up-to-date. 65*9c5db199SXin Li """ 66*9c5db199SXin Li 67*9c5db199SXin Li @timeout_util.TimeoutDecorator(cros_constants.LONG_VERIFY_TIMEOUT_SEC) 68*9c5db199SXin Li def verify(self, host): 69*9c5db199SXin Li try: 70*9c5db199SXin Li if ( 71*9c5db199SXin Li not host.get_dut_host_info() 72*9c5db199SXin Li or not host.get_dut_host_info().servo_cros_stable_version): 73*9c5db199SXin Li logging.info('Servo stable version missed.' 74*9c5db199SXin Li ' Skip update check action.') 75*9c5db199SXin Li return 76*9c5db199SXin Li # We have seen cases that invalid GPT headers/entries block 77*9c5db199SXin Li # v3s from been update, so always try to repair here. 78*9c5db199SXin Li # See crbug.com/994396, crbug.com/1057302. 79*9c5db199SXin Li host.run('cgpt repair /dev/mmcblk0', ignore_status=True) 80*9c5db199SXin Li host.update_image() 81*9c5db199SXin Li # We don't want failure from update block DUT repair action. 82*9c5db199SXin Li # See crbug.com/1029950. 83*9c5db199SXin Li except Exception as e: 84*9c5db199SXin Li six.reraise(hosts.AutoservNonCriticalVerifyError, 85*9c5db199SXin Li hosts.AutoservNonCriticalVerifyError(e), 86*9c5db199SXin Li sys.exc_info()[2]) 87*9c5db199SXin Li 88*9c5db199SXin Li def _is_applicable(self, host): 89*9c5db199SXin Li # Run only for servo_v3 host. 90*9c5db199SXin Li if host.is_labstation() or host.is_containerized_servod(): 91*9c5db199SXin Li return False 92*9c5db199SXin Li # Only run if the host is in the physical lab. 93*9c5db199SXin Li return host.is_in_lab() 94*9c5db199SXin Li 95*9c5db199SXin Li @property 96*9c5db199SXin Li def description(self): 97*9c5db199SXin Li return 'Servo_v3 host software is up-to-date' 98*9c5db199SXin Li 99*9c5db199SXin Li 100*9c5db199SXin Liclass _StartServodVerifier(hosts.Verifier): 101*9c5db199SXin Li """First start of servod on the host. 102*9c5db199SXin Li 103*9c5db199SXin Li Single running action to start servod in the first time. 104*9c5db199SXin Li This verifier created to fit current flow and will be revisited later. 105*9c5db199SXin Li Action never fails! 106*9c5db199SXin Li """ 107*9c5db199SXin Li 108*9c5db199SXin Li @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 109*9c5db199SXin Li def verify(self, host): 110*9c5db199SXin Li if not hasattr(self, 'started'): 111*9c5db199SXin Li logging.info('Starting servod!') 112*9c5db199SXin Li host.restart_servod(quick_startup=True) 113*9c5db199SXin Li # caching the value to prevent restart service when trigger verifier. 114*9c5db199SXin Li self.started = True 115*9c5db199SXin Li 116*9c5db199SXin Li @property 117*9c5db199SXin Li def description(self): 118*9c5db199SXin Li return 'Initial servod start' 119*9c5db199SXin Li 120*9c5db199SXin Li 121*9c5db199SXin Liclass _RootServoPresentVerifier(hosts.Verifier): 122*9c5db199SXin Li """Verifier that first servo is present.""" 123*9c5db199SXin Li 124*9c5db199SXin Li @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 125*9c5db199SXin Li def verify(self, host): 126*9c5db199SXin Li device = None 127*9c5db199SXin Li topology = host.get_topology() 128*9c5db199SXin Li topology.read(host.get_dut_host_info()) 129*9c5db199SXin Li try: 130*9c5db199SXin Li device = topology.get_root_servo() 131*9c5db199SXin Li except Exception as e: 132*9c5db199SXin Li if host.is_containerized_servod(): 133*9c5db199SXin Li host.restart_servod() 134*9c5db199SXin Li logging.debug('Restarting servod container (Not critical) %s', 135*9c5db199SXin Li e) 136*9c5db199SXin Li else: 137*9c5db199SXin Li host.request_reboot() 138*9c5db199SXin Li logging.info( 139*9c5db199SXin Li 'Reboot labstation requested, it will be handled' 140*9c5db199SXin Li ' by labstation AdminRepair task.' 141*9c5db199SXin Li ' Unable to detect root servo info from topology.') 142*9c5db199SXin Li logging.debug('(Not critical) %s', e) 143*9c5db199SXin Li if device: 144*9c5db199SXin Li logging.info('Root servo is present') 145*9c5db199SXin Li return 146*9c5db199SXin Li device = topology.get_root_servo_from_cache() 147*9c5db199SXin Li if device: 148*9c5db199SXin Li logging.debug('Found device: %s', device) 149*9c5db199SXin Li if device.get_serial_number() != host.servo_serial: 150*9c5db199SXin Li self.serial_mismatch = True 151*9c5db199SXin Li raise hosts.AutoservVerifyError('Serial mismatch detected') 152*9c5db199SXin Li logging.info('Root servo is present') 153*9c5db199SXin Li return 154*9c5db199SXin Li # Leaving error in case we got empty device. 155*9c5db199SXin Li raise hosts.AutoservVerifyError('Root servo not found!') 156*9c5db199SXin Li 157*9c5db199SXin Li def _is_applicable(self, host): 158*9c5db199SXin Li if host.is_containerized_servod(): 159*9c5db199SXin Li logging.info('Servod is running within a container.') 160*9c5db199SXin Li return True 161*9c5db199SXin Li if not host.is_labstation(): 162*9c5db199SXin Li logging.info('Not supported for servo_v3.') 163*9c5db199SXin Li return False 164*9c5db199SXin Li # Only run if the host is in the physical lab. 165*9c5db199SXin Li return host.is_in_lab() 166*9c5db199SXin Li 167*9c5db199SXin Li @property 168*9c5db199SXin Li def description(self): 169*9c5db199SXin Li return 'Root servo is present' 170*9c5db199SXin Li 171*9c5db199SXin Li 172*9c5db199SXin Liclass _RootServoV3PresentVerifier(hosts.Verifier): 173*9c5db199SXin Li """Verifier that first servo is present.""" 174*9c5db199SXin Li 175*9c5db199SXin Li RETRY_COUNT = 3 176*9c5db199SXin Li 177*9c5db199SXin Li @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 178*9c5db199SXin Li def verify(self, host): 179*9c5db199SXin Li for a in range(self.RETRY_COUNT): 180*9c5db199SXin Li logging.debug('Attempt: %s find servo board on servo_v3.', a + 1) 181*9c5db199SXin Li present = host.is_servo_board_present_on_servo_v3() 182*9c5db199SXin Li if present == False: 183*9c5db199SXin Li raise hosts.AutoservVerifyError('Servo board not found!') 184*9c5db199SXin Li elif present == True: 185*9c5db199SXin Li logging.debug('Servo board is present') 186*9c5db199SXin Li return 187*9c5db199SXin Li raise hosts.AutoservVerifyError('Fail to find servo board!') 188*9c5db199SXin Li 189*9c5db199SXin Li def _is_applicable(self, host): 190*9c5db199SXin Li if host.is_containerized_servod(): 191*9c5db199SXin Li logging.info('Servod is running within a container.') 192*9c5db199SXin Li return False 193*9c5db199SXin Li # Do not run for servos under labstations. 194*9c5db199SXin Li if host.is_labstation(): 195*9c5db199SXin Li logging.info('Servod is running on labstation.') 196*9c5db199SXin Li return False 197*9c5db199SXin Li # Only run if the host is in the physical lab. 198*9c5db199SXin Li return host.is_in_lab() 199*9c5db199SXin Li 200*9c5db199SXin Li @property 201*9c5db199SXin Li def description(self): 202*9c5db199SXin Li return 'Servo board on servo_v3 is present' 203*9c5db199SXin Li 204*9c5db199SXin Li 205*9c5db199SXin Liclass _ServoFwVerifier(hosts.Verifier): 206*9c5db199SXin Li """Verifier to check is a servo fw is up-to-date.""" 207*9c5db199SXin Li 208*9c5db199SXin Li @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 209*9c5db199SXin Li def verify(self, host): 210*9c5db199SXin Li try: 211*9c5db199SXin Li if servo_updater.any_servo_needs_firmware_update(host): 212*9c5db199SXin Li raise hosts.AutoservVerifyError( 213*9c5db199SXin Li 'Some servo requires firmware update') 214*9c5db199SXin Li except servo_updater.ServoFwVersionMissedError as e: 215*9c5db199SXin Li # Do not fail as it will trigger re-flash fw on the servo 216*9c5db199SXin Li logging.info( 217*9c5db199SXin Li 'Issue with detect new version of firmware for servo.' 218*9c5db199SXin Li ' Please file a bug agains Fleet Automation team (go/fleet-bug)' 219*9c5db199SXin Li ) 220*9c5db199SXin Li 221*9c5db199SXin Li def _is_applicable(self, host): 222*9c5db199SXin Li if host.is_containerized_servod(): 223*9c5db199SXin Li logging.info('Servod is running within a container.') 224*9c5db199SXin Li return True 225*9c5db199SXin Li # Run only for servos under labstations. 226*9c5db199SXin Li if not host.is_labstation(): 227*9c5db199SXin Li logging.info('Not supported for servo_v3.') 228*9c5db199SXin Li return False 229*9c5db199SXin Li # Only run if the host is in the physical lab. 230*9c5db199SXin Li return host.is_in_lab() 231*9c5db199SXin Li 232*9c5db199SXin Li @property 233*9c5db199SXin Li def description(self): 234*9c5db199SXin Li return 'Servo fw is up-to-date' 235*9c5db199SXin Li 236*9c5db199SXin Li 237*9c5db199SXin Liclass _ConfigVerifier(hosts.Verifier): 238*9c5db199SXin Li """ 239*9c5db199SXin Li Base verifier for the servo config file verifiers. 240*9c5db199SXin Li """ 241*9c5db199SXin Li 242*9c5db199SXin Li CONFIG_FILE = '/var/lib/servod/config' 243*9c5db199SXin Li ATTR = '' 244*9c5db199SXin Li 245*9c5db199SXin Li @staticmethod 246*9c5db199SXin Li def _get_config_val(host, config_file, attr): 247*9c5db199SXin Li """ 248*9c5db199SXin Li Get the `attr` for `host` from `config_file`. 249*9c5db199SXin Li 250*9c5db199SXin Li @param host Host to be checked for `config_file`. 251*9c5db199SXin Li @param config_file Path to the config file to be tested. 252*9c5db199SXin Li @param attr Attribute to get from config file. 253*9c5db199SXin Li 254*9c5db199SXin Li @return The attr val as set in the config file, or `None` if 255*9c5db199SXin Li the file was absent. 256*9c5db199SXin Li """ 257*9c5db199SXin Li getboard = ('CONFIG=%s ; [ -f $CONFIG ] && ' 258*9c5db199SXin Li '. $CONFIG && echo $%s' % (config_file, attr)) 259*9c5db199SXin Li attr_val = host.run(getboard, ignore_status=True).stdout 260*9c5db199SXin Li return attr_val.strip('\n') if attr_val else None 261*9c5db199SXin Li 262*9c5db199SXin Li @staticmethod 263*9c5db199SXin Li def _validate_attr(host, val, expected_val, attr, config_file): 264*9c5db199SXin Li """ 265*9c5db199SXin Li Check that the attr setting is valid for the host. 266*9c5db199SXin Li 267*9c5db199SXin Li This presupposes that a valid config file was found. Raise an 268*9c5db199SXin Li execption if: 269*9c5db199SXin Li * There was no attr setting from the file (i.e. the setting 270*9c5db199SXin Li is an empty string), or 271*9c5db199SXin Li * The attr setting is valid, the attr is known, 272*9c5db199SXin Li and the setting doesn't match the DUT. 273*9c5db199SXin Li 274*9c5db199SXin Li @param host Host to be checked for `config_file`. 275*9c5db199SXin Li @param val Value to be tested. 276*9c5db199SXin Li @param expected_val Expected value. 277*9c5db199SXin Li @param attr Attribute we're validating. 278*9c5db199SXin Li @param config_file Path to the config file to be tested. 279*9c5db199SXin Li """ 280*9c5db199SXin Li if not val: 281*9c5db199SXin Li raise hosts.AutoservVerifyError( 282*9c5db199SXin Li 'config file %s exists, but %s ' 283*9c5db199SXin Li 'is not set' % (attr, config_file)) 284*9c5db199SXin Li if expected_val is not None and val != expected_val: 285*9c5db199SXin Li raise hosts.AutoservVerifyError( 286*9c5db199SXin Li '%s is %s; it should be %s' % (attr, val, expected_val)) 287*9c5db199SXin Li 288*9c5db199SXin Li 289*9c5db199SXin Li def _get_config(self, host): 290*9c5db199SXin Li """ 291*9c5db199SXin Li Return the config file to check. 292*9c5db199SXin Li 293*9c5db199SXin Li @param host Host object. 294*9c5db199SXin Li 295*9c5db199SXin Li @return The config file to check. 296*9c5db199SXin Li """ 297*9c5db199SXin Li return '%s_%d' % (self.CONFIG_FILE, host.servo_port) 298*9c5db199SXin Li 299*9c5db199SXin Li @property 300*9c5db199SXin Li def description(self): 301*9c5db199SXin Li return 'servo %s setting is correct' % self.ATTR 302*9c5db199SXin Li 303*9c5db199SXin Li 304*9c5db199SXin Liclass _SerialConfigVerifier(_ConfigVerifier): 305*9c5db199SXin Li """ 306*9c5db199SXin Li Verifier for the servo SERIAL configuration. 307*9c5db199SXin Li """ 308*9c5db199SXin Li 309*9c5db199SXin Li ATTR = 'SERIAL' 310*9c5db199SXin Li 311*9c5db199SXin Li @timeout_util.TimeoutDecorator(cros_constants.SHORT_VERIFY_TIMEOUT_SEC) 312*9c5db199SXin Li def verify(self, host): 313*9c5db199SXin Li """ 314*9c5db199SXin Li Test whether the `host` has a `SERIAL` setting configured. 315*9c5db199SXin Li 316*9c5db199SXin Li This tests the config file names used by the `servod` upstart 317*9c5db199SXin Li job for a valid setting of the `SERIAL` variable. The following 318*9c5db199SXin Li conditions raise errors: 319*9c5db199SXin Li * The SERIAL setting doesn't match the DUT's entry in the AFE 320*9c5db199SXin Li database. 321*9c5db199SXin Li * There is no config file. 322*9c5db199SXin Li """ 323*9c5db199SXin Li if not host.is_cros_host(): 324*9c5db199SXin Li return 325*9c5db199SXin Li # Not all servo hosts will have a servo serial so don't verify if it's 326*9c5db199SXin Li # not set. 327*9c5db199SXin Li if host.servo_serial is None: 328*9c5db199SXin Li return 329*9c5db199SXin Li config = self._get_config(host) 330*9c5db199SXin Li serialval = self._get_config_val(host, config, self.ATTR) 331*9c5db199SXin Li if serialval is None: 332*9c5db199SXin Li raise hosts.AutoservVerifyError( 333*9c5db199SXin Li 'Servo serial is unconfigured; should be %s' 334*9c5db199SXin Li % host.servo_serial 335*9c5db199SXin Li ) 336*9c5db199SXin Li 337*9c5db199SXin Li self._validate_attr(host, serialval, host.servo_serial, self.ATTR, 338*9c5db199SXin Li config) 339*9c5db199SXin Li 340*9c5db199SXin Li 341*9c5db199SXin Li 342*9c5db199SXin Liclass _BoardConfigVerifier(_ConfigVerifier): 343*9c5db199SXin Li """ 344*9c5db199SXin Li Verifier for the servo BOARD configuration. 345*9c5db199SXin Li """ 346*9c5db199SXin Li 347*9c5db199SXin Li ATTR = 'BOARD' 348*9c5db199SXin Li 349*9c5db199SXin Li @timeout_util.TimeoutDecorator(cros_constants.SHORT_VERIFY_TIMEOUT_SEC) 350*9c5db199SXin Li def verify(self, host): 351*9c5db199SXin Li """ 352*9c5db199SXin Li Test whether the `host` has a `BOARD` setting configured. 353*9c5db199SXin Li 354*9c5db199SXin Li This tests the config file names used by the `servod` upstart 355*9c5db199SXin Li job for a valid setting of the `BOARD` variable. The following 356*9c5db199SXin Li conditions raise errors: 357*9c5db199SXin Li * A config file exists, but the content contains no setting 358*9c5db199SXin Li for BOARD. 359*9c5db199SXin Li * The BOARD setting doesn't match the DUT's entry in the AFE 360*9c5db199SXin Li database. 361*9c5db199SXin Li * There is no config file. 362*9c5db199SXin Li """ 363*9c5db199SXin Li if not host.is_cros_host(): 364*9c5db199SXin Li return 365*9c5db199SXin Li config = self._get_config(host) 366*9c5db199SXin Li boardval = self._get_config_val(host, config, self.ATTR) 367*9c5db199SXin Li if boardval is None: 368*9c5db199SXin Li msg = 'Servo board is unconfigured' 369*9c5db199SXin Li if host.servo_board is not None: 370*9c5db199SXin Li msg += '; should be %s' % host.servo_board 371*9c5db199SXin Li raise hosts.AutoservVerifyError(msg) 372*9c5db199SXin Li 373*9c5db199SXin Li self._validate_attr(host, boardval, host.servo_board, self.ATTR, 374*9c5db199SXin Li config) 375*9c5db199SXin Li 376*9c5db199SXin Li 377*9c5db199SXin Liclass _ServodJobVerifier(hosts.Verifier): 378*9c5db199SXin Li """ 379*9c5db199SXin Li Verifier to check that the `servod` upstart job is running. 380*9c5db199SXin Li """ 381*9c5db199SXin Li 382*9c5db199SXin Li @timeout_util.TimeoutDecorator(cros_constants.SHORT_VERIFY_TIMEOUT_SEC) 383*9c5db199SXin Li def verify(self, host): 384*9c5db199SXin Li if not host.is_cros_host(): 385*9c5db199SXin Li return 386*9c5db199SXin Li status_cmd = 'status servod PORT=%d' % host.servo_port 387*9c5db199SXin Li job_status = host.run(status_cmd, ignore_status=True).stdout 388*9c5db199SXin Li if 'start/running' not in job_status: 389*9c5db199SXin Li raise hosts.AutoservVerifyError( 390*9c5db199SXin Li 'servod not running on %s port %d' % 391*9c5db199SXin Li (host.hostname, host.servo_port)) 392*9c5db199SXin Li 393*9c5db199SXin Li @property 394*9c5db199SXin Li def description(self): 395*9c5db199SXin Li return 'servod upstart job is running' 396*9c5db199SXin Li 397*9c5db199SXin Li 398*9c5db199SXin Liclass _ServodEchoVerifier(hosts.Verifier): 399*9c5db199SXin Li """ 400*9c5db199SXin Li Verifier to check that the `servod` upstart job is responsible. 401*9c5db199SXin Li """ 402*9c5db199SXin Li 403*9c5db199SXin Li SERVOD_INITIALIZED = 'servodtool instance wait-for-active -p %d --timeout 60' 404*9c5db199SXin Li SERVOD_RESPONSIVE = 'dut-control -p %d serialname' 405*9c5db199SXin Li 406*9c5db199SXin Li @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 407*9c5db199SXin Li def verify(self, host): 408*9c5db199SXin Li self._verify_servod_initialized(host) 409*9c5db199SXin Li self._verify_servod_responsive(host) 410*9c5db199SXin Li 411*9c5db199SXin Li def _verify_servod_initialized(self, host): 412*9c5db199SXin Li # Verify that servod initialized. 413*9c5db199SXin Li cmd = self.SERVOD_INITIALIZED % host.servo_port 414*9c5db199SXin Li res = host.run(cmd, ignore_status=True, timeout=120) 415*9c5db199SXin Li if res.exit_status != 0: 416*9c5db199SXin Li raise hosts.AutoservVerifyError( 417*9c5db199SXin Li 'Servod instance is not initialized') 418*9c5db199SXin Li logging.debug("Presented instance: %s", res.stdout.strip()) 419*9c5db199SXin Li 420*9c5db199SXin Li def _verify_servod_responsive(self, host): 421*9c5db199SXin Li # Verify if servod started and process is responsible. 422*9c5db199SXin Li cmd = self.SERVOD_RESPONSIVE % host.servo_port 423*9c5db199SXin Li res = host.run(cmd, ignore_status=True, timeout=120) 424*9c5db199SXin Li if res.exit_status != 0: 425*9c5db199SXin Li raise hosts.AutoservVerifyError( 426*9c5db199SXin Li 'Servod is not responsive for dut-control commands') 427*9c5db199SXin Li logging.info('Servod responsive: %s', res.stdout) 428*9c5db199SXin Li 429*9c5db199SXin Li @property 430*9c5db199SXin Li def description(self): 431*9c5db199SXin Li return 'Servod is running and responsive to dut-control run.' 432*9c5db199SXin Li 433*9c5db199SXin Li 434*9c5db199SXin Liclass _DiskSpaceVerifier(hosts.Verifier): 435*9c5db199SXin Li """ 436*9c5db199SXin Li Verifier to make sure there is enough disk space left on servohost. 437*9c5db199SXin Li """ 438*9c5db199SXin Li 439*9c5db199SXin Li @timeout_util.TimeoutDecorator(cros_constants.SHORT_VERIFY_TIMEOUT_SEC) 440*9c5db199SXin Li def verify(self, host): 441*9c5db199SXin Li # Check available space of stateful is greater than threshold, in Gib. 442*9c5db199SXin Li host.check_diskspace('/mnt/stateful_partition', 0.5) 443*9c5db199SXin Li 444*9c5db199SXin Li @property 445*9c5db199SXin Li def description(self): 446*9c5db199SXin Li return 'servohost has enough disk space.' 447*9c5db199SXin Li 448*9c5db199SXin Li def _is_applicable(self, host): 449*9c5db199SXin Li if host.is_containerized_servod(): 450*9c5db199SXin Li logging.info('Servod is running within a container.') 451*9c5db199SXin Li return False 452*9c5db199SXin Li return True 453*9c5db199SXin Li 454*9c5db199SXin Li 455*9c5db199SXin Liclass _ServodConnectionVerifier(hosts.Verifier): 456*9c5db199SXin Li """ 457*9c5db199SXin Li Verifier to check that we can connect to servod server. 458*9c5db199SXin Li 459*9c5db199SXin Li If this verifier failed, it most likely servod was crashed or in a 460*9c5db199SXin Li crashing loop. For servo_v4 it's usually caused by not able to detect 461*9c5db199SXin Li CCD or servo_micro. 462*9c5db199SXin Li """ 463*9c5db199SXin Li 464*9c5db199SXin Li @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 465*9c5db199SXin Li def verify(self, host): 466*9c5db199SXin Li host.initialize_servo() 467*9c5db199SXin Li 468*9c5db199SXin Li @property 469*9c5db199SXin Li def description(self): 470*9c5db199SXin Li return 'servod service is taking calls' 471*9c5db199SXin Li 472*9c5db199SXin Li 473*9c5db199SXin Liclass _ServodControlVerifier(hosts.Verifier): 474*9c5db199SXin Li """ 475*9c5db199SXin Li Verifier to check basic servo control functionality. 476*9c5db199SXin Li 477*9c5db199SXin Li This tests the connection to the target servod service with a simple 478*9c5db199SXin Li method call. As a side-effect, all servo signals are initialized to 479*9c5db199SXin Li default values. 480*9c5db199SXin Li 481*9c5db199SXin Li N.B. Initializing servo signals is necessary because the power 482*9c5db199SXin Li button and lid switch verifiers both test against expected initial 483*9c5db199SXin Li values. 484*9c5db199SXin Li """ 485*9c5db199SXin Li 486*9c5db199SXin Li @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 487*9c5db199SXin Li def verify(self, host): 488*9c5db199SXin Li try: 489*9c5db199SXin Li host.initialize_dut_for_servo() 490*9c5db199SXin Li except Exception as e: 491*9c5db199SXin Li six.reraise(hosts.AutoservNonCriticalVerifyError, 492*9c5db199SXin Li hosts.AutoservNonCriticalVerifyError(e), 493*9c5db199SXin Li sys.exc_info()[2]) 494*9c5db199SXin Li 495*9c5db199SXin Li @property 496*9c5db199SXin Li def description(self): 497*9c5db199SXin Li return 'Basic servod control is working' 498*9c5db199SXin Li 499*9c5db199SXin Li 500*9c5db199SXin Liclass _Cr50ConsoleVerifier(hosts.Verifier): 501*9c5db199SXin Li """Verifier to check if cr50 console is present and working. 502*9c5db199SXin Li 503*9c5db199SXin Li Validating based by running commands and expect they will not fail. 504*9c5db199SXin Li If any command fail then console is not working as expected. 505*9c5db199SXin Li """ 506*9c5db199SXin Li 507*9c5db199SXin Li COMMAND_TO_CHECK_CONSOLE = ( 508*9c5db199SXin Li 'gsc_ccd_level', 509*9c5db199SXin Li 'cr50_testlab', 510*9c5db199SXin Li 'cr50_ccd_state_flags', 511*9c5db199SXin Li ) 512*9c5db199SXin Li 513*9c5db199SXin Li @ignore_exception_for_non_cros_host 514*9c5db199SXin Li @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 515*9c5db199SXin Li def verify(self, host): 516*9c5db199SXin Li try: 517*9c5db199SXin Li for command in self.COMMAND_TO_CHECK_CONSOLE: 518*9c5db199SXin Li if host.get_servo().has_control(command): 519*9c5db199SXin Li # Response of command is not important. 520*9c5db199SXin Li host.get_servo().get(command) 521*9c5db199SXin Li except Exception as e: 522*9c5db199SXin Li six.reraise(hosts.AutoservNonCriticalVerifyError, 523*9c5db199SXin Li hosts.AutoservNonCriticalVerifyError(e), 524*9c5db199SXin Li sys.exc_info()[2]) 525*9c5db199SXin Li 526*9c5db199SXin Li def _is_applicable(self, host): 527*9c5db199SXin Li # Only when DUT is running through ccd or c2d2. 528*9c5db199SXin Li # TODO(coconutruben): replace with ccd API when available in servo.py 529*9c5db199SXin Li return host.get_servo() and host.get_servo().main_device_uses_gsc_drv() 530*9c5db199SXin Li 531*9c5db199SXin Li @property 532*9c5db199SXin Li def description(self): 533*9c5db199SXin Li return 'CR50 console is working' 534*9c5db199SXin Li 535*9c5db199SXin Li 536*9c5db199SXin Liclass _CCDTestlabVerifier(hosts.Verifier): 537*9c5db199SXin Li """ 538*9c5db199SXin Li Verifier to check that ccd testlab is enabled. 539*9c5db199SXin Li 540*9c5db199SXin Li All DUT connected by ccd has to supported cr50 with enabled testlab 541*9c5db199SXin Li to allow manipulation by servo. The flag testlab is sticky and will 542*9c5db199SXin Li stay enabled if was set up. The testlab can be enabled when ccd is 543*9c5db199SXin Li open. (go/ccd-setup) 544*9c5db199SXin Li """ 545*9c5db199SXin Li @ignore_exception_for_non_cros_host 546*9c5db199SXin Li @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 547*9c5db199SXin Li def verify(self, host): 548*9c5db199SXin Li if not host.get_servo().has_control('cr50_testlab'): 549*9c5db199SXin Li raise hosts.AutoservVerifyError( 550*9c5db199SXin Li 'gsc has to be supported when use servo with ' 551*9c5db199SXin Li 'ccd_*/type-c connection') 552*9c5db199SXin Li 553*9c5db199SXin Li status = host.get_servo().get('cr50_testlab') 554*9c5db199SXin Li # check by 'on' to fail when get unexpected value 555*9c5db199SXin Li if status == 'on': 556*9c5db199SXin Li # If servo uses cr50 to control the dut, open ccd so repair actions 557*9c5db199SXin Li # that reset the dut will work (cr50_reboot, cold_reset, warm_reset) 558*9c5db199SXin Li if host.get_servo().main_device_uses_gsc_drv(): 559*9c5db199SXin Li host.get_servo().set_nocheck('cr50_testlab', 'open') 560*9c5db199SXin Li # ccd testlab enabled 561*9c5db199SXin Li return 562*9c5db199SXin Li raise hosts.AutoservNonCriticalVerifyError( 563*9c5db199SXin Li 'The ccd testlab is disabled; DUT requires manual work ' 564*9c5db199SXin Li 'to enable it (go/ccd-setup).') 565*9c5db199SXin Li 566*9c5db199SXin Li def _is_applicable(self, host): 567*9c5db199SXin Li # Only when DUT is running through ccd. 568*9c5db199SXin Li # TODO(coconutruben): replace with ccd API when available in servo.py 569*9c5db199SXin Li return host.get_servo() and host.get_servo().main_device_is_ccd() 570*9c5db199SXin Li 571*9c5db199SXin Li @property 572*9c5db199SXin Li def description(self): 573*9c5db199SXin Li return 'ccd testlab enabled' 574*9c5db199SXin Li 575*9c5db199SXin Liclass _CCDPowerDeliveryVerifier(hosts.Verifier): 576*9c5db199SXin Li """Verifier to check and reset servo_v4_role for servos that support 577*9c5db199SXin Li power delivery feature(a.k.a power pass through). 578*9c5db199SXin Li 579*9c5db199SXin Li There are currently two position of servo_v4_role, src and snk: 580*9c5db199SXin Li src -- servo in power delivery mode and passes power to the DUT. 581*9c5db199SXin Li snk -- servo in normal mode and not passes power to DUT. 582*9c5db199SXin Li We want to ensure that servo_v4_role is set to src. 583*9c5db199SXin Li """ 584*9c5db199SXin Li @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 585*9c5db199SXin Li def verify(self, host): 586*9c5db199SXin Li if host.get_servo(): 587*9c5db199SXin Li self._printControl(host.get_servo(), 'ppdut5_mv') 588*9c5db199SXin Li self._printControl(host.get_servo(), 'ppchg5_mv') 589*9c5db199SXin Li if host.get_servo().get('servo_pd_role') == 'snk': 590*9c5db199SXin Li raise hosts.AutoservNonCriticalVerifyError( 591*9c5db199SXin Li 'Power delivery not in src role.') 592*9c5db199SXin Li 593*9c5db199SXin Li def _printControl(self, servo, control): 594*9c5db199SXin Li if servo.has_control(control): 595*9c5db199SXin Li logging.info("%s: %s", control, servo.get(control)) 596*9c5db199SXin Li 597*9c5db199SXin Li def _is_applicable(self, host): 598*9c5db199SXin Li return (host.is_in_lab() and 599*9c5db199SXin Li host.get_servo().supports_built_in_pd_control()) 600*9c5db199SXin Li 601*9c5db199SXin Li @property 602*9c5db199SXin Li def description(self): 603*9c5db199SXin Li return 'ensure applicable servo is in "src" mode for power delivery' 604*9c5db199SXin Li 605*9c5db199SXin Li 606*9c5db199SXin Liclass _BaseDUTConnectionVerifier(hosts.Verifier): 607*9c5db199SXin Li """Verifier to check connection between DUT and servo.""" 608*9c5db199SXin Li 609*9c5db199SXin Li # Bus voltage on ppdut5. Value can be: 610*9c5db199SXin Li # - less than 500 - DUT is likely not connected 611*9c5db199SXin Li # - between 500 and 4000 - unexpected value 612*9c5db199SXin Li # - more than 4000 - DUT is likely connected 613*9c5db199SXin Li MAX_PPDUT5_MV_WHEN_NOT_CONNECTED = 500 614*9c5db199SXin Li MIN_PPDUT5_MV_WHEN_CONNECTED = 4000 615*9c5db199SXin Li 616*9c5db199SXin Li def _is_usb_hub_connected(self, host): 617*9c5db199SXin Li """Checking bus voltage on ppdut5. 618*9c5db199SXin Li 619*9c5db199SXin Li Supported only on servo_v4 boards. 620*9c5db199SXin Li If voltage value is lower than 500 then device is not connected. 621*9c5db199SXin Li When value higher 4000 means the device is connected. If value 622*9c5db199SXin Li between 500 and 4000 is not expected and will be marked as connected 623*9c5db199SXin Li and collected information which DUT has this exception. 624*9c5db199SXin Li 625*9c5db199SXin Li @returns: bool 626*9c5db199SXin Li """ 627*9c5db199SXin Li logging.debug('Started check by ppdut5_mv:on') 628*9c5db199SXin Li try: 629*9c5db199SXin Li val = host.get_servo().get('ppdut5_mv') 630*9c5db199SXin Li logging.info('ppdut5_mv=%s', val) 631*9c5db199SXin Li if val < self.MAX_PPDUT5_MV_WHEN_NOT_CONNECTED: 632*9c5db199SXin Li # servo is not connected to the DUT 633*9c5db199SXin Li return False 634*9c5db199SXin Li if val < self.MIN_PPDUT5_MV_WHEN_CONNECTED: 635*9c5db199SXin Li # is unexpected value. 636*9c5db199SXin Li # collecting metrics to look case by case 637*9c5db199SXin Li # TODO(otabek) for analysis b:163845694 638*9c5db199SXin Li data = host._get_host_metrics_data() 639*9c5db199SXin Li metrics.Counter('chromeos/autotest/repair/ppdut5_mv_case' 640*9c5db199SXin Li ).increment(fields=data) 641*9c5db199SXin Li # else: 642*9c5db199SXin Li # servo is physical connected to the DUT 643*9c5db199SXin Li except Exception as e: 644*9c5db199SXin Li logging.debug('(Not critical) %s', e) 645*9c5db199SXin Li return True 646*9c5db199SXin Li 647*9c5db199SXin Li def _is_ribbon_cable_connected(self, host): 648*9c5db199SXin Li """Check if ribbon cable is connected to the DUT. 649*9c5db199SXin Li 650*9c5db199SXin Li The servo_micro/flex - can be checked by `cold_reset` signal. 651*9c5db199SXin Li When `cold_reset` is `on` it commonly indicates that the DUT 652*9c5db199SXin Li is disconnected. To avoid mistake of real signal we try 653*9c5db199SXin Li switch it off and if is cannot then servo is not connected. 654*9c5db199SXin Li 655*9c5db199SXin Li @returns: bool 656*9c5db199SXin Li """ 657*9c5db199SXin Li logging.debug('Started check by cold_reset:on') 658*9c5db199SXin Li try: 659*9c5db199SXin Li val = host.get_servo().get('cold_reset') 660*9c5db199SXin Li logging.info('cold_reset=%s', val) 661*9c5db199SXin Li if val == 'on': 662*9c5db199SXin Li # If cold_reset has is on can be right signal 663*9c5db199SXin Li # or caused by missing connection between servo_micro and DUT. 664*9c5db199SXin Li # if we can switch it to the off then it was signal. 665*9c5db199SXin Li host.get_servo().set('cold_reset', 'off') 666*9c5db199SXin Li except error.TestFail: 667*9c5db199SXin Li logging.debug('Ribbon cable is not connected to the DUT.') 668*9c5db199SXin Li return False 669*9c5db199SXin Li except Exception as e: 670*9c5db199SXin Li logging.debug('(Not critical) %s', e) 671*9c5db199SXin Li return True 672*9c5db199SXin Li 673*9c5db199SXin Li def _is_dut_power_on(self, host): 674*9c5db199SXin Li # DUT is running in normal state. 675*9c5db199SXin Li # if EC not supported by board then we expect error 676*9c5db199SXin Li try: 677*9c5db199SXin Li return host.get_servo().get('ec_system_powerstate') == 'S0' 678*9c5db199SXin Li except Exception as e: 679*9c5db199SXin Li logging.debug('(Not critical) %s', e) 680*9c5db199SXin Li return False 681*9c5db199SXin Li 682*9c5db199SXin Li def _is_servo_v4_type_a(self, host): 683*9c5db199SXin Li return host.is_labstation() and host.get_servo().is_servo_v4_type_a() 684*9c5db199SXin Li 685*9c5db199SXin Li def _is_servo_v4_type_c(self, host): 686*9c5db199SXin Li return host.is_labstation() and host.get_servo().is_servo_v4_type_c() 687*9c5db199SXin Li 688*9c5db199SXin Li def _is_servo_v3(self, host): 689*9c5db199SXin Li return not host.is_labstation() 690*9c5db199SXin Li 691*9c5db199SXin Li 692*9c5db199SXin Liclass _DUTConnectionVerifier(_BaseDUTConnectionVerifier): 693*9c5db199SXin Li """Verifier to check connection Servo to the DUT. 694*9c5db199SXin Li 695*9c5db199SXin Li Servo_v4 type-a connected to the DUT by: 696*9c5db199SXin Li 1) servo_micro - checked by `cold_reset`. 697*9c5db199SXin Li Servo_v4 type-c connected to the DUT by: 698*9c5db199SXin Li 1) ccd - checked by ppdut5_mv. 699*9c5db199SXin Li Servo_v3 connected to the DUT by: 700*9c5db199SXin Li 1) legacy servo header - can be checked by `cold_reset`. 701*9c5db199SXin Li """ 702*9c5db199SXin Li 703*9c5db199SXin Li @ignore_exception_for_non_cros_host 704*9c5db199SXin Li @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 705*9c5db199SXin Li def verify(self, host): 706*9c5db199SXin Li if self._is_servo_v4_type_a(host): 707*9c5db199SXin Li if not self._is_ribbon_cable_connected(host): 708*9c5db199SXin Li raise hosts.AutoservVerifyError( 709*9c5db199SXin Li 'Servo_micro is likely not connected to the DUT.') 710*9c5db199SXin Li elif self._is_servo_v4_type_c(host): 711*9c5db199SXin Li if (host.get_servo().supports_built_in_pd_control() 712*9c5db199SXin Li and not self._is_usb_hub_connected(host)): 713*9c5db199SXin Li raise hosts.AutoservVerifyError( 714*9c5db199SXin Li 'Servo_v4 is likely not connected to the DUT.') 715*9c5db199SXin Li elif self._is_servo_v3(host): 716*9c5db199SXin Li if not self._is_ribbon_cable_connected(host): 717*9c5db199SXin Li raise hosts.AutoservVerifyError( 718*9c5db199SXin Li 'Servo_v3 is likely not connected to the DUT.') 719*9c5db199SXin Li 720*9c5db199SXin Li @property 721*9c5db199SXin Li def description(self): 722*9c5db199SXin Li return 'Ensure the Servo connected to the DUT.' 723*9c5db199SXin Li 724*9c5db199SXin Li 725*9c5db199SXin Liclass _ServoHubConnectionVerifier(_BaseDUTConnectionVerifier): 726*9c5db199SXin Li """Verifier to check connection ServoHub to DUT. 727*9c5db199SXin Li 728*9c5db199SXin Li Servo_v4 type-a connected to the DUT by: 729*9c5db199SXin Li 1) USB hub - checked by ppdut5_mv. 730*9c5db199SXin Li """ 731*9c5db199SXin Li 732*9c5db199SXin Li @ignore_exception_for_non_cros_host 733*9c5db199SXin Li @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 734*9c5db199SXin Li def verify(self, host): 735*9c5db199SXin Li if self._is_servo_v4_type_a(host): 736*9c5db199SXin Li if (self._is_dut_power_on(host) 737*9c5db199SXin Li and not self._is_usb_hub_connected(host)): 738*9c5db199SXin Li raise hosts.AutoservVerifyError( 739*9c5db199SXin Li 'Servo USB hub is likely not connected to the DUT.') 740*9c5db199SXin Li 741*9c5db199SXin Li def _is_applicable(self, host): 742*9c5db199SXin Li if host.is_ec_supported(): 743*9c5db199SXin Li return True 744*9c5db199SXin Li logging.info('Host does not support EC.') 745*9c5db199SXin Li return False 746*9c5db199SXin Li 747*9c5db199SXin Li @property 748*9c5db199SXin Li def description(self): 749*9c5db199SXin Li return 'Ensure the Servo HUB connected to the DUT.' 750*9c5db199SXin Li 751*9c5db199SXin Li 752*9c5db199SXin Liclass _BaseCr50SBUVerifier(_BaseDUTConnectionVerifier): 753*9c5db199SXin Li """Check servod issue related to SBU voltage.""" 754*9c5db199SXin Li 755*9c5db199SXin Li # Min SBU voltage to detect usb-device 756*9c5db199SXin Li SBU_THRESHOLD = 2500.0 757*9c5db199SXin Li # How many times collect SBU voltage to calc AVG value. 758*9c5db199SXin Li _TOTAL_CHECK_SBU_VOLTAGE = 10 759*9c5db199SXin Li 760*9c5db199SXin Li def _is_applicable(self, host): 761*9c5db199SXin Li if host.is_localhost(): 762*9c5db199SXin Li logging.info('Target servo is not in a lab,' 763*9c5db199SXin Li ' action is not applicable.') 764*9c5db199SXin Li return False 765*9c5db199SXin Li if not self._is_servo_v4_type_c(host): 766*9c5db199SXin Li logging.info('Check support only servo-v4 (type-c),' 767*9c5db199SXin Li ' action is not applicable.') 768*9c5db199SXin Li return False 769*9c5db199SXin Li return True 770*9c5db199SXin Li 771*9c5db199SXin Li def _is_sbu_voltage_issue(self, host): 772*9c5db199SXin Li """Check if servo does not detected by SBU voltage issue.""" 773*9c5db199SXin Li command = 'dut_sbu_voltage_float_fault' 774*9c5db199SXin Li if host.get_servo().has_control(command): 775*9c5db199SXin Li if host.get_servo().get(command) == 'on': 776*9c5db199SXin Li return True 777*9c5db199SXin Li return False 778*9c5db199SXin Li 779*9c5db199SXin Li def _get_max_sbu_value(self, host): 780*9c5db199SXin Li """Get average voltage on SBU lines.""" 781*9c5db199SXin Li servo = host.get_servo() 782*9c5db199SXin Li if not servo.has_control('servo_dut_sbu1_mv'): 783*9c5db199SXin Li return -1 784*9c5db199SXin Li s1 = 0 785*9c5db199SXin Li s2 = 0 786*9c5db199SXin Li for i in range(self._TOTAL_CHECK_SBU_VOLTAGE): 787*9c5db199SXin Li try: 788*9c5db199SXin Li sbu1 = int(servo.get('servo_dut_sbu1_mv')) 789*9c5db199SXin Li sbu2 = int(servo.get('servo_dut_sbu2_mv')) 790*9c5db199SXin Li logging.debug('Attempt:%2d, sbu1 %4d sbu2 %4d', i, sbu1, sbu2) 791*9c5db199SXin Li s1 += sbu1 792*9c5db199SXin Li s2 += sbu2 793*9c5db199SXin Li except error.TestFail as e: 794*9c5db199SXin Li # This is a nice to have but if reading this fails, it 795*9c5db199SXin Li # shouldn't interfere with the test. 796*9c5db199SXin Li logging.exception(e) 797*9c5db199SXin Li logging.debug('Total: sbu1 %4d sbu2 %4d', s1, s2) 798*9c5db199SXin Li # Use float to get values with changes 799*9c5db199SXin Li s1 = s1 / float(self._TOTAL_CHECK_SBU_VOLTAGE) 800*9c5db199SXin Li s2 = s2 / float(self._TOTAL_CHECK_SBU_VOLTAGE) 801*9c5db199SXin Li logging.debug('Avg: sbu1 %7.2f sbu2 %7.2f', s1, s2) 802*9c5db199SXin Li max_sbu = max(s1, s2) 803*9c5db199SXin Li logging.info('Max sbu: %7.2f', max_sbu) 804*9c5db199SXin Li return max_sbu 805*9c5db199SXin Li 806*9c5db199SXin Li 807*9c5db199SXin Liclass _Cr50OffVerifier(_BaseCr50SBUVerifier): 808*9c5db199SXin Li """Check if CR50 is in deep sleep and fail to detected. 809*9c5db199SXin Li 810*9c5db199SXin Li If SBU voltage is higher threshold but still cannot be detected 811*9c5db199SXin Li as usb device then probably CR50 is in deep sleep. 812*9c5db199SXin Li Threshold is 2500 mV on any SBU lines. 813*9c5db199SXin Li """ 814*9c5db199SXin Li 815*9c5db199SXin Li @ignore_exception_for_non_cros_host 816*9c5db199SXin Li @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 817*9c5db199SXin Li def verify(self, host): 818*9c5db199SXin Li if self._is_sbu_voltage_issue(host): 819*9c5db199SXin Li if self._get_max_sbu_value(host) > self.SBU_THRESHOLD: 820*9c5db199SXin Li raise hosts.AutoservVerifyError( 821*9c5db199SXin Li 'CR50 voltage detected but usb device not enumerated') 822*9c5db199SXin Li 823*9c5db199SXin Li @property 824*9c5db199SXin Li def description(self): 825*9c5db199SXin Li return 'CR50 voltage detected but not enumerated.' 826*9c5db199SXin Li 827*9c5db199SXin Li 828*9c5db199SXin Liclass _Cr50LowSBUVerifier(_BaseCr50SBUVerifier): 829*9c5db199SXin Li """Check if servod fail to detect CR50 due low voltage. 830*9c5db199SXin Li 831*9c5db199SXin Li CR50 cannot be enumerated as SBU voltage line lower then 832*9c5db199SXin Li threshold. 833*9c5db199SXin Li Threshold is 2500 mV on any SBU lines. 834*9c5db199SXin Li """ 835*9c5db199SXin Li 836*9c5db199SXin Li @ignore_exception_for_non_cros_host 837*9c5db199SXin Li @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 838*9c5db199SXin Li def verify(self, host): 839*9c5db199SXin Li if self._is_sbu_voltage_issue(host): 840*9c5db199SXin Li v = self._get_max_sbu_value(host) 841*9c5db199SXin Li if v > 1 and v <= self.SBU_THRESHOLD: 842*9c5db199SXin Li raise hosts.AutoservVerifyError( 843*9c5db199SXin Li 'Cr50 is not detected due to SBU voltages' 844*9c5db199SXin Li ' being below %dmV' % self.SBU_THRESHOLD) 845*9c5db199SXin Li 846*9c5db199SXin Li @property 847*9c5db199SXin Li def description(self): 848*9c5db199SXin Li return 'Cr50 not detected as both SBU voltages are below threshold.' 849*9c5db199SXin Li 850*9c5db199SXin Li 851*9c5db199SXin Liclass _TopologyVerifier(hosts.Verifier): 852*9c5db199SXin Li """Verifier that all servo component is presented.""" 853*9c5db199SXin Li 854*9c5db199SXin Li @ignore_exception_for_non_cros_host 855*9c5db199SXin Li @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 856*9c5db199SXin Li def verify(self, host): 857*9c5db199SXin Li topology = host.get_topology() 858*9c5db199SXin Li topology.read(host.get_dut_host_info()) 859*9c5db199SXin Li try: 860*9c5db199SXin Li # Linux takes 1 second to detect and enumerate USB device since 861*9c5db199SXin Li # 2010 year. We take 10 seconds to be sure as old standard was 862*9c5db199SXin Li # 5 seconds. 863*9c5db199SXin Li time.sleep(10) 864*9c5db199SXin Li topology.validate(raise_error=True, 865*9c5db199SXin Li dual_set=host.is_dual_setup(), 866*9c5db199SXin Li compare=True) 867*9c5db199SXin Li except servo_topology.ServoTopologyError as e: 868*9c5db199SXin Li six.reraise(hosts.AutoservVerifyError, 869*9c5db199SXin Li hosts.AutoservVerifyError(e), 870*9c5db199SXin Li sys.exc_info()[2]) 871*9c5db199SXin Li 872*9c5db199SXin Li def _is_applicable(self, host): 873*9c5db199SXin Li if host.is_localhost(): 874*9c5db199SXin Li logging.info('Target servo is not in a lab,' 875*9c5db199SXin Li ' action is not applicable.') 876*9c5db199SXin Li return False 877*9c5db199SXin Li if not host.is_servo_topology_supported(): 878*9c5db199SXin Li logging.info('Target servo-topology is not supported,' 879*9c5db199SXin Li ' action is not applicable.') 880*9c5db199SXin Li return False 881*9c5db199SXin Li return True 882*9c5db199SXin Li 883*9c5db199SXin Li @property 884*9c5db199SXin Li def description(self): 885*9c5db199SXin Li return 'Ensure all Servo component present.' 886*9c5db199SXin Li 887*9c5db199SXin Li 888*9c5db199SXin Liclass _PowerButtonVerifier(hosts.Verifier): 889*9c5db199SXin Li """ 890*9c5db199SXin Li Verifier to check the `pwr_button` signal. 891*9c5db199SXin Li 892*9c5db199SXin Li Tests that the `pwr_button` signal shows the power button has been 893*9c5db199SXin Li released. When `pwr_button` is stuck at `press`, it commonly 894*9c5db199SXin Li indicates that the ribbon cable is disconnected. 895*9c5db199SXin Li """ 896*9c5db199SXin Li # TODO (crbug.com/646593) - Remove list below once servo has been updated 897*9c5db199SXin Li # with a fake pwr_button signal. 898*9c5db199SXin Li _BOARDS_WO_PWR_BUTTON = ['arkham', 'gale', 'mistral', 'storm', 'whirlwind'] 899*9c5db199SXin Li 900*9c5db199SXin Li @ignore_exception_for_non_cros_host 901*9c5db199SXin Li @timeout_util.TimeoutDecorator(cros_constants.SHORT_VERIFY_TIMEOUT_SEC) 902*9c5db199SXin Li def verify(self, host): 903*9c5db199SXin Li if host.servo_board in self._BOARDS_WO_PWR_BUTTON: 904*9c5db199SXin Li return 905*9c5db199SXin Li try: 906*9c5db199SXin Li button = host.get_servo().get('pwr_button') 907*9c5db199SXin Li except Exception as e: 908*9c5db199SXin Li six.reraise(hosts.AutoservNonCriticalVerifyError, 909*9c5db199SXin Li hosts.AutoservNonCriticalVerifyError(e), 910*9c5db199SXin Li sys.exc_info()[2]) 911*9c5db199SXin Li 912*9c5db199SXin Li if button != 'release': 913*9c5db199SXin Li raise hosts.AutoservNonCriticalVerifyError( 914*9c5db199SXin Li 'Check ribbon cable: \'pwr_button\' is stuck') 915*9c5db199SXin Li 916*9c5db199SXin Li def _is_applicable(self, host): 917*9c5db199SXin Li return (host.get_servo() and host.get_servo().main_device_is_flex()) 918*9c5db199SXin Li 919*9c5db199SXin Li @property 920*9c5db199SXin Li def description(self): 921*9c5db199SXin Li return 'pwr_button control is normal' 922*9c5db199SXin Li 923*9c5db199SXin Li 924*9c5db199SXin Liclass _BatteryVerifier(hosts.Verifier): 925*9c5db199SXin Li """Collect battery info for analysis.""" 926*9c5db199SXin Li 927*9c5db199SXin Li @ignore_exception_for_non_cros_host 928*9c5db199SXin Li @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 929*9c5db199SXin Li def verify(self, host): 930*9c5db199SXin Li try: 931*9c5db199SXin Li servo = host.get_servo() 932*9c5db199SXin Li charging = False 933*9c5db199SXin Li if servo.has_control('battery_is_charging'): 934*9c5db199SXin Li charging = servo.get('battery_is_charging') 935*9c5db199SXin Li level = -1 936*9c5db199SXin Li if servo.has_control('battery_charge_percent'): 937*9c5db199SXin Li level = servo.get('battery_charge_percent') 938*9c5db199SXin Li design_mah = servo.get('battery_full_design_mah') 939*9c5db199SXin Li charge_mah = servo.get('battery_full_charge_mah') 940*9c5db199SXin Li logging.info('Charging: %s', charging) 941*9c5db199SXin Li logging.info('Percentage: %s', level) 942*9c5db199SXin Li logging.info('Full charge max: %s', charge_mah) 943*9c5db199SXin Li logging.info('Full design max: %s', design_mah) 944*9c5db199SXin Li # based on analysis of ratio we can find out what is 945*9c5db199SXin Li # the level when we can say that battery is dead 946*9c5db199SXin Li ratio = int(math.floor(charge_mah / design_mah * 100.0)) 947*9c5db199SXin Li logging.info('Ratio: %s', ratio) 948*9c5db199SXin Li data = { 949*9c5db199SXin Li 'board': host.servo_board or 'unknown', 950*9c5db199SXin Li 'model': host.servo_model or 'unknown', 951*9c5db199SXin Li 'ratio': ratio 952*9c5db199SXin Li } 953*9c5db199SXin Li metrics.Counter('chromeos/autotest/battery/ratio').increment( 954*9c5db199SXin Li fields=data) 955*9c5db199SXin Li except Exception as e: 956*9c5db199SXin Li # Keeping it with info level because we do not expect it. 957*9c5db199SXin Li logging.info('(Not critical) %s', e) 958*9c5db199SXin Li 959*9c5db199SXin Li def _is_applicable(self, host): 960*9c5db199SXin Li if not host.is_ec_supported(): 961*9c5db199SXin Li logging.info('The board not support EC') 962*9c5db199SXin Li return False 963*9c5db199SXin Li dut_info = host.get_dut_host_info() 964*9c5db199SXin Li if dut_info: 965*9c5db199SXin Li host_info = host.get_dut_host_info() 966*9c5db199SXin Li if host_info.get_label_value('power') != 'battery': 967*9c5db199SXin Li logging.info('The board does not have battery') 968*9c5db199SXin Li return False 969*9c5db199SXin Li servo = host.get_servo() 970*9c5db199SXin Li if (not servo.has_control('battery_full_design_mah') 971*9c5db199SXin Li or not servo.has_control('battery_full_charge_mah')): 972*9c5db199SXin Li logging.info('The board is not supported battery controls...') 973*9c5db199SXin Li return False 974*9c5db199SXin Li return True 975*9c5db199SXin Li 976*9c5db199SXin Li @property 977*9c5db199SXin Li def description(self): 978*9c5db199SXin Li return 'Logs battery levels' 979*9c5db199SXin Li 980*9c5db199SXin Li 981*9c5db199SXin Liclass _LidVerifier(hosts.Verifier): 982*9c5db199SXin Li """ 983*9c5db199SXin Li Verifier to check the `lid_open` signal. 984*9c5db199SXin Li """ 985*9c5db199SXin Li 986*9c5db199SXin Li @ignore_exception_for_non_cros_host 987*9c5db199SXin Li @timeout_util.TimeoutDecorator(cros_constants.SHORT_VERIFY_TIMEOUT_SEC) 988*9c5db199SXin Li def verify(self, host): 989*9c5db199SXin Li try: 990*9c5db199SXin Li lid_open = host.get_servo().get('lid_open') 991*9c5db199SXin Li except Exception as e: 992*9c5db199SXin Li six.reraise(hosts.AutoservNonCriticalVerifyError, 993*9c5db199SXin Li hosts.AutoservNonCriticalVerifyError(e), 994*9c5db199SXin Li sys.exc_info()[2]) 995*9c5db199SXin Li 996*9c5db199SXin Li if lid_open != 'yes' and lid_open != 'not_applicable': 997*9c5db199SXin Li raise hosts.AutoservNonCriticalVerifyError( 998*9c5db199SXin Li 'Check lid switch: lid_open is %s' % lid_open) 999*9c5db199SXin Li 1000*9c5db199SXin Li @property 1001*9c5db199SXin Li def description(self): 1002*9c5db199SXin Li return 'lid_open control is normal' 1003*9c5db199SXin Li 1004*9c5db199SXin Li 1005*9c5db199SXin Liclass ECConsoleVerifier(hosts.Verifier): 1006*9c5db199SXin Li """ 1007*9c5db199SXin Li Verifier response from the EC console. 1008*9c5db199SXin Li """ 1009*9c5db199SXin Li 1010*9c5db199SXin Li COMMAND_TO_CHECK_CONSOLE = ( 1011*9c5db199SXin Li 'ec_system_powerstate', 1012*9c5db199SXin Li 'ec_board', 1013*9c5db199SXin Li ) 1014*9c5db199SXin Li 1015*9c5db199SXin Li @ignore_exception_for_non_cros_host 1016*9c5db199SXin Li @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 1017*9c5db199SXin Li def verify(self, host): 1018*9c5db199SXin Li if not host.is_ec_supported(): 1019*9c5db199SXin Li logging.info('The board does not support EC') 1020*9c5db199SXin Li return 1021*9c5db199SXin Li 1022*9c5db199SXin Li for command in self.COMMAND_TO_CHECK_CONSOLE: 1023*9c5db199SXin Li if host.get_servo().has_control(command): 1024*9c5db199SXin Li try: 1025*9c5db199SXin Li # Response of command is not important. 1026*9c5db199SXin Li r = host.get_servo().get(command) 1027*9c5db199SXin Li logging.debug('Result %s:%s', command, r) 1028*9c5db199SXin Li # Exiting as we confirmed that console is working. 1029*9c5db199SXin Li return 1030*9c5db199SXin Li except Exception as e: 1031*9c5db199SXin Li logging.error('Fail to read %s control. Error: %s', 1032*9c5db199SXin Li command, e) 1033*9c5db199SXin Li # If we reached this point then no command succeeded. 1034*9c5db199SXin Li raise hosts.AutoservNonCriticalVerifyError( 1035*9c5db199SXin Li 'EC console is not responding; ' 1036*9c5db199SXin Li 'may be caused of broken EC firmware') 1037*9c5db199SXin Li 1038*9c5db199SXin Li @property 1039*9c5db199SXin Li def description(self): 1040*9c5db199SXin Li return 'Check EC console' 1041*9c5db199SXin Li 1042*9c5db199SXin Li 1043*9c5db199SXin Liclass ServodDutControllerMissingVerifier(hosts.Verifier): 1044*9c5db199SXin Li """Verifier to check whether the servod dut controller is missing or not. 1045*9c5db199SXin Li 1046*9c5db199SXin Li When servod is initializing, it checks if DUT controller is 1047*9c5db199SXin Li missing. If yes,then it sets 'dut_controller_missing_fault' to 1048*9c5db199SXin Li 'on', otherwise, to 'off'. Missing controller means servo 1049*9c5db199SXin Li component connected to the DUT is missing, or is not responsive. 1050*9c5db199SXin Li """ 1051*9c5db199SXin Li 1052*9c5db199SXin Li @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 1053*9c5db199SXin Li def verify(self, host): 1054*9c5db199SXin Li logging.debug('ServodDutControllerMissingVerifier: Starting verifier.') 1055*9c5db199SXin Li if host.get_servo().get('dut_controller_missing_fault') == 'on': 1056*9c5db199SXin Li logging.debug('ServodDutControllerMissingVerifier: DUT Controller missing fault is on.') 1057*9c5db199SXin Li raise hosts.AutoservVerifyError('Servod is missing dut controller') 1058*9c5db199SXin Li else: 1059*9c5db199SXin Li logging.debug('ServodDutControllerMissingVerifier: DUT Controller missing fault is not on.') 1060*9c5db199SXin Li 1061*9c5db199SXin Li def _is_applicable(self, host): 1062*9c5db199SXin Li if host.is_containerized_servod(): 1063*9c5db199SXin Li logging.debug('ServodDutControllerMissingVerifier: Detected containerized servod.') 1064*9c5db199SXin Li logging.info('Servod is running within a container') 1065*9c5db199SXin Li return True 1066*9c5db199SXin Li if not host.is_labstation(): 1067*9c5db199SXin Li logging.debug('ServodDutControllerMissingVerifier: Detected non-labstation.') 1068*9c5db199SXin Li logging.info('Not supported for servo_v3.') 1069*9c5db199SXin Li return False 1070*9c5db199SXin Li return host.is_in_lab() 1071*9c5db199SXin Li 1072*9c5db199SXin Li @property 1073*9c5db199SXin Li def description(self): 1074*9c5db199SXin Li return 'ensure servod does not have missing dut controller' 1075*9c5db199SXin Li 1076*9c5db199SXin Li 1077*9c5db199SXin Liclass _ConnectionVerifier(repair_utils.SshVerifier): 1078*9c5db199SXin Li """ 1079*9c5db199SXin Li Ensure the servo host container is up. 1080*9c5db199SXin Li """ 1081*9c5db199SXin Li 1082*9c5db199SXin Li def verify(self, host): 1083*9c5db199SXin Li if host.is_containerized_servod(): 1084*9c5db199SXin Li # We need start servod container first before check it-is present 1085*9c5db199SXin Li host.start_containerized_servod() 1086*9c5db199SXin Li return super(_ConnectionVerifier, self).verify(host) 1087*9c5db199SXin Li 1088*9c5db199SXin Li @property 1089*9c5db199SXin Li def description(self): 1090*9c5db199SXin Li return 'Check the connection to the machine or container running servod.' 1091*9c5db199SXin Li 1092*9c5db199SXin Li 1093*9c5db199SXin Liclass _RestartServod(hosts.RepairAction): 1094*9c5db199SXin Li """Restart `servod` with the proper BOARD setting.""" 1095*9c5db199SXin Li 1096*9c5db199SXin Li @timeout_util.TimeoutDecorator(cros_constants.REPAIR_TIMEOUT_SEC) 1097*9c5db199SXin Li def repair(self, host): 1098*9c5db199SXin Li if host.is_containerized_servod(): 1099*9c5db199SXin Li logging.debug('Restarting servod container') 1100*9c5db199SXin Li elif not host.is_cros_host(): 1101*9c5db199SXin Li raise hosts.AutoservRepairError( 1102*9c5db199SXin Li 'Can\'t restart servod: not running ' 1103*9c5db199SXin Li 'embedded ChromeOS.', 1104*9c5db199SXin Li 'servo_not_applicable_to_non_cros_host') 1105*9c5db199SXin Li host.restart_servod() 1106*9c5db199SXin Li 1107*9c5db199SXin Li @property 1108*9c5db199SXin Li def description(self): 1109*9c5db199SXin Li return 'Start servod with the proper config settings.' 1110*9c5db199SXin Li 1111*9c5db199SXin Li 1112*9c5db199SXin Liclass _ServoRebootRepair(repair_utils.RebootRepair): 1113*9c5db199SXin Li """Try repair servo by reboot servohost. 1114*9c5db199SXin Li 1115*9c5db199SXin Li This is the same as the standard `RebootRepair`, for servo_v3 it will 1116*9c5db199SXin Li reboot the beaglebone board immediately while for labstation it will 1117*9c5db199SXin Li request a reboot by touch a flag file on its labstation, then 1118*9c5db199SXin Li labstation reboot will be handled by labstation AdminRepair task as 1119*9c5db199SXin Li labstation host multiple servos and need do an synchronized reboot. 1120*9c5db199SXin Li """ 1121*9c5db199SXin Li 1122*9c5db199SXin Li @timeout_util.TimeoutDecorator(cros_constants.REPAIR_TIMEOUT_SEC) 1123*9c5db199SXin Li def repair(self, host): 1124*9c5db199SXin Li super(_ServoRebootRepair, self).repair(host) 1125*9c5db199SXin Li # restart servod for v3 after reboot. 1126*9c5db199SXin Li host.restart_servod() 1127*9c5db199SXin Li 1128*9c5db199SXin Li def _is_applicable(self, host): 1129*9c5db199SXin Li if host.is_localhost() or not host.is_cros_host(): 1130*9c5db199SXin Li logging.info('Target servo is not in a lab, the reboot repair' 1131*9c5db199SXin Li ' action is not applicable.') 1132*9c5db199SXin Li return False 1133*9c5db199SXin Li 1134*9c5db199SXin Li if host.is_labstation(): 1135*9c5db199SXin Li host.request_reboot() 1136*9c5db199SXin Li logging.info('Reboot labstation requested, it will be handled' 1137*9c5db199SXin Li ' by labstation AdminRepair task.') 1138*9c5db199SXin Li return False 1139*9c5db199SXin Li return True 1140*9c5db199SXin Li 1141*9c5db199SXin Li @property 1142*9c5db199SXin Li def description(self): 1143*9c5db199SXin Li return 'Reboot the servo host.' 1144*9c5db199SXin Li 1145*9c5db199SXin Li 1146*9c5db199SXin Liclass _ToggleCCLineRepair(hosts.RepairAction): 1147*9c5db199SXin Li """Try repair servod by toggle cc. 1148*9c5db199SXin Li 1149*9c5db199SXin Li When cr50 is not enumerated we can try to recover it by toggle cc line. 1150*9c5db199SXin Li """ 1151*9c5db199SXin Li # Timeout for shut down configuration channel. 1152*9c5db199SXin Li CC_OFF_TIMEOUT = 10 1153*9c5db199SXin Li # Timeout for initialize configuration channel. 1154*9c5db199SXin Li CC_ON_TIMEOUT = 30 1155*9c5db199SXin Li 1156*9c5db199SXin Li @timeout_util.TimeoutDecorator(cros_constants.REPAIR_TIMEOUT_SEC) 1157*9c5db199SXin Li def repair(self, host): 1158*9c5db199SXin Li logging.info('Turn off configuration channel and wait 10 seconds.') 1159*9c5db199SXin Li servo_uart_cmd = 'servo_v4_uart_cmd' 1160*9c5db199SXin Li if not host.get_servo().has_control(servo_uart_cmd): 1161*9c5db199SXin Li servo_uart_cmd = 'servo_v4p1_uart_cmd' 1162*9c5db199SXin Li host.get_servo().set_nocheck(servo_uart_cmd, 'cc off') 1163*9c5db199SXin Li # wait till command will be effected 1164*9c5db199SXin Li time.sleep(self.CC_OFF_TIMEOUT) 1165*9c5db199SXin Li 1166*9c5db199SXin Li logging.info('Turn on configuration channel and wait 30 seconds.') 1167*9c5db199SXin Li # alternative option to turn line on is by `cc srcdts` 1168*9c5db199SXin Li host.get_servo().set_nocheck('servo_pd_role', 'src') 1169*9c5db199SXin Li host.get_servo().set_nocheck('servo_dts_mode', 'on') 1170*9c5db199SXin Li # wait till command will be effected 1171*9c5db199SXin Li time.sleep(self.CC_ON_TIMEOUT) 1172*9c5db199SXin Li host.restart_servod() 1173*9c5db199SXin Li 1174*9c5db199SXin Li def _is_applicable(self, host): 1175*9c5db199SXin Li if host.is_localhost(): 1176*9c5db199SXin Li logging.debug('Not supported for localhost.') 1177*9c5db199SXin Li return False 1178*9c5db199SXin Li if not host.servo_serial: 1179*9c5db199SXin Li logging.debug('Servod does not have serial.') 1180*9c5db199SXin Li return False 1181*9c5db199SXin Li if not host.servo_recovery: 1182*9c5db199SXin Li logging.debug('Servod is not running in recovery mode.') 1183*9c5db199SXin Li return False 1184*9c5db199SXin Li if not (host.is_labstation() or host.is_containerized_servod()): 1185*9c5db199SXin Li logging.debug('Not supported for servo_v3.') 1186*9c5db199SXin Li return False 1187*9c5db199SXin Li if not host.get_servo(): 1188*9c5db199SXin Li logging.debug('Servo is not initialized.') 1189*9c5db199SXin Li return False 1190*9c5db199SXin Li return self._is_type_c(host) 1191*9c5db199SXin Li 1192*9c5db199SXin Li def _is_type_c(self, host): 1193*9c5db199SXin Li if host.get_dut_host_info(): 1194*9c5db199SXin Li servo_type = host.get_dut_host_info().get_label_value( 1195*9c5db199SXin Li servo_constants.SERVO_TYPE_LABEL_PREFIX) 1196*9c5db199SXin Li return 'ccd' in servo_type 1197*9c5db199SXin Li return False 1198*9c5db199SXin Li 1199*9c5db199SXin Li @property 1200*9c5db199SXin Li def description(self): 1201*9c5db199SXin Li return 'Toggle cc lines' 1202*9c5db199SXin Li 1203*9c5db199SXin Li 1204*9c5db199SXin Liclass _FakedisconnectRepair(hosts.RepairAction): 1205*9c5db199SXin Li """Try repair servod by mimic reconnection of servo. 1206*9c5db199SXin Li 1207*9c5db199SXin Li When cr50 is not enumerated as we can try to recover it by reconnect to DUT. 1208*9c5db199SXin Li """ 1209*9c5db199SXin Li # Delay to disconnect. 1210*9c5db199SXin Li DISC_DELAY_MS = 100 1211*9c5db199SXin Li # Timeout to wait to restore the connection. 1212*9c5db199SXin Li DISC_TIMEOUT_MS = 2000 1213*9c5db199SXin Li # Timeout to wait to execute the command and apply effect. 1214*9c5db199SXin Li EXEC_TIMEOUT = (DISC_DELAY_MS + DISC_TIMEOUT_MS) / 1000 + 2 1215*9c5db199SXin Li 1216*9c5db199SXin Li @timeout_util.TimeoutDecorator(cros_constants.REPAIR_TIMEOUT_SEC) 1217*9c5db199SXin Li def repair(self, host): 1218*9c5db199SXin Li disc_cmd = ('fakedisconnect %d %d' % 1219*9c5db199SXin Li (self.DISC_DELAY_MS, self.DISC_TIMEOUT_MS)) 1220*9c5db199SXin Li # cannot use 'set' as control is not returned executed commands 1221*9c5db199SXin Li servo_uart_cmd = 'servo_v4_uart_cmd' 1222*9c5db199SXin Li if not host.get_servo().has_control(servo_uart_cmd): 1223*9c5db199SXin Li servo_uart_cmd = 'servo_v4p1_uart_cmd' 1224*9c5db199SXin Li host.get_servo().set_nocheck(servo_uart_cmd, disc_cmd) 1225*9c5db199SXin Li logging.debug('Waiting %ss for affect of action', self.EXEC_TIMEOUT) 1226*9c5db199SXin Li time.sleep(self.EXEC_TIMEOUT) 1227*9c5db199SXin Li host.restart_servod() 1228*9c5db199SXin Li 1229*9c5db199SXin Li def _is_applicable(self, host): 1230*9c5db199SXin Li if host.is_localhost(): 1231*9c5db199SXin Li logging.debug('Not supported for localhost.') 1232*9c5db199SXin Li return False 1233*9c5db199SXin Li if not host.servo_serial: 1234*9c5db199SXin Li logging.debug('Servod does not have serial.') 1235*9c5db199SXin Li return False 1236*9c5db199SXin Li if not host.servo_recovery: 1237*9c5db199SXin Li logging.debug('Servod is not running in recovery mode.') 1238*9c5db199SXin Li return False 1239*9c5db199SXin Li if not (host.is_labstation() or host.is_containerized_servod()): 1240*9c5db199SXin Li logging.debug('Not supported for servo_v3.') 1241*9c5db199SXin Li return False 1242*9c5db199SXin Li if not host.get_servo(): 1243*9c5db199SXin Li logging.debug('Servo is not initialized.') 1244*9c5db199SXin Li return False 1245*9c5db199SXin Li return self._is_type_c(host) 1246*9c5db199SXin Li 1247*9c5db199SXin Li def _is_type_c(self, host): 1248*9c5db199SXin Li if host.get_dut_host_info(): 1249*9c5db199SXin Li servo_type = host.get_dut_host_info().get_label_value( 1250*9c5db199SXin Li servo_constants.SERVO_TYPE_LABEL_PREFIX) 1251*9c5db199SXin Li return 'ccd' in servo_type 1252*9c5db199SXin Li return False 1253*9c5db199SXin Li 1254*9c5db199SXin Li @property 1255*9c5db199SXin Li def description(self): 1256*9c5db199SXin Li return 'Fake reconnect to DUT' 1257*9c5db199SXin Li 1258*9c5db199SXin Li 1259*9c5db199SXin Liclass _PowerDeliveryRepair(hosts.RepairAction): 1260*9c5db199SXin Li """Repair to check servo_v4_role for servos that support 1261*9c5db199SXin Li power delivery feature(a.k.a power pass through). 1262*9c5db199SXin Li 1263*9c5db199SXin Li There are currently two position of servo_v4_role, src and snk: 1264*9c5db199SXin Li src -- servo in power delivery mode and passes power to the DUT. 1265*9c5db199SXin Li snk -- servo in normal mode and not passes power to DUT. 1266*9c5db199SXin Li """ 1267*9c5db199SXin Li # How many time retry to set PD in correct mode and verify that is stay. 1268*9c5db199SXin Li # Set 5 as each attempt has 10 attempts inside 'set' method. 1269*9c5db199SXin Li _SET_ATTEMPT_COUNT = 5 1270*9c5db199SXin Li 1271*9c5db199SXin Li @timeout_util.TimeoutDecorator(cros_constants.REPAIR_TIMEOUT_SEC) 1272*9c5db199SXin Li def repair(self, host): 1273*9c5db199SXin Li host.get_servo().set_nocheck('servo_pd_role', 'snk') 1274*9c5db199SXin Li time.sleep(1) 1275*9c5db199SXin Li for x in range(self._SET_ATTEMPT_COUNT): 1276*9c5db199SXin Li logging.debug('Try set servo_v4_role to src.' 1277*9c5db199SXin Li ' Attempt: %s', x + 1) 1278*9c5db199SXin Li try: 1279*9c5db199SXin Li host.get_servo().set('servo_pd_role', 'src') 1280*9c5db199SXin Li # Waiting a few seconds as it can be change to snk if PD 1281*9c5db199SXin Li # on servo has issue. 1282*9c5db199SXin Li time.sleep(5) 1283*9c5db199SXin Li except BaseException as e: 1284*9c5db199SXin Li logging.debug('Setting PD with retries failed %s', e) 1285*9c5db199SXin Li if host.get_servo().get('servo_pd_role') == 'src': 1286*9c5db199SXin Li break 1287*9c5db199SXin Li if host.get_servo().get('servo_pd_role') == 'snk': 1288*9c5db199SXin Li raise hosts.AutoservNonCriticalVerifyError( 1289*9c5db199SXin Li 'Cannot switch power delivery to the src role') 1290*9c5db199SXin Li # Restart servod to re-initialize servos. 1291*9c5db199SXin Li # In some cases if device did not receive power can block detection 1292*9c5db199SXin Li # of servo components. 1293*9c5db199SXin Li host.restart_servod() 1294*9c5db199SXin Li 1295*9c5db199SXin Li def _is_type_c(self, host): 1296*9c5db199SXin Li return (host.is_in_lab() and host.get_servo() 1297*9c5db199SXin Li and host.get_servo().supports_built_in_pd_control()) 1298*9c5db199SXin Li 1299*9c5db199SXin Li @property 1300*9c5db199SXin Li def description(self): 1301*9c5db199SXin Li return 'Recover power delivery on servo' 1302*9c5db199SXin Li 1303*9c5db199SXin Li 1304*9c5db199SXin Liclass _ECRebootRepair(hosts.RepairAction): 1305*9c5db199SXin Li """ 1306*9c5db199SXin Li Reboot EC on DUT from servo. 1307*9c5db199SXin Li """ 1308*9c5db199SXin Li 1309*9c5db199SXin Li def _is_applicable(self, host): 1310*9c5db199SXin Li return (not host.is_localhost()) and host.is_ec_supported() 1311*9c5db199SXin Li 1312*9c5db199SXin Li @timeout_util.TimeoutDecorator(cros_constants.REPAIR_TIMEOUT_SEC) 1313*9c5db199SXin Li def repair(self, host): 1314*9c5db199SXin Li host.get_servo().ec_reboot() 1315*9c5db199SXin Li 1316*9c5db199SXin Li @property 1317*9c5db199SXin Li def description(self): 1318*9c5db199SXin Li return 'Reboot EC' 1319*9c5db199SXin Li 1320*9c5db199SXin Li 1321*9c5db199SXin Liclass _DutRebootRepair(hosts.RepairAction): 1322*9c5db199SXin Li """ 1323*9c5db199SXin Li Reboot DUT to recover some servo controls depending on EC console. 1324*9c5db199SXin Li 1325*9c5db199SXin Li Some servo controls, like lid_open, requires communicating with DUT through 1326*9c5db199SXin Li EC UART console. Failure of this kinds of controls can be recovered by 1327*9c5db199SXin Li rebooting the DUT. 1328*9c5db199SXin Li """ 1329*9c5db199SXin Li 1330*9c5db199SXin Li @timeout_util.TimeoutDecorator(cros_constants.REPAIR_TIMEOUT_SEC) 1331*9c5db199SXin Li def repair(self, host): 1332*9c5db199SXin Li host.get_servo().get_power_state_controller().reset() 1333*9c5db199SXin Li # Get the lid_open value which requires EC console. 1334*9c5db199SXin Li lid_open = host.get_servo().get('lid_open') 1335*9c5db199SXin Li if lid_open != 'yes' and lid_open != 'not_applicable': 1336*9c5db199SXin Li raise hosts.AutoservVerifyError( 1337*9c5db199SXin Li 'Still fail to contact EC console after rebooting DUT') 1338*9c5db199SXin Li 1339*9c5db199SXin Li @property 1340*9c5db199SXin Li def description(self): 1341*9c5db199SXin Li return 'Reset the DUT via servo' 1342*9c5db199SXin Li 1343*9c5db199SXin Li 1344*9c5db199SXin Liclass _DiskCleanupRepair(hosts.RepairAction): 1345*9c5db199SXin Li """ 1346*9c5db199SXin Li Remove old logs/metrics/crash_dumps on servohost to free up disk space. 1347*9c5db199SXin Li """ 1348*9c5db199SXin Li KEEP_LOGS_MAX_DAYS = 5 1349*9c5db199SXin Li 1350*9c5db199SXin Li FILE_TO_REMOVE = [ 1351*9c5db199SXin Li '/var/lib/metrics/uma-events', '/var/spool/crash/*', 1352*9c5db199SXin Li '/var/log/chrome/*', '/var/log/ui/*', 1353*9c5db199SXin Li '/home/chronos/BrowserMetrics/*' 1354*9c5db199SXin Li ] 1355*9c5db199SXin Li 1356*9c5db199SXin Li @timeout_util.TimeoutDecorator(cros_constants.SHORT_REPAIR_TIMEOUT_SEC) 1357*9c5db199SXin Li def repair(self, host): 1358*9c5db199SXin Li if host.is_localhost(): 1359*9c5db199SXin Li # we don't want to remove anything from local testing. 1360*9c5db199SXin Li return 1361*9c5db199SXin Li 1362*9c5db199SXin Li # Remove old servod logs. 1363*9c5db199SXin Li host.run('/usr/bin/find /var/log/servod_* -mtime +%d -print -delete' 1364*9c5db199SXin Li % self.KEEP_LOGS_MAX_DAYS, ignore_status=True) 1365*9c5db199SXin Li 1366*9c5db199SXin Li # Remove pre-defined metrics and crash dumps. 1367*9c5db199SXin Li for path in self.FILE_TO_REMOVE: 1368*9c5db199SXin Li host.run('rm %s' % path, ignore_status=True) 1369*9c5db199SXin Li 1370*9c5db199SXin Li @property 1371*9c5db199SXin Li def description(self): 1372*9c5db199SXin Li return 'Clean up old logs/metrics on servohost to free up disk space.' 1373*9c5db199SXin Li 1374*9c5db199SXin Li 1375*9c5db199SXin Liclass _ServoFwUpdateRepair(hosts.RepairAction): 1376*9c5db199SXin Li """Update firmware for servos. 1377*9c5db199SXin Li 1378*9c5db199SXin Li We try to update servo 3 times and then try to force update it. 1379*9c5db199SXin Li """ 1380*9c5db199SXin Li 1381*9c5db199SXin Li @timeout_util.TimeoutDecorator(cros_constants.REPAIR_TIMEOUT_SEC) 1382*9c5db199SXin Li def repair(self, host): 1383*9c5db199SXin Li try: 1384*9c5db199SXin Li servo_updater.update_servo_firmware(host, 1385*9c5db199SXin Li try_attempt_count=3, 1386*9c5db199SXin Li force_update=False, 1387*9c5db199SXin Li try_force_update=True) 1388*9c5db199SXin Li except servo_updater.ServoUpdaterError as er: 1389*9c5db199SXin Li # Catch servo_updater issue to cache it. 1390*9c5db199SXin Li self.servo_updater_issue_detected = True 1391*9c5db199SXin Li raise hosts.AutoservVerifyError('ServoUpdater issue detected') 1392*9c5db199SXin Li 1393*9c5db199SXin Li def _is_applicable(self, host): 1394*9c5db199SXin Li # Run only for servo_v4 and servo_v4p1. 1395*9c5db199SXin Li return host.is_labstation() or host.is_containerized_servod() 1396*9c5db199SXin Li 1397*9c5db199SXin Li @property 1398*9c5db199SXin Li def description(self): 1399*9c5db199SXin Li return 'Update servo-fw if required.' 1400*9c5db199SXin Li 1401*9c5db199SXin Li 1402*9c5db199SXin Liclass _ServoMicroFlashRepair(hosts.RepairAction): 1403*9c5db199SXin Li """ 1404*9c5db199SXin Li Remove old logs/metrics/crash_dumps on servohost to free up disk space. 1405*9c5db199SXin Li """ 1406*9c5db199SXin Li _TARGET_SERVO = 'servo_micro' 1407*9c5db199SXin Li 1408*9c5db199SXin Li @timeout_util.TimeoutDecorator(cros_constants.REPAIR_TIMEOUT_SEC) 1409*9c5db199SXin Li def repair(self, host): 1410*9c5db199SXin Li if not host.is_cros_host(): 1411*9c5db199SXin Li raise hosts.AutoservRepairError( 1412*9c5db199SXin Li 'Can\'t restart servod: not running ' 1413*9c5db199SXin Li 'embedded ChromeOS.', 1414*9c5db199SXin Li 'servo_not_applicable_to_non_cros_host') 1415*9c5db199SXin Li servo = host.get_servo() 1416*9c5db199SXin Li if not servo or self._TARGET_SERVO not in servo.get_servo_type(): 1417*9c5db199SXin Li logging.info("Servo-micro is not present on set-up") 1418*9c5db199SXin Li return 1419*9c5db199SXin Li 1420*9c5db199SXin Li try: 1421*9c5db199SXin Li servo_updater.update_servo_firmware(host, 1422*9c5db199SXin Li boards=(self._TARGET_SERVO, ), 1423*9c5db199SXin Li force_update=True, 1424*9c5db199SXin Li ignore_version=True) 1425*9c5db199SXin Li except Exception as e: 1426*9c5db199SXin Li logging.debug("(Not critical) Servo device update error: %s", e) 1427*9c5db199SXin Li raise hosts.AutoservVerifyError( 1428*9c5db199SXin Li 'Still fail to contact EC console after rebooting DUT') 1429*9c5db199SXin Li # Update time when we reflashed the fw on the device 1430*9c5db199SXin Li dhp = host.get_dut_health_profile() 1431*9c5db199SXin Li dhp.refresh_servo_miro_fw_update_run_time() 1432*9c5db199SXin Li host.restart_servod() 1433*9c5db199SXin Li 1434*9c5db199SXin Li def is_time_to_try(self, dhp): 1435*9c5db199SXin Li """Verify that it is time when we can try to re-flash fw on servo_micro. 1436*9c5db199SXin Li 1437*9c5db199SXin Li Re-flashing limited to once per 2 weeks to avoid over-flashing 1438*9c5db199SXin Li the servo device. 1439*9c5db199SXin Li """ 1440*9c5db199SXin Li today_time = int(time.time()) 1441*9c5db199SXin Li last_check = dhp.get_servo_micro_fw_update_time_epoch() 1442*9c5db199SXin Li can_run = today_time > (last_check + (14 * 24 * 60 * 60)) 1443*9c5db199SXin Li if not can_run: 1444*9c5db199SXin Li logging.info("The servo_micro fw updated in las 2 weeks ago.") 1445*9c5db199SXin Li return can_run 1446*9c5db199SXin Li 1447*9c5db199SXin Li def _is_applicable(self, host): 1448*9c5db199SXin Li return (not host.is_localhost() and host.get_dut_health_profile() 1449*9c5db199SXin Li and self.is_time_to_try(host.get_dut_health_profile())) 1450*9c5db199SXin Li 1451*9c5db199SXin Li @property 1452*9c5db199SXin Li def description(self): 1453*9c5db199SXin Li return 'Re-flash servo_micro firmware.' 1454*9c5db199SXin Li 1455*9c5db199SXin Li 1456*9c5db199SXin Lidef _servo_verifier_actions(): 1457*9c5db199SXin Li """ 1458*9c5db199SXin Li Return a verifiers for a `ServoHost`. 1459*9c5db199SXin Li """ 1460*9c5db199SXin Li return ( 1461*9c5db199SXin Li (_ConnectionVerifier, 'connection', []), 1462*9c5db199SXin Li (_RootServoPresentVerifier, 'servo_root_present', ['connection']), 1463*9c5db199SXin Li (_RootServoV3PresentVerifier, 'servo_v3_root_present', 1464*9c5db199SXin Li ['connection']), 1465*9c5db199SXin Li (_ServoFwVerifier, 'servo_fw', ['servo_root_present']), 1466*9c5db199SXin Li (_StartServodVerifier, 'start_servod', 1467*9c5db199SXin Li ['servo_fw', 'servo_v3_root_present']), 1468*9c5db199SXin Li (_DiskSpaceVerifier, 'servo_disk_space', ['connection']), 1469*9c5db199SXin Li (_UpdateVerifier, 'servo_update', ['servo_v3_root_present']), 1470*9c5db199SXin Li (_BoardConfigVerifier, 'servo_config_board', ['connection']), 1471*9c5db199SXin Li (_SerialConfigVerifier, 'servo_config_serial', ['connection']), 1472*9c5db199SXin Li (_ServodJobVerifier, 'servod_started', [ 1473*9c5db199SXin Li 'start_servod', 'servo_config_board', 1474*9c5db199SXin Li 'servo_config_serial', 'servo_disk_space' 1475*9c5db199SXin Li ]), 1476*9c5db199SXin Li (_ServodEchoVerifier, 'servod_echo', ['servod_started']), 1477*9c5db199SXin Li (_TopologyVerifier, 'servo_topology', ['servod_echo']), 1478*9c5db199SXin Li (_ServodConnectionVerifier, 'servod_connection', ['servod_echo']), 1479*9c5db199SXin Li (_Cr50LowSBUVerifier, 'servo_cr50_low_sbu', ['servod_connection']), 1480*9c5db199SXin Li (ServodDutControllerMissingVerifier, 1481*9c5db199SXin Li 'servod_dut_controller_missing', ['servod_connection']), 1482*9c5db199SXin Li (_Cr50OffVerifier, 'servo_cr50_off', ['servod_connection']), 1483*9c5db199SXin Li (_ServodControlVerifier, 'servod_control', ['servod_connection']), 1484*9c5db199SXin Li (_DUTConnectionVerifier, 'servo_dut_connected', 1485*9c5db199SXin Li ['servod_connection']), 1486*9c5db199SXin Li (_ServoHubConnectionVerifier, 'servo_hub_connected', 1487*9c5db199SXin Li ['servo_dut_connected']), 1488*9c5db199SXin Li (_PowerButtonVerifier, 'servo_pwr_button', ['servo_hub_connected' 1489*9c5db199SXin Li ]), 1490*9c5db199SXin Li (_BatteryVerifier, 'servo_battery', ['servo_hub_connected']), 1491*9c5db199SXin Li (_LidVerifier, 'servo_lid_open', ['servo_hub_connected']), 1492*9c5db199SXin Li (ECConsoleVerifier, 'servo_ec_console', ['servo_dut_connected']), 1493*9c5db199SXin Li (_Cr50ConsoleVerifier, 'servo_cr50_console', 1494*9c5db199SXin Li ['servo_dut_connected']), 1495*9c5db199SXin Li (_CCDTestlabVerifier, 'servo_ccd_testlab', ['servo_cr50_console']), 1496*9c5db199SXin Li (_CCDPowerDeliveryVerifier, 'servo_power_delivery', 1497*9c5db199SXin Li ['servod_connection']), 1498*9c5db199SXin Li ) 1499*9c5db199SXin Li 1500*9c5db199SXin Li 1501*9c5db199SXin Lidef _servo_repair_actions(): 1502*9c5db199SXin Li """ 1503*9c5db199SXin Li Return a `RepairStrategy` for a `ServoHost`. 1504*9c5db199SXin Li """ 1505*9c5db199SXin Li config = ['servo_config_board', 'servo_config_serial', 'start_servod'] 1506*9c5db199SXin Li base_triggers = [ 1507*9c5db199SXin Li 'servod_started', 'servo_topology', 'servod_connection', 1508*9c5db199SXin Li 'servod_echo', 'servod_control', 'servo_dut_connected', 1509*9c5db199SXin Li 'servo_hub_connected', 'servo_pwr_button', 'servo_cr50_console', 1510*9c5db199SXin Li 'servo_cr50_low_sbu', 'servo_cr50_off', 'servo_power_delivery', 1511*9c5db199SXin Li 'servod_dut_controller_missing' 1512*9c5db199SXin Li ] 1513*9c5db199SXin Li dut_triggers = [ 1514*9c5db199SXin Li 'servod_control', 'servo_lid_open', 'servo_ec_console', 1515*9c5db199SXin Li 'servo_topology', 'servo_dut_connected', 'servo_hub_connected', 1516*9c5db199SXin Li 'servo_cr50_low_sbu', 'servo_cr50_off', 'servo_cr50_console', 1517*9c5db199SXin Li 'servo_power_delivery', 'servod_dut_controller_missing' 1518*9c5db199SXin Li ] 1519*9c5db199SXin Li reboot_triggers = [ 1520*9c5db199SXin Li 'servo_topology', 'servo_root_present', 'servo_disk_space', 1521*9c5db199SXin Li 'servo_power_delivery' 1522*9c5db199SXin Li ] 1523*9c5db199SXin Li return ( 1524*9c5db199SXin Li (_ServoFwUpdateRepair, 'servo_fw_update', ['connection'], 1525*9c5db199SXin Li ['servo_fw']), 1526*9c5db199SXin Li (_DiskCleanupRepair, 'servo_disk_cleanup', ['connection'], 1527*9c5db199SXin Li ['servo_disk_space']), 1528*9c5db199SXin Li (_ServoMicroFlashRepair, 'servo_micro_flash', 1529*9c5db199SXin Li ['connection', 'servo_topology'], ['servo_dut_connected']), 1530*9c5db199SXin Li (_RestartServod, 'servod_restart', ['connection', 'servo_fw'], 1531*9c5db199SXin Li config + base_triggers), 1532*9c5db199SXin Li (_ServoRebootRepair, 'servo_reboot', ['connection'], 1533*9c5db199SXin Li reboot_triggers), 1534*9c5db199SXin Li (_PowerDeliveryRepair, 'servo_pd_recover', ['servod_connection'], 1535*9c5db199SXin Li base_triggers), 1536*9c5db199SXin Li (_FakedisconnectRepair, 'servo_fakedisconnect', 1537*9c5db199SXin Li ['servod_connection'], base_triggers), 1538*9c5db199SXin Li (_ToggleCCLineRepair, 'servo_cc', ['servod_connection'], 1539*9c5db199SXin Li base_triggers), 1540*9c5db199SXin Li (_DutRebootRepair, 'servo_dut_reboot', ['servod_connection'], 1541*9c5db199SXin Li dut_triggers), 1542*9c5db199SXin Li (_ECRebootRepair, 'servo_ec_reboot', ['servod_connection'], 1543*9c5db199SXin Li dut_triggers), 1544*9c5db199SXin Li ) 1545*9c5db199SXin Li 1546*9c5db199SXin Li 1547*9c5db199SXin Lidef create_servo_repair_strategy(): 1548*9c5db199SXin Li """ 1549*9c5db199SXin Li Return a `RepairStrategy` for a `ServoHost`. 1550*9c5db199SXin Li """ 1551*9c5db199SXin Li return hosts.RepairStrategy(_servo_verifier_actions(), 1552*9c5db199SXin Li _servo_repair_actions(), 'servo') 1553