Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / api / images.py @ bb54871d

History | View | Annotate | Download (11.3 kB)

1 6ef51e9f Giorgos Verigakis
# Copyright 2011-2012 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 2035039b Giorgos Verigakis
from logging import getLogger
35 2035039b Giorgos Verigakis
36 6ef51e9f Giorgos Verigakis
import dateutil.parser
37 6ef51e9f Giorgos Verigakis
38 d8e50a39 Giorgos Verigakis
from django.conf.urls.defaults import patterns
39 c36934a7 Giorgos Verigakis
from django.http import HttpResponse
40 c36934a7 Giorgos Verigakis
from django.template.loader import render_to_string
41 29a59bc1 Giorgos Verigakis
from django.utils import simplejson as json
42 c36934a7 Giorgos Verigakis
43 b36f78fa Giorgos Verigakis
from synnefo.api import util
44 b36f78fa Giorgos Verigakis
from synnefo.api.common import method_not_allowed
45 6ef51e9f Giorgos Verigakis
from synnefo.api.faults import BadRequest, ItemNotFound, ServiceUnavailable
46 6ef51e9f Giorgos Verigakis
from synnefo.api.util import api_method, isoformat, isoparse
47 6ef51e9f Giorgos Verigakis
from synnefo.plankton.backend import ImageBackend
48 b36f78fa Giorgos Verigakis
49 c36934a7 Giorgos Verigakis
50 9e98ba3c Giorgos Verigakis
log = getLogger('synnefo.api')
51 9e98ba3c Giorgos Verigakis
52 c36934a7 Giorgos Verigakis
urlpatterns = patterns('synnefo.api.images',
53 c36934a7 Giorgos Verigakis
    (r'^(?:/|.json|.xml)?$', 'demux'),
54 c36934a7 Giorgos Verigakis
    (r'^/detail(?:.json|.xml)?$', 'list_images', {'detail': True}),
55 6ef51e9f Giorgos Verigakis
    (r'^/([\w-]+)(?:.json|.xml)?$', 'image_demux'),
56 6ef51e9f Giorgos Verigakis
    (r'^/([\w-]+)/meta(?:.json|.xml)?$', 'metadata_demux'),
57 6ef51e9f Giorgos Verigakis
    (r'^/([\w-]+)/meta/(.+?)(?:.json|.xml)?$', 'metadata_item_demux')
58 c36934a7 Giorgos Verigakis
)
59 c36934a7 Giorgos Verigakis
60 c36934a7 Giorgos Verigakis
def demux(request):
61 c36934a7 Giorgos Verigakis
    if request.method == 'GET':
62 c36934a7 Giorgos Verigakis
        return list_images(request)
63 c36934a7 Giorgos Verigakis
    elif request.method == 'POST':
64 c36934a7 Giorgos Verigakis
        return create_image(request)
65 c36934a7 Giorgos Verigakis
    else:
66 d8e50a39 Giorgos Verigakis
        return method_not_allowed(request)
67 c36934a7 Giorgos Verigakis
68 c36934a7 Giorgos Verigakis
def image_demux(request, image_id):
69 c36934a7 Giorgos Verigakis
    if request.method == 'GET':
70 c36934a7 Giorgos Verigakis
        return get_image_details(request, image_id)
71 c36934a7 Giorgos Verigakis
    elif request.method == 'DELETE':
72 c36934a7 Giorgos Verigakis
        return delete_image(request, image_id)
73 c36934a7 Giorgos Verigakis
    else:
74 d8e50a39 Giorgos Verigakis
        return method_not_allowed(request)
75 c36934a7 Giorgos Verigakis
76 432fc8c3 Giorgos Verigakis
def metadata_demux(request, image_id):
77 432fc8c3 Giorgos Verigakis
    if request.method == 'GET':
78 432fc8c3 Giorgos Verigakis
        return list_metadata(request, image_id)
79 432fc8c3 Giorgos Verigakis
    elif request.method == 'POST':
80 432fc8c3 Giorgos Verigakis
        return update_metadata(request, image_id)
81 432fc8c3 Giorgos Verigakis
    else:
82 432fc8c3 Giorgos Verigakis
        return method_not_allowed(request)
83 432fc8c3 Giorgos Verigakis
84 432fc8c3 Giorgos Verigakis
def metadata_item_demux(request, image_id, key):
85 432fc8c3 Giorgos Verigakis
    if request.method == 'GET':
86 432fc8c3 Giorgos Verigakis
        return get_metadata_item(request, image_id, key)
