Browse Source

Remove Images API v1 entry points

This change removes option to configure Images API v1
This change removes Images API v1 endpoints from the router
This change removes all v1 tests
This change removes the v1 dependant glance-cache-manage command

This change does not remove all v1 codebase. Further cleanup and
decoupling will be needed.

Change-Id: Ia086230cc8c92f7b7dfd5b001923110d5bc55d4d
Erno Kuvaja 1 year ago
parent
commit
3dde3204d5

+ 0
- 2
glance/api/__init__.py View File

@@ -20,8 +20,6 @@ CONF = cfg.CONF
20 20
 
21 21
 
22 22
 def root_app_factory(loader, global_conf, **local_conf):
23
-    if not CONF.enable_v1_api and '/v1' in local_conf:
24
-        del local_conf['/v1']
25 23
     if not CONF.enable_v2_api and '/v2' in local_conf:
26 24
         del local_conf['/v2']
27 25
     return paste.urlmap.urlmap_factory(loader, global_conf, **local_conf)

+ 0
- 17
glance/api/middleware/cache.py View File

@@ -31,7 +31,6 @@ import webob
31 31
 
32 32
 from glance.api.common import size_checked_iter
33 33
 from glance.api import policy
34
-from glance.api.v1 import images
35 34
 from glance.common import exception
36 35
 from glance.common import utils
37 36
 from glance.common import wsgi
@@ -55,7 +54,6 @@ class CacheFilter(wsgi.Middleware):
55 54
 
56 55
     def __init__(self, app):
57 56
         self.cache = image_cache.ImageCache()
58
-        self.serializer = images.ImageSerializer()
59 57
         self.policy = policy.Enforcer()
60 58
         LOG.info(_LI("Initialized image cache middleware"))
61 59
         super(CacheFilter, self).__init__(app)
@@ -214,21 +212,6 @@ class CacheFilter(wsgi.Middleware):
214 212
         else:
215 213
             return (image_id, method, version)
216 214
 
217
-    def _process_v1_request(self, request, image_id, image_iterator,
218
-                            image_meta):
219
-        # Don't display location
220
-        if 'location' in image_meta:
221
-            del image_meta['location']
222
-        image_meta.pop('location_data', None)
223
-        self._verify_metadata(image_meta)
224
-
225
-        response = webob.Response(request=request)
226
-        raw_response = {
227
-            'image_iterator': image_iterator,
228
-            'image_meta': image_meta,
229
-        }
230
-        return self.serializer.show(response, raw_response)
231
-
232 215
     def _process_v2_request(self, request, image_id, image_iterator,
233 216
                             image_meta):
234 217
         # We do some contortions to get the image_metadata so

+ 0
- 4
glance/api/middleware/version_negotiation.py View File

@@ -72,10 +72,6 @@ class VersionNegotiationFilter(wsgi.Middleware):
72 72
 
73 73
     def _get_allowed_versions(self):
74 74
         allowed_versions = {}
75
-        if CONF.enable_v1_api:
76
-            allowed_versions['v1'] = 1
77
-            allowed_versions['v1.0'] = 1
78
-            allowed_versions['v1.1'] = 1
79 75
         if CONF.enable_v2_api:
80 76
             allowed_versions['v2'] = 2
81 77
             allowed_versions['v2.0'] = 2

+ 0
- 1351
glance/api/v1/images.py
File diff suppressed because it is too large
View File


+ 0
- 248
glance/api/v1/members.py View File

@@ -1,248 +0,0 @@
1
-# Copyright 2012 OpenStack Foundation.
2
-# Copyright 2013 NTT corp.
3
-# All Rights Reserved.
4
-#
5
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
6
-#    not use this file except in compliance with the License. You may obtain
7
-#    a copy of the License at
8
-#
9
-#         http://www.apache.org/licenses/LICENSE-2.0
10
-#
11
-#    Unless required by applicable law or agreed to in writing, software
12
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14
-#    License for the specific language governing permissions and limitations
15
-#    under the License.
16
-
17
-from oslo_config import cfg
18
-from oslo_log import log as logging
19
-from oslo_utils import encodeutils
20
-import webob.exc
21
-
22
-from glance.api import policy
23
-from glance.api.v1 import controller
24
-from glance.common import exception
25
-from glance.common import utils
26
-from glance.common import wsgi
27
-from glance.i18n import _
28
-import glance.registry.client.v1.api as registry
29
-
30
-LOG = logging.getLogger(__name__)
31
-CONF = cfg.CONF
32
-CONF.import_opt('image_member_quota', 'glance.common.config')
33
-
34
-
35
-class Controller(controller.BaseController):
36
-
37
-    def __init__(self):
38
-        self.policy = policy.Enforcer()
39
-
40
-    def _check_can_access_image_members(self, context):
41
-        if context.owner is None and not context.is_admin:
42
-            raise webob.exc.HTTPUnauthorized(_("No authenticated user"))
43
-
44
-    def _enforce(self, req, action):
45
-        """Authorize an action against our policies"""
46
-        try:
47
-            self.policy.enforce(req.context, action, {})
48
-        except exception.Forbidden:
49
-            LOG.debug("User not permitted to perform '%s' action", action)
50
-            raise webob.exc.HTTPForbidden()
51
-
52
-    def _raise_404_if_image_deleted(self, req, image_id):
53
-        image = self.get_image_meta_or_404(req, image_id)
54
-        if image['status'] == 'deleted':
55
-            msg = _("Image with identifier %s has been deleted.") % image_id
56
-            raise webob.exc.HTTPNotFound(msg)
57
-
58
-    def index(self, req, image_id):
59
-        """
60
-        Return a list of dictionaries indicating the members of the
61
-        image, i.e., those tenants the image is shared with.
62
-
63
-        :param req: the Request object coming from the wsgi layer
64
-        :param image_id: The opaque image identifier
65
-        :returns: The response body is a mapping of the following form
66
-
67
-        ::
68
-
69
-            {'members': [
70
-                {'member_id': <MEMBER>,
71
-                 'can_share': <SHARE_PERMISSION>, ...}, ...
72
-            ]}
73
-
74
-        """
75
-        self._enforce(req, 'get_members')
76
-        self._raise_404_if_image_deleted(req, image_id)
77
-
78
-        try:
79
-            members = registry.get_image_members(req.context, image_id)
80
-        except exception.NotFound:
81
-            msg = _("Image with identifier %s not found") % image_id
82
-            LOG.warn(msg)
83
-            raise webob.exc.HTTPNotFound(msg)
84
-        except exception.Forbidden:
85
-            msg = _("Unauthorized image access")
86
-            LOG.warn(msg)
87
-            raise webob.exc.HTTPForbidden(msg)
88
-        return dict(members=members)
89
-
90
-    @utils.mutating
91
-    def delete(self, req, image_id, id):
92
-        """
93
-        Removes a membership from the image.
94
-        """
95
-        self._check_can_access_image_members(req.context)
96
-        self._enforce(req, 'delete_member')
97
-        self._raise_404_if_image_deleted(req, image_id)
98
-
99
-        try:
100
-            registry.delete_member(req.context, image_id, id)
101
-            self._update_store_acls(req, image_id)
102
-        except exception.NotFound as e:
103
-            LOG.debug(encodeutils.exception_to_unicode(e))
104
-            raise webob.exc.HTTPNotFound(explanation=e.msg)
105
-        except exception.Forbidden as e:
106
-            LOG.debug("User not permitted to remove membership from image "
107
-                      "'%s'", image_id)
108
-            raise webob.exc.HTTPNotFound(explanation=e.msg)
109
-
110
-        return webob.exc.HTTPNoContent()
111
-
112
-    def default(self, req, image_id, id, body=None):
113
-        """This will cover the missing 'show' and 'create' actions"""
114
-        raise webob.exc.HTTPMethodNotAllowed()
115
-
116
-    def _enforce_image_member_quota(self, req, attempted):
117
-        if CONF.image_member_quota < 0:
118
-            # If value is negative, allow unlimited number of members
119
-            return
120
-
121
-        maximum = CONF.image_member_quota
122
-        if attempted > maximum:
123
-            msg = _("The limit has been exceeded on the number of allowed "
124
-                    "image members for this image. Attempted: %(attempted)s, "
125
-                    "Maximum: %(maximum)s") % {'attempted': attempted,
126
-                                               'maximum': maximum}
127
-            raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg,
128
-                                                      request=req)
129
-
130
-    @utils.mutating
131
-    def update(self, req, image_id, id, body=None):
132
-        """
133
-        Adds a membership to the image, or updates an existing one.
134
-        If a body is present, it is a dict with the following format
135
-
136
-        ::
137
-
138
-            {'member': {
139
-                'can_share': [True|False]
140
-            }}
141
-
142
-        If `can_share` is provided, the member's ability to share is
143
-        set accordingly.  If it is not provided, existing memberships
144
-        remain unchanged and new memberships default to False.
145
-        """
146
-        self._check_can_access_image_members(req.context)
147
-        self._enforce(req, 'modify_member')
148
-        self._raise_404_if_image_deleted(req, image_id)
149
-
150
-        new_number_of_members = len(registry.get_image_members(req.context,
151
-                                                               image_id)) + 1
152
-        self._enforce_image_member_quota(req, new_number_of_members)
153
-
154
-        # Figure out can_share
155
-        can_share = None
156
-        if body and 'member' in body and 'can_share' in body['member']:
157
-            can_share = bool(body['member']['can_share'])
158
-        try:
159
-            registry.add_member(req.context, image_id, id, can_share)
160
-            self._update_store_acls(req, image_id)
161
-        except exception.Invalid as e:
162
-            LOG.debug(encodeutils.exception_to_unicode(e))
163
-            raise webob.exc.HTTPBadRequest(explanation=e.msg)
164
-        except exception.NotFound as e:
165
-            LOG.debug(encodeutils.exception_to_unicode(e))
166
-            raise webob.exc.HTTPNotFound(explanation=e.msg)
167
-        except exception.Forbidden as e:
168
-            LOG.debug(encodeutils.exception_to_unicode(e))
169
-            raise webob.exc.HTTPNotFound(explanation=e.msg)
170
-
171
-        return webob.exc.HTTPNoContent()
172
-
173
-    @utils.mutating
174
-    def update_all(self, req, image_id, body):
175
-        """
176
-        Replaces the members of the image with those specified in the
177
-        body.  The body is a dict with the following format
178
-
179
-        ::
180
-
181
-            {'memberships': [
182
-                {'member_id': <MEMBER_ID>,
183
-                 ['can_share': [True|False]]}, ...
184
-            ]}
185
-
186
-        """
187
-        self._check_can_access_image_members(req.context)
188
-        self._enforce(req, 'modify_member')
189
-        self._raise_404_if_image_deleted(req, image_id)
190
-
191
-        memberships = body.get('memberships')
192
-        if memberships:
193
-            new_number_of_members = len(body['memberships'])
194
-            self._enforce_image_member_quota(req, new_number_of_members)
195
-
196
-        try:
197
-            registry.replace_members(req.context, image_id, body)
198
-            self._update_store_acls(req, image_id)
199
-        except exception.Invalid as e:
200
-            LOG.debug(encodeutils.exception_to_unicode(e))
201
-            raise webob.exc.HTTPBadRequest(explanation=e.msg)
202
-        except exception.NotFound as e:
203
-            LOG.debug(encodeutils.exception_to_unicode(e))
204
-            raise webob.exc.HTTPNotFound(explanation=e.msg)
205
-        except exception.Forbidden as e:
206
-            LOG.debug(encodeutils.exception_to_unicode(e))
207
-            raise webob.exc.HTTPNotFound(explanation=e.msg)
208
-
209
-        return webob.exc.HTTPNoContent()
210
-
211
-    def index_shared_images(self, req, id):
212
-        """
213
-        Retrieves list of image memberships for the given member.
214
-
215
-        :param req: the Request object coming from the wsgi layer
216
-        :param id: the opaque member identifier
217
-        :returns: The response body is a mapping of the following form
218
-
219
-        ::
220
-
221
-            {'shared_images': [
222
-                {'image_id': <IMAGE>,
223
-                 'can_share': <SHARE_PERMISSION>, ...}, ...
224
-            ]}
225
-
226
-        """
227
-        try:
228
-            members = registry.get_member_images(req.context, id)
229
-        except exception.NotFound as e:
230
-            LOG.debug(encodeutils.exception_to_unicode(e))
231
-            raise webob.exc.HTTPNotFound(explanation=e.msg)
232
-        except exception.Forbidden as e:
233
-            LOG.debug(encodeutils.exception_to_unicode(e))
234
-            raise webob.exc.HTTPForbidden(explanation=e.msg)
235
-        return dict(shared_images=members)
236
-
237
-    def _update_store_acls(self, req, image_id):
238
-        image_meta = self.get_image_meta_or_404(req, image_id)
239
-        location_uri = image_meta.get('location')
240
-        public = image_meta.get('is_public')
241
-        self.update_store_acls(req, image_id, location_uri, public)
242
-
243
-
244
-def create_resource():
245
-    """Image members resource factory method"""
246
-    deserializer = wsgi.JSONRequestDeserializer()
247
-    serializer = wsgi.JSONResponseSerializer()
248
-    return wsgi.Resource(Controller(), deserializer, serializer)

