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
16from collections import OrderedDict
17import unittest
18import logging
19try:
20    import mock  # Python 2
21except ImportError:
22    from unittest import mock  # Python 3
23
24from nose.tools import ok_, eq_, raises
25
26from ryu.lib.packet.bgp import BGPPathAttributeOrigin
27from ryu.lib.packet.bgp import BGPPathAttributeAsPath
28from ryu.lib.packet.bgp import BGP_ATTR_ORIGIN_IGP
29from ryu.lib.packet.bgp import BGP_ATTR_TYPE_ORIGIN
30from ryu.lib.packet.bgp import BGP_ATTR_TYPE_AS_PATH
31from ryu.lib.packet.bgp import BGP_ATTR_TYPE_EXTENDED_COMMUNITIES
32from ryu.lib.packet.bgp import IPAddrPrefix
33from ryu.lib.packet.bgp import IP6AddrPrefix
34from ryu.lib.packet.bgp import EvpnArbitraryEsi
35from ryu.lib.packet.bgp import EvpnLACPEsi
36from ryu.lib.packet.bgp import EvpnEthernetAutoDiscoveryNLRI
37from ryu.lib.packet.bgp import EvpnMacIPAdvertisementNLRI
38from ryu.lib.packet.bgp import EvpnInclusiveMulticastEthernetTagNLRI
39from ryu.lib.packet.bgp import FlowSpecIPv4NLRI
40from ryu.lib.packet.bgp import BGPPathAttributeExtendedCommunities
41from ryu.services.protocols.bgp.bgpspeaker import EVPN_MAX_ET
42from ryu.services.protocols.bgp.bgpspeaker import ESI_TYPE_LACP
43from ryu.services.protocols.bgp.bgpspeaker import FLOWSPEC_FAMILY_IPV4
44from ryu.services.protocols.bgp.bgpspeaker import FLOWSPEC_FAMILY_VPNV4
45from ryu.services.protocols.bgp.bgpspeaker import FLOWSPEC_TA_SAMPLE
46from ryu.services.protocols.bgp.bgpspeaker import FLOWSPEC_TA_TERMINAL
47from ryu.services.protocols.bgp.core import BgpCoreError
48from ryu.services.protocols.bgp.core_managers import table_manager
49from ryu.services.protocols.bgp.rtconf.vrfs import VRF_RF_IPV4
50from ryu.services.protocols.bgp.rtconf.vrfs import VRF_RF_IPV6
51from ryu.services.protocols.bgp.rtconf.vrfs import VRF_RF_L2_EVPN
52from ryu.services.protocols.bgp.rtconf.vrfs import VRF_RF_IPV4_FLOWSPEC
53from ryu.services.protocols.bgp.utils.bgp import create_v4flowspec_actions
54
55
56LOG = logging.getLogger(__name__)
57
58
59class 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        )
938