Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (14.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 a4e9c19c Christos Stavrakakis
from urllib import unquote, quote
39 c34de90f Giorgos Verigakis
40 c34de90f Giorgos Verigakis
from django.conf import settings
41 d2b8ec7b Christos Stavrakakis
from django.http import HttpResponse
42 4e571185 Dionysis Grigoropoulos
from django.utils.encoding import (smart_unicode, smart_str,
43 4e571185 Dionysis Grigoropoulos
                                   DjangoUnicodeDecodeError)
44 d9d1763e Christos Stavrakakis
45 d9d1763e Christos Stavrakakis
from snf_django.lib import api
46 d2b8ec7b Christos Stavrakakis
from snf_django.lib.api import faults
47 cda71050 Christos Stavrakakis
from synnefo.plankton.utils import image_backend
48 78fa9134 Christos Stavrakakis
from synnefo.plankton.backend import split_url, InvalidLocation
49 78fa9134 Christos Stavrakakis
50 c34de90f Giorgos Verigakis
51 04ba5fd4 Giorgos Verigakis
FILTERS = ('name', 'container_format', 'disk_format', 'status', 'size_min',
52 04ba5fd4 Giorgos Verigakis
           'size_max')
53 c34de90f Giorgos Verigakis
54 04ba5fd4 Giorgos Verigakis
PARAMS = ('sort_key', 'sort_dir')
55 c34de90f Giorgos Verigakis
56 04ba5fd4 Giorgos Verigakis
SORT_KEY_OPTIONS = ('id', 'name', 'status', 'size', 'disk_format',
57 04ba5fd4 Giorgos Verigakis
                    'container_format', 'created_at', 'updated_at')
58 04ba5fd4 Giorgos Verigakis
59 04ba5fd4 Giorgos Verigakis
SORT_DIR_OPTIONS = ('asc', 'desc')
60 04ba5fd4 Giorgos Verigakis
61 04ba5fd4 Giorgos Verigakis
LIST_FIELDS = ('status', 'name', 'disk_format', 'container_format', 'size',
62 04ba5fd4 Giorgos Verigakis
               'id')
63 04ba5fd4 Giorgos Verigakis
64 04ba5fd4 Giorgos Verigakis
DETAIL_FIELDS = ('name', 'disk_format', 'container_format', 'size', 'checksum',
65 04ba5fd4 Giorgos Verigakis
                 'location', 'created_at', 'updated_at', 'deleted_at',
66 04ba5fd4 Giorgos Verigakis
                 'status', 'is_public', 'owner', 'properties', 'id')
67 04ba5fd4 Giorgos Verigakis
68 04ba5fd4 Giorgos Verigakis
ADD_FIELDS = ('name', 'id', 'store', 'disk_format', 'container_format', 'size',
69 04ba5fd4 Giorgos Verigakis
              'checksum', 'is_public', 'owner', 'properties', 'location')
70 04ba5fd4 Giorgos Verigakis
71 04ba5fd4 Giorgos Verigakis
UPDATE_FIELDS = ('name', 'disk_format', 'container_format', 'is_public',
72 7e00b33e Giorgos Verigakis
                 'owner', 'properties', 'status')
73 c34de90f Giorgos Verigakis
74 54d26a27 Dionysis Grigoropoulos
DISK_FORMATS = ('diskdump', 'extdump', 'ntfsdump')
75 54d26a27 Dionysis Grigoropoulos
76 54d26a27 Dionysis Grigoropoulos
CONTAINER_FORMATS = ('aki', 'ari', 'ami', 'bare', 'ovf')
77 54d26a27 Dionysis Grigoropoulos
78 54d26a27 Dionysis Grigoropoulos
STORE_TYPES = ('pithos')
79 54d26a27 Dionysis Grigoropoulos
80 c34de90f Giorgos Verigakis
81 15137c54 Giorgos Verigakis
log = getLogger('synnefo.plankton')
82 c34de90f Giorgos Verigakis
83 c34de90f Giorgos Verigakis
84 15137c54 Giorgos Verigakis
def _create_image_response(image):
85 a4e9c19c Christos Stavrakakis
    """Encode the image parameters to HTTP Response Headers.
86 a4e9c19c Christos Stavrakakis

87 a4e9c19c Christos Stavrakakis
    Headers are encoded to UTF-8 then quoted.
88 a4e9c19c Christos Stavrakakis

89 a4e9c19c Christos Stavrakakis
    """
90 04ba5fd4 Giorgos Verigakis
    response = HttpResponse()
91 a4e9c19c Christos Stavrakakis
    encode_header = lambda x: quote(smart_str(x, encoding='utf-8'))
92 29490fca Christos Stavrakakis
93 04ba5fd4 Giorgos Verigakis
    for key in DETAIL_FIELDS:
94 04ba5fd4 Giorgos Verigakis
        if key == 'properties':
95 a4e9c19c Christos Stavrakakis
            for pkey, pval in image.get('properties', {}).items():
96 a4e9c19c Christos Stavrakakis
                pkey = 'http-x-image-meta-property-' + pkey.replace('_', '-')
97 a4e9c19c Christos Stavrakakis
                pkey = quote(smart_str(pkey, encoding='utf-8'))
98 a4e9c19c Christos Stavrakakis
                pval = quote(smart_str(pval, encoding='utf-8'))
99 a4e9c19c Christos Stavrakakis
                response[encode_header(pkey)] = encode_header(pval)
100 04ba5fd4 Giorgos Verigakis
        else:
101 a4e9c19c Christos Stavrakakis
            key = 'http-x-image-meta-' + key.replace('_', '-')
102 a4e9c19c Christos Stavrakakis
            val = image.get('key', '')
103 a4e9c19c Christos Stavrakakis
            response[encode_header(key)] = encode_header(val)
104 29490fca Christos Stavrakakis
105 04ba5fd4 Giorgos Verigakis
    return response
106 04ba5fd4 Giorgos Verigakis
107 04ba5fd4 Giorgos Verigakis
108 a4e9c19c Christos Stavrakakis
def headers_to_image_params(request):
109 a4e9c19c Christos Stavrakakis
    """Decode the HTTP request headers to the acceptable image parameters.
110 a4e9c19c Christos Stavrakakis

111 a4e9c19c Christos Stavrakakis
    Get image properties from the corresponding headers of the HTTP request.
112 a4e9c19c Christos Stavrakakis
    Image headers are unquoted and then decoded to Unicode using UTF-8
113 a4e9c19c Christos Stavrakakis
    encoding. Image headers keys are lowered and all punctation characters
114 a4e9c19c Christos Stavrakakis
    are replaced with underscore.
115 a4e9c19c Christos Stavrakakis

116 a4e9c19c Christos Stavrakakis
    """
117 a4e9c19c Christos Stavrakakis
118 04ba5fd4 Giorgos Verigakis
    def normalize(s):
119 04ba5fd4 Giorgos Verigakis
        return ''.join('_' if c in punctuation else c.lower() for c in s)
120 29490fca Christos Stavrakakis
121 04ba5fd4 Giorgos Verigakis
    META_PREFIX = 'HTTP_X_IMAGE_META_'
122 04ba5fd4 Giorgos Verigakis
    META_PREFIX_LEN = len(META_PREFIX)
123 04ba5fd4 Giorgos Verigakis
    META_PROPERTY_PREFIX = 'HTTP_X_IMAGE_META_PROPERTY_'
124 04ba5fd4 Giorgos Verigakis
    META_PROPERTY_PREFIX_LEN = len(META_PROPERTY_PREFIX)
125 29490fca Christos Stavrakakis
126 a4e9c19c Christos Stavrakakis
    decode_header = lambda x: smart_unicode(unquote(x), encoding="utf-8")
127 a4e9c19c Christos Stavrakakis
    is_img_property = lambda x: x.startswith(META_PROPERTY_PREFIX)
128 a4e9c19c Christos Stavrakakis
    is_img_param = lambda x: x.startswith(META_PREFIX)
129 29490fca Christos Stavrakakis
130 a4e9c19c Christos Stavrakakis
    params = {}
131 a4e9c19c Christos Stavrakakis
    properties = {}
132 4e571185 Dionysis Grigoropoulos
    try:
133 4e571185 Dionysis Grigoropoulos
        for key, val in request.META.items():
134 4e571185 Dionysis Grigoropoulos
            if isinstance(val, basestring):
135 4e571185 Dionysis Grigoropoulos
                val = decode_header(val)
136 4e571185 Dionysis Grigoropoulos
            if is_img_property(key):
137 4e571185 Dionysis Grigoropoulos
                key = decode_header(key[META_PROPERTY_PREFIX_LEN:])
138 4e571185 Dionysis Grigoropoulos
                properties[normalize(key)] = val
139 4e571185 Dionysis Grigoropoulos
            elif is_img_param(key):
140 4e571185 Dionysis Grigoropoulos
                key = decode_header(key[META_PREFIX_LEN:])
141 4e571185 Dionysis Grigoropoulos
                params[normalize(key)] = val
142 4e571185 Dionysis Grigoropoulos
    except DjangoUnicodeDecodeError:
143 4e571185 Dionysis Grigoropoulos
        raise faults.BadRequest("Could not decode request as UTF-8 string")
144 29490fca Christos Stavrakakis
145 a4e9c19c Christos Stavrakakis
    if properties:
146 a4e9c19c Christos Stavrakakis
        params['properties'] = properties
147 29490fca Christos Stavrakakis
148 a4e9c19c Christos Stavrakakis
    return params
149 c34de90f Giorgos Verigakis
150 c34de90f Giorgos Verigakis
151 d9d1763e Christos Stavrakakis
@api.api_method(http_method="POST", user_required=True, logger=log)
152 15137c54 Giorgos Verigakis
def add_image(request):
153 15137c54 Giorgos Verigakis
    """Add a new virtual machine image
154 29490fca Christos Stavrakakis

155 15137c54 Giorgos Verigakis
    Described in:
156 15137c54 Giorgos Verigakis
    3.6. Adding a New Virtual Machine Image
157 29490fca Christos Stavrakakis

158 15137c54 Giorgos Verigakis
    Implementation notes:
159 15137c54 Giorgos Verigakis
      * The implementation is very inefficient as it loads the whole image
160 15137c54 Giorgos Verigakis
        in memory.
161 29490fca Christos Stavrakakis

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

165 15137c54 Giorgos Verigakis
    Extensions:
166 15137c54 Giorgos Verigakis
      * An x-image-meta-location header can be passed with a link to file,
167 15137c54 Giorgos Verigakis
        instead of uploading the data.
168 15137c54 Giorgos Verigakis
    """
169 29490fca Christos Stavrakakis
170 a4e9c19c Christos Stavrakakis
    params = headers_to_image_params(request)
171 15137c54 Giorgos Verigakis
    log.debug('add_image %s', params)
172 29490fca Christos Stavrakakis
173 78fa9134 Christos Stavrakakis
    if not set(params.keys()).issubset(set(ADD_FIELDS)):
174 78fa9134 Christos Stavrakakis
        raise faults.BadRequest("Invalid parameters")
175 29490fca Christos Stavrakakis
176 7bd1d3b5 Giorgos Verigakis
    name = params.pop('name')
177 78fa9134 Christos Stavrakakis
    if name is None:
178 78fa9134 Christos Stavrakakis
        raise faults.BadRequest("Image 'name' parameter is required")
179 df455d57 Christos Stavrakakis
    elif len(smart_unicode(name, encoding="utf-8")) == 0:
180 1a9aa726 Dionysis Grigoropoulos
        raise faults.BadRequest("Invalid image name")
181 f5afd99b Giorgos Verigakis
    location = params.pop('location', None)
182 78fa9134 Christos Stavrakakis
    if location is None:
183 78fa9134 Christos Stavrakakis
        raise faults.BadRequest("'location' parameter is required")
184 78fa9134 Christos Stavrakakis
185 59573532 Christos Stavrakakis
    try:
186 59573532 Christos Stavrakakis
        split_url(location)
187 78fa9134 Christos Stavrakakis
    except InvalidLocation:
188 59573532 Christos Stavrakakis
        raise faults.BadRequest("Invalid location '%s'" % location)
189 29490fca Christos Stavrakakis
190 54d26a27 Dionysis Grigoropoulos
    validate_fields(params)
191 54d26a27 Dionysis Grigoropoulos
192 f5afd99b Giorgos Verigakis
    if location:
193 cda71050 Christos Stavrakakis
        with image_backend(request.user_uniq) as backend:
194 cda71050 Christos Stavrakakis
            image = backend.register(name, location, params)
195 04ba5fd4 Giorgos Verigakis
    else:
196 b694875c Christos Stavrakakis
        #f = StringIO(request.body)
197 cda71050 Christos Stavrakakis
        #image = backend.put(name, f, params)
198 f5afd99b Giorgos Verigakis
        return HttpResponse(status=501)     # Not Implemented
199 29490fca Christos Stavrakakis
200 04ba5fd4 Giorgos Verigakis
    if not image:
201 0a72907b Giorgos Verigakis
        return HttpResponse('Registration failed', status=500)
202 29490fca Christos Stavrakakis
203 15137c54 Giorgos Verigakis
    return _create_image_response(image)
204 15137c54 Giorgos Verigakis
205 15137c54 Giorgos Verigakis
206 d9d1763e Christos Stavrakakis
@api.api_method(http_method="DELETE", user_required=True, logger=log)
207 f13aab5d Christos Stavrakakis
def delete_image(request, image_id):
208 f13aab5d Christos Stavrakakis
    """Delete an Image.
209 f13aab5d Christos Stavrakakis

210 f13aab5d Christos Stavrakakis
    This API call is not described in the Openstack Glance API.
211 f13aab5d Christos Stavrakakis

212 f13aab5d Christos Stavrakakis
    Implementation notes:
213 f13aab5d Christos Stavrakakis
      * The implementation does not delete the Image from the storage
214 f13aab5d Christos Stavrakakis
        backend. Instead it unregisters the image by removing all the
215 f13aab5d Christos Stavrakakis
        metadata from the plankton metadata domain.
216 f13aab5d Christos Stavrakakis

217 f13aab5d Christos Stavrakakis
    """
218 f13aab5d Christos Stavrakakis
    log.info("delete_image '%s'" % image_id)
219 f13aab5d Christos Stavrakakis
    userid = request.user_uniq
220 cda71050 Christos Stavrakakis
    with image_backend(userid) as backend:
221 cda71050 Christos Stavrakakis
        backend.unregister(image_id)
222 f13aab5d Christos Stavrakakis
    log.info("User '%s' deleted image '%s'" % (userid, image_id))
223 f13aab5d Christos Stavrakakis
    return HttpResponse(status=204)
224 f13aab5d Christos Stavrakakis
225 f13aab5d Christos Stavrakakis
226 d9d1763e Christos Stavrakakis
@api.api_method(http_method="PUT", user_required=True, logger=log)
227 15137c54 Giorgos Verigakis
def add_image_member(request, image_id, member):
228 15137c54 Giorgos Verigakis
    """Add a member to an image
229 29490fca Christos Stavrakakis

230 15137c54 Giorgos Verigakis
    Described in:
231 15137c54 Giorgos Verigakis
    3.9. Adding a Member to an Image
232 29490fca Christos Stavrakakis

233 15137c54 Giorgos Verigakis
    Limitations:
234 15137c54 Giorgos Verigakis
      * Passing a body to enable `can_share` is not supported.
235 15137c54 Giorgos Verigakis
    """
236 29490fca Christos Stavrakakis
237 15137c54 Giorgos Verigakis
    log.debug('add_image_member %s %s', image_id, member)
238 cda71050 Christos Stavrakakis
    with image_backend(request.user_uniq) as backend:
239 cda71050 Christos Stavrakakis
        backend.add_user(image_id, member)
240 15137c54 Giorgos Verigakis
    return HttpResponse(status=204)
241 c34de90f Giorgos Verigakis
242 c34de90f Giorgos Verigakis
243 d9d1763e Christos Stavrakakis
@api.api_method(http_method="GET", user_required=True, logger=log)
244 15137c54 Giorgos Verigakis
def get_image(request, image_id):
245 15137c54 Giorgos Verigakis
    """Retrieve a virtual machine image
246 29490fca Christos Stavrakakis

247 15137c54 Giorgos Verigakis
    Described in:
248 15137c54 Giorgos Verigakis
    3.5. Retrieving a Virtual Machine Image
249 29490fca Christos Stavrakakis

250 15137c54 Giorgos Verigakis
    Implementation notes:
251 15137c54 Giorgos Verigakis
      * The implementation is very inefficient as it loads the whole image
252 15137c54 Giorgos Verigakis
        in memory.
253 15137c54 Giorgos Verigakis
    """
254 29490fca Christos Stavrakakis
255 cda71050 Christos Stavrakakis
    #image = backend.get_image(image_id)
256 f5afd99b Giorgos Verigakis
    #if not image:
257 f5afd99b Giorgos Verigakis
    #    return HttpResponseNotFound()
258 f5afd99b Giorgos Verigakis
    #
259 f5afd99b Giorgos Verigakis
    #response = _create_image_response(image)
260 cda71050 Christos Stavrakakis
    #data = backend.get_data(image)
261 f5afd99b Giorgos Verigakis
    #response.content = data
262 f5afd99b Giorgos Verigakis
    #response['Content-Length'] = len(data)
263 f5afd99b Giorgos Verigakis
    #response['Content-Type'] = 'application/octet-stream'
264 f5afd99b Giorgos Verigakis
    #response['ETag'] = image['checksum']
265 f5afd99b Giorgos Verigakis
    #return response
266 f5afd99b Giorgos Verigakis
    return HttpResponse(status=501)     # Not Implemented
267 c34de90f Giorgos Verigakis
268 c34de90f Giorgos Verigakis
269 d9d1763e Christos Stavrakakis
@api.api_method(http_method="HEAD", user_required=True, logger=log)
270 15137c54 Giorgos Verigakis
def get_image_meta(request, image_id):
271 15137c54 Giorgos Verigakis
    """Return detailed metadata on a specific image
272 29490fca Christos Stavrakakis

273 15137c54 Giorgos Verigakis
    Described in:
274 15137c54 Giorgos Verigakis
    3.4. Requesting Detailed Metadata on a Specific Image
275 15137c54 Giorgos Verigakis
    """
276 04ba5fd4 Giorgos Verigakis
277 cda71050 Christos Stavrakakis
    with image_backend(request.user_uniq) as backend:
278 cda71050 Christos Stavrakakis
        image = backend.get_image(image_id)
279 15137c54 Giorgos Verigakis
    return _create_image_response(image)
280 15137c54 Giorgos Verigakis
281 15137c54 Giorgos Verigakis
282 d9d1763e Christos Stavrakakis
@api.api_method(http_method="GET", user_required=True, logger=log)
283 15137c54 Giorgos Verigakis
def list_image_members(request, image_id):
284 15137c54 Giorgos Verigakis
    """List image memberships
285 15137c54 Giorgos Verigakis

286 15137c54 Giorgos Verigakis
    Described in:
287 15137c54 Giorgos Verigakis
    3.7. Requesting Image Memberships
288 15137c54 Giorgos Verigakis
    """
289 29490fca Christos Stavrakakis
290 cda71050 Christos Stavrakakis
    with image_backend(request.user_uniq) as backend:
291 cda71050 Christos Stavrakakis
        users = backend.list_users(image_id)
292 cda71050 Christos Stavrakakis
293 cda71050 Christos Stavrakakis
    members = [{'member_id': u, 'can_share': False} for u in users]
294 15137c54 Giorgos Verigakis
    data = json.dumps({'members': members}, indent=settings.DEBUG)
295 15137c54 Giorgos Verigakis
    return HttpResponse(data)
296 04ba5fd4 Giorgos Verigakis
297 04ba5fd4 Giorgos Verigakis
298 d9d1763e Christos Stavrakakis
@api.api_method(http_method="GET", user_required=True, logger=log)
299 b2f94a0c Christos Stavrakakis
def list_images(request, detail=False):
300 b2f94a0c Christos Stavrakakis
    """Return a list of available images.
301 29490fca Christos Stavrakakis

302 b2f94a0c Christos Stavrakakis
    This includes images owned by the user, images shared with the user and
303 b2f94a0c Christos Stavrakakis
    public images.
304 29490fca Christos Stavrakakis

305 15137c54 Giorgos Verigakis
    """
306 04ba5fd4 Giorgos Verigakis
307 04ba5fd4 Giorgos Verigakis
    def get_request_params(keys):
308 04ba5fd4 Giorgos Verigakis
        params = {}
309 04ba5fd4 Giorgos Verigakis
        for key in keys:
310 04ba5fd4 Giorgos Verigakis
            val = request.GET.get(key, None)
311 04ba5fd4 Giorgos Verigakis
            if val is not None:
312 04ba5fd4 Giorgos Verigakis
                params[key] = val
313 04ba5fd4 Giorgos Verigakis
        return params
314 04ba5fd4 Giorgos Verigakis
315 15137c54 Giorgos Verigakis
    log.debug('list_public_images detail=%s', detail)
316 04ba5fd4 Giorgos Verigakis
317 04ba5fd4 Giorgos Verigakis
    filters = get_request_params(FILTERS)
318 04ba5fd4 Giorgos Verigakis
    params = get_request_params(PARAMS)
319 04ba5fd4 Giorgos Verigakis
320 04ba5fd4 Giorgos Verigakis
    params.setdefault('sort_key', 'created_at')
321 04ba5fd4 Giorgos Verigakis
    params.setdefault('sort_dir', 'desc')
322 04ba5fd4 Giorgos Verigakis
323 78fa9134 Christos Stavrakakis
    if not params['sort_key'] in SORT_KEY_OPTIONS:
324 78fa9134 Christos Stavrakakis
        raise faults.BadRequest("Invalid 'sort_key'")
325 78fa9134 Christos Stavrakakis
    if not params['sort_dir'] in SORT_DIR_OPTIONS:
326 78fa9134 Christos Stavrakakis
        raise faults.BadRequest("Invalid 'sort_dir'")
327 04ba5fd4 Giorgos Verigakis
328 c3bcaeff Stratos Psomadakis
    if 'size_max' in filters:
329 c3bcaeff Stratos Psomadakis
        try:
330 c3bcaeff Stratos Psomadakis
            filters['size_max'] = int(filters['size_max'])
331 c3bcaeff Stratos Psomadakis
        except ValueError:
332 d2b8ec7b Christos Stavrakakis
            raise faults.BadRequest("Malformed request.")
333 c3bcaeff Stratos Psomadakis
334 c3bcaeff Stratos Psomadakis
    if 'size_min' in filters:
335 c3bcaeff Stratos Psomadakis
        try:
336 c3bcaeff Stratos Psomadakis
            filters['size_min'] = int(filters['size_min'])
337 c3bcaeff Stratos Psomadakis
        except ValueError:
338 d2b8ec7b Christos Stavrakakis
            raise faults.BadRequest("Malformed request.")
339 c3bcaeff Stratos Psomadakis
340 cda71050 Christos Stavrakakis
    with image_backend(request.user_uniq) as backend:
341 14c94c48 Christos Stavrakakis
        images = backend.list_images(filters, params)
342 29490fca Christos Stavrakakis
343 04ba5fd4 Giorgos Verigakis
    # Remove keys that should not be returned
344 04ba5fd4 Giorgos Verigakis
    fields = DETAIL_FIELDS if detail else LIST_FIELDS
345 04ba5fd4 Giorgos Verigakis
    for image in images:
346 04ba5fd4 Giorgos Verigakis
        for key in image.keys():
347 04ba5fd4 Giorgos Verigakis
            if key not in fields:
348 04ba5fd4 Giorgos Verigakis
                del image[key]
349 04ba5fd4 Giorgos Verigakis
350 04ba5fd4 Giorgos Verigakis
    data = json.dumps(images, indent=settings.DEBUG)
351 04ba5fd4 Giorgos Verigakis
    return HttpResponse(data)
352 04ba5fd4 Giorgos Verigakis
353 04ba5fd4 Giorgos Verigakis
354 d9d1763e Christos Stavrakakis
@api.api_method(http_method="GET", user_required=True, logger=log)
355 b2f94a0c Christos Stavrakakis
def list_shared_images(request, member):
356 15137c54 Giorgos Verigakis
    """Request shared images
357 29490fca Christos Stavrakakis

358 15137c54 Giorgos Verigakis
    Described in:
359 15137c54 Giorgos Verigakis
    3.8. Requesting Shared Images
360 29490fca Christos Stavrakakis

361 15137c54 Giorgos Verigakis
    Implementation notes:
362 8482ef55 Giorgos Verigakis
      * It is not clear what this method should do. We return the IDs of
363 8482ef55 Giorgos Verigakis
        the users's images that are accessible by `member`.
364 15137c54 Giorgos Verigakis
    """
365 29490fca Christos Stavrakakis
366 b2f94a0c Christos Stavrakakis
    log.debug('list_shared_images %s', member)
367 29490fca Christos Stavrakakis
368 15137c54 Giorgos Verigakis
    images = []
369 cda71050 Christos Stavrakakis
    with image_backend(request.user_uniq) as backend:
370 14c94c48 Christos Stavrakakis
        for image in backend.list_shared_images(member=member):
371 cda71050 Christos Stavrakakis
            image_id = image['id']
372 cda71050 Christos Stavrakakis
            images.append({'image_id': image_id, 'can_share': False})
373 29490fca Christos Stavrakakis
374 15137c54 Giorgos Verigakis
    data = json.dumps({'shared_images': images}, indent=settings.DEBUG)
375 15137c54 Giorgos Verigakis
    return HttpResponse(data)
376 15137c54 Giorgos Verigakis
377 15137c54 Giorgos Verigakis
378 d9d1763e Christos Stavrakakis
@api.api_method(http_method="DELETE", user_required=True, logger=log)
379 15137c54 Giorgos Verigakis
def remove_image_member(request, image_id, member):
380 15137c54 Giorgos Verigakis
    """Remove a member from an image
381 15137c54 Giorgos Verigakis

382 15137c54 Giorgos Verigakis
    Described in:
383 f5afd99b Giorgos Verigakis
    3.10. Removing a Member from an Image
384 15137c54 Giorgos Verigakis
    """
385 15137c54 Giorgos Verigakis
386 15137c54 Giorgos Verigakis
    log.debug('remove_image_member %s %s', image_id, member)
387 cda71050 Christos Stavrakakis
    with image_backend(request.user_uniq) as backend:
388 cda71050 Christos Stavrakakis
        backend.remove_user(image_id, member)
389 15137c54 Giorgos Verigakis
    return HttpResponse(status=204)
390 15137c54 Giorgos Verigakis
391 15137c54 Giorgos Verigakis
392 d9d1763e Christos Stavrakakis
@api.api_method(http_method="PUT", user_required=True, logger=log)
393 15137c54 Giorgos Verigakis
def update_image(request, image_id):
394 15137c54 Giorgos Verigakis
    """Update an image
395 29490fca Christos Stavrakakis

396 15137c54 Giorgos Verigakis
    Described in:
397 15137c54 Giorgos Verigakis
    3.6.2. Updating an Image
398 29490fca Christos Stavrakakis

399 15137c54 Giorgos Verigakis
    Implementation notes:
400 15137c54 Giorgos Verigakis
      * It is not clear which metadata are allowed to be updated. We support:
401 15137c54 Giorgos Verigakis
        name, disk_format, container_format, is_public, owner, properties
402 15137c54 Giorgos Verigakis
        and status.
403 15137c54 Giorgos Verigakis
    """
404 29490fca Christos Stavrakakis
405 a4e9c19c Christos Stavrakakis
    meta = headers_to_image_params(request)
406 15137c54 Giorgos Verigakis
    log.debug('update_image %s', meta)
407 29490fca Christos Stavrakakis
408 78fa9134 Christos Stavrakakis
    if not set(meta.keys()).issubset(set(UPDATE_FIELDS)):
409 78fa9134 Christos Stavrakakis
        raise faults.BadRequest("Invalid metadata")
410 29490fca Christos Stavrakakis
411 54d26a27 Dionysis Grigoropoulos
    validate_fields(meta)
412 54d26a27 Dionysis Grigoropoulos
413 cda71050 Christos Stavrakakis
    with image_backend(request.user_uniq) as backend:
414 cda71050 Christos Stavrakakis
        image = backend.update_metadata(image_id, meta)
415 15137c54 Giorgos Verigakis
    return _create_image_response(image)
416 15137c54 Giorgos Verigakis
417 15137c54 Giorgos Verigakis
418 d9d1763e Christos Stavrakakis
@api.api_method(http_method="PUT", user_required=True, logger=log)
419 15137c54 Giorgos Verigakis
def update_image_members(request, image_id):
420 15137c54 Giorgos Verigakis
    """Replace a membership list for an image
421 29490fca Christos Stavrakakis

422 15137c54 Giorgos Verigakis
    Described in:
423 15137c54 Giorgos Verigakis
    3.11. Replacing a Membership List for an Image
424 29490fca Christos Stavrakakis

425 15137c54 Giorgos Verigakis
    Limitations:
426 15137c54 Giorgos Verigakis
      * can_share value is ignored
427 15137c54 Giorgos Verigakis
    """
428 29490fca Christos Stavrakakis
429 15137c54 Giorgos Verigakis
    log.debug('update_image_members %s', image_id)
430 5a6cee09 Christos Stavrakakis
    data = api.utils.get_json_body(request)
431 15137c54 Giorgos Verigakis
    members = []
432 5a6cee09 Christos Stavrakakis
433 5a6cee09 Christos Stavrakakis
    memberships = api.utils.get_attribute(data, "memberships", attr_type=list)
434 5a6cee09 Christos Stavrakakis
    for member in memberships:
435 5a6cee09 Christos Stavrakakis
        if not isinstance(member, dict):
436 5a6cee09 Christos Stavrakakis
            raise faults.BadRequest("Invalid 'memberships' field")
437 5a6cee09 Christos Stavrakakis
        member = api.utils.get_attribute(member, "member_id")
438 5a6cee09 Christos Stavrakakis
        members.append(member)
439 29490fca Christos Stavrakakis
440 cda71050 Christos Stavrakakis
    with image_backend(request.user_uniq) as backend:
441 cda71050 Christos Stavrakakis
        backend.replace_users(image_id, members)
442 15137c54 Giorgos Verigakis
    return HttpResponse(status=204)
443 54d26a27 Dionysis Grigoropoulos
444 54d26a27 Dionysis Grigoropoulos
445 54d26a27 Dionysis Grigoropoulos
def validate_fields(params):
446 54d26a27 Dionysis Grigoropoulos
    if "id" in params:
447 54d26a27 Dionysis Grigoropoulos
        raise faults.BadRequest("Setting the image ID is not supported")
448 54d26a27 Dionysis Grigoropoulos
449 54d26a27 Dionysis Grigoropoulos
    if "store" in params:
450 54d26a27 Dionysis Grigoropoulos
        if params["store"] not in STORE_TYPES:
451 54d26a27 Dionysis Grigoropoulos
            raise faults.BadRequest("Invalid store type '%s'" %
452 54d26a27 Dionysis Grigoropoulos
                                    params["store"])
453 54d26a27 Dionysis Grigoropoulos
454 54d26a27 Dionysis Grigoropoulos
    if "disk_format" in params:
455 54d26a27 Dionysis Grigoropoulos
        if params["disk_format"] not in DISK_FORMATS:
456 54d26a27 Dionysis Grigoropoulos
            raise faults.BadRequest("Invalid disk format '%s'" %
457 54d26a27 Dionysis Grigoropoulos
                                    params['disk_format'])
458 54d26a27 Dionysis Grigoropoulos
459 54d26a27 Dionysis Grigoropoulos
    if "container_format" in params:
460 54d26a27 Dionysis Grigoropoulos
        if params["container_format"] not in CONTAINER_FORMATS:
461 54d26a27 Dionysis Grigoropoulos
            raise faults.BadRequest("Invalid container format '%s'" %
462 54d26a27 Dionysis Grigoropoulos
                                    params['container_format'])