+ 1
- 79
glance/api/v1/router.py View File

@@ -14,8 +14,6 @@
14 14
 #    under the License.
15 15
 
16 16
 
17
-from glance.api.v1 import images
18
-from glance.api.v1 import members
19 17
 from glance.common import wsgi
20 18
 
21 19
 
@@ -26,84 +24,8 @@ class API(wsgi.Router):
26 24
     def __init__(self, mapper):
27 25
         reject_method_resource = wsgi.Resource(wsgi.RejectMethodController())
28 26
 
29
-        images_resource = images.create_resource()
30
-
31 27
         mapper.connect("/",
32
-                       controller=images_resource,
33
-                       action="index")
34
-        mapper.connect("/images",
35
-                       controller=images_resource,
36
-                       action='index',
37
-                       conditions={'method': ['GET']})
38
-        mapper.connect("/images",
39
-                       controller=images_resource,
40
-                       action='create',
41
-                       conditions={'method': ['POST']})
42
-        mapper.connect("/images",
43
-                       controller=reject_method_resource,
44
-                       action='reject',
45
-                       allowed_methods='GET, POST')
46
-        mapper.connect("/images/detail",
47
-                       controller=images_resource,
48
-                       action='detail',
49
-                       conditions={'method': ['GET', 'HEAD']})
50
-        mapper.connect("/images/detail",
51
-                       controller=reject_method_resource,
52
-                       action='reject',
53
-                       allowed_methods='GET, HEAD')
54
-        mapper.connect("/images/{id}",
55
-                       controller=images_resource,
56
-                       action="meta",
57
-                       conditions=dict(method=["HEAD"]))
58
-        mapper.connect("/images/{id}",
59
-                       controller=images_resource,
60
-                       action="show",
61
-                       conditions=dict(method=["GET"]))
62
-        mapper.connect("/images/{id}",
63
-                       controller=images_resource,
64
-                       action="update",
65
-                       conditions=dict(method=["PUT"]))
66
-        mapper.connect("/images/{id}",
67
-                       controller=images_resource,
68
-                       action="delete",
69
-                       conditions=dict(method=["DELETE"]))
70
-        mapper.connect("/images/{id}",
71
-                       controller=reject_method_resource,
72
-                       action='reject',
73
-                       allowed_methods='GET, HEAD, PUT, DELETE')
74
-
75
-        members_resource = members.create_resource()
76
-
77
-        mapper.connect("/images/{image_id}/members",
78
-                       controller=members_resource,
79
-                       action="index",
80
-                       conditions={'method': ['GET']})
81
-        mapper.connect("/images/{image_id}/members",
82
-                       controller=members_resource,
83
-                       action="update_all",
84
-                       conditions=dict(method=["PUT"]))
85
-        mapper.connect("/images/{image_id}/members",
86
-                       controller=reject_method_resource,
87
-                       action='reject',
88
-                       allowed_methods='GET, PUT')
89
-        mapper.connect("/images/{image_id}/members/{id}",
90
-                       controller=members_resource,
91
-                       action="show",
92
-                       conditions={'method': ['GET']})
93
-        mapper.connect("/images/{image_id}/members/{id}",
94
-                       controller=members_resource,
95
-                       action="update",
96
-                       conditions={'method': ['PUT']})
97
-        mapper.connect("/images/{image_id}/members/{id}",
98
-                       controller=members_resource,
99
-                       action="delete",
100
-                       conditions={'method': ['DELETE']})
101
-        mapper.connect("/images/{image_id}/members/{id}",
102 28
                        controller=reject_method_resource,
103
-                       action='reject',
104
-                       allowed_methods='GET, PUT, DELETE')
105
-        mapper.connect("/shared-images/{id}",
106
-                       controller=members_resource,
107
-                       action="index_shared_images")
29
+                       action="reject")
108 30
 
109 31
         super(API, self).__init__(mapper)

+ 1
- 15
glance/api/versions.py View File

@@ -20,7 +20,7 @@ from six.moves import http_client
20 20
 import webob.dec
21 21
 
22 22
 from glance.common import wsgi
23
-from glance.i18n import _, _LW
23
+from glance.i18n import _
24 24
 
25 25
 
26 26
 versions_opts = [
@@ -82,20 +82,6 @@ class Controller(object):
82 82
                 build_version_object(2.1, 'v2', 'SUPPORTED'),
83 83
                 build_version_object(2.0, 'v2', 'SUPPORTED'),
84 84
             ])
85
-        if CONF.enable_v1_api:
86
-            LOG.warn(_LW('The Images (Glance) v1 API is deprecated and will '
87
-                         'be removed on or after the Pike release, following '
88
-                         'the standard OpenStack deprecation policy. '
89
-                         'Currently, the solution is to set '
90
-                         'enable_v1_api=False and enable_v2_api=True in your '
91
-                         'glance-api.conf file. Once those options are '
92
-                         'removed from the code, Images (Glance) v2 API will '
93
-                         'be switched on by default and will be the only '
94
-                         'option to deploy and use.'))
95
-            version_objs.extend([
96
-                build_version_object(1.1, 'v1', 'DEPRECATED'),
97
-                build_version_object(1.0, 'v1', 'DEPRECATED'),
98
-            ])
99 85
 
100 86
         status = explicit and http_client.OK or http_client.MULTIPLE_CHOICES