87 432fc8c3 Giorgos Verigakis
    elif request.method == 'PUT':
88 432fc8c3 Giorgos Verigakis
        return create_metadata_item(request, image_id, key)
89 432fc8c3 Giorgos Verigakis
    elif request.method == 'DELETE':
90 432fc8c3 Giorgos Verigakis
        return delete_metadata_item(request, image_id, key)
91 432fc8c3 Giorgos Verigakis
    else:
92 432fc8c3 Giorgos Verigakis
        return method_not_allowed(request)
93 432fc8c3 Giorgos Verigakis
94 c36934a7 Giorgos Verigakis
95 c36934a7 Giorgos Verigakis
def image_to_dict(image, detail=True):
96 6ef51e9f Giorgos Verigakis
    d = dict(id=image['id'], name=image['name'])
97 c36934a7 Giorgos Verigakis
    if detail:
98 6ef51e9f Giorgos Verigakis
        d['updated'] = isoformat(dateutil.parser.parse(image['updated_at']))
99 6ef51e9f Giorgos Verigakis
        d['created'] = isoformat(dateutil.parser.parse(image['created_at']))
100 6ef51e9f Giorgos Verigakis
        d['status'] = 'DELETED' if image['deleted_at'] else 'ACTIVE'
101 6ef51e9f Giorgos Verigakis
        d['progress'] = 100 if image['status'] == 'available' else 0
102 6ef51e9f Giorgos Verigakis
        if image['properties']:
103 6ef51e9f Giorgos Verigakis
            d['metadata'] = {'values': image['properties']}
104 c36934a7 Giorgos Verigakis
    return d
105 c36934a7 Giorgos Verigakis
106 c36934a7 Giorgos Verigakis
107 6ef51e9f Giorgos Verigakis
@api_method('GET')
108 c36934a7 Giorgos Verigakis
def list_images(request, detail=False):
109 c36934a7 Giorgos Verigakis
    # Normal Response Codes: 200, 203
110 c36934a7 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
111 c36934a7 Giorgos Verigakis
    #                       serviceUnavailable (503),
112 c36934a7 Giorgos Verigakis
    #                       unauthorized (401),
113 c36934a7 Giorgos Verigakis
    #                       badRequest (400),
114 c36934a7 Giorgos Verigakis
    #                       overLimit (413)
115 93677203 Giorgos Verigakis
    
116 9e98ba3c Giorgos Verigakis
    log.debug('list_images detail=%s', detail)
117 4b3b8688 Giorgos Verigakis
    backend = ImageBackend(request.user_uniq)
118 75768d0e Giorgos Verigakis
    
119 6ef51e9f Giorgos Verigakis
    since = isoparse(request.GET.get('changes-since'))
120 d8e50a39 Giorgos Verigakis
    if since:
121 6ef51e9f Giorgos Verigakis
        images = []
122 6ef51e9f Giorgos Verigakis
        for image in backend.iter():
123 6ef51e9f Giorgos Verigakis
            updated = dateutil.parser.parse(image['updated_at'])
124 6ef51e9f Giorgos Verigakis
            if updated >= since:
125 6ef51e9f Giorgos Verigakis
                images.append(image)
126 6ef51e9f Giorgos Verigakis
        if not images:
127 d8e50a39 Giorgos Verigakis
            return HttpResponse(status=304)
128 93677203 Giorgos Verigakis
    else:
129 6ef51e9f Giorgos Verigakis
        images = backend.list()
130 93677203 Giorgos Verigakis
    
131 6ef51e9f Giorgos Verigakis
    reply = [image_to_dict(image, detail) for image in images]
132 75768d0e Giorgos Verigakis
    
133 d8e50a39 Giorgos Verigakis
    if request.serialization == 'xml':
134 6ef51e9f Giorgos Verigakis
        data = render_to_string('list_images.xml',
135 6ef51e9f Giorgos Verigakis
                                dict(images=reply, detail=detail))
136 c36934a7 Giorgos Verigakis
    else:
137 6ef51e9f Giorgos Verigakis
        data = json.dumps(dict(images={'values': reply}))
138 6ef51e9f Giorgos Verigakis
    
139 d8e50a39 Giorgos Verigakis
    return HttpResponse(data, status=200)
140 c36934a7 Giorgos Verigakis
141 6ef51e9f Giorgos Verigakis
142 6ef51e9f Giorgos Verigakis
@api_method('POST')
143 c36934a7 Giorgos Verigakis
def create_image(request):
144 c36934a7 Giorgos Verigakis
    # Normal Response Code: 202
