def test_update_vrf_table_l2_evpn_with_vni(self):
        # Prepare test data
        route_dist = '65000:100'
        prefix_str = None  # should be ignored
        kwargs = {
            'ethernet_tag_id': 100,
            'mac_addr': 'aa:bb:cc:dd:ee:ff',
            'ip_addr': '192.168.0.1',
            'vni': 500,
        }
        esi = EvpnArbitraryEsi(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00')
        prefix_inst = EvpnMacIPAdvertisementNLRI(
            route_dist=route_dist,
            esi=esi,
            **kwargs)
        next_hop = '10.0.0.1'
        route_family = VRF_RF_L2_EVPN
        route_type = EvpnMacIPAdvertisementNLRI.ROUTE_TYPE_NAME
        tunnel_type = 'vxlan'
        kwargs['esi'] = 0

        # Instantiate TableCoreManager
        tbl_mng = table_manager.TableCoreManager(None, None)
        vrf_table_mock = mock.MagicMock()
        tbl_mng._tables = {(route_dist, route_family): vrf_table_mock}

        # Test
        tbl_mng.update_vrf_table(
            route_dist=route_dist,
            prefix=prefix_str,
            next_hop=next_hop,
            route_family=route_family,
            route_type=route_type,
            tunnel_type=tunnel_type,
            **kwargs)

        # Check
        call_args_list = vrf_table_mock.insert_vrf_path.call_args_list
        ok_(len(call_args_list) == 1)  # insert_vrf_path should be called once
        args, kwargs = call_args_list[0]
        ok_(len(args) == 0)  # no positional argument
        eq_(str(prefix_inst), str(kwargs['nlri']))
        eq_(next_hop, kwargs['next_hop'])
        eq_(False, kwargs['gen_lbl'])  # should not generate MPLS labels
        eq_(tunnel_type, kwargs['tunnel_type'])
    def test_update_vrf_table_l2_evpn_with_esi_int(self):
        # Prepare test data
        route_dist = '65000:100'
        prefix_str = None  # should be ignored
        kwargs = {
            'ethernet_tag_id': 100,
            'mac_addr': 'aa:bb:cc:dd:ee:ff',
            'ip_addr': '192.168.0.1',
            'mpls_labels': [],  # not be used
        }
        esi = EvpnArbitraryEsi(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00')
        prefix_inst = EvpnMacIPAdvertisementNLRI(route_dist=route_dist,
                                                 esi=esi,
                                                 **kwargs)
        next_hop = '10.0.0.1'
        route_family = VRF_RF_L2_EVPN
        route_type = EvpnMacIPAdvertisementNLRI.ROUTE_TYPE_NAME
        kwargs['esi'] = 0

        self._test_update_vrf_table(prefix_inst, route_dist, prefix_str,
                                    next_hop, route_family, route_type,
                                    **kwargs)
    def update_vrf_table(self,
                         route_dist,
                         prefix=None,
                         next_hop=None,
                         route_family=None,
                         route_type=None,
                         tunnel_type=None,
                         is_withdraw=False,
                         redundancy_mode=None,
                         pmsi_tunnel_type=None,
                         **kwargs):
        """Update a BGP route in the VRF table identified by `route_dist`
        with the given `next_hop`.

        If `is_withdraw` is False, which is the default, add a BGP route
        to the VRF table identified by `route_dist` with the given
        `next_hop`.
        If `is_withdraw` is True, remove a BGP route from the VRF table
        and the given `next_hop` is ignored.

        If `route_family` is VRF_RF_L2_EVPN, `route_type` and `kwargs`
        are required to construct EVPN NLRI and `prefix` is ignored.

        ``redundancy_mode`` specifies a redundancy mode type.

`       `pmsi_tunnel_type` specifies the type of the PMSI tunnel attribute
         used to encode the multicast tunnel identifier.
        This field is advertised only if route_type is
        EVPN_MULTICAST_ETAG_ROUTE.

        Returns assigned VPN label.
        """
        from ryu.services.protocols.bgp.core import BgpCoreError

        assert route_dist

        if is_withdraw:
            gen_lbl = False
            next_hop = None
        else:
            gen_lbl = True
            if not (is_valid_ipv4(next_hop) or is_valid_ipv6(next_hop)):
                raise BgpCoreError(desc='Invalid IPv4/IPv6 nexthop: %s' %
                                   next_hop)

        vrf_table = self._tables.get((route_dist, route_family))
        if vrf_table is None:
            raise BgpCoreError(
                desc='VRF table  does not exist: route_dist=%s, '
                'route_family=%s' % (route_dist, route_family))

        vni = kwargs.get('vni', None)

        if route_family == VRF_RF_IPV4:
            if not is_valid_ipv4_prefix(prefix):
                raise BgpCoreError(desc='Invalid IPv4 prefix: %s' % prefix)
            ip, masklen = prefix.split('/')
            prefix = IPAddrPrefix(int(masklen), ip)
        elif route_family == VRF_RF_IPV6:
            if not is_valid_ipv6_prefix(prefix):
                raise BgpCoreError(desc='Invalid IPv6 prefix: %s' % prefix)
            ip6, masklen = prefix.split('/')
            prefix = IP6AddrPrefix(int(masklen), ip6)
        elif route_family == VRF_RF_L2_EVPN:
            assert route_type
            if route_type == EvpnMacIPAdvertisementNLRI.ROUTE_TYPE_NAME:
                # MPLS labels will be assigned automatically
                kwargs['mpls_labels'] = []
            if route_type == EvpnInclusiveMulticastEthernetTagNLRI.ROUTE_TYPE_NAME:
                # Inclusive Multicast Ethernet Tag Route does not have "vni",
                # omit "vni" from "kwargs" here.
                vni = kwargs.pop('vni', None)
            subclass = EvpnNLRI._lookup_type_name(route_type)
            kwargs['route_dist'] = route_dist
            esi = kwargs.get('esi', None)
            if esi is not None:
                if isinstance(esi, dict):
                    esi_type = esi.get('type', 0)
                    esi_class = EvpnEsi._lookup_type(esi_type)
                    kwargs['esi'] = esi_class.from_jsondict(esi)
                else:  # isinstance(esi, numbers.Integral)
                    kwargs['esi'] = EvpnArbitraryEsi(
                        type_desc.Int9.from_user(esi))
            if vni is not None:
                # Disable to generate MPLS labels,
                # because encapsulation type is not MPLS.
                from ryu.services.protocols.bgp.api.prefix import (
                    TUNNEL_TYPE_VXLAN, TUNNEL_TYPE_NVGRE)
                assert tunnel_type in [
                    None, TUNNEL_TYPE_VXLAN, TUNNEL_TYPE_NVGRE
                ]
                gen_lbl = False
            prefix = subclass(**kwargs)
        else:
            raise BgpCoreError(desc='Unsupported route family %s' %
                               route_family)

        # We do not check if we have a path to given prefix, we issue
        # withdrawal. Hence multiple withdrawals have not side effect.
        return vrf_table.insert_vrf_path(nlri=prefix,
                                         next_hop=next_hop,
                                         gen_lbl=gen_lbl,
                                         is_withdraw=is_withdraw,
                                         redundancy_mode=redundancy_mode,
                                         vni=vni,
                                         tunnel_type=tunnel_type,
                                         pmsi_tunnel_type=pmsi_tunnel_type)
示例#4
0
    def update_vrf_table(self,
                         route_dist,
                         prefix=None,
                         next_hop=None,
                         route_family=None,
                         route_type=None,
                         tunnel_type=None,
                         is_withdraw=False,
                         **kwargs):
        """Update a BGP route in the VRF table identified by `route_dist`
        with the given `next_hop`.

        If `is_withdraw` is False, which is the default, add a BGP route
        to the VRF table identified by `route_dist` with the given
        `next_hop`.
        If `is_withdraw` is True, remove a BGP route from the VRF table
        and the given `next_hop` is ignored.

        If `route_family` is VRF_RF_L2_EVPN, `route_type` and `kwargs`
        are required to construct EVPN NLRI and `prefix` is ignored.

        Returns assigned VPN label.
        """
        from ryu.services.protocols.bgp.core import BgpCoreError

        assert route_dist

        if is_withdraw:
            gen_lbl = False
            next_hop = None
        else:
            gen_lbl = True
            if not (is_valid_ipv4(next_hop) or is_valid_ipv6(next_hop)):
                raise BgpCoreError(desc='Invalid IPv4/IPv6 nexthop: %s' %
                                   next_hop)

        vrf_table = self._tables.get((route_dist, route_family))
        if vrf_table is None:
            raise BgpCoreError(
                desc='VRF table  does not exist: route_dist=%s, '
                'route_family=%s' % (route_dist, route_family))

        if route_family == VRF_RF_IPV4:
            if not is_valid_ipv4_prefix(prefix):
                raise BgpCoreError(desc='Invalid IPv4 prefix: %s' % prefix)
            ip, masklen = prefix.split('/')
            prefix = IPAddrPrefix(int(masklen), ip)
        elif route_family == VRF_RF_IPV6:
            if not is_valid_ipv6_prefix(prefix):
                raise BgpCoreError(desc='Invalid IPv6 prefix: %s' % prefix)
            ip6, masklen = prefix.split('/')
            prefix = IP6AddrPrefix(int(masklen), ip6)
        elif route_family == VRF_RF_L2_EVPN:
            assert route_type
            if route_type == EvpnMacIPAdvertisementNLRI.ROUTE_TYPE_NAME:
                # MPLS labels will be assigned automatically
                kwargs['mpls_labels'] = []
            subclass = EvpnNLRI._lookup_type_name(route_type)
            kwargs['route_dist'] = route_dist
            esi = kwargs.get('esi', None)
            if esi is not None:
                # Note: Currently, we support arbitrary 9-octet ESI value only.
                kwargs['esi'] = EvpnArbitraryEsi(type_desc.Int9.from_user(esi))
            if 'vni' in kwargs:
                # Disable to generate MPLS labels, because encapsulation type
                # is not MPLS.
                from ryu.services.protocols.bgp.api.prefix import (
                    TUNNEL_TYPE_VXLAN, TUNNEL_TYPE_NVGRE)
                assert tunnel_type in [TUNNEL_TYPE_VXLAN, TUNNEL_TYPE_NVGRE]
                gen_lbl = False
            prefix = subclass(**kwargs)
        else:
            raise BgpCoreError(desc='Unsupported route family %s' %
                               route_family)

        # We do not check if we have a path to given prefix, we issue
        # withdrawal. Hence multiple withdrawals have not side effect.
        return vrf_table.insert_vrf_path(nlri=prefix,
                                         next_hop=next_hop,
                                         gen_lbl=gen_lbl,
                                         is_withdraw=is_withdraw,
                                         tunnel_type=tunnel_type)