Statistics
| Branch: | Tag: | Revision:

root / api / images.py @ 529178b1

History | View | Annotate | Download (9.6 kB)

1
#
2
# Copyright (c) 2010 Greek Research and Technology Network
3
#
4

    
5
from synnefo.api.common import method_not_allowed
6
from synnefo.api.faults import BadRequest, Unauthorized
7
from synnefo.api.util import (isoformat, isoparse, get_user, get_vm, get_image, get_image_meta,
8
                                get_request_dict, render_metadata, render_meta, api_method)
9
from synnefo.db.models import Image, ImageMetadata
10

    
11
from django.conf.urls.defaults import patterns
12
from django.http import HttpResponse
13
from django.template.loader import render_to_string
14
from django.utils import simplejson as json
15

    
16

    
17
urlpatterns = patterns('synnefo.api.images',
18
    (r'^(?:/|.json|.xml)?$', 'demux'),
19
    (r'^/detail(?:.json|.xml)?$', 'list_images', {'detail': True}),
20
    (r'^/(\d+)(?:.json|.xml)?$', 'image_demux'),
21
    (r'^/(\d+)/meta(?:.json|.xml)?$', 'metadata_demux'),
22
    (r'^/(\d+)/meta/(.+?)(?:.json|.xml)?$', 'metadata_item_demux'),
23
)
24

    
25
def demux(request):
26
    if request.method == 'GET':
27
        return list_images(request)
28
    elif request.method == 'POST':
29
        return create_image(request)
30
    else:
31
        return method_not_allowed(request)
32

    
33
def image_demux(request, image_id):
34
    if request.method == 'GET':
35
        return get_image_details(request, image_id)
36
    elif request.method == 'DELETE':
37
        return delete_image(request, image_id)
38
    else:
39
        return method_not_allowed(request)
40

    
41
def metadata_demux(request, image_id):
42
    if request.method == 'GET':
43
        return list_metadata(request, image_id)
44
    elif request.method == 'POST':
45
        return update_metadata(request, image_id)
46
    else:
47
        return method_not_allowed(request)
48

    
49
def metadata_item_demux(request, image_id, key):
50
    if request.method == 'GET':
51
        return get_metadata_item(request, image_id, key)
52
    elif request.method == 'PUT':
53
        return create_metadata_item(request, image_id, key)
54
    elif request.method == 'DELETE':
55
        return delete_metadata_item(request, image_id, key)
56
    else:
57
        return method_not_allowed(request)
58

    
59

    
60
def image_to_dict(image, detail=True):
61
    d = {'id': image.id, 'name': image.name}
62
    if detail:
63
        d['updated'] = isoformat(image.updated)
64
        d['created'] = isoformat(image.created)
65
        d['status'] = image.state
66
        d['progress'] = 100 if image.state == 'ACTIVE' else 0
67
        if image.sourcevm:
68
            d['serverRef'] = image.sourcevm.id
69
        
70
        metadata = {}
71
        for meta in ImageMetadata.objects.filter(image=image):
72
            metadata[meta.meta_key] = meta.meta_value
73
        
74
        if metadata:
75
            d['metadata'] = {'values': metadata}
76
    
77
    return d
78

    
79
def metadata_to_dict(image):
80
    image_meta = image.imagemetadata_set.all()
81
    return dict((meta.meta_key, meta.meta_value) for meta in image_meta)
82

    
83

    
84
@api_method('GET')
85
def list_images(request, detail=False):
86
    # Normal Response Codes: 200, 203
87
    # Error Response Codes: computeFault (400, 500),
88
    #                       serviceUnavailable (503),
89
    #                       unauthorized (401),
90
    #                       badRequest (400),
91
    #                       overLimit (413)
92
    
93
    since = isoparse(request.GET.get('changes-since'))
94
    
95
    if since:
96
        avail_images = Image.objects.filter(updated__gte=since)
97
        if not avail_images:
98
            return HttpResponse(status=304)
99
    else:
100
        avail_images = Image.objects.all()
101
    
102
    images = [image_to_dict(image, detail) for image in avail_images]
103
    
104
    if request.serialization == 'xml':
105
        data = render_to_string('list_images.xml', {'images': images, 'detail': detail})
106
    else:
107
        data = json.dumps({'images': {'values': images}})
108
    
109
    return HttpResponse(data, status=200)
110

    
111
@api_method('POST')
112
def create_image(request):
113
    # Normal Response Code: 202
114
    # Error Response Codes: computeFault (400, 500),
115
    #                       serviceUnavailable (503),
116
    #                       unauthorized (401),
117
    #                       badMediaType(415),
118
    #                       itemNotFound (404),
119
    #                       badRequest (400),
120
    #                       serverCapacityUnavailable (503),
121
    #                       buildInProgress (409),
122
    #                       resizeNotAllowed (403),
123
    #                       backupOrResizeInProgress (409),
124
    #                       overLimit (413)
125
    
126
    req = get_request_dict(request)
127
    
128
    try:
129
        d = req['image']
130
        server_id = d['serverRef']
131
        name = d['name']
132
    except (KeyError, ValueError):
133
        raise BadRequest('Malformed request.')
134
    
135
    owner = get_user()
136
    vm = get_vm(server_id)