145 c36934a7 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
146 c36934a7 Giorgos Verigakis
    #                       serviceUnavailable (503),
147 c36934a7 Giorgos Verigakis
    #                       unauthorized (401),
148 c36934a7 Giorgos Verigakis
    #                       badMediaType(415),
149 c36934a7 Giorgos Verigakis
    #                       itemNotFound (404),
150 c36934a7 Giorgos Verigakis
    #                       badRequest (400),
151 c36934a7 Giorgos Verigakis
    #                       serverCapacityUnavailable (503),
152 c36934a7 Giorgos Verigakis
    #                       buildInProgress (409),
153 c36934a7 Giorgos Verigakis
    #                       resizeNotAllowed (403),
154 c36934a7 Giorgos Verigakis
    #                       backupOrResizeInProgress (409),
155 c36934a7 Giorgos Verigakis
    #                       overLimit (413)
156 9e98ba3c Giorgos Verigakis
    
157 6ef51e9f Giorgos Verigakis
    raise ServiceUnavailable('Not supported.')
158 aa197ee4 Vangelis Koukis
159 aa197ee4 Vangelis Koukis
160 6ef51e9f Giorgos Verigakis
@api_method('GET')
161 c36934a7 Giorgos Verigakis
def get_image_details(request, image_id):
162 c36934a7 Giorgos Verigakis
    # Normal Response Codes: 200, 203
163 c36934a7 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
164 c36934a7 Giorgos Verigakis
    #                       serviceUnavailable (503),
165 c36934a7 Giorgos Verigakis
    #                       unauthorized (401),
166 c36934a7 Giorgos Verigakis
    #                       badRequest (400),
167 c36934a7 Giorgos Verigakis
    #                       itemNotFound (404),
168 c36934a7 Giorgos Verigakis
    #                       overLimit (413)
169 9e98ba3c Giorgos Verigakis
    
170 9e98ba3c Giorgos Verigakis
    log.debug('get_image_details %s', image_id)
171 4b3b8688 Giorgos Verigakis
    image = util.get_image(image_id, request.user_uniq)
172 6ef51e9f Giorgos Verigakis
    reply = image_to_dict(image)
173 6ef51e9f Giorgos Verigakis
    
174 d8e50a39 Giorgos Verigakis
    if request.serialization == 'xml':
175 6ef51e9f Giorgos Verigakis
        data = render_to_string('image.xml', dict(image=reply))
176 c36934a7 Giorgos Verigakis
    else:
177 6ef51e9f Giorgos Verigakis
        data = json.dumps(dict(image=reply))
178 6ef51e9f Giorgos Verigakis
    
179 c36934a7 Giorgos Verigakis
    return HttpResponse(data, status=200)
180 c36934a7 Giorgos Verigakis
181 6ef51e9f Giorgos Verigakis
182 6ef51e9f Giorgos Verigakis
@api_method('DELETE')
183 c36934a7 Giorgos Verigakis
def delete_image(request, image_id):
184 c36934a7 Giorgos Verigakis
    # Normal Response Code: 204
185 c36934a7 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
186 c36934a7 Giorgos Verigakis
    #                       serviceUnavailable (503),
187 c36934a7 Giorgos Verigakis
    #                       unauthorized (401),
188 c36934a7 Giorgos Verigakis
    #                       itemNotFound (404),
189 c36934a7 Giorgos Verigakis
    #                       overLimit (413)
190 9e98ba3c Giorgos Verigakis
    
191 9e98ba3c Giorgos Verigakis
    log.debug('delete_image %s', image_id)
192 4b3b8688 Giorgos Verigakis
    backend = ImageBackend(request.user_uniq)
193 6ef51e9f Giorgos Verigakis
    backend.delete(image_id)
194 6ef51e9f Giorgos Verigakis
    backend.close()
195 4b3b8688 Giorgos Verigakis
    log.info('User %s deleted image %s', request.user_uniq, image_id)
196 c36934a7 Giorgos Verigakis
    return HttpResponse(status=204)
197 432fc8c3 Giorgos Verigakis
198 6ef51e9f Giorgos Verigakis
199 6ef51e9f Giorgos Verigakis
@api_method('GET')
200 432fc8c3 Giorgos Verigakis
def list_metadata(request, image_id):
201 432fc8c3 Giorgos Verigakis
    # Normal Response Codes: 200, 203
202 432fc8c3 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
203 432fc8c3 Giorgos Verigakis
    #                       serviceUnavailable (503),
