Statistics
| Branch: | Tag: | Revision:

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

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

128 15137c54 Giorgos Verigakis
    Described in:
129 15137c54 Giorgos Verigakis
    3.6. Adding a New Virtual Machine Image
130 29490fca Christos Stavrakakis

131 15137c54 Giorgos Verigakis
    Implementation notes:
132 15137c54 Giorgos Verigakis
      * The implementation is very inefficient as it loads the whole image
133 15137c54 Giorgos Verigakis
        in memory.
134 29490fca Christos Stavrakakis

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

138 15137c54 Giorgos Verigakis
    Extensions:
139 15137c54 Giorgos Verigakis
      * An x-image-meta-location header can be passed with a link to file,
140 15137c54 Giorgos Verigakis
        instead of uploading the data.
141 15137c54 Giorgos Verigakis
    """
142 29490fca Christos Stavrakakis
143 15137c54 Giorgos Verigakis
    params = _get_image_headers(request)
144 15137c54 Giorgos Verigakis
    log.debug('add_image %s', params)
145 29490fca Christos Stavrakakis
146 78fa9134 Christos Stavrakakis
    if not set(params.keys()).issubset(set(ADD_FIELDS)):
147 78fa9134 Christos Stavrakakis
        raise faults.BadRequest("Invalid parameters")
148 29490fca Christos Stavrakakis
149 7bd1d3b5 Giorgos Verigakis
    name = params.pop('name')
150 78fa9134 Christos Stavrakakis
    if name is None:
151 78fa9134 Christos Stavrakakis
        raise faults.BadRequest("Image 'name' parameter is required")
152 78fa9134 Christos Stavrakakis
    elif len(uenc(name)) == 0:
153 1a9aa726 Dionysis Grigoropoulos
        raise faults.BadRequest("Invalid image name")
154 f5afd99b Giorgos Verigakis
    location = params.pop('location', None)
155 78fa9134 Christos Stavrakakis
    if location is None:
156 78fa9134 Christos Stavrakakis
        raise faults.BadRequest("'location' parameter is required")
157 78fa9134 Christos Stavrakakis
158 59573532 Christos Stavrakakis
    try:
159 59573532 Christos Stavrakakis
        split_url(location)
160 78fa9134 Christos Stavrakakis
    except InvalidLocation:
161 59573532 Christos Stavrakakis
        raise faults.BadRequest("Invalid location '%s'" % location)
162 29490fca Christos Stavrakakis
163 f5afd99b Giorgos Verigakis
    if location:
164 cda71050 Christos Stavrakakis
        with image_backend(request.user_uniq) as backend:
165 cda71050 Christos Stavrakakis
            image = backend.register(name, location, params)
166 04ba5fd4 Giorgos Verigakis
    else:
167 b694875c Christos Stavrakakis
        #f = StringIO(request.body)
168 cda71050 Christos Stavrakakis
        #image = backend.put(name, f, params)
169 f5afd99b Giorgos Verigakis
        return HttpResponse(status=501)     # Not Implemented
170 29490fca Christos Stavrakakis
171 04ba5fd4 Giorgos Verigakis
    if not image:
172 0a72907b Giorgos Verigakis
        return HttpResponse('Registration failed', status=500)
173 29490fca Christos Stavrakakis
174 15137c54 Giorgos Verigakis
    return _create_image_response(image)
175 15137c54 Giorgos Verigakis
176 15137c54 Giorgos Verigakis
177 d9d1763e Christos Stavrakakis
@api.api_method(http_method="DELETE", user_required=True, logger=log)
178 f13aab5d Christos Stavrakakis
def delete_image(request, image_id):
179 f13aab5d Christos Stavrakakis
    """Delete an Image.
180 f13aab5d Christos Stavrakakis

181 f13aab5d Christos Stavrakakis
    This API call is not described in the Openstack Glance API.
182 f13aab5d Christos Stavrakakis

183 f13aab5d Christos Stavrakakis
    Implementation notes:
184 f13aab5d Christos Stavrakakis
      * The implementation does not delete the Image from the storage
185 f13aab5d Christos Stavrakakis
        backend. Instead it unregisters the image by removing all the
186 f13aab5d Christos Stavrakakis
        metadata from the plankton metadata domain.
187 f13aab5d Christos Stavrakakis

188 f13aab5d Christos Stavrakakis
    """
189 f13aab5d Christos Stavrakakis
    log.info("delete_image '%s'" % image_id)
190 f13aab5d Christos Stavrakakis
    userid = request.user_uniq
191 cda71050 Christos Stavrakakis
    with image_backend(userid) as backend:
192 cda71050 Christos Stavrakakis
        backend.unregister(image_id)
193 f13aab5d Christos Stavrakakis
    log.info("User '%s' deleted image '%s'" % (userid, image_id))
194 f13aab5d Christos Stavrakakis
    return HttpResponse(status=204)
195 f13aab5d Christos Stavrakakis
196 f13aab5d Christos Stavrakakis
197 d9d1763e Christos Stavrakakis
@api.api_method(http_method="PUT", user_required=True, logger=log)
198 15137c54 Giorgos Verigakis
def add_image_member(request, image_id, member):
199 15137c54 Giorgos Verigakis
    """Add a member to an image
