Statistics
| Branch: | Tag: | Revision:

root / api / util.py @ 314c74e2

History | View | Annotate | Download (7 kB)

1
# vim: ts=4 sts=4 et ai sw=4 fileencoding=utf-8
2
#
3
# Copyright (c) 2011 Greek Research and Technology Network
4
#
5

    
6
from datetime import timedelta, tzinfo
7
from functools import wraps
8
from random import choice
9
from string import ascii_letters, digits
10
from time import time
11
from traceback import format_exc
12
from wsgiref.handlers import format_date_time
13

    
14
from django.conf import settings
15
from django.http import HttpResponse
16
from django.template.loader import render_to_string
17
from django.utils import simplejson as json
18

    
19
from pithos.api.faults import Fault, BadRequest, ItemNotFound, ServiceUnavailable
20
#from synnefo.db.models import SynnefoUser, Image, ImageMetadata, VirtualMachine, VirtualMachineMetadata
21

    
22
import datetime
23
import dateutil.parser
24
import logging
25

    
26

    
27
# class UTC(tzinfo):
28
#     def utcoffset(self, dt):
29
#         return timedelta(0)
30
#     
31
#     def tzname(self, dt):
32
#         return 'UTC'
33
#     
34
#     def dst(self, dt):
35
#         return timedelta(0)
36
# 
37
# 
38
# def isoformat(d):
39
#     """Return an ISO8601 date string that includes a timezon."""
40
#     
41
#     return d.replace(tzinfo=UTC()).isoformat()
42
# 
43
# def isoparse(s):
44
#     """Parse an ISO8601 date string into a datetime object."""
45
#     
46
#     if not s:
47
#         return None
48
#     
49
#     try:
50
#         since = dateutil.parser.parse(s)
51
#         utc_since = since.astimezone(UTC()).replace(tzinfo=None)
52
#     except ValueError:
53
#         raise BadRequest('Invalid changes-since parameter.')
54
#     
55
#     now = datetime.datetime.now()
56
#     if utc_since > now:
57
#         raise BadRequest('changes-since value set in the future.')
58
#     
59
#     if now - utc_since > timedelta(seconds=settings.POLL_LIMIT):
60
#         raise BadRequest('Too old changes-since value.')
61
#     
62
#     return utc_since
63
#     
64
# def random_password(length=8):
65
#     pool = ascii_letters + digits
66
#     return ''.join(choice(pool) for i in range(length))
67
# 
68
# 
69
# def get_user():
70
#     # XXX Placeholder function, everything belongs to a single SynnefoUser for now
71
#     try:
72
#         return SynnefoUser.objects.all()[0]
73
#     except IndexError:
74
#         raise Unauthorized
75
# 
76
# def get_vm(server_id):
77
#     """Return a VirtualMachine instance or raise ItemNotFound."""
78
#     
79
#     try:
80
#         server_id = int(server_id)
81
#         return VirtualMachine.objects.get(id=server_id)
82
#     except ValueError:
83
#         raise BadRequest('Invalid server ID.')
84
#     except VirtualMachine.DoesNotExist:
85
#         raise ItemNotFound('Server not found.')
86
# 
87
# def get_vm_meta(server_id, key):
88
#     """Return a VirtualMachineMetadata instance or raise ItemNotFound."""
89
#     
90
#     try:
91
#         server_id = int(server_id)
92
#         return VirtualMachineMetadata.objects.get(meta_key=key, vm=server_id)
93
#     except VirtualMachineMetadata.DoesNotExist:
94
#         raise ItemNotFound('Metadata key not found.')
95
# 
96
# def get_image(image_id):
97
#     """Return an Image instance or raise ItemNotFound."""
98
#     
99
#     try:
100
#         image_id = int(image_id)
101
#         return Image.objects.get(id=image_id)
102
#     except Image.DoesNotExist:
103
#         raise ItemNotFound('Image not found.')
104
# 
105
# def get_image_meta(image_id, key):
106
#     """Return a ImageMetadata instance or raise ItemNotFound."""
107
# 
108
#     try:
109
#         image_id = int(image_id)
110
#         return ImageMetadata.objects.get(meta_key=key, image=image_id)
111
#     except ImageMetadata.DoesNotExist:
112
#         raise ItemNotFound('Metadata key not found.')
113
# 
114
# 
115
# def get_request_dict(request):
116
#     """Returns data sent by the client as a python dict."""
117
#     
118
#     data = request.raw_post_data
119
#     if request.META.get('CONTENT_TYPE').startswith('application/json'):
120
#         try:
121
#             return json.loads(data)
122
#         except ValueError:
123
#             raise BadRequest('Invalid JSON data.')
124
#     else:
125
#         raise BadRequest('Unsupported Content-Type.')
126

    
127
def update_response_headers(request, response):
128
    if request.serialization == 'xml':
