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)
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)