autotest:add file based lock/unlock logical to servo host.

BUG=chromium:971915
TEST=repair task on santiam lab hosts.

Change-Id: I7fc6c593de089a34d2c216c9bb01e3d500a90d77
Reviewed-on: https://chromium-review.googlesource.com/1655676
Tested-by: Garry Wang <xianuowang@chromium.org>
Commit-Ready: Garry Wang <xianuowang@chromium.org>
Legacy-Commit-Queue: Commit Bot <commit-bot@chromium.org>
Reviewed-by: Garry Wang <xianuowang@chromium.org>
diff --git a/server/hosts/base_servohost.py b/server/hosts/base_servohost.py
index 7b2f839..f835667 100644
--- a/server/hosts/base_servohost.py
+++ b/server/hosts/base_servohost.py
@@ -38,10 +38,13 @@
     """
     REBOOT_CMD = 'sleep 1; reboot & sleep 10; reboot -f'
 
-    SERVOHOST_TEMP_FILE_DIR = '/var/lib/servod/'
+    TEMP_FILE_DIR = '/var/lib/servod/'
+
+    LOCK_FILE_POSTFIX = '_in_use'
+    REBOOT_FILE_POSTFIX = '_reboot'
 
     # Time to wait a rebooting servohost. In seconds
-    SERVOHOST_REBOOT_TIMEOUT = 120
+    REBOOT_TIMEOUT = 120
 
 
     def _initialize(self, hostname, is_in_lab=None, *args, **dargs):
@@ -299,7 +302,7 @@
             raise error.AutoservHostError(
                 'servo host %s failed to shut down.' %
                 self.hostname)
-        if self.wait_up(timeout=self.SERVOHOST_REBOOT_TIMEOUT):
+        if self.wait_up(timeout=self.REBOOT_TIMEOUT):
             logging.info('servo host %s back from reboot, with build %s',
                          self.hostname, self._get_release_version())
         else:
diff --git a/server/hosts/servo_host.py b/server/hosts/servo_host.py
index 811ef4d..5d34ea2 100644
--- a/server/hosts/servo_host.py
+++ b/server/hosts/servo_host.py
@@ -14,6 +14,7 @@
 import os
 
 from autotest_lib.client.bin import utils
+from autotest_lib.client.common_lib import error
 from autotest_lib.client.common_lib import global_config
 from autotest_lib.client.common_lib import hosts
 from autotest_lib.client.common_lib.cros import retry
@@ -87,6 +88,20 @@
         self.servo_model = servo_model
         self.servo_serial = servo_serial
         self._servo = None
+        # Path of the servo host lock file.
+        self._lock_file = (self.TEMP_FILE_DIR + str(self.servo_port)
+                           + self.LOCK_FILE_POSTFIX)
+        # File path to declare a reboot request.
+        self._reboot_file = (self.TEMP_FILE_DIR + str(self.servo_port)
+                             + self.REBOOT_FILE_POSTFIX)
+
+        # Lock the servo host if it's an in-lab labstation to prevent other
+        # task to reboot it until current task completes. We also wait and
+        # make sure the labstation is up here, in the case of the labstation is
+        # in the middle of reboot.
+        if (self.is_in_lab() and self.is_labstation()
+            and self.wait_up(self.REBOOT_TIMEOUT)):
+            self._lock()
 
         self._repair_strategy = (
                 servo_repair.create_servo_repair_strategy())
@@ -183,6 +198,30 @@
         return self._servo
 
 
+    def request_reboot(self):
+        """Request servohost to be rebooted when it's safe to by touch a file.
+        """
+        logging.debug('Request to reboot servohost %s has been created by '
+                      'servo with port %s', self.hostname, self.servo_port)
+        self.run('touch %s' % self._reboot_file, ignore_status=True)
+
+
+    def _lock(self):
+        """lock servohost by touching a file.
+        """
+        logging.debug('Locking servohost %s by touching %s file',
+                      self.hostname, self._lock_file)
+        self.run('touch %s' % self._lock_file, ignore_status=True)
+
+
+    def _unlock(self):
+        """Unlock servohost by removing the lock file.
+        """
+        logging.debug('Unlocking servohost by removing %s file',
+                      self._lock_file)
+        self.run('rm %s' % self._lock_file, ignore_status=True)
+
+
     def close(self):
         """Close the associated servo and the host object."""
         if self._servo:
@@ -191,6 +230,15 @@
                 self._servo.uart_logs_dir = self.job.resultdir
             self._servo.close()
 
+        if self.is_in_lab() and self.is_labstation():
+            # Remove the lock if the host is an in-lab labstation.
+            try:
+                self._unlock()
+            except error.AutoservSSHTimeout:
+                logging.error('Unlock servohost failed due to ssh timeout.'
+                              ' It may caused by servohost went down during'
+                              ' the task.')
+
         super(ServoHost, self).close()
 
 
diff --git a/server/hosts/servo_repair.py b/server/hosts/servo_repair.py
index 953934d..d73d004 100644
--- a/server/hosts/servo_repair.py
+++ b/server/hosts/servo_repair.py
@@ -309,10 +309,9 @@
                 'Target servo is not a test lab servo',
                 'servo_not_applicable_to_host_outside_lab')
         if host.is_labstation():
-            raise hosts.AutoservRepairError(
-                'Repairing that reboot labstation is not supported.'
-                ' See crbug.com/843358',
-                'can_not_repair_labstation_with_multiple_hosts')
+            host.request_reboot()
+            logging.warning('Reboot labstation requested, it will be '
+                            'handled by labstation administrative task.')
         else:
             host.update_image(wait_for_update=True)
             super(_ServoRebootRepair, self).repair(host)