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