Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (17.5 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 f8675683 Christos Stavrakakis
                               BridgePoolTable, MacPrefixPoolTable, Backend,
63 f8675683 Christos Stavrakakis
                               FloatingIP)
64 adc46059 Christos Stavrakakis
from synnefo.db.pools import EmptyPool
65 6dd70a5c Christos Stavrakakis
66 04a1b675 Christos Stavrakakis
from snf_django.lib.astakos import get_user
67 b3fd98ae Christos Stavrakakis
from synnefo.plankton.utils import image_backend
68 b14725eb Christos Stavrakakis
from synnefo.settings import MAX_CIDR_BLOCK
69 9e98ba3c Giorgos Verigakis
70 1b696c26 Christos Stavrakakis
from synnefo.cyclades_settings import cyclades_services, BASE_HOST
71 1b696c26 Christos Stavrakakis
from synnefo.lib.services import get_service_path
72 1b696c26 Christos Stavrakakis
from synnefo.lib import join_urls
73 1b696c26 Christos Stavrakakis
74 1b696c26 Christos Stavrakakis
COMPUTE_URL = \
75 1b696c26 Christos Stavrakakis
    join_urls(BASE_HOST,
76 1b696c26 Christos Stavrakakis
              get_service_path(cyclades_services, "compute", version="v2.0"))
77 1b696c26 Christos Stavrakakis
SERVERS_URL = join_urls(COMPUTE_URL, "servers/")
78 1b696c26 Christos Stavrakakis
NETWORKS_URL = join_urls(COMPUTE_URL, "networks/")
79 1b696c26 Christos Stavrakakis
FLAVORS_URL = join_urls(COMPUTE_URL, "flavors/")
80 1b696c26 Christos Stavrakakis
IMAGES_URL = join_urls(COMPUTE_URL, "images/")
81 1b696c26 Christos Stavrakakis
PLANKTON_URL = \
82 1b696c26 Christos Stavrakakis
    join_urls(BASE_HOST,
83 1b696c26 Christos Stavrakakis
              get_service_path(cyclades_services, "image", version="v1.0"))
84 1b696c26 Christos Stavrakakis
IMAGES_PLANKTON_URL = join_urls(PLANKTON_URL, "images/")
85 1b696c26 Christos Stavrakakis
86 9e98ba3c Giorgos Verigakis
87 9e98ba3c Giorgos Verigakis
log = getLogger('synnefo.api')
88 9e98ba3c Giorgos Verigakis
89 7e2f9d4b Giorgos Verigakis
90 dca2a31f Giorgos Verigakis
def random_password():
91 dca2a31f Giorgos Verigakis
    """Generates a random password
92 ce55f211 Kostas Papadimitriou

93 62eac5a6 Giorgos Verigakis
    We generate a windows compliant password: it must contain at least
94 dca2a31f Giorgos Verigakis
    one charachter from each of the groups: upper case, lower case, digits.
95 dca2a31f Giorgos Verigakis
    """
96 ce55f211 Kostas Papadimitriou
97 dca2a31f Giorgos Verigakis
    pool = lowercase + uppercase + digits
98 dca2a31f Giorgos Verigakis
    lowerset = set(lowercase)
99 dca2a31f Giorgos Verigakis
    upperset = set(uppercase)
100 dca2a31f Giorgos Verigakis
    digitset = set(digits)
101 dca2a31f Giorgos Verigakis
    length = 10
102 ce55f211 Kostas Papadimitriou
103 62eac5a6 Giorgos Verigakis
    password = ''.join(choice(pool) for i in range(length - 2))
104 ce55f211 Kostas Papadimitriou
105 62eac5a6 Giorgos Verigakis
    # Make sure the password is compliant
106 62eac5a6 Giorgos Verigakis
    chars = set(password)
107 62eac5a6 Giorgos Verigakis
    if not chars & lowerset:
108 62eac5a6 Giorgos Verigakis
        password += choice(lowercase)
109 62eac5a6 Giorgos Verigakis
    if not chars & upperset:
110 62eac5a6 Giorgos Verigakis
        password += choice(uppercase)
111 62eac5a6 Giorgos Verigakis
    if not chars & digitset:
112 62eac5a6 Giorgos Verigakis
        password += choice(digits)
113 ce55f211 Kostas Papadimitriou
114 62eac5a6 Giorgos Verigakis
    # Pad if necessary to reach required length
115 62eac5a6 Giorgos Verigakis
    password += ''.join(choice(pool) for i in range(length - len(password)))
116 ce55f211 Kostas Papadimitriou
117 dca2a31f Giorgos Verigakis
    return password
