Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / api / util.py @ 74459c12

History | View | Annotate | Download (15.8 kB)

1 6ef51e9f Giorgos Verigakis
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2 d1387ed7 Christodoulos Psaltis
#
3 adee02b8 Giorgos Verigakis
# Redistribution and use in source and binary forms, with or
4 adee02b8 Giorgos Verigakis
# without modification, are permitted provided that the following
5 adee02b8 Giorgos Verigakis
# conditions are met:
6 d1387ed7 Christodoulos Psaltis
#
7 adee02b8 Giorgos Verigakis
#   1. Redistributions of source code must retain the above
8 adee02b8 Giorgos Verigakis
#      copyright notice, this list of conditions and the following
9 adee02b8 Giorgos Verigakis
#      disclaimer.
10 d1387ed7 Christodoulos Psaltis
#
11 adee02b8 Giorgos Verigakis
#   2. Redistributions in binary form must reproduce the above
12 adee02b8 Giorgos Verigakis
#      copyright notice, this list of conditions and the following
13 adee02b8 Giorgos Verigakis
#      disclaimer in the documentation and/or other materials
14 adee02b8 Giorgos Verigakis
#      provided with the distribution.
15 d1387ed7 Christodoulos Psaltis
#
16 adee02b8 Giorgos Verigakis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 adee02b8 Giorgos Verigakis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 adee02b8 Giorgos Verigakis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 adee02b8 Giorgos Verigakis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 adee02b8 Giorgos Verigakis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 adee02b8 Giorgos Verigakis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 adee02b8 Giorgos Verigakis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 adee02b8 Giorgos Verigakis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 adee02b8 Giorgos Verigakis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 adee02b8 Giorgos Verigakis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 adee02b8 Giorgos Verigakis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 adee02b8 Giorgos Verigakis
# POSSIBILITY OF SUCH DAMAGE.
28 d1387ed7 Christodoulos Psaltis
#
29 adee02b8 Giorgos Verigakis
# The views and conclusions contained in the software and
30 adee02b8 Giorgos Verigakis
# documentation are those of the authors and should not be
31 adee02b8 Giorgos Verigakis
# interpreted as representing official policies, either expressed
32 adee02b8 Giorgos Verigakis
# or implied, of GRNET S.A.
33 7e2f9d4b Giorgos Verigakis
34 c738c935 Giorgos Verigakis
import datetime
35 c738c935 Giorgos Verigakis
36 dca7553e Christos Stavrakakis
from base64 import b64encode, b64decode
37 d8e50a39 Giorgos Verigakis
from datetime import timedelta, tzinfo
38 d8e50a39 Giorgos Verigakis
from functools import wraps
39 c738c935 Giorgos Verigakis
from hashlib import sha256
40 2035039b Giorgos Verigakis
from logging import getLogger
41 d8e50a39 Giorgos Verigakis
from random import choice
42 dca2a31f Giorgos Verigakis
from string import digits, lowercase, uppercase
43 8b01f7f3 Giorgos Verigakis
from time import time
44 d8e50a39 Giorgos Verigakis
from traceback import format_exc
45 8b01f7f3 Giorgos Verigakis
from wsgiref.handlers import format_date_time
46 7e2f9d4b Giorgos Verigakis
47 2035039b Giorgos Verigakis
import dateutil.parser
48 2035039b Giorgos Verigakis
49 c738c935 Giorgos Verigakis
from Crypto.Cipher import AES
50 529178b1 Giorgos Verigakis
51 d8e50a39 Giorgos Verigakis
from django.conf import settings
52 7e2f9d4b Giorgos Verigakis
from django.http import HttpResponse
53 7e2f9d4b Giorgos Verigakis
from django.template.loader import render_to_string
54 29a59bc1 Giorgos Verigakis
from django.utils import simplejson as json
55 b75555e5 Giorgos Verigakis
from django.utils.cache import add_never_cache_headers
56 d3406fbc Christos Stavrakakis
from django.db.models import Q
57 7e2f9d4b Giorgos Verigakis
58 b36f78fa Giorgos Verigakis
from synnefo.api.faults import (Fault, BadRequest, BuildInProgress,
59 6dd70a5c Christos Stavrakakis
                                ItemNotFound, ServiceUnavailable, Unauthorized,
60 dca7553e Christos Stavrakakis
                                BadMediaType, Forbidden, OverLimit)
61 6ef51e9f Giorgos Verigakis
from synnefo.db.models import (Flavor, VirtualMachine, VirtualMachineMetadata,
62 7fede91e Christos Stavrakakis
                               Network, BackendNetwork, NetworkInterface,
63 af6a3bc5 Christos Stavrakakis
                               BridgePoolTable, MacPrefixPoolTable)
