1# Copyright 2019 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import common 6import logging 7from autotest_lib.client.common_lib import hosts 8from autotest_lib.server.hosts import cros_constants 9from autotest_lib.server.hosts import repair_utils 10from autotest_lib.client.common_lib import utils 11 12from chromite.lib import timeout_util 13 14try: 15 from chromite.lib import metrics 16except ImportError: 17 metrics = utils.metrics_mock 18 19# There are some labstations we don't want they receive auto-update, 20# e.g. labstations that used for image qualification purpose 21UPDATE_EXEMPTED_POOL = {'servo_verification', 'labstation_tryjob'} 22 23 24class _LabstationUpdateVerifier(hosts.Verifier): 25 """ 26 Verifier to trigger a labstation update, if necessary. 27 28 The operation doesn't wait for the update to complete and is 29 considered a success whether or not the servo is currently 30 up-to-date. 31 """ 32 33 @timeout_util.TimeoutDecorator(cros_constants.LONG_VERIFY_TIMEOUT_SEC) 34 def verify(self, host): 35 """First, only run this verifier if the host is in the physical lab. 36 Secondly, skip if the test is being run by test_that, because subnet 37 restrictions can cause the update to fail. 38 """ 39 if host.is_in_lab() and host.job and host.job.in_lab: 40 host.update_cros_version_label() 41 info = host.host_info_store.get() 42 if bool(UPDATE_EXEMPTED_POOL & info.pools): 43 logging.info("Skip update because the labstation is in" 44 " one of following exempted pool: %s", info.pools) 45 return 46 47 stable_version = info.stable_versions.get('cros') 48 if stable_version: 49 host.update_image(stable_version=stable_version) 50 else: 51 raise hosts.AutoservVerifyError('Failed to check/update' 52 ' labstation due to no stable' 53 '_version found in host_info' 54 '_store.') 55 56 @property 57 def description(self): 58 return 'Labstation image is updated to current stable-version' 59 60 61class _LabstationRebootVerifier(hosts.Verifier): 62 """Check if reboot is need for the labstation and perform a reboot if it's 63 not currently using by any tests. 64 """ 65 66 @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 67 def verify(self, host): 68 if host.is_reboot_requested(): 69 host.try_reboot() 70 71 @property 72 def description(self): 73 return 'Reboot labstation if requested and the labstation is not in use' 74 75 76class _LabstationLangidVerifier(hosts.Verifier): 77 """Check if labstation has issue with read serial from servo devices. 78 79 TODO(b:162518926): remove when bug will be resolved. 80 """ 81 82 @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC) 83 def verify(self, host): 84 try: 85 cmd = ( 86 "python2 -c 'import usb;" 87 " print([[d.open().getString(d.iSerialNumber, 128)" 88 " for d in bus.devices if d.idVendor == 0x18d1" 89 " and (d.idProduct == 0x501b" #servo_v4 90 " or d.idProduct == 0x501a" #servo_micro 91 " or d.idProduct == 0x5014)" #ccd_cr50 92 " and d.iSerialNumber == 3]" # 3 - slot for serial 93 " for bus in usb.busses()])'") 94 result = host.run(cmd, ignore_status=True, timeout=30) 95 if result.exit_status == 0: 96 return 97 if 'The device has no langid' in result.stderr.strip(): 98 self._mark_host_for_reboot(host) 99 except Exception as e: 100 logging.debug('(Not critical) %s', e) 101 if 'Timeout encountered' in str(e): 102 # Time out mean we cannot get servo attributes in time because 103 # one of the servos has langid. 104 self._mark_host_for_reboot(host) 105 106 def _mark_host_for_reboot(self, host): 107 """Mark Labstation as has issue with langid.""" 108 logging.info('Detected langid issue.') 109 data = {'host': host.hostname, 'board': host.get_board() or ''} 110 metrics.Counter('chromeos/autotest/labstation/langid_issue').increment( 111 fields=data) 112 # labstation reboot will fix the issue but we does not want to 113 # reboot the labstation to often. Just create request to reboot 114 # it for the next time. 115 logging.info('Created request for reboot.') 116 cmd = ('touch %slangid%s' % 117 (host.TEMP_FILE_DIR, host.REBOOT_FILE_POSTFIX)) 118 host.run(cmd, ignore_status=True, timeout=30) 119 120 @property 121 def description(self): 122 return 'Check if labsattion has langid issue' 123 124 125def create_labstation_repair_strategy(): 126 """ 127 Return a `RepairStrategy` for a `LabstationHost`. 128 """ 129 verify_dag = [ 130 (repair_utils.SshVerifier, 'ssh', []), 131 (_LabstationUpdateVerifier, 'update', ['ssh']), 132 (_LabstationLangidVerifier, 'langid', ['ssh']), 133 (_LabstationRebootVerifier, 'reboot', ['ssh']), 134 ] 135 136 repair_actions = [ 137 (repair_utils.RPMCycleRepair, 'rpm', [], ['ssh', 'reboot']), 138 ] 139 return hosts.RepairStrategy(verify_dag, repair_actions, 'labstation') 140