1
# Copyright (C) 2015 Midokura SARL.
4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
5
# not use this file except in compliance with the License. You may obtain
6
# a copy of the License at
8
# http://www.apache.org/licenses/LICENSE-2.0
10
# Unless required by applicable law or agreed to in writing, software
11
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
# License for the specific language governing permissions and limitations
19
from midonet.neutron.tests.unit import test_midonet_plugin_v2 as test_mn
21
from neutron.plugins.common import constants
22
from neutron.tests.unit.api import test_extensions as test_ex
23
from neutron.tests.unit.extensions import test_l3
24
from neutron_lbaas.extensions import loadbalancer
25
from neutron_lbaas.tests.unit.db.loadbalancer import test_db_loadbalancer
26
from oslo_config import cfg
27
from oslo_utils import uuidutils
29
MN_DRIVER_KLASS = ('midonet.neutron.services.loadbalancer.driver.'
30
'MidonetLoadbalancerDriver')
33
class LoadbalancerTestExtensionManager(test_l3.L3TestExtensionManager):
35
def get_resources(self):
36
res = super(LoadbalancerTestExtensionManager, self).get_resources()
37
return res + loadbalancer.Loadbalancer.get_resources()
39
def get_actions(self):
42
def get_request_extensions(self):
46
class LoadbalancerTestCase(test_db_loadbalancer.LoadBalancerTestMixin,
47
test_l3.L3NatTestCaseMixin,
48
test_mn.MidonetPluginV2TestCase):
52
'lb_plugin_name': test_db_loadbalancer.DB_LB_PLUGIN_KLASS}
53
lbaas_provider = (constants.LOADBALANCER + ':lbaas:' +
54
MN_DRIVER_KLASS + ':default')
55
ext_mgr = LoadbalancerTestExtensionManager()
56
cfg.CONF.set_override('service_provider',
60
super(LoadbalancerTestCase, self).setUp(
61
service_plugins=service_plugins, ext_mgr=ext_mgr)
62
self.ext_api = test_ex.setup_extensions_middleware(ext_mgr)
64
# Subnet and router must always exist and associated
65
network = self._make_network(self.fmt, 'net1', True)
66
self._subnet = self._make_subnet(self.fmt, network, "10.0.0.1",
68
self._subnet_id = self._subnet['subnet']['id']
69
router = self._make_router(self.fmt, self._tenant_id, 'router1', True)
70
self._router_id = router['router']['id']
71
self._router_interface_action('add', self._router_id, self._subnet_id,
74
# Also prepare external network and subnet which are needed for VIP
75
ext_network = self._make_network(self.fmt, 'ext_net1', True)
76
self._set_net_external(ext_network['network']['id'])
77
self._ext_subnet = self._make_subnet(self.fmt, ext_network,
78
"200.0.0.1", '200.0.0.0/24')
80
# Router must have gateway set for VIP - Pool association
81
self._add_external_gateway_to_router(self._router_id,
82
ext_network['network']['id'])
84
# Override the default subnet ID used in the upstream load balancer
85
# tests so that the midonet-specific tests use the specific subnet
86
# created in the setup
87
test_db_loadbalancer._subnet_id = self._subnet_id
90
super(LoadbalancerTestCase, self).tearDown()
92
@contextlib.contextmanager
93
def subnet_with_router(self, cidr='10.0.1.0/24'):
94
with self.subnet(cidr=cidr) as sub:
95
subnet = sub['subnet']
96
self._router_interface_action('add', self._router_id,
100
@contextlib.contextmanager
101
def pool_with_hm_associated(self, subnet_id=None, do_delete=True):
102
with self.health_monitor(do_delete=do_delete) as hm:
103
with self.pool(subnet_id=subnet_id, do_delete=do_delete) as pool:
104
# Associate the health_monitor to the pool
105
assoc = {"health_monitor": {
106
"id": hm['health_monitor']['id'],
107
'tenant_id': self._tenant_id}}
108
req = self.new_create_request("pools",
111
id=pool['pool']['id'],
112
subresource="health_monitors")
113
res = req.get_response(self.ext_api)
114
self.assertEqual(res.status_int, exc.HTTPCreated.code)
116
# Due to the policy check, the returned response gets the
117
# associated health monitor IDs stripped and an empty list is
118
# returned. So verify the association by doing a separate
120
req = self.new_show_request('pools',
123
p = self.deserialize(self.fmt, req.get_response(self.ext_api))
127
def _test_create_vip(self, name="VIP", pool_id=None, protocol='HTTP',
128
port_number=80, admin_state_up=True,
130
expected_res_status=exc.HTTPCreated.code):
131
if subnet_id is None:
132
subnet_id = self._ext_subnet['subnet']['id']
133
self._create_vip(self.fmt, name, pool_id, protocol, port_number,
134
admin_state_up, subnet_id=subnet_id,
135
expected_res_status=expected_res_status)
137
def _test_vip_status(self, vip_id, status):
138
req = self.new_show_request('vips', vip_id)
139
resp = req.get_response(self.ext_api)
140
self.assertEqual(exc.HTTPOk.code, resp.status_int)
141
vip = self.deserialize(self.fmt, resp)
142
self.assertEqual(status, vip['vip']['status'])
144
def _test_create_pool(self, name="pool", lb_method='ROUND_ROBIN',
145
protocol='TCP', admin_state_up=True,
146
subnet_id=test_db_loadbalancer._subnet_id,
147
expected_res_status=exc.HTTPOk.code):
148
self._create_pool(self.fmt, name, lb_method, protocol, admin_state_up,
150
expected_res_status=expected_res_status)
152
def test_create_pool(self):
154
keys = [('name', name),
155
('subnet_id', test_db_loadbalancer._subnet_id),
156
('tenant_id', self._tenant_id),
157
('protocol', 'HTTP'),
158
('lb_method', 'ROUND_ROBIN'),
159
('admin_state_up', True),
160
('status', 'ACTIVE')]
161
with self.pool(name=name) as pool:
163
self.assertEqual(pool['pool'][k], v)
165
def test_create_pool_with_bad_subnet(self):
166
# Subnet does not exist so it should throw an error
167
self._create_pool(self.fmt, 'pool1', 'ROUND_ROBIN', 'TCP', True,
168
expected_res_status=exc.HTTPNotFound.code,
169
subnet_id=uuidutils.generate_uuid())
171
def test_create_pool_with_external_subnet(self):
172
# Subnet is on an external network, which results in an error
173
self._test_create_pool(subnet_id=self._ext_subnet['subnet']['id'],
174
expected_res_status=exc.HTTPBadRequest.code)
176
def test_create_pool_with_no_router(self):
177
# Subnet with no router association should throw an exception
178
with self.subnet() as sub:
179
self._test_create_pool(subnet_id=sub['subnet']['id'],
180
expected_res_status=exc.HTTPBadRequest.code)
182
def test_delete_pool(self):
183
with self.pool(do_delete=False) as pool:
184
req = self.new_delete_request('pools', pool['pool']['id'])
185
res = req.get_response(self.ext_api)
186
self.assertEqual(res.status_int, exc.HTTPNoContent.code)
188
def test_show_pool(self):
190
keys = [('name', name),
191
('subnet_id', test_db_loadbalancer._subnet_id),
192
('tenant_id', self._tenant_id),
193
('protocol', 'HTTP'),
194
('lb_method', 'ROUND_ROBIN'),
195
('admin_state_up', True),
196
('status', 'ACTIVE')]
197
with self.pool(name=name) as pool:
198
req = self.new_show_request('pools',
201
res = self.deserialize(self.fmt, req.get_response(self.ext_api))
203
self.assertEqual(res['pool'][k], v)
205
def test_update_pool(self):
206
keys = [('name', 'new_name'),
207
('admin_state_up', False)]
208
with self.pool() as pool:
209
req = self.new_update_request('pools',
212
'admin_state_up': False}},
214
res = self.deserialize(self.fmt, req.get_response(self.ext_api))
216
self.assertEqual(res['pool'][k], v)
218
def test_create_vip(self):
219
keys = [('name', 'vip1'),
220
('subnet_id', self._ext_subnet['subnet']['id']),
221
('tenant_id', self._tenant_id),
222
('protocol', 'HTTP'),
223
('protocol_port', 80),
224
('admin_state_up', True)]
225
with self.pool() as pool:
226
with self.vip(pool=pool, subnet=self._ext_subnet) as vip:
228
self.assertEqual(vip['vip'][k], v)
230
def test_create_vip_with_bad_subnet(self):
231
# Subnet does not exist so it should throw an error
232
with self.pool() as pool:
233
self._test_create_vip(pool_id=pool['pool']['id'],
234
subnet_id=uuidutils.generate_uuid(),
235
expected_res_status=exc.HTTPNotFound.code)
237
def test_create_vip_same_subnet_as_pool_with_hm(self):
238
# VIP and pool subnets cannot be the same if HM is associated
239
with self.pool_with_hm_associated() as (pool, hm):
240
self._test_create_vip(pool_id=pool['pool']['id'],
241
subnet_id=pool['pool']['subnet_id'],
242
expected_res_status=exc.HTTPBadRequest.code)
244
def test_create_vip_same_subnet_as_pool_with_no_hm(self):
245
# VIP and pool subnets can be the same if HM is not associated
246
with self.pool(do_delete=False) as pool:
247
self._test_create_vip(pool_id=pool['pool']['id'],
248
subnet_id=pool['pool']['subnet_id'])
250
def test_create_vip_with_bad_pool(self):
251
# Non-existent pool results in an error
252
self._test_create_vip(pool_id=uuidutils.generate_uuid(),
253
expected_res_status=exc.HTTPNotFound.code)
255
def test_create_vip_with_ext_subnet_and_pool_with_no_gw(self):
256
# Pool associated with vip must be attached to a router that has gw
257
self._remove_external_gateway_from_router(
258
self._router_id, self._ext_subnet['subnet']['network_id'])
259
with self.pool() as pool:
260
self._test_create_vip(pool_id=pool['pool']['id'],
261
subnet_id=self._ext_subnet['subnet']['id'],
262
expected_res_status=exc.HTTPBadRequest.code)
264
def test_update_vip(self):
265
keys = [('name', 'new_name'),
266
('subnet_id', self._ext_subnet['subnet']['id']),
267
('tenant_id', self._tenant_id),
268
('protocol', 'HTTP'),
269
('protocol_port', 80),
270
('admin_state_up', False)]
271
with self.pool() as pool:
272
with self.vip(pool=pool, subnet=self._ext_subnet) as vip:
273
req = self.new_update_request('vips',
276
'admin_state_up': False}},
278
res = self.deserialize(
279
self.fmt, req.get_response(self.ext_api))
281
self.assertEqual(res['vip'][k], v)
283
def test_update_vip_same_subnet_as_pool_with_hm(self):
284
# Updating the pool Id to a pool with the same subnet as the VIP
285
# when HM is associated with the pool results in a validation error
286
with self.subnet_with_router() as sub:
287
subnet = sub['subnet']
288
with self.pool() as pool1:
289
with self.vip(pool=pool1, subnet=sub) as vip:
290
with self.pool_with_hm_associated(
292
subnet_id=subnet['id']) as (pool2, hm):
293
req = self.new_update_request(
294
'vips', {'vip': {'pool_id': pool2['pool']['id']}},
296
res = req.get_response(self.ext_api)
297
self.assertEqual(exc.HTTPBadRequest.code,
299
self._test_vip_status(vip['vip']['id'],
302
def test_update_vip_same_subnet_as_pool_with_no_hm(self):
303
# Updating the pool Id to a pool with the same subnet as the VIP
304
# when HM is NOT associated with the pool is accepted
305
with self.subnet_with_router() as sub:
306
subnet = sub['subnet']
307
with self.pool(subnet_id=subnet['id']) as pool1:
308
with self.pool(subnet_id=self._subnet_id) as pool2:
309
with self.vip(pool=pool1, subnet=self._ext_subnet) as vip:
310
req = self.new_update_request(
311
'vips', {'vip': {'pool_id': pool2['pool']['id']}},
313
res = req.get_response(self.ext_api)
314
self.assertEqual(exc.HTTPOk.code, res.status_int)
315
self._test_vip_status(vip['vip']['id'],
318
def test_create_pool_health_monitor(self):
319
with self.pool_with_hm_associated() as (p, hm):
320
self.assertEqual(len(p['pool']['health_monitors']), 1)
321
self.assertEqual(p['pool']['health_monitors'][0],
322
hm['health_monitor']['id'])
324
def test_create_pool_health_monitor_already_associated(self):
325
# Associating two health monitors to a pool throws an error
326
with self.pool_with_hm_associated() as (p, hm):
327
with self.health_monitor() as hm2:
328
# Associate the second hm
329
assoc2 = {"health_monitor": {
330
"id": hm2['health_monitor']['id'],
331
'tenant_id': self._tenant_id}}
332
req2 = self.new_create_request(
333
"pools", assoc2, fmt=self.fmt, id=p['pool']['id'],
334
subresource="health_monitors")
335
res2 = req2.get_response(self.ext_api)
336
self.assertEqual(res2.status_int, exc.HTTPBadRequest.code)
338
def test_delete_pool_health_monitor(self):
339
with self.pool_with_hm_associated() as (p, hm):
340
req = self.new_delete_request("pools", fmt=self.fmt,
342
sub_id=hm['health_monitor']['id'],
343
subresource="health_monitors")
344
res = req.get_response(self.ext_api)
345
self.assertEqual(res.status_int, exc.HTTPNoContent.code)
347
# Verify that it's gone
348
req = self.new_show_request('pools', p['pool']['id'], fmt=self.fmt)
349
res = self.deserialize(self.fmt, req.get_response(self.ext_api))
350
self.assertEqual(len(res['pool']['health_monitors']), 0)
352
def test_create_pool_health_monitor_with_same_vip_subnet(self):
353
# Associating two health monitors to a pool which is associated
354
# with a vip of the same subnet
355
with self.pool(subnet_id=self._subnet_id) as pool:
356
with self.vip(pool=pool, subnet=self._subnet):
357
with self.health_monitor() as hm:
358
assoc = {"health_monitor": {
359
"id": hm['health_monitor']['id'],
360
'tenant_id': self._tenant_id}}
361
req = self.new_create_request(
362
"pools", assoc, fmt=self.fmt, id=pool['pool']['id'],
363
subresource="health_monitors")
364
res = req.get_response(self.ext_api)
365
self.assertEqual(res.status_int, exc.HTTPBadRequest.code)