Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (11.5 kB)

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