blob: 06a354adc30c3a248718521a1b56d3fb36a87bfe [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
Dana Goyette4dc0adc2019-05-06 14:51:53 -070014import xmlrpclib
Fang Deng5d518f42013-08-02 14:04:32 -070015
16from autotest_lib.client.bin import utils
Garry Wang79e9af62019-06-12 15:19:19 -070017from autotest_lib.client.common_lib import error
beeps5e8c45a2013-12-17 22:05:11 -080018from autotest_lib.client.common_lib import global_config
Richard Barnette9a26ad62016-06-10 12:03:08 -070019from autotest_lib.client.common_lib import hosts
Fang Deng5d518f42013-08-02 14:04:32 -070020from autotest_lib.client.common_lib.cros import retry
Christopher Wileycef1f902014-06-19 11:11:23 -070021from autotest_lib.client.common_lib.cros.network import ping_runner
Richard Barnette9a26ad62016-06-10 12:03:08 -070022from autotest_lib.server.cros.servo import servo
Richard Barnetted31580e2018-05-14 19:58:00 +000023from autotest_lib.server.hosts import servo_repair
Garry Wangebc015b2019-06-06 17:45:06 -070024from autotest_lib.server.hosts import base_servohost
Dan Shi5e2efb72017-02-07 11:40:23 -080025
Fang Deng5d518f42013-08-02 14:04:32 -070026
Simran Basi0739d682015-02-25 16:22:56 -080027# 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.
29SERVO_HOST_ATTR = 'servo_host'
30SERVO_PORT_ATTR = 'servo_port'
Richard Barnettee519dcd2016-08-15 17:37:17 -070031SERVO_BOARD_ATTR = 'servo_board'
Nick Sanders2f3c9852018-10-24 12:10:24 -070032# Model is inferred from host labels.
33SERVO_MODEL_ATTR = 'servo_model'
Kevin Cheng643ce8a2016-09-15 15:42:12 -070034SERVO_SERIAL_ATTR = 'servo_serial'
Prathmesh Prabhucba44292018-08-28 17:44:45 -070035SERVO_ATTR_KEYS = (
36 SERVO_BOARD_ATTR,
37 SERVO_HOST_ATTR,
38 SERVO_PORT_ATTR,
39 SERVO_SERIAL_ATTR,
40)
Simran Basi0739d682015-02-25 16:22:56 -080041
Dan Shi3b2adf62015-09-02 17:46:54 -070042_CONFIG = global_config.global_config
xixuan6cf6d2f2016-01-29 15:29:00 -080043ENABLE_SSH_TUNNEL_FOR_SERVO = _CONFIG.get_config_value(
44 'CROS', 'enable_ssh_tunnel_for_servo', type=bool, default=False)
Simran Basi0739d682015-02-25 16:22:56 -080045
Kevin Cheng5f2ba6c2016-09-28 10:20:05 -070046AUTOTEST_BASE = _CONFIG.get_config_value(
47 'SCHEDULER', 'drone_installation_directory',
48 default='/usr/local/autotest')
49
Fang Deng5d518f42013-08-02 14:04:32 -070050
Garry Wangebc015b2019-06-06 17:45:06 -070051class ServoHost(base_servohost.BaseServoHost):
52 """Host class for a servo host(e.g. beaglebone, labstation)
Dana Goyette0b6e6402019-10-04 11:09:24 -070053 that with a servo instance for a specific port.
54
55 @type _servo: servo.Servo | None
56 """
Fang Deng5d518f42013-08-02 14:04:32 -070057
Raul E Rangel52ca2e82018-07-03 14:10:14 -060058 DEFAULT_PORT = int(os.getenv('SERVOD_PORT', '9999'))
Richard Barnette9a26ad62016-06-10 12:03:08 -070059
Dan Shie5b3c512014-08-21 12:12:09 -070060 # Timeout for initializing servo signals.
Wai-Hong Tam37b6ed32017-09-19 15:52:39 -070061 INITIALIZE_SERVO_TIMEOUT_SECS = 60
Richard Barnette9a26ad62016-06-10 12:03:08 -070062
xixuan6cf6d2f2016-01-29 15:29:00 -080063 # Ready test function
64 SERVO_READY_METHOD = 'get_version'
Fang Deng5d518f42013-08-02 14:04:32 -070065
66
Richard Barnette17bfc6c2016-08-04 18:41:43 -070067 def _initialize(self, servo_host='localhost',
Richard Barnettee519dcd2016-08-15 17:37:17 -070068 servo_port=DEFAULT_PORT, servo_board=None,
Nick Sanders2f3c9852018-10-24 12:10:24 -070069 servo_model=None, servo_serial=None, is_in_lab=None,
70 *args, **dargs):
Fang Deng5d518f42013-08-02 14:04:32 -070071 """Initialize a ServoHost instance.
72
73 A ServoHost instance represents a host that controls a servo.
74
75 @param servo_host: Name of the host where the servod process
76 is running.
Raul E Rangel52ca2e82018-07-03 14:10:14 -060077 @param servo_port: Port the servod process is listening on. Defaults
78 to the SERVOD_PORT environment variable if set,
79 otherwise 9999.
Kevin Cheng5f2ba6c2016-09-28 10:20:05 -070080 @param servo_board: Board that the servo is connected to.
Nick Sanders2f3c9852018-10-24 12:10:24 -070081 @param servo_model: Model that the servo is connected to.
Dan Shi4d478522014-02-14 13:46:32 -080082 @param is_in_lab: True if the servo host is in Cros Lab. Default is set
83 to None, for which utils.host_is_in_lab_zone will be
84 called to check if the servo host is in Cros lab.
Fang Deng5d518f42013-08-02 14:04:32 -070085
86 """
87 super(ServoHost, self)._initialize(hostname=servo_host,
Garry Wangebc015b2019-06-06 17:45:06 -070088 is_in_lab=is_in_lab, *args, **dargs)
Richard Barnette42f4db92018-08-23 15:05:15 -070089 self.servo_port = int(servo_port)
Richard Barnettee519dcd2016-08-15 17:37:17 -070090 self.servo_board = servo_board
Nick Sanders2f3c9852018-10-24 12:10:24 -070091 self.servo_model = servo_model
Kevin Cheng643ce8a2016-09-15 15:42:12 -070092 self.servo_serial = servo_serial
Richard Barnettee519dcd2016-08-15 17:37:17 -070093 self._servo = None
Wai-Hong Tam3a8a2552019-11-19 14:28:04 +080094 self._servod_server_proxy = None
95
Garry Wang79e9af62019-06-12 15:19:19 -070096 # Path of the servo host lock file.
97 self._lock_file = (self.TEMP_FILE_DIR + str(self.servo_port)
98 + self.LOCK_FILE_POSTFIX)
99 # File path to declare a reboot request.
100 self._reboot_file = (self.TEMP_FILE_DIR + str(self.servo_port)
101 + self.REBOOT_FILE_POSTFIX)
102
103 # Lock the servo host if it's an in-lab labstation to prevent other
104 # task to reboot it until current task completes. We also wait and
105 # make sure the labstation is up here, in the case of the labstation is
106 # in the middle of reboot.
Garry Wang7c00b0f2019-06-25 17:28:17 -0700107 self._is_locked = False
Garry Wang42b4d862019-06-25 15:50:49 -0700108 if (self.wait_up(self.REBOOT_TIMEOUT) and self.is_in_lab()
109 and self.is_labstation()):
Garry Wang79e9af62019-06-12 15:19:19 -0700110 self._lock()
Garry Wangebc015b2019-06-06 17:45:06 -0700111
Richard Barnette9a26ad62016-06-10 12:03:08 -0700112 self._repair_strategy = (
113 servo_repair.create_servo_repair_strategy())
Richard Barnettee519dcd2016-08-15 17:37:17 -0700114
Richard Barnette9a26ad62016-06-10 12:03:08 -0700115 def connect_servo(self):
Kevin Cheng5f2ba6c2016-09-28 10:20:05 -0700116 """Establish a connection to the servod server on this host.
Richard Barnette9a26ad62016-06-10 12:03:08 -0700117
118 Initializes `self._servo` and then verifies that all network
119 connections are working. This will create an ssh tunnel if
120 it's required.
121
122 As a side effect of testing the connection, all signals on the
123 target servo are reset to default values, and the USB stick is
124 set to the neutral (off) position.
125 """
Kevin Cheng643ce8a2016-09-15 15:42:12 -0700126 servo_obj = servo.Servo(servo_host=self, servo_serial=self.servo_serial)
Kuang-che Wu05763f52019-08-30 16:48:21 +0800127 self._servo = servo_obj
Richard Barnette9a26ad62016-06-10 12:03:08 -0700128 timeout, _ = retry.timeout(
129 servo_obj.initialize_dut,
130 timeout_sec=self.INITIALIZE_SERVO_TIMEOUT_SECS)
131 if timeout:
132 raise hosts.AutoservVerifyError(
133 'Servo initialize timed out.')
Richard Barnette9a26ad62016-06-10 12:03:08 -0700134
135
136 def disconnect_servo(self):
Kevin Cheng5f2ba6c2016-09-28 10:20:05 -0700137 """Disconnect our servo if it exists.
Richard Barnette9a26ad62016-06-10 12:03:08 -0700138
139 If we've previously successfully connected to our servo,
140 disconnect any established ssh tunnel, and set `self._servo`
141 back to `None`.
142 """
143 if self._servo:
144 # N.B. This call is safe even without a tunnel:
145 # rpc_server_tracker.disconnect() silently ignores
146 # unknown ports.
147 self.rpc_server_tracker.disconnect(self.servo_port)
148 self._servo = None
Fang Deng5d518f42013-08-02 14:04:32 -0700149
Wai-Hong Tam3a8a2552019-11-19 14:28:04 +0800150 def _create_servod_server_proxy(self):
151 """Create a proxy that can be used to communicate with servod server.
Fang Deng5d518f42013-08-02 14:04:32 -0700152
153 @returns: An xmlrpclib.ServerProxy that is connected to the servod
154 server on the host.
Fang Deng5d518f42013-08-02 14:04:32 -0700155 """
Richard Barnette9a26ad62016-06-10 12:03:08 -0700156 if ENABLE_SSH_TUNNEL_FOR_SERVO and not self.is_localhost():
157 return self.rpc_server_tracker.xmlrpc_connect(
158 None, self.servo_port,
159 ready_test_name=self.SERVO_READY_METHOD,
Allen Li2b1a8992018-11-27 14:17:18 -0800160 timeout_seconds=60,
Allen Li556f4532018-12-03 18:11:23 -0800161 request_timeout_seconds=3600)
Richard Barnette9a26ad62016-06-10 12:03:08 -0700162 else:
163 remote = 'http://%s:%s' % (self.hostname, self.servo_port)
164 return xmlrpclib.ServerProxy(remote)
Fang Deng5d518f42013-08-02 14:04:32 -0700165
166
Wai-Hong Tam3a8a2552019-11-19 14:28:04 +0800167 def get_servod_server_proxy(self):
168 """Return a cached proxy if exists; otherwise, create a new one.
169
170 @returns: An xmlrpclib.ServerProxy that is connected to the servod
171 server on the host.
172 """
173 # Single-threaded execution, no race
174 if self._servod_server_proxy is None:
175 self._servod_server_proxy = self._create_servod_server_proxy()
176 return self._servod_server_proxy
177
178
Richard Barnette1edbb162016-11-01 11:47:50 -0700179 def verify(self, silent=False):
180 """Update the servo host and verify it's in a good state.
181
182 @param silent If true, suppress logging in `status.log`.
183 """
Richard Barnetteabbdc252018-07-26 16:57:42 -0700184 message = 'Beginning verify for servo host %s port %s serial %s'
185 message %= (self.hostname, self.servo_port, self.servo_serial)
186 self.record('INFO', None, None, message)
Richard Barnette9a26ad62016-06-10 12:03:08 -0700187 try:
Richard Barnette1edbb162016-11-01 11:47:50 -0700188 self._repair_strategy.verify(self, silent)
Richard Barnette9a26ad62016-06-10 12:03:08 -0700189 except:
190 self.disconnect_servo()
191 raise
Fang Deng5d518f42013-08-02 14:04:32 -0700192
193
Richard Barnette1edbb162016-11-01 11:47:50 -0700194 def repair(self, silent=False):
195 """Attempt to repair servo host.
196
197 @param silent If true, suppress logging in `status.log`.
198 """
Richard Barnetteabbdc252018-07-26 16:57:42 -0700199 message = 'Beginning repair for servo host %s port %s serial %s'
200 message %= (self.hostname, self.servo_port, self.servo_serial)
201 self.record('INFO', None, None, message)
Richard Barnette9a26ad62016-06-10 12:03:08 -0700202 try:
Richard Barnette1edbb162016-11-01 11:47:50 -0700203 self._repair_strategy.repair(self, silent)
Garry Wang464ff1e2019-07-18 17:20:34 -0700204 # If target is a labstation then try to withdraw any existing
205 # reboot request created by this servo because it passed repair.
206 if self.is_labstation():
207 self.withdraw_reboot_request()
Richard Barnette9a26ad62016-06-10 12:03:08 -0700208 except:
209 self.disconnect_servo()
210 raise
Fang Deng5d518f42013-08-02 14:04:32 -0700211
212
Dan Shi4d478522014-02-14 13:46:32 -0800213 def get_servo(self):
214 """Get the cached servo.Servo object.
Fang Deng5d518f42013-08-02 14:04:32 -0700215
Dan Shi4d478522014-02-14 13:46:32 -0800216 @return: a servo.Servo object.
Dana Goyette353d1d92019-06-27 10:43:59 -0700217 @rtype: autotest_lib.server.cros.servo.servo.Servo
Fang Deng5d518f42013-08-02 14:04:32 -0700218 """
Dan Shi4d478522014-02-14 13:46:32 -0800219 return self._servo
220
221
Garry Wang79e9af62019-06-12 15:19:19 -0700222 def request_reboot(self):
223 """Request servohost to be rebooted when it's safe to by touch a file.
224 """
225 logging.debug('Request to reboot servohost %s has been created by '
Garry Wang464ff1e2019-07-18 17:20:34 -0700226 'servo with port # %s', self.hostname, self.servo_port)
Garry Wang79e9af62019-06-12 15:19:19 -0700227 self.run('touch %s' % self._reboot_file, ignore_status=True)
228
229
Garry Wang464ff1e2019-07-18 17:20:34 -0700230 def withdraw_reboot_request(self):
231 """Withdraw a servohost reboot request if exists by remove the flag
232 file.
233 """
234 logging.debug('Withdrawing request to reboot servohost %s that created'
235 ' by servo with port # %s if exists.',
236 self.hostname, self.servo_port)
237 self.run('rm -f %s' % self._reboot_file, ignore_status=True)
238
239
Garry Wang79e9af62019-06-12 15:19:19 -0700240 def _lock(self):
241 """lock servohost by touching a file.
242 """
243 logging.debug('Locking servohost %s by touching %s file',
244 self.hostname, self._lock_file)
245 self.run('touch %s' % self._lock_file, ignore_status=True)
Garry Wang7c00b0f2019-06-25 17:28:17 -0700246 self._is_locked = True
Garry Wang79e9af62019-06-12 15:19:19 -0700247
248
249 def _unlock(self):
250 """Unlock servohost by removing the lock file.
251 """
252 logging.debug('Unlocking servohost by removing %s file',
253 self._lock_file)
254 self.run('rm %s' % self._lock_file, ignore_status=True)
Garry Wang7c00b0f2019-06-25 17:28:17 -0700255 self._is_locked = False
Garry Wang79e9af62019-06-12 15:19:19 -0700256
257
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700258 def close(self):
Congbin Guofc3b8962019-03-22 17:38:46 -0700259 """Close the associated servo and the host object."""
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700260 if self._servo:
Congbin Guo2e5e2a22018-07-27 10:32:48 -0700261 # In some cases when we run as lab-tools, the job object is None.
Congbin Guofc3b8962019-03-22 17:38:46 -0700262 if self.job and not self._servo.uart_logs_dir:
263 self._servo.uart_logs_dir = self.job.resultdir
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700264 self._servo.close()
265
Garry Wang7c00b0f2019-06-25 17:28:17 -0700266 if self._is_locked:
267 # Remove the lock if the servohost has been locked.
Garry Wang79e9af62019-06-12 15:19:19 -0700268 try:
269 self._unlock()
270 except error.AutoservSSHTimeout:
271 logging.error('Unlock servohost failed due to ssh timeout.'
272 ' It may caused by servohost went down during'
273 ' the task.')
274
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700275 super(ServoHost, self).close()
276
277
Richard Barnetteea3e4602016-06-10 12:36:41 -0700278def make_servo_hostname(dut_hostname):
279 """Given a DUT's hostname, return the hostname of its servo.
280
281 @param dut_hostname: hostname of a DUT.
282
283 @return hostname of the DUT's servo.
284
285 """
286 host_parts = dut_hostname.split('.')
287 host_parts[0] = host_parts[0] + '-servo'
288 return '.'.join(host_parts)
289
290
291def servo_host_is_up(servo_hostname):
Kevin Cheng5f2ba6c2016-09-28 10:20:05 -0700292 """Given a servo host name, return if it's up or not.
Richard Barnetteea3e4602016-06-10 12:36:41 -0700293
294 @param servo_hostname: hostname of the servo host.
295
296 @return True if it's up, False otherwise
297 """
298 # Technically, this duplicates the SSH ping done early in the servo
299 # proxy initialization code. However, this ping ends in a couple
300 # seconds when if fails, rather than the 60 seconds it takes to decide
301 # that an SSH ping has timed out. Specifically, that timeout happens
302 # when our servo DNS name resolves, but there is no host at that IP.
303 logging.info('Pinging servo host at %s', servo_hostname)
304 ping_config = ping_runner.PingConfig(
305 servo_hostname, count=3,
306 ignore_result=True, ignore_status=True)
307 return ping_runner.PingRunner().ping(ping_config).received > 0
308
309
Richard Barnettee519dcd2016-08-15 17:37:17 -0700310def _map_afe_board_to_servo_board(afe_board):
311 """Map a board we get from the AFE to a servo appropriate value.
312
313 Many boards are identical to other boards for servo's purposes.
314 This function makes that mapping.
315
316 @param afe_board string board name received from AFE.
317 @return board we expect servo to have.
318
319 """
320 KNOWN_SUFFIXES = ['-freon', '_freon', '_moblab', '-cheets']
321 BOARD_MAP = {'gizmo': 'panther'}
322 mapped_board = afe_board
323 if afe_board in BOARD_MAP:
324 mapped_board = BOARD_MAP[afe_board]
325 else:
326 for suffix in KNOWN_SUFFIXES:
327 if afe_board.endswith(suffix):
328 mapped_board = afe_board[0:-len(suffix)]
329 break
330 if mapped_board != afe_board:
331 logging.info('Mapping AFE board=%s to %s', afe_board, mapped_board)
332 return mapped_board
333
334
Prathmesh Prabhub4810232018-09-07 13:24:08 -0700335def get_servo_args_for_host(dut_host):
Kevin Cheng5f2ba6c2016-09-28 10:20:05 -0700336 """Return servo data associated with a given DUT.
Richard Barnetteea3e4602016-06-10 12:36:41 -0700337
Richard Barnetteea3e4602016-06-10 12:36:41 -0700338 @param dut_host Instance of `Host` on which to find the servo
339 attributes.
Prathmesh Prabhuf605dd32018-08-28 17:09:04 -0700340 @return `servo_args` dict with host and an optional port.
Richard Barnetteea3e4602016-06-10 12:36:41 -0700341 """
Prathmesh Prabhucba44292018-08-28 17:44:45 -0700342 info = dut_host.host_info_store.get()
343 servo_args = {k: v for k, v in info.attributes.iteritems()
344 if k in SERVO_ATTR_KEYS}
Richard Barnetteea3e4602016-06-10 12:36:41 -0700345
Prathmesh Prabhucba44292018-08-28 17:44:45 -0700346 if SERVO_PORT_ATTR in servo_args:
347 try:
348 servo_args[SERVO_PORT_ATTR] = int(servo_args[SERVO_PORT_ATTR])
349 except ValueError:
350 logging.error('servo port is not an int: %s',
351 servo_args[SERVO_PORT_ATTR])
352 # Reset servo_args because we don't want to use an invalid port.
353 servo_args.pop(SERVO_HOST_ATTR, None)
354
355 if info.board:
356 servo_args[SERVO_BOARD_ATTR] = _map_afe_board_to_servo_board(info.board)
Nick Sanders2f3c9852018-10-24 12:10:24 -0700357 if info.model:
358 servo_args[SERVO_MODEL_ATTR] = info.model
Prathmesh Prabhu6f5f6362018-09-05 17:20:31 -0700359 return servo_args if SERVO_HOST_ATTR in servo_args else None
Richard Barnetteea3e4602016-06-10 12:36:41 -0700360
361
Prathmesh Prabhuefb1b482018-08-28 17:15:05 -0700362def _tweak_args_for_ssp_moblab(servo_args):
363 if servo_args[SERVO_HOST_ATTR] in ['localhost', '127.0.0.1']:
364 servo_args[SERVO_HOST_ATTR] = _CONFIG.get_config_value(
365 'SSP', 'host_container_ip', type=str, default=None)
366
367
Dan Shi023aae32016-05-25 11:13:01 -0700368def create_servo_host(dut, servo_args, try_lab_servo=False,
Richard Barnette9a26ad62016-06-10 12:03:08 -0700369 try_servo_repair=False):
Kevin Cheng5f2ba6c2016-09-28 10:20:05 -0700370 """Create a ServoHost object for a given DUT, if appropriate.
Dan Shi4d478522014-02-14 13:46:32 -0800371
Richard Barnette9a26ad62016-06-10 12:03:08 -0700372 This function attempts to create and verify or repair a `ServoHost`
373 object for a servo connected to the given `dut`, subject to various
374 constraints imposed by the parameters:
375 * When the `servo_args` parameter is not `None`, a servo
376 host must be created, and must be checked with `repair()`.
377 * Otherwise, if a servo exists in the lab and `try_lab_servo` is
378 true:
379 * If `try_servo_repair` is true, then create a servo host and
380 check it with `repair()`.
381 * Otherwise, if the servo responds to `ping` then create a
382 servo host and check it with `verify()`.
Fang Denge545abb2014-12-30 18:43:47 -0800383
Richard Barnette9a26ad62016-06-10 12:03:08 -0700384 In cases where `servo_args` was not `None`, repair failure
385 exceptions are passed back to the caller; otherwise, exceptions
Richard Barnette07c2e1d2016-10-26 14:24:28 -0700386 are logged and then discarded. Note that this only happens in cases
387 where we're called from a test (not special task) control file that
388 has an explicit dependency on servo. In that case, we require that
389 repair not write to `status.log`, so as to avoid polluting test
390 results.
391
392 TODO(jrbarnette): The special handling for servo in test control
393 files is a thorn in my flesh; I dearly hope to see it cut out before
394 my retirement.
Richard Barnette9a26ad62016-06-10 12:03:08 -0700395
396 Parameters for a servo host consist of a host name, port number, and
397 DUT board, and are determined from one of these sources, in order of
398 priority:
Richard Barnetteea3e4602016-06-10 12:36:41 -0700399 * Servo attributes from the `dut` parameter take precedence over
400 all other sources of information.
401 * If a DNS entry for the servo based on the DUT hostname exists in
402 the CrOS lab network, that hostname is used with the default
Richard Barnette9a26ad62016-06-10 12:03:08 -0700403 port and the DUT's board.
Richard Barnetteea3e4602016-06-10 12:36:41 -0700404 * If no other options are found, the parameters will be taken
Richard Barnette9a26ad62016-06-10 12:03:08 -0700405 from the `servo_args` dict passed in from the caller.
Richard Barnetteea3e4602016-06-10 12:36:41 -0700406
407 @param dut An instance of `Host` from which to take
408 servo parameters (if available).
409 @param servo_args A dictionary with servo parameters to use if
410 they can't be found from `dut`. If this
411 argument is supplied, unrepaired exceptions
412 from `verify()` will be passed back to the
413 caller.
414 @param try_lab_servo If not true, servo host creation will be
415 skipped unless otherwise required by the
416 caller.
Richard Barnette9a26ad62016-06-10 12:03:08 -0700417 @param try_servo_repair If true, check a servo host with
418 `repair()` instead of `verify()`.
Dan Shi4d478522014-02-14 13:46:32 -0800419
420 @returns: A ServoHost object or None. See comments above.
421
422 """
Richard Barnette07c2e1d2016-10-26 14:24:28 -0700423 servo_dependency = servo_args is not None
Richard Barnette07c2e1d2016-10-26 14:24:28 -0700424 if dut is not None and (try_lab_servo or servo_dependency):
Prathmesh Prabhub4810232018-09-07 13:24:08 -0700425 servo_args_override = get_servo_args_for_host(dut)
Richard Barnetteea3e4602016-06-10 12:36:41 -0700426 if servo_args_override is not None:
Prathmesh Prabhuefb1b482018-08-28 17:15:05 -0700427 if utils.in_moblab_ssp():
428 _tweak_args_for_ssp_moblab(servo_args_override)
Prathmesh Prabhu88bf6052018-08-28 16:21:26 -0700429 logging.debug(
430 'Overriding provided servo_args (%s) with arguments'
431 ' determined from the host (%s)',
432 servo_args,
433 servo_args_override,
434 )
Richard Barnetteea3e4602016-06-10 12:36:41 -0700435 servo_args = servo_args_override
Prathmesh Prabhucba44292018-08-28 17:44:45 -0700436
Richard Barnetteea3e4602016-06-10 12:36:41 -0700437 if servo_args is None:
Prathmesh Prabhu88bf6052018-08-28 16:21:26 -0700438 logging.debug('No servo_args provided, and failed to find overrides.')
Richard Barnetteea3e4602016-06-10 12:36:41 -0700439 return None
Prathmesh Prabhucba44292018-08-28 17:44:45 -0700440 if SERVO_HOST_ATTR not in servo_args:
441 logging.debug('%s attribute missing from servo_args: %s',
442 SERVO_HOST_ATTR, servo_args)
443 return None
Richard Barnette07c2e1d2016-10-26 14:24:28 -0700444 if (not servo_dependency and not try_servo_repair and
Richard Barnette9a26ad62016-06-10 12:03:08 -0700445 not servo_host_is_up(servo_args[SERVO_HOST_ATTR])):
Prathmesh Prabhu88bf6052018-08-28 16:21:26 -0700446 logging.debug('ServoHost is not up.')
Dan Shibbb0cb62014-03-24 17:50:57 -0700447 return None
Prathmesh Prabhu88bf6052018-08-28 16:21:26 -0700448
Garry Wangebc015b2019-06-06 17:45:06 -0700449 newhost = ServoHost(**servo_args)
450
Richard Barnette9a26ad62016-06-10 12:03:08 -0700451 # Note that the logic of repair() includes everything done
452 # by verify(). It's sufficient to call one or the other;
453 # we don't need both.
Richard Barnette07c2e1d2016-10-26 14:24:28 -0700454 if servo_dependency:
455 newhost.repair(silent=True)
Prathmesh Prabhu88bf6052018-08-28 16:21:26 -0700456 return newhost
457
458 if try_servo_repair:
459 try:
460 newhost.repair()
461 except Exception:
462 logging.exception('servo repair failed for %s', newhost.hostname)
Richard Barnette9a26ad62016-06-10 12:03:08 -0700463 else:
464 try:
Prathmesh Prabhu88bf6052018-08-28 16:21:26 -0700465 newhost.verify()
Kevin Cheng5f2ba6c2016-09-28 10:20:05 -0700466 except Exception:
Prathmesh Prabhu88bf6052018-08-28 16:21:26 -0700467 logging.exception('servo verify failed for %s', newhost.hostname)
Richard Barnette9a26ad62016-06-10 12:03:08 -0700468 return newhost