Statistics
| Branch: | Tag: | Revision:

root / api / images.py @ f4fe8796

History | View | Annotate | Download (11.3 kB)

1 adee02b8 Giorgos Verigakis
# Copyright 2011 GRNET S.A. All rights reserved.
2 adee02b8 Giorgos Verigakis
# 
3 adee02b8 Giorgos Verigakis
# Redistribution and use in source and binary forms, with or
4 adee02b8 Giorgos Verigakis
# without modification, are permitted provided that the following
5 adee02b8 Giorgos Verigakis
# conditions are met:
6 adee02b8 Giorgos Verigakis
# 
7 adee02b8 Giorgos Verigakis
#   1. Redistributions of source code must retain the above
8 adee02b8 Giorgos Verigakis
#      copyright notice, this list of conditions and the following
9 adee02b8 Giorgos Verigakis
#      disclaimer.
10 adee02b8 Giorgos Verigakis
# 
11 adee02b8 Giorgos Verigakis
#   2. Redistributions in binary form must reproduce the above
12 adee02b8 Giorgos Verigakis
#      copyright notice, this list of conditions and the following
13 adee02b8 Giorgos Verigakis
#      disclaimer in the documentation and/or other materials
14 adee02b8 Giorgos Verigakis
#      provided with the distribution.
15 adee02b8 Giorgos Verigakis
# 
16 adee02b8 Giorgos Verigakis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 adee02b8 Giorgos Verigakis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 adee02b8 Giorgos Verigakis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 adee02b8 Giorgos Verigakis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 adee02b8 Giorgos Verigakis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 adee02b8 Giorgos Verigakis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 adee02b8 Giorgos Verigakis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 adee02b8 Giorgos Verigakis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 adee02b8 Giorgos Verigakis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 adee02b8 Giorgos Verigakis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 adee02b8 Giorgos Verigakis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 adee02b8 Giorgos Verigakis
# POSSIBILITY OF SUCH DAMAGE.
28 adee02b8 Giorgos Verigakis
# 
29 adee02b8 Giorgos Verigakis
# The views and conclusions contained in the software and
30 adee02b8 Giorgos Verigakis
# documentation are those of the authors and should not be
31 adee02b8 Giorgos Verigakis
# interpreted as representing official policies, either expressed
32 adee02b8 Giorgos Verigakis
# or implied, of GRNET S.A.
33 c36934a7 Giorgos Verigakis
34 d8e50a39 Giorgos Verigakis
from django.conf.urls.defaults import patterns
35 75768d0e Giorgos Verigakis
from django.db.models import Q
36 c36934a7 Giorgos Verigakis
from django.http import HttpResponse
37 c36934a7 Giorgos Verigakis
from django.template.loader import render_to_string
38 29a59bc1 Giorgos Verigakis
from django.utils import simplejson as json
39 c36934a7 Giorgos Verigakis
40 b36f78fa Giorgos Verigakis
from synnefo.api import util
41 b36f78fa Giorgos Verigakis
from synnefo.api.common import method_not_allowed
42 b36f78fa Giorgos Verigakis
from synnefo.api.faults import BadRequest
43 b36f78fa Giorgos Verigakis
from synnefo.db.models import Image, ImageMetadata
44 b36f78fa Giorgos Verigakis
45 c36934a7 Giorgos Verigakis
46 c36934a7 Giorgos Verigakis
urlpatterns = patterns('synnefo.api.images',
47 c36934a7 Giorgos Verigakis
    (r'^(?:/|.json|.xml)?$', 'demux'),
48 c36934a7 Giorgos Verigakis
    (r'^/detail(?:.json|.xml)?$', 'list_images', {'detail': True}),
49 c36934a7 Giorgos Verigakis
    (r'^/(\d+)(?:.json|.xml)?$', 'image_demux'),
50 432fc8c3 Giorgos Verigakis
    (r'^/(\d+)/meta(?:.json|.xml)?$', 'metadata_demux'),
51 432fc8c3 Giorgos Verigakis
    (r'^/(\d+)/meta/(.+?)(?:.json|.xml)?$', 'metadata_item_demux'),
52 c36934a7 Giorgos Verigakis
)
53 c36934a7 Giorgos Verigakis
54 c36934a7 Giorgos Verigakis
def demux(request):
55 c36934a7 Giorgos Verigakis
    if request.method == 'GET':
