Revision 9e8be4fb
b/snf-cyclades-app/synnefo/api/floating_ips.py | ||
---|---|---|
126 | 126 |
log.info('allocate_floating_ip %s', req) |
127 | 127 |
|
128 | 128 |
userid = request.user_uniq |
129 |
try: |
|
130 |
pool = req['pool'] |
|
131 |
except KeyError: |
|
132 |
raise faults.BadRequest("Malformed request. Missing" |
|
133 |
" 'pool' attribute") |
|
134 |
|
|
135 |
try: |
|
136 |
objects = Network.objects.select_for_update() |
|
137 |
network = objects.get(id=pool, public=True, deleted=False) |
|
138 |
except Network.DoesNotExist: |
|
139 |
raise faults.ItemNotFound("Pool '%s' does not exist." % pool) |
|
140 |
|
|
129 |
pool = req.get("pool", None) |
|
141 | 130 |
address = req.get("address", None) |
142 | 131 |
machine = None |
132 |
net_objects = Network.objects.select_for_update().filter(public=True, |
|
133 |
deleted=False) |
|
143 | 134 |
try: |
144 |
if address is None: |
|
145 |
address = util.get_network_free_address(network) # Get X-Lock |
|
135 |
if pool is None: |
|
136 |
# User did not specified a pool. Choose a random public IP |
|
137 |
network, address = util.allocate_public_ip(net_objects) |
|
146 | 138 |
else: |
147 |
if FloatingIP.objects.filter(network=network, |
|
148 |
deleted=False, |
|
149 |
ipv4=address).exists(): |
|
150 |
msg = "Floating IP '%s' is reserved" % address |
|
151 |
raise faults.Conflict(msg) |
|
152 |
pool = network.get_pool() # Gets X-Lock |
|
153 |
if not pool.contains(address): |
|
154 |
raise faults.BadRequest("Invalid address") |
|
155 |
if not pool.is_available(address): |
|
156 |
try: |
|
157 |
network.nics.get(ipv4=address, |
|
158 |
machine__userid=userid) |
|
159 |
except NetworkInterface.DoesNotExist: |
|
160 |
msg = "Address '%s' is already in use" % address |
|
139 |
try: |
|
140 |
network = net_objects.get(id=pool) |
|
141 |
except Network.DoesNotExist: |
|
142 |
raise faults.ItemNotFound("Pool '%s' does not exist." % pool) |
|
143 |
if address is None: |
|
144 |
# User did not specified an IP address. Choose a random one |
|
145 |
# Gets X-Lock on IP pool |
|
146 |
address = util.get_network_free_address(network) |
|
147 |
else: |
|
148 |
# User specified an IP address. Check that it is not a used |
|
149 |
# floating IP |
|
150 |
if FloatingIP.objects.filter(network=network, |
|
151 |
deleted=False, |
|
152 |
ipv4=address).exists(): |
|
153 |
msg = "Floating IP '%s' is reserved" % address |
|
161 | 154 |
raise faults.Conflict(msg) |
162 |
pool.reserve(address) |
|
163 |
pool.save() |
|
155 |
pool = network.get_pool() # Gets X-Lock |
|
156 |
# Check address belongs to pool |
|
157 |
if not pool.contains(address): |
|
158 |
raise faults.BadRequest("Invalid address") |
|
159 |
if pool.is_available(address): |
|
160 |
pool.reserve(address) |
|
161 |
pool.save() |
|
162 |
# If address is not available, check that it belongs to the |
|
163 |
# same user |
|
164 |
elif not network.nics.filter(ipv4=address, |
|
165 |
machine__userid=userid).exists(): |
|
166 |
msg = "Address '%s' is already in use" % address |
|
167 |
raise faults.Conflict(msg) |
|
164 | 168 |
floating_ip = FloatingIP.objects.create(ipv4=address, network=network, |
165 | 169 |
userid=userid, machine=machine) |
166 | 170 |
quotas.issue_and_accept_commission(floating_ip) |
b/snf-cyclades-app/synnefo/api/test/floating_ips.py | ||
---|---|---|
37 | 37 |
from synnefo.db.models import FloatingIP |
38 | 38 |
from synnefo.db.models_factory import (FloatingIPFactory, NetworkFactory, |
39 | 39 |
VirtualMachineFactory, |
40 |
NetworkInterfaceFactory) |
|
40 |
NetworkInterfaceFactory, |
|
41 |
BackendNetworkFactory) |
|
41 | 42 |
from mock import patch, Mock |
42 | 43 |
|
43 | 44 |
|
... | ... | |
100 | 101 |
{"instance_id": None, "ip": "192.168.2.1", |
101 | 102 |
"fixed_ip": None, "id": "1", "pool": "1"}) |
102 | 103 |
|
104 |
def test_reserve_no_pool(self): |
|
105 |
# No networks |
|
106 |
with mocked_quotaholder(): |
|
107 |
response = self.post(URL, "test_user", json.dumps({}), "json") |
|
108 |
self.assertFault(response, 413, "overLimit") |
|
109 |
# Full network |
|
110 |
net = NetworkFactory(userid="test_user", subnet="192.168.2.0/32", |
|
111 |
gateway=None, public=True) |
|
112 |
with mocked_quotaholder(): |
|
113 |
response = self.post(URL, "test_user", json.dumps({}), "json") |
|
114 |
self.assertFault(response, 413, "overLimit") |
|
115 |
# Success |
|
116 |
net2 = NetworkFactory(userid="test_user", subnet="192.168.2.0/24", |
|
117 |
gateway=None, public=True) |
|
118 |
with mocked_quotaholder(): |
|
119 |
response = self.post(URL, "test_user", json.dumps({}), "json") |
|
120 |
self.assertEqual(json.loads(response.content)["floating_ip"], |
|
121 |
{"instance_id": None, "ip": "192.168.2.1", |
|
122 |
"fixed_ip": None, "id": "1", "pool": str(net2.id)}) |
|
123 |
|
|
103 | 124 |
def test_reserve_full(self): |
104 | 125 |
net = NetworkFactory(userid="test_user", subnet="192.168.2.0/32", |
105 | 126 |
gateway=None, public=True) |
... | ... | |
186 | 207 |
|
187 | 208 |
@patch("synnefo.logic.backend", Mock()) |
188 | 209 |
def test_delete_network_with_floating_ips(self): |
189 |
ip = FloatingIPFactory(machine=None) |
|
210 |
ip = FloatingIPFactory(machine=None, network__flavor="IP_LESS_ROUTED")
|
|
190 | 211 |
net = ip.network |
191 | 212 |
# Can not remove network with floating IPs |
192 | 213 |
with mocked_quotaholder(): |
... | ... | |
247 | 268 |
# In use |
248 | 269 |
vm1 = VirtualMachineFactory() |
249 | 270 |
ip1 = FloatingIPFactory(userid=self.vm.userid, machine=vm1) |
271 |
BackendNetworkFactory(network=ip1.network, backend=vm1.backend, |
|
272 |
operstate='ACTIVE') |
|
250 | 273 |
request = {"addFloatingIp": {"address": ip1.ipv4}} |
251 | 274 |
response = self.post(url, self.vm.userid, json.dumps(request), "json") |
252 | 275 |
self.assertFault(response, 409, "conflict") |
253 | 276 |
# Success |
254 | 277 |
ip1 = FloatingIPFactory(userid=self.vm.userid, machine=None) |
278 |
BackendNetworkFactory(network=ip1.network, backend=self.vm.backend, |
|
279 |
operstate='ACTIVE') |
|
255 | 280 |
request = {"addFloatingIp": {"address": ip1.ipv4}} |
256 | 281 |
response = self.post(url, self.vm.userid, json.dumps(request), "json") |
257 | 282 |
self.assertEqual(response.status_code, 202) |
b/snf-cyclades-app/synnefo/api/util.py | ||
---|---|---|
213 | 213 |
return disk_template, provider |
214 | 214 |
|
215 | 215 |
|
216 |
def get_network(network_id, user_id, for_update=False): |
|
216 |
def get_network(network_id, user_id, for_update=False, non_deleted=False):
|
|
217 | 217 |
"""Return a Network instance or raise ItemNotFound.""" |
218 | 218 |
|
219 | 219 |
try: |
... | ... | |
221 | 221 |
objects = Network.objects |
222 | 222 |
if for_update: |
223 | 223 |
objects = objects.select_for_update() |
224 |
return objects.get(Q(userid=user_id) | Q(public=True), id=network_id) |
|
224 |
network = objects.get(Q(userid=user_id) | Q(public=True), |
|
225 |
id=network_id) |
|
226 |
if non_deleted and network.deleted: |
|
227 |
raise faults.BadRequest("Networkhas been deleted.") |
|
228 |
return network |
|
225 | 229 |
except (ValueError, Network.DoesNotExist): |
226 | 230 |
raise faults.ItemNotFound('Network not found.') |
227 | 231 |
|
... | ... | |
336 | 340 |
return address |
337 | 341 |
|
338 | 342 |
|
343 |
def allocate_public_ip(networks=None): |
|
344 |
"""Allocate an IP address from public networks.""" |
|
345 |
if networks is None: |
|
346 |
networks = Network.objects.select_for_update().filter(public=True, |
|
347 |
deleted=False) |
|
348 |
for network in networks: |
|
349 |
try: |
|
350 |
address = get_network_free_address(network) |
|
351 |
except: |
|
352 |
pass |
|
353 |
else: |
|
354 |
return network, address |
|
355 |
msg = "Can not allocate public IP. Public networks are full." |
|
356 |
log.error(msg) |
|
357 |
raise faults.OverLimit(msg) |
|
358 |
|
|
359 |
|
|
339 | 360 |
def get_nic(machine, network): |
340 | 361 |
try: |
341 | 362 |
return NetworkInterface.objects.get(machine=machine, network=network) |
Also available in: Unified diff