Merge remote branch 'cros/upstream' into autotest-rebase

Merged to upstream trunk@5066, from trunk@4749.

There is no way I could enlist each individual CL from the upstream here since it will blow up the changelist description field.

BUG=
TEST=
Had patched this CL into a fresh cut client to avoid any side effect.
run_remote_test bvt from both emerged location and third_party/autotest/file.

Both test passed!

We should also keep any eye on this to see how it gets propagated into cautotest server.
TBR=dalecurtis

Change-Id: I72f2bc7a9de530178484aea1bfb5ace68bcad029
diff --git a/scheduler/drone_manager.py b/scheduler/drone_manager.py
index 75724f3..e094f14 100644
--- a/scheduler/drone_manager.py
+++ b/scheduler/drone_manager.py
@@ -159,8 +159,7 @@
         self._results_dir = base_results_dir
 
         for hostname in drone_hostnames:
-            drone = self._add_drone(hostname)
-            drone.call('initialize', self.absolute_path(''))
+            self._add_drone(hostname)
 
         if not self._drones:
             # all drones failed to initialize
@@ -205,8 +204,9 @@
     def _add_drone(self, hostname):
         logging.info('Adding drone %s' % hostname)
         drone = drones.get_drone(hostname)
-        self._drones[drone.hostname] = drone
-        return drone
+        if drone:
+            self._drones[drone.hostname] = drone
+            drone.call('initialize', self.absolute_path(''))
 
 
     def _remove_drone(self, hostname):
diff --git a/scheduler/drones.py b/scheduler/drones.py
index 85a5ee2..b742b55 100644
--- a/scheduler/drones.py
+++ b/scheduler/drones.py
@@ -7,6 +7,11 @@
 AUTOTEST_INSTALL_DIR = global_config.global_config.get_config_value('SCHEDULER',
                                                  'drone_installation_directory')
 
+class DroneUnreachable(Exception):
+    """The drone is non-sshable."""
+    pass
+
+
 class _AbstractDrone(object):
     """
     Attributes:
@@ -111,6 +116,9 @@
         super(_RemoteDrone, self).__init__()
         self.hostname = hostname
         self._host = drone_utility.create_host(hostname)
+        if not self._host.is_up():
+            logging.error('Drone %s is unpingable, kicking out', hostname)
+            raise DroneUnreachable
         self._autotest_install_dir = AUTOTEST_INSTALL_DIR
 
 
@@ -156,4 +164,7 @@
     """
     if hostname == 'localhost':
         return _LocalDrone()
-    return _RemoteDrone(hostname)
+    try:
+        return _RemoteDrone(hostname)
+    except DroneUnreachable:
+        return None
diff --git a/scheduler/metahost_scheduler.py b/scheduler/metahost_scheduler.py
index 9588e95..98f49be 100644
--- a/scheduler/metahost_scheduler.py
+++ b/scheduler/metahost_scheduler.py
@@ -54,16 +54,16 @@
 
 
     def schedule_metahost(self, queue_entry, scheduling_utility):
-         """Schedule the given queue entry, if possible.
+        """Schedule the given queue entry, if possible.
 
-         This method should make necessary database changes culminating in
-         assigning a host to the given queue entry in the database.  It may
-         take no action if no host can be assigned currently.
+        This method should make necessary database changes culminating in
+        assigning a host to the given queue entry in the database.  It may
+        take no action if no host can be assigned currently.
 
-         @param queue_entry: a HostQueueEntry DBObject
-         @param scheduling_utility: a HostSchedulingUtility object
-         """
-         raise NotImplementedError
+        @param queue_entry: a HostQueueEntry DBObject
+        @param scheduling_utility: a HostSchedulingUtility object
+        """
+        raise NotImplementedError
 
 
     def recovery_on_startup(self):