Merge lp:~yolanda.robla/ubuntu/precise/glance/essex-sru into lp:ubuntu/precise-updates/glance

Proposed by Yolanda Robla
Status: Superseded
Proposed branch: lp:~yolanda.robla/ubuntu/precise/glance/essex-sru
Merge into: lp:ubuntu/precise-updates/glance
Diff against target: 1573 lines (+309/-1085)
21 files modified
.gitignore (+0/-11)
.gitreview (+0/-5)
.mailmap (+0/-19)
.pc/CVE-2012-4573.patch/glance/api/v1/images.py (+0/-973)
.pc/applied-patches (+0/-1)
.pc/fix_migration_012_foreign_keys.patch/Authors (+1/-0)
Authors (+1/-0)
PKG-INFO (+15/-0)
debian/changelog (+18/-0)
debian/glance-api.logrotate (+1/-1)
debian/glance-registry.logrotate (+1/-1)
debian/patches/CVE-2012-4573.patch (+0/-35)
debian/patches/fix_migration_012_foreign_keys.patch (+22/-26)
debian/patches/series (+0/-1)
glance.egg-info/PKG-INFO (+15/-0)
glance.egg-info/SOURCES.txt (+217/-0)
glance.egg-info/dependency_links.txt (+1/-0)
glance.egg-info/top_level.txt (+1/-0)
glance/vcsversion.py (+7/-0)
setup.cfg (+8/-11)
tools/pip-requires (+1/-1)
To merge this branch: bzr merge lp:~yolanda.robla/ubuntu/precise/glance/essex-sru
Reviewer Review Type Date Requested Status
James Page Needs Information
Review via email: mp+140403@code.launchpad.net

This proposal has been superseded by a proposal from 2012-12-18.

To post a comment you must log in.
Revision history for this message
James Page (james-page) wrote :

Yolanda

Specifically what was the pep8 issue that was causing the build to fail?

[ Yolanda Robla ]
* debian/rules: skipping pep8 tests to allow building

review: Needs Information
53. By Yolanda Robla

removed skipping pep8 tests
fix typos in changelog

Unmerged revisions

53. By Yolanda Robla

removed skipping pep8 tests
fix typos in changelog

52. By Yolanda Robla

