Statistics
| Branch: | Tag: | Revision:

root / api / util.py @ 838c404d

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