blob: e1aee0216ce557ab0feb685a6791dfc175fda64e [file] [log] [blame]
#!/usr/bin/python
# 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.
"""Loads ShopFloorHandler module and wraps it as a FastCGI server.
Note that it only accepts requests with path "/shop_floor/<port>/<token>".
Example:
/path/to/shop_floor_launcher -a 127.0.0.1 -p 8085 -t abcde \
-m cros.factory.umpire.<board>_shop_floor_handler
"""
import logging
import optparse
import socket
import factory_common # pylint: disable=W0611
from cros.factory.umpire import common
from cros.factory.umpire import fastcgi_server
from cros.factory.umpire import shop_floor_handler
def _LoadShopFloorHandler(module_name):
"""Loads a ShopFloorHandle module.
Args:
module_name: Name of module containing a ShopFloorHandler class.
Returns:
Module reference.
"""
logging.debug('_LoadShopFloorHandler: %s', module_name)
return __import__(module_name, fromlist=['ShopFloorHandler']).ShopFloorHandler
def _SetLogging(verbosity, quiet):
"""Sets logging level and format.
Args:
verbosity: logging verbosity. 0 for INFO and 1 for DEBUG.
quiet: True to disable INFO logging.
"""
VERBOSITY_MAP = {0: logging.INFO,
1: logging.DEBUG}
log_level = VERBOSITY_MAP.get(verbosity or 0, logging.NOTSET)
log_format = ['%(asctime)s %(levelname)s']
if verbosity > 0:
log_format.append('(%(filename)s:%(lineno)d)')
log_format.append('%(message)s')
logging.basicConfig(level=log_level, format=' '.join(log_format))
if quiet:
logging.disable(logging.INFO)
def _DisableDNSLookup():
"""Disables all DNS lookups.
Since otherwise the logging code may try to resolve IP addresses, which may
delay request handling.
"""
def FakeGetFQDN(name=None):
return name or 'localhost'
socket.getfqdn = FakeGetFQDN
def _ShopFloorHandlerFactory(module):
"""Creates ShopFloorHandler instance.
It exists the program if either ShopFloorHandler module fails to load or
the module does not inherit from ShopFloorHandlerBase.
Args:
module: ShopFloorHandler module name.
Returns:
A ShopFloorHandler instance.
"""
try:
logging.debug('Loading ShopFloorHandler module: %s', module)
instance = _LoadShopFloorHandler(module)()
if not isinstance(instance, shop_floor_handler.ShopFloorHandlerBase):
logging.critical('Module does not inherit ShopFloorHandlerBase: %s',
module)
exit(1)
except: # pylint: disable=W0702
logging.exception('Failed loading module: %s', module)
exit(1)
return instance
def main():
description = (
'It wraps the ShopFloorHandler module as an FastCGI service which '
'handles XMLRPC requests. Note that it only handles POST requests with '
'path "/shop_floor/<port>/<token>".')
parser = optparse.OptionParser(description=description)
parser.add_option('-a', '--address', dest='address', metavar='ADDR',
default='127.0.0.1',
help='address to bind (default: %default)')
parser.add_option('-p', '--port', dest='port', metavar='PORT', type='int',
help='port to bind')
parser.add_option(
'-m', '--module', dest='module', metavar='MODULE',
help=('ShopFloorHandler module to load, in PACKAGE.MODULE format. '
'e.g. cros.factory.umpire.<board>_shop_floor_handler'))
parser.add_option('-t', '--token', dest='token', metavar='TOKEN',
help='a unique token for the handler process')
parser.add_option('-v', '--verbose', action='count', dest='verbose',
help='increase message verbosity')
parser.add_option('-q', '--quiet', action='store_true', dest='quiet',
help='turn off verbose messages')
# Parse args.
(options, args) = parser.parse_args()
if args:
parser.error('Invalid args: %s' % ' '.join(args))
if not options.module:
parser.error('You need to assign the module to be loaded (-m).')
_SetLogging(options.verbose, options.quiet)
_DisableDNSLookup()
instance = _ShopFloorHandlerFactory(options.module)
# WSGI environ's SCRIPT_NAME to accept.
script_name = '%s/%d/%s' % (common.HANDLER_BASE, options.port, options.token)
logging.info(
'Starting FastCGI server (module:%s) on http://%s:%d%s',
options.module, options.address, options.port, script_name)
# FastCGI server runs forever.
fastcgi_server.FastCGIServer(
options.address, options.port, instance, script_name=script_name)
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG,
format='%(levelname)5s %(message)s')
main()