Merge lp:~rconradharris/glance/registry_db_migration into lp:~glance-coresec/glance/cactus-trunk
- registry_db_migration
- Merge into cactus-trunk
Status: | Merged |
---|---|
Approved by: | Jay Pipes |
Approved revision: | 74 |
Merged at revision: | 64 |
Proposed branch: | lp:~rconradharris/glance/registry_db_migration |
Merge into: | lp:~glance-coresec/glance/cactus-trunk |
Diff against target: |
916 lines (+642/-20) 22 files modified
MANIFEST.in (+1/-0) bin/glance-manage (+130/-0) bin/glance-upload (+3/-0) doc/source/conf.py (+2/-0) doc/source/glanceapi.rst (+6/-6) doc/source/man/glanceapi.rst (+1/-1) doc/source/man/glancemanage.py (+54/-0) doc/source/man/glanceregistry.rst (+1/-1) glance/common/config.py (+15/-7) glance/common/exception.py (+8/-0) glance/registry/db/migrate_repo/README (+4/-0) glance/registry/db/migrate_repo/__init__.py (+1/-0) glance/registry/db/migrate_repo/manage.py (+3/-0) glance/registry/db/migrate_repo/migrate.cfg (+20/-0) glance/registry/db/migrate_repo/schema.py (+100/-0) glance/registry/db/migrate_repo/versions/001_add_images_table.py (+55/-0) glance/registry/db/migrate_repo/versions/002_add_image_properties_table.py (+63/-0) glance/registry/db/migrate_repo/versions/__init__.py (+1/-0) glance/registry/db/migration.py (+117/-0) glance/registry/db/models.py (+8/-5) tests/unit/test_migrations.py (+48/-0) tools/pip-requires (+1/-0) |
To merge this branch: | bzr merge lp:~rconradharris/glance/registry_db_migration |
Related bugs: | |
Related blueprints: |
Complete DB Migration of the Registry database
(Essential)
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Matt Dietz (community) | Approve | ||
Jay Pipes (community) | Approve | ||
Review via email: mp+48406@code.launchpad.net |
This proposal supersedes a proposal from 2011-02-02.
Commit message
Description of the change
Adds sqlalchemy migrations.
Also adds a glance-manage utility for managing migrations.
Potentially glance-manage (and glance-upload) could be merged into glance-admin when that lands.
Jay Pipes (jaypipes) wrote : Posted in a previous version of this proposal | # |
Rick Harris (rconradharris) wrote : Posted in a previous version of this proposal | # |
I can't think of any meaningful tests as this is just a CLI wrapper around sqlalchemy-migrate (which itself has been tested quite a bit ;-).
Jay Pipes (jaypipes) wrote : Posted in a previous version of this proposal | # |
> I can't think of any meaningful tests as this is just a CLI wrapper around
> sqlalchemy-migrate (which itself has been tested quite a bit ;-).
What about something as simple as this?
Rick Harris (rconradharris) wrote : Posted in a previous version of this proposal | # |
Honestly, doesn't strike me as super-useful; but that said, it's better to err on the side of caution, so I'll defer to your judgement here.
Test-mode engaged...
Jay Pipes (jaypipes) wrote : | # |
OK, so I had an additional thought about where the database migration code should go. Since the database migration is only for the reference implementation Glance registry server (bin/glance-
Note that I think glance-upload can and should be merged into the glance-admin program, just not these specific registry database commands.
Jay Pipes (jaypipes) wrote : | # |
OK, let's get this merged...we can always address the binary naming stuff later... I want to push on to api-image-format blueprint..
Matt Dietz (cerberus) wrote : | # |
Seems alright to me.
Preview Diff
1 | === modified file 'MANIFEST.in' | |||
2 | --- MANIFEST.in 2011-01-20 10:13:46 +0000 | |||
3 | +++ MANIFEST.in 2011-02-03 00:03:10 +0000 | |||
4 | @@ -6,5 +6,6 @@ | |||
5 | 6 | include tests/test_data.py | 6 | include tests/test_data.py |
6 | 7 | include tests/utils.py | 7 | include tests/utils.py |
7 | 8 | include run_tests.py | 8 | include run_tests.py |
8 | 9 | include glance/registry/db/migrate_repo/migrate.cfg | ||
9 | 9 | graft doc | 10 | graft doc |
10 | 10 | graft tools | 11 | graft tools |
11 | 11 | 12 | ||
12 | === added file 'bin/glance-manage' | |||
13 | --- bin/glance-manage 1970-01-01 00:00:00 +0000 | |||
14 | +++ bin/glance-manage 2011-02-03 00:03:10 +0000 | |||
15 | @@ -0,0 +1,130 @@ | |||
16 | 1 | #!/usr/bin/env python | ||
17 | 2 | # vim: tabstop=4 shiftwidth=4 softtabstop=4 | ||
18 | 3 | |||
19 | 4 | # Copyright 2010 United States Government as represented by the | ||
20 | 5 | # Administrator of the National Aeronautics and Space Administration. | ||
21 | 6 | # Copyright 2011 OpenStack LLC. | ||
22 | 7 | # All Rights Reserved. | ||
23 | 8 | # | ||
24 | 9 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
25 | 10 | # not use this file except in compliance with the License. You may obtain | ||
26 | 11 | # a copy of the License at | ||
27 | 12 | # | ||
28 | 13 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
29 | 14 | # | ||
30 | 15 | # Unless required by applicable law or agreed to in writing, software | ||
31 | 16 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
32 | 17 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
33 | 18 | # License for the specific language governing permissions and limitations | ||
34 | 19 | # under the License. | ||
35 | 20 | |||
36 | 21 | """ | ||
37 | 22 | Glance Management Utility | ||
38 | 23 | """ | ||
39 | 24 | |||
40 | 25 | # FIXME(sirp): When we have glance-admin we can consider merging this into it | ||
41 | 26 | # Perhaps for consistency with Nova, we would then rename glance-admin -> | ||
42 | 27 | # glance-manage (or the other way around) | ||
43 | 28 | |||
44 | 29 | import optparse | ||
45 | 30 | import os | ||
46 | 31 | import sys | ||
47 | 32 | |||
48 | 33 | ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | ||
49 | 34 | |||
50 | 35 | sys.path.append(ROOT_DIR) | ||
51 | 36 | |||
52 | 37 | from glance import version as glance_version | ||
53 | 38 | from glance.common import config | ||
54 | 39 | from glance.common import exception | ||
55 | 40 | import glance.registry.db | ||
56 | 41 | import glance.registry.db.migration | ||
57 | 42 | |||
58 | 43 | |||
59 | 44 | def create_options(parser): | ||
60 | 45 | """ | ||
61 | 46 | Sets up the CLI and config-file options that may be | ||
62 | 47 | parsed and program commands. | ||
63 | 48 | |||
64 | 49 | :param parser: The option parser | ||
65 | 50 | """ | ||
66 | 51 | glance.registry.db.add_options(parser) | ||
67 | 52 | config.add_common_options(parser) | ||
68 | 53 | config.add_log_options('glance-manage', parser) | ||
69 | 54 | |||
70 | 55 | |||
71 | 56 | def do_db_version(options, args): | ||
72 | 57 | """Print database's current migration level""" | ||
73 | 58 | print glance.registry.db.migration.db_version(options) | ||
74 | 59 | |||
75 | 60 | |||
76 | 61 | def do_upgrade(options, args): | ||
77 | 62 | """Upgrade the database's migration level""" | ||
78 | 63 | try: | ||
79 | 64 | db_version = args[1] | ||
80 | 65 | except IndexError: | ||
81 | 66 | db_version = None | ||
82 | 67 | |||
83 | 68 | glance.registry.db.migration.upgrade(options, version=db_version) | ||
84 | 69 | |||
85 | 70 | |||
86 | 71 | def do_downgrade(options, args): | ||
87 | 72 | """Downgrade the database's migration level""" | ||
88 | 73 | try: | ||
89 | 74 | db_version = args[1] | ||
90 | 75 | except IndexError: | ||
91 | 76 | raise exception.MissingArgumentError( | ||
92 | 77 | "downgrade requires a version argument") | ||
93 | 78 | |||
94 | 79 | glance.registry.db.migration.downgrade(options, version=db_version) | ||
95 | 80 | |||
96 | 81 | |||
97 | 82 | def do_version_control(options, args): | ||
98 | 83 | """Place a database under migration control""" | ||
99 | 84 | glance.registry.db.migration.version_control(options) | ||
100 | 85 | |||
101 | 86 | |||
102 | 87 | def do_db_sync(options, args): | ||
103 | 88 | """Place a database under migration control and upgrade""" | ||
104 | 89 | try: | ||
105 | 90 | db_version = args[1] | ||
106 | 91 | except IndexError: | ||
107 | 92 | db_version = None | ||
108 | 93 | glance.registry.db.migration.db_sync(options, version=db_version) | ||
109 | 94 | |||
110 | 95 | |||
111 | 96 | def dispatch_cmd(options, args): | ||
112 | 97 | """Search for do_* cmd in this module and then run it""" | ||
113 | 98 | cmd = args[0] | ||
114 | 99 | try: | ||
115 | 100 | cmd_func = globals()['do_%s' % cmd] | ||
116 | 101 | except KeyError: | ||
117 | 102 | sys.exit("ERROR: unrecognized command '%s'" % cmd) | ||
118 | 103 | |||
119 | 104 | try: | ||
120 | 105 | cmd_func(options, args) | ||
121 | 106 | except exception.Error, e: | ||
122 | 107 | sys.exit("ERROR: %s" % e) | ||
123 | 108 | |||
124 | 109 | |||
125 | 110 | def main(): | ||
126 | 111 | version = '%%prog %s' % glance_version.version_string() | ||
127 | 112 | usage = "%prog [options] <cmd>" | ||
128 | 113 | oparser = optparse.OptionParser(usage, version=version) | ||
129 | 114 | create_options(oparser) | ||
130 | 115 | (options, args) = config.parse_options(oparser) | ||
131 | 116 | |||
132 | 117 | try: | ||
133 | 118 | config.setup_logging(options) | ||
134 | 119 | except RuntimeError, e: | ||
135 | 120 | sys.exit("ERROR: %s" % e) | ||
136 | 121 | |||
137 | 122 | if not args: | ||
138 | 123 | oparser.print_usage() | ||
139 | 124 | sys.exit(1) | ||
140 | 125 | |||
141 | 126 | dispatch_cmd(options, args) | ||
142 | 127 | |||
143 | 128 | |||
144 | 129 | if __name__ == '__main__': | ||
145 | 130 | main() | ||
146 | 0 | 131 | ||
147 | === modified file 'bin/glance-upload' | |||
148 | --- bin/glance-upload 2011-01-23 17:18:48 +0000 | |||
149 | +++ bin/glance-upload 2011-02-03 00:03:10 +0000 | |||
150 | @@ -33,6 +33,9 @@ | |||
151 | 33 | <filename> <name> | 33 | <filename> <name> |
152 | 34 | 34 | ||
153 | 35 | """ | 35 | """ |
154 | 36 | |||
155 | 37 | # FIXME(sirp): This can be merged into glance-admin when that becomes | ||
156 | 38 | # available | ||
157 | 36 | import argparse | 39 | import argparse |
158 | 37 | import pprint | 40 | import pprint |
159 | 38 | import sys | 41 | import sys |
160 | 39 | 42 | ||
161 | === modified file 'doc/source/conf.py' | |||
162 | --- doc/source/conf.py 2011-01-27 20:01:02 +0000 | |||
163 | +++ doc/source/conf.py 2011-02-03 00:03:10 +0000 | |||
164 | @@ -129,6 +129,8 @@ | |||
165 | 129 | ('man/glanceapi', 'glance-api', u'Glance API Server', | 129 | ('man/glanceapi', 'glance-api', u'Glance API Server', |
166 | 130 | [u'OpenStack'], 1), | 130 | [u'OpenStack'], 1), |
167 | 131 | ('man/glanceregistry', 'glance-registry', u'Glance Registry Server', | 131 | ('man/glanceregistry', 'glance-registry', u'Glance Registry Server', |
168 | 132 | [u'OpenStack'], 1), | ||
169 | 133 | ('man/glancemanage', 'glance-manage', u'Glance Management Utility', | ||
170 | 132 | [u'OpenStack'], 1) | 134 | [u'OpenStack'], 1) |
171 | 133 | ] | 135 | ] |
172 | 134 | 136 | ||
173 | 135 | 137 | ||
174 | === modified file 'doc/source/glanceapi.rst' | |||
175 | --- doc/source/glanceapi.rst 2011-01-26 17:26:54 +0000 | |||
176 | +++ doc/source/glanceapi.rst 2011-02-03 00:03:10 +0000 | |||
177 | @@ -24,7 +24,7 @@ | |||
178 | 24 | Server*. | 24 | Server*. |
179 | 25 | 25 | ||
180 | 26 | Assume there is a Glance API server running at the URL | 26 | Assume there is a Glance API server running at the URL |
182 | 27 | ``http://glance.example.com``. | 27 | ``http://glance.example.com``. |
183 | 28 | 28 | ||
184 | 29 | Let's walk through how a user might request information from this server. | 29 | Let's walk through how a user might request information from this server. |
185 | 30 | 30 | ||
186 | @@ -116,7 +116,7 @@ | |||
187 | 116 | x-image-meta-store swift | 116 | x-image-meta-store swift |
188 | 117 | x-image-meta-created_at 2010-02-03 09:34:01 | 117 | x-image-meta-created_at 2010-02-03 09:34:01 |
189 | 118 | x-image-meta-updated_at 2010-02-03 09:34:01 | 118 | x-image-meta-updated_at 2010-02-03 09:34:01 |
191 | 119 | x-image-meta-deleted_at | 119 | x-image-meta-deleted_at |
192 | 120 | x-image-meta-status available | 120 | x-image-meta-status available |
193 | 121 | x-image-meta-is_public True | 121 | x-image-meta-is_public True |
194 | 122 | x-image-meta-property-distro Ubuntu 10.04 LTS | 122 | x-image-meta-property-distro Ubuntu 10.04 LTS |
195 | @@ -126,7 +126,7 @@ | |||
196 | 126 | All timestamps returned are in UTC | 126 | All timestamps returned are in UTC |
197 | 127 | 127 | ||
198 | 128 | The `x-image-meta-updated_at` timestamp is the timestamp when an | 128 | The `x-image-meta-updated_at` timestamp is the timestamp when an |
200 | 129 | image's metadata was last updated, not its image data, as all | 129 | image's metadata was last updated, not its image data, as all |
201 | 130 | image data is immutable once stored in Glance | 130 | image data is immutable once stored in Glance |
202 | 131 | 131 | ||
203 | 132 | There may be multiple headers that begin with the prefix | 132 | There may be multiple headers that begin with the prefix |
204 | @@ -165,7 +165,7 @@ | |||
205 | 165 | x-image-meta-store swift | 165 | x-image-meta-store swift |
206 | 166 | x-image-meta-created_at 2010-02-03 09:34:01 | 166 | x-image-meta-created_at 2010-02-03 09:34:01 |
207 | 167 | x-image-meta-updated_at 2010-02-03 09:34:01 | 167 | x-image-meta-updated_at 2010-02-03 09:34:01 |
209 | 168 | x-image-meta-deleted_at | 168 | x-image-meta-deleted_at |
210 | 169 | x-image-meta-status available | 169 | x-image-meta-status available |
211 | 170 | x-image-meta-is_public True | 170 | x-image-meta-is_public True |
212 | 171 | x-image-meta-property-distro Ubuntu 10.04 LTS | 171 | x-image-meta-property-distro Ubuntu 10.04 LTS |
213 | @@ -175,7 +175,7 @@ | |||
214 | 175 | All timestamps returned are in UTC | 175 | All timestamps returned are in UTC |
215 | 176 | 176 | ||
216 | 177 | The `x-image-meta-updated_at` timestamp is the timestamp when an | 177 | The `x-image-meta-updated_at` timestamp is the timestamp when an |
218 | 178 | image's metadata was last updated, not its image data, as all | 178 | image's metadata was last updated, not its image data, as all |
219 | 179 | image data is immutable once stored in Glance | 179 | image data is immutable once stored in Glance |
220 | 180 | 180 | ||
221 | 181 | There may be multiple headers that begin with the prefix | 181 | There may be multiple headers that begin with the prefix |
222 | @@ -232,7 +232,7 @@ | |||
223 | 232 | 232 | ||
224 | 233 | * ``x-image-meta-id`` | 233 | * ``x-image-meta-id`` |
225 | 234 | 234 | ||
227 | 235 | This header is optional. | 235 | This header is optional. |
228 | 236 | 236 | ||
229 | 237 | When present, Glance will use the supplied identifier for the image. | 237 | When present, Glance will use the supplied identifier for the image. |
230 | 238 | If the identifier already exists in that Glance node, then a | 238 | If the identifier already exists in that Glance node, then a |
231 | 239 | 239 | ||
232 | === modified file 'doc/source/man/glanceapi.rst' | |||
233 | --- doc/source/man/glanceapi.rst 2011-01-19 21:38:39 +0000 | |||
234 | +++ doc/source/man/glanceapi.rst 2011-02-03 00:03:10 +0000 | |||
235 | @@ -48,7 +48,7 @@ | |||
236 | 48 | running ``glance-api`` | 48 | running ``glance-api`` |
237 | 49 | 49 | ||
238 | 50 | FILES | 50 | FILES |
240 | 51 | ======== | 51 | ===== |
241 | 52 | 52 | ||
242 | 53 | None | 53 | None |
243 | 54 | 54 | ||
244 | 55 | 55 | ||
245 | === added file 'doc/source/man/glancemanage.py' | |||
246 | --- doc/source/man/glancemanage.py 1970-01-01 00:00:00 +0000 | |||
247 | +++ doc/source/man/glancemanage.py 2011-02-03 00:03:10 +0000 | |||
248 | @@ -0,0 +1,54 @@ | |||
249 | 1 | ============= | ||
250 | 2 | glance-manage | ||
251 | 3 | ============= | ||
252 | 4 | |||
253 | 5 | ------------------------- | ||
254 | 6 | Glance Management Utility | ||
255 | 7 | ------------------------- | ||
256 | 8 | |||
257 | 9 | :Author: glance@lists.launchpad.net | ||
258 | 10 | :Date: 2010-11-16 | ||
259 | 11 | :Copyright: OpenStack LLC | ||
260 | 12 | :Version: 0.1.2 | ||
261 | 13 | :Manual section: 1 | ||
262 | 14 | :Manual group: cloud computing | ||
263 | 15 | |||
264 | 16 | SYNOPSIS | ||
265 | 17 | ======== | ||
266 | 18 | |||
267 | 19 | glance-manage [options] | ||
268 | 20 | |||
269 | 21 | DESCRIPTION | ||
270 | 22 | =========== | ||
271 | 23 | |||
272 | 24 | glance-manage is a utility for managing and configuring a Glance installation. | ||
273 | 25 | One important use of glance-manage is to setup the database. To do this run:: | ||
274 | 26 | |||
275 | 27 | glance-manage db_sync | ||
276 | 28 | |||
277 | 29 | OPTIONS | ||
278 | 30 | ======= | ||
279 | 31 | |||
280 | 32 | **General options** | ||
281 | 33 | |||
282 | 34 | **-v, --verbose** | ||
283 | 35 | Print more verbose output | ||
284 | 36 | |||
285 | 37 | **--sql_connection=CONN_STRING** | ||
286 | 38 | A proper SQLAlchemy connection string as described | ||
287 | 39 | `here <http://www.sqlalchemy.org/docs/05/reference/sqlalchemy/connections.html?highlight=engine#sqlalchemy.create_engine>`_ | ||
288 | 40 | |||
289 | 41 | FILES | ||
290 | 42 | ===== | ||
291 | 43 | |||
292 | 44 | None | ||
293 | 45 | |||
294 | 46 | SEE ALSO | ||
295 | 47 | ======== | ||
296 | 48 | |||
297 | 49 | * `OpenStack Glance <http://glance.openstack.org>`__ | ||
298 | 50 | |||
299 | 51 | BUGS | ||
300 | 52 | ==== | ||
301 | 53 | |||
302 | 54 | * Glance is sourced in Launchpad so you can view current bugs at `OpenStack Glance <http://glance.openstack.org>`__ | ||
303 | 0 | 55 | ||
304 | === modified file 'doc/source/man/glanceregistry.rst' | |||
305 | --- doc/source/man/glanceregistry.rst 2011-01-19 21:38:39 +0000 | |||
306 | +++ doc/source/man/glanceregistry.rst 2011-02-03 00:03:10 +0000 | |||
307 | @@ -43,7 +43,7 @@ | |||
308 | 43 | `here <http://www.sqlalchemy.org/docs/05/reference/sqlalchemy/connections.html?highlight=engine#sqlalchemy.create_engine>`_ | 43 | `here <http://www.sqlalchemy.org/docs/05/reference/sqlalchemy/connections.html?highlight=engine#sqlalchemy.create_engine>`_ |
309 | 44 | 44 | ||
310 | 45 | FILES | 45 | FILES |
312 | 46 | ======== | 46 | ===== |
313 | 47 | 47 | ||
314 | 48 | None | 48 | None |
315 | 49 | 49 | ||
316 | 50 | 50 | ||
317 | === modified file 'glance/common/config.py' | |||
318 | --- glance/common/config.py 2011-02-02 16:40:57 +0000 | |||
319 | +++ glance/common/config.py 2011-02-03 00:03:10 +0000 | |||
320 | @@ -27,9 +27,11 @@ | |||
321 | 27 | import os | 27 | import os |
322 | 28 | import sys | 28 | import sys |
323 | 29 | 29 | ||
324 | 30 | import glance.common.exception as exception | ||
325 | 30 | 31 | ||
326 | 31 | DEFAULT_LOG_FORMAT = "%(asctime)s %(levelname)8s [%(name)s] %(message)s" | 32 | DEFAULT_LOG_FORMAT = "%(asctime)s %(levelname)8s [%(name)s] %(message)s" |
327 | 32 | DEFAULT_LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S" | 33 | DEFAULT_LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S" |
328 | 34 | DEFAULT_LOG_HANDLER = 'stream' | ||
329 | 33 | LOGGING_HANDLER_CHOICES = ['syslog', 'file', 'stream'] | 35 | LOGGING_HANDLER_CHOICES = ['syslog', 'file', 'stream'] |
330 | 34 | 36 | ||
331 | 35 | 37 | ||
332 | @@ -132,7 +134,8 @@ | |||
333 | 132 | "any other logging options specified. Please see " | 134 | "any other logging options specified. Please see " |
334 | 133 | "the Python logging module documentation for " | 135 | "the Python logging module documentation for " |
335 | 134 | "details on logging configuration files.") | 136 | "details on logging configuration files.") |
337 | 135 | group.add_option('--log-handler', default='stream', metavar="HANDLER", | 137 | group.add_option('--log-handler', default=DEFAULT_LOG_HANDLER, |
338 | 138 | metavar="HANDLER", | ||
339 | 136 | choices=LOGGING_HANDLER_CHOICES, | 139 | choices=LOGGING_HANDLER_CHOICES, |
340 | 137 | help="What logging handler to use? " | 140 | help="What logging handler to use? " |
341 | 138 | "Default: %default") | 141 | "Default: %default") |
342 | @@ -159,7 +162,7 @@ | |||
343 | 159 | :param options: Mapping of typed option key/values | 162 | :param options: Mapping of typed option key/values |
344 | 160 | """ | 163 | """ |
345 | 161 | 164 | ||
347 | 162 | if options['log_config']: | 165 | if options.get('log_config', None): |
348 | 163 | # Use a logging configuration file for all settings... | 166 | # Use a logging configuration file for all settings... |
349 | 164 | if os.path.exists(options['log_config']): | 167 | if os.path.exists(options['log_config']): |
350 | 165 | logging.config.fileConfig(options['log_config']) | 168 | logging.config.fileConfig(options['log_config']) |
351 | @@ -179,14 +182,16 @@ | |||
352 | 179 | root_logger.setLevel(logging.WARNING) | 182 | root_logger.setLevel(logging.WARNING) |
353 | 180 | 183 | ||
354 | 181 | # Set log configuration from options... | 184 | # Set log configuration from options... |
357 | 182 | formatter = logging.Formatter(options['log_format'], | 185 | log_format = options.get('log_format', DEFAULT_LOG_FORMAT) |
358 | 183 | options['log_date_format']) | 186 | log_date_format = options.get('log_date_format', DEFAULT_LOG_DATE_FORMAT) |
359 | 187 | formatter = logging.Formatter(log_format, log_date_format) | ||
360 | 184 | 188 | ||
362 | 185 | if options['log_handler'] == 'syslog': | 189 | log_handler = options.get('log_handler', DEFAULT_LOG_HANDLER) |
363 | 190 | if log_handler == 'syslog': | ||
364 | 186 | syslog = logging.handlers.SysLogHandler(address='/dev/log') | 191 | syslog = logging.handlers.SysLogHandler(address='/dev/log') |
365 | 187 | syslog.setFormatter(formatter) | 192 | syslog.setFormatter(formatter) |
366 | 188 | root_logger.addHandler(syslog) | 193 | root_logger.addHandler(syslog) |
368 | 189 | elif options['log_handler'] == 'file': | 194 | elif log_handler == 'file': |
369 | 190 | logfile = options['log_file'] | 195 | logfile = options['log_file'] |
370 | 191 | logdir = options['log_dir'] | 196 | logdir = options['log_dir'] |
371 | 192 | if logdir: | 197 | if logdir: |
372 | @@ -195,10 +200,13 @@ | |||
373 | 195 | logfile.setFormatter(formatter) | 200 | logfile.setFormatter(formatter) |
374 | 196 | logfile.setFormatter(formatter) | 201 | logfile.setFormatter(formatter) |
375 | 197 | root_logger.addHandler(logfile) | 202 | root_logger.addHandler(logfile) |
377 | 198 | else: | 203 | elif log_handler == 'stream': |
378 | 199 | handler = logging.StreamHandler(sys.stdout) | 204 | handler = logging.StreamHandler(sys.stdout) |
379 | 200 | handler.setFormatter(formatter) | 205 | handler.setFormatter(formatter) |
380 | 201 | root_logger.addHandler(handler) | 206 | root_logger.addHandler(handler) |
381 | 207 | else: | ||
382 | 208 | raise exception.BadInputError( | ||
383 | 209 | "unrecognized log handler '%(log_handler)s'" % locals()) | ||
384 | 202 | 210 | ||
385 | 203 | # Log the options used when starting if we're in debug mode... | 211 | # Log the options used when starting if we're in debug mode... |
386 | 204 | if debug: | 212 | if debug: |
387 | 205 | 213 | ||
388 | === modified file 'glance/common/exception.py' | |||
389 | --- glance/common/exception.py 2011-01-26 17:26:54 +0000 | |||
390 | +++ glance/common/exception.py 2011-02-03 00:03:10 +0000 | |||
391 | @@ -75,6 +75,14 @@ | |||
392 | 75 | pass | 75 | pass |
393 | 76 | 76 | ||
394 | 77 | 77 | ||
395 | 78 | class MissingArgumentError(Error): | ||
396 | 79 | pass | ||
397 | 80 | |||
398 | 81 | |||
399 | 82 | class DatabaseMigrationError(Error): | ||
400 | 83 | pass | ||
401 | 84 | |||
402 | 85 | |||
403 | 78 | def wrap_exception(f): | 86 | def wrap_exception(f): |
404 | 79 | def _wrap(*args, **kw): | 87 | def _wrap(*args, **kw): |
405 | 80 | try: | 88 | try: |
406 | 81 | 89 | ||
407 | === added directory 'glance/registry/db/migrate_repo' | |||
408 | === added file 'glance/registry/db/migrate_repo/README' | |||
409 | --- glance/registry/db/migrate_repo/README 1970-01-01 00:00:00 +0000 | |||
410 | +++ glance/registry/db/migrate_repo/README 2011-02-03 00:03:10 +0000 | |||
411 | @@ -0,0 +1,4 @@ | |||
412 | 1 | This is a database migration repository. | ||
413 | 2 | |||
414 | 3 | More information at | ||
415 | 4 | http://code.google.com/p/sqlalchemy-migrate/ | ||
416 | 0 | 5 | ||
417 | === added file 'glance/registry/db/migrate_repo/__init__.py' | |||
418 | --- glance/registry/db/migrate_repo/__init__.py 1970-01-01 00:00:00 +0000 | |||
419 | +++ glance/registry/db/migrate_repo/__init__.py 2011-02-03 00:03:10 +0000 | |||
420 | @@ -0,0 +1,1 @@ | |||
421 | 1 | # template repository default module | ||
422 | 0 | 2 | ||
423 | === added file 'glance/registry/db/migrate_repo/manage.py' | |||
424 | --- glance/registry/db/migrate_repo/manage.py 1970-01-01 00:00:00 +0000 | |||
425 | +++ glance/registry/db/migrate_repo/manage.py 2011-02-03 00:03:10 +0000 | |||
426 | @@ -0,0 +1,3 @@ | |||
427 | 1 | #!/usr/bin/env python | ||
428 | 2 | from migrate.versioning.shell import main | ||
429 | 3 | main(debug='False', repository='.') | ||
430 | 0 | 4 | ||
431 | === added file 'glance/registry/db/migrate_repo/migrate.cfg' | |||
432 | --- glance/registry/db/migrate_repo/migrate.cfg 1970-01-01 00:00:00 +0000 | |||
433 | +++ glance/registry/db/migrate_repo/migrate.cfg 2011-02-03 00:03:10 +0000 | |||
434 | @@ -0,0 +1,20 @@ | |||
435 | 1 | [db_settings] | ||
436 | 2 | # Used to identify which repository this database is versioned under. | ||
437 | 3 | # You can use the name of your project. | ||
438 | 4 | repository_id=Glance Migrations | ||
439 | 5 | |||
440 | 6 | # The name of the database table used to track the schema version. | ||
441 | 7 | # This name shouldn't already be used by your project. | ||
442 | 8 | # If this is changed once a database is under version control, you'll need to | ||
443 | 9 | # change the table name in each database too. | ||
444 | 10 | version_table=migrate_version | ||
445 | 11 | |||
446 | 12 | # When committing a change script, Migrate will attempt to generate the | ||
447 | 13 | # sql for all supported databases; normally, if one of them fails - probably | ||
448 | 14 | # because you don't have that database installed - it is ignored and the | ||
449 | 15 | # commit continues, perhaps ending successfully. | ||
450 | 16 | # Databases in this list MUST compile successfully during a commit, or the | ||
451 | 17 | # entire commit will fail. List the databases your application will actually | ||
452 | 18 | # be using to ensure your updates to that database work properly. | ||
453 | 19 | # This must be a list; example: ['postgres','sqlite'] | ||
454 | 20 | required_dbs=[] | ||
455 | 0 | 21 | ||
456 | === added file 'glance/registry/db/migrate_repo/schema.py' | |||
457 | --- glance/registry/db/migrate_repo/schema.py 1970-01-01 00:00:00 +0000 | |||
458 | +++ glance/registry/db/migrate_repo/schema.py 2011-02-03 00:03:10 +0000 | |||
459 | @@ -0,0 +1,100 @@ | |||
460 | 1 | # vim: tabstop=4 shiftwidth=4 softtabstop=4 | ||
461 | 2 | |||
462 | 3 | # Copyright 2011 OpenStack LLC. | ||
463 | 4 | # All Rights Reserved. | ||
464 | 5 | # | ||
465 | 6 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
466 | 7 | # not use this file except in compliance with the License. You may obtain | ||
467 | 8 | # a copy of the License at | ||
468 | 9 | # | ||
469 | 10 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
470 | 11 | # | ||
471 | 12 | # Unless required by applicable law or agreed to in writing, software | ||
472 | 13 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
473 | 14 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
474 | 15 | # License for the specific language governing permissions and limitations | ||
475 | 16 | # under the License. | ||
476 | 17 | |||
477 | 18 | """ | ||
478 | 19 | Various conveniences used for migration scripts | ||
479 | 20 | """ | ||
480 | 21 | |||
481 | 22 | import logging | ||
482 | 23 | |||
483 | 24 | import sqlalchemy.types | ||
484 | 25 | from sqlalchemy.schema import MetaData | ||
485 | 26 | |||
486 | 27 | |||
487 | 28 | logger = logging.getLogger('glance.registry.db.migrate_repo.schema') | ||
488 | 29 | |||
489 | 30 | |||
490 | 31 | String = lambda length: sqlalchemy.types.String( | ||
491 | 32 | length=length, convert_unicode=False, assert_unicode=None, | ||
492 | 33 | unicode_error=None, _warn_on_bytestring=False) | ||
493 | 34 | |||
494 | 35 | |||
495 | 36 | Text = lambda: sqlalchemy.types.Text( | ||
496 | 37 | length=None, convert_unicode=False, assert_unicode=None, | ||
497 | 38 | unicode_error=None, _warn_on_bytestring=False) | ||
498 | 39 | |||
499 | 40 | |||
500 | 41 | Boolean = lambda: sqlalchemy.types.Boolean(create_constraint=True, name=None) | ||
501 | 42 | |||
502 | 43 | |||
503 | 44 | DateTime = lambda: sqlalchemy.types.DateTime(timezone=False) | ||
504 | 45 | |||
505 | 46 | |||
506 | 47 | Integer = lambda: sqlalchemy.types.Integer() | ||
507 | 48 | |||
508 | 49 | |||
509 | 50 | def from_migration_import(module_name, fromlist): | ||
510 | 51 | """Import a migration file and return the module | ||
511 | 52 | |||
512 | 53 | :param module_name: name of migration module to import from | ||
513 | 54 | (ex: 001_add_images_table) | ||
514 | 55 | :param fromlist: list of items to import (ex: define_images_table) | ||
515 | 56 | :retval: module object | ||
516 | 57 | |||
517 | 58 | This bit of ugliness warrants an explanation: | ||
518 | 59 | |||
519 | 60 | As you're writing migrations, you'll frequently want to refer to | ||
520 | 61 | tables defined in previous migrations. | ||
521 | 62 | |||
522 | 63 | In the interest of not repeating yourself, you need a way of importing | ||
523 | 64 | that table into a 'future' migration. | ||
524 | 65 | |||
525 | 66 | However, tables are bound to metadata, so what you need to import is | ||
526 | 67 | really a table factory, which you can late-bind to your current | ||
527 | 68 | metadata object. | ||
528 | 69 | |||
529 | 70 | Moreover, migrations begin with a number (001...), which means they | ||
530 | 71 | aren't valid Python identifiers. This means we can't perform a | ||
531 | 72 | 'normal' import on them (the Python lexer will 'splode). Instead, we | ||
532 | 73 | need to use __import__ magic to bring the table-factory into our | ||
533 | 74 | namespace. | ||
534 | 75 | |||
535 | 76 | Example Usage: | ||
536 | 77 | |||
537 | 78 | (define_images_table,) = from_migration_import( | ||
538 | 79 | '001_add_images_table', ['define_images_table']) | ||
539 | 80 | |||
540 | 81 | images = define_images_table(meta) | ||
541 | 82 | |||
542 | 83 | # Refer to images table | ||
543 | 84 | |||
544 | 85 | """ | ||
545 | 86 | module_path = 'glance.registry.db.migrate_repo.versions.%s' % module_name | ||
546 | 87 | module = __import__(module_path, globals(), locals(), fromlist, -1) | ||
547 | 88 | return [getattr(module, item) for item in fromlist] | ||
548 | 89 | |||
549 | 90 | |||
550 | 91 | def create_tables(tables): | ||
551 | 92 | for table in tables: | ||
552 | 93 | logger.info("creating table %(table)s" % locals()) | ||
553 | 94 | table.create() | ||
554 | 95 | |||
555 | 96 | |||
556 | 97 | def drop_tables(tables): | ||
557 | 98 | for table in tables: | ||
558 | 99 | logger.info("dropping table %(table)s" % locals()) | ||
559 | 100 | table.drop() | ||
560 | 0 | 101 | ||
561 | === added directory 'glance/registry/db/migrate_repo/versions' | |||
562 | === added file 'glance/registry/db/migrate_repo/versions/001_add_images_table.py' | |||
563 | --- glance/registry/db/migrate_repo/versions/001_add_images_table.py 1970-01-01 00:00:00 +0000 | |||
564 | +++ glance/registry/db/migrate_repo/versions/001_add_images_table.py 2011-02-03 00:03:10 +0000 | |||
565 | @@ -0,0 +1,55 @@ | |||
566 | 1 | # vim: tabstop=4 shiftwidth=4 softtabstop=4 | ||
567 | 2 | |||
568 | 3 | # Copyright 2011 OpenStack LLC. | ||
569 | 4 | # All Rights Reserved. | ||
570 | 5 | # | ||
571 | 6 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
572 | 7 | # not use this file except in compliance with the License. You may obtain | ||
573 | 8 | # a copy of the License at | ||
574 | 9 | # | ||
575 | 10 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
576 | 11 | # | ||
577 | 12 | # Unless required by applicable law or agreed to in writing, software | ||
578 | 13 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
579 | 14 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
580 | 15 | # License for the specific language governing permissions and limitations | ||
581 | 16 | # under the License. | ||
582 | 17 | |||
583 | 18 | from sqlalchemy.schema import (Column, MetaData, Table) | ||
584 | 19 | |||
585 | 20 | from glance.registry.db.migrate_repo.schema import ( | ||
586 | 21 | Boolean, DateTime, Integer, String, Text, create_tables, drop_tables) | ||
587 | 22 | |||
588 | 23 | |||
589 | 24 | def define_images_table(meta): | ||
590 | 25 | images = Table('images', meta, | ||
591 | 26 | Column('id', Integer(), primary_key=True, nullable=False), | ||
592 | 27 | Column('name', String(255)), | ||
593 | 28 | Column('type', String(30)), | ||
594 | 29 | Column('size', Integer()), | ||
595 | 30 | Column('status', String(30), nullable=False), | ||
596 | 31 | Column('is_public', Boolean(), nullable=False, default=False, | ||
597 | 32 | index=True), | ||
598 | 33 | Column('location', Text()), | ||
599 | 34 | Column('created_at', DateTime(), nullable=False), | ||
600 | 35 | Column('updated_at', DateTime()), | ||
601 | 36 | Column('deleted_at', DateTime()), | ||
602 | 37 | Column('deleted', Boolean(), nullable=False, default=False, | ||
603 | 38 | index=True), | ||
604 | 39 | mysql_engine='InnoDB') | ||
605 | 40 | |||
606 | 41 | return images | ||
607 | 42 | |||
608 | 43 | |||
609 | 44 | def upgrade(migrate_engine): | ||
610 | 45 | meta = MetaData() | ||
611 | 46 | meta.bind = migrate_engine | ||
612 | 47 | tables = [define_images_table(meta)] | ||
613 | 48 | create_tables(tables) | ||
614 | 49 | |||
615 | 50 | |||
616 | 51 | def downgrade(migrate_engine): | ||
617 | 52 | meta = MetaData() | ||
618 | 53 | meta.bind = migrate_engine | ||
619 | 54 | tables = [define_images_table(meta)] | ||
620 | 55 | drop_tables(tables) | ||
621 | 0 | 56 | ||
622 | === added file 'glance/registry/db/migrate_repo/versions/002_add_image_properties_table.py' | |||
623 | --- glance/registry/db/migrate_repo/versions/002_add_image_properties_table.py 1970-01-01 00:00:00 +0000 | |||
624 | +++ glance/registry/db/migrate_repo/versions/002_add_image_properties_table.py 2011-02-03 00:03:10 +0000 | |||
625 | @@ -0,0 +1,63 @@ | |||
626 | 1 | # vim: tabstop=4 shiftwidth=4 softtabstop=4 | ||
627 | 2 | |||
628 | 3 | # Copyright 2011 OpenStack LLC. | ||
629 | 4 | # All Rights Reserved. | ||
630 | 5 | # | ||
631 | 6 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
632 | 7 | # not use this file except in compliance with the License. You may obtain | ||
633 | 8 | # a copy of the License at | ||
634 | 9 | # | ||
635 | 10 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
636 | 11 | # | ||
637 | 12 | # Unless required by applicable law or agreed to in writing, software | ||
638 | 13 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
639 | 14 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
640 | 15 | # License for the specific language governing permissions and limitations | ||
641 | 16 | # under the License. | ||
642 | 17 | |||
643 | 18 | from sqlalchemy.schema import ( | ||
644 | 19 | Column, ForeignKey, Index, MetaData, Table, UniqueConstraint) | ||
645 | 20 | |||
646 | 21 | from glance.registry.db.migrate_repo.schema import ( | ||
647 | 22 | Boolean, DateTime, Integer, String, Text, create_tables, drop_tables, | ||
648 | 23 | from_migration_import) | ||
649 | 24 | |||
650 | 25 | |||
651 | 26 | def define_image_properties_table(meta): | ||
652 | 27 | (define_images_table,) = from_migration_import( | ||
653 | 28 | '001_add_images_table', ['define_images_table']) | ||
654 | 29 | |||
655 | 30 | images = define_images_table(meta) | ||
656 | 31 | |||
657 | 32 | image_properties = Table('image_properties', meta, | ||
658 | 33 | Column('id', Integer(), primary_key=True, nullable=False), | ||
659 | 34 | Column('image_id', Integer(), ForeignKey('images.id'), nullable=False, | ||
660 | 35 | index=True), | ||
661 | 36 | Column('key', String(255), nullable=False), | ||
662 | 37 | Column('value', Text()), | ||
663 | 38 | Column('created_at', DateTime(), nullable=False), | ||
664 | 39 | Column('updated_at', DateTime()), | ||
665 | 40 | Column('deleted_at', DateTime()), | ||
666 | 41 | Column('deleted', Boolean(), nullable=False, default=False, | ||
667 | 42 | index=True), | ||
668 | 43 | UniqueConstraint('image_id', 'key'), | ||
669 | 44 | mysql_engine='InnoDB') | ||
670 | 45 | |||
671 | 46 | Index('ix_image_properties_image_id_key', image_properties.c.image_id, | ||
672 | 47 | image_properties.c.key) | ||
673 | 48 | |||
674 | 49 | return image_properties | ||
675 | 50 | |||
676 | 51 | |||
677 | 52 | def upgrade(migrate_engine): | ||
678 | 53 | meta = MetaData() | ||
679 | 54 | meta.bind = migrate_engine | ||
680 | 55 | tables = [define_image_properties_table(meta)] | ||
681 | 56 | create_tables(tables) | ||
682 | 57 | |||
683 | 58 | |||
684 | 59 | def downgrade(migrate_engine): | ||
685 | 60 | meta = MetaData() | ||
686 | 61 | meta.bind = migrate_engine | ||
687 | 62 | tables = [define_image_properties_table(meta)] | ||
688 | 63 | drop_tables(tables) | ||
689 | 0 | 64 | ||
690 | === added file 'glance/registry/db/migrate_repo/versions/__init__.py' | |||
691 | --- glance/registry/db/migrate_repo/versions/__init__.py 1970-01-01 00:00:00 +0000 | |||
692 | +++ glance/registry/db/migrate_repo/versions/__init__.py 2011-02-03 00:03:10 +0000 | |||
693 | @@ -0,0 +1,1 @@ | |||
694 | 1 | # template repository default versions module | ||
695 | 0 | 2 | ||
696 | === added file 'glance/registry/db/migration.py' | |||
697 | --- glance/registry/db/migration.py 1970-01-01 00:00:00 +0000 | |||
698 | +++ glance/registry/db/migration.py 2011-02-03 00:03:10 +0000 | |||
699 | @@ -0,0 +1,117 @@ | |||
700 | 1 | # vim: tabstop=4 shiftwidth=4 softtabstop=4 | ||
701 | 2 | |||
702 | 3 | # Copyright 2011 OpenStack LLC. | ||
703 | 4 | # All Rights Reserved. | ||
704 | 5 | # | ||
705 | 6 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
706 | 7 | # not use this file except in compliance with the License. You may obtain | ||
707 | 8 | # a copy of the License at | ||
708 | 9 | # | ||
709 | 10 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
710 | 11 | # | ||
711 | 12 | # Unless required by applicable law or agreed to in writing, software | ||
712 | 13 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
713 | 14 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
714 | 15 | # License for the specific language governing permissions and limitations | ||
715 | 16 | # under the License. | ||
716 | 17 | |||
717 | 18 | import logging | ||
718 | 19 | import os | ||
719 | 20 | |||
720 | 21 | from migrate.versioning import api as versioning_api | ||
721 | 22 | from migrate.versioning import exceptions as versioning_exceptions | ||
722 | 23 | |||
723 | 24 | from glance.common import exception | ||
724 | 25 | |||
725 | 26 | |||
726 | 27 | def db_version(options): | ||
727 | 28 | """Return the database's current migration number | ||
728 | 29 | |||
729 | 30 | :param options: options dict | ||
730 | 31 | :retval version number | ||
731 | 32 | """ | ||
732 | 33 | repo_path = _find_migrate_repo() | ||
733 | 34 | sql_connection = options['sql_connection'] | ||
734 | 35 | try: | ||
735 | 36 | return versioning_api.db_version(sql_connection, repo_path) | ||
736 | 37 | except versioning_exceptions.DatabaseNotControlledError, e: | ||
737 | 38 | msg = ("database '%(sql_connection)s' is not under migration control" | ||
738 | 39 | % locals()) | ||
739 | 40 | raise exception.DatabaseMigrationError(msg) | ||
740 | 41 | |||
741 | 42 | |||
742 | 43 | def upgrade(options, version=None): | ||
743 | 44 | """Upgrade the database's current migration level | ||
744 | 45 | |||
745 | 46 | :param options: options dict | ||
746 | 47 | :param version: version to upgrade (defaults to latest) | ||
747 | 48 | :retval version number | ||
748 | 49 | """ | ||
749 | 50 | db_version(options) # Ensure db is under migration control | ||
750 | 51 | repo_path = _find_migrate_repo() | ||
751 | 52 | sql_connection = options['sql_connection'] | ||
752 | 53 | version_str = version or 'latest' | ||
753 | 54 | logging.info("Upgrading %(sql_connection)s to version %(version_str)s" % | ||
754 | 55 | locals()) | ||
755 | 56 | return versioning_api.upgrade(sql_connection, repo_path, version) | ||
756 | 57 | |||
757 | 58 | |||
758 | 59 | def downgrade(options, version): | ||
759 | 60 | """Downgrade the database's current migration level | ||
760 | 61 | |||
761 | 62 | :param options: options dict | ||
762 | 63 | :param version: version to downgrade to | ||
763 | 64 | :retval version number | ||
764 | 65 | """ | ||
765 | 66 | db_version(options) # Ensure db is under migration control | ||
766 | 67 | repo_path = _find_migrate_repo() | ||
767 | 68 | sql_connection = options['sql_connection'] | ||
768 | 69 | logging.info("Downgrading %(sql_connection)s to version %(version)s" % | ||
769 | 70 | locals()) | ||
770 | 71 | return versioning_api.downgrade(sql_connection, repo_path, version) | ||
771 | 72 | |||
772 | 73 | |||
773 | 74 | def version_control(options): | ||
774 | 75 | """Place a database under migration control | ||
775 | 76 | |||
776 | 77 | :param options: options dict | ||
777 | 78 | """ | ||
778 | 79 | sql_connection = options['sql_connection'] | ||
779 | 80 | try: | ||
780 | 81 | _version_control(options) | ||
781 | 82 | except versioning_exceptions.DatabaseAlreadyControlledError, e: | ||
782 | 83 | msg = ("database '%(sql_connection)s' is already under migration " | ||
783 | 84 | "control" % locals()) | ||
784 | 85 | raise exception.DatabaseMigrationError(msg) | ||
785 | 86 | |||
786 | 87 | |||
787 | 88 | def _version_control(options): | ||
788 | 89 | """Place a database under migration control | ||
789 | 90 | |||
790 | 91 | :param options: options dict | ||
791 | 92 | """ | ||
792 | 93 | repo_path = _find_migrate_repo() | ||
793 | 94 | sql_connection = options['sql_connection'] | ||
794 | 95 | return versioning_api.version_control(sql_connection, repo_path) | ||
795 | 96 | |||
796 | 97 | |||
797 | 98 | def db_sync(options, version=None): | ||
798 | 99 | """Place a database under migration control and perform an upgrade | ||
799 | 100 | |||
800 | 101 | :param options: options dict | ||
801 | 102 | :retval version number | ||
802 | 103 | """ | ||
803 | 104 | try: | ||
804 | 105 | _version_control(options) | ||
805 | 106 | except versioning_exceptions.DatabaseAlreadyControlledError, e: | ||
806 | 107 | pass | ||
807 | 108 | |||
808 | 109 | upgrade(options, version=version) | ||
809 | 110 | |||
810 | 111 | |||
811 | 112 | def _find_migrate_repo(): | ||
812 | 113 | """Get the path for the migrate repository.""" | ||
813 | 114 | path = os.path.join(os.path.abspath(os.path.dirname(__file__)), | ||
814 | 115 | 'migrate_repo') | ||
815 | 116 | assert os.path.exists(path) | ||
816 | 117 | return path | ||
817 | 0 | 118 | ||
818 | === modified file 'glance/registry/db/models.py' | |||
819 | --- glance/registry/db/models.py 2011-01-31 19:39:39 +0000 | |||
820 | +++ glance/registry/db/models.py 2011-02-03 00:03:10 +0000 | |||
821 | @@ -42,10 +42,11 @@ | |||
822 | 42 | __protected_attributes__ = set([ | 42 | __protected_attributes__ = set([ |
823 | 43 | "created_at", "updated_at", "deleted_at", "deleted"]) | 43 | "created_at", "updated_at", "deleted_at", "deleted"]) |
824 | 44 | 44 | ||
826 | 45 | created_at = Column(DateTime, default=datetime.datetime.utcnow) | 45 | created_at = Column(DateTime, default=datetime.datetime.utcnow, |
827 | 46 | nullable=False) | ||
828 | 46 | updated_at = Column(DateTime, onupdate=datetime.datetime.utcnow) | 47 | updated_at = Column(DateTime, onupdate=datetime.datetime.utcnow) |
829 | 47 | deleted_at = Column(DateTime) | 48 | deleted_at = Column(DateTime) |
831 | 48 | deleted = Column(Boolean, default=False) | 49 | deleted = Column(Boolean, nullable=False, default=False) |
832 | 49 | 50 | ||
833 | 50 | def save(self, session=None): | 51 | def save(self, session=None): |
834 | 51 | """Save this object""" | 52 | """Save this object""" |
835 | @@ -96,8 +97,8 @@ | |||
836 | 96 | name = Column(String(255)) | 97 | name = Column(String(255)) |
837 | 97 | type = Column(String(30)) | 98 | type = Column(String(30)) |
838 | 98 | size = Column(Integer) | 99 | size = Column(Integer) |
841 | 99 | status = Column(String(30)) | 100 | status = Column(String(30), nullable=False) |
842 | 100 | is_public = Column(Boolean, default=False) | 101 | is_public = Column(Boolean, nullable=False, default=False) |
843 | 101 | location = Column(Text) | 102 | location = Column(Text) |
844 | 102 | 103 | ||
845 | 103 | @validates('type') | 104 | @validates('type') |
846 | @@ -123,5 +124,7 @@ | |||
847 | 123 | image_id = Column(Integer, ForeignKey('images.id'), nullable=False) | 124 | image_id = Column(Integer, ForeignKey('images.id'), nullable=False) |
848 | 124 | image = relationship(Image, backref=backref('properties')) | 125 | image = relationship(Image, backref=backref('properties')) |
849 | 125 | 126 | ||
851 | 126 | key = Column(String(255), index=True) | 127 | # FIXME(sirp): KEY is a reserved word in SQL, might be a good idea to |
852 | 128 | # rename this column | ||
853 | 129 | key = Column(String(255), index=True, nullable=False) | ||
854 | 127 | value = Column(Text) | 130 | value = Column(Text) |
855 | 128 | 131 | ||
856 | === added file 'tests/unit/test_migrations.py' | |||
857 | --- tests/unit/test_migrations.py 1970-01-01 00:00:00 +0000 | |||
858 | +++ tests/unit/test_migrations.py 2011-02-03 00:03:10 +0000 | |||
859 | @@ -0,0 +1,48 @@ | |||
860 | 1 | # vim: tabstop=4 shiftwidth=4 softtabstop=4 | ||
861 | 2 | |||
862 | 3 | # Copyright 2010-2011 OpenStack, LLC | ||
863 | 4 | # All Rights Reserved. | ||
864 | 5 | # | ||
865 | 6 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
866 | 7 | # not use this file except in compliance with the License. You may obtain | ||
867 | 8 | # a copy of the License at | ||
868 | 9 | # | ||
869 | 10 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
870 | 11 | # | ||
871 | 12 | # Unless required by applicable law or agreed to in writing, software | ||
872 | 13 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
873 | 14 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
874 | 15 | # License for the specific language governing permissions and limitations | ||
875 | 16 | # under the License. | ||
876 | 17 | |||
877 | 18 | import os | ||
878 | 19 | import unittest | ||
879 | 20 | |||
880 | 21 | import glance.registry.db.migration as migration_api | ||
881 | 22 | import glance.common.config as config | ||
882 | 23 | |||
883 | 24 | class TestMigrations(unittest.TestCase): | ||
884 | 25 | """Test sqlalchemy-migrate migrations""" | ||
885 | 26 | |||
886 | 27 | def setUp(self): | ||
887 | 28 | self.db_path = "glance_test_migration.sqlite" | ||
888 | 29 | self.options = dict(sql_connection="sqlite:///%s" % self.db_path, | ||
889 | 30 | verbose=False) | ||
890 | 31 | config.setup_logging(self.options) | ||
891 | 32 | |||
892 | 33 | def tearDown(self): | ||
893 | 34 | if os.path.exists(self.db_path): | ||
894 | 35 | os.unlink(self.db_path) | ||
895 | 36 | |||
896 | 37 | def test_db_sync_downgrade_then_upgrade(self): | ||
897 | 38 | migration_api.db_sync(self.options) | ||
898 | 39 | |||
899 | 40 | latest = migration_api.db_version(self.options) | ||
900 | 41 | |||
901 | 42 | migration_api.downgrade(self.options, latest-1) | ||
902 | 43 | cur_version = migration_api.db_version(self.options) | ||
903 | 44 | self.assertEqual(cur_version, latest-1) | ||
904 | 45 | |||
905 | 46 | migration_api.upgrade(self.options, cur_version+1) | ||
906 | 47 | cur_version = migration_api.db_version(self.options) | ||
907 | 48 | self.assertEqual(cur_version, latest) | ||
908 | 0 | 49 | ||
909 | === modified file 'tools/pip-requires' | |||
910 | --- tools/pip-requires 2011-01-28 21:54:34 +0000 | |||
911 | +++ tools/pip-requires 2011-02-03 00:03:10 +0000 | |||
912 | @@ -14,3 +14,4 @@ | |||
913 | 14 | argparse | 14 | argparse |
914 | 15 | mox==0.5.0 | 15 | mox==0.5.0 |
915 | 16 | -f http://pymox.googlecode.com/files/mox-0.5.0.tar.gz | 16 | -f http://pymox.googlecode.com/files/mox-0.5.0.tar.gz |
916 | 17 | sqlalchemy-migrate>=0.6 |
Very nice work, Rick. Any chance we can figure out a way of testing this?