1# Lint as: python2, python3 2# Copyright 2016 The Chromium OS Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6"""Feedback client implementation for interacting with a human tester.""" 7 8import six.moves.xmlrpc_client 9 10import common 11from autotest_lib.client.common_lib import error 12from autotest_lib.client.common_lib.feedback import client 13 14 15# Query return codes. 16# 17QUERY_RET_SUCCESS = 0 18QUERY_RET_FAIL = 1 19QUERY_RET_ERROR = 2 20 21 22class Client(client.Client): 23 """Human tester feedback implementation.""" 24 25 def __init__(self, test_name, dut_name, remote_addr): 26 """Constructs the client object. 27 28 @param test_name: The name of the test. 29 @param dut_name: The name of the DUT. 30 @param remote_addr: The 'name:port' of the remote feedback service host. 31 """ 32 super(Client, self).__init__() 33 self._client_id = '%s:%s' % (test_name, dut_name) 34 self._remote_addr = remote_addr 35 self._query_num = 0 36 self._rpc_proxy = None 37 self.tmp_dir = None 38 self.dut_tmp_dir = None 39 40 41 def _make_query_call(self, query_num, query_method, **kwargs): 42 """Make an RPC query call (used by query objects). 43 44 @param query_num: The unique query identifying number. 45 @param query_method: The query method being called. 46 47 @raise xmlrpclib.Error: An error during RPC call processing. 48 """ 49 # XML-RPC does not support kwargs, so we just pass it as a dictionary. 50 return self._rpc_proxy.query_call(self._client_id, query_num, 51 query_method, kwargs) 52 53 54 # Interface overrides. 55 # 56 def _initialize_impl(self, test, host): 57 """Initializes the feedback object. 58 59 Initializes an XML-RPC proxy and registers the client at the remote end. 60 61 @param test: An object representing the test case. 62 @param host: An object representing the DUT. 63 """ 64 self._rpc_proxy = six.moves.xmlrpc_client.ServerProxy('http://%s' % self._remote_addr) 65 try: 66 self._rpc_proxy.new_client(self._client_id) 67 except six.moves.xmlrpc_client.Error as e: 68 raise error.TestError('Feedback client registration error: %s' % e) 69 self.tmp_dir = test.tmpdir 70 self.dut_tmp_dir = host.get_tmp_dir() 71 72 73 def _new_query_impl(self, query_id): 74 """Instantiates a new query. 75 76 @param query_id: A query identifier. 77 78 @return A query object. 79 """ 80 if query_id in client.INPUT_QUERIES: 81 query_cls = InputQuery 82 elif query_id in client.OUTPUT_QUERIES: 83 query_cls = OutputQuery 84 else: 85 raise error.TestError('Unknown query (%s)' % query_id) 86 87 # Create, register and return a new query. 88 self._query_num += 1 89 try: 90 self._rpc_proxy.new_query(self._client_id, query_id, self._query_num) 91 except six.moves.xmlrpc_client.Error as e: 92 raise error.TestError('Feedback query registration error: %s' % e) 93 return query_cls(self, self._query_num) 94 95 96 def _finalize_impl(self): 97 """Finalizes the feedback object.""" 98 try: 99 self._rpc_proxy.delete_client(self._client_id) 100 except six.moves.xmlrpc_client.Error as e: 101 raise error.TestError( 102 'Feedback client deregistration error: %s' % e) 103 104 105class _Query(object): 106 """Human tester feedback query base class.""" 107 108 def __init__(self, client, query_num): 109 super(_Query, self).__init__() 110 self.client = client 111 self.query_num = query_num 112 113 114 def _make_query_call(self, query_method, **kwargs): 115 try: 116 ret, desc = self.client._make_query_call(self.query_num, 117 query_method, **kwargs) 118 except six.moves.xmlrpc_client.Error as e: 119 ret, desc = QUERY_RET_ERROR, str(e) 120 121 if ret == QUERY_RET_SUCCESS: 122 return 123 if ret == QUERY_RET_FAIL: 124 raise error.TestFail('Tester feedback request failed: %s' % desc) 125 if ret == QUERY_RET_ERROR: 126 raise error.TestError('Tester feedback request error: %s' % desc) 127 raise error.TestError('Unknown feedback call return code (%s)' % ret) 128 129 130 # Interface overrides. 131 # 132 def _prepare_impl(self, **kwargs): 133 self._make_query_call('prepare', **kwargs) 134 135 136 def _validate_impl(self, **kwargs): 137 self._make_query_call('validate', **kwargs) 138 139 140class OutputQuery(_Query, client.OutputQuery): 141 """Human tester feedback output query.""" 142 143 def __init__(self, client, query_num): 144 super(OutputQuery, self).__init__(client, query_num) 145 146 147class InputQuery(_Query, client.InputQuery): 148 """Human tester feedback input query.""" 149 150 def __init__(self, client, query_num): 151 super(InputQuery, self).__init__(client, query_num) 152 153 154 # Interface override. 155 # 156 def _emit_impl(self): 157 self._make_query_call('emit') 158