[ Adam Gandelman ]
* debian/glance-{registry, api}.logrotate: Fix incorrect logfile
  locations. (LP: #1049314)

[ Yolanda Robla ]
* debian/rules: skipping pep8 tests to allow building

[ Yolanda Robla Mota ]
* Resynchronize with stable/essex (efd7e75b):
  - [efd7e75] Non-admin users can cause public glance images to be deleted
    from the backend storage repository (CVE-2012-4573)
  - [e6be061] Jenkins jobs fail because of incompatibility between sqlalchemy-
    migrate and the newest sqlalchemy-0.8.0b1 (LP: #1073569)

* Dropped patches, superseeded by snapshot:
  - debian/patches/CVE-2012-4573.patch: [efd7e75]

51. By Jamie Strandboge

* SECURITY UPDATE: deletion of arbitrary public and shared images via
  authenticated user
  - debian/patches/CVE-2012-4573.patch: adjust glance/api/v1/images.py to
    ensure image is owned by user before delayed_deletion
  - CVE-2012-4573

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== removed file '.gitignore'
2--- .gitignore 2012-06-24 03:14:33 +0000
3+++ .gitignore 1970-01-01 00:00:00 +0000
4@@ -1,11 +0,0 @@
5-*.pyc
6-*.swp
7-*.log
8-.glance-venv
9-.venv
10-.tox
11-build
12-dist
13-glance.egg-info
14-glance/vcsversion.py
15-tests.sqlite
16
17=== removed file '.gitreview'
18--- .gitreview 2012-06-24 03:14:33 +0000
19+++ .gitreview 1970-01-01 00:00:00 +0000
20@@ -1,5 +0,0 @@
21-[gerrit]
22-host=review.openstack.org
23-port=29418
24-project=openstack/glance.git
25-defaultbranch=stable/essex
26
27=== removed file '.mailmap'
28--- .mailmap 2012-06-24 03:14:33 +0000
29+++ .mailmap 1970-01-01 00:00:00 +0000
30@@ -1,19 +0,0 @@
31-# Format is:
32-# <preferred e-mail> <other e-mail 1>
33-# <preferred e-mail> <other e-mail 2>
34-<adam.gandelman@canonical.com> <adamg@canonical.com>
35-<brian.waldon@rackspace.com> <bcwaldon@gmail.com>
36-<corywright@gmail.com> <cory.wright@rackspace.com>
37-<dprince@redhat.com> <dan.prince@rackspace.com>
38-<jsuh@isi.edu> <jsuh@bespin>
39-<josh@jk0.org> <josh.kearney@rackspace.com>
40-<rconradharris@gmail.com> <rick.harris@rackspace.com>
41-<rconradharris@gmail.com> <rick@quasar.racklabs.com>
42-<rick@openstack.org> <rclark@chat-blanc>
43-<soren.hansen@rackspace.com> <soren@linux2go.dk>
44-<soren.hansen@rackspace.com> <soren@openstack.org>
45-<jeblair@hp.com> <corvus@gnu.org>
46-<jeblair@hp.com> <james.blair@rackspace.com>
47-<chris@pistoncloud.com> <chris@slicehost.com>
48-<ken.pepple@gmail.com> <ken.pepple@rabbityard.com>
49-<P@draigBrady.com> <pbrady@redhat.com>
50
51=== removed directory '.pc/CVE-2012-4573.patch'
52=== removed directory '.pc/CVE-2012-4573.patch/glance'
53=== removed directory '.pc/CVE-2012-4573.patch/glance/api'
54=== removed directory '.pc/CVE-2012-4573.patch/glance/api/v1'
55=== removed file '.pc/CVE-2012-4573.patch/glance/api/v1/images.py'
56--- .pc/CVE-2012-4573.patch/glance/api/v1/images.py 2012-11-08 07:19:39 +0000
57+++ .pc/CVE-2012-4573.patch/glance/api/v1/images.py 1970-01-01 00:00:00 +0000
58@@ -1,973 +0,0 @@
59-# vim: tabstop=4 shiftwidth=4 softtabstop=4
60-
61-# Copyright 2010 OpenStack LLC.
62-# All Rights Reserved.
63-#
64-# Licensed under the Apache License, Version 2.0 (the "License"); you may
65-# not use this file except in compliance with the License. You may obtain
66-# a copy of the License at
67-#
68-# http://www.apache.org/licenses/LICENSE-2.0
69-#
70-# Unless required by applicable law or agreed to in writing, software
71-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
72-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
73-# License for the specific language governing permissions and limitations
74-# under the License.
75-
76-"""
77-/images endpoint for Glance v1 API
78-"""
79-
80-import errno
81-import logging
82-import sys
83-import traceback
84-
85-from webob.exc import (HTTPError,
86- HTTPNotFound,
87- HTTPConflict,
88- HTTPBadRequest,
89- HTTPForbidden,
90- HTTPRequestEntityTooLarge,
91- HTTPServiceUnavailable,
92- )
93-
94-from glance.api import policy
95-import glance.api.v1
96-from glance.api.v1 import controller
97-from glance.api.v1 import filters
98-from glance.common import cfg
99-from glance.common import exception
100-from glance.common import wsgi
101-from glance.common import utils
102-import glance.store
103-import glance.store.filesystem
104-import glance.store.http
105-import glance.store.rbd
106-import glance.store.s3
107-import glance.store.swift
108-from glance.store import (get_from_backend,
109- get_size_from_backend,
110- schedule_delete_from_backend,
111- get_store_from_location,
112- get_store_from_scheme)
113-from glance import registry
114-from glance import notifier
115-
116-
117-logger = logging.getLogger(__name__)
118-SUPPORTED_PARAMS = glance.api.v1.SUPPORTED_PARAMS
119-SUPPORTED_FILTERS = glance.api.v1.SUPPORTED_FILTERS
120-
121-
122-# 1 PiB, which is a *huge* image by anyone's measure. This is just to protect
123-# against client programming errors (or DoS attacks) in the image metadata.
124-# We have a known limit of 1 << 63 in the database -- images.size is declared
125-# as a BigInteger.
126-IMAGE_SIZE_CAP = 1 << 50
127-
128-
129-class Controller(controller.BaseController):
130- """
131- WSGI controller for images resource in Glance v1 API
132-
133- The images resource API is a RESTful web service for image data. The API
134- is as follows::
135-
136- GET /images -- Returns a set of brief metadata about images
137- GET /images/detail -- Returns a set of detailed metadata about
138- images
139- HEAD /images/<ID> -- Return metadata about an image with id <ID>
140- GET /images/<ID> -- Return image data for image with id <ID>
141- POST /images -- Store image data and return metadata about the
142- newly-stored image
143- PUT /images/<ID> -- Update image metadata and/or upload image
144- data for a previously-reserved image
145- DELETE /images/<ID> -- Delete the image with id <ID>
146- """
147-
148- default_store_opt = cfg.StrOpt('default_store', default='file')
149-
150- def __init__(self, conf):
151- self.conf = conf
152- self.conf.register_opt(self.default_store_opt)
153- glance.store.create_stores(conf)
154- self.verify_store_or_exit(self.conf.default_store)
155- self.notifier = notifier.Notifier(conf)
156- registry.configure_registry_client(conf)
157- self.policy = policy.Enforcer(conf)
158-
159- def _enforce(self, req, action):
160- """Authorize an action against our policies"""
161- try:
162- self.policy.enforce(req.context, action, {})
163- except exception.Forbidden:
164- raise HTTPForbidden()
165-
166- def index(self, req):
167- """
168- Returns the following information for all public, available images:
169-
170- * id -- The opaque image identifier
171- * name -- The name of the image
172- * disk_format -- The disk image format
173- * container_format -- The "container" format of the image
174- * checksum -- MD5 checksum of the image data
175- * size -- Size of image data in bytes
176-
177- :param req: The WSGI/Webob Request object
178- :retval The response body is a mapping of the following form::
179-
180- {'images': [
181- {'id': <ID>,
182- 'name': <NAME>,
183- 'disk_format': <DISK_FORMAT>,
184- 'container_format': <DISK_FORMAT>,
185- 'checksum': <CHECKSUM>
186- 'size': <SIZE>}, ...
187- ]}
188- """
189- self._enforce(req, 'get_images')
190- params = self._get_query_params(req)
191- try:
192- images = registry.get_images_list(req.context, **params)
193- except exception.Invalid, e:
194- raise HTTPBadRequest(explanation="%s" % e)
195-
196- return dict(images=images)
197-
198- def detail(self, req):
199- """
200- Returns detailed information for all public, available images
201-
202- :param req: The WSGI/Webob Request object
203- :retval The response body is a mapping of the following form::
204-
205- {'images': [
206- {'id': <ID>,
207- 'name': <NAME>,
208- 'size': <SIZE>,
209- 'disk_format': <DISK_FORMAT>,
210- 'container_format': <CONTAINER_FORMAT>,
211- 'checksum': <CHECKSUM>,
212- 'min_disk': <MIN_DISK>,
213- 'min_ram': <MIN_RAM>,
214- 'store': <STORE>,
215- 'status': <STATUS>,
216- 'created_at': <TIMESTAMP>,
217- 'updated_at': <TIMESTAMP>,
218- 'deleted_at': <TIMESTAMP>|<NONE>,
219- 'properties': {'distro': 'Ubuntu 10.04 LTS', ...}}, ...
220- ]}
221- """
222- self._enforce(req, 'get_images')
223- params = self._get_query_params(req)
224- try:
225- images = registry.get_images_detail(req.context, **params)
226- # Strip out the Location attribute. Temporary fix for
227- # LP Bug #755916. This information is still coming back
228- # from the registry, since the API server still needs access
229- # to it, however we do not return this potential security
230- # information to the API end user...
231- for image in images:
232- del image['location']
233- except exception.Invalid, e:
234- raise HTTPBadRequest(explanation="%s" % e)
235- return dict(images=images)
236-
237- def _get_query_params(self, req):
238- """
239- Extracts necessary query params from request.
240-
241- :param req: the WSGI Request object
242- :retval dict of parameters that can be used by registry client
243- """
244- params = {'filters': self._get_filters(req)}
245-
246- for PARAM in SUPPORTED_PARAMS:
247- if PARAM in req.params:
248- params[PARAM] = req.params.get(PARAM)
249- return params
250-
251- def _get_filters(self, req):
252- """
253- Return a dictionary of query param filters from the request
254-
255- :param req: the Request object coming from the wsgi layer
256- :retval a dict of key/value filters
257- """
258- query_filters = {}
259- for param in req.params:
260- if param in SUPPORTED_FILTERS or param.startswith('property-'):
261- query_filters[param] = req.params.get(param)
262- if not filters.validate(param, query_filters[param]):
263- raise HTTPBadRequest('Bad value passed to filter %s '
264- 'got %s' % (param,
265- query_filters[param]))
266- return query_filters
267-
268- def meta(self, req, id):
269- """
270- Returns metadata about an image in the HTTP headers of the
271- response object
272-
273- :param req: The WSGI/Webob Request object
274- :param id: The opaque image identifier
275- :retval similar to 'show' method but without image_data
276-
277- :raises HTTPNotFound if image metadata is not available to user
278- """
279- self._enforce(req, 'get_image')
280- image_meta = self.get_image_meta_or_404(req, id)
281- del image_meta['location']
282- return {
283- 'image_meta': image_meta
284- }
285-
286- @staticmethod
287- def _validate_source(source, req):
288- """
289- External sources (as specified via the location or copy-from headers)
290- are supported only over non-local store types, i.e. S3, Swift, HTTP.
291- Note the absence of file:// for security reasons, see LP bug #942118.
292- If the above constraint is violated, we reject with 400 "Bad Request".
293- """
294- if source:
295- for scheme in ['s3', 'swift', 'http']:
296- if source.lower().startswith(scheme):
297- return source
298- msg = _("External sourcing not supported for store %s") % source
299- logger.error(msg)
300- raise HTTPBadRequest(msg, request=req, content_type="text/plain")
301-
302- @staticmethod
303- def _copy_from(req):
304- return req.headers.get('x-glance-api-copy-from')
305-
306- @staticmethod
307- def _external_source(image_meta, req):
308- source = image_meta.get('location', Controller._copy_from(req))
309- return Controller._validate_source(source, req)
310-
311- @staticmethod
312- def _get_from_store(where):
313- try:
314- image_data, image_size = get_from_backend(where)
315- except exception.NotFound, e:
316- raise HTTPNotFound(explanation="%s" % e)
317- image_size = int(image_size) if image_size else None
318- return image_data, image_size
319-
320- def show(self, req, id):
321- """
322- Returns an iterator that can be used to retrieve an image's
323- data along with the image metadata.
324-
325- :param req: The WSGI/Webob Request object
326- :param id: The opaque image identifier
327-
328- :raises HTTPNotFound if image is not available to user
329- """
330- self._enforce(req, 'get_image')
331- image_meta = self.get_active_image_meta_or_404(req, id)
332-
333- if image_meta.get('size') == 0:
334- image_iterator = iter([])
335- else:
336- image_iterator, size = self._get_from_store(image_meta['location'])
337- image_meta['size'] = size or image_meta['size']
338-
339- del image_meta['location']
340- return {
341- 'image_iterator': image_iterator,
342- 'image_meta': image_meta,
343- }
344-
345- def _reserve(self, req, image_meta):
346- """
347- Adds the image metadata to the registry and assigns
348- an image identifier if one is not supplied in the request
349- headers. Sets the image's status to `queued`.
350-
351- :param req: The WSGI/Webob Request object
352- :param id: The opaque image identifier
353- :param image_meta: The image metadata
354-
355- :raises HTTPConflict if image already exists
356- :raises HTTPBadRequest if image metadata is not valid
357- """
358- location = self._external_source(image_meta, req)
359-
360- image_meta['status'] = ('active' if image_meta.get('size') == 0
361- else 'queued')
362-
363- if location:
364- store = get_store_from_location(location)
365- # check the store exists before we hit the registry, but we
366- # don't actually care what it is at this point
367- self.get_store_or_400(req, store)
368-
369- # retrieve the image size from remote store (if not provided)
370- image_meta['size'] = self._get_size(image_meta, location)
371- else:
372- # Ensure that the size attribute is set to zero for directly
373- # uploadable images (if not provided). The size will be set
374- # to a non-zero value during upload
375- image_meta['size'] = image_meta.get('size', 0)
376-
377- try:
378- image_meta = registry.add_image_metadata(req.context, image_meta)
379- return image_meta
380- except exception.Duplicate:
381- msg = (_("An image with identifier %s already exists")
382- % image_meta['id'])
383- logger.error(msg)
384- raise HTTPConflict(msg, request=req, content_type="text/plain")
385- except exception.Invalid, e:
386- msg = (_("Failed to reserve image. Got error: %(e)s") % locals())
387- for line in msg.split('\n'):
388- logger.error(line)
389- raise HTTPBadRequest(msg, request=req, content_type="text/plain")
390- except exception.Forbidden:
391- msg = _("Forbidden to reserve image.")
392- logger.error(msg)
393- raise HTTPForbidden(msg, request=req, content_type="text/plain")
394-
395- def _upload(self, req, image_meta):
396- """
397- Uploads the payload of the request to a backend store in
398- Glance. If the `x-image-meta-store` header is set, Glance
399- will attempt to use that store, if not, Glance will use the
400- store set by the flag `default_store`.
401-
402- :param req: The WSGI/Webob Request object
403- :param image_meta: Mapping of metadata about image
404-
405- :raises HTTPConflict if image already exists
406- :retval The location where the image was stored
407- """
408-
409- copy_from = self._copy_from(req)
410- if copy_from:
411- image_data, image_size = self._get_from_store(copy_from)
412- image_meta['size'] = image_size or image_meta['size']
413- else:
414- try:
415- req.get_content_type('application/octet-stream')
416- except exception.InvalidContentType:
417- self._safe_kill(req, image_meta['id'])
418- msg = _("Content-Type must be application/octet-stream")
419- logger.error(msg)
420- raise HTTPBadRequest(explanation=msg)
421-
422- image_data = req.body_file
423-
424- if req.content_length:
425- image_size = int(req.content_length)
426- elif 'x-image-meta-size' in req.headers:
427- image_size = int(req.headers['x-image-meta-size'])
428- else:
429- logger.debug(_("Got request with no content-length and no "
430- "x-image-meta-size header"))
431- image_size = 0
432-
433- store_name = req.headers.get('x-image-meta-store',
434- self.conf.default_store)
435-
436- store = self.get_store_or_400(req, store_name)
437-
438- image_id = image_meta['id']
439- logger.debug(_("Setting image %s to status 'saving'"), image_id)
440- registry.update_image_metadata(req.context, image_id,
441- {'status': 'saving'})
442- try:
443- logger.debug(_("Uploading image data for image %(image_id)s "
444- "to %(store_name)s store"), locals())
445-
446- if image_size > IMAGE_SIZE_CAP:
447- max_image_size = IMAGE_SIZE_CAP
448- msg = _("Denying attempt to upload image larger than "
449- "%(max_image_size)d. Supplied image size was "
450- "%(image_size)d") % locals()
451- logger.warn(msg)
452- raise HTTPBadRequest(msg, request=req)
453-
454- location, size, checksum = store.add(image_meta['id'],
455- image_data,
456- image_size)
457-
458- # Verify any supplied checksum value matches checksum
459- # returned from store when adding image
460- supplied_checksum = image_meta.get('checksum')
461- if supplied_checksum and supplied_checksum != checksum:
462- msg = _("Supplied checksum (%(supplied_checksum)s) and "
463- "checksum generated from uploaded image "
464- "(%(checksum)s) did not match. Setting image "
465- "status to 'killed'.") % locals()
466- logger.error(msg)
467- self._safe_kill(req, image_id)
468- raise HTTPBadRequest(msg, content_type="text/plain",
469- request=req)
470-
471- # Update the database with the checksum returned
472- # from the backend store
473- logger.debug(_("Updating image %(image_id)s data. "
474- "Checksum set to %(checksum)s, size set "
475- "to %(size)d"), locals())
476- update_data = {'checksum': checksum,
477- 'size': size}
478- image_meta = registry.update_image_metadata(req.context,
479- image_id,
480- update_data)
481- self.notifier.info('image.upload', image_meta)
482-
483- return location
484-
485- except exception.Duplicate, e:
486- msg = _("Attempt to upload duplicate image: %s") % e
487- logger.error(msg)
488- self._safe_kill(req, image_id)
489- self.notifier.error('image.upload', msg)
490- raise HTTPConflict(msg, request=req)
491-
492- except exception.Forbidden, e:
493- msg = _("Forbidden upload attempt: %s") % e
494- logger.error(msg)
495- self._safe_kill(req, image_id)
496- self.notifier.error('image.upload', msg)
497- raise HTTPForbidden(msg, request=req, content_type="text/plain")
498-
499- except exception.StorageFull, e:
500- msg = _("Image storage media is full: %s") % e
501- logger.error(msg)
502- self._safe_kill(req, image_id)
503- self.notifier.error('image.upload', msg)
504- raise HTTPRequestEntityTooLarge(msg, request=req,
505- content_type='text/plain')
506-
507- except exception.StorageWriteDenied, e:
508- msg = _("Insufficient permissions on image storage media: %s") % e
509- logger.error(msg)
510- self._safe_kill(req, image_id)
511- self.notifier.error('image.upload', msg)
512- raise HTTPServiceUnavailable(msg, request=req,
513- content_type='text/plain')
514-
515- except HTTPError, e:
516- self._safe_kill(req, image_id)
517- self.notifier.error('image.upload', e.explanation)
518- raise
519-
520- except Exception, e:
521- tb_info = traceback.format_exc()
522- logger.error(tb_info)
523-
524- self._safe_kill(req, image_id)
525-
526- msg = _("Error uploading image: (%(class_name)s): "
527- "%(exc)s") % ({'class_name': e.__class__.__name__,
528- 'exc': str(e)})
529-
530- self.notifier.error('image.upload', msg)
531- raise HTTPBadRequest(msg, request=req)
532-
533- def _activate(self, req, image_id, location):
534- """
535- Sets the image status to `active` and the image's location
536- attribute.
537-
538- :param req: The WSGI/Webob Request object
539- :param image_id: Opaque image identifier
540- :param location: Location of where Glance stored this image
541- """
542- image_meta = {}
543- image_meta['location'] = location
544- image_meta['status'] = 'active'
545-
546- try:
547- return registry.update_image_metadata(req.context,
548- image_id,
549- image_meta)
550- except exception.Invalid, e:
551- msg = (_("Failed to activate image. Got error: %(e)s")
552- % locals())
553- for line in msg.split('\n'):
554- logger.error(line)
555- self.notifier.error('image.update', msg)
556- raise HTTPBadRequest(msg, request=req, content_type="text/plain")
557-
558- def _kill(self, req, image_id):
559- """
560- Marks the image status to `killed`.
561-
562- :param req: The WSGI/Webob Request object
563- :param image_id: Opaque image identifier
564- """
565- registry.update_image_metadata(req.context, image_id,
566- {'status': 'killed'})
567-
568- def _safe_kill(self, req, image_id):
569- """
570- Mark image killed without raising exceptions if it fails.
571-
572- Since _kill is meant to be called from exceptions handlers, it should
573- not raise itself, rather it should just log its error.
574-
575- :param req: The WSGI/Webob Request object
576- :param image_id: Opaque image identifier
577- """
578- try:
579- self._kill(req, image_id)
580- except Exception, e:
581- logger.error(_("Unable to kill image %(id)s: "
582- "%(exc)s") % ({'id': image_id,
583- 'exc': repr(e)}))
584-
585- def _upload_and_activate(self, req, image_meta):
586- """
587- Safely uploads the image data in the request payload
588- and activates the image in the registry after a successful
589- upload.
590-
591- :param req: The WSGI/Webob Request object
592- :param image_meta: Mapping of metadata about image
593-
594- :retval Mapping of updated image data
595- """
596- image_id = image_meta['id']
597- # This is necessary because of a bug in Webob 1.0.2 - 1.0.7
598- # See: https://bitbucket.org/ianb/webob/
599- # issue/12/fix-for-issue-6-broke-chunked-transfer
600- req.is_body_readable = True
601- location = self._upload(req, image_meta)
602- return self._activate(req, image_id, location)
603-
604- def _get_size(self, image_meta, location):
605- # retrieve the image size from remote store (if not provided)
606- return image_meta.get('size', 0) or get_size_from_backend(location)
607-
608- def _handle_source(self, req, image_id, image_meta, image_data):
609- if image_data or self._copy_from(req):
610- image_meta = self._upload_and_activate(req, image_meta)
611- else:
612- location = image_meta.get('location')
613- if location:
614- image_meta = self._activate(req, image_id, location)
615- return image_meta
616-
617- def create(self, req, image_meta, image_data):
618- """
619- Adds a new image to Glance. Four scenarios exist when creating an
620- image:
621-
622- 1. If the image data is available directly for upload, create can be
623- passed the image data as the request body and the metadata as the
624- request headers. The image will initially be 'queued', during
625- upload it will be in the 'saving' status, and then 'killed' or
626- 'active' depending on whether the upload completed successfully.
627-
628- 2. If the image data exists somewhere else, you can upload indirectly
629- from the external source using the x-glance-api-copy-from header.
630- Once the image is uploaded, the external store is not subsequently
631- consulted, i.e. the image content is served out from the configured
632- glance image store. State transitions are as for option #1.
633-
634- 3. If the image data exists somewhere else, you can reference the
635- source using the x-image-meta-location header. The image content
636- will be served out from the external store, i.e. is never uploaded
637- to the configured glance image store.
638-
639- 4. If the image data is not available yet, but you'd like reserve a
640- spot for it, you can omit the data and a record will be created in
641- the 'queued' state. This exists primarily to maintain backwards
642- compatibility with OpenStack/Rackspace API semantics.
643-
644- The request body *must* be encoded as application/octet-stream,
645- otherwise an HTTPBadRequest is returned.
646-
647- Upon a successful save of the image data and metadata, a response
648- containing metadata about the image is returned, including its
649- opaque identifier.
650-
651- :param req: The WSGI/Webob Request object
652- :param image_meta: Mapping of metadata about image
653- :param image_data: Actual image data that is to be stored
654-
655- :raises HTTPBadRequest if x-image-meta-location is missing
656- and the request body is not application/octet-stream
657- image data.
658- """
659- self._enforce(req, 'add_image')
660- if image_meta.get('is_public'):
661- self._enforce(req, 'publicize_image')
662- if req.context.read_only:
663- msg = _("Read-only access")
664- logger.debug(msg)
665- raise HTTPForbidden(msg, request=req,
666- content_type="text/plain")
667-
668- image_meta = self._reserve(req, image_meta)
669- id = image_meta['id']
670-
671- image_meta = self._handle_source(req, id, image_meta, image_data)
672-
673- # Prevent client from learning the location, as it
674- # could contain security credentials
675- image_meta.pop('location', None)
676-
677- return {'image_meta': image_meta}
678-
679- def update(self, req, id, image_meta, image_data):
680- """
681- Updates an existing image with the registry.
682-
683- :param request: The WSGI/Webob Request object
684- :param id: The opaque image identifier
685-
686- :retval Returns the updated image information as a mapping
687- """
688- self._enforce(req, 'modify_image')
689- if image_meta.get('is_public'):
690- self._enforce(req, 'publicize_image')
691- if req.context.read_only:
692- msg = _("Read-only access")
693- logger.debug(msg)
694- raise HTTPForbidden(msg, request=req,
695- content_type="text/plain")
696-
697- orig_image_meta = self.get_image_meta_or_404(req, id)
698- orig_status = orig_image_meta['status']
699-
700- # The default behaviour for a PUT /images/<IMAGE_ID> is to
701- # override any properties that were previously set. This, however,
702- # leads to a number of issues for the common use case where a caller
703- # registers an image with some properties and then almost immediately
704- # uploads an image file along with some more properties. Here, we
705- # check for a special header value to be false in order to force
706- # properties NOT to be purged. However we also disable purging of
707- # properties if an image file is being uploaded...
708- purge_props = req.headers.get('x-glance-registry-purge-props', True)
709- purge_props = (utils.bool_from_string(purge_props) and
710- image_data is None)
711-
712- if image_data is not None and orig_status != 'queued':
713- raise HTTPConflict(_("Cannot upload to an unqueued image"))
714-
715- # Only allow the Location|Copy-From fields to be modified if the
716- # image is in queued status, which indicates that the user called
717- # POST /images but originally supply neither a Location|Copy-From
718- # field NOR image data
719- location = self._external_source(image_meta, req)
720- reactivating = orig_status != 'queued' and location
721- activating = orig_status == 'queued' and (location or image_data)
722-
723- if reactivating:
724- msg = _("Attempted to update Location field for an image "
725- "not in queued status.")
726- raise HTTPBadRequest(msg, request=req, content_type="text/plain")
727-
728- try:
729- if location:
730- image_meta['size'] = self._get_size(image_meta, location)
731-
732- image_meta = registry.update_image_metadata(req.context,
733- id,
734- image_meta,
735- purge_props)
736-
737- if activating:
738- image_meta = self._handle_source(req, id, image_meta,
739- image_data)
740- except exception.Invalid, e:
741- msg = (_("Failed to update image metadata. Got error: %(e)s")
742- % locals())
743- for line in msg.split('\n'):
744- logger.error(line)
745- self.notifier.error('image.update', msg)
746- raise HTTPBadRequest(msg, request=req, content_type="text/plain")
747- except exception.NotFound, e:
748- msg = ("Failed to find image to update: %(e)s" % locals())
749- for line in msg.split('\n'):
750- logger.info(line)
751- self.notifier.info('image.update', msg)
752- raise HTTPNotFound(msg, request=req, content_type="text/plain")
753- except exception.Forbidden, e:
754- msg = ("Forbidden to update image: %(e)s" % locals())
755- for line in msg.split('\n'):
756- logger.info(line)
757- self.notifier.info('image.update', msg)
758- raise HTTPForbidden(msg, request=req, content_type="text/plain")
759- else:
760- self.notifier.info('image.update', image_meta)
761-
762- # Prevent client from learning the location, as it
763- # could contain security credentials
764- image_meta.pop('location', None)
765-
766- return {'image_meta': image_meta}
767-
768- def delete(self, req, id):
769- """
770- Deletes the image and all its chunks from the Glance
771-
772- :param req: The WSGI/Webob Request object
773- :param id: The opaque image identifier
774-
775- :raises HttpBadRequest if image registry is invalid
776- :raises HttpNotFound if image or any chunk is not available
777- :raises HttpUnauthorized if image or any chunk is not
778- deleteable by the requesting user
779- """
780- self._enforce(req, 'delete_image')
781- if req.context.read_only:
782- msg = _("Read-only access")
783- logger.debug(msg)
784- raise HTTPForbidden(msg, request=req,
785- content_type="text/plain")
786-
787- image = self.get_image_meta_or_404(req, id)
788- if image['protected']:
789- msg = _("Image is protected")
790- logger.debug(msg)
791- raise HTTPForbidden(msg, request=req,
792- content_type="text/plain")
793-
794- # The image's location field may be None in the case
795- # of a saving or queued image, therefore don't ask a backend
796- # to delete the image if the backend doesn't yet store it.
797- # See https://bugs.launchpad.net/glance/+bug/747799
798- try:
799- if image['location']:
800- schedule_delete_from_backend(image['location'], self.conf,
801- req.context, id)
802- registry.delete_image_metadata(req.context, id)
803- except exception.NotFound, e:
804- msg = ("Failed to find image to delete: %(e)s" % locals())
805- for line in msg.split('\n'):
806- logger.info(line)
807- self.notifier.info('image.delete', msg)
808- raise HTTPNotFound(msg, request=req, content_type="text/plain")
809- except exception.Forbidden, e:
810- msg = ("Forbidden to delete image: %(e)s" % locals())
811- for line in msg.split('\n'):
812- logger.info(line)
813- self.notifier.info('image.delete', msg)
814- raise HTTPForbidden(msg, request=req, content_type="text/plain")
815- else:
816- self.notifier.info('image.delete', id)
817-
818- def get_store_or_400(self, request, store_name):
819- """
820- Grabs the storage backend for the supplied store name
821- or raises an HTTPBadRequest (400) response
822-
823- :param request: The WSGI/Webob Request object
824- :param store_name: The backend store name
825-
826- :raises HTTPNotFound if store does not exist
827- """
828- try:
829- return get_store_from_scheme(store_name)
830- except exception.UnknownScheme:
831- msg = (_("Requested store %s not available on this Glance server")
832- % store_name)
833- logger.error(msg)
834- raise HTTPBadRequest(msg, request=request,
835- content_type='text/plain')
836-
837- def verify_store_or_exit(self, store_name):
838- """
839- Verifies availability of the storage backend for the
840- given store name or exits
841-
842- :param store_name: The backend store name
843- """
844- try:
845- get_store_from_scheme(store_name)
846- except exception.UnknownScheme:
847- msg = (_("Default store %s not available on this Glance server\n")
848- % store_name)
849- logger.error(msg)
850- # message on stderr will only be visible if started directly via
851- # bin/glance-api, as opposed to being daemonized by glance-control
852- sys.stderr.write(msg)
853- sys.exit(255)
854-
855-
856-class ImageDeserializer(wsgi.JSONRequestDeserializer):
857- """Handles deserialization of specific controller method requests."""
858-
859- def _deserialize(self, request):
860- result = {}
861- try:
862- result['image_meta'] = utils.get_image_meta_from_headers(request)
863- except exception.Invalid:
864- image_size_str = request.headers['x-image-meta-size']
865- msg = _("Incoming image size of %s was not convertible to "
866- "an integer.") % image_size_str
867- raise HTTPBadRequest(msg, request=request)
868-
869- image_meta = result['image_meta']
870- if 'size' in image_meta:
871- incoming_image_size = image_meta['size']
872- if incoming_image_size > IMAGE_SIZE_CAP:
873- max_image_size = IMAGE_SIZE_CAP
874- msg = _("Denying attempt to upload image larger than "
875- "%(max_image_size)d. Supplied image size was "
876- "%(incoming_image_size)d") % locals()
877- logger.warn(msg)
878- raise HTTPBadRequest(msg, request=request)
879-
880- data = request.body_file if self.has_body(request) else None
881- result['image_data'] = data
882- return result
883-
884- def create(self, request):
885- return self._deserialize(request)
886-
887- def update(self, request):
888- return self._deserialize(request)
889-
890-
891-class ImageSerializer(wsgi.JSONResponseSerializer):
892- """Handles serialization of specific controller method responses."""
893-
894- def __init__(self, conf):
895- self.conf = conf
896- self.notifier = notifier.Notifier(conf)
897-
898- def _inject_location_header(self, response, image_meta):
899- location = self._get_image_location(image_meta)
900- response.headers['Location'] = location
901-
902- def _inject_checksum_header(self, response, image_meta):
903- response.headers['ETag'] = image_meta['checksum']
904-
905- def _inject_image_meta_headers(self, response, image_meta):
906- """
907- Given a response and mapping of image metadata, injects
908- the Response with a set of HTTP headers for the image
909- metadata. Each main image metadata field is injected
910- as a HTTP header with key 'x-image-meta-<FIELD>' except
911- for the properties field, which is further broken out
912- into a set of 'x-image-meta-property-<KEY>' headers
913-
914- :param response: The Webob Response object
915- :param image_meta: Mapping of image metadata
916- """
917- headers = utils.image_meta_to_http_headers(image_meta)
918-
919- for k, v in headers.items():
920- response.headers[k] = v
921-
922- def _get_image_location(self, image_meta):
923- """Build a relative url to reach the image defined by image_meta."""
924- return "/v1/images/%s" % image_meta['id']
925-
926- def meta(self, response, result):
927- image_meta = result['image_meta']
928- self._inject_image_meta_headers(response, image_meta)
929- self._inject_location_header(response, image_meta)
930- self._inject_checksum_header(response, image_meta)
931- return response
932-
933- def image_send_notification(self, bytes_written, expected_size,
934- image_meta, request):
935- """Send an image.send message to the notifier."""
936- try:
937- context = request.context
938- payload = {
939- 'bytes_sent': bytes_written,
940- 'image_id': image_meta['id'],
941- 'owner_id': image_meta['owner'],
942- 'receiver_tenant_id': context.tenant,
943- 'receiver_user_id': context.user,
944- 'destination_ip': request.remote_addr,
945- }
946- if bytes_written != expected_size:
947- self.notifier.error('image.send', payload)
948- else:
949- self.notifier.info('image.send', payload)
950- except Exception, err:
951- msg = _("An error occurred during image.send"
952- " notification: %(err)s") % locals()
953- logger.error(msg)
954-
955- def show(self, response, result):
956- image_meta = result['image_meta']
957- image_id = image_meta['id']
958-
959- # We use a secondary iterator here to wrap the
960- # iterator coming back from the store driver in
961- # order to check for disconnections from the backend
962- # storage connections and log an error if the size of
963- # the transferred image is not the same as the expected
964- # size of the image file. See LP Bug #882585.
965- def checked_iter(image_id, expected_size, image_iter):
966- bytes_written = 0
967-
968- def notify_image_sent_hook(env):
969- self.image_send_notification(bytes_written, expected_size,
970- image_meta, response.request)
971-
972- # Add hook to process after response is fully sent
973- if 'eventlet.posthooks' in response.request.environ:
974- response.request.environ['eventlet.posthooks'].append(
975- (notify_image_sent_hook, (), {}))
976-
977- try:
978- for chunk in image_iter:
979- yield chunk
980- bytes_written += len(chunk)
981- except Exception, err:
982- msg = _("An error occurred reading from backend storage "
983- "for image %(image_id): %(err)s") % locals()
984- logger.error(msg)
985- raise
986-
987- if expected_size != bytes_written:
988- msg = _("Backend storage for image %(image_id)s "
989- "disconnected after writing only %(bytes_written)d "
990- "bytes") % locals()
991- logger.error(msg)
992- raise IOError(errno.EPIPE, _("Corrupt image download for "
993- "image %(image_id)s") % locals())
994-
995- image_iter = result['image_iterator']
996- # image_meta['size'] is a str
997- expected_size = int(image_meta['size'])
998- response.app_iter = checked_iter(image_id, expected_size, image_iter)
999- # Using app_iter blanks content-length, so we set it here...
1000- response.headers['Content-Length'] = image_meta['size']
1001- response.headers['Content-Type'] = 'application/octet-stream'
1002-
1003- self._inject_image_meta_headers(response, image_meta)
1004- self._inject_location_header(response, image_meta)
1005- self._inject_checksum_header(response, image_meta)
1006-
1007- return response
1008-
1009- def update(self, response, result):
1010- image_meta = result['image_meta']
1011- response.body = self.to_json(dict(image=image_meta))
1012- response.headers['Content-Type'] = 'application/json'
1013- self._inject_location_header(response, image_meta)
1014- self._inject_checksum_header(response, image_meta)
1015- return response
1016-
1017- def create(self, response, result):
1018- image_meta = result['image_meta']
1019- response.status = 201
1020- response.headers['Content-Type'] = 'application/json'
1021- response.body = self.to_json(dict(image=image_meta))
1022- self._inject_location_header(response, image_meta)
1023- self._inject_checksum_header(response, image_meta)
1024- return response
1025-
1026-
1027-def create_resource(conf):
1028- """Images resource factory method"""
1029- deserializer = ImageDeserializer()
1030- serializer = ImageSerializer(conf)
1031- return wsgi.Resource(Controller(conf), deserializer, serializer)
1032
1033=== modified file '.pc/applied-patches'
1034--- .pc/applied-patches 2012-11-08 07:19:39 +0000
1035+++ .pc/applied-patches 2012-12-18 14:13:22 +0000
1036@@ -3,4 +3,3 @@
1037 disable-network-for-docs.patch
1038 disable_db_table_auto_create.patch
1039 fix_migration_012_foreign_keys.patch
1040-CVE-2012-4573.patch
1041
1042=== modified file '.pc/fix_migration_012_foreign_keys.patch/Authors'
1043--- .pc/fix_migration_012_foreign_keys.patch/Authors 2012-06-24 03:14:33 +0000
1044+++ .pc/fix_migration_012_foreign_keys.patch/Authors 2012-12-18 14:13:22 +0000
1045@@ -54,6 +54,7 @@
1046 Rick Harris <rconradharris@gmail.com>
1047 Reynolds Chin <benzwt@gmail.com>
1048 Russell Bryant <rbryant@redhat.com>
1049+Sean Dague <sdague@linux.vnet.ibm.com>
1050 Soren Hansen <soren.hansen@rackspace.com>
1051 Stuart McLaren <stuart.mclaren@hp.com>
1052 Taku Fukushima <tfukushima@dcl.info.waseda.ac.jp>
1053
1054=== modified file 'Authors'
1055--- Authors 2012-06-24 03:14:33 +0000
1056+++ Authors 2012-12-18 14:13:22 +0000
1057@@ -55,6 +55,7 @@
1058 Reynolds Chin <benzwt@gmail.com>
1059 Russell Bryant <rbryant@redhat.com>
1060 Sam Morrison <sorrison@gmail.com>
1061+Sean Dague <sdague@linux.vnet.ibm.com>
1062 Soren Hansen <soren.hansen@rackspace.com>
1063 Stuart McLaren <stuart.mclaren@hp.com>
1064 Taku Fukushima <tfukushima@dcl.info.waseda.ac.jp>
1065
1066=== added file 'PKG-INFO'
1067--- PKG-INFO 1970-01-01 00:00:00 +0000
1068+++ PKG-INFO 2012-12-18 14:13:22 +0000
1069@@ -0,0 +1,15 @@
1070+Metadata-Version: 1.1
1071+Name: glance
1072+Version: 2012.1.3
1073+Summary: The Glance project provides services for discovering, registering, and retrieving virtual machine images
1074+Home-page: http://glance.openstack.org/
1075+Author: OpenStack
1076+Author-email: openstack@lists.launchpad.net
1077+License: Apache License (2.0)
1078+Description: UNKNOWN
1079+Platform: UNKNOWN
1080+Classifier: Development Status :: 4 - Beta
1081+Classifier: License :: OSI Approved :: Apache Software License
1082+Classifier: Operating System :: POSIX :: Linux
1083+Classifier: Programming Language :: Python :: 2.6
1084+Classifier: Environment :: No Input/Output (Daemon)
1085
1086=== modified file 'debian/changelog'
1087--- debian/changelog 2012-11-08 07:19:39 +0000
1088+++ debian/changelog 2012-12-18 14:13:22 +0000
1089@@ -1,3 +1,21 @@
1090+glance (2012.1.4+stable-20121217-efd7e75b-0ubuntu1) precise-proposed; urgency=low
1091+
1092+ [ Adam Gandelman ]
1093+ * debian/glance-{registry, api}.logrotate: Fix incorrect logfile
1094+ locations. (LP: #1049314)
1095+
1096+ [ Yolanda Robla Mota ]
1097+ * Resynchronize with stable/essex (efd7e75b):
1098+ - [efd7e75] Non-admin users can cause public glance images to be deleted
1099+ from the backend storage repository (CVE-2012-4573)
1100+ - [e6be061] Jenkins jobs fail because of incompatibility between sqlalchemy-
1101+ migrate and the newest sqlalchemy-0.8.0b1 (LP: #1073569)
1102+
1103+ * Dropped patches, superseeded by snapshot:
1104+ - debian/patches/CVE-2012-4573.patch: [efd7e75]
1105+
1106+ -- Yolanda Robla Mota <yolanda.robla@canonical.com> Mon, 17 Dec 2012 10:56:46 +0000
1107+
1108 glance (2012.1.3+stable~20120821-120fcf-0ubuntu1.2) precise-security; urgency=low
1109
1110 * SECURITY UPDATE: deletion of arbitrary public and shared images via
1111
1112=== modified file 'debian/glance-api.logrotate'
1113--- debian/glance-api.logrotate 2012-01-13 10:50:40 +0000
1114+++ debian/glance-api.logrotate 2012-12-18 14:13:22 +0000
1115@@ -1,4 +1,4 @@
1116-/var/log/glance/glance-api.log {
1117+/var/log/glance/api.log {
1118 daily
1119 missingok
1120 compress
1121
1122=== modified file 'debian/glance-registry.logrotate'
1123--- debian/glance-registry.logrotate 2012-01-13 10:50:40 +0000
1124+++ debian/glance-registry.logrotate 2012-12-18 14:13:22 +0000
1125@@ -1,4 +1,4 @@
1126-/var/log/glance/glance-api.log {
1127+/var/log/glance/registry.log {
1128 daily
1129 missingok
1130 compress
1131
1132=== removed file 'debian/patches/CVE-2012-4573.patch'
1133--- debian/patches/CVE-2012-4573.patch 2012-11-08 07:19:39 +0000
1134+++ debian/patches/CVE-2012-4573.patch 1970-01-01 00:00:00 +0000
1135@@ -1,35 +0,0 @@
1136-From efd7e75b1f419a52c7103c7840e24af8e5deb29d Mon Sep 17 00:00:00 2001
1137-From: Brian Waldon <bcwaldon@gmail.com>
1138-Date: Wed, 7 Nov 2012 10:06:43 -0500
1139-Subject: [PATCH] Ensure image owned by user before delayed_deletion
1140-
1141-Fixes bug 1065187.
1142-
1143-Change-Id: Icf2f117a094c712bad645ef5f297e9f7da994c84
1144----
1145- glance/api/v1/images.py | 9 +++++++++
1146- 1 file changed, 9 insertions(+)
1147-
1148-diff --git a/glance/api/v1/images.py b/glance/api/v1/images.py
1149-index 9bedf20..1a8eac8 100644
1150---- a/glance/api/v1/images.py
1151-+++ b/glance/api/v1/images.py
1152-@@ -727,6 +727,15 @@ class Controller(controller.BaseController):
1153- content_type="text/plain")
1154-
1155- image = self.get_image_meta_or_404(req, id)
1156-+
1157-+ if not (req.context.is_admin
1158-+ or image['owner'] == None
1159-+ or image['owner'] == req.context.owner):
1160-+ msg = _("Unable to delete image you do not own")
1161-+ logger.debug(msg)
1162-+ raise HTTPForbidden(msg, request=req,
1163-+ content_type="text/plain")
1164-+
1165- if image['protected']:
1166- msg = _("Image is protected")
1167- logger.debug(msg)
1168---
1169-1.7.9.5
1170-
1171
1172=== modified file 'debian/patches/fix_migration_012_foreign_keys.patch'
1173--- debian/patches/fix_migration_012_foreign_keys.patch 2012-04-12 15:02:08 +0000
1174+++ debian/patches/fix_migration_012_foreign_keys.patch 2012-12-18 14:13:22 +0000
1175@@ -1,5 +1,3 @@
1176-From: 4e35808f94db8c17a20608e137e2940195821fac
1177-Author: Sam Morrison <sorrison@gmail.com>
1178 Date: Mon Apr 2 11:22:10 2012 +1000
1179 Bug: https://bugs.launchpad.net/glance/+bug/976908
1180 Origin, upstream: https://review.openstack.org/#change,6061
1181@@ -8,25 +6,21 @@
1182
1183 * fix bug 976908
1184
1185-Change-Id: I0248f825396d08688238e6d2ef37c8fcb49e8c9d
1186-
1187-
1188-diff --git a/Authors b/Authors
1189-index 8158c2a..d9c9302 100644
1190---- a/Authors
1191-+++ b/Authors
1192-@@ -52,6 +52,7 @@ Rick Clark <rick@openstack.org>
1193+Change-Id: I0248f825396d08688238e6d2ef37c8fcb49e8c9
1194+diff -Naurp glance-2012.1.3.orig/Authors glance-2012.1.3/Authors
1195+--- glance-2012.1.3.orig/Authors 2012-11-26 15:19:40.000000000 -0600
1196++++ glance-2012.1.3/Authors 2012-11-26 15:42:42.691087411 -0600
1197+@@ -54,6 +54,7 @@ Rick Clark <rick@openstack.org>
1198 Rick Harris <rconradharris@gmail.com>
1199 Reynolds Chin <benzwt@gmail.com>
1200 Russell Bryant <rbryant@redhat.com>
1201 +Sam Morrison <sorrison@gmail.com>
1202+ Sean Dague <sdague@linux.vnet.ibm.com>
1203 Soren Hansen <soren.hansen@rackspace.com>
1204 Stuart McLaren <stuart.mclaren@hp.com>
1205- Taku Fukushima <tfukushima@dcl.info.waseda.ac.jp>
1206-diff --git a/glance/registry/db/migrate_repo/versions/012_id_to_uuid.py b/glance/registry/db/migrate_repo/versions/012_id_to_uuid.py
1207-index fe6bf86..8db18c1 100644
1208---- a/glance/registry/db/migrate_repo/versions/012_id_to_uuid.py
1209-+++ b/glance/registry/db/migrate_repo/versions/012_id_to_uuid.py
1210+diff -Naurp glance-2012.1.3.orig/glance/registry/db/migrate_repo/versions/012_id_to_uuid.py glance-2012.1.3/glance/registry/db/migrate_repo/versions/012_id_to_uuid.py
1211+--- glance-2012.1.3.orig/glance/registry/db/migrate_repo/versions/012_id_to_uuid.py 2012-11-26 15:19:40.000000000 -0600
1212++++ glance-2012.1.3/glance/registry/db/migrate_repo/versions/012_id_to_uuid.py 2012-11-26 15:41:59.231087390 -0600
1213 @@ -225,18 +225,24 @@ def _get_table(table_name, metadata):
1214
1215 def _get_foreign_keys(t_images, t_image_members, t_image_properties):
1216@@ -36,27 +30,29 @@
1217 + foreign_keys = []
1218 + if t_image_members.foreign_keys:
1219 + img_members_fk_name = list(t_image_members.foreign_keys)[0].name
1220-
1221-- fk1 = migrate.ForeignKeyConstraint([t_image_members.c.image_id],
1222-- [t_images.c.id],
1223-- name=image_members_fk_name)
1224++
1225 + fk1 = migrate.ForeignKeyConstraint([t_image_members.c.image_id],
1226 + [t_images.c.id],
1227 + name=img_members_fk_name)
1228 + foreign_keys.append(fk1)
1229-
1230-- fk2 = migrate.ForeignKeyConstraint([t_image_properties.c.image_id],
1231-- [t_images.c.id],
1232-- name=image_properties_fk_name)
1233++
1234 + if t_image_properties.foreign_keys:
1235 + img_properties_fk_name = list(t_image_properties.foreign_keys)[0].name
1236-
1237-- return fk1, fk2
1238++
1239 + fk2 = migrate.ForeignKeyConstraint([t_image_properties.c.image_id],
1240 + [t_images.c.id],
1241 + name=img_properties_fk_name)
1242 + foreign_keys.append(fk2)
1243-+
1244+
1245+- fk1 = migrate.ForeignKeyConstraint([t_image_members.c.image_id],
1246+- [t_images.c.id],
1247+- name=image_members_fk_name)
1248+-
1249+- fk2 = migrate.ForeignKeyConstraint([t_image_properties.c.image_id],
1250+- [t_images.c.id],
1251+- name=image_properties_fk_name)
1252+-
1253+- return fk1, fk2
1254 + return foreign_keys
1255
1256
1257
1258=== modified file 'debian/patches/series'
1259--- debian/patches/series 2012-11-08 07:19:39 +0000
1260+++ debian/patches/series 2012-12-18 14:13:22 +0000
1261@@ -3,4 +3,3 @@
1262 disable-network-for-docs.patch
1263 disable_db_table_auto_create.patch
1264 fix_migration_012_foreign_keys.patch
1265-CVE-2012-4573.patch
1266
1267=== added directory 'glance.egg-info'
1268=== added file 'glance.egg-info/PKG-INFO'
1269--- glance.egg-info/PKG-INFO 1970-01-01 00:00:00 +0000
1270+++ glance.egg-info/PKG-INFO 2012-12-18 14:13:22 +0000
1271@@ -0,0 +1,15 @@
1272+Metadata-Version: 1.1
1273+Name: glance
1274+Version: 2012.1.3
1275+Summary: The Glance project provides services for discovering, registering, and retrieving virtual machine images
1276+Home-page: http://glance.openstack.org/
1277+Author: OpenStack
1278+Author-email: openstack@lists.launchpad.net
1279+License: Apache License (2.0)
1280+Description: UNKNOWN
1281+Platform: UNKNOWN
1282+Classifier: Development Status :: 4 - Beta
1283+Classifier: License :: OSI Approved :: Apache Software License
1284+Classifier: Operating System :: POSIX :: Linux
1285+Classifier: Programming Language :: Python :: 2.6
1286+Classifier: Environment :: No Input/Output (Daemon)
1287
1288=== added file 'glance.egg-info/SOURCES.txt'
1289--- glance.egg-info/SOURCES.txt 1970-01-01 00:00:00 +0000
1290+++ glance.egg-info/SOURCES.txt 2012-12-18 14:13:22 +0000
1291@@ -0,0 +1,217 @@
1292+Authors
1293+HACKING.rst
1294+LICENSE
1295+MANIFEST.in
1296+README.rst
1297+babel.cfg
1298+pylintrc
1299+run_tests.py
1300+run_tests.sh
1301+setup.cfg
1302+setup.py
1303+tox.ini
1304+bin/glance
1305+bin/glance-api
1306+bin/glance-cache-cleaner
1307+bin/glance-cache-manage
1308+bin/glance-cache-prefetcher
1309+bin/glance-cache-pruner
1310+bin/glance-control
1311+bin/glance-manage
1312+bin/glance-registry
1313+bin/glance-scrubber
1314+doc/source/architecture.rst
1315+doc/source/authentication.rst
1316+doc/source/cache.rst
1317+doc/source/client.rst
1318+doc/source/conf.py
1319+doc/source/configuring.rst
1320+doc/source/controllingservers.rst
1321+doc/source/formats.rst
1322+doc/source/glance.rst
1323+doc/source/glanceapi.rst
1324+doc/source/identifiers.rst
1325+doc/source/index.rst
1326+doc/source/installing.rst
1327+doc/source/notifications.rst
1328+doc/source/policies.rst
1329+doc/source/statuses.rst
1330+doc/source/_static/basic.css
1331+doc/source/_static/default.css
1332+doc/source/_static/jquery.tweet.js
1333+doc/source/_static/tweaks.css
1334+doc/source/_templates/.placeholder
1335+doc/source/_theme/layout.html
1336+doc/source/_theme/theme.conf
1337+doc/source/man/glance.rst
1338+doc/source/man/glanceapi.rst
1339+doc/source/man/glancecachecleaner.rst
1340+doc/source/man/glancecachemanage.rst
1341+doc/source/man/glancecacheprefetcher.rst
1342+doc/source/man/glancecachepruner.rst
1343+doc/source/man/glancecontrol.rst
1344+doc/source/man/glancemanage.rst
1345+doc/source/man/glanceregistry.rst
1346+doc/source/man/glancescrubber.rst
1347+etc/glance-api-paste.ini
1348+etc/glance-api.conf
1349+etc/glance-cache-paste.ini
1350+etc/glance-cache.conf
1351+etc/glance-registry-paste.ini
1352+etc/glance-registry.conf
1353+etc/glance-scrubber-paste.ini
1354+etc/glance-scrubber.conf
1355+etc/logging.cnf.sample
1356+etc/policy.json
1357+glance/__init__.py
1358+glance/client.py
1359+glance/vcsversion.py
1360+glance/version.py
1361+glance.egg-info/PKG-INFO
1362+glance.egg-info/SOURCES.txt
1363+glance.egg-info/dependency_links.txt
1364+glance.egg-info/top_level.txt
1365+glance/api/__init__.py
1366+glance/api/cached_images.py
1367+glance/api/policy.py
1368+glance/api/versions.py
1369+glance/api/middleware/__init__.py
1370+glance/api/middleware/cache.py
1371+glance/api/middleware/cache_manage.py
1372+glance/api/middleware/version_negotiation.py
1373+glance/api/v1/__init__.py
1374+glance/api/v1/controller.py
1375+glance/api/v1/filters.py
1376+glance/api/v1/images.py
1377+glance/api/v1/members.py
1378+glance/api/v1/router.py
1379+glance/common/__init__.py
1380+glance/common/animation.py
1381+glance/common/auth.py
1382+glance/common/cfg.py
1383+glance/common/client.py
1384+glance/common/config.py
1385+glance/common/context.py
1386+glance/common/crypt.py
1387+glance/common/exception.py
1388+glance/common/policy.py
1389+glance/common/utils.py
1390+glance/common/wsgi.py
1391+glance/image_cache/__init__.py
1392+glance/image_cache/cleaner.py
1393+glance/image_cache/prefetcher.py
1394+glance/image_cache/pruner.py
1395+glance/image_cache/queue_image.py
1396+glance/image_cache/drivers/__init__.py
1397+glance/image_cache/drivers/base.py
1398+glance/image_cache/drivers/sqlite.py
1399+glance/image_cache/drivers/xattr.py
1400+glance/locale/__init__.py
1401+glance/locale/glance.pot
1402+glance/notifier/__init__.py
1403+glance/notifier/notify_kombu.py
1404+glance/notifier/notify_log.py
1405+glance/notifier/notify_noop.py
1406+glance/notifier/notify_qpid.py
1407+glance/notifier/strategy.py
1408+glance/registry/__init__.py
1409+glance/registry/client.py
1410+glance/registry/context.py
1411+glance/registry/api/__init__.py
1412+glance/registry/api/v1/__init__.py
1413+glance/registry/api/v1/images.py
1414+glance/registry/api/v1/members.py
1415+glance/registry/db/__init__.py
1416+glance/registry/db/api.py
1417+glance/registry/db/migration.py
1418+glance/registry/db/models.py
1419+glance/registry/db/migrate_repo/README
1420+glance/registry/db/migrate_repo/__init__.py
1421+glance/registry/db/migrate_repo/manage.py
1422+glance/registry/db/migrate_repo/migrate.cfg
1423+glance/registry/db/migrate_repo/schema.py
1424+glance/registry/db/migrate_repo/versions/001_add_images_table.py
1425+glance/registry/db/migrate_repo/versions/002_add_image_properties_table.py
1426+glance/registry/db/migrate_repo/versions/003_add_disk_format.py
1427+glance/registry/db/migrate_repo/versions/003_sqlite_downgrade.sql
1428+glance/registry/db/migrate_repo/versions/003_sqlite_upgrade.sql
1429+glance/registry/db/migrate_repo/versions/004_add_checksum.py
1430+glance/registry/db/migrate_repo/versions/005_size_big_integer.py
1431+glance/registry/db/migrate_repo/versions/006_key_to_name.py
1432+glance/registry/db/migrate_repo/versions/006_mysql_downgrade.sql
1433+glance/registry/db/migrate_repo/versions/006_mysql_upgrade.sql
1434+glance/registry/db/migrate_repo/versions/006_sqlite_downgrade.sql
1435+glance/registry/db/migrate_repo/versions/006_sqlite_upgrade.sql
1436+glance/registry/db/migrate_repo/versions/007_add_owner.py
1437+glance/registry/db/migrate_repo/versions/008_add_image_members_table.py
1438+glance/registry/db/migrate_repo/versions/009_add_mindisk_and_minram.py
1439+glance/registry/db/migrate_repo/versions/010_default_update_at.py
1440+glance/registry/db/migrate_repo/versions/011_make_mindisk_and_minram_notnull.py
1441+glance/registry/db/migrate_repo/versions/012_id_to_uuid.py
1442+glance/registry/db/migrate_repo/versions/013_add_protected.py
1443+glance/registry/db/migrate_repo/versions/013_sqlite_downgrade.sql
1444+glance/registry/db/migrate_repo/versions/__init__.py
1445+glance/store/__init__.py
1446+glance/store/base.py
1447+glance/store/filesystem.py
1448+glance/store/http.py
1449+glance/store/location.py
1450+glance/store/rbd.py
1451+glance/store/s3.py
1452+glance/store/scrubber.py
1453+glance/store/swift.py
1454+glance/tests/__init__.py
1455+glance/tests/logcapture.py
1456+glance/tests/stubs.py
1457+glance/tests/utils.py
1458+glance/tests/etc/policy.json
1459+glance/tests/functional/__init__.py
1460+glance/tests/functional/store_utils.py
1461+glance/tests/functional/test_api.py
1462+glance/tests/functional/test_bin_glance.py
1463+glance/tests/functional/test_bin_glance_cache_manage.py
1464+glance/tests/functional/test_cache_middleware.py
1465+glance/tests/functional/test_client_exceptions.py
1466+glance/tests/functional/test_client_redirects.py
1467+glance/tests/functional/test_copy_to_file.py
1468+glance/tests/functional/test_logging.py
1469+glance/tests/functional/test_misc.py
1470+glance/tests/functional/test_multiprocessing.py
1471+glance/tests/functional/test_rbd.py
1472+glance/tests/functional/test_respawn.py
1473+glance/tests/functional/test_s3.py
1474+glance/tests/functional/test_scrubber.py
1475+glance/tests/functional/test_sqlite.py
1476+glance/tests/functional/test_ssl.py
1477+glance/tests/functional/test_swift.py
1478+glance/tests/unit/__init__.py
1479+glance/tests/unit/base.py
1480+glance/tests/unit/test_api.py
1481+glance/tests/unit/test_auth.py
1482+glance/tests/unit/test_cfg.py
1483+glance/tests/unit/test_clients.py
1484+glance/tests/unit/test_config.py
1485+glance/tests/unit/test_context.py
1486+glance/tests/unit/test_db.py
1487+glance/tests/unit/test_filesystem_store.py
1488+glance/tests/unit/test_http_store.py
1489+glance/tests/unit/test_image_cache.py
1490+glance/tests/unit/test_migrations.conf
1491+glance/tests/unit/test_migrations.py
1492+glance/tests/unit/test_misc.py
1493+glance/tests/unit/test_notifier.py
1494+glance/tests/unit/test_s3_store.py
1495+glance/tests/unit/test_skip_examples.py
1496+glance/tests/unit/test_store_location.py
1497+glance/tests/unit/test_swift_store.py
1498+glance/tests/unit/test_utils.py
1499+glance/tests/unit/test_versions.py
1500+glance/tests/unit/test_wsgi.py
1501+glance/tests/var/ca.crt
1502+glance/tests/var/certificate.crt
1503+glance/tests/var/privatekey.key
1504+tools/install_venv.py
1505+tools/migrate_image_owners.py
1506+tools/pip-requires
1507+tools/test-requires
1508+tools/with_venv.sh
1509\ No newline at end of file
1510
1511=== added file 'glance.egg-info/dependency_links.txt'
1512--- glance.egg-info/dependency_links.txt 1970-01-01 00:00:00 +0000
1513+++ glance.egg-info/dependency_links.txt 2012-12-18 14:13:22 +0000
1514@@ -0,0 +1,1 @@
1515+
1516
1517=== added file 'glance.egg-info/top_level.txt'
1518--- glance.egg-info/top_level.txt 1970-01-01 00:00:00 +0000
1519+++ glance.egg-info/top_level.txt 2012-12-18 14:13:22 +0000
1520@@ -0,0 +1,1 @@
1521+glance
1522
1523=== added file 'glance/vcsversion.py'
1524--- glance/vcsversion.py 1970-01-01 00:00:00 +0000
1525+++ glance/vcsversion.py 2012-12-18 14:13:22 +0000
1526@@ -0,0 +1,7 @@
1527+
1528+# This file is automatically generated by setup.py, So don't edit it. :)
1529+version_info = {
1530+ 'branch_nick': 'stable/essex',
1531+ 'revision_id': 'efd7e75b1f419a52c7103c7840e24af8e5deb29d',
1532+ 'revno': 1464
1533+}
1534
1535=== modified file 'setup.cfg'
1536--- setup.cfg 2012-06-24 03:14:33 +0000
1537+++ setup.cfg 2012-12-18 14:13:22 +0000
1538@@ -23,14 +23,11 @@
1539 output_file = glance/locale/glance.pot
1540
1541 [nosetests]
1542-# NOTE(jkoelker) To run the test suite under nose install the following
1543-# coverage http://pypi.python.org/pypi/coverage
1544-# tissue http://pypi.python.org/pypi/tissue (pep8 checker)
1545-# openstack-nose https://github.com/jkoelker/openstack-nose
1546-verbosity=2
1547-detailed-errors=1
1548-with-openstack=1
1549-openstack-red=0.05
1550-openstack-yellow=0.025
1551-openstack-show-elapsed=1
1552-openstack-color=1
1553+verbosity = 2
1554+detailed-errors = 1
1555+with-openstack = 1
1556+openstack-red = 0.05
1557+openstack-yellow = 0.025
1558+openstack-show-elapsed = 1
1559+openstack-color = 1
1560+
1561
1562=== modified file 'tools/pip-requires'
1563--- tools/pip-requires 2012-06-24 03:14:33 +0000
1564+++ tools/pip-requires 2012-12-18 14:13:22 +0000
1565@@ -3,7 +3,7 @@
1566 # package to get the right headers...
1567 greenlet>=0.3.1
1568
1569-SQLAlchemy>=0.7
1570+SQLAlchemy>=0.7,<=0.7.9
1571 anyjson
1572 eventlet>=0.9.12
1573 PasteDeploy

Subscribers

People subscribed via source and target branches

to all changes: