commit:     9923cf35ae299d9643c1122dfea857e5f76667f8
Author:     Matthew Thode <prometheanfire <AT> gentoo <DOT> org>
AuthorDate: Thu Feb  4 17:32:33 2016 +0000
Commit:     Matt Thode <prometheanfire <AT> gentoo <DOT> org>
CommitDate: Thu Feb  4 17:33:45 2016 +0000
URL:        https://gitweb.gentoo.org/repo/gentoo.git/commit/?id=9923cf35

app-admin/glance: fix bug 573844 CVE-2016-0757

Package-Manager: portage-2.2.26

 .../glance/files/CVE-2015-5286_2015.1.1.patch      | 137 ---------
 .../glance/files/cve-2015-5163-stable-kilo.patch   | 260 ----------------
 .../glance/files/cve-2015-5251-stable-kilo.patch   | 192 ------------
 .../files/cve-2016-0757-stable-liberty.patch       | 332 +++++++++++++++++++++
 .../files/glance-2013.2-sphinx_mapping.patch       |  12 -
 app-admin/glance/glance-11.0.1-r1.ebuild           | 229 ++++++++++++++
 6 files changed, 561 insertions(+), 601 deletions(-)

diff --git a/app-admin/glance/files/CVE-2015-5286_2015.1.1.patch 
b/app-admin/glance/files/CVE-2015-5286_2015.1.1.patch
deleted file mode 100644
index 0478135..0000000
--- a/app-admin/glance/files/CVE-2015-5286_2015.1.1.patch
+++ /dev/null
@@ -1,137 +0,0 @@
-From 5bebd513fa71edcdb84f7dec7b16f3523c0c1092 Mon Sep 17 00:00:00 2001
-From: Mike Fedosin <mfedo...@mirantis.com>
-Date: Sun, 20 Sep 2015 17:01:22 +0300
-Subject: Cleanup chunks for deleted image if token expired
-
-In patch I47229b366c25367ec1bd48aec684e0880f3dfe60 it was
-introduced the logic that if image was deleted during file
-upload when we want to update image status from 'saving'
-to 'active' it's expected to get Duplicate error and delete
-stale chunks after that. But if user's token is expired
-there will be Unathorized exception and chunks will stay
-in store and clog it.
-And when, the upload operation for such an image is
-completed the operator configured quota can be exceeded.
-
-This patch fixes the issue of left over chunks for an image
-which was deleted from saving status, by correctly handle
-auth exceptions from registry server.
-
-Partial-bug: #1498163
-
-Conflicts:
-       glance/api/v1/upload_utils.py
-        (Kilo catches NotFound instead of ImagenotFound)
-
-Change-Id: I17a66eca55bfb83107046910e69c4da01415deec
-(cherry picked from commit 98a8832777a0639a4031e52c69f0d565b3f500c5)
-
-diff --git a/glance/api/v1/upload_utils.py b/glance/api/v1/upload_utils.py
-index 7adb2dc..ad4f724 100644
---- a/glance/api/v1/upload_utils.py
-+++ b/glance/api/v1/upload_utils.py
-@@ -171,6 +171,14 @@ def upload_data_to_store(req, image_meta, image_data, 
store, notifier):
-                     raise exception.NotFound()
-                 else:
-                     raise
-+
-+        except exception.NotAuthenticated as e:
-+            # Delete image data due to possible token expiration.
-+            LOG.debug("Authentication error - the token may have "
-+                      "expired during file upload. Deleting image data for "
-+                      " %s " % image_id)
-+            initiate_deletion(req, location_data, image_id)
-+            raise webob.exc.HTTPUnauthorized(explanation=e.msg, request=req)
-         except exception.NotFound:
-             msg = _LI("Image %s could not be found after upload. The image 
may"
-                       " have been deleted during the upload.") % image_id
-diff --git a/glance/api/v2/image_data.py b/glance/api/v2/image_data.py
-index 4025eeb..9967662 100644
---- a/glance/api/v2/image_data.py
-+++ b/glance/api/v2/image_data.py
-@@ -88,7 +88,19 @@ class ImageDataController(object):
-                 raise webob.exc.HTTPGone(explanation=msg,
-                                          request=req,
-                                          content_type='text/plain')
--
-+            except exception.NotAuthenticated:
-+                msg = (_("Authentication error - the token may have "
-+                         "expired during file upload. Deleting image data for 
"
-+                         "%s.") % image_id)
-+                LOG.debug(msg)
-+                try:
-+                    image.delete()
-+                except exception.NotAuthenticated:
-+                    # NOTE: Ignore this exception
-+                    pass
-+                raise webob.exc.HTTPUnauthorized(explanation=msg,
-+                                                 request=req,
-+                                                 content_type='text/plain')
-         except ValueError as e:
-             LOG.debug("Cannot save data for image %(id)s: %(e)s",
-                       {'id': image_id, 'e': utils.exception_to_str(e)})
-diff --git a/glance/tests/unit/v1/test_upload_utils.py 
b/glance/tests/unit/v1/test_upload_utils.py
-index 1afaf00..8d05515 100644
---- a/glance/tests/unit/v1/test_upload_utils.py
-+++ b/glance/tests/unit/v1/test_upload_utils.py
-@@ -323,3 +323,29 @@ class TestUploadUtils(base.StoreClearingUnitTest):
-                                   'metadata': {}}, image_meta['id'])
-                         mock_safe_kill.assert_called_once_with(
-                             req, image_meta['id'], 'saving')
-+
-+    @mock.patch.object(registry, 'update_image_metadata',
-+                       side_effect=exception.NotAuthenticated)
-+    @mock.patch.object(upload_utils, 'initiate_deletion')
-+    def test_activate_image_with_expired_token(
-+            self, mocked_delete, mocked_update):
-+        """Test token expiration during image upload.
-+
-+        If users token expired before image was uploaded then if auth error
-+        was caught from registry during changing image status from 'saving'
-+        to 'active' then it's required to delete all image data.
-+        """
-+        context = mock.Mock()
-+        req = mock.Mock()
-+        req.context = context
-+        with self._get_store_and_notifier() as (location, checksum, 
image_meta,
-+                                                image_data, store, notifier,
-+                                                update_data):
-+            self.assertRaises(webob.exc.HTTPUnauthorized,
-+                              upload_utils.upload_data_to_store,
-+                              req, image_meta, image_data, store, notifier)
-+            self.assertEqual(2, mocked_update.call_count)
-+            mocked_delete.assert_called_once_with(
-+                req,
-+                {'url': 'file://foo/bar', 'status': 'active', 'metadata': {}},
-+                'c80a1a6c-bd1f-41c5-90ee-81afedb1d58d')
-diff --git a/glance/tests/unit/v2/test_image_data_resource.py 
b/glance/tests/unit/v2/test_image_data_resource.py
-index bc8891e..7458eda 100644
---- a/glance/tests/unit/v2/test_image_data_resource.py
-+++ b/glance/tests/unit/v2/test_image_data_resource.py
-@@ -192,6 +192,23 @@ class TestImagesController(base.StoreClearingUnitTest):
-         self.assertRaises(webob.exc.HTTPBadRequest, self.controller.upload,
-                           request, unit_test_utils.UUID1, 'YYYY', 4)
- 
-+    def test_upload_with_expired_token(self):
-+        def side_effect(image, from_state=None):
-+            if from_state == 'saving':
-+                raise exception.NotAuthenticated()
-+
-+        mocked_save = mock.Mock(side_effect=side_effect)
-+        mocked_delete = mock.Mock()
-+        request = unit_test_utils.get_fake_request()
-+        image = FakeImage('abcd')
-+        image.delete = mocked_delete
-+        self.image_repo.result = image
-+        self.image_repo.save = mocked_save
-+        self.assertRaises(webob.exc.HTTPUnauthorized, self.controller.upload,
-+                          request, unit_test_utils.UUID1, 'YYYY', 4)
-+        self.assertEqual(3, mocked_save.call_count)
-+        mocked_delete.assert_called_once_with()
-+
-     def test_upload_non_existent_image_during_save_initiates_deletion(self):
-         def fake_save_not_found(self):
-             raise exception.NotFound()
--- 
-cgit v0.10.2
-

