Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (12.2 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 cda71050 Christos Stavrakakis
from synnefo.plankton.utils import image_backend
46 59573532 Christos Stavrakakis
from synnefo.plankton.backend import split_url
47 c34de90f Giorgos Verigakis
48 c34de90f Giorgos Verigakis
49 04ba5fd4 Giorgos Verigakis
FILTERS = ('name', 'container_format', 'disk_format', 'status', 'size_min',
50 04ba5fd4 Giorgos Verigakis
           'size_max')
51 c34de90f Giorgos Verigakis
52 04ba5fd4 Giorgos Verigakis
PARAMS = ('sort_key', 'sort_dir')
53 c34de90f Giorgos Verigakis
54 04ba5fd4 Giorgos Verigakis
SORT_KEY_OPTIONS = ('id', 'name', 'status', 'size', 'disk_format',
55 04ba5fd4 Giorgos Verigakis
                    'container_format', 'created_at', 'updated_at')
56 04ba5fd4 Giorgos Verigakis
57 04ba5fd4 Giorgos Verigakis
SORT_DIR_OPTIONS = ('asc', 'desc')
58 04ba5fd4 Giorgos Verigakis
59 04ba5fd4 Giorgos Verigakis
LIST_FIELDS = ('status', 'name', 'disk_format', 'container_format', 'size',
60 04ba5fd4 Giorgos Verigakis
               'id')
61 04ba5fd4 Giorgos Verigakis
62 04ba5fd4 Giorgos Verigakis
DETAIL_FIELDS = ('name', 'disk_format', 'container_format', 'size', 'checksum',
63 04ba5fd4 Giorgos Verigakis
                 'location', 'created_at', 'updated_at', 'deleted_at',
64 04ba5fd4 Giorgos Verigakis
                 'status', 'is_public', 'owner', 'properties', 'id')
65 04ba5fd4 Giorgos Verigakis
66 04ba5fd4 Giorgos Verigakis
ADD_FIELDS = ('name', 'id', 'store', 'disk_format', 'container_format', 'size',
67 04ba5fd4 Giorgos Verigakis
              'checksum', 'is_public', 'owner', 'properties', 'location')
68 04ba5fd4 Giorgos Verigakis
69 04ba5fd4 Giorgos Verigakis
UPDATE_FIELDS = ('name', 'disk_format', 'container_format', 'is_public',
70 7e00b33e Giorgos Verigakis
                 'owner', 'properties', 'status')
71 c34de90f Giorgos Verigakis
72 c34de90f Giorgos Verigakis
73 15137c54 Giorgos Verigakis
log = getLogger('synnefo.plankton')
74 c34de90f Giorgos Verigakis
75 c34de90f Giorgos Verigakis
76 15137c54 Giorgos Verigakis
def _create_image_response(image):
77 04ba5fd4 Giorgos Verigakis
    response = HttpResponse()
78 29490fca Christos Stavrakakis
79 04ba5fd4 Giorgos Verigakis
    for key in DETAIL_FIELDS:
80 04ba5fd4 Giorgos Verigakis
        if key == 'properties':
81 04ba5fd4 Giorgos Verigakis
            for k, v in image.get('properties', {}).items():
82 04ba5fd4 Giorgos Verigakis
                name = 'x-image-meta-property-' + k.replace('_', '-')
83 04ba5fd4 Giorgos Verigakis
                response[name] = v
84 04ba5fd4 Giorgos Verigakis
        else:
85 04ba5fd4 Giorgos Verigakis
            name = 'x-image-meta-' + key.replace('_', '-')
86 04ba5fd4 Giorgos Verigakis
            response[name] = image.get(key, '')
87 29490fca Christos Stavrakakis
88 04ba5fd4 Giorgos Verigakis
    return response
89 04ba5fd4 Giorgos Verigakis
90 04ba5fd4 Giorgos Verigakis
91 15137c54 Giorgos Verigakis
def _get_image_headers(request):
92 04ba5fd4 Giorgos Verigakis
    def normalize(s):
93 04ba5fd4 Giorgos Verigakis
        return ''.join('_' if c in punctuation else c.lower() for c in s)
94 29490fca Christos Stavrakakis
95 04ba5fd4 Giorgos Verigakis
    META_PREFIX = 'HTTP_X_IMAGE_META_'
96 04ba5fd4 Giorgos Verigakis
    META_PREFIX_LEN = len(META_PREFIX)
97 04ba5fd4 Giorgos Verigakis
    META_PROPERTY_PREFIX = 'HTTP_X_IMAGE_META_PROPERTY_'
98 04ba5fd4 Giorgos Verigakis
    META_PROPERTY_PREFIX_LEN = len(META_PROPERTY_PREFIX)
99 29490fca Christos Stavrakakis
100 04ba5fd4 Giorgos Verigakis
    headers = {'properties': {}}
101 29490fca Christos Stavrakakis
102 04ba5fd4 Giorgos Verigakis
    for key, val in request.META.items():
103 04ba5fd4 Giorgos Verigakis
        if key.startswith(META_PROPERTY_PREFIX):
104 04ba5fd4 Giorgos Verigakis
            name = normalize(key[META_PROPERTY_PREFIX_LEN:])
105 4ec83e68 Giorgos Verigakis
            headers['properties'][unquote(name)] = unquote(val)
106 04ba5fd4 Giorgos Verigakis
        elif key.startswith(META_PREFIX):
107 04ba5fd4 Giorgos Verigakis
            name = normalize(key[META_PREFIX_LEN:])
108 4ec83e68 Giorgos Verigakis
            headers[unquote(name)] = unquote(val)
109 29490fca Christos Stavrakakis
110 7e00b33e Giorgos Verigakis
    is_public = headers.get('is_public', None)
111 7e00b33e Giorgos Verigakis
    if is_public is not None:
112 7e00b33e Giorgos Verigakis
        headers['is_public'] = True if is_public.lower() == 'true' else False
113 29490fca Christos Stavrakakis
114 7e00b33e Giorgos Verigakis
    if not headers['properties']:
115 7e00b33e Giorgos Verigakis
        del headers['properties']
116 29490fca Christos Stavrakakis
117 04ba5fd4 Giorgos Verigakis
    return headers
118 c34de90f Giorgos Verigakis
119 c34de90f Giorgos Verigakis
120 d9d1763e Christos Stavrakakis
@api.api_method(http_method="POST", user_required=True, logger=log)
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 59573532 Christos Stavrakakis
    try:
148 59573532 Christos Stavrakakis
        split_url(location)
149 59573532 Christos Stavrakakis
    except AssertionError:
150 59573532 Christos Stavrakakis
        raise faults.BadRequest("Invalid location '%s'" % location)
151 29490fca Christos Stavrakakis
152 f5afd99b Giorgos Verigakis
    if location:
153 cda71050 Christos Stavrakakis
        with image_backend(request.user_uniq) as backend:
154 cda71050 Christos Stavrakakis
            image = backend.register(name, location, params)
155 04ba5fd4 Giorgos Verigakis
    else:
156 b694875c Christos Stavrakakis
        #f = StringIO(request.body)
157 cda71050 Christos Stavrakakis
        #image = backend.put(name, f, params)
158 f5afd99b Giorgos Verigakis
        return HttpResponse(status=501)     # Not Implemented
159 29490fca Christos Stavrakakis
160 04ba5fd4 Giorgos Verigakis
    if not image:
161 0a72907b Giorgos Verigakis
        return HttpResponse('Registration failed', status=500)
162 29490fca Christos Stavrakakis
163 15137c54 Giorgos Verigakis
    return _create_image_response(image)
164 15137c54 Giorgos Verigakis
165 15137c54 Giorgos Verigakis
166 d9d1763e Christos Stavrakakis
@api.api_method(http_method="DELETE", user_required=True, logger=log)
167 f13aab5d Christos Stavrakakis
def delete_image(request, image_id):
168 f13aab5d Christos Stavrakakis
    """Delete an Image.
169 f13aab5d Christos Stavrakakis

170 f13aab5d Christos Stavrakakis
    This API call is not described in the Openstack Glance API.
171 f13aab5d Christos Stavrakakis

172 f13aab5d Christos Stavrakakis
    Implementation notes:
173 f13aab5d Christos Stavrakakis
      * The implementation does not delete the Image from the storage
174 f13aab5d Christos Stavrakakis
        backend. Instead it unregisters the image by removing all the
175 f13aab5d Christos Stavrakakis
        metadata from the plankton metadata domain.
176 f13aab5d Christos Stavrakakis

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

190 15137c54 Giorgos Verigakis
    Described in:
191 15137c54 Giorgos Verigakis
    3.9. Adding a Member to an Image
192 29490fca Christos Stavrakakis

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

207 15137c54 Giorgos Verigakis
    Described in:
208 15137c54 Giorgos Verigakis
    3.5. Retrieving a Virtual Machine Image
209 29490fca Christos Stavrakakis

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

233 15137c54 Giorgos Verigakis
    Described in:
234 15137c54 Giorgos Verigakis
    3.4. Requesting Detailed Metadata on a Specific Image
235 15137c54 Giorgos Verigakis
    """
236 04ba5fd4 Giorgos Verigakis
237 cda71050 Christos Stavrakakis
    with image_backend(request.user_uniq) as backend:
238 cda71050 Christos Stavrakakis
        image = backend.get_image(image_id)
239 15137c54 Giorgos Verigakis
    return _create_image_response(image)
240 15137c54 Giorgos Verigakis
241 15137c54 Giorgos Verigakis
242 d9d1763e Christos Stavrakakis
@api.api_method(http_method="GET", user_required=True, logger=log)
243 15137c54 Giorgos Verigakis
def list_image_members(request, image_id):
244 15137c54 Giorgos Verigakis
    """List image memberships
245 15137c54 Giorgos Verigakis

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

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

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

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

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

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

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

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

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

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