371056d51164d66d3246762f67bfecf1836e10db
[openstack-build/neutron-build.git] / neutron / tests / unit / extensions / test_extraroute.py
1 # Copyright 2013, Nachi Ueno, NTT MCL, Inc.
2 # All Rights Reserved.
3 #
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
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
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
14 #    under the License.
15
16 from oslo_config import cfg
17 from oslo_utils import uuidutils
18 from webob import exc
19
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
27
28
29 _uuid = uuidutils.generate_uuid
30 _get_path = test_base._get_path
31
32
33 class ExtraRouteTestExtensionManager(object):
34
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()
39
40     def get_actions(self):
41         return []
42
43     def get_request_extensions(self):
44         return []
45
46
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"]
51
52
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"]
58
59
60 class ExtraRouteDBTestCaseBase(object):
61     def _routes_update_prepare(self, router_id, subnet_id,
62                                port_id, routes, skip_add=False):
63         if not skip_add:
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)
67
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)
71
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'],
79                                                        routes)
80                     self.assertEqual(body['router']['routes'], routes)
81                     self._routes_update_cleanup(p['port']['id'],
82                                                 None, r['router']['id'], [])
83
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'], [])
101
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'],
110                                                        routes)
111                     self.assertEqual(body['router']['routes'], routes)
112                     self._router_interface_action(
113                         'remove',
114                         r['router']['id'],
115                         None,
116                         p['port']['id'],
117                         expected_code=exc.HTTPConflict.code)
118
119                     self._routes_update_cleanup(p['port']['id'],
120                                                 None, r['router']['id'], [])
121
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'],
134                                                        routes)
135                     self.assertEqual(
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'], [])
141
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'],
153                                                    routes1)
154                 self.assertEqual(body['router']['routes'], routes1)
155
156                 body = self._routes_update_prepare(r2['router']['id'],
157                                                    None, p2['port']['id'],
158                                                    routes2)
159                 self.assertEqual(body['router']['routes'], routes2)
160
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'], [])
165
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'],
182                                                        routes_orig)
183                     self.assertEqual(
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'],
189                                                        routes_left,
190                                                        skip_add=True)
191                     self.assertEqual(
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'], [])
197
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',
203                                                   r['router']['id'],
204                                                   None,
205                                                   p['port']['id'])
206
207                     self._update('routers', r['router']['id'],
208                                  {'router': {'routes': routes}},
209                                  expected_code=exc.HTTPBadRequest.code)
210                     # clean-up
211                     self._router_interface_action('remove',
212                                                   r['router']['id'],
213                                                   None,
214                                                   p['port']['id'])
215
216     def test_no_destination_route(self):
217         self._test_malformed_route([{'nexthop': '10.0.1.6'}])
218
219     def test_no_nexthop_route(self):
220         self._test_malformed_route({'destination': '135.207.0.0/16'})
221
222     def test_none_destination(self):
223         self._test_malformed_route([{'destination': None,
224                                      'nexthop': '10.0.1.3'}])
225
226     def test_none_nexthop(self):
227         self._test_malformed_route([{'destination': '135.207.0.0/16',
228                                      'nexthop': None}])
229
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',
235                                                   r['router']['id'],
236                                                   None,
237                                                   p['port']['id'])
238                     port_ip = p['port']['fixed_ips'][0]['ip_address']
239                     routes = [{'destination': '135.207.0.0/16',
240                                'nexthop': port_ip}]
241
242                     self._update('routers', r['router']['id'],
243                                  {'router': {'routes':
244                                              routes}},
245                                  expected_code=exc.HTTPBadRequest.code)
246                     # clean-up
247                     self._router_interface_action('remove',
248                                                   r['router']['id'],
249                                                   None,
250                                                   p['port']['id'])
251
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',
257                                                   r['router']['id'],
258                                                   None,
259                                                   p['port']['id'])
260
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'}]
269
270                     self._update('routers', r['router']['id'],
271                                  {'router': {'routes':
272                                              routes}},
273                                  expected_code=exc.HTTPBadRequest.code)
274
275                     # clean-up
276                     self._router_interface_action('remove',
277                                                   r['router']['id'],
278                                                   None,
279                                                   p['port']['id'])
280
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',
286                                                   r['router']['id'],
287                                                   None,
288                                                   p['port']['id'])
289
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'}]
294
295                     self._update('routers', r['router']['id'],
296                                  {'router': {'routes':
297                                              routes}},
298                                  expected_code=exc.HTTPBadRequest.code)
299
300                     # clean-up
301                     self._router_interface_action('remove',
302                                                   r['router']['id'],
303                                                   None,
304                                                   p['port']['id'])
305
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',
311                                                   r['router']['id'],
312                                                   None,
313                                                   p['port']['id'])
314
315                     routes = [{'destination': '512.207.0.0/16',
316                                'nexthop': '10.0.1.3'}]
317
318                     self._update('routers', r['router']['id'],
319                                  {'router': {'routes':
320                                              routes}},
321                                  expected_code=exc.HTTPBadRequest.code)
322
323                     routes = [{'destination': '127.207.0.0/48',
324                                'nexthop': '10.0.1.3'}]
325
326                     self._update('routers', r['router']['id'],
327                                  {'router': {'routes':
328                                              routes}},
329                                  expected_code=exc.HTTPBadRequest.code)
330
331                     routes = [{'destination': 'invalid_ip_address',
332                                'nexthop': '10.0.1.3'}]
333
334                     self._update('routers', r['router']['id'],
335                                  {'router': {'routes':
336                                              routes}},
337                                  expected_code=exc.HTTPBadRequest.code)
338
339                     # clean-up
340                     self._router_interface_action('remove',
341                                                   r['router']['id'],
342                                                   None,
343                                                   p['port']['id'])
344
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',
350                                                   r['router']['id'],
351                                                   None,
352                                                   p['port']['id'])
353
354                     routes = [{'destination': '127.207.0.0/16',
355                                'nexthop': ' 300.10.10.4'}]
356
357                     self._update('routers', r['router']['id'],
358                                  {'router': {'routes':
359                                              routes}},
360                                  expected_code=exc.HTTPBadRequest.code)
361
362                     # clean-up
363                     self._router_interface_action('remove',
364                                                   r['router']['id'],
365                                                   None,
366                                                   p['port']['id'])
367
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',
373                                                   r['router']['id'],
374                                                   None,
375                                                   p['port']['id'])
376
377                     routes = [{'destination': '127.207.0.0/16',
378                                'nexthop': ' 20.10.10.4'}]
379
380                     self._update('routers', r['router']['id'],
381                                  {'router': {'routes':
382                                              routes}},
383                                  expected_code=exc.HTTPBadRequest.code)
384
385                     # clean-up
386                     self._router_interface_action('remove',
387                                                   r['router']['id'],
388                                                   None,
389                                                   p['port']['id'])
390
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(
396                     r['router']['id'],
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(
402                     'json',
403                     200,
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)
409
410                 routes = [{'destination': '135.207.0.0/16',
411                            'nexthop': '10.0.1.3'}]
412
413                 body = self._update('routers', r['router']['id'],
414                                     {'router': {'routes':
415                                                 routes}})
416
417                 body = self._show('routers', r['router']['id'])
418                 self.assertEqual(body['router']['routes'],
419                                  routes)
420
421                 self._remove_external_gateway_from_router(
422                     r['router']['id'],
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)
427
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),
433                                       [('name', 'desc')])
434
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)
442
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',
448                                                     (router1, router2,
449                                                      router3),
450                                                     ('name', 'asc'), 2, 2)
451
452
453 class ExtraRouteDBIntTestCase(test_l3.L3NatDBIntTestCase,
454                               ExtraRouteDBTestCaseBase):
455
456     def setUp(self, plugin=None, ext_mgr=None):
457         if not plugin:
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,
465                                                      ext_mgr=ext_mgr)
466         self.setup_notification_driver()
467
468
469 class ExtraRouteDBSepTestCase(test_l3.L3NatDBSepTestCase,
470                               ExtraRouteDBTestCaseBase):
471     def setUp(self):
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}
478
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)
486
487         self.setup_notification_driver()