[autotest] Check servod process's status in verify_software

We've seen servo verification took very long time when servod is actually not
running. This adds a lot of overhead to the test. The timeout can be as long
as 2 minutes and servo verification is done 3 times in each test.

This CL add a check of actual servod process in servo_host.verify_software.
If servod is not running, servo host object will fail to be verified without
waiting for actual servo call to timeout in servo.initialize_dut, e.g.,
self.set('dut_hub_pwren', 'on')

Also add a timeout around servo.initialize_dut for 30 seconds.

BUG=chromium:404209
TEST=local servo setup and test_that

Change-Id: I46d2d10cd188db57c73b749c688d3d788a6df305
Reviewed-on: https://chromium-review.googlesource.com/213671
Tested-by: Dan Shi <dshi@chromium.org>
Reviewed-by: Richard Barnette <jrbarnette@chromium.org>
Commit-Queue: Richard Barnette <jrbarnette@chromium.org>
diff --git a/server/hosts/servo_host.py b/server/hosts/servo_host.py
index 9b9dbaf..2aa8b5b 100644
--- a/server/hosts/servo_host.py
+++ b/server/hosts/servo_host.py
@@ -79,6 +79,8 @@
     REBOOT_DELAY_SECS = 20
     # Servod process name.
     SERVOD_PROCESS = 'servod'
+    # Timeout for initializing servo signals.
+    INITIALIZE_SERVO_TIMEOUT_SECS = 30
 
     _MAX_POWER_CYCLE_ATTEMPTS = 3
     _timer = stats.Timer('servo_host')
@@ -357,6 +359,32 @@
                     (self.hostname, e))
 
 
+    def _check_servod_status(self):
+        """Check if servod process is running.
+
+        If servod is not running, there is no need to verify if servo is
+        working. Check the process before making any servod call can avoid
+        long timeout that eventually fail any servod call.
+        If the servo host is set to localhost, failure of servod status check
+        will be ignored, as servo call may use ssh tunnel.
+
+        @raises ServoHostVerifyFailure if servod process does not exist.
+
+        """
+        try:
+            pid = int(self.run('pgrep servod').stdout.strip())
+            logging.info('servod is running, PID=%d', pid)
+        except (error.AutoservRunError, error.AutoservSSHTimeout) as e:
+            if self._is_localhost:
+                logging.info('Ignoring servod status check failure. servo host '
+                             'is set to localhost, servo call may use ssh '
+                             'tunnel to go through.')
+            else:
+                raise ServoHostVerifyFailure(
+                        'Servod status check failed for %s: %s' %
+                        (self.hostname, e))
+
+
     @_timer.decorate
     def _update_image(self):
         """Update the image on the servo host, if needed.
@@ -475,6 +503,9 @@
         logging.info('Verifying if servo config file exists.')
         self._check_servo_config()
 
+        logging.info('Verifying if servod is running.')
+        self._check_servod_status()
+
         logging.info('Verifying servo host %s with sanity checks.',
                      self.hostname)
         self._check_servo_host_usb()
@@ -485,7 +516,11 @@
             self._check_servod()
         else:
             self._servo = servo.Servo(servo_host=self)
-            self._servo.initialize_dut()
+            timeout, _ = retry.timeout(
+                    self._servo.initialize_dut,
+                    timeout_sec=self.INITIALIZE_SERVO_TIMEOUT_SECS)
+            if timeout:
+                raise ServoHostVerifyFailure('Servo initialize timed out.')
 
         logging.info('Sanity checks pass on servo host %s', self.hostname)