Home | History | Annotate | Download | only in tester_feedback
      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 
      7 import logging
      8 import xmlrpclib
      9 
     10 import common
     11 from autotest_lib.client.common_lib.feedback import tester_feedback_client
     12 
     13 import query_delegate
     14 
     15 
     16 class 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