Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / logic / ips.py @ 2eda9c61

History | View | Annotate | Download (9 kB)

1 91884d63 Giorgos Korfiatis
# Copyright 2013 GRNET S.A. All rights reserved.
2 91884d63 Giorgos Korfiatis
#
3 91884d63 Giorgos Korfiatis
# Redistribution and use in source and binary forms, with or
4 91884d63 Giorgos Korfiatis
# without modification, are permitted provided that the following
5 91884d63 Giorgos Korfiatis
# conditions are met:
6 91884d63 Giorgos Korfiatis
#
7 91884d63 Giorgos Korfiatis
#   1. Redistributions of source code must retain the above
8 91884d63 Giorgos Korfiatis
#      copyright notice, this list of conditions and the following
9 91884d63 Giorgos Korfiatis
#      disclaimer.
10 91884d63 Giorgos Korfiatis
#
11 91884d63 Giorgos Korfiatis
#   2. Redistributions in binary form must reproduce the above
12 91884d63 Giorgos Korfiatis
#      copyright notice, this list of conditions and the following
13 91884d63 Giorgos Korfiatis
#      disclaimer in the documentation and/or other materials
14 91884d63 Giorgos Korfiatis
#      provided with the distribution.
15 91884d63 Giorgos Korfiatis
#
16 91884d63 Giorgos Korfiatis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 91884d63 Giorgos Korfiatis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 91884d63 Giorgos Korfiatis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 91884d63 Giorgos Korfiatis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 91884d63 Giorgos Korfiatis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 91884d63 Giorgos Korfiatis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 91884d63 Giorgos Korfiatis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 91884d63 Giorgos Korfiatis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 91884d63 Giorgos Korfiatis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 91884d63 Giorgos Korfiatis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 91884d63 Giorgos Korfiatis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 91884d63 Giorgos Korfiatis
# POSSIBILITY OF SUCH DAMAGE.
28 91884d63 Giorgos Korfiatis
#
29 91884d63 Giorgos Korfiatis
# The views and conclusions contained in the software and
30 91884d63 Giorgos Korfiatis
# documentation are those of the authors and should not be
31 91884d63 Giorgos Korfiatis
# interpreted as representing official policies, either expressed
32 91884d63 Giorgos Korfiatis
# or implied, of GRNET S.A.
33 91884d63 Giorgos Korfiatis
34 3c96580c Christos Stavrakakis
import logging
35 3c96580c Christos Stavrakakis
36 3c96580c Christos Stavrakakis
from snf_django.lib.api import faults
37 3c96580c Christos Stavrakakis
from django.db import transaction
38 3c96580c Christos Stavrakakis
from synnefo import quotas
39 3c96580c Christos Stavrakakis
from synnefo.db import pools
40 e21ac0fa Christos Stavrakakis
from synnefo.db.models import (IPPoolTable, IPAddress, Network)
41 3c96580c Christos Stavrakakis
log = logging.getLogger(__name__)
42 3c96580c Christos Stavrakakis
43 3c96580c Christos Stavrakakis
44 3c96580c Christos Stavrakakis
def allocate_ip_from_pools(pool_rows, userid, address=None, floating_ip=False):
45 3c96580c Christos Stavrakakis
    """Try to allocate a value from a number of pools.
46 3c96580c Christos Stavrakakis

47 3c96580c Christos Stavrakakis
    This function takes as argument a number of PoolTable objects and tries to
48 3c96580c Christos Stavrakakis
    allocate a value from them. If all pools are empty EmptyPool is raised.
49 3c96580c Christos Stavrakakis
    If an address is specified and does not belong to any of the pools,
50 3c96580c Christos Stavrakakis
    InvalidValue is raised.
51 3c96580c Christos Stavrakakis

52 3c96580c Christos Stavrakakis
    """
53 3c96580c Christos Stavrakakis
    for pool_row in pool_rows:
54 3c96580c Christos Stavrakakis
        pool = pool_row.pool
55 3c96580c Christos Stavrakakis
        try:
56 3c96580c Christos Stavrakakis
            value = pool.get(value=address)
57 3c96580c Christos Stavrakakis
            pool.save()
58 3c96580c Christos Stavrakakis
            subnet = pool_row.subnet
59 3c96580c Christos Stavrakakis
            ipaddress = IPAddress.objects.create(subnet=subnet,
60 3c96580c Christos Stavrakakis
                                                 network=subnet.network,
61 3c96580c Christos Stavrakakis
                                                 userid=userid,
62 3c96580c Christos Stavrakakis
                                                 address=value,
63 5920f82c Christos Stavrakakis
                                                 floating_ip=floating_ip,
64 5920f82c Christos Stavrakakis
                                                 ipversion=4)