118 dca2a31f Giorgos Verigakis
119 d8e50a39 Giorgos Verigakis
120 c738c935 Giorgos Verigakis
def zeropad(s):
121 c738c935 Giorgos Verigakis
    """Add zeros at the end of a string in order to make its length
122 c738c935 Giorgos Verigakis
       a multiple of 16."""
123 d1387ed7 Christodoulos Psaltis
124 c738c935 Giorgos Verigakis
    npad = 16 - len(s) % 16
125 c738c935 Giorgos Verigakis
    return s + '\x00' * npad
126 c738c935 Giorgos Verigakis
127 a08e4270 Vangelis Koukis
128 c738c935 Giorgos Verigakis
def encrypt(plaintext):
129 c738c935 Giorgos Verigakis
    # Make sure key is 32 bytes long
130 c738c935 Giorgos Verigakis
    key = sha256(settings.SECRET_KEY).digest()
131 d1387ed7 Christodoulos Psaltis
132 c738c935 Giorgos Verigakis
    aes = AES.new(key)
133 c738c935 Giorgos Verigakis
    enc = aes.encrypt(zeropad(plaintext))
134 c738c935 Giorgos Verigakis
    return b64encode(enc)
135 c738c935 Giorgos Verigakis
136 7e2f9d4b Giorgos Verigakis
137 7f2dbcad Christos Stavrakakis
def get_vm(server_id, user_id, for_update=False, non_deleted=False,
138 7f2dbcad Christos Stavrakakis
           non_suspended=False):
139 e221ade2 Christos Stavrakakis
    """Find a VirtualMachine instance based on ID and owner."""
140 aa197ee4 Vangelis Koukis
141 d8e50a39 Giorgos Verigakis
    try:
142 d8e50a39 Giorgos Verigakis
        server_id = int(server_id)
143 7f2dbcad Christos Stavrakakis
        servers = VirtualMachine.objects
144 7f2dbcad Christos Stavrakakis
        if for_update:
145 7f2dbcad Christos Stavrakakis
            servers = servers.select_for_update()
146 7f2dbcad Christos Stavrakakis
        vm = servers.get(id=server_id, userid=user_id)
147 e221ade2 Christos Stavrakakis
        if non_deleted and vm.deleted:
148 5b0832fc Christos Stavrakakis
            raise faults.BadRequest("Server has been deleted.")
149 e221ade2 Christos Stavrakakis
        if non_suspended and vm.suspended:
150 bd40abfa Christos Stavrakakis
            raise faults.Forbidden("Administratively Suspended VM")
151 3a522cff Christos Stavrakakis
        return vm
152 d8e50a39 Giorgos Verigakis
    except ValueError:
153 bd40abfa Christos Stavrakakis
        raise faults.BadRequest('Invalid server ID.')
154 d8e50a39 Giorgos Verigakis
    except VirtualMachine.DoesNotExist:
155 bd40abfa Christos Stavrakakis
        raise faults.ItemNotFound('Server not found.')
156 d8e50a39 Giorgos Verigakis
157 7f2dbcad Christos Stavrakakis
158 40777cc8 Giorgos Verigakis
def get_vm_meta(vm, key):
159 d8e50a39 Giorgos Verigakis
    """Return a VirtualMachineMetadata instance or raise ItemNotFound."""
160 aa197ee4 Vangelis Koukis
161 d8e50a39 Giorgos Verigakis
    try:
162 40777cc8 Giorgos Verigakis
        return VirtualMachineMetadata.objects.get(meta_key=key, vm=vm)
163 d8e50a39 Giorgos Verigakis
    except VirtualMachineMetadata.DoesNotExist:
164 bd40abfa Christos Stavrakakis
        raise faults.ItemNotFound('Metadata key not found.')
165 d8e50a39 Giorgos Verigakis
166 a08e4270 Vangelis Koukis
167 6ef51e9f Giorgos Verigakis
def get_image(image_id, user_id):
168 d8e50a39 Giorgos Verigakis
    """Return an Image instance or raise ItemNotFound."""
169 aa197ee4 Vangelis Koukis
170 b3fd98ae Christos Stavrakakis
    with image_backend(user_id) as backend:
171 cda71050 Christos Stavrakakis
        return backend.get_image(image_id)
172 3a9b3cde Giorgos Verigakis
173 a08e4270 Vangelis Koukis
174 dca7553e Christos Stavrakakis
def get_image_dict(image_id, user_id):
175 dca7553e Christos Stavrakakis
    image = {}
176 dca7553e Christos Stavrakakis
    img = get_image(image_id, user_id)
177 dca7553e Christos Stavrakakis
    properties = img.get('properties', {})
178 bcd80cd9 Christos Stavrakakis
    image["id"] = img["id"]