diff --git a/app-admin/glance/files/cve-2015-5163-stable-kilo.patch 
b/app-admin/glance/files/cve-2015-5163-stable-kilo.patch
deleted file mode 100644
index 91507c9..0000000
--- a/app-admin/glance/files/cve-2015-5163-stable-kilo.patch
+++ /dev/null
@@ -1,260 +0,0 @@
-From eb99e45829a1b4c93db5692bdbf636a86faa56c4 Mon Sep 17 00:00:00 2001
-From: Flavio Percoco <flape...@gmail.com>
-Date: Thu, 9 Jul 2015 14:44:04 +0200
-Subject: Don't import files with backed files
-
-There's a security issue where it'd be possible to import images with
-backed files using the task engine and then use/convert those to access
-system files or any other file in the system. An example of an attack
-would be to import an image with a backing file pointing to
-`/etc/passwd`, then convert it to raw and download the generated image.
-
-This patch forbids importing files with baking files entirely. It does
-that in the `_ImportToFS` task, which is the one that imports the image
-locally to then execute other tasks on it. It's not necessary for the
-`_ImportToStore` task because other tasks won't be executed when the
-image is imported in the final store.
-
-Change-Id: I35f43c3b3f326942fb53b7dadb94700ac4513494
-Closes-bug: #1471912
-(cherry picked from commit d529863a1e8d2307526bdb395b4aebe97f81603d)
-
-diff --git a/glance/async/flows/base_import.py 
b/glance/async/flows/base_import.py
-index 7656bde..d216aa8 100644
---- a/glance/async/flows/base_import.py
-+++ b/glance/async/flows/base_import.py
-@@ -13,12 +13,15 @@
- #    License for the specific language governing permissions and limitations
- #    under the License.
- 
-+import json
- import logging
- import os
- 
- import glance_store as store_api
- from glance_store import backend
-+from oslo_concurrency import processutils as putils
- from oslo_config import cfg
-+from oslo_utils import excutils
- import six
- from stevedore import named
- from taskflow.patterns import linear_flow as lf
-@@ -146,6 +149,29 @@ class _ImportToFS(task.Task):
-         data = script_utils.get_image_data_iter(self.uri)
- 
-         path = self.store.add(image_id, data, 0, context=None)[0]
-+
-+        try:
-+            # NOTE(flaper87): Consider moving this code to a common
-+            # place that other tasks can consume as well.
-+            stdout, stderr = putils.trycmd('qemu-img', 'info',
-+                                           '--output=json', path,
-+                                           log_errors=putils.LOG_ALL_ERRORS)
-+        except OSError as exc:
-+            with excutils.save_and_reraise_exception():
-+                msg = (_LE('Failed to execute security checks on the image '
-+                           '%(task_id)s: %(exc)s') %
-+                       {'task_id': self.task_id, 'exc': exc.message})
-+                LOG.error(msg)
-+
-+        metadata = json.loads(stdout)
-+
-+        backing_file = metadata.get('backing-filename')
-+        if backing_file is not None:
-+            msg = _("File %(path)s has invalid backing file "
-+                    "%(bfile)s, aborting.") % {'path': path,
-+                                               'bfile': backing_file}
-+            raise RuntimeError(msg)
-+
-         return path
- 
-     def revert(self, image_id, result=None, **kwargs):
-diff --git a/glance/tests/unit/async/flows/test_import.py 
b/glance/tests/unit/async/flows/test_import.py
-index 70f790c..4cf3d13 100644
---- a/glance/tests/unit/async/flows/test_import.py
-+++ b/glance/tests/unit/async/flows/test_import.py
-@@ -13,14 +13,17 @@
- #    License for the specific language governing permissions and limitations
- #    under the License.
- 
-+import json
- import mock
- import os
- import urllib2
- 
- import glance_store
-+from oslo_concurrency import processutils as putils
- from oslo_config import cfg
- from six.moves import cStringIO
- from taskflow import task
-+from taskflow.types import failure
- 
- import glance.async.flows.base_import as import_flow
- from glance.async import taskflow_executor
-@@ -106,16 +109,23 @@ class TestImportTask(test_utils.BaseTestCase):
- 
-         with mock.patch.object(script_utils, 'get_image_data_iter') as dmock:
-             dmock.return_value = cStringIO("TEST_IMAGE")
--            executor.begin_processing(self.task.task_id)
--            image_path = os.path.join(self.test_dir, self.image.image_id)
--            tmp_image_path = os.path.join(self.work_dir,
--                                          "%s.tasks_import" % image_path)
--            self.assertFalse(os.path.exists(tmp_image_path))
--            self.assertTrue(os.path.exists(image_path))
--            self.assertEqual(1, len(list(self.image.locations)))
--            self.assertEqual("file://%s/%s" % (self.test_dir,
--                                               self.image.image_id),
--                             self.image.locations[0]['url'])
-+
-+            with mock.patch.object(putils, 'trycmd') as tmock:
-+                tmock.return_value = (json.dumps({
-+                    'format': 'qcow2',
-+                }), None)
-+
-+                executor.begin_processing(self.task.task_id)
-+                image_path = os.path.join(self.test_dir, self.image.image_id)
-+                tmp_image_path = os.path.join(self.work_dir,
-+                                              "%s.tasks_import" % image_path)
-+
-+                self.assertFalse(os.path.exists(tmp_image_path))
-+                self.assertTrue(os.path.exists(image_path))
-+                self.assertEqual(1, len(list(self.image.locations)))
-+                self.assertEqual("file://%s/%s" % (self.test_dir,
-+                                                   self.image.image_id),
-+                                 self.image.locations[0]['url'])
- 
-     def test_import_flow_missing_work_dir(self):
-         self.config(engine_mode='serial', group='taskflow_executor')
-@@ -151,6 +161,54 @@ class TestImportTask(test_utils.BaseTestCase):
-                 self.assertFalse(os.path.exists(tmp_image_path))
-                 self.assertTrue(os.path.exists(image_path))
- 
-+    def test_import_flow_backed_file_import_to_fs(self):
-+        self.config(engine_mode='serial', group='taskflow_executor')
-+
-+        img_factory = mock.MagicMock()
-+
-+        executor = taskflow_executor.TaskExecutor(
-+            self.context,
-+            self.task_repo,
-+            self.img_repo,
-+            img_factory)
-+
-+        self.task_repo.get.return_value = self.task
-+
-+        def create_image(*args, **kwargs):
-+            kwargs['image_id'] = UUID1
-+            return self.img_factory.new_image(*args, **kwargs)
-+
-+        self.img_repo.get.return_value = self.image
-+        img_factory.new_image.side_effect = create_image
-+
-+        with mock.patch.object(script_utils, 'get_image_data_iter') as dmock:
-+            dmock.return_value = cStringIO("TEST_IMAGE")
-+
-+            with mock.patch.object(putils, 'trycmd') as tmock:
-+                tmock.return_value = (json.dumps({
-+                    'backing-filename': '/etc/password'
-+                }), None)
-+
-+                with mock.patch.object(import_flow._ImportToFS,
-+                                       'revert') as rmock:
-+                    self.assertRaises(RuntimeError,
-+                                      executor.begin_processing,
-+                                      self.task.task_id)
-+                    self.assertTrue(rmock.called)
-+                    self.assertIsInstance(rmock.call_args[1]['result'],
-+                                          failure.Failure)
-+
-+                    image_path = os.path.join(self.test_dir,
-+                                              self.image.image_id)
-+
-+                    fname = "%s.tasks_import" % image_path
-+                    tmp_image_path = os.path.join(self.work_dir, fname)
-+
-+                    self.assertFalse(os.path.exists(tmp_image_path))
-+                    # Note(sabari): The image should not have been uploaded to
-+                    # the store as the flow failed before ImportToStore Task.
-+                    self.assertFalse(os.path.exists(image_path))
-+
-     def test_import_flow_revert(self):
-         self.config(engine_mode='serial',
-                     group='taskflow_executor')
-@@ -175,20 +233,31 @@ class TestImportTask(test_utils.BaseTestCase):
-         with mock.patch.object(script_utils, 'get_image_data_iter') as dmock:
-             dmock.return_value = cStringIO("TEST_IMAGE")
- 
--            with mock.patch.object(import_flow, "_get_import_flows") as imock:
--                imock.return_value = (x for x in [_ErrorTask()])
--                self.assertRaises(RuntimeError,
--                                  executor.begin_processing, 
self.task.task_id)
--                image_path = os.path.join(self.test_dir, self.image.image_id)
--                tmp_image_path = os.path.join(self.work_dir,
--                                              "%s.tasks_import" % image_path)
--                self.assertFalse(os.path.exists(tmp_image_path))
--
--                # NOTE(flaper87): Eventually, we want this to be assertTrue.
--                # The current issue is there's no way to tell taskflow to
--                # continue on failures. That is, revert the subflow but keep
--                # executing the parent flow. Under discussion/development.
--                self.assertFalse(os.path.exists(image_path))
-+            with mock.patch.object(putils, 'trycmd') as tmock:
-+                tmock.return_value = (json.dumps({
-+                    'format': 'qcow2',
-+                }), None)
-+
-+                with mock.patch.object(import_flow,
-+                                       "_get_import_flows") as imock:
-+                    imock.return_value = (x for x in [_ErrorTask()])
-+                    self.assertRaises(RuntimeError,
-+                                      executor.begin_processing,
-+                                      self.task.task_id)
-+
-+                    image_path = os.path.join(self.test_dir,
-+                                              self.image.image_id)
-+                    tmp_image_path = os.path.join(self.work_dir,
-+                                                  ("%s.tasks_import" %
-+                                                   image_path))
-+                    self.assertFalse(os.path.exists(tmp_image_path))
-+
-+                    # NOTE(flaper87): Eventually, we want this to be 
assertTrue
-+                    # The current issue is there's no way to tell taskflow to
-+                    # continue on failures. That is, revert the subflow but
-+                    # keep executing the parent flow. Under
-+                    # discussion/development.
-+                    self.assertFalse(os.path.exists(image_path))
- 
-     def test_import_flow_no_import_flows(self):
-         self.config(engine_mode='serial',
-@@ -271,15 +340,20 @@ class TestImportTask(test_utils.BaseTestCase):
-         with mock.patch.object(script_utils, 'get_image_data_iter') as dmock:
-             dmock.return_value = "test"
- 
--            image_id = UUID1
--            path = import_fs.execute(image_id)
--            reader, size = glance_store.get_from_backend(path)
--            self.assertEqual(4, size)
--            self.assertEqual(dmock.return_value, "".join(reader))
-+            with mock.patch.object(putils, 'trycmd') as tmock:
-+                tmock.return_value = (json.dumps({
-+                    'format': 'qcow2',
-+                }), None)
-+
-+                image_id = UUID1
-+                path = import_fs.execute(image_id)
-+                reader, size = glance_store.get_from_backend(path)
-+                self.assertEqual(4, size)
-+                self.assertEqual(dmock.return_value, "".join(reader))
- 
--            image_path = os.path.join(self.work_dir, image_id)
--            tmp_image_path = os.path.join(self.work_dir, image_path)
--            self.assertTrue(os.path.exists(tmp_image_path))
-+                image_path = os.path.join(self.work_dir, image_id)
-+                tmp_image_path = os.path.join(self.work_dir, image_path)
-+                self.assertTrue(os.path.exists(tmp_image_path))
- 
-     def test_delete_from_fs(self):
-         delete_fs = import_flow._DeleteFromFS(self.task.task_id,
--- 
-cgit v0.10.2
-

diff --git a/app-admin/glance/files/cve-2015-5251-stable-kilo.patch 
b/app-admin/glance/files/cve-2015-5251-stable-kilo.patch
deleted file mode 100644
index f868645..0000000
--- a/app-admin/glance/files/cve-2015-5251-stable-kilo.patch
+++ /dev/null
@@ -1,192 +0,0 @@
-From 9beca533f42ae1fc87418de0c360e19bc59b24b5 Mon Sep 17 00:00:00 2001
-From: Stuart McLaren <stuart.mcla...@hp.com>
-Date: Tue, 11 Aug 2015 10:37:09 +0000
-Subject: [PATCH] Prevent image status being directly modified via v1
-
-Users shouldn't be able to change an image's status directly via the
-v1 API.
-
-Some existing consumers of Glance set the x-image-meta-status header in
-requests to the Glance API, eg:
-
-https://github.com/openstack/nova/blob/master/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance#L184
-
-We should try to prevent users setting 'status' via v1, but without breaking
-existing benign API calls such as these.
-
-I've adopted the following approach (which has some prior art in 'protected 
properties').
-
-If a PUT request is received which contains an x-image-meta-status header:
-
-* The user provided status is ignored if it matches the current image
-  status (this prevents benign calls such as the nova one above from
-  breaking). The usual code (eg 200) will be returned.
-
-* If the user provided status doesn't match the current image status (ie
-  there is a real attempt to change the value) 403 will be returned. This
-  will break any calls which currently intentionally change the status.
-
-APIImpact
-
-Closes-bug: 1482371
-
-Change-Id: I44fadf32abb57c962b67467091c3f51c1ccc25e6
-(cherry picked from commit 4d08db5b6d42323ac1958ef3b7417d875e7bea8c)
----
- glance/api/v1/__init__.py                          |  3 +
- glance/api/v1/images.py                            |  9 +++
- glance/tests/functional/v1/test_api.py             | 89 ++++++++++++++++++++++
- .../integration/legacy_functional/test_v1_api.py   |  2 +
- 4 files changed, 103 insertions(+)
-
-diff --git a/glance/api/v1/__init__.py b/glance/api/v1/__init__.py
-index 
74de9aa1411d8e926770b67f7d851cf14e794414..9306bbb4fe78f77a26bb539c717fdfd2b38767c8
 100644
---- a/glance/api/v1/__init__.py
-+++ b/glance/api/v1/__init__.py
-@@ -21,3 +21,6 @@ SUPPORTED_PARAMS = ('limit', 'marker', 'sort_key', 
'sort_dir')
- 
- # Metadata which only an admin can change once the image is active
- ACTIVE_IMMUTABLE = ('size', 'checksum')
-+
-+# Metadata which cannot be changed (irrespective of the current image state)
-+IMMUTABLE = ('status',)
-diff --git a/glance/api/v1/images.py b/glance/api/v1/images.py
-index 
e33b91fbca79377e78ccfd329fa542ad422f5ffc..95e32949d958d0f57a3b60c141b91784a5801f5a
 100644
---- a/glance/api/v1/images.py
-+++ b/glance/api/v1/images.py
-@@ -57,6 +57,7 @@ _LW = i18n._LW
- SUPPORTED_PARAMS = glance.api.v1.SUPPORTED_PARAMS
- SUPPORTED_FILTERS = glance.api.v1.SUPPORTED_FILTERS
- ACTIVE_IMMUTABLE = glance.api.v1.ACTIVE_IMMUTABLE
-+IMMUTABLE = glance.api.v1.IMMUTABLE
- 
- CONF = cfg.CONF
- CONF.import_opt('disk_formats', 'glance.common.config', group='image_format')
-@@ -912,6 +913,14 @@ class Controller(controller.BaseController):
-                                         request=req,
-                                         content_type="text/plain")
- 
-+        for key in IMMUTABLE:
-+            if (image_meta.get(key) is not None and
-+                    image_meta.get(key) != orig_image_meta.get(key)):
-+                msg = _("Forbidden to modify '%s' of image.") % key
-+                raise HTTPForbidden(explanation=msg,
-+                                    request=req,
-+                                    content_type="text/plain")
-+
-         # The default behaviour for a PUT /images/<IMAGE_ID> is to
-         # override any properties that were previously set. This, however,
-         # leads to a number of issues for the common use case where a caller
-diff --git a/glance/tests/functional/v1/test_api.py 
b/glance/tests/functional/v1/test_api.py
-index 
9fba3bb5e40c8742530691228c7b436b385fc2ca..6b3bfbb4270f1eb0f50418504e65be30ea23d10b
 100644