101 87
         response = webob.Response(request=req,

+ 0
- 490
glance/cmd/cache_manage.py View File

@@ -1,490 +0,0 @@
1
-#!/usr/bin/env python
2
-
3
-# Copyright 2011 OpenStack Foundation
4
-# All Rights Reserved.
5
-#
6
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
7
-#    not use this file except in compliance with the License. You may obtain
8
-#    a copy of the License at
9
-#
10
-#         http://www.apache.org/licenses/LICENSE-2.0
11
-#
12
-#    Unless required by applicable law or agreed to in writing, software
13
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15
-#    License for the specific language governing permissions and limitations
16
-#    under the License.
17
-
18
-"""
19
-A simple cache management utility for Glance.
20
-"""
21
-from __future__ import print_function
22
-
23
-import argparse
24
-import collections
25
-import datetime
26
-import functools
27
-import os
28
-import sys
29
-import time
30
-
31
-from oslo_utils import encodeutils
32
-import prettytable
33
-
34
-from six.moves import input
35
-
36
-# If ../glance/__init__.py exists, add ../ to Python search path, so that
37
-# it will override what happens to be installed in /usr/(local/)lib/python...
38
-possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
39
-                                   os.pardir,
40
-                                   os.pardir))
41
-if os.path.exists(os.path.join(possible_topdir, 'glance', '__init__.py')):
42
-    sys.path.insert(0, possible_topdir)
43
-
44
-from glance.common import exception
45
-import glance.image_cache.client
46
-from glance.version import version_info as version
47
-
48
-
49
-SUCCESS = 0
50
-FAILURE = 1
51
-
52
-
53
-def catch_error(action):
54
-    """Decorator to provide sensible default error handling for actions."""
55
-    def wrap(func):
56
-        @functools.wraps(func)
57
-        def wrapper(*args, **kwargs):
58
-            try:
59
-                ret = func(*args, **kwargs)
60
-                return SUCCESS if ret is None else ret
61
-            except exception.NotFound:
62
-                options = args[0]
63
-                print("Cache management middleware not enabled on host %s" %
64
-                      options.host)
65
-                return FAILURE
66
-            except exception.Forbidden:
67
-                print("Not authorized to make this request.")
68
-                return FAILURE
69
-            except Exception as e:
70
-                options = args[0]
71
-                if options.debug:
72
-                    raise
73
-                print("Failed to %s. Got error:" % action)
74
-                pieces = encodeutils.exception_to_unicode(e).split('\n')
75
-                for piece in pieces:
76
-                    print(piece)
77
-                return FAILURE
78
-
79
-        return wrapper
80
-    return wrap
81
-
82
-
83
-@catch_error('show cached images')
84
-def list_cached(args):
85
-    """%(prog)s list-cached [options]
86
-
87
-    List all images currently cached.
88
-    """
89
-    client = get_client(args)
90
-    images = client.get_cached_images()
91
-    if not images:
92
-        print("No cached images.")
93
-        return SUCCESS
94
-
95
-    print("Found %d cached images..." % len(images))
96
-
97
-    pretty_table = prettytable.PrettyTable(("ID",
98
-                                            "Last Accessed (UTC)",
99
-                                            "Last Modified (UTC)",
100
-                                            "Size",
101
-                                            "Hits"))
102
-    pretty_table.align['Size'] = "r"
103
-    pretty_table.align['Hits'] = "r"
104
-
105
-    for image in images:
106
-        last_accessed = image['last_accessed']
107
-        if last_accessed == 0:
108
-            last_accessed = "N/A"
109
-        else:
110
-            last_accessed = datetime.datetime.utcfromtimestamp(
111
-                last_accessed).isoformat()
112
-
113
-        pretty_table.add_row((
114
-            image['image_id'],
115
-            last_accessed,
116
-            datetime.datetime.utcfromtimestamp(
117
-                image['last_modified']).isoformat(),
118
-            image['size'],
119
-            image['hits']))
120
-
121
-    print(pretty_table.get_string())
122
-    return SUCCESS
123
-
124
-
125
-@catch_error('show queued images')
126
-def list_queued(args):
127
-    """%(prog)s list-queued [options]
128
-
129
-    List all images currently queued for caching.
130
-    """
131
-    client = get_client(args)
132
-    images = client.get_queued_images()
133
-    if not images:
134
-        print("No queued images.")
135
-        return SUCCESS
136
-
137
-    print("Found %d queued images..." % len(images))
138
-
139
-    pretty_table = prettytable.PrettyTable(("ID",))
140
-
141
-    for image in images:
142
-        pretty_table.add_row((image,))
143
-
144
-    print(pretty_table.get_string())
145
-
146
-
147
-@catch_error('queue the specified image for caching')
148
-def queue_image(args):
149
-    """%(prog)s queue-image <IMAGE_ID> [options]
150
-
151
-    Queues an image for caching.
152
-    """
153
-    if len(args.command) == 2:
154
-        image_id = args.command[1]
155
-    else:
156
-        print("Please specify one and only ID of the image you wish to ")
157
-        print("queue from the cache as the first argument")
158
-        return FAILURE
159
-
160
-    if (not args.force and
161
-        not user_confirm("Queue image %(image_id)s for caching?" %
162
-                         {'image_id': image_id}, default=False)):
163
-        return SUCCESS
164
-
165
-    client = get_client(args)
166
-    client.queue_image_for_caching(image_id)
167
-
168
-    if args.verbose:
169
-        print("Queued image %(image_id)s for caching" %
170
-              {'image_id': image_id})
171
-
172
-    return SUCCESS
173
-
174
-
175
-@catch_error('delete the specified cached image')
176
-def delete_cached_image(args):
177
-    """%(prog)s delete-cached-image <IMAGE_ID> [options]
178
-
179
-    Deletes an image from the cache.
180
-    """
181
-    if len(args.command) == 2:
182
-        image_id = args.command[1]
183
-    else:
184
-        print("Please specify one and only ID of the image you wish to ")
185
-        print("delete from the cache as the first argument")
186
-        return FAILURE
187
-
188
-    if (not args.force and
189
-        not user_confirm("Delete cached image %(image_id)s?" %
190
-                         {'image_id': image_id}, default=False)):
191
-        return SUCCESS
192
-
193
-    client = get_client(args)
194
-    client.delete_cached_image(image_id)
195
-
196
-    if args.verbose:
197
-        print("Deleted cached image %(image_id)s" % {'image_id': image_id})
198
-
199
-    return SUCCESS
200
-
201
-
202
-@catch_error('Delete all cached images')
203
-def delete_all_cached_images(args):
204
-    """%(prog)s delete-all-cached-images [options]
205
-
206
-    Remove all images from the cache.
207
-    """
208
-    if (not args.force and
209
-            not user_confirm("Delete all cached images?", default=False)):
210
-        return SUCCESS
211
-
212
-    client = get_client(args)
213
-    num_deleted = client.delete_all_cached_images()
214
-
215
-    if args.verbose:
216
-        print("Deleted %(num_deleted)s cached images" %
217
-              {'num_deleted': num_deleted})
218
-
219
-    return SUCCESS
220
-
221
-
222
-@catch_error('delete the specified queued image')
223
-def delete_queued_image(args):
224
-    """%(prog)s delete-queued-image <IMAGE_ID> [options]
225
-
226
-    Deletes an image from the cache.
227
-    """
228
-    if len(args.command) == 2:
229
-        image_id = args.command[1]
230
-    else:
231
-        print("Please specify one and only ID of the image you wish to ")
232
-        print("delete from the cache as the first argument")
233
-        return FAILURE
234
-
235
-    if (not args.force and
236
-        not user_confirm("Delete queued image %(image_id)s?" %
237
-                         {'image_id': image_id}, default=False)):
238
-        return SUCCESS
239
-
240
-    client = get_client(args)
241
-    client.delete_queued_image(image_id)
242
-
243
-    if args.verbose:
244
-        print("Deleted queued image %(image_id)s" % {'image_id': image_id})
245
-
246
-    return SUCCESS
247
-
248
-
249
-@catch_error('Delete all queued images')
250
-def delete_all_queued_images(args):
251
-    """%(prog)s delete-all-queued-images [options]
252
-
253
-    Remove all images from the cache queue.
254
-    """
255
-    if (not args.force and
256
-            not user_confirm("Delete all queued images?", default=False)):
257
-        return SUCCESS
258
-
259
-    client = get_client(args)
260
-    num_deleted = client.delete_all_queued_images()
261
-
262
-    if args.verbose:
263
-        print("Deleted %(num_deleted)s queued images" %
264
-              {'num_deleted': num_deleted})
265
-
266
-    return SUCCESS
267
-
268
-
269
-def get_client(options):
270
-    """Return a new client object to a Glance server.
271
-
272
-    specified by the --host and --port options
273
-    supplied to the CLI
274
-    """
275
-    return glance.image_cache.client.get_client(
276
-        host=options.host,
277
-        port=options.port,
278
-        username=options.os_username,
279
-        password=options.os_password,
280
-        tenant=options.os_tenant_name,
281
-        auth_url=options.os_auth_url,
282
-        auth_strategy=options.os_auth_strategy,
283
-        auth_token=options.os_auth_token,
284
-        region=options.os_region_name,
285
-        insecure=options.insecure)
286
-
287
-
288
-def env(*vars, **kwargs):
289
-    """Search for the first defined of possibly many env vars.
290
-
291
-    Returns the first environment variable defined in vars, or
292
-    returns the default defined in kwargs.
293
-    """
294
-    for v in vars:
295
-        value = os.environ.get(v)
296
-        if value:
297
-            return value
298
-    return kwargs.get('default', '')
299
-
300
-
301
-def print_help(args):
302
-    """
303
-    Print help specific to a command
304
-    """
305
-    command = lookup_command(args.command[1])
306
-    print(command.__doc__ % {'prog': os.path.basename(sys.argv[0])})
307
-
308
-
309
-def parse_args(parser):
310
-    """Set up the CLI and config-file options that may be
311
-    parsed and program commands.
312
-
313
-    :param parser: The option parser
314
-    """
315
-    parser.add_argument('command', default='help', nargs='*',
316
-                        help='The command to execute')
317
-    parser.add_argument('-v', '--verbose', default=False, action="store_true",
318
-                        help="Print more verbose output.")
319
-    parser.add_argument('-d', '--debug', default=False, action="store_true",
320
-                        help="Print debugging output.")
321
-    parser.add_argument('-H', '--host', metavar="ADDRESS", default="0.0.0.0",
322
-                        help="Address of Glance API host.")
323
-    parser.add_argument('-p', '--port', dest="port", metavar="PORT",
324
-                        type=int, default=9292,
325
-                        help="Port the Glance API host listens on.")
326
-    parser.add_argument('-k', '--insecure', dest="insecure",
327
-                        default=False, action="store_true",
328
-                        help='Explicitly allow glance to perform "insecure" '
329
-                             "SSL (https) requests. The server's certificate "
330
-                             "will not be verified against any certificate "
331
-                             "authorities. This option should be used with "
332
-                             "caution.")
333
-    parser.add_argument('-f', '--force', dest="force",
334
-                        default=False, action="store_true",
335
-                        help="Prevent select actions from requesting "
336
-                             "user confirmation.")
337
-
338
-    parser.add_argument('--os-auth-token',
339
-                        dest='os_auth_token',
340
-                        default=env('OS_AUTH_TOKEN'),
341
-                        help='Defaults to env[OS_AUTH_TOKEN].')
342
-    parser.add_argument('-A', '--os_auth_token', '--auth_token',
343
-                        dest='os_auth_token',
344
-                        help=argparse.SUPPRESS)
345
-
346
-    parser.add_argument('--os-username',
347
-                        dest='os_username',
348
-                        default=env('OS_USERNAME'),
349
-                        help='Defaults to env[OS_USERNAME].')
350
-    parser.add_argument('-I', '--os_username',
351
-                        dest='os_username',
352
-                        help=argparse.SUPPRESS)
353
-
354
-    parser.add_argument('--os-password',
355
-                        dest='os_password',
356
-                        default=env('OS_PASSWORD'),
357
-                        help='Defaults to env[OS_PASSWORD].')
358
-    parser.add_argument('-K', '--os_password',
359
-                        dest='os_password',
360
-                        help=argparse.SUPPRESS)
361
-
362
-    parser.add_argument('--os-region-name',
363
-                        dest='os_region_name',
364
-                        default=env('OS_REGION_NAME'),
365
-                        help='Defaults to env[OS_REGION_NAME].')
366
-    parser.add_argument('-R', '--os_region_name',
367
-                        dest='os_region_name',
368
-                        help=argparse.SUPPRESS)
369
-
370
-    parser.add_argument('--os-tenant-id',
371
-                        dest='os_tenant_id',
372
-                        default=env('OS_TENANT_ID'),
373
-                        help='Defaults to env[OS_TENANT_ID].')
374
-    parser.add_argument('--os_tenant_id',
375
-                        dest='os_tenant_id',
376
-                        help=argparse.SUPPRESS)
377
-
378
-    parser.add_argument('--os-tenant-name',
379
-                        dest='os_tenant_name',
380
-                        default=env('OS_TENANT_NAME'),
381
-                        help='Defaults to env[OS_TENANT_NAME].')
382
-    parser.add_argument('-T', '--os_tenant_name',
383
-                        dest='os_tenant_name',
384
-                        help=argparse.SUPPRESS)
385
-
386
-    parser.add_argument('--os-auth-url',
387
-                        default=env('OS_AUTH_URL'),
388
-                        help='Defaults to env[OS_AUTH_URL].')
389
-    parser.add_argument('-N', '--os_auth_url',
390
-                        dest='os_auth_url',
391
-                        help=argparse.SUPPRESS)
392
-
393
-    parser.add_argument('-S', '--os_auth_strategy', dest="os_auth_strategy",
394
-                        metavar="STRATEGY",
395
-                        help="Authentication strategy (keystone or noauth).")
396
-
397
-    version_string = version.cached_version_string()
398
-    parser.add_argument('--version', action='version',
399
-                        version=version_string)
400
-
401
-    return parser.parse_args()
402
-
403
-
404
-CACHE_COMMANDS = collections.OrderedDict()
405
-CACHE_COMMANDS['help'] = (
406
-    print_help, 'Output help for one of the commands below')
407
-CACHE_COMMANDS['list-cached'] = (
408
-    list_cached, 'List all images currently cached')
409
-CACHE_COMMANDS['list-queued'] = (
410
-    list_queued, 'List all images currently queued for caching')
411
-CACHE_COMMANDS['queue-image'] = (
412
-    queue_image, 'Queue an image for caching')
413
-CACHE_COMMANDS['delete-cached-image'] = (
414
-    delete_cached_image, 'Purges an image from the cache')
415
-CACHE_COMMANDS['delete-all-cached-images'] = (
416
-    delete_all_cached_images, 'Removes all images from the cache')
417
-CACHE_COMMANDS['delete-queued-image'] = (
418
-    delete_queued_image, 'Deletes an image from the cache queue')
419
-CACHE_COMMANDS['delete-all-queued-images'] = (
420
-    delete_all_queued_images, 'Deletes all images from the cache queue')
421
-
422
-
423
-def _format_command_help():
424
-    """Formats the help string for subcommands."""
425
-    help_msg = "Commands:\n\n"
426
-
427
-    for command, info in CACHE_COMMANDS.items():
428
-        if command == 'help':
429
-            command = 'help <command>'
430
-        help_msg += "    %-28s%s\n\n" % (command, info[1])
431
-
432
-    return help_msg
433
-
434
-
435
-def lookup_command(command_name):
436
-    try:
437
-        command = CACHE_COMMANDS[command_name]
438
-        return command[0]
439
-    except KeyError:
440
-        print('\nError: "%s" is not a valid command.\n' % command_name)
441
-        print(_format_command_help())
442
-        sys.exit("Unknown command: %(cmd_name)s" % {'cmd_name': command_name})
443
-
444
-
445
-def user_confirm(prompt, default=False):
446
-    """Yes/No question dialog with user.
447
-
448
-    :param prompt: question/statement to present to user (string)
449
-    :param default: boolean value to return if empty string
450
-                    is received as response to prompt
451
-
452
-    """
453
-    if default:
454
-        prompt_default = "[Y/n]"
455
-    else:
456
-        prompt_default = "[y/N]"
457
-
458
-    answer = input("%s %s " % (prompt, prompt_default))
459
-
460
-    if answer == "":
461
-        return default
462
-    else:
463
-        return answer.lower() in ("yes", "y")
464
-
465
-
466
-def main():
467
-    parser = argparse.ArgumentParser(
468
-        description=_format_command_help(),
469
-        formatter_class=argparse.RawDescriptionHelpFormatter)
470
-    args = parse_args(parser)
471
-
472
-    if args.command[0] == 'help' and len(args.command) == 1:
473
-        parser.print_help()
474
-        return
475
-
476
-    # Look up the command to run
477
-    command = lookup_command(args.command[0])
478
-
479
-    try:
480
-        start_time = time.time()
481
-        result = command(args)
482
-        end_time = time.time()
483
-        if args.verbose:
484
-            print("Completed in %-0.4f sec." % (end_time - start_time))
485
-        sys.exit(result)
486
-    except (RuntimeError, NotImplementedError) as e:
487
-        sys.exit("ERROR: %s" % e)
488
-
489
-if __name__ == '__main__':
490
-    main()

+ 1
- 71
glance/common/config.py View File

@@ -454,50 +454,6 @@ Possible values:
454 454
 Related options:
455 455
     * None
456 456
 
457
-""")),
458
-    # NOTE(nikhil): Even though deprecated, the configuration option
459
-    # ``enable_v1_api`` is set to True by default on purpose. Having it enabled
460
-    # helps the projects that haven't been able to fully move to v2 yet by
461
-    # keeping the devstack setup to use glance v1 as well. We need to switch it
462
-    # to False by default soon after Newton is cut so that we can identify the
463
-    # projects that haven't moved to v2 yet and start having some interesting
464
-    # conversations with them. Switching to False in Newton may result into
465
-    # destabilizing the gate and affect the release.
466
-    cfg.BoolOpt('enable_v1_api',
467
-                default=True,
468
-                deprecated_reason=_DEPRECATE_GLANCE_V1_MSG,
469
-                deprecated_since='Newton',
470
-                help=_("""
471
-Deploy the v1 OpenStack Images API.
472
-
473
-When this option is set to ``True``, Glance service will respond to
474
-requests on registered endpoints conforming to the v1 OpenStack
475
-Images API.
476
-
477
-NOTES:
478
-    * If this option is enabled, then ``enable_v1_registry`` must
479
-      also be set to ``True`` to enable mandatory usage of Registry
480
-      service with v1 API.
481
-
482
-    * If this option is disabled, then the ``enable_v1_registry``
483
-      option, which is enabled by default, is also recommended
484
-      to be disabled.
485
-
486
-    * This option is separate from ``enable_v2_api``, both v1 and v2
487
-      OpenStack Images API can be deployed independent of each
488
-      other.
489
-
490
-    * If deploying only the v2 Images API, this option, which is
491
-      enabled by default, should be disabled.
492
-
493
-Possible values:
494
-    * True
495
-    * False
496
-
497
-Related options:
498
-    * enable_v1_registry
499
-    * enable_v2_api
500
-
501 457
 """)),
502 458
     cfg.BoolOpt('enable_v2_api',
503 459
                 default=True,
@@ -523,20 +479,12 @@ NOTES:
523 479
       option, which is enabled by default, is also recommended
524 480
       to be disabled.
525 481
 
526
-    * This option is separate from ``enable_v1_api``, both v1 and v2
527
-      OpenStack Images API can be deployed independent of each
528
-      other.
529
-
530
-    * If deploying only the v1 Images API, this option, which is
531
-      enabled by default, should be disabled.
532
-
533 482
 Possible values:
534 483
     * True
535 484
     * False
536 485
 
537 486
 Related options:
538 487
     * enable_v2_registry
539
-    * enable_v1_api
540 488
 
541 489
 """)),