56 c36934a7 Giorgos Verigakis
        return list_images(request)
57 c36934a7 Giorgos Verigakis
    elif request.method == 'POST':
58 c36934a7 Giorgos Verigakis
        return create_image(request)
59 c36934a7 Giorgos Verigakis
    else:
60 d8e50a39 Giorgos Verigakis
        return method_not_allowed(request)
61 c36934a7 Giorgos Verigakis
62 c36934a7 Giorgos Verigakis
def image_demux(request, image_id):
63 c36934a7 Giorgos Verigakis
    if request.method == 'GET':
64 c36934a7 Giorgos Verigakis
        return get_image_details(request, image_id)
65 c36934a7 Giorgos Verigakis
    elif request.method == 'DELETE':
66 c36934a7 Giorgos Verigakis
        return delete_image(request, image_id)
67 c36934a7 Giorgos Verigakis
    else:
68 d8e50a39 Giorgos Verigakis
        return method_not_allowed(request)
69 c36934a7 Giorgos Verigakis
70 432fc8c3 Giorgos Verigakis
def metadata_demux(request, image_id):
71 432fc8c3 Giorgos Verigakis
    if request.method == 'GET':
72 432fc8c3 Giorgos Verigakis
        return list_metadata(request, image_id)
73 432fc8c3 Giorgos Verigakis
    elif request.method == 'POST':
74 432fc8c3 Giorgos Verigakis
        return update_metadata(request, image_id)
75 432fc8c3 Giorgos Verigakis
    else:
76 432fc8c3 Giorgos Verigakis
        return method_not_allowed(request)
77 432fc8c3 Giorgos Verigakis
78 432fc8c3 Giorgos Verigakis
def metadata_item_demux(request, image_id, key):
79 432fc8c3 Giorgos Verigakis
    if request.method == 'GET':
80 432fc8c3 Giorgos Verigakis
        return get_metadata_item(request, image_id, key)
81 432fc8c3 Giorgos Verigakis
    elif request.method == 'PUT':
82 432fc8c3 Giorgos Verigakis
        return create_metadata_item(request, image_id, key)
83 432fc8c3 Giorgos Verigakis
    elif request.method == 'DELETE':
84 432fc8c3 Giorgos Verigakis
        return delete_metadata_item(request, image_id, key)
85 432fc8c3 Giorgos Verigakis
    else:
86 432fc8c3 Giorgos Verigakis
        return method_not_allowed(request)
87 432fc8c3 Giorgos Verigakis
88 c36934a7 Giorgos Verigakis
89 c36934a7 Giorgos Verigakis
def image_to_dict(image, detail=True):
90 c36934a7 Giorgos Verigakis
    d = {'id': image.id, 'name': image.name}
91 c36934a7 Giorgos Verigakis
    if detail:
92 b36f78fa Giorgos Verigakis
        d['updated'] = util.isoformat(image.updated)
93 b36f78fa Giorgos Verigakis
        d['created'] = util.isoformat(image.created)
94 c36934a7 Giorgos Verigakis
        d['status'] = image.state
95 c36934a7 Giorgos Verigakis
        d['progress'] = 100 if image.state == 'ACTIVE' else 0
96 c36934a7 Giorgos Verigakis
        if image.sourcevm:
97 c36934a7 Giorgos Verigakis
            d['serverRef'] = image.sourcevm.id
98 aa197ee4 Vangelis Koukis
99 a62a4d20 Giorgos Verigakis
        metadata = {}
100 a62a4d20 Giorgos Verigakis
        for meta in ImageMetadata.objects.filter(image=image):
101 a62a4d20 Giorgos Verigakis
            metadata[meta.meta_key] = meta.meta_value
102 aa197ee4 Vangelis Koukis
103 a62a4d20 Giorgos Verigakis
        if metadata:
104 a62a4d20 Giorgos Verigakis
            d['metadata'] = {'values': metadata}