179 bcd80cd9 Christos Stavrakakis
    image["name"] = img["name"]
180 dca7553e Christos Stavrakakis
    image['backend_id'] = img['location']
181 dca7553e Christos Stavrakakis
    image['format'] = img['disk_format']
182 e440e835 Christos Stavrakakis
    image['metadata'] = dict((key.upper(), val)
183 dca7553e Christos Stavrakakis
                             for key, val in properties.items())
184 2cb812be Christos Stavrakakis
    image['checksum'] = img['checksum']
185 2cb812be Christos Stavrakakis
186 dca7553e Christos Stavrakakis
    return image
187 dca7553e Christos Stavrakakis
188 dca7553e Christos Stavrakakis
189 aa8230bd Christos Stavrakakis
def get_flavor(flavor_id, include_deleted=False):
190 529178b1 Giorgos Verigakis
    """Return a Flavor instance or raise ItemNotFound."""
191 aa197ee4 Vangelis Koukis
192 529178b1 Giorgos Verigakis
    try:
193 529178b1 Giorgos Verigakis
        flavor_id = int(flavor_id)
194 aa8230bd Christos Stavrakakis
        if include_deleted:
195 aa8230bd Christos Stavrakakis
            return Flavor.objects.get(id=flavor_id)
196 aa8230bd Christos Stavrakakis
        else:
197 aa8230bd Christos Stavrakakis
            return Flavor.objects.get(id=flavor_id, deleted=include_deleted)
198 6ef51e9f Giorgos Verigakis
    except (ValueError, Flavor.DoesNotExist):
199 bd40abfa Christos Stavrakakis
        raise faults.ItemNotFound('Flavor not found.')
200 d8e50a39 Giorgos Verigakis
201 a08e4270 Vangelis Koukis
202 9dcb5b8a Christos Stavrakakis
def get_flavor_provider(flavor):
203 9dcb5b8a Christos Stavrakakis
    """Extract provider from disk template.
204 9dcb5b8a Christos Stavrakakis

205 9dcb5b8a Christos Stavrakakis
    Provider for `ext` disk_template is encoded in the disk template
206 9dcb5b8a Christos Stavrakakis
    name, which is formed `ext_<provider_name>`. Provider is None
207 9dcb5b8a Christos Stavrakakis
    for all other disk templates.
208 9dcb5b8a Christos Stavrakakis

209 9dcb5b8a Christos Stavrakakis
    """
210 9dcb5b8a Christos Stavrakakis
    disk_template = flavor.disk_template
211 9dcb5b8a Christos Stavrakakis
    provider = None
212 9dcb5b8a Christos Stavrakakis
    if disk_template.startswith("ext"):
213 9dcb5b8a Christos Stavrakakis
        disk_template, provider = disk_template.split("_", 1)
214 9dcb5b8a Christos Stavrakakis
    return disk_template, provider
215 9dcb5b8a Christos Stavrakakis
216 9dcb5b8a Christos Stavrakakis
217 9e8be4fb Christos Stavrakakis
def get_network(network_id, user_id, for_update=False, non_deleted=False):
218 e2ee7808 Giorgos Verigakis
    """Return a Network instance or raise ItemNotFound."""
219 aa197ee4 Vangelis Koukis
220 e2ee7808 Giorgos Verigakis
    try:
221 7fede91e Christos Stavrakakis
        network_id = int(network_id)
222 d3406fbc Christos Stavrakakis
        objects = Network.objects
223 d2e73c0c Christos Stavrakakis
        if for_update:
224 d3406fbc Christos Stavrakakis
            objects = objects.select_for_update()
225 9e8be4fb Christos Stavrakakis
        network = objects.get(Q(userid=user_id) | Q(public=True),
226 9e8be4fb Christos Stavrakakis
                              id=network_id)
227 9e8be4fb Christos Stavrakakis
        if non_deleted and network.deleted:
228 f8675683 Christos Stavrakakis
            raise faults.BadRequest("Network has been deleted.")
229 9e8be4fb Christos Stavrakakis
        return network
230 6ef51e9f Giorgos Verigakis
    except (ValueError, Network.DoesNotExist):
231 bd40abfa Christos Stavrakakis
        raise faults.ItemNotFound('Network not found.')
232 e2ee7808 Giorgos Verigakis
233 a08e4270 Vangelis Koukis
234 f8675683 Christos Stavrakakis
def get_floating_ip(user_id, ipv4, for_update=False):
235 f8675683 Christos Stavrakakis
    try:
236 f8675683 Christos Stavrakakis
        objects = FloatingIP.objects
237 f8675683 Christos Stavrakakis
        if for_update:
238 f8675683 Christos Stavrakakis
            objects = objects.select_for_update()
