blob: a7cffa60bd17441be2285328a7027950e7db2dcf [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
Fang Deng5d518f42013-08-02 14:04:32 -070013import xmlrpclib
Raul E Rangel52ca2e82018-07-03 14:10:14 -060014import os
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)
53 that with a servo instance for a specific port."""
Fang Deng5d518f42013-08-02 14:04:32 -070054
Raul E Rangel52ca2e82018-07-03 14:10:14 -060055 DEFAULT_PORT = int(os.getenv('SERVOD_PORT', '9999'))
Richard Barnette9a26ad62016-06-10 12:03:08 -070056
Dan Shie5b3c512014-08-21 12:12:09 -070057 # Timeout for initializing servo signals.
Wai-Hong Tam37b6ed32017-09-19 15:52:39 -070058 INITIALIZE_SERVO_TIMEOUT_SECS = 60
Richard Barnette9a26ad62016-06-10 12:03:08 -070059
xixuan6cf6d2f2016-01-29 15:29:00 -080060 # Ready test function
61 SERVO_READY_METHOD = 'get_version'
Fang Deng5d518f42013-08-02 14:04:32 -070062
63
Richard Barnette17bfc6c2016-08-04 18:41:43 -070064 def _initialize(self, servo_host='localhost',
Richard Barnettee519dcd2016-08-15 17:37:17 -070065 servo_port=DEFAULT_PORT, servo_board=None,
Nick Sanders2f3c9852018-10-24 12:10:24 -070066 servo_model=None, servo_serial=None, is_in_lab=None,
67 *args, **dargs):
Fang Deng5d518f42013-08-02 14:04:32 -070068 """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 Rangel52ca2e82018-07-03 14:10:14 -060074 @param servo_port: Port the servod process is listening on. Defaults
75 to the SERVOD_PORT environment variable if set,
76 otherwise 9999.
Kevin Cheng5f2ba6c2016-09-28 10:20:05 -070077 @param servo_board: Board that the servo is connected to.
Nick Sanders2f3c9852018-10-24 12:10:24 -070078 @param servo_model: Model that the servo is connected to.
Dan Shi4d478522014-02-14 13:46:32 -080079 @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 Deng5d518f42013-08-02 14:04:32 -070082
83 """
84 super(ServoHost, self)._initialize(hostname=servo_host,
Garry Wangebc015b2019-06-06 17:45:06 -070085 is_in_lab=is_in_lab, *args, **dargs)
Richard Barnette42f4db92018-08-23 15:05:15 -070086 self.servo_port = int(servo_port)
Richard Barnettee519dcd2016-08-15 17:37:17 -070087 self.servo_board = servo_board
Nick Sanders2f3c9852018-10-24 12:10:24 -070088 self.servo_model = servo_model
Kevin Cheng643ce8a2016-09-15 15:42:12 -070089 self.servo_serial = servo_serial
Richard Barnettee519dcd2016-08-15 17:37:17 -070090 self._servo = None
Garry Wang79e9af62019-06-12 15:19:19 -070091 # 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.
Garry Wang42b4d862019-06-25 15:50:49 -0700102 if (self.wait_up(self.REBOOT_TIMEOUT) and self.is_in_lab()
103 and self.is_labstation()):
Garry Wang79e9af62019-06-12 15:19:19 -0700104 self._lock()
Garry Wangebc015b2019-06-06 17:45:06 -0700105
Richard Barnette9a26ad62016-06-10 12:03:08 -0700106 self._repair_strategy = (
107 servo_repair.create_servo_repair_strategy())
Richard Barnettee519dcd2016-08-15 17:37:17 -0700108
Richard Barnette9a26ad62016-06-10 12:03:08 -0700109
110 def connect_servo(self):
Kevin Cheng5f2ba6c2016-09-28 10:20:05 -0700111 """Establish a connection to the servod server on this host.
Richard Barnette9a26ad62016-06-10 12:03:08 -0700112
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 Cheng643ce8a2016-09-15 15:42:12 -0700121 servo_obj = servo.Servo(servo_host=self, servo_serial=self.servo_serial)
Richard Barnette9a26ad62016-06-10 12:03:08 -0700122 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 Cheng5f2ba6c2016-09-28 10:20:05 -0700132 """Disconnect our servo if it exists.
Richard Barnette9a26ad62016-06-10 12:03:08 -0700133
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 Deng5d518f42013-08-02 14:04:32 -0700144
145
Fang Deng5d518f42013-08-02 14:04:32 -0700146 def get_servod_server_proxy(self):
Kevin Cheng5f2ba6c2016-09-28 10:20:05 -0700147 """Return a proxy that can be used to communicate with servod server.
Fang Deng5d518f42013-08-02 14:04:32 -0700148
149 @returns: An xmlrpclib.ServerProxy that is connected to the servod
150 server on the host.
Fang Deng5d518f42013-08-02 14:04:32 -0700151 """
Richard Barnette9a26ad62016-06-10 12:03:08 -0700152 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 Li2b1a8992018-11-27 14:17:18 -0800156 timeout_seconds=60,
Allen Li556f4532018-12-03 18:11:23 -0800157 request_timeout_seconds=3600)
Richard Barnette9a26ad62016-06-10 12:03:08 -0700158 else:
159 remote = 'http://%s:%s' % (self.hostname, self.servo_port)
160 return xmlrpclib.ServerProxy(remote)
Fang Deng5d518f42013-08-02 14:04:32 -0700161
162
Richard Barnette1edbb162016-11-01 11:47:50 -0700163 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 Barnetteabbdc252018-07-26 16:57:42 -0700168 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 Barnette9a26ad62016-06-10 12:03:08 -0700171 try:
Richard Barnette1edbb162016-11-01 11:47:50 -0700172 self._repair_strategy.verify(self, silent)
Richard Barnette9a26ad62016-06-10 12:03:08 -0700173 except:
174 self.disconnect_servo()
175 raise
Fang Deng5d518f42013-08-02 14:04:32 -0700176
177
Richard Barnette1edbb162016-11-01 11:47:50 -0700178 def repair(self, silent=False):
179 """Attempt to repair servo host.
180
181 @param silent If true, suppress logging in `status.log`.
182 """
Richard Barnetteabbdc252018-07-26 16:57:42 -0700183 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 Barnette9a26ad62016-06-10 12:03:08 -0700186 try:
Richard Barnette1edbb162016-11-01 11:47:50 -0700187 self._repair_strategy.repair(self, silent)
Richard Barnette9a26ad62016-06-10 12:03:08 -0700188 except:
189 self.disconnect_servo()
190 raise
Fang Deng5d518f42013-08-02 14:04:32 -0700191
192
Dan Shi4d478522014-02-14 13:46:32 -0800193 def get_servo(self):
194 """Get the cached servo.Servo object.
Fang Deng5d518f42013-08-02 14:04:32 -0700195
Dan Shi4d478522014-02-14 13:46:32 -0800196 @return: a servo.Servo object.
Fang Deng5d518f42013-08-02 14:04:32 -0700197 """
Dan Shi4d478522014-02-14 13:46:32 -0800198 return self._servo
199
200
Garry Wang79e9af62019-06-12 15:19:19 -0700201 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 Guoa1f9cba2018-07-03 11:36:59 -0700225 def close(self):
Congbin Guofc3b8962019-03-22 17:38:46 -0700226 """Close the associated servo and the host object."""
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700227 if self._servo:
Congbin Guo2e5e2a22018-07-27 10:32:48 -0700228 # In some cases when we run as lab-tools, the job object is None.
Congbin Guofc3b8962019-03-22 17:38:46 -0700229 if self.job and not self._servo.uart_logs_dir:
230 self._servo.uart_logs_dir = self.job.resultdir
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700231 self._servo.close()
232
Garry Wang79e9af62019-06-12 15:19:19 -0700233 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 Guoa1f9cba2018-07-03 11:36:59 -0700242 super(ServoHost, self).close()
243
244
Richard Barnetteea3e4602016-06-10 12:36:41 -0700245def 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
258def servo_host_is_up(servo_hostname):
Kevin Cheng5f2ba6c2016-09-28 10:20:05 -0700259 """Given a servo host name, return if it's up or not.
Richard Barnetteea3e4602016-06-10 12:36:41 -0700260
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 Barnettee519dcd2016-08-15 17:37:17 -0700277def _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 Prabhub4810232018-09-07 13:24:08 -0700302def get_servo_args_for_host(dut_host):
Kevin Cheng5f2ba6c2016-09-28 10:20:05 -0700303 """Return servo data associated with a given DUT.
Richard Barnetteea3e4602016-06-10 12:36:41 -0700304
Richard Barnetteea3e4602016-06-10 12:36:41 -0700305 @param dut_host Instance of `Host` on which to find the servo
306 attributes.
Prathmesh Prabhuf605dd32018-08-28 17:09:04 -0700307 @return `servo_args` dict with host and an optional port.
Richard Barnetteea3e4602016-06-10 12:36:41 -0700308 """
Prathmesh Prabhucba44292018-08-28 17:44:45 -0700309 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 Barnetteea3e4602016-06-10 12:36:41 -0700312
Prathmesh Prabhucba44292018-08-28 17:44:45 -0700313 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 Sanders2f3c9852018-10-24 12:10:24 -0700324 if info.model:
325 servo_args[SERVO_MODEL_ATTR] = info.model
Prathmesh Prabhu6f5f6362018-09-05 17:20:31 -0700326 return servo_args if SERVO_HOST_ATTR in servo_args else None
Richard Barnetteea3e4602016-06-10 12:36:41 -0700327
328
Prathmesh Prabhuefb1b482018-08-28 17:15:05 -0700329def _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 Shi023aae32016-05-25 11:13:01 -0700335def create_servo_host(dut, servo_args, try_lab_servo=False,
Richard Barnette9a26ad62016-06-10 12:03:08 -0700336 try_servo_repair=False):
Kevin Cheng5f2ba6c2016-09-28 10:20:05 -0700337 """Create a ServoHost object for a given DUT, if appropriate.
Dan Shi4d478522014-02-14 13:46:32 -0800338
Richard Barnette9a26ad62016-06-10 12:03:08 -0700339 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 Denge545abb2014-12-30 18:43:47 -0800350
Richard Barnette9a26ad62016-06-10 12:03:08 -0700351 In cases where `servo_args` was not `None`, repair failure
352 exceptions are passed back to the caller; otherwise, exceptions
Richard Barnette07c2e1d2016-10-26 14:24:28 -0700353 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 Barnette9a26ad62016-06-10 12:03:08 -0700362
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 Barnetteea3e4602016-06-10 12:36:41 -0700366 * 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 Barnette9a26ad62016-06-10 12:03:08 -0700370 port and the DUT's board.
Richard Barnetteea3e4602016-06-10 12:36:41 -0700371 * If no other options are found, the parameters will be taken
Richard Barnette9a26ad62016-06-10 12:03:08 -0700372 from the `servo_args` dict passed in from the caller.
Richard Barnetteea3e4602016-06-10 12:36:41 -0700373
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 Barnette9a26ad62016-06-10 12:03:08 -0700384 @param try_servo_repair If true, check a servo host with
385 `repair()` instead of `verify()`.
Dan Shi4d478522014-02-14 13:46:32 -0800386
387 @returns: A ServoHost object or None. See comments above.
388
389 """
Richard Barnette07c2e1d2016-10-26 14:24:28 -0700390 servo_dependency = servo_args is not None
Richard Barnette07c2e1d2016-10-26 14:24:28 -0700391 if dut is not None and (try_lab_servo or servo_dependency):
Prathmesh Prabhub4810232018-09-07 13:24:08 -0700392 servo_args_override = get_servo_args_for_host(dut)
Richard Barnetteea3e4602016-06-10 12:36:41 -0700393 if servo_args_override is not None:
Prathmesh Prabhuefb1b482018-08-28 17:15:05 -0700394 if utils.in_moblab_ssp():
395 _tweak_args_for_ssp_moblab(servo_args_override)
Prathmesh Prabhu88bf6052018-08-28 16:21:26 -0700396 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 Barnetteea3e4602016-06-10 12:36:41 -0700402 servo_args = servo_args_override
Prathmesh Prabhucba44292018-08-28 17:44:45 -0700403
Richard Barnetteea3e4602016-06-10 12:36:41 -0700404 if servo_args is None:
Prathmesh Prabhu88bf6052018-08-28 16:21:26 -0700405 logging.debug('No servo_args provided, and failed to find overrides.')
Richard Barnetteea3e4602016-06-10 12:36:41 -0700406 return None
Prathmesh Prabhucba44292018-08-28 17:44:45 -0700407 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 Barnette07c2e1d2016-10-26 14:24:28 -0700411 if (not servo_dependency and not try_servo_repair and
Richard Barnette9a26ad62016-06-10 12:03:08 -0700412 not servo_host_is_up(servo_args[SERVO_HOST_ATTR])):
Prathmesh Prabhu88bf6052018-08-28 16:21:26 -0700413 logging.debug('ServoHost is not up.')
Dan Shibbb0cb62014-03-24 17:50:57 -0700414 return None
Prathmesh Prabhu88bf6052018-08-28 16:21:26 -0700415
Garry Wangebc015b2019-06-06 17:45:06 -0700416 newhost = ServoHost(**servo_args)
417
Richard Barnette9a26ad62016-06-10 12:03:08 -0700418 # 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 Barnette07c2e1d2016-10-26 14:24:28 -0700421 if servo_dependency:
422 newhost.repair(silent=True)
Prathmesh Prabhu88bf6052018-08-28 16:21:26 -0700423 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 Barnette9a26ad62016-06-10 12:03:08 -0700430 else:
431 try:
Prathmesh Prabhu88bf6052018-08-28 16:21:26 -0700432 newhost.verify()
Kevin Cheng5f2ba6c2016-09-28 10:20:05 -0700433 except Exception:
Prathmesh Prabhu88bf6052018-08-28 16:21:26 -0700434 logging.exception('servo verify failed for %s', newhost.hostname)
Richard Barnette9a26ad62016-06-10 12:03:08 -0700435 return newhost