autotest: ignore servo command errors for test_that

Print but do not raise servo command error if servo host is not
cros host. The goal is to be able to run tests with "test_that" from
workstations while using Sweetberry as the servod device. Sweetberry
does not support many servo commands that autotest verify process
assumes. As a result, using Sweetberry with "test_that" fails
because of servo command errors. This CL prints but does not raise
the servo command errors, so that we can use Sweetberry with
"test_that" command.

BUG=b:148178790
TEST=test_that <ip> power_ServodWrapper \
--args 'test=power_Dummy servo_host=localhost servo_port=9996 \
ina_rate=1 vbat_rate=60 pdash_note=dummy' \
--autotest_dir ~/trunk/src/third_party/autotest/files/

Change-Id: I95f27c4e3d586b3cc4b7218ce994182a149b6e1e
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/autotest/+/2036624
Commit-Queue: Mengqi Guo <mqg@chromium.org>
Tested-by: Mengqi Guo <mqg@chromium.org>
Auto-Submit: Mengqi Guo <mqg@chromium.org>
Reviewed-by: Ruben Rodriguez Buchillon <coconutruben@chromium.org>
diff --git a/server/cros/servo/servo.py b/server/cros/servo/servo.py
index 4373dfe..442cb4a 100644
--- a/server/cros/servo/servo.py
+++ b/server/cros/servo/servo.py
@@ -444,9 +444,13 @@
             e.filename = '%s:%s' % (self._servo_host.hostname,
                                     self._servo_host.servo_port)
             raise
-        self.set('usb_mux_oe1', 'on')
         self._usb_state = None
-        self.switch_usbkey('off')
+        if self.has_control('usb_mux_oe1'):
+            self.set('usb_mux_oe1', 'on')
+            self.switch_usbkey('off')
+        else:
+            logging.warning('Servod command \'usb_mux_oe1\' is not available. '
+                            'Any USB drive related servo routines will fail.')
         self._uart.start_capture()
         if cold_reset:
             self._power_state.reset()
diff --git a/server/hosts/servo_repair.py b/server/hosts/servo_repair.py
index d92ac9f..ef71648 100644
--- a/server/hosts/servo_repair.py
+++ b/server/hosts/servo_repair.py
@@ -2,13 +2,37 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import functools
 import logging
 
 import common
 from autotest_lib.client.common_lib import hosts
+from autotest_lib.server.cros.servo import servo
 from autotest_lib.server.hosts import repair_utils
 
 
+def ignore_exception_for_non_cros_host(func):
+    """
+    Decorator to ignore ControlUnavailableError if servo host is not cros host.
+    When using test_that command on a workstation, this enables usage of
+    additional servo devices such as servo micro and Sweetberry. This shall not
+    change any lab behavior.
+    """
+    @functools.wraps(func)
+    def wrapper(self, host):
+        """
+        Wrapper around func.
+        """
+        try:
+            func(self, host)
+        except servo.ControlUnavailableError as e:
+            if host.is_cros_host():
+                raise
+            logging.warning("Servo host is not cros host, ignore %s: %s",
+                            type(e).__name__, e)
+    return wrapper
+
+
 class _UpdateVerifier(hosts.Verifier):
     """
     Verifier to trigger a servo host update, if necessary.
@@ -239,6 +263,7 @@
     # with a dummy pwr_button signal.
     _BOARDS_WO_PWR_BUTTON = ['arkham', 'gale', 'mistral', 'storm', 'whirlwind']
 
+    @ignore_exception_for_non_cros_host
     def verify(self, host):
         if host.servo_board in self._BOARDS_WO_PWR_BUTTON:
             return
@@ -247,6 +272,7 @@
             raise hosts.AutoservVerifyError(
                     'Check ribbon cable: \'pwr_button\' is stuck')
 
+
     @property
     def description(self):
         return 'pwr_button control is normal'
@@ -257,6 +283,7 @@
     Verifier to check sanity of the `lid_open` signal.
     """
 
+    @ignore_exception_for_non_cros_host
     def verify(self, host):
         lid_open = host.get_servo().get('lid_open')
         if lid_open != 'yes' and lid_open != 'not_applicable':