1
# Copyright (C) 2014 eNovance SAS <licensing@enovance.com>
3
# Licensed under the Apache License, Version 2.0 (the "License"); you may
4
# not use this file except in compliance with the License. You may obtain
5
# a copy of the License at
7
# http://www.apache.org/licenses/LICENSE-2.0
9
# Unless required by applicable law or agreed to in writing, software
10
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12
# License for the specific language governing permissions and limitations
16
from oslo.config import cfg
18
from neutron.common import constants
19
from neutron import context
20
from neutron.db import agents_db
21
from neutron.db import common_db_mixin
22
from neutron.db import l3_hamode_db
23
from neutron.extensions import l3_ext_ha_mode
24
from neutron import manager
25
from neutron.openstack.common import uuidutils
26
from neutron.tests.unit import testlib_api
27
from neutron.tests.unit import testlib_plugin
29
_uuid = uuidutils.generate_uuid
32
class FakeL3Plugin(common_db_mixin.CommonDbMixin,
33
l3_hamode_db.L3_HA_NAT_db_mixin):
37
class FakeL3PluginWithAgents(FakeL3Plugin,
38
agents_db.AgentDbMixin):
42
class L3HATestFramework(testlib_api.SqlTestCase,
43
testlib_plugin.PluginSetupHelper):
45
super(L3HATestFramework, self).setUp()
47
self.admin_ctx = context.get_admin_context()
48
self.setup_coreplugin('neutron.plugins.ml2.plugin.Ml2Plugin')
49
self.core_plugin = manager.NeutronManager.get_plugin()
50
mock.patch.object(l3_hamode_db.L3_HA_NAT_db_mixin, 'get_l3_agents',
51
create=True, return_value=[1, 2]).start()
52
notif_p = mock.patch.object(l3_hamode_db.L3_HA_NAT_db_mixin,
53
'_notify_ha_interfaces_updated')
54
self.notif_m = notif_p.start()
55
cfg.CONF.set_override('allow_overlapping_ips', True)
57
def _create_router(self, ha=True, tenant_id='tenant1', distributed=None):
58
router = {'name': 'router1', 'admin_state_up': True}
61
if distributed is not None:
62
router['distributed'] = distributed
63
return self.plugin._create_router_db(self.admin_ctx, router, tenant_id)
65
def _update_router(self, router_id, ha=True, distributed=None):
66
data = {'ha': ha} if ha is not None else {}
67
if distributed is not None:
68
data['distributed'] = distributed
69
return self.plugin._update_router_db(self.admin_ctx, router_id,
73
class L3HAGetSyncDataTestCase(L3HATestFramework):
76
super(L3HAGetSyncDataTestCase, self).setUp()
77
self.plugin = FakeL3PluginWithAgents()
78
self._register_agents()
80
def _register_agents(self):
82
'agent_type': constants.AGENT_TYPE_L3,
83
'binary': 'neutron-l3-agent',
87
self.plugin.create_or_update_agent(self.admin_ctx, agent_status)
88
agent_status['host'] = 'l3host_2'
89
self.plugin.create_or_update_agent(self.admin_ctx, agent_status)
90
self.agent1, self.agent2 = self.plugin.get_agents(self.admin_ctx)
92
def _bind_router(self, router_id):
93
with self.admin_ctx.session.begin(subtransactions=True):
94
bindings = self.plugin.get_ha_router_port_bindings(self.admin_ctx,
97
for agent_id, binding in zip(
98
[self.agent1['id'], self.agent2['id']], bindings):
99
binding.l3_agent_id = agent_id
101
def test_l3_agent_routers_query_interface(self):
102
router = self._create_router()
103
self._bind_router(router.id)
104
routers = self.plugin.get_ha_sync_data_for_host(self.admin_ctx,
106
self.assertEqual(1, len(routers))
109
self.assertIsNotNone(router.get('ha'))
111
interface = router.get(constants.HA_INTERFACE_KEY)
112
self.assertIsNotNone(interface)
114
self.assertEqual(constants.DEVICE_OWNER_ROUTER_HA_INTF,
115
interface['device_owner'])
116
self.assertEqual(cfg.CONF.l3_ha_net_cidr, interface['subnet']['cidr'])
118
def test_update_state(self):
119
router = self._create_router()
120
self._bind_router(router.id)
121
routers = self.plugin.get_ha_sync_data_for_host(self.admin_ctx,
123
state = routers[0].get(constants.HA_ROUTER_STATE_KEY)
124
self.assertEqual('standby', state)
126
self.plugin.update_router_state(self.admin_ctx, router.id, 'active',
129
routers = self.plugin.get_ha_sync_data_for_host(self.admin_ctx,
132
state = routers[0].get(constants.HA_ROUTER_STATE_KEY)
133
self.assertEqual('active', state)
136
class L3HATestCase(L3HATestFramework):
139
super(L3HATestCase, self).setUp()
140
self.plugin = FakeL3Plugin()
142
def test_verify_configuration_succeed(self):
143
# Default configuration should pass
144
self.plugin._verify_configuration()
146
def test_verify_configuration_l3_ha_net_cidr_is_not_a_cidr(self):
147
cfg.CONF.set_override('l3_ha_net_cidr', 'not a cidr')
149
l3_ext_ha_mode.HANetworkCIDRNotValid,
150
self.plugin._verify_configuration)
152
def test_verify_configuration_l3_ha_net_cidr_is_not_a_subnet(self):
153
cfg.CONF.set_override('l3_ha_net_cidr', '10.0.0.1/8')
155
l3_ext_ha_mode.HANetworkCIDRNotValid,
156
self.plugin._verify_configuration)
158
def test_verify_conifguration_min_l3_agents_per_router_below_minimum(self):
159
cfg.CONF.set_override('min_l3_agents_per_router', 0)
161
l3_ext_ha_mode.HAMinimumAgentsNumberNotValid,
162
self.plugin._verify_configuration)
164
def test_ha_router_create(self):
165
router = self._create_router()
166
self.assertTrue(router.extra_attributes['ha'])
168
def test_ha_router_create_with_distributed(self):
169
self.assertRaises(l3_ext_ha_mode.DistributedHARouterNotSupported,
173
def test_no_ha_router_create(self):
174
router = self._create_router(ha=False)
175
self.assertFalse(router.extra_attributes['ha'])
177
def test_router_create_with_ha_conf_enabled(self):
178
cfg.CONF.set_override('l3_ha', True)
180
router = self._create_router(ha=None)
181
self.assertTrue(router.extra_attributes['ha'])
183
def test_migration_from_ha(self):
184
router = self._create_router()
185
self.assertTrue(router.extra_attributes['ha'])
187
router = self._update_router(router.id, ha=False)
188
self.assertFalse(router.extra_attributes['ha'])
189
self.assertIsNone(router.extra_attributes['ha_vr_id'])
191
def test_migration_to_ha(self):
192
router = self._create_router(ha=False)
193
self.assertFalse(router.extra_attributes['ha'])
195
router = self._update_router(router.id, ha=True)
196
self.assertTrue(router.extra_attributes['ha'])
197
self.assertIsNotNone(router.extra_attributes['ha_vr_id'])
199
def test_migrate_ha_router_to_distributed(self):
200
router = self._create_router()
201
self.assertTrue(router.extra_attributes['ha'])
203
self.assertRaises(l3_ext_ha_mode.DistributedHARouterNotSupported,
208
def test_unique_ha_network_per_tenant(self):
211
self._create_router(tenant_id=tenant1)
212
self._create_router(tenant_id=tenant2)
213
ha_network1 = self.plugin.get_ha_network(self.admin_ctx, tenant1)
214
ha_network2 = self.plugin.get_ha_network(self.admin_ctx, tenant2)
216
ha_network1['network_id'], ha_network2['network_id'])
218
def _deployed_router_change_ha_flag(self, to_ha):
219
self._create_router(ha=not to_ha)
220
routers = self.plugin.get_ha_sync_data_for_host(self.admin_ctx)
222
interface = router.get(constants.HA_INTERFACE_KEY)
224
self.assertIsNone(interface)
226
self.assertIsNotNone(interface)
228
self._update_router(router['id'], to_ha)
229
routers = self.plugin.get_ha_sync_data_for_host(self.admin_ctx)
231
interface = router.get(constants.HA_INTERFACE_KEY)
233
self.assertIsNotNone(interface)
235
self.assertIsNone(interface)
237
def test_deployed_router_can_have_ha_enabled(self):
238
self._deployed_router_change_ha_flag(to_ha=True)
240
def test_deployed_router_can_have_ha_disabled(self):
241
self._deployed_router_change_ha_flag(to_ha=False)
243
def test_create_ha_router_notifies_agent(self):
244
self._create_router()
245
self.assertTrue(self.notif_m.called)
247
def test_update_router_to_ha_notifies_agent(self):
248
router = self._create_router(ha=False)
249
self.notif_m.reset_mock()
250
self._update_router(router.id, ha=True)
251
self.assertTrue(self.notif_m.called)
253
def test_unique_vr_id_between_routers(self):
254
self._create_router()
255
self._create_router()
256
routers = self.plugin.get_ha_sync_data_for_host(self.admin_ctx)
257
self.assertEqual(2, len(routers))
258
self.assertNotEqual(routers[0]['ha_vr_id'], routers[1]['ha_vr_id'])
260
@mock.patch('neutron.db.l3_hamode_db.VR_ID_RANGE', new=set(range(1, 1)))
261
def test_vr_id_depleted(self):
262
self.assertRaises(l3_ext_ha_mode.NoVRIDAvailable, self._create_router)
264
@mock.patch('neutron.db.l3_hamode_db.VR_ID_RANGE', new=set(range(1, 2)))
265
def test_vr_id_unique_range_per_tenant(self):
266
self._create_router()
267
self._create_router(tenant_id=_uuid())
268
routers = self.plugin.get_ha_sync_data_for_host(self.admin_ctx)
269
self.assertEqual(2, len(routers))
270
self.assertEqual(routers[0]['ha_vr_id'], routers[1]['ha_vr_id'])
272
@mock.patch('neutron.db.l3_hamode_db.MAX_ALLOCATION_TRIES', new=2)
273
def test_vr_id_allocation_contraint_conflict(self):
274
router = self._create_router()
275
network = self.plugin.get_ha_network(self.admin_ctx, router.tenant_id)
277
with mock.patch.object(self.plugin, '_get_allocated_vr_id',
278
return_value=set()) as alloc:
279
self.assertRaises(l3_ext_ha_mode.MaxVRIDAllocationTriesReached,
280
self.plugin._allocate_vr_id, self.admin_ctx,
281
network.network_id, router.id)
282
self.assertEqual(2, len(alloc.mock_calls))
284
def test_vr_id_allocation_delete_router(self):
285
router = self._create_router()
286
network = self.plugin.get_ha_network(self.admin_ctx, router.tenant_id)
288
allocs_before = self.plugin._get_allocated_vr_id(self.admin_ctx,
290
router = self._create_router()
291
allocs_current = self.plugin._get_allocated_vr_id(self.admin_ctx,
293
self.assertNotEqual(allocs_before, allocs_current)
295
self.plugin.delete_router(self.admin_ctx, router.id)
296
allocs_after = self.plugin._get_allocated_vr_id(self.admin_ctx,
298
self.assertEqual(allocs_before, allocs_after)
300
def test_vr_id_allocation_router_migration(self):
301
router = self._create_router()
302
network = self.plugin.get_ha_network(self.admin_ctx, router.tenant_id)
304
allocs_before = self.plugin._get_allocated_vr_id(self.admin_ctx,
306
router = self._create_router()
307
self._update_router(router.id, ha=False)
308
allocs_after = self.plugin._get_allocated_vr_id(self.admin_ctx,
310
self.assertEqual(allocs_before, allocs_after)
312
def test_one_ha_router_one_not(self):
313
self._create_router(ha=False)
314
self._create_router()
315
routers = self.plugin.get_ha_sync_data_for_host(self.admin_ctx)
317
ha0 = routers[0]['ha']
318
ha1 = routers[1]['ha']
320
self.assertNotEqual(ha0, ha1)
322
def test_add_ha_port_binding_failure_rolls_back_port(self):
323
router = self._create_router()
324
device_filter = {'device_id': [router.id]}
325
ports_before = self.core_plugin.get_ports(
326
self.admin_ctx, filters=device_filter)
327
network = self.plugin.get_ha_network(self.admin_ctx, router.tenant_id)
329
with mock.patch.object(self.plugin, '_create_ha_port_binding',
330
side_effect=ValueError):
331
self.assertRaises(ValueError, self.plugin.add_ha_port,
332
self.admin_ctx, router.id, network.network_id,
335
ports_after = self.core_plugin.get_ports(
336
self.admin_ctx, filters=device_filter)
338
self.assertEqual(ports_before, ports_after)
340
def test_create_ha_network_binding_failure_rolls_back_network(self):
341
networks_before = self.core_plugin.get_networks(self.admin_ctx)
343
with mock.patch.object(self.plugin,
344
'_create_ha_network_tenant_binding',
345
side_effect=ValueError):
346
self.assertRaises(ValueError, self.plugin._create_ha_network,
347
self.admin_ctx, _uuid())
349
networks_after = self.core_plugin.get_networks(self.admin_ctx)
350
self.assertEqual(networks_before, networks_after)
352
def test_create_ha_network_subnet_failure_rolls_back_network(self):
353
networks_before = self.core_plugin.get_networks(self.admin_ctx)
355
with mock.patch.object(self.plugin, '_create_ha_subnet',
356
side_effect=ValueError):
357
self.assertRaises(ValueError, self.plugin._create_ha_network,
358
self.admin_ctx, _uuid())
360
networks_after = self.core_plugin.get_networks(self.admin_ctx)
361
self.assertEqual(networks_before, networks_after)
363
def test_create_ha_interfaces_binding_failure_rolls_back_ports(self):
364
router = self._create_router()
365
network = self.plugin.get_ha_network(self.admin_ctx, router.tenant_id)
366
device_filter = {'device_id': [router.id]}
367
ports_before = self.core_plugin.get_ports(
368
self.admin_ctx, filters=device_filter)
370
with mock.patch.object(self.plugin, '_create_ha_port_binding',
371
side_effect=ValueError):
372
self.assertRaises(ValueError, self.plugin._create_ha_interfaces,
373
self.admin_ctx, router, network)
375
ports_after = self.core_plugin.get_ports(
376
self.admin_ctx, filters=device_filter)
377
self.assertEqual(ports_before, ports_after)
379
def test_create_router_db_ha_attribute_failure_rolls_back_router(self):
380
routers_before = self.plugin.get_routers(self.admin_ctx)
382
for method in ('_set_vr_id',
383
'_create_ha_interfaces',
384
'_notify_ha_interfaces_updated'):
385
with mock.patch.object(self.plugin, method,
386
side_effect=ValueError):
387
self.assertRaises(ValueError, self._create_router)
389
routers_after = self.plugin.get_routers(self.admin_ctx)
390
self.assertEqual(routers_before, routers_after)