239 f8675683 Christos Stavrakakis
        return objects.get(userid=user_id, ipv4=ipv4, deleted=False)
240 f8675683 Christos Stavrakakis
    except FloatingIP.DoesNotExist:
241 f8675683 Christos Stavrakakis
        raise faults.ItemNotFound("Floating IP does not exist.")
242 f8675683 Christos Stavrakakis
243 f8675683 Christos Stavrakakis
244 9ae613af Christos Stavrakakis
def validate_network_params(subnet, gateway=None, subnet6=None, gateway6=None):
245 d368ec0c Christos Stavrakakis
    try:
246 d368ec0c Christos Stavrakakis
        # Use strict option to not all subnets with host bits set
247 9ae613af Christos Stavrakakis
        network = ipaddr.IPv4Network(subnet, strict=True)
248 d368ec0c Christos Stavrakakis
    except ValueError:
249 bd40abfa Christos Stavrakakis
        raise faults.BadRequest("Invalid network IPv4 subnet")
250 d368ec0c Christos Stavrakakis
251 d368ec0c Christos Stavrakakis
    # Check that network size is allowed!
252 d368ec0c Christos Stavrakakis
    if not validate_network_size(network.prefixlen):
253 bd40abfa Christos Stavrakakis
        raise faults.OverLimit(message="Unsupported network size",
254 9ae613af Christos Stavrakakis
                        details="Network mask must be in range (%s, 29]" %
255 9ae613af Christos Stavrakakis
                                MAX_CIDR_BLOCK)
256 9ae613af Christos Stavrakakis
257 9ae613af Christos Stavrakakis
    # Check that gateway belongs to network
258 9ae613af Christos Stavrakakis
    if gateway:
259 9ae613af Christos Stavrakakis
        try:
260 9ae613af Christos Stavrakakis
            gateway = ipaddr.IPv4Address(gateway)
261 9ae613af Christos Stavrakakis
        except ValueError:
262 bd40abfa Christos Stavrakakis
            raise faults.BadRequest("Invalid network IPv4 gateway")
263 9ae613af Christos Stavrakakis
        if not gateway in network:
264 bd40abfa Christos Stavrakakis
            raise faults.BadRequest("Invalid network IPv4 gateway")
265 9ae613af Christos Stavrakakis
266 9ae613af Christos Stavrakakis
    if subnet6:
267 9ae613af Christos Stavrakakis
        try:
268 9ae613af Christos Stavrakakis
            # Use strict option to not all subnets with host bits set
269 9ae613af Christos Stavrakakis
            network6 = ipaddr.IPv6Network(subnet6, strict=True)
270 9ae613af Christos Stavrakakis
        except ValueError:
271 bd40abfa Christos Stavrakakis
            raise faults.BadRequest("Invalid network IPv6 subnet")
272 9ae613af Christos Stavrakakis
        if gateway6:
273 9ae613af Christos Stavrakakis
            try:
274 9ae613af Christos Stavrakakis
                gateway6 = ipaddr.IPv6Address(gateway6)
275 9ae613af Christos Stavrakakis
            except ValueError:
276 bd40abfa Christos Stavrakakis
                raise faults.BadRequest("Invalid network IPv6 gateway")
277 9ae613af Christos Stavrakakis
            if not gateway6 in network6:
278 bd40abfa Christos Stavrakakis
                raise faults.BadRequest("Invalid network IPv6 gateway")
279 d368ec0c Christos Stavrakakis
280 d368ec0c Christos Stavrakakis
281 b14725eb Christos Stavrakakis
def validate_network_size(cidr_block):
282 b14725eb Christos Stavrakakis
    """Return True if network size is allowed."""
283 b14725eb Christos Stavrakakis
    return cidr_block <= 29 and cidr_block > MAX_CIDR_BLOCK
284 b14725eb Christos Stavrakakis
285 b14725eb Christos Stavrakakis
286 adc46059 Christos Stavrakakis
def allocate_public_address(backend):
287 adc46059 Christos Stavrakakis
    """Allocate a public IP for a vm."""
288 adc46059 Christos Stavrakakis
    for network in backend_public_networks(backend):
289 adc46059 Christos Stavrakakis
        try:
290 adc46059 Christos Stavrakakis
            address = get_network_free_address(network)
291 34c03a51 Christos Stavrakakis
        except faults.OverLimit:
292 adc46059 Christos Stavrakakis
            pass
293 34c03a51 Christos Stavrakakis
        else:
294 34c03a51 Christos Stavrakakis
            return (network, address)
295 adc46059 Christos Stavrakakis
    return (None, None)
