Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (10.2 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 15137c54 Giorgos Verigakis
from django.http import HttpResponse, HttpResponseNotFound
42 c34de90f Giorgos Verigakis
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 15137c54 Giorgos Verigakis
                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 b2f94a0c Christos Stavrakakis
    images = request.backend.list(filters, params)
257 29490fca Christos Stavrakakis
258 04ba5fd4 Giorgos Verigakis
    # Remove keys that should not be returned
259 04ba5fd4 Giorgos Verigakis
    fields = DETAIL_FIELDS if detail else LIST_FIELDS
260 04ba5fd4 Giorgos Verigakis
    for image in images:
261 04ba5fd4 Giorgos Verigakis
        for key in image.keys():
262 04ba5fd4 Giorgos Verigakis
            if key not in fields:
263 04ba5fd4 Giorgos Verigakis
                del image[key]
264 04ba5fd4 Giorgos Verigakis
265 04ba5fd4 Giorgos Verigakis
    data = json.dumps(images, indent=settings.DEBUG)
266 04ba5fd4 Giorgos Verigakis
    return HttpResponse(data)
267 04ba5fd4 Giorgos Verigakis
268 04ba5fd4 Giorgos Verigakis
269 15137c54 Giorgos Verigakis
@plankton_method('GET')
270 b2f94a0c Christos Stavrakakis
def list_shared_images(request, member):
271 15137c54 Giorgos Verigakis
    """Request shared images
272 29490fca Christos Stavrakakis

273 15137c54 Giorgos Verigakis
    Described in:
274 15137c54 Giorgos Verigakis
    3.8. Requesting Shared Images
275 29490fca Christos Stavrakakis

276 15137c54 Giorgos Verigakis
    Implementation notes:
277 8482ef55 Giorgos Verigakis
      * It is not clear what this method should do. We return the IDs of
278 8482ef55 Giorgos Verigakis
        the users's images that are accessible by `member`.
279 15137c54 Giorgos Verigakis
    """
280 29490fca Christos Stavrakakis
281 b2f94a0c Christos Stavrakakis
    log.debug('list_shared_images %s', member)
282 29490fca Christos Stavrakakis
283 15137c54 Giorgos Verigakis
    images = []
284 b2f94a0c Christos Stavrakakis
    for image in request.backend.iter_shared(member=member):
285 aed77afe Christos Stavrakakis
        image_id = image['id']
286 8482ef55 Giorgos Verigakis
        images.append({'image_id': image_id, 'can_share': False})
287 29490fca Christos Stavrakakis
288 15137c54 Giorgos Verigakis
    data = json.dumps({'shared_images': images}, indent=settings.DEBUG)
289 15137c54 Giorgos Verigakis
    return HttpResponse(data)
290 15137c54 Giorgos Verigakis
291 15137c54 Giorgos Verigakis
292 15137c54 Giorgos Verigakis
@plankton_method('DELETE')
293 15137c54 Giorgos Verigakis
def remove_image_member(request, image_id, member):
294 15137c54 Giorgos Verigakis
    """Remove a member from an image
295 15137c54 Giorgos Verigakis

296 15137c54 Giorgos Verigakis
    Described in:
297 f5afd99b Giorgos Verigakis
    3.10. Removing a Member from an Image
298 15137c54 Giorgos Verigakis
    """
299 15137c54 Giorgos Verigakis
300 15137c54 Giorgos Verigakis
    log.debug('remove_image_member %s %s', image_id, member)
301 15137c54 Giorgos Verigakis
    request.backend.remove_user(image_id, member)
302 15137c54 Giorgos Verigakis
    return HttpResponse(status=204)
303 15137c54 Giorgos Verigakis
304 15137c54 Giorgos Verigakis
305 04ba5fd4 Giorgos Verigakis
@plankton_method('PUT')
306 15137c54 Giorgos Verigakis
def update_image(request, image_id):
307 15137c54 Giorgos Verigakis
    """Update an image
308 29490fca Christos Stavrakakis

309 15137c54 Giorgos Verigakis
    Described in:
310 15137c54 Giorgos Verigakis
    3.6.2. Updating an Image
311 29490fca Christos Stavrakakis

312 15137c54 Giorgos Verigakis
    Implementation notes:
313 15137c54 Giorgos Verigakis
      * It is not clear which metadata are allowed to be updated. We support:
314 15137c54 Giorgos Verigakis
        name, disk_format, container_format, is_public, owner, properties
315 15137c54 Giorgos Verigakis
        and status.
316 15137c54 Giorgos Verigakis
    """
317 29490fca Christos Stavrakakis
318 15137c54 Giorgos Verigakis
    meta = _get_image_headers(request)
319 15137c54 Giorgos Verigakis
    log.debug('update_image %s', meta)
320 29490fca Christos Stavrakakis
321 7e00b33e Giorgos Verigakis
    assert set(meta.keys()).issubset(set(UPDATE_FIELDS))
322 29490fca Christos Stavrakakis
323 f5afd99b Giorgos Verigakis
    image = request.backend.update(image_id, meta)
324 15137c54 Giorgos Verigakis
    return _create_image_response(image)
325 15137c54 Giorgos Verigakis
326 15137c54 Giorgos Verigakis
327 15137c54 Giorgos Verigakis
@plankton_method('PUT')
328 15137c54 Giorgos Verigakis
def update_image_members(request, image_id):
329 15137c54 Giorgos Verigakis
    """Replace a membership list for an image
330 29490fca Christos Stavrakakis

331 15137c54 Giorgos Verigakis
    Described in:
332 15137c54 Giorgos Verigakis
    3.11. Replacing a Membership List for an Image
333 29490fca Christos Stavrakakis

334 15137c54 Giorgos Verigakis
    Limitations:
335 15137c54 Giorgos Verigakis
      * can_share value is ignored
336 15137c54 Giorgos Verigakis
    """
337 29490fca Christos Stavrakakis
338 15137c54 Giorgos Verigakis
    log.debug('update_image_members %s', image_id)
339 15137c54 Giorgos Verigakis
    members = []
340 15137c54 Giorgos Verigakis
    try:
341 15137c54 Giorgos Verigakis
        data = json.loads(request.raw_post_data)
342 15137c54 Giorgos Verigakis
        for member in data['memberships']:
343 15137c54 Giorgos Verigakis
            members.append(member['member_id'])
344 15137c54 Giorgos Verigakis
    except (ValueError, KeyError, TypeError):
345 15137c54 Giorgos Verigakis
        return HttpResponse(status=400)
346 29490fca Christos Stavrakakis
347 15137c54 Giorgos Verigakis
    request.backend.replace_users(image_id, members)
348 15137c54 Giorgos Verigakis
    return HttpResponse(status=204)