Create servo object with host, when appropriate.

This adds a 'servo' member to instances of SiteHost, when the
target hostname has an associated "-servo" hostname as well.
For ServoTest and subclasses, this object is used in preference
to creating a servo object on localhost, if it exists.

BUG=chromium-os:29209
TEST=platform_LongPressPower with a servo attached to localhost
TEST=same test with servo debug header detached to force failure

Change-Id: I22ad9ec2ec3213d3332d36b557007bdf771c2918
Reviewed-on: https://gerrit.chromium.org/gerrit/20137
Reviewed-by: Chris Sosa <sosa@chromium.org>
Tested-by: Richard Barnette <jrbarnette@chromium.org>
Commit-Ready: Richard Barnette <jrbarnette@chromium.org>
diff --git a/server/cros/servo_test.py b/server/cros/servo_test.py
index dbbafa4..0e05c0d 100644
--- a/server/cros/servo_test.py
+++ b/server/cros/servo_test.py
@@ -22,8 +22,7 @@
         If use_faft flag is Ture, a remote FAFT client will be launched.
     """
     version = 2
-    # Abstracts access to all Servo functions.
-    servo = None
+
     # Exposes RPC access to a remote PyAuto client.
     pyauto = None
     # Exposes RPC access to a remote FAFT client.
@@ -71,13 +70,19 @@
         },
     }
 
-    def initialize(self, host, cmdline_args, use_pyauto=False, use_faft=False):
-        """Create a Servo object and install the dependency.
+    def _init_servo(self, host, cmdline_args):
+        """Initialize `self.servo`.
 
-        If use_pyauto/use_faft is True the PyAuto/FAFTClient dependency is
-        installed on the client and a remote PyAuto/FAFTClient server is
-        launched and connected.
+        If the host has an attached servo object, use that.
+        Otherwise assume that there's a locally attached servo
+        device, and start servod on localhost.
+
         """
+        if host.servo:
+            self.servo = host.servo
+            self._servo_is_local = False
+            return
+
         # Assign default arguments for servo invocation.
         args = {
             'servo_host': 'localhost', 'servo_port': 9999,
@@ -102,21 +107,40 @@
                 else:
                     args[key] = val
 
+        self.servo = servo.Servo(
+            args['servo_host'], args['servo_port'], args['xml_config'],
+            args['servo_vid'], args['servo_pid'], args['servo_serial'])
+        self._servo_is_local = True
+
+
+    def _release_servo(self):
+        """Clean up `self.servo` if it is locally attached."""
+        if self._servo_is_local:
+            del self.servo
+        self._servo_is_local = False
+
+
+    def initialize(self, host, cmdline_args, use_pyauto=False, use_faft=False):
+        """Create a Servo object and install the dependency.
+
+        If use_pyauto/use_faft is True the PyAuto/FAFTClient dependency is
+        installed on the client and a remote PyAuto/FAFTClient server is
+        launched and connected.
+        """
         # Initialize servotest args.
         self._client = host;
         self._remote_infos['pyauto']['used'] = use_pyauto
         self._remote_infos['faft']['used'] = use_faft
 
-        self.servo = servo.Servo(
-            args['servo_host'], args['servo_port'], args['xml_config'],
-            args['servo_vid'], args['servo_pid'], args['servo_serial'])
+        self._init_servo(host, cmdline_args)
+
         # Initializes dut, may raise AssertionError if pre-defined gpio
         # sequence to set GPIO's fail.  Autotest does not handle exception
         # throwing in initialize and will cause a test to hang.
         try:
             self.servo.initialize_dut()
         except (AssertionError, xmlrpclib.Fault) as e:
-            del self.servo
+            self._release_servo()
             raise error.TestFail(e)
 
         # Install PyAuto/FAFTClient dependency.
@@ -241,8 +265,7 @@
 
     def cleanup(self):
         """Delete the Servo object, call remote cleanup, and kill ssh."""
-        if self.servo:
-            del self.servo
+        self._release_servo()
         for info in self._remote_infos.itervalues():
             if info['remote_process'] and info['remote_process'].poll() is None:
                 remote_object = getattr(self, info['ref_name'])