Merge lp:~jaypipes/glance/use-optparse into lp:~glance-coresec/glance/cactus-trunk
- use-optparse
- Merge into cactus-trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Devin Carlen | ||||
Approved revision: | 62 | ||||
Merged at revision: | 58 | ||||
Proposed branch: | lp:~jaypipes/glance/use-optparse | ||||
Merge into: | lp:~glance-coresec/glance/cactus-trunk | ||||
Prerequisite: | lp:~jaypipes/glance/versioning | ||||
Diff against target: |
1727 lines (+402/-683) 21 files modified
bin/glance-api (+73/-10) bin/glance-registry (+53/-6) glance/common/config.py (+54/-0) glance/common/db/sqlalchemy/__init__.py (+0/-16) glance/common/db/sqlalchemy/session.py (+0/-49) glance/common/flags.py (+0/-177) glance/common/server.py (+38/-51) glance/common/utils.py (+0/-17) glance/registry/__init__.py (+18/-24) glance/registry/db/__init__.py (+0/-9) glance/registry/db/api.py (+45/-2) glance/registry/db/models.py (+2/-15) glance/registry/server.py (+10/-5) glance/server.py (+34/-20) glance/store/__init__.py (+23/-0) glance/store/filesystem.py (+19/-11) tests/stubs.py (+7/-2) tests/unit/test_api.py (+26/-35) tests/unit/test_clients.py (+0/-6) tests/unit/test_registry_api.py (+0/-227) tools/pip-requires (+0/-1) |
||||
To merge this branch: | bzr merge lp:~jaypipes/glance/use-optparse | ||||
Related bugs: |
|
||||
Related blueprints: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Rick Harris (community) | Approve | ||
Devin Carlen (community) | Approve | ||
Review via email: mp+47918@code.launchpad.net |
Commit message
Replaces gflags with optparse
Description of the change
Replaces gflags with optparse
Jay Pipes (jaypipes) wrote : | # |
And how does that go towards our goal of supporting Python 2.6? :)
Devin Carlen (devcamcar) wrote : | # |
It doesn't, of course. :)
It's just one of those things - if it is easy to use argparse over optparse, then we get some future proofing which is always good.
http://
It looks like it would be pretty trivial to make the change, but I'll leave it up to you at the end of the day.
Either way I'll approve this for now, if you decide to go with argparse it can be a separate merge.
Rick Harris (rconradharris) wrote : | # |
I'll throw in my 2c here.
I personally agree with Devin, argparse is a great library, does some things that optparse can't, and is easy enough to include as a dependency. So I'd be a +1 on moving to argparse.
That said, I'm totally cool with going with optparse for now and then moving to argparse when 2.7 becomes more standard.
No biggie.
Jay Pipes (jaypipes) wrote : | # |
I like argparse too :)
We'll move to argparse fully eventually, prolly sooner than later. I'll bring it up at the next design summit and let's see if we can get consensus on the ML and at the summit on it.
Jay Pipes (jaypipes) wrote : | # |
Discovered a bug this morning while working on a separate issue but having branched from my local use-optparse tree:
(.glance-
(.glance-
Traceback (most recent call last):
File "./bin/glance-api", line 116, in <module>
server.
File "/home/
daemonize(args, name, main, options)
File "/home/
files_
File "/home/
self.open()
File "/home/
self.
File "/home/
self.acquire()
File "/home/
super(
File "/home/
super(
File "/home/
raise LockTimeout
lockfile.
This doesn't happen on Bexar's trunk, so it's a bug introduced by this patch...
Rick Harris (rconradharris) wrote : | # |
lgtm
yotta-nit:
70 if len(args):
Could just be `if args`, no need to compute actual number of arguments since it's truthiness suffices.
Jay Pipes (jaypipes) wrote : | # |
Just noticed this in /glance/
283 - _ENGINE = create_
284 + _ENGINE = create_
Oops.
Devin Carlen (devcamcar) wrote : | # |
Oops, I totally missed that too :)
Jay Pipes (jaypipes) wrote : | # |
Alrighty, fixed the issue with a hard-coded sqlite:// sql_connection setting...
Also removed the glance.
Devin Carlen (devcamcar) wrote : | # |
ok, -really- approving this time ;)
Preview Diff
1 | === modified file 'bin/glance-api' |
2 | --- bin/glance-api 2011-01-27 04:04:29 +0000 |
3 | +++ bin/glance-api 2011-01-31 19:42:44 +0000 |
4 | @@ -3,6 +3,7 @@ |
5 | |
6 | # Copyright 2010 United States Government as represented by the |
7 | # Administrator of the National Aeronautics and Space Administration. |
8 | +# Copyright 2011 OpenStack LLC. |
9 | # All Rights Reserved. |
10 | # |
11 | # Licensed under the Apache License, Version 2.0 (the "License"); you may |
12 | @@ -21,6 +22,7 @@ |
13 | Glance API Server |
14 | """ |
15 | |
16 | +import optparse |
17 | import os |
18 | import sys |
19 | |
20 | @@ -28,14 +30,72 @@ |
21 | |
22 | sys.path.append(ROOT_DIR) |
23 | |
24 | -from glance.common import flags |
25 | -from glance.common import utils |
26 | +from glance import version |
27 | +from glance.common import config |
28 | from glance.common import server |
29 | - |
30 | - |
31 | -FLAGS = flags.FLAGS |
32 | -flags.DEFINE_string('api_host', '0.0.0.0', 'API server lives at this address') |
33 | -flags.DEFINE_integer('api_port', 9292, 'API server listens on this port') |
34 | +import glance.store |
35 | + |
36 | + |
37 | +DEFAULT_STORE_CHOICES = ['file', 'swift', 's3'] |
38 | + |
39 | + |
40 | +def create_options(parser): |
41 | + """ |
42 | + Sets up the CLI and config-file options that may be |
43 | + parsed and program commands. |
44 | + |
45 | + :param parser: The option parser |
46 | + """ |
47 | + parser.add_option('-v', '--verbose', default=False, dest="verbose", |
48 | + action="store_true", |
49 | + help="Print more verbose output") |
50 | + parser.add_option('-H', '--host', |
51 | + dest="host", metavar="ADDRESS", |
52 | + default="0.0.0.0", |
53 | + help="Address of Glance API server. " |
54 | + "Default: %default") |
55 | + parser.add_option('-p', '--port', |
56 | + dest="port", metavar="PORT", type=int, |
57 | + default=9292, |
58 | + help="Port the Glance API server listens on. " |
59 | + "Default: %default") |
60 | + parser.add_option('--daemonize', default=False, action="store_true", |
61 | + help="Daemonize this process") |
62 | + parser.add_option('--use-syslog', default=True, action="store_true", |
63 | + help="Output to syslog when daemonizing. " |
64 | + "Default: %default") |
65 | + parser.add_option('--logfile', default=None, |
66 | + metavar="PATH", |
67 | + help="(Optional) Name of log file to output to.") |
68 | + parser.add_option("--logdir", default=None, |
69 | + help="(Optional) The directory to keep log files in " |
70 | + "(will be prepended to --logfile)") |
71 | + parser.add_option("--pidfile", default=None, |
72 | + help="(Optional) Name of pid file for the server") |
73 | + parser.add_option('--working-directory', '--working-dir', |
74 | + default=os.path.abspath(os.getcwd()), |
75 | + help="The working directory. Default: %default") |
76 | + parser.add_option("--uid", type=int, default=os.getuid(), |
77 | + help="uid under which to run. Default: %default") |
78 | + parser.add_option("--gid", type=int, default=os.getgid(), |
79 | + help="gid under which to run. Default: %default") |
80 | + parser.add_option('--registry-host', |
81 | + dest="registry_host", metavar="ADDRESS", |
82 | + default="0.0.0.0", |
83 | + help="Address of a Glance Registry server. " |
84 | + "Default: %default") |
85 | + parser.add_option('--registry-port', |
86 | + dest="registry_port", metavar="PORT", type=int, |
87 | + default=9191, |
88 | + help="Port a Glance Registry server listens on. " |
89 | + "Default: %default") |
90 | + parser.add_option('--default-store', metavar="STORE", |
91 | + default="file", |
92 | + choices=DEFAULT_STORE_CHOICES, |
93 | + help="The backend store that Glance will use to store " |
94 | + "virtual machine images to. Choices: ('%s') " |
95 | + "Default: %%default" % "','".join(DEFAULT_STORE_CHOICES)) |
96 | + glance.store.add_options(parser) |
97 | |
98 | |
99 | def main(_args): |
100 | @@ -44,10 +104,13 @@ |
101 | from glance.common import wsgi |
102 | from glance.server import API |
103 | server = wsgi.Server() |
104 | - server.start(API(), FLAGS.api_port, host=FLAGS.api_host) |
105 | + server.start(API(options), options['port'], host=options['host']) |
106 | server.wait() |
107 | |
108 | |
109 | if __name__ == '__main__': |
110 | - utils.default_flagfile() |
111 | - server.serve('glance-api', main) |
112 | + oparser = optparse.OptionParser(version='%%prog %s' |
113 | + % version.version_string()) |
114 | + create_options(oparser) |
115 | + (options, args) = config.parse_options(oparser) |
116 | + server.serve('glance-api', main, options, args) |
117 | |
118 | === modified file 'bin/glance-registry' |
119 | --- bin/glance-registry 2011-01-27 04:04:29 +0000 |
120 | +++ bin/glance-registry 2011-01-31 19:42:44 +0000 |
121 | @@ -22,6 +22,7 @@ |
122 | Reference implementation server for Glance Registry |
123 | """ |
124 | |
125 | +import optparse |
126 | import os |
127 | import sys |
128 | |
129 | @@ -29,12 +30,55 @@ |
130 | |
131 | sys.path.append(ROOT_DIR) |
132 | |
133 | -from glance.common import flags |
134 | -from glance.common import utils |
135 | +from glance import version |
136 | +from glance.common import config |
137 | from glance.common import server |
138 | |
139 | |
140 | -FLAGS = flags.FLAGS |
141 | +def create_options(parser): |
142 | + """ |
143 | + Sets up the CLI and config-file options that may be |
144 | + parsed and program commands. |
145 | + |
146 | + :param parser: The option parser |
147 | + """ |
148 | + parser.add_option('-v', '--verbose', default=False, dest="verbose", |
149 | + action="store_true", |
150 | + help="Print more verbose output") |
151 | + parser.add_option('-H', '--host', |
152 | + dest="host", metavar="ADDRESS", |
153 | + default="0.0.0.0", |
154 | + help="Address of Glance API server. " |
155 | + "Default: %default") |
156 | + parser.add_option('-p', '--port', |
157 | + dest="port", metavar="PORT", type=int, |
158 | + default=9191, |
159 | + help="Port the Glance Registry server listens on. " |
160 | + "Default: %default") |
161 | + parser.add_option('--daemonize', default=False, action="store_true", |
162 | + help="Daemonize this process") |
163 | + parser.add_option('--use-syslog', default=True, action="store_true", |
164 | + help="Output to syslog when daemonizing. " |
165 | + "Default: %default") |
166 | + parser.add_option('--logfile', default=None, |
167 | + metavar="PATH", |
168 | + help="(Optional) Name of log file to output to.") |
169 | + parser.add_option("--logdir", default=None, |
170 | + help="(Optional) The directory to keep log files in " |
171 | + "(will be prepended to --logfile)") |
172 | + parser.add_option("--pidfile", default=None, |
173 | + help="(Optional) Name of pid file for the server") |
174 | + parser.add_option('--working-directory', '--working-dir', |
175 | + default=os.path.abspath(os.getcwd()), |
176 | + help="The working directory. Default: %default") |
177 | + parser.add_option("--uid", type=int, default=os.getuid(), |
178 | + help="uid under which to run. Default: %default") |
179 | + parser.add_option("--gid", type=int, default=os.getgid(), |
180 | + help="gid under which to run. Default: %default") |
181 | + parser.add_option('--sql-connection', metavar="CONNECTION", |
182 | + default='sqlite:///glance.sqlite', |
183 | + help="A valid SQLAlchemy connection string for the " |
184 | + "registry database. Default: %default") |
185 | |
186 | |
187 | def main(_args): |
188 | @@ -43,10 +87,13 @@ |
189 | from glance.common import wsgi |
190 | from glance.registry.server import API |
191 | server = wsgi.Server() |
192 | - server.start(API(), FLAGS.registry_port, host=FLAGS.registry_host) |
193 | + server.start(API(options), int(options['port']), host=options['host']) |
194 | server.wait() |
195 | |
196 | |
197 | if __name__ == '__main__': |
198 | - utils.default_flagfile() |
199 | - server.serve('glance-registry', main) |
200 | + oparser = optparse.OptionParser(version='%%prog %s' |
201 | + % version.version_string()) |
202 | + create_options(oparser) |
203 | + (options, args) = config.parse_options(oparser) |
204 | + server.serve('glance-registry', main, options, args) |
205 | |
206 | === added file 'glance/common/config.py' |
207 | --- glance/common/config.py 1970-01-01 00:00:00 +0000 |
208 | +++ glance/common/config.py 2011-01-31 19:42:44 +0000 |
209 | @@ -0,0 +1,54 @@ |
210 | +#!/usr/bin/env python |
211 | +# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
212 | + |
213 | +# Copyright 2011 OpenStack LLC. |
214 | +# All Rights Reserved. |
215 | +# |
216 | +# Licensed under the Apache License, Version 2.0 (the "License"); you may |
217 | +# not use this file except in compliance with the License. You may obtain |
218 | +# a copy of the License at |
219 | +# |
220 | +# http://www.apache.org/licenses/LICENSE-2.0 |
221 | +# |
222 | +# Unless required by applicable law or agreed to in writing, software |
223 | +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
224 | +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
225 | +# License for the specific language governing permissions and limitations |
226 | +# under the License. |
227 | + |
228 | + |
229 | +def parse_options(parser, cli_args=None): |
230 | + """ |
231 | + Returns the parsed CLI options, command to run and its arguments, merged |
232 | + with any same-named options found in a configuration file. |
233 | + |
234 | + The function returns a tuple of (options, args), where options is a |
235 | + mapping of option key/str(value) pairs, and args is the set of arguments |
236 | + (not options) supplied on the command-line. |
237 | + |
238 | + The reason that the option values are returned as strings only is that |
239 | + ConfigParser and paste.deploy only accept string values... |
240 | + |
241 | + :param parser: The option parser |
242 | + :param cli_args: (Optional) Set of arguments to process. If not present, |
243 | + sys.argv[1:] is used. |
244 | + :retval tuple of (options, args) |
245 | + """ |
246 | + |
247 | + (options, args) = parser.parse_args(cli_args) |
248 | + |
249 | + return (vars(options), args) |
250 | + |
251 | + |
252 | +def options_to_conf(options): |
253 | + """ |
254 | + Converts a mapping of options having typed values into |
255 | + a mapping of configuration options having only stringified values. |
256 | + |
257 | + This method is used to convert the return of parse_options()[0] |
258 | + into the configuration mapping that is expected by ConfigParser |
259 | + and paste.deploy. |
260 | + |
261 | + :params options: Mapping of typed option key/values |
262 | + """ |
263 | + return dict([(k, str(v)) for k, v in options.items()]) |
264 | |
265 | === removed directory 'glance/common/db/sqlalchemy' |
266 | === removed file 'glance/common/db/sqlalchemy/__init__.py' |
267 | --- glance/common/db/sqlalchemy/__init__.py 2011-01-26 17:26:54 +0000 |
268 | +++ glance/common/db/sqlalchemy/__init__.py 1970-01-01 00:00:00 +0000 |
269 | @@ -1,16 +0,0 @@ |
270 | -# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
271 | - |
272 | -# Copyright 2010-2011 OpenStack LLC. |
273 | -# All Rights Reserved. |
274 | -# |
275 | -# Licensed under the Apache License, Version 2.0 (the "License"); you may |
276 | -# not use this file except in compliance with the License. You may obtain |
277 | -# a copy of the License at |
278 | -# |
279 | -# http://www.apache.org/licenses/LICENSE-2.0 |
280 | -# |
281 | -# Unless required by applicable law or agreed to in writing, software |
282 | -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
283 | -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
284 | -# License for the specific language governing permissions and limitations |
285 | -# under the License. |
286 | |
287 | === removed file 'glance/common/db/sqlalchemy/session.py' |
288 | --- glance/common/db/sqlalchemy/session.py 2011-01-26 17:26:54 +0000 |
289 | +++ glance/common/db/sqlalchemy/session.py 1970-01-01 00:00:00 +0000 |
290 | @@ -1,49 +0,0 @@ |
291 | -# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
292 | - |
293 | -# Copyright 2010 United States Government as represented by the |
294 | -# Administrator of the National Aeronautics and Space Administration. |
295 | -# All Rights Reserved. |
296 | -# |
297 | -# Licensed under the Apache License, Version 2.0 (the "License"); you may |
298 | -# not use this file except in compliance with the License. You may obtain |
299 | -# a copy of the License at |
300 | -# |
301 | -# http://www.apache.org/licenses/LICENSE-2.0 |
302 | -# |
303 | -# Unless required by applicable law or agreed to in writing, software |
304 | -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
305 | -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
306 | -# License for the specific language governing permissions and limitations |
307 | -# under the License. |
308 | - |
309 | -""" |
310 | -Session Handling for SQLAlchemy backend |
311 | -""" |
312 | - |
313 | -from sqlalchemy import create_engine |
314 | -from sqlalchemy.orm import sessionmaker |
315 | - |
316 | -from glance.common import flags |
317 | - |
318 | -FLAGS = flags.FLAGS |
319 | - |
320 | -_ENGINE = None |
321 | -_MAKER = None |
322 | - |
323 | - |
324 | -def get_engine(echo=False): |
325 | - global _ENGINE |
326 | - if not _ENGINE: |
327 | - _ENGINE = create_engine(FLAGS.sql_connection, echo=echo) |
328 | - return _ENGINE |
329 | - |
330 | - |
331 | -def get_session(autocommit=True, expire_on_commit=False): |
332 | - """Helper method to grab session""" |
333 | - global _MAKER |
334 | - if not _MAKER: |
335 | - engine = get_engine() |
336 | - _MAKER = sessionmaker(bind=engine, |
337 | - autocommit=autocommit, |
338 | - expire_on_commit=expire_on_commit) |
339 | - return _MAKER() |
340 | |
341 | === removed file 'glance/common/flags.py' |
342 | --- glance/common/flags.py 2011-01-26 17:26:54 +0000 |
343 | +++ glance/common/flags.py 1970-01-01 00:00:00 +0000 |
344 | @@ -1,177 +0,0 @@ |
345 | -# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
346 | - |
347 | -# Copyright 2010 United States Government as represented by the |
348 | -# Administrator of the National Aeronautics and Space Administration. |
349 | -# All Rights Reserved. |
350 | -# |
351 | -# Licensed under the Apache License, Version 2.0 (the "License"); you may |
352 | -# not use this file except in compliance with the License. You may obtain |
353 | -# a copy of the License at |
354 | -# |
355 | -# http://www.apache.org/licenses/LICENSE-2.0 |
356 | -# |
357 | -# Unless required by applicable law or agreed to in writing, software |
358 | -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
359 | -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
360 | -# License for the specific language governing permissions and limitations |
361 | -# under the License. |
362 | - |
363 | -""" |
364 | -Package-level global flags are defined here, the rest are defined |
365 | -where they're used. |
366 | -""" |
367 | - |
368 | -import getopt |
369 | -import os |
370 | -import socket |
371 | -import sys |
372 | - |
373 | -import gflags |
374 | - |
375 | - |
376 | -class FlagValues(gflags.FlagValues): |
377 | - """Extension of gflags.FlagValues that allows undefined and runtime flags. |
378 | - |
379 | - Unknown flags will be ignored when parsing the command line, but the |
380 | - command line will be kept so that it can be replayed if new flags are |
381 | - defined after the initial parsing. |
382 | - |
383 | - """ |
384 | - |
385 | - def __init__(self): |
386 | - gflags.FlagValues.__init__(self) |
387 | - self.__dict__['__dirty'] = [] |
388 | - self.__dict__['__was_already_parsed'] = False |
389 | - self.__dict__['__stored_argv'] = [] |
390 | - |
391 | - def __call__(self, argv): |
392 | - # We're doing some hacky stuff here so that we don't have to copy |
393 | - # out all the code of the original verbatim and then tweak a few lines. |
394 | - # We're hijacking the output of getopt so we can still return the |
395 | - # leftover args at the end |
396 | - sneaky_unparsed_args = {"value": None} |
397 | - original_argv = list(argv) |
398 | - |
399 | - if self.IsGnuGetOpt(): |
400 | - orig_getopt = getattr(getopt, 'gnu_getopt') |
401 | - orig_name = 'gnu_getopt' |
402 | - else: |
403 | - orig_getopt = getattr(getopt, 'getopt') |
404 | - orig_name = 'getopt' |
405 | - |
406 | - def _sneaky(*args, **kw): |
407 | - optlist, unparsed_args = orig_getopt(*args, **kw) |
408 | - sneaky_unparsed_args['value'] = unparsed_args |
409 | - return optlist, unparsed_args |
410 | - |
411 | - try: |
412 | - setattr(getopt, orig_name, _sneaky) |
413 | - args = gflags.FlagValues.__call__(self, argv) |
414 | - except gflags.UnrecognizedFlagError: |
415 | - # Undefined args were found, for now we don't care so just |
416 | - # act like everything went well |
417 | - # (these three lines are copied pretty much verbatim from the end |
418 | - # of the __call__ function we are wrapping) |
419 | - unparsed_args = sneaky_unparsed_args['value'] |
420 | - if unparsed_args: |
421 | - if self.IsGnuGetOpt(): |
422 | - args = argv[:1] + unparsed_args |
423 | - else: |
424 | - args = argv[:1] + original_argv[-len(unparsed_args):] |
425 | - else: |
426 | - args = argv[:1] |
427 | - finally: |
428 | - setattr(getopt, orig_name, orig_getopt) |
429 | - |
430 | - # Store the arguments for later, we'll need them for new flags |
431 | - # added at runtime |
432 | - self.__dict__['__stored_argv'] = original_argv |
433 | - self.__dict__['__was_already_parsed'] = True |
434 | - self.ClearDirty() |
435 | - return args |
436 | - |
437 | - def SetDirty(self, name): |
438 | - """Mark a flag as dirty so that accessing it will case a reparse.""" |
439 | - self.__dict__['__dirty'].append(name) |
440 | - |
441 | - def IsDirty(self, name): |
442 | - return name in self.__dict__['__dirty'] |
443 | - |
444 | - def ClearDirty(self): |
445 | - self.__dict__['__is_dirty'] = [] |
446 | - |
447 | - def WasAlreadyParsed(self): |
448 | - return self.__dict__['__was_already_parsed'] |
449 | - |
450 | - def ParseNewFlags(self): |
451 | - if '__stored_argv' not in self.__dict__: |
452 | - return |
453 | - new_flags = FlagValues() |
454 | - for k in self.__dict__['__dirty']: |
455 | - new_flags[k] = gflags.FlagValues.__getitem__(self, k) |
456 | - |
457 | - new_flags(self.__dict__['__stored_argv']) |
458 | - for k in self.__dict__['__dirty']: |
459 | - setattr(self, k, getattr(new_flags, k)) |
460 | - self.ClearDirty() |
461 | - |
462 | - def __setitem__(self, name, flag): |
463 | - gflags.FlagValues.__setitem__(self, name, flag) |
464 | - if self.WasAlreadyParsed(): |
465 | - self.SetDirty(name) |
466 | - |
467 | - def __getitem__(self, name): |
468 | - if self.IsDirty(name): |
469 | - self.ParseNewFlags() |
470 | - return gflags.FlagValues.__getitem__(self, name) |
471 | - |
472 | - def __getattr__(self, name): |
473 | - if self.IsDirty(name): |
474 | - self.ParseNewFlags() |
475 | - return gflags.FlagValues.__getattr__(self, name) |
476 | - |
477 | - |
478 | -FLAGS = FlagValues() |
479 | - |
480 | - |
481 | -def _wrapper(func): |
482 | - def _wrapped(*args, **kw): |
483 | - kw.setdefault('flag_values', FLAGS) |
484 | - func(*args, **kw) |
485 | - _wrapped.func_name = func.func_name |
486 | - return _wrapped |
487 | - |
488 | - |
489 | -DEFINE = _wrapper(gflags.DEFINE) |
490 | -DEFINE_string = _wrapper(gflags.DEFINE_string) |
491 | -DEFINE_integer = _wrapper(gflags.DEFINE_integer) |
492 | -DEFINE_bool = _wrapper(gflags.DEFINE_bool) |
493 | -DEFINE_boolean = _wrapper(gflags.DEFINE_boolean) |
494 | -DEFINE_float = _wrapper(gflags.DEFINE_float) |
495 | -DEFINE_enum = _wrapper(gflags.DEFINE_enum) |
496 | -DEFINE_list = _wrapper(gflags.DEFINE_list) |
497 | -DEFINE_spaceseplist = _wrapper(gflags.DEFINE_spaceseplist) |
498 | -DEFINE_multistring = _wrapper(gflags.DEFINE_multistring) |
499 | -DEFINE_multi_int = _wrapper(gflags.DEFINE_multi_int) |
500 | - |
501 | - |
502 | -def DECLARE(name, module_string, flag_values=FLAGS): |
503 | - if module_string not in sys.modules: |
504 | - __import__(module_string, globals(), locals()) |
505 | - if name not in flag_values: |
506 | - raise gflags.UnrecognizedFlag( |
507 | - "%s not defined by %s" % (name, module_string)) |
508 | - |
509 | - |
510 | -# __GLOBAL FLAGS ONLY__ |
511 | -# Define any app-specific flags in their own files, docs at: |
512 | -# http://code.google.com/p/python-gflags/source/browse/trunk/gflags.py#39 |
513 | - |
514 | -# TODO(sirp): move this out to an application specific setting when we create |
515 | -# Nova/Glance common library |
516 | -DEFINE_string('sql_connection', |
517 | - 'sqlite:///%s/glance.sqlite' % os.path.abspath("./"), |
518 | - 'connection string for sql database') |
519 | -DEFINE_bool('verbose', False, 'show debug output') |
520 | -DEFINE_string('default_store', 'file', |
521 | - 'Default storage backend. Default: "file"') |
522 | |
523 | === modified file 'glance/common/server.py' |
524 | --- glance/common/server.py 2011-01-26 17:26:54 +0000 |
525 | +++ glance/common/server.py 2011-01-31 19:42:44 +0000 |
526 | @@ -25,30 +25,11 @@ |
527 | import logging |
528 | import logging.handlers |
529 | import os |
530 | +import pprint |
531 | import signal |
532 | import sys |
533 | import time |
534 | |
535 | -from glance.common import flags |
536 | - |
537 | - |
538 | -FLAGS = flags.FLAGS |
539 | -flags.DEFINE_bool('daemonize', False, 'daemonize this process') |
540 | -# NOTE(termie): right now I am defaulting to using syslog when we daemonize |
541 | -# it may be better to do something else -shrug- |
542 | -# NOTE(Devin): I think we should let each process have its own log file |
543 | -# and put it in /var/logs/nova/(appname).log |
544 | -# This makes debugging much easier and cuts down on sys log |
545 | -# clutter. |
546 | -flags.DEFINE_bool('use_syslog', True, 'output to syslog when daemonizing') |
547 | -flags.DEFINE_string('logfile', None, 'log file to output to') |
548 | -flags.DEFINE_string('logdir', None, 'directory to keep log files in ' |
549 | - '(will be prepended to $logfile)') |
550 | -flags.DEFINE_string('pidfile', None, 'pid file to output to') |
551 | -flags.DEFINE_string('working_directory', './', 'working directory...') |
552 | -flags.DEFINE_integer('uid', os.getuid(), 'uid under which to run') |
553 | -flags.DEFINE_integer('gid', os.getgid(), 'gid under which to run') |
554 | - |
555 | |
556 | def stop(pidfile): |
557 | """ |
558 | @@ -64,6 +45,7 @@ |
559 | |
560 | # Try killing the daemon process |
561 | try: |
562 | + print "Killing process from pidfile %s" % pidfile |
563 | while 1: |
564 | os.kill(pid, signal.SIGTERM) |
565 | time.sleep(0.1) |
566 | @@ -77,53 +59,58 @@ |
567 | sys.exit(1) |
568 | |
569 | |
570 | -def serve(name, main): |
571 | +def serve(name, main, options, args): |
572 | """Controller for server""" |
573 | - argv = FLAGS(sys.argv) |
574 | - |
575 | - if not FLAGS.pidfile: |
576 | - FLAGS.pidfile = '%s.pid' % name |
577 | - |
578 | - logging.debug("Full set of FLAGS: \n\n\n") |
579 | - for flag in FLAGS: |
580 | - logging.debug("%s : %s", flag, FLAGS.get(flag, None)) |
581 | + |
582 | + pidfile = options['pidfile'] |
583 | + if not pidfile: |
584 | + options['pidfile'] = '%s.pid' % name |
585 | |
586 | action = 'start' |
587 | - if len(argv) > 1: |
588 | - action = argv.pop() |
589 | + if len(args): |
590 | + action = args.pop() |
591 | |
592 | if action == 'stop': |
593 | - stop(FLAGS.pidfile) |
594 | + stop(options['pidfile']) |
595 | sys.exit() |
596 | elif action == 'restart': |
597 | - stop(FLAGS.pidfile) |
598 | + stop(options['pidfile']) |
599 | elif action == 'start': |
600 | pass |
601 | else: |
602 | - print 'usage: %s [options] [start|stop|restart]' % argv[0] |
603 | + print 'usage: %s [options] [start|stop|restart]' % name |
604 | sys.exit(1) |
605 | - daemonize(argv, name, main) |
606 | - |
607 | - |
608 | -def daemonize(args, name, main): |
609 | + daemonize(args, name, main, options) |
610 | + |
611 | + |
612 | +def daemonize(args, name, main, options): |
613 | """Does the work of daemonizing the process""" |
614 | logging.getLogger('amqplib').setLevel(logging.WARN) |
615 | + pidfile = options['pidfile'] |
616 | + logfile = options['logfile'] |
617 | + if not logfile: |
618 | + logfile = None |
619 | + logdir = options['logdir'] |
620 | + if not logdir: |
621 | + logdir = None |
622 | + daemonize = options['daemonize'] |
623 | + use_syslog = options['use_syslog'] |
624 | files_to_keep = [] |
625 | - if FLAGS.daemonize: |
626 | + if daemonize: |
627 | logger = logging.getLogger() |
628 | formatter = logging.Formatter( |
629 | name + '(%(name)s): %(levelname)s %(message)s') |
630 | - if FLAGS.use_syslog and not FLAGS.logfile: |
631 | + if use_syslog and not logfile: |
632 | syslog = logging.handlers.SysLogHandler(address='/dev/log') |
633 | syslog.setFormatter(formatter) |
634 | logger.addHandler(syslog) |
635 | files_to_keep.append(syslog.socket) |
636 | else: |
637 | - if not FLAGS.logfile: |
638 | - FLAGS.logfile = '%s.log' % name |
639 | - if FLAGS.logdir: |
640 | - FLAGS.logfile = os.path.join(FLAGS.logdir, FLAGS.logfile) |
641 | - logfile = logging.FileHandler(FLAGS.logfile) |
642 | + if not logfile: |
643 | + logfile = '%s.log' % name |
644 | + if logdir: |
645 | + logfile = os.path.join(logdir, logfile) |
646 | + logfile = logging.FileHandler(logfile) |
647 | logfile.setFormatter(formatter) |
648 | logger.addHandler(logfile) |
649 | files_to_keep.append(logfile.stream) |
650 | @@ -131,21 +118,21 @@ |
651 | else: |
652 | stdin, stdout, stderr = sys.stdin, sys.stdout, sys.stderr |
653 | |
654 | - if FLAGS.verbose: |
655 | + if options['verbose']: |
656 | logging.getLogger().setLevel(logging.DEBUG) |
657 | else: |
658 | logging.getLogger().setLevel(logging.WARNING) |
659 | |
660 | with daemon.DaemonContext( |
661 | - detach_process=FLAGS.daemonize, |
662 | - working_directory=FLAGS.working_directory, |
663 | - pidfile=pidlockfile.TimeoutPIDLockFile(FLAGS.pidfile, |
664 | + detach_process=daemonize, |
665 | + working_directory=options['working_directory'], |
666 | + pidfile=pidlockfile.TimeoutPIDLockFile(pidfile, |
667 | acquire_timeout=1, |
668 | threaded=False), |
669 | stdin=stdin, |
670 | stdout=stdout, |
671 | stderr=stderr, |
672 | - uid=FLAGS.uid, |
673 | - gid=FLAGS.gid, |
674 | + uid=options['uid'], |
675 | + gid=options['gid'], |
676 | files_preserve=files_to_keep): |
677 | main(args) |
678 | |
679 | === modified file 'glance/common/utils.py' |
680 | --- glance/common/utils.py 2011-01-26 17:26:54 +0000 |
681 | +++ glance/common/utils.py 2011-01-31 19:42:44 +0000 |
682 | @@ -30,11 +30,9 @@ |
683 | import sys |
684 | |
685 | from glance.common import exception |
686 | -from glance.common import flags |
687 | from glance.common.exception import ProcessExecutionError |
688 | |
689 | |
690 | -FLAGS = flags.FLAGS |
691 | TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ" |
692 | |
693 | |
694 | @@ -147,21 +145,6 @@ |
695 | return int(address.split(".")[-1]) |
696 | |
697 | |
698 | -def get_my_ip(): |
699 | - """Returns the actual ip of the local machine.""" |
700 | - if getattr(FLAGS, 'fake_tests', None): |
701 | - return '127.0.0.1' |
702 | - try: |
703 | - csock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
704 | - csock.connect(('www.google.com', 80)) |
705 | - (addr, port) = csock.getsockname() |
706 | - csock.close() |
707 | - return addr |
708 | - except socket.gaierror as ex: |
709 | - logging.warn("Couldn't get IP, using 127.0.0.1 %s", ex) |
710 | - return "127.0.0.1" |
711 | - |
712 | - |
713 | def isotime(at=None): |
714 | if not at: |
715 | at = datetime.datetime.utcnow() |
716 | |
717 | === modified file 'glance/registry/__init__.py' |
718 | --- glance/registry/__init__.py 2011-01-26 20:44:36 +0000 |
719 | +++ glance/registry/__init__.py 2011-01-31 19:42:44 +0000 |
720 | @@ -20,45 +20,39 @@ |
721 | Registry API |
722 | """ |
723 | |
724 | -from glance.common import flags |
725 | from glance.registry import client |
726 | |
727 | -FLAGS = flags.FLAGS |
728 | - |
729 | -# TODO(jaypipes): Separate server flags from client flags |
730 | -# and allow a list of client host/port |
731 | -# combinations |
732 | -flags.DEFINE_string('registry_host', '0.0.0.0', |
733 | - 'Registry server lives at this address') |
734 | -flags.DEFINE_integer('registry_port', 9191, |
735 | - 'Registry server listens on this port') |
736 | - |
737 | - |
738 | -def get_images_list(): |
739 | - c = client.RegistryClient(FLAGS.registry_host, FLAGS.registry_port) |
740 | + |
741 | +def get_registry_client(options): |
742 | + return client.RegistryClient(options['registry_host'], |
743 | + int(options['registry_port'])) |
744 | + |
745 | + |
746 | +def get_images_list(options): |
747 | + c = get_registry_client(options) |
748 | return c.get_images() |
749 | |
750 | |
751 | -def get_images_detail(): |
752 | - c = client.RegistryClient(FLAGS.registry_host, FLAGS.registry_port) |
753 | +def get_images_detail(options): |
754 | + c = get_registry_client(options) |
755 | return c.get_images_detailed() |
756 | |
757 | |
758 | -def get_image_metadata(image_id): |
759 | - c = client.RegistryClient(FLAGS.registry_host, FLAGS.registry_port) |
760 | +def get_image_metadata(options, image_id): |
761 | + c = get_registry_client(options) |
762 | return c.get_image(image_id) |
763 | |
764 | |
765 | -def add_image_metadata(image_data): |
766 | - c = client.RegistryClient(FLAGS.registry_host, FLAGS.registry_port) |
767 | +def add_image_metadata(options, image_data): |
768 | + c = get_registry_client(options) |
769 | return c.add_image(image_data) |
770 | |
771 | |
772 | -def update_image_metadata(image_id, image_data): |
773 | - c = client.RegistryClient(FLAGS.registry_host, FLAGS.registry_port) |
774 | +def update_image_metadata(options, image_id, image_data): |
775 | + c = get_registry_client(options) |
776 | return c.update_image(image_id, image_data) |
777 | |
778 | |
779 | -def delete_image_metadata(image_id): |
780 | - c = client.RegistryClient(FLAGS.registry_host, FLAGS.registry_port) |
781 | +def delete_image_metadata(options, image_id): |
782 | + c = get_registry_client(options) |
783 | return c.delete_image(image_id) |
784 | |
785 | === modified file 'glance/registry/db/__init__.py' |
786 | --- glance/registry/db/__init__.py 2011-01-28 19:39:19 +0000 |
787 | +++ glance/registry/db/__init__.py 2011-01-31 19:42:44 +0000 |
788 | @@ -16,12 +16,3 @@ |
789 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
790 | # License for the specific language governing permissions and limitations |
791 | # under the License. |
792 | - |
793 | -""" |
794 | -DB abstraction for Nova and Glance |
795 | -""" |
796 | - |
797 | -from glance.registry.db import models |
798 | - |
799 | - |
800 | -models.register_models() |
801 | |
802 | === modified file 'glance/registry/db/api.py' |
803 | --- glance/registry/db/api.py 2011-01-28 19:54:10 +0000 |
804 | +++ glance/registry/db/api.py 2011-01-31 19:42:44 +0000 |
805 | @@ -21,13 +21,19 @@ |
806 | Defines interface for DB access |
807 | """ |
808 | |
809 | + |
810 | +from sqlalchemy import create_engine |
811 | +from sqlalchemy.ext.declarative import declarative_base |
812 | from sqlalchemy.orm import joinedload |
813 | +from sqlalchemy.orm import sessionmaker |
814 | |
815 | from glance.common import exception |
816 | from glance.common import utils |
817 | -from glance.common.db.sqlalchemy.session import get_session |
818 | from glance.registry.db import models |
819 | |
820 | +_ENGINE = None |
821 | +_MAKER = None |
822 | +BASE = declarative_base() |
823 | |
824 | # attributes common to all models |
825 | BASE_MODEL_ATTRS = set(['id', 'created_at', 'updated_at', 'deleted_at', |
826 | @@ -36,7 +42,44 @@ |
827 | IMAGE_ATTRS = BASE_MODEL_ATTRS | set(['name', 'type', 'status', 'size', |
828 | 'is_public', 'location']) |
829 | |
830 | -################### |
831 | + |
832 | +def configure_db(options): |
833 | + """ |
834 | + Establish the database, create an engine if needed, and |
835 | + register the models. |
836 | + |
837 | + :param options: Mapping of configuration options |
838 | + """ |
839 | + global _ENGINE |
840 | + if not _ENGINE: |
841 | + _ENGINE = create_engine(options['sql_connection'], |
842 | + echo=options['verbose']) |
843 | + register_models() |
844 | + |
845 | + |
846 | +def get_session(autocommit=True, expire_on_commit=False): |
847 | + """Helper method to grab session""" |
848 | + global _MAKER, _ENGINE |
849 | + if not _MAKER: |
850 | + assert _ENGINE |
851 | + _MAKER = sessionmaker(bind=_ENGINE, |
852 | + autocommit=autocommit, |
853 | + expire_on_commit=expire_on_commit) |
854 | + return _MAKER() |
855 | + |
856 | + |
857 | +def register_models(): |
858 | + """Register Models and create properties""" |
859 | + global _ENGINE |
860 | + assert _ENGINE |
861 | + BASE.metadata.create_all(_ENGINE) |
862 | + |
863 | + |
864 | +def unregister_models(): |
865 | + """Unregister Models, useful clearing out data before testing""" |
866 | + global _ENGINE |
867 | + assert _ENGINE |
868 | + BASE.metadata.drop_all(engine) |
869 | |
870 | |
871 | def image_create(context, values): |
872 | |
873 | === modified file 'glance/registry/db/models.py' |
874 | --- glance/registry/db/models.py 2011-01-28 19:39:19 +0000 |
875 | +++ glance/registry/db/models.py 2011-01-31 19:42:44 +0000 |
876 | @@ -29,10 +29,9 @@ |
877 | from sqlalchemy import UniqueConstraint |
878 | from sqlalchemy.ext.declarative import declarative_base |
879 | |
880 | -from glance.common.db.sqlalchemy.session import get_session, get_engine |
881 | +import glance.registry.db.api |
882 | from glance.common import exception |
883 | |
884 | - |
885 | BASE = declarative_base() |
886 | |
887 | |
888 | @@ -50,7 +49,7 @@ |
889 | |
890 | def save(self, session=None): |
891 | """Save this object""" |
892 | - session = session or get_session() |
893 | + session = session or glance.registry.db.api.get_session() |
894 | session.add(self) |
895 | session.flush() |
896 | |
897 | @@ -126,15 +125,3 @@ |
898 | |
899 | key = Column(String(255), index=True) |
900 | value = Column(Text) |
901 | - |
902 | - |
903 | -def register_models(): |
904 | - """Register Models and create properties""" |
905 | - engine = get_engine() |
906 | - BASE.metadata.create_all(engine) |
907 | - |
908 | - |
909 | -def unregister_models(): |
910 | - """Unregister Models, useful clearing out data before testing""" |
911 | - engine = get_engine() |
912 | - BASE.metadata.drop_all(engine) |
913 | |
914 | === modified file 'glance/registry/server.py' |
915 | --- glance/registry/server.py 2011-01-28 19:39:19 +0000 |
916 | +++ glance/registry/server.py 2011-01-31 19:42:44 +0000 |
917 | @@ -27,8 +27,12 @@ |
918 | from glance.registry.db import api as db_api |
919 | |
920 | |
921 | -class ImageController(wsgi.Controller): |
922 | - """Image Controller """ |
923 | +class Controller(wsgi.Controller): |
924 | + """Controller for the reference implementation registry server""" |
925 | + |
926 | + def __init__(self, options): |
927 | + self.options = options |
928 | + db_api.configure_db(options) |
929 | |
930 | def index(self, req): |
931 | """Return basic information for all public, non-deleted images |
932 | @@ -141,11 +145,12 @@ |
933 | class API(wsgi.Router): |
934 | """WSGI entry point for all Registry requests.""" |
935 | |
936 | - def __init__(self): |
937 | + def __init__(self, options): |
938 | mapper = routes.Mapper() |
939 | - mapper.resource("image", "images", controller=ImageController(), |
940 | + controller = Controller(options) |
941 | + mapper.resource("image", "images", controller=controller, |
942 | collection={'detail': 'GET'}) |
943 | - mapper.connect("/", controller=ImageController(), action="index") |
944 | + mapper.connect("/", controller=controller, action="index") |
945 | super(API, self).__init__(mapper) |
946 | |
947 | |
948 | |
949 | === modified file 'glance/server.py' |
950 | --- glance/server.py 2011-01-27 04:19:13 +0000 |
951 | +++ glance/server.py 2011-01-31 19:42:44 +0000 |
952 | @@ -39,7 +39,6 @@ |
953 | HTTPBadRequest) |
954 | |
955 | from glance.common import exception |
956 | -from glance.common import flags |
957 | from glance.common import wsgi |
958 | from glance.store import (get_from_backend, |
959 | delete_from_backend, |
960 | @@ -50,9 +49,6 @@ |
961 | from glance import util |
962 | |
963 | |
964 | -FLAGS = flags.FLAGS |
965 | - |
966 | - |
967 | class Controller(wsgi.Controller): |
968 | |
969 | """ |
970 | @@ -73,6 +69,9 @@ |
971 | DELETE /images/<ID> -- Delete the image with id <ID> |
972 | """ |
973 | |
974 | + def __init__(self, options): |
975 | + self.options = options |
976 | + |
977 | def index(self, req): |
978 | """ |
979 | Returns the following information for all public, available images: |
980 | @@ -92,7 +91,7 @@ |
981 | 'type': <TYPE>}, ... |
982 | ]} |
983 | """ |
984 | - images = registry.get_images_list() |
985 | + images = registry.get_images_list(self.options) |
986 | return dict(images=images) |
987 | |
988 | def detail(self, req): |
989 | @@ -115,7 +114,7 @@ |
990 | 'properties': {'distro': 'Ubuntu 10.04 LTS', ...}}, ... |
991 | ]} |
992 | """ |
993 | - images = registry.get_images_detail() |
994 | + images = registry.get_images_detail(self.options) |
995 | return dict(images=images) |
996 | |
997 | def meta(self, req, id): |
998 | @@ -183,7 +182,8 @@ |
999 | image_meta['size'] = image_meta.get('size', 0) |
1000 | |
1001 | try: |
1002 | - image_meta = registry.add_image_metadata(image_meta) |
1003 | + image_meta = registry.add_image_metadata(self.options, |
1004 | + image_meta) |
1005 | return image_meta |
1006 | except exception.Duplicate: |
1007 | msg = "An image with identifier %s already exists"\ |
1008 | @@ -212,21 +212,27 @@ |
1009 | "Content-Type must be application/octet-stream") |
1010 | |
1011 | image_store = req.headers.get( |
1012 | - 'x-image-meta-store', FLAGS.default_store) |
1013 | + 'x-image-meta-store', self.options['default_store']) |
1014 | |
1015 | store = self.get_store_or_400(req, image_store) |
1016 | |
1017 | image_meta['status'] = 'saving' |
1018 | - registry.update_image_metadata(image_meta['id'], image_meta) |
1019 | + registry.update_image_metadata(self.options, |
1020 | + image_meta['id'], |
1021 | + image_meta) |
1022 | |
1023 | try: |
1024 | - location, size = store.add(image_meta['id'], req.body_file) |
1025 | + location, size = store.add(image_meta['id'], |
1026 | + req.body_file, |
1027 | + self.options) |
1028 | # If size returned from store is different from size |
1029 | # already stored in registry, update the registry with |
1030 | # the new size of the image |
1031 | if image_meta.get('size', 0) != size: |
1032 | image_meta['size'] = size |
1033 | - registry.update_image_metadata(image_meta['id'], image_meta) |
1034 | + registry.update_image_metadata(self.options, |
1035 | + image_meta['id'], |
1036 | + image_meta) |
1037 | return location |
1038 | except exception.Duplicate, e: |
1039 | logging.error("Error adding image to store: %s", str(e)) |
1040 | @@ -243,7 +249,9 @@ |
1041 | """ |
1042 | image_meta['location'] = location |
1043 | image_meta['status'] = 'active' |
1044 | - registry.update_image_metadata(image_meta['id'], image_meta) |
1045 | + registry.update_image_metadata(self.options, |
1046 | + image_meta['id'], |
1047 | + image_meta) |
1048 | |
1049 | def _kill(self, req, image_meta): |
1050 | """ |
1051 | @@ -253,7 +261,9 @@ |
1052 | :param image_meta: Mapping of metadata about image |
1053 | """ |
1054 | image_meta['status'] = 'killed' |
1055 | - registry.update_image_metadata(image_meta['id'], image_meta) |
1056 | + registry.update_image_metadata(self.options, |
1057 | + image_meta['id'], |
1058 | + image_meta) |
1059 | |
1060 | def _safe_kill(self, req, image_meta): |
1061 | """ |
1062 | @@ -351,7 +361,9 @@ |
1063 | raise HTTPConflict("Cannot upload to an unqueued image") |
1064 | |
1065 | new_image_meta = util.get_image_meta_from_headers(req) |
1066 | - image_meta = registry.update_image_metadata(id, new_image_meta) |
1067 | + image_meta = registry.update_image_metadata(self.options, |
1068 | + id, |
1069 | + new_image_meta) |
1070 | |
1071 | if has_body: |
1072 | self._upload_and_activate(req, image_meta) |
1073 | @@ -374,7 +386,7 @@ |
1074 | |
1075 | delete_from_backend(image['location']) |
1076 | |
1077 | - registry.delete_image_metadata(id) |
1078 | + registry.delete_image_metadata(self.options, id) |
1079 | |
1080 | def get_image_meta_or_404(self, request, id): |
1081 | """ |
1082 | @@ -387,7 +399,7 @@ |
1083 | :raises HTTPNotFound if image does not exist |
1084 | """ |
1085 | try: |
1086 | - return registry.get_image_metadata(id) |
1087 | + return registry.get_image_metadata(self.options, id) |
1088 | except exception.NotFound: |
1089 | raise HTTPNotFound(body='Image not found', |
1090 | request=request, |
1091 | @@ -417,11 +429,13 @@ |
1092 | |
1093 | """WSGI entry point for all Glance API requests.""" |
1094 | |
1095 | - def __init__(self): |
1096 | + def __init__(self, options): |
1097 | + self.options = options |
1098 | mapper = routes.Mapper() |
1099 | - mapper.resource("image", "images", controller=Controller(), |
1100 | + controller = Controller(options) |
1101 | + mapper.resource("image", "images", controller=controller, |
1102 | collection={'detail': 'GET'}) |
1103 | - mapper.connect("/", controller=Controller(), action="index") |
1104 | - mapper.connect("/images/{id}", controller=Controller(), action="meta", |
1105 | + mapper.connect("/", controller=controller, action="index") |
1106 | + mapper.connect("/images/{id}", controller=controller, action="meta", |
1107 | conditions=dict(method=["HEAD"])) |
1108 | super(API, self).__init__(mapper) |
1109 | |
1110 | === modified file 'glance/store/__init__.py' |
1111 | --- glance/store/__init__.py 2011-01-27 04:19:13 +0000 |
1112 | +++ glance/store/__init__.py 2011-01-31 19:42:44 +0000 |
1113 | @@ -139,3 +139,26 @@ |
1114 | authurl = "https://%s" % '/'.join(path_parts) |
1115 | |
1116 | return user, key, authurl, container, obj |
1117 | + |
1118 | + |
1119 | +def add_options(parser): |
1120 | + """ |
1121 | + Adds any configuration options that each store might |
1122 | + have. |
1123 | + |
1124 | + :param parser: An optparse.OptionParser object |
1125 | + :retval None |
1126 | + """ |
1127 | + # TODO(jaypipes): Remove these imports... |
1128 | + from glance.store.http import HTTPBackend |
1129 | + from glance.store.s3 import S3Backend |
1130 | + from glance.store.swift import SwiftBackend |
1131 | + from glance.store.filesystem import FilesystemBackend |
1132 | + |
1133 | + backend_classes = [FilesystemBackend, |
1134 | + HTTPBackend, |
1135 | + SwiftBackend, |
1136 | + S3Backend] |
1137 | + for b in backend_classes: |
1138 | + if hasattr(b, 'add_options'): |
1139 | + b.add_options(parser) |
1140 | |
1141 | === modified file 'glance/store/filesystem.py' |
1142 | --- glance/store/filesystem.py 2011-01-27 04:19:13 +0000 |
1143 | +++ glance/store/filesystem.py 2011-01-31 19:42:44 +0000 |
1144 | @@ -23,17 +23,9 @@ |
1145 | import urlparse |
1146 | |
1147 | from glance.common import exception |
1148 | -from glance.common import flags |
1149 | import glance.store |
1150 | |
1151 | |
1152 | -flags.DEFINE_string('filesystem_store_datadir', '/var/lib/glance/images/', |
1153 | - 'Location to write image data. ' |
1154 | - 'Default: /var/lib/glance/images/') |
1155 | - |
1156 | -FLAGS = flags.FLAGS |
1157 | - |
1158 | - |
1159 | class ChunkedFile(object): |
1160 | |
1161 | """ |
1162 | @@ -102,21 +94,22 @@ |
1163 | raise exception.NotFound("Image file %s does not exist" % fn) |
1164 | |
1165 | @classmethod |
1166 | - def add(cls, id, data): |
1167 | + def add(cls, id, data, options): |
1168 | """ |
1169 | Stores image data to disk and returns a location that the image was |
1170 | written to. By default, the backend writes the image data to a file |
1171 | `/<DATADIR>/<ID>`, where <DATADIR> is the value of |
1172 | - FLAGS.filesystem_store_datadir and <ID> is the supplied image ID. |
1173 | + options['filesystem_store_datadir'] and <ID> is the supplied image ID. |
1174 | |
1175 | :param id: The opaque image identifier |
1176 | :param data: The image data to write, as a file-like object |
1177 | + :param options: Conf mapping |
1178 | |
1179 | :retval Tuple with (location, size) |
1180 | The location that was written, with file:// scheme prepended |
1181 | and the size in bytes of the data written |
1182 | """ |
1183 | - datadir = FLAGS.filesystem_store_datadir |
1184 | + datadir = options['filesystem_store_datadir'] |
1185 | |
1186 | if not os.path.exists(datadir): |
1187 | os.makedirs(datadir) |
1188 | @@ -137,3 +130,18 @@ |
1189 | f.write(buf) |
1190 | |
1191 | return ('file://%s' % filepath, bytes_written) |
1192 | + |
1193 | + @classmethod |
1194 | + def add_options(cls, parser): |
1195 | + """ |
1196 | + Adds specific configuration options for this store |
1197 | + |
1198 | + :param parser: An optparse.OptionParser object |
1199 | + :retval None |
1200 | + """ |
1201 | + |
1202 | + parser.add_option('--filesystem-store-datadir', metavar="DIR", |
1203 | + default="/var/lib/glance/images/", |
1204 | + help="Location to write image data. This directory " |
1205 | + "should be writeable by the user that runs the " |
1206 | + "glance-api program. Default: %default") |
1207 | |
1208 | === modified file 'tests/stubs.py' |
1209 | --- tests/stubs.py 2011-01-28 19:39:19 +0000 |
1210 | +++ tests/stubs.py 2011-01-31 19:42:44 +0000 |
1211 | @@ -239,7 +239,8 @@ |
1212 | self.req.body = body |
1213 | |
1214 | def getresponse(self): |
1215 | - res = self.req.get_response(rserver.API()) |
1216 | + res = self.req.get_response(rserver.API({'sql_connection': 'sqlite://', |
1217 | + 'verbose': True})) |
1218 | |
1219 | # httplib.Response has a read() method...fake it out |
1220 | def fake_reader(): |
1221 | @@ -284,7 +285,11 @@ |
1222 | self.req.body = body |
1223 | |
1224 | def getresponse(self): |
1225 | - res = self.req.get_response(server.API()) |
1226 | + res = self.req.get_response(server.API({'verbose': True, |
1227 | + 'registry_host': '0.0.0.0', |
1228 | + 'registry_port': '9191', |
1229 | + 'default_store': 'file', |
1230 | + 'filesystem_store_datadir': FAKE_FILESYSTEM_ROOTDIR})) |
1231 | |
1232 | # httplib.Response has a read() method...fake it out |
1233 | def fake_reader(): |
1234 | |
1235 | === modified file 'tests/unit/test_api.py' |
1236 | --- tests/unit/test_api.py 2011-01-26 17:26:54 +0000 |
1237 | +++ tests/unit/test_api.py 2011-01-31 19:42:44 +0000 |
1238 | @@ -23,12 +23,9 @@ |
1239 | import webob |
1240 | |
1241 | from glance import server |
1242 | -from glance.common import flags |
1243 | from glance.registry import server as rserver |
1244 | from tests import stubs |
1245 | |
1246 | -FLAGS = flags.FLAGS |
1247 | - |
1248 | |
1249 | class TestRegistryAPI(unittest.TestCase): |
1250 | def setUp(self): |
1251 | @@ -37,6 +34,7 @@ |
1252 | stubs.stub_out_registry_and_store_server(self.stubs) |
1253 | stubs.stub_out_registry_db_image_api(self.stubs) |
1254 | stubs.stub_out_filesystem_backend() |
1255 | + self.api = rserver.API({}) |
1256 | |
1257 | def tearDown(self): |
1258 | """Clear the test environment""" |
1259 | @@ -51,7 +49,7 @@ |
1260 | fixture = {'id': 2, |
1261 | 'name': 'fake image #2'} |
1262 | req = webob.Request.blank('/') |
1263 | - res = req.get_response(rserver.API()) |
1264 | + res = req.get_response(self.api) |
1265 | res_dict = json.loads(res.body) |
1266 | self.assertEquals(res.status_int, 200) |
1267 | |
1268 | @@ -69,7 +67,7 @@ |
1269 | fixture = {'id': 2, |
1270 | 'name': 'fake image #2'} |
1271 | req = webob.Request.blank('/images') |
1272 | - res = req.get_response(rserver.API()) |
1273 | + res = req.get_response(self.api) |
1274 | res_dict = json.loads(res.body) |
1275 | self.assertEquals(res.status_int, 200) |
1276 | |
1277 | @@ -91,7 +89,7 @@ |
1278 | 'status': 'active'} |
1279 | |
1280 | req = webob.Request.blank('/images/detail') |
1281 | - res = req.get_response(rserver.API()) |
1282 | + res = req.get_response(self.api) |
1283 | res_dict = json.loads(res.body) |
1284 | self.assertEquals(res.status_int, 200) |
1285 | |
1286 | @@ -112,7 +110,7 @@ |
1287 | req.method = 'POST' |
1288 | req.body = json.dumps(dict(image=fixture)) |
1289 | |
1290 | - res = req.get_response(rserver.API()) |
1291 | + res = req.get_response(self.api) |
1292 | |
1293 | self.assertEquals(res.status_int, 200) |
1294 | |
1295 | @@ -140,10 +138,7 @@ |
1296 | req.method = 'POST' |
1297 | req.body = json.dumps(dict(image=fixture)) |
1298 | |
1299 | - # TODO(jaypipes): Port Nova's Fault infrastructure |
1300 | - # over to Glance to support exception catching into |
1301 | - # standard HTTP errors. |
1302 | - res = req.get_response(rserver.API()) |
1303 | + res = req.get_response(self.api) |
1304 | self.assertEquals(res.status_int, webob.exc.HTTPBadRequest.code) |
1305 | |
1306 | def test_update_image(self): |
1307 | @@ -156,7 +151,7 @@ |
1308 | req.method = 'PUT' |
1309 | req.body = json.dumps(dict(image=fixture)) |
1310 | |
1311 | - res = req.get_response(rserver.API()) |
1312 | + res = req.get_response(self.api) |
1313 | |
1314 | self.assertEquals(res.status_int, 200) |
1315 | |
1316 | @@ -179,10 +174,7 @@ |
1317 | req.method = 'PUT' |
1318 | req.body = json.dumps(dict(image=fixture)) |
1319 | |
1320 | - # TODO(jaypipes): Port Nova's Fault infrastructure |
1321 | - # over to Glance to support exception catching into |
1322 | - # standard HTTP errors. |
1323 | - res = req.get_response(rserver.API()) |
1324 | + res = req.get_response(self.api) |
1325 | self.assertEquals(res.status_int, |
1326 | webob.exc.HTTPNotFound.code) |
1327 | |
1328 | @@ -191,7 +183,7 @@ |
1329 | |
1330 | # Grab the original number of images |
1331 | req = webob.Request.blank('/images') |
1332 | - res = req.get_response(rserver.API()) |
1333 | + res = req.get_response(self.api) |
1334 | res_dict = json.loads(res.body) |
1335 | self.assertEquals(res.status_int, 200) |
1336 | |
1337 | @@ -202,13 +194,13 @@ |
1338 | |
1339 | req.method = 'DELETE' |
1340 | |
1341 | - res = req.get_response(rserver.API()) |
1342 | + res = req.get_response(self.api) |
1343 | |
1344 | self.assertEquals(res.status_int, 200) |
1345 | |
1346 | # Verify one less image |
1347 | req = webob.Request.blank('/images') |
1348 | - res = req.get_response(rserver.API()) |
1349 | + res = req.get_response(self.api) |
1350 | res_dict = json.loads(res.body) |
1351 | self.assertEquals(res.status_int, 200) |
1352 | |
1353 | @@ -223,10 +215,7 @@ |
1354 | |
1355 | req.method = 'DELETE' |
1356 | |
1357 | - # TODO(jaypipes): Port Nova's Fault infrastructure |
1358 | - # over to Glance to support exception catching into |
1359 | - # standard HTTP errors. |
1360 | - res = req.get_response(rserver.API()) |
1361 | + res = req.get_response(self.api) |
1362 | self.assertEquals(res.status_int, |
1363 | webob.exc.HTTPNotFound.code) |
1364 | |
1365 | @@ -238,12 +227,14 @@ |
1366 | stubs.stub_out_registry_and_store_server(self.stubs) |
1367 | stubs.stub_out_registry_db_image_api(self.stubs) |
1368 | stubs.stub_out_filesystem_backend() |
1369 | - self.orig_filesystem_store_datadir = FLAGS.filesystem_store_datadir |
1370 | - FLAGS.filesystem_store_datadir = stubs.FAKE_FILESYSTEM_ROOTDIR |
1371 | + self.api = server.API({'registry_host': '0.0.0.0', |
1372 | + 'registry_port': '9191', |
1373 | + 'sql_connection': 'sqlite://', |
1374 | + 'default_store': 'file', |
1375 | + 'filesystem_store_datadir': stubs.FAKE_FILESYSTEM_ROOTDIR}) |
1376 | |
1377 | def tearDown(self): |
1378 | """Clear the test environment""" |
1379 | - FLAGS.filesystem_store_datadir = self.orig_filesystem_store_datadir |
1380 | stubs.clean_out_fake_filesystem_backend() |
1381 | self.stubs.UnsetAll() |
1382 | |
1383 | @@ -256,7 +247,7 @@ |
1384 | req.method = 'POST' |
1385 | for k, v in fixture_headers.iteritems(): |
1386 | req.headers[k] = v |
1387 | - res = req.get_response(server.API()) |
1388 | + res = req.get_response(self.api) |
1389 | self.assertEquals(res.status_int, httplib.OK) |
1390 | |
1391 | res_body = json.loads(res.body)['image'] |
1392 | @@ -274,7 +265,7 @@ |
1393 | |
1394 | req.headers['Content-Type'] = 'application/octet-stream' |
1395 | req.body = "chunk00000remainder" |
1396 | - res = req.get_response(server.API()) |
1397 | + res = req.get_response(self.api) |
1398 | self.assertEquals(res.status_int, webob.exc.HTTPBadRequest.code) |
1399 | |
1400 | def test_add_image_basic_file_store(self): |
1401 | @@ -289,7 +280,7 @@ |
1402 | |
1403 | req.headers['Content-Type'] = 'application/octet-stream' |
1404 | req.body = "chunk00000remainder" |
1405 | - res = req.get_response(server.API()) |
1406 | + res = req.get_response(self.api) |
1407 | self.assertEquals(res.status_int, 200) |
1408 | |
1409 | res_body = json.loads(res.body)['image'] |
1410 | @@ -302,7 +293,7 @@ |
1411 | 'x-image-meta-name': 'fake image #2'} |
1412 | req = webob.Request.blank("/images/2") |
1413 | req.method = 'HEAD' |
1414 | - res = req.get_response(server.API()) |
1415 | + res = req.get_response(self.api) |
1416 | self.assertEquals(res.status_int, 200) |
1417 | |
1418 | for key, value in expected_headers.iteritems(): |
1419 | @@ -310,28 +301,28 @@ |
1420 | |
1421 | def test_show_image_basic(self): |
1422 | req = webob.Request.blank("/images/2") |
1423 | - res = req.get_response(server.API()) |
1424 | + res = req.get_response(self.api) |
1425 | self.assertEqual('chunk00000remainder', res.body) |
1426 | |
1427 | def test_show_non_exists_image(self): |
1428 | req = webob.Request.blank("/images/42") |
1429 | - res = req.get_response(server.API()) |
1430 | + res = req.get_response(self.api) |
1431 | self.assertEquals(res.status_int, webob.exc.HTTPNotFound.code) |
1432 | |
1433 | def test_delete_image(self): |
1434 | req = webob.Request.blank("/images/2") |
1435 | req.method = 'DELETE' |
1436 | - res = req.get_response(server.API()) |
1437 | + res = req.get_response(self.api) |
1438 | self.assertEquals(res.status_int, 200) |
1439 | |
1440 | req = webob.Request.blank("/images/2") |
1441 | req.method = 'GET' |
1442 | - res = req.get_response(server.API()) |
1443 | + res = req.get_response(self.api) |
1444 | self.assertEquals(res.status_int, webob.exc.HTTPNotFound.code, |
1445 | res.body) |
1446 | |
1447 | def test_delete_non_exists_image(self): |
1448 | req = webob.Request.blank("/images/42") |
1449 | req.method = 'DELETE' |
1450 | - res = req.get_response(server.API()) |
1451 | + res = req.get_response(self.api) |
1452 | self.assertEquals(res.status_int, webob.exc.HTTPNotFound.code) |
1453 | |
1454 | === modified file 'tests/unit/test_clients.py' |
1455 | --- tests/unit/test_clients.py 2011-01-26 20:47:01 +0000 |
1456 | +++ tests/unit/test_clients.py 2011-01-31 19:42:44 +0000 |
1457 | @@ -25,12 +25,9 @@ |
1458 | |
1459 | from glance import client |
1460 | from glance.registry import client as rclient |
1461 | -from glance.common import flags |
1462 | from glance.common import exception |
1463 | from tests import stubs |
1464 | |
1465 | -FLAGS = flags.FLAGS |
1466 | - |
1467 | |
1468 | class TestBadClients(unittest.TestCase): |
1469 | |
1470 | @@ -273,13 +270,10 @@ |
1471 | stubs.stub_out_registry_db_image_api(self.stubs) |
1472 | stubs.stub_out_registry_and_store_server(self.stubs) |
1473 | stubs.stub_out_filesystem_backend() |
1474 | - self.orig_filesystem_store_datadir = FLAGS.filesystem_store_datadir |
1475 | - FLAGS.filesystem_store_datadir = stubs.FAKE_FILESYSTEM_ROOTDIR |
1476 | self.client = client.Client("0.0.0.0") |
1477 | |
1478 | def tearDown(self): |
1479 | """Clear the test environment""" |
1480 | - FLAGS.filesystem_store_datadir = self.orig_filesystem_store_datadir |
1481 | stubs.clean_out_fake_filesystem_backend() |
1482 | self.stubs.UnsetAll() |
1483 | |
1484 | |
1485 | === removed file 'tests/unit/test_registry_api.py' |
1486 | --- tests/unit/test_registry_api.py 2011-01-26 17:26:54 +0000 |
1487 | +++ tests/unit/test_registry_api.py 1970-01-01 00:00:00 +0000 |
1488 | @@ -1,227 +0,0 @@ |
1489 | -# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
1490 | - |
1491 | -# Copyright 2010-2011 OpenStack, LLC |
1492 | -# All Rights Reserved. |
1493 | -# |
1494 | -# Licensed under the Apache License, Version 2.0 (the "License"); you may |
1495 | -# not use this file except in compliance with the License. You may obtain |
1496 | -# a copy of the License at |
1497 | -# |
1498 | -# http://www.apache.org/licenses/LICENSE-2.0 |
1499 | -# |
1500 | -# Unless required by applicable law or agreed to in writing, software |
1501 | -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
1502 | -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
1503 | -# License for the specific language governing permissions and limitations |
1504 | -# under the License. |
1505 | - |
1506 | -import json |
1507 | -import stubout |
1508 | -import unittest |
1509 | -import webob |
1510 | - |
1511 | -from glance.common import exception |
1512 | -from glance.registry import server |
1513 | -from tests import stubs |
1514 | - |
1515 | - |
1516 | -class TestImageController(unittest.TestCase): |
1517 | - def setUp(self): |
1518 | - """Establish a clean test environment""" |
1519 | - self.stubs = stubout.StubOutForTesting() |
1520 | - stubs.stub_out_registry_db_image_api(self.stubs) |
1521 | - |
1522 | - def tearDown(self): |
1523 | - """Clear the test environment""" |
1524 | - self.stubs.UnsetAll() |
1525 | - |
1526 | - def test_get_root(self): |
1527 | - """Tests that the root registry API returns "index", |
1528 | - which is a list of public images |
1529 | - |
1530 | - """ |
1531 | - fixture = {'id': 2, |
1532 | - 'name': 'fake image #2'} |
1533 | - req = webob.Request.blank('/') |
1534 | - res = req.get_response(server.API()) |
1535 | - res_dict = json.loads(res.body) |
1536 | - self.assertEquals(res.status_int, 200) |
1537 | - |
1538 | - images = res_dict['images'] |
1539 | - self.assertEquals(len(images), 1) |
1540 | - |
1541 | - for k, v in fixture.iteritems(): |
1542 | - self.assertEquals(v, images[0][k]) |
1543 | - |
1544 | - def test_get_index(self): |
1545 | - """Tests that the /images registry API returns list of |
1546 | - public images |
1547 | - |
1548 | - """ |
1549 | - fixture = {'id': 2, |
1550 | - 'name': 'fake image #2'} |
1551 | - req = webob.Request.blank('/images') |
1552 | - res = req.get_response(server.API()) |
1553 | - res_dict = json.loads(res.body) |
1554 | - self.assertEquals(res.status_int, 200) |
1555 | - |
1556 | - images = res_dict['images'] |
1557 | - self.assertEquals(len(images), 1) |
1558 | - |
1559 | - for k, v in fixture.iteritems(): |
1560 | - self.assertEquals(v, images[0][k]) |
1561 | - |
1562 | - def test_get_details(self): |
1563 | - """Tests that the /images/detail registry API returns |
1564 | - a mapping containing a list of detailed image information |
1565 | - |
1566 | - """ |
1567 | - fixture = {'id': 2, |
1568 | - 'name': 'fake image #2', |
1569 | - 'is_public': True, |
1570 | - 'type': 'kernel', |
1571 | - 'status': 'active', |
1572 | - } |
1573 | - req = webob.Request.blank('/images/detail') |
1574 | - res = req.get_response(server.API()) |
1575 | - res_dict = json.loads(res.body) |
1576 | - self.assertEquals(res.status_int, 200) |
1577 | - |
1578 | - images = res_dict['images'] |
1579 | - self.assertEquals(len(images), 1) |
1580 | - |
1581 | - for k, v in fixture.iteritems(): |
1582 | - self.assertEquals(v, images[0][k]) |
1583 | - |
1584 | - def test_create_image(self): |
1585 | - """Tests that the /images POST registry API creates the image""" |
1586 | - fixture = {'name': 'fake public image', |
1587 | - 'is_public': True, |
1588 | - 'type': 'kernel', |
1589 | - } |
1590 | - |
1591 | - req = webob.Request.blank('/images') |
1592 | - |
1593 | - req.method = 'POST' |
1594 | - req.body = json.dumps(dict(image=fixture)) |
1595 | - |
1596 | - res = req.get_response(server.API()) |
1597 | - |
1598 | - self.assertEquals(res.status_int, 200) |
1599 | - |
1600 | - res_dict = json.loads(res.body) |
1601 | - |
1602 | - for k, v in fixture.iteritems(): |
1603 | - self.assertEquals(v, res_dict['image'][k]) |
1604 | - |
1605 | - # Test ID auto-assigned properly |
1606 | - self.assertEquals(3, res_dict['image']['id']) |
1607 | - |
1608 | - # Test status was updated properly |
1609 | - self.assertEquals('active', res_dict['image']['status']) |
1610 | - |
1611 | - def test_create_image_with_bad_status(self): |
1612 | - """Tests proper exception is raised if a bad status is set""" |
1613 | - fixture = {'id': 3, |
1614 | - 'name': 'fake public image', |
1615 | - 'is_public': True, |
1616 | - 'type': 'kernel', |
1617 | - 'status': 'bad status', |
1618 | - } |
1619 | - |
1620 | - req = webob.Request.blank('/images') |
1621 | - |
1622 | - req.method = 'POST' |
1623 | - req.body = json.dumps(dict(image=fixture)) |
1624 | - |
1625 | - # TODO(jaypipes): Port Nova's Fault infrastructure |
1626 | - # over to Glance to support exception catching into |
1627 | - # standard HTTP errors. |
1628 | - res = req.get_response(server.API()) |
1629 | - self.assertEquals(res.status_int, webob.exc.HTTPBadRequest.code) |
1630 | - |
1631 | - def test_update_image(self): |
1632 | - """Tests that the /images PUT registry API updates the image""" |
1633 | - fixture = {'name': 'fake public image #2', |
1634 | - 'type': 'ramdisk', |
1635 | - } |
1636 | - |
1637 | - req = webob.Request.blank('/images/2') |
1638 | - |
1639 | - req.method = 'PUT' |
1640 | - req.body = json.dumps(dict(image=fixture)) |
1641 | - |
1642 | - res = req.get_response(server.API()) |
1643 | - |
1644 | - self.assertEquals(res.status_int, 200) |
1645 | - |
1646 | - res_dict = json.loads(res.body) |
1647 | - |
1648 | - for k, v in fixture.iteritems(): |
1649 | - self.assertEquals(v, res_dict['image'][k]) |
1650 | - |
1651 | - def test_update_image_not_existing(self): |
1652 | - """Tests proper exception is raised if attempt to update non-existing |
1653 | - image""" |
1654 | - fixture = {'id': 3, |
1655 | - 'name': 'fake public image', |
1656 | - 'is_public': True, |
1657 | - 'type': 'kernel', |
1658 | - 'status': 'bad status', |
1659 | - } |
1660 | - |
1661 | - req = webob.Request.blank('/images/3') |
1662 | - |
1663 | - req.method = 'PUT' |
1664 | - req.body = json.dumps(dict(image=fixture)) |
1665 | - |
1666 | - # TODO(jaypipes): Port Nova's Fault infrastructure |
1667 | - # over to Glance to support exception catching into |
1668 | - # standard HTTP errors. |
1669 | - res = req.get_response(server.API()) |
1670 | - self.assertEquals(res.status_int, |
1671 | - webob.exc.HTTPNotFound.code) |
1672 | - |
1673 | - def test_delete_image(self): |
1674 | - """Tests that the /images DELETE registry API deletes the image""" |
1675 | - |
1676 | - # Grab the original number of images |
1677 | - req = webob.Request.blank('/images') |
1678 | - res = req.get_response(server.API()) |
1679 | - res_dict = json.loads(res.body) |
1680 | - self.assertEquals(res.status_int, 200) |
1681 | - |
1682 | - orig_num_images = len(res_dict['images']) |
1683 | - |
1684 | - # Delete image #2 |
1685 | - req = webob.Request.blank('/images/2') |
1686 | - |
1687 | - req.method = 'DELETE' |
1688 | - |
1689 | - res = req.get_response(server.API()) |
1690 | - |
1691 | - self.assertEquals(res.status_int, 200) |
1692 | - |
1693 | - # Verify one less image |
1694 | - req = webob.Request.blank('/images') |
1695 | - res = req.get_response(server.API()) |
1696 | - res_dict = json.loads(res.body) |
1697 | - self.assertEquals(res.status_int, 200) |
1698 | - |
1699 | - new_num_images = len(res_dict['images']) |
1700 | - self.assertEquals(new_num_images, orig_num_images - 1) |
1701 | - |
1702 | - def test_delete_image_not_existing(self): |
1703 | - """Tests proper exception is raised if attempt to delete non-existing |
1704 | - image""" |
1705 | - |
1706 | - req = webob.Request.blank('/images/3') |
1707 | - |
1708 | - req.method = 'DELETE' |
1709 | - |
1710 | - # TODO(jaypipes): Port Nova's Fault infrastructure |
1711 | - # over to Glance to support exception catching into |
1712 | - # standard HTTP errors. |
1713 | - res = req.get_response(server.API()) |
1714 | - self.assertEquals(res.status_int, |
1715 | - webob.exc.HTTPNotFound.code) |
1716 | |
1717 | === modified file 'tools/pip-requires' |
1718 | --- tools/pip-requires 2011-01-26 20:44:36 +0000 |
1719 | +++ tools/pip-requires 2011-01-31 19:42:44 +0000 |
1720 | @@ -6,7 +6,6 @@ |
1721 | eventlet>=0.9.12 |
1722 | lockfile==0.8 |
1723 | python-daemon==1.5.5 |
1724 | -python-gflags>=1.3 |
1725 | routes |
1726 | webob |
1727 | wsgiref |
I recommend you removing optparse and use argparse. optparse is marked deprecated as of python 2.7.