200 29490fca Christos Stavrakakis

201 15137c54 Giorgos Verigakis
    Described in:
202 15137c54 Giorgos Verigakis
    3.9. Adding a Member to an Image
203 29490fca Christos Stavrakakis

204 15137c54 Giorgos Verigakis
    Limitations:
205 15137c54 Giorgos Verigakis
      * Passing a body to enable `can_share` is not supported.
206 15137c54 Giorgos Verigakis
    """
207 29490fca Christos Stavrakakis
208 15137c54 Giorgos Verigakis
    log.debug('add_image_member %s %s', image_id, member)
209 cda71050 Christos Stavrakakis
    with image_backend(request.user_uniq) as backend:
210 cda71050 Christos Stavrakakis
        backend.add_user(image_id, member)
211 15137c54 Giorgos Verigakis
    return HttpResponse(status=204)
212 c34de90f Giorgos Verigakis
213 c34de90f Giorgos Verigakis
214 d9d1763e Christos Stavrakakis
@api.api_method(http_method="GET", user_required=True, logger=log)
215 15137c54 Giorgos Verigakis
def get_image(request, image_id):
216 15137c54 Giorgos Verigakis
    """Retrieve a virtual machine image
217 29490fca Christos Stavrakakis

218 15137c54 Giorgos Verigakis
    Described in:
219 15137c54 Giorgos Verigakis
    3.5. Retrieving a Virtual Machine Image
220 29490fca Christos Stavrakakis

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

244 15137c54 Giorgos Verigakis
    Described in:
245 15137c54 Giorgos Verigakis
    3.4. Requesting Detailed Metadata on a Specific Image
246 15137c54 Giorgos Verigakis
    """
247 04ba5fd4 Giorgos Verigakis
248 cda71050 Christos Stavrakakis
    with image_backend(request.user_uniq) as backend:
249 cda71050 Christos Stavrakakis
        image = backend.get_image(image_id)
250 15137c54 Giorgos Verigakis
    return _create_image_response(image)
251 15137c54 Giorgos Verigakis
252 15137c54 Giorgos Verigakis
253 d9d1763e Christos Stavrakakis
@api.api_method(http_method="GET", user_required=True, logger=log)
254 15137c54 Giorgos Verigakis
def list_image_members(request, image_id):
255 15137c54 Giorgos Verigakis
    """List image memberships
256 15137c54 Giorgos Verigakis

257 15137c54 Giorgos Verigakis
    Described in:
258 15137c54 Giorgos Verigakis
    3.7. Requesting Image Memberships
259 15137c54 Giorgos Verigakis
    """
260 29490fca Christos Stavrakakis
261 cda71050 Christos Stavrakakis
    with image_backend(request.user_uniq) as backend:
262 cda71050 Christos Stavrakakis
        users = backend.list_users(image_id)
263 cda71050 Christos Stavrakakis
264 cda71050 Christos Stavrakakis
    members = [{'member_id': u, 'can_share': False} for u in users]
265 15137c54 Giorgos Verigakis
    data = json.dumps({'members': members}, indent=settings.DEBUG)
266 15137c54 Giorgos Verigakis
    return HttpResponse(data)
267 04ba5fd4 Giorgos Verigakis
268 04ba5fd4 Giorgos Verigakis
269 d9d1763e Christos Stavrakakis
@api.api_method(http_method="GET", user_required=True, logger=log)
270 b2f94a0c Christos Stavrakakis
def list_images(request, detail=False):
271 b2f94a0c Christos Stavrakakis
    """Return a list of available images.
272 29490fca Christos Stavrakakis

273 b2f94a0c Christos Stavrakakis
    This includes images owned by the user, images shared with the user and
274 b2f94a0c Christos Stavrakakis
    public images.
275 29490fca Christos Stavrakakis

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

329 15137c54 Giorgos Verigakis
    Described in:
330 15137c54 Giorgos Verigakis
    3.8. Requesting Shared Images
331 29490fca Christos Stavrakakis

332 15137c54 Giorgos Verigakis
    Implementation notes:
333 8482ef55 Giorgos Verigakis
      * It is not clear what this method should do. We return the IDs of
334 8482ef55 Giorgos Verigakis
        the users's images that are accessible by `member`.
