Tom Wai-Hong Tam | 2b1ef4f | 2013-07-20 00:54:48 +0800 | [diff] [blame] | 1 | # Copyright (c) 2013 The Chromium OS Authors. All rights reserved. |
| 2 | # Use of this source code is governed by a BSD-style license that can be |
| 3 | # found in the LICENSE file. |
| 4 | |
| 5 | import httplib |
| 6 | import socket |
Yusuf Mohsinally | 8d19e3c | 2013-11-21 14:25:45 -0800 | [diff] [blame] | 7 | import time |
Tom Wai-Hong Tam | 2b1ef4f | 2013-07-20 00:54:48 +0800 | [diff] [blame] | 8 | import xmlrpclib |
| 9 | |
| 10 | from autotest_lib.client.cros.faft.config import Config as ClientConfig |
Tom Wai-Hong Tam | eeed7fb | 2015-05-08 09:43:29 +0800 | [diff] [blame] | 11 | from autotest_lib.server import autotest |
Tom Wai-Hong Tam | 2b1ef4f | 2013-07-20 00:54:48 +0800 | [diff] [blame] | 12 | |
| 13 | |
| 14 | class _Method: |
| 15 | """Class to save the name of the RPC method instead of the real object. |
| 16 | |
| 17 | It keeps the name of the RPC method locally first such that the RPC method |
| 18 | can be evalulated to a real object while it is called. Its purpose is to |
| 19 | refer to the latest RPC proxy as the original previous-saved RPC proxy may |
| 20 | be lost due to reboot. |
| 21 | |
| 22 | The call_method is the method which does refer to the latest RPC proxy. |
| 23 | """ |
| 24 | def __init__(self, call_method, name): |
| 25 | self.__call_method = call_method |
| 26 | self.__name = name |
| 27 | |
| 28 | def __getattr__(self, name): |
| 29 | # Support a nested method. |
| 30 | return _Method(self.__call_method, "%s.%s" % (self.__name, name)) |
| 31 | |
| 32 | def __call__(self, *args, **dargs): |
| 33 | return self.__call_method(self.__name, *args, **dargs) |
| 34 | |
| 35 | |
| 36 | class RPCProxy(object): |
| 37 | """Proxy to the FAFTClient RPC server on DUT. |
| 38 | |
| 39 | It acts as a proxy to the FAFTClient on DUT. It is smart enough to: |
| 40 | - postpone the RPC connection to the first class method call; |
| 41 | - reconnect to the RPC server in case connection lost, e.g. reboot; |
| 42 | - always call the latest RPC proxy object. |
| 43 | """ |
| 44 | _client_config = ClientConfig() |
| 45 | |
| 46 | def __init__(self, host): |
| 47 | """Constructor. |
| 48 | |
| 49 | @param host: The host object, passed via the test control file. |
| 50 | """ |
| 51 | self._client = host |
| 52 | self._faft_client = None |
| 53 | |
| 54 | def __del__(self): |
| 55 | self.disconnect() |
| 56 | |
| 57 | def __getattr__(self, name): |
| 58 | """Return a _Method object only, not its real object.""" |
| 59 | return _Method(self.__call_faft_client, name) |
| 60 | |
| 61 | def __call_faft_client(self, name, *args, **dargs): |
| 62 | """Make the call on the latest RPC proxy object. |
| 63 | |
| 64 | This method gets the internal method of the RPC proxy and calls it. |
| 65 | |
| 66 | @param name: Name of the RPC method, a nested method supported. |
| 67 | @param args: The rest of arguments. |
| 68 | @param dargs: The rest of dict-type arguments. |
| 69 | @return: The return value of the FAFTClient RPC method. |
| 70 | """ |
| 71 | try: |
| 72 | return getattr(self._faft_client, name)(*args, **dargs) |
| 73 | except (AttributeError, # _faft_client not initialized, still None |
| 74 | socket.error, |
| 75 | httplib.BadStatusLine, |
| 76 | xmlrpclib.ProtocolError): |
| 77 | # Reconnect the RPC server in case connection lost, e.g. reboot. |
| 78 | self.connect() |
| 79 | # Try again. |
| 80 | return getattr(self._faft_client, name)(*args, **dargs) |
| 81 | |
| 82 | def connect(self): |
| 83 | """Connect the RPC server.""" |
Tom Wai-Hong Tam | eeed7fb | 2015-05-08 09:43:29 +0800 | [diff] [blame] | 84 | # Make sure Autotest dependency is there. |
| 85 | autotest.Autotest(self._client).install() |
Roshan Pius | 58e5dd3 | 2015-10-16 15:16:42 -0700 | [diff] [blame] | 86 | self._faft_client = self._client.rpc_server_tracker.xmlrpc_connect( |
Tom Wai-Hong Tam | 2b1ef4f | 2013-07-20 00:54:48 +0800 | [diff] [blame] | 87 | self._client_config.rpc_command, |
| 88 | self._client_config.rpc_port, |
| 89 | command_name=self._client_config.rpc_command_short, |
| 90 | ready_test_name=self._client_config.rpc_ready_call, |
Yusuf Mohsinally | fff89d6 | 2013-11-18 16:34:07 -0800 | [diff] [blame] | 91 | timeout_seconds=self._client_config.rpc_timeout, |
Yusuf Mohsinally | 8d19e3c | 2013-11-21 14:25:45 -0800 | [diff] [blame] | 92 | logfile="%s.%s" % (self._client_config.rpc_logfile, |
| 93 | time.time()) |
| 94 | ) |
Tom Wai-Hong Tam | 2b1ef4f | 2013-07-20 00:54:48 +0800 | [diff] [blame] | 95 | |
| 96 | def disconnect(self): |
| 97 | """Disconnect the RPC server.""" |
Roshan Pius | 58e5dd3 | 2015-10-16 15:16:42 -0700 | [diff] [blame] | 98 | self._client.rpc_server_tracker.disconnect(self._client_config.rpc_port) |
Tom Wai-Hong Tam | 2b1ef4f | 2013-07-20 00:54:48 +0800 | [diff] [blame] | 99 | self._faft_client = None |