Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (10.6 kB)

1 c34de90f Giorgos Verigakis
# Copyright 2011 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 c3bcaeff Stratos Psomadakis
from django.http import (HttpResponse, HttpResponseNotFound,
42 c3bcaeff Stratos Psomadakis
                         HttpResponseBadRequest)
43 c34de90f Giorgos Verigakis
from synnefo.plankton.util import plankton_method
44 c34de90f Giorgos Verigakis
45 c34de90f Giorgos Verigakis
46 04ba5fd4 Giorgos Verigakis
FILTERS = ('name', 'container_format', 'disk_format', 'status', 'size_min',
47 04ba5fd4 Giorgos Verigakis
           'size_max')
48 c34de90f Giorgos Verigakis
49 04ba5fd4 Giorgos Verigakis
PARAMS = ('sort_key', 'sort_dir')
50 c34de90f Giorgos Verigakis
51 04ba5fd4 Giorgos Verigakis
SORT_KEY_OPTIONS = ('id', 'name', 'status', 'size', 'disk_format',
52 04ba5fd4 Giorgos Verigakis
                    'container_format', 'created_at', 'updated_at')
53 04ba5fd4 Giorgos Verigakis
54 04ba5fd4 Giorgos Verigakis
SORT_DIR_OPTIONS = ('asc', 'desc')
55 04ba5fd4 Giorgos Verigakis
56 04ba5fd4 Giorgos Verigakis
LIST_FIELDS = ('status', 'name', 'disk_format', 'container_format', 'size',
57 04ba5fd4 Giorgos Verigakis
               'id')
58 04ba5fd4 Giorgos Verigakis
59 04ba5fd4 Giorgos Verigakis
DETAIL_FIELDS = ('name', 'disk_format', 'container_format', 'size', 'checksum',
60 04ba5fd4 Giorgos Verigakis
                 'location', 'created_at', 'updated_at', 'deleted_at',
61 04ba5fd4 Giorgos Verigakis
                 'status', 'is_public', 'owner', 'properties', 'id')
62 04ba5fd4 Giorgos Verigakis
63 04ba5fd4 Giorgos Verigakis
ADD_FIELDS = ('name', 'id', 'store', 'disk_format', 'container_format', 'size',
64 04ba5fd4 Giorgos Verigakis
              'checksum', 'is_public', 'owner', 'properties', 'location')
65 04ba5fd4 Giorgos Verigakis
66 04ba5fd4 Giorgos Verigakis
UPDATE_FIELDS = ('name', 'disk_format', 'container_format', 'is_public',
67 7e00b33e Giorgos Verigakis
                 'owner', 'properties', 'status')
68 c34de90f Giorgos Verigakis
69 c34de90f Giorgos Verigakis
70 15137c54 Giorgos Verigakis
log = getLogger('synnefo.plankton')
71 c34de90f Giorgos Verigakis
72 c34de90f Giorgos Verigakis
73 15137c54 Giorgos Verigakis
def _create_image_response(image):
74 04ba5fd4 Giorgos Verigakis
    response = HttpResponse()
75 29490fca Christos Stavrakakis
76 04ba5fd4 Giorgos Verigakis
    for key in DETAIL_FIELDS:
77 04ba5fd4 Giorgos Verigakis
        if key == 'properties':
78 04ba5fd4 Giorgos Verigakis
            for k, v in image.get('properties', {}).items():
79 04ba5fd4 Giorgos Verigakis
                name = 'x-image-meta-property-' + k.replace('_', '-')
80 04ba5fd4 Giorgos Verigakis
                response[name] = v
81 04ba5fd4 Giorgos Verigakis
        else:
82 04ba5fd4 Giorgos Verigakis
            name = 'x-image-meta-' + key.replace('_', '-')
83 04ba5fd4 Giorgos Verigakis
            response[name] = image.get(key, '')
84 29490fca Christos Stavrakakis
85 04ba5fd4 Giorgos Verigakis
    return response
86 04ba5fd4 Giorgos Verigakis
87 04ba5fd4 Giorgos Verigakis
88 15137c54 Giorgos Verigakis
def _get_image_headers(request):
89 04ba5fd4 Giorgos Verigakis
    def normalize(s):
90 04ba5fd4 Giorgos Verigakis
        return ''.join('_' if c in punctuation else c.lower() for c in s)
91 29490fca Christos Stavrakakis
92 04ba5fd4 Giorgos Verigakis
    META_PREFIX = 'HTTP_X_IMAGE_META_'
93 04ba5fd4 Giorgos Verigakis
    META_PREFIX_LEN = len(META_PREFIX)
94 04ba5fd4 Giorgos Verigakis
    META_PROPERTY_PREFIX = 'HTTP_X_IMAGE_META_PROPERTY_'
95 04ba5fd4 Giorgos Verigakis
    META_PROPERTY_PREFIX_LEN = len(META_PROPERTY_PREFIX)
96 29490fca Christos Stavrakakis
97 04ba5fd4 Giorgos Verigakis
    headers = {'properties': {}}
98 29490fca Christos Stavrakakis
99 04ba5fd4 Giorgos Verigakis
    for key, val in request.META.items():
100 04ba5fd4 Giorgos Verigakis
        if key.startswith(META_PROPERTY_PREFIX):
101 04ba5fd4 Giorgos Verigakis
            name = normalize(key[META_PROPERTY_PREFIX_LEN:])
102 4ec83e68 Giorgos Verigakis
            headers['properties'][unquote(name)] = unquote(val)
103 04ba5fd4 Giorgos Verigakis
        elif key.startswith(META_PREFIX):
104 04ba5fd4 Giorgos Verigakis
            name = normalize(key[META_PREFIX_LEN:])
105 4ec83e68 Giorgos Verigakis
            headers[unquote(name)] = unquote(val)
106 29490fca Christos Stavrakakis
107 7e00b33e Giorgos Verigakis
    is_public = headers.get('is_public', None)
108 7e00b33e Giorgos Verigakis
    if is_public is not None:
109 7e00b33e Giorgos Verigakis
        headers['is_public'] = True if is_public.lower() == 'true' else False
110 29490fca Christos Stavrakakis
111 7e00b33e Giorgos Verigakis
    if not headers['properties']:
112 7e00b33e Giorgos Verigakis
        del headers['properties']
113 29490fca Christos Stavrakakis
114 04ba5fd4 Giorgos Verigakis
    return headers
115 c34de90f Giorgos Verigakis
116 c34de90f Giorgos Verigakis
117 04ba5fd4 Giorgos Verigakis
@plankton_method('POST')
118 15137c54 Giorgos Verigakis
def add_image(request):
119 15137c54 Giorgos Verigakis
    """Add a new virtual machine image
120 29490fca Christos Stavrakakis

121 15137c54 Giorgos Verigakis
    Described in:
122 15137c54 Giorgos Verigakis
    3.6. Adding a New Virtual Machine Image
123 29490fca Christos Stavrakakis

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

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

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

162 15137c54 Giorgos Verigakis
    Described in:
163 15137c54 Giorgos Verigakis
    3.9. Adding a Member to an Image
164 29490fca Christos Stavrakakis

165 15137c54 Giorgos Verigakis
    Limitations:
166 15137c54 Giorgos Verigakis
      * Passing a body to enable `can_share` is not supported.
167 15137c54 Giorgos Verigakis
    """
168 29490fca Christos Stavrakakis
169 15137c54 Giorgos Verigakis
    log.debug('add_image_member %s %s', image_id, member)
170 15137c54 Giorgos Verigakis
    request.backend.add_user(image_id, member)
171 15137c54 Giorgos Verigakis
    return HttpResponse(status=204)
172 c34de90f Giorgos Verigakis
173 c34de90f Giorgos Verigakis
174 c34de90f Giorgos Verigakis
@plankton_method('GET')
175 15137c54 Giorgos Verigakis
def get_image(request, image_id):
176 15137c54 Giorgos Verigakis
    """Retrieve a virtual machine image
177 29490fca Christos Stavrakakis

178 15137c54 Giorgos Verigakis
    Described in:
179 15137c54 Giorgos Verigakis
    3.5. Retrieving a Virtual Machine Image
180 29490fca Christos Stavrakakis

181 15137c54 Giorgos Verigakis
    Implementation notes:
182 15137c54 Giorgos Verigakis
      * The implementation is very inefficient as it loads the whole image
183 15137c54 Giorgos Verigakis
        in memory.
184 15137c54 Giorgos Verigakis
    """
185 29490fca Christos Stavrakakis
186 89b2e9b8 Giorgos Verigakis
    #image = request.backend.get_image(image_id)
187 f5afd99b Giorgos Verigakis
    #if not image:
188 f5afd99b Giorgos Verigakis
    #    return HttpResponseNotFound()
189 f5afd99b Giorgos Verigakis
    #
190 f5afd99b Giorgos Verigakis
    #response = _create_image_response(image)
191 f5afd99b Giorgos Verigakis
    #data = request.backend.get_data(image)
192 f5afd99b Giorgos Verigakis
    #response.content = data
193 f5afd99b Giorgos Verigakis
    #response['Content-Length'] = len(data)
194 f5afd99b Giorgos Verigakis
    #response['Content-Type'] = 'application/octet-stream'
195 f5afd99b Giorgos Verigakis
    #response['ETag'] = image['checksum']
196 f5afd99b Giorgos Verigakis
    #return response
197 f5afd99b Giorgos Verigakis
    return HttpResponse(status=501)     # Not Implemented
198 c34de90f Giorgos Verigakis
199 c34de90f Giorgos Verigakis
200 04ba5fd4 Giorgos Verigakis
@plankton_method('HEAD')
201 15137c54 Giorgos Verigakis
def get_image_meta(request, image_id):
202 15137c54 Giorgos Verigakis
    """Return detailed metadata on a specific image
203 29490fca Christos Stavrakakis

204 15137c54 Giorgos Verigakis
    Described in:
205 15137c54 Giorgos Verigakis
    3.4. Requesting Detailed Metadata on a Specific Image
206 15137c54 Giorgos Verigakis
    """
207 04ba5fd4 Giorgos Verigakis
208 89b2e9b8 Giorgos Verigakis
    image = request.backend.get_image(image_id)
209 04ba5fd4 Giorgos Verigakis
    if not image:
210 04ba5fd4 Giorgos Verigakis
        return HttpResponseNotFound()
211 15137c54 Giorgos Verigakis
    return _create_image_response(image)
212 15137c54 Giorgos Verigakis
213 15137c54 Giorgos Verigakis
214 15137c54 Giorgos Verigakis
@plankton_method('GET')
215 15137c54 Giorgos Verigakis
def list_image_members(request, image_id):
216 15137c54 Giorgos Verigakis
    """List image memberships
217 15137c54 Giorgos Verigakis

218 15137c54 Giorgos Verigakis
    Described in:
219 15137c54 Giorgos Verigakis
    3.7. Requesting Image Memberships
220 15137c54 Giorgos Verigakis
    """
221 29490fca Christos Stavrakakis
222 15137c54 Giorgos Verigakis
    members = [{'member_id': user, 'can_share': False}
223 cc92b70f Christos Stavrakakis
               for user in request.backend.list_users(image_id)]
224 15137c54 Giorgos Verigakis
    data = json.dumps({'members': members}, indent=settings.DEBUG)
225 15137c54 Giorgos Verigakis
    return HttpResponse(data)
226 04ba5fd4 Giorgos Verigakis
227 04ba5fd4 Giorgos Verigakis
228 04ba5fd4 Giorgos Verigakis
@plankton_method('GET')
229 b2f94a0c Christos Stavrakakis
def list_images(request, detail=False):
230 b2f94a0c Christos Stavrakakis
    """Return a list of available images.
231 29490fca Christos Stavrakakis

232 b2f94a0c Christos Stavrakakis
    This includes images owned by the user, images shared with the user and
233 b2f94a0c Christos Stavrakakis
    public images.
234 29490fca Christos Stavrakakis

235 15137c54 Giorgos Verigakis
    """
236 04ba5fd4 Giorgos Verigakis
237 04ba5fd4 Giorgos Verigakis
    def get_request_params(keys):
238 04ba5fd4 Giorgos Verigakis
        params = {}
239 04ba5fd4 Giorgos Verigakis
        for key in keys:
240 04ba5fd4 Giorgos Verigakis
            val = request.GET.get(key, None)
241 04ba5fd4 Giorgos Verigakis
            if val is not None:
242 04ba5fd4 Giorgos Verigakis
                params[key] = val
243 04ba5fd4 Giorgos Verigakis
        return params
