Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / plankton / views.py @ d2b8ec7b

History | View | Annotate | Download (11.8 kB)

1 d9d1763e Christos Stavrakakis
# Copyright 2011-2013 GRNET S.A. All rights reserved.
2 c34de90f Giorgos Verigakis
#
3 c34de90f Giorgos Verigakis
# Redistribution and use in source and binary forms, with or
4 c34de90f Giorgos Verigakis
# without modification, are permitted provided that the following
5 c34de90f Giorgos Verigakis
# conditions are met:
6 c34de90f Giorgos Verigakis
#
7 c34de90f Giorgos Verigakis
#   1. Redistributions of source code must retain the above
8 c34de90f Giorgos Verigakis
#      copyright notice, this list of conditions and the following
9 c34de90f Giorgos Verigakis
#      disclaimer.
10 c34de90f Giorgos Verigakis
#
11 c34de90f Giorgos Verigakis
#   2. Redistributions in binary form must reproduce the above
12 c34de90f Giorgos Verigakis
#      copyright notice, this list of conditions and the following
13 c34de90f Giorgos Verigakis
#      disclaimer in the documentation and/or other materials
14 c34de90f Giorgos Verigakis
#      provided with the distribution.
15 c34de90f Giorgos Verigakis
#
16 c34de90f Giorgos Verigakis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 c34de90f Giorgos Verigakis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 c34de90f Giorgos Verigakis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 c34de90f Giorgos Verigakis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 c34de90f Giorgos Verigakis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 c34de90f Giorgos Verigakis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 c34de90f Giorgos Verigakis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 c34de90f Giorgos Verigakis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 c34de90f Giorgos Verigakis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 c34de90f Giorgos Verigakis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 c34de90f Giorgos Verigakis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 c34de90f Giorgos Verigakis
# POSSIBILITY OF SUCH DAMAGE.
28 c34de90f Giorgos Verigakis
#
29 c34de90f Giorgos Verigakis
# The views and conclusions contained in the software and
30 c34de90f Giorgos Verigakis
# documentation are those of the authors and should not be
31 c34de90f Giorgos Verigakis
# interpreted as representing official policies, either expressed
32 c34de90f Giorgos Verigakis
# or implied, of GRNET S.A.
33 c34de90f Giorgos Verigakis
34 c34de90f Giorgos Verigakis
import json
35 c34de90f Giorgos Verigakis
36 2035039b Giorgos Verigakis
from logging import getLogger
37 c34de90f Giorgos Verigakis
from string import punctuation
38 4ec83e68 Giorgos Verigakis
from urllib import unquote
39 c34de90f Giorgos Verigakis
40 c34de90f Giorgos Verigakis
from django.conf import settings
41 d2b8ec7b Christos Stavrakakis
from django.http import HttpResponse
42 d9d1763e Christos Stavrakakis
43 d9d1763e Christos Stavrakakis
from snf_django.lib import api
44 d2b8ec7b Christos Stavrakakis
from snf_django.lib.api import faults
45 d9d1763e Christos Stavrakakis
from synnefo.plankton.utils import plankton_method
46 c34de90f Giorgos Verigakis
47 c34de90f Giorgos Verigakis
48 04ba5fd4 Giorgos Verigakis
FILTERS = ('name', 'container_format', 'disk_format', 'status', 'size_min',
49 04ba5fd4 Giorgos Verigakis
           'size_max')
50 c34de90f Giorgos Verigakis
51 04ba5fd4 Giorgos Verigakis
PARAMS = ('sort_key', 'sort_dir')
52 c34de90f Giorgos Verigakis
53 04ba5fd4 Giorgos Verigakis
SORT_KEY_OPTIONS = ('id', 'name', 'status', 'size', 'disk_format',
54 04ba5fd4 Giorgos Verigakis
                    'container_format', 'created_at', 'updated_at')
55 04ba5fd4 Giorgos Verigakis
56 04ba5fd4 Giorgos Verigakis
SORT_DIR_OPTIONS = ('asc', 'desc')
57 04ba5fd4 Giorgos Verigakis
58 04ba5fd4 Giorgos Verigakis
LIST_FIELDS = ('status', 'name', 'disk_format', 'container_format', 'size',
59 04ba5fd4 Giorgos Verigakis
               'id')
60 04ba5fd4 Giorgos Verigakis
61 04ba5fd4 Giorgos Verigakis
DETAIL_FIELDS = ('name', 'disk_format', 'container_format', 'size', 'checksum',
62 04ba5fd4 Giorgos Verigakis
                 'location', 'created_at', 'updated_at', 'deleted_at',
63 04ba5fd4 Giorgos Verigakis
                 'status', 'is_public', 'owner', 'properties', 'id')
64 04ba5fd4 Giorgos Verigakis
65 04ba5fd4 Giorgos Verigakis
ADD_FIELDS = ('name', 'id', 'store', 'disk_format', 'container_format', 'size',
66 04ba5fd4 Giorgos Verigakis
              'checksum', 'is_public', 'owner', 'properties', 'location')
67 04ba5fd4 Giorgos Verigakis
68 04ba5fd4 Giorgos Verigakis
UPDATE_FIELDS = ('name', 'disk_format', 'container_format', 'is_public',
69 7e00b33e Giorgos Verigakis
                 'owner', 'properties', 'status')
70 c34de90f Giorgos Verigakis
71 c34de90f Giorgos Verigakis
72 15137c54 Giorgos Verigakis
log = getLogger('synnefo.plankton')
73 c34de90f Giorgos Verigakis
74 c34de90f Giorgos Verigakis
75 15137c54 Giorgos Verigakis
def _create_image_response(image):
76 04ba5fd4 Giorgos Verigakis
    response = HttpResponse()
77 29490fca Christos Stavrakakis
78 04ba5fd4 Giorgos Verigakis
    for key in DETAIL_FIELDS:
79 04ba5fd4 Giorgos Verigakis
        if key == 'properties':
80 04ba5fd4 Giorgos Verigakis
            for k, v in image.get('properties', {}).items():
81 04ba5fd4 Giorgos Verigakis
                name = 'x-image-meta-property-' + k.replace('_', '-')
82 04ba5fd4 Giorgos Verigakis
                response[name] = v
83 04ba5fd4 Giorgos Verigakis
        else:
84 04ba5fd4 Giorgos Verigakis
            name = 'x-image-meta-' + key.replace('_', '-')
85 04ba5fd4 Giorgos Verigakis
            response[name] = image.get(key, '')
86 29490fca Christos Stavrakakis
87 04ba5fd4 Giorgos Verigakis
    return response
88 04ba5fd4 Giorgos Verigakis
89 04ba5fd4 Giorgos Verigakis
90 15137c54 Giorgos Verigakis
def _get_image_headers(request):
91 04ba5fd4 Giorgos Verigakis
    def normalize(s):
92 04ba5fd4 Giorgos Verigakis
        return ''.join('_' if c in punctuation else c.lower() for c in s)
93 29490fca Christos Stavrakakis
94 04ba5fd4 Giorgos Verigakis
    META_PREFIX = 'HTTP_X_IMAGE_META_'
95 04ba5fd4 Giorgos Verigakis
    META_PREFIX_LEN = len(META_PREFIX)
96 04ba5fd4 Giorgos Verigakis
    META_PROPERTY_PREFIX = 'HTTP_X_IMAGE_META_PROPERTY_'
97 04ba5fd4 Giorgos Verigakis
    META_PROPERTY_PREFIX_LEN = len(META_PROPERTY_PREFIX)
98 29490fca Christos Stavrakakis
99 04ba5fd4 Giorgos Verigakis
    headers = {'properties': {}}
100 29490fca Christos Stavrakakis
101 04ba5fd4 Giorgos Verigakis
    for key, val in request.META.items():
102 04ba5fd4 Giorgos Verigakis
        if key.startswith(META_PROPERTY_PREFIX):
103 04ba5fd4 Giorgos Verigakis
            name = normalize(key[META_PROPERTY_PREFIX_LEN:])
104 4ec83e68 Giorgos Verigakis
            headers['properties'][unquote(name)] = unquote(val)
105 04ba5fd4 Giorgos Verigakis
        elif key.startswith(META_PREFIX):
