blob: 860f69f35aa7cc56ce72c4fd5e6bd60cb0d5a20f [file] [log] [blame]
Fang Deng5d518f42013-08-02 14:04:32 -07001# 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 Deng5d518f42013-08-02 14:04:32 -070012import logging
Raul E Rangel52ca2e82018-07-03 14:10:14 -060013import os
Gregory Nisbet265a52c2019-12-10 20:38:42 -080014import traceback
Dana Goyette4dc0adc2019-05-06 14:51:53 -070015import xmlrpclib
Fang Deng5d518f42013-08-02 14:04:32 -070016
17from autotest_lib.client.bin import utils
Garry Wang79e9af62019-06-12 15:19:19 -070018from autotest_lib.client.common_lib import error
beeps5e8c45a2013-12-17 22:05:11 -080019from autotest_lib.client.common_lib import global_config
Richard Barnette9a26ad62016-06-10 12:03:08 -070020from autotest_lib.client.common_lib import hosts
Fang Deng5d518f42013-08-02 14:04:32 -070021from autotest_lib.client.common_lib.cros import retry
Christopher Wileycef1f902014-06-19 11:11:23 -070022from autotest_lib.client.common_lib.cros.network import ping_runner
Richard Barnette9a26ad62016-06-10 12:03:08 -070023from autotest_lib.server.cros.servo import servo
Richard Barnetted31580e2018-05-14 19:58:00 +000024from autotest_lib.server.hosts import servo_repair
Garry Wangebc015b2019-06-06 17:45:06 -070025from autotest_lib.server.hosts import base_servohost
Dan Shi5e2efb72017-02-07 11:40:23 -080026
Fang Deng5d518f42013-08-02 14:04:32 -070027
Simran Basi0739d682015-02-25 16:22:56 -080028# Names of the host attributes in the database that represent the values for
29# the servo_host and servo_port for a servo connected to the DUT.
30SERVO_HOST_ATTR = 'servo_host'
31SERVO_PORT_ATTR = 'servo_port'
Richard Barnettee519dcd2016-08-15 17:37:17 -070032SERVO_BOARD_ATTR = 'servo_board'
Nick Sanders2f3c9852018-10-24 12:10:24 -070033# Model is inferred from host labels.
34SERVO_MODEL_ATTR = 'servo_model'
Kevin Cheng643ce8a2016-09-15 15:42:12 -070035SERVO_SERIAL_ATTR = 'servo_serial'
Prathmesh Prabhucba44292018-08-28 17:44:45 -070036SERVO_ATTR_KEYS = (
37 SERVO_BOARD_ATTR,
38 SERVO_HOST_ATTR,
39 SERVO_PORT_ATTR,
40 SERVO_SERIAL_ATTR,
41)
Simran Basi0739d682015-02-25 16:22:56 -080042
Dan Shi3b2adf62015-09-02 17:46:54 -070043_CONFIG = global_config.global_config
xixuan6cf6d2f2016-01-29 15:29:00 -080044ENABLE_SSH_TUNNEL_FOR_SERVO = _CONFIG.get_config_value(
45 'CROS', 'enable_ssh_tunnel_for_servo', type=bool, default=False)
Simran Basi0739d682015-02-25 16:22:56 -080046
Kevin Cheng5f2ba6c2016-09-28 10:20:05 -070047AUTOTEST_BASE = _CONFIG.get_config_value(
48 'SCHEDULER', 'drone_installation_directory',
49 default='/usr/local/autotest')
50
Fang Deng5d518f42013-08-02 14:04:32 -070051
Garry Wangebc015b2019-06-06 17:45:06 -070052class ServoHost(base_servohost.BaseServoHost):
53 """Host class for a servo host(e.g. beaglebone, labstation)
Dana Goyette0b6e6402019-10-04 11:09:24 -070054 that with a servo instance for a specific port.
55
56 @type _servo: servo.Servo | None
57 """
Fang Deng5d518f42013-08-02 14:04:32 -070058
Raul E Rangel52ca2e82018-07-03 14:10:14 -060059 DEFAULT_PORT = int(os.getenv('SERVOD_PORT', '9999'))
Richard Barnette9a26ad62016-06-10 12:03:08 -070060
Dan Shie5b3c512014-08-21 12:12:09 -070061 # Timeout for initializing servo signals.
Wai-Hong Tam37b6ed32017-09-19 15:52:39 -070062 INITIALIZE_SERVO_TIMEOUT_SECS = 60
Richard Barnette9a26ad62016-06-10 12:03:08 -070063
xixuan6cf6d2f2016-01-29 15:29:00 -080064 # Ready test function
65 SERVO_READY_METHOD = 'get_version'
Fang Deng5d518f42013-08-02 14:04:32 -070066
67
Richard Barnette17bfc6c2016-08-04 18:41:43 -070068 def _initialize(self, servo_host='localhost',
Richard Barnettee519dcd2016-08-15 17:37:17 -070069 servo_port=DEFAULT_PORT, servo_board=None,
Nick Sanders2f3c9852018-10-24 12:10:24 -070070 servo_model=None, servo_serial=None, is_in_lab=None,
71 *args, **dargs):
Fang Deng5d518f42013-08-02 14:04:32 -070072 """Initialize a ServoHost instance.
73
74 A ServoHost instance represents a host that controls a servo.
75
76 @param servo_host: Name of the host where the servod process
77 is running.
Raul E Rangel52ca2e82018-07-03 14:10:14 -060078 @param servo_port: Port the servod process is listening on. Defaults
79 to the SERVOD_PORT environment variable if set,
80 otherwise 9999.
Kevin Cheng5f2ba6c2016-09-28 10:20:05 -070081 @param servo_board: Board that the servo is connected to.
Nick Sanders2f3c9852018-10-24 12:10:24 -070082 @param servo_model: Model that the servo is connected to.
Dan Shi4d478522014-02-14 13:46:32 -080083 @param is_in_lab: True if the servo host is in Cros Lab. Default is set
84 to None, for which utils.host_is_in_lab_zone will be
85 called to check if the servo host is in Cros lab.
Fang Deng5d518f42013-08-02 14:04:32 -070086
87 """
88 super(ServoHost, self)._initialize(hostname=servo_host,
Garry Wangebc015b2019-06-06 17:45:06 -070089 is_in_lab=is_in_lab, *args, **dargs)
Richard Barnette42f4db92018-08-23 15:05:15 -070090 self.servo_port = int(servo_port)
Richard Barnettee519dcd2016-08-15 17:37:17 -070091 self.servo_board = servo_board
Nick Sanders2f3c9852018-10-24 12:10:24 -070092 self.servo_model = servo_model
Kevin Cheng643ce8a2016-09-15 15:42:12 -070093 self.servo_serial = servo_serial
Richard Barnettee519dcd2016-08-15 17:37:17 -070094 self._servo = None
Wai-Hong Tam3a8a2552019-11-19 14:28:04 +080095 self._servod_server_proxy = None
96
Garry Wang79e9af62019-06-12 15:19:19 -070097 # Path of the servo host lock file.
98 self._lock_file = (self.TEMP_FILE_DIR + str(self.servo_port)
99 + self.LOCK_FILE_POSTFIX)
100 # File path to declare a reboot request.
101 self._reboot_file = (self.TEMP_FILE_DIR + str(self.servo_port)
102 + self.REBOOT_FILE_POSTFIX)
103
104 # Lock the servo host if it's an in-lab labstation to prevent other
105 # task to reboot it until current task completes. We also wait and
106 # make sure the labstation is up here, in the case of the labstation is
107 # in the middle of reboot.
Garry Wang7c00b0f2019-06-25 17:28:17 -0700108 self._is_locked = False
Garry Wang42b4d862019-06-25 15:50:49 -0700109 if (self.wait_up(self.REBOOT_TIMEOUT) and self.is_in_lab()
110 and self.is_labstation()):
Garry Wang79e9af62019-06-12 15:19:19 -0700111 self._lock()
Garry Wangebc015b2019-06-06 17:45:06 -0700112
Richard Barnette9a26ad62016-06-10 12:03:08 -0700113 self._repair_strategy = (
114 servo_repair.create_servo_repair_strategy())
Richard Barnettee519dcd2016-08-15 17:37:17 -0700115
Richard Barnette9a26ad62016-06-10 12:03:08 -0700116 def connect_servo(self):
Kevin Cheng5f2ba6c2016-09-28 10:20:05 -0700117 """Establish a connection to the servod server on this host.
Richard Barnette9a26ad62016-06-10 12:03:08 -0700118
119 Initializes `self._servo` and then verifies that all network
120 connections are working. This will create an ssh tunnel if
121 it's required.
122
123 As a side effect of testing the connection, all signals on the
124 target servo are reset to default values, and the USB stick is
125 set to the neutral (off) position.
126 """
Kevin Cheng643ce8a2016-09-15 15:42:12 -0700127 servo_obj = servo.Servo(servo_host=self, servo_serial=self.servo_serial)
Kuang-che Wu05763f52019-08-30 16:48:21 +0800128 self._servo = servo_obj
Richard Barnette9a26ad62016-06-10 12:03:08 -0700129 timeout, _ = retry.timeout(
130 servo_obj.initialize_dut,
131 timeout_sec=self.INITIALIZE_SERVO_TIMEOUT_SECS)
132 if timeout:
133 raise hosts.AutoservVerifyError(
134 'Servo initialize timed out.')
Richard Barnette9a26ad62016-06-10 12:03:08 -0700135
136
137 def disconnect_servo(self):
Kevin Cheng5f2ba6c2016-09-28 10:20:05 -0700138 """Disconnect our servo if it exists.
Richard Barnette9a26ad62016-06-10 12:03:08 -0700139
140 If we've previously successfully connected to our servo,
141 disconnect any established ssh tunnel, and set `self._servo`
142 back to `None`.
143 """
144 if self._servo:
145 # N.B. This call is safe even without a tunnel:
146 # rpc_server_tracker.disconnect() silently ignores
147 # unknown ports.
148 self.rpc_server_tracker.disconnect(self.servo_port)
149 self._servo = None
Fang Deng5d518f42013-08-02 14:04:32 -0700150
Wai-Hong Tam3a8a2552019-11-19 14:28:04 +0800151 def _create_servod_server_proxy(self):
152 """Create a proxy that can be used to communicate with servod server.
Fang Deng5d518f42013-08-02 14:04:32 -0700153
154 @returns: An xmlrpclib.ServerProxy that is connected to the servod
155 server on the host.
Fang Deng5d518f42013-08-02 14:04:32 -0700156 """
Richard Barnette9a26ad62016-06-10 12:03:08 -0700157 if ENABLE_SSH_TUNNEL_FOR_SERVO and not self.is_localhost():
158 return self.rpc_server_tracker.xmlrpc_connect(
159 None, self.servo_port,
160 ready_test_name=self.SERVO_READY_METHOD,
Allen Li2b1a8992018-11-27 14:17:18 -0800161 timeout_seconds=60,
Allen Li556f4532018-12-03 18:11:23 -0800162 request_timeout_seconds=3600)
Richard Barnette9a26ad62016-06-10 12:03:08 -0700163 else:
164 remote = 'http://%s:%s' % (self.hostname, self.servo_port)
165 return xmlrpclib.ServerProxy(remote)
Fang Deng5d518f42013-08-02 14:04:32 -0700166
167
Wai-Hong Tam3a8a2552019-11-19 14:28:04 +0800168 def get_servod_server_proxy(self):
169 """Return a cached proxy if exists; otherwise, create a new one.
170
171 @returns: An xmlrpclib.ServerProxy that is connected to the servod
172 server on the host.
173 """
174 # Single-threaded execution, no race
175 if self._servod_server_proxy is None:
176 self._servod_server_proxy = self._create_servod_server_proxy()
177 return self._servod_server_proxy
178
179
Richard Barnette1edbb162016-11-01 11:47:50 -0700180 def verify(self, silent=False):
181 """Update the servo host and verify it's in a good state.
182
183 @param silent If true, suppress logging in `status.log`.
184 """
Richard Barnetteabbdc252018-07-26 16:57:42 -0700185 message = 'Beginning verify for servo host %s port %s serial %s'
186 message %= (self.hostname, self.servo_port, self.servo_serial)
187 self.record('INFO', None, None, message)
Richard Barnette9a26ad62016-06-10 12:03:08 -0700188 try:
Richard Barnette1edbb162016-11-01 11:47:50 -0700189 self._repair_strategy.verify(self, silent)
Richard Barnette9a26ad62016-06-10 12:03:08 -0700190 except:
191 self.disconnect_servo()
192 raise
Fang Deng5d518f42013-08-02 14:04:32 -0700193
194
Richard Barnette1edbb162016-11-01 11:47:50 -0700195 def repair(self, silent=False):
196 """Attempt to repair servo host.
197
198 @param silent If true, suppress logging in `status.log`.
199 """
Richard Barnetteabbdc252018-07-26 16:57:42 -0700200 message = 'Beginning repair for servo host %s port %s serial %s'
201 message %= (self.hostname, self.servo_port, self.servo_serial)
202 self.record('INFO', None, None, message)
Richard Barnette9a26ad62016-06-10 12:03:08 -0700203 try:
Richard Barnette1edbb162016-11-01 11:47:50 -0700204 self._repair_strategy.repair(self, silent)
Garry Wang464ff1e2019-07-18 17:20:34 -0700205 # If target is a labstation then try to withdraw any existing
206 # reboot request created by this servo because it passed repair.
207 if self.is_labstation():
208 self.withdraw_reboot_request()
Richard Barnette9a26ad62016-06-10 12:03:08 -0700209 except:
210 self.disconnect_servo()
211 raise
Fang Deng5d518f42013-08-02 14:04:32 -0700212
213
Dan Shi4d478522014-02-14 13:46:32 -0800214 def get_servo(self):
215 """Get the cached servo.Servo object.
Fang Deng5d518f42013-08-02 14:04:32 -0700216
Dan Shi4d478522014-02-14 13:46:32 -0800217 @return: a servo.Servo object.
Dana Goyette353d1d92019-06-27 10:43:59 -0700218 @rtype: autotest_lib.server.cros.servo.servo.Servo
Fang Deng5d518f42013-08-02 14:04:32 -0700219 """
Dan Shi4d478522014-02-14 13:46:32 -0800220 return self._servo
221
222
Garry Wang79e9af62019-06-12 15:19:19 -0700223 def request_reboot(self):
224 """Request servohost to be rebooted when it's safe to by touch a file.
225 """
226 logging.debug('Request to reboot servohost %s has been created by '
Garry Wang464ff1e2019-07-18 17:20:34 -0700227 'servo with port # %s', self.hostname, self.servo_port)
Garry Wang79e9af62019-06-12 15:19:19 -0700228 self.run('touch %s' % self._reboot_file, ignore_status=True)
229
230
Garry Wang464ff1e2019-07-18 17:20:34 -0700231 def withdraw_reboot_request(self):
232 """Withdraw a servohost reboot request if exists by remove the flag
233 file.
234 """
235 logging.debug('Withdrawing request to reboot servohost %s that created'
236 ' by servo with port # %s if exists.',
237 self.hostname, self.servo_port)
238 self.run('rm -f %s' % self._reboot_file, ignore_status=True)
239
240
Garry Wang79e9af62019-06-12 15:19:19 -0700241 def _lock(self):
242 """lock servohost by touching a file.
243 """
244 logging.debug('Locking servohost %s by touching %s file',
245 self.hostname, self._lock_file)
246 self.run('touch %s' % self._lock_file, ignore_status=True)
Garry Wang7c00b0f2019-06-25 17:28:17 -0700247 self._is_locked = True
Garry Wang79e9af62019-06-12 15:19:19 -0700248
249
250 def _unlock(self):
251 """Unlock servohost by removing the lock file.
252 """
253 logging.debug('Unlocking servohost by removing %s file',
254 self._lock_file)
255 self.run('rm %s' % self._lock_file, ignore_status=True)
Garry Wang7c00b0f2019-06-25 17:28:17 -0700256 self._is_locked = False
Garry Wang79e9af62019-06-12 15:19:19 -0700257
258
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700259 def close(self):
Congbin Guofc3b8962019-03-22 17:38:46 -0700260 """Close the associated servo and the host object."""
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700261 if self._servo:
Congbin Guo2e5e2a22018-07-27 10:32:48 -0700262 # In some cases when we run as lab-tools, the job object is None.
Congbin Guofc3b8962019-03-22 17:38:46 -0700263 if self.job and not self._servo.uart_logs_dir:
264 self._servo.uart_logs_dir = self.job.resultdir
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700265 self._servo.close()
266
Garry Wang7c00b0f2019-06-25 17:28:17 -0700267 if self._is_locked:
268 # Remove the lock if the servohost has been locked.
Garry Wang79e9af62019-06-12 15:19:19 -0700269 try:
270 self._unlock()
271 except error.AutoservSSHTimeout:
272 logging.error('Unlock servohost failed due to ssh timeout.'
273 ' It may caused by servohost went down during'
274 ' the task.')
275
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700276 super(ServoHost, self).close()
277
278
Richard Barnetteea3e4602016-06-10 12:36:41 -0700279def make_servo_hostname(dut_hostname):
280 """Given a DUT's hostname, return the hostname of its servo.
281
282 @param dut_hostname: hostname of a DUT.
283
284 @return hostname of the DUT's servo.
285
286 """
287 host_parts = dut_hostname.split('.')
288 host_parts[0] = host_parts[0] + '-servo'
289 return '.'.join(host_parts)
290
291
292def servo_host_is_up(servo_hostname):
Kevin Cheng5f2ba6c2016-09-28 10:20:05 -0700293 """Given a servo host name, return if it's up or not.
Richard Barnetteea3e4602016-06-10 12:36:41 -0700294
295 @param servo_hostname: hostname of the servo host.
296
297 @return True if it's up, False otherwise
298 """
299 # Technically, this duplicates the SSH ping done early in the servo
300 # proxy initialization code. However, this ping ends in a couple
301 # seconds when if fails, rather than the 60 seconds it takes to decide
302 # that an SSH ping has timed out. Specifically, that timeout happens
303 # when our servo DNS name resolves, but there is no host at that IP.
304 logging.info('Pinging servo host at %s', servo_hostname)
305 ping_config = ping_runner.PingConfig(
306 servo_hostname, count=3,
307 ignore_result=True, ignore_status=True)
308 return ping_runner.PingRunner().ping(ping_config).received > 0
309
310
Richard Barnettee519dcd2016-08-15 17:37:17 -0700311def _map_afe_board_to_servo_board(afe_board):
312 """Map a board we get from the AFE to a servo appropriate value.
313
314 Many boards are identical to other boards for servo's purposes.
315 This function makes that mapping.
316
317 @param afe_board string board name received from AFE.
318 @return board we expect servo to have.
319
320 """
321 KNOWN_SUFFIXES = ['-freon', '_freon', '_moblab', '-cheets']
322 BOARD_MAP = {'gizmo': 'panther'}
323 mapped_board = afe_board
324 if afe_board in BOARD_MAP:
325 mapped_board = BOARD_MAP[afe_board]
326 else:
327 for suffix in KNOWN_SUFFIXES:
328 if afe_board.endswith(suffix):
329 mapped_board = afe_board[0:-len(suffix)]
330 break
331 if mapped_board != afe_board:
332 logging.info('Mapping AFE board=%s to %s', afe_board, mapped_board)
333 return mapped_board
334
335
Prathmesh Prabhub4810232018-09-07 13:24:08 -0700336def get_servo_args_for_host(dut_host):
Kevin Cheng5f2ba6c2016-09-28 10:20:05 -0700337 """Return servo data associated with a given DUT.
Richard Barnetteea3e4602016-06-10 12:36:41 -0700338
Richard Barnetteea3e4602016-06-10 12:36:41 -0700339 @param dut_host Instance of `Host` on which to find the servo
340 attributes.
Prathmesh Prabhuf605dd32018-08-28 17:09:04 -0700341 @return `servo_args` dict with host and an optional port.
Richard Barnetteea3e4602016-06-10 12:36:41 -0700342 """
Prathmesh Prabhucba44292018-08-28 17:44:45 -0700343 info = dut_host.host_info_store.get()
344 servo_args = {k: v for k, v in info.attributes.iteritems()
345 if k in SERVO_ATTR_KEYS}
Richard Barnetteea3e4602016-06-10 12:36:41 -0700346
Prathmesh Prabhucba44292018-08-28 17:44:45 -0700347 if SERVO_PORT_ATTR in servo_args:
348 try:
349 servo_args[SERVO_PORT_ATTR] = int(servo_args[SERVO_PORT_ATTR])
350 except ValueError:
351 logging.error('servo port is not an int: %s',
352 servo_args[SERVO_PORT_ATTR])
353 # Reset servo_args because we don't want to use an invalid port.
354 servo_args.pop(SERVO_HOST_ATTR, None)
355
356 if info.board:
357 servo_args[SERVO_BOARD_ATTR] = _map_afe_board_to_servo_board(info.board)
Nick Sanders2f3c9852018-10-24 12:10:24 -0700358 if info.model:
359 servo_args[SERVO_MODEL_ATTR] = info.model
Prathmesh Prabhu6f5f6362018-09-05 17:20:31 -0700360 return servo_args if SERVO_HOST_ATTR in servo_args else None
Richard Barnetteea3e4602016-06-10 12:36:41 -0700361
362
Prathmesh Prabhuefb1b482018-08-28 17:15:05 -0700363def _tweak_args_for_ssp_moblab(servo_args):
364 if servo_args[SERVO_HOST_ATTR] in ['localhost', '127.0.0.1']:
365 servo_args[SERVO_HOST_ATTR] = _CONFIG.get_config_value(
366 'SSP', 'host_container_ip', type=str, default=None)
367
368
Dan Shi023aae32016-05-25 11:13:01 -0700369def create_servo_host(dut, servo_args, try_lab_servo=False,
Gregory Nisbetde13e2a2019-12-09 22:44:00 -0800370 try_servo_repair=False, dut_host_info=None):
Kevin Cheng5f2ba6c2016-09-28 10:20:05 -0700371 """Create a ServoHost object for a given DUT, if appropriate.
Dan Shi4d478522014-02-14 13:46:32 -0800372
Richard Barnette9a26ad62016-06-10 12:03:08 -0700373 This function attempts to create and verify or repair a `ServoHost`
374 object for a servo connected to the given `dut`, subject to various
375 constraints imposed by the parameters:
376 * When the `servo_args` parameter is not `None`, a servo
377 host must be created, and must be checked with `repair()`.
378 * Otherwise, if a servo exists in the lab and `try_lab_servo` is
379 true:
380 * If `try_servo_repair` is true, then create a servo host and
381 check it with `repair()`.
382 * Otherwise, if the servo responds to `ping` then create a
383 servo host and check it with `verify()`.
Fang Denge545abb2014-12-30 18:43:47 -0800384
Richard Barnette9a26ad62016-06-10 12:03:08 -0700385 In cases where `servo_args` was not `None`, repair failure
386 exceptions are passed back to the caller; otherwise, exceptions
Richard Barnette07c2e1d2016-10-26 14:24:28 -0700387 are logged and then discarded. Note that this only happens in cases
388 where we're called from a test (not special task) control file that
389 has an explicit dependency on servo. In that case, we require that
390 repair not write to `status.log`, so as to avoid polluting test
391 results.
392
393 TODO(jrbarnette): The special handling for servo in test control
394 files is a thorn in my flesh; I dearly hope to see it cut out before
395 my retirement.
Richard Barnette9a26ad62016-06-10 12:03:08 -0700396
397 Parameters for a servo host consist of a host name, port number, and
398 DUT board, and are determined from one of these sources, in order of
399 priority:
Richard Barnetteea3e4602016-06-10 12:36:41 -0700400 * Servo attributes from the `dut` parameter take precedence over
401 all other sources of information.
402 * If a DNS entry for the servo based on the DUT hostname exists in
403 the CrOS lab network, that hostname is used with the default
Richard Barnette9a26ad62016-06-10 12:03:08 -0700404 port and the DUT's board.
Richard Barnetteea3e4602016-06-10 12:36:41 -0700405 * If no other options are found, the parameters will be taken
Richard Barnette9a26ad62016-06-10 12:03:08 -0700406 from the `servo_args` dict passed in from the caller.
Richard Barnetteea3e4602016-06-10 12:36:41 -0700407
408 @param dut An instance of `Host` from which to take
409 servo parameters (if available).
410 @param servo_args A dictionary with servo parameters to use if
411 they can't be found from `dut`. If this
412 argument is supplied, unrepaired exceptions
413 from `verify()` will be passed back to the
414 caller.
415 @param try_lab_servo If not true, servo host creation will be
416 skipped unless otherwise required by the
417 caller.
Richard Barnette9a26ad62016-06-10 12:03:08 -0700418 @param try_servo_repair If true, check a servo host with
419 `repair()` instead of `verify()`.
Dan Shi4d478522014-02-14 13:46:32 -0800420
421 @returns: A ServoHost object or None. See comments above.
422
423 """
Richard Barnette07c2e1d2016-10-26 14:24:28 -0700424 servo_dependency = servo_args is not None
Richard Barnette07c2e1d2016-10-26 14:24:28 -0700425 if dut is not None and (try_lab_servo or servo_dependency):
Prathmesh Prabhub4810232018-09-07 13:24:08 -0700426 servo_args_override = get_servo_args_for_host(dut)
Richard Barnetteea3e4602016-06-10 12:36:41 -0700427 if servo_args_override is not None:
Prathmesh Prabhuefb1b482018-08-28 17:15:05 -0700428 if utils.in_moblab_ssp():
429 _tweak_args_for_ssp_moblab(servo_args_override)
Prathmesh Prabhu88bf6052018-08-28 16:21:26 -0700430 logging.debug(
431 'Overriding provided servo_args (%s) with arguments'
432 ' determined from the host (%s)',
433 servo_args,
434 servo_args_override,
435 )
Richard Barnetteea3e4602016-06-10 12:36:41 -0700436 servo_args = servo_args_override
Prathmesh Prabhucba44292018-08-28 17:44:45 -0700437
Richard Barnetteea3e4602016-06-10 12:36:41 -0700438 if servo_args is None:
Prathmesh Prabhu88bf6052018-08-28 16:21:26 -0700439 logging.debug('No servo_args provided, and failed to find overrides.')
Richard Barnetteea3e4602016-06-10 12:36:41 -0700440 return None
Prathmesh Prabhucba44292018-08-28 17:44:45 -0700441 if SERVO_HOST_ATTR not in servo_args:
442 logging.debug('%s attribute missing from servo_args: %s',
443 SERVO_HOST_ATTR, servo_args)
444 return None
Richard Barnette07c2e1d2016-10-26 14:24:28 -0700445 if (not servo_dependency and not try_servo_repair and
Richard Barnette9a26ad62016-06-10 12:03:08 -0700446 not servo_host_is_up(servo_args[SERVO_HOST_ATTR])):
Prathmesh Prabhu88bf6052018-08-28 16:21:26 -0700447 logging.debug('ServoHost is not up.')
Dan Shibbb0cb62014-03-24 17:50:57 -0700448 return None
Prathmesh Prabhu88bf6052018-08-28 16:21:26 -0700449
Garry Wangebc015b2019-06-06 17:45:06 -0700450 newhost = ServoHost(**servo_args)
451
Gregory Nisbetde13e2a2019-12-09 22:44:00 -0800452 # TODO(gregorynisbet): Clean all of this up.
453 logging.debug('create_servo_host: attempt to set info store on '
454 'servo host')
455 try:
456 if dut_host_info is None:
457 logging.debug('create_servo_host: dut_host_info is '
458 'None, skipping')
459 else:
460 newhost.set_dut_host_info(dut_host_info)
461 logging.debug('create_servo_host: successfully set info '
462 'store')
463 except Exception:
464 logging.error("create_servo_host: (%s)", traceback.format_exc())
465
Richard Barnette9a26ad62016-06-10 12:03:08 -0700466 # Note that the logic of repair() includes everything done
467 # by verify(). It's sufficient to call one or the other;
468 # we don't need both.
Richard Barnette07c2e1d2016-10-26 14:24:28 -0700469 if servo_dependency:
470 newhost.repair(silent=True)
Prathmesh Prabhu88bf6052018-08-28 16:21:26 -0700471 return newhost
472
473 if try_servo_repair:
474 try:
475 newhost.repair()
476 except Exception:
477 logging.exception('servo repair failed for %s', newhost.hostname)
Richard Barnette9a26ad62016-06-10 12:03:08 -0700478 else:
479 try:
Prathmesh Prabhu88bf6052018-08-28 16:21:26 -0700480 newhost.verify()
Kevin Cheng5f2ba6c2016-09-28 10:20:05 -0700481 except Exception:
Prathmesh Prabhu88bf6052018-08-28 16:21:26 -0700482 logging.exception('servo verify failed for %s', newhost.hostname)
Richard Barnette9a26ad62016-06-10 12:03:08 -0700483 return newhost