64 adc46059 Christos Stavrakakis
from synnefo.db.pools import EmptyPool
65 6dd70a5c Christos Stavrakakis
66 4b3b8688 Giorgos Verigakis
from synnefo.lib.astakos import get_user
67 3a9b3cde Giorgos Verigakis
from synnefo.plankton.backend import ImageBackend
68 b14725eb Christos Stavrakakis
from synnefo.settings import MAX_CIDR_BLOCK
69 9e98ba3c Giorgos Verigakis
70 9e98ba3c Giorgos Verigakis
71 9e98ba3c Giorgos Verigakis
log = getLogger('synnefo.api')
72 9e98ba3c Giorgos Verigakis
73 7e2f9d4b Giorgos Verigakis
74 d8e50a39 Giorgos Verigakis
class UTC(tzinfo):
75 d8e50a39 Giorgos Verigakis
    def utcoffset(self, dt):
76 d8e50a39 Giorgos Verigakis
        return timedelta(0)
77 aa197ee4 Vangelis Koukis
78 d8e50a39 Giorgos Verigakis
    def tzname(self, dt):
79 d8e50a39 Giorgos Verigakis
        return 'UTC'
80 aa197ee4 Vangelis Koukis
81 d8e50a39 Giorgos Verigakis
    def dst(self, dt):
82 d8e50a39 Giorgos Verigakis
        return timedelta(0)
83 7e2f9d4b Giorgos Verigakis
84 7e2f9d4b Giorgos Verigakis
85 d8e50a39 Giorgos Verigakis
def isoformat(d):
86 529178b1 Giorgos Verigakis
    """Return an ISO8601 date string that includes a timezone."""
87 aa197ee4 Vangelis Koukis
88 d8e50a39 Giorgos Verigakis
    return d.replace(tzinfo=UTC()).isoformat()
89 d8e50a39 Giorgos Verigakis
90 a08e4270 Vangelis Koukis
91 d8e50a39 Giorgos Verigakis
def isoparse(s):
92 d8e50a39 Giorgos Verigakis
    """Parse an ISO8601 date string into a datetime object."""
93 aa197ee4 Vangelis Koukis
94 d8e50a39 Giorgos Verigakis
    if not s:
95 d8e50a39 Giorgos Verigakis
        return None
96 aa197ee4 Vangelis Koukis
97 d8e50a39 Giorgos Verigakis
    try:
98 d8e50a39 Giorgos Verigakis
        since = dateutil.parser.parse(s)
99 e87d30f3 Giorgos Verigakis
        utc_since = since.astimezone(UTC()).replace(tzinfo=None)
100 d8e50a39 Giorgos Verigakis
    except ValueError:
101 d8e50a39 Giorgos Verigakis
        raise BadRequest('Invalid changes-since parameter.')
102 aa197ee4 Vangelis Koukis
103 e87d30f3 Giorgos Verigakis
    now = datetime.datetime.now()
104 e87d30f3 Giorgos Verigakis
    if utc_since > now:
105 d8e50a39 Giorgos Verigakis
        raise BadRequest('changes-since value set in the future.')
106 aa197ee4 Vangelis Koukis
107 e87d30f3 Giorgos Verigakis
    if now - utc_since > timedelta(seconds=settings.POLL_LIMIT):
108 d8e50a39 Giorgos Verigakis
        raise BadRequest('Too old changes-since value.')
109 aa197ee4 Vangelis Koukis
110 0140e54b Vangelis Koukis
    return utc_since
111 aa197ee4 Vangelis Koukis
112 dca2a31f Giorgos Verigakis
113 dca2a31f Giorgos Verigakis
def random_password():
114 dca2a31f Giorgos Verigakis
    """Generates a random password
115 ce55f211 Kostas Papadimitriou

116 62eac5a6 Giorgos Verigakis
    We generate a windows compliant password: it must contain at least
117 dca2a31f Giorgos Verigakis
    one charachter from each of the groups: upper case, lower case, digits.
118 dca2a31f Giorgos Verigakis
    """
119 ce55f211 Kostas Papadimitriou
120 dca2a31f Giorgos Verigakis
    pool = lowercase + uppercase + digits
121 dca2a31f Giorgos Verigakis
    lowerset = set(lowercase)
122 dca2a31f Giorgos Verigakis
    upperset = set(uppercase)
123 dca2a31f Giorgos Verigakis
    digitset = set(digits)
124 dca2a31f Giorgos Verigakis
    length = 10
125 ce55f211 Kostas Papadimitriou
126 62eac5a6 Giorgos Verigakis
    password = ''.join(choice(pool) for i in range(length - 2))
127 ce55f211 Kostas Papadimitriou
128 62eac5a6 Giorgos Verigakis
    # Make sure the password is compliant
129 62eac5a6 Giorgos Verigakis
    chars = set(password)
