Statistics
| Branch: | Tag: | Revision:

root / api / images.py @ 838c404d

History | View | Annotate | Download (9.4 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.util import *
7
from synnefo.db.models import Image, ImageMetadata, VirtualMachine
8

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

    
14

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

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

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

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

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

    
57

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

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

    
82

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

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

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

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

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

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

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

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

    
213
    updated = {}
214

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

    
224
    return render_metadata(request, metadata, status=201)
225

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

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

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

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

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

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

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