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