Fang Deng | 5d518f4 | 2013-08-02 14:04:32 -0700 | [diff] [blame] | 1 | # Copyright (c) 2013 The Chromium OS Authors. All rights reserved. |
| 2 | # Use of this source code is governed by a BSD-style license that can be |
| 3 | # found in the LICENSE file. |
| 4 | # |
| 5 | # Expects to be run in an environment with sudo and no interactive password |
| 6 | # prompt, such as within the Chromium OS development chroot. |
| 7 | |
| 8 | |
| 9 | """This file provides core logic for servo verify/repair process.""" |
| 10 | |
| 11 | |
Fang Deng | 5d518f4 | 2013-08-02 14:04:32 -0700 | [diff] [blame] | 12 | import logging |
Fang Deng | 5d518f4 | 2013-08-02 14:04:32 -0700 | [diff] [blame] | 13 | import xmlrpclib |
Raul E Rangel | 52ca2e8 | 2018-07-03 14:10:14 -0600 | [diff] [blame] | 14 | import os |
Fang Deng | 5d518f4 | 2013-08-02 14:04:32 -0700 | [diff] [blame] | 15 | |
| 16 | from autotest_lib.client.bin import utils |
Garry Wang | 79e9af6 | 2019-06-12 15:19:19 -0700 | [diff] [blame^] | 17 | from autotest_lib.client.common_lib import error |
beeps | 5e8c45a | 2013-12-17 22:05:11 -0800 | [diff] [blame] | 18 | from autotest_lib.client.common_lib import global_config |
Richard Barnette | 9a26ad6 | 2016-06-10 12:03:08 -0700 | [diff] [blame] | 19 | from autotest_lib.client.common_lib import hosts |
Fang Deng | 5d518f4 | 2013-08-02 14:04:32 -0700 | [diff] [blame] | 20 | from autotest_lib.client.common_lib.cros import retry |
Christopher Wiley | cef1f90 | 2014-06-19 11:11:23 -0700 | [diff] [blame] | 21 | from autotest_lib.client.common_lib.cros.network import ping_runner |
Richard Barnette | 9a26ad6 | 2016-06-10 12:03:08 -0700 | [diff] [blame] | 22 | from autotest_lib.server.cros.servo import servo |
Richard Barnette | d31580e | 2018-05-14 19:58:00 +0000 | [diff] [blame] | 23 | from autotest_lib.server.hosts import servo_repair |
Garry Wang | ebc015b | 2019-06-06 17:45:06 -0700 | [diff] [blame] | 24 | from autotest_lib.server.hosts import base_servohost |
Dan Shi | 5e2efb7 | 2017-02-07 11:40:23 -0800 | [diff] [blame] | 25 | |
Fang Deng | 5d518f4 | 2013-08-02 14:04:32 -0700 | [diff] [blame] | 26 | |
Simran Basi | 0739d68 | 2015-02-25 16:22:56 -0800 | [diff] [blame] | 27 | # Names of the host attributes in the database that represent the values for |
| 28 | # the servo_host and servo_port for a servo connected to the DUT. |
| 29 | SERVO_HOST_ATTR = 'servo_host' |
| 30 | SERVO_PORT_ATTR = 'servo_port' |
Richard Barnette | e519dcd | 2016-08-15 17:37:17 -0700 | [diff] [blame] | 31 | SERVO_BOARD_ATTR = 'servo_board' |
Nick Sanders | 2f3c985 | 2018-10-24 12:10:24 -0700 | [diff] [blame] | 32 | # Model is inferred from host labels. |
| 33 | SERVO_MODEL_ATTR = 'servo_model' |
Kevin Cheng | 643ce8a | 2016-09-15 15:42:12 -0700 | [diff] [blame] | 34 | SERVO_SERIAL_ATTR = 'servo_serial' |
Prathmesh Prabhu | cba4429 | 2018-08-28 17:44:45 -0700 | [diff] [blame] | 35 | SERVO_ATTR_KEYS = ( |
| 36 | SERVO_BOARD_ATTR, |
| 37 | SERVO_HOST_ATTR, |
| 38 | SERVO_PORT_ATTR, |
| 39 | SERVO_SERIAL_ATTR, |
| 40 | ) |
Simran Basi | 0739d68 | 2015-02-25 16:22:56 -0800 | [diff] [blame] | 41 | |
Dan Shi | 3b2adf6 | 2015-09-02 17:46:54 -0700 | [diff] [blame] | 42 | _CONFIG = global_config.global_config |
xixuan | 6cf6d2f | 2016-01-29 15:29:00 -0800 | [diff] [blame] | 43 | ENABLE_SSH_TUNNEL_FOR_SERVO = _CONFIG.get_config_value( |
| 44 | 'CROS', 'enable_ssh_tunnel_for_servo', type=bool, default=False) |
Simran Basi | 0739d68 | 2015-02-25 16:22:56 -0800 | [diff] [blame] | 45 | |
Kevin Cheng | 5f2ba6c | 2016-09-28 10:20:05 -0700 | [diff] [blame] | 46 | AUTOTEST_BASE = _CONFIG.get_config_value( |
| 47 | 'SCHEDULER', 'drone_installation_directory', |
| 48 | default='/usr/local/autotest') |
| 49 | |
Fang Deng | 5d518f4 | 2013-08-02 14:04:32 -0700 | [diff] [blame] | 50 | |
Garry Wang | ebc015b | 2019-06-06 17:45:06 -0700 | [diff] [blame] | 51 | class ServoHost(base_servohost.BaseServoHost): |
| 52 | """Host class for a servo host(e.g. beaglebone, labstation) |
| 53 | that with a servo instance for a specific port.""" |
Fang Deng | 5d518f4 | 2013-08-02 14:04:32 -0700 | [diff] [blame] | 54 | |
Raul E Rangel | 52ca2e8 | 2018-07-03 14:10:14 -0600 | [diff] [blame] | 55 | DEFAULT_PORT = int(os.getenv('SERVOD_PORT', '9999')) |
Richard Barnette | 9a26ad6 | 2016-06-10 12:03:08 -0700 | [diff] [blame] | 56 | |
Dan Shi | e5b3c51 | 2014-08-21 12:12:09 -0700 | [diff] [blame] | 57 | # Timeout for initializing servo signals. |
Wai-Hong Tam | 37b6ed3 | 2017-09-19 15:52:39 -0700 | [diff] [blame] | 58 | INITIALIZE_SERVO_TIMEOUT_SECS = 60 |
Richard Barnette | 9a26ad6 | 2016-06-10 12:03:08 -0700 | [diff] [blame] | 59 | |
xixuan | 6cf6d2f | 2016-01-29 15:29:00 -0800 | [diff] [blame] | 60 | # Ready test function |
| 61 | SERVO_READY_METHOD = 'get_version' |
Fang Deng | 5d518f4 | 2013-08-02 14:04:32 -0700 | [diff] [blame] | 62 | |
| 63 | |
Richard Barnette | 17bfc6c | 2016-08-04 18:41:43 -0700 | [diff] [blame] | 64 | def _initialize(self, servo_host='localhost', |
Richard Barnette | e519dcd | 2016-08-15 17:37:17 -0700 | [diff] [blame] | 65 | servo_port=DEFAULT_PORT, servo_board=None, |
Nick Sanders | 2f3c985 | 2018-10-24 12:10:24 -0700 | [diff] [blame] | 66 | servo_model=None, servo_serial=None, is_in_lab=None, |
| 67 | *args, **dargs): |
Fang Deng | 5d518f4 | 2013-08-02 14:04:32 -0700 | [diff] [blame] | 68 | """Initialize a ServoHost instance. |
| 69 | |
| 70 | A ServoHost instance represents a host that controls a servo. |
| 71 | |
| 72 | @param servo_host: Name of the host where the servod process |
| 73 | is running. |
Raul E Rangel | 52ca2e8 | 2018-07-03 14:10:14 -0600 | [diff] [blame] | 74 | @param servo_port: Port the servod process is listening on. Defaults |
| 75 | to the SERVOD_PORT environment variable if set, |
| 76 | otherwise 9999. |
Kevin Cheng | 5f2ba6c | 2016-09-28 10:20:05 -0700 | [diff] [blame] | 77 | @param servo_board: Board that the servo is connected to. |
Nick Sanders | 2f3c985 | 2018-10-24 12:10:24 -0700 | [diff] [blame] | 78 | @param servo_model: Model that the servo is connected to. |
Dan Shi | 4d47852 | 2014-02-14 13:46:32 -0800 | [diff] [blame] | 79 | @param is_in_lab: True if the servo host is in Cros Lab. Default is set |
| 80 | to None, for which utils.host_is_in_lab_zone will be |
| 81 | called to check if the servo host is in Cros lab. |
Fang Deng | 5d518f4 | 2013-08-02 14:04:32 -0700 | [diff] [blame] | 82 | |
| 83 | """ |
| 84 | super(ServoHost, self)._initialize(hostname=servo_host, |
Garry Wang | ebc015b | 2019-06-06 17:45:06 -0700 | [diff] [blame] | 85 | is_in_lab=is_in_lab, *args, **dargs) |
Richard Barnette | 42f4db9 | 2018-08-23 15:05:15 -0700 | [diff] [blame] | 86 | self.servo_port = int(servo_port) |
Richard Barnette | e519dcd | 2016-08-15 17:37:17 -0700 | [diff] [blame] | 87 | self.servo_board = servo_board |
Nick Sanders | 2f3c985 | 2018-10-24 12:10:24 -0700 | [diff] [blame] | 88 | self.servo_model = servo_model |
Kevin Cheng | 643ce8a | 2016-09-15 15:42:12 -0700 | [diff] [blame] | 89 | self.servo_serial = servo_serial |
Richard Barnette | e519dcd | 2016-08-15 17:37:17 -0700 | [diff] [blame] | 90 | self._servo = None |
Garry Wang | 79e9af6 | 2019-06-12 15:19:19 -0700 | [diff] [blame^] | 91 | # Path of the servo host lock file. |
| 92 | self._lock_file = (self.TEMP_FILE_DIR + str(self.servo_port) |
| 93 | + self.LOCK_FILE_POSTFIX) |
| 94 | # File path to declare a reboot request. |
| 95 | self._reboot_file = (self.TEMP_FILE_DIR + str(self.servo_port) |
| 96 | + self.REBOOT_FILE_POSTFIX) |
| 97 | |
| 98 | # Lock the servo host if it's an in-lab labstation to prevent other |
| 99 | # task to reboot it until current task completes. We also wait and |
| 100 | # make sure the labstation is up here, in the case of the labstation is |
| 101 | # in the middle of reboot. |
| 102 | if (self.is_in_lab() and self.is_labstation() |
| 103 | and self.wait_up(self.REBOOT_TIMEOUT)): |
| 104 | self._lock() |
Garry Wang | ebc015b | 2019-06-06 17:45:06 -0700 | [diff] [blame] | 105 | |
Richard Barnette | 9a26ad6 | 2016-06-10 12:03:08 -0700 | [diff] [blame] | 106 | self._repair_strategy = ( |
| 107 | servo_repair.create_servo_repair_strategy()) |
Richard Barnette | e519dcd | 2016-08-15 17:37:17 -0700 | [diff] [blame] | 108 | |
Richard Barnette | 9a26ad6 | 2016-06-10 12:03:08 -0700 | [diff] [blame] | 109 | |
| 110 | def connect_servo(self): |
Kevin Cheng | 5f2ba6c | 2016-09-28 10:20:05 -0700 | [diff] [blame] | 111 | """Establish a connection to the servod server on this host. |
Richard Barnette | 9a26ad6 | 2016-06-10 12:03:08 -0700 | [diff] [blame] | 112 | |
| 113 | Initializes `self._servo` and then verifies that all network |
| 114 | connections are working. This will create an ssh tunnel if |
| 115 | it's required. |
| 116 | |
| 117 | As a side effect of testing the connection, all signals on the |
| 118 | target servo are reset to default values, and the USB stick is |
| 119 | set to the neutral (off) position. |
| 120 | """ |
Kevin Cheng | 643ce8a | 2016-09-15 15:42:12 -0700 | [diff] [blame] | 121 | servo_obj = servo.Servo(servo_host=self, servo_serial=self.servo_serial) |
Richard Barnette | 9a26ad6 | 2016-06-10 12:03:08 -0700 | [diff] [blame] | 122 | timeout, _ = retry.timeout( |
| 123 | servo_obj.initialize_dut, |
| 124 | timeout_sec=self.INITIALIZE_SERVO_TIMEOUT_SECS) |
| 125 | if timeout: |
| 126 | raise hosts.AutoservVerifyError( |
| 127 | 'Servo initialize timed out.') |
| 128 | self._servo = servo_obj |
| 129 | |
| 130 | |
| 131 | def disconnect_servo(self): |
Kevin Cheng | 5f2ba6c | 2016-09-28 10:20:05 -0700 | [diff] [blame] | 132 | """Disconnect our servo if it exists. |
Richard Barnette | 9a26ad6 | 2016-06-10 12:03:08 -0700 | [diff] [blame] | 133 | |
| 134 | If we've previously successfully connected to our servo, |
| 135 | disconnect any established ssh tunnel, and set `self._servo` |
| 136 | back to `None`. |
| 137 | """ |
| 138 | if self._servo: |
| 139 | # N.B. This call is safe even without a tunnel: |
| 140 | # rpc_server_tracker.disconnect() silently ignores |
| 141 | # unknown ports. |
| 142 | self.rpc_server_tracker.disconnect(self.servo_port) |
| 143 | self._servo = None |
Fang Deng | 5d518f4 | 2013-08-02 14:04:32 -0700 | [diff] [blame] | 144 | |
| 145 | |
Fang Deng | 5d518f4 | 2013-08-02 14:04:32 -0700 | [diff] [blame] | 146 | def get_servod_server_proxy(self): |
Kevin Cheng | 5f2ba6c | 2016-09-28 10:20:05 -0700 | [diff] [blame] | 147 | """Return a proxy that can be used to communicate with servod server. |
Fang Deng | 5d518f4 | 2013-08-02 14:04:32 -0700 | [diff] [blame] | 148 | |
| 149 | @returns: An xmlrpclib.ServerProxy that is connected to the servod |
| 150 | server on the host. |
Fang Deng | 5d518f4 | 2013-08-02 14:04:32 -0700 | [diff] [blame] | 151 | """ |
Richard Barnette | 9a26ad6 | 2016-06-10 12:03:08 -0700 | [diff] [blame] | 152 | if ENABLE_SSH_TUNNEL_FOR_SERVO and not self.is_localhost(): |
| 153 | return self.rpc_server_tracker.xmlrpc_connect( |
| 154 | None, self.servo_port, |
| 155 | ready_test_name=self.SERVO_READY_METHOD, |
Allen Li | 2b1a899 | 2018-11-27 14:17:18 -0800 | [diff] [blame] | 156 | timeout_seconds=60, |
Allen Li | 556f453 | 2018-12-03 18:11:23 -0800 | [diff] [blame] | 157 | request_timeout_seconds=3600) |
Richard Barnette | 9a26ad6 | 2016-06-10 12:03:08 -0700 | [diff] [blame] | 158 | else: |
| 159 | remote = 'http://%s:%s' % (self.hostname, self.servo_port) |
| 160 | return xmlrpclib.ServerProxy(remote) |
Fang Deng | 5d518f4 | 2013-08-02 14:04:32 -0700 | [diff] [blame] | 161 | |
| 162 | |
Richard Barnette | 1edbb16 | 2016-11-01 11:47:50 -0700 | [diff] [blame] | 163 | def verify(self, silent=False): |
| 164 | """Update the servo host and verify it's in a good state. |
| 165 | |
| 166 | @param silent If true, suppress logging in `status.log`. |
| 167 | """ |
Richard Barnette | abbdc25 | 2018-07-26 16:57:42 -0700 | [diff] [blame] | 168 | message = 'Beginning verify for servo host %s port %s serial %s' |
| 169 | message %= (self.hostname, self.servo_port, self.servo_serial) |
| 170 | self.record('INFO', None, None, message) |
Richard Barnette | 9a26ad6 | 2016-06-10 12:03:08 -0700 | [diff] [blame] | 171 | try: |
Richard Barnette | 1edbb16 | 2016-11-01 11:47:50 -0700 | [diff] [blame] | 172 | self._repair_strategy.verify(self, silent) |
Richard Barnette | 9a26ad6 | 2016-06-10 12:03:08 -0700 | [diff] [blame] | 173 | except: |
| 174 | self.disconnect_servo() |
| 175 | raise |
Fang Deng | 5d518f4 | 2013-08-02 14:04:32 -0700 | [diff] [blame] | 176 | |
| 177 | |
Richard Barnette | 1edbb16 | 2016-11-01 11:47:50 -0700 | [diff] [blame] | 178 | def repair(self, silent=False): |
| 179 | """Attempt to repair servo host. |
| 180 | |
| 181 | @param silent If true, suppress logging in `status.log`. |
| 182 | """ |
Richard Barnette | abbdc25 | 2018-07-26 16:57:42 -0700 | [diff] [blame] | 183 | message = 'Beginning repair for servo host %s port %s serial %s' |
| 184 | message %= (self.hostname, self.servo_port, self.servo_serial) |
| 185 | self.record('INFO', None, None, message) |
Richard Barnette | 9a26ad6 | 2016-06-10 12:03:08 -0700 | [diff] [blame] | 186 | try: |
Richard Barnette | 1edbb16 | 2016-11-01 11:47:50 -0700 | [diff] [blame] | 187 | self._repair_strategy.repair(self, silent) |
Richard Barnette | 9a26ad6 | 2016-06-10 12:03:08 -0700 | [diff] [blame] | 188 | except: |
| 189 | self.disconnect_servo() |
| 190 | raise |
Fang Deng | 5d518f4 | 2013-08-02 14:04:32 -0700 | [diff] [blame] | 191 | |
| 192 | |
Dan Shi | 4d47852 | 2014-02-14 13:46:32 -0800 | [diff] [blame] | 193 | def get_servo(self): |
| 194 | """Get the cached servo.Servo object. |
Fang Deng | 5d518f4 | 2013-08-02 14:04:32 -0700 | [diff] [blame] | 195 | |
Dan Shi | 4d47852 | 2014-02-14 13:46:32 -0800 | [diff] [blame] | 196 | @return: a servo.Servo object. |
Fang Deng | 5d518f4 | 2013-08-02 14:04:32 -0700 | [diff] [blame] | 197 | """ |
Dan Shi | 4d47852 | 2014-02-14 13:46:32 -0800 | [diff] [blame] | 198 | return self._servo |
| 199 | |
| 200 | |
Garry Wang | 79e9af6 | 2019-06-12 15:19:19 -0700 | [diff] [blame^] | 201 | def request_reboot(self): |
| 202 | """Request servohost to be rebooted when it's safe to by touch a file. |
| 203 | """ |
| 204 | logging.debug('Request to reboot servohost %s has been created by ' |
| 205 | 'servo with port %s', self.hostname, self.servo_port) |
| 206 | self.run('touch %s' % self._reboot_file, ignore_status=True) |
| 207 | |
| 208 | |
| 209 | def _lock(self): |
| 210 | """lock servohost by touching a file. |
| 211 | """ |
| 212 | logging.debug('Locking servohost %s by touching %s file', |
| 213 | self.hostname, self._lock_file) |
| 214 | self.run('touch %s' % self._lock_file, ignore_status=True) |
| 215 | |
| 216 | |
| 217 | def _unlock(self): |
| 218 | """Unlock servohost by removing the lock file. |
| 219 | """ |
| 220 | logging.debug('Unlocking servohost by removing %s file', |
| 221 | self._lock_file) |
| 222 | self.run('rm %s' % self._lock_file, ignore_status=True) |
| 223 | |
| 224 | |
Congbin Guo | a1f9cba | 2018-07-03 11:36:59 -0700 | [diff] [blame] | 225 | def close(self): |
Congbin Guo | fc3b896 | 2019-03-22 17:38:46 -0700 | [diff] [blame] | 226 | """Close the associated servo and the host object.""" |
Congbin Guo | a1f9cba | 2018-07-03 11:36:59 -0700 | [diff] [blame] | 227 | if self._servo: |
Congbin Guo | 2e5e2a2 | 2018-07-27 10:32:48 -0700 | [diff] [blame] | 228 | # In some cases when we run as lab-tools, the job object is None. |
Congbin Guo | fc3b896 | 2019-03-22 17:38:46 -0700 | [diff] [blame] | 229 | if self.job and not self._servo.uart_logs_dir: |
| 230 | self._servo.uart_logs_dir = self.job.resultdir |
Congbin Guo | a1f9cba | 2018-07-03 11:36:59 -0700 | [diff] [blame] | 231 | self._servo.close() |
| 232 | |
Garry Wang | 79e9af6 | 2019-06-12 15:19:19 -0700 | [diff] [blame^] | 233 | if self.is_in_lab() and self.is_labstation(): |
| 234 | # Remove the lock if the host is an in-lab labstation. |
| 235 | try: |
| 236 | self._unlock() |
| 237 | except error.AutoservSSHTimeout: |
| 238 | logging.error('Unlock servohost failed due to ssh timeout.' |
| 239 | ' It may caused by servohost went down during' |
| 240 | ' the task.') |
| 241 | |
Congbin Guo | a1f9cba | 2018-07-03 11:36:59 -0700 | [diff] [blame] | 242 | super(ServoHost, self).close() |
| 243 | |
| 244 | |
Richard Barnette | ea3e460 | 2016-06-10 12:36:41 -0700 | [diff] [blame] | 245 | def make_servo_hostname(dut_hostname): |
| 246 | """Given a DUT's hostname, return the hostname of its servo. |
| 247 | |
| 248 | @param dut_hostname: hostname of a DUT. |
| 249 | |
| 250 | @return hostname of the DUT's servo. |
| 251 | |
| 252 | """ |
| 253 | host_parts = dut_hostname.split('.') |
| 254 | host_parts[0] = host_parts[0] + '-servo' |
| 255 | return '.'.join(host_parts) |
| 256 | |
| 257 | |
| 258 | def servo_host_is_up(servo_hostname): |
Kevin Cheng | 5f2ba6c | 2016-09-28 10:20:05 -0700 | [diff] [blame] | 259 | """Given a servo host name, return if it's up or not. |
Richard Barnette | ea3e460 | 2016-06-10 12:36:41 -0700 | [diff] [blame] | 260 | |
| 261 | @param servo_hostname: hostname of the servo host. |
| 262 | |
| 263 | @return True if it's up, False otherwise |
| 264 | """ |
| 265 | # Technically, this duplicates the SSH ping done early in the servo |
| 266 | # proxy initialization code. However, this ping ends in a couple |
| 267 | # seconds when if fails, rather than the 60 seconds it takes to decide |
| 268 | # that an SSH ping has timed out. Specifically, that timeout happens |
| 269 | # when our servo DNS name resolves, but there is no host at that IP. |
| 270 | logging.info('Pinging servo host at %s', servo_hostname) |
| 271 | ping_config = ping_runner.PingConfig( |
| 272 | servo_hostname, count=3, |
| 273 | ignore_result=True, ignore_status=True) |
| 274 | return ping_runner.PingRunner().ping(ping_config).received > 0 |
| 275 | |
| 276 | |
Richard Barnette | e519dcd | 2016-08-15 17:37:17 -0700 | [diff] [blame] | 277 | def _map_afe_board_to_servo_board(afe_board): |
| 278 | """Map a board we get from the AFE to a servo appropriate value. |
| 279 | |
| 280 | Many boards are identical to other boards for servo's purposes. |
| 281 | This function makes that mapping. |
| 282 | |
| 283 | @param afe_board string board name received from AFE. |
| 284 | @return board we expect servo to have. |
| 285 | |
| 286 | """ |
| 287 | KNOWN_SUFFIXES = ['-freon', '_freon', '_moblab', '-cheets'] |
| 288 | BOARD_MAP = {'gizmo': 'panther'} |
| 289 | mapped_board = afe_board |
| 290 | if afe_board in BOARD_MAP: |
| 291 | mapped_board = BOARD_MAP[afe_board] |
| 292 | else: |
| 293 | for suffix in KNOWN_SUFFIXES: |
| 294 | if afe_board.endswith(suffix): |
| 295 | mapped_board = afe_board[0:-len(suffix)] |
| 296 | break |
| 297 | if mapped_board != afe_board: |
| 298 | logging.info('Mapping AFE board=%s to %s', afe_board, mapped_board) |
| 299 | return mapped_board |
| 300 | |
| 301 | |
Prathmesh Prabhu | b481023 | 2018-09-07 13:24:08 -0700 | [diff] [blame] | 302 | def get_servo_args_for_host(dut_host): |
Kevin Cheng | 5f2ba6c | 2016-09-28 10:20:05 -0700 | [diff] [blame] | 303 | """Return servo data associated with a given DUT. |
Richard Barnette | ea3e460 | 2016-06-10 12:36:41 -0700 | [diff] [blame] | 304 | |
Richard Barnette | ea3e460 | 2016-06-10 12:36:41 -0700 | [diff] [blame] | 305 | @param dut_host Instance of `Host` on which to find the servo |
| 306 | attributes. |
Prathmesh Prabhu | f605dd3 | 2018-08-28 17:09:04 -0700 | [diff] [blame] | 307 | @return `servo_args` dict with host and an optional port. |
Richard Barnette | ea3e460 | 2016-06-10 12:36:41 -0700 | [diff] [blame] | 308 | """ |
Prathmesh Prabhu | cba4429 | 2018-08-28 17:44:45 -0700 | [diff] [blame] | 309 | info = dut_host.host_info_store.get() |
| 310 | servo_args = {k: v for k, v in info.attributes.iteritems() |
| 311 | if k in SERVO_ATTR_KEYS} |
Richard Barnette | ea3e460 | 2016-06-10 12:36:41 -0700 | [diff] [blame] | 312 | |
Prathmesh Prabhu | cba4429 | 2018-08-28 17:44:45 -0700 | [diff] [blame] | 313 | if SERVO_PORT_ATTR in servo_args: |
| 314 | try: |
| 315 | servo_args[SERVO_PORT_ATTR] = int(servo_args[SERVO_PORT_ATTR]) |
| 316 | except ValueError: |
| 317 | logging.error('servo port is not an int: %s', |
| 318 | servo_args[SERVO_PORT_ATTR]) |
| 319 | # Reset servo_args because we don't want to use an invalid port. |
| 320 | servo_args.pop(SERVO_HOST_ATTR, None) |
| 321 | |
| 322 | if info.board: |
| 323 | servo_args[SERVO_BOARD_ATTR] = _map_afe_board_to_servo_board(info.board) |
Nick Sanders | 2f3c985 | 2018-10-24 12:10:24 -0700 | [diff] [blame] | 324 | if info.model: |
| 325 | servo_args[SERVO_MODEL_ATTR] = info.model |
Prathmesh Prabhu | 6f5f636 | 2018-09-05 17:20:31 -0700 | [diff] [blame] | 326 | return servo_args if SERVO_HOST_ATTR in servo_args else None |
Richard Barnette | ea3e460 | 2016-06-10 12:36:41 -0700 | [diff] [blame] | 327 | |
| 328 | |
Prathmesh Prabhu | efb1b48 | 2018-08-28 17:15:05 -0700 | [diff] [blame] | 329 | def _tweak_args_for_ssp_moblab(servo_args): |
| 330 | if servo_args[SERVO_HOST_ATTR] in ['localhost', '127.0.0.1']: |
| 331 | servo_args[SERVO_HOST_ATTR] = _CONFIG.get_config_value( |
| 332 | 'SSP', 'host_container_ip', type=str, default=None) |
| 333 | |
| 334 | |
Dan Shi | 023aae3 | 2016-05-25 11:13:01 -0700 | [diff] [blame] | 335 | def create_servo_host(dut, servo_args, try_lab_servo=False, |
Richard Barnette | 9a26ad6 | 2016-06-10 12:03:08 -0700 | [diff] [blame] | 336 | try_servo_repair=False): |
Kevin Cheng | 5f2ba6c | 2016-09-28 10:20:05 -0700 | [diff] [blame] | 337 | """Create a ServoHost object for a given DUT, if appropriate. |
Dan Shi | 4d47852 | 2014-02-14 13:46:32 -0800 | [diff] [blame] | 338 | |
Richard Barnette | 9a26ad6 | 2016-06-10 12:03:08 -0700 | [diff] [blame] | 339 | This function attempts to create and verify or repair a `ServoHost` |
| 340 | object for a servo connected to the given `dut`, subject to various |
| 341 | constraints imposed by the parameters: |
| 342 | * When the `servo_args` parameter is not `None`, a servo |
| 343 | host must be created, and must be checked with `repair()`. |
| 344 | * Otherwise, if a servo exists in the lab and `try_lab_servo` is |
| 345 | true: |
| 346 | * If `try_servo_repair` is true, then create a servo host and |
| 347 | check it with `repair()`. |
| 348 | * Otherwise, if the servo responds to `ping` then create a |
| 349 | servo host and check it with `verify()`. |
Fang Deng | e545abb | 2014-12-30 18:43:47 -0800 | [diff] [blame] | 350 | |
Richard Barnette | 9a26ad6 | 2016-06-10 12:03:08 -0700 | [diff] [blame] | 351 | In cases where `servo_args` was not `None`, repair failure |
| 352 | exceptions are passed back to the caller; otherwise, exceptions |
Richard Barnette | 07c2e1d | 2016-10-26 14:24:28 -0700 | [diff] [blame] | 353 | are logged and then discarded. Note that this only happens in cases |
| 354 | where we're called from a test (not special task) control file that |
| 355 | has an explicit dependency on servo. In that case, we require that |
| 356 | repair not write to `status.log`, so as to avoid polluting test |
| 357 | results. |
| 358 | |
| 359 | TODO(jrbarnette): The special handling for servo in test control |
| 360 | files is a thorn in my flesh; I dearly hope to see it cut out before |
| 361 | my retirement. |
Richard Barnette | 9a26ad6 | 2016-06-10 12:03:08 -0700 | [diff] [blame] | 362 | |
| 363 | Parameters for a servo host consist of a host name, port number, and |
| 364 | DUT board, and are determined from one of these sources, in order of |
| 365 | priority: |
Richard Barnette | ea3e460 | 2016-06-10 12:36:41 -0700 | [diff] [blame] | 366 | * Servo attributes from the `dut` parameter take precedence over |
| 367 | all other sources of information. |
| 368 | * If a DNS entry for the servo based on the DUT hostname exists in |
| 369 | the CrOS lab network, that hostname is used with the default |
Richard Barnette | 9a26ad6 | 2016-06-10 12:03:08 -0700 | [diff] [blame] | 370 | port and the DUT's board. |
Richard Barnette | ea3e460 | 2016-06-10 12:36:41 -0700 | [diff] [blame] | 371 | * If no other options are found, the parameters will be taken |
Richard Barnette | 9a26ad6 | 2016-06-10 12:03:08 -0700 | [diff] [blame] | 372 | from the `servo_args` dict passed in from the caller. |
Richard Barnette | ea3e460 | 2016-06-10 12:36:41 -0700 | [diff] [blame] | 373 | |
| 374 | @param dut An instance of `Host` from which to take |
| 375 | servo parameters (if available). |
| 376 | @param servo_args A dictionary with servo parameters to use if |
| 377 | they can't be found from `dut`. If this |
| 378 | argument is supplied, unrepaired exceptions |
| 379 | from `verify()` will be passed back to the |
| 380 | caller. |
| 381 | @param try_lab_servo If not true, servo host creation will be |
| 382 | skipped unless otherwise required by the |
| 383 | caller. |
Richard Barnette | 9a26ad6 | 2016-06-10 12:03:08 -0700 | [diff] [blame] | 384 | @param try_servo_repair If true, check a servo host with |
| 385 | `repair()` instead of `verify()`. |
Dan Shi | 4d47852 | 2014-02-14 13:46:32 -0800 | [diff] [blame] | 386 | |
| 387 | @returns: A ServoHost object or None. See comments above. |
| 388 | |
| 389 | """ |
Richard Barnette | 07c2e1d | 2016-10-26 14:24:28 -0700 | [diff] [blame] | 390 | servo_dependency = servo_args is not None |
Richard Barnette | 07c2e1d | 2016-10-26 14:24:28 -0700 | [diff] [blame] | 391 | if dut is not None and (try_lab_servo or servo_dependency): |
Prathmesh Prabhu | b481023 | 2018-09-07 13:24:08 -0700 | [diff] [blame] | 392 | servo_args_override = get_servo_args_for_host(dut) |
Richard Barnette | ea3e460 | 2016-06-10 12:36:41 -0700 | [diff] [blame] | 393 | if servo_args_override is not None: |
Prathmesh Prabhu | efb1b48 | 2018-08-28 17:15:05 -0700 | [diff] [blame] | 394 | if utils.in_moblab_ssp(): |
| 395 | _tweak_args_for_ssp_moblab(servo_args_override) |
Prathmesh Prabhu | 88bf605 | 2018-08-28 16:21:26 -0700 | [diff] [blame] | 396 | logging.debug( |
| 397 | 'Overriding provided servo_args (%s) with arguments' |
| 398 | ' determined from the host (%s)', |
| 399 | servo_args, |
| 400 | servo_args_override, |
| 401 | ) |
Richard Barnette | ea3e460 | 2016-06-10 12:36:41 -0700 | [diff] [blame] | 402 | servo_args = servo_args_override |
Prathmesh Prabhu | cba4429 | 2018-08-28 17:44:45 -0700 | [diff] [blame] | 403 | |
Richard Barnette | ea3e460 | 2016-06-10 12:36:41 -0700 | [diff] [blame] | 404 | if servo_args is None: |
Prathmesh Prabhu | 88bf605 | 2018-08-28 16:21:26 -0700 | [diff] [blame] | 405 | logging.debug('No servo_args provided, and failed to find overrides.') |
Richard Barnette | ea3e460 | 2016-06-10 12:36:41 -0700 | [diff] [blame] | 406 | return None |
Prathmesh Prabhu | cba4429 | 2018-08-28 17:44:45 -0700 | [diff] [blame] | 407 | if SERVO_HOST_ATTR not in servo_args: |
| 408 | logging.debug('%s attribute missing from servo_args: %s', |
| 409 | SERVO_HOST_ATTR, servo_args) |
| 410 | return None |
Richard Barnette | 07c2e1d | 2016-10-26 14:24:28 -0700 | [diff] [blame] | 411 | if (not servo_dependency and not try_servo_repair and |
Richard Barnette | 9a26ad6 | 2016-06-10 12:03:08 -0700 | [diff] [blame] | 412 | not servo_host_is_up(servo_args[SERVO_HOST_ATTR])): |
Prathmesh Prabhu | 88bf605 | 2018-08-28 16:21:26 -0700 | [diff] [blame] | 413 | logging.debug('ServoHost is not up.') |
Dan Shi | bbb0cb6 | 2014-03-24 17:50:57 -0700 | [diff] [blame] | 414 | return None |
Prathmesh Prabhu | 88bf605 | 2018-08-28 16:21:26 -0700 | [diff] [blame] | 415 | |
Garry Wang | ebc015b | 2019-06-06 17:45:06 -0700 | [diff] [blame] | 416 | newhost = ServoHost(**servo_args) |
| 417 | |
Richard Barnette | 9a26ad6 | 2016-06-10 12:03:08 -0700 | [diff] [blame] | 418 | # Note that the logic of repair() includes everything done |
| 419 | # by verify(). It's sufficient to call one or the other; |
| 420 | # we don't need both. |
Richard Barnette | 07c2e1d | 2016-10-26 14:24:28 -0700 | [diff] [blame] | 421 | if servo_dependency: |
| 422 | newhost.repair(silent=True) |
Prathmesh Prabhu | 88bf605 | 2018-08-28 16:21:26 -0700 | [diff] [blame] | 423 | return newhost |
| 424 | |
| 425 | if try_servo_repair: |
| 426 | try: |
| 427 | newhost.repair() |
| 428 | except Exception: |
| 429 | logging.exception('servo repair failed for %s', newhost.hostname) |
Richard Barnette | 9a26ad6 | 2016-06-10 12:03:08 -0700 | [diff] [blame] | 430 | else: |
| 431 | try: |
Prathmesh Prabhu | 88bf605 | 2018-08-28 16:21:26 -0700 | [diff] [blame] | 432 | newhost.verify() |
Kevin Cheng | 5f2ba6c | 2016-09-28 10:20:05 -0700 | [diff] [blame] | 433 | except Exception: |
Prathmesh Prabhu | 88bf605 | 2018-08-28 16:21:26 -0700 | [diff] [blame] | 434 | logging.exception('servo verify failed for %s', newhost.hostname) |
Richard Barnette | 9a26ad6 | 2016-06-10 12:03:08 -0700 | [diff] [blame] | 435 | return newhost |