Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (12.6 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 14c94c48 Christos Stavrakakis
from itertools import ifilter
36 2035039b Giorgos Verigakis
37 b3fd98ae Christos Stavrakakis
from dateutil.parser import parse as date_parse
38 6ef51e9f Giorgos Verigakis
39 6b256427 Christos Stavrakakis
from django.conf.urls import patterns
40 c36934a7 Giorgos Verigakis
from django.http import HttpResponse
41 c36934a7 Giorgos Verigakis
from django.template.loader import render_to_string
42 29a59bc1 Giorgos Verigakis
from django.utils import simplejson as json
43 c36934a7 Giorgos Verigakis
44 b3fd98ae Christos Stavrakakis
from snf_django.lib import api
45 b3fd98ae Christos Stavrakakis
from snf_django.lib.api import faults, utils
46 b36f78fa Giorgos Verigakis
from synnefo.api import util
47 b3fd98ae Christos Stavrakakis
from synnefo.plankton.utils import image_backend
48 b36f78fa Giorgos Verigakis
49 c36934a7 Giorgos Verigakis
50 b3fd98ae Christos Stavrakakis
log = getLogger(__name__)
51 9e98ba3c Giorgos Verigakis
52 e440e835 Christos Stavrakakis
urlpatterns = patterns(
53 e440e835 Christos Stavrakakis
    'synnefo.api.images',
54 c36934a7 Giorgos Verigakis
    (r'^(?:/|.json|.xml)?$', 'demux'),
55 c36934a7 Giorgos Verigakis
    (r'^/detail(?:.json|.xml)?$', 'list_images', {'detail': True}),
56 6ef51e9f Giorgos Verigakis
    (r'^/([\w-]+)(?:.json|.xml)?$', 'image_demux'),
57 8a992938 Christos Stavrakakis
    (r'^/([\w-]+)/metadata(?:.json|.xml)?$', 'metadata_demux'),
58 8a992938 Christos Stavrakakis
    (r'^/([\w-]+)/metadata/(.+?)(?:.json|.xml)?$', 'metadata_item_demux')
59 c36934a7 Giorgos Verigakis
)
60 c36934a7 Giorgos Verigakis
61 e440e835 Christos Stavrakakis
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 2aba7764 Sofia Papagiannaki
        return api.api_method_not_allowed(request,
69 2aba7764 Sofia Papagiannaki
                                          allowed_methods=['GET', 'POST'])
70 c36934a7 Giorgos Verigakis
71 e440e835 Christos Stavrakakis
72 c36934a7 Giorgos Verigakis
def image_demux(request, image_id):
73 c36934a7 Giorgos Verigakis
    if request.method == 'GET':
74 c36934a7 Giorgos Verigakis
        return get_image_details(request, image_id)
75 c36934a7 Giorgos Verigakis
    elif request.method == 'DELETE':
76 c36934a7 Giorgos Verigakis
        return delete_image(request, image_id)
77 c36934a7 Giorgos Verigakis
    else:
78 2aba7764 Sofia Papagiannaki
        return api.api_method_not_allowed(request,
79 2aba7764 Sofia Papagiannaki
                                          allowed_methods=['GET', 'DELETE'])
80 c36934a7 Giorgos Verigakis
81 e440e835 Christos Stavrakakis
82 432fc8c3 Giorgos Verigakis
def metadata_demux(request, image_id):
83 432fc8c3 Giorgos Verigakis
    if request.method == 'GET':
84 432fc8c3 Giorgos Verigakis
        return list_metadata(request, image_id)
85 432fc8c3 Giorgos Verigakis
    elif request.method == 'POST':
86 432fc8c3 Giorgos Verigakis
        return update_metadata(request, image_id)
87 432fc8c3 Giorgos Verigakis
    else:
88 2aba7764 Sofia Papagiannaki
        return api.api_method_not_allowed(request,
89 2aba7764 Sofia Papagiannaki
                                          allowed_methods=['GET', 'POST'])
90 432fc8c3 Giorgos Verigakis
91 e440e835 Christos Stavrakakis
92 432fc8c3 Giorgos Verigakis
def metadata_item_demux(request, image_id, key):
93 432fc8c3 Giorgos Verigakis
    if request.method == 'GET':
94 432fc8c3 Giorgos Verigakis
        return get_metadata_item(request, image_id, key)
95 432fc8c3 Giorgos Verigakis
    elif request.method == 'PUT':
96 432fc8c3 Giorgos Verigakis
        return create_metadata_item(request, image_id, key)
97 432fc8c3 Giorgos Verigakis
    elif request.method == 'DELETE':
98 432fc8c3 Giorgos Verigakis
        return delete_metadata_item(request, image_id, key)
99 432fc8c3 Giorgos Verigakis
    else:
100 2aba7764 Sofia Papagiannaki
        return api.api_method_not_allowed(request,
101 2aba7764 Sofia Papagiannaki
                                          allowed_methods=['GET',
102 2aba7764 Sofia Papagiannaki
                                                           'PUT',
103 2aba7764 Sofia Papagiannaki
                                                           'DELETE'])
104 432fc8c3 Giorgos Verigakis
105 c36934a7 Giorgos Verigakis
106 eb3c549b Christos Stavrakakis
API_STATUS_FROM_IMAGE_STATUS = {
107 eb3c549b Christos Stavrakakis
    "CREATING": "SAVING",
108 eb3c549b Christos Stavrakakis
    "AVAILABLE": "ACTIVE",
109 eb3c549b Christos Stavrakakis
    "ERROR": "ERROR",
110 eb3c549b Christos Stavrakakis
    "DELETED": "DELETED"}
111 eb3c549b Christos Stavrakakis
112 eb3c549b Christos Stavrakakis
113 c36934a7 Giorgos Verigakis
def image_to_dict(image, detail=True):
114 6ef51e9f Giorgos Verigakis
    d = dict(id=image['id'], name=image['name'])
115 c36934a7 Giorgos Verigakis
    if detail:
116 b3fd98ae Christos Stavrakakis
        d['updated'] = utils.isoformat(date_parse(image['updated_at']))
117 b3fd98ae Christos Stavrakakis
        d['created'] = utils.isoformat(date_parse(image['created_at']))
118 eb3c549b Christos Stavrakakis
        img_status = image.get("status", "").upper()
119 eb3c549b Christos Stavrakakis
        status = API_STATUS_FROM_IMAGE_STATUS.get(img_status, "UNKNOWN")
120 eb3c549b Christos Stavrakakis
        d['status'] = status
121 eb3c549b Christos Stavrakakis
        d['progress'] = 100 if status == 'ACTIVE' else 0
122 936e0be3 Christos Stavrakakis
        d['user_id'] = image['owner']
123 936e0be3 Christos Stavrakakis
        d['tenant_id'] = image['owner']
124 d984eedc Christos Stavrakakis
        d['public'] = image["is_public"]
125 27e57c27 Christos Stavrakakis
        d['links'] = util.image_to_links(image["id"])
126 936e0be3 Christos Stavrakakis
        if image["properties"]:
127 5029ff36 Christos Stavrakakis
            d['metadata'] = image['properties']
128 936e0be3 Christos Stavrakakis
        else:
129 936e0be3 Christos Stavrakakis
            d['metadata'] = {}
130 e7953d63 Christos Stavrakakis
        d["is_snapshot"] = image["is_snapshot"]
131 c36934a7 Giorgos Verigakis
    return d
132 c36934a7 Giorgos Verigakis
133 c36934a7 Giorgos Verigakis
134 b3fd98ae Christos Stavrakakis
@api.api_method("GET", user_required=True, logger=log)
135 c36934a7 Giorgos Verigakis
def list_images(request, detail=False):
136 c36934a7 Giorgos Verigakis
    # Normal Response Codes: 200, 203
137 c36934a7 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
138 c36934a7 Giorgos Verigakis
    #                       serviceUnavailable (503),
139 c36934a7 Giorgos Verigakis
    #                       unauthorized (401),
140 c36934a7 Giorgos Verigakis
    #                       badRequest (400),
141 c36934a7 Giorgos Verigakis
    #                       overLimit (413)
142 cd2b0bf5 Christos Stavrakakis
143 0c37a721 Christos Stavrakakis
    log.debug('list_images detail=%s', detail)
144 cda71050 Christos Stavrakakis
    since = utils.isoparse(request.GET.get('changes-since'))
145 b3a77863 Christos Stavrakakis
    with image_backend(request.user_uniq) as backend:
146 14c94c48 Christos Stavrakakis
        images = backend.list_images()
147 b3a77863 Christos Stavrakakis
        if since:
148 14c94c48 Christos Stavrakakis
            updated_since = lambda img: date_parse(img["updated_at"]) >= since
149 14c94c48 Christos Stavrakakis
            images = ifilter(updated_since, images)
150 b3a77863 Christos Stavrakakis
            if not images:
151 b3a77863 Christos Stavrakakis
                return HttpResponse(status=304)
152 cd2b0bf5 Christos Stavrakakis
153 cd2b0bf5 Christos Stavrakakis
    images = sorted(images, key=lambda x: x['id'])
154 6ef51e9f Giorgos Verigakis
    reply = [image_to_dict(image, detail) for image in images]
155 cd2b0bf5 Christos Stavrakakis
156 d8e50a39 Giorgos Verigakis
    if request.serialization == 'xml':
157 6ef51e9f Giorgos Verigakis
        data = render_to_string('list_images.xml',
158 6ef51e9f Giorgos Verigakis
                                dict(images=reply, detail=detail))
159 c36934a7 Giorgos Verigakis
    else:
160 5029ff36 Christos Stavrakakis
        data = json.dumps(dict(images=reply))
161 cd2b0bf5 Christos Stavrakakis
162 d8e50a39 Giorgos Verigakis
    return HttpResponse(data, status=200)
163 c36934a7 Giorgos Verigakis
164 6ef51e9f Giorgos Verigakis
165 b3fd98ae Christos Stavrakakis
@api.api_method('POST', user_required=True, logger=log)
166 c36934a7 Giorgos Verigakis
def create_image(request):
167 c36934a7 Giorgos Verigakis
    # Normal Response Code: 202
168 c36934a7 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
169 c36934a7 Giorgos Verigakis
    #                       serviceUnavailable (503),
170 c36934a7 Giorgos Verigakis
    #                       unauthorized (401),
171 c36934a7 Giorgos Verigakis
    #                       badMediaType(415),
172 c36934a7 Giorgos Verigakis
    #                       itemNotFound (404),
173 c36934a7 Giorgos Verigakis
    #                       badRequest (400),
174 c36934a7 Giorgos Verigakis
    #                       serverCapacityUnavailable (503),
175 c36934a7 Giorgos Verigakis
    #                       buildInProgress (409),
176 c36934a7 Giorgos Verigakis
    #                       resizeNotAllowed (403),
177 c36934a7 Giorgos Verigakis
    #                       backupOrResizeInProgress (409),
178 c36934a7 Giorgos Verigakis
    #                       overLimit (413)
179 cd2b0bf5 Christos Stavrakakis
180 bd40abfa Christos Stavrakakis
    raise faults.NotImplemented('Not supported.')
181 aa197ee4 Vangelis Koukis
182 aa197ee4 Vangelis Koukis
183 b3fd98ae Christos Stavrakakis
@api.api_method('GET', user_required=True, logger=log)
184 c36934a7 Giorgos Verigakis
def get_image_details(request, image_id):
185 c36934a7 Giorgos Verigakis
    # Normal Response Codes: 200, 203
186 c36934a7 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
187 c36934a7 Giorgos Verigakis
    #                       serviceUnavailable (503),
188 c36934a7 Giorgos Verigakis
    #                       unauthorized (401),
189 c36934a7 Giorgos Verigakis
    #                       badRequest (400),
190 c36934a7 Giorgos Verigakis
    #                       itemNotFound (404),
191 c36934a7 Giorgos Verigakis
    #                       overLimit (413)
192 cd2b0bf5 Christos Stavrakakis
193 0c37a721 Christos Stavrakakis
    log.debug('get_image_details %s', image_id)
194 cda71050 Christos Stavrakakis
    with image_backend(request.user_uniq) as backend:
195 cda71050 Christos Stavrakakis
        image = backend.get_image(image_id)
196 6ef51e9f Giorgos Verigakis
    reply = image_to_dict(image)
197 cd2b0bf5 Christos Stavrakakis
198 d8e50a39 Giorgos Verigakis
    if request.serialization == 'xml':
199 6ef51e9f Giorgos Verigakis
        data = render_to_string('image.xml', dict(image=reply))
200 c36934a7 Giorgos Verigakis
    else:
201 6ef51e9f Giorgos Verigakis
        data = json.dumps(dict(image=reply))
202 cd2b0bf5 Christos Stavrakakis
203 c36934a7 Giorgos Verigakis
    return HttpResponse(data, status=200)
204 c36934a7 Giorgos Verigakis
205 6ef51e9f Giorgos Verigakis
206 b3fd98ae Christos Stavrakakis
@api.api_method('DELETE', user_required=True, logger=log)
207 c36934a7 Giorgos Verigakis
def delete_image(request, image_id):
208 c36934a7 Giorgos Verigakis
    # Normal Response Code: 204
209 c36934a7 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
210 c36934a7 Giorgos Verigakis
    #                       serviceUnavailable (503),
211 c36934a7 Giorgos Verigakis
    #                       unauthorized (401),
212 c36934a7 Giorgos Verigakis
    #                       itemNotFound (404),
213 c36934a7 Giorgos Verigakis
    #                       overLimit (413)
214 cd2b0bf5 Christos Stavrakakis
215 bf5c82dc Christos Stavrakakis
    log.info('delete_image %s', image_id)
216 b3a77863 Christos Stavrakakis
    with image_backend(request.user_uniq) as backend:
217 f13aab5d Christos Stavrakakis
        backend.unregister(image_id)
218 4b3b8688 Giorgos Verigakis
    log.info('User %s deleted image %s', request.user_uniq, image_id)
219 c36934a7 Giorgos Verigakis
    return HttpResponse(status=204)
220 432fc8c3 Giorgos Verigakis
221 6ef51e9f Giorgos Verigakis
222 b3fd98ae Christos Stavrakakis
@api.api_method('GET', user_required=True, logger=log)
223 432fc8c3 Giorgos Verigakis
def list_metadata(request, image_id):
224 432fc8c3 Giorgos Verigakis
    # Normal Response Codes: 200, 203
225 432fc8c3 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
226 432fc8c3 Giorgos Verigakis
    #                       serviceUnavailable (503),
227 432fc8c3 Giorgos Verigakis
    #                       unauthorized (401),
228 432fc8c3 Giorgos Verigakis
    #                       badRequest (400),
229 432fc8c3 Giorgos Verigakis
    #                       overLimit (413)
230 cd2b0bf5 Christos Stavrakakis
231 0c37a721 Christos Stavrakakis
    log.debug('list_image_metadata %s', image_id)
232 cda71050 Christos Stavrakakis
    with image_backend(request.user_uniq) as backend:
233 cda71050 Christos Stavrakakis
        image = backend.get_image(image_id)
234 6ef51e9f Giorgos Verigakis
    metadata = image['properties']
235 5029ff36 Christos Stavrakakis
    return util.render_metadata(request, metadata, use_values=False,
236 5029ff36 Christos Stavrakakis
                                status=200)
237 432fc8c3 Giorgos Verigakis
238 6ef51e9f Giorgos Verigakis
239 b3fd98ae Christos Stavrakakis
@api.api_method('POST', user_required=True, logger=log)
240 432fc8c3 Giorgos Verigakis
def update_metadata(request, image_id):
241 432fc8c3 Giorgos Verigakis
    # Normal Response Code: 201
242 432fc8c3 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
243 432fc8c3 Giorgos Verigakis
    #                       serviceUnavailable (503),
244 432fc8c3 Giorgos Verigakis
    #                       unauthorized (401),
245 432fc8c3 Giorgos Verigakis
    #                       badRequest (400),
246 432fc8c3 Giorgos Verigakis
    #                       buildInProgress (409),
247 432fc8c3 Giorgos Verigakis
    #                       badMediaType(415),
248 432fc8c3 Giorgos Verigakis
    #                       overLimit (413)
249 cd2b0bf5 Christos Stavrakakis
250 b3fd98ae Christos Stavrakakis
    req = utils.get_request_dict(request)
251 bf5c82dc Christos Stavrakakis
    log.info('update_image_metadata %s %s', image_id, req)
252 cda71050 Christos Stavrakakis
    with image_backend(request.user_uniq) as backend:
253 cda71050 Christos Stavrakakis
        image = backend.get_image(image_id)
254 cda71050 Christos Stavrakakis
        try:
255 cda71050 Christos Stavrakakis
            metadata = req['metadata']
256 cda71050 Christos Stavrakakis
            assert isinstance(metadata, dict)
257 cda71050 Christos Stavrakakis
        except (KeyError, AssertionError):
258 cda71050 Christos Stavrakakis
            raise faults.BadRequest('Malformed request.')
259 cd2b0bf5 Christos Stavrakakis
260 cda71050 Christos Stavrakakis
        properties = image['properties']
261 cda71050 Christos Stavrakakis
        properties.update(metadata)
262 cd2b0bf5 Christos Stavrakakis
263 cda71050 Christos Stavrakakis
        backend.update_metadata(image_id, dict(properties=properties))
264 cd2b0bf5 Christos Stavrakakis
265 6ef51e9f Giorgos Verigakis
    return util.render_metadata(request, properties, status=201)
266 432fc8c3 Giorgos Verigakis
267 6ef51e9f Giorgos Verigakis
268 b3fd98ae Christos Stavrakakis
@api.api_method('GET', user_required=True, logger=log)
269 432fc8c3 Giorgos Verigakis
def get_metadata_item(request, image_id, key):
270 432fc8c3 Giorgos Verigakis
    # Normal Response Codes: 200, 203
271 432fc8c3 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
272 432fc8c3 Giorgos Verigakis
    #                       serviceUnavailable (503),
273 432fc8c3 Giorgos Verigakis
    #                       unauthorized (401),
274 432fc8c3 Giorgos Verigakis
    #                       itemNotFound (404),
275 432fc8c3 Giorgos Verigakis
    #                       badRequest (400),
276 432fc8c3 Giorgos Verigakis
    #                       overLimit (413)
277 cd2b0bf5 Christos Stavrakakis
278 0c37a721 Christos Stavrakakis
    log.debug('get_image_metadata_item %s %s', image_id, key)
279 cda71050 Christos Stavrakakis
    with image_backend(request.user_uniq) as backend:
280 cda71050 Christos Stavrakakis
        image = backend.get_image(image_id)
281 6ef51e9f Giorgos Verigakis
    val = image['properties'].get(key)
282 6ef51e9f Giorgos Verigakis
    if val is None:
283 bd40abfa Christos Stavrakakis
        raise faults.ItemNotFound('Metadata key not found.')
284 6ef51e9f Giorgos Verigakis
    return util.render_meta(request, {key: val}, status=200)
285 6ef51e9f Giorgos Verigakis
286 432fc8c3 Giorgos Verigakis
287 b3fd98ae Christos Stavrakakis
@api.api_method('PUT', user_required=True, logger=log)
288 432fc8c3 Giorgos Verigakis
def create_metadata_item(request, image_id, key):
289 432fc8c3 Giorgos Verigakis
    # Normal Response Code: 201
290 432fc8c3 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
291 432fc8c3 Giorgos Verigakis
    #                       serviceUnavailable (503),
292 432fc8c3 Giorgos Verigakis
    #                       unauthorized (401),
293 432fc8c3 Giorgos Verigakis
    #                       itemNotFound (404),
294 432fc8c3 Giorgos Verigakis
    #                       badRequest (400),
295 432fc8c3 Giorgos Verigakis
    #                       buildInProgress (409),
296 432fc8c3 Giorgos Verigakis
    #                       badMediaType(415),
297 432fc8c3 Giorgos Verigakis
    #                       overLimit (413)
298 cd2b0bf5 Christos Stavrakakis
299 b3fd98ae Christos Stavrakakis
    req = utils.get_request_dict(request)
300 bf5c82dc Christos Stavrakakis
    log.info('create_image_metadata_item %s %s %s', image_id, key, req)
301 432fc8c3 Giorgos Verigakis
    try:
302 432fc8c3 Giorgos Verigakis
        metadict = req['meta']
303 432fc8c3 Giorgos Verigakis
        assert isinstance(metadict, dict)
304 432fc8c3 Giorgos Verigakis
        assert len(metadict) == 1
305 432fc8c3 Giorgos Verigakis
        assert key in metadict
306 432fc8c3 Giorgos Verigakis
    except (KeyError, AssertionError):
307 bd40abfa Christos Stavrakakis
        raise faults.BadRequest('Malformed request.')
308 6ef51e9f Giorgos Verigakis
309 6ef51e9f Giorgos Verigakis
    val = metadict[key]
310 b3a77863 Christos Stavrakakis
    with image_backend(request.user_uniq) as backend:
311 cda71050 Christos Stavrakakis
        image = backend.get_image(image_id)
312 cda71050 Christos Stavrakakis
        properties = image['properties']
313 cda71050 Christos Stavrakakis
        properties[key] = val
314 cda71050 Christos Stavrakakis
315 cda71050 Christos Stavrakakis
        backend.update_metadata(image_id, dict(properties=properties))
316 cd2b0bf5 Christos Stavrakakis
317 6ef51e9f Giorgos Verigakis
    return util.render_meta(request, {key: val}, status=201)
318 6ef51e9f Giorgos Verigakis
319 432fc8c3 Giorgos Verigakis
320 b3fd98ae Christos Stavrakakis
@api.api_method('DELETE', user_required=True, logger=log)
321 432fc8c3 Giorgos Verigakis
def delete_metadata_item(request, image_id, key):
322 432fc8c3 Giorgos Verigakis
    # Normal Response Code: 204
323 432fc8c3 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
324 432fc8c3 Giorgos Verigakis
    #                       serviceUnavailable (503),
325 432fc8c3 Giorgos Verigakis
    #                       unauthorized (401),
326 432fc8c3 Giorgos Verigakis
    #                       itemNotFound (404),
327 432fc8c3 Giorgos Verigakis
    #                       badRequest (400),
328 432fc8c3 Giorgos Verigakis
    #                       buildInProgress (409),
329 432fc8c3 Giorgos Verigakis
    #                       badMediaType(415),
330 432fc8c3 Giorgos Verigakis
    #                       overLimit (413),
331 cd2b0bf5 Christos Stavrakakis
332 bf5c82dc Christos Stavrakakis
    log.info('delete_image_metadata_item %s %s', image_id, key)
333 b3a77863 Christos Stavrakakis
    with image_backend(request.user_uniq) as backend:
334 cda71050 Christos Stavrakakis
        image = backend.get_image(image_id)
335 cda71050 Christos Stavrakakis
        properties = image['properties']
336 cda71050 Christos Stavrakakis
        properties.pop(key, None)
337 cda71050 Christos Stavrakakis
338 cda71050 Christos Stavrakakis
        backend.update_metadata(image_id, dict(properties=properties))
339 cd2b0bf5 Christos Stavrakakis
340 432fc8c3 Giorgos Verigakis
    return HttpResponse(status=204)