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

Proposed by Yolanda Robla
Status: Rejected
Rejected by: James Page
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 Disapprove
Review via email: mp+140451@code.launchpad.net

This proposal supersedes a proposal from 2012-12-18.

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

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
Revision history for this message
James Page (james-page) wrote :

As discussed with Yolanda on IRC, this update is pretty much no-change as the security issue is already patched and the jenkins problem does not effect Ubuntu.

review: Disapprove

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:12:30 +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:12:30 +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:12:30 +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:12:30 +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:12:30 +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:12:30 +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:12:30 +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:12:30 +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:12:30 +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:12:30 +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:12:30 +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:12:30 +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:12:30 +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:12:30 +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:12:30 +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:12:30 +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: