Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(300)

Side by Side Diff: devserver.py

Issue 3708004: Move dev server to use cherrypy. (Closed) Base URL: http://git.chromium.org/git/dev-util.git
Patch Set: last Created 10 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « buildutil.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/python
2
1 # Copyright (c) 2009-2010 The Chromium OS Authors. All rights reserved. 3 # Copyright (c) 2009-2010 The Chromium OS Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 4 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 5 # found in the LICENSE file.
4 6
7 """A CherryPy-based webserver to host images and build packages."""
8
9 import cherrypy
5 import optparse 10 import optparse
6 import os 11 import os
7 import sys 12 import sys
8 import web
9 13
10 import autoupdate 14 import autoupdate
11 import buildutil
12 15
13 # Sets up global to share between classes. 16 # Sets up global to share between classes.
14 global updater 17 global updater
15 updater = None 18 updater = None
16 19
20 def _GetConfig(options):
21 """Returns the configuration for the devserver."""
22 base_config = { 'global':
23 { 'server.log_request_headers': True,
24 'server.protocol_version': 'HTTP/1.1',
25 'server.socket_host': '0.0.0.0',
26 'server.socket_port': int(options.port),
27 'server.socket_timeout': 600,
28 'tools.staticdir.root': os.getcwd(),
29 },
30 '/update':
31 {
32 # Gets rid of cherrypy parsing post file for args.
33 'request.process_request_body': False,
34 },
35 # Sets up the static dir for file hosting.
36 '/static':
37 { 'tools.staticdir.dir': 'static',
38 'tools.staticdir.on': True,
39 },
40 }
41 return base_config
17 42
18 class index:
19 def GET(self):
20 return render.index(None)
21
22
23 class update:
24 """
25 Processes updates from the client machine. If an update is found, the url
26 references a static link that can be served automagically from web.py.
27 """
28 def POST(self, args=None):
29 return updater.HandleUpdatePing(web.data(), args)
30
31
32 class build:
33 """
34 builds the package specified by the pkg parameter and returns the name
35 of the output file.
36 """
37 def POST(self):
38 input = web.input()
39 web.debug('emerging %s ' % input.pkg)
40 emerge_command = 'emerge-%s %s' % (input.board, input.pkg)
41 err = os.system(emerge_command)
42 if err != 0:
43 raise Exception('failed to execute %s' % emerge_command)
44 eclean_command = 'eclean-%s -d packages' % input.board
45 err = os.system(eclean_command)
46 if err != 0:
47 raise Exception('failed to execute %s' % emerge_command)
48
49
50 def OverrideWSGIServer(server_address, wsgi_app):
51 """Creates a CherryPyWSGIServer instance.
52
53 Overrides web.py's WSGIServer routine (web.httpserver.WSGIServer) to
54 increase the accepted connection socket timeout from the default 10
55 seconds to 10 minutes. The extra time is necessary to serve delta
56 updates as well as update requests from a low priority update_engine
57 process running on a heavily loaded Chrome OS device.
58 """
59 web.debug('using local OverrideWSGIServer routine')
60 from web.wsgiserver import CherryPyWSGIServer
61 return CherryPyWSGIServer(server_address, wsgi_app, server_name="localhost",
62 timeout=600)
63 43
64 def _PrepareToServeUpdatesOnly(image_dir): 44 def _PrepareToServeUpdatesOnly(image_dir):
65 """Sets up symlink to image_dir for serving purposes.""" 45 """Sets up symlink to image_dir for serving purposes."""
66 assert os.path.exists(image_dir), '%s must exist.' % image_dir 46 assert os.path.exists(image_dir), '%s must exist.' % image_dir
67 # If we're serving out of an archived build dir (e.g. a 47 # If we're serving out of an archived build dir (e.g. a
68 # buildbot), prepare this webserver's magic 'static/' dir with a 48 # buildbot), prepare this webserver's magic 'static/' dir with a
69 # link to the build archive. 49 # link to the build archive.
70 web.debug('Preparing autoupdate for "serve updates only" mode.') 50 cherrypy.log('Preparing autoupdate for "serve updates only" mode.',
51 'DEVSERVER')
71 if os.path.exists('static/archive'): 52 if os.path.exists('static/archive'):
72 if image_dir != os.readlink('static/archive'): 53 if image_dir != os.readlink('static/archive'):
73 web.debug('removing stale symlink to %s' % image_dir) 54 cherrypy.log('removing stale symlink to %s' % image_dir, 'DEVSERVER')
74 os.unlink('static/archive') 55 os.unlink('static/archive')
75 os.symlink(image_dir, 'static/archive') 56 os.symlink(image_dir, 'static/archive')
76 else: 57 else:
77 os.symlink(image_dir, 'static/archive') 58 os.symlink(image_dir, 'static/archive')
78 web.debug('archive dir: %s ready to be used to serve images.' % image_dir) 59 cherrypy.log('archive dir: %s ready to be used to serve images.' % image_dir,
60 'DEVSERVER')
61
62
63 class DevServerRoot:
64 """The Root Class for the Dev Server.
65
66 CherryPy works as follows:
67 For each method in this class, cherrpy interprets root/path
68 as a call to an instance of DevServerRoot->method_name. For example,
69 a call to http://myhost/build will call build. CherryPy automatically
70 parses http args and places them as keyword arguments in each method.
71 For paths http://myhost/update/dir1/dir2, you can use *args so that
72 cherrypy uses the update method and puts the extra paths in args.
73 """
74
75 def build(self, board, pkg):
76 """Builds the package specified."""
77 cherrypy.log('emerging %s' % pkg, 'BUILD')
78 emerge_command = 'emerge-%s %s' % (board, pkg)
79 err = os.system(emerge_command)
80 if err != 0:
81 raise Exception('failed to execute %s' % emerge_command)
82 eclean_command = 'eclean-%s -d packages' % board
83 err = os.system(eclean_command)
84 if err != 0:
85 raise Exception('failed to execute %s' % emerge_command)
86
87 def index(self):
88 return 'Welcome to the Dev Server!'
89
90 def update(self, *args):
91 label = '/'.join(args)
92 body_length = int(cherrypy.request.headers['Content-Length'])
93 data = cherrypy.request.rfile.read(body_length)
94 return updater.HandleUpdatePing(data, label)
95
96 # Expose actual methods. Necessary to actually have these callable.
97 build.exposed = True
98 update.exposed = True
99 index.exposed = True
79 100
80 101
81 if __name__ == '__main__': 102 if __name__ == '__main__':
82 usage = 'usage: %prog [options]' 103 usage = 'usage: %prog [options]'
83 parser = optparse.OptionParser(usage) 104 parser = optparse.OptionParser(usage)
84 parser.add_option('--archive_dir', dest='archive_dir', 105 parser.add_option('--archive_dir', dest='archive_dir',
85 help='serve archived builds only.') 106 help='serve archived builds only.')
86 parser.add_option('--client_prefix', dest='client_prefix', 107 parser.add_option('--client_prefix', dest='client_prefix',
87 help='Required prefix for client software version.', 108 help='Required prefix for client software version.',
88 default='MementoSoftwareUpdate') 109 default='MementoSoftwareUpdate')
89 parser.add_option('--factory_config', dest='factory_config', 110 parser.add_option('--factory_config', dest='factory_config',
90 help='Config file for serving images from factory floor.') 111 help='Config file for serving images from factory floor.')
91 parser.add_option('--image', dest='image', 112 parser.add_option('--image', dest='image',
92 help='Force update using this image.') 113 help='Force update using this image.')
114 parser.add_option('--port', default=8080,
115 help='Port for the dev server to use.')
93 parser.add_option('-t', action='store_true', dest='test_image') 116 parser.add_option('-t', action='store_true', dest='test_image')
94 parser.add_option('-u', '--urlbase', dest='urlbase', 117 parser.add_option('-u', '--urlbase', dest='urlbase',
95 help='base URL, other than devserver, for update images.') 118 help='base URL, other than devserver, for update images.')
96 parser.add_option('--use_cached', action="store_true", default=False, 119 parser.add_option('--use_cached', action="store_true", default=False,
97 help='Prefer cached image regardless of timestamps.') 120 help='Prefer cached image regardless of timestamps.')
98 parser.add_option('--validate_factory_config', action="store_true", 121 parser.add_option('--validate_factory_config', action="store_true",
99 dest='validate_factory_config', 122 dest='validate_factory_config',
100 help='Validate factory config file, then exit.') 123 help='Validate factory config file, then exit.')
101 # Clean up the args, due to httpserver's hardcoded use of sys.argv. 124 parser.set_usage(parser.format_help())
102 options, sys.argv = parser.parse_args(sys.argv) 125 (options, _) = parser.parse_args()
103 126
104 root_dir = os.path.realpath('%s/../..' % 127 devserver_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
105 os.path.dirname(os.path.abspath(sys.argv[0]))) 128 root_dir = os.path.realpath('%s/../..' % devserver_dir)
106
107 serve_only = False 129 serve_only = False
108 130
109 if options.archive_dir: 131 if options.archive_dir:
110 static_dir = os.path.realpath(options.archive_dir) 132 static_dir = os.path.realpath(options.archive_dir)
111 _PrepareToServeUpdatesOnly(static_dir) 133 _PrepareToServeUpdatesOnly(static_dir)
112 serve_only = True 134 serve_only = True
113 else: 135 else:
114 static_dir = os.path.realpath('%s/static' % 136 static_dir = os.path.realpath('%s/static' % devserver_dir)
115 os.path.dirname(os.path.abspath(sys.argv[0])))
116 web.debug('dev root is %s' % root_dir)
117 os.system('mkdir -p %s' % static_dir) 137 os.system('mkdir -p %s' % static_dir)
118 138
119 web.debug('Serving from %s' % static_dir) 139 cherrypy.log('Source root is %s' % root_dir, 'DEVSERVER')
140 cherrypy.log('Serving from %s' % static_dir, 'DEVSERVER')
120 141
121 updater = autoupdate.Autoupdate( 142 updater = autoupdate.Autoupdate(
122 root_dir=root_dir, 143 root_dir=root_dir,
123 static_dir=static_dir, 144 static_dir=static_dir,
124 serve_only=serve_only, 145 serve_only=serve_only,
125 urlbase=options.urlbase, 146 urlbase=options.urlbase,
126 test_image=options.test_image, 147 test_image=options.test_image,
127 factory_config_path=options.factory_config, 148 factory_config_path=options.factory_config,
128 client_prefix=options.client_prefix, 149 client_prefix=options.client_prefix,
129 forced_image=options.image, 150 forced_image=options.image,
130 use_cached=options.use_cached) 151 use_cached=options.use_cached,
152 port=options.port)
153
154 # Sanity-check for use of validate_factory_config.
155 if not options.factory_config and options.validate_factory_config:
156 parser.error('You need a factory_config to validate.')
131 157
132 if options.factory_config: 158 if options.factory_config:
133 updater.ImportFactoryConfigFile(options.factory_config, 159 updater.ImportFactoryConfigFile(options.factory_config,
134 options.validate_factory_config) 160 options.validate_factory_config)
161 # We don't run the dev server with this option.
162 if options.validate_factory_config:
163 sys.exit(0)
135 164
136 if not options.validate_factory_config: 165 cherrypy.quickstart(DevServerRoot(), config=_GetConfig(options))
137 # We do not need to run the dev server for validating the factory config.
138 # TODO(nsanders): Write unit test to validate.
139 urls = ('/', 'index',
140 '/update', 'update',
141 '/update/(.+)', 'update',
142 '/build', 'build')
143
144 # Overrides the default WSGIServer routine -- see OverrideWSGIServer.
145 web.httpserver.WSGIServer = OverrideWSGIServer
146 app = web.application(urls, globals(), autoreload=True)
147 render = web.template.render('templates/')
148 app.run()
OLDNEW
« no previous file with comments | « buildutil.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698