| # Copyright 2016 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Tester feedback delegate.""" |
| |
| import logging |
| import xmlrpclib |
| |
| import common |
| from autotest_lib.client.common_lib.feedback import tester_feedback_client |
| |
| import query_delegate |
| |
| |
| class FeedbackDelegate(object): |
| """An object for managing feedback RPC calls.""" |
| |
| def __init__(self, multiplexer): |
| self._multiplexer = multiplexer |
| self._clients = {} |
| |
| |
| def _get_client(self, client_id): |
| """Returns the query dictionary for a client. |
| |
| @param client_id: The client identifier. |
| |
| @return: A dictionary mapping registered query numbers to query delegate |
| objects for the given client. |
| |
| @raise xmlrpclib.Fault: The client was not registered. |
| """ |
| if client_id not in self._clients: |
| raise xmlrpclib.Fault('Unknown client (%s)' % client_id) |
| return self._clients[client_id] |
| |
| |
| def _get_delegate_cls(self, query_id): |
| """Returns a query delegate class for a given query type. |
| |
| @param query_id: The query type for which a delegate is needed. |
| |
| @return: A query delegate class. |
| |
| @raise xmlrpclib.Fault: Query type is invalid or unsupported. |
| """ |
| try: |
| return query_delegate.get_delegate_cls(query_id) |
| except ValueError: |
| raise xmlrpclib.Fault('Unknown query type (%s)' % query_id) |
| except NotImplementedError: |
| raise xmlrpclib.Fault('Unsupported query type (%s)' % query_id) |
| |
| |
| def new_client(self, client_id): |
| """Register a new client. |
| |
| A client identifier is unique for a given test and DUT: at any given |
| time, there's only one test that is using this identifier. That said, |
| a client identifier may be reused across different tests at different |
| times within the lifetime of the feedback delegate. In general, clients |
| are expected to unregister when they finish running. However, for the |
| delegate to be resilient to test crashes, we forgo this requirement and |
| only emit a warning. |
| |
| @param client_id: The client identifier. |
| |
| @return: True (avoiding None with XML-RPC). |
| """ |
| if client_id in self._clients: |
| logging.warning('Overwriting existing client entry %s; prior ' |
| 'instance did not shutdown properly?', client_id) |
| self._clients[client_id] = {} |
| return True |
| |
| |
| def delete_client(self, client_id): |
| """Unregister a client. |
| |
| @param client_id: The client identifier. |
| |
| @return: True (avoiding None with XML-RPC). |
| """ |
| del self._clients[client_id] |
| return True |
| |
| |
| def new_query(self, client_id, query_id, query_num): |
| """Register a new query from a client. |
| |
| @param client_id: The client identifier. |
| @param query_id: The query type. |
| @param query_num: The query's unique number. |
| |
| @return: True (avoiding None with XML-RPC). |
| |
| @raise xmlrpclib.Fault: The client or query arguments are invalid. |
| """ |
| client = self._get_client(client_id) |
| if query_num in client: |
| raise xmlrpclib.Fault('New query (%s) is already registered' % |
| query_num) |
| test_name, dut_name = client_id.split(':') |
| client[query_num] = self._get_delegate_cls(query_id)( |
| test_name, dut_name, self._multiplexer) |
| return True |
| |
| |
| def query_call(self, client_id, query_num, query_method, kwargs_dict): |
| """Perform a query call. |
| |
| @param client_id: The client identifier. |
| @param query_num: The query unique number. |
| @param query_method: The method being called. |
| @param kwargs_dict: Extra arguments being passed to the method call. |
| |
| @return: A pair containing a method return code (constant defined in |
| tester_feedback_client) and a description of the result |
| (string). |
| |
| @raise: xmlrpclib.Fault: Method execution failed. |
| """ |
| try: |
| query = self._get_client(client_id)[query_num] |
| except KeyError: |
| raise xmlrpclib.Fault('Query %d unknown to client %s' % |
| (query_num, client_id)) |
| |
| # Route the query call to the appropriate method. |
| local_method = getattr(query, query_method, None) |
| if local_method is None: |
| ret = (tester_feedback_client.QUERY_RET_ERROR, |
| 'Unknown query method (%s)' % query_method) |
| else: |
| ret = local_method(**kwargs_dict) |
| |
| # If there's an explicit result, return it; otherwise, return success. |
| if ret is None: |
| return tester_feedback_client.QUERY_RET_SUCCESS, '' |
| return ret |