Revision 29490fca snf-cyclades-app/synnefo/plankton/views.py
b/snf-cyclades-app/synnefo/plankton/views.py | ||
---|---|---|
73 | 73 |
|
74 | 74 |
def _create_image_response(image): |
75 | 75 |
response = HttpResponse() |
76 |
|
|
76 |
|
|
77 | 77 |
for key in DETAIL_FIELDS: |
78 | 78 |
if key == 'properties': |
79 | 79 |
for k, v in image.get('properties', {}).items(): |
... | ... | |
82 | 82 |
else: |
83 | 83 |
name = 'x-image-meta-' + key.replace('_', '-') |
84 | 84 |
response[name] = image.get(key, '') |
85 |
|
|
85 |
|
|
86 | 86 |
return response |
87 | 87 |
|
88 | 88 |
|
89 | 89 |
def _get_image_headers(request): |
90 | 90 |
def normalize(s): |
91 | 91 |
return ''.join('_' if c in punctuation else c.lower() for c in s) |
92 |
|
|
92 |
|
|
93 | 93 |
META_PREFIX = 'HTTP_X_IMAGE_META_' |
94 | 94 |
META_PREFIX_LEN = len(META_PREFIX) |
95 | 95 |
META_PROPERTY_PREFIX = 'HTTP_X_IMAGE_META_PROPERTY_' |
96 | 96 |
META_PROPERTY_PREFIX_LEN = len(META_PROPERTY_PREFIX) |
97 |
|
|
97 |
|
|
98 | 98 |
headers = {'properties': {}} |
99 |
|
|
99 |
|
|
100 | 100 |
for key, val in request.META.items(): |
101 | 101 |
if key.startswith(META_PROPERTY_PREFIX): |
102 | 102 |
name = normalize(key[META_PROPERTY_PREFIX_LEN:]) |
... | ... | |
104 | 104 |
elif key.startswith(META_PREFIX): |
105 | 105 |
name = normalize(key[META_PREFIX_LEN:]) |
106 | 106 |
headers[unquote(name)] = unquote(val) |
107 |
|
|
107 |
|
|
108 | 108 |
is_public = headers.get('is_public', None) |
109 | 109 |
if is_public is not None: |
110 | 110 |
headers['is_public'] = True if is_public.lower() == 'true' else False |
111 |
|
|
111 |
|
|
112 | 112 |
if not headers['properties']: |
113 | 113 |
del headers['properties'] |
114 |
|
|
114 |
|
|
115 | 115 |
return headers |
116 | 116 |
|
117 | 117 |
|
118 | 118 |
@plankton_method('POST') |
119 | 119 |
def add_image(request): |
120 | 120 |
"""Add a new virtual machine image |
121 |
|
|
121 |
|
|
122 | 122 |
Described in: |
123 | 123 |
3.6. Adding a New Virtual Machine Image |
124 |
|
|
124 |
|
|
125 | 125 |
Implementation notes: |
126 | 126 |
* The implementation is very inefficient as it loads the whole image |
127 | 127 |
in memory. |
128 |
|
|
128 |
|
|
129 | 129 |
Limitations: |
130 | 130 |
* x-image-meta-id is not supported. Will always return 409 Conflict. |
131 |
|
|
131 |
|
|
132 | 132 |
Extensions: |
133 | 133 |
* An x-image-meta-location header can be passed with a link to file, |
134 | 134 |
instead of uploading the data. |
135 | 135 |
""" |
136 |
|
|
136 |
|
|
137 | 137 |
params = _get_image_headers(request) |
138 | 138 |
log.debug('add_image %s', params) |
139 |
|
|
139 |
|
|
140 | 140 |
assert 'name' in params |
141 | 141 |
assert set(params.keys()).issubset(set(ADD_FIELDS)) |
142 |
|
|
142 |
|
|
143 | 143 |
name = params.pop('name') |
144 | 144 |
location = params.pop('location', None) |
145 |
|
|
145 |
|
|
146 | 146 |
if location: |
147 | 147 |
image = request.backend.register(name, location, params) |
148 | 148 |
else: |
149 | 149 |
#f = StringIO(request.raw_post_data) |
150 | 150 |
#image = request.backend.put(name, f, params) |
151 | 151 |
return HttpResponse(status=501) # Not Implemented |
152 |
|
|
152 |
|
|
153 | 153 |
if not image: |
154 | 154 |
return HttpResponse('Registration failed', status=500) |
155 |
|
|
155 |
|
|
156 | 156 |
return _create_image_response(image) |
157 | 157 |
|
158 | 158 |
|
159 | 159 |
@plankton_method('PUT') |
160 | 160 |
def add_image_member(request, image_id, member): |
161 | 161 |
"""Add a member to an image |
162 |
|
|
162 |
|
|
163 | 163 |
Described in: |
164 | 164 |
3.9. Adding a Member to an Image |
165 |
|
|
165 |
|
|
166 | 166 |
Limitations: |
167 | 167 |
* Passing a body to enable `can_share` is not supported. |
168 | 168 |
""" |
169 |
|
|
169 |
|
|
170 | 170 |
log.debug('add_image_member %s %s', image_id, member) |
171 | 171 |
request.backend.add_user(image_id, member) |
172 | 172 |
return HttpResponse(status=204) |
... | ... | |
175 | 175 |
@plankton_method('GET') |
176 | 176 |
def get_image(request, image_id): |
177 | 177 |
"""Retrieve a virtual machine image |
178 |
|
|
178 |
|
|
179 | 179 |
Described in: |
180 | 180 |
3.5. Retrieving a Virtual Machine Image |
181 |
|
|
181 |
|
|
182 | 182 |
Implementation notes: |
183 | 183 |
* The implementation is very inefficient as it loads the whole image |
184 | 184 |
in memory. |
185 | 185 |
""" |
186 |
|
|
186 |
|
|
187 | 187 |
#image = request.backend.get_image(image_id) |
188 | 188 |
#if not image: |
189 | 189 |
# return HttpResponseNotFound() |
... | ... | |
201 | 201 |
@plankton_method('HEAD') |
202 | 202 |
def get_image_meta(request, image_id): |
203 | 203 |
"""Return detailed metadata on a specific image |
204 |
|
|
204 |
|
|
205 | 205 |
Described in: |
206 | 206 |
3.4. Requesting Detailed Metadata on a Specific Image |
207 | 207 |
""" |
... | ... | |
219 | 219 |
Described in: |
220 | 220 |
3.7. Requesting Image Memberships |
221 | 221 |
""" |
222 |
|
|
222 |
|
|
223 | 223 |
members = [{'member_id': user, 'can_share': False} |
224 | 224 |
for user in request.backend.list_users(image_id)] |
225 | 225 |
data = json.dumps({'members': members}, indent=settings.DEBUG) |
... | ... | |
229 | 229 |
@plankton_method('GET') |
230 | 230 |
def list_public_images(request, detail=False): |
231 | 231 |
"""Return a list of public VM images. |
232 |
|
|
232 |
|
|
233 | 233 |
Described in: |
234 | 234 |
3.1. Requesting a List of Public VM Images |
235 | 235 |
3.2. Requesting Detailed Metadata on Public VM Images |
236 | 236 |
3.3. Filtering Images Returned via GET /images andGET /images/detail |
237 |
|
|
237 |
|
|
238 | 238 |
Extensions: |
239 | 239 |
* Image ID is returned in both compact and detail listings |
240 | 240 |
""" |
... | ... | |
259 | 259 |
assert params['sort_dir'] in SORT_DIR_OPTIONS |
260 | 260 |
|
261 | 261 |
images = request.backend.list_public(filters, params) |
262 |
|
|
262 |
|
|
263 | 263 |
# Remove keys that should not be returned |
264 | 264 |
fields = DETAIL_FIELDS if detail else LIST_FIELDS |
265 | 265 |
for image in images: |
... | ... | |
274 | 274 |
@plankton_method('GET') |
275 | 275 |
def list_shared_images(request, member): |
276 | 276 |
"""Request shared images |
277 |
|
|
277 |
|
|
278 | 278 |
Described in: |
279 | 279 |
3.8. Requesting Shared Images |
280 |
|
|
280 |
|
|
281 | 281 |
Implementation notes: |
282 | 282 |
* It is not clear what this method should do. We return the IDs of |
283 | 283 |
the users's images that are accessible by `member`. |
284 | 284 |
""" |
285 |
|
|
285 |
|
|
286 | 286 |
log.debug('list_shared_images %s', member) |
287 |
|
|
287 |
|
|
288 | 288 |
images = [] |
289 | 289 |
for image_id in request.backend.iter_shared(member): |
290 | 290 |
images.append({'image_id': image_id, 'can_share': False}) |
291 |
|
|
291 |
|
|
292 | 292 |
data = json.dumps({'shared_images': images}, indent=settings.DEBUG) |
293 | 293 |
return HttpResponse(data) |
294 | 294 |
|
... | ... | |
309 | 309 |
@plankton_method('PUT') |
310 | 310 |
def update_image(request, image_id): |
311 | 311 |
"""Update an image |
312 |
|
|
312 |
|
|
313 | 313 |
Described in: |
314 | 314 |
3.6.2. Updating an Image |
315 |
|
|
315 |
|
|
316 | 316 |
Implementation notes: |
317 | 317 |
* It is not clear which metadata are allowed to be updated. We support: |
318 | 318 |
name, disk_format, container_format, is_public, owner, properties |
319 | 319 |
and status. |
320 | 320 |
""" |
321 |
|
|
321 |
|
|
322 | 322 |
meta = _get_image_headers(request) |
323 | 323 |
log.debug('update_image %s', meta) |
324 |
|
|
324 |
|
|
325 | 325 |
assert set(meta.keys()).issubset(set(UPDATE_FIELDS)) |
326 |
|
|
326 |
|
|
327 | 327 |
image = request.backend.update(image_id, meta) |
328 | 328 |
return _create_image_response(image) |
329 | 329 |
|
... | ... | |
331 | 331 |
@plankton_method('PUT') |
332 | 332 |
def update_image_members(request, image_id): |
333 | 333 |
"""Replace a membership list for an image |
334 |
|
|
334 |
|
|
335 | 335 |
Described in: |
336 | 336 |
3.11. Replacing a Membership List for an Image |
337 |
|
|
337 |
|
|
338 | 338 |
Limitations: |
339 | 339 |
* can_share value is ignored |
340 | 340 |
""" |
341 |
|
|
341 |
|
|
342 | 342 |
log.debug('update_image_members %s', image_id) |
343 | 343 |
members = [] |
344 | 344 |
try: |
... | ... | |
347 | 347 |
members.append(member['member_id']) |
348 | 348 |
except (ValueError, KeyError, TypeError): |
349 | 349 |
return HttpResponse(status=400) |
350 |
|
|
350 |
|
|
351 | 351 |
request.backend.replace_users(image_id, members) |
352 | 352 |
return HttpResponse(status=204) |
Also available in: Unified diff