542 490
     cfg.BoolOpt('enable_v1_registry',
@@ -544,25 +492,7 @@ Related options:
544 492
                 deprecated_reason=_DEPRECATE_GLANCE_V1_MSG,
545 493
                 deprecated_since='Newton',
546 494
                 help=_("""
547
-Deploy the v1 API Registry service.
548
-
549
-When this option is set to ``True``, the Registry service
550
-will be enabled in Glance for v1 API requests.
551
-
552
-NOTES:
553
-    * Use of Registry is mandatory in v1 API, so this option must
554
-      be set to ``True`` if the ``enable_v1_api`` option is enabled.
555
-
556
-    * If deploying only the v2 OpenStack Images API, this option,
557
-      which is enabled by default, should be disabled.
558
-
559
-Possible values:
560
-    * True
561
-    * False
562
-
563
-Related options:
564
-    * enable_v1_api
565
-
495
+                    DEPRECATED FOR REMOVAL
566 496
 """)),
567 497
     cfg.BoolOpt('enable_v2_registry',
568 498
                 default=True,

+ 1
- 0
glance/common/store_utils.py View File

@@ -27,6 +27,7 @@ from glance import scrubber
27 27
 LOG = logging.getLogger(__name__)
28 28
 
29 29
 CONF = cfg.CONF
30
+CONF.import_opt('use_user_token', 'glance.registry.client')
30 31
 
31 32
 RESTRICTED_URI_SCHEMAS = frozenset(['file', 'filesystem', 'swift+config'])
32 33
 

+ 0
- 132
glance/image_cache/client.py View File

@@ -1,132 +0,0 @@
1
-# Copyright 2012 OpenStack Foundation
2
-# All Rights Reserved.
3
-#
4
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
5
-#    not use this file except in compliance with the License. You may obtain
6
-#    a copy of the License at
7
-#
8
-#         http://www.apache.org/licenses/LICENSE-2.0
9
-#
10
-#    Unless required by applicable law or agreed to in writing, software
11
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
-#    License for the specific language governing permissions and limitations
14
-#    under the License.
15
-
16
-import os
17
-
18
-from oslo_serialization import jsonutils as json
19
-
20
-from glance.common import client as base_client
21
-from glance.common import exception
22
-from glance.i18n import _
23
-
24
-
25
-class CacheClient(base_client.BaseClient):
26
-
27
-    DEFAULT_PORT = 9292
28
-    DEFAULT_DOC_ROOT = '/v1'
29
-
30
-    def delete_cached_image(self, image_id):
31
-        """
32
-        Delete a specified image from the cache
33
-        """
34
-        self.do_request("DELETE", "/cached_images/%s" % image_id)
35
-        return True
36
-
37
-    def get_cached_images(self, **kwargs):
38
-        """
39
-        Returns a list of images stored in the image cache.
40
-        """
41
-        res = self.do_request("GET", "/cached_images")
42
-        data = json.loads(res.read())['cached_images']
43
-        return data
44
-
45
-    def get_queued_images(self, **kwargs):
46
-        """
47
-        Returns a list of images queued for caching
48
-        """
49
-        res = self.do_request("GET", "/queued_images")
50
-        data = json.loads(res.read())['queued_images']
51
-        return data
52
-
53
-    def delete_all_cached_images(self):
54
-        """
55
-        Delete all cached images
56
-        """
57
-        res = self.do_request("DELETE", "/cached_images")
58
-        data = json.loads(res.read())
59
-        num_deleted = data['num_deleted']
60
-        return num_deleted
61
-
62
-    def queue_image_for_caching(self, image_id):
63
-        """
64
-        Queue an image for prefetching into cache
65
-        """
66
-        self.do_request("PUT", "/queued_images/%s" % image_id)
67
-        return True
68
-
69
-    def delete_queued_image(self, image_id):
70
-        """
71
-        Delete a specified image from the cache queue
72
-        """
73
-        self.do_request("DELETE", "/queued_images/%s" % image_id)
74
-        return True
75
-
76
-    def delete_all_queued_images(self):
77
-        """
78
-        Delete all queued images
79
-        """
80
-        res = self.do_request("DELETE", "/queued_images")
81
-        data = json.loads(res.read())
82
-        num_deleted = data['num_deleted']
83
-        return num_deleted
84
-
85
-
86
-def get_client(host, port=None, timeout=None, use_ssl=False, username=None,
87
-               password=None, tenant=None,
88
-               auth_url=None, auth_strategy=None,
89
-               auth_token=None, region=None,
90
-               is_silent_upload=False, insecure=False):
91
-    """
92
-    Returns a new client Glance client object based on common kwargs.
93
-    If an option isn't specified falls back to common environment variable
94
-    defaults.
95
-    """
96
-
97
-    if auth_url or os.getenv('OS_AUTH_URL'):
98
-        force_strategy = 'keystone'
99
-    else:
100
-        force_strategy = None
101
-
102
-    creds = {
103
-        'username': username or
104
-        os.getenv('OS_AUTH_USER', os.getenv('OS_USERNAME')),
105
-        'password': password or
106
-        os.getenv('OS_AUTH_KEY', os.getenv('OS_PASSWORD')),
107
-        'tenant': tenant or
108
-        os.getenv('OS_AUTH_TENANT', os.getenv('OS_TENANT_NAME')),
109
-        'auth_url': auth_url or
110
-        os.getenv('OS_AUTH_URL'),
111
-        'strategy': force_strategy or
112
-        auth_strategy or
113
-        os.getenv('OS_AUTH_STRATEGY', 'noauth'),
114
-        'region': region or
115
-        os.getenv('OS_REGION_NAME'),
116
-    }
117
-
118
-    if creds['strategy'] == 'keystone' and not creds['auth_url']:
119
-        msg = _("--os_auth_url option or OS_AUTH_URL environment variable "
120
-                "required when keystone authentication strategy is enabled\n")
121
-        raise exception.ClientConfigurationError(msg)
122
-
123
-    return CacheClient(
124
-        host=host,
125
-        port=port,
126
-        timeout=timeout,
127
-        use_ssl=use_ssl,
128
-        auth_token=auth_token or
129
-        os.getenv('OS_TOKEN'),
130
-        creds=creds,
131
-        insecure=insecure,
132
-        configure_via_auth=False)

+ 0
- 3
glance/tests/functional/__init__.py View File

@@ -74,9 +74,7 @@ class Server(object):
74 74
         self.show_image_direct_url = False
75 75
         self.show_multiple_locations = False
76 76
         self.property_protection_file = ''
77
-        self.enable_v1_api = True
78 77
         self.enable_v2_api = True
79
-        self.enable_v1_registry = True
80 78
         self.enable_v2_registry = True
81 79
         self.needs_database = False
82 80
         self.log_file = None
@@ -346,7 +344,6 @@ sql_connection = %(sql_connection)s
346 344
 show_image_direct_url = %(show_image_direct_url)s
347 345
 show_multiple_locations = %(show_multiple_locations)s
348 346
 user_storage_quota = %(user_storage_quota)s
349
-enable_v1_api = %(enable_v1_api)s
350 347
 enable_v2_api = %(enable_v2_api)s
351 348
 lock_path = %(lock_path)s
352 349
 property_protection_file = %(property_protection_file)s

+ 12
- 4
glance/tests/functional/serial/test_scrubber.py View File

@@ -78,8 +78,10 @@ class TestScrubber(functional.FunctionalTest):
78 78
         scrubs them
79 79
         """
80 80
         self.cleanup()
81
+        kwargs = self.__dict__.copy()
82
+        kwargs['use_user_token'] = True
81 83
         self.start_servers(delayed_delete=True, daemon=True,
82
-                           metadata_encryption_key='')
84
+                           metadata_encryption_key='', **kwargs)
83 85
         path = "http://%s:%d/v2/images" % ("127.0.0.1", self.api_port)
84 86
         response, content = self._send_create_image_http_request(path)
85 87
         self.assertEqual(http_client.CREATED, response.status)
@@ -112,8 +114,10 @@ class TestScrubber(functional.FunctionalTest):
112 114
         daemon mode
113 115
         """
114 116
         self.cleanup()
117
+        kwargs = self.__dict__.copy()
118
+        kwargs['use_user_token'] = True
115 119
         self.start_servers(delayed_delete=True, daemon=False,
116
-                           metadata_encryption_key='')
120
+                           metadata_encryption_key='', **kwargs)
117 121
         path = "http://%s:%d/v2/images" % ("127.0.0.1", self.api_port)
118 122
         response, content = self._send_create_image_http_request(path)
119 123
         self.assertEqual(http_client.CREATED, response.status)
@@ -159,8 +163,10 @@ class TestScrubber(functional.FunctionalTest):
159 163
 
160 164
         # Start servers.
161 165
         self.cleanup()
166
+        kwargs = self.__dict__.copy()
167
+        kwargs['use_user_token'] = True
162 168
         self.start_servers(delayed_delete=True, daemon=False,
163
-                           default_store='file')
169
+                           default_store='file', **kwargs)
164 170
 
165 171
         # Check that we are using a file backend.
166 172
         self.assertEqual(self.api_server.default_store, 'file')
@@ -235,8 +241,10 @@ class TestScrubber(functional.FunctionalTest):
235 241
 
236 242
     def test_scrubber_restore_image(self):
237 243
         self.cleanup()
244
+        kwargs = self.__dict__.copy()
245
+        kwargs['use_user_token'] = True
238 246
         self.start_servers(delayed_delete=True, daemon=False,
239
-                           metadata_encryption_key='')
247
+                           metadata_encryption_key='', **kwargs)
240 248
         path = "http://%s:%d/v2/images" % ("127.0.0.1", self.api_port)
241 249
         response, content = self._send_create_image_http_request(path)
242 250
         self.assertEqual(http_client.CREATED, response.status)

+ 1
- 149
glance/tests/functional/test_api.py View File

@@ -22,25 +22,6 @@ from six.moves import http_client
22 22
 
23 23
 from glance.tests import functional
24 24
 
25
-# TODO(rosmaita): all the EXPERIMENTAL stuff in this file can be ripped out
26
-# when v2.6 becomes CURRENT in Queens
27
-
28
-
29
-def _generate_v1_versions(url):
30
-    v1_versions = {'versions': [
31
-        {
32
-            'id': 'v1.1',
33
-            'status': 'DEPRECATED',
34
-            'links': [{'rel': 'self', 'href': url % '1'}],
35
-        },
36
-        {
37
-            'id': 'v1.0',
38
-            'status': 'DEPRECATED',
39
-            'links': [{'rel': 'self', 'href': url % '1'}],
40
-        },
41
-    ]}
42
-    return v1_versions
43
-
44 25
 
45 26
 def _generate_v2_versions(url):
46 27
     version_list = []
@@ -86,9 +67,8 @@ def _generate_v2_versions(url):
86 67
 
87 68
 
88 69
 def _generate_all_versions(url):
89
-    v1 = _generate_v1_versions(url)
90 70
     v2 = _generate_v2_versions(url)
91
-    all_versions = {'versions': v2['versions'] + v1['versions']}
71
+    all_versions = {'versions': v2['versions']}
92 72
     return all_versions
93 73
 
94 74
 
@@ -96,7 +76,6 @@ class TestApiVersions(functional.FunctionalTest):
96 76
 
97 77
     def test_version_configurations(self):
98 78
         """Test that versioning is handled properly through all channels"""
99
-        # v1 and v2 api enabled
100 79
         self.start_servers(**self.__dict__.copy())
101 80
 
102 81
         url = 'http://127.0.0.1:%d/v%%s/' % self.api_port
@@ -111,7 +90,6 @@ class TestApiVersions(functional.FunctionalTest):
111 90
         self.assertEqual(versions, content)
112 91
 
113 92
     def test_v2_api_configuration(self):
114
-        self.api_server.enable_v1_api = False
115 93
         self.api_server.enable_v2_api = True
116 94
         self.start_servers(**self.__dict__.copy())
117 95
 
@@ -126,22 +104,6 @@ class TestApiVersions(functional.FunctionalTest):
126 104
         content = jsonutils.loads(content_json.decode())
127 105
         self.assertEqual(versions, content)
128 106
 