130 62eac5a6 Giorgos Verigakis
    if not chars & lowerset:
131 62eac5a6 Giorgos Verigakis
        password += choice(lowercase)
132 62eac5a6 Giorgos Verigakis
    if not chars & upperset:
133 62eac5a6 Giorgos Verigakis
        password += choice(uppercase)
134 62eac5a6 Giorgos Verigakis
    if not chars & digitset:
135 62eac5a6 Giorgos Verigakis
        password += choice(digits)
136 ce55f211 Kostas Papadimitriou
137 62eac5a6 Giorgos Verigakis
    # Pad if necessary to reach required length
138 62eac5a6 Giorgos Verigakis
    password += ''.join(choice(pool) for i in range(length - len(password)))
139 ce55f211 Kostas Papadimitriou
140 dca2a31f Giorgos Verigakis
    return password
141 dca2a31f Giorgos Verigakis
142 d8e50a39 Giorgos Verigakis
143 c738c935 Giorgos Verigakis
def zeropad(s):
144 c738c935 Giorgos Verigakis
    """Add zeros at the end of a string in order to make its length
145 c738c935 Giorgos Verigakis
       a multiple of 16."""
146 d1387ed7 Christodoulos Psaltis
147 c738c935 Giorgos Verigakis
    npad = 16 - len(s) % 16
148 c738c935 Giorgos Verigakis
    return s + '\x00' * npad
149 c738c935 Giorgos Verigakis
150 a08e4270 Vangelis Koukis
151 c738c935 Giorgos Verigakis
def encrypt(plaintext):
152 c738c935 Giorgos Verigakis
    # Make sure key is 32 bytes long
153 c738c935 Giorgos Verigakis
    key = sha256(settings.SECRET_KEY).digest()
154 d1387ed7 Christodoulos Psaltis
155 c738c935 Giorgos Verigakis
    aes = AES.new(key)
156 c738c935 Giorgos Verigakis
    enc = aes.encrypt(zeropad(plaintext))
157 c738c935 Giorgos Verigakis
    return b64encode(enc)
158 c738c935 Giorgos Verigakis
159 7e2f9d4b Giorgos Verigakis
160 7f2dbcad Christos Stavrakakis
def get_vm(server_id, user_id, for_update=False, non_deleted=False,
161 7f2dbcad Christos Stavrakakis
           non_suspended=False):
162 e221ade2 Christos Stavrakakis
    """Find a VirtualMachine instance based on ID and owner."""
163 aa197ee4 Vangelis Koukis
164 d8e50a39 Giorgos Verigakis
    try:
165 d8e50a39 Giorgos Verigakis
        server_id = int(server_id)
166 7f2dbcad Christos Stavrakakis
        servers = VirtualMachine.objects
167 7f2dbcad Christos Stavrakakis
        if for_update:
168 7f2dbcad Christos Stavrakakis
            servers = servers.select_for_update()
169 7f2dbcad Christos Stavrakakis
        vm = servers.get(id=server_id, userid=user_id)
170 e221ade2 Christos Stavrakakis
        if non_deleted and vm.deleted:
171 e221ade2 Christos Stavrakakis
            raise VirtualMachine.DeletedError
172 e221ade2 Christos Stavrakakis
        if non_suspended and vm.suspended:
173 17c2ed57 Christos Stavrakakis
            raise Forbidden("Administratively Suspended VM")
174 3a522cff Christos Stavrakakis
        return vm
175 d8e50a39 Giorgos Verigakis
    except ValueError:
176 d8e50a39 Giorgos Verigakis
        raise BadRequest('Invalid server ID.')
177 d8e50a39 Giorgos Verigakis
    except VirtualMachine.DoesNotExist:
178 d8e50a39 Giorgos Verigakis
        raise ItemNotFound('Server not found.')
179 d8e50a39 Giorgos Verigakis
180 7f2dbcad Christos Stavrakakis
181 40777cc8 Giorgos Verigakis
def get_vm_meta(vm, key):
182 d8e50a39 Giorgos Verigakis
    """Return a VirtualMachineMetadata instance or raise ItemNotFound."""
183 aa197ee4 Vangelis Koukis
184 d8e50a39 Giorgos Verigakis
    try:
185 40777cc8 Giorgos Verigakis
        return VirtualMachineMetadata.objects.get(meta_key=key, vm=vm)
186 d8e50a39 Giorgos Verigakis
    except VirtualMachineMetadata.DoesNotExist:
187 d8e50a39 Giorgos Verigakis
        raise ItemNotFound('Metadata key not found.')
188 d8e50a39 Giorgos Verigakis
189 a08e4270 Vangelis Koukis
190 6ef51e9f Giorgos Verigakis
def get_image(image_id, user_id):
191 d8e50a39 Giorgos Verigakis
    """Return an Image instance or raise ItemNotFound."""
