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
17 from oslo_utils import uuidutils
18 import sqlalchemy as sa
19 from sqlalchemy import orm
21 from neutron.api.rpc.handlers import l3_rpc
22 from neutron.api.v2 import attributes
23 from neutron.common import constants
24 from neutron.common import exceptions as n_exc
25 from neutron import context
26 from neutron.db import agents_db
27 from neutron.db import common_db_mixin
28 from neutron.db import l3_agentschedulers_db
29 from neutron.db import l3_hamode_db
30 from neutron.extensions import l3
31 from neutron.extensions import l3_ext_ha_mode
32 from neutron.extensions import portbindings
33 from neutron.extensions import providernet
34 from neutron import manager
35 from neutron.scheduler import l3_agent_scheduler
36 from neutron.tests.common import helpers
37 from neutron.tests.unit import testlib_api
39 _uuid = uuidutils.generate_uuid
42 class FakeL3PluginWithAgents(common_db_mixin.CommonDbMixin,
43 l3_hamode_db.L3_HA_NAT_db_mixin,
44 l3_agentschedulers_db.L3AgentSchedulerDbMixin,
45 agents_db.AgentDbMixin):
49 class L3HATestFramework(testlib_api.SqlTestCase):
51 super(L3HATestFramework, self).setUp()
53 self.admin_ctx = context.get_admin_context()
54 self.setup_coreplugin('neutron.plugins.ml2.plugin.Ml2Plugin')
55 self.core_plugin = manager.NeutronManager.get_plugin()
56 notif_p = mock.patch.object(l3_hamode_db.L3_HA_NAT_db_mixin,
57 '_notify_ha_interfaces_updated')
58 self.notif_m = notif_p.start()
59 cfg.CONF.set_override('allow_overlapping_ips', True)
61 self.plugin = FakeL3PluginWithAgents()
62 self.plugin.router_scheduler = l3_agent_scheduler.ChanceScheduler()
63 self.agent1 = helpers.register_l3_agent()
64 self.agent2 = helpers.register_l3_agent(
65 'host_2', constants.L3_AGENT_MODE_DVR_SNAT)
67 def _create_router(self, ha=True, tenant_id='tenant1', distributed=None,
71 ctx.tenant_id = tenant_id
72 router = {'name': 'router1',
73 'admin_state_up': True,
74 'tenant_id': tenant_id}
77 if distributed is not None:
78 router['distributed'] = distributed
79 return self.plugin.create_router(ctx, {'router': router})
81 def _migrate_router(self, router_id, ha):
82 self._update_router(router_id, admin_state=False)
83 self._update_router(router_id, ha=ha)
84 return self._update_router(router_id, admin_state=True)
86 def _update_router(self, router_id, ha=None, distributed=None, ctx=None,
90 data = {'ha': ha} if ha is not None else {}
91 if distributed is not None:
92 data['distributed'] = distributed
93 if admin_state is not None:
94 data['admin_state_up'] = admin_state
95 return self.plugin._update_router_db(ctx, router_id, data)
97 def _bind_router(self, router_id):
98 with self.admin_ctx.session.begin(subtransactions=True):
99 agents_db = self.plugin.get_agents_db(self.admin_ctx)
100 self.plugin.router_scheduler._bind_ha_router_to_agents(
107 class L3HATestCase(L3HATestFramework):
108 def test_verify_configuration_succeed(self):
109 # Default configuration should pass
110 self.plugin._verify_configuration()
112 def test_verify_configuration_l3_ha_net_cidr_is_not_a_cidr(self):
113 cfg.CONF.set_override('l3_ha_net_cidr', 'not a cidr')
115 l3_ext_ha_mode.HANetworkCIDRNotValid,
116 self.plugin._verify_configuration)
118 def test_verify_configuration_l3_ha_net_cidr_is_not_a_subnet(self):
119 cfg.CONF.set_override('l3_ha_net_cidr', '10.0.0.1/8')
121 l3_ext_ha_mode.HANetworkCIDRNotValid,
122 self.plugin._verify_configuration)
124 def test_verify_configuration_min_l3_agents_per_router_below_minimum(self):
125 cfg.CONF.set_override('min_l3_agents_per_router', 0)
127 l3_ext_ha_mode.HAMinimumAgentsNumberNotValid,
128 self.plugin._check_num_agents_per_router)
130 def test_verify_configuration_max_l3_agents_below_min_l3_agents(self):
131 cfg.CONF.set_override('max_l3_agents_per_router', 3)
132 cfg.CONF.set_override('min_l3_agents_per_router', 4)
134 l3_ext_ha_mode.HAMaximumAgentsNumberNotValid,
135 self.plugin._check_num_agents_per_router)
137 def test_verify_configuration_max_l3_agents_unlimited(self):
138 cfg.CONF.set_override('max_l3_agents_per_router',
139 l3_hamode_db.UNLIMITED_AGENTS_PER_ROUTER)
140 self.plugin._check_num_agents_per_router()
142 def test_get_ha_router_port_bindings(self):
143 router = self._create_router()
144 self._bind_router(router['id'])
145 bindings = self.plugin.get_ha_router_port_bindings(
146 self.admin_ctx, [router['id']])
147 binding_dicts = [{'router_id': binding['router_id'],
148 'l3_agent_id': binding['l3_agent_id']}
149 for binding in bindings]
150 self.assertIn({'router_id': router['id'],
151 'l3_agent_id': self.agent1['id']}, binding_dicts)
152 self.assertIn({'router_id': router['id'],
153 'l3_agent_id': self.agent2['id']}, binding_dicts)
155 def test_get_l3_bindings_hosting_router_with_ha_states_ha_router(self):
156 router = self._create_router()
157 self._bind_router(router['id'])
158 self.plugin.update_routers_states(
159 self.admin_ctx, {router['id']: 'active'}, self.agent1['host'])
160 bindings = self.plugin.get_l3_bindings_hosting_router_with_ha_states(
161 self.admin_ctx, router['id'])
162 agent_ids = [(agent[0]['id'], agent[1]) for agent in bindings]
163 self.assertIn((self.agent1['id'], 'active'), agent_ids)
164 self.assertIn((self.agent2['id'], 'standby'), agent_ids)
166 def test_get_l3_bindings_hosting_router_with_ha_states_agent_none(self):
167 router = self._create_router()
168 # Do not bind router to leave agents as None
169 res = self.admin_ctx.session.query(
170 l3_hamode_db.L3HARouterAgentPortBinding).filter(
171 l3_hamode_db.L3HARouterAgentPortBinding.router_id == router['id']
173 # Check that agents are None
174 self.assertEqual([None, None], [r.agent for r in res])
175 bindings = self.plugin.get_l3_bindings_hosting_router_with_ha_states(
176 self.admin_ctx, router['id'])
177 self.assertEqual([], bindings)
179 def test_get_l3_bindings_hosting_router_with_ha_states_not_scheduled(self):
180 router = self._create_router(ha=False)
181 # Check that there no L3 agents scheduled for this router
182 res = self.admin_ctx.session.query(
183 l3_hamode_db.L3HARouterAgentPortBinding).filter(
184 l3_hamode_db.L3HARouterAgentPortBinding.router_id == router['id']
186 self.assertEqual([], [r.agent for r in res])
187 bindings = self.plugin.get_l3_bindings_hosting_router_with_ha_states(
188 self.admin_ctx, router['id'])
189 self.assertEqual([], bindings)
191 def test_ha_router_create(self):
192 router = self._create_router()
193 self.assertTrue(router['ha'])
195 def test_ha_router_create_with_distributed(self):
196 self.assertRaises(l3_ext_ha_mode.DistributedHARouterNotSupported,
200 def test_no_ha_router_create(self):
201 router = self._create_router(ha=False)
202 self.assertFalse(router['ha'])
204 def test_add_ha_network_settings(self):
205 cfg.CONF.set_override('l3_ha_network_type', 'abc')
206 cfg.CONF.set_override('l3_ha_network_physical_name', 'def')
209 self.plugin._add_ha_network_settings(network)
211 self.assertEqual('abc', network[providernet.NETWORK_TYPE])
212 self.assertEqual('def', network[providernet.PHYSICAL_NETWORK])
214 def test_router_create_with_ha_conf_enabled(self):
215 cfg.CONF.set_override('l3_ha', True)
217 router = self._create_router(ha=None)
218 self.assertTrue(router['ha'])
220 def test_migration_from_ha(self):
221 router = self._create_router()
222 self.assertTrue(router['ha'])
224 router = self._migrate_router(router['id'], False)
225 self.assertFalse(router.extra_attributes['ha'])
226 self.assertIsNone(router.extra_attributes['ha_vr_id'])
228 def test_migration_to_ha(self):
229 router = self._create_router(ha=False)
230 self.assertFalse(router['ha'])
232 router = self._migrate_router(router['id'], True)
233 self.assertTrue(router.extra_attributes['ha'])
234 self.assertIsNotNone(router.extra_attributes['ha_vr_id'])
236 def test_migration_requires_admin_state_down(self):
237 router = self._create_router(ha=False)
238 self.assertRaises(n_exc.BadRequest,
243 def test_migrate_ha_router_to_distributed(self):
244 router = self._create_router()
245 self.assertTrue(router['ha'])
247 self.assertRaises(l3_ext_ha_mode.DistributedHARouterNotSupported,
252 def test_migrate_distributed_router_to_ha(self):
253 router = self._create_router(ha=False, distributed=True)
254 self.assertFalse(router['ha'])
255 self.assertTrue(router['distributed'])
257 self.assertRaises(l3_ext_ha_mode.DistributedHARouterNotSupported,
262 def test_migrate_legacy_router_to_distributed_and_ha(self):
263 router = self._create_router(ha=False, distributed=False)
264 self.assertFalse(router['ha'])
265 self.assertFalse(router['distributed'])
267 self.assertRaises(l3_ext_ha_mode.DistributedHARouterNotSupported,
273 def test_unbind_ha_router(self):
274 router = self._create_router()
275 self._bind_router(router['id'])
277 bound_agents = self.plugin.get_l3_agents_hosting_routers(
278 self.admin_ctx, [router['id']])
279 self.assertEqual(2, len(bound_agents))
281 with mock.patch.object(manager.NeutronManager,
282 'get_service_plugins') as mock_manager:
283 self.plugin._unbind_ha_router(self.admin_ctx, router['id'])
285 bound_agents = self.plugin.get_l3_agents_hosting_routers(
286 self.admin_ctx, [router['id']])
287 self.assertEqual(0, len(bound_agents))
288 self.assertEqual(2, mock_manager.call_count)
290 def test_get_ha_sync_data_for_host_with_non_dvr_agent(self):
291 with mock.patch.object(self.plugin,
292 '_get_dvr_sync_data') as mock_get_sync:
293 self.plugin.supported_extension_aliases = ['dvr', 'l3-ha']
294 self.plugin.get_ha_sync_data_for_host(self.admin_ctx,
297 self.assertFalse(mock_get_sync.called)
299 def test_get_ha_sync_data_for_host_with_dvr_agent(self):
300 with mock.patch.object(self.plugin,
301 '_get_dvr_sync_data') as mock_get_sync:
302 self.plugin.supported_extension_aliases = ['dvr', 'l3-ha']
303 self.plugin.get_ha_sync_data_for_host(self.admin_ctx,
306 self.assertTrue(mock_get_sync.called)
308 def test_l3_agent_routers_query_interface(self):
309 router = self._create_router()
310 self._bind_router(router['id'])
311 routers = self.plugin.get_ha_sync_data_for_host(self.admin_ctx,
314 self.assertEqual(1, len(routers))
317 self.assertIsNotNone(router.get('ha'))
319 interface = router.get(constants.HA_INTERFACE_KEY)
320 self.assertIsNotNone(interface)
322 self.assertEqual(constants.DEVICE_OWNER_ROUTER_HA_INTF,
323 interface['device_owner'])
325 subnets = interface['subnets']
326 self.assertEqual(1, len(subnets))
327 self.assertEqual(cfg.CONF.l3_ha_net_cidr, subnets[0]['cidr'])
329 def test_unique_ha_network_per_tenant(self):
332 self._create_router(tenant_id=tenant1)
333 self._create_router(tenant_id=tenant2)
334 ha_network1 = self.plugin.get_ha_network(self.admin_ctx, tenant1)
335 ha_network2 = self.plugin.get_ha_network(self.admin_ctx, tenant2)
337 ha_network1['network_id'], ha_network2['network_id'])
339 def _deployed_router_change_ha_flag(self, to_ha):
340 router1 = self._create_router(ha=not to_ha)
341 self._bind_router(router1['id'])
342 routers = self.plugin.get_ha_sync_data_for_host(
343 self.admin_ctx, self.agent1['host'], self.agent1)
345 interface = router.get(constants.HA_INTERFACE_KEY)
347 self.assertIsNone(interface)
349 self.assertIsNotNone(interface)
351 self._migrate_router(router['id'], to_ha)
352 self.plugin.schedule_router(self.admin_ctx, router1['id'])
353 routers = self.plugin.get_ha_sync_data_for_host(
354 self.admin_ctx, self.agent1['host'], self.agent1)
356 interface = router.get(constants.HA_INTERFACE_KEY)
358 self.assertIsNotNone(interface)
360 self.assertIsNone(interface)
362 def test_deployed_router_can_have_ha_enabled(self):
363 self._deployed_router_change_ha_flag(to_ha=True)
365 def test_deployed_router_can_have_ha_disabled(self):
366 self._deployed_router_change_ha_flag(to_ha=False)
368 def test_create_ha_router_notifies_agent(self):
369 self._create_router()
370 self.assertTrue(self.notif_m.called)
372 def test_update_router_to_ha_notifies_agent(self):
373 router = self._create_router(ha=False)
374 self.notif_m.reset_mock()
375 self._migrate_router(router['id'], True)
376 self.assertTrue(self.notif_m.called)
378 def test_unique_vr_id_between_routers(self):
379 router1 = self._create_router()
380 router2 = self._create_router()
381 self._bind_router(router1['id'])
382 self._bind_router(router2['id'])
383 routers = self.plugin.get_ha_sync_data_for_host(
384 self.admin_ctx, self.agent1['host'], self.agent1)
385 self.assertEqual(2, len(routers))
386 self.assertNotEqual(routers[0]['ha_vr_id'], routers[1]['ha_vr_id'])
388 @mock.patch('neutron.db.l3_hamode_db.VR_ID_RANGE', new=set(range(1, 1)))
389 def test_vr_id_depleted(self):
390 self.assertRaises(l3_ext_ha_mode.NoVRIDAvailable, self._create_router)
392 @mock.patch('neutron.db.l3_hamode_db.VR_ID_RANGE', new=set(range(1, 2)))
393 def test_vr_id_unique_range_per_tenant(self):
394 router1 = self._create_router()
395 router2 = self._create_router(tenant_id=_uuid())
396 self._bind_router(router1['id'])
397 self._bind_router(router2['id'])
398 routers = self.plugin.get_ha_sync_data_for_host(
399 self.admin_ctx, self.agent1['host'], self.agent1)
400 self.assertEqual(2, len(routers))
401 self.assertEqual(routers[0]['ha_vr_id'], routers[1]['ha_vr_id'])
403 @mock.patch('neutron.db.l3_hamode_db.MAX_ALLOCATION_TRIES', new=2)
404 def test_vr_id_allocation_contraint_conflict(self):
405 router = self._create_router()
406 network = self.plugin.get_ha_network(self.admin_ctx,
409 with mock.patch.object(self.plugin, '_get_allocated_vr_id',
410 return_value=set()) as alloc:
411 self.assertRaises(l3_ext_ha_mode.MaxVRIDAllocationTriesReached,
412 self.plugin._allocate_vr_id, self.admin_ctx,
413 network.network_id, router['id'])
414 self.assertEqual(2, len(alloc.mock_calls))
416 def test_vr_id_allocation_delete_router(self):
417 router = self._create_router()
418 network = self.plugin.get_ha_network(self.admin_ctx,
421 allocs_before = self.plugin._get_allocated_vr_id(self.admin_ctx,
423 router = self._create_router()
424 allocs_current = self.plugin._get_allocated_vr_id(self.admin_ctx,
426 self.assertNotEqual(allocs_before, allocs_current)
428 self.plugin.delete_router(self.admin_ctx, router['id'])
429 allocs_after = self.plugin._get_allocated_vr_id(self.admin_ctx,
431 self.assertEqual(allocs_before, allocs_after)
433 def test_vr_id_allocation_router_migration(self):
434 router = self._create_router()
435 network = self.plugin.get_ha_network(self.admin_ctx,
438 allocs_before = self.plugin._get_allocated_vr_id(self.admin_ctx,
440 router = self._create_router()
441 self._migrate_router(router['id'], False)
442 allocs_after = self.plugin._get_allocated_vr_id(self.admin_ctx,
444 self.assertEqual(allocs_before, allocs_after)
446 def test_one_ha_router_one_not(self):
447 router1 = self._create_router(ha=False)
448 router2 = self._create_router()
449 self._bind_router(router1['id'])
450 self._bind_router(router2['id'])
451 routers = self.plugin.get_ha_sync_data_for_host(
452 self.admin_ctx, self.agent1['host'], self.agent1)
454 ha0 = routers[0]['ha']
455 ha1 = routers[1]['ha']
457 self.assertNotEqual(ha0, ha1)
459 def test_add_ha_port_subtransactions_blocked(self):
460 with self.admin_ctx.session.begin():
461 self.assertRaises(RuntimeError, self.plugin.add_ha_port,
462 self.admin_ctx, 'id', 'id', 'id')
464 def test_add_ha_port_binding_failure_rolls_back_port(self):
465 router = self._create_router()
466 device_filter = {'device_id': [router['id']]}
467 ports_before = self.core_plugin.get_ports(
468 self.admin_ctx, filters=device_filter)
469 network = self.plugin.get_ha_network(self.admin_ctx,
472 with mock.patch.object(self.plugin, '_create_ha_port_binding',
473 side_effect=ValueError):
474 self.assertRaises(ValueError, self.plugin.add_ha_port,
475 self.admin_ctx, router['id'], network.network_id,
478 ports_after = self.core_plugin.get_ports(
479 self.admin_ctx, filters=device_filter)
481 self.assertEqual(ports_before, ports_after)
483 def test_create_ha_network_binding_failure_rolls_back_network(self):
484 networks_before = self.core_plugin.get_networks(self.admin_ctx)
486 with mock.patch.object(self.plugin,
487 '_create_ha_network_tenant_binding',
488 side_effect=ValueError):
489 self.assertRaises(ValueError, self.plugin._create_ha_network,
490 self.admin_ctx, _uuid())
492 networks_after = self.core_plugin.get_networks(self.admin_ctx)
493 self.assertEqual(networks_before, networks_after)
495 def test_create_ha_network_subnet_failure_rolls_back_network(self):
496 networks_before = self.core_plugin.get_networks(self.admin_ctx)
498 with mock.patch.object(self.plugin, '_create_ha_subnet',
499 side_effect=ValueError):
500 self.assertRaises(ValueError, self.plugin._create_ha_network,
501 self.admin_ctx, _uuid())
503 networks_after = self.core_plugin.get_networks(self.admin_ctx)
504 self.assertEqual(networks_before, networks_after)
506 def test_create_ha_interfaces_binding_failure_rolls_back_ports(self):
507 router = self._create_router()
508 network = self.plugin.get_ha_network(self.admin_ctx,
510 device_filter = {'device_id': [router['id']]}
511 ports_before = self.core_plugin.get_ports(
512 self.admin_ctx, filters=device_filter)
514 router_db = self.plugin._get_router(self.admin_ctx, router['id'])
515 with mock.patch.object(self.plugin, '_create_ha_port_binding',
516 side_effect=ValueError):
517 self.assertRaises(ValueError, self.plugin._create_ha_interfaces,
518 self.admin_ctx, router_db, network)
520 ports_after = self.core_plugin.get_ports(
521 self.admin_ctx, filters=device_filter)
522 self.assertEqual(ports_before, ports_after)
524 def test_create_router_db_ha_attribute_failure_rolls_back_router(self):
525 routers_before = self.plugin.get_routers(self.admin_ctx)
527 for method in ('_set_vr_id',
528 '_create_ha_interfaces',
529 '_notify_ha_interfaces_updated'):
530 with mock.patch.object(self.plugin, method,
531 side_effect=ValueError):
532 self.assertRaises(ValueError, self._create_router)
534 routers_after = self.plugin.get_routers(self.admin_ctx)
535 self.assertEqual(routers_before, routers_after)
537 def test_get_active_host_for_ha_router(self):
538 router = self._create_router()
539 self._bind_router(router['id'])
542 self.plugin.get_active_host_for_ha_router(
543 self.admin_ctx, router['id']))
544 self.plugin.update_routers_states(
545 self.admin_ctx, {router['id']: 'active'}, self.agent2['host'])
548 self.plugin.get_active_host_for_ha_router(
549 self.admin_ctx, router['id']))
551 def test_update_routers_states(self):
552 router1 = self._create_router()
553 self._bind_router(router1['id'])
554 router2 = self._create_router()
555 self._bind_router(router2['id'])
557 routers = self.plugin.get_ha_sync_data_for_host(
558 self.admin_ctx, self.agent1['host'], self.agent1)
559 for router in routers:
560 self.assertEqual('standby', router[constants.HA_ROUTER_STATE_KEY])
562 states = {router1['id']: 'active',
563 router2['id']: 'standby'}
564 self.plugin.update_routers_states(
565 self.admin_ctx, states, self.agent1['host'])
567 routers = self.plugin.get_ha_sync_data_for_host(
568 self.admin_ctx, self.agent1['host'], self.agent1)
569 for router in routers:
570 self.assertEqual(states[router['id']],
571 router[constants.HA_ROUTER_STATE_KEY])
573 def test_set_router_states_handles_concurrently_deleted_router(self):
574 router1 = self._create_router()
575 self._bind_router(router1['id'])
576 router2 = self._create_router()
577 self._bind_router(router2['id'])
578 bindings = self.plugin.get_ha_router_port_bindings(
579 self.admin_ctx, [router1['id'], router2['id']])
580 self.plugin.delete_router(self.admin_ctx, router1['id'])
581 self.plugin._set_router_states(
582 self.admin_ctx, bindings, {router1['id']: 'active',
583 router2['id']: 'active'})
584 routers = self.plugin.get_ha_sync_data_for_host(
585 self.admin_ctx, self.agent1['host'], self.agent1)
586 self.assertEqual('active', routers[0][constants.HA_ROUTER_STATE_KEY])
588 def test_exclude_dvr_agents_for_ha_candidates(self):
589 """Test dvr agents are not counted in the ha candidates.
591 This test case tests that when get_number_of_agents_for_scheduling
592 is called, it doesn't count dvr agents.
594 # Test setup registers two l3 agents.
595 # Register another l3 agent with dvr mode and assert that
596 # get_number_of_ha_agent_candidates return 2.
597 helpers.register_l3_agent('host_3', constants.L3_AGENT_MODE_DVR)
598 num_ha_candidates = self.plugin.get_number_of_agents_for_scheduling(
600 self.assertEqual(2, num_ha_candidates)
602 def test_get_number_of_agents_for_scheduling_not_enough_agents(self):
603 cfg.CONF.set_override('min_l3_agents_per_router', 3)
604 helpers.kill_agent(helpers.register_l3_agent(host='l3host_3')['id'])
605 self.assertRaises(l3_ext_ha_mode.HANotEnoughAvailableAgents,
606 self.plugin.get_number_of_agents_for_scheduling,
609 def test_ha_network_deleted_if_no_ha_router_present_two_tenants(self):
610 # Create two routers in different tenants.
611 router1 = self._create_router()
612 router2 = self._create_router(tenant_id='tenant2')
613 nets_before = [net['name'] for net in
614 self.core_plugin.get_networks(self.admin_ctx)]
615 # Check that HA networks created for each tenant
616 self.assertIn('HA network tenant %s' % router1['tenant_id'],
618 self.assertIn('HA network tenant %s' % router2['tenant_id'],
621 self.plugin.delete_router(self.admin_ctx, router1['id'])
622 nets_after = [net['name'] for net in
623 self.core_plugin.get_networks(self.admin_ctx)]
624 # Check that HA network for tenant1 is deleted and for tenant2 is not.
625 self.assertNotIn('HA network tenant %s' % router1['tenant_id'],
627 self.assertIn('HA network tenant %s' % router2['tenant_id'],
630 def test_ha_network_is_not_delete_if_ha_router_is_present(self):
631 # Create 2 routers in one tenant and check if one is deleted, HA
632 # network still exists.
633 router1 = self._create_router()
634 router2 = self._create_router()
635 nets_before = [net['name'] for net in
636 self.core_plugin.get_networks(self.admin_ctx)]
637 self.assertIn('HA network tenant %s' % router1['tenant_id'],
639 self.plugin.delete_router(self.admin_ctx, router2['id'])
640 nets_after = [net['name'] for net in
641 self.core_plugin.get_networks(self.admin_ctx)]
642 self.assertIn('HA network tenant %s' % router1['tenant_id'],
645 def test_ha_network_delete_ha_and_non_ha_router(self):
646 # Create HA and non-HA router. Check after deletion HA router HA
647 # network is deleted.
648 router1 = self._create_router(ha=False)
649 router2 = self._create_router()
650 nets_before = [net['name'] for net in
651 self.core_plugin.get_networks(self.admin_ctx)]
652 self.assertIn('HA network tenant %s' % router1['tenant_id'],
654 self.plugin.delete_router(self.admin_ctx, router2['id'])
655 nets_after = [net['name'] for net in
656 self.core_plugin.get_networks(self.admin_ctx)]
657 self.assertNotIn('HA network tenant %s' % router1['tenant_id'],
660 def _test_ha_network_is_not_deleted_raise_exception(self, exception):
661 router1 = self._create_router()
662 nets_before = [net['name'] for net in
663 self.core_plugin.get_networks(self.admin_ctx)]
664 self.assertIn('HA network tenant %s' % router1['tenant_id'],
666 with mock.patch.object(self.plugin, '_delete_ha_network',
667 side_effect=exception):
668 self.plugin.delete_router(self.admin_ctx, router1['id'])
669 nets_after = [net['name'] for net in
670 self.core_plugin.get_networks(self.admin_ctx)]
671 self.assertIn('HA network tenant %s' % router1['tenant_id'],
674 def test_ha_network_is_not_deleted_if_another_ha_router_is_created(self):
675 # If another router was created during deletion of current router,
676 # _delete_ha_network will fail with InvalidRequestError. Check that HA
677 # network won't be deleted.
678 self._test_ha_network_is_not_deleted_raise_exception(
679 sa.exc.InvalidRequestError)
681 def test_ha_network_is_not_deleted_if_network_in_use(self):
682 self._test_ha_network_is_not_deleted_raise_exception(
683 n_exc.NetworkInUse(net_id="foo_net_id"))
685 def test_ha_network_is_not_deleted_if_db_deleted_error(self):
686 self._test_ha_network_is_not_deleted_raise_exception(
687 orm.exc.ObjectDeletedError(None))
689 def test_ha_router_create_failed_no_ha_network_delete(self):
690 tenant_id = "foo_tenant_id"
691 nets_before = self.core_plugin.get_networks(self.admin_ctx)
692 self.assertNotIn('HA network tenant %s' % tenant_id,
695 # Unable to create HA network
696 with mock.patch.object(self.core_plugin, 'create_network',
697 side_effect=n_exc.NoNetworkAvailable):
698 self.assertRaises(n_exc.NoNetworkAvailable,
702 nets_after = self.core_plugin.get_networks(self.admin_ctx)
703 self.assertEqual(nets_before, nets_after)
704 self.assertNotIn('HA network tenant %s' % tenant_id,
708 class L3HAModeDbTestCase(L3HATestFramework):
710 def _create_network(self, plugin, ctx, name='net',
711 tenant_id='tenant1'):
712 network = {'network': {'name': name,
714 'admin_state_up': True,
715 'tenant_id': tenant_id}}
716 return plugin.create_network(ctx, network)['id']
718 def _create_subnet(self, plugin, ctx, network_id, cidr='10.0.0.0/8',
719 name='subnet', tenant_id='tenant1'):
720 subnet = {'subnet': {'name': name,
722 'network_id': network_id,
724 'gateway_ip': attributes.ATTR_NOT_SPECIFIED,
725 'allocation_pools': attributes.ATTR_NOT_SPECIFIED,
726 'dns_nameservers': attributes.ATTR_NOT_SPECIFIED,
727 'host_routes': attributes.ATTR_NOT_SPECIFIED,
728 'tenant_id': tenant_id,
730 'ipv6_ra_mode': attributes.ATTR_NOT_SPECIFIED}}
731 created_subnet = plugin.create_subnet(ctx, subnet)
732 return created_subnet
734 def test_remove_ha_in_use(self):
735 router = self._create_router(ctx=self.admin_ctx)
736 network_id = self._create_network(self.core_plugin, self.admin_ctx)
737 subnet = self._create_subnet(self.core_plugin, self.admin_ctx,
739 interface_info = {'subnet_id': subnet['id']}
740 self.plugin.add_router_interface(self.admin_ctx,
743 self.assertRaises(l3.RouterInUse, self.plugin.delete_router,
744 self.admin_ctx, router['id'])
745 bindings = self.plugin.get_ha_router_port_bindings(
746 self.admin_ctx, [router['id']])
747 self.assertEqual(2, len(bindings))
749 def test_update_router_port_bindings_no_ports(self):
750 self.plugin._update_router_port_bindings(
751 self.admin_ctx, {}, self.agent1['host'])
753 def _get_first_interface(self, router_id):
754 device_filter = {'device_id': [router_id],
756 [constants.DEVICE_OWNER_ROUTER_INTF]}
757 return self.core_plugin.get_ports(
759 filters=device_filter)[0]
761 def test_update_router_port_bindings_updates_host(self):
762 network_id = self._create_network(self.core_plugin, self.admin_ctx)
763 subnet = self._create_subnet(self.core_plugin, self.admin_ctx,
765 interface_info = {'subnet_id': subnet['id']}
767 router = self._create_router()
768 self._bind_router(router['id'])
769 self.plugin.add_router_interface(self.admin_ctx,
772 self.plugin._update_router_port_bindings(
773 self.admin_ctx, {router['id']: 'active'}, self.agent1['host'])
775 port = self._get_first_interface(router['id'])
776 self.assertEqual(self.agent1['host'], port[portbindings.HOST_ID])
778 self.plugin._update_router_port_bindings(
779 self.admin_ctx, {router['id']: 'active'}, self.agent2['host'])
780 port = self._get_first_interface(router['id'])
781 self.assertEqual(self.agent2['host'], port[portbindings.HOST_ID])
783 def test_ensure_host_set_on_ports_binds_correctly(self):
784 network_id = self._create_network(self.core_plugin, self.admin_ctx)
785 subnet = self._create_subnet(self.core_plugin, self.admin_ctx,
787 interface_info = {'subnet_id': subnet['id']}
789 router = self._create_router()
790 self._bind_router(router['id'])
791 self.plugin.add_router_interface(self.admin_ctx,
794 port = self._get_first_interface(router['id'])
795 self.assertEqual('', port[portbindings.HOST_ID])
797 # Update the router object to include the first interface
799 self.plugin.list_active_sync_routers_on_active_l3_agent(
800 self.admin_ctx, self.agent1['host'], [router['id']]))[0]
802 # ensure_host_set_on_ports binds an unbound port
803 callback = l3_rpc.L3RpcCallback()
804 callback._l3plugin = self.plugin
805 callback._ensure_host_set_on_ports(
806 self.admin_ctx, self.agent1['host'], [router])
807 port = self._get_first_interface(router['id'])
808 self.assertEqual(self.agent1['host'], port[portbindings.HOST_ID])
810 # ensure_host_set_on_ports does not rebind a bound port
812 self.plugin.list_active_sync_routers_on_active_l3_agent(
813 self.admin_ctx, self.agent1['host'], [router['id']]))[0]
814 callback._ensure_host_set_on_ports(
815 self.admin_ctx, self.agent2['host'], [router])
816 port = self._get_first_interface(router['id'])
817 self.assertEqual(self.agent1['host'], port[portbindings.HOST_ID])
820 class L3HAUserTestCase(L3HATestFramework):
823 super(L3HAUserTestCase, self).setUp()
824 self.user_ctx = context.Context('', _uuid())
826 def test_create_ha_router(self):
827 self._create_router(ctx=self.user_ctx)
829 def test_update_router(self):
830 router = self._create_router(ctx=self.user_ctx)
831 self._update_router(router['id'], ctx=self.user_ctx)
833 def test_delete_router(self):
834 router = self._create_router(ctx=self.user_ctx)
835 self.plugin.delete_router(self.user_ctx, router['id'])