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 5import httplib 6import socket 7import time 8import xmlrpclib 9 10from autotest_lib.client.cros.faft.config import Config as ClientConfig 11from autotest_lib.server import autotest 12 13 14class _Method(object): 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 def __str__(self): 36 """Return a description of the method object""" 37 return "%s('%s')" % (self.__class__.__name__, self.__name) 38 39 def __repr__(self): 40 """Return a description of the method object""" 41 return "<%s '%s'>" % (self.__class__.__name__, self.__name) 42 43 44class RPCProxy(object): 45 """Proxy to the FAFTClient RPC server on DUT. 46 47 It acts as a proxy to the FAFTClient on DUT. It is smart enough to: 48 - postpone the RPC connection to the first class method call; 49 - reconnect to the RPC server in case connection lost, e.g. reboot; 50 - always call the latest RPC proxy object. 51 52 @ivar _client: the ssh host object 53 @type host: autotest_lib.server.hosts.abstract_ssh.AbstractSSHHost 54 @ivar _faft_client: the real serverproxy to use for calls 55 @type _faft_client: xmlrpclib.ServerProxy 56 """ 57 _client_config = ClientConfig() 58 59 def __init__(self, host): 60 """Constructor. 61 62 @param host: The host object, passed via the test control file. 63 """ 64 self._client = host 65 self._faft_client = None 66 67 def __del__(self): 68 self.disconnect() 69 70 def __getattr__(self, name): 71 """Return a _Method object only, not its real object.""" 72 return _Method(self.__call_faft_client, name) 73 74 def __call_faft_client(self, name, *args, **dargs): 75 """Make the call on the latest RPC proxy object. 76 77 This method gets the internal method of the RPC proxy and calls it. 78 79 @param name: Name of the RPC method, a nested method supported. 80 @param args: The rest of arguments. 81 @param dargs: The rest of dict-type arguments. 82 @return: The return value of the FAFTClient RPC method. 83 """ 84 if self._faft_client is None: 85 self.connect() 86 try: 87 return getattr(self._faft_client, name)(*args, **dargs) 88 except (socket.error, 89 httplib.BadStatusLine, 90 xmlrpclib.ProtocolError): 91 # Reconnect the RPC server in case connection lost, e.g. reboot. 92 self.connect() 93 # Try again. 94 return getattr(self._faft_client, name)(*args, **dargs) 95 96 def connect(self): 97 """Connect the RPC server.""" 98 # Make sure Autotest dependency is there. 99 autotest.Autotest(self._client).install() 100 self._faft_client = self._client.rpc_server_tracker.xmlrpc_connect( 101 self._client_config.rpc_command, 102 self._client_config.rpc_port, 103 command_name=self._client_config.rpc_command_short, 104 ready_test_name=self._client_config.rpc_ready_call, 105 timeout_seconds=self._client_config.rpc_timeout, 106 logfile="%s.%s" % (self._client_config.rpc_logfile, 107 time.time()) 108 ) 109 110 def disconnect(self): 111 """Disconnect the RPC server.""" 112 self._client.rpc_server_tracker.disconnect(self._client_config.rpc_port) 113 self._faft_client = None 114 115 def __str__(self): 116 """Return a description of the proxy object""" 117 return '%s(%s)' % (self.__class__.__name__, self._client) 118 119 def __repr__(self): 120 """Return a description of the proxy object""" 121 return "<%s '%s'>" % (self.__class__.__name__, self._client.hostname) 122