root / snf-cyclades-app / synnefo / logic / ips.py @ 435bb7fb
History | View | Annotate | Download (7 kB)
1 | 3c96580c | Christos Stavrakakis | import logging |
---|---|---|---|
2 | 3c96580c | Christos Stavrakakis | |
3 | 3c96580c | Christos Stavrakakis | from snf_django.lib.api import faults |
4 | 3c96580c | Christos Stavrakakis | from django.db import transaction |
5 | 3c96580c | Christos Stavrakakis | from synnefo import quotas |
6 | 3c96580c | Christos Stavrakakis | from synnefo.db import pools |
7 | 3c96580c | Christos Stavrakakis | from synnefo.db.models import (IPPoolTable, IPAddress) |
8 | 3c96580c | Christos Stavrakakis | log = logging.getLogger(__name__) |
9 | 3c96580c | Christos Stavrakakis | |
10 | 3c96580c | Christos Stavrakakis | |
11 | 3c96580c | Christos Stavrakakis | def allocate_ip_from_pools(pool_rows, userid, address=None, floating_ip=False): |
12 | 3c96580c | Christos Stavrakakis | """Try to allocate a value from a number of pools.
|
13 | 3c96580c | Christos Stavrakakis |
|
14 | 3c96580c | Christos Stavrakakis | This function takes as argument a number of PoolTable objects and tries to
|
15 | 3c96580c | Christos Stavrakakis | allocate a value from them. If all pools are empty EmptyPool is raised.
|
16 | 3c96580c | Christos Stavrakakis | If an address is specified and does not belong to any of the pools,
|
17 | 3c96580c | Christos Stavrakakis | InvalidValue is raised.
|
18 | 3c96580c | Christos Stavrakakis |
|
19 | 3c96580c | Christos Stavrakakis | """
|
20 | 3c96580c | Christos Stavrakakis | for pool_row in pool_rows: |
21 | 3c96580c | Christos Stavrakakis | pool = pool_row.pool |
22 | 3c96580c | Christos Stavrakakis | try:
|
23 | 3c96580c | Christos Stavrakakis | value = pool.get(value=address) |
24 | 3c96580c | Christos Stavrakakis | pool.save() |
25 | 3c96580c | Christos Stavrakakis | subnet = pool_row.subnet |
26 | 3c96580c | Christos Stavrakakis | ipaddress = IPAddress.objects.create(subnet=subnet, |
27 | 3c96580c | Christos Stavrakakis | network=subnet.network, |
28 | 3c96580c | Christos Stavrakakis | userid=userid, |
29 | 3c96580c | Christos Stavrakakis | address=value, |
30 | 3c96580c | Christos Stavrakakis | floating_ip=floating_ip) |
31 | 3c96580c | Christos Stavrakakis | return ipaddress
|
32 | 3c96580c | Christos Stavrakakis | except pools.EmptyPool:
|
33 | 3c96580c | Christos Stavrakakis | pass
|
34 | 3c96580c | Christos Stavrakakis | except pools.InvalidValue:
|
35 | 3c96580c | Christos Stavrakakis | pass
|
36 | 3c96580c | Christos Stavrakakis | if address is None: |
37 | 3c96580c | Christos Stavrakakis | raise pools.EmptyPool("No more IP addresses available on pools %s" % |
38 | 3c96580c | Christos Stavrakakis | pool_rows) |
39 | 3c96580c | Christos Stavrakakis | else:
|
40 | 3c96580c | Christos Stavrakakis | raise pools.InvalidValue("Address %s does not belong to pools %s" % |
41 | 3c96580c | Christos Stavrakakis | (address, pool_rows)) |
42 | 3c96580c | Christos Stavrakakis | |
43 | 3c96580c | Christos Stavrakakis | |
44 | 3c96580c | Christos Stavrakakis | def allocate_ip(network, userid, address=None, floating_ip=False): |
45 | 3c96580c | Christos Stavrakakis | """Try to allocate an IP from networks IP pools."""
|
46 | 3c96580c | Christos Stavrakakis | if network.action == "DESTROY": |
47 | 8d5795b4 | Christos Stavrakakis | raise faults.Conflict("Cannot allocate IP. Network %s is being" |
48 | 3c96580c | Christos Stavrakakis | " deleted" % network.id)
|
49 | 8f335041 | Christos Stavrakakis | elif network.drained:
|
50 | 8f335041 | Christos Stavrakakis | raise faults.Conflict("Can not allocate IP while network '%s' is in" |
51 | 8f335041 | Christos Stavrakakis | " 'SNF:DRAINED' status" % network.id)
|
52 | 8f335041 | Christos Stavrakakis | |
53 | 3c96580c | Christos Stavrakakis | ip_pools = IPPoolTable.objects.select_for_update()\ |
54 | 3c96580c | Christos Stavrakakis | .filter(subnet__network=network) |
55 | 3c96580c | Christos Stavrakakis | try:
|
56 | 3c96580c | Christos Stavrakakis | return allocate_ip_from_pools(ip_pools, userid, address=address,
|
57 | 3c96580c | Christos Stavrakakis | floating_ip=floating_ip) |
58 | 3c96580c | Christos Stavrakakis | except pools.EmptyPool:
|
59 | 3c96580c | Christos Stavrakakis | raise faults.Conflict("No more IP addresses available on network %s" |
60 | 3c96580c | Christos Stavrakakis | % network.id) |
61 | 3c96580c | Christos Stavrakakis | except pools.ValueNotAvailable:
|
62 | 3c96580c | Christos Stavrakakis | raise faults.Conflict("IP address %s is already used." % address) |
63 | 3c96580c | Christos Stavrakakis | except pools.InvalidValue:
|
64 | 3c96580c | Christos Stavrakakis | raise faults.BadRequest("Address %s does not belong to network %s" % |
65 | 3c96580c | Christos Stavrakakis | (address, network.id)) |
66 | 3c96580c | Christos Stavrakakis | |
67 | 3c96580c | Christos Stavrakakis | |
68 | 3aecadc8 | Christos Stavrakakis | def allocate_public_ip(userid, floating_ip=False, backend=None, networks=None): |
69 | 3c96580c | Christos Stavrakakis | """Try to allocate a public or floating IP address.
|
70 | 3c96580c | Christos Stavrakakis |
|
71 | 3c96580c | Christos Stavrakakis | Try to allocate a a public IPv4 address from one of the available networks.
|
72 | 3c96580c | Christos Stavrakakis | If 'floating_ip' is set, only networks which are floating IP pools will be
|
73 | 3c96580c | Christos Stavrakakis | used and the IPAddress that will be created will be marked as a floating
|
74 | 3c96580c | Christos Stavrakakis | IP. If 'backend' is set, only the networks that exist in this backend will
|
75 | 3c96580c | Christos Stavrakakis | be used.
|
76 | 3c96580c | Christos Stavrakakis |
|
77 | 3c96580c | Christos Stavrakakis | """
|
78 | 3c96580c | Christos Stavrakakis | |
79 | 3c96580c | Christos Stavrakakis | ip_pool_rows = IPPoolTable.objects.select_for_update()\ |
80 | 3c96580c | Christos Stavrakakis | .prefetch_related("subnet__network")\
|
81 | 3c96580c | Christos Stavrakakis | .filter(subnet__deleted=False)\
|
82 | 3c96580c | Christos Stavrakakis | .filter(subnet__network__deleted=False)\
|
83 | 3c96580c | Christos Stavrakakis | .filter(subnet__network__public=True)\
|
84 | 3c96580c | Christos Stavrakakis | .filter(subnet__network__drained=False)
|
85 | 3aecadc8 | Christos Stavrakakis | if networks is not None: |
86 | 3aecadc8 | Christos Stavrakakis | ip_pool_rows = ip_pool_rows.filter(subnet__network__in=networks) |
87 | 3c96580c | Christos Stavrakakis | if floating_ip:
|
88 | 3c96580c | Christos Stavrakakis | ip_pool_rows = ip_pool_rows\ |
89 | 3c96580c | Christos Stavrakakis | .filter(subnet__network__floating_ip_pool=True)
|
90 | 3c96580c | Christos Stavrakakis | if backend is not None: |
91 | 3c96580c | Christos Stavrakakis | ip_pool_rows = ip_pool_rows\ |
92 | 3c96580c | Christos Stavrakakis | .filter(subnet__network__backend_networks__backend=backend) |
93 | 3c96580c | Christos Stavrakakis | |
94 | 3c96580c | Christos Stavrakakis | try:
|
95 | 3c96580c | Christos Stavrakakis | return allocate_ip_from_pools(ip_pool_rows, userid,
|
96 | 3c96580c | Christos Stavrakakis | floating_ip=floating_ip) |
97 | 3c96580c | Christos Stavrakakis | except pools.EmptyPool:
|
98 | 3c96580c | Christos Stavrakakis | ip_type = "floating" if floating_ip else "public" |
99 | 3c96580c | Christos Stavrakakis | log_msg = "Failed to allocate a %s IP. Reason:" % ip_type
|
100 | 3c96580c | Christos Stavrakakis | if ip_pool_rows:
|
101 | 3c96580c | Christos Stavrakakis | log_msg += " No network exists."
|
102 | 3c96580c | Christos Stavrakakis | else:
|
103 | 3c96580c | Christos Stavrakakis | log_msg += " All network are full."
|
104 | 3c96580c | Christos Stavrakakis | if backend is not None: |
105 | 3c96580c | Christos Stavrakakis | log_msg += " Backend: %s" % backend
|
106 | 3c96580c | Christos Stavrakakis | log.error(log_msg) |
107 | 8d5795b4 | Christos Stavrakakis | exception_msg = "Cannot allocate a %s IP address." % ip_type
|
108 | 3aecadc8 | Christos Stavrakakis | raise faults.Conflict(exception_msg)
|
109 | 3c96580c | Christos Stavrakakis | |
110 | 3c96580c | Christos Stavrakakis | |
111 | 3c96580c | Christos Stavrakakis | @transaction.commit_on_success
|
112 | 3c96580c | Christos Stavrakakis | def create_floating_ip(userid, network=None, address=None): |
113 | 3c96580c | Christos Stavrakakis | if network is None: |
114 | 3c96580c | Christos Stavrakakis | floating_ip = allocate_public_ip(userid, floating_ip=True)
|
115 | 3c96580c | Christos Stavrakakis | else:
|
116 | 3c96580c | Christos Stavrakakis | if not network.floating_ip_pool: |
117 | 8d5795b4 | Christos Stavrakakis | msg = ("Cannot allocate floating IP. Network %s is"
|
118 | 3c96580c | Christos Stavrakakis | " not a floating IP pool.")
|
119 | 3c96580c | Christos Stavrakakis | raise faults.Conflict(msg % network.id)
|
120 | 3c96580c | Christos Stavrakakis | if network.action == "DESTROY": |
121 | 8d5795b4 | Christos Stavrakakis | msg = "Cannot allocate floating IP. Network %s is being deleted."
|
122 | 3c96580c | Christos Stavrakakis | raise faults.Conflict(msg % network.id)
|
123 | 3c96580c | Christos Stavrakakis | |
124 | 3c96580c | Christos Stavrakakis | # Allocate the floating IP
|
125 | 3c96580c | Christos Stavrakakis | floating_ip = allocate_ip(network, userid, address=address, |
126 | 3c96580c | Christos Stavrakakis | floating_ip=True)
|
127 | 3c96580c | Christos Stavrakakis | |
128 | 3c96580c | Christos Stavrakakis | # Issue commission (quotas)
|
129 | 3c96580c | Christos Stavrakakis | quotas.issue_and_accept_commission(floating_ip) |
130 | 3c96580c | Christos Stavrakakis | transaction.commit() |
131 | 3c96580c | Christos Stavrakakis | |
132 | 3c96580c | Christos Stavrakakis | log.info("Created floating IP '%s' for user IP '%s'", floating_ip, userid)
|
133 | 3c96580c | Christos Stavrakakis | |
134 | 3c96580c | Christos Stavrakakis | return floating_ip
|
135 | 3c96580c | Christos Stavrakakis | |
136 | 3c96580c | Christos Stavrakakis | |
137 | 3aecadc8 | Christos Stavrakakis | def get_free_floating_ip(userid, network=None): |
138 | 3aecadc8 | Christos Stavrakakis | """Get one of the free available floating IPs of the user.
|
139 | 3aecadc8 | Christos Stavrakakis |
|
140 | 3aecadc8 | Christos Stavrakakis | Get one of the users floating IPs that is not connected to any port
|
141 | 3aecadc8 | Christos Stavrakakis | or server. If network is specified, the floating IP must be from
|
142 | 3aecadc8 | Christos Stavrakakis | that network.
|
143 | 3aecadc8 | Christos Stavrakakis |
|
144 | 3aecadc8 | Christos Stavrakakis | """
|
145 | 3aecadc8 | Christos Stavrakakis | floating_ips = IPAddress.objects\ |
146 | 3aecadc8 | Christos Stavrakakis | .filter(userid=userid, deleted=False, nic=None) |
147 | 3aecadc8 | Christos Stavrakakis | if network is not None: |
148 | 3aecadc8 | Christos Stavrakakis | floating_ips = floating_ips.filter(network=network) |
149 | 3aecadc8 | Christos Stavrakakis | |
150 | 3aecadc8 | Christos Stavrakakis | for floating_ip in floating_ips: |
151 | 3aecadc8 | Christos Stavrakakis | floating_ip = IPAddress.objects.select_for_update()\ |
152 | 3aecadc8 | Christos Stavrakakis | .get(id=floating_ip.id) |
153 | 3aecadc8 | Christos Stavrakakis | if floating_ip.nic is None: |
154 | 3aecadc8 | Christos Stavrakakis | return floating_ip
|
155 | 3aecadc8 | Christos Stavrakakis | |
156 | 3aecadc8 | Christos Stavrakakis | msg = "Cannot allocate a floating IP for connecting new server to"
|
157 | 3aecadc8 | Christos Stavrakakis | if network is not None: |
158 | 3aecadc8 | Christos Stavrakakis | msg += " network '%s'." % network.id
|
159 | 3aecadc8 | Christos Stavrakakis | else:
|
160 | 3aecadc8 | Christos Stavrakakis | msg += " a public network."
|
161 | 3aecadc8 | Christos Stavrakakis | msg += " Please create more floating IPs."
|
162 | 3aecadc8 | Christos Stavrakakis | raise faults.Conflict(msg)
|
163 | 3aecadc8 | Christos Stavrakakis | |
164 | 3aecadc8 | Christos Stavrakakis | |
165 | 3c96580c | Christos Stavrakakis | @transaction.commit_on_success
|
166 | 3c96580c | Christos Stavrakakis | def delete_floating_ip(floating_ip): |
167 | 3c96580c | Christos Stavrakakis | if floating_ip.nic:
|
168 | 3c96580c | Christos Stavrakakis | # This is safe, you also need for_update to attach floating IP to
|
169 | 3c96580c | Christos Stavrakakis | # instance.
|
170 | 3c96580c | Christos Stavrakakis | msg = "Floating IP '%s' is attached to instance." % floating_ip.id
|
171 | 3c96580c | Christos Stavrakakis | raise faults.Conflict(msg)
|
172 | 3c96580c | Christos Stavrakakis | |
173 | 3c96580c | Christos Stavrakakis | # Return the address of the floating IP back to pool
|
174 | 3c96580c | Christos Stavrakakis | floating_ip.release_address() |
175 | 3c96580c | Christos Stavrakakis | # And mark the floating IP as deleted
|
176 | 3c96580c | Christos Stavrakakis | floating_ip.deleted = True
|
177 | 3c96580c | Christos Stavrakakis | floating_ip.save() |
178 | 3c96580c | Christos Stavrakakis | # Release quota for floating IP
|
179 | 3c96580c | Christos Stavrakakis | quotas.issue_and_accept_commission(floating_ip, delete=True)
|
180 | 3c96580c | Christos Stavrakakis | transaction.commit() |
181 | 3c96580c | Christos Stavrakakis | # Delete the floating IP from DB
|
182 | 3c96580c | Christos Stavrakakis | log.info("Deleted floating IP '%s' of user '%s", floating_ip,
|
183 | 3c96580c | Christos Stavrakakis | floating_ip.userid) |
184 | 3c96580c | Christos Stavrakakis | floating_ip.delete() |