blob: 48cc6f34b483fed9b8758fe4a7f1641b6c91dd12 [file] [log] [blame]
J. Richard Barnette24adbf42012-04-11 15:04:53 -07001# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Dale Curtisaa5eedb2011-08-23 16:18:52 -07002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
Aviv Keshet74c89a92013-02-04 15:18:30 -08005import functools
Christopher Wiley0ed712b2013-04-09 15:25:12 -07006import httplib
J. Richard Barnette1d78b012012-05-15 13:56:30 -07007import logging
Dan Shi0f466e82013-02-22 15:44:58 -08008import os
Simran Basid5e5e272012-09-24 15:23:59 -07009import re
Christopher Wileyd78249a2013-03-01 13:05:31 -080010import socket
J. Richard Barnette1d78b012012-05-15 13:56:30 -070011import subprocess
J. Richard Barnette134ec2c2012-04-25 12:59:37 -070012import time
J. Richard Barnette1d78b012012-05-15 13:56:30 -070013import xmlrpclib
J. Richard Barnette134ec2c2012-04-25 12:59:37 -070014
J. Richard Barnette45e93de2012-04-11 17:24:15 -070015from autotest_lib.client.bin import utils
Richard Barnette0c73ffc2012-11-19 15:21:18 -080016from autotest_lib.client.common_lib import error
17from autotest_lib.client.common_lib import global_config
J. Richard Barnette45e93de2012-04-11 17:24:15 -070018from autotest_lib.client.common_lib.cros import autoupdater
Richard Barnette03a0c132012-11-05 12:40:35 -080019from autotest_lib.client.common_lib.cros import dev_server
Christopher Wileyd78249a2013-03-01 13:05:31 -080020from autotest_lib.client.common_lib.cros import retry
Richard Barnette82c35912012-11-20 10:09:10 -080021from autotest_lib.client.cros import constants
J. Richard Barnette45e93de2012-04-11 17:24:15 -070022from autotest_lib.server import autoserv_parser
Chris Sosaf4d43ff2012-10-30 11:21:05 -070023from autotest_lib.server import autotest
Dan Shia1ecd5c2013-06-06 11:21:31 -070024from autotest_lib.server import utils as server_utils
Scott Zawalski89c44dd2013-02-26 09:28:02 -050025from autotest_lib.server.cros.dynamic_suite import constants as ds_constants
Simran Basi5e6339a2013-03-21 11:34:32 -070026from autotest_lib.server.cros.dynamic_suite import tools, frontend_wrappers
Fang Deng96667ca2013-08-01 17:46:18 -070027from autotest_lib.server.hosts import abstract_ssh
Fang Deng5d518f42013-08-02 14:04:32 -070028from autotest_lib.server.hosts import servo_host
beeps687243d2013-07-18 15:29:27 -070029from autotest_lib.site_utils.graphite import stats
Simran Basidcff4252012-11-20 16:13:20 -080030from autotest_lib.site_utils.rpm_control_system import rpm_client
Simran Basid5e5e272012-09-24 15:23:59 -070031
32
beeps32a63082013-08-22 14:02:29 -070033try:
34 import jsonrpclib
35except ImportError:
36 jsonrpclib = None
Fang Deng96667ca2013-08-01 17:46:18 -070037
Fang Dengd1c2b732013-08-20 12:59:46 -070038
beepsc87ff602013-07-31 21:53:00 -070039class FactoryImageCheckerException(error.AutoservError):
40 """Exception raised when an image is a factory image."""
41 pass
42
43
Aviv Keshet74c89a92013-02-04 15:18:30 -080044def add_label_detector(label_function_list, label_list=None, label=None):
45 """Decorator used to group functions together into the provided list.
46 @param label_function_list: List of label detecting functions to add
47 decorated function to.
48 @param label_list: List of detectable labels to add detectable labels to.
49 (Default: None)
50 @param label: Label string that is detectable by this detection function
51 (Default: None)
J. Richard Barnette7214e0b2013-02-06 15:20:49 -080052 """
Simran Basic6f1f7a2012-10-16 10:47:46 -070053 def add_func(func):
Aviv Keshet74c89a92013-02-04 15:18:30 -080054 """
55 @param func: The function to be added as a detector.
56 """
57 label_function_list.append(func)
58 if label and label_list is not None:
59 label_list.append(label)
Simran Basic6f1f7a2012-10-16 10:47:46 -070060 return func
61 return add_func
62
63
Fang Deng0ca40e22013-08-27 17:47:44 -070064class CrosHost(abstract_ssh.AbstractSSHHost):
J. Richard Barnette45e93de2012-04-11 17:24:15 -070065 """Chromium OS specific subclass of Host."""
66
67 _parser = autoserv_parser.autoserv_parser
Scott Zawalski62bacae2013-03-05 10:40:32 -050068 _AFE = frontend_wrappers.RetryingAFE(timeout_min=5, delay_sec=10)
J. Richard Barnette45e93de2012-04-11 17:24:15 -070069
Richard Barnette03a0c132012-11-05 12:40:35 -080070 # Timeout values (in seconds) associated with various Chrome OS
71 # state changes.
J. Richard Barnette134ec2c2012-04-25 12:59:37 -070072 #
Richard Barnette0c73ffc2012-11-19 15:21:18 -080073 # In general, a good rule of thumb is that the timeout can be up
74 # to twice the typical measured value on the slowest platform.
75 # The times here have not necessarily been empirically tested to
76 # meet this criterion.
J. Richard Barnetteeb69d722012-06-18 17:29:44 -070077 #
78 # SLEEP_TIMEOUT: Time to allow for suspend to memory.
Richard Barnette0c73ffc2012-11-19 15:21:18 -080079 # RESUME_TIMEOUT: Time to allow for resume after suspend, plus
80 # time to restart the netwowrk.
J. Richard Barnetteeb69d722012-06-18 17:29:44 -070081 # BOOT_TIMEOUT: Time to allow for boot from power off. Among
Richard Barnette0c73ffc2012-11-19 15:21:18 -080082 # other things, this must account for the 30 second dev-mode
J. Richard Barnetted4649c62013-03-06 17:42:27 -080083 # screen delay and time to start the network.
J. Richard Barnetteeb69d722012-06-18 17:29:44 -070084 # USB_BOOT_TIMEOUT: Time to allow for boot from a USB device,
Richard Barnette0c73ffc2012-11-19 15:21:18 -080085 # including the 30 second dev-mode delay and time to start the
J. Richard Barnetted4649c62013-03-06 17:42:27 -080086 # network.
Richard Barnette0c73ffc2012-11-19 15:21:18 -080087 # SHUTDOWN_TIMEOUT: Time to allow for shut down.
Chris Sosab76e0ee2013-05-22 16:55:41 -070088 # REBOOT_TIMEOUT: How long to wait for a reboot.
beepsf079cfb2013-09-18 17:49:51 -070089 # INSTALL_TIMEOUT: Time to allow for chromeos-install.
J. Richard Barnetteeb69d722012-06-18 17:29:44 -070090
91 SLEEP_TIMEOUT = 2
J. Richard Barnetted4649c62013-03-06 17:42:27 -080092 RESUME_TIMEOUT = 10
J. Richard Barnettefbcc7122013-07-24 18:24:59 -070093 BOOT_TIMEOUT = 60
J. Richard Barnetteeb69d722012-06-18 17:29:44 -070094 USB_BOOT_TIMEOUT = 150
Dan Shi2c88eed2013-11-12 10:18:38 -080095 POWERWASH_BOOT_TIMEOUT = 60
Chris Sosab76e0ee2013-05-22 16:55:41 -070096
97 # We have a long timeout to ensure we don't flakily fail due to other
98 # issues. Shorter timeouts are vetted in platform_RebootAfterUpdate.
Simran Basi1160e2c2013-10-04 16:00:24 -070099 # TODO(sbasi - crbug.com/276094) Restore to 5 mins once the 'host did not
100 # return from reboot' bug is solved.
101 REBOOT_TIMEOUT = 480
Chris Sosab76e0ee2013-05-22 16:55:41 -0700102
beepsf079cfb2013-09-18 17:49:51 -0700103 INSTALL_TIMEOUT = 240
Richard Barnette03a0c132012-11-05 12:40:35 -0800104
Ismail Noorbasha07fdb612013-02-14 14:13:31 -0800105 # _USB_POWER_TIMEOUT: Time to allow for USB to power toggle ON and OFF.
106 # _POWER_CYCLE_TIMEOUT: Time to allow for manual power cycle.
107 _USB_POWER_TIMEOUT = 5
108 _POWER_CYCLE_TIMEOUT = 10
109
beeps32a63082013-08-22 14:02:29 -0700110 _RPC_PROXY_URL = 'http://localhost:%d'
Christopher Wileydd181852013-10-10 19:56:58 -0700111 _RPC_SHUTDOWN_POLLING_PERIOD_SECONDS = 2
112 _RPC_SHUTDOWN_TIMEOUT_SECONDS = 20
Richard Barnette0c73ffc2012-11-19 15:21:18 -0800113
Richard Barnette82c35912012-11-20 10:09:10 -0800114 _RPM_RECOVERY_BOARDS = global_config.global_config.get_config_value('CROS',
115 'rpm_recovery_boards', type=str).split(',')
116
117 _MAX_POWER_CYCLE_ATTEMPTS = 6
118 _LAB_MACHINE_FILE = '/mnt/stateful_partition/.labmachine'
119 _RPM_HOSTNAME_REGEX = ('chromeos[0-9]+(-row[0-9]+)?-rack[0-9]+[a-z]*-'
120 'host[0-9]+')
121 _LIGHTSENSOR_FILES = ['in_illuminance0_input',
122 'in_illuminance0_raw',
123 'illuminance0_input']
124 _LIGHTSENSOR_SEARCH_DIR = '/sys/bus/iio/devices'
125 _LABEL_FUNCTIONS = []
Aviv Keshet74c89a92013-02-04 15:18:30 -0800126 _DETECTABLE_LABELS = []
127 label_decorator = functools.partial(add_label_detector, _LABEL_FUNCTIONS,
128 _DETECTABLE_LABELS)
J. Richard Barnette134ec2c2012-04-25 12:59:37 -0700129
J. Richard Barnetteb6de7e32013-02-14 13:28:04 -0800130 # Constants used in ping_wait_up() and ping_wait_down().
131 #
132 # _PING_WAIT_COUNT is the approximate number of polling
133 # cycles to use when waiting for a host state change.
134 #
135 # _PING_STATUS_DOWN and _PING_STATUS_UP are names used
136 # for arguments to the internal _ping_wait_for_status()
137 # method.
138 _PING_WAIT_COUNT = 40
139 _PING_STATUS_DOWN = False
140 _PING_STATUS_UP = True
141
Ismail Noorbasha07fdb612013-02-14 14:13:31 -0800142 # Allowed values for the power_method argument.
143
144 # POWER_CONTROL_RPM: Passed as default arg for power_off/on/cycle() methods.
145 # POWER_CONTROL_SERVO: Used in set_power() and power_cycle() methods.
146 # POWER_CONTROL_MANUAL: Used in set_power() and power_cycle() methods.
147 POWER_CONTROL_RPM = 'RPM'
148 POWER_CONTROL_SERVO = 'servoj10'
149 POWER_CONTROL_MANUAL = 'manual'
150
151 POWER_CONTROL_VALID_ARGS = (POWER_CONTROL_RPM,
152 POWER_CONTROL_SERVO,
153 POWER_CONTROL_MANUAL)
Richard Barnette0c73ffc2012-11-19 15:21:18 -0800154
Simran Basi5e6339a2013-03-21 11:34:32 -0700155 _RPM_OUTLET_CHANGED = 'outlet_changed'
156
beeps687243d2013-07-18 15:29:27 -0700157
J. Richard Barnette964fba02012-10-24 17:34:29 -0700158 @staticmethod
beeps46dadc92013-11-07 14:07:10 -0800159 def check_host(host, timeout=10):
160 """
161 Check if the given host is a chrome-os host.
162
163 @param host: An ssh host representing a device.
164 @param timeout: The timeout for the run command.
165
166 @return: True if the host device is chromeos.
167
beeps46dadc92013-11-07 14:07:10 -0800168 """
169 try:
Christopher Wileyfc3eac02013-11-21 16:24:57 -0800170 result = host.run('grep -q CHROMEOS /etc/lsb-release',
171 ignore_status=True, timeout=timeout)
beeps46dadc92013-11-07 14:07:10 -0800172 except (error.AutoservRunError, error.AutoservSSHTimeout):
173 return False
174 return result.exit_status == 0
175
176
177 @staticmethod
J. Richard Barnette7214e0b2013-02-06 15:20:49 -0800178 def get_servo_arguments(args_dict):
179 """Extract servo options from `args_dict` and return the result.
180
181 Take the provided dictionary of argument options and return
182 a subset that represent standard arguments needed to
183 construct a servo object for a host. The intent is to
184 provide standard argument processing from run_remote_tests
185 for tests that require a servo to operate.
186
187 Recommended usage:
188 ~~~~~~~~
189 args_dict = utils.args_to_dict(args)
Fang Deng0ca40e22013-08-27 17:47:44 -0700190 servo_args = hosts.CrosHost.get_servo_arguments(args_dict)
J. Richard Barnette7214e0b2013-02-06 15:20:49 -0800191 host = hosts.create_host(machine, servo_args=servo_args)
192 ~~~~~~~~
193
194 @param args_dict Dictionary from which to extract the servo
195 arguments.
196 """
J. Richard Barnette964fba02012-10-24 17:34:29 -0700197 servo_args = {}
198 for arg in ('servo_host', 'servo_port'):
J. Richard Barnette7214e0b2013-02-06 15:20:49 -0800199 if arg in args_dict:
200 servo_args[arg] = args_dict[arg]
J. Richard Barnette964fba02012-10-24 17:34:29 -0700201 return servo_args
J. Richard Barnette134ec2c2012-04-25 12:59:37 -0700202
J. Richard Barnette964fba02012-10-24 17:34:29 -0700203
Fang Dengd1c2b732013-08-20 12:59:46 -0700204 def _initialize(self, hostname, servo_args=None, ssh_verbosity_flag='',
Aviv Keshetc5947fa2013-09-04 14:06:29 -0700205 ssh_options='',
Fang Dengd1c2b732013-08-20 12:59:46 -0700206 *args, **dargs):
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700207 """Initialize superclasses, and |self.servo|.
208
Fang Deng5d518f42013-08-02 14:04:32 -0700209 This method checks whether a servo is required by checking whether
210 servo_args is None. This method will only attempt to create a servo
211 object when servo is required by the test.
212
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700213 For creating the host servo object, there are three
214 possibilities: First, if the host is a lab system known to
215 have a servo board, we connect to that servo unconditionally.
216 Second, if we're called from a control file that requires
J. Richard Barnette55fb8062012-05-23 10:29:31 -0700217 servo features for testing, it will pass settings for
218 `servo_host`, `servo_port`, or both. If neither of these
219 cases apply, `self.servo` will be `None`.
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700220
221 """
Fang Deng0ca40e22013-08-27 17:47:44 -0700222 super(CrosHost, self)._initialize(hostname=hostname,
J. Richard Barnette45e93de2012-04-11 17:24:15 -0700223 *args, **dargs)
J. Richard Barnettef0859852012-08-20 14:55:50 -0700224 # self.env is a dictionary of environment variable settings
225 # to be exported for commands run on the host.
226 # LIBC_FATAL_STDERR_ can be useful for diagnosing certain
227 # errors that might happen.
228 self.env['LIBC_FATAL_STDERR_'] = '1'
beeps32a63082013-08-22 14:02:29 -0700229 self._rpc_proxy_map = {}
Fang Dengd1c2b732013-08-20 12:59:46 -0700230 self._ssh_verbosity_flag = ssh_verbosity_flag
Aviv Keshetc5947fa2013-09-04 14:06:29 -0700231 self._ssh_options = ssh_options
Fang Deng5d518f42013-08-02 14:04:32 -0700232 self.servo = None
233 # TODO(fdeng): We need to simplify the
234 # process of servo and servo_host initialization.
235 # crbug.com/298432
236 self._servo_host = self._create_servo_host(servo_args)
237 # TODO(fdeng): 'servo_args is not None' is used to determine whether
238 # a test needs a servo. Better solution is needed.
239 # There are three possible cases here:
240 # 1. servo_arg is None
241 # 2. servo arg is an empty dictionary
242 # 3. servo_arg is a dictionary that has entries of 'servo_host',
243 # 'servo_port'(optional).
244 # We assume that:
245 # a. A test that requires a servo always calls get_servo_arguments
246 # and passes in its return value as |servo_args|.
247 # b. get_servo_arguments never returns None.
248 # Based on the assumptions, we reason that only in case 2 and 3
249 # a servo is required, i.e. when the servo_args is not None.
250 if servo_args is not None:
251 self.servo = self._servo_host.create_healthy_servo_object()
252
253
254 def _create_servo_host(self, servo_args):
255 """Create a ServoHost object.
256
257 There three possible cases:
258 1) If the DUT is in Cros Lab and has a beaglebone and a servo, then
259 create a ServoHost object pointing to the beaglebone. servo_args
260 is ignored.
261 2) If not case 1) and servo_args is neither None nor empty, then
262 create a ServoHost object using servo_args.
263 3) If neither case 1) or 2) applies, return None.
264
265 @param servo_args: A dictionary that contains args for creating
266 a ServoHost object,
267 e.g. {'servo_host': '172.11.11.111',
268 'servo_port': 9999}.
269 See comments above.
270
271 @returns: A ServoHost object or None. See comments above.
272
273 """
274 servo_host_name = servo_host.make_servo_hostname(self.hostname)
275 if utils.host_is_in_lab_zone(servo_host_name):
276 return servo_host.ServoHost(servo_host=servo_host_name)
277 elif servo_args is not None:
278 return servo_host.ServoHost(**servo_args)
279 else:
280 return None
J. Richard Barnette45e93de2012-04-11 17:24:15 -0700281
282
Scott Zawalski89c44dd2013-02-26 09:28:02 -0500283 def get_repair_image_name(self):
284 """Generate a image_name from variables in the global config.
285
286 @returns a str of $board-version/$BUILD.
287
288 """
289 stable_version = global_config.global_config.get_config_value(
290 'CROS', 'stable_cros_version')
291 build_pattern = global_config.global_config.get_config_value(
292 'CROS', 'stable_build_pattern')
293 board = self._get_board_from_afe()
294 if board is None:
295 raise error.AutoservError('DUT has no board attribute, '
296 'cannot be repaired.')
297 return build_pattern % (board, stable_version)
298
299
Scott Zawalski62bacae2013-03-05 10:40:32 -0500300 def _host_in_AFE(self):
301 """Check if the host is an object the AFE knows.
302
303 @returns the host object.
304 """
305 return self._AFE.get_hosts(hostname=self.hostname)
306
307
Chris Sosab76e0ee2013-05-22 16:55:41 -0700308 def lookup_job_repo_url(self):
309 """Looks up the job_repo_url for the host.
310
311 @returns job_repo_url from AFE or None if not found.
312
313 @raises KeyError if the host does not have a job_repo_url
314 """
315 if not self._host_in_AFE():
316 return None
317
318 hosts = self._AFE.get_hosts(hostname=self.hostname)
beepsb5efc532013-06-04 11:29:34 -0700319 if hosts and ds_constants.JOB_REPO_URL in hosts[0].attributes:
320 return hosts[0].attributes[ds_constants.JOB_REPO_URL]
Chris Sosab76e0ee2013-05-22 16:55:41 -0700321
322
Scott Zawalski89c44dd2013-02-26 09:28:02 -0500323 def clear_cros_version_labels_and_job_repo_url(self):
324 """Clear cros_version labels and host attribute job_repo_url."""
Scott Zawalski62bacae2013-03-05 10:40:32 -0500325 if not self._host_in_AFE():
Scott Zawalskieadbf702013-03-14 09:23:06 -0400326 return
327
Scott Zawalski62bacae2013-03-05 10:40:32 -0500328 host_list = [self.hostname]
329 labels = self._AFE.get_labels(
330 name__startswith=ds_constants.VERSION_PREFIX,
331 host__hostname=self.hostname)
Dan Shi0f466e82013-02-22 15:44:58 -0800332
Scott Zawalski62bacae2013-03-05 10:40:32 -0500333 for label in labels:
334 label.remove_hosts(hosts=host_list)
Scott Zawalski89c44dd2013-02-26 09:28:02 -0500335
beepscb6f1e22013-06-28 19:14:10 -0700336 self.update_job_repo_url(None, None)
337
338
339 def update_job_repo_url(self, devserver_url, image_name):
340 """
341 Updates the job_repo_url host attribute and asserts it's value.
342
343 @param devserver_url: The devserver to use in the job_repo_url.
344 @param image_name: The name of the image to use in the job_repo_url.
345
346 @raises AutoservError: If we failed to update the job_repo_url.
347 """
348 repo_url = None
349 if devserver_url and image_name:
350 repo_url = tools.get_package_url(devserver_url, image_name)
351 self._AFE.set_host_attribute(ds_constants.JOB_REPO_URL, repo_url,
Scott Zawalski62bacae2013-03-05 10:40:32 -0500352 hostname=self.hostname)
beepscb6f1e22013-06-28 19:14:10 -0700353 if self.lookup_job_repo_url() != repo_url:
354 raise error.AutoservError('Failed to update job_repo_url with %s, '
355 'host %s' % (repo_url, self.hostname))
Scott Zawalski89c44dd2013-02-26 09:28:02 -0500356
357
Dan Shie9309262013-06-19 22:50:21 -0700358 def add_cros_version_labels_and_job_repo_url(self, image_name):
Scott Zawalskieadbf702013-03-14 09:23:06 -0400359 """Add cros_version labels and host attribute job_repo_url.
360
361 @param image_name: The name of the image e.g.
362 lumpy-release/R27-3837.0.0
Dan Shi7458bf62013-06-10 12:50:16 -0700363
Scott Zawalskieadbf702013-03-14 09:23:06 -0400364 """
Scott Zawalski62bacae2013-03-05 10:40:32 -0500365 if not self._host_in_AFE():
Scott Zawalskieadbf702013-03-14 09:23:06 -0400366 return
Scott Zawalski62bacae2013-03-05 10:40:32 -0500367
Scott Zawalskieadbf702013-03-14 09:23:06 -0400368 cros_label = '%s%s' % (ds_constants.VERSION_PREFIX, image_name)
Dan Shie9309262013-06-19 22:50:21 -0700369 devserver_url = dev_server.ImageServer.resolve(image_name).url()
Scott Zawalski62bacae2013-03-05 10:40:32 -0500370
371 labels = self._AFE.get_labels(name=cros_label)
372 if labels:
373 label = labels[0]
374 else:
375 label = self._AFE.create_label(name=cros_label)
376
377 label.add_hosts([self.hostname])
beepscb6f1e22013-06-28 19:14:10 -0700378 self.update_job_repo_url(devserver_url, image_name)
379
380
beepsdae65fd2013-07-26 16:24:41 -0700381 def verify_job_repo_url(self, tag=''):
beepscb6f1e22013-06-28 19:14:10 -0700382 """
383 Make sure job_repo_url of this host is valid.
384
joychen03eaad92013-06-26 09:55:21 -0700385 Eg: The job_repo_url "http://lmn.cd.ab.xyx:8080/static/\
beepscb6f1e22013-06-28 19:14:10 -0700386 lumpy-release/R29-4279.0.0/autotest/packages" claims to have the
387 autotest package for lumpy-release/R29-4279.0.0. If this isn't the case,
388 download and extract it. If the devserver embedded in the url is
389 unresponsive, update the job_repo_url of the host after staging it on
390 another devserver.
391
392 @param job_repo_url: A url pointing to the devserver where the autotest
393 package for this build should be staged.
beepsdae65fd2013-07-26 16:24:41 -0700394 @param tag: The tag from the server job, in the format
395 <job_id>-<user>/<hostname>, or <hostless> for a server job.
beepscb6f1e22013-06-28 19:14:10 -0700396
397 @raises DevServerException: If we could not resolve a devserver.
398 @raises AutoservError: If we're unable to save the new job_repo_url as
399 a result of choosing a new devserver because the old one failed to
400 respond to a health check.
beeps0c865032013-07-30 11:37:06 -0700401 @raises urllib2.URLError: If the devserver embedded in job_repo_url
402 doesn't respond within the timeout.
beepscb6f1e22013-06-28 19:14:10 -0700403 """
404 job_repo_url = self.lookup_job_repo_url()
405 if not job_repo_url:
406 logging.warning('No job repo url set on host %s', self.hostname)
407 return
408
409 logging.info('Verifying job repo url %s', job_repo_url)
410 devserver_url, image_name = tools.get_devserver_build_from_package_url(
411 job_repo_url)
412
beeps0c865032013-07-30 11:37:06 -0700413 ds = dev_server.ImageServer(devserver_url)
beepscb6f1e22013-06-28 19:14:10 -0700414
415 logging.info('Staging autotest artifacts for %s on devserver %s',
416 image_name, ds.url())
beeps687243d2013-07-18 15:29:27 -0700417
418 start_time = time.time()
beepscb6f1e22013-06-28 19:14:10 -0700419 ds.stage_artifacts(image_name, ['autotest'])
beeps687243d2013-07-18 15:29:27 -0700420 stage_time = time.time() - start_time
421
422 # Record how much of the verification time comes from a devserver
423 # restage. If we're doing things right we should not see multiple
424 # devservers for a given board/build/branch path.
425 try:
J. Richard Barnette3cbd76b2013-11-27 12:11:25 -0800426 board, build_type, branch = server_utils.ParseBuildName(
beeps687243d2013-07-18 15:29:27 -0700427 image_name)[:3]
J. Richard Barnette3cbd76b2013-11-27 12:11:25 -0800428 except server_utils.ParseBuildNameException:
beeps687243d2013-07-18 15:29:27 -0700429 pass
430 else:
beeps0c865032013-07-30 11:37:06 -0700431 devserver = devserver_url[
Chris Sosa65425082013-10-16 13:26:22 -0700432 devserver_url.find('/') + 2:devserver_url.rfind(':')]
beeps687243d2013-07-18 15:29:27 -0700433 stats_key = {
434 'board': board,
435 'build_type': build_type,
436 'branch': branch,
beeps0c865032013-07-30 11:37:06 -0700437 'devserver': devserver.replace('.', '_'),
beeps687243d2013-07-18 15:29:27 -0700438 }
439 stats.Gauge('verify_job_repo_url').send(
440 '%(board)s.%(build_type)s.%(branch)s.%(devserver)s' % stats_key,
441 stage_time)
beepscb6f1e22013-06-28 19:14:10 -0700442
Scott Zawalskieadbf702013-03-14 09:23:06 -0400443
Dan Shi0f466e82013-02-22 15:44:58 -0800444 def _try_stateful_update(self, update_url, force_update, updater):
445 """Try to use stateful update to initialize DUT.
446
447 When DUT is already running the same version that machine_install
448 tries to install, stateful update is a much faster way to clean up
449 the DUT for testing, compared to a full reimage. It is implemeted
450 by calling autoupdater.run_update, but skipping updating root, as
451 updating the kernel is time consuming and not necessary.
452
453 @param update_url: url of the image.
454 @param force_update: Set to True to update the image even if the DUT
455 is running the same version.
456 @param updater: ChromiumOSUpdater instance used to update the DUT.
457 @returns: True if the DUT was updated with stateful update.
458
459 """
460 if not updater.check_version():
461 return False
462 if not force_update:
463 logging.info('Canceling stateful update because the new and '
464 'old versions are the same.')
465 return False
466 # Following folders should be rebuilt after stateful update.
467 # A test file is used to confirm each folder gets rebuilt after
468 # the stateful update.
469 folders_to_check = ['/var', '/home', '/mnt/stateful_partition']
470 test_file = '.test_file_to_be_deleted'
471 for folder in folders_to_check:
472 touch_path = os.path.join(folder, test_file)
473 self.run('touch %s' % touch_path)
474
475 if not updater.run_update(force_update=True, update_root=False):
476 return False
477
478 # Reboot to complete stateful update.
Chris Sosab76e0ee2013-05-22 16:55:41 -0700479 self.reboot(timeout=self.REBOOT_TIMEOUT, wait=True)
Dan Shi0f466e82013-02-22 15:44:58 -0800480 check_file_cmd = 'test -f %s; echo $?'
481 for folder in folders_to_check:
482 test_file_path = os.path.join(folder, test_file)
483 result = self.run(check_file_cmd % test_file_path,
484 ignore_status=True)
485 if result.exit_status == 1:
486 return False
487 return True
488
489
J. Richard Barnette7275b612013-06-04 18:13:11 -0700490 def _post_update_processing(self, updater, expected_kernel=None):
Dan Shi0f466e82013-02-22 15:44:58 -0800491 """After the DUT is updated, confirm machine_install succeeded.
492
493 @param updater: ChromiumOSUpdater instance used to update the DUT.
J. Richard Barnette7275b612013-06-04 18:13:11 -0700494 @param expected_kernel: kernel expected to be active after reboot,
495 or `None` to skip rollback checking.
Dan Shi0f466e82013-02-22 15:44:58 -0800496
497 """
J. Richard Barnette7275b612013-06-04 18:13:11 -0700498 # Touch the lab machine file to leave a marker that
499 # distinguishes this image from other test images.
500 # Afterwards, we must re-run the autoreboot script because
501 # it depends on the _LAB_MACHINE_FILE.
Dan Shi0f466e82013-02-22 15:44:58 -0800502 self.run('touch %s' % self._LAB_MACHINE_FILE)
Dan Shi0f466e82013-02-22 15:44:58 -0800503 self.run('start autoreboot')
Chris Sosa65425082013-10-16 13:26:22 -0700504 updater.verify_boot_expectations(
505 expected_kernel, rollback_message=
506 'Build %s failed to boot on %s; system rolled back to previous'
507 'build' % (updater.update_version, self.hostname))
J. Richard Barnette7275b612013-06-04 18:13:11 -0700508 # Check that we've got the build we meant to install.
509 if not updater.check_version_to_confirm_install():
510 raise autoupdater.ChromiumOSError(
511 'Failed to update %s to build %s; found build '
512 '%s instead' % (self.hostname,
Chris Sosa65425082013-10-16 13:26:22 -0700513 updater.update_version,
514 updater.get_build_id()))
Dan Shi0f466e82013-02-22 15:44:58 -0800515
516
J. Richard Barnettee4af8b92013-05-01 13:16:12 -0700517 def _stage_image_for_update(self, image_name=None):
Scott Zawalskieadbf702013-03-14 09:23:06 -0400518 """Stage a build on a devserver and return the update_url.
519
520 @param image_name: a name like lumpy-release/R27-3837.0.0
521 @returns an update URL like:
522 http://172.22.50.205:8082/update/lumpy-release/R27-3837.0.0
523 """
J. Richard Barnettee4af8b92013-05-01 13:16:12 -0700524 if not image_name:
525 image_name = self.get_repair_image_name()
526 logging.info('Staging build for AU: %s', image_name)
Scott Zawalskieadbf702013-03-14 09:23:06 -0400527 devserver = dev_server.ImageServer.resolve(image_name)
528 devserver.trigger_download(image_name, synchronous=False)
529 return tools.image_url_pattern() % (devserver.url(), image_name)
530
531
J. Richard Barnettee4af8b92013-05-01 13:16:12 -0700532 def stage_image_for_servo(self, image_name=None):
533 """Stage a build on a devserver and return the update_url.
534
535 @param image_name: a name like lumpy-release/R27-3837.0.0
536 @returns an update URL like:
537 http://172.22.50.205:8082/update/lumpy-release/R27-3837.0.0
538 """
539 if not image_name:
540 image_name = self.get_repair_image_name()
541 logging.info('Staging build for servo install: %s', image_name)
542 devserver = dev_server.ImageServer.resolve(image_name)
543 devserver.stage_artifacts(image_name, ['test_image'])
544 return devserver.get_test_image_url(image_name)
545
546
beepse539be02013-07-31 21:57:39 -0700547 def stage_factory_image_for_servo(self, image_name):
548 """Stage a build on a devserver and return the update_url.
549
550 @param image_name: a name like <baord>/4262.204.0
beeps12c0a3c2013-09-03 11:58:27 -0700551
beepse539be02013-07-31 21:57:39 -0700552 @return: An update URL, eg:
553 http://<devserver>/static/canary-channel/\
554 <board>/4262.204.0/factory_test/chromiumos_factory_image.bin
beeps12c0a3c2013-09-03 11:58:27 -0700555
556 @raises: ValueError if the factory artifact name is missing from
557 the config.
558
beepse539be02013-07-31 21:57:39 -0700559 """
560 if not image_name:
561 logging.error('Need an image_name to stage a factory image.')
562 return
563
beeps12c0a3c2013-09-03 11:58:27 -0700564 factory_artifact = global_config.global_config.get_config_value(
565 'CROS', 'factory_artifact', type=str, default='')
566 if not factory_artifact:
567 raise ValueError('Cannot retrieve the factory artifact name from '
568 'autotest config, and hence cannot stage factory '
569 'artifacts.')
570
beepse539be02013-07-31 21:57:39 -0700571 logging.info('Staging build for servo install: %s', image_name)
572 devserver = dev_server.ImageServer.resolve(image_name)
573 devserver.stage_artifacts(
574 image_name,
beeps12c0a3c2013-09-03 11:58:27 -0700575 [factory_artifact],
576 archive_url=None)
beepse539be02013-07-31 21:57:39 -0700577
578 return tools.factory_image_url_pattern() % (devserver.url(), image_name)
579
580
Chris Sosaa3ac2152012-05-23 22:23:13 -0700581 def machine_install(self, update_url=None, force_update=False,
Scott Zawalski89c44dd2013-02-26 09:28:02 -0500582 local_devserver=False, repair=False):
583 """Install the DUT.
584
Dan Shi0f466e82013-02-22 15:44:58 -0800585 Use stateful update if the DUT is already running the same build.
586 Stateful update does not update kernel and tends to run much faster
587 than a full reimage. If the DUT is running a different build, or it
588 failed to do a stateful update, full update, including kernel update,
589 will be applied to the DUT.
590
Scott Zawalskieadbf702013-03-14 09:23:06 -0400591 Once a host enters machine_install its cros_version label will be
592 removed as well as its host attribute job_repo_url (used for
593 package install).
594
Scott Zawalski89c44dd2013-02-26 09:28:02 -0500595 @param update_url: The url to use for the update
596 pattern: http://$devserver:###/update/$build
597 If update_url is None and repair is True we will install the
598 stable image listed in global_config under
599 CROS.stable_cros_version.
600 @param force_update: Force an update even if the version installed
601 is the same. Default:False
602 @param local_devserver: Used by run_remote_test to allow people to
603 use their local devserver. Default: False
604 @param repair: Whether or not we are in repair mode. This adds special
605 cases for repairing a machine like starting update_engine.
606 Setting repair to True sets force_update to True as well.
607 default: False
608 @raises autoupdater.ChromiumOSError
609
610 """
Dan Shi7458bf62013-06-10 12:50:16 -0700611 if update_url:
612 logging.debug('update url is set to %s', update_url)
613 else:
614 logging.debug('update url is not set, resolving...')
J. Richard Barnettee4af8b92013-05-01 13:16:12 -0700615 if self._parser.options.image:
616 requested_build = self._parser.options.image
617 if requested_build.startswith('http://'):
618 update_url = requested_build
Dan Shi7458bf62013-06-10 12:50:16 -0700619 logging.debug('update url is retrieved from requested_build'
620 ': %s', update_url)
J. Richard Barnettee4af8b92013-05-01 13:16:12 -0700621 else:
622 # Try to stage any build that does not start with
623 # http:// on the devservers defined in
624 # global_config.ini.
Dan Shi7458bf62013-06-10 12:50:16 -0700625 update_url = self._stage_image_for_update(requested_build)
626 logging.debug('Build staged, and update_url is set to: %s',
627 update_url)
J. Richard Barnettee4af8b92013-05-01 13:16:12 -0700628 elif repair:
629 update_url = self._stage_image_for_update()
Dan Shi7458bf62013-06-10 12:50:16 -0700630 logging.debug('Build staged, and update_url is set to: %s',
631 update_url)
Scott Zawalskieadbf702013-03-14 09:23:06 -0400632 else:
J. Richard Barnettee4af8b92013-05-01 13:16:12 -0700633 raise autoupdater.ChromiumOSError(
634 'Update failed. No update URL provided.')
Scott Zawalski89c44dd2013-02-26 09:28:02 -0500635
Scott Zawalski89c44dd2013-02-26 09:28:02 -0500636 if repair:
Dan Shi0f466e82013-02-22 15:44:58 -0800637 # In case the system is in a bad state, we always reboot the machine
638 # before machine_install.
Chris Sosab76e0ee2013-05-22 16:55:41 -0700639 self.reboot(timeout=self.REBOOT_TIMEOUT, wait=True)
Scott Zawalski89c44dd2013-02-26 09:28:02 -0500640 self.run('stop update-engine; start update-engine')
641 force_update = True
Dan Shi0f466e82013-02-22 15:44:58 -0800642
Chris Sosaa3ac2152012-05-23 22:23:13 -0700643 updater = autoupdater.ChromiumOSUpdater(update_url, host=self,
Chris Sosa72312602013-04-16 15:01:56 -0700644 local_devserver=local_devserver)
Dan Shi0f466e82013-02-22 15:44:58 -0800645 updated = False
Scott Zawalskieadbf702013-03-14 09:23:06 -0400646 # Remove cros-version and job_repo_url host attribute from host.
647 self.clear_cros_version_labels_and_job_repo_url()
Dan Shi0f466e82013-02-22 15:44:58 -0800648 # If the DUT is already running the same build, try stateful update
649 # first. Stateful update does not update kernel and tends to run much
650 # faster than a full reimage.
651 try:
Chris Sosab76e0ee2013-05-22 16:55:41 -0700652 updated = self._try_stateful_update(
653 update_url, force_update, updater)
Dan Shi0f466e82013-02-22 15:44:58 -0800654 if updated:
655 logging.info('DUT is updated with stateful update.')
656 except Exception as e:
657 logging.exception(e)
658 logging.warn('Failed to stateful update DUT, force to update.')
J. Richard Barnette45e93de2012-04-11 17:24:15 -0700659
Dan Shi0f466e82013-02-22 15:44:58 -0800660 inactive_kernel = None
661 # Do a full update if stateful update is not applicable or failed.
662 if not updated:
663 # In case the system is in a bad state, we always reboot the
664 # machine before machine_install.
Chris Sosab76e0ee2013-05-22 16:55:41 -0700665 self.reboot(timeout=self.REBOOT_TIMEOUT, wait=True)
Chris Sosab7612bc2013-03-21 10:32:37 -0700666
667 # TODO(sosa): Remove temporary hack to get rid of bricked machines
668 # that can't update due to a corrupted policy.
669 self.run('rm -rf /var/lib/whitelist')
670 self.run('touch /var/lib/whitelist')
671 self.run('chmod -w /var/lib/whitelist')
Scott Zawalskib550d5a2013-03-22 09:23:59 -0400672 self.run('stop update-engine; start update-engine')
Chris Sosab7612bc2013-03-21 10:32:37 -0700673
Dan Shi0f466e82013-02-22 15:44:58 -0800674 if updater.run_update(force_update):
675 updated = True
676 # Figure out active and inactive kernel.
677 active_kernel, inactive_kernel = updater.get_kernel_state()
J. Richard Barnette45e93de2012-04-11 17:24:15 -0700678
Dan Shi0f466e82013-02-22 15:44:58 -0800679 # Ensure inactive kernel has higher priority than active.
680 if (updater.get_kernel_priority(inactive_kernel)
681 < updater.get_kernel_priority(active_kernel)):
682 raise autoupdater.ChromiumOSError(
683 'Update failed. The priority of the inactive kernel'
684 ' partition is less than that of the active kernel'
685 ' partition.')
J. Richard Barnette45e93de2012-04-11 17:24:15 -0700686
Dan Shi0f466e82013-02-22 15:44:58 -0800687 # Updater has returned successfully; reboot the host.
Chris Sosab76e0ee2013-05-22 16:55:41 -0700688 self.reboot(timeout=self.REBOOT_TIMEOUT, wait=True)
J. Richard Barnette45e93de2012-04-11 17:24:15 -0700689
Dan Shi0f466e82013-02-22 15:44:58 -0800690 if updated:
691 self._post_update_processing(updater, inactive_kernel)
Scott Zawalskieadbf702013-03-14 09:23:06 -0400692 image_name = autoupdater.url_to_image_name(update_url)
Dan Shie9309262013-06-19 22:50:21 -0700693 self.add_cros_version_labels_and_job_repo_url(image_name)
Simran Basi13fa1ba2013-03-04 10:56:47 -0800694
J. Richard Barnette45e93de2012-04-11 17:24:15 -0700695 # Clean up any old autotest directories which may be lying around.
696 for path in global_config.global_config.get_config_value(
697 'AUTOSERV', 'client_autodir_paths', type=list):
698 self.run('rm -rf ' + path)
699
700
Dan Shi10e992b2013-08-30 11:02:59 -0700701 def show_update_engine_log(self):
702 """Output update engine log."""
703 logging.debug('Dumping %s', constants.UPDATE_ENGINE_LOG)
704 self.run('cat %s' % constants.UPDATE_ENGINE_LOG)
705
706
Richard Barnette82c35912012-11-20 10:09:10 -0800707 def _get_board_from_afe(self):
708 """Retrieve this host's board from its labels in the AFE.
709
710 Looks for a host label of the form "board:<board>", and
711 returns the "<board>" part of the label. `None` is returned
712 if there is not a single, unique label matching the pattern.
713
714 @returns board from label, or `None`.
715 """
Dan Shia1ecd5c2013-06-06 11:21:31 -0700716 return server_utils.get_board_from_afe(self.hostname, self._AFE)
Simran Basi833814b2013-01-29 13:13:43 -0800717
718
719 def get_build(self):
720 """Retrieve the current build for this Host from the AFE.
721
722 Looks through this host's labels in the AFE to determine its build.
723
724 @returns The current build or None if it could not find it or if there
725 were multiple build labels assigned to this host.
726 """
Dan Shia1ecd5c2013-06-06 11:21:31 -0700727 return server_utils.get_build_from_afe(self.hostname, self._AFE)
Richard Barnette82c35912012-11-20 10:09:10 -0800728
729
Scott Zawalski89c44dd2013-02-26 09:28:02 -0500730 def _install_repair(self):
731 """Attempt to repair this host using upate-engine.
732
733 If the host is up, try installing the DUT with a stable
734 "repair" version of Chrome OS as defined in the global_config
735 under CROS.stable_cros_version.
736
Scott Zawalski62bacae2013-03-05 10:40:32 -0500737 @raises AutoservRepairMethodNA if the DUT is not reachable.
738 @raises ChromiumOSError if the install failed for some reason.
Scott Zawalski89c44dd2013-02-26 09:28:02 -0500739
740 """
741 if not self.is_up():
Scott Zawalski62bacae2013-03-05 10:40:32 -0500742 raise error.AutoservRepairMethodNA('DUT unreachable for install.')
Scott Zawalski89c44dd2013-02-26 09:28:02 -0500743
744 logging.info('Attempting to reimage machine to repair image.')
745 try:
746 self.machine_install(repair=True)
Fang Dengd0672f32013-03-18 17:18:09 -0700747 except autoupdater.ChromiumOSError as e:
748 logging.exception(e)
Scott Zawalski89c44dd2013-02-26 09:28:02 -0500749 logging.info('Repair via install failed.')
Scott Zawalski62bacae2013-03-05 10:40:32 -0500750 raise
Scott Zawalski89c44dd2013-02-26 09:28:02 -0500751
752
Dan Shi2c88eed2013-11-12 10:18:38 -0800753 def _install_repair_with_powerwash(self):
Dan Shi9cc48452013-11-12 12:39:26 -0800754 """Attempt to powerwash first then repair this host using update-engine.
Dan Shi2c88eed2013-11-12 10:18:38 -0800755
Dan Shi9cc48452013-11-12 12:39:26 -0800756 update-engine may fail due to a bad image. In such case, powerwash
757 may help to cleanup the DUT for update-engine to work again.
Dan Shi2c88eed2013-11-12 10:18:38 -0800758
759 @raises AutoservRepairMethodNA if the DUT is not reachable.
760 @raises ChromiumOSError if the install failed for some reason.
761
762 """
763 if not self.is_up():
764 raise error.AutoservRepairMethodNA('DUT unreachable for install.')
765
766 logging.info('Attempting to powerwash the DUT.')
767 self.run('echo "fast safe" > '
768 '/mnt/stateful_partition/factory_install_reset')
769 self.reboot(timeout=self.POWERWASH_BOOT_TIMEOUT, wait=True)
770 if not self.is_up():
Dan Shi9cc48452013-11-12 12:39:26 -0800771 logging.error('Powerwash failed. DUT did not come back after '
Dan Shi2c88eed2013-11-12 10:18:38 -0800772 'reboot.')
773 raise error.AutoservRepairFailure(
774 'DUT failed to boot from powerwash after %d seconds' %
775 self.POWERWASH_BOOT_TIMEOUT)
776
777 logging.info('Powerwash succeeded.')
778 self._install_repair()
779
780
beepsf079cfb2013-09-18 17:49:51 -0700781 def servo_install(self, image_url=None, usb_boot_timeout=USB_BOOT_TIMEOUT,
782 install_timeout=INSTALL_TIMEOUT):
Scott Zawalski62bacae2013-03-05 10:40:32 -0500783 """
784 Re-install the OS on the DUT by:
785 1) installing a test image on a USB storage device attached to the Servo
786 board,
Richard Barnette03a0c132012-11-05 12:40:35 -0800787 2) booting that image in recovery mode, and then
J. Richard Barnette31b2e312013-04-04 16:05:22 -0700788 3) installing the image with chromeos-install.
789
Scott Zawalski62bacae2013-03-05 10:40:32 -0500790 @param image_url: If specified use as the url to install on the DUT.
791 otherwise boot the currently staged image on the USB stick.
beepsf079cfb2013-09-18 17:49:51 -0700792 @param usb_boot_timeout: The usb_boot_timeout to use during reimage.
793 Factory images need a longer usb_boot_timeout than regular
794 cros images.
795 @param install_timeout: The timeout to use when installing the chromeos
796 image. Factory images need a longer install_timeout.
Richard Barnette03a0c132012-11-05 12:40:35 -0800797
Scott Zawalski62bacae2013-03-05 10:40:32 -0500798 @raises AutoservError if the image fails to boot.
Richard Barnette03a0c132012-11-05 12:40:35 -0800799 """
beepsf079cfb2013-09-18 17:49:51 -0700800
801 usb_boot_timer_key = ('servo_install.usb_boot_timeout_%s'
802 % usb_boot_timeout)
803 logging.info('Downloading image to USB, then booting from it. Usb boot '
804 'timeout = %s', usb_boot_timeout)
805 timer = stats.Timer(usb_boot_timer_key)
806 timer.start()
J. Richard Barnette31b2e312013-04-04 16:05:22 -0700807 self.servo.install_recovery_image(image_url)
beepsf079cfb2013-09-18 17:49:51 -0700808 if not self.wait_up(timeout=usb_boot_timeout):
Scott Zawalski62bacae2013-03-05 10:40:32 -0500809 raise error.AutoservRepairFailure(
810 'DUT failed to boot from USB after %d seconds' %
beepsf079cfb2013-09-18 17:49:51 -0700811 usb_boot_timeout)
812 timer.stop()
Scott Zawalski62bacae2013-03-05 10:40:32 -0500813
beepsf079cfb2013-09-18 17:49:51 -0700814 install_timer_key = ('servo_install.install_timeout_%s'
815 % install_timeout)
816 timer = stats.Timer(install_timer_key)
817 timer.start()
818 logging.info('Installing image through chromeos-install.')
819 self.run('chromeos-install --yes', timeout=install_timeout)
820 timer.stop()
821
822 logging.info('Power cycling DUT through servo.')
Richard Barnette03a0c132012-11-05 12:40:35 -0800823 self.servo.power_long_press()
Fang Dengafb88142013-05-30 17:44:31 -0700824 self.servo.switch_usbkey('off')
J. Richard Barnettefbcc7122013-07-24 18:24:59 -0700825 # We *must* use power_on() here; on Parrot it's how we get
826 # out of recovery mode.
827 self.servo.get_power_state_controller().power_on()
beepsf079cfb2013-09-18 17:49:51 -0700828
829 logging.info('Waiting for DUT to come back up.')
Richard Barnette03a0c132012-11-05 12:40:35 -0800830 if not self.wait_up(timeout=self.BOOT_TIMEOUT):
831 raise error.AutoservError('DUT failed to reboot installed '
832 'test image after %d seconds' %
Scott Zawalski62bacae2013-03-05 10:40:32 -0500833 self.BOOT_TIMEOUT)
834
835
J. Richard Barnettee4af8b92013-05-01 13:16:12 -0700836 def _servo_repair_reinstall(self):
Scott Zawalski62bacae2013-03-05 10:40:32 -0500837 """Reinstall the DUT utilizing servo and a test image.
838
839 Re-install the OS on the DUT by:
840 1) installing a test image on a USB storage device attached to the Servo
841 board,
842 2) booting that image in recovery mode, and then
843 3) installing the image with chromeos-install.
844
Scott Zawalski62bacae2013-03-05 10:40:32 -0500845 @raises AutoservRepairMethodNA if the device does not have servo
846 support.
847
848 """
849 if not self.servo:
850 raise error.AutoservRepairMethodNA('Repair Reinstall NA: '
851 'DUT has no servo support.')
852
853 logging.info('Attempting to recovery servo enabled device with '
854 'servo_repair_reinstall')
855
J. Richard Barnettee4af8b92013-05-01 13:16:12 -0700856 image_url = self.stage_image_for_servo()
Scott Zawalski62bacae2013-03-05 10:40:32 -0500857 self.servo_install(image_url)
858
859
860 def _servo_repair_power(self):
861 """Attempt to repair DUT using an attached Servo.
862
863 Attempt to power on the DUT via power_long_press.
864
865 @raises AutoservRepairMethodNA if the device does not have servo
866 support.
867 @raises AutoservRepairFailure if the repair fails for any reason.
868 """
869 if not self.servo:
870 raise error.AutoservRepairMethodNA('Repair Power NA: '
871 'DUT has no servo support.')
872
873 logging.info('Attempting to recover servo enabled device by '
874 'powering it off and on.')
875 self.servo.get_power_state_controller().power_off()
876 self.servo.get_power_state_controller().power_on()
877 if self.wait_up(self.BOOT_TIMEOUT):
878 return
879
880 raise error.AutoservRepairFailure('DUT did not boot after long_press.')
Richard Barnette03a0c132012-11-05 12:40:35 -0800881
882
Richard Barnette82c35912012-11-20 10:09:10 -0800883 def _powercycle_to_repair(self):
884 """Utilize the RPM Infrastructure to bring the host back up.
885
886 If the host is not up/repaired after the first powercycle we utilize
887 auto fallback to the last good install by powercycling and rebooting the
888 host 6 times.
Scott Zawalski62bacae2013-03-05 10:40:32 -0500889
890 @raises AutoservRepairMethodNA if the device does not support remote
891 power.
892 @raises AutoservRepairFailure if the repair fails for any reason.
893
Richard Barnette82c35912012-11-20 10:09:10 -0800894 """
Scott Zawalski62bacae2013-03-05 10:40:32 -0500895 if not self.has_power():
896 raise error.AutoservRepairMethodNA('Device does not support power.')
897
Richard Barnette82c35912012-11-20 10:09:10 -0800898 logging.info('Attempting repair via RPM powercycle.')
899 failed_cycles = 0
900 self.power_cycle()
901 while not self.wait_up(timeout=self.BOOT_TIMEOUT):
902 failed_cycles += 1
903 if failed_cycles >= self._MAX_POWER_CYCLE_ATTEMPTS:
Scott Zawalski62bacae2013-03-05 10:40:32 -0500904 raise error.AutoservRepairFailure(
905 'Powercycled host %s %d times; device did not come back'
906 ' online.' % (self.hostname, failed_cycles))
Richard Barnette82c35912012-11-20 10:09:10 -0800907 self.power_cycle()
908 if failed_cycles == 0:
909 logging.info('Powercycling was successful first time.')
910 else:
911 logging.info('Powercycling was successful after %d failures.',
912 failed_cycles)
913
914
915 def repair_full(self):
916 """Repair a host for repair level NO_PROTECTION.
917
918 This overrides the base class function for repair; it does
919 not call back to the parent class, but instead offers a
920 simplified implementation based on the capabilities in the
921 Chrome OS test lab.
922
Fang Deng5d518f42013-08-02 14:04:32 -0700923 It first verifies and repairs servo if it is a DUT in CrOS
Fang Deng03590af2013-10-07 17:34:20 -0700924 lab and a servo is attached.
Fang Deng5d518f42013-08-02 14:04:32 -0700925
J. Richard Barnettefde55fc2013-03-15 17:47:01 -0700926 If `self.verify()` fails, the following procedures are
927 attempted:
928 1. Try to re-install to a known stable image using
929 auto-update.
Scott Zawalski62bacae2013-03-05 10:40:32 -0500930 2. If there's a servo for the DUT, try to power the DUT off and
931 on.
932 3. If there's a servo for the DUT, try to re-install via
J. Richard Barnettefde55fc2013-03-15 17:47:01 -0700933 the servo.
Scott Zawalski62bacae2013-03-05 10:40:32 -0500934 4. If the DUT can be power-cycled via RPM, try to repair
Richard Barnette82c35912012-11-20 10:09:10 -0800935 by power-cycling.
936
937 As with the parent method, the last operation performed on
938 the DUT must be to call `self.verify()`; if that call fails,
939 the exception it raises is passed back to the caller.
J. Richard Barnettefde55fc2013-03-15 17:47:01 -0700940
Scott Zawalski62bacae2013-03-05 10:40:32 -0500941 @raises AutoservRepairTotalFailure if the repair process fails to
942 fix the DUT.
Fang Deng5d518f42013-08-02 14:04:32 -0700943 @raises ServoHostRepairTotalFailure if the repair process fails to
944 fix the servo host if one is attached to the DUT.
945 @raises AutoservSshPermissionDeniedError if it is unable
946 to ssh to the servo host due to permission error.
947
Richard Barnette82c35912012-11-20 10:09:10 -0800948 """
Fang Deng5d518f42013-08-02 14:04:32 -0700949 if self._servo_host:
Fang Deng03590af2013-10-07 17:34:20 -0700950 try:
951 self.servo = self._servo_host.create_healthy_servo_object()
952 except Exception as e:
953 self.servo = None
954 logging.error('Could not create a healthy servo: %s', e)
Fang Deng5d518f42013-08-02 14:04:32 -0700955
Scott Zawalski62bacae2013-03-05 10:40:32 -0500956 # TODO(scottz): This should use something similar to label_decorator,
957 # but needs to be populated in order so DUTs are repaired with the
958 # least amount of effort.
Dan Shi2c88eed2013-11-12 10:18:38 -0800959 repair_funcs = [self._install_repair,
960 self._install_repair_with_powerwash,
961 self._servo_repair_power,
J. Richard Barnettee4af8b92013-05-01 13:16:12 -0700962 self._servo_repair_reinstall,
Scott Zawalski62bacae2013-03-05 10:40:32 -0500963 self._powercycle_to_repair]
964 errors = []
Simran Basie6130932013-10-01 14:07:52 -0700965 board = self._get_board_from_afe()
Scott Zawalski62bacae2013-03-05 10:40:32 -0500966 for repair_func in repair_funcs:
967 try:
968 repair_func()
969 self.verify()
Simran Basie6130932013-10-01 14:07:52 -0700970 stats.Counter(
971 '%s.SUCCEEDED' % repair_func.__name__).increment()
972 if board:
973 stats.Counter(
974 '%s.SUCCEEDED.%s' % (repair_func.__name__,
975 board)).increment()
Scott Zawalski62bacae2013-03-05 10:40:32 -0500976 return
Simran Basie6130932013-10-01 14:07:52 -0700977 except error.AutoservRepairMethodNA as e:
978 stats.Counter(
979 '%s.RepairNA' % repair_func.__name__).increment()
980 if board:
981 stats.Counter(
982 '%s.RepairNA.%s' % (repair_func.__name__,
983 board)).increment()
984 logging.warn('Repair function NA: %s', e)
985 errors.append(str(e))
Scott Zawalski62bacae2013-03-05 10:40:32 -0500986 except Exception as e:
Simran Basie6130932013-10-01 14:07:52 -0700987 stats.Counter(
988 '%s.FAILED' % repair_func.__name__).increment()
989 if board:
990 stats.Counter(
991 '%s.FAILED.%s' % (repair_func.__name__,
992 board)).increment()
Scott Zawalski62bacae2013-03-05 10:40:32 -0500993 logging.warn('Failed to repair device: %s', e)
994 errors.append(str(e))
Scott Zawalski89c44dd2013-02-26 09:28:02 -0500995
Simran Basie6130932013-10-01 14:07:52 -0700996 stats.Counter('Full_Repair_Failed').increment()
997 if board:
998 stats.Counter(
999 'Full_Repair_Failed.%s' % board).increment()
Scott Zawalski62bacae2013-03-05 10:40:32 -05001000 raise error.AutoservRepairTotalFailure(
1001 'All attempts at repairing the device failed:\n%s' %
1002 '\n'.join(errors))
Richard Barnette82c35912012-11-20 10:09:10 -08001003
1004
J. Richard Barnette1d78b012012-05-15 13:56:30 -07001005 def close(self):
beeps32a63082013-08-22 14:02:29 -07001006 self.rpc_disconnect_all()
Fang Deng0ca40e22013-08-27 17:47:44 -07001007 super(CrosHost, self).close()
J. Richard Barnette1d78b012012-05-15 13:56:30 -07001008
1009
Simran Basi5e6339a2013-03-21 11:34:32 -07001010 def _cleanup_poweron(self):
1011 """Special cleanup method to make sure hosts always get power back."""
1012 afe = frontend_wrappers.RetryingAFE(timeout_min=5, delay_sec=10)
1013 hosts = afe.get_hosts(hostname=self.hostname)
1014 if not hosts or not (self._RPM_OUTLET_CHANGED in
1015 hosts[0].attributes):
1016 return
1017 logging.debug('This host has recently interacted with the RPM'
1018 ' Infrastructure. Ensuring power is on.')
1019 try:
1020 self.power_on()
1021 except rpm_client.RemotePowerException:
1022 # If cleanup has completed but there was an issue with the RPM
1023 # Infrastructure, log an error message rather than fail cleanup
1024 logging.error('Failed to turn Power On for this host after '
1025 'cleanup through the RPM Infrastructure.')
1026 afe.set_host_attribute(self._RPM_OUTLET_CHANGED, None,
1027 hostname=self.hostname)
1028
1029
beepsc87ff602013-07-31 21:53:00 -07001030 def _is_factory_image(self):
1031 """Checks if the image on the DUT is a factory image.
1032
1033 @return: True if the image on the DUT is a factory image.
1034 False otherwise.
1035 """
1036 result = self.run('[ -f /root/.factory_test ]', ignore_status=True)
1037 return result.exit_status == 0
1038
1039
1040 def _restart_ui(self):
1041 """Restarts ui.
1042
1043 @raises: FactoryImageCheckerException for factory images, since
1044 we cannot attempt to restart ui on them.
1045 error.AutoservRunError for any other type of error that
1046 occurs while restarting ui.
1047 """
1048 if self._is_factory_image():
1049 raise FactoryImageCheckerException('Cannot restart ui on factory '
1050 'images')
1051
Chris Sosaf4d43ff2012-10-30 11:21:05 -07001052 client_at = autotest.Autotest(self)
beepsc87ff602013-07-31 21:53:00 -07001053 client_at.run_static_method('autotest_lib.client.cros.cros_ui',
1054 '_clear_login_prompt_state')
1055 self.run('restart ui')
1056 client_at.run_static_method('autotest_lib.client.cros.cros_ui',
1057 '_wait_for_login_prompt')
1058
1059
1060 def cleanup(self):
Richard Barnette82c35912012-11-20 10:09:10 -08001061 self.run('rm -f %s' % constants.CLEANUP_LOGS_PAUSED_FILE)
Scott Zawalskiddbc31e2012-11-15 11:29:01 -05001062 try:
beepsc87ff602013-07-31 21:53:00 -07001063 self._restart_ui()
1064 except (error.AutotestRunError, error.AutoservRunError,
1065 FactoryImageCheckerException):
Scott Zawalskiddbc31e2012-11-15 11:29:01 -05001066 logging.warn('Unable to restart ui, rebooting device.')
1067 # Since restarting the UI fails fall back to normal Autotest
1068 # cleanup routines, i.e. reboot the machine.
Fang Deng0ca40e22013-08-27 17:47:44 -07001069 super(CrosHost, self).cleanup()
Simran Basi5e6339a2013-03-21 11:34:32 -07001070 # Check if the rpm outlet was manipulated.
Simran Basid5e5e272012-09-24 15:23:59 -07001071 if self.has_power():
Simran Basi5e6339a2013-03-21 11:34:32 -07001072 self._cleanup_poweron()
J. Richard Barnette45e93de2012-04-11 17:24:15 -07001073
1074
Yu-Ju Honga2be94a2012-07-31 09:48:52 -07001075 def reboot(self, **dargs):
1076 """
1077 This function reboots the site host. The more generic
1078 RemoteHost.reboot() performs sync and sleeps for 5
1079 seconds. This is not necessary for Chrome OS devices as the
1080 sync should be finished in a short time during the reboot
1081 command.
1082 """
Tom Wai-Hong Tamf5cd1d42012-08-13 12:04:08 +08001083 if 'reboot_cmd' not in dargs:
1084 dargs['reboot_cmd'] = ('((reboot & sleep 10; reboot -f &)'
1085 ' </dev/null >/dev/null 2>&1 &)')
Yu-Ju Honga2be94a2012-07-31 09:48:52 -07001086 # Enable fastsync to avoid running extra sync commands before reboot.
Tom Wai-Hong Tamf5cd1d42012-08-13 12:04:08 +08001087 if 'fastsync' not in dargs:
1088 dargs['fastsync'] = True
Fang Deng0ca40e22013-08-27 17:47:44 -07001089 super(CrosHost, self).reboot(**dargs)
Yu-Ju Honga2be94a2012-07-31 09:48:52 -07001090
1091
J. Richard Barnette45e93de2012-04-11 17:24:15 -07001092 def verify_software(self):
Richard Barnetteb2bc13c2013-01-08 17:32:51 -08001093 """Verify working software on a Chrome OS system.
J. Richard Barnette45e93de2012-04-11 17:24:15 -07001094
Richard Barnetteb2bc13c2013-01-08 17:32:51 -08001095 Tests for the following conditions:
1096 1. All conditions tested by the parent version of this
1097 function.
1098 2. Sufficient space in /mnt/stateful_partition.
Fang Deng6b05f5b2013-03-20 13:42:11 -07001099 3. Sufficient space in /mnt/stateful_partition/encrypted.
1100 4. update_engine answers a simple status request over DBus.
J. Richard Barnette45e93de2012-04-11 17:24:15 -07001101
J. Richard Barnette45e93de2012-04-11 17:24:15 -07001102 """
Fang Deng0ca40e22013-08-27 17:47:44 -07001103 super(CrosHost, self).verify_software()
J. Richard Barnette45e93de2012-04-11 17:24:15 -07001104 self.check_diskspace(
1105 '/mnt/stateful_partition',
1106 global_config.global_config.get_config_value(
Fang Deng6b05f5b2013-03-20 13:42:11 -07001107 'SERVER', 'gb_diskspace_required', type=float,
1108 default=20.0))
1109 self.check_diskspace(
1110 '/mnt/stateful_partition/encrypted',
1111 global_config.global_config.get_config_value(
1112 'SERVER', 'gb_encrypted_diskspace_required', type=float,
1113 default=0.1))
beepsc87ff602013-07-31 21:53:00 -07001114
1115 # Factory images don't run update engine,
1116 # goofy controls dbus on these DUTs.
1117 if not self._is_factory_image():
1118 self.run('update_engine_client --status')
Scott Zawalskifbca4a92013-03-04 15:56:42 -05001119 # Makes sure python is present, loads and can use built in functions.
1120 # We have seen cases where importing cPickle fails with undefined
1121 # symbols in cPickle.so.
1122 self.run('python -c "import cPickle"')
J. Richard Barnette134ec2c2012-04-25 12:59:37 -07001123
1124
Fang Deng96667ca2013-08-01 17:46:18 -07001125 def make_ssh_command(self, user='root', port=22, opts='', hosts_file=None,
1126 connect_timeout=None, alive_interval=None):
1127 """Override default make_ssh_command to use options tuned for Chrome OS.
1128
1129 Tuning changes:
1130 - ConnectTimeout=30; maximum of 30 seconds allowed for an SSH
1131 connection failure. Consistency with remote_access.sh.
1132
1133 - ServerAliveInterval=180; which causes SSH to ping connection every
1134 180 seconds. In conjunction with ServerAliveCountMax ensures
1135 that if the connection dies, Autotest will bail out quickly.
1136 Originally tried 60 secs, but saw frequent job ABORTS where
1137 the test completed successfully.
1138
1139 - ServerAliveCountMax=3; consistency with remote_access.sh.
1140
1141 - ConnectAttempts=4; reduce flakiness in connection errors;
1142 consistency with remote_access.sh.
1143
1144 - UserKnownHostsFile=/dev/null; we don't care about the keys.
1145 Host keys change with every new installation, don't waste
1146 memory/space saving them.
1147
1148 - SSH protocol forced to 2; needed for ServerAliveInterval.
1149
1150 @param user User name to use for the ssh connection.
1151 @param port Port on the target host to use for ssh connection.
1152 @param opts Additional options to the ssh command.
1153 @param hosts_file Ignored.
1154 @param connect_timeout Ignored.
1155 @param alive_interval Ignored.
1156 """
Aviv Keshetc5947fa2013-09-04 14:06:29 -07001157 base_command = ('/usr/bin/ssh -a -x %s %s %s'
1158 ' -o StrictHostKeyChecking=no'
Fang Deng96667ca2013-08-01 17:46:18 -07001159 ' -o UserKnownHostsFile=/dev/null -o BatchMode=yes'
1160 ' -o ConnectTimeout=30 -o ServerAliveInterval=180'
1161 ' -o ServerAliveCountMax=3 -o ConnectionAttempts=4'
1162 ' -o Protocol=2 -l %s -p %d')
Aviv Keshetc5947fa2013-09-04 14:06:29 -07001163 return base_command % (self._ssh_verbosity_flag, self._ssh_options,
1164 opts, user, port)
Fang Deng96667ca2013-08-01 17:46:18 -07001165
1166
beeps32a63082013-08-22 14:02:29 -07001167 def _create_ssh_tunnel(self, port, local_port):
1168 """Create an ssh tunnel from local_port to port.
1169
1170 @param port: remote port on the host.
1171 @param local_port: local forwarding port.
1172
1173 @return: the tunnel process.
1174 """
1175 # Chrome OS on the target closes down most external ports
1176 # for security. We could open the port, but doing that
1177 # would conflict with security tests that check that only
1178 # expected ports are open. So, to get to the port on the
1179 # target we use an ssh tunnel.
1180 tunnel_options = '-n -N -q -L %d:localhost:%d' % (local_port, port)
1181 ssh_cmd = self.make_ssh_command(opts=tunnel_options)
1182 tunnel_cmd = '%s %s' % (ssh_cmd, self.hostname)
1183 logging.debug('Full tunnel command: %s', tunnel_cmd)
1184 tunnel_proc = subprocess.Popen(tunnel_cmd, shell=True, close_fds=True)
1185 logging.debug('Started ssh tunnel, local = %d'
1186 ' remote = %d, pid = %d',
1187 local_port, port, tunnel_proc.pid)
1188 return tunnel_proc
1189
1190
Christopher Wileydd181852013-10-10 19:56:58 -07001191 def _setup_rpc(self, port, command_name, remote_pid=None):
beeps32a63082013-08-22 14:02:29 -07001192 """Sets up a tunnel process and performs rpc connection book keeping.
1193
1194 This method assumes that xmlrpc and jsonrpc never conflict, since
1195 we can only either have an xmlrpc or a jsonrpc server listening on
1196 a remote port. As such, it enforces a single proxy->remote port
1197 policy, i.e if one starts a jsonrpc proxy/server from port A->B,
1198 and then tries to start an xmlrpc proxy forwarded to the same port,
1199 the xmlrpc proxy will override the jsonrpc tunnel process, however:
1200
1201 1. None of the methods on the xmlrpc proxy will work because
1202 the server listening on B is jsonrpc.
1203
1204 2. The xmlrpc client cannot initiate a termination of the JsonRPC
1205 server, as the only use case currently is goofy, which is tied to
1206 the factory image. It is much easier to handle a failed xmlrpc
1207 call on the client than it is to terminate goofy in this scenario,
1208 as doing the latter might leave the DUT in a hard to recover state.
1209
1210 With the current implementation newer rpc proxy connections will
1211 terminate the tunnel processes of older rpc connections tunneling
1212 to the same remote port. If methods are invoked on the client
1213 after this has happened they will fail with connection closed errors.
1214
1215 @param port: The remote forwarding port.
1216 @param command_name: The name of the remote process, to terminate
1217 using pkill.
1218
1219 @return A url that we can use to initiate the rpc connection.
1220 """
1221 self.rpc_disconnect(port)
1222 local_port = utils.get_unused_port()
1223 tunnel_proc = self._create_ssh_tunnel(port, local_port)
Christopher Wileydd181852013-10-10 19:56:58 -07001224 self._rpc_proxy_map[port] = (command_name, tunnel_proc, remote_pid)
beeps32a63082013-08-22 14:02:29 -07001225 return self._RPC_PROXY_URL % local_port
1226
1227
Christopher Wileyd78249a2013-03-01 13:05:31 -08001228 def xmlrpc_connect(self, command, port, command_name=None,
Yusuf Mohsinallyfff89d62013-11-18 16:34:07 -08001229 ready_test_name=None, timeout_seconds=10,
1230 logfile='/dev/null'):
J. Richard Barnette1d78b012012-05-15 13:56:30 -07001231 """Connect to an XMLRPC server on the host.
1232
1233 The `command` argument should be a simple shell command that
1234 starts an XMLRPC server on the given `port`. The command
1235 must not daemonize, and must terminate cleanly on SIGTERM.
1236 The command is started in the background on the host, and a
1237 local XMLRPC client for the server is created and returned
1238 to the caller.
1239
1240 Note that the process of creating an XMLRPC client makes no
1241 attempt to connect to the remote server; the caller is
1242 responsible for determining whether the server is running
1243 correctly, and is ready to serve requests.
1244
Christopher Wileyd78249a2013-03-01 13:05:31 -08001245 Optionally, the caller can pass ready_test_name, a string
1246 containing the name of a method to call on the proxy. This
1247 method should take no parameters and return successfully only
1248 when the server is ready to process client requests. When
1249 ready_test_name is set, xmlrpc_connect will block until the
1250 proxy is ready, and throw a TestError if the server isn't
1251 ready by timeout_seconds.
1252
beeps32a63082013-08-22 14:02:29 -07001253 If a server is already running on the remote port, this
1254 method will kill it and disconnect the tunnel process
1255 associated with the connection before establishing a new one,
1256 by consulting the rpc_proxy_map in rpc_disconnect.
1257
J. Richard Barnette1d78b012012-05-15 13:56:30 -07001258 @param command Shell command to start the server.
1259 @param port Port number on which the server is expected to
1260 be serving.
J. Richard Barnette7214e0b2013-02-06 15:20:49 -08001261 @param command_name String to use as input to `pkill` to
1262 terminate the XMLRPC server on the host.
Christopher Wileyd78249a2013-03-01 13:05:31 -08001263 @param ready_test_name String containing the name of a
1264 method defined on the XMLRPC server.
1265 @param timeout_seconds Number of seconds to wait
1266 for the server to become 'ready.' Will throw a
1267 TestFail error if server is not ready in time.
Yusuf Mohsinallyfff89d62013-11-18 16:34:07 -08001268 @param logfile Logfile to send output when running
1269 'command' argument.
Yusuf Mohsinally8d19e3c2013-11-21 14:25:45 -08001270
J. Richard Barnette1d78b012012-05-15 13:56:30 -07001271 """
Christopher Wileyc14f06a2013-10-16 13:55:39 -07001272 # Clean up any existing state. If the caller is willing
1273 # to believe their server is down, we ought to clean up
1274 # any tunnels we might have sitting around.
1275 self.rpc_disconnect(port)
J. Richard Barnette1d78b012012-05-15 13:56:30 -07001276 # Start the server on the host. Redirection in the command
1277 # below is necessary, because 'ssh' won't terminate until
1278 # background child processes close stdin, stdout, and
1279 # stderr.
Yusuf Mohsinallyfff89d62013-11-18 16:34:07 -08001280 remote_cmd = '%s </dev/null >%s 2>&1 & echo $!' % (command, logfile)
Christopher Wileydd181852013-10-10 19:56:58 -07001281 remote_pid = self.run(remote_cmd).stdout.rstrip('\n')
J. Richard Barnette1d78b012012-05-15 13:56:30 -07001282 logging.debug('Started XMLRPC server on host %s, pid = %s',
1283 self.hostname, remote_pid)
1284
Christopher Wileydd181852013-10-10 19:56:58 -07001285 # Tunnel through SSH to be able to reach that remote port.
1286 rpc_url = self._setup_rpc(port, command_name, remote_pid=remote_pid)
Christopher Wileyd78249a2013-03-01 13:05:31 -08001287 proxy = xmlrpclib.ServerProxy(rpc_url, allow_none=True)
Christopher Wileydd181852013-10-10 19:56:58 -07001288
Christopher Wileyd78249a2013-03-01 13:05:31 -08001289 if ready_test_name is not None:
J. Richard Barnette13eb7c02013-03-07 12:06:29 -08001290 # retry.retry logs each attempt; calculate delay_sec to
1291 # keep log spam to a dull roar.
Christopher Wiley0ed712b2013-04-09 15:25:12 -07001292 @retry.retry((socket.error,
1293 xmlrpclib.ProtocolError,
1294 httplib.BadStatusLine),
Chris Sosa65425082013-10-16 13:26:22 -07001295 timeout_min=timeout_seconds / 60.0,
1296 delay_sec=min(max(timeout_seconds / 20.0, 0.1), 1))
Christopher Wileyd78249a2013-03-01 13:05:31 -08001297 def ready_test():
1298 """ Call proxy.ready_test_name(). """
1299 getattr(proxy, ready_test_name)()
1300 successful = False
1301 try:
1302 logging.info('Waiting %d seconds for XMLRPC server '
1303 'to start.', timeout_seconds)
1304 ready_test()
1305 successful = True
Christopher Wileyd78249a2013-03-01 13:05:31 -08001306 finally:
1307 if not successful:
1308 logging.error('Failed to start XMLRPC server.')
beeps32a63082013-08-22 14:02:29 -07001309 self.rpc_disconnect(port)
Christopher Wileyd78249a2013-03-01 13:05:31 -08001310 logging.info('XMLRPC server started successfully.')
1311 return proxy
J. Richard Barnette1d78b012012-05-15 13:56:30 -07001312
J. Richard Barnette1d78b012012-05-15 13:56:30 -07001313
Jason Abeleb6f924f2013-11-13 16:01:54 -08001314 def syslog(self, message, tag='autotest'):
1315 """Logs a message to syslog on host.
1316
1317 @param message String message to log into syslog
1318 @param tag String tag prefix for syslog
1319
1320 """
1321 self.run('logger -t "%s" "%s"' % (tag, message))
1322
1323
beeps32a63082013-08-22 14:02:29 -07001324 def jsonrpc_connect(self, port):
1325 """Creates a jsonrpc proxy connection through an ssh tunnel.
1326
1327 This method exists to facilitate communication with goofy (which is
1328 the default system manager on all factory images) and as such, leaves
1329 most of the rpc server sanity checking to the caller. Unlike
1330 xmlrpc_connect, this method does not facilitate the creation of a remote
1331 jsonrpc server, as the only clients of this code are factory tests,
1332 for which the goofy system manager is built in to the image and starts
1333 when the target boots.
1334
1335 One can theoretically create multiple jsonrpc proxies all forwarded
1336 to the same remote port, provided the remote port has an rpc server
1337 listening. However, in doing so we stand the risk of leaking an
1338 existing tunnel process, so we always disconnect any older tunnels
1339 we might have through rpc_disconnect.
1340
1341 @param port: port on the remote host that is serving this proxy.
1342
1343 @return: The client proxy.
1344 """
1345 if not jsonrpclib:
1346 logging.warning('Jsonrpclib could not be imported. Check that '
1347 'site-packages contains jsonrpclib.')
1348 return None
1349
1350 proxy = jsonrpclib.jsonrpc.ServerProxy(self._setup_rpc(port, None))
1351
1352 logging.info('Established a jsonrpc connection through port %s.', port)
1353 return proxy
1354
1355
1356 def rpc_disconnect(self, port):
1357 """Disconnect from an RPC server on the host.
1358
1359 Terminates the remote RPC server previously started for
J. Richard Barnette1d78b012012-05-15 13:56:30 -07001360 the given `port`. Also closes the local ssh tunnel created
1361 for the connection to the host. This function does not
beeps32a63082013-08-22 14:02:29 -07001362 directly alter the state of a previously returned RPC
J. Richard Barnette1d78b012012-05-15 13:56:30 -07001363 client object; however disconnection will cause all
1364 subsequent calls to methods on the object to fail.
1365
1366 This function does nothing if requested to disconnect a port
beeps32a63082013-08-22 14:02:29 -07001367 that was not previously connected via _setup_rpc.
J. Richard Barnette1d78b012012-05-15 13:56:30 -07001368
1369 @param port Port number passed to a previous call to
beeps32a63082013-08-22 14:02:29 -07001370 `_setup_rpc()`.
J. Richard Barnette1d78b012012-05-15 13:56:30 -07001371 """
beeps32a63082013-08-22 14:02:29 -07001372 if port not in self._rpc_proxy_map:
J. Richard Barnette1d78b012012-05-15 13:56:30 -07001373 return
Christopher Wileydd181852013-10-10 19:56:58 -07001374 remote_name, tunnel_proc, remote_pid = self._rpc_proxy_map[port]
J. Richard Barnette1d78b012012-05-15 13:56:30 -07001375 if remote_name:
1376 # We use 'pkill' to find our target process rather than
1377 # a PID, because the host may have rebooted since
1378 # connecting, and we don't want to kill an innocent
1379 # process with the same PID.
1380 #
1381 # 'pkill' helpfully exits with status 1 if no target
1382 # process is found, for which run() will throw an
Simran Basid5e5e272012-09-24 15:23:59 -07001383 # exception. We don't want that, so we the ignore
J. Richard Barnette1d78b012012-05-15 13:56:30 -07001384 # status.
1385 self.run("pkill -f '%s'" % remote_name, ignore_status=True)
Christopher Wileydd181852013-10-10 19:56:58 -07001386 if remote_pid:
1387 logging.info('Waiting for RPC server "%s" shutdown',
1388 remote_name)
1389 start_time = time.time()
1390 while (time.time() - start_time <
1391 self._RPC_SHUTDOWN_TIMEOUT_SECONDS):
1392 running_processes = self.run(
1393 "pgrep -f '%s'" % remote_name,
1394 ignore_status=True).stdout.split()
1395 if not remote_pid in running_processes:
1396 logging.info('Shut down RPC server.')
1397 break
1398 time.sleep(self._RPC_SHUTDOWN_POLLING_PERIOD_SECONDS)
1399 else:
1400 raise error.TestError('Failed to shutdown RPC server %s' %
1401 remote_name)
J. Richard Barnette1d78b012012-05-15 13:56:30 -07001402
1403 if tunnel_proc.poll() is None:
1404 tunnel_proc.terminate()
1405 logging.debug('Terminated tunnel, pid %d', tunnel_proc.pid)
1406 else:
1407 logging.debug('Tunnel pid %d terminated early, status %d',
1408 tunnel_proc.pid, tunnel_proc.returncode)
beeps32a63082013-08-22 14:02:29 -07001409 del self._rpc_proxy_map[port]
J. Richard Barnette1d78b012012-05-15 13:56:30 -07001410
1411
beeps32a63082013-08-22 14:02:29 -07001412 def rpc_disconnect_all(self):
1413 """Disconnect all known RPC proxy ports."""
1414 for port in self._rpc_proxy_map.keys():
1415 self.rpc_disconnect(port)
J. Richard Barnette1d78b012012-05-15 13:56:30 -07001416
1417
J. Richard Barnetteb6de7e32013-02-14 13:28:04 -08001418 def _ping_check_status(self, status):
1419 """Ping the host once, and return whether it has a given status.
J. Richard Barnette134ec2c2012-04-25 12:59:37 -07001420
J. Richard Barnetteb6de7e32013-02-14 13:28:04 -08001421 @param status Check the ping status against this value.
1422 @return True iff `status` and the result of ping are the same
1423 (i.e. both True or both False).
1424
1425 """
1426 ping_val = utils.ping(self.hostname, tries=1, deadline=1)
1427 return not (status ^ (ping_val == 0))
1428
1429 def _ping_wait_for_status(self, status, timeout):
1430 """Wait for the host to have a given status (UP or DOWN).
1431
1432 Status is checked by polling. Polling will not last longer
1433 than the number of seconds in `timeout`. The polling
1434 interval will be long enough that only approximately
1435 _PING_WAIT_COUNT polling cycles will be executed, subject
1436 to a maximum interval of about one minute.
1437
1438 @param status Waiting will stop immediately if `ping` of the
1439 host returns this status.
1440 @param timeout Poll for at most this many seconds.
1441 @return True iff the host status from `ping` matched the
1442 requested status at the time of return.
1443
1444 """
1445 # _ping_check_status() takes about 1 second, hence the
1446 # "- 1" in the formula below.
1447 poll_interval = min(int(timeout / self._PING_WAIT_COUNT), 60) - 1
1448 end_time = time.time() + timeout
1449 while time.time() <= end_time:
1450 if self._ping_check_status(status):
1451 return True
1452 if poll_interval > 0:
1453 time.sleep(poll_interval)
1454
1455 # The last thing we did was sleep(poll_interval), so it may
1456 # have been too long since the last `ping`. Check one more
1457 # time, just to be sure.
1458 return self._ping_check_status(status)
1459
1460 def ping_wait_up(self, timeout):
1461 """Wait for the host to respond to `ping`.
1462
1463 N.B. This method is not a reliable substitute for
1464 `wait_up()`, because a host that responds to ping will not
1465 necessarily respond to ssh. This method should only be used
1466 if the target DUT can be considered functional even if it
1467 can't be reached via ssh.
1468
1469 @param timeout Minimum time to allow before declaring the
1470 host to be non-responsive.
1471 @return True iff the host answered to ping before the timeout.
1472
1473 """
1474 return self._ping_wait_for_status(self._PING_STATUS_UP, timeout)
J. Richard Barnette134ec2c2012-04-25 12:59:37 -07001475
Andrew Bresticker678c0c72013-01-22 10:44:09 -08001476 def ping_wait_down(self, timeout):
J. Richard Barnette134ec2c2012-04-25 12:59:37 -07001477 """Wait until the host no longer responds to `ping`.
1478
J. Richard Barnetteb6de7e32013-02-14 13:28:04 -08001479 This function can be used as a slightly faster version of
1480 `wait_down()`, by avoiding potentially long ssh timeouts.
1481
1482 @param timeout Minimum time to allow for the host to become
1483 non-responsive.
1484 @return True iff the host quit answering ping before the
1485 timeout.
1486
J. Richard Barnette134ec2c2012-04-25 12:59:37 -07001487 """
J. Richard Barnetteb6de7e32013-02-14 13:28:04 -08001488 return self._ping_wait_for_status(self._PING_STATUS_DOWN, timeout)
J. Richard Barnette134ec2c2012-04-25 12:59:37 -07001489
1490 def test_wait_for_sleep(self):
1491 """Wait for the client to enter low-power sleep mode.
1492
1493 The test for "is asleep" can't distinguish a system that is
1494 powered off; to confirm that the unit was asleep, it is
1495 necessary to force resume, and then call
1496 `test_wait_for_resume()`.
1497
1498 This function is expected to be called from a test as part
1499 of a sequence like the following:
1500
1501 ~~~~~~~~
1502 boot_id = host.get_boot_id()
1503 # trigger sleep on the host
1504 host.test_wait_for_sleep()
1505 # trigger resume on the host
1506 host.test_wait_for_resume(boot_id)
1507 ~~~~~~~~
1508
1509 @exception TestFail The host did not go to sleep within
1510 the allowed time.
1511 """
Andrew Bresticker678c0c72013-01-22 10:44:09 -08001512 if not self.ping_wait_down(timeout=self.SLEEP_TIMEOUT):
J. Richard Barnette134ec2c2012-04-25 12:59:37 -07001513 raise error.TestFail(
1514 'client failed to sleep after %d seconds' %
J. Richard Barnetteeb69d722012-06-18 17:29:44 -07001515 self.SLEEP_TIMEOUT)
J. Richard Barnette134ec2c2012-04-25 12:59:37 -07001516
1517
1518 def test_wait_for_resume(self, old_boot_id):
1519 """Wait for the client to resume from low-power sleep mode.
1520
1521 The `old_boot_id` parameter should be the value from
1522 `get_boot_id()` obtained prior to entering sleep mode. A
1523 `TestFail` exception is raised if the boot id changes.
1524
1525 See @ref test_wait_for_sleep for more on this function's
1526 usage.
1527
J. Richard Barnette7214e0b2013-02-06 15:20:49 -08001528 @param old_boot_id A boot id value obtained before the
J. Richard Barnette134ec2c2012-04-25 12:59:37 -07001529 target host went to sleep.
1530
1531 @exception TestFail The host did not respond within the
1532 allowed time.
1533 @exception TestFail The host responded, but the boot id test
1534 indicated a reboot rather than a sleep
1535 cycle.
1536 """
J. Richard Barnetteeb69d722012-06-18 17:29:44 -07001537 if not self.wait_up(timeout=self.RESUME_TIMEOUT):
J. Richard Barnette134ec2c2012-04-25 12:59:37 -07001538 raise error.TestFail(
1539 'client failed to resume from sleep after %d seconds' %
J. Richard Barnetteeb69d722012-06-18 17:29:44 -07001540 self.RESUME_TIMEOUT)
J. Richard Barnette134ec2c2012-04-25 12:59:37 -07001541 else:
1542 new_boot_id = self.get_boot_id()
1543 if new_boot_id != old_boot_id:
1544 raise error.TestFail(
1545 'client rebooted, but sleep was expected'
1546 ' (old boot %s, new boot %s)'
1547 % (old_boot_id, new_boot_id))
1548
1549
1550 def test_wait_for_shutdown(self):
1551 """Wait for the client to shut down.
1552
1553 The test for "has shut down" can't distinguish a system that
1554 is merely asleep; to confirm that the unit was down, it is
1555 necessary to force boot, and then call test_wait_for_boot().
1556
1557 This function is expected to be called from a test as part
1558 of a sequence like the following:
1559
1560 ~~~~~~~~
1561 boot_id = host.get_boot_id()
1562 # trigger shutdown on the host
1563 host.test_wait_for_shutdown()
1564 # trigger boot on the host
1565 host.test_wait_for_boot(boot_id)
1566 ~~~~~~~~
1567
1568 @exception TestFail The host did not shut down within the
1569 allowed time.
1570 """
Andrew Bresticker678c0c72013-01-22 10:44:09 -08001571 if not self.ping_wait_down(timeout=self.SHUTDOWN_TIMEOUT):
J. Richard Barnette134ec2c2012-04-25 12:59:37 -07001572 raise error.TestFail(
1573 'client failed to shut down after %d seconds' %
J. Richard Barnetteeb69d722012-06-18 17:29:44 -07001574 self.SHUTDOWN_TIMEOUT)
J. Richard Barnette134ec2c2012-04-25 12:59:37 -07001575
1576
1577 def test_wait_for_boot(self, old_boot_id=None):
1578 """Wait for the client to boot from cold power.
1579
1580 The `old_boot_id` parameter should be the value from
1581 `get_boot_id()` obtained prior to shutting down. A
1582 `TestFail` exception is raised if the boot id does not
1583 change. The boot id test is omitted if `old_boot_id` is not
1584 specified.
1585
1586 See @ref test_wait_for_shutdown for more on this function's
1587 usage.
1588
J. Richard Barnette7214e0b2013-02-06 15:20:49 -08001589 @param old_boot_id A boot id value obtained before the
J. Richard Barnette134ec2c2012-04-25 12:59:37 -07001590 shut down.
1591
1592 @exception TestFail The host did not respond within the
1593 allowed time.
1594 @exception TestFail The host responded, but the boot id test
1595 indicated that there was no reboot.
1596 """
J. Richard Barnetteeb69d722012-06-18 17:29:44 -07001597 if not self.wait_up(timeout=self.REBOOT_TIMEOUT):
J. Richard Barnette134ec2c2012-04-25 12:59:37 -07001598 raise error.TestFail(
1599 'client failed to reboot after %d seconds' %
J. Richard Barnetteeb69d722012-06-18 17:29:44 -07001600 self.REBOOT_TIMEOUT)
J. Richard Barnette134ec2c2012-04-25 12:59:37 -07001601 elif old_boot_id:
1602 if self.get_boot_id() == old_boot_id:
1603 raise error.TestFail(
1604 'client is back up, but did not reboot'
1605 ' (boot %s)' % old_boot_id)
Simran Basid5e5e272012-09-24 15:23:59 -07001606
1607
1608 @staticmethod
1609 def check_for_rpm_support(hostname):
1610 """For a given hostname, return whether or not it is powered by an RPM.
1611
Simran Basi1df55112013-09-06 11:25:09 -07001612 @param hostname: hostname to check for rpm support.
1613
Simran Basid5e5e272012-09-24 15:23:59 -07001614 @return None if this host does not follows the defined naming format
1615 for RPM powered DUT's in the lab. If it does follow the format,
1616 it returns a regular expression MatchObject instead.
1617 """
Fang Deng0ca40e22013-08-27 17:47:44 -07001618 return re.match(CrosHost._RPM_HOSTNAME_REGEX, hostname)
Simran Basid5e5e272012-09-24 15:23:59 -07001619
1620
1621 def has_power(self):
1622 """For this host, return whether or not it is powered by an RPM.
1623
1624 @return True if this host is in the CROS lab and follows the defined
1625 naming format.
1626 """
Fang Deng0ca40e22013-08-27 17:47:44 -07001627 return CrosHost.check_for_rpm_support(self.hostname)
Simran Basid5e5e272012-09-24 15:23:59 -07001628
1629
Ismail Noorbasha07fdb612013-02-14 14:13:31 -08001630 def _set_power(self, state, power_method):
1631 """Sets the power to the host via RPM, Servo or manual.
1632
1633 @param state Specifies which power state to set to DUT
1634 @param power_method Specifies which method of power control to
1635 use. By default "RPM" will be used. Valid values
1636 are the strings "RPM", "manual", "servoj10".
1637
1638 """
1639 ACCEPTABLE_STATES = ['ON', 'OFF']
1640
1641 if state.upper() not in ACCEPTABLE_STATES:
1642 raise error.TestError('State must be one of: %s.'
1643 % (ACCEPTABLE_STATES,))
1644
1645 if power_method == self.POWER_CONTROL_SERVO:
1646 logging.info('Setting servo port J10 to %s', state)
1647 self.servo.set('prtctl3_pwren', state.lower())
1648 time.sleep(self._USB_POWER_TIMEOUT)
1649 elif power_method == self.POWER_CONTROL_MANUAL:
1650 logging.info('You have %d seconds to set the AC power to %s.',
1651 self._POWER_CYCLE_TIMEOUT, state)
1652 time.sleep(self._POWER_CYCLE_TIMEOUT)
1653 else:
1654 if not self.has_power():
1655 raise error.TestFail('DUT does not have RPM connected.')
Simran Basi5e6339a2013-03-21 11:34:32 -07001656 afe = frontend_wrappers.RetryingAFE(timeout_min=5, delay_sec=10)
1657 afe.set_host_attribute(self._RPM_OUTLET_CHANGED, True,
1658 hostname=self.hostname)
Simran Basi1df55112013-09-06 11:25:09 -07001659 rpm_client.set_power(self.hostname, state.upper(), timeout_mins=5)
Simran Basid5e5e272012-09-24 15:23:59 -07001660
1661
Ismail Noorbasha07fdb612013-02-14 14:13:31 -08001662 def power_off(self, power_method=POWER_CONTROL_RPM):
1663 """Turn off power to this host via RPM, Servo or manual.
1664
1665 @param power_method Specifies which method of power control to
1666 use. By default "RPM" will be used. Valid values
1667 are the strings "RPM", "manual", "servoj10".
1668
1669 """
1670 self._set_power('OFF', power_method)
Simran Basid5e5e272012-09-24 15:23:59 -07001671
1672
Ismail Noorbasha07fdb612013-02-14 14:13:31 -08001673 def power_on(self, power_method=POWER_CONTROL_RPM):
1674 """Turn on power to this host via RPM, Servo or manual.
1675
1676 @param power_method Specifies which method of power control to
1677 use. By default "RPM" will be used. Valid values
1678 are the strings "RPM", "manual", "servoj10".
1679
1680 """
1681 self._set_power('ON', power_method)
1682
1683
1684 def power_cycle(self, power_method=POWER_CONTROL_RPM):
1685 """Cycle power to this host by turning it OFF, then ON.
1686
1687 @param power_method Specifies which method of power control to
1688 use. By default "RPM" will be used. Valid values
1689 are the strings "RPM", "manual", "servoj10".
1690
1691 """
1692 if power_method in (self.POWER_CONTROL_SERVO,
1693 self.POWER_CONTROL_MANUAL):
1694 self.power_off(power_method=power_method)
1695 time.sleep(self._POWER_CYCLE_TIMEOUT)
1696 self.power_on(power_method=power_method)
1697 else:
1698 rpm_client.set_power(self.hostname, 'CYCLE')
Simran Basic6f1f7a2012-10-16 10:47:46 -07001699
1700
1701 def get_platform(self):
1702 """Determine the correct platform label for this host.
1703
1704 @returns a string representing this host's platform.
1705 """
1706 crossystem = utils.Crossystem(self)
1707 crossystem.init()
1708 # Extract fwid value and use the leading part as the platform id.
1709 # fwid generally follow the format of {platform}.{firmware version}
1710 # Example: Alex.X.YYY.Z or Google_Alex.X.YYY.Z
1711 platform = crossystem.fwid().split('.')[0].lower()
1712 # Newer platforms start with 'Google_' while the older ones do not.
1713 return platform.replace('google_', '')
1714
1715
Aviv Keshet74c89a92013-02-04 15:18:30 -08001716 @label_decorator()
Simran Basic6f1f7a2012-10-16 10:47:46 -07001717 def get_board(self):
1718 """Determine the correct board label for this host.
1719
1720 @returns a string representing this host's board.
1721 """
1722 release_info = utils.parse_cmd_output('cat /etc/lsb-release',
1723 run_method=self.run)
1724 board = release_info['CHROMEOS_RELEASE_BOARD']
1725 # Devices in the lab generally have the correct board name but our own
1726 # development devices have {board_name}-signed-{key_type}. The board
1727 # name may also begin with 'x86-' which we need to keep.
Simran Basi833814b2013-01-29 13:13:43 -08001728 board_format_string = ds_constants.BOARD_PREFIX + '%s'
Simran Basic6f1f7a2012-10-16 10:47:46 -07001729 if 'x86' not in board:
Simran Basi833814b2013-01-29 13:13:43 -08001730 return board_format_string % board.split('-')[0]
1731 return board_format_string % '-'.join(board.split('-')[0:2])
Simran Basic6f1f7a2012-10-16 10:47:46 -07001732
1733
Aviv Keshet74c89a92013-02-04 15:18:30 -08001734 @label_decorator('lightsensor')
Simran Basic6f1f7a2012-10-16 10:47:46 -07001735 def has_lightsensor(self):
1736 """Determine the correct board label for this host.
1737
1738 @returns the string 'lightsensor' if this host has a lightsensor or
1739 None if it does not.
1740 """
1741 search_cmd = "find -L %s -maxdepth 4 | egrep '%s'" % (
Richard Barnette82c35912012-11-20 10:09:10 -08001742 self._LIGHTSENSOR_SEARCH_DIR, '|'.join(self._LIGHTSENSOR_FILES))
Simran Basic6f1f7a2012-10-16 10:47:46 -07001743 try:
1744 # Run the search cmd following the symlinks. Stderr_tee is set to
1745 # None as there can be a symlink loop, but the command will still
1746 # execute correctly with a few messages printed to stderr.
1747 self.run(search_cmd, stdout_tee=None, stderr_tee=None)
1748 return 'lightsensor'
1749 except error.AutoservRunError:
1750 # egrep exited with a return code of 1 meaning none of the possible
1751 # lightsensor files existed.
1752 return None
1753
1754
Aviv Keshet74c89a92013-02-04 15:18:30 -08001755 @label_decorator('bluetooth')
Simran Basic6f1f7a2012-10-16 10:47:46 -07001756 def has_bluetooth(self):
1757 """Determine the correct board label for this host.
1758
1759 @returns the string 'bluetooth' if this host has bluetooth or
1760 None if it does not.
1761 """
1762 try:
1763 self.run('test -d /sys/class/bluetooth/hci0')
1764 # test exited with a return code of 0.
1765 return 'bluetooth'
1766 except error.AutoservRunError:
1767 # test exited with a return code 1 meaning the directory did not
1768 # exist.
1769 return None
1770
1771
Ilja Friedel0ce0b602013-08-15 18:45:27 -07001772 @label_decorator('graphics')
1773 def get_graphics(self):
1774 """
1775 Determine the correct board label for this host.
1776
1777 @returns a string representing this host's graphics. For now ARM boards
1778 return graphics:gles while all other boards return graphics:gl. This
1779 may change over time, but for robustness reasons this should avoid
1780 executing code in actual graphics libraries (which may not be ready and
1781 is tested by graphics_GLAPICheck).
1782 """
1783 uname = self.run('uname -a').stdout.lower()
1784 if 'arm' in uname:
1785 return 'graphics:gles'
1786 return 'graphics:gl'
1787
1788
Simran Basic6f1f7a2012-10-16 10:47:46 -07001789 def get_labels(self):
1790 """Return a list of labels for this given host.
1791
1792 This is the main way to retrieve all the automatic labels for a host
1793 as it will run through all the currently implemented label functions.
1794 """
1795 labels = []
Richard Barnette82c35912012-11-20 10:09:10 -08001796 for label_function in self._LABEL_FUNCTIONS:
Simran Basic6f1f7a2012-10-16 10:47:46 -07001797 label = label_function(self)
1798 if label:
1799 labels.append(label)
1800 return labels