106 04ba5fd4 Giorgos Verigakis
            name = normalize(key[META_PREFIX_LEN:])
107 4ec83e68 Giorgos Verigakis
            headers[unquote(name)] = unquote(val)
108 29490fca Christos Stavrakakis
109 7e00b33e Giorgos Verigakis
    is_public = headers.get('is_public', None)
110 7e00b33e Giorgos Verigakis
    if is_public is not None:
111 7e00b33e Giorgos Verigakis
        headers['is_public'] = True if is_public.lower() == 'true' else False
112 29490fca Christos Stavrakakis
113 7e00b33e Giorgos Verigakis
    if not headers['properties']:
114 7e00b33e Giorgos Verigakis
        del headers['properties']
115 29490fca Christos Stavrakakis
116 04ba5fd4 Giorgos Verigakis
    return headers
117 c34de90f Giorgos Verigakis
118 c34de90f Giorgos Verigakis
119 d9d1763e Christos Stavrakakis
@api.api_method(http_method="POST", user_required=True, logger=log)
120 d9d1763e Christos Stavrakakis
@plankton_method
121 15137c54 Giorgos Verigakis
def add_image(request):
122 15137c54 Giorgos Verigakis
    """Add a new virtual machine image
123 29490fca Christos Stavrakakis

124 15137c54 Giorgos Verigakis
    Described in:
125 15137c54 Giorgos Verigakis
    3.6. Adding a New Virtual Machine Image
126 29490fca Christos Stavrakakis

127 15137c54 Giorgos Verigakis
    Implementation notes:
128 15137c54 Giorgos Verigakis
      * The implementation is very inefficient as it loads the whole image
129 15137c54 Giorgos Verigakis
        in memory.
130 29490fca Christos Stavrakakis

131 15137c54 Giorgos Verigakis
    Limitations:
132 15137c54 Giorgos Verigakis
      * x-image-meta-id is not supported. Will always return 409 Conflict.
133 29490fca Christos Stavrakakis

134 15137c54 Giorgos Verigakis
    Extensions:
135 15137c54 Giorgos Verigakis
      * An x-image-meta-location header can be passed with a link to file,
136 15137c54 Giorgos Verigakis
        instead of uploading the data.
137 15137c54 Giorgos Verigakis
    """
138 29490fca Christos Stavrakakis
139 15137c54 Giorgos Verigakis
    params = _get_image_headers(request)
140 15137c54 Giorgos Verigakis
    log.debug('add_image %s', params)
141 29490fca Christos Stavrakakis
142 f5afd99b Giorgos Verigakis
    assert 'name' in params
143 04ba5fd4 Giorgos Verigakis
    assert set(params.keys()).issubset(set(ADD_FIELDS))
144 29490fca Christos Stavrakakis
145 7bd1d3b5 Giorgos Verigakis
    name = params.pop('name')
146 f5afd99b Giorgos Verigakis
    location = params.pop('location', None)
147 29490fca Christos Stavrakakis
148 f5afd99b Giorgos Verigakis
    if location:
149 f5afd99b Giorgos Verigakis
        image = request.backend.register(name, location, params)
150 04ba5fd4 Giorgos Verigakis
    else:
151 c23d211a Giorgos Verigakis
        #f = StringIO(request.raw_post_data)
152 c23d211a Giorgos Verigakis
        #image = request.backend.put(name, f, params)
153 f5afd99b Giorgos Verigakis
        return HttpResponse(status=501)     # Not Implemented
154 29490fca Christos Stavrakakis
155 04ba5fd4 Giorgos Verigakis
    if not image:
156 0a72907b Giorgos Verigakis
        return HttpResponse('Registration failed', status=500)
157 29490fca Christos Stavrakakis
158 15137c54 Giorgos Verigakis
    return _create_image_response(image)
