Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (13.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 df455d57 Christos Stavrakakis
from django.utils.encoding import smart_unicode
43 d9d1763e Christos Stavrakakis
44 d9d1763e Christos Stavrakakis
from snf_django.lib import api
45 d2b8ec7b Christos Stavrakakis
from snf_django.lib.api import faults
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 04ba5fd4 Giorgos Verigakis
                 'status', 'is_public', 'owner', 'properties', 'id')
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 54d26a27 Dionysis Grigoropoulos
DISK_FORMATS = ('diskdump', 'extdump', 'ntfsdump')
74 54d26a27 Dionysis Grigoropoulos
75 54d26a27 Dionysis Grigoropoulos
CONTAINER_FORMATS = ('aki', 'ari', 'ami', 'bare', 'ovf')
76 54d26a27 Dionysis Grigoropoulos
77 54d26a27 Dionysis Grigoropoulos
STORE_TYPES = ('pithos')
78 54d26a27 Dionysis Grigoropoulos
79 c34de90f Giorgos Verigakis
80 15137c54 Giorgos Verigakis
log = getLogger('synnefo.plankton')
81 c34de90f Giorgos Verigakis
82 c34de90f Giorgos Verigakis
83 15137c54 Giorgos Verigakis
def _create_image_response(image):
84 04ba5fd4 Giorgos Verigakis
    response = HttpResponse()
85 29490fca Christos Stavrakakis
86 04ba5fd4 Giorgos Verigakis
    for key in DETAIL_FIELDS:
87 04ba5fd4 Giorgos Verigakis
        if key == 'properties':
88 04ba5fd4 Giorgos Verigakis
            for k, v in image.get('properties', {}).items():
89 04ba5fd4 Giorgos Verigakis
                name = 'x-image-meta-property-' + k.replace('_', '-')
90 df455d57 Christos Stavrakakis
                response[name] = smart_unicode(v, encoding="utf-8")
91 04ba5fd4 Giorgos Verigakis
        else:
92 04ba5fd4 Giorgos Verigakis
            name = 'x-image-meta-' + key.replace('_', '-')
93 df455d57 Christos Stavrakakis
            response[name] = smart_unicode(image.get(key, ''), encoding="utf-8")
94 29490fca Christos Stavrakakis
95 04ba5fd4 Giorgos Verigakis
    return response
96 04ba5fd4 Giorgos Verigakis
97 04ba5fd4 Giorgos Verigakis
98 15137c54 Giorgos Verigakis
def _get_image_headers(request):
99 04ba5fd4 Giorgos Verigakis
    def normalize(s):
100 04ba5fd4 Giorgos Verigakis
        return ''.join('_' if c in punctuation else c.lower() for c in s)
101 29490fca Christos Stavrakakis
102 04ba5fd4 Giorgos Verigakis
    META_PREFIX = 'HTTP_X_IMAGE_META_'
103 04ba5fd4 Giorgos Verigakis
    META_PREFIX_LEN = len(META_PREFIX)
104 04ba5fd4 Giorgos Verigakis
    META_PROPERTY_PREFIX = 'HTTP_X_IMAGE_META_PROPERTY_'
105 04ba5fd4 Giorgos Verigakis
    META_PROPERTY_PREFIX_LEN = len(META_PROPERTY_PREFIX)
106 29490fca Christos Stavrakakis
107 04ba5fd4 Giorgos Verigakis
    headers = {'properties': {}}
108 29490fca Christos Stavrakakis
109 04ba5fd4 Giorgos Verigakis
    for key, val in request.META.items():
110 04ba5fd4 Giorgos Verigakis
        if key.startswith(META_PROPERTY_PREFIX):
111 04ba5fd4 Giorgos Verigakis
            name = normalize(key[META_PROPERTY_PREFIX_LEN:])
112 4ec83e68 Giorgos Verigakis
            headers['properties'][unquote(name)] = unquote(val)
113 04ba5fd4 Giorgos Verigakis
        elif key.startswith(META_PREFIX):
114 04ba5fd4 Giorgos Verigakis
            name = normalize(key[META_PREFIX_LEN:])
115 4ec83e68 Giorgos Verigakis
            headers[unquote(name)] = unquote(val)
116 29490fca Christos Stavrakakis
117 7e00b33e Giorgos Verigakis
    is_public = headers.get('is_public', None)
118 7e00b33e Giorgos Verigakis
    if is_public is not None:
119 7e00b33e Giorgos Verigakis
        headers['is_public'] = True if is_public.lower() == 'true' else False
120 29490fca Christos Stavrakakis
121 7e00b33e Giorgos Verigakis
    if not headers['properties']:
122 7e00b33e Giorgos Verigakis
        del headers['properties']
123 29490fca Christos Stavrakakis
124 04ba5fd4 Giorgos Verigakis
    return headers
