| Index: devserver.py
|
| diff --git a/devserver.py b/devserver.py
|
| old mode 100644
|
| new mode 100755
|
| index e4516a8a6231f0c229de1c0230bd4c3a18ae1576..c3ee31a4eaa4da4b8ac67e1bbc66392b73bb77c3
|
| --- a/devserver.py
|
| +++ b/devserver.py
|
| @@ -1,81 +1,102 @@
|
| +#!/usr/bin/python
|
| +
|
| # Copyright (c) 2009-2010 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.
|
|
|
| +"""A CherryPy-based webserver to host images and build packages."""
|
| +
|
| +import cherrypy
|
| import optparse
|
| import os
|
| import sys
|
| -import web
|
|
|
| import autoupdate
|
| -import buildutil
|
|
|
| # Sets up global to share between classes.
|
| global updater
|
| updater = None
|
|
|
| +def _GetConfig(options):
|
| + """Returns the configuration for the devserver."""
|
| + base_config = { 'global':
|
| + { 'server.log_request_headers': True,
|
| + 'server.protocol_version': 'HTTP/1.1',
|
| + 'server.socket_host': '0.0.0.0',
|
| + 'server.socket_port': int(options.port),
|
| + 'server.socket_timeout': 600,
|
| + 'tools.staticdir.root': os.getcwd(),
|
| + },
|
| + '/update':
|
| + {
|
| + # Gets rid of cherrypy parsing post file for args.
|
| + 'request.process_request_body': False,
|
| + },
|
| + # Sets up the static dir for file hosting.
|
| + '/static':
|
| + { 'tools.staticdir.dir': 'static',
|
| + 'tools.staticdir.on': True,
|
| + },
|
| + }
|
| + return base_config
|
|
|
| -class index:
|
| - def GET(self):
|
| - return render.index(None)
|
|
|
| +def _PrepareToServeUpdatesOnly(image_dir):
|
| + """Sets up symlink to image_dir for serving purposes."""
|
| + assert os.path.exists(image_dir), '%s must exist.' % image_dir
|
| + # If we're serving out of an archived build dir (e.g. a
|
| + # buildbot), prepare this webserver's magic 'static/' dir with a
|
| + # link to the build archive.
|
| + cherrypy.log('Preparing autoupdate for "serve updates only" mode.',
|
| + 'DEVSERVER')
|
| + if os.path.exists('static/archive'):
|
| + if image_dir != os.readlink('static/archive'):
|
| + cherrypy.log('removing stale symlink to %s' % image_dir, 'DEVSERVER')
|
| + os.unlink('static/archive')
|
| + os.symlink(image_dir, 'static/archive')
|
| + else:
|
| + os.symlink(image_dir, 'static/archive')
|
| + cherrypy.log('archive dir: %s ready to be used to serve images.' % image_dir,
|
| + 'DEVSERVER')
|
|
|
| -class update:
|
| - """
|
| - Processes updates from the client machine. If an update is found, the url
|
| - references a static link that can be served automagically from web.py.
|
| - """
|
| - def POST(self, args=None):
|
| - return updater.HandleUpdatePing(web.data(), args)
|
|
|
| +class DevServerRoot:
|
| + """The Root Class for the Dev Server.
|
|
|
| -class build:
|
| - """
|
| - builds the package specified by the pkg parameter and returns the name
|
| - of the output file.
|
| + CherryPy works as follows:
|
| + For each method in this class, cherrpy interprets root/path
|
| + as a call to an instance of DevServerRoot->method_name. For example,
|
| + a call to http://myhost/build will call build. CherryPy automatically
|
| + parses http args and places them as keyword arguments in each method.
|
| + For paths http://myhost/update/dir1/dir2, you can use *args so that
|
| + cherrypy uses the update method and puts the extra paths in args.
|
| """
|
| - def POST(self):
|
| - input = web.input()
|
| - web.debug('emerging %s ' % input.pkg)
|
| - emerge_command = 'emerge-%s %s' % (input.board, input.pkg)
|
| +
|
| + def build(self, board, pkg):
|
| + """Builds the package specified."""
|
| + cherrypy.log('emerging %s' % pkg, 'BUILD')
|
| + emerge_command = 'emerge-%s %s' % (board, pkg)
|
| err = os.system(emerge_command)
|
| if err != 0:
|
| raise Exception('failed to execute %s' % emerge_command)
|
| - eclean_command = 'eclean-%s -d packages' % input.board
|
| + eclean_command = 'eclean-%s -d packages' % board
|
| err = os.system(eclean_command)
|
| if err != 0:
|
| raise Exception('failed to execute %s' % emerge_command)
|
|
|
| + def index(self):
|
| + return 'Welcome to the Dev Server!'
|
|
|
| -def OverrideWSGIServer(server_address, wsgi_app):
|
| - """Creates a CherryPyWSGIServer instance.
|
| + def update(self, *args):
|
| + label = '/'.join(args)
|
| + body_length = int(cherrypy.request.headers['Content-Length'])
|
| + data = cherrypy.request.rfile.read(body_length)
|
| + return updater.HandleUpdatePing(data, label)
|
|
|
| - Overrides web.py's WSGIServer routine (web.httpserver.WSGIServer) to
|
| - increase the accepted connection socket timeout from the default 10
|
| - seconds to 10 minutes. The extra time is necessary to serve delta
|
| - updates as well as update requests from a low priority update_engine
|
| - process running on a heavily loaded Chrome OS device.
|
| - """
|
| - web.debug('using local OverrideWSGIServer routine')
|
| - from web.wsgiserver import CherryPyWSGIServer
|
| - return CherryPyWSGIServer(server_address, wsgi_app, server_name="localhost",
|
| - timeout=600)
|
| -
|
| -def _PrepareToServeUpdatesOnly(image_dir):
|
| - """Sets up symlink to image_dir for serving purposes."""
|
| - assert os.path.exists(image_dir), '%s must exist.' % image_dir
|
| - # If we're serving out of an archived build dir (e.g. a
|
| - # buildbot), prepare this webserver's magic 'static/' dir with a
|
| - # link to the build archive.
|
| - web.debug('Preparing autoupdate for "serve updates only" mode.')
|
| - if os.path.exists('static/archive'):
|
| - if image_dir != os.readlink('static/archive'):
|
| - web.debug('removing stale symlink to %s' % image_dir)
|
| - os.unlink('static/archive')
|
| - os.symlink(image_dir, 'static/archive')
|
| - else:
|
| - os.symlink(image_dir, 'static/archive')
|
| - web.debug('archive dir: %s ready to be used to serve images.' % image_dir)
|
| + # Expose actual methods. Necessary to actually have these callable.
|
| + build.exposed = True
|
| + update.exposed = True
|
| + index.exposed = True
|
|
|
|
|
| if __name__ == '__main__':
|
| @@ -90,6 +111,8 @@ if __name__ == '__main__':
|
| help='Config file for serving images from factory floor.')
|
| parser.add_option('--image', dest='image',
|
| help='Force update using this image.')
|
| + parser.add_option('--port', default=8080,
|
| + help='Port for the dev server to use.')
|
| parser.add_option('-t', action='store_true', dest='test_image')
|
| parser.add_option('-u', '--urlbase', dest='urlbase',
|
| help='base URL, other than devserver, for update images.')
|
| @@ -98,12 +121,11 @@ if __name__ == '__main__':
|
| parser.add_option('--validate_factory_config', action="store_true",
|
| dest='validate_factory_config',
|
| help='Validate factory config file, then exit.')
|
| - # Clean up the args, due to httpserver's hardcoded use of sys.argv.
|
| - options, sys.argv = parser.parse_args(sys.argv)
|
| -
|
| - root_dir = os.path.realpath('%s/../..' %
|
| - os.path.dirname(os.path.abspath(sys.argv[0])))
|
| + parser.set_usage(parser.format_help())
|
| + (options, _) = parser.parse_args()
|
|
|
| + devserver_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
|
| + root_dir = os.path.realpath('%s/../..' % devserver_dir)
|
| serve_only = False
|
|
|
| if options.archive_dir:
|
| @@ -111,12 +133,11 @@ if __name__ == '__main__':
|
| _PrepareToServeUpdatesOnly(static_dir)
|
| serve_only = True
|
| else:
|
| - static_dir = os.path.realpath('%s/static' %
|
| - os.path.dirname(os.path.abspath(sys.argv[0])))
|
| - web.debug('dev root is %s' % root_dir)
|
| + static_dir = os.path.realpath('%s/static' % devserver_dir)
|
| os.system('mkdir -p %s' % static_dir)
|
|
|
| - web.debug('Serving from %s' % static_dir)
|
| + cherrypy.log('Source root is %s' % root_dir, 'DEVSERVER')
|
| + cherrypy.log('Serving from %s' % static_dir, 'DEVSERVER')
|
|
|
| updater = autoupdate.Autoupdate(
|
| root_dir=root_dir,
|
| @@ -127,22 +148,18 @@ if __name__ == '__main__':
|
| factory_config_path=options.factory_config,
|
| client_prefix=options.client_prefix,
|
| forced_image=options.image,
|
| - use_cached=options.use_cached)
|
| + use_cached=options.use_cached,
|
| + port=options.port)
|
| +
|
| + # Sanity-check for use of validate_factory_config.
|
| + if not options.factory_config and options.validate_factory_config:
|
| + parser.error('You need a factory_config to validate.')
|
|
|
| if options.factory_config:
|
| - updater.ImportFactoryConfigFile(options.factory_config,
|
| + updater.ImportFactoryConfigFile(options.factory_config,
|
| options.validate_factory_config)
|
| + # We don't run the dev server with this option.
|
| + if options.validate_factory_config:
|
| + sys.exit(0)
|
|
|
| - if not options.validate_factory_config:
|
| - # We do not need to run the dev server for validating the factory config.
|
| - # TODO(nsanders): Write unit test to validate.
|
| - urls = ('/', 'index',
|
| - '/update', 'update',
|
| - '/update/(.+)', 'update',
|
| - '/build', 'build')
|
| -
|
| - # Overrides the default WSGIServer routine -- see OverrideWSGIServer.
|
| - web.httpserver.WSGIServer = OverrideWSGIServer
|
| - app = web.application(urls, globals(), autoreload=True)
|
| - render = web.template.render('templates/')
|
| - app.run()
|
| + cherrypy.quickstart(DevServerRoot(), config=_GetConfig(options))
|
|
|