159 15137c54 Giorgos Verigakis
160 15137c54 Giorgos Verigakis
161 d9d1763e Christos Stavrakakis
@api.api_method(http_method="DELETE", user_required=True, logger=log)
162 d9d1763e Christos Stavrakakis
@plankton_method
163 f13aab5d Christos Stavrakakis
def delete_image(request, image_id):
164 f13aab5d Christos Stavrakakis
    """Delete an Image.
165 f13aab5d Christos Stavrakakis

166 f13aab5d Christos Stavrakakis
    This API call is not described in the Openstack Glance API.
167 f13aab5d Christos Stavrakakis

168 f13aab5d Christos Stavrakakis
    Implementation notes:
169 f13aab5d Christos Stavrakakis
      * The implementation does not delete the Image from the storage
170 f13aab5d Christos Stavrakakis
        backend. Instead it unregisters the image by removing all the
171 f13aab5d Christos Stavrakakis
        metadata from the plankton metadata domain.
172 f13aab5d Christos Stavrakakis

173 f13aab5d Christos Stavrakakis
    """
174 f13aab5d Christos Stavrakakis
    log.info("delete_image '%s'" % image_id)
175 f13aab5d Christos Stavrakakis
    userid = request.user_uniq
176 f13aab5d Christos Stavrakakis
    request.backend.unregister(image_id)
177 f13aab5d Christos Stavrakakis
    log.info("User '%s' deleted image '%s'" % (userid, image_id))
178 f13aab5d Christos Stavrakakis
    return HttpResponse(status=204)
179 f13aab5d Christos Stavrakakis
180 f13aab5d Christos Stavrakakis
181 d9d1763e Christos Stavrakakis
@api.api_method(http_method="PUT", user_required=True, logger=log)
182 d9d1763e Christos Stavrakakis
@plankton_method
183 15137c54 Giorgos Verigakis
def add_image_member(request, image_id, member):
184 15137c54 Giorgos Verigakis
    """Add a member to an image
185 29490fca Christos Stavrakakis

186 15137c54 Giorgos Verigakis
    Described in:
187 15137c54 Giorgos Verigakis
    3.9. Adding a Member to an Image
188 29490fca Christos Stavrakakis

189 15137c54 Giorgos Verigakis
    Limitations:
190 15137c54 Giorgos Verigakis
      * Passing a body to enable `can_share` is not supported.
191 15137c54 Giorgos Verigakis
    """
192 29490fca Christos Stavrakakis
193 15137c54 Giorgos Verigakis
    log.debug('add_image_member %s %s', image_id, member)
194 15137c54 Giorgos Verigakis
    request.backend.add_user(image_id, member)
195 15137c54 Giorgos Verigakis
    return HttpResponse(status=204)
196 c34de90f Giorgos Verigakis
197 c34de90f Giorgos Verigakis
198 d9d1763e Christos Stavrakakis
@api.api_method(http_method="GET", user_required=True, logger=log)
199 d9d1763e Christos Stavrakakis
@plankton_method
200 15137c54 Giorgos Verigakis
def get_image(request, image_id):
201 15137c54 Giorgos Verigakis
    """Retrieve a virtual machine image
202 29490fca Christos Stavrakakis

203 15137c54 Giorgos Verigakis
    Described in:
204 15137c54 Giorgos Verigakis
    3.5. Retrieving a Virtual Machine Image
205 29490fca Christos Stavrakakis

206 15137c54 Giorgos Verigakis
    Implementation notes:
207 15137c54 Giorgos Verigakis
      * The implementation is very inefficient as it loads the whole image
208 15137c54 Giorgos Verigakis
        in memory.
209 15137c54 Giorgos Verigakis
    """
210 29490fca Christos Stavrakakis
211 89b2e9b8 Giorgos Verigakis
    #image = request.backend.get_image(image_id)
212 f5afd99b Giorgos Verigakis
    #if not image:
213 f5afd99b Giorgos Verigakis
    #    return HttpResponseNotFound()
214 f5afd99b Giorgos Verigakis
    #
215 f5afd99b Giorgos Verigakis
    #response = _create_image_response(image)
216 f5afd99b Giorgos Verigakis
    #data = request.backend.get_data(image)
217 f5afd99b Giorgos Verigakis
    #response.content = data
218 f5afd99b Giorgos Verigakis
    #response['Content-Length'] = len(data)
219 f5afd99b Giorgos Verigakis
    #response['Content-Type'] = 'application/octet-stream'
220 f5afd99b Giorgos Verigakis
    #response['ETag'] = image['checksum']
221 f5afd99b Giorgos Verigakis
    #return response