65 3c96580c Christos Stavrakakis
            return ipaddress
66 3c96580c Christos Stavrakakis
        except pools.EmptyPool:
67 3c96580c Christos Stavrakakis
            pass
68 3c96580c Christos Stavrakakis
        except pools.InvalidValue:
69 3c96580c Christos Stavrakakis
            pass
70 3c96580c Christos Stavrakakis
    if address is None:
71 3c96580c Christos Stavrakakis
        raise pools.EmptyPool("No more IP addresses available on pools %s" %
72 3c96580c Christos Stavrakakis
                              pool_rows)
73 3c96580c Christos Stavrakakis
    else:
74 3c96580c Christos Stavrakakis
        raise pools.InvalidValue("Address %s does not belong to pools %s" %
75 3c96580c Christos Stavrakakis
                                 (address, pool_rows))
76 3c96580c Christos Stavrakakis
77 3c96580c Christos Stavrakakis
78 3c96580c Christos Stavrakakis
def allocate_ip(network, userid, address=None, floating_ip=False):
79 3c96580c Christos Stavrakakis
    """Try to allocate an IP from networks IP pools."""
80 3c96580c Christos Stavrakakis
    if network.action == "DESTROY":
81 8d5795b4 Christos Stavrakakis
        raise faults.Conflict("Cannot allocate IP. Network %s is being"
82 3c96580c Christos Stavrakakis
                              " deleted" % network.id)
83 8f335041 Christos Stavrakakis
    elif network.drained:
84 8f335041 Christos Stavrakakis
        raise faults.Conflict("Can not allocate IP while network '%s' is in"
85 8f335041 Christos Stavrakakis
                              " 'SNF:DRAINED' status" % network.id)
86 8f335041 Christos Stavrakakis
87 3c96580c Christos Stavrakakis
    ip_pools = IPPoolTable.objects.select_for_update()\
88 1113f89d Christos Stavrakakis
        .filter(subnet__network=network).order_by('id')
89 3c96580c Christos Stavrakakis
    try:
90 3c96580c Christos Stavrakakis
        return allocate_ip_from_pools(ip_pools, userid, address=address,
91 3c96580c Christos Stavrakakis
                                      floating_ip=floating_ip)
92 3c96580c Christos Stavrakakis
    except pools.EmptyPool:
93 3c96580c Christos Stavrakakis
        raise faults.Conflict("No more IP addresses available on network %s"
94 3c96580c Christos Stavrakakis
                              % network.id)
95 3c96580c Christos Stavrakakis
    except pools.ValueNotAvailable:
96 3c96580c Christos Stavrakakis
        raise faults.Conflict("IP address %s is already used." % address)
97 3c96580c Christos Stavrakakis
    except pools.InvalidValue:
98 3c96580c Christos Stavrakakis
        raise faults.BadRequest("Address %s does not belong to network %s" %
99 3c96580c Christos Stavrakakis
                                (address, network.id))
100 3c96580c Christos Stavrakakis
101 3c96580c Christos Stavrakakis
102 3aecadc8 Christos Stavrakakis
def allocate_public_ip(userid, floating_ip=False, backend=None, networks=None):
103 3c96580c Christos Stavrakakis
    """Try to allocate a public or floating IP address.
104 3c96580c Christos Stavrakakis

105 3c96580c Christos Stavrakakis
    Try to allocate a a public IPv4 address from one of the available networks.
106 3c96580c Christos Stavrakakis
    If 'floating_ip' is set, only networks which are floating IP pools will be
107 3c96580c Christos Stavrakakis
    used and the IPAddress that will be created will be marked as a floating
108 3c96580c Christos Stavrakakis
    IP. If 'backend' is set, only the networks that exist in this backend will
109 3c96580c Christos Stavrakakis
    be used.
110 3c96580c Christos Stavrakakis

111 3c96580c Christos Stavrakakis
    """
112 3c96580c Christos Stavrakakis
113 3c96580c Christos Stavrakakis
    ip_pool_rows = IPPoolTable.objects.select_for_update()\
114 3c96580c Christos Stavrakakis
        .prefetch_related("subnet__network")\
115 3c96580c Christos Stavrakakis
        .filter(subnet__deleted=False)\
116 3c96580c Christos Stavrakakis
        .filter(subnet__network__deleted=False)\
117 3c96580c Christos Stavrakakis
        .filter(subnet__network__public=True)\