129
-    def test_v1_api_configuration(self):
130
-        self.api_server.enable_v1_api = True
131
-        self.api_server.enable_v2_api = False
132
-        self.start_servers(**self.__dict__.copy())
133
-
134
-        url = 'http://127.0.0.1:%d/v%%s/' % self.api_port
135
-        versions = _generate_v1_versions(url)
136
-
137
-        # Verify version choices returned.
138
-        path = 'http://%s:%d' % ('127.0.0.1', self.api_port)
139
-        http = httplib2.Http()
140
-        response, content_json = http.request(path, 'GET')
141
-        self.assertEqual(http_client.MULTIPLE_CHOICES, response.status)
142
-        content = jsonutils.loads(content_json.decode())
143
-        self.assertEqual(versions, content)
144
-
145 107
 
146 108
 class TestApiPaths(functional.FunctionalTest):
147 109
     def setUp(self):
@@ -165,26 +127,6 @@ class TestApiPaths(functional.FunctionalTest):
165 127
         content = jsonutils.loads(content_json.decode())
166 128
         self.assertEqual(self.versions, content)
167 129
 
168
-    def test_get_images_path(self):
169
-        """Assert GET /images with `no Accept:` header.
170
-        Verify version choices returned.
171
-        """
172
-        path = 'http://%s:%d/images' % ('127.0.0.1', self.api_port)
173
-        http = httplib2.Http()
174
-        response, content_json = http.request(path, 'GET')
175
-        self.assertEqual(http_client.MULTIPLE_CHOICES, response.status)
176
-        content = jsonutils.loads(content_json.decode())
177
-        self.assertEqual(self.versions, content)
178
-
179
-    def test_get_v1_images_path(self):
180
-        """GET /v1/images with `no Accept:` header.
181
-        Verify empty images list returned.
182
-        """
183
-        path = 'http://%s:%d/v1/images' % ('127.0.0.1', self.api_port)
184
-        http = httplib2.Http()
185
-        response, content = http.request(path, 'GET')
186
-        self.assertEqual(http_client.OK, response.status)
187
-
188 130
     def test_get_root_path_with_unknown_header(self):
189 131
         """Assert GET / with Accept: unknown header
190 132
         Verify version choices returned. Verify message in API log about
@@ -198,49 +140,6 @@ class TestApiPaths(functional.FunctionalTest):
198 140
         content = jsonutils.loads(content_json.decode())
199 141
         self.assertEqual(self.versions, content)
200 142
 
201
-    def test_get_root_path_with_openstack_header(self):
202
-        """Assert GET / with an Accept: application/vnd.openstack.images-v1
203
-        Verify empty image list returned
204
-        """
205
-        path = 'http://%s:%d/images' % ('127.0.0.1', self.api_port)
206
-        http = httplib2.Http()
207
-        headers = {'Accept': 'application/vnd.openstack.images-v1'}
208
-        response, content = http.request(path, 'GET', headers=headers)
209
-        self.assertEqual(http_client.OK, response.status)
210
-        self.assertEqual(self.images_json, content.decode())
211
-
212
-    def test_get_images_path_with_openstack_header(self):
213
-        """Assert GET /images with a
214
-        `Accept: application/vnd.openstack.compute-v1` header.
215
-        Verify version choices returned. Verify message in API log
216
-        about unknown accept header.
217
-        """
218
-        path = 'http://%s:%d/images' % ('127.0.0.1', self.api_port)
219
-        http = httplib2.Http()
220
-        headers = {'Accept': 'application/vnd.openstack.compute-v1'}
221
-        response, content_json = http.request(path, 'GET', headers=headers)
222
-        self.assertEqual(http_client.MULTIPLE_CHOICES, response.status)
223
-        content = jsonutils.loads(content_json.decode())
224
-        self.assertEqual(self.versions, content)
225
-
226
-    def test_get_v10_images_path(self):
227
-        """Assert GET /v1.0/images with no Accept: header
228
-        Verify version choices returned
229
-        """
230
-        path = 'http://%s:%d/v1.a/images' % ('127.0.0.1', self.api_port)
231
-        http = httplib2.Http()
232
-        response, content = http.request(path, 'GET')
233
-        self.assertEqual(http_client.MULTIPLE_CHOICES, response.status)
234
-
235
-    def test_get_v1a_images_path(self):
236
-        """Assert GET /v1.a/images with no Accept: header
237
-        Verify version choices returned
238
-        """
239
-        path = 'http://%s:%d/v1.a/images' % ('127.0.0.1', self.api_port)
240
-        http = httplib2.Http()
241
-        response, content = http.request(path, 'GET')
242
-        self.assertEqual(http_client.MULTIPLE_CHOICES, response.status)
243
-
244 143
     def test_get_va1_images_path(self):
245 144
         """Assert GET /va.1/images with no Accept: header
246 145
         Verify version choices returned
@@ -263,28 +162,6 @@ class TestApiPaths(functional.FunctionalTest):
263 162
         content = jsonutils.loads(content_json.decode())
264 163
         self.assertEqual(self.versions, content)
265 164
 
266
-    def test_get_versions_path_with_openstack_header(self):
267
-        """Assert GET /versions with the
268
-        `Accept: application/vnd.openstack.images-v1` header.
269
-        Verify version choices returned.
270
-        """
271
-        path = 'http://%s:%d/versions' % ('127.0.0.1', self.api_port)
272
-        http = httplib2.Http()
273
-        headers = {'Accept': 'application/vnd.openstack.images-v1'}
274
-        response, content_json = http.request(path, 'GET', headers=headers)
275
-        self.assertEqual(http_client.OK, response.status)
276
-        content = jsonutils.loads(content_json.decode())
277
-        self.assertEqual(self.versions, content)
278
-
279
-    def test_get_v1_versions_path(self):
280
-        """Assert GET /v1/versions with `no Accept:` header
281
-        Verify 404 returned
282
-        """
283
-        path = 'http://%s:%d/v1/versions' % ('127.0.0.1', self.api_port)
284
-        http = httplib2.Http()
285
-        response, content = http.request(path, 'GET')
286
-        self.assertEqual(http_client.NOT_FOUND, response.status)
287
-
288 165
     def test_get_versions_choices(self):
289 166
         """Verify version choices returned"""
290 167
         path = 'http://%s:%d/v10' % ('127.0.0.1', self.api_port)
@@ -293,28 +170,3 @@ class TestApiPaths(functional.FunctionalTest):
293 170
         self.assertEqual(http_client.MULTIPLE_CHOICES, response.status)
294 171
         content = jsonutils.loads(content_json.decode())
295 172
         self.assertEqual(self.versions, content)
296
-
297
-    def test_get_images_path_with_openstack_v2_header(self):
298
-        """Assert GET /images with a
299
-        `Accept: application/vnd.openstack.compute-v2` header.
300
-        Verify version choices returned. Verify message in API log
301
-        about unknown version in accept header.
302
-        """
303
-        path = 'http://%s:%d/images' % ('127.0.0.1', self.api_port)
304
-        http = httplib2.Http()
305
-        headers = {'Accept': 'application/vnd.openstack.images-v10'}
306
-        response, content_json = http.request(path, 'GET', headers=headers)
307
-        self.assertEqual(http_client.MULTIPLE_CHOICES, response.status)
308
-        content = jsonutils.loads(content_json.decode())
309
-        self.assertEqual(self.versions, content)
310
-
311
-    def test_get_v12_images_path(self):
312
-        """Assert GET /v1.2/images with `no Accept:` header
313
-        Verify version choices returned
314
-        """
315
-        path = 'http://%s:%d/v1.2/images' % ('127.0.0.1', self.api_port)
316
-        http = httplib2.Http()
317
-        response, content_json = http.request(path, 'GET')
318
-        self.assertEqual(http_client.MULTIPLE_CHOICES, response.status)
319
-        content = jsonutils.loads(content_json.decode())
320
-        self.assertEqual(self.versions, content)

+ 0
- 358
glance/tests/functional/test_bin_glance_cache_manage.py View File