222 f5afd99b Giorgos Verigakis
    return HttpResponse(status=501)     # Not Implemented
223 c34de90f Giorgos Verigakis
224 c34de90f Giorgos Verigakis
225 d9d1763e Christos Stavrakakis
@api.api_method(http_method="HEAD", user_required=True, logger=log)
226 d9d1763e Christos Stavrakakis
@plankton_method
227 15137c54 Giorgos Verigakis
def get_image_meta(request, image_id):
228 15137c54 Giorgos Verigakis
    """Return detailed metadata on a specific image
229 29490fca Christos Stavrakakis

230 15137c54 Giorgos Verigakis
    Described in:
231 15137c54 Giorgos Verigakis
    3.4. Requesting Detailed Metadata on a Specific Image
232 15137c54 Giorgos Verigakis
    """
233 04ba5fd4 Giorgos Verigakis
234 89b2e9b8 Giorgos Verigakis
    image = request.backend.get_image(image_id)
235 04ba5fd4 Giorgos Verigakis
    if not image:
236 d2b8ec7b Christos Stavrakakis
        raise faults.ItemNotFound()
237 15137c54 Giorgos Verigakis
    return _create_image_response(image)
238 15137c54 Giorgos Verigakis
239 15137c54 Giorgos Verigakis
240 d9d1763e Christos Stavrakakis
@api.api_method(http_method="GET", user_required=True, logger=log)
241 d9d1763e Christos Stavrakakis
@plankton_method
242 15137c54 Giorgos Verigakis
def list_image_members(request, image_id):
243 15137c54 Giorgos Verigakis
    """List image memberships
244 15137c54 Giorgos Verigakis

245 15137c54 Giorgos Verigakis
    Described in:
246 15137c54 Giorgos Verigakis
    3.7. Requesting Image Memberships
247 15137c54 Giorgos Verigakis
    """
248 29490fca Christos Stavrakakis
249 15137c54 Giorgos Verigakis
    members = [{'member_id': user, 'can_share': False}
250 cc92b70f Christos Stavrakakis
               for user in request.backend.list_users(image_id)]
251 15137c54 Giorgos Verigakis
    data = json.dumps({'members': members}, indent=settings.DEBUG)
252 15137c54 Giorgos Verigakis
    return HttpResponse(data)
253 04ba5fd4 Giorgos Verigakis
254 04ba5fd4 Giorgos Verigakis
255 d9d1763e Christos Stavrakakis
@api.api_method(http_method="GET", user_required=True, logger=log)
256 d9d1763e Christos Stavrakakis
@plankton_method
257 b2f94a0c Christos Stavrakakis
def list_images(request, detail=False):
258 b2f94a0c Christos Stavrakakis
    """Return a list of available images.
259 29490fca Christos Stavrakakis

260 b2f94a0c Christos Stavrakakis
    This includes images owned by the user, images shared with the user and
261 b2f94a0c Christos Stavrakakis
    public images.
262 29490fca Christos Stavrakakis

263 15137c54 Giorgos Verigakis
    """
264 04ba5fd4 Giorgos Verigakis
265 04ba5fd4 Giorgos Verigakis
    def get_request_params(keys):
266 04ba5fd4 Giorgos Verigakis
        params = {}
267 04ba5fd4 Giorgos Verigakis
        for key in keys:
268 04ba5fd4 Giorgos Verigakis
            val = request.GET.get(key, None)
269 04ba5fd4 Giorgos Verigakis
            if val is not None:
270 04ba5fd4 Giorgos Verigakis
                params[key] = val
271 04ba5fd4 Giorgos Verigakis
        return params
272 04ba5fd4 Giorgos Verigakis
273 15137c54 Giorgos Verigakis
    log.debug('list_public_images detail=%s', detail)
274 04ba5fd4 Giorgos Verigakis
275 04ba5fd4 Giorgos Verigakis
    filters = get_request_params(FILTERS)
276 04ba5fd4 Giorgos Verigakis
    params = get_request_params(PARAMS)
277 04ba5fd4 Giorgos Verigakis
278 04ba5fd4 Giorgos Verigakis
    params.setdefault('sort_key', 'created_at')