192 aa197ee4 Vangelis Koukis
193 6ef51e9f Giorgos Verigakis
    backend = ImageBackend(user_id)
194 d8e50a39 Giorgos Verigakis
    try:
195 6ef51e9f Giorgos Verigakis
        image = backend.get_image(image_id)
196 3a9b3cde Giorgos Verigakis
        if not image:
197 3a9b3cde Giorgos Verigakis
            raise ItemNotFound('Image not found.')
198 3a9b3cde Giorgos Verigakis
        return image
199 3a9b3cde Giorgos Verigakis
    finally:
200 3a9b3cde Giorgos Verigakis
        backend.close()
201 3a9b3cde Giorgos Verigakis
202 a08e4270 Vangelis Koukis
203 dca7553e Christos Stavrakakis
def get_image_dict(image_id, user_id):
204 dca7553e Christos Stavrakakis
    image = {}
205 dca7553e Christos Stavrakakis
    img = get_image(image_id, user_id)
206 dca7553e Christos Stavrakakis
    properties = img.get('properties', {})
207 dca7553e Christos Stavrakakis
    image['backend_id'] = img['location']
208 dca7553e Christos Stavrakakis
    image['format'] = img['disk_format']
209 dca7553e Christos Stavrakakis
    image['metadata'] = dict((key.upper(), val) \
210 dca7553e Christos Stavrakakis
                             for key, val in properties.items())
211 dca7553e Christos Stavrakakis
    return image
212 dca7553e Christos Stavrakakis
213 dca7553e Christos Stavrakakis
214 aa8230bd Christos Stavrakakis
def get_flavor(flavor_id, include_deleted=False):
215 529178b1 Giorgos Verigakis
    """Return a Flavor instance or raise ItemNotFound."""
216 aa197ee4 Vangelis Koukis
217 529178b1 Giorgos Verigakis
    try:
218 529178b1 Giorgos Verigakis
        flavor_id = int(flavor_id)
219 aa8230bd Christos Stavrakakis
        if include_deleted:
220 aa8230bd Christos Stavrakakis
            return Flavor.objects.get(id=flavor_id)
221 aa8230bd Christos Stavrakakis
        else:
222 aa8230bd Christos Stavrakakis
            return Flavor.objects.get(id=flavor_id, deleted=include_deleted)
223 6ef51e9f Giorgos Verigakis
    except (ValueError, Flavor.DoesNotExist):
224 529178b1 Giorgos Verigakis
        raise ItemNotFound('Flavor not found.')
225 d8e50a39 Giorgos Verigakis
226 a08e4270 Vangelis Koukis
227 d2e73c0c Christos Stavrakakis
def get_network(network_id, user_id, for_update=False):
228 e2ee7808 Giorgos Verigakis
    """Return a Network instance or raise ItemNotFound."""
229 aa197ee4 Vangelis Koukis
230 e2ee7808 Giorgos Verigakis
    try:
231 7fede91e Christos Stavrakakis
        network_id = int(network_id)
232 d3406fbc Christos Stavrakakis
        objects = Network.objects
233 d2e73c0c Christos Stavrakakis
        if for_update:
234 d3406fbc Christos Stavrakakis
            objects = objects.select_for_update()
235 d3406fbc Christos Stavrakakis
        return objects.get(Q(userid=user_id) | Q(public=True), id=network_id)
236 6ef51e9f Giorgos Verigakis
    except (ValueError, Network.DoesNotExist):
237 e2ee7808 Giorgos Verigakis
        raise ItemNotFound('Network not found.')
238 e2ee7808 Giorgos Verigakis
239 a08e4270 Vangelis Koukis
240 b14725eb Christos Stavrakakis
def validate_network_size(cidr_block):
241 b14725eb Christos Stavrakakis
    """Return True if network size is allowed."""
242 b14725eb Christos Stavrakakis
    return cidr_block <= 29 and cidr_block > MAX_CIDR_BLOCK
243 b14725eb Christos Stavrakakis
244 b14725eb Christos Stavrakakis
245 adc46059 Christos Stavrakakis
def allocate_public_address(backend):
246 adc46059 Christos Stavrakakis
    """Allocate a public IP for a vm."""
247 adc46059 Christos Stavrakakis
    for network in backend_public_networks(backend):
248 adc46059 Christos Stavrakakis
        try:
249 adc46059 Christos Stavrakakis
            address = get_network_free_address(network)
250 adc46059 Christos Stavrakakis
            return (network, address)
251 adc46059 Christos Stavrakakis
        except EmptyPool:
252 adc46059 Christos Stavrakakis
            pass
253 adc46059 Christos Stavrakakis
    return (None, None)