105 aa197ee4 Vangelis Koukis
106 c36934a7 Giorgos Verigakis
    return d
107 c36934a7 Giorgos Verigakis
108 432fc8c3 Giorgos Verigakis
def metadata_to_dict(image):
109 432fc8c3 Giorgos Verigakis
    image_meta = image.imagemetadata_set.all()
110 432fc8c3 Giorgos Verigakis
    return dict((meta.meta_key, meta.meta_value) for meta in image_meta)
111 432fc8c3 Giorgos Verigakis
112 c36934a7 Giorgos Verigakis
113 b36f78fa Giorgos Verigakis
@util.api_method('GET')
114 c36934a7 Giorgos Verigakis
def list_images(request, detail=False):
115 c36934a7 Giorgos Verigakis
    # Normal Response Codes: 200, 203
116 c36934a7 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
117 c36934a7 Giorgos Verigakis
    #                       serviceUnavailable (503),
118 c36934a7 Giorgos Verigakis
    #                       unauthorized (401),
119 c36934a7 Giorgos Verigakis
    #                       badRequest (400),
120 c36934a7 Giorgos Verigakis
    #                       overLimit (413)
121 93677203 Giorgos Verigakis
    
122 93677203 Giorgos Verigakis
    user_images = Image.objects.filter(Q(owner=request.user) | Q(public=True))
123 b36f78fa Giorgos Verigakis
    since = util.isoparse(request.GET.get('changes-since'))
124 75768d0e Giorgos Verigakis
    
125 d8e50a39 Giorgos Verigakis
    if since:
126 93677203 Giorgos Verigakis
        user_images = user_images.filter(updated__gte=since)
127 93677203 Giorgos Verigakis
        if not user_images:
128 d8e50a39 Giorgos Verigakis
            return HttpResponse(status=304)
129 93677203 Giorgos Verigakis
    else:
130 93677203 Giorgos Verigakis
        user_images = user_images.exclude(state='DELETED')
131 93677203 Giorgos Verigakis
    
132 93677203 Giorgos Verigakis
    images = [image_to_dict(image, detail) for image in user_images]
133 75768d0e Giorgos Verigakis
    
134 d8e50a39 Giorgos Verigakis
    if request.serialization == 'xml':
135 b36f78fa Giorgos Verigakis
        data = render_to_string('list_images.xml', {
136 b36f78fa Giorgos Verigakis
            'images': images,
137 b36f78fa Giorgos Verigakis
            'detail': detail})
138 c36934a7 Giorgos Verigakis
    else:
139 c36934a7 Giorgos Verigakis
        data = json.dumps({'images': {'values': images}})
140 aa197ee4 Vangelis Koukis
141 d8e50a39 Giorgos Verigakis
    return HttpResponse(data, status=200)
142 c36934a7 Giorgos Verigakis
143 b36f78fa Giorgos Verigakis
@util.api_method('POST')
144 c36934a7 Giorgos Verigakis
def create_image(request):
145 c36934a7 Giorgos Verigakis
    # Normal Response Code: 202
146 c36934a7 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
147 c36934a7 Giorgos Verigakis
    #                       serviceUnavailable (503),
148 c36934a7 Giorgos Verigakis
    #                       unauthorized (401),
149 c36934a7 Giorgos Verigakis
    #                       badMediaType(415),
150 c36934a7 Giorgos Verigakis
    #                       itemNotFound (404),
151 c36934a7 Giorgos Verigakis
    #                       badRequest (400),
152 c36934a7 Giorgos Verigakis
    #                       serverCapacityUnavailable (503),
153 c36934a7 Giorgos Verigakis
    #                       buildInProgress (409),
154 c36934a7 Giorgos Verigakis
    #                       resizeNotAllowed (403),
155 c36934a7 Giorgos Verigakis
    #                       backupOrResizeInProgress (409),
156 c36934a7 Giorgos Verigakis
    #                       overLimit (413)
157 aa197ee4 Vangelis Koukis
158 b36f78fa Giorgos Verigakis
    req = util.get_request_dict(request)
159 aa197ee4 Vangelis Koukis
160 c36934a7 Giorgos Verigakis
    try:
