Revision fde2c1f7
b/snf-cyclades-app/synnefo/api/floating_ips.py | ||
---|---|---|
40 | 40 |
from snf_django.lib.api import faults, utils |
41 | 41 |
from synnefo.api import util |
42 | 42 |
from synnefo import quotas |
43 |
from synnefo.db.models import Network, IPAddress, NetworkInterface |
|
43 |
from synnefo.db.models import Network, IPAddress |
|
44 |
from synnefo.db import pools |
|
44 | 45 |
|
45 | 46 |
|
46 | 47 |
from logging import getLogger |
... | ... | |
77 | 78 |
|
78 | 79 |
|
79 | 80 |
def ip_to_dict(floating_ip): |
80 |
machine_id = floating_ip.machine_id |
|
81 |
machine_id = None |
|
82 |
if floating_ip.nic is not None: |
|
83 |
machine_id = floating_ip.nic.machine_id |
|
81 | 84 |
return {"fixed_ip": None, |
82 | 85 |
"id": str(floating_ip.id), |
83 | 86 |
"instance_id": str(machine_id) if machine_id else None, |
84 |
"ip": floating_ip.ipv4,
|
|
87 |
"ip": floating_ip.address,
|
|
85 | 88 |
"pool": str(floating_ip.network_id)} |
86 | 89 |
|
87 | 90 |
|
... | ... | |
92 | 95 |
log.debug("list_floating_ips") |
93 | 96 |
|
94 | 97 |
userid = request.user_uniq |
95 |
floating_ips = IPAddress.objects.filter(userid=userid).order_by("id") |
|
98 |
floating_ips = IPAddress.objects.filter(userid=userid, deleted=False, |
|
99 |
floating_ip=True).order_by("id")\ |
|
100 |
.select_related("nic") |
|
96 | 101 |
floating_ips = utils.filter_modified_since(request, objects=floating_ips) |
97 | 102 |
|
98 | 103 |
floating_ips = map(ip_to_dict, floating_ips) |
... | ... | |
108 | 113 |
def get_floating_ip(request, floating_ip_id): |
109 | 114 |
"""Return information for a floating IP.""" |
110 | 115 |
userid = request.user_uniq |
111 |
try: |
|
112 |
floating_ip = IPAddress.objects.get(id=floating_ip_id, |
|
113 |
deleted=False, |
|
114 |
userid=userid) |
|
115 |
except IPAddress.DoesNotExist: |
|
116 |
raise faults.ItemNotFound("Floating IP '%s' does not exist" % |
|
117 |
floating_ip_id) |
|
116 |
floating_ip = util.get_floating_ip_by_id(userid, floating_ip_id) |
|
118 | 117 |
request.serialization = "json" |
119 | 118 |
data = json.dumps({"floating_ip": ip_to_dict(floating_ip)}) |
120 | 119 |
return HttpResponse(data, status=200) |
... | ... | |
122 | 121 |
|
123 | 122 |
@api.api_method(http_method='POST', user_required=True, logger=log, |
124 | 123 |
serializations=["json"]) |
125 |
@transaction.commit_manually
|
|
124 |
@transaction.commit_on_success
|
|
126 | 125 |
def allocate_floating_ip(request): |
127 | 126 |
"""Allocate a floating IP.""" |
128 | 127 |
req = utils.get_request_dict(request) |
... | ... | |
131 | 130 |
userid = request.user_uniq |
132 | 131 |
pool = req.get("pool", None) |
133 | 132 |
address = req.get("address", None) |
134 |
machine = None |
|
135 |
net_objects = Network.objects.select_for_update()\ |
|
136 |
.filter(public=True, floating_ip_pool=True, |
|
137 |
deleted=False) |
|
138 |
try: |
|
139 |
if pool is None: |
|
140 |
# User did not specified a pool. Choose a random public IP |
|
141 |
network, address = util.get_free_ip(net_objects) |
|
142 |
else: |
|
143 |
try: |
|
144 |
network_id = int(pool) |
|
145 |
except ValueError: |
|
146 |
raise faults.BadRequest("Invalid pool ID.") |
|
147 |
network = next((n for n in net_objects if n.id == network_id), |
|
148 |
None) |
|
149 |
if network is None: |
|
150 |
raise faults.ItemNotFound("Pool '%s' does not exist." % pool) |
|
151 |
if address is None: |
|
152 |
# User did not specified an IP address. Choose a random one |
|
153 |
# Gets X-Lock on IP pool |
|
154 |
address = util.get_network_free_address(network) |
|
155 |
else: |
|
156 |
# User specified an IP address. Check that it is not a used |
|
157 |
# floating IP |
|
158 |
if IPAddress.objects.filter(network=network, |
|
159 |
deleted=False, |
|
160 |
ipv4=address).exists(): |
|
161 |
msg = "Floating IP '%s' is reserved" % address |
|
162 |
raise faults.Conflict(msg) |
|
163 |
pool = network.get_pool() # Gets X-Lock |
|
164 |
# Check address belongs to pool |
|
165 |
if not pool.contains(address): |
|
166 |
raise faults.BadRequest("Invalid address") |
|
167 |
if pool.is_available(address): |
|
168 |
pool.reserve(address) |
|
169 |
pool.save() |
|
170 |
# If address is not available, check that it belongs to the |
|
171 |
# same user |
|
172 |
else: |
|
173 |
try: |
|
174 |
nic = network.nics.get(ipv4=address, |
|
175 |
machine__userid=userid) |
|
176 |
nic.ip_type = "FLOATING" |
|
177 |
nic.save() |
|
178 |
except NetworkInterface.DoesNotExist: |
|
179 |
msg = "Address '%s' is already in use" % address |
|
180 |
raise faults.Conflict(msg) |
|
181 |
floating_ip = IPAddress.objects.create(ipv4=address, network=network, |
|
182 |
userid=userid, machine=machine) |
|
183 |
quotas.issue_and_accept_commission(floating_ip) |
|
184 |
except: |
|
185 |
transaction.rollback() |
|
186 |
raise |
|
187 |
else: |
|
188 |
transaction.commit() |
|
189 | 133 |
|
190 |
log.info("User '%s' allocated floating IP '%s", userid, floating_ip) |
|
134 |
if pool is None: |
|
135 |
# User did not specified a pool. Choose a random public IP |
|
136 |
try: |
|
137 |
floating_ip = util.allocate_public_ip(userid=userid, |
|
138 |
floating_ip=True) |
|
139 |
except pools.EmptyPool: |
|
140 |
raise faults.Conflict("No more IP addresses available.") |
|
141 |
else: |
|
142 |
try: |
|
143 |
network_id = int(pool) |
|
144 |
except ValueError: |
|
145 |
raise faults.BadRequest("Invalid pool ID.") |
|
146 |
network = util.get_network(network_id, userid, for_update=True, |
|
147 |
non_deleted=True) |
|
148 |
if not network.floating_ip_pool: |
|
149 |
# Check that it is a floating IP pool |
|
150 |
raise faults.ItemNotFound("Floating IP pool %s does not exist." % |
|
151 |
network_id) |
|
152 |
floating_ip = util.allocate_ip(network, userid, address=address, |
|
153 |
floating_ip=True) |
|
154 |
|
|
155 |
quotas.issue_and_accept_commission(floating_ip) |
|
156 |
transaction.commit() |
|
157 |
|
|
158 |
log.info("User '%s' allocated floating IP '%s'", userid, floating_ip) |
|
191 | 159 |
|
192 | 160 |
request.serialization = "json" |
193 | 161 |
data = json.dumps({"floating_ip": ip_to_dict(floating_ip)}) |
... | ... | |
201 | 169 |
"""Release a floating IP.""" |
202 | 170 |
userid = request.user_uniq |
203 | 171 |
log.info("release_floating_ip '%s'. User '%s'.", floating_ip_id, userid) |
204 |
try: |
|
205 |
floating_ip = IPAddress.objects.select_for_update()\ |
|
206 |
.get(id=floating_ip_id, |
|
207 |
deleted=False, |
|
208 |
userid=userid) |
|
209 |
except IPAddress.DoesNotExist: |
|
210 |
raise faults.ItemNotFound("Floating IP '%s' does not exist" % |
|
211 |
floating_ip_id) |
|
212 |
|
|
213 |
# Since we have got an exlusively lock in floating IP, and since |
|
214 |
# to remove a floating IP you need the same lock, the in_use() query |
|
215 |
# is safe |
|
216 |
if floating_ip.in_use(): |
|
217 |
msg = "Floating IP '%s' is used" % floating_ip.id |
|
218 |
raise faults.Conflict(message=msg) |
|
219 |
|
|
220 |
try: |
|
221 |
floating_ip.network.release_address(floating_ip.ipv4) |
|
222 |
floating_ip.deleted = True |
|
223 |
quotas.issue_and_accept_commission(floating_ip, delete=True) |
|
224 |
except: |
|
225 |
transaction.rollback() |
|
226 |
raise |
|
227 |
else: |
|
228 |
floating_ip.delete() |
|
229 |
transaction.commit() |
|
172 |
|
|
173 |
floating_ip = util.get_floating_ip_by_id(userid, floating_ip_id, |
|
174 |
for_update=True) |
|
175 |
if floating_ip.nic: |
|
176 |
# This is safe, you also need for_update to attach floating IP to |
|
177 |
# instance. |
|
178 |
msg = "Floating IP '%s' is attached to instance." % floating_ip.id |
|
179 |
raise faults.Conflict(msg) |
|
180 |
|
|
181 |
# Return the address of the floating IP back to pool |
|
182 |
floating_ip.release_address() |
|
183 |
# And mark the floating IP as deleted |
|
184 |
floating_ip.deleted = True |
|
185 |
floating_ip.save() |
|
186 |
# Release quota for floating IP |
|
187 |
quotas.issue_and_accept_commission(floating_ip, delete=True) |
|
188 |
transaction.commit() |
|
189 |
# Delete the floating IP from DB |
|
190 |
floating_ip.delete() |
|
230 | 191 |
|
231 | 192 |
log.info("User '%s' released IP '%s", userid, floating_ip) |
232 | 193 |
|
233 | 194 |
return HttpResponse(status=204) |
234 | 195 |
|
235 | 196 |
|
236 |
def network_to_pool(network): |
|
237 |
pool = network.get_pool(locked=False) |
|
238 |
return {"name": str(network.id), |
|
239 |
"size": pool.pool_size, |
|
240 |
"free": pool.count_available()} |
|
241 |
|
|
242 |
|
|
197 |
# Floating IP pools |
|
243 | 198 |
@api.api_method(http_method='GET', user_required=True, logger=log, |
244 | 199 |
serializations=["json"]) |
245 | 200 |
def list_floating_ip_pools(request): |
246 |
networks = Network.objects.filter(public=True, floating_ip_pool=True) |
|
201 |
networks = Network.objects.filter(public=True, floating_ip_pool=True, |
|
202 |
deleted=False) |
|
247 | 203 |
networks = utils.filter_modified_since(request, objects=networks) |
248 |
pools = map(network_to_pool, networks) |
|
204 |
pools = map(network_to_floating_ip_pool, networks)
|
|
249 | 205 |
request.serialization = "json" |
250 | 206 |
data = json.dumps({"floating_ip_pools": pools}) |
251 | 207 |
request.serialization = "json" |
252 | 208 |
return HttpResponse(data, status=200) |
209 |
|
|
210 |
|
|
211 |
def network_to_floating_ip_pool(network): |
|
212 |
"""Convert a 'Network' object to a floating IP pool dict.""" |
|
213 |
total, free = network.ip_count() |
|
214 |
return {"name": str(network.id), |
|
215 |
"size": total, |
|
216 |
"free": free} |
b/snf-cyclades-app/synnefo/api/tests/__init__.py | ||
---|---|---|
40 | 40 |
from .versions import * |
41 | 41 |
from .extensions import * |
42 | 42 |
from .subnets import * |
43 |
#from .floating_ips import * |
|
43 |
from .floating_ips import * |
b/snf-cyclades-app/synnefo/api/tests/floating_ips.py | ||
---|---|---|
35 | 35 |
|
36 | 36 |
from snf_django.utils.testing import BaseAPITest, mocked_quotaholder |
37 | 37 |
from synnefo.db.models import IPAddress |
38 |
from synnefo.db.models_factory import (FloatingIPFactory, NetworkFactory, |
|
39 |
VirtualMachineFactory, |
|
40 |
NetworkInterfaceFactory, |
|
41 |
BackendNetworkFactory) |
|
38 |
from synnefo.db import models_factory as mf |
|
39 |
from synnefo.db.models_factory import (NetworkFactory, |
|
40 |
VirtualMachineFactory) |
|
42 | 41 |
from mock import patch, Mock |
43 | 42 |
from functools import partial |
44 | 43 |
|
... | ... | |
59 | 58 |
|
60 | 59 |
|
61 | 60 |
class FloatingIPAPITest(BaseAPITest): |
61 |
def setUp(self): |
|
62 |
self.pool = mf.NetworkWithSubnetFactory(floating_ip_pool=True, |
|
63 |
public=True, |
|
64 |
subnet__cidr="192.168.2.0/24", |
|
65 |
subnet__gateway="192.168.2.1") |
|
66 |
|
|
62 | 67 |
def test_no_floating_ip(self): |
63 | 68 |
response = self.get(URL) |
64 | 69 |
self.assertSuccess(response) |
65 | 70 |
self.assertEqual(json.loads(response.content)["floating_ips"], []) |
66 | 71 |
|
67 | 72 |
def test_list_ips(self): |
68 |
ip = FloatingIPFactory(userid="user1") |
|
69 |
FloatingIPFactory(userid="user1", deleted=True) |
|
73 |
ip = mf.IPv4AddressFactory(userid="user1", floating_ip=True) |
|
70 | 74 |
with mocked_quotaholder(): |
71 | 75 |
response = self.get(URL, "user1") |
72 | 76 |
self.assertSuccess(response) |
73 | 77 |
api_ip = json.loads(response.content)["floating_ips"][0] |
74 | 78 |
self.assertEqual(api_ip, |
75 |
{"instance_id": str(ip.machine.id), "ip": ip.ipv4, |
|
76 |
"fixed_ip": None, "id": str(ip.id), "pool": |
|
77 |
str(ip.network.id)}) |
|
79 |
{"instance_id": str(ip.nic.machine_id), |
|
80 |
"ip": ip.address, |
|
81 |
"fixed_ip": None, |
|
82 |
"id": str(ip.id), |
|
83 |
"pool": str(ip.network_id)}) |
|
78 | 84 |
|
79 | 85 |
def test_get_ip(self): |
80 |
ip = FloatingIPFactory(userid="user1")
|
|
86 |
ip = mf.IPv4AddressFactory(userid="user1", floating_ip=True)
|
|
81 | 87 |
with mocked_quotaholder(): |
82 | 88 |
response = self.get(URL + "/%s" % ip.id, "user1") |
83 | 89 |
self.assertSuccess(response) |
84 | 90 |
api_ip = json.loads(response.content)["floating_ip"] |
85 | 91 |
self.assertEqual(api_ip, |
86 |
{"instance_id": str(ip.machine.id), "ip": ip.ipv4, |
|
87 |
"fixed_ip": None, "id": str(ip.id), "pool": |
|
88 |
str(ip.network.id)}) |
|
92 |
{"instance_id": str(ip.nic.machine_id), |
|
93 |
"ip": ip.address, |
|
94 |
"fixed_ip": None, |
|
95 |
"id": str(ip.id), |
|
96 |
"pool": str(ip.network_id)}) |
|
89 | 97 |
|
90 | 98 |
def test_wrong_user(self): |
91 |
ip = FloatingIPFactory(userid="user1") |
|
92 |
with mocked_quotaholder(): |
|
93 |
response = self.delete(URL + "/%s" % ip.id, "user2") |
|
99 |
ip = mf.IPv4AddressFactory(userid="user1", floating_ip=True) |
|
100 |
response = self.delete(URL + "/%s" % ip.id, "user2") |
|
94 | 101 |
self.assertItemNotFound(response) |
95 | 102 |
|
96 | 103 |
def test_deleted_ip(self): |
97 |
ip = FloatingIPFactory(userid="user1", deleted=True)
|
|
98 |
with mocked_quotaholder():
|
|
99 |
response = self.delete(URL + "/%s" % ip.id, "user1")
|
|
104 |
ip = mf.IPv4AddressFactory(userid="user1", floating_ip=True,
|
|
105 |
deleted=True)
|
|
106 |
response = self.delete(URL + "/%s" % ip.id, "user1") |
|
100 | 107 |
self.assertItemNotFound(response) |
101 | 108 |
|
102 | 109 |
def test_reserve(self): |
103 |
net = FloatingIPPoolFactory(userid="test_user", |
|
104 |
subnet="192.168.2.0/24", |
|
105 |
gateway=None) |
|
106 |
request = {'pool': net.id} |
|
110 |
request = {'pool': self.pool.id} |
|
107 | 111 |
with mocked_quotaholder(): |
108 | 112 |
response = self.post(URL, "test_user", json.dumps(request), "json") |
109 | 113 |
self.assertSuccess(response) |
110 | 114 |
ip = floating_ips.get() |
111 |
self.assertEqual(ip.ipv4, "192.168.2.1")
|
|
112 |
self.assertEqual(ip.machine, None)
|
|
113 |
self.assertEqual(ip.network, net)
|
|
115 |
self.assertEqual(ip.address, "192.168.2.2")
|
|
116 |
self.assertEqual(ip.nic, None)
|
|
117 |
self.assertEqual(ip.network, self.pool)
|
|
114 | 118 |
self.assertEqual(json.loads(response.content)["floating_ip"], |
115 |
{"instance_id": None, "ip": "192.168.2.1",
|
|
119 |
{"instance_id": None, "ip": "192.168.2.2",
|
|
116 | 120 |
"fixed_ip": None, "id": str(ip.id), |
117 |
"pool": str(net.id)})
|
|
121 |
"pool": str(self.pool.id)})
|
|
118 | 122 |
|
119 | 123 |
def test_reserve_no_pool(self): |
120 |
# No networks |
|
121 |
with mocked_quotaholder(): |
|
122 |
response = self.post(URL, "test_user", json.dumps({}), "json") |
|
123 |
self.assertFault(response, 413, "overLimit") |
|
124 |
# No floating IP pools |
|
125 |
self.pool.delete() |
|
126 |
response = self.post(URL, "test_user", json.dumps({}), "json") |
|
127 |
self.assertFault(response, 503, 'serviceUnavailable') |
|
128 |
|
|
124 | 129 |
# Full network |
125 |
FloatingIPPoolFactory(userid="test_user", |
|
126 |
subnet="192.168.2.0/32", |
|
127 |
gateway=None) |
|
128 |
with mocked_quotaholder(): |
|
129 |
response = self.post(URL, "test_user", json.dumps({}), "json") |
|
130 |
self.assertFault(response, 413, "overLimit") |
|
131 |
# Success |
|
132 |
net2 = FloatingIPPoolFactory(userid="test_user", |
|
133 |
subnet="192.168.2.0/24", |
|
134 |
gateway=None) |
|
135 |
with mocked_quotaholder(): |
|
136 |
response = self.post(URL, "test_user", json.dumps({}), "json") |
|
137 |
self.assertSuccess(response) |
|
138 |
ip = floating_ips.get() |
|
139 |
self.assertEqual(json.loads(response.content)["floating_ip"], |
|
140 |
{"instance_id": None, "ip": "192.168.2.1", |
|
141 |
"fixed_ip": None, "id": str(ip.id), |
|
142 |
"pool": str(net2.id)}) |
|
130 |
pool = mf.NetworkWithSubnetFactory(floating_ip_pool=True, |
|
131 |
public=True, |
|
132 |
subnet__cidr="192.168.2.0/31", |
|
133 |
subnet__gateway="192.168.2.1") |
|
134 |
response = self.post(URL, "test_user", json.dumps({}), "json") |
|
135 |
self.assertFault(response, 503, 'serviceUnavailable') |
|
143 | 136 |
|
144 |
def test_reserve_full(self): |
|
145 |
net = FloatingIPPoolFactory(userid="test_user", |
|
146 |
subnet="192.168.2.0/32") |
|
147 |
request = {'pool': net.id} |
|
148 |
with mocked_quotaholder(): |
|
149 |
response = self.post(URL, "test_user", json.dumps(request), "json") |
|
150 |
self.assertEqual(response.status_code, 413) |
|
137 |
request = {'pool': pool.id} |
|
138 |
response = self.post(URL, "test_user", json.dumps(request), "json") |
|
139 |
self.assertConflict(response) |
|
151 | 140 |
|
152 | 141 |
def test_reserve_with_address(self): |
153 |
net = FloatingIPPoolFactory(userid="test_user", |
|
154 |
subnet="192.168.2.0/24") |
|
155 |
request = {'pool': net.id, "address": "192.168.2.10"} |
|
142 |
request = {'pool': self.pool.id, "address": "192.168.2.10"} |
|
156 | 143 |
with mocked_quotaholder(): |
157 | 144 |
response = self.post(URL, "test_user", json.dumps(request), "json") |
158 | 145 |
self.assertSuccess(response) |
159 | 146 |
ip = floating_ips.get() |
160 | 147 |
self.assertEqual(json.loads(response.content)["floating_ip"], |
161 | 148 |
{"instance_id": None, "ip": "192.168.2.10", |
162 |
"fixed_ip": None, "id": str(ip.id), "pool":
|
|
163 |
str(net.id)})
|
|
149 |
"fixed_ip": None, "id": str(ip.id), |
|
150 |
"pool": str(self.pool.id)})
|
|
164 | 151 |
|
165 | 152 |
# Already reserved |
166 |
FloatingIPFactory(network=net, ipv4="192.168.2.3") |
|
167 |
request = {'pool': net.id, "address": "192.168.2.3"} |
|
168 | 153 |
with mocked_quotaholder(): |
169 | 154 |
response = self.post(URL, "test_user", json.dumps(request), "json") |
170 | 155 |
self.assertFault(response, 409, "conflict") |
171 | 156 |
|
172 |
# Already used |
|
173 |
pool = net.get_pool() |
|
174 |
pool.reserve("192.168.2.5") |
|
175 |
pool.save() |
|
176 |
# ..by another_user |
|
177 |
nic = NetworkInterfaceFactory(network=net, ipv4="192.168.2.5", |
|
178 |
machine__userid="test2") |
|
179 |
request = {'pool': net.id, "address": "192.168.2.5"} |
|
157 |
# Used by instance |
|
158 |
self.pool.reserve_address("192.168.2.20") |
|
159 |
request = {'pool': self.pool.id, "address": "192.168.2.20"} |
|
180 | 160 |
with mocked_quotaholder(): |
181 | 161 |
response = self.post(URL, "test_user", json.dumps(request), "json") |
182 | 162 |
self.assertFault(response, 409, "conflict") |
183 |
# ..and by him |
|
184 |
nic.delete() |
|
185 |
NetworkInterfaceFactory(network=net, ipv4="192.168.2.5", |
|
186 |
machine__userid="test_user") |
|
187 |
request = {'pool': net.id, "address": "192.168.2.5"} |
|
188 |
with mocked_quotaholder(): |
|
189 |
response = self.post(URL, "test_user", json.dumps(request), "json") |
|
190 |
self.assertSuccess(response) |
|
191 | 163 |
|
192 | 164 |
# Address out of pool |
193 |
request = {'pool': net.id, "address": "192.168.3.5"}
|
|
165 |
request = {'pool': self.pool.id, "address": "192.168.3.5"}
|
|
194 | 166 |
with mocked_quotaholder(): |
195 | 167 |
response = self.post(URL, "test_user", json.dumps(request), "json") |
196 | 168 |
self.assertBadRequest(response) |
197 | 169 |
|
198 | 170 |
def test_release_in_use(self): |
199 |
ip = FloatingIPFactory() |
|
200 |
vm = ip.machine |
|
201 |
vm.operstate = "ACTIVE" |
|
202 |
vm.userid = ip.userid |
|
203 |
vm.save() |
|
204 |
vm.nics.create(index=0, ipv4=ip.ipv4, network=ip.network, |
|
205 |
state="ACTIVE") |
|
171 |
ip = mf.IPv4AddressFactory(userid="user1", floating_ip=True) |
|
172 |
vm = ip.nic.machine |
|
206 | 173 |
with mocked_quotaholder(): |
207 | 174 |
response = self.delete(URL + "/%s" % ip.id, ip.userid) |
208 | 175 |
self.assertFault(response, 409, "conflict") |
209 | 176 |
# Also send a notification to remove the NIC and assert that FIP is in |
210 | 177 |
# use until notification from ganeti arrives |
211 |
request = {"removeFloatingIp": {"address": ip.ipv4}}
|
|
178 |
request = {"removeFloatingIp": {"address": ip.address}}
|
|
212 | 179 |
url = SERVERS_URL + "/%s/action" % vm.id |
213 | 180 |
with patch('synnefo.logic.rapi_pool.GanetiRapiClient') as c: |
214 | 181 |
c().ModifyInstance.return_value = 10 |
... | ... | |
220 | 187 |
self.assertFault(response, 409, "conflict") |
221 | 188 |
|
222 | 189 |
def test_release(self): |
223 |
ip = FloatingIPFactory(machine=None)
|
|
190 |
ip = mf.IPv4AddressFactory(userid="user1", floating_ip=True, nic=None)
|
|
224 | 191 |
with mocked_quotaholder(): |
225 | 192 |
response = self.delete(URL + "/%s" % ip.id, ip.userid) |
226 | 193 |
self.assertSuccess(response) |
... | ... | |
229 | 196 |
|
230 | 197 |
@patch("synnefo.logic.backend", Mock()) |
231 | 198 |
def test_delete_network_with_floating_ips(self): |
232 |
ip = FloatingIPFactory(machine=None, network__flavor="IP_LESS_ROUTED") |
|
233 |
net = ip.network |
|
199 |
ip = mf.IPv4AddressFactory(userid="user1", floating_ip=True, |
|
200 |
network=self.pool, nic=None) |
|
201 |
# Mark the network as non-pubic to not get 403 |
|
202 |
network = ip.network |
|
203 |
network.public = False |
|
204 |
network.save() |
|
234 | 205 |
# Can not remove network with floating IPs |
235 | 206 |
with mocked_quotaholder(): |
236 |
response = self.delete(NETWORKS_URL + "/%s" % net.id,
|
|
237 |
net.userid)
|
|
238 |
self.assertFault(response, 421, "networkInUse")
|
|
207 |
response = self.delete(NETWORKS_URL + "/%s" % self.pool.id,
|
|
208 |
self.pool.userid)
|
|
209 |
self.assertConflict(response)
|
|
239 | 210 |
# But we can with only deleted Floating Ips |
240 | 211 |
ip.deleted = True |
241 | 212 |
ip.save() |
242 | 213 |
with mocked_quotaholder(): |
243 |
response = self.delete(NETWORKS_URL + "/%s" % net.id,
|
|
244 |
net.userid)
|
|
214 |
response = self.delete(NETWORKS_URL + "/%s" % self.pool.id,
|
|
215 |
self.pool.userid)
|
|
245 | 216 |
self.assertSuccess(response) |
246 | 217 |
|
247 | 218 |
|
... | ... | |
255 | 226 |
self.assertEqual(json.loads(response.content)["floating_ip_pools"], []) |
256 | 227 |
|
257 | 228 |
def test_list_pools(self): |
258 |
net = FloatingIPPoolFactory(subnet="192.168.0.0/30", |
|
259 |
gateway="192.168.0.1") |
|
260 |
NetworkFactory(public=True, deleted=True) |
|
261 |
NetworkFactory(public=False, deleted=False) |
|
262 |
NetworkFactory(public=True, deleted=False) |
|
229 |
net = mf.NetworkWithSubnetFactory(floating_ip_pool=True, |
|
230 |
public=True, |
|
231 |
subnet__cidr="192.168.2.0/30", |
|
232 |
subnet__gateway="192.168.2.1") |
|
233 |
mf.NetworkWithSubnetFactory(public=True, deleted=True) |
|
234 |
mf.NetworkWithSubnetFactory(public=False, deleted=False) |
|
235 |
mf.NetworkWithSubnetFactory(public=True, floating_ip_pool=False) |
|
263 | 236 |
response = self.get(POOLS_URL) |
264 | 237 |
self.assertSuccess(response) |
265 | 238 |
self.assertEqual(json.loads(response.content)["floating_ip_pools"], |
... | ... | |
268 | 241 |
|
269 | 242 |
class FloatingIPActionsTest(BaseAPITest): |
270 | 243 |
def setUp(self): |
271 |
vm = VirtualMachineFactory() |
|
272 |
vm.operstate = "ACTIVE" |
|
273 |
vm.save() |
|
274 |
self.vm = vm |
|
244 |
self.vm = VirtualMachineFactory() |
|
245 |
self.vm.operstate = "ACTIVE" |
|
246 |
self.vm.save() |
|
275 | 247 |
|
276 | 248 |
def test_bad_request(self): |
277 | 249 |
url = SERVERS_URL + "/%s/action" % self.vm.id |
... | ... | |
290 | 262 |
response = self.post(url, self.vm.userid, json.dumps(request), "json") |
291 | 263 |
self.assertItemNotFound(response) |
292 | 264 |
# In use |
293 |
vm1 = VirtualMachineFactory() |
|
294 |
ip1 = FloatingIPFactory(userid=self.vm.userid, machine=vm1) |
|
295 |
BackendNetworkFactory(network=ip1.network, backend=vm1.backend, |
|
296 |
operstate='ACTIVE') |
|
297 |
request = {"addFloatingIp": {"address": ip1.ipv4}} |
|
265 |
ip = mf.IPv4AddressFactory(floating_ip=True, userid=self.vm.userid) |
|
266 |
request = {"addFloatingIp": {"address": ip.address}} |
|
298 | 267 |
response = self.post(url, self.vm.userid, json.dumps(request), "json") |
299 |
self.assertFault(response, 409, "conflict")
|
|
268 |
self.assertConflict(response)
|
|
300 | 269 |
# Success |
301 |
ip1 = FloatingIPFactory(userid=self.vm.userid, machine=None) |
|
302 |
BackendNetworkFactory(network=ip1.network, backend=self.vm.backend, |
|
303 |
operstate='ACTIVE') |
|
304 |
request = {"addFloatingIp": {"address": ip1.ipv4}} |
|
270 |
ip = mf.IPv4AddressFactory(floating_ip=True, nic=None, |
|
271 |
userid=self.vm.userid) |
|
272 |
request = {"addFloatingIp": {"address": ip.address}} |
|
305 | 273 |
mock().ModifyInstance.return_value = 1 |
306 | 274 |
response = self.post(url, self.vm.userid, json.dumps(request), "json") |
307 | 275 |
self.assertEqual(response.status_code, 202) |
308 |
ip1_after = floating_ips.get(id=ip1.id) |
|
309 |
self.assertEqual(ip1_after.machine, self.vm) |
|
310 |
self.assertTrue(ip1_after.in_use()) |
|
311 |
nic = self.vm.nics.get(ipv4=ip1_after.ipv4) |
|
276 |
ip_after = floating_ips.get(id=ip.id) |
|
277 |
self.assertEqual(ip_after.nic.machine, self.vm) |
|
278 |
nic = self.vm.nics.get() |
|
312 | 279 |
nic.state = "ACTIVE" |
313 | 280 |
nic.save() |
314 | 281 |
response = self.get(SERVERS_URL + "/%s" % self.vm.id, |
... | ... | |
323 | 290 |
url = SERVERS_URL + "/%s/action" % self.vm.id |
324 | 291 |
request = {"removeFloatingIp": {"address": "10.0.0.1"}} |
325 | 292 |
response = self.post(url, self.vm.userid, json.dumps(request), "json") |
326 |
self.assertItemNotFound(response)
|
|
293 |
self.assertBadRequest(response)
|
|
327 | 294 |
# Not In Use |
328 |
ip1 = FloatingIPFactory(userid=self.vm.userid, machine=None) |
|
329 |
request = {"removeFloatingIp": {"address": ip1.ipv4}} |
|
295 |
ip = mf.IPv4AddressFactory(floating_ip=True, nic=None, |
|
296 |
userid=self.vm.userid) |
|
297 |
request = {"removeFloatingIp": {"address": ip.address}} |
|
330 | 298 |
response = self.post(url, self.vm.userid, json.dumps(request), "json") |
331 |
self.assertItemNotFound(response)
|
|
299 |
self.assertBadRequest(response)
|
|
332 | 300 |
# Success |
333 |
ip1 = FloatingIPFactory(userid=self.vm.userid, machine=self.vm)
|
|
334 |
NetworkInterfaceFactory(machine=self.vm, ipv4=ip1.ipv4)
|
|
335 |
request = {"removeFloatingIp": {"address": ip1.ipv4}}
|
|
301 |
ip = mf.IPv4AddressFactory(floating_ip=True,
|
|
302 |
userid=self.vm.userid, nic__machine=self.vm)
|
|
303 |
request = {"removeFloatingIp": {"address": ip.address}}
|
|
336 | 304 |
mock().ModifyInstance.return_value = 2 |
337 | 305 |
response = self.post(url, self.vm.userid, json.dumps(request), "json") |
338 | 306 |
self.assertEqual(response.status_code, 202) |
339 | 307 |
# Yet used. Wait for the callbacks |
340 |
ip1_after = floating_ips.get(id=ip1.id) |
|
341 |
self.assertEqual(ip1_after.machine, self.vm) |
|
342 |
self.assertTrue(ip1_after.in_use()) |
|
308 |
ip_after = floating_ips.get(id=ip.id) |
|
309 |
self.assertEqual(ip_after.nic.machine, self.vm) |
b/snf-django-lib/snf_django/utils/testing.py | ||
---|---|---|
237 | 237 |
def assertBadRequest(self, response): |
238 | 238 |
self.assertFault(response, 400, 'badRequest') |
239 | 239 |
|
240 |
def assertConflict(self, response): |
|
241 |
self.assertFault(response, 409, 'conflict') |
|
242 |
|
|
240 | 243 |
def assertItemNotFound(self, response): |
241 | 244 |
self.assertFault(response, 404, 'itemNotFound') |
242 | 245 |
|
Also available in: Unified diff