Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (9.5 kB)

1 ff5edb80 Giorgos Korfiatis
# Copyright 2013-2014 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 47c27955 Giorgos Korfiatis
def create_floating_ip(userid, network=None, address=None, project=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 47c27955 Giorgos Korfiatis
    if project is None:
163 47c27955 Giorgos Korfiatis
        project = userid
164 47c27955 Giorgos Korfiatis
    floating_ip.project = project
165 47c27955 Giorgos Korfiatis
    floating_ip.save()
166 3c96580c Christos Stavrakakis
    # Issue commission (quotas)
167 3c96580c Christos Stavrakakis
    quotas.issue_and_accept_commission(floating_ip)
168 3c96580c Christos Stavrakakis
    transaction.commit()
169 3c96580c Christos Stavrakakis
170 3c96580c Christos Stavrakakis
    log.info("Created floating IP '%s' for user IP '%s'", floating_ip, userid)
171 3c96580c Christos Stavrakakis
172 3c96580c Christos Stavrakakis
    return floating_ip
173 3c96580c Christos Stavrakakis
174 3c96580c Christos Stavrakakis
175 3aecadc8 Christos Stavrakakis
def get_free_floating_ip(userid, network=None):
176 3aecadc8 Christos Stavrakakis
    """Get one of the free available floating IPs of the user.
177 3aecadc8 Christos Stavrakakis

178 3aecadc8 Christos Stavrakakis
    Get one of the users floating IPs that is not connected to any port
179 3aecadc8 Christos Stavrakakis
    or server. If network is specified, the floating IP must be from
180 3aecadc8 Christos Stavrakakis
    that network.
181 3aecadc8 Christos Stavrakakis

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