---- a/glance/tests/functional/v1/test_api.py
-+++ b/glance/tests/functional/v1/test_api.py
-@@ -715,3 +715,92 @@ class TestApi(functional.FunctionalTest):
-         self.assertEqual(404, response.status)
- 
-         self.stop_servers()
-+
-+    def test_status_cannot_be_manipulated_directly(self):
-+        self.cleanup()
-+        self.start_servers(**self.__dict__.copy())
-+        headers = minimal_headers('Image1')
-+
-+        # Create a 'queued' image
-+        http = httplib2.Http()
-+        headers = {'Content-Type': 'application/octet-stream',
-+                   'X-Image-Meta-Disk-Format': 'raw',
-+                   'X-Image-Meta-Container-Format': 'bare'}
-+        path = "http://%s:%d/v1/images"; % ("127.0.0.1", self.api_port)
-+        response, content = http.request(path, 'POST', headers=headers,
-+                                         body=None)
-+        self.assertEqual(201, response.status)
-+        image = jsonutils.loads(content)['image']
-+        self.assertEqual('queued', image['status'])
-+
-+        # Ensure status of 'queued' image can't be changed
-+        path = "http://%s:%d/v1/images/%s"; % ("127.0.0.1", self.api_port,
-+                                              image['id'])
-+        http = httplib2.Http()
-+        headers = {'X-Image-Meta-Status': 'active'}
-+        response, content = http.request(path, 'PUT', headers=headers)
-+        self.assertEqual(403, response.status)
-+        response, content = http.request(path, 'HEAD')
-+        self.assertEqual(200, response.status)
-+        self.assertEqual('queued', response['x-image-meta-status'])
-+
-+        # We allow 'setting' to the same status
-+        http = httplib2.Http()
-+        headers = {'X-Image-Meta-Status': 'queued'}
-+        response, content = http.request(path, 'PUT', headers=headers)
-+        self.assertEqual(200, response.status)
-+        response, content = http.request(path, 'HEAD')
-+        self.assertEqual(200, response.status)
-+        self.assertEqual('queued', response['x-image-meta-status'])
-+
-+        # Make image active
-+        http = httplib2.Http()
-+        headers = {'Content-Type': 'application/octet-stream'}
-+        response, content = http.request(path, 'PUT', headers=headers,
-+                                         body='data')
-+        self.assertEqual(200, response.status)
-+        image = jsonutils.loads(content)['image']
-+        self.assertEqual('active', image['status'])
-+
-+        # Ensure status of 'active' image can't be changed
-+        http = httplib2.Http()
-+        headers = {'X-Image-Meta-Status': 'queued'}
-+        response, content = http.request(path, 'PUT', headers=headers)
-+        self.assertEqual(403, response.status)
-+        response, content = http.request(path, 'HEAD')
-+        self.assertEqual(200, response.status)
-+        self.assertEqual('active', response['x-image-meta-status'])
-+
-+        # We allow 'setting' to the same status
-+        http = httplib2.Http()
-+        headers = {'X-Image-Meta-Status': 'active'}
-+        response, content = http.request(path, 'PUT', headers=headers)
-+        self.assertEqual(200, response.status)
-+        response, content = http.request(path, 'HEAD')
-+        self.assertEqual(200, response.status)
-+        self.assertEqual('active', response['x-image-meta-status'])
-+
-+        # Create a 'queued' image, ensure 'status' header is ignored
-+        http = httplib2.Http()
-+        path = "http://%s:%d/v1/images"; % ("127.0.0.1", self.api_port)
-+        headers = {'Content-Type': 'application/octet-stream',
-+                   'X-Image-Meta-Status': 'active'}
-+        response, content = http.request(path, 'POST', headers=headers,
-+                                         body=None)
-+        self.assertEqual(201, response.status)
-+        image = jsonutils.loads(content)['image']
-+        self.assertEqual('queued', image['status'])
-+
-+        # Create an 'active' image, ensure 'status' header is ignored
-+        http = httplib2.Http()
-+        path = "http://%s:%d/v1/images"; % ("127.0.0.1", self.api_port)
-+        headers = {'Content-Type': 'application/octet-stream',
-+                   'X-Image-Meta-Disk-Format': 'raw',
-+                   'X-Image-Meta-Status': 'queued',
-+                   'X-Image-Meta-Container-Format': 'bare'}
-+        response, content = http.request(path, 'POST', headers=headers,
-+                                         body='data')
-+        self.assertEqual(201, response.status)
-+        image = jsonutils.loads(content)['image']
-+        self.assertEqual('active', image['status'])
-+        self.stop_servers()
-diff --git a/glance/tests/integration/legacy_functional/test_v1_api.py 
b/glance/tests/integration/legacy_functional/test_v1_api.py
-index 
dff436465919569480bdbac537d20a6d61c98f46..511d46dfe18028bb430504784cc9d24c58736c3b
 100644