254 adc46059 Christos Stavrakakis
255 adc46059 Christos Stavrakakis
256 dca7553e Christos Stavrakakis
def get_public_ip(backend):
257 dca7553e Christos Stavrakakis
    """Reserve an IP from a public network.
258 dca7553e Christos Stavrakakis

259 dca7553e Christos Stavrakakis
    This method should run inside a transaction.
260 dca7553e Christos Stavrakakis

261 dca7553e Christos Stavrakakis
    """
262 dca7553e Christos Stavrakakis
    address = None
263 74459c12 Christos Stavrakakis
    if settings.PUBLIC_USE_POOL:
264 dca7553e Christos Stavrakakis
        (network, address) = allocate_public_address(backend)
265 dca7553e Christos Stavrakakis
    else:
266 dca7553e Christos Stavrakakis
        for net in list(backend_public_networks(backend)):
267 dca7553e Christos Stavrakakis
            pool = net.get_pool()
268 dca7553e Christos Stavrakakis
            if not pool.empty():
269 dca7553e Christos Stavrakakis
                address = 'pool'
270 dca7553e Christos Stavrakakis
                network = net
271 dca7553e Christos Stavrakakis
                break
272 dca7553e Christos Stavrakakis
    if address is None:
273 dca7553e Christos Stavrakakis
        log.error("Public networks of backend %s are full", backend)
274 dca7553e Christos Stavrakakis
        raise OverLimit("Can not allocate IP for new machine."
275 dca7553e Christos Stavrakakis
                        " Public networks are full.")
276 dca7553e Christos Stavrakakis
    return (network, address)
277 dca7553e Christos Stavrakakis
278 dca7553e Christos Stavrakakis
279 7fede91e Christos Stavrakakis
def backend_public_networks(backend):
280 7fede91e Christos Stavrakakis
    """Return available public networks of the backend.
281 7fede91e Christos Stavrakakis

282 7fede91e Christos Stavrakakis
    Iterator for non-deleted public networks that are available
283 7fede91e Christos Stavrakakis
    to the specified backend.
284 7fede91e Christos Stavrakakis

285 7fede91e Christos Stavrakakis
    """
286 7fede91e Christos Stavrakakis
    for network in Network.objects.filter(public=True, deleted=False):
287 7fede91e Christos Stavrakakis
        if BackendNetwork.objects.filter(network=network,
288 7fede91e Christos Stavrakakis
                                         backend=backend).exists():
289 7fede91e Christos Stavrakakis
            yield network
290 7fede91e Christos Stavrakakis
291 7fede91e Christos Stavrakakis
292 7fede91e Christos Stavrakakis
def get_network_free_address(network):
293 7fede91e Christos Stavrakakis
    """Reserve an IP address from the IP Pool of the network.
294 7fede91e Christos Stavrakakis

295 fdc94944 Christos Stavrakakis
    Raises EmptyPool
296 7fede91e Christos Stavrakakis

297 7fede91e Christos Stavrakakis
    """
298 7fede91e Christos Stavrakakis
299 fdc94944 Christos Stavrakakis
    pool = network.get_pool()
300 fdc94944 Christos Stavrakakis
    address = pool.get()
301 7fede91e Christos Stavrakakis
    pool.save()
302 7fede91e Christos Stavrakakis
    return address
303 7fede91e Christos Stavrakakis
304 7fede91e Christos Stavrakakis
305 d44c236b Giorgos Verigakis
def get_nic(machine, network):
306 d44c236b Giorgos Verigakis
    try:
307 d44c236b Giorgos Verigakis
        return NetworkInterface.objects.get(machine=machine, network=network)
308 d44c236b Giorgos Verigakis
    except NetworkInterface.DoesNotExist:
309 d44c236b Giorgos Verigakis
        raise ItemNotFound('Server not connected to this network.')
310 d44c236b Giorgos Verigakis
311 7fede91e Christos Stavrakakis
312 08b079e2 Stavros Sachtouris
def get_nic_from_index(vm, nic_index):
313 08b079e2 Stavros Sachtouris
    """Returns the nic_index-th nic of a vm
314 08b079e2 Stavros Sachtouris
       Error Response Codes: itemNotFound (404), badMediaType (415)
315 08b079e2 Stavros Sachtouris
    """
316 08b079e2 Stavros Sachtouris
    matching_nics = vm.nics.filter(index=nic_index)
317 08b079e2 Stavros Sachtouris
    matching_nics_len = len(matching_nics)
318 08b079e2 Stavros Sachtouris
    if matching_nics_len < 1:
319 08b079e2 Stavros Sachtouris
        raise  ItemNotFound('NIC not found on VM')
320 08b079e2 Stavros Sachtouris
    elif matching_nics_len > 1:
321 08b079e2 Stavros Sachtouris
        raise BadMediaType('NIC index conflict on VM')
