root / api / util.py @ 40777cc8
History | View | Annotate | Download (7.3 kB)
1 | 7e2f9d4b | Giorgos Verigakis | #
|
---|---|---|---|
2 | 7e2f9d4b | Giorgos Verigakis | # Copyright (c) 2010 Greek Research and Technology Network
|
3 | 7e2f9d4b | Giorgos Verigakis | #
|
4 | 7e2f9d4b | Giorgos Verigakis | |
5 | d8e50a39 | Giorgos Verigakis | from datetime import timedelta, tzinfo |
6 | d8e50a39 | Giorgos Verigakis | from functools import wraps |
7 | d8e50a39 | Giorgos Verigakis | from random import choice |
8 | d8e50a39 | Giorgos Verigakis | from string import ascii_letters, digits |
9 | 8b01f7f3 | Giorgos Verigakis | from time import time |
10 | d8e50a39 | Giorgos Verigakis | from traceback import format_exc |
11 | 8b01f7f3 | Giorgos Verigakis | from wsgiref.handlers import format_date_time |
12 | 7e2f9d4b | Giorgos Verigakis | |
13 | 529178b1 | Giorgos Verigakis | import datetime |
14 | 529178b1 | Giorgos Verigakis | import dateutil.parser |
15 | 529178b1 | Giorgos Verigakis | import logging |
16 | 529178b1 | Giorgos Verigakis | |
17 | d8e50a39 | Giorgos Verigakis | from django.conf import settings |
18 | 7e2f9d4b | Giorgos Verigakis | from django.http import HttpResponse |
19 | 7e2f9d4b | Giorgos Verigakis | from django.template.loader import render_to_string |
20 | 29a59bc1 | Giorgos Verigakis | from django.utils import simplejson as json |
21 | 7e2f9d4b | Giorgos Verigakis | |
22 | 5231a38a | Giorgos Verigakis | from synnefo.api.faults import (Fault, BadRequest, BuildInProgress, ItemNotFound, |
23 | 5231a38a | Giorgos Verigakis | ServiceUnavailable, Unauthorized) |
24 | 529178b1 | Giorgos Verigakis | from synnefo.db.models import (SynnefoUser, Flavor, Image, ImageMetadata, |
25 | 529178b1 | Giorgos Verigakis | VirtualMachine, VirtualMachineMetadata) |
26 | 7e2f9d4b | Giorgos Verigakis | |
27 | 7e2f9d4b | Giorgos Verigakis | |
28 | d8e50a39 | Giorgos Verigakis | class UTC(tzinfo): |
29 | d8e50a39 | Giorgos Verigakis | def utcoffset(self, dt): |
30 | d8e50a39 | Giorgos Verigakis | return timedelta(0) |
31 | d8e50a39 | Giorgos Verigakis | |
32 | d8e50a39 | Giorgos Verigakis | def tzname(self, dt): |
33 | d8e50a39 | Giorgos Verigakis | return 'UTC' |
34 | d8e50a39 | Giorgos Verigakis | |
35 | d8e50a39 | Giorgos Verigakis | def dst(self, dt): |
36 | d8e50a39 | Giorgos Verigakis | return timedelta(0) |
37 | 7e2f9d4b | Giorgos Verigakis | |
38 | 7e2f9d4b | Giorgos Verigakis | |
39 | d8e50a39 | Giorgos Verigakis | def isoformat(d): |
40 | 529178b1 | Giorgos Verigakis | """Return an ISO8601 date string that includes a timezone."""
|
41 | d8e50a39 | Giorgos Verigakis | |
42 | d8e50a39 | Giorgos Verigakis | return d.replace(tzinfo=UTC()).isoformat()
|
43 | d8e50a39 | Giorgos Verigakis | |
44 | d8e50a39 | Giorgos Verigakis | def isoparse(s): |
45 | d8e50a39 | Giorgos Verigakis | """Parse an ISO8601 date string into a datetime object."""
|
46 | d8e50a39 | Giorgos Verigakis | |
47 | d8e50a39 | Giorgos Verigakis | if not s: |
48 | d8e50a39 | Giorgos Verigakis | return None |
49 | d8e50a39 | Giorgos Verigakis | |
50 | d8e50a39 | Giorgos Verigakis | try:
|
51 | d8e50a39 | Giorgos Verigakis | since = dateutil.parser.parse(s) |
52 | e87d30f3 | Giorgos Verigakis | utc_since = since.astimezone(UTC()).replace(tzinfo=None)
|
53 | d8e50a39 | Giorgos Verigakis | except ValueError: |
54 | d8e50a39 | Giorgos Verigakis | raise BadRequest('Invalid changes-since parameter.') |
55 | d8e50a39 | Giorgos Verigakis | |
56 | e87d30f3 | Giorgos Verigakis | now = datetime.datetime.now() |
57 | e87d30f3 | Giorgos Verigakis | if utc_since > now:
|
58 | d8e50a39 | Giorgos Verigakis | raise BadRequest('changes-since value set in the future.') |
59 | d8e50a39 | Giorgos Verigakis | |
60 | e87d30f3 | Giorgos Verigakis | if now - utc_since > timedelta(seconds=settings.POLL_LIMIT):
|
61 | d8e50a39 | Giorgos Verigakis | raise BadRequest('Too old changes-since value.') |
62 | d8e50a39 | Giorgos Verigakis | |
63 | 0140e54b | Vangelis Koukis | return utc_since
|
64 | d8e50a39 | Giorgos Verigakis | |
65 | d8e50a39 | Giorgos Verigakis | def random_password(length=8): |
66 | d8e50a39 | Giorgos Verigakis | pool = ascii_letters + digits |
67 | d8e50a39 | Giorgos Verigakis | return ''.join(choice(pool) for i in range(length)) |
68 | d8e50a39 | Giorgos Verigakis | |
69 | 7e2f9d4b | Giorgos Verigakis | |
70 | 40777cc8 | Giorgos Verigakis | def get_vm(server_id, owner): |
71 | d8e50a39 | Giorgos Verigakis | """Return a VirtualMachine instance or raise ItemNotFound."""
|
72 | d8e50a39 | Giorgos Verigakis | |
73 | d8e50a39 | Giorgos Verigakis | try:
|
74 | d8e50a39 | Giorgos Verigakis | server_id = int(server_id)
|
75 | 40777cc8 | Giorgos Verigakis | return VirtualMachine.objects.get(id=server_id, owner=owner)
|
76 | d8e50a39 | Giorgos Verigakis | except ValueError: |
77 | d8e50a39 | Giorgos Verigakis | raise BadRequest('Invalid server ID.') |
78 | d8e50a39 | Giorgos Verigakis | except VirtualMachine.DoesNotExist:
|
79 | d8e50a39 | Giorgos Verigakis | raise ItemNotFound('Server not found.') |
80 | d8e50a39 | Giorgos Verigakis | |
81 | 40777cc8 | Giorgos Verigakis | def get_vm_meta(vm, key): |
82 | d8e50a39 | Giorgos Verigakis | """Return a VirtualMachineMetadata instance or raise ItemNotFound."""
|
83 | d8e50a39 | Giorgos Verigakis | |
84 | d8e50a39 | Giorgos Verigakis | try:
|
85 | 40777cc8 | Giorgos Verigakis | return VirtualMachineMetadata.objects.get(meta_key=key, vm=vm)
|
86 | d8e50a39 | Giorgos Verigakis | except VirtualMachineMetadata.DoesNotExist:
|
87 | d8e50a39 | Giorgos Verigakis | raise ItemNotFound('Metadata key not found.') |
88 | d8e50a39 | Giorgos Verigakis | |
89 | 40777cc8 | Giorgos Verigakis | def get_image(image_id, owner): |
90 | d8e50a39 | Giorgos Verigakis | """Return an Image instance or raise ItemNotFound."""
|
91 | d8e50a39 | Giorgos Verigakis | |
92 | d8e50a39 | Giorgos Verigakis | try:
|
93 | d8e50a39 | Giorgos Verigakis | image_id = int(image_id)
|
94 | 40777cc8 | Giorgos Verigakis | return Image.objects.get(id=image_id, owner=owner)
|
95 | 40777cc8 | Giorgos Verigakis | except ValueError: |
96 | 40777cc8 | Giorgos Verigakis | raise BadRequest('Invalid image ID.') |
97 | d8e50a39 | Giorgos Verigakis | except Image.DoesNotExist:
|
98 | d8e50a39 | Giorgos Verigakis | raise ItemNotFound('Image not found.') |
99 | d8e50a39 | Giorgos Verigakis | |
100 | 40777cc8 | Giorgos Verigakis | def get_image_meta(image, key): |
101 | 432fc8c3 | Giorgos Verigakis | """Return a ImageMetadata instance or raise ItemNotFound."""
|
102 | 432fc8c3 | Giorgos Verigakis | |
103 | 432fc8c3 | Giorgos Verigakis | try:
|
104 | 40777cc8 | Giorgos Verigakis | return ImageMetadata.objects.get(meta_key=key, image=image)
|
105 | 432fc8c3 | Giorgos Verigakis | except ImageMetadata.DoesNotExist:
|
106 | 432fc8c3 | Giorgos Verigakis | raise ItemNotFound('Metadata key not found.') |
107 | 432fc8c3 | Giorgos Verigakis | |
108 | 529178b1 | Giorgos Verigakis | def get_flavor(flavor_id): |
109 | 529178b1 | Giorgos Verigakis | """Return a Flavor instance or raise ItemNotFound."""
|
110 | 529178b1 | Giorgos Verigakis | |
111 | 529178b1 | Giorgos Verigakis | try:
|
112 | 529178b1 | Giorgos Verigakis | flavor_id = int(flavor_id)
|
113 | 529178b1 | Giorgos Verigakis | return Flavor.objects.get(id=flavor_id)
|
114 | 40777cc8 | Giorgos Verigakis | except ValueError: |
115 | 40777cc8 | Giorgos Verigakis | raise BadRequest('Invalid flavor ID.') |
116 | 529178b1 | Giorgos Verigakis | except Flavor.DoesNotExist:
|
117 | 529178b1 | Giorgos Verigakis | raise ItemNotFound('Flavor not found.') |
118 | d8e50a39 | Giorgos Verigakis | |
119 | 7e2f9d4b | Giorgos Verigakis | def get_request_dict(request): |
120 | d8e50a39 | Giorgos Verigakis | """Returns data sent by the client as a python dict."""
|
121 | d8e50a39 | Giorgos Verigakis | |
122 | 7e2f9d4b | Giorgos Verigakis | data = request.raw_post_data |
123 | 7e45ddef | Dimitris Moraitis | if request.META.get('CONTENT_TYPE').startswith('application/json'): |
124 | 7e2f9d4b | Giorgos Verigakis | try:
|
125 | 7e2f9d4b | Giorgos Verigakis | return json.loads(data)
|
126 | 7e2f9d4b | Giorgos Verigakis | except ValueError: |
127 | d8e50a39 | Giorgos Verigakis | raise BadRequest('Invalid JSON data.') |
128 | d8e50a39 | Giorgos Verigakis | else:
|
129 | d8e50a39 | Giorgos Verigakis | raise BadRequest('Unsupported Content-Type.') |
130 | 7e2f9d4b | Giorgos Verigakis | |
131 | 432fc8c3 | Giorgos Verigakis | |
132 | 8b01f7f3 | Giorgos Verigakis | def update_response_headers(request, response): |
133 | 8b01f7f3 | Giorgos Verigakis | if request.serialization == 'xml': |
134 | 8b01f7f3 | Giorgos Verigakis | response['Content-Type'] = 'application/xml' |
135 | 8b01f7f3 | Giorgos Verigakis | elif request.serialization == 'atom': |
136 | 8b01f7f3 | Giorgos Verigakis | response['Content-Type'] = 'application/atom+xml' |
137 | 8b01f7f3 | Giorgos Verigakis | else:
|
138 | 8b01f7f3 | Giorgos Verigakis | response['Content-Type'] = 'application/json' |
139 | 8b01f7f3 | Giorgos Verigakis | |
140 | 4cf8adf8 | Vangelis Koukis | if settings.TEST:
|
141 | cdb65551 | Giorgos Verigakis | response['Date'] = format_date_time(time())
|
142 | 8b01f7f3 | Giorgos Verigakis | |
143 | 432fc8c3 | Giorgos Verigakis | def render_metadata(request, metadata, use_values=False, status=200): |
144 | 432fc8c3 | Giorgos Verigakis | if request.serialization == 'xml': |
145 | 432fc8c3 | Giorgos Verigakis | data = render_to_string('metadata.xml', {'metadata': metadata}) |
146 | 432fc8c3 | Giorgos Verigakis | else:
|
147 | 432fc8c3 | Giorgos Verigakis | d = {'metadata': {'values': metadata}} if use_values else {'metadata': metadata} |
148 | 432fc8c3 | Giorgos Verigakis | data = json.dumps(d) |
149 | 432fc8c3 | Giorgos Verigakis | return HttpResponse(data, status=status)
|
150 | 432fc8c3 | Giorgos Verigakis | |
151 | 432fc8c3 | Giorgos Verigakis | def render_meta(request, meta, status=200): |
152 | 432fc8c3 | Giorgos Verigakis | if request.serialization == 'xml': |
153 | 432fc8c3 | Giorgos Verigakis | data = render_to_string('meta.xml', {'meta': meta}) |
154 | 432fc8c3 | Giorgos Verigakis | else:
|
155 | 432fc8c3 | Giorgos Verigakis | data = json.dumps({'meta': {meta.meta_key: meta.meta_value}})
|
156 | 432fc8c3 | Giorgos Verigakis | return HttpResponse(data, status=status)
|
157 | 432fc8c3 | Giorgos Verigakis | |
158 | c36934a7 | Giorgos Verigakis | def render_fault(request, fault): |
159 | 4cf8adf8 | Vangelis Koukis | if settings.DEBUG or settings.TEST: |
160 | 7e2f9d4b | Giorgos Verigakis | fault.details = format_exc(fault) |
161 | d8e50a39 | Giorgos Verigakis | |
162 | d8e50a39 | Giorgos Verigakis | if request.serialization == 'xml': |
163 | d8e50a39 | Giorgos Verigakis | data = render_to_string('fault.xml', {'fault': fault}) |
164 | 7e2f9d4b | Giorgos Verigakis | else:
|
165 | 7e2f9d4b | Giorgos Verigakis | d = {fault.name: {'code': fault.code, 'message': fault.message, 'details': fault.details}} |
166 | 7e2f9d4b | Giorgos Verigakis | data = json.dumps(d) |
167 | d8e50a39 | Giorgos Verigakis | |
168 | f0656db1 | Giorgos Verigakis | resp = HttpResponse(data, status=fault.code) |
169 | 8b01f7f3 | Giorgos Verigakis | update_response_headers(request, resp) |
170 | f0656db1 | Giorgos Verigakis | return resp
|
171 | d8e50a39 | Giorgos Verigakis | |
172 | 432fc8c3 | Giorgos Verigakis | |
173 | d8e50a39 | Giorgos Verigakis | def request_serialization(request, atom_allowed=False): |
174 | d8e50a39 | Giorgos Verigakis | """Return the serialization format requested.
|
175 | d8e50a39 | Giorgos Verigakis |
|
176 | 529178b1 | Giorgos Verigakis | Valid formats are 'json', 'xml' and 'atom' if `atom_allowed` is True.
|
177 | d8e50a39 | Giorgos Verigakis | """
|
178 | d8e50a39 | Giorgos Verigakis | |
179 | d8e50a39 | Giorgos Verigakis | path = request.path |
180 | d8e50a39 | Giorgos Verigakis | |
181 | d8e50a39 | Giorgos Verigakis | if path.endswith('.json'): |
182 | d8e50a39 | Giorgos Verigakis | return 'json' |
183 | d8e50a39 | Giorgos Verigakis | elif path.endswith('.xml'): |
184 | d8e50a39 | Giorgos Verigakis | return 'xml' |
185 | d8e50a39 | Giorgos Verigakis | elif atom_allowed and path.endswith('.atom'): |
186 | d8e50a39 | Giorgos Verigakis | return 'atom' |
187 | d8e50a39 | Giorgos Verigakis | |
188 | d8e50a39 | Giorgos Verigakis | for item in request.META.get('HTTP_ACCEPT', '').split(','): |
189 | d8e50a39 | Giorgos Verigakis | accept, sep, rest = item.strip().partition(';')
|
190 | d8e50a39 | Giorgos Verigakis | if accept == 'application/json': |
191 | d8e50a39 | Giorgos Verigakis | return 'json' |
192 | d8e50a39 | Giorgos Verigakis | elif accept == 'application/xml': |
193 | d8e50a39 | Giorgos Verigakis | return 'xml' |
194 | d8e50a39 | Giorgos Verigakis | elif atom_allowed and accept == 'application/atom+xml': |
195 | d8e50a39 | Giorgos Verigakis | return 'atom' |
196 | d8e50a39 | Giorgos Verigakis | |
197 | d8e50a39 | Giorgos Verigakis | return 'json' |
198 | 7e2f9d4b | Giorgos Verigakis | |
199 | d8e50a39 | Giorgos Verigakis | def api_method(http_method=None, atom_allowed=False): |
200 | d8e50a39 | Giorgos Verigakis | """Decorator function for views that implement an API method."""
|
201 | d8e50a39 | Giorgos Verigakis | |
202 | c36934a7 | Giorgos Verigakis | def decorator(func): |
203 | c36934a7 | Giorgos Verigakis | @wraps(func)
|
204 | c36934a7 | Giorgos Verigakis | def wrapper(request, *args, **kwargs): |
205 | c36934a7 | Giorgos Verigakis | try:
|
206 | 40777cc8 | Giorgos Verigakis | if not request.user: |
207 | 40777cc8 | Giorgos Verigakis | raise Unauthorized('No user found.') |
208 | d8e50a39 | Giorgos Verigakis | request.serialization = request_serialization(request, atom_allowed) |
209 | d8e50a39 | Giorgos Verigakis | if http_method and request.method != http_method: |
210 | d8e50a39 | Giorgos Verigakis | raise BadRequest('Method not allowed.') |
211 | c36934a7 | Giorgos Verigakis | |
212 | c36934a7 | Giorgos Verigakis | resp = func(request, *args, **kwargs) |
213 | 8b01f7f3 | Giorgos Verigakis | update_response_headers(request, resp) |
214 | c36934a7 | Giorgos Verigakis | return resp
|
215 | 5231a38a | Giorgos Verigakis | except VirtualMachine.DeletedError:
|
216 | 5231a38a | Giorgos Verigakis | fault = BadRequest('Server has been deleted.')
|
217 | 5231a38a | Giorgos Verigakis | return render_fault(request, fault)
|
218 | 5231a38a | Giorgos Verigakis | except VirtualMachine.BuildingError:
|
219 | 5231a38a | Giorgos Verigakis | fault = BuildInProgress('Server is being built.')
|
220 | 5231a38a | Giorgos Verigakis | return render_fault(request, fault)
|
221 | c36934a7 | Giorgos Verigakis | except Fault, fault:
|
222 | c36934a7 | Giorgos Verigakis | return render_fault(request, fault)
|
223 | a62a4d20 | Giorgos Verigakis | except BaseException, e: |
224 | 529178b1 | Giorgos Verigakis | logging.exception('Unexpected error: %s', e)
|
225 | 529178b1 | Giorgos Verigakis | fault = ServiceUnavailable('Unexpected error.')
|
226 | c36934a7 | Giorgos Verigakis | return render_fault(request, fault)
|
227 | c36934a7 | Giorgos Verigakis | return wrapper
|
228 | c36934a7 | Giorgos Verigakis | return decorator |