161 c36934a7 Giorgos Verigakis
        d = req['image']
162 432fc8c3 Giorgos Verigakis
        server_id = d['serverRef']
163 432fc8c3 Giorgos Verigakis
        name = d['name']
164 432fc8c3 Giorgos Verigakis
    except (KeyError, ValueError):
165 432fc8c3 Giorgos Verigakis
        raise BadRequest('Malformed request.')
166 aa197ee4 Vangelis Koukis
167 40777cc8 Giorgos Verigakis
    owner = request.user
168 b36f78fa Giorgos Verigakis
    vm = util.get_vm(server_id, owner)
169 838c404d Giorgos Verigakis
    image = Image.objects.create(name=name, owner=owner, sourcevm=vm)
170 aa197ee4 Vangelis Koukis
171 c36934a7 Giorgos Verigakis
    imagedict = image_to_dict(image)
172 d8e50a39 Giorgos Verigakis
    if request.serialization == 'xml':
173 c36934a7 Giorgos Verigakis
        data = render_to_string('image.xml', {'image': imagedict})
174 c36934a7 Giorgos Verigakis
    else:
175 c36934a7 Giorgos Verigakis
        data = json.dumps({'image': imagedict})
176 aa197ee4 Vangelis Koukis
177 c36934a7 Giorgos Verigakis
    return HttpResponse(data, status=202)
178 c36934a7 Giorgos Verigakis
179 b36f78fa Giorgos Verigakis
@util.api_method('GET')
180 c36934a7 Giorgos Verigakis
def get_image_details(request, image_id):
181 c36934a7 Giorgos Verigakis
    # Normal Response Codes: 200, 203
182 c36934a7 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
183 c36934a7 Giorgos Verigakis
    #                       serviceUnavailable (503),
184 c36934a7 Giorgos Verigakis
    #                       unauthorized (401),
185 c36934a7 Giorgos Verigakis
    #                       badRequest (400),
186 c36934a7 Giorgos Verigakis
    #                       itemNotFound (404),
187 c36934a7 Giorgos Verigakis
    #                       overLimit (413)
188 aa197ee4 Vangelis Koukis
189 b36f78fa Giorgos Verigakis
    image = util.get_image(image_id, request.user)
190 d8e50a39 Giorgos Verigakis
    imagedict = image_to_dict(image)
191 aa197ee4 Vangelis Koukis
192 d8e50a39 Giorgos Verigakis
    if request.serialization == 'xml':
193 c36934a7 Giorgos Verigakis
        data = render_to_string('image.xml', {'image': imagedict})
194 c36934a7 Giorgos Verigakis
    else:
195 c36934a7 Giorgos Verigakis
        data = json.dumps({'image': imagedict})
196 aa197ee4 Vangelis Koukis
197 c36934a7 Giorgos Verigakis
    return HttpResponse(data, status=200)
198 c36934a7 Giorgos Verigakis
199 b36f78fa Giorgos Verigakis
@util.api_method('DELETE')
200 c36934a7 Giorgos Verigakis
def delete_image(request, image_id):
201 c36934a7 Giorgos Verigakis
    # Normal Response Code: 204
202 c36934a7 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
203 c36934a7 Giorgos Verigakis
    #                       serviceUnavailable (503),
204 c36934a7 Giorgos Verigakis
    #                       unauthorized (401),
205 c36934a7 Giorgos Verigakis
    #                       itemNotFound (404),
206 c36934a7 Giorgos Verigakis
    #                       overLimit (413)
207 aa197ee4 Vangelis Koukis
208 b36f78fa Giorgos Verigakis
    image = util.get_image(image_id, request.user)
209 93677203 Giorgos Verigakis
    image.state = 'DELETED'
210 93677203 Giorgos Verigakis
    image.save()
211 c36934a7 Giorgos Verigakis
    return HttpResponse(status=204)
212 432fc8c3 Giorgos Verigakis
213 b36f78fa Giorgos Verigakis
@util.api_method('GET')
214 432fc8c3 Giorgos Verigakis
def list_metadata(request, image_id):
215 432fc8c3 Giorgos Verigakis
    # Normal Response Codes: 200, 203