322 08b079e2 Stavros Sachtouris
    nic = matching_nics[0]
323 08b079e2 Stavros Sachtouris
    return nic
324 e2ee7808 Giorgos Verigakis
325 7fede91e Christos Stavrakakis
326 7e2f9d4b Giorgos Verigakis
def get_request_dict(request):
327 d8e50a39 Giorgos Verigakis
    """Returns data sent by the client as a python dict."""
328 aa197ee4 Vangelis Koukis
329 7e2f9d4b Giorgos Verigakis
    data = request.raw_post_data
330 7e45ddef Dimitris Moraitis
    if request.META.get('CONTENT_TYPE').startswith('application/json'):
331 7e2f9d4b Giorgos Verigakis
        try:
332 7e2f9d4b Giorgos Verigakis
            return json.loads(data)
333 7e2f9d4b Giorgos Verigakis
        except ValueError:
334 d8e50a39 Giorgos Verigakis
            raise BadRequest('Invalid JSON data.')
335 d8e50a39 Giorgos Verigakis
    else:
336 d8e50a39 Giorgos Verigakis
        raise BadRequest('Unsupported Content-Type.')
337 7e2f9d4b Giorgos Verigakis
338 a08e4270 Vangelis Koukis
339 8b01f7f3 Giorgos Verigakis
def update_response_headers(request, response):
340 8b01f7f3 Giorgos Verigakis
    if request.serialization == 'xml':
341 8b01f7f3 Giorgos Verigakis
        response['Content-Type'] = 'application/xml'
342 8b01f7f3 Giorgos Verigakis
    elif request.serialization == 'atom':
343 8b01f7f3 Giorgos Verigakis
        response['Content-Type'] = 'application/atom+xml'
344 8b01f7f3 Giorgos Verigakis
    else:
345 8b01f7f3 Giorgos Verigakis
        response['Content-Type'] = 'application/json'
346 aa197ee4 Vangelis Koukis
347 4cf8adf8 Vangelis Koukis
    if settings.TEST:
348 cdb65551 Giorgos Verigakis
        response['Date'] = format_date_time(time())
349 ce55f211 Kostas Papadimitriou
350 b75555e5 Giorgos Verigakis
    add_never_cache_headers(response)
351 b75555e5 Giorgos Verigakis
352 8b01f7f3 Giorgos Verigakis
353 432fc8c3 Giorgos Verigakis
def render_metadata(request, metadata, use_values=False, status=200):
354 432fc8c3 Giorgos Verigakis
    if request.serialization == 'xml':
355 432fc8c3 Giorgos Verigakis
        data = render_to_string('metadata.xml', {'metadata': metadata})
356 432fc8c3 Giorgos Verigakis
    else:
357 b36f78fa Giorgos Verigakis
        if use_values:
358 b36f78fa Giorgos Verigakis
            d = {'metadata': {'values': metadata}}
359 b36f78fa Giorgos Verigakis
        else:
360 b36f78fa Giorgos Verigakis
            d = {'metadata': metadata}
361 432fc8c3 Giorgos Verigakis
        data = json.dumps(d)
362 432fc8c3 Giorgos Verigakis
    return HttpResponse(data, status=status)
363 432fc8c3 Giorgos Verigakis
364 a08e4270 Vangelis Koukis
365 432fc8c3 Giorgos Verigakis
def render_meta(request, meta, status=200):
366 432fc8c3 Giorgos Verigakis
    if request.serialization == 'xml':
367 6ef51e9f Giorgos Verigakis
        data = render_to_string('meta.xml', dict(key=key, val=val))
368 432fc8c3 Giorgos Verigakis
    else:
369 6ef51e9f Giorgos Verigakis
        data = json.dumps(dict(meta=meta))
370 432fc8c3 Giorgos Verigakis
    return HttpResponse(data, status=status)
371 432fc8c3 Giorgos Verigakis
372 a08e4270 Vangelis Koukis
373 c36934a7 Giorgos Verigakis
def render_fault(request, fault):
374 4cf8adf8 Vangelis Koukis
    if settings.DEBUG or settings.TEST:
375 7e2f9d4b Giorgos Verigakis
        fault.details = format_exc(fault)
376 aa197ee4 Vangelis Koukis
377 d8e50a39 Giorgos Verigakis
    if request.serialization == 'xml':
378 d8e50a39 Giorgos Verigakis
        data = render_to_string('fault.xml', {'fault': fault})
379 7e2f9d4b Giorgos Verigakis
    else:
380 b36f78fa Giorgos Verigakis
        d = {fault.name: {
381 b36f78fa Giorgos Verigakis
                'code': fault.code,
382 b36f78fa Giorgos Verigakis
                'message': fault.message,
383 b36f78fa Giorgos Verigakis
                'details': fault.details}}