279 04ba5fd4 Giorgos Verigakis
    params.setdefault('sort_dir', 'desc')
280 04ba5fd4 Giorgos Verigakis
281 04ba5fd4 Giorgos Verigakis
    assert params['sort_key'] in SORT_KEY_OPTIONS
282 04ba5fd4 Giorgos Verigakis
    assert params['sort_dir'] in SORT_DIR_OPTIONS
283 04ba5fd4 Giorgos Verigakis
284 c3bcaeff Stratos Psomadakis
    if 'size_max' in filters:
285 c3bcaeff Stratos Psomadakis
        try:
286 c3bcaeff Stratos Psomadakis
            filters['size_max'] = int(filters['size_max'])
287 c3bcaeff Stratos Psomadakis
        except ValueError:
288 d2b8ec7b Christos Stavrakakis
            raise faults.BadRequest("Malformed request.")
289 c3bcaeff Stratos Psomadakis
290 c3bcaeff Stratos Psomadakis
    if 'size_min' in filters:
291 c3bcaeff Stratos Psomadakis
        try:
292 c3bcaeff Stratos Psomadakis
            filters['size_min'] = int(filters['size_min'])
293 c3bcaeff Stratos Psomadakis
        except ValueError:
294 d2b8ec7b Christos Stavrakakis
            raise faults.BadRequest("Malformed request.")
295 c3bcaeff Stratos Psomadakis
296 b2f94a0c Christos Stavrakakis
    images = request.backend.list(filters, params)
297 29490fca Christos Stavrakakis
298 04ba5fd4 Giorgos Verigakis
    # Remove keys that should not be returned
299 04ba5fd4 Giorgos Verigakis
    fields = DETAIL_FIELDS if detail else LIST_FIELDS
300 04ba5fd4 Giorgos Verigakis
    for image in images:
301 04ba5fd4 Giorgos Verigakis
        for key in image.keys():
302 04ba5fd4 Giorgos Verigakis
            if key not in fields:
303 04ba5fd4 Giorgos Verigakis
                del image[key]
304 04ba5fd4 Giorgos Verigakis
305 04ba5fd4 Giorgos Verigakis
    data = json.dumps(images, indent=settings.DEBUG)
306 04ba5fd4 Giorgos Verigakis
    return HttpResponse(data)
307 04ba5fd4 Giorgos Verigakis
308 04ba5fd4 Giorgos Verigakis
309 d9d1763e Christos Stavrakakis
@api.api_method(http_method="GET", user_required=True, logger=log)
310 d9d1763e Christos Stavrakakis
@plankton_method
311 b2f94a0c Christos Stavrakakis
def list_shared_images(request, member):
312 15137c54 Giorgos Verigakis
    """Request shared images
313 29490fca Christos Stavrakakis

314 15137c54 Giorgos Verigakis
    Described in:
315 15137c54 Giorgos Verigakis
    3.8. Requesting Shared Images
316 29490fca Christos Stavrakakis

317 15137c54 Giorgos Verigakis
    Implementation notes:
318 8482ef55 Giorgos Verigakis
      * It is not clear what this method should do. We return the IDs of
319 8482ef55 Giorgos Verigakis
        the users's images that are accessible by `member`.
320 15137c54 Giorgos Verigakis
    """
321 29490fca Christos Stavrakakis
322 b2f94a0c Christos Stavrakakis
    log.debug('list_shared_images %s', member)
323 29490fca Christos Stavrakakis
324 15137c54 Giorgos Verigakis
    images = []
325 b2f94a0c Christos Stavrakakis
    for image in request.backend.iter_shared(member=member):
326 aed77afe Christos Stavrakakis
        image_id = image['id']
327 8482ef55 Giorgos Verigakis
        images.append({'image_id': image_id, 'can_share': False})
328 29490fca Christos Stavrakakis
329 15137c54 Giorgos Verigakis
    data = json.dumps({'shared_images': images}, indent=settings.DEBUG)
330 15137c54 Giorgos Verigakis
    return HttpResponse(data)