216 432fc8c3 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
217 432fc8c3 Giorgos Verigakis
    #                       serviceUnavailable (503),
218 432fc8c3 Giorgos Verigakis
    #                       unauthorized (401),
219 432fc8c3 Giorgos Verigakis
    #                       badRequest (400),
220 432fc8c3 Giorgos Verigakis
    #                       overLimit (413)
221 432fc8c3 Giorgos Verigakis
222 b36f78fa Giorgos Verigakis
    image = util.get_image(image_id, request.user)
223 432fc8c3 Giorgos Verigakis
    metadata = metadata_to_dict(image)
224 b36f78fa Giorgos Verigakis
    return util.render_metadata(request, metadata, use_values=True, status=200)
225 432fc8c3 Giorgos Verigakis
226 b36f78fa Giorgos Verigakis
@util.api_method('POST')
227 432fc8c3 Giorgos Verigakis
def update_metadata(request, image_id):
228 432fc8c3 Giorgos Verigakis
    # Normal Response Code: 201
229 432fc8c3 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
230 432fc8c3 Giorgos Verigakis
    #                       serviceUnavailable (503),
231 432fc8c3 Giorgos Verigakis
    #                       unauthorized (401),
232 432fc8c3 Giorgos Verigakis
    #                       badRequest (400),
233 432fc8c3 Giorgos Verigakis
    #                       buildInProgress (409),
234 432fc8c3 Giorgos Verigakis
    #                       badMediaType(415),
235 432fc8c3 Giorgos Verigakis
    #                       overLimit (413)
236 432fc8c3 Giorgos Verigakis
237 b36f78fa Giorgos Verigakis
    image = util.get_image(image_id, request.user)
238 b36f78fa Giorgos Verigakis
    req = util.get_request_dict(request)
239 432fc8c3 Giorgos Verigakis
    try:
240 432fc8c3 Giorgos Verigakis
        metadata = req['metadata']
241 432fc8c3 Giorgos Verigakis
        assert isinstance(metadata, dict)
242 432fc8c3 Giorgos Verigakis
    except (KeyError, AssertionError):
243 432fc8c3 Giorgos Verigakis
        raise BadRequest('Malformed request.')
244 432fc8c3 Giorgos Verigakis
245 432fc8c3 Giorgos Verigakis
    updated = {}
246 432fc8c3 Giorgos Verigakis
247 432fc8c3 Giorgos Verigakis
    for key, val in metadata.items():
248 432fc8c3 Giorgos Verigakis
        try:
249 432fc8c3 Giorgos Verigakis
            meta = ImageMetadata.objects.get(meta_key=key, image=image)
250 432fc8c3 Giorgos Verigakis
            meta.meta_value = val
251 432fc8c3 Giorgos Verigakis
            meta.save()
252 432fc8c3 Giorgos Verigakis
            updated[key] = val
253 432fc8c3 Giorgos Verigakis
        except ImageMetadata.DoesNotExist:
254 432fc8c3 Giorgos Verigakis
            pass    # Ignore non-existent metadata
255 5509b599 Giorgos Verigakis
    
256 5509b599 Giorgos Verigakis
    if updated:
257 5509b599 Giorgos Verigakis
        image.save()
258 5509b599 Giorgos Verigakis
    
259 b36f78fa Giorgos Verigakis
    return util.render_metadata(request, updated, status=201)
260 432fc8c3 Giorgos Verigakis
261 b36f78fa Giorgos Verigakis
@util.api_method('GET')
262 432fc8c3 Giorgos Verigakis
def get_metadata_item(request, image_id, key):
263 432fc8c3 Giorgos Verigakis
    # Normal Response Codes: 200, 203
264 432fc8c3 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
265 432fc8c3 Giorgos Verigakis
    #                       serviceUnavailable (503),
266 432fc8c3 Giorgos Verigakis
    #                       unauthorized (401),
267 432fc8c3 Giorgos Verigakis
    #                       itemNotFound (404),
268 432fc8c3 Giorgos Verigakis
    #                       badRequest (400),
269 432fc8c3 Giorgos Verigakis
    #                       overLimit (413)