@@ -1,358 +0,0 @@
1
-# Copyright 2011 OpenStack Foundation
2
-# All Rights Reserved.
3
-#
4
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
5
-#    not use this file except in compliance with the License. You may obtain
6
-#    a copy of the License at
7
-#
8
-#         http://www.apache.org/licenses/LICENSE-2.0
9
-#
10
-#    Unless required by applicable law or agreed to in writing, software
11
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
-#    License for the specific language governing permissions and limitations
14
-#    under the License.
15
-
16
-"""Functional test case that utilizes the bin/glance-cache-manage CLI tool"""
17
-
18
-import datetime
19
-import hashlib
20
-import os
21
-import sys
22
-
23
-import httplib2
24
-from oslo_serialization import jsonutils
25
-from oslo_utils import units
26
-from six.moves import http_client
27
-# NOTE(jokke): simplified transition to py3, behaves like py2 xrange
28
-from six.moves import range
29
-
30
-from glance.tests import functional
31
-from glance.tests.utils import execute
32
-from glance.tests.utils import minimal_headers
33
-
34
-FIVE_KB = 5 * units.Ki
35
-
36
-
37
-class TestBinGlanceCacheManage(functional.FunctionalTest):
38
-    """Functional tests for the bin/glance CLI tool"""
39
-
40
-    def setUp(self):
41
-        self.image_cache_driver = "sqlite"
42
-
43
-        super(TestBinGlanceCacheManage, self).setUp()
44
-
45
-        self.api_server.deployment_flavor = "cachemanagement"
46
-
47
-        # NOTE(sirp): This is needed in case we are running the tests under an
48
-        # environment in which OS_AUTH_STRATEGY=keystone. The test server we
49
-        # spin up won't have keystone support, so we need to switch to the
50
-        # NoAuth strategy.
51
-        os.environ['OS_AUTH_STRATEGY'] = 'noauth'
52
-        os.environ['OS_AUTH_URL'] = ''
53
-
54
-    def add_image(self, name):
55
-        """
56
-        Adds an image with supplied name and returns the newly-created
57
-        image identifier.
58
-        """
59
-        image_data = b"*" * FIVE_KB
60
-        headers = minimal_headers(name)
61
-        path = "http://%s:%d/v1/images" % ("127.0.0.1", self.api_port)
62
-        http = httplib2.Http()
63
-        response, content = http.request(path, 'POST', headers=headers,
64
-                                         body=image_data)
65
-        self.assertEqual(http_client.CREATED, response.status)
66
-        data = jsonutils.loads(content)
67
-        self.assertEqual(hashlib.md5(image_data).hexdigest(),
68
-                         data['image']['checksum'])
69
-        self.assertEqual(FIVE_KB, data['image']['size'])
70
-        self.assertEqual(name, data['image']['name'])
71
-        self.assertTrue(data['image']['is_public'])
72
-        return data['image']['id']
73
-
74
-    def is_image_cached(self, image_id):
75
-        """
76
-        Return True if supplied image ID is cached, False otherwise
77
-        """
78
-        exe_cmd = '%s -m glance.cmd.cache_manage' % sys.executable
79
-        cmd = "%s --port=%d list-cached" % (exe_cmd, self.api_port)
80
-
81
-        exitcode, out, err = execute(cmd)
82
-
83
-        self.assertEqual(0, exitcode)
84
-        out = out.decode('utf-8')
85
-        return image_id in out
86
-
87
-    def iso_date(self, image_id):
88
-        """
89
-        Return True if supplied image ID is cached, False otherwise
90
-        """
91
-        exe_cmd = '%s -m glance.cmd.cache_manage' % sys.executable
92
-        cmd = "%s --port=%d list-cached" % (exe_cmd, self.api_port)
93
-
94
-        exitcode, out, err = execute(cmd)
95
-        out = out.decode('utf-8')
96
-
97
-        return datetime.datetime.utcnow().strftime("%Y-%m-%d") in out
98
-
99
-    def test_no_cache_enabled(self):
100
-        """
101
-        Test that cache index command works
102
-        """
103
-        self.cleanup()
104
-        self.api_server.deployment_flavor = ''
105
-        self.start_servers()  # Not passing in cache_manage in pipeline...
106
-
107
-        api_port = self.api_port
108
-
109
-        # Verify decent error message returned
110
-        exe_cmd = '%s -m glance.cmd.cache_manage' % sys.executable
111
-        cmd = "%s --port=%d list-cached" % (exe_cmd, api_port)
112
-
113
-        exitcode, out, err = execute(cmd, raise_error=False)
114
-
115
-        self.assertEqual(1, exitcode)
116
-        self.assertIn(b'Cache management middleware not enabled on host',
117
-                      out.strip())
118
-
119
-        self.stop_servers()
120
-
121
-    def test_cache_index(self):
122
-        """
123
-        Test that cache index command works
124
-        """
125
-        self.cleanup()
126
-        self.start_servers(**self.__dict__.copy())
127
-
128
-        api_port = self.api_port
129
-
130
-        # Verify no cached images
131
-        exe_cmd = '%s -m glance.cmd.cache_manage' % sys.executable
132
-        cmd = "%s --port=%d list-cached" % (exe_cmd, api_port)
133
-
134
-        exitcode, out, err = execute(cmd)
135
-
136
-        self.assertEqual(0, exitcode)
137
-        self.assertIn(b'No cached images', out.strip())
138
-
139
-        ids = {}
140
-
141
-        # Add a few images and cache the second one of them
142
-        # by GETing the image...
143
-        for x in range(4):
144
-            ids[x] = self.add_image("Image%s" % x)
145
-
146
-        path = "http://%s:%d/v1/images/%s" % ("127.0.0.1", api_port,
147
-                                              ids[1])
148
-        http = httplib2.Http()
149
-        response, content = http.request(path, 'GET')
150
-        self.assertEqual(http_client.OK, response.status)
151
-
152
-        self.assertTrue(self.is_image_cached(ids[1]),
153
-                        "%s is not cached." % ids[1])
154
-
155
-        self.assertTrue(self.iso_date(ids[1]))
156
-
157
-        self.stop_servers()
158
-
159
-    def test_queue(self):
160
-        """
161
-        Test that we can queue and fetch images using the
162
-        CLI utility
163
-        """
164
-        self.cleanup()
165
-        self.start_servers(**self.__dict__.copy())
166
-
167
-        api_port = self.api_port
168
-
169
-        # Verify no cached images
170
-        exe_cmd = '%s -m glance.cmd.cache_manage' % sys.executable
171
-        cmd = "%s --port=%d list-cached" % (exe_cmd, api_port)
172
-
173
-        exitcode, out, err = execute(cmd)
174
-
175
-        self.assertEqual(0, exitcode)
176
-        self.assertIn(b'No cached images', out.strip())
177
-
178
-        # Verify no queued images
179
-        cmd = "%s --port=%d list-queued" % (exe_cmd, api_port)
180
-
181
-        exitcode, out, err = execute(cmd)
182
-
183
-        self.assertEqual(0, exitcode)
184
-        self.assertIn(b'No queued images', out.strip())
185
-
186
-        ids = {}
187
-
188
-        # Add a few images and cache the second one of them
189
-        # by GETing the image...
190
-        for x in range(4):
191
-            ids[x] = self.add_image("Image%s" % x)
192
-
193
-        # Queue second image and then cache it
194
-        cmd = "%s --port=%d --force queue-image %s" % (
195
-            exe_cmd, api_port, ids[1])
196
-
197
-        exitcode, out, err = execute(cmd)
198
-
199
-        self.assertEqual(0, exitcode)
200
-
201
-        # Verify queued second image
202
-        cmd = "%s --port=%d list-queued" % (exe_cmd, api_port)
203
-
204
-        exitcode, out, err = execute(cmd)
205
-
206
-        self.assertEqual(0, exitcode)
207
-        out = out.decode('utf-8')
208
-        self.assertIn(ids[1], out, 'Image %s was not queued!' % ids[1])
209
-
210
-        # Cache images in the queue by running the prefetcher
211
-        cache_config_filepath = os.path.join(self.test_dir, 'etc',
212
-                                             'glance-cache.conf')
213
-        cache_file_options = {
214
-            'image_cache_dir': self.api_server.image_cache_dir,
215
-            'image_cache_driver': self.image_cache_driver,
216
-            'registry_port': self.registry_server.bind_port,
217
-            'lock_path': self.test_dir,
218
-            'log_file': os.path.join(self.test_dir, 'cache.log'),
219
-            'metadata_encryption_key': "012345678901234567890123456789ab",
220
-            'filesystem_store_datadir': self.test_dir
221
-        }
222
-        with open(cache_config_filepath, 'w') as cache_file:
223
-            cache_file.write("""[DEFAULT]
224
-debug = True
225
-lock_path = %(lock_path)s
226
-image_cache_dir = %(image_cache_dir)s
227
-image_cache_driver = %(image_cache_driver)s
228
-registry_host = 127.0.0.1
229
-registry_port = %(registry_port)s
230
-metadata_encryption_key = %(metadata_encryption_key)s
231
-log_file = %(log_file)s
232
-
233
-[glance_store]
234
-filesystem_store_datadir=%(filesystem_store_datadir)s
235
-""" % cache_file_options)
236
-
237
-        cmd = ("%s -m glance.cmd.cache_prefetcher --config-file %s" %
238
-               (sys.executable, cache_config_filepath))
239
-
240
-        exitcode, out, err = execute(cmd)
241
-
242
-        self.assertEqual(0, exitcode)
243
-        self.assertEqual(b'', out.strip(), out)
244
-
245
-        # Verify no queued images
246
-        cmd = "%s --port=%d list-queued" % (exe_cmd, api_port)
247
-
248
-        exitcode, out, err = execute(cmd)
249
-
250
-        self.assertEqual(0, exitcode)
251
-        self.assertIn(b'No queued images', out.strip())
252
-
253
-        # Verify second image now cached
254
-        cmd = "%s --port=%d list-cached" % (exe_cmd, api_port)
255
-
256
-        exitcode, out, err = execute(cmd)
257
-
258
-        self.assertEqual(0, exitcode)
259
-        out = out.decode('utf-8')
260
-        self.assertIn(ids[1], out, 'Image %s was not cached!' % ids[1])
261
-
262
-        # Queue third image and then delete it from queue
263
-        cmd = "%s --port=%d --force queue-image %s" % (
264
-            exe_cmd, api_port, ids[2])
265
-
266
-        exitcode, out, err = execute(cmd)
267
-
268
-        self.assertEqual(0, exitcode)
269
-
270
-        # Verify queued third image
271
-        cmd = "%s --port=%d list-queued" % (exe_cmd, api_port)
272
-
273
-        exitcode, out, err = execute(cmd)
274
-
275
-        self.assertEqual(0, exitcode)
276
-        out = out.decode('utf-8')
277
-        self.assertIn(ids[2], out, 'Image %s was not queued!' % ids[2])
278
-
279
-        # Delete the image from the queue
280
-        cmd = ("%s --port=%d --force "
281
-               "delete-queued-image %s") % (exe_cmd, api_port, ids[2])
282
-
283
-        exitcode, out, err = execute(cmd)
284
-
285
-        self.assertEqual(0, exitcode)
286
-
287
-        # Verify no queued images
288
-        cmd = "%s --port=%d list-queued" % (exe_cmd, api_port)
289
-
290
-        exitcode, out, err = execute(cmd)
291
-
292
-        self.assertEqual(0, exitcode)
293
-        self.assertIn(b'No queued images', out.strip())
294
-
295
-        # Queue all images
296
-        for x in range(4):
297
-            cmd = ("%s --port=%d --force "
298
-                   "queue-image %s") % (exe_cmd, api_port, ids[x])
299
-
300
-            exitcode, out, err = execute(cmd)
301
-
302
-            self.assertEqual(0, exitcode)
303
-
304
-        # Verify queued third image
305
-        cmd = "%s --port=%d list-queued" % (exe_cmd, api_port)
306
-
307
-        exitcode, out, err = execute(cmd)
308
-
309
-        self.assertEqual(0, exitcode)
310
-        self.assertIn(b'Found 3 queued images', out)
311
-
312
-        # Delete the image from the queue
313
-        cmd = ("%s --port=%d --force "
314
-               "delete-all-queued-images") % (exe_cmd, api_port)
315
-
316
-        exitcode, out, err = execute(cmd)
317
-
318
-        self.assertEqual(0, exitcode)
319
-
320
-        # Verify nothing in queue anymore
321
-        cmd = "%s --port=%d list-queued" % (exe_cmd, api_port)
322
-
323
-        exitcode, out, err = execute(cmd)
324
-
325
-        self.assertEqual(0, exitcode)
326
-        self.assertIn(b'No queued images', out.strip())
327
-
328
-        # verify two image id when queue-image
329
-        cmd = ("%s --port=%d --force "
330
-               "queue-image %s %s") % (exe_cmd, api_port, ids[0], ids[1])
331
-
332
-        exitcode, out, err = execute(cmd, raise_error=False)
333
-
334
-        self.assertEqual(1, exitcode)
335
-        self.assertIn(b'Please specify one and only ID of '
336
-                      b'the image you wish to ', out.strip())
337
-
338
-        # verify two image id when delete-queued-image
339
-        cmd = ("%s --port=%d --force delete-queued-image "
340
-               "%s %s") % (exe_cmd, api_port, ids[0], ids[1])
341
-
342
-        exitcode, out, err = execute(cmd, raise_error=False)
343
-
344
-        self.assertEqual(1, exitcode)
345
-        self.assertIn(b'Please specify one and only ID of '
346
-                      b'the image you wish to ', out.strip())
347
-
348
-        # verify two image id when delete-cached-image
349
-        cmd = ("%s --port=%d --force delete-cached-image "
350
-               "%s %s") % (exe_cmd, api_port, ids[0], ids[1])
351
-
352
-        exitcode, out, err = execute(cmd, raise_error=False)
353
-
354
-        self.assertEqual(1, exitcode)
355
-        self.assertIn(b'Please specify one and only ID of '
356
-                      b'the image you wish to ', out.strip())
357
-
358
-        self.stop_servers()

+ 0
- 746
glance/tests/functional/test_cache_middleware.py View File

@@ -20,25 +20,16 @@ but that is really not relevant, as the image cache is transparent
20 20
 to the backend store.