244 04ba5fd4 Giorgos Verigakis
245 15137c54 Giorgos Verigakis
    log.debug('list_public_images detail=%s', detail)
246 04ba5fd4 Giorgos Verigakis
247 04ba5fd4 Giorgos Verigakis
    filters = get_request_params(FILTERS)
248 04ba5fd4 Giorgos Verigakis
    params = get_request_params(PARAMS)
249 04ba5fd4 Giorgos Verigakis
250 04ba5fd4 Giorgos Verigakis
    params.setdefault('sort_key', 'created_at')
251 04ba5fd4 Giorgos Verigakis
    params.setdefault('sort_dir', 'desc')
252 04ba5fd4 Giorgos Verigakis
253 04ba5fd4 Giorgos Verigakis
    assert params['sort_key'] in SORT_KEY_OPTIONS
254 04ba5fd4 Giorgos Verigakis
    assert params['sort_dir'] in SORT_DIR_OPTIONS
255 04ba5fd4 Giorgos Verigakis
256 c3bcaeff Stratos Psomadakis
    if 'size_max' in filters:
257 c3bcaeff Stratos Psomadakis
        try:
258 c3bcaeff Stratos Psomadakis
            filters['size_max'] = int(filters['size_max'])
259 c3bcaeff Stratos Psomadakis
        except ValueError:
260 c3bcaeff Stratos Psomadakis
            return HttpResponseBadRequest('400 Bad Request')
261 c3bcaeff Stratos Psomadakis
262 c3bcaeff Stratos Psomadakis
    if 'size_min' in filters:
263 c3bcaeff Stratos Psomadakis
        try:
264 c3bcaeff Stratos Psomadakis
            filters['size_min'] = int(filters['size_min'])
265 c3bcaeff Stratos Psomadakis
        except ValueError:
266 c3bcaeff Stratos Psomadakis
            return HttpResponseBadRequest('400 Bad Request')
267 c3bcaeff Stratos Psomadakis
268 b2f94a0c Christos Stavrakakis
    images = request.backend.list(filters, params)
269 29490fca Christos Stavrakakis
270 04ba5fd4 Giorgos Verigakis
    # Remove keys that should not be returned
271 04ba5fd4 Giorgos Verigakis
    fields = DETAIL_FIELDS if detail else LIST_FIELDS
272 04ba5fd4 Giorgos Verigakis
    for image in images:
273 04ba5fd4 Giorgos Verigakis
        for key in image.keys():
274 04ba5fd4 Giorgos Verigakis
            if key not in fields:
275 04ba5fd4 Giorgos Verigakis
                del image[key]
276 04ba5fd4 Giorgos Verigakis
277 04ba5fd4 Giorgos Verigakis
    data = json.dumps(images, indent=settings.DEBUG)
278 04ba5fd4 Giorgos Verigakis
    return HttpResponse(data)
279 04ba5fd4 Giorgos Verigakis
280 04ba5fd4 Giorgos Verigakis
281 15137c54 Giorgos Verigakis
@plankton_method('GET')
282 b2f94a0c Christos Stavrakakis
def list_shared_images(request, member):
283 15137c54 Giorgos Verigakis
    """Request shared images
284 29490fca Christos Stavrakakis

285 15137c54 Giorgos Verigakis
    Described in:
286 15137c54 Giorgos Verigakis
    3.8. Requesting Shared Images
287 29490fca Christos Stavrakakis

288 15137c54 Giorgos Verigakis
    Implementation notes:
289 8482ef55 Giorgos Verigakis
      * It is not clear what this method should do. We return the IDs of
290 8482ef55 Giorgos Verigakis
        the users's images that are accessible by `member`.
291 15137c54 Giorgos Verigakis
    """
292 29490fca Christos Stavrakakis
293 b2f94a0c Christos Stavrakakis
    log.debug('list_shared_images %s', member)
294 29490fca Christos Stavrakakis
295 15137c54 Giorgos Verigakis
    images = []
296 b2f94a0c Christos Stavrakakis
    for image in request.backend.iter_shared(member=member):
297 aed77afe Christos Stavrakakis
        image_id = image['id']
298 8482ef55 Giorgos Verigakis
        images.append({'image_id': image_id, 'can_share': False})