296 adc46059 Christos Stavrakakis
297 adc46059 Christos Stavrakakis
298 dca7553e Christos Stavrakakis
def get_public_ip(backend):
299 dca7553e Christos Stavrakakis
    """Reserve an IP from a public network.
300 dca7553e Christos Stavrakakis

301 dca7553e Christos Stavrakakis
    This method should run inside a transaction.
302 dca7553e Christos Stavrakakis

303 dca7553e Christos Stavrakakis
    """
304 85919a0f Christos Stavrakakis
305 85919a0f Christos Stavrakakis
    # Guarantee exclusive access to backend, because accessing the IP pools of
306 85919a0f Christos Stavrakakis
    # the backend networks may result in a deadlock with backend allocator
307 85919a0f Christos Stavrakakis
    # which also checks that backend networks have a free IP.
308 85919a0f Christos Stavrakakis
    backend = Backend.objects.select_for_update().get(id=backend.id)
309 85919a0f Christos Stavrakakis
310 dca7553e Christos Stavrakakis
    address = None
311 74459c12 Christos Stavrakakis
    if settings.PUBLIC_USE_POOL:
312 dca7553e Christos Stavrakakis
        (network, address) = allocate_public_address(backend)
313 dca7553e Christos Stavrakakis
    else:
314 dca7553e Christos Stavrakakis
        for net in list(backend_public_networks(backend)):
315 dca7553e Christos Stavrakakis
            pool = net.get_pool()
316 dca7553e Christos Stavrakakis
            if not pool.empty():
317 dca7553e Christos Stavrakakis
                address = 'pool'
318 dca7553e Christos Stavrakakis
                network = net
319 dca7553e Christos Stavrakakis
                break
320 dca7553e Christos Stavrakakis
    if address is None:
321 dca7553e Christos Stavrakakis
        log.error("Public networks of backend %s are full", backend)
322 bd40abfa Christos Stavrakakis
        raise faults.OverLimit("Can not allocate IP for new machine."
323 dca7553e Christos Stavrakakis
                        " Public networks are full.")
324 dca7553e Christos Stavrakakis
    return (network, address)
325 dca7553e Christos Stavrakakis
326 dca7553e Christos Stavrakakis
327 7fede91e Christos Stavrakakis
def backend_public_networks(backend):
328 7fede91e Christos Stavrakakis
    """Return available public networks of the backend.
329 7fede91e Christos Stavrakakis

330 7fede91e Christos Stavrakakis
    Iterator for non-deleted public networks that are available
331 7fede91e Christos Stavrakakis
    to the specified backend.
332 7fede91e Christos Stavrakakis

333 7fede91e Christos Stavrakakis
    """
334 6dafedf6 Christos Stavrakakis
    for network in Network.objects.filter(public=True, deleted=False,
335 6dafedf6 Christos Stavrakakis
                                          drained=False):
336 7fede91e Christos Stavrakakis
        if BackendNetwork.objects.filter(network=network,
337 7fede91e Christos Stavrakakis
                                         backend=backend).exists():
338 7fede91e Christos Stavrakakis
            yield network
339 7fede91e Christos Stavrakakis
340 7fede91e Christos Stavrakakis
341 7fede91e Christos Stavrakakis
def get_network_free_address(network):
342 34c03a51 Christos Stavrakakis
    """Reserve an IP address from the IP Pool of the network."""
343 7fede91e Christos Stavrakakis
344 fdc94944 Christos Stavrakakis
    pool = network.get_pool()
345 34c03a51 Christos Stavrakakis
    try:
346 34c03a51 Christos Stavrakakis
        address = pool.get()
347 34c03a51 Christos Stavrakakis
    except EmptyPool:
348 34c03a51 Christos Stavrakakis
        raise faults.OverLimit("Network %s is full." % network.backend_id)
349 34c03a51 Christos Stavrakakis
        address = None
350 7fede91e Christos Stavrakakis
    pool.save()
351 7fede91e Christos Stavrakakis
    return address
352 7fede91e Christos Stavrakakis
353 7fede91e Christos Stavrakakis
354 9e8be4fb Christos Stavrakakis
def allocate_public_ip(networks=None):
355 9e8be4fb Christos Stavrakakis
    """Allocate an IP address from public networks."""
356 9e8be4fb Christos Stavrakakis
    if networks is None:
357 9e8be4fb Christos Stavrakakis
        networks = Network.objects.select_for_update().filter(public=True,
358 9e8be4fb Christos Stavrakakis
                                                              deleted=False)
359 9e8be4fb Christos Stavrakakis
    for network in networks:
360 9e8be4fb Christos Stavrakakis
        try:
361 9e8be4fb Christos Stavrakakis
            address = get_network_free_address(network)
362 9e8be4fb Christos Stavrakakis
        except:
363 9e8be4fb Christos Stavrakakis
            pass
364 9e8be4fb Christos Stavrakakis
        else:
365 9e8be4fb Christos Stavrakakis
            return network, address
366 9e8be4fb Christos Stavrakakis
    msg = "Can not allocate public IP. Public networks are full."
367 9e8be4fb Christos Stavrakakis
    log.error(msg)
368 9e8be4fb Christos Stavrakakis
    raise faults.OverLimit(msg)
369 9e8be4fb Christos Stavrakakis
370 9e8be4fb Christos Stavrakakis
371 d44c236b Giorgos Verigakis
def get_nic(machine, network):
372 d44c236b Giorgos Verigakis
    try:
373 d44c236b Giorgos Verigakis
        return NetworkInterface.objects.get(machine=machine, network=network)
374 d44c236b Giorgos Verigakis
    except NetworkInterface.DoesNotExist:
375 bd40abfa Christos Stavrakakis
        raise faults.ItemNotFound('Server not connected to this network.')
376 d44c236b Giorgos Verigakis
377 7fede91e Christos Stavrakakis
378 08b079e2 Stavros Sachtouris
def get_nic_from_index(vm, nic_index):
379 08b079e2 Stavros Sachtouris
    """Returns the nic_index-th nic of a vm
380 08b079e2 Stavros Sachtouris
       Error Response Codes: itemNotFound (404), badMediaType (415)
381 08b079e2 Stavros Sachtouris
    """
382 08b079e2 Stavros Sachtouris
    matching_nics = vm.nics.filter(index=nic_index)
383 08b079e2 Stavros Sachtouris
    matching_nics_len = len(matching_nics)
384 08b079e2 Stavros Sachtouris
    if matching_nics_len < 1:
385 bd40abfa Christos Stavrakakis
        raise faults.ItemNotFound('NIC not found on VM')
386 08b079e2 Stavros Sachtouris
    elif matching_nics_len > 1:
387 bd40abfa Christos Stavrakakis
        raise faults.BadMediaType('NIC index conflict on VM')
388 08b079e2 Stavros Sachtouris
    nic = matching_nics[0]
389 08b079e2 Stavros Sachtouris
    return nic
390 e2ee7808 Giorgos Verigakis
391 7fede91e Christos Stavrakakis
392 432fc8c3 Giorgos Verigakis
def render_metadata(request, metadata, use_values=False, status=200):
393 432fc8c3 Giorgos Verigakis
    if request.serialization == 'xml':
394 432fc8c3 Giorgos Verigakis
        data = render_to_string('metadata.xml', {'metadata': metadata})
395 432fc8c3 Giorgos Verigakis
    else:
396 b36f78fa Giorgos Verigakis
        if use_values:
397 b36f78fa Giorgos Verigakis
            d = {'metadata': {'values': metadata}}
398 b36f78fa Giorgos Verigakis
        else:
399 b36f78fa Giorgos Verigakis
            d = {'metadata': metadata}
400 432fc8c3 Giorgos Verigakis
        data = json.dumps(d)
401 432fc8c3 Giorgos Verigakis
    return HttpResponse(data, status=status)
402 432fc8c3 Giorgos Verigakis
403 a08e4270 Vangelis Koukis
404 432fc8c3 Giorgos Verigakis
def render_meta(request, meta, status=200):
405 432fc8c3 Giorgos Verigakis
    if request.serialization == 'xml':
406 6ef51e9f Giorgos Verigakis
        data = render_to_string('meta.xml', dict(key=key, val=val))
407 432fc8c3 Giorgos Verigakis
    else:
408 6ef51e9f Giorgos Verigakis
        data = json.dumps(dict(meta=meta))
409 432fc8c3 Giorgos Verigakis
    return HttpResponse(data, status=status)
410 432fc8c3 Giorgos Verigakis
411 a08e4270 Vangelis Koukis
412 08b079e2 Stavros Sachtouris
def construct_nic_id(nic):
413 08b079e2 Stavros Sachtouris
    return "-".join(["nic", unicode(nic.machine.id), unicode(nic.index)])
414 6dd70a5c Christos Stavrakakis
415 6dd70a5c Christos Stavrakakis
416 dca7553e Christos Stavrakakis
def verify_personality(personality):
417 6ec4694f Christos Stavrakakis
    """Verify that a a list of personalities is well formed"""
418 006c6249 Christos Stavrakakis
    if len(personality) > settings.MAX_PERSONALITY:
419 bd40abfa Christos Stavrakakis
        raise faults.OverLimit("Maximum number of personalities"
420 e440e835 Christos Stavrakakis
                        " exceeded")
