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