1# 2# Copyright (C) 2017 The Android Open Source Project 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15# 16 17import logging 18import socket 19import time 20 21from host_controller.tradefed import remote_operation 22 23LOCALHOST = "localhost" 24DEFAULT_PORT = 30103 25 26 27class RemoteClient(object): 28 """The class for sending remote operations to TradeFed.""" 29 30 def __init__(self, host=LOCALHOST, port=DEFAULT_PORT, timeout=None): 31 """Initializes the client of TradeFed remote manager. 32 33 Args: 34 _host: The host name of the remote manager. 35 _port: The port of the remote manager. 36 _timeout: The connect and receive timeout in seconds 37 """ 38 self._host = host 39 self._port = port 40 self._timeout = timeout if timeout else socket.getdefaulttimeout() 41 42 def SendOperations(self, *operations): 43 """Sends a list of operations and waits for each response. 44 45 Args: 46 *operations: A list of remote_operation.RemoteOperation objects. 47 48 Returns: 49 A list of JSON objects. 50 51 Raises: 52 socket.error if fails to communicate with remote manager. 53 remote_operation.RemoteOperationException if any operation fails or 54 has no response. 55 """ 56 op_socket = socket.create_connection((self._host, self._port), 57 self._timeout) 58 logging.info("Connect to %s:%d", self._host, self._port) 59 try: 60 if self._timeout is not None: 61 op_socket.settimeout(self._timeout) 62 ops_str = "\n".join(str(op) for op in operations) 63 logging.info("Send: %s", ops_str) 64 op_socket.send(ops_str) 65 op_socket.shutdown(socket.SHUT_WR) 66 resp_str = "" 67 while True: 68 buf = op_socket.recv(4096) 69 if not buf: 70 break 71 resp_str += buf 72 finally: 73 op_socket.close() 74 logging.info("Receive: %s", resp_str) 75 resp_lines = [x for x in resp_str.split("\n") if x] 76 if len(resp_lines) != len(operations): 77 raise remote_operation.RemoteOperationException( 78 "Unexpected number of responses: %d" % len(resp_lines)) 79 return [operations[i].ParseResponse(resp_lines[i]) 80 for i in range(len(resp_lines))] 81 82 def SendOperation(self, operation): 83 """Sends one operation and waits for its response. 84 85 Args: 86 operation: A remote_operation.RemoteOperation object. 87 88 Returns: 89 A JSON object. 90 91 Raises: 92 socket.error if fails to communicate with remote manager. 93 remote_operation.RemoteOperationException if the operation fails or 94 has no response. 95 """ 96 return self.SendOperations(operation)[0] 97 98 def ListDevices(self): 99 """Sends ListDevices operation. 100 101 Returns: 102 A list of device_info.DeviceInfo which are the devices connected to 103 the host. 104 """ 105 json_obj = self.SendOperation(remote_operation.ListDevices()) 106 return remote_operation.ParseListDevicesResponse(json_obj) 107 108 def WaitForCommandResult(self, serial, timeout, poll_interval=5): 109 """Sends a series of operations to wait until a command finishes. 110 111 Args: 112 serial: The serial number of the device. 113 timeout: A float, the timeout in seconds. 114 poll_interval: A float, the interval of each GetLastCommandResult 115 operation in seconds. 116 117 Returns: 118 A JSON object which is the result of the command. 119 None if timeout. 120 121 Sample 122 {'status': 'INVOCATION_SUCCESS', 123 'free_device_state': 'AVAILABLE'} 124 """ 125 deadline = time.time() + timeout 126 get_result_op = remote_operation.GetLastCommandResult(serial) 127 while True: 128 result = self.SendOperation(get_result_op) 129 if result["status"] != "EXECUTING": 130 return result 131 if time.time() > deadline: 132 return None 133 time.sleep(poll_interval) 134 if time.time() > deadline: 135 return None 136