| # Lint as: python2, python3 |
| # Copyright (c) 2014 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. |
| """Chameleon Server.""" |
| |
| import logging |
| import signal |
| import sys |
| |
| try: |
| from xmlrpc.server import SimpleXMLRPCServer # Python 3.x |
| from xmlrpc.server import SimpleXMLRPCRequestHandler |
| except ImportError: |
| from six.moves.xmlrpc_server import SimpleXMLRPCServer # Python 2.x |
| from six.moves.xmlrpc_server import SimpleXMLRPCRequestHandler |
| |
| from . import chameleon_common # pylint: disable=W0611 |
| from chameleond.utils.caching_server import CachingServer |
| |
| |
| class ChameleonXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): |
| """XMLRPC request handler for Chameleon server. |
| |
| During the response of SimpleXMLRPCRequestHandler, it will try to obtain |
| client's domain name for logging. When there is no DNS server in the |
| network, this step will take a long time and delay the returning of calls |
| from server proxy. We override address_string method to bypass requesting |
| domain name. |
| """ |
| def address_string(self): |
| """Returns the client address formatted for logging. |
| |
| This method is overridden to bypass requesting domain name. |
| |
| Returns: |
| The formatted string for client address. |
| """ |
| host = self.client_address[0] |
| # original: return socket.getfqdn(host) |
| return '%s (no getfqdn)' % host |
| |
| |
| class ChameleonServer(object): |
| """Chameleon Server, which starts a RPC service.""" |
| |
| def __init__(self, driver, *args, **kwargs): |
| """Initializes ChameleonServer object. |
| |
| Args: |
| driver: String of the driver to serve the RPC server. |
| """ |
| # TODO(waihong): Probe all drivers and find a suitable one. |
| self._driver = self._LoadDriver(driver)(*args, **kwargs) |
| |
| def _LoadDriver(self, name): |
| """Load the driver from the driver directory. |
| |
| Args: |
| name: String of the driver name. |
| |
| Returns: |
| The class of the driver. |
| """ |
| module_name = name |
| logging.info('Load module %s...', module_name) |
| package = __import__('chameleond.drivers', fromlist=[module_name]) |
| module = getattr(package, module_name) |
| return getattr(module, 'ChameleondDriver') |
| |
| def RunServer(self, host='0.0.0.0', port=9992): |
| """Runs Chameleon RPC server. |
| |
| Args: |
| host: host address to serve the service. |
| port: port number of RPC server. |
| """ |
| caching = CachingServer(port + 1) |
| server = SimpleXMLRPCServer((host, port), allow_none=True, |
| requestHandler=ChameleonXMLRPCRequestHandler, |
| logRequests=True) |
| server.register_introspection_functions() |
| # Setting allow_dotted_names=True allows a client to access the object |
| # members of self._driver. This is useful to group methods into |
| # different objects, e.g., audio, video, bluetooth hid, etc., in addition |
| # to some generic methods. |
| server.register_instance(self._driver, allow_dotted_names=True) |
| |
| signal_handler = lambda signum, frame: sys.exit(0) |
| signal.signal(signal.SIGTERM, signal_handler) |
| |
| try: |
| # Launch the caching server on the next port, serving cached files. |
| logging.info('Start the caching server process.') |
| caching.start() |
| |
| # Launch the XMLRPC server to serve Chameleond APIs. |
| logging.info('Listening on %s port %d...', host, port) |
| server.serve_forever() |
| finally: |
| logging.info('Terminate the caching server process.') |
| caching.terminate() |