299 29490fca Christos Stavrakakis
300 15137c54 Giorgos Verigakis
    data = json.dumps({'shared_images': images}, indent=settings.DEBUG)
301 15137c54 Giorgos Verigakis
    return HttpResponse(data)
302 15137c54 Giorgos Verigakis
303 15137c54 Giorgos Verigakis
304 15137c54 Giorgos Verigakis
@plankton_method('DELETE')
305 15137c54 Giorgos Verigakis
def remove_image_member(request, image_id, member):
306 15137c54 Giorgos Verigakis
    """Remove a member from an image
307 15137c54 Giorgos Verigakis

308 15137c54 Giorgos Verigakis
    Described in:
309 f5afd99b Giorgos Verigakis
    3.10. Removing a Member from an Image
310 15137c54 Giorgos Verigakis
    """
311 15137c54 Giorgos Verigakis
312 15137c54 Giorgos Verigakis
    log.debug('remove_image_member %s %s', image_id, member)
313 15137c54 Giorgos Verigakis
    request.backend.remove_user(image_id, member)
314 15137c54 Giorgos Verigakis
    return HttpResponse(status=204)
315 15137c54 Giorgos Verigakis
316 15137c54 Giorgos Verigakis
317 04ba5fd4 Giorgos Verigakis
@plankton_method('PUT')
318 15137c54 Giorgos Verigakis
def update_image(request, image_id):
319 15137c54 Giorgos Verigakis
    """Update an image
320 29490fca Christos Stavrakakis

321 15137c54 Giorgos Verigakis
    Described in:
322 15137c54 Giorgos Verigakis
    3.6.2. Updating an Image
323 29490fca Christos Stavrakakis

324 15137c54 Giorgos Verigakis
    Implementation notes:
325 15137c54 Giorgos Verigakis
      * It is not clear which metadata are allowed to be updated. We support:
326 15137c54 Giorgos Verigakis
        name, disk_format, container_format, is_public, owner, properties
327 15137c54 Giorgos Verigakis
        and status.
328 15137c54 Giorgos Verigakis
    """
329 29490fca Christos Stavrakakis
330 15137c54 Giorgos Verigakis
    meta = _get_image_headers(request)
331 15137c54 Giorgos Verigakis
    log.debug('update_image %s', meta)
332 29490fca Christos Stavrakakis
333 7e00b33e Giorgos Verigakis
    assert set(meta.keys()).issubset(set(UPDATE_FIELDS))
334 29490fca Christos Stavrakakis
335 f5afd99b Giorgos Verigakis
    image = request.backend.update(image_id, meta)
336 15137c54 Giorgos Verigakis
    return _create_image_response(image)
337 15137c54 Giorgos Verigakis
338 15137c54 Giorgos Verigakis
339 15137c54 Giorgos Verigakis
@plankton_method('PUT')
340 15137c54 Giorgos Verigakis
def update_image_members(request, image_id):
341 15137c54 Giorgos Verigakis
    """Replace a membership list for an image
342 29490fca Christos Stavrakakis

343 15137c54 Giorgos Verigakis
    Described in:
344 15137c54 Giorgos Verigakis
    3.11. Replacing a Membership List for an Image
345 29490fca Christos Stavrakakis

346 15137c54 Giorgos Verigakis
    Limitations:
347 15137c54 Giorgos Verigakis
      * can_share value is ignored
348 15137c54 Giorgos Verigakis
    """
349 29490fca Christos Stavrakakis
350 15137c54 Giorgos Verigakis
    log.debug('update_image_members %s', image_id)
351 15137c54 Giorgos Verigakis
    members = []
352 15137c54 Giorgos Verigakis
    try:
353 15137c54 Giorgos Verigakis
        data = json.loads(request.raw_post_data)
354 15137c54 Giorgos Verigakis
        for member in data['memberships']:
355 15137c54 Giorgos Verigakis
            members.append(member['member_id'])
356 15137c54 Giorgos Verigakis
    except (ValueError, KeyError, TypeError):
357 15137c54 Giorgos Verigakis
        return HttpResponse(status=400)
358 29490fca Christos Stavrakakis
359 15137c54 Giorgos Verigakis
    request.backend.replace_users(image_id, members)
360 15137c54 Giorgos Verigakis
    return HttpResponse(status=204)