118 3c96580c Christos Stavrakakis
        .filter(subnet__network__drained=False)
119 3aecadc8 Christos Stavrakakis
    if networks is not None:
120 3aecadc8 Christos Stavrakakis
        ip_pool_rows = ip_pool_rows.filter(subnet__network__in=networks)
121 3c96580c Christos Stavrakakis
    if floating_ip:
122 3c96580c Christos Stavrakakis
        ip_pool_rows = ip_pool_rows\
123 3c96580c Christos Stavrakakis
            .filter(subnet__network__floating_ip_pool=True)
124 3c96580c Christos Stavrakakis
    if backend is not None:
125 3c96580c Christos Stavrakakis
        ip_pool_rows = ip_pool_rows\
126 3c96580c Christos Stavrakakis
            .filter(subnet__network__backend_networks__backend=backend)
127 3c96580c Christos Stavrakakis
128 3c96580c Christos Stavrakakis
    try:
129 3c96580c Christos Stavrakakis
        return allocate_ip_from_pools(ip_pool_rows, userid,
130 3c96580c Christos Stavrakakis
                                      floating_ip=floating_ip)
131 3c96580c Christos Stavrakakis
    except pools.EmptyPool:
132 3c96580c Christos Stavrakakis
        ip_type = "floating" if floating_ip else "public"
133 3c96580c Christos Stavrakakis
        log_msg = "Failed to allocate a %s IP. Reason:" % ip_type
134 3c96580c Christos Stavrakakis
        if ip_pool_rows:
135 3c96580c Christos Stavrakakis
            log_msg += " No network exists."
136 3c96580c Christos Stavrakakis
        else:
137 3c96580c Christos Stavrakakis
            log_msg += " All network are full."
138 3c96580c Christos Stavrakakis
        if backend is not None:
139 3c96580c Christos Stavrakakis
            log_msg += " Backend: %s" % backend
140 3c96580c Christos Stavrakakis
        log.error(log_msg)
141 8d5795b4 Christos Stavrakakis
        exception_msg = "Cannot allocate a %s IP address." % ip_type
142 3aecadc8 Christos Stavrakakis
        raise faults.Conflict(exception_msg)
143 3c96580c Christos Stavrakakis
144 3c96580c Christos Stavrakakis
145 3c96580c Christos Stavrakakis
@transaction.commit_on_success
146 3c96580c Christos Stavrakakis
def create_floating_ip(userid, network=None, address=None):
147 3c96580c Christos Stavrakakis
    if network is None:
148 3c96580c Christos Stavrakakis
        floating_ip = allocate_public_ip(userid, floating_ip=True)
149 3c96580c Christos Stavrakakis
    else:
150 3c96580c Christos Stavrakakis
        if not network.floating_ip_pool:
151 8d5795b4 Christos Stavrakakis
            msg = ("Cannot allocate floating IP. Network %s is"
152 3c96580c Christos Stavrakakis
                   " not a floating IP pool.")
153 3c96580c Christos Stavrakakis
            raise faults.Conflict(msg % network.id)
154 3c96580c Christos Stavrakakis
        if network.action == "DESTROY":
155 8d5795b4 Christos Stavrakakis
            msg = "Cannot allocate floating IP. Network %s is being deleted."
156 3c96580c Christos Stavrakakis
            raise faults.Conflict(msg % network.id)
157 3c96580c Christos Stavrakakis
158 3c96580c Christos Stavrakakis
        # Allocate the floating IP
159 3c96580c Christos Stavrakakis
        floating_ip = allocate_ip(network, userid, address=address,
160 3c96580c Christos Stavrakakis
                                  floating_ip=True)
161 3c96580c Christos Stavrakakis
162 3c96580c Christos Stavrakakis
    # Issue commission (quotas)
163 3c96580c Christos Stavrakakis
    quotas.issue_and_accept_commission(floating_ip)
164 3c96580c Christos Stavrakakis
    transaction.commit()
165 3c96580c Christos Stavrakakis
166 3c96580c Christos Stavrakakis
    log.info("Created floating IP '%s' for user IP '%s'", floating_ip, userid)
167 3c96580c Christos Stavrakakis
168 3c96580c Christos Stavrakakis
    return floating_ip
