Statistics
| Branch: | Tag: | Revision:

root / api / images.py @ 7584d408

History | View | Annotate | Download (9.3 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
        if image.sourcevm:
66
            d['serverRef'] = image.sourcevm.id
67
        
68
        metadata = {}
69
        for meta in ImageMetadata.objects.filter(image=image):
70
            metadata[meta.meta_key] = meta.meta_value
71
        
72
        if metadata:
73
            d['metadata'] = {'values': metadata}
74
    
75
    return d
76

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

    
81

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

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

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

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

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

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

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

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

    
212
    updated = {}
213

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

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

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

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

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

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

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

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

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