root / snf-cyclades-app / synnefo / logic / ips.py @ c397dbce
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() |