125 c34de90f Giorgos Verigakis
126 c34de90f Giorgos Verigakis
127 d9d1763e Christos Stavrakakis
@api.api_method(http_method="POST", user_required=True, logger=log)
128 15137c54 Giorgos Verigakis
def add_image(request):
129 15137c54 Giorgos Verigakis
    """Add a new virtual machine image
130 29490fca Christos Stavrakakis

131 15137c54 Giorgos Verigakis
    Described in:
132 15137c54 Giorgos Verigakis
    3.6. Adding a New Virtual Machine Image
133 29490fca Christos Stavrakakis

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

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

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

186 f13aab5d Christos Stavrakakis
    This API call is not described in the Openstack Glance API.
187 f13aab5d Christos Stavrakakis

188 f13aab5d Christos Stavrakakis
    Implementation notes:
189 f13aab5d Christos Stavrakakis
      * The implementation does not delete the Image from the storage
190 f13aab5d Christos Stavrakakis
        backend. Instead it unregisters the image by removing all the
191 f13aab5d Christos Stavrakakis
        metadata from the plankton metadata domain.
192 f13aab5d Christos Stavrakakis

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

206 15137c54 Giorgos Verigakis
    Described in:
207 15137c54 Giorgos Verigakis
    3.9. Adding a Member to an Image
208 29490fca Christos Stavrakakis

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

223 15137c54 Giorgos Verigakis
    Described in:
224 15137c54 Giorgos Verigakis
    3.5. Retrieving a Virtual Machine Image
225 29490fca Christos Stavrakakis

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

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

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

278 b2f94a0c Christos Stavrakakis
    This includes images owned by the user, images shared with the user and
279 b2f94a0c Christos Stavrakakis
    public images.
280 29490fca Christos Stavrakakis

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

334 15137c54 Giorgos Verigakis
    Described in:
335 15137c54 Giorgos Verigakis
    3.8. Requesting Shared Images
336 29490fca Christos Stavrakakis

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

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

372 15137c54 Giorgos Verigakis
    Described in:
373 15137c54 Giorgos Verigakis
    3.6.2. Updating an Image
374 29490fca Christos Stavrakakis

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

398 15137c54 Giorgos Verigakis
    Described in:
399 15137c54 Giorgos Verigakis
    3.11. Replacing a Membership List for an Image
400 29490fca Christos Stavrakakis

401 15137c54 Giorgos Verigakis
    Limitations:
402 15137c54 Giorgos Verigakis
      * can_share value is ignored
403 15137c54 Giorgos Verigakis
    """
404 29490fca Christos Stavrakakis
405 15137c54 Giorgos Verigakis
    log.debug('update_image_members %s', image_id)
406 5a6cee09 Christos Stavrakakis
    data = api.utils.get_json_body(request)
407 15137c54 Giorgos Verigakis
    members = []
408 5a6cee09 Christos Stavrakakis
409 5a6cee09 Christos Stavrakakis
    memberships = api.utils.get_attribute(data, "memberships", attr_type=list)
410 5a6cee09 Christos Stavrakakis
    for member in memberships:
411 5a6cee09 Christos Stavrakakis
        if not isinstance(member, dict):
412 5a6cee09 Christos Stavrakakis
            raise faults.BadRequest("Invalid 'memberships' field")
413 5a6cee09 Christos Stavrakakis
        member = api.utils.get_attribute(member, "member_id")
414 5a6cee09 Christos Stavrakakis
        members.append(member)
415 29490fca Christos Stavrakakis
416 cda71050 Christos Stavrakakis
    with image_backend(request.user_uniq) as backend:
417 cda71050 Christos Stavrakakis
        backend.replace_users(image_id, members)
418 15137c54 Giorgos Verigakis
    return HttpResponse(status=204)
419 54d26a27 Dionysis Grigoropoulos
420 54d26a27 Dionysis Grigoropoulos
421 54d26a27 Dionysis Grigoropoulos
def validate_fields(params):
422 54d26a27 Dionysis Grigoropoulos
    if "id" in params:
423 54d26a27 Dionysis Grigoropoulos
        raise faults.BadRequest("Setting the image ID is not supported")
424 54d26a27 Dionysis Grigoropoulos
425 54d26a27 Dionysis Grigoropoulos
    if "store" in params:
426 54d26a27 Dionysis Grigoropoulos
        if params["store"] not in STORE_TYPES:
427 54d26a27 Dionysis Grigoropoulos
            raise faults.BadRequest("Invalid store type '%s'" %
428 54d26a27 Dionysis Grigoropoulos
                                    params["store"])
429 54d26a27 Dionysis Grigoropoulos
430 54d26a27 Dionysis Grigoropoulos
    if "disk_format" in params:
431 54d26a27 Dionysis Grigoropoulos
        if params["disk_format"] not in DISK_FORMATS:
432 54d26a27 Dionysis Grigoropoulos
            raise faults.BadRequest("Invalid disk format '%s'" %
433 54d26a27 Dionysis Grigoropoulos
                                    params['disk_format'])
434 54d26a27 Dionysis Grigoropoulos
435 54d26a27 Dionysis Grigoropoulos
    if "container_format" in params:
436 54d26a27 Dionysis Grigoropoulos
        if params["container_format"] not in CONTAINER_FORMATS:
437 54d26a27 Dionysis Grigoropoulos
            raise faults.BadRequest("Invalid container format '%s'" %
438 54d26a27 Dionysis Grigoropoulos
                                    params['container_format'])