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"""Tester feedback delegate.""" 6 7import logging 8import xmlrpclib 9 10import common 11from autotest_lib.client.common_lib.feedback import tester_feedback_client 12 13import query_delegate 14 15 16class FeedbackDelegate(object): 17 """An object for managing feedback RPC calls.""" 18 19 def __init__(self, multiplexer): 20 self._multiplexer = multiplexer 21 self._clients = {} 22 23 24 def _get_client(self, client_id): 25 """Returns the query dictionary for a client. 26 27 @param client_id: The client identifier. 28 29 @return: A dictionary mapping registered query numbers to query delegate 30 objects for the given client. 31 32 @raise xmlrpclib.Fault: The client was not registered. 33 """ 34 if client_id not in self._clients: 35 raise xmlrpclib.Fault('Unknown client (%s)' % client_id) 36 return self._clients[client_id] 37 38 39 def _get_delegate_cls(self, query_id): 40 """Returns a query delegate class for a given query type. 41 42 @param query_id: The query type for which a delegate is needed. 43 44 @return: A query delegate class. 45 46 @raise xmlrpclib.Fault: Query type is invalid or unsupported. 47 """ 48 try: 49 return query_delegate.get_delegate_cls(query_id) 50 except ValueError: 51 raise xmlrpclib.Fault('Unknown query type (%s)' % query_id) 52 except NotImplementedError: 53 raise xmlrpclib.Fault('Unsupported query type (%s)' % query_id) 54 55 56 def new_client(self, client_id): 57 """Register a new client. 58 59 A client identifier is unique for a given test and DUT: at any given 60 time, there's only one test that is using this identifier. That said, 61 a client identifier may be reused across different tests at different 62 times within the lifetime of the feedback delegate. In general, clients 63 are expected to unregister when they finish running. However, for the 64 delegate to be resilient to test crashes, we forgo this requirement and 65 only emit a warning. 66 67 @param client_id: The client identifier. 68 69 @return: True (avoiding None with XML-RPC). 70 """ 71 if client_id in self._clients: 72 logging.warning('Overwriting existing client entry %s; prior ' 73 'instance did not shutdown properly?', client_id) 74 self._clients[client_id] = {} 75 return True 76 77 78 def delete_client(self, client_id): 79 """Unregister a client. 80 81 @param client_id: The client identifier. 82 83 @return: True (avoiding None with XML-RPC). 84 """ 85 del self._clients[client_id] 86 return True 87 88 89 def new_query(self, client_id, query_id, query_num): 90 """Register a new query from a client. 91 92 @param client_id: The client identifier. 93 @param query_id: The query type. 94 @param query_num: The query's unique number. 95 96 @return: True (avoiding None with XML-RPC). 97 98 @raise xmlrpclib.Fault: The client or query arguments are invalid. 99 """ 100 client = self._get_client(client_id) 101 if query_num in client: 102 raise xmlrpclib.Fault('New query (%s) is already registered' % 103 query_num) 104 test_name, dut_name = client_id.split(':') 105 client[query_num] = self._get_delegate_cls(query_id)( 106 test_name, dut_name, self._multiplexer) 107 return True 108 109 110 def query_call(self, client_id, query_num, query_method, kwargs_dict): 111 """Perform a query call. 112 113 @param client_id: The client identifier. 114 @param query_num: The query unique number. 115 @param query_method: The method being called. 116 @param kwargs_dict: Extra arguments being passed to the method call. 117 118 @return: A pair containing a method return code (constant defined in 119 tester_feedback_client) and a description of the result 120 (string). 121 122 @raise: xmlrpclib.Fault: Method execution failed. 123 """ 124 try: 125 query = self._get_client(client_id)[query_num] 126 except KeyError: 127 raise xmlrpclib.Fault('Query %d unknown to client %s' % 128 (query_num, client_id)) 129 130 # Route the query call to the appropriate method. 131 local_method = getattr(query, query_method, None) 132 if local_method is None: 133 ret = (tester_feedback_client.QUERY_RET_ERROR, 134 'Unknown query method (%s)' % query_method) 135 else: 136 ret = local_method(**kwargs_dict) 137 138 # If there's an explicit result, return it; otherwise, return success. 139 if ret is None: 140 return tester_feedback_client.QUERY_RET_SUCCESS, '' 141 return ret 142