331 15137c54 Giorgos Verigakis
332 15137c54 Giorgos Verigakis
333 d9d1763e Christos Stavrakakis
@api.api_method(http_method="DELETE", user_required=True, logger=log)
334 d9d1763e Christos Stavrakakis
@plankton_method
335 15137c54 Giorgos Verigakis
def remove_image_member(request, image_id, member):
336 15137c54 Giorgos Verigakis
    """Remove a member from an image
337 15137c54 Giorgos Verigakis

338 15137c54 Giorgos Verigakis
    Described in:
339 f5afd99b Giorgos Verigakis
    3.10. Removing a Member from an Image
340 15137c54 Giorgos Verigakis
    """
341 15137c54 Giorgos Verigakis
342 15137c54 Giorgos Verigakis
    log.debug('remove_image_member %s %s', image_id, member)
343 15137c54 Giorgos Verigakis
    request.backend.remove_user(image_id, member)
344 15137c54 Giorgos Verigakis
    return HttpResponse(status=204)
345 15137c54 Giorgos Verigakis
346 15137c54 Giorgos Verigakis
347 d9d1763e Christos Stavrakakis
@api.api_method(http_method="PUT", user_required=True, logger=log)
348 d9d1763e Christos Stavrakakis
@plankton_method
349 15137c54 Giorgos Verigakis
def update_image(request, image_id):
350 15137c54 Giorgos Verigakis
    """Update an image
351 29490fca Christos Stavrakakis

352 15137c54 Giorgos Verigakis
    Described in:
353 15137c54 Giorgos Verigakis
    3.6.2. Updating an Image
354 29490fca Christos Stavrakakis

355 15137c54 Giorgos Verigakis
    Implementation notes:
356 15137c54 Giorgos Verigakis
      * It is not clear which metadata are allowed to be updated. We support:
357 15137c54 Giorgos Verigakis
        name, disk_format, container_format, is_public, owner, properties
358 15137c54 Giorgos Verigakis
        and status.
359 15137c54 Giorgos Verigakis
    """
360 29490fca Christos Stavrakakis
361 15137c54 Giorgos Verigakis
    meta = _get_image_headers(request)
362 15137c54 Giorgos Verigakis
    log.debug('update_image %s', meta)
363 29490fca Christos Stavrakakis
364 7e00b33e Giorgos Verigakis
    assert set(meta.keys()).issubset(set(UPDATE_FIELDS))
365 29490fca Christos Stavrakakis
366 f5afd99b Giorgos Verigakis
    image = request.backend.update(image_id, meta)
367 15137c54 Giorgos Verigakis
    return _create_image_response(image)
368 15137c54 Giorgos Verigakis
369 15137c54 Giorgos Verigakis
370 d9d1763e Christos Stavrakakis
@api.api_method(http_method="PUT", user_required=True, logger=log)
371 d9d1763e Christos Stavrakakis
@plankton_method
372 15137c54 Giorgos Verigakis
def update_image_members(request, image_id):
373 15137c54 Giorgos Verigakis
    """Replace a membership list for an image
374 29490fca Christos Stavrakakis

375 15137c54 Giorgos Verigakis
    Described in:
376 15137c54 Giorgos Verigakis
    3.11. Replacing a Membership List for an Image
377 29490fca Christos Stavrakakis

378 15137c54 Giorgos Verigakis
    Limitations:
379 15137c54 Giorgos Verigakis
      * can_share value is ignored
380 15137c54 Giorgos Verigakis
    """
381 29490fca Christos Stavrakakis
382 15137c54 Giorgos Verigakis
    log.debug('update_image_members %s', image_id)
383 15137c54 Giorgos Verigakis
    members = []
384 15137c54 Giorgos Verigakis
    try:
385 15137c54 Giorgos Verigakis
        data = json.loads(request.raw_post_data)
386 15137c54 Giorgos Verigakis
        for member in data['memberships']:
387 15137c54 Giorgos Verigakis
            members.append(member['member_id'])
388 15137c54 Giorgos Verigakis
    except (ValueError, KeyError, TypeError):
389 15137c54 Giorgos Verigakis
        return HttpResponse(status=400)
390 29490fca Christos Stavrakakis
391 15137c54 Giorgos Verigakis
    request.backend.replace_users(image_id, members)
392 15137c54 Giorgos Verigakis
    return HttpResponse(status=204)