[autotest] Eureka host.
Creates a eureka host capable of verifying, cleanup, reboot
and machine_install. Also implements the following chagnes:
1. Pipes a base command through is_up, since different ssh
hosts are bound to have different things pre-installed.
2. Changes some timeouts to integers to avoid asserts.
3. Make host factory a little more generic, and check
for chromeos host first.
4. Adds a method to remote wget to server/site_utils.
TEST=Tried verify, cleanup, reoboot and machine_install.
BUG=chromium:292629, chromium:273843
Change-Id: I62a700614838569c6d77184b3c3339e914f4b456
Reviewed-on: https://chromium-review.googlesource.com/176303
Tested-by: Prashanth B <[email protected]>
Reviewed-by: Scott Zawalski <[email protected]>
Reviewed-by: Simran Basi <[email protected]>
Commit-Queue: Prashanth B <[email protected]>
diff --git a/server/cros/eureka_client.py b/server/cros/eureka_client.py
new file mode 100644
index 0000000..1a7f1ba
--- /dev/null
+++ b/server/cros/eureka_client.py
@@ -0,0 +1,135 @@
+# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import httplib
+import json
+import logging
+import socket
+import time
+import urllib2
+
+import common
+
+from autotest_lib.client.common_lib import error
+from autotest_lib.client.common_lib.cros import retry
+
+# Give all our rpcs about six seconds of retry time. If a longer timeout
+# is desired one should retry from the caller, this timeout is only meant
+# to avoid uncontrolled circumstances like network flake, not, say, retry
+# right across a reboot.
+BASE_REQUEST_TIMEOUT = 0.1
+JSON_HEADERS = {'Content-Type': 'application/json'}
+RPC_EXCEPTIONS = (httplib.BadStatusLine, socket.error, urllib2.HTTPError)
+
+
[email protected](RPC_EXCEPTIONS, timeout_min=BASE_REQUEST_TIMEOUT)
+def _get(url):
+ """Get request to the give url.
+
+ @raises: Any of the retry exceptions, if we hit the timeout.
+ @raises: error.TimeoutException, if the call itself times out.
+ eg: a hanging urlopen will get killed with a TimeoutException while
+ multiple retries that hit different Http errors will raise the last
+ HttpError instead of the TimeoutException.
+ """
+ return urllib2.urlopen(url).read()
+
+
[email protected](RPC_EXCEPTIONS, timeout_min=BASE_REQUEST_TIMEOUT)
+def _post(url, data):
+ """Post data to the given url.
+
+ @param data: Json data to post.
+
+ @raises: Any of the retry exceptions, if we hit the timeout.
+ @raises: error.TimeoutException, if the call itself times out.
+ For examples see docstring for _get method.
+ """
+ request = urllib2.Request(url, json.dumps(data),
+ headers=JSON_HEADERS)
+ urllib2.urlopen(request)
+
+
+class EurekaProxyException(Exception):
+ """Generic exception raised when a eureka rpc fails."""
+ pass
+
+
+class EurekaProxy(object):
+ """Client capable of making calls to the eureka device server."""
+ POLLING_INTERVAL = 5
+ SETUP_SERVER_PORT = '8008'
+ EUREKA_SETUP_SERVER = 'http://%s:%s/setup'
+
+ def __init__(self, hostname):
+ """
+ @param host: The host object representing the Eureka device.
+ """
+ self._eureka_setup_server = (self.EUREKA_SETUP_SERVER %
+ (hostname, self.SETUP_SERVER_PORT))
+
+
+ def get_info(self):
+ """Returns information about the eureka device.
+
+ @return: A dictionary containing information about the eureka device.
+ """
+ eureka_info_url = '%s/%s' % (self._eureka_setup_server, 'eureka_info')
+ try:
+ return json.loads(_get(eureka_info_url))
+ except (RPC_EXCEPTIONS, error.TimeoutException) as e:
+ raise EurekaProxyException('Could not retrieve information about '
+ 'eureka device: %s' % e)
+
+
+ def get_build_number(self, timeout_mins=0.1):
+ """
+ Returns the build number of the build on the device.
+
+ @param timeout_mins: Timeout in minutes. By default this should
+ return almost immediately and hence has a timeout of 6 seconds.
+ If we're rebooting, and would like the boot id of the build after
+ the reboot is complete this timeout should be in O(minutes).
+
+ @raises EurekaProxyException: If unable to get build number within the
+ timeout specified.
+ """
+ current_time = int(time.time())
+ end_time = current_time + timeout_mins*60
+
+ while end_time > current_time:
+ try:
+ eureka_info = self.get_info()
+ except EurekaProxyException:
+ pass
+ else:
+ return eureka_info.get('build_version', None)
+ time.sleep(self.POLLING_INTERVAL)
+ current_time = int(time.time())
+
+ raise EurekaProxyException('Timed out trying to get build number.')
+
+
+ def reboot(self, when="now"):
+ """
+ Post to the server asking for a reboot.
+
+ @param when: The time till reboot. Can be any of:
+ now: immediately
+ fdr: set factory data reset flag and reboot now
+ ota: set recovery flag and reboot now
+ ota fdr: set both recovery and fdr flags, and reboot now
+ ota foreground: reboot and start force update page
+ idle: reboot only when idle screen usage > 10 mins
+
+ @raises EurekaProxyException: if we're unable to post a reboot request.
+ """
+ reboot_url = '%s/%s' % (self._eureka_setup_server, 'reboot')
+ reboot_params = {"params": when}
+ logging.info('Rebooting device through %s.', reboot_url)
+ try:
+ _post(reboot_url, reboot_params)
+ except (RPC_EXCEPTIONS, error.TimeoutException) as e:
+ raise EurekaProxyException('Could not reboot eureka device through '
+ '%s: %s' % (self.SETUP_SERVER_PORT, e))
diff --git a/server/hosts/abstract_ssh.py b/server/hosts/abstract_ssh.py
index 4120334..7af5a2e 100644
--- a/server/hosts/abstract_ssh.py
+++ b/server/hosts/abstract_ssh.py
@@ -382,19 +382,21 @@
raise error.AutoservRunError(e.args[0], e.args[1])
- def ssh_ping(self, timeout=60):
+ def ssh_ping(self, timeout=60, base_cmd='true'):
"""
Pings remote host via ssh.
@param timeout: Time in seconds before giving up.
Defaults to 60 seconds.
+ @param base_cmd: The base command to run with the ssh ping.
+ Defaults to true.
@raise AutoservSSHTimeout: If the ssh ping times out.
@raise AutoservSshPermissionDeniedError: If ssh ping fails due to
permissions.
@raise AutoservSshPingHostError: For other AutoservRunErrors.
"""
try:
- self.run("true", timeout=timeout, connect_timeout=timeout)
+ self.run(base_cmd, timeout=timeout, connect_timeout=timeout)
except error.AutoservSSHTimeout:
msg = "Host (ssh) verify timed out (timeout = %d)" % timeout
raise error.AutoservSSHTimeout(msg)
@@ -408,16 +410,17 @@
repr(e.result_obj))
- def is_up(self, timeout=60):
+ def is_up(self, timeout=60, base_cmd='true'):
"""
- Check if the remote host is up.
+ Check if the remote host is up by ssh-ing and running a base command.
@param timeout: timeout in seconds.
+ @param base_cmd: a base command to run with ssh. The default is 'true'.
@returns True if the remote host is up before the timeout expires,
False otherwise.
"""
try:
- self.ssh_ping(timeout=timeout)
+ self.ssh_ping(timeout=timeout, base_cmd=base_cmd)
except error.AutoservError:
return False
else:
@@ -438,8 +441,8 @@
False otherwise
"""
if timeout:
- end_time = time.time() + timeout
- current_time = time.time()
+ current_time = int(time.time())
+ end_time = current_time + timeout
while not timeout or current_time < end_time:
if self.is_up(timeout=end_time - current_time):
@@ -450,7 +453,7 @@
except error.AutoservError:
pass
time.sleep(1)
- current_time = time.time()
+ current_time = int(time.time())
logging.debug('Host %s is still down after waiting %d seconds',
self.hostname, int(timeout + time.time() - end_time))
@@ -498,7 +501,7 @@
"""
#TODO: there is currently no way to distinguish between knowing
#TODO: boot_id was unsupported and not knowing the boot_id.
- current_time = time.time()
+ current_time = int(time.time())
if timeout:
end_time = current_time + timeout
@@ -525,7 +528,7 @@
# the same time that allowed us into that iteration of the loop.
while not timeout or current_time < end_time:
try:
- new_boot_id = self.get_boot_id(timeout=end_time - current_time)
+ new_boot_id = self.get_boot_id(timeout=end_time-current_time)
except error.AutoservError:
logging.debug('Host %s is now unreachable over ssh, is down',
self.hostname)
@@ -549,7 +552,7 @@
self.run('kill -HUP 1', ignore_status=True)
time.sleep(1)
- current_time = time.time()
+ current_time = int(time.time())
return False
diff --git a/server/hosts/adb_host.py b/server/hosts/adb_host.py
index 8ab56da..cc8b99a 100644
--- a/server/hosts/adb_host.py
+++ b/server/hosts/adb_host.py
@@ -29,6 +29,27 @@
"""This class represents a host running an ADB server."""
+ @staticmethod
+ def check_host(host, timeout=10):
+ """
+ Check if the given host is an adb host.
+
+ @param host: An ssh host representing a device.
+ @param timeout: The timeout for the run command.
+
+
+ @return: True if the host device has adb.
+
+ @raises AutoservRunError: If the command failed.
+ @raises AutoservSSHTimeout: Ssh connection has timed out.
+ """
+ try:
+ result = host.run('which adb 2> /dev/null', timeout=timeout)
+ except (error.AutoservRunError, error.AutoservSSHTimeout):
+ return False
+ return result.exit_status == 0
+
+
def _initialize(self, hostname='localhost', serial=None,
device_hostname=None, *args, **dargs):
"""Initialize an ADB Host.
diff --git a/server/hosts/cros_host.py b/server/hosts/cros_host.py
index d71ebe0..d55ff71 100644
--- a/server/hosts/cros_host.py
+++ b/server/hosts/cros_host.py
@@ -157,6 +157,26 @@
@staticmethod
+ def check_host(host, timeout=10):
+ """
+ Check if the given host is a chrome-os host.
+
+ @param host: An ssh host representing a device.
+ @param timeout: The timeout for the run command.
+
+ @return: True if the host device is chromeos.
+
+ @raises AutoservRunError: If the command failed.
+ @raises AutoservSSHTimeout: Ssh connection has timed out.
+ """
+ try:
+ result = host.run('cat /etc/lsb-release > /dev/null', timeout=timeout)
+ except (error.AutoservRunError, error.AutoservSSHTimeout):
+ return False
+ return result.exit_status == 0
+
+
+ @staticmethod
def get_servo_arguments(args_dict):
"""Extract servo options from `args_dict` and return the result.
diff --git a/server/hosts/eureka_host.py b/server/hosts/eureka_host.py
new file mode 100644
index 0000000..edd86b9
--- /dev/null
+++ b/server/hosts/eureka_host.py
@@ -0,0 +1,218 @@
+# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Eureka host.
+
+This host can perform actions either over ssh or by submitting requests to
+an http server running on the client. Though the server provides flexibility
+and allows us to test things at a modular level, there are times we must
+resort to ssh (eg: to reboot into recovery). The server exposes the same stack
+that the chromecast extension needs to communicate with the eureka device, so
+any test involving an eureka host will fail if it cannot submit posts/gets
+to the server. In cases where we can achieve the same action over ssh or
+the rpc server, we choose the rpc server by default, because several existing
+eureka tests do the same.
+"""
+
+import logging
+import os
+
+import common
+
+from autotest_lib.client.common_lib import error
+from autotest_lib.server import site_utils
+from autotest_lib.server.cros import eureka_client
+from autotest_lib.server.hosts import abstract_ssh
+
+
+class EurekaHost(abstract_ssh.AbstractSSHHost):
+ """This class represents a eureka host."""
+
+ # Maximum time to wait for the client server to start.
+ SERVER_START_TIME = 180
+
+ # Maximum time a reboot can take.
+ REBOOT_TIME = 360
+
+ COREDUMP_DIR = '/data/coredump'
+ OTA_LOCATION = '/cache/ota.zip'
+ RECOVERY_DIR = '/cache/recovery'
+ COMMAND_FILE = os.path.join(RECOVERY_DIR, 'command')
+
+
+ @staticmethod
+ def check_host(host, timeout=10):
+ """
+ Check if the given host is a eureka host.
+
+ @param host: An ssh host representing a device.
+ @param timeout: The timeout for the run command.
+
+ @return: True if the host device is eureka.
+
+ @raises AutoservRunError: If the command failed.
+ @raises AutoservSSHTimeout: Ssh connection has timed out.
+ """
+ try:
+ result = host.run('getprop ro.hardware', timeout=timeout)
+ except (error.AutoservRunError, error.AutoservSSHTimeout):
+ return False
+ return 'eureka' in result.stdout
+
+
+ def _initialize(self, hostname, *args, **dargs):
+ super(EurekaHost, self)._initialize(hostname=hostname, *args, **dargs)
+
+ # Eureka devices expose a server that can respond to json over http.
+ self.client = eureka_client.EurekaProxy(hostname)
+
+
+ def get_boot_id(self, timeout=60):
+ """Get a unique ID associated with the current boot.
+
+ @param timeout The number of seconds to wait before timing out, as
+ taken by base_utils.run.
+
+ @return A string unique to this boot or None if not available.
+ """
+ BOOT_ID_FILE = '/proc/sys/kernel/random/boot_id'
+ cmd = 'cat %r' % (BOOT_ID_FILE)
+ return self.run(cmd, timeout=timeout).stdout.strip()
+
+
+ def ssh_ping(self, timeout=60, base_cmd=''):
+ """Checks if we can ssh into the host and run getprop.
+
+ Ssh ping is vital for connectivity checks and waiting on a reboot.
+ A simple true check, or something like if [ 0 ], is not guaranteed
+ to always exit with a successful return value.
+
+ @param timeout: timeout in seconds to wait on the ssh_ping.
+ @param base_cmd: The base command to use to confirm that a round
+ trip ssh works.
+ """
+ super(EurekaHost, self).ssh_ping(timeout=timeout,
+ base_cmd="getprop>/dev/null")
+
+
+ def verify_software(self):
+ """Verified that the server on the client device is responding to gets.
+
+ The server on the client device is crucial for the eureka device to
+ communicate with the chromecast extension. Device verify on the whole
+ consists of verify_(hardware, connectivity and software), ssh
+ connectivity is verified in the base class' verify_connectivity.
+
+ @raises: EurekaProxyException if the server doesn't respond.
+ """
+ self.client.get_info()
+
+
+ def get_kernel_ver(self):
+ """Returns the build number of the build on the device."""
+ return self.client.get_build_number()
+
+
+ def reboot(self, timeout=5):
+ """Reboot the eureka device by submitting a post to the server."""
+
+ # TODO(beeps): crbug.com/318306
+ current_boot_id = self.get_boot_id()
+ try:
+ self.client.reboot()
+ except eureka_client.EurekaProxyException as e:
+ logging.error('Unable to reboot through the eureka proxy: %s', e)
+ return False
+
+ self.wait_for_restart(timeout=timeout, old_boot_id=current_boot_id)
+ return True
+
+
+ def cleanup(self):
+ """Cleanup state.
+
+ If removing state information fails, do a hard reboot. This will hit
+ our reboot method through the ssh host's cleanup.
+ """
+ try:
+ self.run('rm -r /data/*')
+ self.run('rm -f /cache/*')
+ except (error.AutotestRunError, error.AutoservRunError) as e:
+ logging.warn('Unable to remove /data and /cache %s', e)
+ super(EurekaHost, self).cleanup()
+
+
+ def _remount_root(self, permissions):
+ """Remount root partition.
+
+ @param permissions: Permissions to use for the remount, eg: ro, rw.
+
+ @raises error.AutoservRunError: If something goes wrong in executing
+ the remount command.
+ """
+ self.run('mount -o %s,remount /' % permissions)
+
+
+ def _setup_coredump_dirs(self):
+ """Sets up the /data/coredump directory on the client.
+
+ The device will write a memory dump to this directory on crash,
+ if it exists. No crashdump will get written if it doesn't.
+ """
+ try:
+ self.run('mkdir -p %s' % self.COREDUMP_DIR)
+ self.run('chmod 4777 %s' % self.COREDUMP_DIR)
+ except (error.AutotestRunError, error.AutoservRunError) as e:
+ error.AutoservRunError('Unable to create coredump directories with '
+ 'the appropriate permissions: %s' % e)
+
+
+ def _setup_for_recovery(self, update_url):
+ """Sets up the /cache/recovery directory on the client.
+
+ Copies over the OTA zipfile from the update_url to /cache, then
+ sets up the recovery directory. Normal installs are achieved
+ by rebooting into recovery mode.
+
+ @param update_url: A url pointing to a staged ota zip file.
+
+ @raises error.AutoservRunError: If something goes wrong while
+ executing a command.
+ """
+ ssh_cmd = '%s %s' % (self.make_ssh_command(), self.hostname)
+ site_utils.remote_wget(update_url, self.OTA_LOCATION, ssh_cmd)
+ self.run('ls %s' % self.OTA_LOCATION)
+
+ self.run('mkdir -p %s' % self.RECOVERY_DIR)
+
+ # These 2 commands will always return a non-zero exit status
+ # even if they complete successfully. This is a confirmed
+ # non-issue, since the install will actually complete. If one
+ # of the commands fails we can only detect it as a failure
+ # to install the specified build.
+ self.run('echo --update_package>%s' % self.COMMAND_FILE,
+ ignore_status=True)
+ self.run('echo %s>>%s' % (self.OTA_LOCATION, self.COMMAND_FILE),
+ ignore_status=True)
+
+
+ def machine_install(self, update_url):
+ """Installs a build on the Eureka device."""
+ old_build_number = self.client.get_build_number()
+ self._remount_root(permissions='rw')
+ self._setup_coredump_dirs()
+ self._setup_for_recovery(update_url)
+
+ current_boot_id = self.get_boot_id()
+ self.run('reboot recovery &')
+ self.wait_for_restart(timeout=self.REBOOT_TIME,
+ old_boot_id=current_boot_id)
+ new_build_number = self.client.get_build_number(self.SERVER_START_TIME)
+
+ # TODO(beeps): crbug.com/318278
+ if new_build_number == old_build_number:
+ raise error.AutoservRunError('Build number did not change on: '
+ '%s after update with %s' %
+ (self.hostname, update_url()))
diff --git a/server/hosts/factory.py b/server/hosts/factory.py
index 67f78ed..51bbecb 100644
--- a/server/hosts/factory.py
+++ b/server/hosts/factory.py
@@ -1,9 +1,12 @@
"""Provides a factory method to create a host object."""
+import logging
from contextlib import closing
+
from autotest_lib.client.common_lib import error, global_config
from autotest_lib.server import autotest, utils as server_utils
from autotest_lib.server.hosts import site_factory, cros_host, ssh_host, serial
+from autotest_lib.server.hosts import eureka_host
from autotest_lib.server.hosts import adb_host, logfile_monitor
@@ -24,6 +27,11 @@
# for tracking which hostnames have already had job_start called
_started_hostnames = set()
+# A list of all the possible host types, ordered according to frequency of
+# host types in the lab, so the more common hosts don't incur a repeated ssh
+# overhead in checking for less common host types.
+host_types = [cros_host.CrosHost, eureka_host.EurekaHost, adb_host.ADBHost,]
+
def _get_host_arguments():
"""Returns parameters needed to ssh into a host.
@@ -49,8 +57,10 @@
def _detect_host(connectivity_class, hostname, **args):
"""Detect host type.
- Currently checks if adb is on the host and if so returns ADBHost if not or
- if the check fails, it will return CrosHost.
+ Goes through all the possible host classes, calling check_host with a
+ basic host object. Currently this is an ssh host, but theoretically it
+ can be any host object that the check_host method of appropriate host
+ type knows to use.
@param connectivity_class: connectivity class to use to talk to the host
(ParamikoHost or SSHHost)
@@ -58,24 +68,19 @@
@param args: Args that will be passed to the constructor of
the host class.
- @returns Class type to use for this host.
+ @returns: Class type of the first host class that returns True to the
+ check_host method.
"""
- # Detect if adb is on the host. If so we are using an ADBHost. If not use,
- # CrosHost.
- try:
- # Attempt to find adb on the system. If that succeeds use ADBHost.
- with closing(connectivity_class(hostname, **args)) as host:
- # Send stderr to /dev/null which cleans up log spam about not
- # finding adb installed.
- result = host.run('which adb 2> /dev/null', timeout=10)
- return adb_host.ADBHost
- except (error.AutoservRunError, error.AutoservSSHTimeout):
- # If any errors occur use CrosHost.
- # TODO(fdeng): this method should should dynamically discover
- # and allocate host types, crbug.com/273843
- # TODO crbug.com/302026 (sbasi) - adjust this pathway for ADBHost in
- # the future should a host require verify/repair.
- return cros_host.CrosHost
+ # TODO crbug.com/302026 (sbasi) - adjust this pathway for ADBHost in
+ # the future should a host require verify/repair.
+ with closing(connectivity_class(hostname, **args)) as host:
+ for host_module in host_types:
+ if host_module.check_host(host, timeout=10):
+ return host_module
+
+ logging.warning('Unable to apply conventional host detection methods, '
+ 'defaulting to chromeos host.')
+ return cros_host.CrosHost
def create_host(
diff --git a/server/site_tests/adb_Reboot/control b/server/site_tests/adb_Reboot/control
deleted file mode 100644
index f520b2e..0000000
--- a/server/site_tests/adb_Reboot/control
+++ /dev/null
@@ -1,27 +0,0 @@
-# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-from autotest_lib.client.common_lib import utils
-
-AUTHOR = "sbasi, [email protected]"
-NAME = "adb_Reboot"
-TIME = "SHORT"
-TEST_CATEGORY = "Functional"
-TEST_CLASS = "adb"
-TEST_TYPE = "server"
-
-DOC = """
-This server side test suite tests the ADB Reboot functionality.
-"""
-
-args_dict = utils.args_to_dict(args)
-
-def run_benchmark(machine):
- device_hostname = args_dict.get('device_hostname')
- host = hosts.create_host(machine,
- device_hostname=device_hostname)
- job.run_test("adb_Reboot", host=host)
-
-
-parallel_simple(run_benchmark, machines)
diff --git a/server/site_tests/generic_RebootTest/control b/server/site_tests/generic_RebootTest/control
new file mode 100644
index 0000000..95134fd
--- /dev/null
+++ b/server/site_tests/generic_RebootTest/control
@@ -0,0 +1,51 @@
+# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import logging
+
+from autotest_lib.client.common_lib import error, utils
+
+
+AUTHOR = "sbasi, [email protected]"
+NAME = "generic_RebootTest"
+TIME = "SHORT"
+TEST_CATEGORY = "Functional"
+TEST_CLASS = "Generic"
+TEST_TYPE = "server"
+
+DOC = """
+This server side test checks if a host, specified through the command line,
+can successfully reboot. It automatically detects the type of the host,
+creates the appropriate host object, and calls reboot on the host object.
+
+Example usage:
+test_that generic_RebootTest <eureka/cros/beaglobonedevice ip> --board=<board>
+A note about --board: <unless you're emerging eureka sources you
+can just use a chromeos board here, as all we need is test_that
+from the sysroot>.
+
+Typically, for the case of an adb host, we can send adb commands to the
+android device either through usb or tcp. If no device_hostname is specified
+the adb commands are sent to the android device over usb, via the beaglebone,
+whereas if the ip of the android device is specified through device_hostname
+we'll send the adb commands over tcp.
+"""
+
+args_dict = utils.args_to_dict(args)
+
+def run_reboot_test(machine):
+ device_hostname = args_dict.get('device_hostname', None)
+ try:
+ host = hosts.create_host(machine,
+ device_hostname=device_hostname)
+ except error.AutoservError as e:
+ raise error.AutoservError(
+ 'Failed to create host for %s: %s. If this is an android host that '
+ 'requires a beaglebone jump host, you need to specify the device '
+ 'hostname through test_that --args="device_hostname=<android ip>".'
+ % (machine, e))
+
+ job.run_test("generic_RebootTest", host=host, disable_sysinfo=True)
+
+
+parallel_simple(run_reboot_test, machines)
diff --git a/server/site_tests/adb_Reboot/adb_Reboot.py b/server/site_tests/generic_RebootTest/generic_RebootTest.py
similarity index 79%
rename from server/site_tests/adb_Reboot/adb_Reboot.py
rename to server/site_tests/generic_RebootTest/generic_RebootTest.py
index 58b8b5d..8ba3e27 100644
--- a/server/site_tests/adb_Reboot/adb_Reboot.py
+++ b/server/site_tests/generic_RebootTest/generic_RebootTest.py
@@ -6,11 +6,10 @@
from autotest_lib.server import test
-class adb_Reboot(test.test):
+class generic_RebootTest(test.test):
"""Reboot a device. Should be ran on ADBHosts only."""
version = 1
-
def run_once(self, host):
if not host.reboot():
- raise error.TestFail('ADB failed to reboot as expected.')
+ raise error.TestFail('Host failed to reboot.')
diff --git a/server/site_utils.py b/server/site_utils.py
index ab8f496..c1390bf 100644
--- a/server/site_utils.py
+++ b/server/site_utils.py
@@ -103,3 +103,17 @@
sheriff_ids += ['%[email protected]' % alias.replace(' ', '')
for alias in ldaps.group(1).split(',')]
return sheriff_ids
+
+
+def remote_wget(source_url, dest_path, ssh_cmd):
+ """wget source_url from localhost to dest_path on remote host using ssh.
+
+ @param source_url: The complete url of the source of the package to send.
+ @param dest_path: The path on the remote host's file system where we would
+ like to store the package.
+ @param ssh_cmd: The ssh command to use in performing the remote wget.
+ """
+ wget_cmd = ("wget -O - %s | %s 'cat >%s'" %
+ (source_url, ssh_cmd, dest_path))
+ base_utils.run(wget_cmd)
+