421 dca7553e Christos Stavrakakis
    for p in personality:
422 dca7553e Christos Stavrakakis
        # Verify that personalities are well-formed
423 dca7553e Christos Stavrakakis
        try:
424 dca7553e Christos Stavrakakis
            assert isinstance(p, dict)
425 dca7553e Christos Stavrakakis
            keys = set(p.keys())
426 dca7553e Christos Stavrakakis
            allowed = set(['contents', 'group', 'mode', 'owner', 'path'])
427 dca7553e Christos Stavrakakis
            assert keys.issubset(allowed)
428 dca7553e Christos Stavrakakis
            contents = p['contents']
429 dca7553e Christos Stavrakakis
            if len(contents) > settings.MAX_PERSONALITY_SIZE:
430 dca7553e Christos Stavrakakis
                # No need to decode if contents already exceed limit
431 bd40abfa Christos Stavrakakis
                raise faults.OverLimit("Maximum size of personality exceeded")
432 dca7553e Christos Stavrakakis
            if len(b64decode(contents)) > settings.MAX_PERSONALITY_SIZE:
433 bd40abfa Christos Stavrakakis
                raise faults.OverLimit("Maximum size of personality exceeded")
434 dca7553e Christos Stavrakakis
        except AssertionError:
435 bd40abfa Christos Stavrakakis
            raise faults.BadRequest("Malformed personality in request")
436 2a599282 Christos Stavrakakis
437 2a599282 Christos Stavrakakis
438 b7d38981 Dimitris Aragiorgis
def values_from_flavor(flavor):
439 b7d38981 Dimitris Aragiorgis
    """Get Ganeti connectivity info from flavor type.
440 b7d38981 Dimitris Aragiorgis

441 b7d38981 Dimitris Aragiorgis
    If link or mac_prefix equals to "pool", then the resources
442 b7d38981 Dimitris Aragiorgis
    are allocated from the corresponding Pools.
443 b7d38981 Dimitris Aragiorgis

444 b7d38981 Dimitris Aragiorgis
    """
445 b7d38981 Dimitris Aragiorgis
    try:
446 b7d38981 Dimitris Aragiorgis
        flavor = Network.FLAVORS[flavor]
447 b7d38981 Dimitris Aragiorgis
    except KeyError:
448 bd40abfa Christos Stavrakakis
        raise faults.BadRequest("Unknown network flavor")
449 b7d38981 Dimitris Aragiorgis
450 b7d38981 Dimitris Aragiorgis
    mode = flavor.get("mode")
451 b7d38981 Dimitris Aragiorgis
452 b7d38981 Dimitris Aragiorgis
    link = flavor.get("link")
453 b7d38981 Dimitris Aragiorgis
    if link == "pool":
454 b7d38981 Dimitris Aragiorgis
        link = allocate_resource("bridge")
455 b7d38981 Dimitris Aragiorgis
456 b7d38981 Dimitris Aragiorgis
    mac_prefix = flavor.get("mac_prefix")
457 b7d38981 Dimitris Aragiorgis
    if mac_prefix == "pool":
458 b7d38981 Dimitris Aragiorgis
        mac_prefix = allocate_resource("mac_prefix")
459 b7d38981 Dimitris Aragiorgis
460 b7d38981 Dimitris Aragiorgis
    tags = flavor.get("tags")
461 b7d38981 Dimitris Aragiorgis
462 b7d38981 Dimitris Aragiorgis
    return mode, link, mac_prefix, tags
463 b7d38981 Dimitris Aragiorgis
464 b7d38981 Dimitris Aragiorgis
465 b7d38981 Dimitris Aragiorgis
def allocate_resource(res_type):
466 b7d38981 Dimitris Aragiorgis
    table = get_pool_table(res_type)
467 b7d38981 Dimitris Aragiorgis
    pool = table.get_pool()
468 b7d38981 Dimitris Aragiorgis
    value = pool.get()
469 b7d38981 Dimitris Aragiorgis
    pool.save()
470 b7d38981 Dimitris Aragiorgis
    return value
471 b7d38981 Dimitris Aragiorgis
472 b7d38981 Dimitris Aragiorgis
473 b7d38981 Dimitris Aragiorgis
def release_resource(res_type, value):
474 b7d38981 Dimitris Aragiorgis
    table = get_pool_table(res_type)
475 b7d38981 Dimitris Aragiorgis
    pool = table.get_pool()
476 b7d38981 Dimitris Aragiorgis
    pool.put(value)
477 b7d38981 Dimitris Aragiorgis
    pool.save()
478 b7d38981 Dimitris Aragiorgis
479 b7d38981 Dimitris Aragiorgis
480 b7d38981 Dimitris Aragiorgis
def get_pool_table(res_type):
481 b7d38981 Dimitris Aragiorgis
    if res_type == "bridge":
