1*9c5db199SXin Li# Lint as: python2, python3 2*9c5db199SXin Li# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. 3*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be 4*9c5db199SXin Li# found in the LICENSE file. 5*9c5db199SXin Li 6*9c5db199SXin Liimport logging 7*9c5db199SXin Lifrom six.moves import http_client as httplib 8*9c5db199SXin Liimport socket 9*9c5db199SXin Liimport time 10*9c5db199SXin Lifrom six.moves import xmlrpc_client as xmlrpclib 11*9c5db199SXin Li 12*9c5db199SXin Lifrom autotest_lib.client.cros.faft.config import Config as ClientConfig 13*9c5db199SXin Lifrom autotest_lib.server import autotest 14*9c5db199SXin Li 15*9c5db199SXin Li 16*9c5db199SXin Liclass _Method(object): 17*9c5db199SXin Li """Class to save the name of the RPC method instead of the real object. 18*9c5db199SXin Li 19*9c5db199SXin Li It keeps the name of the RPC method locally first such that the RPC method 20*9c5db199SXin Li can be evalulated to a real object while it is called. Its purpose is to 21*9c5db199SXin Li refer to the latest RPC proxy as the original previous-saved RPC proxy may 22*9c5db199SXin Li be lost due to reboot. 23*9c5db199SXin Li 24*9c5db199SXin Li The call_method is the method which does refer to the latest RPC proxy. 25*9c5db199SXin Li """ 26*9c5db199SXin Li def __init__(self, call_method, name): 27*9c5db199SXin Li self.__call_method = call_method 28*9c5db199SXin Li self.__name = name 29*9c5db199SXin Li 30*9c5db199SXin Li def __getattr__(self, name): 31*9c5db199SXin Li # Support a nested method. 32*9c5db199SXin Li return _Method(self.__call_method, "%s.%s" % (self.__name, name)) 33*9c5db199SXin Li 34*9c5db199SXin Li def __call__(self, *args, **dargs): 35*9c5db199SXin Li return self.__call_method(self.__name, *args, **dargs) 36*9c5db199SXin Li 37*9c5db199SXin Li def __repr__(self): 38*9c5db199SXin Li """Return a description of the method object""" 39*9c5db199SXin Li return "%s('%s')" % (self.__class__.__name__, self.__name) 40*9c5db199SXin Li 41*9c5db199SXin Li def __str__(self): 42*9c5db199SXin Li """Return a description of the method object""" 43*9c5db199SXin Li return "<%s '%s'>" % (self.__class__.__name__, self.__name) 44*9c5db199SXin Li 45*9c5db199SXin Li 46*9c5db199SXin Liclass RPCProxy(object): 47*9c5db199SXin Li """Proxy to the FAFTClient RPC server on DUT. 48*9c5db199SXin Li 49*9c5db199SXin Li It acts as a proxy to the FAFTClient on DUT. It is smart enough to: 50*9c5db199SXin Li - postpone the RPC connection to the first class method call; 51*9c5db199SXin Li - reconnect to the RPC server in case connection lost, e.g. reboot; 52*9c5db199SXin Li - always call the latest RPC proxy object. 53*9c5db199SXin Li 54*9c5db199SXin Li @ivar _client: the ssh host object 55*9c5db199SXin Li @type host: autotest_lib.server.hosts.abstract_ssh.AbstractSSHHost 56*9c5db199SXin Li @ivar _faft_client: the real serverproxy to use for calls 57*9c5db199SXin Li @type _faft_client: xmlrpclib.ServerProxy | None 58*9c5db199SXin Li """ 59*9c5db199SXin Li _client_config = ClientConfig() 60*9c5db199SXin Li 61*9c5db199SXin Li def __init__(self, host): 62*9c5db199SXin Li """Constructor. 63*9c5db199SXin Li 64*9c5db199SXin Li @param host: The host object, passed via the test control file. 65*9c5db199SXin Li """ 66*9c5db199SXin Li self.host = host 67*9c5db199SXin Li self._faft_client = None 68*9c5db199SXin Li self.logfiles = [] 69*9c5db199SXin Li 70*9c5db199SXin Li def __del__(self): 71*9c5db199SXin Li self.disconnect() 72*9c5db199SXin Li 73*9c5db199SXin Li def __getattr__(self, name): 74*9c5db199SXin Li """Return a _Method object only, not its real object.""" 75*9c5db199SXin Li return _Method(self.__call_faft_client, name) 76*9c5db199SXin Li 77*9c5db199SXin Li def __call_faft_client(self, name, *args, **dargs): 78*9c5db199SXin Li """Make the call on the latest RPC proxy object. 79*9c5db199SXin Li 80*9c5db199SXin Li This method gets the internal method of the RPC proxy and calls it. 81*9c5db199SXin Li 82*9c5db199SXin Li @param name: Name of the RPC method, a nested method supported. 83*9c5db199SXin Li @param args: The rest of arguments. 84*9c5db199SXin Li @param dargs: The rest of dict-type arguments. 85*9c5db199SXin Li @return: The return value of the FAFTClient RPC method. 86*9c5db199SXin Li """ 87*9c5db199SXin Li if self._faft_client is None: 88*9c5db199SXin Li self.connect() 89*9c5db199SXin Li try: 90*9c5db199SXin Li return getattr(self._faft_client, name)(*args, **dargs) 91*9c5db199SXin Li except (socket.error, 92*9c5db199SXin Li httplib.BadStatusLine, 93*9c5db199SXin Li xmlrpclib.ProtocolError): 94*9c5db199SXin Li # Reconnect the RPC server in case connection lost, e.g. reboot. 95*9c5db199SXin Li self.connect() 96*9c5db199SXin Li # Try again. 97*9c5db199SXin Li return getattr(self._faft_client, name)(*args, **dargs) 98*9c5db199SXin Li 99*9c5db199SXin Li def connect(self): 100*9c5db199SXin Li """Connect the RPC server.""" 101*9c5db199SXin Li # Make sure Autotest dependency is there. 102*9c5db199SXin Li autotest.Autotest(self.host).install() 103*9c5db199SXin Li logfile = "%s.%s" % (self._client_config.rpc_logfile, time.time()) 104*9c5db199SXin Li self.logfiles.append(logfile) 105*9c5db199SXin Li self._faft_client = self.host.rpc_server_tracker.xmlrpc_connect( 106*9c5db199SXin Li self._client_config.rpc_command, 107*9c5db199SXin Li self._client_config.rpc_port, 108*9c5db199SXin Li command_name=self._client_config.rpc_command_short, 109*9c5db199SXin Li ready_test_name=self._client_config.rpc_ready_call, 110*9c5db199SXin Li timeout_seconds=self._client_config.rpc_timeout, 111*9c5db199SXin Li logfile=logfile, 112*9c5db199SXin Li server_desc=str(self), 113*9c5db199SXin Li request_timeout_seconds=self._client_config. 114*9c5db199SXin Li rpc_request_timeout, 115*9c5db199SXin Li ) 116*9c5db199SXin Li 117*9c5db199SXin Li def disconnect(self): 118*9c5db199SXin Li """Disconnect the RPC server.""" 119*9c5db199SXin Li # The next start of the RPC server will terminate any leftovers, 120*9c5db199SXin Li # so no need to pkill upon disconnect. 121*9c5db199SXin Li if self._faft_client is not None: 122*9c5db199SXin Li logging.debug("Closing FAFT RPC server connection.") 123*9c5db199SXin Li self.host.rpc_server_tracker.disconnect(self._client_config.rpc_port, 124*9c5db199SXin Li pkill=False) 125*9c5db199SXin Li self._faft_client = None 126*9c5db199SXin Li 127*9c5db199SXin Li def quit(self): 128*9c5db199SXin Li """Tell the RPC server to quit, then disconnect from it.""" 129*9c5db199SXin Li if self._faft_client is None: 130*9c5db199SXin Li return 131*9c5db199SXin Li logging.debug("Telling FAFT RPC server to quit.") 132*9c5db199SXin Li try: 133*9c5db199SXin Li remote_quit = getattr( 134*9c5db199SXin Li self._faft_client, self._client_config.rpc_quit_call) 135*9c5db199SXin Li remote_quit() 136*9c5db199SXin Li need_pkill = False 137*9c5db199SXin Li except Exception as e: 138*9c5db199SXin Li logging.warning("Error while telling FAFT RPC server to quit: %s", e) 139*9c5db199SXin Li # If we failed to tell the RPC server to quit for some reason, 140*9c5db199SXin Li # fall back to SIGTERM, because it may not have exited. 141*9c5db199SXin Li need_pkill = True 142*9c5db199SXin Li 143*9c5db199SXin Li self.host.rpc_server_tracker.disconnect(self._client_config.rpc_port, 144*9c5db199SXin Li pkill=need_pkill) 145*9c5db199SXin Li self._faft_client = None 146*9c5db199SXin Li 147*9c5db199SXin Li def collect_logfiles(self, dest): 148*9c5db199SXin Li """Download all logfiles from the DUT, then delete them.""" 149*9c5db199SXin Li if self.logfiles: 150*9c5db199SXin Li for logfile in self.logfiles: 151*9c5db199SXin Li if self.host.run("test -f", args=[logfile], 152*9c5db199SXin Li ignore_status=True).exit_status == 0: 153*9c5db199SXin Li self.host.get_file(logfile, dest) 154*9c5db199SXin Li self.host.run("rm -f", ignore_status=True, args=[logfile]) 155*9c5db199SXin Li self.logfiles.clear() 156*9c5db199SXin Li 157*9c5db199SXin Li def __repr__(self): 158*9c5db199SXin Li """Return a description of the proxy object""" 159*9c5db199SXin Li return '%s(%s)' % (self.__class__.__name__, self.host) 160*9c5db199SXin Li 161*9c5db199SXin Li def __str__(self): 162*9c5db199SXin Li """Return a description of the proxy object""" 163*9c5db199SXin Li return "<%s '%s:%s'>" % (self.__class__.__name__, self.host.hostname, 164*9c5db199SXin Li self._client_config.rpc_port) 165