1 # Copyright 2013 VMware, Inc.
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
18 from oslo_config import cfg
19 from oslo_utils import uuidutils
23 from neutron.common import constants
24 from neutron.db import api as db_api
25 from neutron.db import external_net_db
26 from neutron.db import l3_db
27 from neutron.db import l3_gwmode_db
28 from neutron.db import models_v2
29 from neutron.extensions import l3
30 from neutron.extensions import l3_ext_gw_mode
31 from neutron.tests import base
32 from neutron.tests.unit.db import test_db_base_plugin_v2
33 from neutron.tests.unit.extensions import test_l3
34 from neutron.tests.unit import testlib_api
36 _uuid = uuidutils.generate_uuid
37 FAKE_GW_PORT_ID = _uuid()
38 FAKE_GW_PORT_MAC = 'aa:bb:cc:dd:ee:ff'
39 FAKE_FIP_EXT_PORT_ID = _uuid()
40 FAKE_FIP_EXT_PORT_MAC = '11:22:33:44:55:66'
41 FAKE_FIP_INT_PORT_ID = _uuid()
42 FAKE_FIP_INT_PORT_MAC = 'aa:aa:aa:aa:aa:aa'
43 FAKE_ROUTER_PORT_ID = _uuid()
44 FAKE_ROUTER_PORT_MAC = 'bb:bb:bb:bb:bb:bb'
47 class TestExtensionManager(object):
49 def get_resources(self):
50 # Simulate extension of L3 attribute map
51 for key in l3.RESOURCE_ATTRIBUTE_MAP.keys():
52 l3.RESOURCE_ATTRIBUTE_MAP[key].update(
53 l3_ext_gw_mode.EXTENDED_ATTRIBUTES_2_0.get(key, {}))
54 return l3.L3.get_resources()
56 def get_actions(self):
59 def get_request_extensions(self):
63 # A simple class for making a concrete class out of the mixin
64 # for the case of a plugin that integrates l3 routing.
65 class TestDbIntPlugin(test_l3.TestL3NatIntPlugin,
66 l3_gwmode_db.L3_NAT_db_mixin):
68 supported_extension_aliases = ["external-net", "router", "ext-gw-mode"]
71 # A simple class for making a concrete class out of the mixin
72 # for the case of a l3 router service plugin
73 class TestDbSepPlugin(test_l3.TestL3NatServicePlugin,
74 l3_gwmode_db.L3_NAT_db_mixin):
76 supported_extension_aliases = ["router", "ext-gw-mode"]
79 class TestGetEnableSnat(testscenarios.WithScenarios, base.BaseTestCase):
81 ('enabled', {'enable_snat_by_default': True}),
82 ('disabled', {'enable_snat_by_default': False})]
85 super(TestGetEnableSnat, self).setUp()
86 self.config(enable_snat_by_default=self.enable_snat_by_default)
88 def _test_get_enable_snat(self, expected, info):
89 observed = l3_gwmode_db.L3_NAT_dbonly_mixin._get_enable_snat(info)
90 self.assertEqual(expected, observed)
92 def test_get_enable_snat_without_gw_info(self):
93 self._test_get_enable_snat(self.enable_snat_by_default, {})
95 def test_get_enable_snat_without_enable_snat(self):
96 info = {'network_id': _uuid()}
97 self._test_get_enable_snat(self.enable_snat_by_default, info)
99 def test_get_enable_snat_with_snat_enabled(self):
100 self._test_get_enable_snat(True, {'enable_snat': True})
102 def test_get_enable_snat_with_snat_disabled(self):
103 self._test_get_enable_snat(False, {'enable_snat': False})
106 class TestL3GwModeMixin(testlib_api.SqlTestCase):
109 super(TestL3GwModeMixin, self).setUp()
110 plugin = __name__ + '.' + TestDbIntPlugin.__name__
111 self.setup_coreplugin(plugin)
112 self.target_object = TestDbIntPlugin()
114 ctx_patcher = mock.patch('neutron.context', autospec=True)
115 mock_context = ctx_patcher.start()
116 self.context = mock_context.get_admin_context()
117 # This ensure also calls to elevated work in unit tests
118 self.context.elevated.return_value = self.context
119 self.context.session = db_api.get_session()
120 # Create sample data for tests
121 self.ext_net_id = _uuid()
122 self.int_net_id = _uuid()
123 self.int_sub_id = _uuid()
124 self.tenant_id = 'the_tenant'
125 self.network = models_v2.Network(
127 tenant_id=self.tenant_id,
129 status=constants.NET_STATUS_ACTIVE)
130 self.net_ext = external_net_db.ExternalNetwork(
131 network_id=self.ext_net_id)
132 self.context.session.add(self.network)
133 # The following is to avoid complaints from SQLite on
134 # foreign key violations
135 self.context.session.flush()
136 self.context.session.add(self.net_ext)
137 self.router = l3_db.Router(
140 tenant_id=self.tenant_id,
142 status=constants.NET_STATUS_ACTIVE,
145 self.context.session.add(self.router)
146 self.context.session.flush()
147 self.router_gw_port = models_v2.Port(
149 tenant_id=self.tenant_id,
150 device_id=self.router.id,
151 device_owner=l3_db.DEVICE_OWNER_ROUTER_GW,
153 status=constants.PORT_STATUS_ACTIVE,
154 mac_address=FAKE_GW_PORT_MAC,
155 network_id=self.ext_net_id)
156 self.router.gw_port_id = self.router_gw_port.id
157 self.context.session.add(self.router)
158 self.context.session.add(self.router_gw_port)
159 self.context.session.flush()
160 self.fip_ext_port = models_v2.Port(
161 id=FAKE_FIP_EXT_PORT_ID,
162 tenant_id=self.tenant_id,
164 device_id=self.router.id,
165 device_owner=l3_db.DEVICE_OWNER_FLOATINGIP,
166 status=constants.PORT_STATUS_ACTIVE,
167 mac_address=FAKE_FIP_EXT_PORT_MAC,
168 network_id=self.ext_net_id)
169 self.context.session.add(self.fip_ext_port)
170 self.context.session.flush()
171 self.int_net = models_v2.Network(
173 tenant_id=self.tenant_id,
175 status=constants.NET_STATUS_ACTIVE)
176 self.int_sub = models_v2.Subnet(
178 tenant_id=self.tenant_id,
181 gateway_ip='3.3.3.1',
182 network_id=self.int_net_id)
183 self.router_port = models_v2.Port(
184 id=FAKE_ROUTER_PORT_ID,
185 tenant_id=self.tenant_id,
187 device_id=self.router.id,
188 device_owner=l3_db.DEVICE_OWNER_ROUTER_INTF,
189 status=constants.PORT_STATUS_ACTIVE,
190 mac_address=FAKE_ROUTER_PORT_MAC,
191 network_id=self.int_net_id)
192 self.router_port_ip_info = models_v2.IPAllocation(
193 port_id=self.router_port.id,
194 network_id=self.int_net.id,
195 subnet_id=self.int_sub_id,
196 ip_address='3.3.3.1')
197 self.context.session.add(self.int_net)
198 self.context.session.add(self.int_sub)
199 self.context.session.add(self.router_port)
200 self.context.session.add(self.router_port_ip_info)
201 self.context.session.flush()
202 self.fip_int_port = models_v2.Port(
203 id=FAKE_FIP_INT_PORT_ID,
204 tenant_id=self.tenant_id,
206 device_id='something',
207 device_owner=constants.DEVICE_OWNER_COMPUTE_PREFIX + 'nova',
208 status=constants.PORT_STATUS_ACTIVE,
209 mac_address=FAKE_FIP_INT_PORT_MAC,
210 network_id=self.int_net_id)
211 self.fip_int_ip_info = models_v2.IPAllocation(
212 port_id=self.fip_int_port.id,
213 network_id=self.int_net.id,
214 subnet_id=self.int_sub_id,
215 ip_address='3.3.3.3')
216 self.fip = l3_db.FloatingIP(
218 floating_ip_address='1.1.1.2',
219 floating_network_id=self.ext_net_id,
220 floating_port_id=FAKE_FIP_EXT_PORT_ID,
222 fixed_ip_address=None,
224 self.context.session.add(self.fip_int_port)
225 self.context.session.add(self.fip_int_ip_info)
226 self.context.session.add(self.fip)
227 self.context.session.flush()
228 self.fip_request = {'port_id': FAKE_FIP_INT_PORT_ID,
229 'tenant_id': self.tenant_id}
231 def _get_gwports_dict(self, gw_ports):
232 return dict((gw_port['id'], gw_port)
233 for gw_port in gw_ports)
235 def _reset_ext_gw(self):
236 # Reset external gateway
237 self.router.gw_port_id = None
238 self.context.session.add(self.router)
239 self.context.session.flush()
241 def _test_update_router_gw(self, current_enable_snat, gw_info=None,
242 expected_enable_snat=True):
243 if not current_enable_snat:
244 previous_gw_info = {'network_id': self.ext_net_id,
245 'enable_snat': current_enable_snat}
246 self.target_object._update_router_gw_info(
247 self.context, self.router.id, previous_gw_info)
249 self.target_object._update_router_gw_info(
250 self.context, self.router.id, gw_info)
251 router = self.target_object._get_router(
252 self.context, self.router.id)
254 self.assertEqual(FAKE_GW_PORT_ID,
256 self.assertEqual(FAKE_GW_PORT_MAC,
257 router.gw_port.mac_address)
258 except AttributeError:
259 self.assertIsNone(router.gw_port)
260 self.assertEqual(expected_enable_snat, router.enable_snat)
262 def test_update_router_gw_with_gw_info_none(self):
263 self._test_update_router_gw(current_enable_snat=True)
265 def test_update_router_gw_without_info_and_snat_disabled_previously(self):
266 self._test_update_router_gw(current_enable_snat=False)
268 def test_update_router_gw_with_network_only(self):
269 info = {'network_id': self.ext_net_id}
270 self._test_update_router_gw(current_enable_snat=True, gw_info=info)
272 def test_update_router_gw_with_network_and_snat_disabled_previously(self):
273 info = {'network_id': self.ext_net_id}
274 self._test_update_router_gw(current_enable_snat=False, gw_info=info)
276 def test_update_router_gw_with_snat_disabled(self):
277 info = {'network_id': self.ext_net_id,
278 'enable_snat': False}
279 self._test_update_router_gw(
280 current_enable_snat=True, gw_info=info, expected_enable_snat=False)
282 def test_update_router_gw_with_snat_enabled(self):
283 info = {'network_id': self.ext_net_id,
285 self._test_update_router_gw(current_enable_snat=False, gw_info=info)
287 def test_make_router_dict_no_ext_gw(self):
289 router_dict = self.target_object._make_router_dict(self.router)
290 self.assertIsNone(router_dict[l3.EXTERNAL_GW_INFO])
292 def test_make_router_dict_with_ext_gw(self):
293 router_dict = self.target_object._make_router_dict(self.router)
294 self.assertEqual({'network_id': self.ext_net_id,
296 'external_fixed_ips': []},
297 router_dict[l3.EXTERNAL_GW_INFO])
299 def test_make_router_dict_with_ext_gw_snat_disabled(self):
300 self.router.enable_snat = False
301 router_dict = self.target_object._make_router_dict(self.router)
302 self.assertEqual({'network_id': self.ext_net_id,
303 'enable_snat': False,
304 'external_fixed_ips': []},
305 router_dict[l3.EXTERNAL_GW_INFO])
307 def test_build_routers_list_no_ext_gw(self):
309 router_dict = self.target_object._make_router_dict(self.router)
310 routers = self.target_object._build_routers_list(self.context,
313 self.assertEqual(1, len(routers))
315 self.assertIsNone(router.get('gw_port'))
316 self.assertIsNone(router.get('enable_snat'))
318 def test_build_routers_list_with_ext_gw(self):
319 router_dict = self.target_object._make_router_dict(self.router)
320 routers = self.target_object._build_routers_list(
321 self.context, [router_dict],
322 self._get_gwports_dict([self.router.gw_port]))
323 self.assertEqual(1, len(routers))
325 self.assertIsNotNone(router.get('gw_port'))
326 self.assertEqual(FAKE_GW_PORT_ID, router['gw_port']['id'])
327 self.assertTrue(router.get('enable_snat'))
329 def test_build_routers_list_with_ext_gw_snat_disabled(self):
330 self.router.enable_snat = False
331 router_dict = self.target_object._make_router_dict(self.router)
332 routers = self.target_object._build_routers_list(
333 self.context, [router_dict],
334 self._get_gwports_dict([self.router.gw_port]))
335 self.assertEqual(1, len(routers))
337 self.assertIsNotNone(router.get('gw_port'))
338 self.assertEqual(FAKE_GW_PORT_ID, router['gw_port']['id'])
339 self.assertFalse(router.get('enable_snat'))
341 def test_build_routers_list_with_gw_port_mismatch(self):
342 router_dict = self.target_object._make_router_dict(self.router)
343 routers = self.target_object._build_routers_list(
344 self.context, [router_dict], {})
345 self.assertEqual(1, len(routers))
347 self.assertIsNone(router.get('gw_port'))
348 self.assertIsNone(router.get('enable_snat'))
351 class ExtGwModeIntTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase,
352 test_l3.L3NatTestCaseMixin):
354 def setUp(self, plugin=None, svc_plugins=None, ext_mgr=None):
355 # Store l3 resource attribute map as it will be updated
356 self._l3_attribute_map_bk = {}
357 for item in l3.RESOURCE_ATTRIBUTE_MAP:
358 self._l3_attribute_map_bk[item] = (
359 l3.RESOURCE_ATTRIBUTE_MAP[item].copy())
361 'neutron.tests.unit.extensions.test_l3_ext_gw_mode.'
363 # for these tests we need to enable overlapping ips
364 cfg.CONF.set_default('allow_overlapping_ips', True)
365 ext_mgr = ext_mgr or TestExtensionManager()
366 super(ExtGwModeIntTestCase, self).setUp(plugin=plugin,
368 service_plugins=svc_plugins)
369 self.addCleanup(self.restore_l3_attribute_map)
371 def restore_l3_attribute_map(self):
372 l3.RESOURCE_ATTRIBUTE_MAP = self._l3_attribute_map_bk
375 super(ExtGwModeIntTestCase, self).tearDown()
377 def _set_router_external_gateway(self, router_id, network_id,
379 expected_code=exc.HTTPOk.code,
380 neutron_context=None):
381 ext_gw_info = {'network_id': network_id}
382 # Need to set enable_snat also if snat_enabled == False
383 if snat_enabled is not None:
384 ext_gw_info['enable_snat'] = snat_enabled
385 return self._update('routers', router_id,
386 {'router': {'external_gateway_info':
388 expected_code=expected_code,
389 neutron_context=neutron_context)
391 def test_router_create_show_no_ext_gwinfo(self):
394 expected_value = [('name', name), ('tenant_id', tenant_id),
395 ('admin_state_up', True), ('status', 'ACTIVE'),
396 ('external_gateway_info', None)]
397 with self.router(name=name, admin_state_up=True,
398 tenant_id=tenant_id) as router:
399 res = self._show('routers', router['router']['id'])
400 for k, v in expected_value:
401 self.assertEqual(res['router'][k], v)
403 def _test_router_create_show_ext_gwinfo(self, snat_input_value,
404 snat_expected_value):
407 with self.subnet() as s:
408 ext_net_id = s['subnet']['network_id']
409 self._set_net_external(ext_net_id)
410 input_value = {'network_id': ext_net_id}
411 if snat_input_value in (True, False):
412 input_value['enable_snat'] = snat_input_value
413 expected_value = [('name', name), ('tenant_id', tenant_id),
414 ('admin_state_up', True), ('status', 'ACTIVE'),
415 ('external_gateway_info',
416 {'network_id': ext_net_id,
417 'enable_snat': snat_expected_value,
418 'external_fixed_ips': [{
419 'ip_address': mock.ANY,
420 'subnet_id': s['subnet']['id']}]})]
422 name=name, admin_state_up=True, tenant_id=tenant_id,
423 external_gateway_info=input_value) as router:
424 res = self._show('routers', router['router']['id'])
425 for k, v in expected_value:
426 self.assertEqual(res['router'][k], v)
428 def test_router_create_show_ext_gwinfo_default(self):
429 self._test_router_create_show_ext_gwinfo(None, True)
431 def test_router_create_show_ext_gwinfo_with_snat_enabled(self):
432 self._test_router_create_show_ext_gwinfo(True, True)
434 def test_router_create_show_ext_gwinfo_with_snat_disabled(self):
435 self._test_router_create_show_ext_gwinfo(False, False)
437 def _test_router_update_ext_gwinfo(self, snat_input_value,
438 snat_expected_value=False,
439 expected_http_code=exc.HTTPOk.code):
440 with self.router() as r:
441 with self.subnet() as s:
443 ext_net_id = s['subnet']['network_id']
444 self._set_net_external(ext_net_id)
445 self._set_router_external_gateway(
446 r['router']['id'], ext_net_id,
447 snat_enabled=snat_input_value,
448 expected_code=expected_http_code)
449 if expected_http_code != exc.HTTPOk.code:
451 body = self._show('routers', r['router']['id'])
452 res_gw_info = body['router']['external_gateway_info']
453 self.assertEqual(res_gw_info['network_id'], ext_net_id)
454 self.assertEqual(res_gw_info['enable_snat'],
457 self._remove_external_gateway_from_router(
458 r['router']['id'], ext_net_id)
460 def test_router_update_ext_gwinfo_default(self):
461 self._test_router_update_ext_gwinfo(None, True)
463 def test_router_update_ext_gwinfo_with_snat_enabled(self):
464 self._test_router_update_ext_gwinfo(True, True)
466 def test_router_update_ext_gwinfo_with_snat_disabled(self):
467 self._test_router_update_ext_gwinfo(False, False)
469 def test_router_update_ext_gwinfo_with_invalid_snat_setting(self):
470 self._test_router_update_ext_gwinfo(
471 'xxx', None, expected_http_code=exc.HTTPBadRequest.code)
474 class ExtGwModeSepTestCase(ExtGwModeIntTestCase):
476 def setUp(self, plugin=None):
477 # Store l3 resource attribute map as it will be updated
478 self._l3_attribute_map_bk = {}
479 for item in l3.RESOURCE_ATTRIBUTE_MAP:
480 self._l3_attribute_map_bk[item] = (
481 l3.RESOURCE_ATTRIBUTE_MAP[item].copy())
483 'neutron.tests.unit.extensions.test_l3.TestNoL3NatPlugin')
484 # the L3 service plugin
485 l3_plugin = ('neutron.tests.unit.extensions.test_l3_ext_gw_mode.'
487 svc_plugins = {'l3_plugin_name': l3_plugin}
488 # for these tests we need to enable overlapping ips
489 cfg.CONF.set_default('allow_overlapping_ips', True)
490 super(ExtGwModeSepTestCase, self).setUp(plugin=plugin,
491 svc_plugins=svc_plugins)
492 self.addCleanup(self.restore_l3_attribute_map)