From 06889e47404b3db625876b88fb7cdb8973e2a3f4 Mon Sep 17 00:00:00 2001 From: Sebastian Lohff Date: Wed, 14 Aug 2024 14:05:16 +0200 Subject: [PATCH 1/2] Use route-maps instead of network for BGP prefixes The current firmware of our Cisco routers (<= 17.6) requires a full sync every time we touch the bgp subtree via netconf-yang. For our current prod regions can cause a complete config lock of the /native tree, which can take up to two minutes. In this timeframe no other reconfiguration is possible. Before this commit we used network statements for advertising BGPVPNs and DAPNets. This is now done via "redistribute connected" and "redistribute static" + a route-map, to set the right communities for extraroutes overlapping with a DAPNet. The config will still get locked for DAPNet routers or when a BGPVPN is attached, but all subsequent modifications (like adding/deleting a subnet or adding/deleting an extraroute to the router) will only change a prefixlist and therefore not touch the BGP config tree. Long term it would be desirable if Cisco would fix their confd implementation, though. WIP: * config is still hardcoded, shouldn't be that way * we have to discuss if we want to advertise DAPNets via route-map or network statement --- asr1k_neutron_l3/common/config.py | 3 + asr1k_neutron_l3/models/netconf_yang/bgp.py | 66 ++++++++++--------- .../models/netconf_yang/prefix.py | 10 ++- .../models/netconf_yang/route_map.py | 35 ++++++---- asr1k_neutron_l3/models/neutron/l3/bgp.py | 6 +- asr1k_neutron_l3/models/neutron/l3/prefix.py | 26 +++++++- .../models/neutron/l3/route_map.py | 30 ++++++++- asr1k_neutron_l3/models/neutron/l3/router.py | 50 ++++++++++++-- .../unit/models/netconf_yang/test_parsing.py | 39 ++++++++++- 9 files changed, 206 insertions(+), 59 deletions(-) diff --git a/asr1k_neutron_l3/common/config.py b/asr1k_neutron_l3/common/config.py index b6ffdc0e..e8e31cb4 100644 --- a/asr1k_neutron_l3/common/config.py +++ b/asr1k_neutron_l3/common/config.py @@ -83,6 +83,9 @@ help="Route-Map to apply to all DAPNet BGP network statements"), cfg.StrOpt('dapnet_extra_routes_rm', default='RM-DAP-EXTRA-ROUTES', help="Route-Map to apply to all BGP network statements for extra routes that are contained in a DAPNet"), + # FIXME: move defaults away from here + cfg.ListOpt('dapn_extra_routes_communities', default=["65126", "4268097541"], + help="Communities to assign to DAPNet extraroutes (via redistribute statement)"), cfg.IntOpt('external_iface_arp_timeout', default=1800, help="Set ARP timeout for the external interface of a router. Set to 0 to not set this attribute"), cfg.IntOpt('internal_iface_arp_timeout', default=0, diff --git a/asr1k_neutron_l3/models/netconf_yang/bgp.py b/asr1k_neutron_l3/models/netconf_yang/bgp.py index 7ed4705b..c4ac523d 100644 --- a/asr1k_neutron_l3/models/netconf_yang/bgp.py +++ b/asr1k_neutron_l3/models/netconf_yang/bgp.py @@ -38,6 +38,7 @@ class BGPConstants(object): REDISTRIBUTE_VRF = "redistribute-vrf" CONNECTED = "connected" STATIC = "static" + DEFAULT = "default" UNICAST = "unicast" NETWORK = "network" WITH_MASK = "with-mask" @@ -82,14 +83,14 @@ def __parameters__(cls): return [ {'key': 'asn', 'id': True, 'yang-key': 'id'}, {'key': 'vrf', 'yang-key': 'name'}, - {'key': 'connected', 'yang-path': 'ipv4-unicast/redistribute', 'default': False, - 'yang-type': YANG_TYPE.EMPTY}, - {'key': 'static', 'yang-path': 'ipv4-unicast/redistribute', 'default': False, - 'yang-type': YANG_TYPE.EMPTY}, {'key': 'connected', 'yang-path': 'ipv4-unicast/redistribute-vrf', 'default': False, 'yang-type': YANG_TYPE.EMPTY}, + {'key': 'connected_with_rm', 'yang-key': 'route-map', + 'yang-path': 'ipv4-unicast/redistribute-vrf/connected'}, {'key': 'static', 'yang-path': 'ipv4-unicast/redistribute-vrf', 'default': False, 'yang-type': YANG_TYPE.EMPTY}, + {'key': 'static_with_rm', 'yang-key': 'route-map', + 'yang-path': 'ipv4-unicast/redistribute-vrf/default/static'}, {'key': 'networks_v4', 'yang-path': 'ipv4-unicast/network', 'yang-key': BGPConstants.WITH_MASK, 'type': [Network], 'default': []}, ] @@ -152,34 +153,37 @@ def __init__(self, **kwargs): self.asn = kwargs.get("asn", None) def to_dict(self, context): - result = OrderedDict() - if self.vrf is not None: - vrf = OrderedDict() - vrf[BGPConstants.NAME] = self.vrf - vrf[BGPConstants.IPV4_UNICAST] = { - xml_utils.OPERATION: NC_OPERATION.PUT, - } + if self.vrf is None: + return {} - REDIST_CONST = BGPConstants.REDISTRIBUTE_VRF if context.version_min_17_3 else BGPConstants.REDISTRIBUTE - - # redistribute connected/static is only used with 16.9 - if self.from_device or not context.version_min_17_3: - if self.connected or self.static: - vrf[BGPConstants.IPV4_UNICAST][REDIST_CONST] = {} - if self.connected: - vrf[BGPConstants.IPV4_UNICAST][REDIST_CONST][BGPConstants.CONNECTED] = '' - if self.static: - vrf[BGPConstants.IPV4_UNICAST][REDIST_CONST][BGPConstants.STATIC] = '' - - # networks are currently only announced with 17.4 - if self.networks_v4 and (self.from_device or context.version_min_17_3): - vrf[BGPConstants.IPV4_UNICAST][BGPConstants.NETWORK] = { - BGPConstants.WITH_MASK: [ - net.to_dict(context) for net in sorted(self.networks_v4, key=lambda x: (x.number, x.mask)) - ] - } - result[BGPConstants.VRF] = vrf - return dict(result) + result = {} + + vrf = {} + vrf[BGPConstants.NAME] = self.vrf + vrf[BGPConstants.IPV4_UNICAST] = { + xml_utils.OPERATION: NC_OPERATION.PUT, + } + + if self.connected or self.connected_with_rm or self.static or self.static_with_rm: + redist = vrf[BGPConstants.IPV4_UNICAST].setdefault(BGPConstants.REDISTRIBUTE_VRF, {}) + if self.connected_with_rm: + redist[BGPConstants.CONNECTED] = {BGPConstants.ROUTE_MAP: self.connected_with_rm} + elif self.connected: + redist[BGPConstants.CONNECTED] = '' + + if self.static_with_rm: + redist[BGPConstants.STATIC] = {BGPConstants.DEFAULT: {BGPConstants.ROUTE_MAP: self.static_with_rm}} + elif self.static: + redist[BGPConstants.STATIC] = '' + + if self.networks_v4: + vrf[BGPConstants.IPV4_UNICAST][BGPConstants.NETWORK] = { + BGPConstants.WITH_MASK: [ + net.to_dict(context) for net in sorted(self.networks_v4, key=lambda x: (x.number, x.mask)) + ] + } + result[BGPConstants.VRF] = vrf + return result def to_delete_dict(self, context): result = OrderedDict() diff --git a/asr1k_neutron_l3/models/netconf_yang/prefix.py b/asr1k_neutron_l3/models/netconf_yang/prefix.py index 6bd9381f..a770a83f 100644 --- a/asr1k_neutron_l3/models/netconf_yang/prefix.py +++ b/asr1k_neutron_l3/models/netconf_yang/prefix.py @@ -92,12 +92,10 @@ def __init__(self, **kwargs): @property def neutron_router_id(self): - if self.name is not None and self.name.startswith('ext-'): - return utils.vrf_id_to_uuid(self.name[4:]) - elif self.name is not None and self.name.startswith('snat-'): - return utils.vrf_id_to_uuid(self.name[5:]) - elif self.name is not None and self.name.startswith('route-'): - return utils.vrf_id_to_uuid(self.name[6:]) + for prefix in 'ext-', 'snat-', 'route-', 'BGPVPN-', 'ROUTABLEINT-', 'ROUTABLEEXTRA-': + if self.name and self.name.startswith(prefix): + return utils.vrf_id_to_uuid(self.name[len(prefix):]) + return None def add_seq(self, seq): if seq.no is None: diff --git a/asr1k_neutron_l3/models/netconf_yang/route_map.py b/asr1k_neutron_l3/models/netconf_yang/route_map.py index 2f520347..24fc26ca 100644 --- a/asr1k_neutron_l3/models/netconf_yang/route_map.py +++ b/asr1k_neutron_l3/models/netconf_yang/route_map.py @@ -29,6 +29,9 @@ class RouteMapConstants(object): OPERATION = "operation" SET = "set" EXTCOMMUNITY = 'extcommunity' + COMMUNITY = 'community' + COMMUNITY_WELL_KNOWN = 'community-well-known' + COMMUNITY_LIST = 'community-list' RT = 'rt' RANGE = 'range' ADDITIVE = 'additive' @@ -128,6 +131,8 @@ def __parameters__(cls): {'key': 'prefix_list', 'yang-key': 'prefix-list', 'yang-path': 'match/ip/address'}, {'key': 'access_list', 'yang-key': 'access-list', 'yang-path': 'match/ip/address'}, {'key': 'ip_precedence', 'yang-path': 'set/ip/precedence', 'yang-key': 'precedence-fields'}, + {'key': 'community_list', 'yang-key': 'community-list', 'yang-path': 'set/community/community-well-known', + 'default': []}, {'key': 'drop_on_17_3', 'default': False}, ] @@ -158,24 +163,26 @@ def to_dict(self, context): seq[RouteMapConstants.OPERATION] = self.operation if self.asn: - seq[RouteMapConstants.SET] = { - RouteMapConstants.EXTCOMMUNITY: {RouteMapConstants.RT: {RouteMapConstants.ASN: self.asn}}} + seq.setdefault(RouteMapConstants.SET, {}) + seq[RouteMapConstants.SET][RouteMapConstants.EXTCOMMUNITY] = { + RouteMapConstants.RT: {RouteMapConstants.ASN: self.asn}, + } if self.next_hop is not None: + seq.setdefault(RouteMapConstants.SET, {}) if context.version_min_17_3: - seq[RouteMapConstants.SET] = { - RouteMapConstants.IP: {RouteMapConstants.NEXT_HOP: {RouteMapConstants.ADDRESS: [self.next_hop]}} + seq[RouteMapConstants.SET][RouteMapConstants.IP] = { + RouteMapConstants.NEXT_HOP: {RouteMapConstants.ADDRESS: [self.next_hop]}, } if self.force: # it looks like the force flag is now part of the address list in 17.3+ seq[RouteMapConstants.SET][RouteMapConstants.IP][RouteMapConstants.NEXT_HOP][ RouteMapConstants.ADDRESS].append(RouteMapConstants.FORCE) else: - seq[RouteMapConstants.SET] = { - RouteMapConstants.IP: { - RouteMapConstants.NEXT_HOP: { - RouteMapConstants.NEXT_HOP_ADDR: { - RouteMapConstants.ADDRESS: self.next_hop}}}} + seq[RouteMapConstants.SET][RouteMapConstants.IP] = { + RouteMapConstants.NEXT_HOP: { + RouteMapConstants.NEXT_HOP_ADDR: { + RouteMapConstants.ADDRESS: self.next_hop}}} if self.force: seq[RouteMapConstants.SET][RouteMapConstants.IP][RouteMapConstants.NEXT_HOP][ RouteMapConstants.NEXT_HOP_ADDR][RouteMapConstants.FORCE] = "" @@ -188,13 +195,19 @@ def to_dict(self, context): seq[RouteMapConstants.MATCH] = { RouteMapConstants.IP: {RouteMapConstants.ADDRESS: {RouteMapConstants.ACCESS_LIST: self.access_list}}} if self.ip_precedence: - if RouteMapConstants.SET not in seq: - seq[RouteMapConstants.SET] = {} + seq.setdefault(RouteMapConstants.SET, {}) if RouteMapConstants.IP not in seq[RouteMapConstants.SET]: seq[RouteMapConstants.SET][RouteMapConstants.IP] = {} seq[RouteMapConstants.SET][RouteMapConstants.IP][RouteMapConstants.PRECEDENCE] = { RouteMapConstants.PRECEDENCE_FIELDS: self.ip_precedence, } + if self.community_list: + seq.setdefault(RouteMapConstants.SET, {}) + seq[RouteMapConstants.SET][RouteMapConstants.COMMUNITY] = { + RouteMapConstants.COMMUNITY_WELL_KNOWN: { + RouteMapConstants.COMMUNITY_LIST: self.community_list, + }, + } seq[xml_utils.NS] = xml_utils.NS_CISCO_ROUTE_MAP return seq diff --git a/asr1k_neutron_l3/models/neutron/l3/bgp.py b/asr1k_neutron_l3/models/neutron/l3/bgp.py index 8521320a..d309157f 100644 --- a/asr1k_neutron_l3/models/neutron/l3/bgp.py +++ b/asr1k_neutron_l3/models/neutron/l3/bgp.py @@ -23,7 +23,8 @@ class AddressFamily(base.Base): def __init__(self, vrf, asn=None, routable_interface=False, rt_export=[], - connected_cidrs=[], routable_networks=[], extra_routes=[]): + connected_cidrs=[], routable_networks=[], extra_routes=[], + redist_rm=None): super(AddressFamily, self).__init__() self.vrf = utils.uuid_to_vrf_id(vrf) self.routable_interface = routable_interface @@ -50,7 +51,8 @@ def __init__(self, vrf, asn=None, routable_interface=False, rt_export=[], self.enable_bgp = True self._rest_definition = bgp.AddressFamily(vrf=self.vrf, asn=self.asn, enable_bgp=self.enable_bgp, - static=True, connected=True, networks_v4=self.networks_v4) + networks_v4=self.networks_v4, + static_with_rm=redist_rm, connected_with_rm=redist_rm) def get(self): return bgp.AddressFamily.get(self.vrf, asn=self.asn, enable_bgp=self.enable_bgp) diff --git a/asr1k_neutron_l3/models/neutron/l3/prefix.py b/asr1k_neutron_l3/models/neutron/l3/prefix.py index 9d887a5e..27a888dd 100644 --- a/asr1k_neutron_l3/models/neutron/l3/prefix.py +++ b/asr1k_neutron_l3/models/neutron/l3/prefix.py @@ -24,6 +24,7 @@ class BasePrefix(base.Base): def __init__(self, name_prefix, router_id, gateway_interface, internal_interfaces): self.vrf = utils.uuid_to_vrf_id(router_id) + self.list_name = f"{name_prefix}-{self.vrf}" self.internal_interfaces = internal_interfaces self.gateway_interface = gateway_interface self.gateway_address_scope = None @@ -32,7 +33,7 @@ def __init__(self, name_prefix, router_id, gateway_interface, internal_interface if self.gateway_interface is not None: self.gateway_address_scope = self.gateway_interface.address_scope - self._rest_definition = prefix.Prefix(name="{}-{}".format(name_prefix, self.vrf)) + self._rest_definition = prefix.Prefix(name=self.list_name) class ExtPrefix(BasePrefix): @@ -74,3 +75,26 @@ def __init__(self, router_id=None, gateway_interface=None, internal_interfaces=N permit_ge = utils.prefix_from_cidr(cidr) + 1 self._rest_definition.add_seq(prefix.PrefixSeq(no=i * 10, permit_ip=cidr, permit_ge=permit_ge)) i += 1 + + +class BasePrefixWithPrefixes(BasePrefix): + PREFIX_NAME = None + + def __init__(self, router_id, prefixes): + super().__init__(self.PREFIX_NAME, router_id, None, None) + + self.has_prefixes = bool(prefixes) + for n, cidr in enumerate(sorted(prefixes), 1): + self._rest_definition.add_seq(prefix.PrefixSeq(no=n * 10, permit_ip=cidr)) + + +class BgpvpnPrefixes(BasePrefixWithPrefixes): + PREFIX_NAME = "BGPVPN" + + +class RoutableInternalPrefixes(BasePrefixWithPrefixes): + PREFIX_NAME = "ROUTABLEINT" + + +class RoutableExtraPrefixes(BasePrefixWithPrefixes): + PREFIX_NAME = "ROUTABLEEXTRA" diff --git a/asr1k_neutron_l3/models/neutron/l3/route_map.py b/asr1k_neutron_l3/models/neutron/l3/route_map.py index 45e8bf8a..c867d5d7 100644 --- a/asr1k_neutron_l3/models/neutron/l3/route_map.py +++ b/asr1k_neutron_l3/models/neutron/l3/route_map.py @@ -13,9 +13,11 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. +from oslo_config import cfg + from asr1k_neutron_l3.common import utils from asr1k_neutron_l3.models.netconf_yang import route_map -from asr1k_neutron_l3.models.neutron.l3 import base +from asr1k_neutron_l3.models.neutron.l3 import base, prefix class RouteMap(base.Base): @@ -82,3 +84,29 @@ def __init__(self, name, gateway_interface=None): ip_precedence='routine')) self._rest_definition = route_map.RouteMap(name=self.name, seq=sequences) + + +class BgpvpnRedistRouteMap(base.Base): + def __init__(self, router_id): + super().__init__() + + self.vrf = utils.uuid_to_vrf_id(router_id) + self.name = "BGPVPNREDIST-{}".format(self.vrf) + + sequences = [ + # FIXME: remove once we decided we don't need it (as we do it via network statements) + route_map.MapSequence(seq_no=10, + operation='permit', + access_list=f"{prefix.RoutableInternalPrefixes.PREFIX_NAME}-{self.vrf}"), + + # FIXME: set community + route_map.MapSequence(seq_no=20, + operation='permit', + community_list=cfg.CONF.asr1k_l3.dapn_extra_routes_communities, + access_list=f"{prefix.RoutableExtraPrefixes.PREFIX_NAME}-{self.vrf}"), + route_map.MapSequence(seq_no=30, + operation='permit', + access_list=f"{prefix.BgpvpnPrefixes.PREFIX_NAME}-{self.vrf}"), + ] + + self._rest_definition = route_map.RouteMap(name=self.name, seq=sequences) diff --git a/asr1k_neutron_l3/models/neutron/l3/router.py b/asr1k_neutron_l3/models/neutron/l3/router.py index 603f0296..b1d6ab03 100644 --- a/asr1k_neutron_l3/models/neutron/l3/router.py +++ b/asr1k_neutron_l3/models/neutron/l3/router.py @@ -94,6 +94,7 @@ def __init__(self, router_info): self.pbr_route_map = route_map.PBRRouteMap(self.router_info.get('id'), gateway_interface=self.gateway_interface) self.bgp_address_family = self._build_bgp_address_family() + self.bgpvpn_extras = self._build_bgpvpn_extras() self.dynamic_nat = self._build_dynamic_nat() self.nat_pool = self._build_nat_pool() @@ -222,16 +223,45 @@ def _route_has_connected_interface(self, l3_route): return False def _build_bgp_address_family(self): - connected_cidrs = self.get_internal_cidrs() - extra_routes = list() - if self.router_info["bgpvpn_advertise_extra_routes"]: - extra_routes = [x.cidr for x in self.routes.routes if x.cidr != "0.0.0.0/0"] + routable_connected_cidrs = [ + cidr for cidr in self.get_internal_cidrs() + if any(utils.network_in_network(cidr, rn) for rn in self.get_routable_networks())] return bgp.AddressFamily(self.router_info.get('id'), asn=self.config.asr1k_l3.fabric_asn, routable_interface=self.routable_interface, - rt_export=self.rt_export, connected_cidrs=connected_cidrs, + rt_export=self.rt_export, connected_cidrs=routable_connected_cidrs, routable_networks=self.get_routable_networks(), - extra_routes=extra_routes) + extra_routes=[], + redist_rm="BGPVPNREDIST-{}".format(utils.uuid_to_vrf_id(self.router_id))) + + def _build_bgpvpn_extras(self): + # connected routes without DAPNets (DAPNets will be announced via network statement) + connected_cidrs = [cidr for cidr in self.get_internal_cidrs() + if not any(utils.network_in_network(cidr, rn) + for rn in self.get_routable_networks())] + + # extra routes + extra_routes = [] + if self.router_info["bgpvpn_advertise_extra_routes"]: + extra_routes = [x.cidr for x in self.routes.routes if x.cidr != "0.0.0.0/0"] + + routable_internal = [] + routable_extra = [] + bgpvpn_cidrs = connected_cidrs + + for cidr in extra_routes: + if any(utils.network_in_network(cidr, rn) for rn in self.get_routable_networks()): + routable_extra.append(cidr) + else: + bgpvpn_cidrs.append(cidr) + + return [ + prefix.RoutableInternalPrefixes(self.router_id, routable_internal), # FIXME: will be empty with current setup + prefix.RoutableExtraPrefixes(self.router_id, routable_extra), + prefix.BgpvpnPrefixes(self.router_id, bgpvpn_cidrs), + + route_map.BgpvpnRedistRouteMap(self.router_id), + ] def _build_dynamic_nat(self): pool_nat = nat.DynamicNAT(self.router_id, gateway_interface=self.gateway_interface, @@ -347,8 +377,10 @@ def _update(self): if self.routable_interface or len(self.rt_export) > 0: results.append(self.bgp_address_family.update()) + results.extend(obj.update() for obj in self.bgpvpn_extras) else: results.append(self.bgp_address_family.delete()) + results.extend(obj.delete() for obj in self.bgpvpn_extras) if self.nat_acl: results.append(self.nat_acl.update()) @@ -416,6 +448,7 @@ def _delete(self): results.append(self.nat_acl.delete()) results.append(self.pbr_acl.delete()) results.append(self.bgp_address_family.delete()) + results.extend(obj.delete() for obj in self.bgpvpn_extras) for interface in self.interfaces.all_interfaces: results.append(interface.delete()) @@ -437,6 +470,11 @@ def diff(self): if not bgp_diff.valid: diff_results['bgp'] = bgp_diff.to_dict() + for obj in self.bgpvpn_extras: + obj_diff = obj.diff() + if not obj_diff.valid: + diff_results.setdefault('bgpvpn_extras', []).append(obj_diff.to_dict()) + for prefix_list in self.prefix_lists: if prefix_list.internal_interfaces is not None and len(prefix_list.internal_interfaces) > 0: prefix_diff = prefix_list.diff() diff --git a/asr1k_neutron_l3/tests/unit/models/netconf_yang/test_parsing.py b/asr1k_neutron_l3/tests/unit/models/netconf_yang/test_parsing.py index a6788793..4f819564 100644 --- a/asr1k_neutron_l3/tests/unit/models/netconf_yang/test_parsing.py +++ b/asr1k_neutron_l3/tests/unit/models/netconf_yang/test_parsing.py @@ -20,6 +20,7 @@ from asr1k_neutron_l3.models.netconf_yang.l2_interface import BridgeDomain from asr1k_neutron_l3.models.netconf_yang.vrf import VrfDefinition from asr1k_neutron_l3.models.netconf_yang.nat import StaticNatList +from asr1k_neutron_l3.models.netconf_yang.route_map import RouteMap class ParsingTest(base.BaseTestCase): @@ -246,7 +247,9 @@ def test_bgp_parsing(self): - + + stonechat + @@ -266,6 +269,10 @@ def test_bgp_parsing(self): context = FakeASR1KContext() bgp_af = bgp.AddressFamily.from_xml(bgp_xml, context) + self.assertTrue(bgp_af.connected) + self.assertEqual("stonechat", bgp_af.connected_with_rm) + self.assertTrue(bgp_af.static) + self.assertIsNone(bgp_af.static_with_rm) parsed_cidrs = {net.cidr for net in bgp_af.networks_v4} self.assertEqual(orig_cidrs, parsed_cidrs) for network in bgp_af.networks_v4: @@ -408,3 +415,33 @@ def test_parse_nat_garp_flag(self): snl = StaticNatList.from_xml(xml, context) nat = snl.static_nats[0] self.assertEqual('6657', nat.garp_bdvif_iface) + + def test_parse_route_map_community_set(self): + xml = """ + + + + + RM-DAP-EXTRA-ROUTES + + 10 + permit + + + + 65126 + 4268097541 + + + + + + + + +""" + context = FakeASR1KContext() + rm = RouteMap.from_xml(xml, context) + + self.assertEqual(["65126", "4268097541"], rm.seq[0].community_list) From 4142bf6cdbba695e99657cac9c1ffcd6ff531e0c Mon Sep 17 00:00:00 2001 From: Sebastian Lohff Date: Wed, 14 Aug 2024 16:47:59 +0200 Subject: [PATCH 2/2] Use route-map instead of network for DAPNets We now also advertise DAPNets via route-map. WIP: Commit will most likely be squashed --- asr1k_neutron_l3/common/config.py | 2 ++ .../models/neutron/l3/route_map.py | 4 +--- asr1k_neutron_l3/models/neutron/l3/router.py | 24 +++++++------------ 3 files changed, 11 insertions(+), 19 deletions(-) diff --git a/asr1k_neutron_l3/common/config.py b/asr1k_neutron_l3/common/config.py index e8e31cb4..ca13adf5 100644 --- a/asr1k_neutron_l3/common/config.py +++ b/asr1k_neutron_l3/common/config.py @@ -84,6 +84,8 @@ cfg.StrOpt('dapnet_extra_routes_rm', default='RM-DAP-EXTRA-ROUTES', help="Route-Map to apply to all BGP network statements for extra routes that are contained in a DAPNet"), # FIXME: move defaults away from here + cfg.ListOpt('dapn_routable_nets_communities', default=["65126"], + help="Communities to assign to DAPNets (via redistribute statement)"), cfg.ListOpt('dapn_extra_routes_communities', default=["65126", "4268097541"], help="Communities to assign to DAPNet extraroutes (via redistribute statement)"), cfg.IntOpt('external_iface_arp_timeout', default=1800, diff --git a/asr1k_neutron_l3/models/neutron/l3/route_map.py b/asr1k_neutron_l3/models/neutron/l3/route_map.py index c867d5d7..5d368321 100644 --- a/asr1k_neutron_l3/models/neutron/l3/route_map.py +++ b/asr1k_neutron_l3/models/neutron/l3/route_map.py @@ -94,12 +94,10 @@ def __init__(self, router_id): self.name = "BGPVPNREDIST-{}".format(self.vrf) sequences = [ - # FIXME: remove once we decided we don't need it (as we do it via network statements) route_map.MapSequence(seq_no=10, operation='permit', + community_list=cfg.CONF.asr1k_l3.dapn_routable_nets_communities, access_list=f"{prefix.RoutableInternalPrefixes.PREFIX_NAME}-{self.vrf}"), - - # FIXME: set community route_map.MapSequence(seq_no=20, operation='permit', community_list=cfg.CONF.asr1k_l3.dapn_extra_routes_communities, diff --git a/asr1k_neutron_l3/models/neutron/l3/router.py b/asr1k_neutron_l3/models/neutron/l3/router.py index b1d6ab03..bf50c322 100644 --- a/asr1k_neutron_l3/models/neutron/l3/router.py +++ b/asr1k_neutron_l3/models/neutron/l3/router.py @@ -223,40 +223,32 @@ def _route_has_connected_interface(self, l3_route): return False def _build_bgp_address_family(self): - routable_connected_cidrs = [ - cidr for cidr in self.get_internal_cidrs() - if any(utils.network_in_network(cidr, rn) for rn in self.get_routable_networks())] - return bgp.AddressFamily(self.router_info.get('id'), asn=self.config.asr1k_l3.fabric_asn, routable_interface=self.routable_interface, - rt_export=self.rt_export, connected_cidrs=routable_connected_cidrs, + rt_export=self.rt_export, connected_cidrs=[], routable_networks=self.get_routable_networks(), extra_routes=[], redist_rm="BGPVPNREDIST-{}".format(utils.uuid_to_vrf_id(self.router_id))) def _build_bgpvpn_extras(self): - # connected routes without DAPNets (DAPNets will be announced via network statement) - connected_cidrs = [cidr for cidr in self.get_internal_cidrs() - if not any(utils.network_in_network(cidr, rn) - for rn in self.get_routable_networks())] - - # extra routes extra_routes = [] if self.router_info["bgpvpn_advertise_extra_routes"]: extra_routes = [x.cidr for x in self.routes.routes if x.cidr != "0.0.0.0/0"] routable_internal = [] routable_extra = [] - bgpvpn_cidrs = connected_cidrs - - for cidr in extra_routes: + bgpvpn_cidrs = [] + for cidr in self.get_internal_cidrs() + extra_routes: if any(utils.network_in_network(cidr, rn) for rn in self.get_routable_networks()): - routable_extra.append(cidr) + if cidr in extra_routes: + routable_extra.append(cidr) + else: + routable_internal.append(cidr) else: bgpvpn_cidrs.append(cidr) return [ - prefix.RoutableInternalPrefixes(self.router_id, routable_internal), # FIXME: will be empty with current setup + prefix.RoutableInternalPrefixes(self.router_id, routable_internal), prefix.RoutableExtraPrefixes(self.router_id, routable_extra), prefix.BgpvpnPrefixes(self.router_id, bgpvpn_cidrs),