169 3c96580c Christos Stavrakakis
170 3c96580c Christos Stavrakakis
171 3aecadc8 Christos Stavrakakis
def get_free_floating_ip(userid, network=None):
172 3aecadc8 Christos Stavrakakis
    """Get one of the free available floating IPs of the user.
173 3aecadc8 Christos Stavrakakis

174 3aecadc8 Christos Stavrakakis
    Get one of the users floating IPs that is not connected to any port
175 3aecadc8 Christos Stavrakakis
    or server. If network is specified, the floating IP must be from
176 3aecadc8 Christos Stavrakakis
    that network.
177 3aecadc8 Christos Stavrakakis

178 3aecadc8 Christos Stavrakakis
    """
179 3aecadc8 Christos Stavrakakis
    floating_ips = IPAddress.objects\
180 036b07b6 Christos Stavrakakis
                            .filter(userid=userid, deleted=False, nic=None,
181 036b07b6 Christos Stavrakakis
                                    floating_ip=True)
182 3aecadc8 Christos Stavrakakis
    if network is not None:
183 3aecadc8 Christos Stavrakakis
        floating_ips = floating_ips.filter(network=network)
184 3aecadc8 Christos Stavrakakis
185 3aecadc8 Christos Stavrakakis
    for floating_ip in floating_ips:
186 3aecadc8 Christos Stavrakakis
        floating_ip = IPAddress.objects.select_for_update()\
187 3aecadc8 Christos Stavrakakis
                                       .get(id=floating_ip.id)
188 3aecadc8 Christos Stavrakakis
        if floating_ip.nic is None:
189 3aecadc8 Christos Stavrakakis
            return floating_ip
190 3aecadc8 Christos Stavrakakis
191 e74a5b4b Christos Stavrakakis
    msg = "Cannot find an unused floating IP to connect server to"
192 3aecadc8 Christos Stavrakakis
    if network is not None:
193 3aecadc8 Christos Stavrakakis
        msg += " network '%s'." % network.id
194 3aecadc8 Christos Stavrakakis
    else:
195 3aecadc8 Christos Stavrakakis
        msg += " a public network."
196 e74a5b4b Christos Stavrakakis
    msg += " Please create a floating IP."
197 3aecadc8 Christos Stavrakakis
    raise faults.Conflict(msg)
198 3aecadc8 Christos Stavrakakis
199 3aecadc8 Christos Stavrakakis
200 3c96580c Christos Stavrakakis
@transaction.commit_on_success
201 3c96580c Christos Stavrakakis
def delete_floating_ip(floating_ip):
202 3c96580c Christos Stavrakakis
    if floating_ip.nic:
203 3c96580c Christos Stavrakakis
        # This is safe, you also need for_update to attach floating IP to
204 3c96580c Christos Stavrakakis
        # instance.
205 cad9202c Christos Stavrakakis
        server = floating_ip.nic.machine
206 cad9202c Christos Stavrakakis
        if server is None:
207 cad9202c Christos Stavrakakis
            msg = ("Floating IP '%s' is used by port '%s'" %
208 cad9202c Christos Stavrakakis
                   (floating_ip.id, floating_ip.nic_id))
209 cad9202c Christos Stavrakakis
        else:
210 cad9202c Christos Stavrakakis
            msg = ("Floating IP '%s' is used by server '%s'" %
211 cad9202c Christos Stavrakakis
                   (floating_ip.id, floating_ip.nic.machine_id))
212 3c96580c Christos Stavrakakis
        raise faults.Conflict(msg)
213 3c96580c Christos Stavrakakis
214 e21ac0fa Christos Stavrakakis
    # Lock network to prevent deadlock
215 e21ac0fa Christos Stavrakakis
    Network.objects.select_for_update().get(id=floating_ip.network_id)
216 e21ac0fa Christos Stavrakakis
217 3c96580c Christos Stavrakakis
    # Return the address of the floating IP back to pool
218 3c96580c Christos Stavrakakis
    floating_ip.release_address()
219 3c96580c Christos Stavrakakis
    # And mark the floating IP as deleted
220 3c96580c Christos Stavrakakis
    floating_ip.deleted = True
221 3c96580c Christos Stavrakakis
    floating_ip.save()
222 3c96580c Christos Stavrakakis
    # Release quota for floating IP
223 368d879e Giorgos Korfiatis
    quotas.issue_and_accept_commission(floating_ip, action="DESTROY")
224 3c96580c Christos Stavrakakis
    transaction.commit()
225 3c96580c Christos Stavrakakis
    # Delete the floating IP from DB
226 3c96580c Christos Stavrakakis
    log.info("Deleted floating IP '%s' of user '%s", floating_ip,
227 3c96580c Christos Stavrakakis
             floating_ip.userid)
228 3c96580c Christos Stavrakakis
    floating_ip.delete()