Statistics
| Branch: | Tag: | Revision:

root / api / util.py @ 5509b599

History | View | Annotate | Download (7.5 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 e2ee7808 Giorgos Verigakis
                                VirtualMachine, VirtualMachineMetadata, Network)
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 aa197ee4 Vangelis Koukis
32 d8e50a39 Giorgos Verigakis
    def tzname(self, dt):
33 d8e50a39 Giorgos Verigakis
        return 'UTC'
34 aa197ee4 Vangelis Koukis
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 aa197ee4 Vangelis Koukis
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 aa197ee4 Vangelis Koukis
47 d8e50a39 Giorgos Verigakis
    if not s:
48 d8e50a39 Giorgos Verigakis
        return None
49 aa197ee4 Vangelis Koukis
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 aa197ee4 Vangelis Koukis
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 aa197ee4 Vangelis Koukis
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 aa197ee4 Vangelis Koukis
63 0140e54b Vangelis Koukis
    return utc_since
64 aa197ee4 Vangelis Koukis
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 aa197ee4 Vangelis Koukis
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 aa197ee4 Vangelis Koukis
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 aa197ee4 Vangelis Koukis
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 aa197ee4 Vangelis Koukis
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 b19653d1 Giorgos Verigakis
def get_network(network_id, owner):
120 e2ee7808 Giorgos Verigakis
    """Return a Network instance or raise ItemNotFound."""
121 aa197ee4 Vangelis Koukis
122 e2ee7808 Giorgos Verigakis
    try:
123 b19653d1 Giorgos Verigakis
        return Network.objects.get(id=network_id, owner=owner)
124 e2ee7808 Giorgos Verigakis
    except ValueError:
125 b19653d1 Giorgos Verigakis
        raise BadRequest('Invalid network ID.')
126 13b954b0 Giorgos Verigakis
    except Network.DoesNotExist:
127 e2ee7808 Giorgos Verigakis
        raise ItemNotFound('Network not found.')
128 e2ee7808 Giorgos Verigakis
129 e2ee7808 Giorgos Verigakis
130 7e2f9d4b Giorgos Verigakis
def get_request_dict(request):
131 d8e50a39 Giorgos Verigakis
    """Returns data sent by the client as a python dict."""
132 aa197ee4 Vangelis Koukis
133 7e2f9d4b Giorgos Verigakis
    data = request.raw_post_data
134 7e45ddef Dimitris Moraitis
    if request.META.get('CONTENT_TYPE').startswith('application/json'):
135 7e2f9d4b Giorgos Verigakis
        try:
136 7e2f9d4b Giorgos Verigakis
            return json.loads(data)
137 7e2f9d4b Giorgos Verigakis
        except ValueError:
138 d8e50a39 Giorgos Verigakis
            raise BadRequest('Invalid JSON data.')
139 d8e50a39 Giorgos Verigakis
    else:
140 d8e50a39 Giorgos Verigakis
        raise BadRequest('Unsupported Content-Type.')
141 7e2f9d4b Giorgos Verigakis
142 8b01f7f3 Giorgos Verigakis
def update_response_headers(request, response):
143 8b01f7f3 Giorgos Verigakis
    if request.serialization == 'xml':
144 8b01f7f3 Giorgos Verigakis
        response['Content-Type'] = 'application/xml'
145 8b01f7f3 Giorgos Verigakis
    elif request.serialization == 'atom':
146 8b01f7f3 Giorgos Verigakis
        response['Content-Type'] = 'application/atom+xml'
147 8b01f7f3 Giorgos Verigakis
    else:
148 8b01f7f3 Giorgos Verigakis
        response['Content-Type'] = 'application/json'
149 aa197ee4 Vangelis Koukis
150 4cf8adf8 Vangelis Koukis
    if settings.TEST:
151 cdb65551 Giorgos Verigakis
        response['Date'] = format_date_time(time())
152 8b01f7f3 Giorgos Verigakis
153 432fc8c3 Giorgos Verigakis
def render_metadata(request, metadata, use_values=False, status=200):
154 432fc8c3 Giorgos Verigakis
    if request.serialization == 'xml':
155 432fc8c3 Giorgos Verigakis
        data = render_to_string('metadata.xml', {'metadata': metadata})
156 432fc8c3 Giorgos Verigakis
    else:
157 432fc8c3 Giorgos Verigakis
        d = {'metadata': {'values': metadata}} if use_values else {'metadata': metadata}
158 432fc8c3 Giorgos Verigakis
        data = json.dumps(d)
159 432fc8c3 Giorgos Verigakis
    return HttpResponse(data, status=status)
160 432fc8c3 Giorgos Verigakis
161 432fc8c3 Giorgos Verigakis
def render_meta(request, meta, status=200):
162 432fc8c3 Giorgos Verigakis
    if request.serialization == 'xml':
163 432fc8c3 Giorgos Verigakis
        data = render_to_string('meta.xml', {'meta': meta})
164 432fc8c3 Giorgos Verigakis
    else:
165 432fc8c3 Giorgos Verigakis
        data = json.dumps({'meta': {meta.meta_key: meta.meta_value}})
166 432fc8c3 Giorgos Verigakis
    return HttpResponse(data, status=status)
167 432fc8c3 Giorgos Verigakis
168 c36934a7 Giorgos Verigakis
def render_fault(request, fault):
169 4cf8adf8 Vangelis Koukis
    if settings.DEBUG or settings.TEST:
170 7e2f9d4b Giorgos Verigakis
        fault.details = format_exc(fault)
171 aa197ee4 Vangelis Koukis
172 d8e50a39 Giorgos Verigakis
    if request.serialization == 'xml':
173 d8e50a39 Giorgos Verigakis
        data = render_to_string('fault.xml', {'fault': fault})
174 7e2f9d4b Giorgos Verigakis
    else:
175 7e2f9d4b Giorgos Verigakis
        d = {fault.name: {'code': fault.code, 'message': fault.message, 'details': fault.details}}
176 7e2f9d4b Giorgos Verigakis
        data = json.dumps(d)
177 aa197ee4 Vangelis Koukis
178 f0656db1 Giorgos Verigakis
    resp = HttpResponse(data, status=fault.code)
179 8b01f7f3 Giorgos Verigakis
    update_response_headers(request, resp)
180 f0656db1 Giorgos Verigakis
    return resp
181 d8e50a39 Giorgos Verigakis
182 432fc8c3 Giorgos Verigakis
183 d8e50a39 Giorgos Verigakis
def request_serialization(request, atom_allowed=False):
184 d8e50a39 Giorgos Verigakis
    """Return the serialization format requested.
185 aa197ee4 Vangelis Koukis

186 529178b1 Giorgos Verigakis
    Valid formats are 'json', 'xml' and 'atom' if `atom_allowed` is True.
187 d8e50a39 Giorgos Verigakis
    """
188 aa197ee4 Vangelis Koukis
189 d8e50a39 Giorgos Verigakis
    path = request.path
190 aa197ee4 Vangelis Koukis
191 d8e50a39 Giorgos Verigakis
    if path.endswith('.json'):
192 d8e50a39 Giorgos Verigakis
        return 'json'
193 d8e50a39 Giorgos Verigakis
    elif path.endswith('.xml'):
194 d8e50a39 Giorgos Verigakis
        return 'xml'
195 d8e50a39 Giorgos Verigakis
    elif atom_allowed and path.endswith('.atom'):
196 d8e50a39 Giorgos Verigakis
        return 'atom'
197 aa197ee4 Vangelis Koukis
198 d8e50a39 Giorgos Verigakis
    for item in request.META.get('HTTP_ACCEPT', '').split(','):
199 d8e50a39 Giorgos Verigakis
        accept, sep, rest = item.strip().partition(';')
200 d8e50a39 Giorgos Verigakis
        if accept == 'application/json':
201 d8e50a39 Giorgos Verigakis
            return 'json'
202 d8e50a39 Giorgos Verigakis
        elif accept == 'application/xml':
203 d8e50a39 Giorgos Verigakis
            return 'xml'
204 d8e50a39 Giorgos Verigakis
        elif atom_allowed and accept == 'application/atom+xml':
205 d8e50a39 Giorgos Verigakis
            return 'atom'
206 aa197ee4 Vangelis Koukis
207 d8e50a39 Giorgos Verigakis
    return 'json'
208 7e2f9d4b Giorgos Verigakis
209 d8e50a39 Giorgos Verigakis
def api_method(http_method=None, atom_allowed=False):
210 d8e50a39 Giorgos Verigakis
    """Decorator function for views that implement an API method."""
211 aa197ee4 Vangelis Koukis
212 c36934a7 Giorgos Verigakis
    def decorator(func):
213 c36934a7 Giorgos Verigakis
        @wraps(func)
214 c36934a7 Giorgos Verigakis
        def wrapper(request, *args, **kwargs):
215 c36934a7 Giorgos Verigakis
            try:
216 97e56881 Vangelis Koukis
                request.serialization = request_serialization(request, atom_allowed)
217 40777cc8 Giorgos Verigakis
                if not request.user:
218 40777cc8 Giorgos Verigakis
                    raise Unauthorized('No user found.')
219 d8e50a39 Giorgos Verigakis
                if http_method and request.method != http_method:
220 d8e50a39 Giorgos Verigakis
                    raise BadRequest('Method not allowed.')
221 aa197ee4 Vangelis Koukis
222 c36934a7 Giorgos Verigakis
                resp = func(request, *args, **kwargs)
223 8b01f7f3 Giorgos Verigakis
                update_response_headers(request, resp)
224 c36934a7 Giorgos Verigakis
                return resp
225 5231a38a Giorgos Verigakis
            except VirtualMachine.DeletedError:
226 5231a38a Giorgos Verigakis
                fault = BadRequest('Server has been deleted.')
227 5231a38a Giorgos Verigakis
                return render_fault(request, fault)
228 5231a38a Giorgos Verigakis
            except VirtualMachine.BuildingError:
229 5231a38a Giorgos Verigakis
                fault = BuildInProgress('Server is being built.')
230 5231a38a Giorgos Verigakis
                return render_fault(request, fault)
231 c36934a7 Giorgos Verigakis
            except Fault, fault:
232 c36934a7 Giorgos Verigakis
                return render_fault(request, fault)
233 a62a4d20 Giorgos Verigakis
            except BaseException, e:
234 529178b1 Giorgos Verigakis
                logging.exception('Unexpected error: %s', e)
235 529178b1 Giorgos Verigakis
                fault = ServiceUnavailable('Unexpected error.')
236 c36934a7 Giorgos Verigakis
                return render_fault(request, fault)
237 c36934a7 Giorgos Verigakis
        return wrapper
238 c36934a7 Giorgos Verigakis
    return decorator