Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (10.5 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 c23d211a Giorgos Verigakis
from StringIO import StringIO
39 4ec83e68 Giorgos Verigakis
from urllib import unquote
40 c34de90f Giorgos Verigakis
41 c34de90f Giorgos Verigakis
from django.conf import settings
42 15137c54 Giorgos Verigakis
from django.http import HttpResponse, HttpResponseNotFound
43 c34de90f Giorgos Verigakis
44 c34de90f Giorgos Verigakis
from synnefo.plankton.util import plankton_method
45 c34de90f Giorgos Verigakis
46 c34de90f Giorgos Verigakis
47 04ba5fd4 Giorgos Verigakis
FILTERS = ('name', 'container_format', 'disk_format', 'status', 'size_min',
48 04ba5fd4 Giorgos Verigakis
           'size_max')
49 c34de90f Giorgos Verigakis
50 04ba5fd4 Giorgos Verigakis
PARAMS = ('sort_key', 'sort_dir')
51 c34de90f Giorgos Verigakis
52 04ba5fd4 Giorgos Verigakis
SORT_KEY_OPTIONS = ('id', 'name', 'status', 'size', 'disk_format',
53 04ba5fd4 Giorgos Verigakis
                    'container_format', 'created_at', 'updated_at')
54 04ba5fd4 Giorgos Verigakis
55 04ba5fd4 Giorgos Verigakis
SORT_DIR_OPTIONS = ('asc', 'desc')
56 04ba5fd4 Giorgos Verigakis
57 04ba5fd4 Giorgos Verigakis
LIST_FIELDS = ('status', 'name', 'disk_format', 'container_format', 'size',
58 04ba5fd4 Giorgos Verigakis
               'id')
59 04ba5fd4 Giorgos Verigakis
60 04ba5fd4 Giorgos Verigakis
DETAIL_FIELDS = ('name', 'disk_format', 'container_format', 'size', 'checksum',
61 04ba5fd4 Giorgos Verigakis
                 'location', 'created_at', 'updated_at', 'deleted_at',
62 04ba5fd4 Giorgos Verigakis
                 'status', 'is_public', 'owner', 'properties', 'id')
63 04ba5fd4 Giorgos Verigakis
64 04ba5fd4 Giorgos Verigakis
ADD_FIELDS = ('name', 'id', 'store', 'disk_format', 'container_format', 'size',
65 04ba5fd4 Giorgos Verigakis
              'checksum', 'is_public', 'owner', 'properties', 'location')
66 04ba5fd4 Giorgos Verigakis
67 04ba5fd4 Giorgos Verigakis
UPDATE_FIELDS = ('name', 'disk_format', 'container_format', 'is_public',
68 7e00b33e Giorgos Verigakis
                 'owner', 'properties', 'status')
69 c34de90f Giorgos Verigakis
70 c34de90f Giorgos Verigakis
71 15137c54 Giorgos Verigakis
log = getLogger('synnefo.plankton')
72 c34de90f Giorgos Verigakis
73 c34de90f Giorgos Verigakis
74 15137c54 Giorgos Verigakis
def _create_image_response(image):
75 04ba5fd4 Giorgos Verigakis
    response = HttpResponse()
76 c34de90f Giorgos Verigakis
    
77 04ba5fd4 Giorgos Verigakis
    for key in DETAIL_FIELDS:
78 04ba5fd4 Giorgos Verigakis
        if key == 'properties':
79 04ba5fd4 Giorgos Verigakis
            for k, v in image.get('properties', {}).items():
80 04ba5fd4 Giorgos Verigakis
                name = 'x-image-meta-property-' + k.replace('_', '-')
81 04ba5fd4 Giorgos Verigakis
                response[name] = v
82 04ba5fd4 Giorgos Verigakis
        else:
83 04ba5fd4 Giorgos Verigakis
            name = 'x-image-meta-' + key.replace('_', '-')
84 04ba5fd4 Giorgos Verigakis
            response[name] = image.get(key, '')
85 c34de90f Giorgos Verigakis
    
86 04ba5fd4 Giorgos Verigakis
    return response
87 04ba5fd4 Giorgos Verigakis
88 04ba5fd4 Giorgos Verigakis
89 15137c54 Giorgos Verigakis
def _get_image_headers(request):
90 04ba5fd4 Giorgos Verigakis
    def normalize(s):
91 04ba5fd4 Giorgos Verigakis
        return ''.join('_' if c in punctuation else c.lower() for c in s)
92 04ba5fd4 Giorgos Verigakis
    
93 04ba5fd4 Giorgos Verigakis
    META_PREFIX = 'HTTP_X_IMAGE_META_'
94 04ba5fd4 Giorgos Verigakis
    META_PREFIX_LEN = len(META_PREFIX)
95 04ba5fd4 Giorgos Verigakis
    META_PROPERTY_PREFIX = 'HTTP_X_IMAGE_META_PROPERTY_'
96 04ba5fd4 Giorgos Verigakis
    META_PROPERTY_PREFIX_LEN = len(META_PROPERTY_PREFIX)
97 c34de90f Giorgos Verigakis
    
98 04ba5fd4 Giorgos Verigakis
    headers = {'properties': {}}
99 c34de90f Giorgos Verigakis
    
100 04ba5fd4 Giorgos Verigakis
    for key, val in request.META.items():
101 04ba5fd4 Giorgos Verigakis
        if key.startswith(META_PROPERTY_PREFIX):
102 04ba5fd4 Giorgos Verigakis
            name = normalize(key[META_PROPERTY_PREFIX_LEN:])
103 4ec83e68 Giorgos Verigakis
            headers['properties'][unquote(name)] = unquote(val)
104 04ba5fd4 Giorgos Verigakis
        elif key.startswith(META_PREFIX):
105 04ba5fd4 Giorgos Verigakis
            name = normalize(key[META_PREFIX_LEN:])
106 4ec83e68 Giorgos Verigakis
            headers[unquote(name)] = unquote(val)
107 04ba5fd4 Giorgos Verigakis
    
108 7e00b33e Giorgos Verigakis
    is_public = headers.get('is_public', None)
109 7e00b33e Giorgos Verigakis
    if is_public is not None:
110 7e00b33e Giorgos Verigakis
        headers['is_public'] = True if is_public.lower() == 'true' else False
111 7e00b33e Giorgos Verigakis
    
112 7e00b33e Giorgos Verigakis
    if not headers['properties']:
113 7e00b33e Giorgos Verigakis
        del headers['properties']
114 c34de90f Giorgos Verigakis
    
115 04ba5fd4 Giorgos Verigakis
    return headers
116 c34de90f Giorgos Verigakis
117 c34de90f Giorgos Verigakis
118 04ba5fd4 Giorgos Verigakis
@plankton_method('POST')
119 15137c54 Giorgos Verigakis
def add_image(request):
120 15137c54 Giorgos Verigakis
    """Add a new virtual machine image
121 15137c54 Giorgos Verigakis
    
122 15137c54 Giorgos Verigakis
    Described in:
123 15137c54 Giorgos Verigakis
    3.6. Adding a New Virtual Machine Image
124 15137c54 Giorgos Verigakis
    
125 15137c54 Giorgos Verigakis
    Implementation notes:
126 15137c54 Giorgos Verigakis
      * The implementation is very inefficient as it loads the whole image
127 15137c54 Giorgos Verigakis
        in memory.
128 c34de90f Giorgos Verigakis
    
129 15137c54 Giorgos Verigakis
    Limitations:
130 15137c54 Giorgos Verigakis
      * x-image-meta-id is not supported. Will always return 409 Conflict.
131 15137c54 Giorgos Verigakis
    
132 15137c54 Giorgos Verigakis
    Extensions:
133 15137c54 Giorgos Verigakis
      * An x-image-meta-location header can be passed with a link to file,
134 15137c54 Giorgos Verigakis
        instead of uploading the data.
135 15137c54 Giorgos Verigakis
    """
136 15137c54 Giorgos Verigakis
    
137 15137c54 Giorgos Verigakis
    params = _get_image_headers(request)
138 15137c54 Giorgos Verigakis
    log.debug('add_image %s', params)
139 c34de90f Giorgos Verigakis
    
140 f5afd99b Giorgos Verigakis
    assert 'name' in params
141 04ba5fd4 Giorgos Verigakis
    assert set(params.keys()).issubset(set(ADD_FIELDS))
142 04ba5fd4 Giorgos Verigakis
    
143 7bd1d3b5 Giorgos Verigakis
    name = params.pop('name')
144 f5afd99b Giorgos Verigakis
    location = params.pop('location', None)
145 7bd1d3b5 Giorgos Verigakis
    
146 f5afd99b Giorgos Verigakis
    if location:
147 f5afd99b Giorgos Verigakis
        image = request.backend.register(name, location, params)
148 04ba5fd4 Giorgos Verigakis
    else:
149 c23d211a Giorgos Verigakis
        #f = StringIO(request.raw_post_data)
150 c23d211a Giorgos Verigakis
        #image = request.backend.put(name, f, params)
151 f5afd99b Giorgos Verigakis
        return HttpResponse(status=501)     # Not Implemented
152 04ba5fd4 Giorgos Verigakis
    
153 04ba5fd4 Giorgos Verigakis
    if not image:
154 0a72907b Giorgos Verigakis
        return HttpResponse('Registration failed', status=500)
155 15137c54 Giorgos Verigakis
    
156 15137c54 Giorgos Verigakis
    return _create_image_response(image)
157 15137c54 Giorgos Verigakis
158 15137c54 Giorgos Verigakis
159 15137c54 Giorgos Verigakis
@plankton_method('PUT')
160 15137c54 Giorgos Verigakis
def add_image_member(request, image_id, member):
161 15137c54 Giorgos Verigakis
    """Add a member to an image
162 15137c54 Giorgos Verigakis
    
163 15137c54 Giorgos Verigakis
    Described in:
164 15137c54 Giorgos Verigakis
    3.9. Adding a Member to an Image
165 15137c54 Giorgos Verigakis
    
166 15137c54 Giorgos Verigakis
    Limitations:
167 15137c54 Giorgos Verigakis
      * Passing a body to enable `can_share` is not supported.
168 15137c54 Giorgos Verigakis
    """
169 15137c54 Giorgos Verigakis
    
170 15137c54 Giorgos Verigakis
    log.debug('add_image_member %s %s', image_id, member)
171 15137c54 Giorgos Verigakis
    request.backend.add_user(image_id, member)
172 15137c54 Giorgos Verigakis
    return HttpResponse(status=204)
173 c34de90f Giorgos Verigakis
174 c34de90f Giorgos Verigakis
175 c34de90f Giorgos Verigakis
@plankton_method('GET')
176 15137c54 Giorgos Verigakis
def get_image(request, image_id):
177 15137c54 Giorgos Verigakis
    """Retrieve a virtual machine image
178 15137c54 Giorgos Verigakis
    
179 15137c54 Giorgos Verigakis
    Described in:
180 15137c54 Giorgos Verigakis
    3.5. Retrieving a Virtual Machine Image
181 15137c54 Giorgos Verigakis
    
182 15137c54 Giorgos Verigakis
    Implementation notes:
183 15137c54 Giorgos Verigakis
      * The implementation is very inefficient as it loads the whole image
184 15137c54 Giorgos Verigakis
        in memory.
185 15137c54 Giorgos Verigakis
    """
186 c34de90f Giorgos Verigakis
    
187 89b2e9b8 Giorgos Verigakis
    #image = request.backend.get_image(image_id)
188 f5afd99b Giorgos Verigakis
    #if not image:
189 f5afd99b Giorgos Verigakis
    #    return HttpResponseNotFound()
190 f5afd99b Giorgos Verigakis
    #
191 f5afd99b Giorgos Verigakis
    #response = _create_image_response(image)
192 f5afd99b Giorgos Verigakis
    #data = request.backend.get_data(image)
193 f5afd99b Giorgos Verigakis
    #response.content = data
194 f5afd99b Giorgos Verigakis
    #response['Content-Length'] = len(data)
195 f5afd99b Giorgos Verigakis
    #response['Content-Type'] = 'application/octet-stream'
196 f5afd99b Giorgos Verigakis
    #response['ETag'] = image['checksum']
197 f5afd99b Giorgos Verigakis
    #return response
198 f5afd99b Giorgos Verigakis
    return HttpResponse(status=501)     # Not Implemented
199 c34de90f Giorgos Verigakis
200 c34de90f Giorgos Verigakis
201 04ba5fd4 Giorgos Verigakis
@plankton_method('HEAD')
202 15137c54 Giorgos Verigakis
def get_image_meta(request, image_id):
203 15137c54 Giorgos Verigakis
    """Return detailed metadata on a specific image
204 15137c54 Giorgos Verigakis
    
205 15137c54 Giorgos Verigakis
    Described in:
206 15137c54 Giorgos Verigakis
    3.4. Requesting Detailed Metadata on a Specific Image
207 15137c54 Giorgos Verigakis
    """
208 04ba5fd4 Giorgos Verigakis
209 89b2e9b8 Giorgos Verigakis
    image = request.backend.get_image(image_id)
210 04ba5fd4 Giorgos Verigakis
    if not image:
211 04ba5fd4 Giorgos Verigakis
        return HttpResponseNotFound()
212 15137c54 Giorgos Verigakis
    return _create_image_response(image)
213 15137c54 Giorgos Verigakis
214 15137c54 Giorgos Verigakis
215 15137c54 Giorgos Verigakis
@plankton_method('GET')
216 15137c54 Giorgos Verigakis
def list_image_members(request, image_id):
217 15137c54 Giorgos Verigakis
    """List image memberships
218 15137c54 Giorgos Verigakis

219 15137c54 Giorgos Verigakis
    Described in:
220 15137c54 Giorgos Verigakis
    3.7. Requesting Image Memberships
221 15137c54 Giorgos Verigakis
    """
222 15137c54 Giorgos Verigakis
    
223 15137c54 Giorgos Verigakis
    members = [{'member_id': user, 'can_share': False}
224 15137c54 Giorgos Verigakis
                for user in request.backend.list_users(image_id)]
225 15137c54 Giorgos Verigakis
    data = json.dumps({'members': members}, indent=settings.DEBUG)
226 15137c54 Giorgos Verigakis
    return HttpResponse(data)
227 04ba5fd4 Giorgos Verigakis
228 04ba5fd4 Giorgos Verigakis
229 04ba5fd4 Giorgos Verigakis
@plankton_method('GET')
230 15137c54 Giorgos Verigakis
def list_public_images(request, detail=False):
231 15137c54 Giorgos Verigakis
    """Return a list of public VM images.
232 15137c54 Giorgos Verigakis
    
233 15137c54 Giorgos Verigakis
    Described in:
234 15137c54 Giorgos Verigakis
    3.1. Requesting a List of Public VM Images
235 15137c54 Giorgos Verigakis
    3.2. Requesting Detailed Metadata on Public VM Images
236 15137c54 Giorgos Verigakis
    3.3. Filtering Images Returned via GET /images andGET /images/detail
237 15137c54 Giorgos Verigakis
    
238 15137c54 Giorgos Verigakis
    Extensions:
239 15137c54 Giorgos Verigakis
      * Image ID is returned in both compact and detail listings
240 15137c54 Giorgos Verigakis
    """
241 04ba5fd4 Giorgos Verigakis
242 04ba5fd4 Giorgos Verigakis
    def get_request_params(keys):
243 04ba5fd4 Giorgos Verigakis
        params = {}
244 04ba5fd4 Giorgos Verigakis
        for key in keys:
245 04ba5fd4 Giorgos Verigakis
            val = request.GET.get(key, None)
246 04ba5fd4 Giorgos Verigakis
            if val is not None:
247 04ba5fd4 Giorgos Verigakis
                params[key] = val
248 04ba5fd4 Giorgos Verigakis
        return params
249 04ba5fd4 Giorgos Verigakis
250 15137c54 Giorgos Verigakis
    log.debug('list_public_images detail=%s', detail)
251 04ba5fd4 Giorgos Verigakis
252 04ba5fd4 Giorgos Verigakis
    filters = get_request_params(FILTERS)
253 04ba5fd4 Giorgos Verigakis
    params = get_request_params(PARAMS)
254 04ba5fd4 Giorgos Verigakis
255 04ba5fd4 Giorgos Verigakis
    params.setdefault('sort_key', 'created_at')
256 04ba5fd4 Giorgos Verigakis
    params.setdefault('sort_dir', 'desc')
257 04ba5fd4 Giorgos Verigakis
258 04ba5fd4 Giorgos Verigakis
    assert params['sort_key'] in SORT_KEY_OPTIONS
259 04ba5fd4 Giorgos Verigakis
    assert params['sort_dir'] in SORT_DIR_OPTIONS
260 04ba5fd4 Giorgos Verigakis
261 7bd1d3b5 Giorgos Verigakis
    images = request.backend.list_public(filters, params)
262 c34de90f Giorgos Verigakis
    
263 04ba5fd4 Giorgos Verigakis
    # Remove keys that should not be returned
264 04ba5fd4 Giorgos Verigakis
    fields = DETAIL_FIELDS if detail else LIST_FIELDS
265 04ba5fd4 Giorgos Verigakis
    for image in images:
266 04ba5fd4 Giorgos Verigakis
        for key in image.keys():
267 04ba5fd4 Giorgos Verigakis
            if key not in fields:
268 04ba5fd4 Giorgos Verigakis
                del image[key]
269 04ba5fd4 Giorgos Verigakis
270 04ba5fd4 Giorgos Verigakis
    data = json.dumps(images, indent=settings.DEBUG)
271 04ba5fd4 Giorgos Verigakis
    return HttpResponse(data)
272 04ba5fd4 Giorgos Verigakis
273 04ba5fd4 Giorgos Verigakis
274 15137c54 Giorgos Verigakis
@plankton_method('GET')
275 15137c54 Giorgos Verigakis
def list_shared_images(request, member):
276 15137c54 Giorgos Verigakis
    """Request shared images
277 15137c54 Giorgos Verigakis
    
278 15137c54 Giorgos Verigakis
    Described in:
279 15137c54 Giorgos Verigakis
    3.8. Requesting Shared Images
280 15137c54 Giorgos Verigakis
    
281 15137c54 Giorgos Verigakis
    Implementation notes:
282 8482ef55 Giorgos Verigakis
      * It is not clear what this method should do. We return the IDs of
283 8482ef55 Giorgos Verigakis
        the users's images that are accessible by `member`.
284 15137c54 Giorgos Verigakis
    """
285 15137c54 Giorgos Verigakis
    
286 15137c54 Giorgos Verigakis
    log.debug('list_shared_images %s', member)
287 15137c54 Giorgos Verigakis
    
288 15137c54 Giorgos Verigakis
    images = []
289 8482ef55 Giorgos Verigakis
    for image_id in request.backend.iter_shared(member):
290 8482ef55 Giorgos Verigakis
        images.append({'image_id': image_id, 'can_share': False})
291 15137c54 Giorgos Verigakis
    
292 15137c54 Giorgos Verigakis
    data = json.dumps({'shared_images': images}, indent=settings.DEBUG)
293 15137c54 Giorgos Verigakis
    return HttpResponse(data)
294 15137c54 Giorgos Verigakis
295 15137c54 Giorgos Verigakis
296 15137c54 Giorgos Verigakis
@plankton_method('DELETE')
297 15137c54 Giorgos Verigakis
def remove_image_member(request, image_id, member):
298 15137c54 Giorgos Verigakis
    """Remove a member from an image
299 15137c54 Giorgos Verigakis

300 15137c54 Giorgos Verigakis
    Described in:
301 f5afd99b Giorgos Verigakis
    3.10. Removing a Member from an Image
302 15137c54 Giorgos Verigakis
    """
303 15137c54 Giorgos Verigakis
304 15137c54 Giorgos Verigakis
    log.debug('remove_image_member %s %s', image_id, member)
305 15137c54 Giorgos Verigakis
    request.backend.remove_user(image_id, member)
306 15137c54 Giorgos Verigakis
    return HttpResponse(status=204)
307 15137c54 Giorgos Verigakis
308 15137c54 Giorgos Verigakis
309 04ba5fd4 Giorgos Verigakis
@plankton_method('PUT')
310 15137c54 Giorgos Verigakis
def update_image(request, image_id):
311 15137c54 Giorgos Verigakis
    """Update an image
312 15137c54 Giorgos Verigakis
    
313 15137c54 Giorgos Verigakis
    Described in:
314 15137c54 Giorgos Verigakis
    3.6.2. Updating an Image
315 c34de90f Giorgos Verigakis
    
316 15137c54 Giorgos Verigakis
    Implementation notes:
317 15137c54 Giorgos Verigakis
      * It is not clear which metadata are allowed to be updated. We support:
318 15137c54 Giorgos Verigakis
        name, disk_format, container_format, is_public, owner, properties
319 15137c54 Giorgos Verigakis
        and status.
320 15137c54 Giorgos Verigakis
    """
321 15137c54 Giorgos Verigakis
    
322 15137c54 Giorgos Verigakis
    meta = _get_image_headers(request)
323 15137c54 Giorgos Verigakis
    log.debug('update_image %s', meta)
324 7e00b33e Giorgos Verigakis
    
325 7e00b33e Giorgos Verigakis
    assert set(meta.keys()).issubset(set(UPDATE_FIELDS))
326 c34de90f Giorgos Verigakis
    
327 f5afd99b Giorgos Verigakis
    image = request.backend.update(image_id, meta)
328 15137c54 Giorgos Verigakis
    return _create_image_response(image)
329 15137c54 Giorgos Verigakis
330 15137c54 Giorgos Verigakis
331 15137c54 Giorgos Verigakis
@plankton_method('PUT')
332 15137c54 Giorgos Verigakis
def update_image_members(request, image_id):
333 15137c54 Giorgos Verigakis
    """Replace a membership list for an image
334 7e00b33e Giorgos Verigakis
    
335 15137c54 Giorgos Verigakis
    Described in:
336 15137c54 Giorgos Verigakis
    3.11. Replacing a Membership List for an Image
337 15137c54 Giorgos Verigakis
    
338 15137c54 Giorgos Verigakis
    Limitations:
339 15137c54 Giorgos Verigakis
      * can_share value is ignored
340 15137c54 Giorgos Verigakis
    """
341 15137c54 Giorgos Verigakis
    
342 15137c54 Giorgos Verigakis
    log.debug('update_image_members %s', image_id)
343 15137c54 Giorgos Verigakis
    members = []
344 15137c54 Giorgos Verigakis
    try:
345 15137c54 Giorgos Verigakis
        data = json.loads(request.raw_post_data)
346 15137c54 Giorgos Verigakis
        for member in data['memberships']:
347 15137c54 Giorgos Verigakis
            members.append(member['member_id'])
348 15137c54 Giorgos Verigakis
    except (ValueError, KeyError, TypeError):
349 15137c54 Giorgos Verigakis
        return HttpResponse(status=400)
350 15137c54 Giorgos Verigakis
    
351 15137c54 Giorgos Verigakis
    request.backend.replace_users(image_id, members)
352 15137c54 Giorgos Verigakis
    return HttpResponse(status=204)