Statistics
| Branch: | Tag: | Revision:

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