1 # Copyright 2013, Nachi Ueno, NTT MCL, 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
16 from oslo_config import cfg
17 from oslo_utils import uuidutils
20 from neutron.common import constants
21 from neutron.common import utils
22 from neutron.db import extraroute_db
23 from neutron.extensions import extraroute
24 from neutron.extensions import l3
25 from neutron.tests.unit.api.v2 import test_base
26 from neutron.tests.unit.extensions import test_l3 as test_l3
29 _uuid = uuidutils.generate_uuid
30 _get_path = test_base._get_path
33 class ExtraRouteTestExtensionManager(object):
35 def get_resources(self):
36 l3.RESOURCE_ATTRIBUTE_MAP['routers'].update(
37 extraroute.EXTENDED_ATTRIBUTES_2_0['routers'])
38 return l3.L3.get_resources()
40 def get_actions(self):
43 def get_request_extensions(self):
47 # This plugin class is for tests with plugin that integrates L3.
48 class TestExtraRouteIntPlugin(test_l3.TestL3NatIntPlugin,
49 extraroute_db.ExtraRoute_db_mixin):
50 supported_extension_aliases = ["external-net", "router", "extraroute"]
53 # A fake l3 service plugin class with extra route capability for
54 # plugins that delegate away L3 routing functionality
55 class TestExtraRouteL3NatServicePlugin(test_l3.TestL3NatServicePlugin,
56 extraroute_db.ExtraRoute_db_mixin):
57 supported_extension_aliases = ["router", "extraroute"]
60 class ExtraRouteDBTestCaseBase(object):
61 def _routes_update_prepare(self, router_id, subnet_id,
62 port_id, routes, skip_add=False):
64 self._router_interface_action('add', router_id, subnet_id, port_id)
65 self._update('routers', router_id, {'router': {'routes': routes}})
66 return self._show('routers', router_id)
68 def _routes_update_cleanup(self, port_id, subnet_id, router_id, routes):
69 self._update('routers', router_id, {'router': {'routes': routes}})
70 self._router_interface_action('remove', router_id, subnet_id, port_id)
72 def test_route_update_with_one_route(self):
73 routes = [{'destination': '135.207.0.0/16', 'nexthop': '10.0.1.3'}]
74 with self.router() as r:
75 with self.subnet(cidr='10.0.1.0/24') as s:
76 with self.port(subnet=s) as p:
77 body = self._routes_update_prepare(r['router']['id'],
78 None, p['port']['id'],
80 self.assertEqual(body['router']['routes'], routes)
81 self._routes_update_cleanup(p['port']['id'],
82 None, r['router']['id'], [])
84 def test_route_clear_routes_with_None(self):
85 routes = [{'destination': '135.207.0.0/16',
86 'nexthop': '10.0.1.3'},
87 {'destination': '12.0.0.0/8',
88 'nexthop': '10.0.1.4'},
89 {'destination': '141.212.0.0/16',
90 'nexthop': '10.0.1.5'}]
91 with self.router() as r:
92 with self.subnet(cidr='10.0.1.0/24') as s:
93 with self.port(subnet=s) as p:
94 self._routes_update_prepare(r['router']['id'],
95 None, p['port']['id'], routes)
96 body = self._update('routers', r['router']['id'],
97 {'router': {'routes': None}})
98 self.assertEqual([], body['router']['routes'])
99 self._routes_update_cleanup(p['port']['id'],
100 None, r['router']['id'], [])
102 def test_router_interface_in_use_by_route(self):
103 routes = [{'destination': '135.207.0.0/16',
104 'nexthop': '10.0.1.3'}]
105 with self.router() as r:
106 with self.subnet(cidr='10.0.1.0/24') as s:
107 with self.port(subnet=s) as p:
108 body = self._routes_update_prepare(r['router']['id'],
109 None, p['port']['id'],
111 self.assertEqual(body['router']['routes'], routes)
112 self._router_interface_action(
117 expected_code=exc.HTTPConflict.code)
119 self._routes_update_cleanup(p['port']['id'],
120 None, r['router']['id'], [])
122 def test_route_update_with_multi_routes(self):
123 routes = [{'destination': '135.207.0.0/16',
124 'nexthop': '10.0.1.3'},
125 {'destination': '12.0.0.0/8',
126 'nexthop': '10.0.1.4'},
127 {'destination': '141.212.0.0/16',
128 'nexthop': '10.0.1.5'}]
129 with self.router() as r:
130 with self.subnet(cidr='10.0.1.0/24') as s:
131 with self.port(subnet=s) as p:
132 body = self._routes_update_prepare(r['router']['id'],
133 None, p['port']['id'],
136 sorted(body['router']['routes'],
137 key=utils.safe_sort_key),
138 sorted(routes, key=utils.safe_sort_key))
139 self._routes_update_cleanup(p['port']['id'],
140 None, r['router']['id'], [])
142 def test_routes_update_for_multiple_routers(self):
143 routes1 = [{'destination': '135.207.0.0/16',
144 'nexthop': '10.0.0.3'}]
145 routes2 = [{'destination': '12.0.0.0/8',
146 'nexthop': '10.0.0.4'}]
147 with self.router() as r1,\
148 self.router() as r2,\
149 self.subnet(cidr='10.0.0.0/24') as s:
150 with self.port(subnet=s) as p1, self.port(subnet=s) as p2:
151 body = self._routes_update_prepare(r1['router']['id'],
152 None, p1['port']['id'],
154 self.assertEqual(body['router']['routes'], routes1)
156 body = self._routes_update_prepare(r2['router']['id'],
157 None, p2['port']['id'],
159 self.assertEqual(body['router']['routes'], routes2)
161 self._routes_update_cleanup(p1['port']['id'],
162 None, r1['router']['id'], [])
163 self._routes_update_cleanup(p2['port']['id'],
164 None, r2['router']['id'], [])
166 def test_router_update_delete_routes(self):
167 routes_orig = [{'destination': '135.207.0.0/16',
168 'nexthop': '10.0.1.3'},
169 {'destination': '12.0.0.0/8',
170 'nexthop': '10.0.1.4'},
171 {'destination': '141.212.0.0/16',
172 'nexthop': '10.0.1.5'}]
173 routes_left = [{'destination': '135.207.0.0/16',
174 'nexthop': '10.0.1.3'},
175 {'destination': '141.212.0.0/16',
176 'nexthop': '10.0.1.5'}]
177 with self.router() as r:
178 with self.subnet(cidr='10.0.1.0/24') as s:
179 with self.port(subnet=s) as p:
180 body = self._routes_update_prepare(r['router']['id'],
181 None, p['port']['id'],
184 sorted(body['router']['routes'],
185 key=utils.safe_sort_key),
186 sorted(routes_orig, key=utils.safe_sort_key))
187 body = self._routes_update_prepare(r['router']['id'],
188 None, p['port']['id'],
192 sorted(body['router']['routes'],
193 key=utils.safe_sort_key),
194 sorted(routes_left, key=utils.safe_sort_key))
195 self._routes_update_cleanup(p['port']['id'],
196 None, r['router']['id'], [])
198 def _test_malformed_route(self, routes):
199 with self.router() as r:
200 with self.subnet(cidr='10.0.1.0/24') as s:
201 with self.port(subnet=s) as p:
202 self._router_interface_action('add',
207 self._update('routers', r['router']['id'],
208 {'router': {'routes': routes}},
209 expected_code=exc.HTTPBadRequest.code)
211 self._router_interface_action('remove',
216 def test_no_destination_route(self):
217 self._test_malformed_route([{'nexthop': '10.0.1.6'}])
219 def test_no_nexthop_route(self):
220 self._test_malformed_route({'destination': '135.207.0.0/16'})
222 def test_none_destination(self):
223 self._test_malformed_route([{'destination': None,
224 'nexthop': '10.0.1.3'}])
226 def test_none_nexthop(self):
227 self._test_malformed_route([{'destination': '135.207.0.0/16',
230 def test_nexthop_is_port_ip(self):
231 with self.router() as r:
232 with self.subnet(cidr='10.0.1.0/24') as s:
233 with self.port(subnet=s) as p:
234 self._router_interface_action('add',
238 port_ip = p['port']['fixed_ips'][0]['ip_address']
239 routes = [{'destination': '135.207.0.0/16',
242 self._update('routers', r['router']['id'],
243 {'router': {'routes':
245 expected_code=exc.HTTPBadRequest.code)
247 self._router_interface_action('remove',
252 def test_router_update_with_too_many_routes(self):
253 with self.router() as r:
254 with self.subnet(cidr='10.0.1.0/24') as s:
255 with self.port(subnet=s) as p:
256 self._router_interface_action('add',
261 routes = [{'destination': '135.207.0.0/16',
262 'nexthop': '10.0.1.3'},
263 {'destination': '12.0.0.0/8',
264 'nexthop': '10.0.1.4'},
265 {'destination': '141.212.0.0/16',
266 'nexthop': '10.0.1.5'},
267 {'destination': '192.168.0.0/16',
268 'nexthop': '10.0.1.6'}]
270 self._update('routers', r['router']['id'],
271 {'router': {'routes':
273 expected_code=exc.HTTPBadRequest.code)
276 self._router_interface_action('remove',
281 def test_router_update_with_dup_address(self):
282 with self.router() as r:
283 with self.subnet(cidr='10.0.1.0/24') as s:
284 with self.port(subnet=s) as p:
285 self._router_interface_action('add',
290 routes = [{'destination': '135.207.0.0/16',
291 'nexthop': '10.0.1.3'},
292 {'destination': '135.207.0.0/16',
293 'nexthop': '10.0.1.3'}]
295 self._update('routers', r['router']['id'],
296 {'router': {'routes':
298 expected_code=exc.HTTPBadRequest.code)
301 self._router_interface_action('remove',
306 def test_router_update_with_invalid_ip_address(self):
307 with self.router() as r:
308 with self.subnet(cidr='10.0.1.0/24') as s:
309 with self.port(subnet=s) as p:
310 self._router_interface_action('add',
315 routes = [{'destination': '512.207.0.0/16',
316 'nexthop': '10.0.1.3'}]
318 self._update('routers', r['router']['id'],
319 {'router': {'routes':
321 expected_code=exc.HTTPBadRequest.code)
323 routes = [{'destination': '127.207.0.0/48',
324 'nexthop': '10.0.1.3'}]
326 self._update('routers', r['router']['id'],
327 {'router': {'routes':
329 expected_code=exc.HTTPBadRequest.code)
331 routes = [{'destination': 'invalid_ip_address',
332 'nexthop': '10.0.1.3'}]
334 self._update('routers', r['router']['id'],
335 {'router': {'routes':
337 expected_code=exc.HTTPBadRequest.code)
340 self._router_interface_action('remove',
345 def test_router_update_with_invalid_nexthop_ip(self):
346 with self.router() as r:
347 with self.subnet(cidr='10.0.1.0/24') as s:
348 with self.port(subnet=s) as p:
349 self._router_interface_action('add',
354 routes = [{'destination': '127.207.0.0/16',
355 'nexthop': ' 300.10.10.4'}]
357 self._update('routers', r['router']['id'],
358 {'router': {'routes':
360 expected_code=exc.HTTPBadRequest.code)
363 self._router_interface_action('remove',
368 def test_router_update_with_nexthop_is_outside_port_subnet(self):
369 with self.router() as r:
370 with self.subnet(cidr='10.0.1.0/24') as s:
371 with self.port(subnet=s) as p:
372 self._router_interface_action('add',
377 routes = [{'destination': '127.207.0.0/16',
378 'nexthop': ' 20.10.10.4'}]
380 self._update('routers', r['router']['id'],
381 {'router': {'routes':
383 expected_code=exc.HTTPBadRequest.code)
386 self._router_interface_action('remove',
391 def test_router_update_on_external_port(self):
392 with self.router() as r:
393 with self.subnet(cidr='10.0.1.0/24') as s:
394 self._set_net_external(s['subnet']['network_id'])
395 self._add_external_gateway_to_router(
397 s['subnet']['network_id'])
398 body = self._show('routers', r['router']['id'])
399 net_id = body['router']['external_gateway_info']['network_id']
400 self.assertEqual(net_id, s['subnet']['network_id'])
401 port_res = self._list_ports(
404 s['subnet']['network_id'],
405 tenant_id=r['router']['tenant_id'],
406 device_owner=constants.DEVICE_OWNER_ROUTER_GW)
407 port_list = self.deserialize('json', port_res)
408 self.assertEqual(len(port_list['ports']), 1)
410 routes = [{'destination': '135.207.0.0/16',
411 'nexthop': '10.0.1.3'}]
413 body = self._update('routers', r['router']['id'],
414 {'router': {'routes':
417 body = self._show('routers', r['router']['id'])
418 self.assertEqual(body['router']['routes'],
421 self._remove_external_gateway_from_router(
423 s['subnet']['network_id'])
424 body = self._show('routers', r['router']['id'])
425 gw_info = body['router']['external_gateway_info']
426 self.assertIsNone(gw_info)
428 def test_router_list_with_sort(self):
429 with self.router(name='router1') as router1,\
430 self.router(name='router2') as router2,\
431 self.router(name='router3') as router3:
432 self._test_list_with_sort('router', (router3, router2, router1),
435 def test_router_list_with_pagination(self):
436 with self.router(name='router1') as router1,\
437 self.router(name='router2') as router2,\
438 self.router(name='router3') as router3:
439 self._test_list_with_pagination('router',
440 (router1, router2, router3),
441 ('name', 'asc'), 2, 2)
443 def test_router_list_with_pagination_reverse(self):
444 with self.router(name='router1') as router1,\
445 self.router(name='router2') as router2,\
446 self.router(name='router3') as router3:
447 self._test_list_with_pagination_reverse('router',
450 ('name', 'asc'), 2, 2)
453 class ExtraRouteDBIntTestCase(test_l3.L3NatDBIntTestCase,
454 ExtraRouteDBTestCaseBase):
456 def setUp(self, plugin=None, ext_mgr=None):
458 plugin = ('neutron.tests.unit.extensions.test_extraroute.'
459 'TestExtraRouteIntPlugin')
460 # for these tests we need to enable overlapping ips
461 cfg.CONF.set_default('allow_overlapping_ips', True)
462 cfg.CONF.set_default('max_routes', 3)
463 ext_mgr = ExtraRouteTestExtensionManager()
464 super(test_l3.L3BaseForIntTests, self).setUp(plugin=plugin,
466 self.setup_notification_driver()
469 class ExtraRouteDBSepTestCase(test_l3.L3NatDBSepTestCase,
470 ExtraRouteDBTestCaseBase):
472 # the plugin without L3 support
473 plugin = 'neutron.tests.unit.extensions.test_l3.TestNoL3NatPlugin'
474 # the L3 service plugin
475 l3_plugin = ('neutron.tests.unit.extensions.test_extraroute.'
476 'TestExtraRouteL3NatServicePlugin')
477 service_plugins = {'l3_plugin_name': l3_plugin}
479 # for these tests we need to enable overlapping ips
480 cfg.CONF.set_default('allow_overlapping_ips', True)
481 cfg.CONF.set_default('max_routes', 3)
482 ext_mgr = ExtraRouteTestExtensionManager()
483 super(test_l3.L3BaseForSepTests, self).setUp(
484 plugin=plugin, ext_mgr=ext_mgr,
485 service_plugins=service_plugins)
487 self.setup_notification_driver()