Merge lp:~jaypipes/glance/use-paste-deploy into lp:~glance-coresec/glance/cactus-trunk
- use-paste-deploy
- Merge into cactus-trunk
Status: | Merged |
---|---|
Approved by: | Jay Pipes |
Approved revision: | 79 |
Merged at revision: | 69 |
Proposed branch: | lp:~jaypipes/glance/use-paste-deploy |
Merge into: | lp:~glance-coresec/glance/cactus-trunk |
Prerequisite: | lp:~jaypipes/glance/use-config-parser |
Diff against target: |
1592 lines (+717/-584) 19 files modified
.bzrignore (+5/-0) bin/glance-api (+7/-38) bin/glance-combined (+9/-43) bin/glance-control (+216/-0) bin/glance-registry (+7/-28) doc/source/configuring.rst (+20/-0) doc/source/controllingservers.rst (+165/-0) doc/source/gettingstarted.rst (+3/-45) doc/source/index.rst (+3/-0) doc/source/installing.rst (+100/-0) etc/glance.conf.sample (+32/-8) glance/common/config.py (+103/-115) glance/common/server.py (+0/-129) glance/common/wsgi.py (+27/-51) glance/registry/server.py (+10/-0) glance/server.py (+7/-0) setup.py (+1/-0) tests/unit/test_config.py (+1/-125) tools/pip-requires (+1/-2) |
To merge this branch: | bzr merge lp:~jaypipes/glance/use-paste-deploy |
Related bugs: | |
Related blueprints: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Rick Harris (community) | Approve | ||
Devin Carlen (community) | Approve | ||
Chuck Thier (community) | Approve | ||
Review via email: mp+48489@code.launchpad.net |
Commit message
Removes lockfile and custom python-daemon server initialization
in favour of paste.deploy.
Description of the change
Removes lockfile and custom python-daemon server initialization
in favour of paste.deploy.
We use a solution that is a hybrid of the code in Nova and Swift:
* We continue to use the object-based WSGI Server/Router objects
in glance.common.wsgi.
* We load options from a configuration file like Swift does, merging
in the typed options returned from glance.
* We use a module-level app_factory method instead of @classmethods on
the wsgi.Router class, so this is more like Swift than Nova.
NOTE: Due to http://
removed the CLI option --log-format and use a hard-coded DEFAULT_LOG_FORMAT.
You are still able to adjust the log format using the --log-config-file
option, however, and setting the format string in the log config file.
Chuck Thier (cthier) wrote : | # |
Chuck Thier (cthier) : | # |
Jay Pipes (jaypipes) wrote : | # |
Setting to In Progress while I:
* Remove the daemon options leftover from before paste.deploy
* Write documentation on starting the bin/* servers with paste.script and manually
Rick Harris (rconradharris) wrote : | # |
Just recapping here for posterity:
1. Paste.deploy fails on my machine because it cannot find an app-factory config line in the configuration file even though it's there. Basically:
* We parse config_file ourselves and flatten it into a simple dict
* We pass this options dict to paste.deploy as global_confs
* paste.deploy re-parses the config file (to gather loaders, like "use" and "app-factory" clauses)
* Since we passed in this global_conf that contained some of this info, paste.deploy gets confused, thinks we already defined the app_factory, and won't redefine it
* Since app_factory is not *actually* defined, paste.deploy explodes
Proposed solution:
Not exactly sure why we're passing in global_confs. Since paste.deploy parses the config file itself, it will pull out what it's interested in on it's own.
2. It was mentioned that this patch is using the "swift-style" of passing the config file as the first argument. However, I'm not seeing where the args param is parsed and treated as a config_filename.
Looks like paste.deploy is getting confused by out config options. We store options a flat-dictionary which we pass to paste.deploy as global_confs. paste.deploy attempts to read config-file again and parse out options, however, since global_confs already contains some keys (erroneously since we flattened the section info out), paste.deploy
Jay Pipes (jaypipes) wrote : | # |
> Just recapping here for posterity:
>
> 1. Paste.deploy fails on my machine because it cannot find an app-factory
> config line in the configuration file even though it's there. Basically:
>
> * We parse config_file ourselves and flatten it into a simple dict
> * We pass this options dict to paste.deploy as global_confs
> * paste.deploy re-parses the config file (to gather loaders, like "use" and
> "app-factory" clauses)
> * Since we passed in this global_conf that contained some of this info,
> paste.deploy gets confused, thinks we already defined the app_factory, and
> won't redefine it
> * Since app_factory is not *actually* defined, paste.deploy explodes
>
> Proposed solution:
>
> Not exactly sure why we're passing in global_confs. Since paste.deploy parses
> the config file itself, it will pull out what it's interested in on it's own.
As discussed, pulling the section config processing from common.
> 2. It was mentioned that this patch is using the "swift-style" of passing the
> config file as the first argument. However, I'm not seeing where the args
> param is parsed and treated as a config_filename.
Lines 339-341 in glance.
> Looks like paste.deploy is getting confused by out config options. We store
> options a flat-dictionary which we pass to paste.deploy as global_confs.
> paste.deploy attempts to read config-file again and parse out options,
> however, since global_confs already contains some keys (erroneously since we
> flattened the section info out), paste.deploy
Ya. I'm just going to remove the ability to override config files with CLI options, since ConfigParser/
-jay
Jay Pipes (jaypipes) wrote : | # |
Changes the server daemon programs to be configured only via
paste.deploy configuration files. Removed ability to configure
server options from CLI options when starting the servers with
the exception of --verbose and --debug, which are useful during
debugging.
Updated the documentation controllingserv
Please re-review.
Rick Harris (rconradharris) wrote : | # |
Nice, it's working for me now.
Small tweaks/suggestions:
* setup.py will need glance-control
* Looks like get_config_
* Was mentioned earlier as a femto-nit, do we want to change cnf -> conf to match Nova and Swift?
Jay Pipes (jaypipes) wrote : | # |
Working on fixes now... thx Rick.
Jay Pipes (jaypipes) wrote : | # |
ok, fixes pushed. pls review for third time :) thx.
Rick Harris (rconradharris) wrote : | # |
Great cleanups Jay, thanks!
I'm marking Approved b/c what follows are nits that shouldn't necessarily hold up patch. I'll leave to your discretion whether they should be fixed with this merge:
* options_to_conf is no longer used, so we can strike that as well
* the defaults regex code is no longer used (outside of tests) so we can strike that as well
I think with both of those removed, common.config will be pretty clean.
Preview Diff
1 | === modified file '.bzrignore' |
2 | --- .bzrignore 2010-10-15 19:46:21 +0000 |
3 | +++ .bzrignore 2011-02-10 01:05:12 +0000 |
4 | @@ -2,3 +2,8 @@ |
5 | glance.egg-info |
6 | glance.sqlite |
7 | *.glance-venv |
8 | +dist/ |
9 | +ChangeLog |
10 | +*.pid |
11 | +*.log |
12 | +glance/vcsversion.py |
13 | |
14 | === modified file 'bin/glance-api' |
15 | --- bin/glance-api 2011-02-03 02:17:38 +0000 |
16 | +++ bin/glance-api 2011-02-10 01:05:12 +0000 |
17 | @@ -32,8 +32,7 @@ |
18 | |
19 | from glance import version |
20 | from glance.common import config |
21 | -from glance.common import server |
22 | -import glance.store |
23 | +from glance.common import wsgi |
24 | |
25 | |
26 | def create_options(parser): |
27 | @@ -43,52 +42,22 @@ |
28 | |
29 | :param parser: The option parser |
30 | """ |
31 | - parser.add_option('-H', '--host', |
32 | - dest="host", metavar="ADDRESS", |
33 | - default="0.0.0.0", |
34 | - help="Address of Glance API server. " |
35 | - "Default: %default") |
36 | - parser.add_option('-p', '--port', |
37 | - dest="port", metavar="PORT", type=int, |
38 | - default=9292, |
39 | - help="Port the Glance API server listens on. " |
40 | - "Default: %default") |
41 | - parser.add_option('--registry-host', |
42 | - dest="registry_host", metavar="ADDRESS", |
43 | - default="0.0.0.0", |
44 | - help="Address of a Glance Registry server. " |
45 | - "Default: %default") |
46 | - parser.add_option('--registry-port', |
47 | - dest="registry_port", metavar="PORT", type=int, |
48 | - default=9191, |
49 | - help="Port a Glance Registry server listens on. " |
50 | - "Default: %default") |
51 | - |
52 | - glance.store.add_options(parser) |
53 | config.add_common_options(parser) |
54 | - config.add_daemon_options(parser) |
55 | config.add_log_options('glance-api', parser) |
56 | |
57 | |
58 | -def main(_args): |
59 | - # NOTE(sirp): importing in main so that eventlet is imported AFTER |
60 | - # daemonization. See https://bugs.launchpad.net/bugs/687661 |
61 | - from glance.common import wsgi |
62 | - from glance.server import API |
63 | - server = wsgi.Server() |
64 | - server.start(API(options), options['port'], host=options['host']) |
65 | - server.wait() |
66 | - |
67 | - |
68 | if __name__ == '__main__': |
69 | oparser = optparse.OptionParser(version='%%prog %s' |
70 | % version.version_string()) |
71 | create_options(oparser) |
72 | - conf_options = config.get_config_file_options() |
73 | - (options, args) = config.parse_options(oparser, defaults=conf_options) |
74 | + (options, args) = config.parse_options(oparser) |
75 | |
76 | try: |
77 | config.setup_logging(options) |
78 | - server.serve('glance-api', main, options, args) |
79 | + conf, app = config.load_paste_app('glance-api', options, args) |
80 | + |
81 | + server = wsgi.Server() |
82 | + server.start(app, int(conf['bind_port']), conf['bind_host']) |
83 | + server.wait() |
84 | except RuntimeError, e: |
85 | sys.exit("ERROR: %s" % e) |
86 | |
87 | === modified file 'bin/glance-combined' |
88 | --- bin/glance-combined 2011-02-03 02:17:38 +0000 |
89 | +++ bin/glance-combined 2011-02-10 01:05:12 +0000 |
90 | @@ -34,9 +34,7 @@ |
91 | |
92 | from glance import version |
93 | from glance.common import config |
94 | -from glance.common import server |
95 | -import glance.registry.db |
96 | -import glance.store |
97 | +from glance.common import wsgi |
98 | |
99 | |
100 | def create_options(parser): |
101 | @@ -46,56 +44,24 @@ |
102 | |
103 | :param parser: The option parser |
104 | """ |
105 | - parser.add_option('--api-host', |
106 | - dest="api_host", metavar="ADDRESS", |
107 | - default="0.0.0.0", |
108 | - help="Address of Glance API server. " |
109 | - "Default: %default") |
110 | - parser.add_option('--api-port', |
111 | - dest="api_port", metavar="PORT", type=int, |
112 | - default=9292, |
113 | - help="Port the Glance API server listens on. " |
114 | - "Default: %default") |
115 | - parser.add_option('--registry-host', |
116 | - dest="registry_host", metavar="ADDRESS", |
117 | - default="0.0.0.0", |
118 | - help="Address of a Glance Registry server. " |
119 | - "Default: %default") |
120 | - parser.add_option('--registry-port', |
121 | - dest="registry_port", metavar="PORT", type=int, |
122 | - default=9191, |
123 | - help="Port a Glance Registry server listens on. " |
124 | - "Default: %default") |
125 | - |
126 | - glance.store.add_options(parser) |
127 | - glance.registry.db.add_options(parser) |
128 | config.add_common_options(parser) |
129 | - config.add_daemon_options(parser) |
130 | config.add_log_options('glance-combined', parser) |
131 | |
132 | |
133 | -def main(_args): |
134 | - # NOTE(sirp): importing in main so that eventlet is imported AFTER |
135 | - # daemonization. See https://bugs.launchpad.net/bugs/687661 |
136 | - from glance.common import wsgi |
137 | - from glance.server import API |
138 | - from glance.registry.server import API as rAPI |
139 | - server = wsgi.Server() |
140 | - server.start(API(options), options['api_port'], host=options['api_host']) |
141 | - server.start(rAPI(options), options['registry_port'], |
142 | - host=options['registry_host']) |
143 | - server.wait() |
144 | - |
145 | - |
146 | if __name__ == '__main__': |
147 | oparser = optparse.OptionParser(version='%%prog %s' |
148 | % version.version_string()) |
149 | create_options(oparser) |
150 | - conf_options = config.get_config_file_options() |
151 | - (options, args) = config.parse_options(oparser, defaults=conf_options) |
152 | + (options, args) = config.parse_options(oparser) |
153 | |
154 | try: |
155 | config.setup_logging(options) |
156 | - server.serve('glance-combined', main, options, args) |
157 | + |
158 | + server = wsgi.Server() |
159 | + conf, app = config.load_paste_app('glance-api', options, args) |
160 | + server.start(app, int(conf['bind_port']), conf['bind_host']) |
161 | + conf, app = config.load_paste_app('glance-registry', options, args) |
162 | + server.start(app, int(conf['bind_port']), conf['bind_host']) |
163 | + server.wait() |
164 | except RuntimeError, e: |
165 | sys.exit("ERROR: %s" % e) |
166 | |
167 | === added file 'bin/glance-control' |
168 | --- bin/glance-control 1970-01-01 00:00:00 +0000 |
169 | +++ bin/glance-control 2011-02-10 01:05:12 +0000 |
170 | @@ -0,0 +1,216 @@ |
171 | +#!/usr/bin/python |
172 | +# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
173 | + |
174 | +# Copyright (c) 2011 OpenStack, LLC. |
175 | +# |
176 | +# Licensed under the Apache License, Version 2.0 (the "License"); |
177 | +# you may not use this file except in compliance with the License. |
178 | +# You may obtain a copy of the License at |
179 | +# |
180 | +# http://www.apache.org/licenses/LICENSE-2.0 |
181 | +# |
182 | +# Unless required by applicable law or agreed to in writing, software |
183 | +# distributed under the License is distributed on an "AS IS" BASIS, |
184 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
185 | +# implied. |
186 | +# See the License for the specific language governing permissions and |
187 | +# limitations under the License. |
188 | + |
189 | +""" |
190 | +Helper script for starting/stopping/reloading Glance server programs. |
191 | +Thanks for some of the code, Swifties ;) |
192 | +""" |
193 | + |
194 | +from __future__ import with_statement |
195 | + |
196 | +import errno |
197 | +import glob |
198 | +import os |
199 | +import optparse |
200 | +import resource |
201 | +import signal |
202 | +import sys |
203 | +import time |
204 | + |
205 | +from glance import version |
206 | +from glance.common import config |
207 | + |
208 | +ALL_COMMANDS = ['start', 'stop', 'shutdown', 'restart', |
209 | + 'reload', 'force-reload'] |
210 | +ALL_SERVERS = ['glance-api', 'glance-registry'] |
211 | +GRACEFUL_SHUTDOWN_SERVERS = ['glance-api', 'glance-registry'] |
212 | +MAX_DESCRIPTORS = 32768 |
213 | +MAX_MEMORY = (1024 * 1024 * 1024) * 2 # 2 GB |
214 | +USAGE = """%prog [options] <SERVER> <COMMAND> [CONFPATH] |
215 | + |
216 | +Where <SERVER> is one of: |
217 | + |
218 | + all, api, registry |
219 | + |
220 | +And command is one of: |
221 | + |
222 | + start, stop, shutdown, restart, reload, force-reload |
223 | + |
224 | +And CONFPATH is the optional configuration file to use.""" |
225 | + |
226 | + |
227 | +def pid_files(server): |
228 | + if os.path.exists('/var/run/glance/%s.pid' % server): |
229 | + pid_files = ['/var/run/glance/%s.pid' % server] |
230 | + else: |
231 | + pid_files = glob.glob('/var/run/glance/%s/*.pid' % server) |
232 | + for pid_file in pid_files: |
233 | + pid = int(open(pid_file).read().strip()) |
234 | + yield pid_file, pid |
235 | + |
236 | + |
237 | +def do_start(server, options, args): |
238 | + server_type = '-'.join(server.split('-')[:-1]) |
239 | + |
240 | + for pid_file, pid in pid_files(server): |
241 | + if os.path.exists('/proc/%s' % pid): |
242 | + print "%s appears to already be running: %s" % (server, pid_file) |
243 | + return |
244 | + else: |
245 | + print "Removing stale pid file %s" % pid_file |
246 | + os.unlink(pid_file) |
247 | + |
248 | + try: |
249 | + resource.setrlimit(resource.RLIMIT_NOFILE, |
250 | + (MAX_DESCRIPTORS, MAX_DESCRIPTORS)) |
251 | + resource.setrlimit(resource.RLIMIT_DATA, |
252 | + (MAX_MEMORY, MAX_MEMORY)) |
253 | + except ValueError: |
254 | + print "Unable to increase file descriptor limit. Running as non-root?" |
255 | + os.environ['PYTHON_EGG_CACHE'] = '/tmp' |
256 | + |
257 | + def write_pid_file(pid_file, pid): |
258 | + dir, file = os.path.split(pid_file) |
259 | + if not os.path.exists(dir): |
260 | + try: |
261 | + os.makedirs(dir) |
262 | + except OSError, err: |
263 | + if err.errno == errno.EACCES: |
264 | + sys.exit('Unable to create %s. Running as non-root?' |
265 | + % dir) |
266 | + fp = open(pid_file, 'w') |
267 | + fp.write('%d\n' % pid) |
268 | + fp.close() |
269 | + |
270 | + def launch(ini_file, pid_file): |
271 | + args = [server, ini_file] |
272 | + print 'Starting %s with %s' % (server, ini_file) |
273 | + |
274 | + pid = os.fork() |
275 | + if pid == 0: |
276 | + os.setsid() |
277 | + with open(os.devnull, 'r+b') as nullfile: |
278 | + for desc in (0, 1, 2): # close stdio |
279 | + try: |
280 | + os.dup2(nullfile.fileno(), desc) |
281 | + except OSError: |
282 | + pass |
283 | + try: |
284 | + os.execlp('%s' % server, server, ini_file) |
285 | + except OSError, e: |
286 | + sys.exit('unable to launch %s. Got error: %s' |
287 | + % (server, str(e))) |
288 | + sys.exit(0) |
289 | + else: |
290 | + write_pid_file(pid_file, pid) |
291 | + |
292 | + pid_file = '/var/run/glance/%s.pid' % server |
293 | + conf_file = config.find_config_file(options, args) |
294 | + if not conf_file: |
295 | + sys.exit("Could not find any configuration file to use!") |
296 | + launch_args = [(conf_file, pid_file)] |
297 | + |
298 | + # start all servers |
299 | + for conf_file, pid_file in launch_args: |
300 | + launch(conf_file, pid_file) |
301 | + |
302 | + |
303 | +def do_stop(server, options, args, graceful=False): |
304 | + if graceful and server in GRACEFUL_SHUTDOWN_SERVERS: |
305 | + sig = signal.SIGHUP |
306 | + else: |
307 | + sig = signal.SIGTERM |
308 | + |
309 | + did_anything = False |
310 | + pfiles = pid_files(server) |
311 | + for pid_file, pid in pfiles: |
312 | + did_anything = True |
313 | + try: |
314 | + print 'Stopping %s pid: %s signal: %s' % (server, pid, sig) |
315 | + os.kill(pid, sig) |
316 | + except OSError: |
317 | + print "Process %d not running" % pid |
318 | + try: |
319 | + os.unlink(pid_file) |
320 | + except OSError: |
321 | + pass |
322 | + for pid_file, pid in pfiles: |
323 | + for _junk in xrange(150): # 15 seconds |
324 | + if not os.path.exists('/proc/%s' % pid): |
325 | + break |
326 | + time.sleep(0.1) |
327 | + else: |
328 | + print 'Waited 15 seconds for pid %s (%s) to die; giving up' % \ |
329 | + (pid, pid_file) |
330 | + if not did_anything: |
331 | + print 'No %s running' % server |
332 | + |
333 | + |
334 | +if __name__ == '__main__': |
335 | + oparser = optparse.OptionParser(usage=USAGE, version='%%prog %s' |
336 | + % version.version_string()) |
337 | + config.add_common_options(oparser) |
338 | + (options, args) = config.parse_options(oparser) |
339 | + |
340 | + if len(args) < 2: |
341 | + oparser.print_usage() |
342 | + sys.exit(1) |
343 | + |
344 | + server = args.pop(0).lower() |
345 | + if server == 'all': |
346 | + servers = ALL_SERVERS |
347 | + else: |
348 | + if not server.startswith('glance-'): |
349 | + server = 'glance-%s' % server |
350 | + if server not in ALL_SERVERS: |
351 | + server_list = ", ".join([s.replace('glance-', '') |
352 | + for s in ALL_SERVERS]) |
353 | + msg = ("Unknown server '%(server)s' specified. Please specify " |
354 | + "all, or one of the servers: %(server_list)s" % locals()) |
355 | + sys.exit(msg) |
356 | + servers = [server] |
357 | + |
358 | + command = args.pop(0).lower() |
359 | + if command not in ALL_COMMANDS: |
360 | + command_list = ", ".join(ALL_COMMANDS) |
361 | + msg = ("Unknown command %(command)s specified. Please specify a " |
362 | + "command in this list: %(command_list)s" % locals()) |
363 | + sys.exit(msg) |
364 | + |
365 | + if command == 'start': |
366 | + for server in servers: |
367 | + do_start(server, options, args) |
368 | + |
369 | + if command == 'stop': |
370 | + for server in servers: |
371 | + do_stop(server, options, args) |
372 | + |
373 | + if command == 'shutdown': |
374 | + for server in servers: |
375 | + do_stop(server, options, args, graceful=True) |
376 | + |
377 | + if command == 'restart': |
378 | + for server in servers: |
379 | + do_stop(server, options, args) |
380 | + for server in servers: |
381 | + do_start(server, options, args) |
382 | + |
383 | + if command == 'reload' or command == 'force-reload': |
384 | + for server in servers: |
385 | + do_stop(server, options, args, graceful=True) |
386 | + do_start(server, options, args) |
387 | |
388 | === modified file 'bin/glance-registry' |
389 | --- bin/glance-registry 2011-02-03 02:17:38 +0000 |
390 | +++ bin/glance-registry 2011-02-10 01:05:12 +0000 |
391 | @@ -30,10 +30,9 @@ |
392 | |
393 | sys.path.append(ROOT_DIR) |
394 | |
395 | -import glance.registry.db |
396 | from glance import version |
397 | from glance.common import config |
398 | -from glance.common import server |
399 | +from glance.common import wsgi |
400 | |
401 | |
402 | def create_options(parser): |
403 | @@ -43,42 +42,22 @@ |
404 | |
405 | :param parser: The option parser |
406 | """ |
407 | - parser.add_option('-H', '--host', |
408 | - dest="host", metavar="ADDRESS", |
409 | - default="0.0.0.0", |
410 | - help="Address of Glance API server. " |
411 | - "Default: %default") |
412 | - parser.add_option('-p', '--port', |
413 | - dest="port", metavar="PORT", type=int, |
414 | - default=9191, |
415 | - help="Port the Glance Registry server listens on. " |
416 | - "Default: %default") |
417 | - |
418 | - glance.registry.db.add_options(parser) |
419 | config.add_common_options(parser) |
420 | - config.add_daemon_options(parser) |
421 | config.add_log_options('glance-registry', parser) |
422 | |
423 | |
424 | -def main(_args): |
425 | - # NOTE(sirp): importing in main so that eventlet is imported AFTER |
426 | - # daemonization. See https://bugs.launchpad.net/bugs/687661 |
427 | - from glance.common import wsgi |
428 | - from glance.registry.server import API |
429 | - server = wsgi.Server() |
430 | - server.start(API(options), int(options['port']), host=options['host']) |
431 | - server.wait() |
432 | - |
433 | - |
434 | if __name__ == '__main__': |
435 | oparser = optparse.OptionParser(version='%%prog %s' |
436 | % version.version_string()) |
437 | create_options(oparser) |
438 | - conf_options = config.get_config_file_options() |
439 | - (options, args) = config.parse_options(oparser, defaults=conf_options) |
440 | + (options, args) = config.parse_options(oparser) |
441 | |
442 | try: |
443 | config.setup_logging(options) |
444 | - server.serve('glance-registry', main, options, args) |
445 | + conf, app = config.load_paste_app('glance-registry', options, args) |
446 | + |
447 | + server = wsgi.Server() |
448 | + server.start(app, int(conf['bind_port']), conf['bind_host']) |
449 | + server.wait() |
450 | except RuntimeError, e: |
451 | sys.exit("ERROR: %s" % e) |
452 | |
453 | === added file 'doc/source/configuring.rst' |
454 | --- doc/source/configuring.rst 1970-01-01 00:00:00 +0000 |
455 | +++ doc/source/configuring.rst 2011-02-10 01:05:12 +0000 |
456 | @@ -0,0 +1,20 @@ |
457 | +.. |
458 | + Copyright 2011 OpenStack, LLC |
459 | + All Rights Reserved. |
460 | + |
461 | + Licensed under the Apache License, Version 2.0 (the "License"); you may |
462 | + not use this file except in compliance with the License. You may obtain |
463 | + a copy of the License at |
464 | + |
465 | + http://www.apache.org/licenses/LICENSE-2.0 |
466 | + |
467 | + Unless required by applicable law or agreed to in writing, software |
468 | + distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
469 | + WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
470 | + License for the specific language governing permissions and limitations |
471 | + under the License. |
472 | + |
473 | +Configuring Glance |
474 | +================== |
475 | + |
476 | +.. todo:: Complete details of configuration with paste.deploy config files |
477 | |
478 | === added file 'doc/source/controllingservers.rst' |
479 | --- doc/source/controllingservers.rst 1970-01-01 00:00:00 +0000 |
480 | +++ doc/source/controllingservers.rst 2011-02-10 01:05:12 +0000 |
481 | @@ -0,0 +1,165 @@ |
482 | +.. |
483 | + Copyright 2011 OpenStack, LLC |
484 | + All Rights Reserved. |
485 | + |
486 | + Licensed under the Apache License, Version 2.0 (the "License"); you may |
487 | + not use this file except in compliance with the License. You may obtain |
488 | + a copy of the License at |
489 | + |
490 | + http://www.apache.org/licenses/LICENSE-2.0 |
491 | + |
492 | + Unless required by applicable law or agreed to in writing, software |
493 | + distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
494 | + WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
495 | + License for the specific language governing permissions and limitations |
496 | + under the License. |
497 | + |
498 | +Controlling Glance Servers |
499 | +========================== |
500 | + |
501 | +This section describes the ways to start, stop, and reload Glance's server |
502 | +programs. |
503 | + |
504 | +Starting a server |
505 | +----------------- |
506 | + |
507 | +There are two ways to start a Glance server (either the API server or the |
508 | +reference implementation registry server that ships with Glance): |
509 | + |
510 | +* Manually calling the server program |
511 | + |
512 | +* Using the ``glance-control`` server daemon wrapper program |
513 | + |
514 | +Manually starting the server |
515 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
516 | + |
517 | +The first is by directly calling the server program, passing in command-line |
518 | +options and a single argument for the ``paste.deploy`` configuration file to |
519 | +use when configuring the server application. |
520 | + |
521 | +.. note:: |
522 | + |
523 | + Glance ships with an ``etc/`` directory that contains sample ``paste.deploy`` |
524 | + configuration files that you can copy to a standard configuation directory and |
525 | + adapt for your own uses. |
526 | + |
527 | +If you do `not` specifiy a configuration file on the command line, Glance will |
528 | +do its best to locate a ``glance.conf`` configuration file in one of the |
529 | +following directories, stopping at the first config file it finds: |
530 | + |
531 | +* . |
532 | + |
533 | +* ~/.glance |
534 | + |
535 | +* ~/ |
536 | + |
537 | +* /etc/glance/ |
538 | + |
539 | +* /etc |
540 | + |
541 | +If no configuration file is found, you will see any error, like so:: |
542 | + |
543 | + $> glance-api |
544 | + ERROR: Unable to locate any configuration file. Cannot load application glance-api |
545 | + |
546 | +Here is an example showing how you can manually start the ``glance-api`` server |
547 | +in a shell.:: |
548 | + |
549 | + $> sudo glance-api etc/glance.conf.sample --debug |
550 | + 2011-02-09 14:58:29 DEBUG [glance-api] ******************************************************************************** |
551 | + 2011-02-09 14:58:29 DEBUG [glance-api] Configuration options gathered from config file: |
552 | + 2011-02-09 14:58:29 DEBUG [glance-api] /home/jpipes/repos/glance/trunk/etc/glance.conf.sample |
553 | + 2011-02-09 14:58:29 DEBUG [glance-api] ================================================ |
554 | + 2011-02-09 14:58:29 DEBUG [glance-api] bind_host 0.0.0.0 |
555 | + 2011-02-09 14:58:29 DEBUG [glance-api] bind_port 9292 |
556 | + 2011-02-09 14:58:29 DEBUG [glance-api] debug True |
557 | + 2011-02-09 14:58:29 DEBUG [glance-api] default_store file |
558 | + 2011-02-09 14:58:29 DEBUG [glance-api] filesystem_store_datadir /var/lib/glance/images/ |
559 | + 2011-02-09 14:58:29 DEBUG [glance-api] registry_host 0.0.0.0 |
560 | + 2011-02-09 14:58:29 DEBUG [glance-api] registry_port 9191 |
561 | + 2011-02-09 14:58:29 DEBUG [glance-api] verbose False |
562 | + 2011-02-09 14:58:29 DEBUG [glance-api] ******************************************************************************** |
563 | + 2011-02-09 14:58:29 DEBUG [routes.middleware] Initialized with method overriding = True, and path info altering = True |
564 | + (16333) wsgi starting up on http://0.0.0.0:9292/ |
565 | + |
566 | +Simply supply the configuration file as the first argument |
567 | +(``etc/glance.conf.sample`` in the above example) and then any common options |
568 | +you want to use (``--debug`` was used above to show some of the debugging |
569 | +output that the server shows when starting up. Call the server program |
570 | +with ``--help`` to see all available options you can specify on the |
571 | +command line.) |
572 | + |
573 | +For more information on configuring the server via the ``paste.deploy`` |
574 | +configuration files, see the section entitled |
575 | +:doc:`Configuring Glance servers <configuring>` |
576 | + |
577 | +Note that the server does not `daemonize` itself when run manually |
578 | +from the terminal. You can force the server to daemonize using the standard |
579 | +shell backgrounding indicator, ``&``. However, for most use cases, we recommend |
580 | +using the ``glance-control`` server daemon wrapper for daemonizing. See below |
581 | +for more details on daemonization with ``glance-control``. |
582 | + |
583 | +Using the ``glance-control`` program to start the server |
584 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
585 | + |
586 | +The second way to start up a Glance server is to use the ``glance-control`` |
587 | +program. ``glance-control`` is a wrapper script that allows the user to |
588 | +start, stop, restart, and reload the other Glance server programs in |
589 | +a fashion that is more conducive to automation and scripting. |
590 | + |
591 | +Servers started via the ``glance-control`` program are always `daemonized`, |
592 | +meaning that the server program process runs in the background. |
593 | + |
594 | +To start a Glance server with ``glance-control``, simply call |
595 | +``glance-control`` with a server and the word "start", followed by |
596 | +any command-line options you wish to provide. Start the server with ``glance-control`` |
597 | +in the following way:: |
598 | + |
599 | + $> sudo glance-control <SERVER> start [CONFPATH] |
600 | + |
601 | +.. note:: |
602 | + |
603 | + You must use the ``sudo`` program to run ``glance-control`` currently, as the |
604 | + pid files for the server programs are written to /var/run/glance/ |
605 | + |
606 | +Here is an example that shows how to start the ``glance-registry`` server |
607 | +with the ``glance-control`` wrapper script. :: |
608 | + |
609 | + $> sudo glance-control registry start etc/glance.conf.sample |
610 | + Starting glance-registry with /home/jpipes/repos/glance/trunk/etc/glance.conf.sample |
611 | + |
612 | +The same ``paste.deploy`` configuration files are used by ``glance-control`` |
613 | +to start the Glance server programs, and you can specify (as the example above |
614 | +shows) a configuration file when starting the server. |
615 | + |
616 | +.. note:: |
617 | + |
618 | + To start all the Glance servers (currently the glance-api and glance-registry |
619 | + programs) at once, you can specify "all" for the <SERVER> |
620 | + |
621 | +Stopping a server |
622 | +----------------- |
623 | + |
624 | +If you started a Glance server manually and did not use the ``&`` backgrounding |
625 | +function, simply send a terminate signal to the server process by typing |
626 | +``Ctrl-C`` |
627 | + |
628 | +If you started the Glance server using the ``glance-control`` program, you can |
629 | +use the ``glance-control`` program to stop it. Simply do the following:: |
630 | + |
631 | + $> sudo glance-control <SERVER> stop |
632 | + |
633 | +as this example shows:: |
634 | + |
635 | + $> sudo glance-control registry stop |
636 | + Stopping glance-registry pid: 17602 signal: 15 |
637 | + |
638 | +Restarting a server |
639 | +------------------- |
640 | + |
641 | +You can restart a server with the ``glance-control`` program, as demonstrated |
642 | +here:: |
643 | + |
644 | + $> sudo glance-control registry restart etc/glance.conf.sample |
645 | + Stopping glance-registry pid: 17611 signal: 15 |
646 | + Starting glance-registry with /home/jpipes/repos/glance/trunk/etc/glance.conf.sample |
647 | |
648 | === modified file 'doc/source/gettingstarted.rst' |
649 | --- doc/source/gettingstarted.rst 2011-01-26 17:26:54 +0000 |
650 | +++ doc/source/gettingstarted.rst 2011-02-10 01:05:12 +0000 |
651 | @@ -76,48 +76,6 @@ |
652 | |
653 | Glance registry servers are servers that conform to the Glance Registry API. |
654 | Glance ships with a reference implementation of a registry server that |
655 | -complies with this API (``bin/glance-registry``). |
656 | - |
657 | - |
658 | -Starting Up Glance's Servers |
659 | ----------------------------- |
660 | - |
661 | -To get started using Glance, you must first start the Glance API server. |
662 | -After installing Glance, starting up the Glance API server is easy. Simply |
663 | -start the ``glance-api`` program, like so:: |
664 | - |
665 | - $> glance-api |
666 | - |
667 | -Configuring the Glance API server |
668 | -********************************* |
669 | - |
670 | -There are a few options that can be supplied to the API server when |
671 | -starting it up: |
672 | - |
673 | -* ``verbose`` |
674 | - |
675 | - Show more verbose/debugging output |
676 | - |
677 | -* ``api_host`` |
678 | - |
679 | - Address of the host the registry runs on. Defaults to 0.0.0.0. |
680 | - |
681 | -* ``api_port`` |
682 | - |
683 | - Port the registry server listens on. Defaults to 9292. |
684 | - |
685 | -* ``default_store`` |
686 | - |
687 | - The store that the Glance API server will use by default to store |
688 | - images that are added to it. The default value is `filesystem`, and |
689 | - possible choices are: `filesystem`, `swift`, and `s3`. |
690 | - |
691 | -* ``filesystem_store_datadir`` |
692 | - |
693 | - Directory where the filesystem store can write images to. This directory |
694 | - must be writeable by the user that runs ``glance-api`` |
695 | - |
696 | -.. todo:: |
697 | - |
698 | -Link to docs on the different stores when the documentation on Glance |
699 | -stores is complete. |
700 | +complies with this API (``glance-registry``). |
701 | + |
702 | +For more details on Glance's architecture see :doc:`here <architecture>` |
703 | |
704 | === modified file 'doc/source/index.rst' |
705 | --- doc/source/index.rst 2011-01-26 20:47:01 +0000 |
706 | +++ doc/source/index.rst 2011-02-10 01:05:12 +0000 |
707 | @@ -58,6 +58,9 @@ |
708 | :maxdepth: 1 |
709 | |
710 | gettingstarted |
711 | + installing |
712 | + controllingservers |
713 | + configuring |
714 | glanceapi |
715 | client |
716 | |
717 | |
718 | === added file 'doc/source/installing.rst' |
719 | --- doc/source/installing.rst 1970-01-01 00:00:00 +0000 |
720 | +++ doc/source/installing.rst 2011-02-10 01:05:12 +0000 |
721 | @@ -0,0 +1,100 @@ |
722 | +.. |
723 | + Copyright 2011 OpenStack, LLC |
724 | + All Rights Reserved. |
725 | + |
726 | + Licensed under the Apache License, Version 2.0 (the "License"); you may |
727 | + not use this file except in compliance with the License. You may obtain |
728 | + a copy of the License at |
729 | + |
730 | + http://www.apache.org/licenses/LICENSE-2.0 |
731 | + |
732 | + Unless required by applicable law or agreed to in writing, software |
733 | + distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
734 | + WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
735 | + License for the specific language governing permissions and limitations |
736 | + under the License. |
737 | + |
738 | +Installing Glance |
739 | +================= |
740 | + |
741 | +Installing from packages |
742 | +~~~~~~~~~~~~~~~~~~~~~~~~ |
743 | + |
744 | +To install the latest version of Glance from the Launchpad Bazaar repositories, |
745 | +following the following instructions. |
746 | + |
747 | +Debian/Ubuntu |
748 | +############# |
749 | + |
750 | +1. Add the Glance PPA to your sources.lst:: |
751 | + |
752 | + $> sudo add-apt-repository ppa:glance-core/trunk |
753 | + $> sudo apt-get update |
754 | + |
755 | +2. Install Glance:: |
756 | + |
757 | + $> sudo apt-get install glance |
758 | + |
759 | +RedHat/Fedora |
760 | +############# |
761 | + |
762 | +.. todo:: Need some help on this one... |
763 | + |
764 | +Mac OSX |
765 | +####### |
766 | + |
767 | +.. todo:: No idea how to do install on Mac OSX. Somebody with a Mac should complete this section |
768 | + |
769 | +Installing from source tarballs |
770 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
771 | + |
772 | +To install the latest version of Glance from the Launchpad Bazaar repositories, |
773 | +following the following instructions. |
774 | + |
775 | +1. Grab the source tarball from `Launchpad <http://launchpad.net/glance/+download>`_ |
776 | + |
777 | +2. Untar the source tarball:: |
778 | + |
779 | + $> tar -xzf <FILE> |
780 | + |
781 | +3. Change into the package directory and build/install:: |
782 | + |
783 | + $> cd glance-<RELEASE> |
784 | + $> sudo python setup.py install |
785 | + |
786 | +Installing from a Bazaar Branch |
787 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
788 | + |
789 | +To install the latest version of Glance from the Launchpad Bazaar repositories, |
790 | +following the following instructions. |
791 | + |
792 | +Debian/Ubuntu |
793 | +############# |
794 | + |
795 | +1. Install Bazaar and build dependencies:: |
796 | + |
797 | + $> sudo apt-get install bzr python-eventlet python-routes python-greenlet |
798 | + $> sudo apt-get install python-argparse python-sqlalchemy python-wsgiref python-pastedeploy |
799 | + |
800 | +.. note:: |
801 | + |
802 | + If you want to build the Glance documentation locally, you will also want |
803 | + to install the python-sphinx package |
804 | + |
805 | +1. Branch Glance's trunk branch:: |
806 | + |
807 | + $> bzr branch lp:glance |
808 | + |
809 | +1. Install Glance:: |
810 | + |
811 | + $> sudo python setup.py install |
812 | + |
813 | +RedHat/Fedora |
814 | +############# |
815 | + |
816 | +.. todo:: Need some help on this one... |
817 | + |
818 | +Mac OSX |
819 | +####### |
820 | + |
821 | +.. todo:: No idea how to do install on Mac OSX. Somebody with a Mac should complete this section |
822 | |
823 | === renamed file 'etc/glance.cnf.sample' => 'etc/glance.conf.sample' |
824 | --- etc/glance.cnf.sample 2011-02-03 02:17:38 +0000 |
825 | +++ etc/glance.conf.sample 2011-02-10 01:05:12 +0000 |
826 | @@ -1,20 +1,44 @@ |
827 | [DEFAULT] |
828 | # Show more verbose log output (sets INFO log level output) |
829 | -# verbose = True |
830 | +verbose = True |
831 | |
832 | # Show debugging output in logs (sets DEBUG log level output) |
833 | -# debug = True |
834 | +debug = False |
835 | + |
836 | +[app:glance-api] |
837 | +paste.app_factory = glance.server:app_factory |
838 | + |
839 | +# Directory that the Filesystem backend store |
840 | +# writes image data to |
841 | +filesystem_store_datadir=/var/lib/glance/images/ |
842 | |
843 | # Which backend store should Glance use by default is not specified |
844 | # in a request to add a new image to Glance? Default: 'file' |
845 | # Available choices are 'file', 'swift', and 's3' |
846 | -# default_store = file |
847 | +default_store = file |
848 | + |
849 | +# Address to bind the API server |
850 | +bind_host = 0.0.0.0 |
851 | + |
852 | +# Port the bind the API server to |
853 | +bind_port = 9292 |
854 | + |
855 | +# Address to find the registry server |
856 | +registry_host = 0.0.0.0 |
857 | + |
858 | +# Port the registry server is listening on |
859 | +registry_port = 9191 |
860 | + |
861 | +[app:glance-registry] |
862 | +paste.app_factory = glance.registry.server:app_factory |
863 | + |
864 | +# Address to bind the registry server |
865 | +bind_host = 0.0.0.0 |
866 | + |
867 | +# Port the bind the registry server to |
868 | +bind_port = 9191 |
869 | |
870 | # SQLAlchemy connection string for the reference implementation |
871 | # registry server. Any valid SQLAlchemy connection string is fine. |
872 | # See: http://www.sqlalchemy.org/docs/05/reference/sqlalchemy/connections.html#sqlalchemy.create_engine |
873 | -# sql_connection = sqlite://glance.sqlite |
874 | - |
875 | -# The directory that the Filesystem backend store will write disk |
876 | -# images to. Default: /var/lib/glance/images |
877 | -# filesystem_store_datadir = /var/lib/glance/images |
878 | +sql_connection = sqlite:///glance.sqlite |
879 | |
880 | === modified file 'glance/common/config.py' |
881 | --- glance/common/config.py 2011-02-05 20:26:46 +0000 |
882 | +++ glance/common/config.py 2011-02-10 01:05:12 +0000 |
883 | @@ -29,6 +29,8 @@ |
884 | import re |
885 | import sys |
886 | |
887 | +from paste import deploy |
888 | + |
889 | import glance.common.exception as exception |
890 | |
891 | DEFAULT_LOG_FORMAT = "%(asctime)s %(levelname)8s [%(name)s] %(message)s" |
892 | @@ -37,7 +39,7 @@ |
893 | LOGGING_HANDLER_CHOICES = ['syslog', 'file', 'stream'] |
894 | |
895 | |
896 | -def parse_options(parser, cli_args=None, defaults=None): |
897 | +def parse_options(parser, cli_args=None): |
898 | """ |
899 | Returns the parsed CLI options, command to run and its arguments, merged |
900 | with any same-named options found in a configuration file. |
901 | @@ -52,47 +54,14 @@ |
902 | :param parser: The option parser |
903 | :param cli_args: (Optional) Set of arguments to process. If not present, |
904 | sys.argv[1:] is used. |
905 | - :param defaults: (optional) mapping of default values for options |
906 | :retval tuple of (options, args) |
907 | """ |
908 | |
909 | - if defaults: |
910 | - int_re = re.compile(r'^\d+$') |
911 | - float_re = re.compile(r'^([+-]?(((\d+(\.)?)|(\d*\.\d+))' |
912 | - '([eE][+-]?\d+)?))$') |
913 | - for key, value in defaults.items(): |
914 | - # Do our best to figure out what the actual option |
915 | - # type is underneath... |
916 | - if value.lower() in ('true', 'on'): |
917 | - value = True |
918 | - elif value.lower() in ('false', 'off'): |
919 | - value = False |
920 | - elif int_re.match(value): |
921 | - value = int(value) |
922 | - elif float_re.match(value): |
923 | - value = float(value) |
924 | - defaults[key] = value |
925 | - |
926 | - parser.set_defaults(**defaults) |
927 | (options, args) = parser.parse_args(cli_args) |
928 | |
929 | return (vars(options), args) |
930 | |
931 | |
932 | -def options_to_conf(options): |
933 | - """ |
934 | - Converts a mapping of options having typed values into |
935 | - a mapping of configuration options having only stringified values. |
936 | - |
937 | - This method is used to convert the return of parse_options()[0] |
938 | - into the configuration mapping that is expected by ConfigParser |
939 | - and paste.deploy. |
940 | - |
941 | - :params options: Mapping of typed option key/values |
942 | - """ |
943 | - return dict([(k, str(v)) for k, v in options.items()]) |
944 | - |
945 | - |
946 | def add_common_options(parser): |
947 | """ |
948 | Given a supplied optparse.OptionParser, adds an OptionGroup that |
949 | @@ -124,10 +93,16 @@ |
950 | "the daemonizing of this program." |
951 | |
952 | group = optparse.OptionGroup(parser, "Daemon Options", help_text) |
953 | - group.add_option('--daemonize', default=False, action="store_true", |
954 | - help="Daemonize this process") |
955 | - group.add_option("--pidfile", default=None, |
956 | - help="(Optional) Name of pid file for the server") |
957 | + group.add_option('--config', default=None, |
958 | + help="Configuration file to read when loading " |
959 | + "application. If missing, the first argument is " |
960 | + "used. If no arguments are found, then a set of " |
961 | + "standard directories are searched for a config " |
962 | + "file.") |
963 | + group.add_option("--pid-file", default=None, metavar="PATH", |
964 | + help="(Optional) Name of pid file for the server. " |
965 | + "If not specified, the pid file will be named " |
966 | + "/var/run/glance/<SERVER>.pid.") |
967 | group.add_option("--uid", type=int, default=os.getuid(), |
968 | help="uid under which to run. Default: %default") |
969 | group.add_option("--gid", type=int, default=os.getgid(), |
970 | @@ -160,9 +135,6 @@ |
971 | choices=LOGGING_HANDLER_CHOICES, |
972 | help="What logging handler to use? " |
973 | "Default: %default") |
974 | - group.add_option('--log-format', metavar="FORMAT", |
975 | - default=DEFAULT_LOG_FORMAT, |
976 | - help="Format string for log records. Default: %default") |
977 | group.add_option('--log-date-format', metavar="FORMAT", |
978 | default=DEFAULT_LOG_DATE_FORMAT, |
979 | help="Format string for %(asctime)s in log records. " |
980 | @@ -203,6 +175,9 @@ |
981 | root_logger.setLevel(logging.WARNING) |
982 | |
983 | # Set log configuration from options... |
984 | + # Note that we use a hard-coded log format in the options |
985 | + # because of Paste.Deploy bug #379 |
986 | + # http://trac.pythonpaste.org/pythonpaste/ticket/379 |
987 | log_format = options.get('log_format', DEFAULT_LOG_FORMAT) |
988 | log_date_format = options.get('log_date_format', DEFAULT_LOG_DATE_FORMAT) |
989 | formatter = logging.Formatter(log_format, log_date_format) |
990 | @@ -229,79 +204,92 @@ |
991 | raise exception.BadInputError( |
992 | "unrecognized log handler '%(log_handler)s'" % locals()) |
993 | |
994 | - # Log the options used when starting if we're in debug mode... |
995 | - if debug: |
996 | - root_logger.debug("*" * 80) |
997 | - root_logger.debug("Options:") |
998 | - root_logger.debug("========") |
999 | - for key, value in sorted(options.items()): |
1000 | - root_logger.debug("%(key)-30s %(value)s" % locals()) |
1001 | - root_logger.debug("*" * 80) |
1002 | - |
1003 | - |
1004 | -def get_config_file_options(conf_file=None, conf_dirs=None, app_name=None): |
1005 | - """ |
1006 | - Look for configuration files in a number of standard directories and |
1007 | - return a mapping of options found in the files. |
1008 | - |
1009 | - The files that are searched for are in the following order, with |
1010 | - options found in later files overriding options found in earlier |
1011 | - files:: |
1012 | - |
1013 | - /etc/glance.cnf |
1014 | - /etc/glance/glance.cnf |
1015 | - ~/glance.cnf |
1016 | - ~/.glance/glance.cnf |
1017 | - ./glance.cnf |
1018 | - supplied conf_file param, if any. |
1019 | - |
1020 | - :param conf_file: (optional) config file to read options from. Options |
1021 | - from this config file override all others |
1022 | - :param conf_dirs: (optional) sequence of directory paths to search for |
1023 | - config files. Generally just used in testing |
1024 | - :param app_name: (optional) name of application we're interested in. |
1025 | - Supplying this will ensure that only the [DEFAULT] |
1026 | - section and the [app_name] sections of the config |
1027 | - files will be read. If not supplied (the default), all |
1028 | - sections are read for configuration options. |
1029 | - |
1030 | - :retval Mapping of configuration options read from config files |
1031 | - """ |
1032 | - |
1033 | - # Note that we do this in reverse priority order because |
1034 | - # later configs overwrite the values of previously-read |
1035 | - # configuration options |
1036 | - |
1037 | - fixup_path = lambda p: os.path.abspath(os.path.expanduser(p)) |
1038 | - config_file_dirs = conf_dirs or \ |
1039 | - ['/etc', |
1040 | - '/etc/glance/', |
1041 | - fixup_path('~'), |
1042 | - fixup_path(os.path.join('~', '.glance')), |
1043 | - fixup_path(os.getcwd())] |
1044 | - |
1045 | - config_files = [] |
1046 | - results = {} |
1047 | + |
1048 | +def find_config_file(options, args): |
1049 | + """ |
1050 | + Return the first config file found. |
1051 | + |
1052 | + We search for the paste config file in the following order: |
1053 | + * If --config-file option is used, use that |
1054 | + * If args[0] is a file, use that |
1055 | + * Search for glance.conf in standard directories: |
1056 | + * . |
1057 | + * ~.glance/ |
1058 | + * ~ |
1059 | + * /etc/glance |
1060 | + * /etc |
1061 | + |
1062 | + :retval Full path to config file, or None if no config file found |
1063 | + """ |
1064 | + |
1065 | + fix_path = lambda p: os.path.abspath(os.path.expanduser(p)) |
1066 | + if getattr(options, 'config', None): |
1067 | + if os.path.exists(options.config_file): |
1068 | + return fix_path(getattr(options, 'config')) |
1069 | + elif args: |
1070 | + if os.path.exists(args[0]): |
1071 | + return fix_path(args[0]) |
1072 | + |
1073 | + # Handle standard directory search for glance.conf |
1074 | + config_file_dirs = [fix_path(os.getcwd()), |
1075 | + fix_path(os.path.join('~', '.glance')), |
1076 | + fix_path('~'), |
1077 | + '/etc/glance/', |
1078 | + '/etc'] |
1079 | + |
1080 | for cfg_dir in config_file_dirs: |
1081 | - cfg_file = os.path.join(cfg_dir, 'glance.cnf') |
1082 | + cfg_file = os.path.join(cfg_dir, 'glance.conf') |
1083 | if os.path.exists(cfg_file): |
1084 | - config_files.append(cfg_file) |
1085 | - |
1086 | - if conf_file: |
1087 | - config_files.append(fixup_path(conf_file)) |
1088 | - |
1089 | - cp = ConfigParser.ConfigParser() |
1090 | - for config_file in config_files: |
1091 | - if not cp.read(config_file): |
1092 | - msg = 'Unable to read config file: %s' % config_file |
1093 | - raise RuntimeError(msg) |
1094 | - |
1095 | - results.update(cp.defaults()) |
1096 | - # Add any sections we have in the configuration file, too... |
1097 | - for section in cp.sections(): |
1098 | - section_option_keys = cp.options(section) |
1099 | - if not app_name or (app_name == section): |
1100 | - for k in section_option_keys: |
1101 | - results[k] = cp.get(section, k) |
1102 | - |
1103 | - return results |
1104 | + return cfg_file |
1105 | + |
1106 | + |
1107 | +def load_paste_app(app_name, options, args): |
1108 | + """ |
1109 | + Builds and returns a WSGI app from a paste config file. |
1110 | + |
1111 | + We search for the paste config file in the following order: |
1112 | + * If --config-file option is used, use that |
1113 | + * If args[0] is a file, use that |
1114 | + * Search for glance.conf in standard directories: |
1115 | + * . |
1116 | + * ~.glance/ |
1117 | + * ~ |
1118 | + * /etc/glance |
1119 | + * /etc |
1120 | + |
1121 | + :param app_name: Name of the application to load |
1122 | + :param options: Set of typed options returned from parse_options() |
1123 | + :param args: Command line arguments from argv[1:] |
1124 | + |
1125 | + :raises RuntimeError when config file cannot be located or application |
1126 | + cannot be loaded from config file |
1127 | + """ |
1128 | + conf_file = find_config_file(options, args) |
1129 | + if not conf_file: |
1130 | + raise RuntimeError("Unable to locate any configuration file. " |
1131 | + "Cannot load application %s" % app_name) |
1132 | + try: |
1133 | + conf = deploy.appconfig("config:%s" % conf_file, name=app_name) |
1134 | + # We only update the conf dict for the verbose and debug |
1135 | + # flags. Everything else must be set up in the conf file... |
1136 | + conf['verbose'] = options['verbose'] |
1137 | + conf['debug'] = options['debug'] |
1138 | + |
1139 | + # Log the options used when starting if we're in debug mode... |
1140 | + if conf['debug']: |
1141 | + logger = logging.getLogger(app_name) |
1142 | + logger.debug("*" * 80) |
1143 | + logger.debug("Configuration options gathered from config file:") |
1144 | + logger.debug(conf_file) |
1145 | + logger.debug("================================================") |
1146 | + items = dict([(k, v) for k, v in conf.items() |
1147 | + if k not in ('__file__', 'here')]) |
1148 | + for key, value in sorted(items.items()): |
1149 | + logger.debug("%(key)-30s %(value)s" % locals()) |
1150 | + logger.debug("*" * 80) |
1151 | + app = deploy.loadapp("config:%s" % conf_file, name=app_name) |
1152 | + except (LookupError, ImportError), e: |
1153 | + raise RuntimeError("Unable to load %(app_name)s from " |
1154 | + "configuration file %(conf_file)s." |
1155 | + "\nGot: %(e)r" % locals()) |
1156 | + return conf, app |
1157 | |
1158 | === removed file 'glance/common/server.py' |
1159 | --- glance/common/server.py 2011-02-01 21:22:49 +0000 |
1160 | +++ glance/common/server.py 1970-01-01 00:00:00 +0000 |
1161 | @@ -1,129 +0,0 @@ |
1162 | -# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
1163 | - |
1164 | -# Copyright 2010 United States Government as represented by the |
1165 | -# Administrator of the National Aeronautics and Space Administration. |
1166 | -# All Rights Reserved. |
1167 | -# |
1168 | -# Licensed under the Apache License, Version 2.0 (the "License"); you may |
1169 | -# not use this file except in compliance with the License. You may obtain |
1170 | -# a copy of the License at |
1171 | -# |
1172 | -# http://www.apache.org/licenses/LICENSE-2.0 |
1173 | -# |
1174 | -# Unless required by applicable law or agreed to in writing, software |
1175 | -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
1176 | -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
1177 | -# License for the specific language governing permissions and limitations |
1178 | -# under the License. |
1179 | - |
1180 | -""" |
1181 | -Base functionality for nova daemons - gradually being replaced with twistd.py. |
1182 | -""" |
1183 | - |
1184 | -import daemon |
1185 | -from daemon import pidlockfile |
1186 | -import logging |
1187 | -import logging.handlers |
1188 | -import os |
1189 | -import pprint |
1190 | -import signal |
1191 | -import sys |
1192 | -import time |
1193 | - |
1194 | - |
1195 | -def stop(pidfile): |
1196 | - """ |
1197 | - Stop the daemon |
1198 | - """ |
1199 | - # Get the pid from the pidfile |
1200 | - try: |
1201 | - pid = int(open(pidfile, 'r').read().strip()) |
1202 | - except IOError: |
1203 | - message = "pidfile %s does not exist. Daemon not running?\n" |
1204 | - sys.stderr.write(message % pidfile) |
1205 | - return |
1206 | - |
1207 | - # Try killing the daemon process |
1208 | - try: |
1209 | - print "Killing process from pidfile %s" % pidfile |
1210 | - while 1: |
1211 | - os.kill(pid, signal.SIGTERM) |
1212 | - time.sleep(0.1) |
1213 | - except OSError, err: |
1214 | - err = str(err) |
1215 | - if err.find("No such process") > 0: |
1216 | - if os.path.exists(pidfile): |
1217 | - os.remove(pidfile) |
1218 | - else: |
1219 | - print str(err) |
1220 | - sys.exit(1) |
1221 | - |
1222 | - |
1223 | -def serve(name, main, options, args): |
1224 | - """Controller for server""" |
1225 | - |
1226 | - pidfile = options['pidfile'] |
1227 | - if not pidfile: |
1228 | - options['pidfile'] = '%s.pid' % name |
1229 | - |
1230 | - action = 'start' |
1231 | - if len(args): |
1232 | - action = args.pop() |
1233 | - |
1234 | - if action == 'stop': |
1235 | - stop(options['pidfile']) |
1236 | - sys.exit() |
1237 | - elif action == 'restart': |
1238 | - stop(options['pidfile']) |
1239 | - elif action == 'start': |
1240 | - pass |
1241 | - else: |
1242 | - print 'usage: %s [options] [start|stop|restart]' % name |
1243 | - sys.exit(1) |
1244 | - daemonize(args, name, main, options) |
1245 | - |
1246 | - |
1247 | -def daemonize(args, name, main, options): |
1248 | - """Does the work of daemonizing the process""" |
1249 | - logging.getLogger('amqplib').setLevel(logging.WARN) |
1250 | - pidfile = options['pidfile'] |
1251 | - logfile = options['log_file'] |
1252 | - logdir = options['log_dir'] |
1253 | - daemonize = options['daemonize'] |
1254 | - use_syslog = options['log_handler'] == 'syslog' |
1255 | - files_to_keep = [] |
1256 | - if daemonize: |
1257 | - logger = logging.getLogger(name) |
1258 | - formatter = logging.Formatter( |
1259 | - name + '(%(name)s): %(levelname)s %(message)s') |
1260 | - if use_syslog and not logfile: |
1261 | - syslog = logging.handlers.SysLogHandler(address='/dev/log') |
1262 | - syslog.setFormatter(formatter) |
1263 | - logger.addHandler(syslog) |
1264 | - files_to_keep.append(syslog.socket) |
1265 | - elif options['log_handler'] == 'file': |
1266 | - if not logfile: |
1267 | - logfile = '%s.log' % name |
1268 | - if logdir: |
1269 | - logfile = os.path.join(logdir, logfile) |
1270 | - logfile = logging.FileHandler(logfile) |
1271 | - logfile.setFormatter(formatter) |
1272 | - logger.addHandler(logfile) |
1273 | - files_to_keep.append(logfile.stream) |
1274 | - stdin, stdout, stderr = None, None, None |
1275 | - else: |
1276 | - stdin, stdout, stderr = sys.stdin, sys.stdout, sys.stderr |
1277 | - |
1278 | - with daemon.DaemonContext( |
1279 | - detach_process=daemonize, |
1280 | - working_directory=options['working_directory'], |
1281 | - pidfile=pidlockfile.TimeoutPIDLockFile(pidfile, |
1282 | - acquire_timeout=1, |
1283 | - threaded=False), |
1284 | - stdin=stdin, |
1285 | - stdout=stdout, |
1286 | - stderr=stderr, |
1287 | - uid=options['uid'], |
1288 | - gid=options['gid'], |
1289 | - files_preserve=files_to_keep): |
1290 | - main(args) |
1291 | |
1292 | === modified file 'glance/common/wsgi.py' |
1293 | --- glance/common/wsgi.py 2011-01-27 04:19:13 +0000 |
1294 | +++ glance/common/wsgi.py 2011-02-10 01:05:12 +0000 |
1295 | @@ -35,9 +35,6 @@ |
1296 | import webob.exc |
1297 | |
1298 | |
1299 | -logging.getLogger("routes.middleware").addHandler(logging.StreamHandler()) |
1300 | - |
1301 | - |
1302 | def run_server(application, port): |
1303 | """Run a WSGI server with the given application.""" |
1304 | sock = eventlet.listen(('0.0.0.0', port)) |
1305 | @@ -67,48 +64,7 @@ |
1306 | eventlet.wsgi.server(socket, application, custom_pool=self.pool) |
1307 | |
1308 | |
1309 | -class Application(object): |
1310 | -# TODO(gundlach): I think we should toss this class, now that it has no |
1311 | -# purpose. |
1312 | - """Base WSGI application wrapper. Subclasses need to implement __call__.""" |
1313 | - |
1314 | - def __call__(self, environ, start_response): |
1315 | - r"""Subclasses will probably want to implement __call__ like this: |
1316 | - |
1317 | - @webob.dec.wsgify |
1318 | - def __call__(self, req): |
1319 | - # Any of the following objects work as responses: |
1320 | - |
1321 | - # Option 1: simple string |
1322 | - res = 'message\n' |
1323 | - |
1324 | - # Option 2: a nicely formatted HTTP exception page |
1325 | - res = exc.HTTPForbidden(detail='Nice try') |
1326 | - |
1327 | - # Option 3: a webob Response object (in case you need to play with |
1328 | - # headers, or you want to be treated like an iterable, or or or) |
1329 | - res = Response(); |
1330 | - res.app_iter = open('somefile') |
1331 | - |
1332 | - # Option 4: any wsgi app to be run next |
1333 | - res = self.application |
1334 | - |
1335 | - # Option 5: you can get a Response object for a wsgi app, too, to |
1336 | - # play with headers etc |
1337 | - res = req.get_response(self.application) |
1338 | - |
1339 | - # You can then just return your response... |
1340 | - return res |
1341 | - # ... or set req.response and return None. |
1342 | - req.response = res |
1343 | - |
1344 | - See the end of http://pythonpaste.org/webob/modules/dec.html |
1345 | - for more info. |
1346 | - """ |
1347 | - raise NotImplementedError("You must implement __call__") |
1348 | - |
1349 | - |
1350 | -class Middleware(Application): |
1351 | +class Middleware(object): |
1352 | """ |
1353 | Base WSGI middleware wrapper. These classes require an application to be |
1354 | initialized that will be called next. By default the middleware will |
1355 | @@ -116,18 +72,38 @@ |
1356 | behavior. |
1357 | """ |
1358 | |
1359 | - def __init__(self, application): # pylint: disable-msg=W0231 |
1360 | + def __init__(self, application): |
1361 | self.application = application |
1362 | |
1363 | + def process_request(self, req): |
1364 | + """ |
1365 | + Called on each request. |
1366 | + |
1367 | + If this returns None, the next application down the stack will be |
1368 | + executed. If it returns a response then that response will be returned |
1369 | + and execution will stop here. |
1370 | + |
1371 | + """ |
1372 | + return None |
1373 | + |
1374 | + def process_response(self, response): |
1375 | + """Do whatever you'd like to the response.""" |
1376 | + return response |
1377 | + |
1378 | @webob.dec.wsgify |
1379 | - def __call__(self, req): # pylint: disable-msg=W0221 |
1380 | - """Override to implement middleware behavior.""" |
1381 | - return self.application |
1382 | + def __call__(self, req): |
1383 | + response = self.process_request(req) |
1384 | + if response: |
1385 | + return response |
1386 | + response = req.get_response(self.application) |
1387 | + return self.process_response(response) |
1388 | |
1389 | |
1390 | class Debug(Middleware): |
1391 | - """Helper class that can be inserted into any WSGI application chain |
1392 | - to get information about the request and response.""" |
1393 | + """ |
1394 | + Helper class that can be inserted into any WSGI application chain |
1395 | + to get information about the request and response. |
1396 | + """ |
1397 | |
1398 | @webob.dec.wsgify |
1399 | def __call__(self, req): |
1400 | |
1401 | === modified file 'glance/registry/server.py' |
1402 | --- glance/registry/server.py 2011-02-01 21:38:31 +0000 |
1403 | +++ glance/registry/server.py 2011-02-10 01:05:12 +0000 |
1404 | @@ -175,3 +175,13 @@ |
1405 | |
1406 | image_dict['properties'] = properties |
1407 | return image_dict |
1408 | + |
1409 | + |
1410 | +def app_factory(global_conf, **local_conf): |
1411 | + """ |
1412 | + paste.deploy app factory for creating Glance reference implementation |
1413 | + registry server apps |
1414 | + """ |
1415 | + conf = global_conf.copy() |
1416 | + conf.update(local_conf) |
1417 | + return API(conf) |
1418 | |
1419 | === modified file 'glance/server.py' |
1420 | --- glance/server.py 2011-02-05 02:32:31 +0000 |
1421 | +++ glance/server.py 2011-02-10 01:05:12 +0000 |
1422 | @@ -439,3 +439,10 @@ |
1423 | mapper.connect("/images/{id}", controller=controller, action="meta", |
1424 | conditions=dict(method=["HEAD"])) |
1425 | super(API, self).__init__(mapper) |
1426 | + |
1427 | + |
1428 | +def app_factory(global_conf, **local_conf): |
1429 | + """paste.deploy app factory for creating Glance API server apps""" |
1430 | + conf = global_conf.copy() |
1431 | + conf.update(local_conf) |
1432 | + return API(conf) |
1433 | |
1434 | === modified file 'setup.py' |
1435 | --- setup.py 2011-02-08 18:07:42 +0000 |
1436 | +++ setup.py 2011-02-10 01:05:12 +0000 |
1437 | @@ -87,6 +87,7 @@ |
1438 | ], |
1439 | scripts=['bin/glance-api', |
1440 | 'bin/glance-combined', |
1441 | + 'bin/glance-control', |
1442 | 'bin/glance-manage', |
1443 | 'bin/glance-registry', |
1444 | 'bin/glance-upload']) |
1445 | |
1446 | === modified file 'tests/unit/test_config.py' |
1447 | --- tests/unit/test_config.py 2011-02-03 02:17:38 +0000 |
1448 | +++ tests/unit/test_config.py 2011-02-10 01:05:12 +0000 |
1449 | @@ -62,128 +62,4 @@ |
1450 | parser = optparse.OptionParser() |
1451 | config.add_common_options(parser) |
1452 | self.assertRaises(SystemExit, config.parse_options, |
1453 | - parser,['--unknown']) |
1454 | - |
1455 | - def test_options_to_conf(self): |
1456 | - parser = optparse.OptionParser() |
1457 | - config.add_common_options(parser) |
1458 | - parsed_options, args = config.parse_options(parser) |
1459 | - conf_options = config.options_to_conf(parsed_options) |
1460 | - |
1461 | - expected_options = {'verbose': 'False', 'debug': 'False'} |
1462 | - self.assertEquals(expected_options, conf_options) |
1463 | - |
1464 | - def test_get_config_file_options(self): |
1465 | - |
1466 | - # Test when no conf files are found... |
1467 | - expected_options = {} |
1468 | - conf_options = config.get_config_file_options(conf_dirs=['tests']) |
1469 | - self.assertEquals(expected_options, conf_options) |
1470 | - |
1471 | - # Test when a conf file is supplied and only DEFAULT |
1472 | - # section is present |
1473 | - with tempfile.NamedTemporaryFile() as f: |
1474 | - contents = """[DEFAULT] |
1475 | -verbose = True |
1476 | -""" |
1477 | - f.write(contents) |
1478 | - f.flush() |
1479 | - conf_file = f.name |
1480 | - |
1481 | - expected_options = {'verbose': 'True'} |
1482 | - conf_options = config.get_config_file_options(conf_file) |
1483 | - self.assertEquals(expected_options, conf_options) |
1484 | - |
1485 | - # Test when a conf file is supplied and it has a DEFAULT |
1486 | - # section and another section called glance-api, with |
1487 | - # no specified app_name when calling get_config_file_options() |
1488 | - with tempfile.NamedTemporaryFile() as f: |
1489 | - contents = """[DEFAULT] |
1490 | -verbose = True |
1491 | - |
1492 | -[glance-api] |
1493 | -default_store = swift |
1494 | -""" |
1495 | - f.write(contents) |
1496 | - f.flush() |
1497 | - conf_file = f.name |
1498 | - |
1499 | - expected_options = {'verbose': 'True', |
1500 | - 'default_store': 'swift'} |
1501 | - conf_options = config.get_config_file_options(conf_file) |
1502 | - self.assertEquals(expected_options, conf_options) |
1503 | - |
1504 | - # Test when a conf file is supplied and it has a DEFAULT |
1505 | - # section and another section called glance-api, with |
1506 | - # specified app_name is NOT glance-api |
1507 | - with tempfile.NamedTemporaryFile() as f: |
1508 | - contents = """[DEFAULT] |
1509 | -verbose = True |
1510 | - |
1511 | -[glance-api] |
1512 | -default_store = swift |
1513 | -""" |
1514 | - f.write(contents) |
1515 | - f.flush() |
1516 | - conf_file = f.name |
1517 | - |
1518 | - expected_options = {'verbose': 'True'} |
1519 | - app_name = 'glance-registry' |
1520 | - conf_options = config.get_config_file_options(conf_file, |
1521 | - app_name=app_name) |
1522 | - self.assertEquals(expected_options, conf_options) |
1523 | - |
1524 | - # Test when a conf file is supplied and it has a DEFAULT |
1525 | - # section and two other sections. Check that the later section |
1526 | - # overrides the value of the former section... |
1527 | - with tempfile.NamedTemporaryFile() as f: |
1528 | - contents = """[DEFAULT] |
1529 | -verbose = True |
1530 | - |
1531 | -[glance-api] |
1532 | -default_store = swift |
1533 | - |
1534 | -[glance-combined] |
1535 | -default_store = s3 |
1536 | -""" |
1537 | - f.write(contents) |
1538 | - f.flush() |
1539 | - conf_file = f.name |
1540 | - |
1541 | - expected_options = {'verbose': 'True', |
1542 | - 'default_store': 's3'} |
1543 | - conf_options = config.get_config_file_options(conf_file) |
1544 | - self.assertEquals(expected_options, conf_options) |
1545 | - |
1546 | - def test_parse_options_with_defaults(self): |
1547 | - # Test the integration of parse_options() with a set |
1548 | - # of defaults. These defaults generally come from a |
1549 | - # configuration file |
1550 | - defaults = {'verbose': 'on'} |
1551 | - parser = optparse.OptionParser() |
1552 | - config.add_common_options(parser) |
1553 | - parsed_options, args = config.parse_options(parser, defaults=defaults) |
1554 | - |
1555 | - expected_options = {'verbose': True, 'debug': False} |
1556 | - self.assertEquals(expected_options, parsed_options) |
1557 | - |
1558 | - # Write a sample conf file and merge the conf file defaults |
1559 | - # with the parsed options. |
1560 | - with tempfile.NamedTemporaryFile() as f: |
1561 | - contents = """[DEFAULT] |
1562 | -verbose = True |
1563 | -debug = off |
1564 | -""" |
1565 | - f.write(contents) |
1566 | - f.flush() |
1567 | - conf_file = f.name |
1568 | - |
1569 | - expected_options = {'verbose': True, |
1570 | - 'debug': False} |
1571 | - conf_options = config.get_config_file_options(conf_file) |
1572 | - parser = optparse.OptionParser() |
1573 | - config.add_common_options(parser) |
1574 | - parsed_options, args = config.parse_options(parser, |
1575 | - defaults=conf_options) |
1576 | - |
1577 | - self.assertEquals(expected_options, parsed_options) |
1578 | + parser, ['--unknown']) |
1579 | |
1580 | === modified file 'tools/pip-requires' |
1581 | --- tools/pip-requires 2011-02-02 01:43:16 +0000 |
1582 | +++ tools/pip-requires 2011-02-10 01:05:12 +0000 |
1583 | @@ -4,8 +4,7 @@ |
1584 | pylint==0.19 |
1585 | anyjson |
1586 | eventlet>=0.9.12 |
1587 | -lockfile==0.8 |
1588 | -python-daemon==1.5.5 |
1589 | +PasteDeploy |
1590 | routes |
1591 | webob |
1592 | wsgiref |
As far as I can tell, the paste.deploy stuff looks fine to me.