137
    image = Image.objects.create(name=name, owner=owner, sourcevm=vm)
138
    
139
    imagedict = image_to_dict(image)
140
    if request.serialization == 'xml':
141
        data = render_to_string('image.xml', {'image': imagedict})
142
    else:
143
        data = json.dumps({'image': imagedict})
144
    
145
    return HttpResponse(data, status=202)
146

    
147
@api_method('GET')
148
def get_image_details(request, image_id):
149
    # Normal Response Codes: 200, 203
150
    # Error Response Codes: computeFault (400, 500),
151
    #                       serviceUnavailable (503),
152
    #                       unauthorized (401),
153
    #                       badRequest (400),
154
    #                       itemNotFound (404),
155
    #                       overLimit (413)
156
    
157
    image = get_image(image_id)
158
    imagedict = image_to_dict(image)
159
    
160
    if request.serialization == 'xml':
161
        data = render_to_string('image.xml', {'image': imagedict})
162
    else:
163
        data = json.dumps({'image': imagedict})
164
    
165
    return HttpResponse(data, status=200)
166

    
167
@api_method('DELETE')
168
def delete_image(request, image_id):
169
    # Normal Response Code: 204
170
    # Error Response Codes: computeFault (400, 500),
171
    #                       serviceUnavailable (503),
172
    #                       unauthorized (401),
173
    #                       itemNotFound (404),
174
    #                       overLimit (413)
175
    
176
    image = get_image(image_id)
177
    if image.owner != get_user():
178
        raise Unauthorized('Image does not belong to user.')
179
    image.delete()
180
    return HttpResponse(status=204)
181

    
182
@api_method('GET')
183
def list_metadata(request, image_id):
184
    # Normal Response Codes: 200, 203
185
    # Error Response Codes: computeFault (400, 500),
186
    #                       serviceUnavailable (503),
187
    #                       unauthorized (401),
188
    #                       badRequest (400),
189
    #                       overLimit (413)
190

    
191
    image = get_image(image_id)
192
    metadata = metadata_to_dict(image)
193
    return render_metadata(request, metadata, use_values=True, status=200)
194

    
195
@api_method('POST')
196
def update_metadata(request, image_id):
197
    # Normal Response Code: 201
198
    # Error Response Codes: computeFault (400, 500),
199
    #                       serviceUnavailable (503),
200
    #                       unauthorized (401),
201
    #                       badRequest (400),
202
    #                       buildInProgress (409),
203
    #                       badMediaType(415),
204
    #                       overLimit (413)
205

    
206
    image = get_image(image_id)
207
    req = get_request_dict(request)
208
    try:
209
        metadata = req['metadata']
210
        assert isinstance(metadata, dict)
211
    except (KeyError, AssertionError):
212
        raise BadRequest('Malformed request.')
213

    
214
    updated = {}
215

    
216
    for key, val in metadata.items():
217
        try:
218
            meta = ImageMetadata.objects.get(meta_key=key, image=image)
219
            meta.meta_value = val
220
            meta.save()
221
            updated[key] = val
222
        except ImageMetadata.DoesNotExist:
223
            pass    # Ignore non-existent metadata
224

    
225
    return render_metadata(request, updated, status=201)
226

    
227
@api_method('GET')
228
def get_metadata_item(request, image_id, key):
229
    # Normal Response Codes: 200, 203
230
    # Error Response Codes: computeFault (400, 500),
231
    #                       serviceUnavailable (503),
232
    #                       unauthorized (401),
233
    #                       itemNotFound (404),
234
    #                       badRequest (400),
235
    #                       overLimit (413)
236

    
237
    meta = get_image_meta(image_id, key)
238
    return render_meta(request, meta, status=200)
239

    
240
@api_method('PUT')
241
def create_metadata_item(request, image_id, key):
242
    # Normal Response Code: 201
243
    # Error Response Codes: computeFault (400, 500),
244
    #                       serviceUnavailable (503),
245
    #                       unauthorized (401),
246
    #                       itemNotFound (404),
247
    #                       badRequest (400),
248
    #                       buildInProgress (409),
249
    #                       badMediaType(415),
250
    #                       overLimit (413)
251

    
252
    image = get_image(image_id)
253
    req = get_request_dict(request)
254
    try:
255
        metadict = req['meta']
256
        assert isinstance(metadict, dict)
257
        assert len(metadict) == 1
258
        assert key in metadict
259
    except (KeyError, AssertionError):
260
        raise BadRequest('Malformed request.')
261

    
262
    meta, created = ImageMetadata.objects.get_or_create(meta_key=key, image=image)
263
    meta.meta_value = metadict[key]
264
    meta.save()
265
    return render_meta(request, meta, status=201)
266

    
267
@api_method('DELETE')
268
def delete_metadata_item(request, image_id, key):
269
    # Normal Response Code: 204
270
    # Error Response Codes: computeFault (400, 500),
271
    #                       serviceUnavailable (503),
272
    #                       unauthorized (401),
273
    #                       itemNotFound (404),
274
    #                       badRequest (400),
275
    #                       buildInProgress (409),
276
    #                       badMediaType(415),
277
    #                       overLimit (413),
278

    
279
    meta = get_image_meta(image_id, key)
280
    meta.delete()
281
    return HttpResponse(status=204)