backing up
[vsorcdistro/.git] / ryu / build / lib.linux-armv7l-2.7 / ryu / tests / unit / services / protocols / bgp / core_managers / test_table_manager.py
1 # Copyright (C) 2016 Nippon Telegraph and Telephone Corporation.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain 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,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12 # implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15
16 from collections import OrderedDict
17 import unittest
18 import logging
19 try:
20     import mock  # Python 2
21 except ImportError:
22     from unittest import mock  # Python 3
23
24 from nose.tools import ok_, eq_, raises
25
26 from ryu.lib.packet.bgp import BGPPathAttributeOrigin
27 from ryu.lib.packet.bgp import BGPPathAttributeAsPath
28 from ryu.lib.packet.bgp import BGP_ATTR_ORIGIN_IGP
29 from ryu.lib.packet.bgp import BGP_ATTR_TYPE_ORIGIN
30 from ryu.lib.packet.bgp import BGP_ATTR_TYPE_AS_PATH
31 from ryu.lib.packet.bgp import BGP_ATTR_TYPE_EXTENDED_COMMUNITIES
32 from ryu.lib.packet.bgp import IPAddrPrefix
33 from ryu.lib.packet.bgp import IP6AddrPrefix
34 from ryu.lib.packet.bgp import EvpnArbitraryEsi
35 from ryu.lib.packet.bgp import EvpnLACPEsi
36 from ryu.lib.packet.bgp import EvpnEthernetAutoDiscoveryNLRI
37 from ryu.lib.packet.bgp import EvpnMacIPAdvertisementNLRI
38 from ryu.lib.packet.bgp import EvpnInclusiveMulticastEthernetTagNLRI
39 from ryu.lib.packet.bgp import FlowSpecIPv4NLRI
40 from ryu.lib.packet.bgp import BGPPathAttributeExtendedCommunities
41 from ryu.services.protocols.bgp.bgpspeaker import EVPN_MAX_ET
42 from ryu.services.protocols.bgp.bgpspeaker import ESI_TYPE_LACP
43 from ryu.services.protocols.bgp.bgpspeaker import FLOWSPEC_FAMILY_IPV4
44 from ryu.services.protocols.bgp.bgpspeaker import FLOWSPEC_FAMILY_VPNV4
45 from ryu.services.protocols.bgp.bgpspeaker import FLOWSPEC_TA_SAMPLE
46 from ryu.services.protocols.bgp.bgpspeaker import FLOWSPEC_TA_TERMINAL
47 from ryu.services.protocols.bgp.core import BgpCoreError
48 from ryu.services.protocols.bgp.core_managers import table_manager
49 from ryu.services.protocols.bgp.rtconf.vrfs import VRF_RF_IPV4
50 from ryu.services.protocols.bgp.rtconf.vrfs import VRF_RF_IPV6
51 from ryu.services.protocols.bgp.rtconf.vrfs import VRF_RF_L2_EVPN
52 from ryu.services.protocols.bgp.rtconf.vrfs import VRF_RF_IPV4_FLOWSPEC
53 from ryu.services.protocols.bgp.utils.bgp import create_v4flowspec_actions
54
55
56 LOG = logging.getLogger(__name__)
57
58
59 class Test_TableCoreManager(unittest.TestCase):
60     """
61     Test case for bgp.core_managers.table_manager.TableCoreManager
62     """
63
64     @mock.patch(
65         'ryu.services.protocols.bgp.core_managers.TableCoreManager.__init__',
66         mock.MagicMock(return_value=None))
67     def _test_update_vrf_table(self, prefix_inst, route_dist, prefix_str,
68                                next_hop, route_family, route_type,
69                                is_withdraw=False, **kwargs):
70         # Instantiate TableCoreManager
71         tbl_mng = table_manager.TableCoreManager(None, None)
72         vrf_table_mock = mock.MagicMock()
73         tbl_mng._tables = {(route_dist, route_family): vrf_table_mock}
74
75         # Test
76         tbl_mng.update_vrf_table(
77             route_dist=route_dist,
78             prefix=prefix_str,
79             next_hop=next_hop,
80             route_family=route_family,
81             route_type=route_type,
82             is_withdraw=is_withdraw,
83             **kwargs)
84
85         # Check
86         call_args_list = vrf_table_mock.insert_vrf_path.call_args_list
87         ok_(len(call_args_list) == 1)  # insert_vrf_path should be called once
88         args, kwargs = call_args_list[0]
89         ok_(len(args) == 0)  # no positional argument
90         eq_(str(prefix_inst), str(kwargs['nlri']))
91         eq_(is_withdraw, kwargs['is_withdraw'])
92         if is_withdraw:
93             eq_(None, kwargs['next_hop'])
94             eq_(False, kwargs['gen_lbl'])
95         else:
96             eq_(next_hop, kwargs['next_hop'])
97             eq_(True, kwargs['gen_lbl'])
98
99     def test_update_vrf_table_ipv4(self):
100         # Prepare test data
101         route_dist = '65000:100'
102         ip_network = '192.168.0.0'
103         ip_prefix_len = 24
104         prefix_str = '%s/%d' % (ip_network, ip_prefix_len)
105         prefix_inst = IPAddrPrefix(ip_prefix_len, ip_network)
106         next_hop = '10.0.0.1'
107         route_family = VRF_RF_IPV4
108         route_type = None  # should be ignored
109         kwargs = {}  # should be ignored
110
111         self._test_update_vrf_table(prefix_inst, route_dist, prefix_str,
112                                     next_hop, route_family, route_type,
113                                     **kwargs)
114
115     def test_update_vrf_table_ipv6(self):
116         # Prepare test data
117         route_dist = '65000:100'
118         ip_network = 'fe80::'
119         ip_prefix_len = 64
120         prefix_str = '%s/%d' % (ip_network, ip_prefix_len)
121         prefix_inst = IP6AddrPrefix(ip_prefix_len, ip_network)
122         next_hop = 'fe80::0011:aabb:ccdd:eeff'
123         route_family = VRF_RF_IPV6
124         route_type = None  # should be ignored
125         kwargs = {}  # should be ignored
126
127         self._test_update_vrf_table(prefix_inst, route_dist, prefix_str,
128                                     next_hop, route_family, route_type,
129                                     **kwargs)
130
131     def test_update_vrf_table_l2_evpn_with_esi_int(self):
132         # Prepare test data
133         route_dist = '65000:100'
134         prefix_str = None  # should be ignored
135         kwargs = {
136             'ethernet_tag_id': 100,
137             'mac_addr': 'aa:bb:cc:dd:ee:ff',
138             'ip_addr': '192.168.0.1',
139             'mpls_labels': [],  # not be used
140         }
141         esi = EvpnArbitraryEsi(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00')
142         prefix_inst = EvpnMacIPAdvertisementNLRI(
143             route_dist=route_dist,
144             esi=esi,
145             **kwargs)
146         next_hop = '10.0.0.1'
147         route_family = VRF_RF_L2_EVPN
148         route_type = EvpnMacIPAdvertisementNLRI.ROUTE_TYPE_NAME
149         kwargs['esi'] = 0
150
151         self._test_update_vrf_table(prefix_inst, route_dist, prefix_str,
152                                     next_hop, route_family, route_type,
153                                     **kwargs)
154
155     def test_update_vrf_table_l2_evpn_with_esi_dict(self):
156         # Prepare test data
157         route_dist = '65000:100'
158         prefix_str = None  # should be ignored
159         kwargs = {
160             'ethernet_tag_id': EVPN_MAX_ET,
161         }
162         esi = EvpnLACPEsi(mac_addr='aa:bb:cc:dd:ee:ff', port_key=100)
163         prefix_inst = EvpnEthernetAutoDiscoveryNLRI(
164             route_dist=route_dist,
165             esi=esi,
166             **kwargs)
167         next_hop = '0.0.0.0'
168         route_family = VRF_RF_L2_EVPN
169         route_type = EvpnEthernetAutoDiscoveryNLRI.ROUTE_TYPE_NAME
170         kwargs['esi'] = {
171             'type': ESI_TYPE_LACP,
172             'mac_addr': 'aa:bb:cc:dd:ee:ff',
173             'port_key': 100,
174         }
175
176         self._test_update_vrf_table(prefix_inst, route_dist, prefix_str,
177                                     next_hop, route_family, route_type,
178                                     **kwargs)
179
180     def test_update_vrf_table_l2_evpn_without_esi(self):
181         # Prepare test data
182         route_dist = '65000:100'
183         prefix_str = None  # should be ignored
184         kwargs = {
185             'ethernet_tag_id': 100,
186             'ip_addr': '192.168.0.1',
187         }
188         prefix_inst = EvpnInclusiveMulticastEthernetTagNLRI(
189             route_dist=route_dist, **kwargs)
190         next_hop = '10.0.0.1'
191         route_family = VRF_RF_L2_EVPN
192         route_type = EvpnInclusiveMulticastEthernetTagNLRI.ROUTE_TYPE_NAME
193
194         self._test_update_vrf_table(prefix_inst, route_dist, prefix_str,
195                                     next_hop, route_family, route_type,
196                                     **kwargs)
197
198     @mock.patch(
199         'ryu.services.protocols.bgp.core_managers.TableCoreManager.__init__',
200         mock.MagicMock(return_value=None))
201     def test_update_vrf_table_l2_evpn_with_vni(self):
202         # Prepare test data
203         route_dist = '65000:100'
204         prefix_str = None  # should be ignored
205         kwargs = {
206             'ethernet_tag_id': 100,
207             'mac_addr': 'aa:bb:cc:dd:ee:ff',
208             'ip_addr': '192.168.0.1',
209             'vni': 500,
210         }
211         esi = EvpnArbitraryEsi(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00')
212         prefix_inst = EvpnMacIPAdvertisementNLRI(
213             route_dist=route_dist,
214             esi=esi,
215             **kwargs)
216         next_hop = '10.0.0.1'
217         route_family = VRF_RF_L2_EVPN
218         route_type = EvpnMacIPAdvertisementNLRI.ROUTE_TYPE_NAME
219         tunnel_type = 'vxlan'
220         kwargs['esi'] = 0
221
222         # Instantiate TableCoreManager
223         tbl_mng = table_manager.TableCoreManager(None, None)
224         vrf_table_mock = mock.MagicMock()
225         tbl_mng._tables = {(route_dist, route_family): vrf_table_mock}
226
227         # Test
228         tbl_mng.update_vrf_table(
229             route_dist=route_dist,
230             prefix=prefix_str,
231             next_hop=next_hop,
232             route_family=route_family,
233             route_type=route_type,
234             tunnel_type=tunnel_type,
235             **kwargs)
236
237         # Check
238         call_args_list = vrf_table_mock.insert_vrf_path.call_args_list
239         ok_(len(call_args_list) == 1)  # insert_vrf_path should be called once
240         args, kwargs = call_args_list[0]
241         ok_(len(args) == 0)  # no positional argument
242         eq_(str(prefix_inst), str(kwargs['nlri']))
243         eq_(next_hop, kwargs['next_hop'])
244         eq_(False, kwargs['gen_lbl'])  # should not generate MPLS labels
245         eq_(tunnel_type, kwargs['tunnel_type'])
246
247     def test_update_vrf_table_ipv4_withdraw(self):
248         # Prepare test data
249         route_dist = '65000:100'
250         ip_network = '192.168.0.0'
251         ip_prefix_len = 24
252         prefix_str = '%s/%d' % (ip_network, ip_prefix_len)
253         prefix_inst = IPAddrPrefix(ip_prefix_len, ip_network)
254         next_hop = '10.0.0.1'
255         route_family = VRF_RF_IPV4
256         route_type = None  # should be ignored
257         kwargs = {}  # should be ignored
258
259         self._test_update_vrf_table(prefix_inst, route_dist, prefix_str,
260                                     next_hop, route_family, route_type,
261                                     is_withdraw=True, **kwargs)
262
263     @raises(BgpCoreError)
264     @mock.patch(
265         'ryu.services.protocols.bgp.core_managers.TableCoreManager.__init__',
266         mock.MagicMock(return_value=None))
267     def test_update_vrf_table_no_vrf(self):
268         # Prepare test data
269         route_dist = '65000:100'
270         ip_network = '192.168.0.0'
271         ip_prefix_len = 24
272         prefix_str = '%s/%d' % (ip_network, ip_prefix_len)
273         next_hop = '10.0.0.1'
274         route_family = VRF_RF_IPV4
275         route_type = None  # should be ignored
276         kwargs = {}  # should be ignored
277
278         # Instantiate TableCoreManager
279         tbl_mng = table_manager.TableCoreManager(None, None)
280         tbl_mng._tables = {}  # no table
281
282         # Test
283         tbl_mng.update_vrf_table(
284             route_dist=route_dist,
285             prefix=prefix_str,
286             next_hop=next_hop,
287             route_family=route_family,
288             route_type=route_type,
289             **kwargs)
290
291     @raises(BgpCoreError)
292     def test_update_vrf_table_invalid_next_hop(self):
293         # Prepare test data
294         route_dist = '65000:100'
295         ip_network = '192.168.0.0'
296         ip_prefix_len = 24
297         prefix_str = '%s/%d' % (ip_network, ip_prefix_len)
298         prefix_inst = IPAddrPrefix(ip_prefix_len, ip_network)
299         next_hop = 'xxx.xxx.xxx.xxx'  # invalid
300         route_family = VRF_RF_IPV4
301         route_type = None  # should be ignored
302         kwargs = {}  # should be ignored
303
304         self._test_update_vrf_table(prefix_inst, route_dist, prefix_str,
305                                     next_hop, route_family, route_type,
306                                     **kwargs)
307
308     @raises(BgpCoreError)
309     def test_update_vrf_table_invalid_ipv4_prefix(self):
310         # Prepare test data
311         route_dist = '65000:100'
312         ip_network = 'xxx.xxx.xxx.xxx'  # invalid
313         ip_prefix_len = 24
314         prefix_str = '%s/%d' % (ip_network, ip_prefix_len)
315         prefix_inst = IPAddrPrefix(ip_prefix_len, ip_network)
316         next_hop = '10.0.0.1'
317         route_family = VRF_RF_IPV4
318         route_type = None  # should be ignored
319         kwargs = {}  # should be ignored
320
321         self._test_update_vrf_table(prefix_inst, route_dist, prefix_str,
322                                     next_hop, route_family, route_type,
323                                     **kwargs)
324
325     @raises(BgpCoreError)
326     def test_update_vrf_table_invalid_ipv6_prefix(self):
327         # Prepare test data
328         route_dist = '65000:100'
329         ip_network = 'xxxx::'  # invalid
330         ip_prefix_len = 64
331         prefix_str = '%s/%d' % (ip_network, ip_prefix_len)
332         prefix_inst = IP6AddrPrefix(ip_prefix_len, ip_network)
333         next_hop = 'fe80::0011:aabb:ccdd:eeff'
334         route_family = VRF_RF_IPV6
335         route_type = None  # should be ignored
336         kwargs = {}  # should be ignored
337
338         self._test_update_vrf_table(prefix_inst, route_dist, prefix_str,
339                                     next_hop, route_family, route_type,
340                                     **kwargs)
341
342     @raises(BgpCoreError)
343     def test_update_vrf_table_invalid_route_family(self):
344         # Prepare test data
345         route_dist = '65000:100'
346         ip_network = '192.168.0.0'
347         ip_prefix_len = 24
348         prefix_str = '%s/%d' % (ip_network, ip_prefix_len)
349         prefix_inst = IPAddrPrefix(ip_prefix_len, ip_network)
350         next_hop = '10.0.0.1'
351         route_family = 'foobar'  # invalid
352         route_type = None  # should be ignored
353         kwargs = {}  # should be ignored
354
355         self._test_update_vrf_table(prefix_inst, route_dist, prefix_str,
356                                     next_hop, route_family, route_type,
357                                     **kwargs)
358
359     @mock.patch(
360         'ryu.services.protocols.bgp.core_managers.TableCoreManager.__init__',
361         mock.MagicMock(return_value=None))
362     @mock.patch(
363         'ryu.services.protocols.bgp.core_managers.TableCoreManager.learn_path')
364     def _test_update_global_table(self, learn_path_mock, prefix, next_hop,
365                                   is_withdraw, expected_next_hop):
366         # Prepare test data
367         origin = BGPPathAttributeOrigin(BGP_ATTR_ORIGIN_IGP)
368         aspath = BGPPathAttributeAsPath([[]])
369         pathattrs = OrderedDict()
370         pathattrs[BGP_ATTR_TYPE_ORIGIN] = origin
371         pathattrs[BGP_ATTR_TYPE_AS_PATH] = aspath
372         pathattrs = str(pathattrs)
373
374         # Instantiate TableCoreManager
375         tbl_mng = table_manager.TableCoreManager(None, None)
376
377         # Test
378         tbl_mng.update_global_table(
379             prefix=prefix,
380             next_hop=next_hop,
381             is_withdraw=is_withdraw,
382         )
383
384         # Check
385         call_args_list = learn_path_mock.call_args_list
386         ok_(len(call_args_list) == 1)  # learn_path should be called once
387         args, kwargs = call_args_list[0]
388         ok_(len(kwargs) == 0)  # no keyword argument
389         output_path = args[0]
390         eq_(None, output_path.source)
391         eq_(prefix, output_path.nlri.prefix)
392         eq_(pathattrs, str(output_path.pathattr_map))
393         eq_(expected_next_hop, output_path.nexthop)
394         eq_(is_withdraw, output_path.is_withdraw)
395
396     def test_update_global_table_ipv4(self):
397         self._test_update_global_table(
398             prefix='192.168.0.0/24',
399             next_hop='10.0.0.1',
400             is_withdraw=False,
401             expected_next_hop='10.0.0.1',
402         )
403
404     def test_update_global_table_ipv4_withdraw(self):
405         self._test_update_global_table(
406             prefix='192.168.0.0/24',
407             next_hop='10.0.0.1',
408             is_withdraw=True,
409             expected_next_hop='10.0.0.1',
410         )
411
412     def test_update_global_table_ipv4_no_next_hop(self):
413         self._test_update_global_table(
414             prefix='192.168.0.0/24',
415             next_hop=None,
416             is_withdraw=True,
417             expected_next_hop='0.0.0.0',
418         )
419
420     def test_update_global_table_ipv6(self):
421         self._test_update_global_table(
422             prefix='fe80::/64',
423             next_hop='fe80::0011:aabb:ccdd:eeff',
424             is_withdraw=False,
425             expected_next_hop='fe80::0011:aabb:ccdd:eeff',
426         )
427
428     def test_update_global_table_ipv6_withdraw(self):
429         self._test_update_global_table(
430             prefix='fe80::/64',
431             next_hop='fe80::0011:aabb:ccdd:eeff',
432             is_withdraw=True,
433             expected_next_hop='fe80::0011:aabb:ccdd:eeff',
434         )
435
436     def test_update_global_table_ipv6_no_next_hop(self):
437         self._test_update_global_table(
438             prefix='fe80::/64',
439             next_hop=None,
440             is_withdraw=True,
441             expected_next_hop='::',
442         )
443
444     @mock.patch(
445         'ryu.services.protocols.bgp.core_managers.TableCoreManager.__init__',
446         mock.MagicMock(return_value=None))
447     def _test_update_flowspec_vrf_table(self, flowspec_family, route_family,
448                                         route_dist, rules, prefix,
449                                         is_withdraw, actions=None):
450         # Instantiate TableCoreManager
451         tbl_mng = table_manager.TableCoreManager(None, None)
452         vrf_table_mock = mock.MagicMock()
453         tbl_mng._tables = {(route_dist, route_family): vrf_table_mock}
454
455         # Test
456         tbl_mng.update_flowspec_vrf_table(
457             flowspec_family=flowspec_family,
458             route_dist=route_dist,
459             rules=rules,
460             actions=actions,
461             is_withdraw=is_withdraw,
462         )
463
464         # Check
465         call_args_list = vrf_table_mock.insert_vrffs_path.call_args_list
466         ok_(len(
467             call_args_list) == 1)  # insert_vrffs_path should be called once
468         args, kwargs = call_args_list[0]
469         ok_(len(args) == 0)  # no positional argument
470         eq_(prefix, kwargs['nlri'].prefix)
471         eq_(is_withdraw, kwargs['is_withdraw'])
472
473     def test_update_flowspec_vrf_table_vpnv4(self):
474         flowspec_family = 'vpnv4fs'
475         route_family = 'ipv4fs'
476         route_dist = '65001:100'
477         rules = {
478             'dst_prefix': '10.70.1.0/24',
479         }
480         actions = {
481             'traffic_rate': {
482                 'as_number': 0,
483                 'rate_info': 100.0,
484             },
485         }
486         prefix = 'ipv4fs(dst_prefix:10.70.1.0/24)'
487
488         self._test_update_flowspec_vrf_table(
489             flowspec_family=flowspec_family,
490             route_family=route_family,
491             route_dist=route_dist,
492             rules=rules,
493             prefix=prefix,
494             is_withdraw=False,
495             actions=actions,
496         )
497
498     def test_update_flowspec_vrf_table_vpnv4_without_actions(self):
499         flowspec_family = 'vpnv4fs'
500         route_family = 'ipv4fs'
501         route_dist = '65001:100'
502         rules = {
503             'dst_prefix': '10.70.1.0/24',
504         }
505         prefix = 'ipv4fs(dst_prefix:10.70.1.0/24)'
506
507         self._test_update_flowspec_vrf_table(
508             flowspec_family=flowspec_family,
509             route_family=route_family,
510             route_dist=route_dist,
511             rules=rules,
512             prefix=prefix,
513             is_withdraw=False,
514         )
515
516     @raises(BgpCoreError)
517     def test_update_flowspec_vrf_table_vpnv4_invalid_actions(self):
518         flowspec_family = 'vpnv4fs'
519         route_family = 'ipv4fs'
520         route_dist = '65001:100'
521         rules = {
522             'dst_prefix': '10.70.1.0/24',
523         }
524         actions = {
525             'invalid_actions': {
526                 'invalid_param': 10,
527             },
528         }
529         prefix = 'ipv4fs(dst_prefix:10.70.1.0/24)'
530
531         self._test_update_flowspec_vrf_table(
532             flowspec_family=flowspec_family,
533             route_family=route_family,
534             route_dist=route_dist,
535             rules=rules,
536             prefix=prefix,
537             is_withdraw=False,
538             actions=actions,
539         )
540
541     @raises(BgpCoreError)
542     def test_update_flowspec_vrf_table_vpnv4_invalid_flowspec_family(self):
543         flowspec_family = 'invalid'
544         route_family = 'ipv4fs'
545         route_dist = '65001:100'
546         rules = {
547             'dst_prefix': '10.70.1.0/24',
548         }
549         prefix = 'ipv4fs(dst_prefix:10.70.1.0/24)'
550
551         self._test_update_flowspec_vrf_table(
552             flowspec_family=flowspec_family,
553             route_family=route_family,
554             route_dist=route_dist,
555             rules=rules,
556             prefix=prefix,
557             is_withdraw=False,
558         )
559
560     @raises(BgpCoreError)
561     def test_update_flowspec_vrf_table_vpnv4_invalid_route_family(self):
562         flowspec_family = 'vpnv4fs'
563         route_family = 'invalid'
564         route_dist = '65001:100'
565         rules = {
566             'dst_prefix': '10.70.1.0/24',
567         }
568         prefix = 'ipv4fs(dst_prefix:10.70.1.0/24)'
569
570         self._test_update_flowspec_vrf_table(
571             flowspec_family=flowspec_family,
572             route_family=route_family,
573             route_dist=route_dist,
574             rules=rules,
575             prefix=prefix,
576             is_withdraw=False,
577         )
578
579     @mock.patch(
580         'ryu.services.protocols.bgp.core_managers.TableCoreManager.__init__',
581         mock.MagicMock(return_value=None))
582     @mock.patch(
583         'ryu.services.protocols.bgp.core_managers.TableCoreManager.learn_path')
584     def _test_update_flowspec_global_table(self, learn_path_mock,
585                                            flowspec_family, rules, prefix,
586                                            is_withdraw, actions=None):
587         # Instantiate TableCoreManager
588         tbl_mng = table_manager.TableCoreManager(None, None)
589
590         # Test
591         tbl_mng.update_flowspec_global_table(
592             flowspec_family=flowspec_family,
593             rules=rules,
594             actions=actions,
595             is_withdraw=is_withdraw,
596         )
597
598         # Check
599         call_args_list = learn_path_mock.call_args_list
600         ok_(len(call_args_list) == 1)  # learn_path should be called once
601         args, kwargs = call_args_list[0]
602         ok_(len(kwargs) == 0)  # no keyword argument
603         output_path = args[0]
604         eq_(None, output_path.source)
605         eq_(prefix, output_path.nlri.prefix)
606         eq_(None, output_path.nexthop)
607         eq_(is_withdraw, output_path.is_withdraw)
608
609     def test_update_flowspec_global_table_ipv4(self):
610         flowspec_family = 'ipv4fs'
611         rules = {
612             'dst_prefix': '10.60.1.0/24',
613         }
614         actions = {
615             'traffic_rate': {
616                 'as_number': 0,
617                 'rate_info': 100.0,
618             },
619         }
620         prefix = 'ipv4fs(dst_prefix:10.60.1.0/24)'
621
622         self._test_update_flowspec_global_table(
623             flowspec_family=flowspec_family,
624             rules=rules,
625             prefix=prefix,
626             is_withdraw=False,
627             actions=actions,
628         )
629
630     def test_update_flowspec_global_table_ipv4_without_actions(self):
631         flowspec_family = 'ipv4fs'
632         rules = {
633             'dst_prefix': '10.60.1.0/24',
634         }
635         prefix = 'ipv4fs(dst_prefix:10.60.1.0/24)'
636
637         self._test_update_flowspec_global_table(
638             flowspec_family=flowspec_family,
639             rules=rules,
640             prefix=prefix,
641             is_withdraw=False,
642         )
643
644     @raises(BgpCoreError)
645     def test_update_flowspec_global_table_ipv4_invalid_actions(self):
646         flowspec_family = 'ipv4fs'
647         rules = {
648             'dst_prefix': '10.60.1.0/24',
649         }
650         actions = {
651             'invalid_actions': {
652                 'invalid_param': 10,
653             },
654         }
655         prefix = 'ipv4fs(dst_prefix:10.60.1.0/24)'
656
657         self._test_update_flowspec_global_table(
658             flowspec_family=flowspec_family,
659             rules=rules,
660             prefix=prefix,
661             is_withdraw=False,
662             actions=actions,
663         )
664
665     @raises(BgpCoreError)
666     def test_update_flowspec_global_table_ipv4_invalid_flowspec_family(self):
667         flowspec_family = 'invalid'
668         rules = {
669             'dst_prefix': '10.60.1.0/24',
670         }
671         actions = {
672             'traffic_rate': {
673                 'as_number': 0,
674                 'rate_info': 100.0,
675             },
676         }
677         prefix = 'ipv4fs(dst_prefix:10.60.1.0/24)'
678
679         self._test_update_flowspec_global_table(
680             flowspec_family=flowspec_family,
681             rules=rules,
682             prefix=prefix,
683             is_withdraw=False,
684             actions=actions,
685         )
686
687     def test_update_flowspec_global_table_ipv6(self):
688         flowspec_family = 'ipv6fs'
689         rules = {
690             'dst_prefix': '2001::3/128/32',
691         }
692         actions = {
693             'traffic_rate': {
694                 'as_number': 0,
695                 'rate_info': 100.0,
696             },
697         }
698         prefix = 'ipv6fs(dst_prefix:2001::3/128/32)'
699
700         self._test_update_flowspec_global_table(
701             flowspec_family=flowspec_family,
702             rules=rules,
703             prefix=prefix,
704             is_withdraw=False,
705             actions=actions,
706         )
707
708     def test_update_flowspec_global_table_ipv6_without_actions(self):
709         flowspec_family = 'ipv6fs'
710         rules = {
711             'dst_prefix': '2001::3/128/32',
712         }
713         prefix = 'ipv6fs(dst_prefix:2001::3/128/32)'
714
715         self._test_update_flowspec_global_table(
716             flowspec_family=flowspec_family,
717             rules=rules,
718             prefix=prefix,
719             is_withdraw=False,
720         )
721
722     @raises(BgpCoreError)
723     def test_update_flowspec_global_table_ipv6_invalid_actions(self):
724         flowspec_family = 'ipv6fs'
725         rules = {
726             'dst_prefix': '2001::3/128/32',
727         }
728         actions = {
729             'invalid_actions': {
730                 'invalid_param': 10,
731             },
732         }
733         prefix = 'ipv4fs(dst_prefix:2001::3/128/32)'
734
735         self._test_update_flowspec_global_table(
736             flowspec_family=flowspec_family,
737             rules=rules,
738             prefix=prefix,
739             is_withdraw=False,
740             actions=actions,
741         )
742
743     @raises(BgpCoreError)
744     def test_update_flowspec_global_table_ipv6_invalid_flowspec_family(self):
745         flowspec_family = 'invalid'
746         rules = {
747             'dst_prefix': '2001::3/128/32',
748         }
749         actions = {
750             'traffic_rate': {
751                 'as_number': 0,
752                 'rate_info': 100.0,
753             },
754         }
755         prefix = 'ipv4fs(dst_prefix:2001::3/128/32)'
756
757         self._test_update_flowspec_global_table(
758             flowspec_family=flowspec_family,
759             rules=rules,
760             prefix=prefix,
761             is_withdraw=False,
762             actions=actions,
763         )
764
765     def test_update_flowspec_vrf_table_vpnv6(self):
766         flowspec_family = 'vpnv6fs'
767         route_family = 'ipv6fs'
768         route_dist = '65001:100'
769         rules = {
770             'dst_prefix': '2001::3/128/32',
771         }
772         actions = {
773             'traffic_rate': {
774                 'as_number': 0,
775                 'rate_info': 100.0,
776             },
777         }
778         prefix = 'ipv6fs(dst_prefix:2001::3/128/32)'
779
780         self._test_update_flowspec_vrf_table(
781             flowspec_family=flowspec_family,
782             route_family=route_family,
783             route_dist=route_dist,
784             rules=rules,
785             prefix=prefix,
786             is_withdraw=False,
787             actions=actions,
788         )
789
790     def test_update_flowspec_vrf_table_vpnv6_without_actions(self):
791         flowspec_family = 'vpnv6fs'
792         route_family = 'ipv6fs'
793         route_dist = '65001:100'
794         rules = {
795             'dst_prefix': '2001::3/128/32',
796         }
797         prefix = 'ipv6fs(dst_prefix:2001::3/128/32)'
798
799         self._test_update_flowspec_vrf_table(
800             flowspec_family=flowspec_family,
801             route_family=route_family,
802             route_dist=route_dist,
803             rules=rules,
804             prefix=prefix,
805             is_withdraw=False,
806         )
807
808     @raises(BgpCoreError)
809     def test_update_flowspec_vrf_table_vpnv6_invalid_actions(self):
810         flowspec_family = 'vpnv6fs'
811         route_family = 'ipv6fs'
812         route_dist = '65001:100'
813         rules = {
814             'dst_prefix': '2001::3/128/32',
815         }
816         actions = {
817             'invalid_actions': {
818                 'invalid_param': 10,
819             },
820         }
821         prefix = 'ipv6fs(dst_prefix:2001::3/128/32)'
822
823         self._test_update_flowspec_vrf_table(
824             flowspec_family=flowspec_family,
825             route_family=route_family,
826             route_dist=route_dist,
827             rules=rules,
828             prefix=prefix,
829             is_withdraw=False,
830             actions=actions,
831         )
832
833     @raises(BgpCoreError)
834     def test_update_flowspec_vrf_table_vpnv6_invalid_route_family(self):
835         flowspec_family = 'vpnv6fs'
836         route_family = 'invalid'
837         route_dist = '65001:100'
838         rules = {
839             'dst_prefix': '2001::3/128/32',
840         }
841         prefix = 'ipv4fs(dst_prefix:2001::3/128/32)'
842
843         self._test_update_flowspec_vrf_table(
844             flowspec_family=flowspec_family,
845             route_family=route_family,
846             route_dist=route_dist,
847             rules=rules,
848             prefix=prefix,
849             is_withdraw=False,
850         )
851
852     def test_update_flowspec_vrf_table_l2vpn(self):
853         flowspec_family = 'l2vpnfs'
854         route_family = 'l2vpnfs'
855         route_dist = '65001:100'
856         rules = {
857             'dst_mac': '12:34:56:78:9a:bc',
858         }
859         actions = {
860             'traffic_rate': {
861                 'as_number': 0,
862                 'rate_info': 100.0,
863             },
864         }
865         prefix = 'l2vpnfs(dst_mac:12:34:56:78:9a:bc)'
866
867         self._test_update_flowspec_vrf_table(
868             flowspec_family=flowspec_family,
869             route_family=route_family,
870             route_dist=route_dist,
871             rules=rules,
872             prefix=prefix,
873             is_withdraw=False,
874             actions=actions,
875         )
876
877     def test_update_flowspec_vrf_table_l2vpn_without_actions(self):
878         flowspec_family = 'l2vpnfs'
879         route_family = 'l2vpnfs'
880         route_dist = '65001:100'
881         rules = {
882             'dst_mac': '12:34:56:78:9a:bc',
883         }
884         prefix = 'l2vpnfs(dst_mac:12:34:56:78:9a:bc)'
885
886         self._test_update_flowspec_vrf_table(
887             flowspec_family=flowspec_family,
888             route_family=route_family,
889             route_dist=route_dist,
890             rules=rules,
891             prefix=prefix,
892             is_withdraw=False,
893         )
894
895     @raises(BgpCoreError)
896     def test_update_flowspec_vrf_table_l2vpn_invalid_actions(self):
897         flowspec_family = 'l2vpnfs'
898         route_family = 'l2vpnfs'
899         route_dist = '65001:100'
900         rules = {
901             'dst_mac': '12:34:56:78:9a:bc',
902         }
903         actions = {
904             'invalid_actions': {
905                 'invalid_param': 10,
906             },
907         }
908         prefix = 'l2vpnfs(dst_mac:12:34:56:78:9a:bc)'
909
910         self._test_update_flowspec_vrf_table(
911             flowspec_family=flowspec_family,
912             route_family=route_family,
913             route_dist=route_dist,
914             rules=rules,
915             prefix=prefix,
916             is_withdraw=False,
917             actions=actions,
918         )
919
920     @raises(BgpCoreError)
921     def test_update_flowspec_vrf_table_l2vpn_invalid_route_family(self):
922         flowspec_family = 'l2vpnfs'
923         route_family = 'invalid'
924         route_dist = '65001:100'
925         rules = {
926             'dst_mac': '12:34:56:78:9a:bc',
927         }
928         prefix = 'l2vpnfs(dst_mac:12:34:56:78:9a:bc)'
929
930         self._test_update_flowspec_vrf_table(
931             flowspec_family=flowspec_family,
932             route_family=route_family,
933             route_dist=route_dist,
934             rules=rules,
935             prefix=prefix,
936             is_withdraw=False,
937         )