482 b7d38981 Dimitris Aragiorgis
        return BridgePoolTable
483 b7d38981 Dimitris Aragiorgis
    elif res_type == "mac_prefix":
484 b7d38981 Dimitris Aragiorgis
        return MacPrefixPoolTable
485 b7d38981 Dimitris Aragiorgis
    else:
486 b7d38981 Dimitris Aragiorgis
        raise Exception("Unknown resource type")
487 55f562a4 Kostas Papadimitriou
488 55f562a4 Kostas Papadimitriou
489 55f562a4 Kostas Papadimitriou
def get_existing_users():
490 55f562a4 Kostas Papadimitriou
    """
491 55f562a4 Kostas Papadimitriou
    Retrieve user ids stored in cyclades user agnostic models.
492 55f562a4 Kostas Papadimitriou
    """
493 55f562a4 Kostas Papadimitriou
    # also check PublicKeys a user with no servers/networks exist
494 b47b110d Kostas Papadimitriou
    from synnefo.userdata.models import PublicKeyPair
495 55f562a4 Kostas Papadimitriou
    from synnefo.db.models import VirtualMachine, Network
496 55f562a4 Kostas Papadimitriou
497 55f562a4 Kostas Papadimitriou
    keypairusernames = PublicKeyPair.objects.filter().values_list('user',
498 e440e835 Christos Stavrakakis
                                                                  flat=True)
499 55f562a4 Kostas Papadimitriou
    serverusernames = VirtualMachine.objects.filter().values_list('userid',
500 e440e835 Christos Stavrakakis
                                                                  flat=True)
501 55f562a4 Kostas Papadimitriou
    networkusernames = Network.objects.filter().values_list('userid',
502 55f562a4 Kostas Papadimitriou
                                                            flat=True)
503 55f562a4 Kostas Papadimitriou
504 e440e835 Christos Stavrakakis
    return set(list(keypairusernames) + list(serverusernames) +
505 e440e835 Christos Stavrakakis
               list(networkusernames))
506 1b696c26 Christos Stavrakakis
507 1b696c26 Christos Stavrakakis
508 1b696c26 Christos Stavrakakis
def vm_to_links(vm_id):
509 73fbaad3 Christos Stavrakakis
    href = join_urls(SERVERS_URL, str(vm_id))
510 73fbaad3 Christos Stavrakakis
    return [{"rel": rel, "href": href} for rel in ("self", "bookmark")]
511 1b696c26 Christos Stavrakakis
512 1b696c26 Christos Stavrakakis
513 1b696c26 Christos Stavrakakis
def network_to_links(network_id):
514 73fbaad3 Christos Stavrakakis
    href = join_urls(NETWORKS_URL, str(network_id))
515 73fbaad3 Christos Stavrakakis
    return [{"rel": rel, "href": href} for rel in ("self", "bookmark")]
516 1b696c26 Christos Stavrakakis
517 1b696c26 Christos Stavrakakis
518 1b696c26 Christos Stavrakakis
def flavor_to_links(flavor_id):
519 73fbaad3 Christos Stavrakakis
    href = join_urls(FLAVORS_URL, str(flavor_id))
520 73fbaad3 Christos Stavrakakis
    return [{"rel": rel, "href": href} for rel in ("self", "bookmark")]
521 1b696c26 Christos Stavrakakis
522 1b696c26 Christos Stavrakakis
523 1b696c26 Christos Stavrakakis
def image_to_links(image_id):
524 73fbaad3 Christos Stavrakakis
    href = join_urls(IMAGES_URL, str(image_id))
525 73fbaad3 Christos Stavrakakis
    links = [{"rel": rel, "href": href} for rel in ("self", "bookmark")]
526 1b696c26 Christos Stavrakakis
    links.append({"rel": "alternate",
527 73fbaad3 Christos Stavrakakis
                  "href": join_urls(IMAGES_PLANKTON_URL, str(image_id))})
528 1b696c26 Christos Stavrakakis
    return links
529 41a7fae7 Christos Stavrakakis
530 41a7fae7 Christos Stavrakakis
def start_action(vm, action, jobId):
531 41a7fae7 Christos Stavrakakis
    vm.action = action
532 41a7fae7 Christos Stavrakakis
    vm.backendjobid = jobId
533 41a7fae7 Christos Stavrakakis
    vm.backendopcode = None
534 41a7fae7 Christos Stavrakakis
    vm.backendjobstatus = None
535 41a7fae7 Christos Stavrakakis
    vm.backendlogmsg = None
536 41a7fae7 Christos Stavrakakis
    vm.save()