21 21
 """
22 22
 
23
-import hashlib
24 23
 import os
25 24
 import shutil
26
-import sys
27
-import time
28 25
 import uuid
29 26
 
30 27
 import httplib2
31 28
 from oslo_serialization import jsonutils
32 29
 from oslo_utils import units
33 30
 from six.moves import http_client
34
-# NOTE(jokke): simplified transition to py3, behaves like py2 xrange
35
-from six.moves import range
36 31
 
37 32
 from glance.tests import functional
38
-from glance.tests.functional.store_utils import get_http_uri
39
-from glance.tests.functional.store_utils import setup_http
40
-from glance.tests.utils import execute
41
-from glance.tests.utils import minimal_headers
42 33
 from glance.tests.utils import skip_if_disabled
43 34
 from glance.tests.utils import xattr_writes_supported
44 35
 
@@ -47,78 +38,6 @@ FIVE_KB = 5 * units.Ki
47 38
 
48 39
 class BaseCacheMiddlewareTest(object):
49 40
 
50
-    @skip_if_disabled
51
-    def test_cache_middleware_transparent_v1(self):
52
-        """
53
-        We test that putting the cache middleware into the
54
-        application pipeline gives us transparent image caching
55
-        """
56
-        self.cleanup()
57
-        self.start_servers(**self.__dict__.copy())
58
-
59
-        # Add an image and verify a 200 OK is returned
60
-        image_data = b"*" * FIVE_KB
61
-        headers = minimal_headers('Image1')
62
-        path = "http://%s:%d/v1/images" % ("127.0.0.1", self.api_port)
63
-        http = httplib2.Http()
64
-        response, content = http.request(path, 'POST', headers=headers,
65
-                                         body=image_data)
66
-        self.assertEqual(http_client.CREATED, response.status)
67
-        data = jsonutils.loads(content)
68
-        self.assertEqual(hashlib.md5(image_data).hexdigest(),
69
-                         data['image']['checksum'])
70
-        self.assertEqual(FIVE_KB, data['image']['size'])
71
-        self.assertEqual("Image1", data['image']['name'])
72
-        self.assertTrue(data['image']['is_public'])
73
-
74
-        image_id = data['image']['id']
75
-
76
-        # Verify image not in cache
77
-        image_cached_path = os.path.join(self.api_server.image_cache_dir,
78
-                                         image_id)
79
-        self.assertFalse(os.path.exists(image_cached_path))
80
-
81
-        # Grab the image
82
-        path = "http://%s:%d/v1/images/%s" % ("127.0.0.1", self.api_port,
83
-                                              image_id)
84
-        http = httplib2.Http()
85
-        response, content = http.request(path, 'GET')
86
-        self.assertEqual(http_client.OK, response.status)
87
-
88
-        # Verify image now in cache
89
-        image_cached_path = os.path.join(self.api_server.image_cache_dir,
90
-                                         image_id)
91
-
92
-        # You might wonder why the heck this is here... well, it's here
93
-        # because it took me forever to figure out that the disk write
94
-        # cache in Linux was causing random failures of the os.path.exists
95
-        # assert directly below this. Basically, since the cache is writing
96
-        # the image file to disk in a different process, the write buffers
97
-        # don't flush the cache file during an os.rename() properly, resulting
98
-        # in a false negative on the file existence check below. This little
99
-        # loop pauses the execution of this process for no more than 1.5
100
-        # seconds. If after that time the cached image file still doesn't
101
-        # appear on disk, something really is wrong, and the assert should
102
-        # trigger...
103
-        i = 0
104
-        while not os.path.exists(image_cached_path) and i < 30:
105
-            time.sleep(0.05)
106
-            i = i + 1
107
-
108
-        self.assertTrue(os.path.exists(image_cached_path))
109
-
110
-        # Now, we delete the image from the server and verify that
111
-        # the image cache no longer contains the deleted image
112
-        path = "http://%s:%d/v1/images/%s" % ("127.0.0.1", self.api_port,
113
-                                              image_id)
114
-        http = httplib2.Http()
115
-        response, content = http.request(path, 'DELETE')
116
-        self.assertEqual(http_client.OK, response.status)
117
-
118
-        self.assertFalse(os.path.exists(image_cached_path))
119
-
120
-        self.stop_servers()
121
-
122 41
     @skip_if_disabled
123 42
     def test_cache_middleware_transparent_v2(self):
124 43
         """Ensure the v2 API image transfer calls trigger caching"""
@@ -354,102 +273,6 @@ class BaseCacheMiddlewareTest(object):
354 273
 
355 274
         self.stop_servers()
356 275
 
357
-    @skip_if_disabled
358
-    def test_cache_remote_image(self):
359
-        """
360
-        We test that caching is no longer broken for remote images
361
-        """
362
-        self.cleanup()
363
-        self.start_servers(**self.__dict__.copy())
364
-
365
-        setup_http(self)
366
-
367
-        # Add a remote image and verify a 201 Created is returned
368
-        remote_uri = get_http_uri(self, '2')
369
-        headers = {'X-Image-Meta-Name': 'Image2',
370
-                   'X-Image-Meta-disk_format': 'raw',
371
-                   'X-Image-Meta-container_format': 'ovf',
372
-                   'X-Image-Meta-Is-Public': 'True',
373
-                   'X-Image-Meta-Location': remote_uri}
374
-        path = "http://%s:%d/v1/images" % ("127.0.0.1", self.api_port)
375
-        http = httplib2.Http()
376
-        response, content = http.request(path, 'POST', headers=headers)
377
-        self.assertEqual(http_client.CREATED, response.status)
378
-        data = jsonutils.loads(content)
379
-        self.assertEqual(FIVE_KB, data['image']['size'])
380
-
381
-        image_id = data['image']['id']
382
-        path = "http://%s:%d/v1/images/%s" % ("127.0.0.1", self.api_port,
383
-                                              image_id)
384
-
385
-        # Grab the image
386
-        http = httplib2.Http()
387
-        response, content = http.request(path, 'GET')
388
-        self.assertEqual(http_client.OK, response.status)
389
-
390
-        # Grab the image again to ensure it can be served out from
391
-        # cache with the correct size
392
-        http = httplib2.Http()
393
-        response, content = http.request(path, 'GET')
394
-        self.assertEqual(http_client.OK, response.status)
395
-        self.assertEqual(FIVE_KB, int(response['content-length']))
396
-
397
-        self.stop_servers()
398
-
399
-    @skip_if_disabled
400
-    def test_cache_middleware_trans_v1_without_download_image_policy(self):
401
-        """
402
-        Ensure the image v1 API image transfer applied 'download_image'
403
-        policy enforcement.
404
-        """
405
-        self.cleanup()
406
-        self.start_servers(**self.__dict__.copy())
407
-
408
-        # Add an image and verify a 200 OK is returned
409
-        image_data = b"*" * FIVE_KB
410
-        headers = minimal_headers('Image1')
411
-        path = "http://%s:%d/v1/images" % ("127.0.0.1", self.api_port)
412
-        http = httplib2.Http()
413
-        response, content = http.request(path, 'POST', headers=headers,
414
-                                         body=image_data)
415
-        self.assertEqual(http_client.CREATED, response.status)
416
-        data = jsonutils.loads(content)
417
-        self.assertEqual(hashlib.md5(image_data).hexdigest(),
418
-                         data['image']['checksum'])
419
-        self.assertEqual(FIVE_KB, data['image']['size'])
420
-        self.assertEqual("Image1", data['image']['name'])
421
-        self.assertTrue(data['image']['is_public'])
422
-
423
-        image_id = data['image']['id']
424
-
425
-        # Verify image not in cache
426
-        image_cached_path = os.path.join(self.api_server.image_cache_dir,
427
-                                         image_id)
428
-        self.assertFalse(os.path.exists(image_cached_path))
429
-
430
-        rules = {"context_is_admin": "role:admin", "default": "",
431
-                 "download_image": "!"}
432
-        self.set_policy_rules(rules)
433
-
434
-        # Grab the image
435
-        path = "http://%s:%d/v1/images/%s" % ("127.0.0.1", self.api_port,
436
-                                              image_id)
437
-        http = httplib2.Http()
438
-        response, content = http.request(path, 'GET')
439
-        self.assertEqual(http_client.FORBIDDEN, response.status)
440
-
441
-        # Now, we delete the image from the server and verify that
442
-        # the image cache no longer contains the deleted image
443
-        path = "http://%s:%d/v1/images/%s" % ("127.0.0.1", self.api_port,
444
-                                              image_id)
445
-        http = httplib2.Http()
446
-        response, content = http.request(path, 'DELETE')
447
-        self.assertEqual(http_client.OK, response.status)
448
-
449
-        self.assertFalse(os.path.exists(image_cached_path))
450
-
451
-        self.stop_servers()
452
-
453 276
     @skip_if_disabled
454 277
     def test_cache_middleware_trans_v2_without_download_image_policy(self):
455 278
         """
@@ -511,489 +334,6 @@ class BaseCacheMiddlewareTest(object):
511 334
 
512 335
         self.stop_servers()
513 336
 
