Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (18.7 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 9ae613af Christos Stavrakakis
import ipaddr
36 c738c935 Giorgos Verigakis
37 dca7553e Christos Stavrakakis
from base64 import b64encode, b64decode
38 d8e50a39 Giorgos Verigakis
from datetime import timedelta, tzinfo
39 d8e50a39 Giorgos Verigakis
from functools import wraps
40 c738c935 Giorgos Verigakis
from hashlib import sha256
41 2035039b Giorgos Verigakis
from logging import getLogger
42 d8e50a39 Giorgos Verigakis
from random import choice
43 dca2a31f Giorgos Verigakis
from string import digits, lowercase, uppercase
44 8b01f7f3 Giorgos Verigakis
from time import time
45 d8e50a39 Giorgos Verigakis
from traceback import format_exc
46 8b01f7f3 Giorgos Verigakis
from wsgiref.handlers import format_date_time
47 7e2f9d4b Giorgos Verigakis
48 2035039b Giorgos Verigakis
import dateutil.parser
49 2035039b Giorgos Verigakis
50 c738c935 Giorgos Verigakis
from Crypto.Cipher import AES
51 529178b1 Giorgos Verigakis
52 d8e50a39 Giorgos Verigakis
from django.conf import settings
53 7e2f9d4b Giorgos Verigakis
from django.http import HttpResponse
54 7e2f9d4b Giorgos Verigakis
from django.template.loader import render_to_string
55 29a59bc1 Giorgos Verigakis
from django.utils import simplejson as json
56 b75555e5 Giorgos Verigakis
from django.utils.cache import add_never_cache_headers
57 d3406fbc Christos Stavrakakis
from django.db.models import Q
58 7e2f9d4b Giorgos Verigakis
59 bd40abfa Christos Stavrakakis
from snf_django.lib.api import faults
60 6ef51e9f Giorgos Verigakis
from synnefo.db.models import (Flavor, VirtualMachine, VirtualMachineMetadata,
61 7fede91e Christos Stavrakakis
                               Network, BackendNetwork, NetworkInterface,
62 af6a3bc5 Christos Stavrakakis
                               BridgePoolTable, MacPrefixPoolTable)
63 adc46059 Christos Stavrakakis
from synnefo.db.pools import EmptyPool
64 6dd70a5c Christos Stavrakakis
65 4b3b8688 Giorgos Verigakis
from synnefo.lib.astakos import get_user
66 2db7d9df Christos Stavrakakis
from synnefo.plankton.backend import ImageBackend, NotAllowedError
67 b14725eb Christos Stavrakakis
from synnefo.settings import MAX_CIDR_BLOCK
68 9e98ba3c Giorgos Verigakis
69 9e98ba3c Giorgos Verigakis
70 9e98ba3c Giorgos Verigakis
log = getLogger('synnefo.api')
71 9e98ba3c Giorgos Verigakis
72 7e2f9d4b Giorgos Verigakis
73 d8e50a39 Giorgos Verigakis
class UTC(tzinfo):
74 d8e50a39 Giorgos Verigakis
    def utcoffset(self, dt):
75 d8e50a39 Giorgos Verigakis
        return timedelta(0)
76 aa197ee4 Vangelis Koukis
77 d8e50a39 Giorgos Verigakis
    def tzname(self, dt):
78 d8e50a39 Giorgos Verigakis
        return 'UTC'
79 aa197ee4 Vangelis Koukis
80 d8e50a39 Giorgos Verigakis
    def dst(self, dt):
81 d8e50a39 Giorgos Verigakis
        return timedelta(0)
82 7e2f9d4b Giorgos Verigakis
83 7e2f9d4b Giorgos Verigakis
84 d8e50a39 Giorgos Verigakis
def isoformat(d):
85 529178b1 Giorgos Verigakis
    """Return an ISO8601 date string that includes a timezone."""
86 aa197ee4 Vangelis Koukis
87 d8e50a39 Giorgos Verigakis
    return d.replace(tzinfo=UTC()).isoformat()
88 d8e50a39 Giorgos Verigakis
89 a08e4270 Vangelis Koukis
90 d8e50a39 Giorgos Verigakis
def isoparse(s):
91 d8e50a39 Giorgos Verigakis
    """Parse an ISO8601 date string into a datetime object."""
92 aa197ee4 Vangelis Koukis
93 d8e50a39 Giorgos Verigakis
    if not s:
94 d8e50a39 Giorgos Verigakis
        return None
95 aa197ee4 Vangelis Koukis
96 d8e50a39 Giorgos Verigakis
    try:
97 d8e50a39 Giorgos Verigakis
        since = dateutil.parser.parse(s)
98 e87d30f3 Giorgos Verigakis
        utc_since = since.astimezone(UTC()).replace(tzinfo=None)
99 d8e50a39 Giorgos Verigakis
    except ValueError:
100 bd40abfa Christos Stavrakakis
        raise faults.BadRequest('Invalid changes-since parameter.')
101 aa197ee4 Vangelis Koukis
102 e87d30f3 Giorgos Verigakis
    now = datetime.datetime.now()
103 e87d30f3 Giorgos Verigakis
    if utc_since > now:
104 bd40abfa Christos Stavrakakis
        raise faults.BadRequest('changes-since value set in the future.')
105 aa197ee4 Vangelis Koukis
106 e87d30f3 Giorgos Verigakis
    if now - utc_since > timedelta(seconds=settings.POLL_LIMIT):
107 bd40abfa Christos Stavrakakis
        raise faults.BadRequest('Too old changes-since value.')
108 aa197ee4 Vangelis Koukis
109 0140e54b Vangelis Koukis
    return utc_since
110 aa197ee4 Vangelis Koukis
111 dca2a31f Giorgos Verigakis
112 dca2a31f Giorgos Verigakis
def random_password():
113 dca2a31f Giorgos Verigakis
    """Generates a random password
114 ce55f211 Kostas Papadimitriou

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

297 dca7553e Christos Stavrakakis
    This method should run inside a transaction.
298 dca7553e Christos Stavrakakis

299 dca7553e Christos Stavrakakis
    """
300 dca7553e Christos Stavrakakis
    address = None
301 74459c12 Christos Stavrakakis
    if settings.PUBLIC_USE_POOL:
302 dca7553e Christos Stavrakakis
        (network, address) = allocate_public_address(backend)
303 dca7553e Christos Stavrakakis
    else:
304 dca7553e Christos Stavrakakis
        for net in list(backend_public_networks(backend)):
305 dca7553e Christos Stavrakakis
            pool = net.get_pool()
306 dca7553e Christos Stavrakakis
            if not pool.empty():
307 dca7553e Christos Stavrakakis
                address = 'pool'
308 dca7553e Christos Stavrakakis
                network = net
309 dca7553e Christos Stavrakakis
                break
310 dca7553e Christos Stavrakakis
    if address is None:
311 dca7553e Christos Stavrakakis
        log.error("Public networks of backend %s are full", backend)
312 bd40abfa Christos Stavrakakis
        raise faults.OverLimit("Can not allocate IP for new machine."
313 dca7553e Christos Stavrakakis
                        " Public networks are full.")
314 dca7553e Christos Stavrakakis
    return (network, address)
315 dca7553e Christos Stavrakakis
316 dca7553e Christos Stavrakakis
317 7fede91e Christos Stavrakakis
def backend_public_networks(backend):
318 7fede91e Christos Stavrakakis
    """Return available public networks of the backend.
319 7fede91e Christos Stavrakakis

320 7fede91e Christos Stavrakakis
    Iterator for non-deleted public networks that are available
321 7fede91e Christos Stavrakakis
    to the specified backend.
322 7fede91e Christos Stavrakakis

323 7fede91e Christos Stavrakakis
    """
324 7fede91e Christos Stavrakakis
    for network in Network.objects.filter(public=True, deleted=False):
325 7fede91e Christos Stavrakakis
        if BackendNetwork.objects.filter(network=network,
326 7fede91e Christos Stavrakakis
                                         backend=backend).exists():
327 7fede91e Christos Stavrakakis
            yield network
328 7fede91e Christos Stavrakakis
329 7fede91e Christos Stavrakakis
330 7fede91e Christos Stavrakakis
def get_network_free_address(network):
331 7fede91e Christos Stavrakakis
    """Reserve an IP address from the IP Pool of the network.
332 7fede91e Christos Stavrakakis

333 fdc94944 Christos Stavrakakis
    Raises EmptyPool
334 7fede91e Christos Stavrakakis

335 7fede91e Christos Stavrakakis
    """
336 7fede91e Christos Stavrakakis
337 fdc94944 Christos Stavrakakis
    pool = network.get_pool()
338 fdc94944 Christos Stavrakakis
    address = pool.get()
339 7fede91e Christos Stavrakakis
    pool.save()
340 7fede91e Christos Stavrakakis
    return address
341 7fede91e Christos Stavrakakis
342 7fede91e Christos Stavrakakis
343 d44c236b Giorgos Verigakis
def get_nic(machine, network):
344 d44c236b Giorgos Verigakis
    try:
345 d44c236b Giorgos Verigakis
        return NetworkInterface.objects.get(machine=machine, network=network)
346 d44c236b Giorgos Verigakis
    except NetworkInterface.DoesNotExist:
347 bd40abfa Christos Stavrakakis
        raise faults.ItemNotFound('Server not connected to this network.')
348 d44c236b Giorgos Verigakis
349 7fede91e Christos Stavrakakis
350 08b079e2 Stavros Sachtouris
def get_nic_from_index(vm, nic_index):
351 08b079e2 Stavros Sachtouris
    """Returns the nic_index-th nic of a vm
352 08b079e2 Stavros Sachtouris
       Error Response Codes: itemNotFound (404), badMediaType (415)
353 08b079e2 Stavros Sachtouris
    """
354 08b079e2 Stavros Sachtouris
    matching_nics = vm.nics.filter(index=nic_index)
355 08b079e2 Stavros Sachtouris
    matching_nics_len = len(matching_nics)
356 08b079e2 Stavros Sachtouris
    if matching_nics_len < 1:
357 bd40abfa Christos Stavrakakis
        raise faults.ItemNotFound('NIC not found on VM')
358 08b079e2 Stavros Sachtouris
    elif matching_nics_len > 1:
359 bd40abfa Christos Stavrakakis
        raise faults.BadMediaType('NIC index conflict on VM')
360 08b079e2 Stavros Sachtouris
    nic = matching_nics[0]
361 08b079e2 Stavros Sachtouris
    return nic
362 e2ee7808 Giorgos Verigakis
363 7fede91e Christos Stavrakakis
364 7e2f9d4b Giorgos Verigakis
def get_request_dict(request):
365 d8e50a39 Giorgos Verigakis
    """Returns data sent by the client as a python dict."""
366 aa197ee4 Vangelis Koukis
367 7e2f9d4b Giorgos Verigakis
    data = request.raw_post_data
368 7e45ddef Dimitris Moraitis
    if request.META.get('CONTENT_TYPE').startswith('application/json'):
369 7e2f9d4b Giorgos Verigakis
        try:
370 7e2f9d4b Giorgos Verigakis
            return json.loads(data)
371 7e2f9d4b Giorgos Verigakis
        except ValueError:
372 bd40abfa Christos Stavrakakis
            raise faults.BadRequest('Invalid JSON data.')
373 d8e50a39 Giorgos Verigakis
    else:
374 bd40abfa Christos Stavrakakis
        raise faults.BadRequest('Unsupported Content-Type.')
375 7e2f9d4b Giorgos Verigakis
376 a08e4270 Vangelis Koukis
377 8b01f7f3 Giorgos Verigakis
def update_response_headers(request, response):
378 8b01f7f3 Giorgos Verigakis
    if request.serialization == 'xml':
379 8b01f7f3 Giorgos Verigakis
        response['Content-Type'] = 'application/xml'
380 8b01f7f3 Giorgos Verigakis
    elif request.serialization == 'atom':
381 8b01f7f3 Giorgos Verigakis
        response['Content-Type'] = 'application/atom+xml'
382 8b01f7f3 Giorgos Verigakis
    else:
383 8b01f7f3 Giorgos Verigakis
        response['Content-Type'] = 'application/json'
384 aa197ee4 Vangelis Koukis
385 4cf8adf8 Vangelis Koukis
    if settings.TEST:
386 cdb65551 Giorgos Verigakis
        response['Date'] = format_date_time(time())
387 ce55f211 Kostas Papadimitriou
388 b75555e5 Giorgos Verigakis
    add_never_cache_headers(response)
389 b75555e5 Giorgos Verigakis
390 8b01f7f3 Giorgos Verigakis
391 432fc8c3 Giorgos Verigakis
def render_metadata(request, metadata, use_values=False, status=200):
392 432fc8c3 Giorgos Verigakis
    if request.serialization == 'xml':
393 432fc8c3 Giorgos Verigakis
        data = render_to_string('metadata.xml', {'metadata': metadata})
394 432fc8c3 Giorgos Verigakis
    else:
395 b36f78fa Giorgos Verigakis
        if use_values:
396 b36f78fa Giorgos Verigakis
            d = {'metadata': {'values': metadata}}
397 b36f78fa Giorgos Verigakis
        else:
398 b36f78fa Giorgos Verigakis
            d = {'metadata': metadata}
399 432fc8c3 Giorgos Verigakis
        data = json.dumps(d)
400 432fc8c3 Giorgos Verigakis
    return HttpResponse(data, status=status)
401 432fc8c3 Giorgos Verigakis
402 a08e4270 Vangelis Koukis
403 432fc8c3 Giorgos Verigakis
def render_meta(request, meta, status=200):
404 432fc8c3 Giorgos Verigakis
    if request.serialization == 'xml':
405 6ef51e9f Giorgos Verigakis
        data = render_to_string('meta.xml', dict(key=key, val=val))
406 432fc8c3 Giorgos Verigakis
    else:
407 6ef51e9f Giorgos Verigakis
        data = json.dumps(dict(meta=meta))
408 432fc8c3 Giorgos Verigakis
    return HttpResponse(data, status=status)
409 432fc8c3 Giorgos Verigakis
410 a08e4270 Vangelis Koukis
411 c36934a7 Giorgos Verigakis
def render_fault(request, fault):
412 4cf8adf8 Vangelis Koukis
    if settings.DEBUG or settings.TEST:
413 7e2f9d4b Giorgos Verigakis
        fault.details = format_exc(fault)
414 aa197ee4 Vangelis Koukis
415 d8e50a39 Giorgos Verigakis
    if request.serialization == 'xml':
416 d8e50a39 Giorgos Verigakis
        data = render_to_string('fault.xml', {'fault': fault})
417 7e2f9d4b Giorgos Verigakis
    else:
418 e440e835 Christos Stavrakakis
        d = {fault.name: {'code': fault.code,
419 e440e835 Christos Stavrakakis
                          'message': fault.message,
420 e440e835 Christos Stavrakakis
                          'details': fault.details}}
421 7e2f9d4b Giorgos Verigakis
        data = json.dumps(d)
422 aa197ee4 Vangelis Koukis
423 f0656db1 Giorgos Verigakis
    resp = HttpResponse(data, status=fault.code)
424 8b01f7f3 Giorgos Verigakis
    update_response_headers(request, resp)
425 f0656db1 Giorgos Verigakis
    return resp
426 d8e50a39 Giorgos Verigakis
427 432fc8c3 Giorgos Verigakis
428 d8e50a39 Giorgos Verigakis
def request_serialization(request, atom_allowed=False):
429 d8e50a39 Giorgos Verigakis
    """Return the serialization format requested.
430 aa197ee4 Vangelis Koukis

431 529178b1 Giorgos Verigakis
    Valid formats are 'json', 'xml' and 'atom' if `atom_allowed` is True.
432 d8e50a39 Giorgos Verigakis
    """
433 aa197ee4 Vangelis Koukis
434 d8e50a39 Giorgos Verigakis
    path = request.path
435 aa197ee4 Vangelis Koukis
436 d8e50a39 Giorgos Verigakis
    if path.endswith('.json'):
437 d8e50a39 Giorgos Verigakis
        return 'json'
438 d8e50a39 Giorgos Verigakis
    elif path.endswith('.xml'):
439 d8e50a39 Giorgos Verigakis
        return 'xml'
440 d8e50a39 Giorgos Verigakis
    elif atom_allowed and path.endswith('.atom'):
441 d8e50a39 Giorgos Verigakis
        return 'atom'
442 aa197ee4 Vangelis Koukis
443 d8e50a39 Giorgos Verigakis
    for item in request.META.get('HTTP_ACCEPT', '').split(','):
444 d8e50a39 Giorgos Verigakis
        accept, sep, rest = item.strip().partition(';')
445 d8e50a39 Giorgos Verigakis
        if accept == 'application/json':
446 d8e50a39 Giorgos Verigakis
            return 'json'
447 d8e50a39 Giorgos Verigakis
        elif accept == 'application/xml':
448 d8e50a39 Giorgos Verigakis
            return 'xml'
449 d8e50a39 Giorgos Verigakis
        elif atom_allowed and accept == 'application/atom+xml':
450 d8e50a39 Giorgos Verigakis
            return 'atom'
451 aa197ee4 Vangelis Koukis
452 d8e50a39 Giorgos Verigakis
    return 'json'
453 7e2f9d4b Giorgos Verigakis
454 a08e4270 Vangelis Koukis
455 d8e50a39 Giorgos Verigakis
def api_method(http_method=None, atom_allowed=False):
456 d8e50a39 Giorgos Verigakis
    """Decorator function for views that implement an API method."""
457 aa197ee4 Vangelis Koukis
458 c36934a7 Giorgos Verigakis
    def decorator(func):
459 c36934a7 Giorgos Verigakis
        @wraps(func)
460 c36934a7 Giorgos Verigakis
        def wrapper(request, *args, **kwargs):
461 c36934a7 Giorgos Verigakis
            try:
462 6ef51e9f Giorgos Verigakis
                request.serialization = request_serialization(request,
463 6ef51e9f Giorgos Verigakis
                                                              atom_allowed)
464 4b3b8688 Giorgos Verigakis
                get_user(request, settings.ASTAKOS_URL)
465 4b3b8688 Giorgos Verigakis
                if not request.user_uniq:
466 bd40abfa Christos Stavrakakis
                    raise faults.Unauthorized('No user found.')
467 d8e50a39 Giorgos Verigakis
                if http_method and request.method != http_method:
468 bd40abfa Christos Stavrakakis
                    raise faults.BadRequest('Method not allowed.')
469 7fede91e Christos Stavrakakis
470 c36934a7 Giorgos Verigakis
                resp = func(request, *args, **kwargs)
471 8b01f7f3 Giorgos Verigakis
                update_response_headers(request, resp)
472 c36934a7 Giorgos Verigakis
                return resp
473 bd40abfa Christos Stavrakakis
            except faults.Fault, fault:
474 260147b1 Christos Stavrakakis
                if fault.code >= 500:
475 260147b1 Christos Stavrakakis
                    log.exception('API fault')
476 c36934a7 Giorgos Verigakis
                return render_fault(request, fault)
477 7fede91e Christos Stavrakakis
            except BaseException:
478 9e98ba3c Giorgos Verigakis
                log.exception('Unexpected error')
479 bd40abfa Christos Stavrakakis
                fault = faults.ServiceUnavailable('Unexpected error.')
480 c36934a7 Giorgos Verigakis
                return render_fault(request, fault)
481 c36934a7 Giorgos Verigakis
        return wrapper
482 c36934a7 Giorgos Verigakis
    return decorator
483 08b079e2 Stavros Sachtouris
484 3165f027 Christos Stavrakakis
485 08b079e2 Stavros Sachtouris
def construct_nic_id(nic):
486 08b079e2 Stavros Sachtouris
    return "-".join(["nic", unicode(nic.machine.id), unicode(nic.index)])
487 6dd70a5c Christos Stavrakakis
488 6dd70a5c Christos Stavrakakis
489 dca7553e Christos Stavrakakis
def verify_personality(personality):
490 6ec4694f Christos Stavrakakis
    """Verify that a a list of personalities is well formed"""
491 006c6249 Christos Stavrakakis
    if len(personality) > settings.MAX_PERSONALITY:
492 bd40abfa Christos Stavrakakis
        raise faults.OverLimit("Maximum number of personalities"
493 e440e835 Christos Stavrakakis
                        " exceeded")
494 dca7553e Christos Stavrakakis
    for p in personality:
495 dca7553e Christos Stavrakakis
        # Verify that personalities are well-formed
496 dca7553e Christos Stavrakakis
        try:
497 dca7553e Christos Stavrakakis
            assert isinstance(p, dict)
498 dca7553e Christos Stavrakakis
            keys = set(p.keys())
499 dca7553e Christos Stavrakakis
            allowed = set(['contents', 'group', 'mode', 'owner', 'path'])
500 dca7553e Christos Stavrakakis
            assert keys.issubset(allowed)
501 dca7553e Christos Stavrakakis
            contents = p['contents']
502 dca7553e Christos Stavrakakis
            if len(contents) > settings.MAX_PERSONALITY_SIZE:
503 dca7553e Christos Stavrakakis
                # No need to decode if contents already exceed limit
504 bd40abfa Christos Stavrakakis
                raise faults.OverLimit("Maximum size of personality exceeded")
505 dca7553e Christos Stavrakakis
            if len(b64decode(contents)) > settings.MAX_PERSONALITY_SIZE:
506 bd40abfa Christos Stavrakakis
                raise faults.OverLimit("Maximum size of personality exceeded")
507 dca7553e Christos Stavrakakis
        except AssertionError:
508 bd40abfa Christos Stavrakakis
            raise faults.BadRequest("Malformed personality in request")
509 2a599282 Christos Stavrakakis
510 2a599282 Christos Stavrakakis
511 2a599282 Christos Stavrakakis
def get_flavor_provider(flavor):
512 2a599282 Christos Stavrakakis
    """Extract provider from disk template.
513 2a599282 Christos Stavrakakis

514 2a599282 Christos Stavrakakis
    Provider for `ext` disk_template is encoded in the disk template
515 2a599282 Christos Stavrakakis
    name, which is formed `ext_<provider_name>`. Provider is None
516 2a599282 Christos Stavrakakis
    for all other disk templates.
517 2a599282 Christos Stavrakakis

518 2a599282 Christos Stavrakakis
    """
519 2a599282 Christos Stavrakakis
    disk_template = flavor.disk_template
520 2a599282 Christos Stavrakakis
    provider = None
521 2a599282 Christos Stavrakakis
    if disk_template.startswith("ext"):
522 2a599282 Christos Stavrakakis
        disk_template, provider = disk_template.split("_", 1)
523 2a599282 Christos Stavrakakis
    return disk_template, provider
524 b7d38981 Dimitris Aragiorgis
525 e440e835 Christos Stavrakakis
526 b7d38981 Dimitris Aragiorgis
def values_from_flavor(flavor):
527 b7d38981 Dimitris Aragiorgis
    """Get Ganeti connectivity info from flavor type.
528 b7d38981 Dimitris Aragiorgis

529 b7d38981 Dimitris Aragiorgis
    If link or mac_prefix equals to "pool", then the resources
530 b7d38981 Dimitris Aragiorgis
    are allocated from the corresponding Pools.
531 b7d38981 Dimitris Aragiorgis

532 b7d38981 Dimitris Aragiorgis
    """
533 b7d38981 Dimitris Aragiorgis
    try:
534 b7d38981 Dimitris Aragiorgis
        flavor = Network.FLAVORS[flavor]
535 b7d38981 Dimitris Aragiorgis
    except KeyError:
536 bd40abfa Christos Stavrakakis
        raise faults.BadRequest("Unknown network flavor")
537 b7d38981 Dimitris Aragiorgis
538 b7d38981 Dimitris Aragiorgis
    mode = flavor.get("mode")
539 b7d38981 Dimitris Aragiorgis
540 b7d38981 Dimitris Aragiorgis
    link = flavor.get("link")
541 b7d38981 Dimitris Aragiorgis
    if link == "pool":
542 b7d38981 Dimitris Aragiorgis
        link = allocate_resource("bridge")
543 b7d38981 Dimitris Aragiorgis
544 b7d38981 Dimitris Aragiorgis
    mac_prefix = flavor.get("mac_prefix")
545 b7d38981 Dimitris Aragiorgis
    if mac_prefix == "pool":
546 b7d38981 Dimitris Aragiorgis
        mac_prefix = allocate_resource("mac_prefix")
547 b7d38981 Dimitris Aragiorgis
548 b7d38981 Dimitris Aragiorgis
    tags = flavor.get("tags")
549 b7d38981 Dimitris Aragiorgis
550 b7d38981 Dimitris Aragiorgis
    return mode, link, mac_prefix, tags
551 b7d38981 Dimitris Aragiorgis
552 b7d38981 Dimitris Aragiorgis
553 b7d38981 Dimitris Aragiorgis
def allocate_resource(res_type):
554 b7d38981 Dimitris Aragiorgis
    table = get_pool_table(res_type)
555 b7d38981 Dimitris Aragiorgis
    pool = table.get_pool()
556 b7d38981 Dimitris Aragiorgis
    value = pool.get()
557 b7d38981 Dimitris Aragiorgis
    pool.save()
558 b7d38981 Dimitris Aragiorgis
    return value
559 b7d38981 Dimitris Aragiorgis
560 b7d38981 Dimitris Aragiorgis
561 b7d38981 Dimitris Aragiorgis
def release_resource(res_type, value):
562 b7d38981 Dimitris Aragiorgis
    table = get_pool_table(res_type)
563 b7d38981 Dimitris Aragiorgis
    pool = table.get_pool()
564 b7d38981 Dimitris Aragiorgis
    pool.put(value)
565 b7d38981 Dimitris Aragiorgis
    pool.save()
566 b7d38981 Dimitris Aragiorgis
567 b7d38981 Dimitris Aragiorgis
568 b7d38981 Dimitris Aragiorgis
def get_pool_table(res_type):
569 b7d38981 Dimitris Aragiorgis
    if res_type == "bridge":
570 b7d38981 Dimitris Aragiorgis
        return BridgePoolTable
571 b7d38981 Dimitris Aragiorgis
    elif res_type == "mac_prefix":
572 b7d38981 Dimitris Aragiorgis
        return MacPrefixPoolTable
573 b7d38981 Dimitris Aragiorgis
    else:
574 b7d38981 Dimitris Aragiorgis
        raise Exception("Unknown resource type")
575 55f562a4 Kostas Papadimitriou
576 55f562a4 Kostas Papadimitriou
577 55f562a4 Kostas Papadimitriou
def get_existing_users():
578 55f562a4 Kostas Papadimitriou
    """
579 55f562a4 Kostas Papadimitriou
    Retrieve user ids stored in cyclades user agnostic models.
580 55f562a4 Kostas Papadimitriou
    """
581 55f562a4 Kostas Papadimitriou
    # also check PublicKeys a user with no servers/networks exist
582 55f562a4 Kostas Papadimitriou
    from synnefo.ui.userdata.models import PublicKeyPair
583 55f562a4 Kostas Papadimitriou
    from synnefo.db.models import VirtualMachine, Network
584 55f562a4 Kostas Papadimitriou
585 55f562a4 Kostas Papadimitriou
    keypairusernames = PublicKeyPair.objects.filter().values_list('user',
586 e440e835 Christos Stavrakakis
                                                                  flat=True)
587 55f562a4 Kostas Papadimitriou
    serverusernames = VirtualMachine.objects.filter().values_list('userid',
588 e440e835 Christos Stavrakakis
                                                                  flat=True)
589 55f562a4 Kostas Papadimitriou
    networkusernames = Network.objects.filter().values_list('userid',
590 55f562a4 Kostas Papadimitriou
                                                            flat=True)
591 55f562a4 Kostas Papadimitriou
592 e440e835 Christos Stavrakakis
    return set(list(keypairusernames) + list(serverusernames) +
593 e440e835 Christos Stavrakakis
               list(networkusernames))