---- a/glance/tests/integration/legacy_functional/test_v1_api.py
-+++ b/glance/tests/integration/legacy_functional/test_v1_api.py
-@@ -358,6 +358,8 @@ class TestApi(base.ApiTest):
-         path = "/v1/images"
-         response, content = self.http.request(path, 'POST', headers=headers)
-         self.assertEqual(201, response.status)
-+        image = jsonutils.loads(content)['image']
-+        self.assertEqual('active', image['status'])
- 
-         # 2. HEAD image-location
-         # Verify image size is zero and the status is active
--- 
-2.5.0
-

diff --git a/app-admin/glance/files/cve-2016-0757-stable-liberty.patch 
b/app-admin/glance/files/cve-2016-0757-stable-liberty.patch
new file mode 100644
index 0000000..19c8365
--- /dev/null
+++ b/app-admin/glance/files/cve-2016-0757-stable-liberty.patch
@@ -0,0 +1,332 @@
+From c6021e9b3642340036347026a3f251e066e53094 Mon Sep 17 00:00:00 2001
+From: Erno Kuvaja <jo...@usr.fi>
+Date: Tue, 19 Jan 2016 13:37:05 +0000
+Subject: [PATCH] Prevent user to remove last location of the image
+
+If the last location of the image is removed, image transitions back to queued.
+This allows user to upload new data into the existing image record. By
+preventing removal of the last location we prevent the image transition back to
+queued.
+
+This change also prevents doing the same operation via replacing the locations
+with empty list.
+
+SecurityImpact
+DocImpact
+APIImpact
+
+Conflicts:
+       glance/tests/unit/v2/test_images_resource.py
+
+Change-Id: Ieb03aaba887492819f9c58aa67f7acfcea81720e
+Closes-Bug: #1525915
+(cherry picked from commit 2f4504da2149697bcdb93ed855e15025d2a08f8c)
+---
+ glance/api/v2/images.py                            |  19 +++-
+ glance/tests/functional/v2/test_images.py          |  14 ---
+ glance/tests/unit/v2/test_images_resource.py       | 122 ++++-----------------
+ ...oving-last-image-location-d5ee3e00efe14f34.yaml |  10 ++
+ 4 files changed, 44 insertions(+), 121 deletions(-)
+ create mode 100644 
releasenotes/notes/Prevent-removing-last-image-location-d5ee3e00efe14f34.yaml
+
+diff --git a/glance/api/v2/images.py b/glance/api/v2/images.py
+index 17678f2..cf667bf 100644
+--- a/glance/api/v2/images.py
++++ b/glance/api/v2/images.py
+@@ -181,7 +181,10 @@ class ImagesController(object):
+         path = change['path']
+         path_root = path[0]
+         value = change['value']
+-        if path_root == 'locations':
++        if path_root == 'locations' and value == []:
++            msg = _("Cannot set locations to empty list.")
++            raise webob.exc.HTTPForbidden(message=msg)
++        elif path_root == 'locations' and value != []:
+             self._do_replace_locations(image, value)
+         elif path_root == 'owner' and req.context.is_admin == False:
+             msg = _("Owner can't be updated by non admin.")
+@@ -217,7 +220,10 @@ class ImagesController(object):
+         path = change['path']
+         path_root = path[0]
+         if path_root == 'locations':
+-            self._do_remove_locations(image, path[1])
++            try:
++                self._do_remove_locations(image, path[1])
++            except exception.Forbidden as e:
++                raise webob.exc.HTTPForbidden(e.msg)
+         else:
+             if hasattr(image, path_root):
+                 msg = _("Property %s may not be removed.")
+@@ -306,6 +312,11 @@ class ImagesController(object):
+                 explanation=encodeutils.exception_to_unicode(ve))
+ 
+     def _do_remove_locations(self, image, path_pos):
++        if len(image.locations) == 1:
++            LOG.debug("User forbidden to remove last location of image %s",
++                      image.image_id)
++            msg = _("Cannot remove last location in the image.")
++            raise exception.Forbidden(message=msg)
+         pos = self._get_locations_op_pos(path_pos,
+                                          len(image.locations), False)
+         if pos is None:
+@@ -315,11 +326,11 @@ class ImagesController(object):
+             # NOTE(zhiyan): this actually deletes the location
+             # from the backend store.
+             image.locations.pop(pos)
++        # TODO(jokke): Fix this, we should catch what store throws and
++        # provide definitely something else than IternalServerError to user.
+         except Exception as e:
+             raise webob.exc.HTTPInternalServerError(
+                 explanation=encodeutils.exception_to_unicode(e))
+-        if len(image.locations) == 0 and image.status == 'active':
+-            image.status = 'queued'
+ 
+ 
+ class RequestDeserializer(wsgi.JSONRequestDeserializer):
+diff --git a/glance/tests/functional/v2/test_images.py 
b/glance/tests/functional/v2/test_images.py
+index aabc567..f199787 100644
+--- a/glance/tests/functional/v2/test_images.py
++++ b/glance/tests/functional/v2/test_images.py
+@@ -522,20 +522,6 @@ class TestImages(functional.FunctionalTest):
+         response = requests.patch(path, headers=headers, data=data)
+         self.assertEqual(200, response.status_code, response.text)
+ 
+-        # Remove all locations of the image then the image size shouldn't be
+-        # able to access
+-        path = self._url('/v2/images/%s' % image2_id)
+-        media_type = 'application/openstack-images-v2.1-json-patch'
+-        headers = self._headers({'content-type': media_type})
+-        doc = [{'op': 'replace', 'path': '/locations', 'value': []}]
+-        data = jsonutils.dumps(doc)
+-        response = requests.patch(path, headers=headers, data=data)
+-        self.assertEqual(200, response.status_code, response.text)
+-        image = jsonutils.loads(response.text)
+-        self.assertIsNone(image['size'])
+-        self.assertIsNone(image['virtual_size'])
+-        self.assertEqual('queued', image['status'])
+-
+         # Deletion should work. Deleting image-1
+         path = self._url('/v2/images/%s' % image_id)
+         response = requests.delete(path, headers=self._headers())
+diff --git a/glance/tests/unit/v2/test_images_resource.py 
b/glance/tests/unit/v2/test_images_resource.py
+index 698c284..ee09ee7 100644
+--- a/glance/tests/unit/v2/test_images_resource.py
++++ b/glance/tests/unit/v2/test_images_resource.py
+@@ -1417,26 +1417,6 @@ class TestImagesController(base.IsolatedUnitTest):
+         self.assertRaises(webob.exc.HTTPConflict, self.controller.update,
+                           another_request, created_image.image_id, changes)
+ 
+-    def test_update_replace_locations(self):
+-        self.stubs.Set(store, 'get_size_from_backend',
+-                       unit_test_utils.fake_get_size_from_backend)
+-        request = unit_test_utils.get_fake_request()
+-        changes = [{'op': 'replace', 'path': ['locations'], 'value': []}]
+-        output = self.controller.update(request, UUID1, changes)
+-        self.assertEqual(UUID1, output.image_id)
+-        self.assertEqual(0, len(output.locations))
+-        self.assertEqual('queued', output.status)
+-        self.assertIsNone(output.size)
+-
+-        new_location = {'url': '%s/fake_location' % BASE_URI, 'metadata': {}}
+-        changes = [{'op': 'replace', 'path': ['locations'],
+-                    'value': [new_location]}]
+-        output = self.controller.update(request, UUID1, changes)
+-        self.assertEqual(UUID1, output.image_id)
+-        self.assertEqual(1, len(output.locations))
+-        self.assertEqual(new_location, output.locations[0])
+-        self.assertEqual('active', output.status)
+-
+     def test_update_replace_locations_non_empty(self):
+         new_location = {'url': '%s/fake_location' % BASE_URI, 'metadata': {}}
+         request = unit_test_utils.get_fake_request()
+@@ -1448,35 +1428,9 @@ class TestImagesController(base.IsolatedUnitTest):
+     def test_update_replace_locations_invalid(self):
+         request = unit_test_utils.get_fake_request()
+         changes = [{'op': 'replace', 'path': ['locations'], 'value': []}]
+-        output = self.controller.update(request, UUID1, changes)
+-        self.assertEqual(UUID1, output.image_id)
+-        self.assertEqual(0, len(output.locations))
+-        self.assertEqual('queued', output.status)
+-
+-        request = unit_test_utils.get_fake_request()
+-        changes = [{'op': 'replace', 'path': ['locations'],
+-                    'value': [{'url': 'unknow://foo', 'metadata': {}}]}]
+-        self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update,
++        self.assertRaises(webob.exc.HTTPForbidden, self.controller.update,
+                           request, UUID1, changes)
+ 
+-    def test_update_replace_locations_status_exception(self):
+-        self.stubs.Set(store, 'get_size_from_backend',
+-                       unit_test_utils.fake_get_size_from_backend)
+-        request = unit_test_utils.get_fake_request()
+-        changes = [{'op': 'replace', 'path': ['locations'], 'value': []}]
+-        output = self.controller.update(request, UUID2, changes)
+-        self.assertEqual(UUID2, output.image_id)
+-        self.assertEqual(0, len(output.locations))
+-        self.assertEqual('queued', output.status)
+-
+-        self.db.image_update(None, UUID2, {'disk_format': None})
+-
+-        new_location = {'url': '%s/fake_location' % BASE_URI, 'metadata': {}}
+-        changes = [{'op': 'replace', 'path': ['locations'],
+-                    'value': [new_location]}]
+-        self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update,
+-                          request, UUID2, changes)
+-
+     def test_update_add_property(self):
+         request = unit_test_utils.get_fake_request()
+ 
+@@ -1600,24 +1554,6 @@ class TestImagesController(base.IsolatedUnitTest):
+         self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update,
+                           request, UUID1, changes)
+ 
+-    def test_update_add_locations_status_exception(self):
+-        self.stubs.Set(store, 'get_size_from_backend',
+-                       unit_test_utils.fake_get_size_from_backend)
+-        request = unit_test_utils.get_fake_request()
+-        changes = [{'op': 'replace', 'path': ['locations'], 'value': []}]
+-        output = self.controller.update(request, UUID2, changes)
+-        self.assertEqual(UUID2, output.image_id)
+-        self.assertEqual(0, len(output.locations))
+-        self.assertEqual('queued', output.status)
+-
+-        self.db.image_update(None, UUID2, {'disk_format': None})
+-
+-        new_location = {'url': '%s/fake_location' % BASE_URI, 'metadata': {}}
+-        changes = [{'op': 'add', 'path': ['locations', '-'],
+-                    'value': new_location}]
+-        self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update,
+-                          request, UUID2, changes)
+-
+     def test_update_add_duplicate_locations(self):
+         new_location = {'url': '%s/fake_location' % BASE_URI, 'metadata': {}}
+         request = unit_test_utils.get_fake_request()
+@@ -1631,23 +1567,6 @@ class TestImagesController(base.IsolatedUnitTest):
+         self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update,
+                           request, UUID1, changes)
+ 
+-    def test_update_replace_duplicate_locations(self):
+-        self.stubs.Set(store, 'get_size_from_backend',
+-                       unit_test_utils.fake_get_size_from_backend)
+-        request = unit_test_utils.get_fake_request()
+-        changes = [{'op': 'replace', 'path': ['locations'], 'value': []}]
+-        output = self.controller.update(request, UUID1, changes)
+-        self.assertEqual(UUID1, output.image_id)
+-        self.assertEqual(0, len(output.locations))
+-        self.assertEqual('queued', output.status)
+-
+-        new_location = {'url': '%s/fake_location' % BASE_URI, 'metadata': {}}
+-        changes = [{'op': 'replace', 'path': ['locations'],
+-                    'value': [new_location, new_location]}]
+-
+-        self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update,
+-                          request, UUID1, changes)
+-
+     def test_update_add_too_many_locations(self):
+         self.config(image_location_quota=1)
+         request = unit_test_utils.get_fake_request()
+@@ -1748,9 +1667,12 @@ class TestImagesController(base.IsolatedUnitTest):
+             {'op': 'add', 'path': ['locations', '-'],
+              'value': {'url': '%s/fake_location_1' % BASE_URI,
+                        'metadata': {}}},
++            {'op': 'add', 'path': ['locations', '-'],
++             'value': {'url': '%s/fake_location_2' % BASE_URI,
++                       'metadata': {}}},
+         ]
+         self.controller.update(request, UUID1, changes)
+-        self.config(image_location_quota=1)
++        self.config(image_location_quota=2)
+ 
+         # We must remove two properties to avoid being
+         # over the limit of 1 property
+@@ -1763,8 +1685,8 @@ class TestImagesController(base.IsolatedUnitTest):
+         ]
+         output = self.controller.update(request, UUID1, changes)
+         self.assertEqual(UUID1, output.image_id)
+-        self.assertEqual(1, len(output.locations))
+-        self.assertIn('fake_location_3', output.locations[0]['url'])
++        self.assertEqual(2, len(output.locations))
++        self.assertIn('fake_location_3', output.locations[1]['url'])
+         self.assertNotEqual(output.created_at, output.updated_at)
+ 
+     def test_update_remove_base_property(self):
+@@ -1805,24 +1727,23 @@ class TestImagesController(base.IsolatedUnitTest):
+                        unit_test_utils.fake_get_size_from_backend)
+ 
+         request = unit_test_utils.get_fake_request()
+-        changes = [{'op': 'remove', 'path': ['locations', '0']}]
+-        output = self.controller.update(request, UUID1, changes)
+-        self.assertEqual(output.image_id, UUID1)
+-        self.assertEqual(0, len(output.locations))
+-        self.assertEqual('queued', output.status)
+-        self.assertIsNone(output.size)
+-
+         new_location = {'url': '%s/fake_location' % BASE_URI, 'metadata': {}}
+         changes = [{'op': 'add', 'path': ['locations', '-'],
+                     'value': new_location}]
++        self.controller.update(request, UUID1, changes)
++        changes = [{'op': 'remove', 'path': ['locations', '0']}]
+         output = self.controller.update(request, UUID1, changes)
+         self.assertEqual(UUID1, output.image_id)
+         self.assertEqual(1, len(output.locations))
+-        self.assertEqual(new_location, output.locations[0])
+         self.assertEqual('active', output.status)
+ 
+     def test_update_remove_location_invalid_pos(self):
+         request = unit_test_utils.get_fake_request()
++        changes = [
++            {'op': 'add', 'path': ['locations', '-'],
++             'value': {'url': '%s/fake_location' % BASE_URI,
++                       'metadata': {}}}]
++        self.controller.update(request, UUID1, changes)
+         changes = [{'op': 'remove', 'path': ['locations', None]}]
+         self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update,
+                           request, UUID1, changes)
+@@ -1844,6 +1765,11 @@ class TestImagesController(base.IsolatedUnitTest):
+                        fake_delete_image_location_from_backend)
+ 
+         request = unit_test_utils.get_fake_request()
++        changes = [
++            {'op': 'add', 'path': ['locations', '-'],
++             'value': {'url': '%s/fake_location' % BASE_URI,
++                       'metadata': {}}}]
++        self.controller.update(request, UUID1, changes)
+         changes = [{'op': 'remove', 'path': ['locations', '0']}]
+         self.assertRaises(webob.exc.HTTPInternalServerError,
+                           self.controller.update, request, UUID1, changes)
+@@ -2137,16 +2063,6 @@ class 
TestImagesControllerPolicies(base.IsolatedUnitTest):
+         self.assertRaises(webob.exc.HTTPForbidden, self.controller.update,
+                           request, UUID1, changes)
+ 
+-        self.stubs.Set(self.store_utils, 'delete_image_location_from_backend',
+-                       fake_delete_image_location_from_backend)
+-
+-        changes = [{'op': 'replace', 'path': ['locations'], 'value': []}]
+-        self.controller.update(request, UUID1, changes)
+-        changes = [{'op': 'replace', 'path': ['locations'],
+-                    'value': [new_location]}]
+-        self.assertRaises(webob.exc.HTTPForbidden, self.controller.update,
+-                          request, UUID1, changes)
+-
+     def test_update_delete_image_location_unauthorized(self):
+         rules = {"delete_image_location": False}
+         self.policy.set_rules(rules)
+diff --git 
a/releasenotes/notes/Prevent-removing-last-image-location-d5ee3e00efe14f34.yaml 
b/releasenotes/notes/Prevent-removing-last-image-location-d5ee3e00efe14f34.yaml
+new file mode 100644
+index 0000000..344e6e5
+--- /dev/null
++++ 
b/releasenotes/notes/Prevent-removing-last-image-location-d5ee3e00efe14f34.yaml
+@@ -0,0 +1,10 @@
++---
++security:
++  - Fixing bug 1525915; image might be transitioning
++    from active to queued by regular user by removing
++    last location of image (or replacing locations
++    with empty list). This allows user to re-upload
++    data to the image breaking Glance's promise of
++    image data immutability. From now on, last
++    location cannot be removed and locations cannot
++    be replaced with empty list.
+-- 
+1.9.1
+

