a416c31b8fcaf00c79e3a9d36b86e20e327e69df
[openstack-build/neutron-build.git] / neutron / tests / unit / db / test_l3_hamode_db.py
1 # Copyright (C) 2014 eNovance SAS <licensing@enovance.com>
2 #
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
6 #
7 #      http://www.apache.org/licenses/LICENSE-2.0
8 #
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
13 # under the License.
14
15 import mock
16 from oslo_config import cfg
17 from oslo_utils import uuidutils
18 import sqlalchemy as sa
19 from sqlalchemy import orm
20
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
38
39 _uuid = uuidutils.generate_uuid
40
41
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):
46     pass
47
48
49 class L3HATestFramework(testlib_api.SqlTestCase):
50     def setUp(self):
51         super(L3HATestFramework, self).setUp()
52
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)
60
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)
66
67     def _create_router(self, ha=True, tenant_id='tenant1', distributed=None,
68                        ctx=None):
69         if ctx is None:
70             ctx = self.admin_ctx
71         ctx.tenant_id = tenant_id
72         router = {'name': 'router1',
73                   'admin_state_up': True,
74                   'tenant_id': tenant_id}
75         if ha is not None:
76             router['ha'] = ha
77         if distributed is not None:
78             router['distributed'] = distributed
79         return self.plugin.create_router(ctx, {'router': router})
80
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)
85
86     def _update_router(self, router_id, ha=None, distributed=None, ctx=None,
87                        admin_state=None):
88         if ctx is None:
89             ctx = self.admin_ctx
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)
96
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(
101                 self.plugin,
102                 self.admin_ctx,
103                 router_id,
104                 agents_db)
105
106
107 class L3HATestCase(L3HATestFramework):
108     def test_verify_configuration_succeed(self):
109         # Default configuration should pass
110         self.plugin._verify_configuration()
111
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')
114         self.assertRaises(
115             l3_ext_ha_mode.HANetworkCIDRNotValid,
116             self.plugin._verify_configuration)
117
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')
120         self.assertRaises(
121             l3_ext_ha_mode.HANetworkCIDRNotValid,
122             self.plugin._verify_configuration)
123
124     def test_verify_configuration_min_l3_agents_per_router_below_minimum(self):
125         cfg.CONF.set_override('min_l3_agents_per_router', 0)
126         self.assertRaises(
127             l3_ext_ha_mode.HAMinimumAgentsNumberNotValid,
128             self.plugin._check_num_agents_per_router)
129
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)
133         self.assertRaises(
134             l3_ext_ha_mode.HAMaximumAgentsNumberNotValid,
135             self.plugin._check_num_agents_per_router)
136
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()
141
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)
154
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)
165
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']
172         ).all()
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)
178
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']
185         ).all()
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)
190
191     def test_ha_router_create(self):
192         router = self._create_router()
193         self.assertTrue(router['ha'])
194
195     def test_ha_router_create_with_distributed(self):
196         self.assertRaises(l3_ext_ha_mode.DistributedHARouterNotSupported,
197                           self._create_router,
198                           distributed=True)
199
200     def test_no_ha_router_create(self):
201         router = self._create_router(ha=False)
202         self.assertFalse(router['ha'])
203
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')
207
208         network = {}
209         self.plugin._add_ha_network_settings(network)
210
211         self.assertEqual('abc', network[providernet.NETWORK_TYPE])
212         self.assertEqual('def', network[providernet.PHYSICAL_NETWORK])
213
214     def test_router_create_with_ha_conf_enabled(self):
215         cfg.CONF.set_override('l3_ha', True)
216
217         router = self._create_router(ha=None)
218         self.assertTrue(router['ha'])
219
220     def test_migration_from_ha(self):
221         router = self._create_router()
222         self.assertTrue(router['ha'])
223
224         router = self._migrate_router(router['id'], False)
225         self.assertFalse(router.extra_attributes['ha'])
226         self.assertIsNone(router.extra_attributes['ha_vr_id'])
227
228     def test_migration_to_ha(self):
229         router = self._create_router(ha=False)
230         self.assertFalse(router['ha'])
231
232         router = self._migrate_router(router['id'], True)
233         self.assertTrue(router.extra_attributes['ha'])
234         self.assertIsNotNone(router.extra_attributes['ha_vr_id'])
235
236     def test_migration_requires_admin_state_down(self):
237         router = self._create_router(ha=False)
238         self.assertRaises(n_exc.BadRequest,
239                           self._update_router,
240                           router['id'],
241                           ha=True)
242
243     def test_migrate_ha_router_to_distributed(self):
244         router = self._create_router()
245         self.assertTrue(router['ha'])
246
247         self.assertRaises(l3_ext_ha_mode.DistributedHARouterNotSupported,
248                           self._update_router,
249                           router['id'],
250                           distributed=True)
251
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'])
256
257         self.assertRaises(l3_ext_ha_mode.DistributedHARouterNotSupported,
258                           self._update_router,
259                           router['id'],
260                           ha=True)
261
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'])
266
267         self.assertRaises(l3_ext_ha_mode.DistributedHARouterNotSupported,
268                           self._update_router,
269                           router['id'],
270                           ha=True,
271                           distributed=True)
272
273     def test_unbind_ha_router(self):
274         router = self._create_router()
275         self._bind_router(router['id'])
276
277         bound_agents = self.plugin.get_l3_agents_hosting_routers(
278             self.admin_ctx, [router['id']])
279         self.assertEqual(2, len(bound_agents))
280
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'])
284
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)
289
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,
295                                                   self.agent1['host'],
296                                                   self.agent1)
297             self.assertFalse(mock_get_sync.called)
298
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,
304                                                   self.agent2['host'],
305                                                   self.agent2)
306             self.assertTrue(mock_get_sync.called)
307
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,
312                                                         self.agent1['host'],
313                                                         self.agent1)
314         self.assertEqual(1, len(routers))
315         router = routers[0]
316
317         self.assertIsNotNone(router.get('ha'))
318
319         interface = router.get(constants.HA_INTERFACE_KEY)
320         self.assertIsNotNone(interface)
321
322         self.assertEqual(constants.DEVICE_OWNER_ROUTER_HA_INTF,
323                          interface['device_owner'])
324
325         subnets = interface['subnets']
326         self.assertEqual(1, len(subnets))
327         self.assertEqual(cfg.CONF.l3_ha_net_cidr, subnets[0]['cidr'])
328
329     def test_unique_ha_network_per_tenant(self):
330         tenant1 = _uuid()
331         tenant2 = _uuid()
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)
336         self.assertNotEqual(
337             ha_network1['network_id'], ha_network2['network_id'])
338
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)
344         router = routers[0]
345         interface = router.get(constants.HA_INTERFACE_KEY)
346         if to_ha:
347             self.assertIsNone(interface)
348         else:
349             self.assertIsNotNone(interface)
350
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)
355         router = routers[0]
356         interface = router.get(constants.HA_INTERFACE_KEY)
357         if to_ha:
358             self.assertIsNotNone(interface)
359         else:
360             self.assertIsNone(interface)
361
362     def test_deployed_router_can_have_ha_enabled(self):
363         self._deployed_router_change_ha_flag(to_ha=True)
364
365     def test_deployed_router_can_have_ha_disabled(self):
366         self._deployed_router_change_ha_flag(to_ha=False)
367
368     def test_create_ha_router_notifies_agent(self):
369         self._create_router()
370         self.assertTrue(self.notif_m.called)
371
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)
377
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'])
387
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)
391
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'])
402
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,
407                                              router['tenant_id'])
408
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))
415
416     def test_vr_id_allocation_delete_router(self):
417         router = self._create_router()
418         network = self.plugin.get_ha_network(self.admin_ctx,
419                                              router['tenant_id'])
420
421         allocs_before = self.plugin._get_allocated_vr_id(self.admin_ctx,
422                                                          network.network_id)
423         router = self._create_router()
424         allocs_current = self.plugin._get_allocated_vr_id(self.admin_ctx,
425                                                           network.network_id)
426         self.assertNotEqual(allocs_before, allocs_current)
427
428         self.plugin.delete_router(self.admin_ctx, router['id'])
429         allocs_after = self.plugin._get_allocated_vr_id(self.admin_ctx,
430                                                         network.network_id)
431         self.assertEqual(allocs_before, allocs_after)
432
433     def test_vr_id_allocation_router_migration(self):
434         router = self._create_router()
435         network = self.plugin.get_ha_network(self.admin_ctx,
436                                              router['tenant_id'])
437
438         allocs_before = self.plugin._get_allocated_vr_id(self.admin_ctx,
439                                                          network.network_id)
440         router = self._create_router()
441         self._migrate_router(router['id'], False)
442         allocs_after = self.plugin._get_allocated_vr_id(self.admin_ctx,
443                                                         network.network_id)
444         self.assertEqual(allocs_before, allocs_after)
445
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)
453
454         ha0 = routers[0]['ha']
455         ha1 = routers[1]['ha']
456
457         self.assertNotEqual(ha0, ha1)
458
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')
463
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,
470                                              router['tenant_id'])
471
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,
476                               router['tenant_id'])
477
478         ports_after = self.core_plugin.get_ports(
479             self.admin_ctx, filters=device_filter)
480
481         self.assertEqual(ports_before, ports_after)
482
483     def test_create_ha_network_binding_failure_rolls_back_network(self):
484         networks_before = self.core_plugin.get_networks(self.admin_ctx)
485
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())
491
492         networks_after = self.core_plugin.get_networks(self.admin_ctx)
493         self.assertEqual(networks_before, networks_after)
494
495     def test_create_ha_network_subnet_failure_rolls_back_network(self):
496         networks_before = self.core_plugin.get_networks(self.admin_ctx)
497
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())
502
503         networks_after = self.core_plugin.get_networks(self.admin_ctx)
504         self.assertEqual(networks_before, networks_after)
505
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,
509                                              router['tenant_id'])
510         device_filter = {'device_id': [router['id']]}
511         ports_before = self.core_plugin.get_ports(
512             self.admin_ctx, filters=device_filter)
513
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)
519
520         ports_after = self.core_plugin.get_ports(
521             self.admin_ctx, filters=device_filter)
522         self.assertEqual(ports_before, ports_after)
523
524     def test_create_router_db_ha_attribute_failure_rolls_back_router(self):
525         routers_before = self.plugin.get_routers(self.admin_ctx)
526
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)
533
534         routers_after = self.plugin.get_routers(self.admin_ctx)
535         self.assertEqual(routers_before, routers_after)
536
537     def test_get_active_host_for_ha_router(self):
538         router = self._create_router()
539         self._bind_router(router['id'])
540         self.assertEqual(
541             None,
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'])
546         self.assertEqual(
547             self.agent2['host'],
548             self.plugin.get_active_host_for_ha_router(
549                 self.admin_ctx, router['id']))
550
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'])
556
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])
561
562         states = {router1['id']: 'active',
563                   router2['id']: 'standby'}
564         self.plugin.update_routers_states(
565             self.admin_ctx, states, self.agent1['host'])
566
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])
572
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])
587
588     def test_exclude_dvr_agents_for_ha_candidates(self):
589         """Test dvr agents are not counted in the ha candidates.
590
591         This test case tests that when get_number_of_agents_for_scheduling
592         is called, it doesn't count dvr agents.
593         """
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(
599             self.admin_ctx)
600         self.assertEqual(2, num_ha_candidates)
601
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,
607                           self.admin_ctx)
608
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'],
617                       nets_before)
618         self.assertIn('HA network tenant %s' % router2['tenant_id'],
619                       nets_before)
620         # Delete router1
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'],
626                          nets_after)
627         self.assertIn('HA network tenant %s' % router2['tenant_id'],
628                       nets_after)
629
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'],
638                       nets_before)
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'],
643                       nets_after)
644
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'],
653                       nets_before)
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'],
658                          nets_after)
659
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'],
665                       nets_before)
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'],
672                           nets_after)
673
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)
680
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"))
684
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))
688
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,
693                          nets_before)
694
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,
699                               self._create_router,
700                               True,
701                               tenant_id)
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,
705                              nets_after)
706
707
708 class L3HAModeDbTestCase(L3HATestFramework):
709
710     def _create_network(self, plugin, ctx, name='net',
711                         tenant_id='tenant1'):
712         network = {'network': {'name': name,
713                                'shared': False,
714                                'admin_state_up': True,
715                                'tenant_id': tenant_id}}
716         return plugin.create_network(ctx, network)['id']
717
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,
721                   'ip_version': 4,
722                   'network_id': network_id,
723                   'cidr': cidr,
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,
729                   'enable_dhcp': True,
730                   'ipv6_ra_mode': attributes.ATTR_NOT_SPECIFIED}}
731         created_subnet = plugin.create_subnet(ctx, subnet)
732         return created_subnet
733
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,
738                                      network_id)
739         interface_info = {'subnet_id': subnet['id']}
740         self.plugin.add_router_interface(self.admin_ctx,
741                                          router['id'],
742                                          interface_info)
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))
748
749     def test_update_router_port_bindings_no_ports(self):
750         self.plugin._update_router_port_bindings(
751             self.admin_ctx, {}, self.agent1['host'])
752
753     def _get_first_interface(self, router_id):
754         device_filter = {'device_id': [router_id],
755                          'device_owner':
756                          [constants.DEVICE_OWNER_ROUTER_INTF]}
757         return self.core_plugin.get_ports(
758             self.admin_ctx,
759             filters=device_filter)[0]
760
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,
764                                      network_id)
765         interface_info = {'subnet_id': subnet['id']}
766
767         router = self._create_router()
768         self._bind_router(router['id'])
769         self.plugin.add_router_interface(self.admin_ctx,
770                                          router['id'],
771                                          interface_info)
772         self.plugin._update_router_port_bindings(
773             self.admin_ctx, {router['id']: 'active'}, self.agent1['host'])
774
775         port = self._get_first_interface(router['id'])
776         self.assertEqual(self.agent1['host'], port[portbindings.HOST_ID])
777
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])
782
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,
786                                      network_id)
787         interface_info = {'subnet_id': subnet['id']}
788
789         router = self._create_router()
790         self._bind_router(router['id'])
791         self.plugin.add_router_interface(self.admin_ctx,
792                                          router['id'],
793                                          interface_info)
794         port = self._get_first_interface(router['id'])
795         self.assertEqual('', port[portbindings.HOST_ID])
796
797         # Update the router object to include the first interface
798         router = (
799             self.plugin.list_active_sync_routers_on_active_l3_agent(
800                 self.admin_ctx, self.agent1['host'], [router['id']]))[0]
801
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])
809
810         # ensure_host_set_on_ports does not rebind a bound port
811         router = (
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])
818
819
820 class L3HAUserTestCase(L3HATestFramework):
821
822     def setUp(self):
823         super(L3HAUserTestCase, self).setUp()
824         self.user_ctx = context.Context('', _uuid())
825
826     def test_create_ha_router(self):
827         self._create_router(ctx=self.user_ctx)
828
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)
832
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'])