Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (17.9 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 2db7d9df Christos Stavrakakis
from synnefo.plankton.backend import ImageBackend, NotAllowedError
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 2cb812be Christos Stavrakakis
    image['checksum'] = img['checksum']
212 2cb812be Christos Stavrakakis
213 dca7553e Christos Stavrakakis
    return image
214 dca7553e Christos Stavrakakis
215 dca7553e Christos Stavrakakis
216 aa8230bd Christos Stavrakakis
def get_flavor(flavor_id, include_deleted=False):
217 529178b1 Giorgos Verigakis
    """Return a Flavor instance or raise ItemNotFound."""
218 aa197ee4 Vangelis Koukis
219 529178b1 Giorgos Verigakis
    try:
220 529178b1 Giorgos Verigakis
        flavor_id = int(flavor_id)
221 aa8230bd Christos Stavrakakis
        if include_deleted:
222 aa8230bd Christos Stavrakakis
            return Flavor.objects.get(id=flavor_id)
223 aa8230bd Christos Stavrakakis
        else:
224 aa8230bd Christos Stavrakakis
            return Flavor.objects.get(id=flavor_id, deleted=include_deleted)
225 6ef51e9f Giorgos Verigakis
    except (ValueError, Flavor.DoesNotExist):
226 529178b1 Giorgos Verigakis
        raise ItemNotFound('Flavor not found.')
227 d8e50a39 Giorgos Verigakis
228 a08e4270 Vangelis Koukis
229 d2e73c0c Christos Stavrakakis
def get_network(network_id, user_id, for_update=False):
230 e2ee7808 Giorgos Verigakis
    """Return a Network instance or raise ItemNotFound."""
231 aa197ee4 Vangelis Koukis
232 e2ee7808 Giorgos Verigakis
    try:
233 7fede91e Christos Stavrakakis
        network_id = int(network_id)
234 d3406fbc Christos Stavrakakis
        objects = Network.objects
235 d2e73c0c Christos Stavrakakis
        if for_update:
236 d3406fbc Christos Stavrakakis
            objects = objects.select_for_update()
237 d3406fbc Christos Stavrakakis
        return objects.get(Q(userid=user_id) | Q(public=True), id=network_id)
238 6ef51e9f Giorgos Verigakis
    except (ValueError, Network.DoesNotExist):
239 e2ee7808 Giorgos Verigakis
        raise ItemNotFound('Network not found.')
240 e2ee7808 Giorgos Verigakis
241 a08e4270 Vangelis Koukis
242 b14725eb Christos Stavrakakis
def validate_network_size(cidr_block):
243 b14725eb Christos Stavrakakis
    """Return True if network size is allowed."""
244 b14725eb Christos Stavrakakis
    return cidr_block <= 29 and cidr_block > MAX_CIDR_BLOCK
245 b14725eb Christos Stavrakakis
246 b14725eb Christos Stavrakakis
247 adc46059 Christos Stavrakakis
def allocate_public_address(backend):
248 adc46059 Christos Stavrakakis
    """Allocate a public IP for a vm."""
249 adc46059 Christos Stavrakakis
    for network in backend_public_networks(backend):
250 adc46059 Christos Stavrakakis
        try:
251 adc46059 Christos Stavrakakis
            address = get_network_free_address(network)
252 adc46059 Christos Stavrakakis
            return (network, address)
253 adc46059 Christos Stavrakakis
        except EmptyPool:
254 adc46059 Christos Stavrakakis
            pass
255 adc46059 Christos Stavrakakis
    return (None, None)
256 adc46059 Christos Stavrakakis
257 adc46059 Christos Stavrakakis
258 dca7553e Christos Stavrakakis
def get_public_ip(backend):
259 dca7553e Christos Stavrakakis
    """Reserve an IP from a public network.
260 dca7553e Christos Stavrakakis

261 dca7553e Christos Stavrakakis
    This method should run inside a transaction.
262 dca7553e Christos Stavrakakis

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

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

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

297 fdc94944 Christos Stavrakakis
    Raises EmptyPool
298 7fede91e Christos Stavrakakis

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

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

492 2a599282 Christos Stavrakakis
    Provider for `ext` disk_template is encoded in the disk template
493 2a599282 Christos Stavrakakis
    name, which is formed `ext_<provider_name>`. Provider is None
494 2a599282 Christos Stavrakakis
    for all other disk templates.
495 2a599282 Christos Stavrakakis

496 2a599282 Christos Stavrakakis
    """
497 2a599282 Christos Stavrakakis
    disk_template = flavor.disk_template
498 2a599282 Christos Stavrakakis
    provider = None
499 2a599282 Christos Stavrakakis
    if disk_template.startswith("ext"):
500 2a599282 Christos Stavrakakis
        disk_template, provider = disk_template.split("_", 1)
501 2a599282 Christos Stavrakakis
    return disk_template, provider
502 b7d38981 Dimitris Aragiorgis
503 b7d38981 Dimitris Aragiorgis
def values_from_flavor(flavor):
504 b7d38981 Dimitris Aragiorgis
    """Get Ganeti connectivity info from flavor type.
505 b7d38981 Dimitris Aragiorgis

506 b7d38981 Dimitris Aragiorgis
    If link or mac_prefix equals to "pool", then the resources
507 b7d38981 Dimitris Aragiorgis
    are allocated from the corresponding Pools.
508 b7d38981 Dimitris Aragiorgis

509 b7d38981 Dimitris Aragiorgis
    """
510 b7d38981 Dimitris Aragiorgis
    try:
511 b7d38981 Dimitris Aragiorgis
        flavor = Network.FLAVORS[flavor]
512 b7d38981 Dimitris Aragiorgis
    except KeyError:
513 b7d38981 Dimitris Aragiorgis
        raise BadRequest("Unknown network flavor")
514 b7d38981 Dimitris Aragiorgis
515 b7d38981 Dimitris Aragiorgis
    mode = flavor.get("mode")
516 b7d38981 Dimitris Aragiorgis
517 b7d38981 Dimitris Aragiorgis
    link = flavor.get("link")
518 b7d38981 Dimitris Aragiorgis
    if link == "pool":
519 b7d38981 Dimitris Aragiorgis
        link = allocate_resource("bridge")
520 b7d38981 Dimitris Aragiorgis
521 b7d38981 Dimitris Aragiorgis
    mac_prefix = flavor.get("mac_prefix")
522 b7d38981 Dimitris Aragiorgis
    if mac_prefix == "pool":
523 b7d38981 Dimitris Aragiorgis
        mac_prefix = allocate_resource("mac_prefix")
524 b7d38981 Dimitris Aragiorgis
525 b7d38981 Dimitris Aragiorgis
    tags = flavor.get("tags")
526 b7d38981 Dimitris Aragiorgis
527 b7d38981 Dimitris Aragiorgis
    return mode, link, mac_prefix, tags
528 b7d38981 Dimitris Aragiorgis
529 b7d38981 Dimitris Aragiorgis
530 b7d38981 Dimitris Aragiorgis
def allocate_resource(res_type):
531 b7d38981 Dimitris Aragiorgis
    table = get_pool_table(res_type)
532 b7d38981 Dimitris Aragiorgis
    pool = table.get_pool()
533 b7d38981 Dimitris Aragiorgis
    value = pool.get()
534 b7d38981 Dimitris Aragiorgis
    pool.save()
535 b7d38981 Dimitris Aragiorgis
    return value
536 b7d38981 Dimitris Aragiorgis
537 b7d38981 Dimitris Aragiorgis
538 b7d38981 Dimitris Aragiorgis
def release_resource(res_type, value):
539 b7d38981 Dimitris Aragiorgis
    table = get_pool_table(res_type)
540 b7d38981 Dimitris Aragiorgis
    pool = table.get_pool()
541 b7d38981 Dimitris Aragiorgis
    pool.put(value)
542 b7d38981 Dimitris Aragiorgis
    pool.save()
543 b7d38981 Dimitris Aragiorgis
544 b7d38981 Dimitris Aragiorgis
545 b7d38981 Dimitris Aragiorgis
def get_pool_table(res_type):
546 b7d38981 Dimitris Aragiorgis
    if res_type == "bridge":
547 b7d38981 Dimitris Aragiorgis
        return BridgePoolTable
548 b7d38981 Dimitris Aragiorgis
    elif res_type == "mac_prefix":
549 b7d38981 Dimitris Aragiorgis
        return MacPrefixPoolTable
550 b7d38981 Dimitris Aragiorgis
    else:
551 b7d38981 Dimitris Aragiorgis
        raise Exception("Unknown resource type")
552 55f562a4 Kostas Papadimitriou
553 55f562a4 Kostas Papadimitriou
554 55f562a4 Kostas Papadimitriou
def get_existing_users():
555 55f562a4 Kostas Papadimitriou
    """
556 55f562a4 Kostas Papadimitriou
    Retrieve user ids stored in cyclades user agnostic models.
557 55f562a4 Kostas Papadimitriou
    """
558 55f562a4 Kostas Papadimitriou
    # also check PublicKeys a user with no servers/networks exist
559 55f562a4 Kostas Papadimitriou
    from synnefo.ui.userdata.models import PublicKeyPair
560 55f562a4 Kostas Papadimitriou
    from synnefo.db.models import VirtualMachine, Network
561 55f562a4 Kostas Papadimitriou
562 55f562a4 Kostas Papadimitriou
    keypairusernames = PublicKeyPair.objects.filter().values_list('user',
563 55f562a4 Kostas Papadimitriou
                                                               flat=True)
564 55f562a4 Kostas Papadimitriou
    serverusernames = VirtualMachine.objects.filter().values_list('userid',
565 55f562a4 Kostas Papadimitriou
                                                                   flat=True)
566 55f562a4 Kostas Papadimitriou
    networkusernames = Network.objects.filter().values_list('userid',
567 55f562a4 Kostas Papadimitriou
                                                            flat=True)
568 55f562a4 Kostas Papadimitriou
569 55f562a4 Kostas Papadimitriou
    return set(list(keypairusernames) + list(serverusernames) + \
570 55f562a4 Kostas Papadimitriou
           list(networkusernames))