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']) |