270 aa197ee4 Vangelis Koukis
271 b36f78fa Giorgos Verigakis
    image = util.get_image(image_id, request.user)
272 b36f78fa Giorgos Verigakis
    meta = util.get_image_meta(image, key)
273 b36f78fa Giorgos Verigakis
    return util.render_meta(request, meta, status=200)
274 432fc8c3 Giorgos Verigakis
275 b36f78fa Giorgos Verigakis
@util.api_method('PUT')
276 432fc8c3 Giorgos Verigakis
def create_metadata_item(request, image_id, key):
277 432fc8c3 Giorgos Verigakis
    # Normal Response Code: 201
278 432fc8c3 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
279 432fc8c3 Giorgos Verigakis
    #                       serviceUnavailable (503),
280 432fc8c3 Giorgos Verigakis
    #                       unauthorized (401),
281 432fc8c3 Giorgos Verigakis
    #                       itemNotFound (404),
282 432fc8c3 Giorgos Verigakis
    #                       badRequest (400),
283 432fc8c3 Giorgos Verigakis
    #                       buildInProgress (409),
284 432fc8c3 Giorgos Verigakis
    #                       badMediaType(415),
285 432fc8c3 Giorgos Verigakis
    #                       overLimit (413)
286 432fc8c3 Giorgos Verigakis
287 b36f78fa Giorgos Verigakis
    image = util.get_image(image_id, request.user)
288 b36f78fa Giorgos Verigakis
    req = util.get_request_dict(request)
289 432fc8c3 Giorgos Verigakis
    try:
290 432fc8c3 Giorgos Verigakis
        metadict = req['meta']
291 432fc8c3 Giorgos Verigakis
        assert isinstance(metadict, dict)
292 432fc8c3 Giorgos Verigakis
        assert len(metadict) == 1
293 432fc8c3 Giorgos Verigakis
        assert key in metadict
294 432fc8c3 Giorgos Verigakis
    except (KeyError, AssertionError):
295 432fc8c3 Giorgos Verigakis
        raise BadRequest('Malformed request.')
296 b36f78fa Giorgos Verigakis
    
297 b36f78fa Giorgos Verigakis
    meta, created = ImageMetadata.objects.get_or_create(
298 b36f78fa Giorgos Verigakis
        meta_key=key,
299 b36f78fa Giorgos Verigakis
        image=image)
300 b36f78fa Giorgos Verigakis
    
301 432fc8c3 Giorgos Verigakis
    meta.meta_value = metadict[key]
302 432fc8c3 Giorgos Verigakis
    meta.save()
303 5509b599 Giorgos Verigakis
    image.save()
304 b36f78fa Giorgos Verigakis
    return util.render_meta(request, meta, status=201)
305 432fc8c3 Giorgos Verigakis
306 b36f78fa Giorgos Verigakis
@util.api_method('DELETE')
307 432fc8c3 Giorgos Verigakis
def delete_metadata_item(request, image_id, key):
308 432fc8c3 Giorgos Verigakis
    # Normal Response Code: 204
309 432fc8c3 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
310 432fc8c3 Giorgos Verigakis
    #                       serviceUnavailable (503),
311 432fc8c3 Giorgos Verigakis
    #                       unauthorized (401),
312 432fc8c3 Giorgos Verigakis
    #                       itemNotFound (404),
313 432fc8c3 Giorgos Verigakis
    #                       badRequest (400),
314 432fc8c3 Giorgos Verigakis
    #                       buildInProgress (409),
315 432fc8c3 Giorgos Verigakis
    #                       badMediaType(415),
316 432fc8c3 Giorgos Verigakis
    #                       overLimit (413),
317 aa197ee4 Vangelis Koukis
318 b36f78fa Giorgos Verigakis
    image = util.get_image(image_id, request.user)
319 b36f78fa Giorgos Verigakis
    meta = util.get_image_meta(image, key)
320 432fc8c3 Giorgos Verigakis
    meta.delete()
321 5509b599 Giorgos Verigakis
    image.save()
322 432fc8c3 Giorgos Verigakis
    return HttpResponse(status=204)