diff --git a/app-admin/glance/files/glance-2013.2-sphinx_mapping.patch 
b/app-admin/glance/files/glance-2013.2-sphinx_mapping.patch
deleted file mode 100644
index 0a0f575..0000000
--- a/app-admin/glance/files/glance-2013.2-sphinx_mapping.patch
+++ /dev/null
@@ -1,12 +0,0 @@
-diff -ur glance-2013.2.orig/doc/source/conf.py glance-2013.2/doc/source/conf.py
---- doc/source/conf.py 2013-10-17 21:39:46.000000000 +0800
-+++ doc/source/conf.py 2013-11-13 18:51:29.099839976 +0800
-@@ -250,8 +250,3 @@
- 
- # If false, no module index is generated.
- #latex_use_modindex = True
--
--# Example configuration for intersphinx: refer to the Python standard library.
--intersphinx_mapping = {'python': ('http://docs.python.org/', None),
--                       'nova': ('http://nova.openstack.org', None),
--                       'swift': ('http://swift.openstack.org', None)}

diff --git a/app-admin/glance/glance-11.0.1-r1.ebuild 
b/app-admin/glance/glance-11.0.1-r1.ebuild
new file mode 100644
index 0000000..1126feb
--- /dev/null
+++ b/app-admin/glance/glance-11.0.1-r1.ebuild
@@ -0,0 +1,229 @@
+# Copyright 1999-2016 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Id$
+
+EAPI=5
+PYTHON_COMPAT=( python2_7 )
+
+inherit distutils-r1 user
+
+DESCRIPTION="Services for discovering, registering, and retrieving VM images"
+HOMEPAGE="https://launchpad.net/glance";
+SRC_URI="https://tarballs.openstack.org/${PN}/${P}.tar.gz";
+
+LICENSE="Apache-2.0"
+SLOT="0"
+KEYWORDS="~amd64 ~x86"
+IUSE="doc mysql postgres +sqlite +swift test"
+REQUIRED_USE="|| ( mysql postgres sqlite )"
+
+CDEPEND=">=dev-python/pbr-1.6.0[${PYTHON_USEDEP}]"
+DEPEND="
+       dev-python/setuptools[${PYTHON_USEDEP}]
+       ${CDEPEND}
+       test? (
+               ${RDEPEND}
+               >=dev-python/Babel-1.3[${PYTHON_USEDEP}]
+               <=dev-python/Babel-2.1.1[${PYTHON_USEDEP}]
+               >=dev-python/coverage-3.6[${PYTHON_USEDEP}]
+               <=dev-python/coverage-4.0.3[${PYTHON_USEDEP}]
+               >=dev-python/fixtures-1.3.1[${PYTHON_USEDEP}]
+               <=dev-python/fixtures-1.4.0-r9999[${PYTHON_USEDEP}]
+               >=dev-python/mox3-0.7.0[${PYTHON_USEDEP}]
+               <=dev-python/mox3-0.12.0[${PYTHON_USEDEP}]
+               >=dev-python/mock-1.2[${PYTHON_USEDEP}]
+               <=dev-python/mock-1.3.0[${PYTHON_USEDEP}]
+               >=dev-python/sphinx-1.1.2[${PYTHON_USEDEP}]
+               !~dev-python/sphinx-1.2.0[${PYTHON_USEDEP}]
+               <dev-python/sphinx-1.3[${PYTHON_USEDEP}]
+               >=dev-python/requests-2.5.2[${PYTHON_USEDEP}]
+               !~dev-python/requests-2.8.0[${PYTHON_USEDEP}]
+               <=dev-python/requests-2.8.1[${PYTHON_USEDEP}]
+               >=dev-python/testrepository-0.0.18[${PYTHON_USEDEP}]
+               <=dev-python/testrepository-0.0.20[${PYTHON_USEDEP}]
+               >=dev-python/testresources-0.2.4[${PYTHON_USEDEP}]
+               <=dev-python/testresources-1.0.0-r9999[${PYTHON_USEDEP}]
+               >=dev-python/testscenarios-0.4[${PYTHON_USEDEP}]
+               <=dev-python/testscenarios-0.5[${PYTHON_USEDEP}]
+               >=dev-python/testtools-1.4.0[${PYTHON_USEDEP}]
+               <=dev-python/testtools-1.8.1[${PYTHON_USEDEP}]
+               >=dev-python/psutil-1.1.1[${PYTHON_USEDEP}]
+               <dev-python/psutil-2.0.0[${PYTHON_USEDEP}]
+               >=dev-python/oslotest-1.10.0[${PYTHON_USEDEP}]
+               <=dev-python/oslotest-2.0.0[${PYTHON_USEDEP}]
+               >=dev-python/pymysql-0.6.2[${PYTHON_USEDEP}]
+               <=dev-python/pymysql-0.6.7[${PYTHON_USEDEP}]
+               >=dev-python/psycopg-2.5[${PYTHON_USEDEP}]
+               <=dev-python/psycopg-2.6.1[${PYTHON_USEDEP}]
+               >=dev-python/pysendfile-2.0.0[${PYTHON_USEDEP}]
+               <=dev-python/pysendfile-2.0.1[${PYTHON_USEDEP}]
+               <=dev-python/qpid-python-0.32[$(python_gen_usedep 'python2_7')]
+               >=dev-python/pyxattr-0.5.0[${PYTHON_USEDEP}]
+               >=dev-python/python-swiftclient-2.2.0[${PYTHON_USEDEP}]
+               <=dev-python/python-swiftclient-2.7.0[${PYTHON_USEDEP}]
+               >=dev-python/oslo-sphinx-2.5.0[${PYTHON_USEDEP}]
+               <=dev-python/oslo-sphinx-4.1.0[${PYTHON_USEDEP}]
+               >=dev-python/reno-0.1.1[${PYTHON_USEDEP}]
+       )"
+
+#note to self, wsgiref is a python builtin, no need to package it
+#>=dev-python/wsgiref-0.1.2[${PYTHON_USEDEP}]
+
+RDEPEND="
+       ${CDEPEND}
+       sqlite? (
+               >=dev-python/sqlalchemy-0.9.9[sqlite,${PYTHON_USEDEP}]
+               <dev-python/sqlalchemy-1.0.10[sqlite,${PYTHON_USEDEP}]
+       )
+       mysql? (
+               dev-python/mysql-python
+               >=dev-python/sqlalchemy-0.9.9[${PYTHON_USEDEP}]
+               <dev-python/sqlalchemy-1.0.10[${PYTHON_USEDEP}]
+       )
+       postgres? (
+               dev-python/psycopg:2[${PYTHON_USEDEP}]
+               >=dev-python/sqlalchemy-0.9.9[${PYTHON_USEDEP}]
+               <dev-python/sqlalchemy-1.0.10[${PYTHON_USEDEP}]
+       )
+       ~dev-python/anyjson-0.3.3[${PYTHON_USEDEP}]
+       ~dev-python/eventlet-0.17.4[${PYTHON_USEDEP}]
+       >=dev-python/pastedeploy-1.5.0[${PYTHON_USEDEP}]
+       <=dev-python/pastedeploy-1.5.2[${PYTHON_USEDEP}]
+       >=dev-python/routes-1.12.3[${PYTHON_USEDEP}]
+       !~dev-python/routes-2.0[${PYTHON_USEDEP}]
+       !~dev-python/routes-2.1[$(python_gen_usedep 'python2_7')]
+       <=dev-python/routes-2.2[${PYTHON_USEDEP}]
+       >=dev-python/webob-1.2.3[${PYTHON_USEDEP}]
+       <=dev-python/webob-1.5.1[${PYTHON_USEDEP}]
+       >=dev-python/sqlalchemy-migrate-0.9.6[${PYTHON_USEDEP}]
+       <=dev-python/sqlalchemy-migrate-0.10.0[${PYTHON_USEDEP}]
+       >=dev-python/httplib2-0.7.5[${PYTHON_USEDEP}]
+       <=dev-python/httplib2-0.9.2[${PYTHON_USEDEP}]
+       >=dev-python/pycrypto-2.6[${PYTHON_USEDEP}]
+       <=dev-python/pycrypto-2.6.1[${PYTHON_USEDEP}]
+       >=dev-python/iso8601-0.1.9[${PYTHON_USEDEP}]
+       <=dev-python/iso8601-0.1.11[${PYTHON_USEDEP}]
+       >=dev-python/oslo-config-2.3.0[${PYTHON_USEDEP}]
+       <=dev-python/oslo-config-3.1.0[${PYTHON_USEDEP}]
+       >=dev-python/oslo-concurrency-2.3.0[${PYTHON_USEDEP}]
+       <=dev-python/oslo-concurrency-3.1.0[${PYTHON_USEDEP}]
+       >=dev-python/oslo-context-0.2.0[${PYTHON_USEDEP}]
+       <=dev-python/oslo-context-1.0.0[${PYTHON_USEDEP}]
+       >=dev-python/oslo-service-0.7.0[${PYTHON_USEDEP}]
+       <=dev-python/oslo-service-1.1.0[${PYTHON_USEDEP}]
+       >=dev-python/oslo-utils-2.0.0[${PYTHON_USEDEP}]
+       !~dev-python/oslo-utils-2.6.0[${PYTHON_USEDEP}]
+       <=dev-python/oslo-utils-3.2.0[${PYTHON_USEDEP}]
+       >=dev-python/stevedore-1.5.0[${PYTHON_USEDEP}]
+       <=dev-python/stevedore-1.10.0[${PYTHON_USEDEP}]
+       >=dev-python/futurist-0.1.2[${PYTHON_USEDEP}]
+       <=dev-python/futurist-0.8.0[${PYTHON_USEDEP}]
+       >=dev-python/taskflow-1.16.0[${PYTHON_USEDEP}]
+       <=dev-python/taskflow-1.25.0[${PYTHON_USEDEP}]
+       >=dev-python/keystonemiddleware-2.0.0[${PYTHON_USEDEP}]
+       !~dev-python/keystonemiddleware-2.4.0[${PYTHON_USEDEP}]
+       <=dev-python/keystonemiddleware-4.0.0[${PYTHON_USEDEP}]
+       >=dev-python/WSME-0.7[${PYTHON_USEDEP}]
+       <=dev-python/WSME-0.8.0[${PYTHON_USEDEP}]
+       <=dev-python/paste-2.0.2[${PYTHON_USEDEP}]
+       >=dev-python/jsonschema-2.0.0[${PYTHON_USEDEP}]
+       !~dev-python/jsonschema-2.5.0[${PYTHON_USEDEP}]
+       <dev-python/jsonschema-3.0.0[${PYTHON_USEDEP}]
+       >=dev-python/python-keystoneclient-1.6.0[${PYTHON_USEDEP}]
+       !~dev-python/python-keystoneclient-1.8.0[${PYTHON_USEDEP}]
+       <=dev-python/python-keystoneclient-2.0.0-r9999[${PYTHON_USEDEP}]
+       >=dev-python/pyopenssl-0.14[${PYTHON_USEDEP}]
+       <=dev-python/pyopenssl-0.15.1-r9999[${PYTHON_USEDEP}]
+       >=dev-python/six-1.9.0[${PYTHON_USEDEP}]
+       <=dev-python/six-1.10.0[${PYTHON_USEDEP}]
+       >=dev-python/oslo-db-2.4.1[${PYTHON_USEDEP}]
+       <=dev-python/oslo-db-4.1.0[${PYTHON_USEDEP}]
+       >=dev-python/oslo-i18n-1.5.0[${PYTHON_USEDEP}]
+       <=dev-python/oslo-i18n-3.1.0[${PYTHON_USEDEP}]
+       >=dev-python/oslo-log-1.8.0[${PYTHON_USEDEP}]
+       <=dev-python/oslo-log-2.1.0[${PYTHON_USEDEP}]
+       >=dev-python/oslo-messaging-1.16.0[${PYTHON_USEDEP}]
+       !~dev-python/oslo-messaging-1.17.0[${PYTHON_USEDEP}]
+       !~dev-python/oslo-messaging-1.17.1[${PYTHON_USEDEP}]
+       !~dev-python/oslo-messaging-2.6.0[${PYTHON_USEDEP}]
+       !~dev-python/oslo-messaging-2.6.1[${PYTHON_USEDEP}]
+       !~dev-python/oslo-messaging-2.7.0[${PYTHON_USEDEP}]
+       !~dev-python/oslo-messaging-2.8.0[${PYTHON_USEDEP}]
+       !~dev-python/oslo-messaging-2.8.1[${PYTHON_USEDEP}]
+       !~dev-python/oslo-messaging-2.9.0[${PYTHON_USEDEP}]
+       !~dev-python/oslo-messaging-3.1.0[${PYTHON_USEDEP}]
+       <=dev-python/oslo-messaging-3.0.0[${PYTHON_USEDEP}]
+       >=dev-python/oslo-middleware-2.8.0[${PYTHON_USEDEP}]
+       <=dev-python/oslo-middleware-3.3.0[${PYTHON_USEDEP}]
+       >=dev-python/oslo-policy-0.5.0[${PYTHON_USEDEP}]
+       <=dev-python/oslo-policy-1.1.0[${PYTHON_USEDEP}]
+       >=dev-python/oslo-serialization-1.4.0[${PYTHON_USEDEP}]
+       <=dev-python/oslo-serialization-2.1.0[${PYTHON_USEDEP}]
+       >=dev-python/retrying-1.2.3[${PYTHON_USEDEP}]
+       !~dev-python/retrying-1.3.0[${PYTHON_USEDEP}]
+       <=dev-python/retrying-1.3.3[${PYTHON_USEDEP}]
+       >=dev-python/osprofiler-0.3.0[${PYTHON_USEDEP}]
+       <=dev-python/osprofiler-0.3.1[${PYTHON_USEDEP}]
+       >=dev-python/glance_store-0.7.1[${PYTHON_USEDEP}]
+       !~dev-python/glance_store-0.9.0[${PYTHON_USEDEP}]
+       <=dev-python/glance_store-0.9.1[${PYTHON_USEDEP}]
+       >=dev-python/semantic_version-2.3.1[${PYTHON_USEDEP}]
+       <=dev-python/semantic_version-2.4.2[${PYTHON_USEDEP}]
+       >=dev-python/castellan-0.2.0[${PYTHON_USEDEP}]
+       <=dev-python/castellan-0.3.1[${PYTHON_USEDEP}]
+       >=dev-python/cryptography-1.0[${PYTHON_USEDEP}]
+       <=dev-python/cryptography-1.1.2-r9999[${PYTHON_USEDEP}]
+"
+
+PATCHES=(
+       "${FILESDIR}/cve-2016-0757-stable-liberty.patch"
+)
+
+pkg_setup() {
+       enewgroup glance
+       enewuser glance -1 -1 /var/lib/glance glance
+}
+
+python_prepare_all() {
+       sed -i '/xattr/d' test-requirements.txt || die
+       sed -i '/pysendfile/d' test-requirements.txt || die
+       sed -i '/^hacking/d' test-requirements.txt || die
+       distutils-r1_python_prepare_all
+}
+
+python_compile_all() {
+       use doc && "${PYTHON}" setup.py build_sphinx
+}
+
+python_test() {
+       # https://bugs.launchpad.net/glance/+bug/1251105
+       # https://bugs.launchpad.net/glance/+bug/1242501
+       testr init
+       testr run --parallel || die "failed testsuite under python2.7"
+}
+
+python_install() {
+       distutils-r1_python_install
+
+       for svc in api registry scrubber; do
+               newinitd "${FILESDIR}/glance.initd" glance-${svc}
+       done
+
+       diropts -m 0750 -o glance -g glance
+       dodir /var/log/glance /var/lib/glance/images /var/lib/glance/scrubber
+       keepdir /etc/glance
+       keepdir /var/log/glance
+       keepdir /var/lib/glance/images
+       keepdir /var/lib/glance/scrubber
+
+       insinto /etc/glance
+       insopts -m 0640 -o glance -g glance
+       doins etc/*.ini
+       doins etc/*.conf
+       doins etc/*.sample
+}
+
+python_install_all() {
+       use doc && local HTML_DOCS=( doc/build/html/. )
+       distutils-r1_python_install_all
+}

Reply via email to