335 15137c54 Giorgos Verigakis
    """
336 29490fca Christos Stavrakakis
337 b2f94a0c Christos Stavrakakis
    log.debug('list_shared_images %s', member)
338 29490fca Christos Stavrakakis
339 15137c54 Giorgos Verigakis
    images = []
340 cda71050 Christos Stavrakakis
    with image_backend(request.user_uniq) as backend:
341 14c94c48 Christos Stavrakakis
        for image in backend.list_shared_images(member=member):
342 cda71050 Christos Stavrakakis
            image_id = image['id']
343 cda71050 Christos Stavrakakis
            images.append({'image_id': image_id, 'can_share': False})
344 29490fca Christos Stavrakakis
345 15137c54 Giorgos Verigakis
    data = json.dumps({'shared_images': images}, indent=settings.DEBUG)
346 15137c54 Giorgos Verigakis
    return HttpResponse(data)
347 15137c54 Giorgos Verigakis
348 15137c54 Giorgos Verigakis
349 d9d1763e Christos Stavrakakis
@api.api_method(http_method="DELETE", user_required=True, logger=log)
350 15137c54 Giorgos Verigakis
def remove_image_member(request, image_id, member):
351 15137c54 Giorgos Verigakis
    """Remove a member from an image
352 15137c54 Giorgos Verigakis

353 15137c54 Giorgos Verigakis
    Described in:
354 f5afd99b Giorgos Verigakis
    3.10. Removing a Member from an Image
355 15137c54 Giorgos Verigakis
    """
356 15137c54 Giorgos Verigakis
357 15137c54 Giorgos Verigakis
    log.debug('remove_image_member %s %s', image_id, member)
358 cda71050 Christos Stavrakakis
    with image_backend(request.user_uniq) as backend:
359 cda71050 Christos Stavrakakis
        backend.remove_user(image_id, member)
360 15137c54 Giorgos Verigakis
    return HttpResponse(status=204)
361 15137c54 Giorgos Verigakis
362 15137c54 Giorgos Verigakis
363 d9d1763e Christos Stavrakakis
@api.api_method(http_method="PUT", user_required=True, logger=log)
364 15137c54 Giorgos Verigakis
def update_image(request, image_id):
365 15137c54 Giorgos Verigakis
    """Update an image
366 29490fca Christos Stavrakakis

367 15137c54 Giorgos Verigakis
    Described in:
368 15137c54 Giorgos Verigakis
    3.6.2. Updating an Image
369 29490fca Christos Stavrakakis

370 15137c54 Giorgos Verigakis
    Implementation notes:
371 15137c54 Giorgos Verigakis
      * It is not clear which metadata are allowed to be updated. We support:
372 15137c54 Giorgos Verigakis
        name, disk_format, container_format, is_public, owner, properties
373 15137c54 Giorgos Verigakis
        and status.
374 15137c54 Giorgos Verigakis
    """
375 29490fca Christos Stavrakakis
376 15137c54 Giorgos Verigakis
    meta = _get_image_headers(request)
377 15137c54 Giorgos Verigakis
    log.debug('update_image %s', meta)
378 29490fca Christos Stavrakakis
379 78fa9134 Christos Stavrakakis
    if not set(meta.keys()).issubset(set(UPDATE_FIELDS)):
380 78fa9134 Christos Stavrakakis
        raise faults.BadRequest("Invalid metadata")
381 29490fca Christos Stavrakakis
382 cda71050 Christos Stavrakakis
    with image_backend(request.user_uniq) as backend:
383 cda71050 Christos Stavrakakis
        image = backend.update_metadata(image_id, meta)
384 15137c54 Giorgos Verigakis
    return _create_image_response(image)
385 15137c54 Giorgos Verigakis
386 15137c54 Giorgos Verigakis
387 d9d1763e Christos Stavrakakis
@api.api_method(http_method="PUT", user_required=True, logger=log)
388 15137c54 Giorgos Verigakis
def update_image_members(request, image_id):
389 15137c54 Giorgos Verigakis
    """Replace a membership list for an image
390 29490fca Christos Stavrakakis

391 15137c54 Giorgos Verigakis
    Described in:
392 15137c54 Giorgos Verigakis
    3.11. Replacing a Membership List for an Image
393 29490fca Christos Stavrakakis

394 15137c54 Giorgos Verigakis
    Limitations:
395 15137c54 Giorgos Verigakis
      * can_share value is ignored
396 15137c54 Giorgos Verigakis
    """
397 29490fca Christos Stavrakakis
398 15137c54 Giorgos Verigakis
    log.debug('update_image_members %s', image_id)
399 15137c54 Giorgos Verigakis
    members = []
400 15137c54 Giorgos Verigakis
    try:
401 b694875c Christos Stavrakakis
        data = json.loads(request.body)
402 15137c54 Giorgos Verigakis
        for member in data['memberships']:
403 15137c54 Giorgos Verigakis
            members.append(member['member_id'])
404 15137c54 Giorgos Verigakis
    except (ValueError, KeyError, TypeError):
405 15137c54 Giorgos Verigakis
        return HttpResponse(status=400)
406 29490fca Christos Stavrakakis
407 cda71050 Christos Stavrakakis
    with image_backend(request.user_uniq) as backend:
408 cda71050 Christos Stavrakakis
        backend.replace_users(image_id, members)
409 15137c54 Giorgos Verigakis
    return HttpResponse(status=204)