384 7e2f9d4b Giorgos Verigakis
        data = json.dumps(d)
385 aa197ee4 Vangelis Koukis
386 f0656db1 Giorgos Verigakis
    resp = HttpResponse(data, status=fault.code)
387 8b01f7f3 Giorgos Verigakis
    update_response_headers(request, resp)
388 f0656db1 Giorgos Verigakis
    return resp
389 d8e50a39 Giorgos Verigakis
390 432fc8c3 Giorgos Verigakis
391 d8e50a39 Giorgos Verigakis
def request_serialization(request, atom_allowed=False):
392 d8e50a39 Giorgos Verigakis
    """Return the serialization format requested.
393 aa197ee4 Vangelis Koukis

394 529178b1 Giorgos Verigakis
    Valid formats are 'json', 'xml' and 'atom' if `atom_allowed` is True.
395 d8e50a39 Giorgos Verigakis
    """
396 aa197ee4 Vangelis Koukis
397 d8e50a39 Giorgos Verigakis
    path = request.path
398 aa197ee4 Vangelis Koukis
399 d8e50a39 Giorgos Verigakis
    if path.endswith('.json'):
400 d8e50a39 Giorgos Verigakis
        return 'json'
401 d8e50a39 Giorgos Verigakis
    elif path.endswith('.xml'):
402 d8e50a39 Giorgos Verigakis
        return 'xml'
403 d8e50a39 Giorgos Verigakis
    elif atom_allowed and path.endswith('.atom'):
404 d8e50a39 Giorgos Verigakis
        return 'atom'
405 aa197ee4 Vangelis Koukis
406 d8e50a39 Giorgos Verigakis
    for item in request.META.get('HTTP_ACCEPT', '').split(','):
407 d8e50a39 Giorgos Verigakis
        accept, sep, rest = item.strip().partition(';')
408 d8e50a39 Giorgos Verigakis
        if accept == 'application/json':
409 d8e50a39 Giorgos Verigakis
            return 'json'
410 d8e50a39 Giorgos Verigakis
        elif accept == 'application/xml':
411 d8e50a39 Giorgos Verigakis
            return 'xml'
412 d8e50a39 Giorgos Verigakis
        elif atom_allowed and accept == 'application/atom+xml':
413 d8e50a39 Giorgos Verigakis
            return 'atom'
414 aa197ee4 Vangelis Koukis
415 d8e50a39 Giorgos Verigakis
    return 'json'
416 7e2f9d4b Giorgos Verigakis
417 a08e4270 Vangelis Koukis
418 d8e50a39 Giorgos Verigakis
def api_method(http_method=None, atom_allowed=False):
419 d8e50a39 Giorgos Verigakis
    """Decorator function for views that implement an API method."""
420 aa197ee4 Vangelis Koukis
421 c36934a7 Giorgos Verigakis
    def decorator(func):
422 c36934a7 Giorgos Verigakis
        @wraps(func)
423 c36934a7 Giorgos Verigakis
        def wrapper(request, *args, **kwargs):
424 c36934a7 Giorgos Verigakis
            try:
425 6ef51e9f Giorgos Verigakis
                request.serialization = request_serialization(request,
426 6ef51e9f Giorgos Verigakis
                                                              atom_allowed)
427 4b3b8688 Giorgos Verigakis
                get_user(request, settings.ASTAKOS_URL)
428 4b3b8688 Giorgos Verigakis
                if not request.user_uniq:
429 40777cc8 Giorgos Verigakis
                    raise Unauthorized('No user found.')
430 d8e50a39 Giorgos Verigakis
                if http_method and request.method != http_method:
431 d8e50a39 Giorgos Verigakis
                    raise BadRequest('Method not allowed.')
432 7fede91e Christos Stavrakakis
433 c36934a7 Giorgos Verigakis
                resp = func(request, *args, **kwargs)
434 8b01f7f3 Giorgos Verigakis
                update_response_headers(request, resp)
435 c36934a7 Giorgos Verigakis
                return resp
436 5231a38a Giorgos Verigakis
            except VirtualMachine.DeletedError:
437 5231a38a Giorgos Verigakis
                fault = BadRequest('Server has been deleted.')
438 5231a38a Giorgos Verigakis
                return render_fault(request, fault)
439 22a9959d Christos Stavrakakis
            except Network.DeletedError:
440 22a9959d Christos Stavrakakis
                fault = BadRequest('Network has been deleted.')
441 22a9959d Christos Stavrakakis
                return render_fault(request, fault)
442 5231a38a Giorgos Verigakis
            except VirtualMachine.BuildingError:
443 5231a38a Giorgos Verigakis
                fault = BuildInProgress('Server is being built.')
