FAFT: Introduce FAFTClientProxy to setup RPC smartly
This CL seprates the RPC setup stuffs to a new class FAFTClientProxy.
It reuses SiteHost XMLRPC related methods. It is also smart enough to:
- postpone the RPC connection to the first class method call;
- reconnect to the RPC server in case connection lost, e.g. reboot;
- always call the latest RPC proxy object.
BUG=chrome-os-partner:21118;chromium-os:215491
TEST=Manual
Ran the affected test cases, firmware_FAFTSetup, on Spring and passed.
Change-Id: I0d35881a270ed6d4a69cc60045d1d3e3a608b964
Reviewed-on: https://chromium-review.googlesource.com/62645
Reviewed-by: Wai-Hong Tam <waihong@chromium.org>
Tested-by: Wai-Hong Tam <waihong@chromium.org>
Commit-Queue: Wai-Hong Tam <waihong@chromium.org>
diff --git a/server/cros/faft/rpc_proxy.py b/server/cros/faft/rpc_proxy.py
new file mode 100644
index 0000000..5743b1a
--- /dev/null
+++ b/server/cros/faft/rpc_proxy.py
@@ -0,0 +1,92 @@
+# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import httplib
+import socket
+import xmlrpclib
+
+from autotest_lib.client.cros.faft.config import Config as ClientConfig
+
+
+class _Method:
+ """Class to save the name of the RPC method instead of the real object.
+
+ It keeps the name of the RPC method locally first such that the RPC method
+ can be evalulated to a real object while it is called. Its purpose is to
+ refer to the latest RPC proxy as the original previous-saved RPC proxy may
+ be lost due to reboot.
+
+ The call_method is the method which does refer to the latest RPC proxy.
+ """
+ def __init__(self, call_method, name):
+ self.__call_method = call_method
+ self.__name = name
+
+ def __getattr__(self, name):
+ # Support a nested method.
+ return _Method(self.__call_method, "%s.%s" % (self.__name, name))
+
+ def __call__(self, *args, **dargs):
+ return self.__call_method(self.__name, *args, **dargs)
+
+
+class RPCProxy(object):
+ """Proxy to the FAFTClient RPC server on DUT.
+
+ It acts as a proxy to the FAFTClient on DUT. It is smart enough to:
+ - postpone the RPC connection to the first class method call;
+ - reconnect to the RPC server in case connection lost, e.g. reboot;
+ - always call the latest RPC proxy object.
+ """
+ _client_config = ClientConfig()
+
+ def __init__(self, host):
+ """Constructor.
+
+ @param host: The host object, passed via the test control file.
+ """
+ self._client = host
+ self._faft_client = None
+
+ def __del__(self):
+ self.disconnect()
+
+ def __getattr__(self, name):
+ """Return a _Method object only, not its real object."""
+ return _Method(self.__call_faft_client, name)
+
+ def __call_faft_client(self, name, *args, **dargs):
+ """Make the call on the latest RPC proxy object.
+
+ This method gets the internal method of the RPC proxy and calls it.
+
+ @param name: Name of the RPC method, a nested method supported.
+ @param args: The rest of arguments.
+ @param dargs: The rest of dict-type arguments.
+ @return: The return value of the FAFTClient RPC method.
+ """
+ try:
+ return getattr(self._faft_client, name)(*args, **dargs)
+ except (AttributeError, # _faft_client not initialized, still None
+ socket.error,
+ httplib.BadStatusLine,
+ xmlrpclib.ProtocolError):
+ # Reconnect the RPC server in case connection lost, e.g. reboot.
+ self.connect()
+ # Try again.
+ return getattr(self._faft_client, name)(*args, **dargs)
+
+ def connect(self):
+ """Connect the RPC server."""
+ self._faft_client = self._client.xmlrpc_connect(
+ self._client_config.rpc_command,
+ self._client_config.rpc_port,
+ command_name=self._client_config.rpc_command_short,
+ ready_test_name=self._client_config.rpc_ready_call,
+ timeout_seconds=self._client_config.rpc_timeout)
+
+ def disconnect(self):
+ """Disconnect the RPC server."""
+ self._client.xmlrpc_disconnect(self._client_config.rpc_port)
+ self._faft_client = None