514
-    @skip_if_disabled
515
-    def test_cache_middleware_trans_with_deactivated_image(self):
516
-        """
517
-        Ensure the image v1/v2 API image transfer forbids downloading
518
-        deactivated images.
519
-        Image deactivation is not available in v1. So, we'll deactivate the
520
-        image using v2 but test image transfer with both v1 and v2.
521
-        """
522
-        self.cleanup()
523
-        self.start_servers(**self.__dict__.copy())
524
-
525
-        # Add an image and verify a 200 OK is returned
526
-        image_data = b"*" * FIVE_KB
527
-        headers = minimal_headers('Image1')
528
-        path = "http://%s:%d/v1/images" % ("127.0.0.1", self.api_port)
529
-        http = httplib2.Http()
530
-        response, content = http.request(path, 'POST', headers=headers,
531
-                                         body=image_data)
532
-        self.assertEqual(http_client.CREATED, response.status)
533
-        data = jsonutils.loads(content)
534
-        self.assertEqual(hashlib.md5(image_data).hexdigest(),
535
-                         data['image']['checksum'])
536
-        self.assertEqual(FIVE_KB, data['image']['size'])
537
-        self.assertEqual("Image1", data['image']['name'])
538
-        self.assertTrue(data['image']['is_public'])
539
-
540
-        image_id = data['image']['id']
541
-
542
-        # Grab the image
543
-        path = "http://%s:%d/v1/images/%s" % ("127.0.0.1", self.api_port,
544
-                                              image_id)
545
-        http = httplib2.Http()
546
-        response, content = http.request(path, 'GET')
547
-        self.assertEqual(http_client.OK, response.status)
548
-
549
-        # Verify image in cache
550
-        image_cached_path = os.path.join(self.api_server.image_cache_dir,
551
-                                         image_id)
552
-        self.assertTrue(os.path.exists(image_cached_path))
553
-
554
-        # Deactivate the image using v2
555
-        path = "http://%s:%d/v2/images/%s/actions/deactivate"
556
-        path = path % ("127.0.0.1", self.api_port, image_id)
557
-        http = httplib2.Http()
558
-        response, content = http.request(path, 'POST')
559
-        self.assertEqual(http_client.NO_CONTENT, response.status)
560
-
561
-        # Download the image with v1. Ensure it is forbidden
562
-        path = "http://%s:%d/v1/images/%s" % ("127.0.0.1", self.api_port,
563
-                                              image_id)
564
-        http = httplib2.Http()
565
-        response, content = http.request(path, 'GET')
566
-        self.assertEqual(http_client.FORBIDDEN, response.status)
567
-
568
-        # Download the image with v2. This succeeds because
569
-        # we are in admin context.
570
-        path = "http://%s:%d/v2/images/%s/file" % ("127.0.0.1", self.api_port,
571
-                                                   image_id)
572
-        http = httplib2.Http()
573
-        response, content = http.request(path, 'GET')
574
-        self.assertEqual(http_client.OK, response.status)
575
-
576
-        # Reactivate the image using v2
577
-        path = "http://%s:%d/v2/images/%s/actions/reactivate"
578
-        path = path % ("127.0.0.1", self.api_port, image_id)
579
-        http = httplib2.Http()
580
-        response, content = http.request(path, 'POST')
581
-        self.assertEqual(http_client.NO_CONTENT, response.status)
582
-
583
-        # Download the image with v1. Ensure it is allowed
584
-        path = "http://%s:%d/v1/images/%s" % ("127.0.0.1", self.api_port,
585
-                                              image_id)
586
-        http = httplib2.Http()
587
-        response, content = http.request(path, 'GET')
588
-        self.assertEqual(http_client.OK, response.status)
589
-
590
-        # Download the image with v2. Ensure it is allowed
591
-        path = "http://%s:%d/v2/images/%s/file" % ("127.0.0.1", self.api_port,
592
-                                                   image_id)
593
-        http = httplib2.Http()
594
-        response, content = http.request(path, 'GET')
595
-        self.assertEqual(http_client.OK, response.status)
596
-
597
-        # Now, we delete the image from the server and verify that
598
-        # the image cache no longer contains the deleted image
599
-        path = "http://%s:%d/v1/images/%s" % ("127.0.0.1", self.api_port,
600
-                                              image_id)
601
-        http = httplib2.Http()
602
-        response, content = http.request(path, 'DELETE')
603
-        self.assertEqual(http_client.OK, response.status)
604
-
605
-        self.assertFalse(os.path.exists(image_cached_path))
606
-
607
-        self.stop_servers()
608
-
609
-
610
-class BaseCacheManageMiddlewareTest(object):
611
-
612
-    """Base test class for testing cache management middleware"""
613
-
614
-    def verify_no_images(self):
615
-        path = "http://%s:%d/v1/images" % ("127.0.0.1", self.api_port)
616
-        http = httplib2.Http()
617
-        response, content = http.request(path, 'GET')
618
-        self.assertEqual(http_client.OK, response.status)
619
-        data = jsonutils.loads(content)
620
-        self.assertIn('images', data)
621
-        self.assertEqual(0, len(data['images']))
622
-
623
-    def add_image(self, name):
624
-        """
625
-        Adds an image and returns the newly-added image
626
-        identifier
627
-        """
628
-        image_data = b"*" * FIVE_KB
629
-        headers = minimal_headers('%s' % name)
630
-
631
-        path = "http://%s:%d/v1/images" % ("127.0.0.1", self.api_port)
632
-        http = httplib2.Http()
633
-        response, content = http.request(path, 'POST', headers=headers,
634
-                                         body=image_data)
635
-        self.assertEqual(http_client.CREATED, response.status)
636
-        data = jsonutils.loads(content)
637
-        self.assertEqual(hashlib.md5(image_data).hexdigest(),
638
-                         data['image']['checksum'])
639
-        self.assertEqual(FIVE_KB, data['image']['size'])
640
-        self.assertEqual(name, data['image']['name'])
641
-        self.assertTrue(data['image']['is_public'])
642
-        return data['image']['id']
643
-
644
-    def verify_no_cached_images(self):
645
-        """
646
-        Verify no images in the image cache
647
-        """
648
-        path = "http://%s:%d/v1/cached_images" % ("127.0.0.1", self.api_port)
649
-        http = httplib2.Http()
650
-        response, content = http.request(path, 'GET')
651
-        self.assertEqual(http_client.OK, response.status)
652
-
653
-        data = jsonutils.loads(content)
654
-        self.assertIn('cached_images', data)
655
-        self.assertEqual([], data['cached_images'])
656
-
657
-    @skip_if_disabled
658
-    def test_user_not_authorized(self):
659
-        self.cleanup()
660
-        self.start_servers(**self.__dict__.copy())
661
-        self.verify_no_images()
662
-
663
-        image_id1 = self.add_image("Image1")
664
-        image_id2 = self.add_image("Image2")
665
-
666
-        # Verify image does not yet show up in cache (we haven't "hit"
667
-        # it yet using a GET /images/1 ...
668
-        self.verify_no_cached_images()
669
-
670
-        # Grab the image
671
-        path = "http://%s:%d/v1/images/%s" % ("127.0.0.1", self.api_port,
672
-                                              image_id1)
673
-        http = httplib2.Http()
674
-        response, content = http.request(path, 'GET')
675
-        self.assertEqual(http_client.OK, response.status)
676
-
677
-        # Verify image now in cache
678
-        path = "http://%s:%d/v1/cached_images" % ("127.0.0.1", self.api_port)
679
-        http = httplib2.Http()
680
-        response, content = http.request(path, 'GET')
681
-        self.assertEqual(http_client.OK, response.status)
682
-
683
-        data = jsonutils.loads(content)
684
-        self.assertIn('cached_images', data)
685
-
686
-        cached_images = data['cached_images']
687
-        self.assertEqual(1, len(cached_images))
688
-        self.assertEqual(image_id1, cached_images[0]['image_id'])
689
-
690
-        # Set policy to disallow access to cache management
691
-        rules = {"manage_image_cache": '!'}
692
-        self.set_policy_rules(rules)
693
-
694
-        # Verify an unprivileged user cannot see cached images
695
-        path = "http://%s:%d/v1/cached_images" % ("127.0.0.1", self.api_port)
696
-        http = httplib2.Http()
697
-        response, content = http.request(path, 'GET')
698
-        self.assertEqual(http_client.FORBIDDEN, response.status)
699
-
700
-        # Verify an unprivileged user cannot delete images from the cache
701
-        path = "http://%s:%d/v1/cached_images/%s" % ("127.0.0.1",
702
-                                                     self.api_port, image_id1)
703
-        http = httplib2.Http()
704
-        response, content = http.request(path, 'DELETE')
705
-        self.assertEqual(http_client.FORBIDDEN, response.status)
706
-
707
-        # Verify an unprivileged user cannot delete all cached images
708
-        path = "http://%s:%d/v1/cached_images" % ("127.0.0.1", self.api_port)
709
-        http = httplib2.Http()
710
-        response, content = http.request(path, 'DELETE')
711
-        self.assertEqual(http_client.FORBIDDEN, response.status)
712
-
713
-        # Verify an unprivileged user cannot queue an image
714
-        path = "http://%s:%d/v1/queued_images/%s" % ("127.0.0.1",
715
-                                                     self.api_port, image_id2)
716
-        http = httplib2.Http()
717
-        response, content = http.request(path, 'PUT')
718
-        self.assertEqual(http_client.FORBIDDEN, response.status)
719
-
720
-        self.stop_servers()
721
-
722
-    @skip_if_disabled
723
-    def test_cache_manage_get_cached_images(self):
724
-        """
725
-        Tests that cached images are queryable
726
-        """
727
-        self.cleanup()
728
-        self.start_servers(**self.__dict__.copy())
729
-
730
-        self.verify_no_images()
731
-
732
-        image_id = self.add_image("Image1")
733
-
734
-        # Verify image does not yet show up in cache (we haven't "hit"
735
-        # it yet using a GET /images/1 ...
736
-        self.verify_no_cached_images()
737
-
738
-        # Grab the image
739
-        path = "http://%s:%d/v1/images/%s" % ("127.0.0.1", self.api_port,
740
-                                              image_id)
741
-        http = httplib2.Http()
742
-        response, content = http.request(path, 'GET')
743
-        self.assertEqual(http_client.OK, response.status)
744
-
745
-        # Verify image now in cache
746
-        path = "http://%s:%d/v1/cached_images" % ("127.0.0.1", self.api_port)
747
-        http = httplib2.Http()
748
-        response, content = http.request(path, 'GET')
749
-        self.assertEqual(http_client.OK, response.status)
750
-
751
-        data = jsonutils.loads(content)
752
-        self.assertIn('cached_images', data)
753
-
754
-        # Verify the last_modified/last_accessed values are valid floats
755
-        for cached_image in data['cached_images']:
756
-            for time_key in ('last_modified', 'last_accessed'):
757
-                time_val = cached_image[time_key]
758
-                try:
759
-                    float(time_val)
760
-                except ValueError:
761
-                    self.fail('%s time %s for cached image %s not a valid '
762
-                              'float' % (time_key, time_val,
763
-                                         cached_image['image_id']))
764
-
765
-        cached_images = data['cached_images']
766
-        self.assertEqual(1, len(cached_images))
767
-        self.assertEqual(image_id, cached_images[0]['image_id'])
768
-        self.assertEqual(0, cached_images[0]['hits'])
769
-
770
-        # Hit the image
771
-        path = "http://%s:%d/v1/images/%s" % ("127.0.0.1", self.api_port,
772
-                                              image_id)
773
-        http = httplib2.Http()
774
-        response, content = http.request(path, 'GET')
775
-        self.assertEqual(http_client.OK, response.status)
776
-
777
-        # Verify image hits increased in output of manage GET
778
-        path = "http://%s:%d/v1/cached_images" % ("127.0.0.1", self.api_port)
779
-        http = httplib2.Http()
780
-        response, content = http.request(path, 'GET')
781
-        self.assertEqual(http_client.OK, response.status)
782
-
783
-        data = jsonutils.loads(content)
784
-        self.assertIn('cached_images', data)
785
-
786
-        cached_images = data['cached_images']
787
-        self.assertEqual(1, len(cached_images))
788
-        self.assertEqual(image_id, cached_images[0]['image_id'])
789
-        self.assertEqual(1, cached_images[0]['hits'])
790
-
791
-        self.stop_servers()
792
-
793
-    @skip_if_disabled
794
-    def test_cache_manage_delete_cached_images(self):
795
-        """
796
-        Tests that cached images may be deleted
797
-        """
798
-        self.cleanup()
799
-        self.start_servers(**self.__dict__.copy())
800
-
801
-        self.verify_no_images()
802
-
803
-        ids = {}
804
-
805
-        # Add a bunch of images...
806
-        for x in range(4):
807
-            ids[x] = self.add_image("Image%s" % str(x))
808
-
809
-        # Verify no images in cached_images because no image has been hit
810
-        # yet using a GET /images/<IMAGE_ID> ...
811
-        self.verify_no_cached_images()
812
-
813
-        # Grab the images, essentially caching them...
814
-        for x in range(4):
815
-            path = "http://%s:%d/v1/images/%s" % ("127.0.0.1", self.api_port,
816
-                                                  ids[x])
817
-            http = httplib2.Http()
818
-            response, content = http.request(path, 'GET')
819
-            self.assertEqual(http_client.OK, response.status,
820
-                             "Failed to find image %s" % ids[x])
821
-
822
-        # Verify images now in cache
823
-        path = "http://%s:%d/v1/cached_images" % ("127.0.0.1", self.api_port)
824
-        http = httplib2.Http()
825
-        response, content = http.request(path, 'GET')
826
-        self.assertEqual(http_client.OK, response.status)
827
-
828
-        data = jsonutils.loads(content)
829
-        self.assertIn('cached_images', data)
830
-
831
-        cached_images = data['cached_images']
832
-        self.assertEqual(4, len(cached_images))
833
-
834
-        for x in range(4, 0):  # Cached images returned last modified order
835
-            self.assertEqual(ids[x], cached_images[x]['image_id'])
836
-            self.assertEqual(0, cached_images[x]['hits'])
837
-
838
-        # Delete third image of the cached images and verify no longer in cache
839
-        path = "http://%s:%d/v1/cached_images/%s" % ("127.0.0.1",
840
-                                                     self.api_port, ids[2])
841
-        http = httplib2.Http()
842
-        response, content = http.request(path, 'DELETE')
843
-        self.assertEqual(http_client.OK, response.status)
844
-
845
-        path = "http://%s:%d/v1/cached_images" % ("127.0.0.1", self.api_port)
846
-        http = httplib2.Http()
847
-        response, content = http.request(path, 'GET')
848
-        self.assertEqual(http_client.OK, response.status)
849
-
850
-        data = jsonutils.loads(content)
851
-        self.assertIn('cached_images', data)
852
-
853
-        cached_images = data['cached_images']
854
-        self.assertEqual(3, len(cached_images))
855
-        self.assertNotIn(ids[2], [x['image_id'] for x in cached_images])
856
-
857
-        # Delete all cached images and verify nothing in cache
858
-        path = "http://%s:%d/v1/cached_images" % ("127.0.0.1", self.api_port)
859
-        http = httplib2.Http()
860
-        response, content = http.request(path, 'DELETE')
861
-        self.assertEqual(http_client.OK, response.status)
862
-
863
-        path = "http://%s:%d/v1/cached_images" % ("127.0.0.1", self.api_port)
864
-        http = httplib2.Http()
865
-        response, content = http.request(path, 'GET')
866
-        self.assertEqual(http_client.OK, response.status)
867
-
868
-        data = jsonutils.loads(content)
869
-        self.assertIn('cached_images', data)
870
-
871
-        cached_images = data['cached_images']
872
-        self.assertEqual(0, len(cached_images))
873
-
874
-        self.stop_servers()
875
-
876
-    @skip_if_disabled
877
-    def test_cache_manage_delete_queued_images(self):
878
-        """
879
-        Tests that all queued images may be deleted at once
880
-        """
881
-        self.cleanup()
882
-        self.start_servers(**self.__dict__.copy())
883
-
884
-        self.verify_no_images()
885
-
886
-        ids = {}
887
-        NUM_IMAGES = 4
888
-
889
-        # Add and then queue some images
890
-        for x in range(NUM_IMAGES):
891
-            ids[x] = self.add_image("Image%s" % str(x))
892
-            path = "http://%s:%d/v1/queued_images/%s" % ("127.0.0.1",
893
-                                                         self.api_port, ids[x])
894
-            http = httplib2.Http()
895
-            response, content = http.request(path, 'PUT')
896
-            self.assertEqual(http_client.OK, response.status)
897
-
898
-        # Delete all queued images
899
-        path = "http://%s:%d/v1/queued_images" % ("127.0.0.1", self.api_port)
900
-        http = httplib2.Http()
901
-        response, content = http.request(path, 'DELETE')
902
-        self.assertEqual(http_client.OK, response.status)
903
-
904
-        data = jsonutils.loads(content)
905
-        num_deleted = data['num_deleted']
906
-        self.assertEqual(NUM_IMAGES, num_deleted)
907
-
908
-        # Verify a second delete now returns num_deleted=0
909
-        path = "http://%s:%d/v1/queued_images" % ("127.0.0.1", self.api_port)
910
-        http = httplib2.Http()
911
-        response, content = http.request(path, 'DELETE')
912
-        self.assertEqual(http_client.OK, response.status)
913
-
914
-        data = jsonutils.loads(content)
915
-        num_deleted = data['num_deleted']
916
-        self.assertEqual(0, num_deleted)
917
-
918
-        self.stop_servers()
919
-
920
-    @skip_if_disabled
921
-    def test_queue_and_prefetch(self):
922
-        """
923
-        Tests that images may be queued and prefetched
924
-        """
925
-        self.cleanup()
926
-        self.start_servers(**self.__dict__.copy())
927
-
928
-        cache_config_filepath = os.path.join(self.test_dir, 'etc',
929
-                                             'glance-cache.conf')
930
-        cache_file_options = {
931
-            'image_cache_dir': self.api_server.image_cache_dir,
932
-            'image_cache_driver': self.image_cache_driver,
933
-            'registry_port': self.registry_server.bind_port,
934
-            'log_file': os.path.join(self.test_dir, 'cache.log'),
935
-            'lock_path': self.test_dir,
936
-            'metadata_encryption_key': "012345678901234567890123456789ab",
937
-            'filesystem_store_datadir': self.test_dir
938
-        }
939
-        with open(cache_config_filepath, 'w') as cache_file:
940
-            cache_file.write("""[DEFAULT]
941
-debug = True
942
-lock_path = %(lock_path)s
943
-image_cache_dir = %(image_cache_dir)s
944
-image_cache_driver = %(image_cache_driver)s
945
-registry_host = 127.0.0.1
946
-registry_port = %(registry_port)s
947
-metadata_encryption_key = %(metadata_encryption_key)s
948
-log_file = %(log_file)s
949
-
950
-[glance_store]
951
-filesystem_store_datadir=%(filesystem_store_datadir)s
952
-""" % cache_file_options)
953
-
954
-        self.verify_no_images()
955
-
956
-        ids = {}
957
-
958
-        # Add a bunch of images...
959
-        for x in range(4):
960
-            ids[x] = self.add_image("Image%s" % str(x))
961
-
962
-        # Queue the first image, verify no images still in cache after queueing
963
-        # then run the prefetcher and verify that the image is then in the
964
-        # cache
965
-        path = "http://%s:%d/v1/queued_images/%s" % ("127.0.0.1",
966
-                                                     self.api_port, ids[0])
967
-        http = httplib2.Http()
968
-        response, content = http.request(path, 'PUT')
969
-        self.assertEqual(http_client.OK, response.status)
970
-
971
-        self.verify_no_cached_images()
972
-
973
-        cmd = ("%s -m glance.cmd.cache_prefetcher --config-file %s" %
974