204 432fc8c3 Giorgos Verigakis
    #                       unauthorized (401),
205 432fc8c3 Giorgos Verigakis
    #                       badRequest (400),
206 432fc8c3 Giorgos Verigakis
    #                       overLimit (413)
207 9e98ba3c Giorgos Verigakis
    
208 9e98ba3c Giorgos Verigakis
    log.debug('list_image_metadata %s', image_id)
209 4b3b8688 Giorgos Verigakis
    image = util.get_image(image_id, request.user_uniq)
210 6ef51e9f Giorgos Verigakis
    metadata = image['properties']
211 b36f78fa Giorgos Verigakis
    return util.render_metadata(request, metadata, use_values=True, status=200)
212 432fc8c3 Giorgos Verigakis
213 6ef51e9f Giorgos Verigakis
214 6ef51e9f Giorgos Verigakis
@api_method('POST')
215 432fc8c3 Giorgos Verigakis
def update_metadata(request, image_id):
216 432fc8c3 Giorgos Verigakis
    # Normal Response Code: 201
217 432fc8c3 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
218 432fc8c3 Giorgos Verigakis
    #                       serviceUnavailable (503),
219 432fc8c3 Giorgos Verigakis
    #                       unauthorized (401),
220 432fc8c3 Giorgos Verigakis
    #                       badRequest (400),
221 432fc8c3 Giorgos Verigakis
    #                       buildInProgress (409),
222 432fc8c3 Giorgos Verigakis
    #                       badMediaType(415),
223 432fc8c3 Giorgos Verigakis
    #                       overLimit (413)
224 9e98ba3c Giorgos Verigakis
    
225 b36f78fa Giorgos Verigakis
    req = util.get_request_dict(request)
226 9e98ba3c Giorgos Verigakis
    log.debug('update_image_metadata %s %s', image_id, req)
227 4b3b8688 Giorgos Verigakis
    image = util.get_image(image_id, request.user_uniq)
228 432fc8c3 Giorgos Verigakis
    try:
229 432fc8c3 Giorgos Verigakis
        metadata = req['metadata']
230 432fc8c3 Giorgos Verigakis
        assert isinstance(metadata, dict)
231 432fc8c3 Giorgos Verigakis
    except (KeyError, AssertionError):
232 432fc8c3 Giorgos Verigakis
        raise BadRequest('Malformed request.')
233 5509b599 Giorgos Verigakis
    
234 6ef51e9f Giorgos Verigakis
    properties = image['properties']
235 6ef51e9f Giorgos Verigakis
    properties.update(metadata)
236 6ef51e9f Giorgos Verigakis
    
237 4b3b8688 Giorgos Verigakis
    backend = ImageBackend(request.user_uniq)
238 6ef51e9f Giorgos Verigakis
    backend.update(image_id, dict(properties=properties))
239 6ef51e9f Giorgos Verigakis
    backend.close()
240 5509b599 Giorgos Verigakis
    
241 6ef51e9f Giorgos Verigakis
    return util.render_metadata(request, properties, status=201)
242 432fc8c3 Giorgos Verigakis
243 6ef51e9f Giorgos Verigakis
244 6ef51e9f Giorgos Verigakis
@api_method('GET')
245 432fc8c3 Giorgos Verigakis
def get_metadata_item(request, image_id, key):
246 432fc8c3 Giorgos Verigakis
    # Normal Response Codes: 200, 203
247 432fc8c3 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
248 432fc8c3 Giorgos Verigakis
    #                       serviceUnavailable (503),
249 432fc8c3 Giorgos Verigakis
    #                       unauthorized (401),
250 432fc8c3 Giorgos Verigakis
    #                       itemNotFound (404),
251 432fc8c3 Giorgos Verigakis
    #                       badRequest (400),
252 432fc8c3 Giorgos Verigakis
    #                       overLimit (413)
253 9e98ba3c Giorgos Verigakis
    
254 9e98ba3c Giorgos Verigakis
    log.debug('get_image_metadata_item %s %s', image_id, key)
255 4b3b8688 Giorgos Verigakis
    image = util.get_image(image_id, request.user_uniq)
256 6ef51e9f Giorgos Verigakis
    val = image['properties'].get(key)
257 6ef51e9f Giorgos Verigakis
    if val is None:
258 6ef51e9f Giorgos Verigakis
        raise ItemNotFound('Metadata key not found.')
259 6ef51e9f Giorgos Verigakis
    return util.render_meta(request, {key: val}, status=200)
