Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use route-maps instead of network for BGP prefixes #114

Draft
wants to merge 2 commits into
base: stable/yoga-m3
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions asr1k_neutron_l3/common/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@
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_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,
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,
Expand Down
66 changes: 35 additions & 31 deletions asr1k_neutron_l3/models/netconf_yang/bgp.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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': []},
]
Expand Down Expand Up @@ -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()
Expand Down
10 changes: 4 additions & 6 deletions asr1k_neutron_l3/models/netconf_yang/prefix.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
35 changes: 24 additions & 11 deletions asr1k_neutron_l3/models/netconf_yang/route_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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},
]

Expand Down Expand Up @@ -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] = ""
Expand All @@ -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
Expand Down
6 changes: 4 additions & 2 deletions asr1k_neutron_l3/models/neutron/l3/bgp.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down
26 changes: 25 additions & 1 deletion asr1k_neutron_l3/models/neutron/l3/prefix.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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):
Expand Down Expand Up @@ -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"
28 changes: 27 additions & 1 deletion asr1k_neutron_l3/models/neutron/l3/route_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -82,3 +84,27 @@ 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 = [
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}"),
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)
44 changes: 37 additions & 7 deletions asr1k_neutron_l3/models/neutron/l3/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -222,16 +223,37 @@ 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"]

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_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):
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 = []
for cidr in self.get_internal_cidrs() + extra_routes:
if any(utils.network_in_network(cidr, rn) for rn in self.get_routable_networks()):
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),
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,
Expand Down Expand Up @@ -347,8 +369,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())
Expand Down Expand Up @@ -416,6 +440,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())
Expand All @@ -437,6 +462,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()
Expand Down
Loading
Loading