Revision aa197ee4 api/util.py
b/api/util.py | ||
---|---|---|
28 | 28 |
class UTC(tzinfo): |
29 | 29 |
def utcoffset(self, dt): |
30 | 30 |
return timedelta(0) |
31 |
|
|
31 |
|
|
32 | 32 |
def tzname(self, dt): |
33 | 33 |
return 'UTC' |
34 |
|
|
34 |
|
|
35 | 35 |
def dst(self, dt): |
36 | 36 |
return timedelta(0) |
37 | 37 |
|
38 | 38 |
|
39 | 39 |
def isoformat(d): |
40 | 40 |
"""Return an ISO8601 date string that includes a timezone.""" |
41 |
|
|
41 |
|
|
42 | 42 |
return d.replace(tzinfo=UTC()).isoformat() |
43 | 43 |
|
44 | 44 |
def isoparse(s): |
45 | 45 |
"""Parse an ISO8601 date string into a datetime object.""" |
46 |
|
|
46 |
|
|
47 | 47 |
if not s: |
48 | 48 |
return None |
49 |
|
|
49 |
|
|
50 | 50 |
try: |
51 | 51 |
since = dateutil.parser.parse(s) |
52 | 52 |
utc_since = since.astimezone(UTC()).replace(tzinfo=None) |
53 | 53 |
except ValueError: |
54 | 54 |
raise BadRequest('Invalid changes-since parameter.') |
55 |
|
|
55 |
|
|
56 | 56 |
now = datetime.datetime.now() |
57 | 57 |
if utc_since > now: |
58 | 58 |
raise BadRequest('changes-since value set in the future.') |
59 |
|
|
59 |
|
|
60 | 60 |
if now - utc_since > timedelta(seconds=settings.POLL_LIMIT): |
61 | 61 |
raise BadRequest('Too old changes-since value.') |
62 |
|
|
62 |
|
|
63 | 63 |
return utc_since |
64 |
|
|
64 |
|
|
65 | 65 |
def random_password(length=8): |
66 | 66 |
pool = ascii_letters + digits |
67 | 67 |
return ''.join(choice(pool) for i in range(length)) |
... | ... | |
69 | 69 |
|
70 | 70 |
def get_vm(server_id, owner): |
71 | 71 |
"""Return a VirtualMachine instance or raise ItemNotFound.""" |
72 |
|
|
72 |
|
|
73 | 73 |
try: |
74 | 74 |
server_id = int(server_id) |
75 | 75 |
return VirtualMachine.objects.get(id=server_id, owner=owner) |
... | ... | |
80 | 80 |
|
81 | 81 |
def get_vm_meta(vm, key): |
82 | 82 |
"""Return a VirtualMachineMetadata instance or raise ItemNotFound.""" |
83 |
|
|
83 |
|
|
84 | 84 |
try: |
85 | 85 |
return VirtualMachineMetadata.objects.get(meta_key=key, vm=vm) |
86 | 86 |
except VirtualMachineMetadata.DoesNotExist: |
... | ... | |
88 | 88 |
|
89 | 89 |
def get_image(image_id, owner): |
90 | 90 |
"""Return an Image instance or raise ItemNotFound.""" |
91 |
|
|
91 |
|
|
92 | 92 |
try: |
93 | 93 |
image_id = int(image_id) |
94 | 94 |
return Image.objects.get(id=image_id, owner=owner) |
... | ... | |
107 | 107 |
|
108 | 108 |
def get_flavor(flavor_id): |
109 | 109 |
"""Return a Flavor instance or raise ItemNotFound.""" |
110 |
|
|
110 |
|
|
111 | 111 |
try: |
112 | 112 |
flavor_id = int(flavor_id) |
113 | 113 |
return Flavor.objects.get(id=flavor_id) |
... | ... | |
118 | 118 |
|
119 | 119 |
def get_network(network_id, owner): |
120 | 120 |
"""Return a Network instance or raise ItemNotFound.""" |
121 |
|
|
121 |
|
|
122 | 122 |
try: |
123 | 123 |
return Network.objects.get(id=network_id, owner=owner) |
124 | 124 |
except ValueError: |
... | ... | |
129 | 129 |
|
130 | 130 |
def get_request_dict(request): |
131 | 131 |
"""Returns data sent by the client as a python dict.""" |
132 |
|
|
132 |
|
|
133 | 133 |
data = request.raw_post_data |
134 | 134 |
if request.META.get('CONTENT_TYPE').startswith('application/json'): |
135 | 135 |
try: |
... | ... | |
146 | 146 |
response['Content-Type'] = 'application/atom+xml' |
147 | 147 |
else: |
148 | 148 |
response['Content-Type'] = 'application/json' |
149 |
|
|
149 |
|
|
150 | 150 |
if settings.TEST: |
151 | 151 |
response['Date'] = format_date_time(time()) |
152 | 152 |
|
... | ... | |
168 | 168 |
def render_fault(request, fault): |
169 | 169 |
if settings.DEBUG or settings.TEST: |
170 | 170 |
fault.details = format_exc(fault) |
171 |
|
|
171 |
|
|
172 | 172 |
if request.serialization == 'xml': |
173 | 173 |
data = render_to_string('fault.xml', {'fault': fault}) |
174 | 174 |
else: |
175 | 175 |
d = {fault.name: {'code': fault.code, 'message': fault.message, 'details': fault.details}} |
176 | 176 |
data = json.dumps(d) |
177 |
|
|
177 |
|
|
178 | 178 |
resp = HttpResponse(data, status=fault.code) |
179 | 179 |
update_response_headers(request, resp) |
180 | 180 |
return resp |
... | ... | |
182 | 182 |
|
183 | 183 |
def request_serialization(request, atom_allowed=False): |
184 | 184 |
"""Return the serialization format requested. |
185 |
|
|
185 |
|
|
186 | 186 |
Valid formats are 'json', 'xml' and 'atom' if `atom_allowed` is True. |
187 | 187 |
""" |
188 |
|
|
188 |
|
|
189 | 189 |
path = request.path |
190 |
|
|
190 |
|
|
191 | 191 |
if path.endswith('.json'): |
192 | 192 |
return 'json' |
193 | 193 |
elif path.endswith('.xml'): |
194 | 194 |
return 'xml' |
195 | 195 |
elif atom_allowed and path.endswith('.atom'): |
196 | 196 |
return 'atom' |
197 |
|
|
197 |
|
|
198 | 198 |
for item in request.META.get('HTTP_ACCEPT', '').split(','): |
199 | 199 |
accept, sep, rest = item.strip().partition(';') |
200 | 200 |
if accept == 'application/json': |
... | ... | |
203 | 203 |
return 'xml' |
204 | 204 |
elif atom_allowed and accept == 'application/atom+xml': |
205 | 205 |
return 'atom' |
206 |
|
|
206 |
|
|
207 | 207 |
return 'json' |
208 | 208 |
|
209 | 209 |
def api_method(http_method=None, atom_allowed=False): |
210 | 210 |
"""Decorator function for views that implement an API method.""" |
211 |
|
|
211 |
|
|
212 | 212 |
def decorator(func): |
213 | 213 |
@wraps(func) |
214 | 214 |
def wrapper(request, *args, **kwargs): |
... | ... | |
218 | 218 |
raise Unauthorized('No user found.') |
219 | 219 |
if http_method and request.method != http_method: |
220 | 220 |
raise BadRequest('Method not allowed.') |
221 |
|
|
221 |
|
|
222 | 222 |
resp = func(request, *args, **kwargs) |
223 | 223 |
update_response_headers(request, resp) |
224 | 224 |
return resp |
Also available in: Unified diff