260 6ef51e9f Giorgos Verigakis
261 432fc8c3 Giorgos Verigakis
262 6ef51e9f Giorgos Verigakis
@api_method('PUT')
263 432fc8c3 Giorgos Verigakis
def create_metadata_item(request, image_id, key):
264 432fc8c3 Giorgos Verigakis
    # Normal Response Code: 201
265 432fc8c3 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
266 432fc8c3 Giorgos Verigakis
    #                       serviceUnavailable (503),
267 432fc8c3 Giorgos Verigakis
    #                       unauthorized (401),
268 432fc8c3 Giorgos Verigakis
    #                       itemNotFound (404),
269 432fc8c3 Giorgos Verigakis
    #                       badRequest (400),
270 432fc8c3 Giorgos Verigakis
    #                       buildInProgress (409),
271 432fc8c3 Giorgos Verigakis
    #                       badMediaType(415),
272 432fc8c3 Giorgos Verigakis
    #                       overLimit (413)
273 9e98ba3c Giorgos Verigakis
    
274 b36f78fa Giorgos Verigakis
    req = util.get_request_dict(request)
275 9e98ba3c Giorgos Verigakis
    log.debug('create_image_metadata_item %s %s %s', image_id, key, req)
276 432fc8c3 Giorgos Verigakis
    try:
277 432fc8c3 Giorgos Verigakis
        metadict = req['meta']
278 432fc8c3 Giorgos Verigakis
        assert isinstance(metadict, dict)
279 432fc8c3 Giorgos Verigakis
        assert len(metadict) == 1
280 432fc8c3 Giorgos Verigakis
        assert key in metadict
281 432fc8c3 Giorgos Verigakis
    except (KeyError, AssertionError):
282 432fc8c3 Giorgos Verigakis
        raise BadRequest('Malformed request.')
283 6ef51e9f Giorgos Verigakis
284 6ef51e9f Giorgos Verigakis
    val = metadict[key]
285 4b3b8688 Giorgos Verigakis
    image = util.get_image(image_id, request.user_uniq)
286 6ef51e9f Giorgos Verigakis
    properties = image['properties']
287 6ef51e9f Giorgos Verigakis
    properties[key] = val
288 b36f78fa Giorgos Verigakis
    
289 4b3b8688 Giorgos Verigakis
    backend = ImageBackend(request.user_uniq)
290 6ef51e9f Giorgos Verigakis
    backend.update(image_id, dict(properties=properties))
291 6ef51e9f Giorgos Verigakis
    backend.close()
292 b36f78fa Giorgos Verigakis
    
293 6ef51e9f Giorgos Verigakis
    return util.render_meta(request, {key: val}, status=201)
294 6ef51e9f Giorgos Verigakis
295 432fc8c3 Giorgos Verigakis
296 6ef51e9f Giorgos Verigakis
@api_method('DELETE')
297 432fc8c3 Giorgos Verigakis
def delete_metadata_item(request, image_id, key):
298 432fc8c3 Giorgos Verigakis
    # Normal Response Code: 204
299 432fc8c3 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
300 432fc8c3 Giorgos Verigakis
    #                       serviceUnavailable (503),
301 432fc8c3 Giorgos Verigakis
    #                       unauthorized (401),
302 432fc8c3 Giorgos Verigakis
    #                       itemNotFound (404),
303 432fc8c3 Giorgos Verigakis
    #                       badRequest (400),
304 432fc8c3 Giorgos Verigakis
    #                       buildInProgress (409),
305 432fc8c3 Giorgos Verigakis
    #                       badMediaType(415),
306 432fc8c3 Giorgos Verigakis
    #                       overLimit (413),
307 9e98ba3c Giorgos Verigakis
    
308 9e98ba3c Giorgos Verigakis
    log.debug('delete_image_metadata_item %s %s', image_id, key)
309 4b3b8688 Giorgos Verigakis
    image = util.get_image(image_id, request.user_uniq)
310 6ef51e9f Giorgos Verigakis
    properties = image['properties']
311 6ef51e9f Giorgos Verigakis
    properties.pop(key, None)
312 6ef51e9f Giorgos Verigakis
    
313 4b3b8688 Giorgos Verigakis
    backend = ImageBackend(request.user_uniq)
314 6ef51e9f Giorgos Verigakis
    backend.update(image_id, dict(properties=properties))
315 6ef51e9f Giorgos Verigakis
    backend.close()
316 6ef51e9f Giorgos Verigakis
    
317 432fc8c3 Giorgos Verigakis
    return HttpResponse(status=204)