444 5231a38a Giorgos Verigakis
                return render_fault(request, fault)
445 c36934a7 Giorgos Verigakis
            except Fault, fault:
446 260147b1 Christos Stavrakakis
                if fault.code >= 500:
447 260147b1 Christos Stavrakakis
                    log.exception('API fault')
448 c36934a7 Giorgos Verigakis
                return render_fault(request, fault)
449 7fede91e Christos Stavrakakis
            except BaseException:
450 9e98ba3c Giorgos Verigakis
                log.exception('Unexpected error')
451 529178b1 Giorgos Verigakis
                fault = ServiceUnavailable('Unexpected error.')
452 c36934a7 Giorgos Verigakis
                return render_fault(request, fault)
453 c36934a7 Giorgos Verigakis
        return wrapper
454 c36934a7 Giorgos Verigakis
    return decorator
455 08b079e2 Stavros Sachtouris
456 3165f027 Christos Stavrakakis
457 08b079e2 Stavros Sachtouris
def construct_nic_id(nic):
458 08b079e2 Stavros Sachtouris
    return "-".join(["nic", unicode(nic.machine.id), unicode(nic.index)])
459 6dd70a5c Christos Stavrakakis
460 6dd70a5c Christos Stavrakakis
461 af6a3bc5 Christos Stavrakakis
def net_resources(net_type):
462 af6a3bc5 Christos Stavrakakis
    mac_prefix = settings.MAC_POOL_BASE
463 af6a3bc5 Christos Stavrakakis
    if net_type == 'PRIVATE_MAC_FILTERED':
464 3165f027 Christos Stavrakakis
        link = settings.PRIVATE_MAC_FILTERED_BRIDGE
465 af6a3bc5 Christos Stavrakakis
        mac_pool = MacPrefixPoolTable.get_pool()
466 af6a3bc5 Christos Stavrakakis
        mac_prefix = mac_pool.get()
467 af6a3bc5 Christos Stavrakakis
        mac_pool.save()
468 af6a3bc5 Christos Stavrakakis
    elif net_type == 'PRIVATE_PHYSICAL_VLAN':
469 03992c72 Christos Stavrakakis
        pool = BridgePoolTable.get_pool()
470 03992c72 Christos Stavrakakis
        link = pool.get()
471 03992c72 Christos Stavrakakis
        pool.save()
472 af6a3bc5 Christos Stavrakakis
    elif net_type == 'CUSTOM_ROUTED':
473 3165f027 Christos Stavrakakis
        link = settings.CUSTOM_ROUTED_ROUTING_TABLE
474 af6a3bc5 Christos Stavrakakis
    elif net_type == 'CUSTOM_BRIDGED':
475 3165f027 Christos Stavrakakis
        link = settings.CUSTOM_BRIDGED_BRIDGE
476 af6a3bc5 Christos Stavrakakis
    elif net_type == 'PUBLIC_ROUTED':
477 3165f027 Christos Stavrakakis
        link = settings.PUBLIC_ROUTED_ROUTING_TABLE
478 3165f027 Christos Stavrakakis
    else:
479 af6a3bc5 Christos Stavrakakis
        raise BadRequest('Unknown network type')
480 6dd70a5c Christos Stavrakakis
481 af6a3bc5 Christos Stavrakakis
    return link, mac_prefix
482 dca7553e Christos Stavrakakis
483 dca7553e Christos Stavrakakis
484 dca7553e Christos Stavrakakis
def verify_personality(personality):
485 dca7553e Christos Stavrakakis
    for p in personality:
486 dca7553e Christos Stavrakakis
        # Verify that personalities are well-formed
487 dca7553e Christos Stavrakakis
        try:
488 dca7553e Christos Stavrakakis
            assert isinstance(p, dict)
489 dca7553e Christos Stavrakakis
            keys = set(p.keys())
490 dca7553e Christos Stavrakakis
            allowed = set(['contents', 'group', 'mode', 'owner', 'path'])
491 dca7553e Christos Stavrakakis
            assert keys.issubset(allowed)
492 dca7553e Christos Stavrakakis
            contents = p['contents']
493 dca7553e Christos Stavrakakis
            if len(contents) > settings.MAX_PERSONALITY_SIZE:
494 dca7553e Christos Stavrakakis
                # No need to decode if contents already exceed limit
495 dca7553e Christos Stavrakakis
                raise OverLimit("Maximum size of personality exceeded")
496 dca7553e Christos Stavrakakis
            if len(b64decode(contents)) > settings.MAX_PERSONALITY_SIZE:
497 dca7553e Christos Stavrakakis
                raise OverLimit("Maximum size of personality exceeded")
498 dca7553e Christos Stavrakakis
        except AssertionError:
499 dca7553e Christos Stavrakakis
            raise BadRequest("Malformed personality in request")