# # Copyright (C) 2017 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # import logging import socket import time from host_controller.tradefed import remote_operation LOCALHOST = "localhost" DEFAULT_PORT = 30103 class RemoteClient(object): """The class for sending remote operations to TradeFed.""" def __init__(self, host=LOCALHOST, port=DEFAULT_PORT, timeout=None): """Initializes the client of TradeFed remote manager. Args: _host: The host name of the remote manager. _port: The port of the remote manager. _timeout: The connect and receive timeout in seconds """ self._host = host self._port = port self._timeout = timeout if timeout else socket.getdefaulttimeout() def SendOperations(self, *operations): """Sends a list of operations and waits for each response. Args: *operations: A list of remote_operation.RemoteOperation objects. Returns: A list of JSON objects. Raises: socket.error if fails to communicate with remote manager. remote_operation.RemoteOperationException if any operation fails or has no response. """ op_socket = socket.create_connection((self._host, self._port), self._timeout) logging.info("Connect to %s:%d", self._host, self._port) try: if self._timeout is not None: op_socket.settimeout(self._timeout) ops_str = "\n".join(str(op) for op in operations) logging.info("Send: %s", ops_str) op_socket.send(ops_str) op_socket.shutdown(socket.SHUT_WR) resp_str = "" while True: buf = op_socket.recv(4096) if not buf: break resp_str += buf finally: op_socket.close() logging.info("Receive: %s", resp_str) resp_lines = [x for x in resp_str.split("\n") if x] if len(resp_lines) != len(operations): raise remote_operation.RemoteOperationException( "Unexpected number of responses: %d" % len(resp_lines)) return [operations[i].ParseResponse(resp_lines[i]) for i in range(len(resp_lines))] def SendOperation(self, operation): """Sends one operation and waits for its response. Args: operation: A remote_operation.RemoteOperation object. Returns: A JSON object. Raises: socket.error if fails to communicate with remote manager. remote_operation.RemoteOperationException if the operation fails or has no response. """ return self.SendOperations(operation)[0] def ListDevices(self): """Sends ListDevices operation. Returns: A list of device_info.DeviceInfo which are the devices connected to the host. """ json_obj = self.SendOperation(remote_operation.ListDevices()) return remote_operation.ParseListDevicesResponse(json_obj) def WaitForCommandResult(self, serial, timeout, poll_interval=5): """Sends a series of operations to wait until a command finishes. Args: serial: The serial number of the device. timeout: A float, the timeout in seconds. poll_interval: A float, the interval of each GetLastCommandResult operation in seconds. Returns: A JSON object which is the result of the command. None if timeout. Sample {'status': 'INVOCATION_SUCCESS', 'free_device_state': 'AVAILABLE'} """ deadline = time.time() + timeout get_result_op = remote_operation.GetLastCommandResult(serial) while True: result = self.SendOperation(get_result_op) if result["status"] != "EXECUTING": return result if time.time() > deadline: return None time.sleep(poll_interval) if time.time() > deadline: return None