129
        response['Content-Type'] = 'application/xml; charset=UTF-8'
130
    elif request.serialization == 'json':
131
        response['Content-Type'] = 'application/json; charset=UTF-8'
132
    else:
133
        response['Content-Type'] = 'text/plain; charset=UTF-8'
134

    
135
    if settings.TEST:
136
        response['Date'] = format_date_time(time())
137

    
138
# def render_metadata(request, metadata, use_values=False, status=200):
139
#     if request.serialization == 'xml':
140
#         data = render_to_string('metadata.xml', {'metadata': metadata})
141
#     else:
142
#         d = {'metadata': {'values': metadata}} if use_values else {'metadata': metadata}
143
#         data = json.dumps(d)
144
#     return HttpResponse(data, status=status)
145
# 
146
# def render_meta(request, meta, status=200):
147
#     if request.serialization == 'xml':
148
#         data = render_to_string('meta.xml', {'meta': meta})
149
#     else:
150
#         data = json.dumps({'meta': {meta.meta_key: meta.meta_value}})
151
#     return HttpResponse(data, status=status)
152

    
153
def render_fault(request, fault):
154
    if settings.DEBUG or settings.TEST:
155
        fault.details = format_exc(fault)
156
    
157
#     if request.serialization == 'xml':
158
#         data = render_to_string('fault.xml', {'fault': fault})
159
#     else:
160
#         d = {fault.name: {'code': fault.code, 'message': fault.message, 'details': fault.details}}
161
#         data = json.dumps(d)
162
    
163
#     resp = HttpResponse(data, status=fault.code)
164
    resp = HttpResponse(status=fault.code)
165
    update_response_headers(request, resp)
166
    return resp
167

    
168
def request_serialization(request, format_allowed=False):
169
    """Return the serialization format requested.
170
       
171
       Valid formats are 'text' and 'json', 'xml' if `format_allowed` is True.
172
    """
173
    
174
    if not format_allowed:
175
        return 'text'
176
    
177
    format = request.GET.get('format')
178
    if format == 'json':
179
        return 'json'
180
    elif format == 'xml':
181
        return 'xml'
182
    
183
#     for item in request.META.get('HTTP_ACCEPT', '').split(','):
184
#         accept, sep, rest = item.strip().partition(';')
185
#         if accept == 'application/json':
186
#             return 'json'
187
#         elif accept == 'application/xml':
188
#             return 'xml'
189
    
190
    return 'text'
191

    
192
def api_method(http_method = None, format_allowed = False):
193
    """Decorator function for views that implement an API method."""
194
    
195
    def decorator(func):
196
        @wraps(func)
197
        def wrapper(request, *args, **kwargs):
198
            try:
199
                request.serialization = request_serialization(request, format_allowed)
200
                if http_method and request.method != http_method:
201
                    raise BadRequest('Method not allowed.')
202
                
203
                resp = func(request, *args, **kwargs)
204
                update_response_headers(request, resp)
205
                return resp
206
            
207
            except Fault, fault:
208
                return render_fault(request, fault)
209
            except BaseException, e:
210
                logging.exception('Unexpected error: %s' % e)
211
                fault = ServiceUnavailable('Unexpected error')
212
                return render_fault(request, fault)
213
        return wrapper
214
    return decorator