diff --git a/docs/module/lag.md b/docs/module/lag.md new file mode 100644 index 0000000000..76f132d7d5 --- /dev/null +++ b/docs/module/lag.md @@ -0,0 +1,57 @@ +# Link Aggregation Group (LAG) Configuration Module + +This configuration module configures link bonding parameters, for LAGs between 2 devices (i.e. not MC-LAG) + +(lag-platform)= +LAG is currently supported on these platforms: + +| Operating system | lag | LACP off | LACP passive +| --------------------- | :-------: | :--------: | :----------: +| Cumulus Linux | ✅ | ✅ | ❌ +| FRR | ✅ | ✅ | ❌ + +## Parameters + +The following parameters can be set globally or per node/link: + +* **mode**: lag mode, one of "802.3ad" (default), "balance-xor" or "active-backup" +* **lacp**: LACP protocol interval: "fast", "slow" or "off" + + Note that 'link down' is not easily detectable in a virtual environment with veth pairs, therefore it is strongly recommended + to enable LACP whenever possible + +* **lacp_mode**: "active" (default) or "passive"; note that at most 1 node can be passive + +The following parameters can be set per link: +* **members**: List of links that form the LAG, mandatory and formatted like **topology.links** +* **ifindex**: Optional parameter to control naming of the bonding device + +By creating a link with **lag.members** defined, a *lag* type link is created with the given list of member links. + +## Example + +To create a LAG consisting of 2 links between devices 'r1' and 'r2': + +``` +module: [ lag ] + +nodes: [ r1, r2 ] + +links: +- lag.members: [ r1-r2, r1-r2 ] +``` +Additional parameters such as vlan trunks, OSPF cost, etc. can be applied to such *lag* type links. + +In case additional attributes are required for the member links, the members can be expanded: +``` +links: +- lag.members: + - r1: + ifindex: 49 # Use 100G links 1/1/49 and 1/1/50 + r2: + ifindex: 49 + - r1: + ifindex: 50 + r2: + ifindex: 50 +``` diff --git a/netsim/ansible/tasks/frr/initial-clab.yml b/netsim/ansible/tasks/frr/initial-clab.yml index 11a51ae568..23fe209fef 100644 --- a/netsim/ansible/tasks/frr/initial-clab.yml +++ b/netsim/ansible/tasks/frr/initial-clab.yml @@ -5,6 +5,7 @@ modprobe vrf || echo "FAILED" become: true delegate_to: localhost + run_once: true tags: [ print_action, always ] register: modprobe_result ignore_errors: True diff --git a/netsim/ansible/templates/dhcp/cumulus.j2 b/netsim/ansible/templates/dhcp/cumulus.j2 index 7df58b12d9..928ab4f740 100644 --- a/netsim/ansible/templates/dhcp/cumulus.j2 +++ b/netsim/ansible/templates/dhcp/cumulus.j2 @@ -2,7 +2,7 @@ # # Disable IPv6 RA on DHCPv6 client interfaces # -{% for l in interfaces if l.type in ['lan','p2p','stub'] and l.dhcp.client.ipv6 is defined %} +{% for l in interfaces if l.type in ['lan','p2p','stub','lag'] and l.dhcp.client.ipv6 is defined %} {% if loop.first %} echo "Disable IPv6 RA" cat >/tmp/config </etc/network/interfaces.d/12-dhcp.intf </etc/network/interfaces.d/11-physical.intf </etc/frr/vtysh.conf # # Set Ethernet interface MTU -{% for l in interfaces if l.mtu is defined %} +{% for l in interfaces if l.mtu is defined and l.get('type',"")!='lag' %} ip link set {{ l.ifname }} mtu {{ l.mtu }} {% endfor %} + # # Rest of initial configuration done through VTYSH # diff --git a/netsim/ansible/templates/lag/cumulus.j2 b/netsim/ansible/templates/lag/cumulus.j2 new file mode 100644 index 0000000000..2114a5a5cc --- /dev/null +++ b/netsim/ansible/templates/lag/cumulus.j2 @@ -0,0 +1,33 @@ +#!/bin/bash +# +set -e + +echo "LAG: creating bond interface(s)" +# +# Create bond interface entry +# +{%- macro bond_interface(data) %} +auto {{ data.ifname }} +iface {{ data.ifname }} + pre-up ip link add {{ data.ifname }} type bond + bond-slaves {%- for i in interfaces if 'parentindex' in i and i.parentindex==data.linkindex %} {{ i.ifname }}{%- endfor %} +{% set _lacp = data.lag.lacp|default(lag.lacp) %} +{% if _lacp=='slow' %} + bond-lacp-rate slow +{% elif _lacp=='off' or data.lag.mode|default(lag.mode)=="balance-xor" %} + bond-mode balance-xor +{% endif %} +{% endmacro %} + +cat >/etc/network/interfaces.d/20-bond.intf </etc/network/interfaces.d/51-bridge-interfaces.intf < list: link_intf = [] @@ -381,6 +381,7 @@ def assign_link_prefix( addr_pools: Box, nodes: Box, link_path: str = 'links') -> Box: + if 'prefix' in link: # User specified a static link prefix pfx_list = addressing.parse_prefix(link.prefix,path=link_path) if log.debug_active('addr'): # pragma: no cover (debugging printout) @@ -851,7 +852,7 @@ def check_link_type(data: Box) -> bool: if link_type == 'loopback' and node_cnt != 1: log.error( - f'Looopback link {data._linkname} can have a single node attached\n... {data}', + f'Loopback link {data._linkname} can have a single node attached\n... {data}', log.IncorrectValue, 'links') return False @@ -1082,7 +1083,7 @@ def transform(link_list: typing.Optional[Box], defaults: Box, nodes: Box, pools: continue set_link_bridge_name(link,defaults) - link_default_pools = ['p2p','lan'] if link.type == 'p2p' else ['lan'] + link_default_pools = ['p2p','lan'] if link.type in ['p2p','lag'] else ['lan'] assign_link_prefix(link,link_default_pools,pools,nodes,link._linkname) copy_link_gateway(link,nodes) assign_interface_addresses(link,pools,nodes,defaults) diff --git a/netsim/cli/__init__.py b/netsim/cli/__init__.py index 8b89cb31e8..ed234bd657 100755 --- a/netsim/cli/__init__.py +++ b/netsim/cli/__init__.py @@ -27,7 +27,7 @@ def parser_add_debug(parser: argparse.ArgumentParser) -> None: choices=sorted([ 'all','addr','cli','links','libvirt','clab','modules','plugin','template', 'vlan','vrf','quirks','validate','addressing','groups','status', - 'external','defaults']), + 'external','defaults','lag']), help=argparse.SUPPRESS) parser.add_argument('--test', dest='test', action='store',nargs='*', choices=['errors'], diff --git a/netsim/defaults/attributes.yml b/netsim/defaults/attributes.yml index dfd0259c2d..0c0a2cabae 100644 --- a/netsim/defaults/attributes.yml +++ b/netsim/defaults/attributes.yml @@ -55,7 +55,7 @@ link: # Global link attributes _alt_types: [ bool, prefix_str, named_pfx ] role: id pool: id - type: { type: str, valid_values: [ lan, p2p, stub, loopback, tunnel, vlan_member ] } + type: { type: str, valid_values: [ lan, p2p, stub, loopback, tunnel, vlan_member, lag ] } unnumbered: bool interfaces: mtu: { type: int, min_value: 64, max_value: 65535 } diff --git a/netsim/devices/cumulus.yml b/netsim/devices/cumulus.yml index 059e283e22..f0fb938e55 100644 --- a/netsim/devices/cumulus.yml +++ b/netsim/devices/cumulus.yml @@ -3,6 +3,7 @@ description: Cumulus VX 4.x or 5.x configured without NVUE interface_name: swp{ifindex} loopback_interface_name: lo{ifindex if ifindex else ""} tunnel_interface_name: "tun{ifindex}" +lag_interface_name: "bond{lag.ifindex}" mgmt_if: eth0 libvirt: image: CumulusCommunity/cumulus-vx:4.4.5 @@ -63,6 +64,8 @@ features: asymmetrical_irb: True gateway: protocol: [ anycast, vrrp ] + lag: + passive: False ospf: unnumbered: True import: [ bgp, ripv2, connected, vrf ] diff --git a/netsim/devices/frr.yml b/netsim/devices/frr.yml index 074a28760f..e7468eefda 100644 --- a/netsim/devices/frr.yml +++ b/netsim/devices/frr.yml @@ -4,6 +4,7 @@ interface_name: eth{ifindex} mgmt_if: eth0 loopback_interface_name: lo{ifindex if ifindex else ""} tunnel_interface_name: "tun{ifindex}" +lag_interface_name: "bond{lag.ifindex}" routing: _rm_per_af: True group_vars: @@ -71,6 +72,8 @@ features: ipv4: true ipv6: true network: true + lag: + passive: False mpls: ldp: true vpn: diff --git a/netsim/modules/lag.py b/netsim/modules/lag.py new file mode 100644 index 0000000000..aa5a4f00f2 --- /dev/null +++ b/netsim/modules/lag.py @@ -0,0 +1,118 @@ +import typing +import netaddr + +from box import Box, BoxList +from . import _Module, _dataplane +from .. import data +from ..data import types as _types +from ..utils import log +from ..augment import devices, links + +ID_SET = 'lag_id' + +""" +populate_lag_id_set -- Collect any user defined lag.ifindex values globally and initialize ID generator +""" +def populate_lag_id_set(topology: Box) -> None: + _dataplane.create_id_set(ID_SET) + # Note that 0 is a valid lag.ifindex value + LAG_IDS = { l.lag.ifindex for l in topology.links + if isinstance(l.get('lag.ifindex',None),int) } + _dataplane.extend_id_set(ID_SET,LAG_IDS) + _dataplane.set_id_counter(ID_SET,topology.defaults.lag.start_lag_id,100) + +""" +create_lag_member_links -- iterate over topology.links and expand any that have lag.members defined +""" +def create_lag_member_links(l: Box, topology: Box) -> None: + lag_members = l.lag.members + l.lag.pop("members",None) # Remove explicit list of members + l2_ifdata = { 'type': "p2p", 'prefix': False } # Construct an L2 member link + for a in list(topology.defaults.lag.attributes.lag_l2_ifattr): + if a in l: + l2_ifdata[a] = l[a] + + for idx,member in enumerate(lag_members): + member = links.adjust_link_object(member,f'{l._linkname}.lag[{idx+1}]',topology.nodes) + + if len(member.interfaces)!=2: # Check that there are exactly 2 nodes involved + log.error(f'Link {member._linkname} in LAG {l.lag.ifindex} must have exactly 2 nodes', + category=log.IncorrectAttr, + module='lag') + return + else: # Check that they all support LAG + for i in member.interfaces: + _n = topology.nodes[i.node] + features = devices.get_device_features(_n,topology.defaults) + if 'lag' not in features: + log.error(f'Node {_n.name} ({_n.device}) does not support lag module, cannot be part of LAG {member._linkname}', + category=log.IncorrectAttr, + module='lag') + return + if 'lag' not in _n.get('module',[]): + log.error(f'lag module not enabled for node {_n.name}, cannot be part of LAG {member._linkname}', + category=log.IncorrectAttr, + module='lag') + return + + member = l2_ifdata + member # Copy L2 data into member link + member.linkindex = len(topology.links)+1 + member.parentindex = l.linkindex # Keep track of parent + if log.debug_active('lag'): + print(f'LAG create_lag_member_links -> adding link {member}') + topology.links.append(member) + if not l.interfaces: # Copy interfaces from first member link + l.interfaces = member.interfaces + [] # Deep copy, assumes all links have same 2 nodes + else: + base = { n.node for n in l.interfaces } # List the (2) nodes from the first link + others = { n.node for n in member.interfaces if n.node not in base } + if others: + log.error(f'All LAG link members must connect the same pair of nodes({base}), found {others}', + category=log.IncorrectAttr, + module='lag') + +def process_lag_links(topology: Box) -> None: + for l in list(topology.links): + if 'lag' not in l: + continue + elif not 'members' in l.lag: + log.error(f'must define "lag.members" on LAG link {l._linkname}', + category=log.IncorrectAttr, + module='lag') + continue + elif not _types.must_be_list(parent=l.lag,key='members',path=l._linkname,module='lag'): + continue + + l.type = 'lag' + if 'ifindex' not in l.lag: # Use user provided lag.ifindex, if any + l.lag.ifindex = _dataplane.get_next_id(ID_SET) + + create_lag_member_links(l,topology) + +class LAG(_Module): + + def module_pre_transform(self, topology: Box) -> None: + if log.debug_active('lag'): + print(f'LAG module_pre_transform') + populate_lag_id_set(topology) + + # Expand lag.members into additional p2p links + process_lag_links(topology) + + """ + After attribute propagation and consolidation, verify that requested features are supported + + Only gets called for nodes with 'lag' module enabled + """ + def node_post_transform(self, node: Box, topology: Box) -> None: + features = devices.get_device_features(node,topology.defaults) + for i in node.interfaces: + if 'lag' not in i: + continue + + lacp_mode = i.get('lag.lacp_mode') # Inheritance copying is done elsewhere + if lacp_mode=='passive' and not features.lag.get('passive',False): + log.error(f'Node {node.name} does not support passive LACP configured on interface {i.ifname}', + category=log.IncorrectAttr, + module='lag', + hint='lag') diff --git a/netsim/modules/lag.yml b/netsim/modules/lag.yml new file mode 100644 index 0000000000..20566e9f4e --- /dev/null +++ b/netsim/modules/lag.yml @@ -0,0 +1,31 @@ +# LAG default settings and attributes +# +no_propagate: [ start_lag_id ] +start_lag_id: 0 # Start naming bonding interfaces using this lag.ifindex + +lacp: "fast" # Link Aggregation Control Protocol, standby signalling through link down not working +lacp_mode: "active" # At least 1 side must be active +mode: "802.3ad" # Default to active/active with LACP + +attributes: + global: + lacp: { type: str, valid_values: [ "off", "slow", "fast" ] } + lacp_mode: { type: str, valid_values: [ "passive", "active" ] } + mode: { type: str, valid_values: [ "802.3ad", "balance-xor", "active-backup" ] } + node: + lacp: + lacp_mode: + mode: { type: str, valid_values: [ "802.3ad", "balance-xor", "active-backup" ] } + link: # Most should be consistent across both interfaces on the link + lacp: { copy: global } + lacp_mode: { copy: global } + ifindex: { type: int, min_value: 0, max_value: 10000 } # Optional, to control naming of the bonding interface + members: + mode: { type: str, valid_values: [ "802.3ad", "balance-xor", "active-backup" ] } + + node_copy: [lacp,lacp_mode,mode] + + # Copy only these L2 attributes into LAG physical link members + lag_l2_ifattr: + lag.ifindex: + mtu: diff --git a/netsim/modules/vlan.py b/netsim/modules/vlan.py index 80c4cc85a6..eb7c8b307a 100644 --- a/netsim/modules/vlan.py +++ b/netsim/modules/vlan.py @@ -512,7 +512,7 @@ def create_vlan_link_data(init: typing.Union[Box,dict],vname: str, parent: typin return link_data """ -create_vlan_member_interface: Create interface data for a VLAN mamber link +create_vlan_member_interface: Create interface data for a VLAN member link Used by create_vlan_links and create_loopback_vlan_links """ diff --git a/netsim/modules/vlan.yml b/netsim/modules/vlan.yml index 1ef8e54482..9ec7068d2c 100644 --- a/netsim/modules/vlan.yml +++ b/netsim/modules/vlan.yml @@ -1,6 +1,6 @@ # VLAN default settings and attributes # ---- +transform_after: [ lag ] no_propagate: [ start_vlan_id, mode ] start_vlan_id: 1000 mode: irb diff --git a/netsim/providers/clab.yml b/netsim/providers/clab.yml index 503ed83750..ef91201fb2 100644 --- a/netsim/providers/clab.yml +++ b/netsim/providers/clab.yml @@ -20,6 +20,7 @@ cleanup: [ clab.yml, clab_files ] bridge_type: bridge # Use 'ovs-bridge' to create Openvswitch bridges runtime: docker # Default runtime, see Containerlab documentation kmods: + lag: [ bonding ] mpls: [ mpls-router, mpls-iptunnel ] sr: [ mpls-router, mpls-iptunnel ] vxlan: [ vxlan, udp_tunnel, ip6_udp_tunnel ] diff --git a/tests/errors/invalid-module.log b/tests/errors/invalid-module.log index f3e75a55d6..f930454c43 100644 --- a/tests/errors/invalid-module.log +++ b/tests/errors/invalid-module.log @@ -1,5 +1,5 @@ IncorrectValue in topology: attribute nodes.r2.module has invalid value(s): whatever -... valid values are: bfd,bgp,dhcp,eigrp,evpn,gateway,isis,mpls,ospf,ripv2,routing,sr,srv6,vlan,vrf,vxlan +... valid values are: bfd,bgp,dhcp,eigrp,evpn,gateway,isis,lag,mpls,ospf,ripv2,routing,sr,srv6,vlan,vrf,vxlan IncorrectValue in topology: attribute module has invalid value(s): provider -... valid values are: bfd,bgp,dhcp,eigrp,evpn,gateway,isis,mpls,ospf,ripv2,routing,sr,srv6,vlan,vrf,vxlan +... valid values are: bfd,bgp,dhcp,eigrp,evpn,gateway,isis,lag,mpls,ospf,ripv2,routing,sr,srv6,vlan,vrf,vxlan Fatal error in netlab: Cannot proceed beyond this point due to errors, exiting diff --git a/tests/errors/link-invalid-type.log b/tests/errors/link-invalid-type.log index e1f37082a0..e47a24f608 100644 --- a/tests/errors/link-invalid-type.log +++ b/tests/errors/link-invalid-type.log @@ -1,4 +1,4 @@ IncorrectValue in links: attribute links[1].type has invalid value(s): wtf -... valid values are: lan,p2p,stub,loopback,tunnel,vlan_member +... valid values are: lan,p2p,stub,loopback,tunnel,vlan_member,lag ... use 'netlab show attributes link' to display valid attributes Fatal error in netlab: Cannot proceed beyond this point due to errors, exiting diff --git a/tests/errors/module-missing-prerequisite.log b/tests/errors/module-missing-prerequisite.log index 57497e761c..98c749d6b6 100644 --- a/tests/errors/module-missing-prerequisite.log +++ b/tests/errors/module-missing-prerequisite.log @@ -1,3 +1,3 @@ IncorrectValue in topology: attribute nodes.r1.module has invalid value(s): mody -... valid values are: bfd,bgp,dhcp,eigrp,evpn,gateway,isis,modx,mpls,ospf,ripv2,routing,sr,srv6,vlan,vrf,vxlan +... valid values are: bfd,bgp,dhcp,eigrp,evpn,gateway,isis,lag,modx,mpls,ospf,ripv2,routing,sr,srv6,vlan,vrf,vxlan Fatal error in netlab: Cannot proceed beyond this point due to errors, exiting diff --git a/tests/errors/validate-list.log b/tests/errors/validate-list.log index 0adeba9504..8bf3c25b25 100644 --- a/tests/errors/validate-list.log +++ b/tests/errors/validate-list.log @@ -1,5 +1,5 @@ IncorrectValue in groups: attribute groups.g1.module has invalid value(s): a -... valid values are: bfd,bgp,dhcp,eigrp,evpn,gateway,isis,mpls,ospf,ripv2,routing,sr,srv6,vlan,vrf,vxlan +... valid values are: bfd,bgp,dhcp,eigrp,evpn,gateway,isis,lag,mpls,ospf,ripv2,routing,sr,srv6,vlan,vrf,vxlan IncorrectType in groups: attribute 'groups.g2.module' must be a scalar or a list, found dictionary ... use 'netlab show attributes group' to display valid attributes IncorrectType in groups: attribute 'groups.g2.module' must be a scalar or a list, found dictionary diff --git a/tests/integration/lag/01-l3-lag.yml b/tests/integration/lag/01-l3-lag.yml new file mode 100644 index 0000000000..feed428235 --- /dev/null +++ b/tests/integration/lag/01-l3-lag.yml @@ -0,0 +1,36 @@ +--- +message: | + The devices under test are a pair of routers with a L3 LAG link between them + h1 and h2 should be able to ping each other +groups: + _auto_create: True + hosts: + members: [ h1, h2 ] + device: linux + provider: clab + switches: + members: [ r1,r2,r3 ] + module: [ lag, ospf ] + +links: +- lag.members: [r1-r2,r1-r2] +- lag.ifindex: 0 # Name it as 'bond0' -> First one becomes 'bond1' + lag.members: + - r2: + ifindex: 3 + r3: + ifindex: 3 + - r2: + ifindex: 4 + r3: + ifindex: 4 +- h1-r1 +- r3-h2 + +validate: + ping: + description: Pinging H2 from H1 + nodes: [ h1 ] + wait_msg: Waiting for STP to enable the ports + wait: 45 + plugin: ping('h2') diff --git a/tests/integration/lag/02-l3-lag-with-vlans.yml b/tests/integration/lag/02-l3-lag-with-vlans.yml new file mode 100644 index 0000000000..b025cc2b3a --- /dev/null +++ b/tests/integration/lag/02-l3-lag-with-vlans.yml @@ -0,0 +1,47 @@ +--- +message: | + The devices under test are a pair of routers with a L3 LAG link with a trunk of VLANs between them + h1 and h2 should be able to ping each other, same applies for h3 and h4 +groups: + _auto_create: True + hosts: + members: [ h1, h2, h3, h4 ] + device: linux + provider: clab + switches: + members: [ r1,r2 ] + module: [lag,vlan] + +vlans: + v1: + v2: + +links: +- vlan.trunk: [ v1, v2 ] + lag.members: + - r1-r2 + - r1-r2 +- r1: + vlan.access: v1 + h1: +- r2: + vlan.access: v1 + h2: +- r1: + vlan.access: v2 + h3: +- r2: + vlan.access: v2 + h4: + +validate: + ping12: + description: Pinging H2 from H1 on VLAN v1 + nodes: [ h1 ] + wait_msg: Waiting for STP to enable the ports + wait: 45 + plugin: ping('h2') + ping34: + description: Pinging H4 from H3 on VLAN v2 + nodes: [ h3 ] + plugin: ping('h4') \ No newline at end of file diff --git a/tests/topology/expected/lag-l3-vlan-trunk.yml b/tests/topology/expected/lag-l3-vlan-trunk.yml new file mode 100644 index 0000000000..d6af16bb08 --- /dev/null +++ b/tests/topology/expected/lag-l3-vlan-trunk.yml @@ -0,0 +1,461 @@ +input: +- topology/input/lag-l3-vlan-trunk.yml +- package:topology-defaults.yml +lag: + lacp: fast + lacp_mode: active + mode: 802.3ad +links: +- bridge: input_1 + interfaces: + - ifindex: 30000 + ifname: bond0 + node: r1 + vlan: + trunk: + v1: {} + v2: {} + - ifindex: 30000 + ifname: bond0 + node: r2 + vlan: + trunk: + v1: {} + v2: {} + lag: + ifindex: 0 + linkindex: 1 + node_count: 2 + prefix: {} + type: lag + vlan: + trunk: + v1: {} + v2: {} +- interfaces: + - ifindex: 1 + ifname: eth1 + node: r1 + - ifindex: 1 + ifname: eth1 + node: r2 + lag: + ifindex: 0 + linkindex: 2 + node_count: 2 + parentindex: 1 + prefix: false + type: p2p +- interfaces: + - ifindex: 2 + ifname: eth2 + node: r1 + - ifindex: 2 + ifname: eth2 + node: r2 + lag: + ifindex: 0 + linkindex: 3 + node_count: 2 + parentindex: 1 + prefix: false + type: p2p +- interfaces: + - ifindex: 3 + ifname: eth3 + node: r1 + - ifindex: 3 + ifname: eth3 + node: r2 + lag: + ifindex: 0 + linkindex: 4 + node_count: 2 + parentindex: 1 + prefix: false + type: p2p +module: +- lag +- vlan +name: input +nodes: + r1: + af: + ipv4: true + box: quay.io/frrouting/frr:10.0.1 + clab: + binds: + - clab_files/r1/daemons:/etc/frr/daemons + - clab_files/r1/hosts:/etc/hosts + config_templates: + - daemons:/etc/frr/daemons + - hosts:/etc/hosts + kind: linux + device: frr + hostname: clab-input-r1 + id: 1 + interfaces: + - ifindex: 30000 + ifname: bond0 + lag: + ifindex: 0 + lacp: fast + lacp_mode: active + mode: 802.3ad + linkindex: 1 + mtu: 1500 + name: r1 -> r2 + neighbors: + - ifname: bond0 + node: r2 + subif_index: 2 + type: lag + virtual_interface: true + - ifindex: 1 + ifname: eth1 + lag: + ifindex: 0 + lacp: fast + lacp_mode: active + mode: 802.3ad + linkindex: 2 + mtu: 1500 + name: r1 -> r2 + neighbors: + - ifname: eth1 + node: r2 + parentindex: 1 + type: p2p + - ifindex: 2 + ifname: eth2 + lag: + ifindex: 0 + lacp: fast + lacp_mode: active + mode: 802.3ad + linkindex: 3 + mtu: 1500 + name: r1 -> r2 + neighbors: + - ifname: eth2 + node: r2 + parentindex: 1 + type: p2p + - ifindex: 3 + ifname: eth3 + lag: + ifindex: 0 + lacp: fast + lacp_mode: active + mode: 802.3ad + linkindex: 4 + mtu: 1500 + name: r1 -> r2 + neighbors: + - ifname: eth3 + node: r2 + parentindex: 1 + type: p2p + - ifindex: 4 + ifname: bond0.1000 + lag: + lacp: fast + lacp_mode: active + mode: 802.3ad + parent_ifindex: 30000 + parent_ifname: bond0 + type: vlan_member + virtual_interface: true + vlan: + access: v1 + access_id: 1000 + - ifindex: 5 + ifname: bond0.1001 + lag: + lacp: fast + lacp_mode: active + mode: 802.3ad + parent_ifindex: 30000 + parent_ifname: bond0 + type: vlan_member + virtual_interface: true + vlan: + access: v2 + access_id: 1001 + - bridge_group: 1 + ifindex: 6 + ifname: vlan1000 + ipv4: 172.16.0.1/24 + lag: + lacp: fast + lacp_mode: active + mode: 802.3ad + name: VLAN v1 (1000) -> [r2] + neighbors: + - ifname: vlan1000 + ipv4: 172.16.0.2/24 + node: r2 + type: svi + virtual_interface: true + vlan: + mode: irb + name: v1 + - bridge_group: 2 + ifindex: 7 + ifname: vlan1001 + ipv4: 172.16.1.1/24 + lag: + lacp: fast + lacp_mode: active + mode: 802.3ad + name: VLAN v2 (1001) -> [r2] + neighbors: + - ifname: vlan1001 + ipv4: 172.16.1.2/24 + node: r2 + type: svi + virtual_interface: true + vlan: + mode: irb + name: v2 + lag: + lacp: fast + lacp_mode: active + mode: 802.3ad + loopback: + ifindex: 0 + ifname: lo + ipv4: 10.0.0.1/32 + neighbors: [] + type: loopback + virtual_interface: true + mgmt: + ifname: eth0 + ipv4: 192.168.121.101 + mac: 08:4f:a9:00:00:01 + module: + - lag + - vlan + mtu: 1500 + name: r1 + vlan: + max_bridge_group: 2 + vlans: + v1: + bridge_group: 1 + id: 1000 + mode: irb + prefix: + allocation: id_based + ipv4: 172.16.0.0/24 + v2: + bridge_group: 2 + id: 1001 + mode: irb + prefix: + allocation: id_based + ipv4: 172.16.1.0/24 + r2: + af: + ipv4: true + box: quay.io/frrouting/frr:10.0.1 + clab: + binds: + - clab_files/r2/daemons:/etc/frr/daemons + - clab_files/r2/hosts:/etc/hosts + config_templates: + - daemons:/etc/frr/daemons + - hosts:/etc/hosts + kind: linux + device: frr + hostname: clab-input-r2 + id: 2 + interfaces: + - ifindex: 30000 + ifname: bond0 + lag: + ifindex: 0 + lacp: fast + lacp_mode: active + mode: 802.3ad + linkindex: 1 + mtu: 1500 + name: r2 -> r1 + neighbors: + - ifname: bond0 + node: r1 + subif_index: 2 + type: lag + virtual_interface: true + - ifindex: 1 + ifname: eth1 + lag: + ifindex: 0 + lacp: fast + lacp_mode: active + mode: 802.3ad + linkindex: 2 + mtu: 1500 + name: r2 -> r1 + neighbors: + - ifname: eth1 + node: r1 + parentindex: 1 + type: p2p + - ifindex: 2 + ifname: eth2 + lag: + ifindex: 0 + lacp: fast + lacp_mode: active + mode: 802.3ad + linkindex: 3 + mtu: 1500 + name: r2 -> r1 + neighbors: + - ifname: eth2 + node: r1 + parentindex: 1 + type: p2p + - ifindex: 3 + ifname: eth3 + lag: + ifindex: 0 + lacp: fast + lacp_mode: active + mode: 802.3ad + linkindex: 4 + mtu: 1500 + name: r2 -> r1 + neighbors: + - ifname: eth3 + node: r1 + parentindex: 1 + type: p2p + - ifindex: 4 + ifname: bond0.1000 + lag: + lacp: fast + lacp_mode: active + mode: 802.3ad + parent_ifindex: 30000 + parent_ifname: bond0 + type: vlan_member + virtual_interface: true + vlan: + access: v1 + access_id: 1000 + - ifindex: 5 + ifname: bond0.1001 + lag: + lacp: fast + lacp_mode: active + mode: 802.3ad + parent_ifindex: 30000 + parent_ifname: bond0 + type: vlan_member + virtual_interface: true + vlan: + access: v2 + access_id: 1001 + - bridge_group: 1 + ifindex: 6 + ifname: vlan1000 + ipv4: 172.16.0.2/24 + lag: + lacp: fast + lacp_mode: active + mode: 802.3ad + name: VLAN v1 (1000) -> [r1] + neighbors: + - ifname: vlan1000 + ipv4: 172.16.0.1/24 + node: r1 + type: svi + virtual_interface: true + vlan: + mode: irb + name: v1 + - bridge_group: 2 + ifindex: 7 + ifname: vlan1001 + ipv4: 172.16.1.2/24 + lag: + lacp: fast + lacp_mode: active + mode: 802.3ad + name: VLAN v2 (1001) -> [r1] + neighbors: + - ifname: vlan1001 + ipv4: 172.16.1.1/24 + node: r1 + type: svi + virtual_interface: true + vlan: + mode: irb + name: v2 + lag: + lacp: fast + lacp_mode: active + mode: 802.3ad + loopback: + ifindex: 0 + ifname: lo + ipv4: 10.0.0.2/32 + neighbors: [] + type: loopback + virtual_interface: true + mgmt: + ifname: eth0 + ipv4: 192.168.121.102 + mac: 08:4f:a9:00:00:02 + module: + - lag + - vlan + mtu: 1500 + name: r2 + vlan: + max_bridge_group: 2 + vlans: + v1: + bridge_group: 1 + id: 1000 + mode: irb + prefix: + allocation: id_based + ipv4: 172.16.0.0/24 + v2: + bridge_group: 2 + id: 1001 + mode: irb + prefix: + allocation: id_based + ipv4: 172.16.1.0/24 +provider: clab +vlans: + v1: + host_count: 0 + id: 1000 + neighbors: + - ifname: vlan1000 + ipv4: 172.16.0.2/24 + node: r2 + - ifname: vlan1000 + ipv4: 172.16.0.1/24 + node: r1 + prefix: + allocation: id_based + ipv4: 172.16.0.0/24 + v2: + host_count: 0 + id: 1001 + neighbors: + - ifname: vlan1001 + ipv4: 172.16.1.2/24 + node: r2 + - ifname: vlan1001 + ipv4: 172.16.1.1/24 + node: r1 + prefix: + allocation: id_based + ipv4: 172.16.1.0/24 diff --git a/tests/topology/expected/lag-l3.yml b/tests/topology/expected/lag-l3.yml new file mode 100644 index 0000000000..3129279126 --- /dev/null +++ b/tests/topology/expected/lag-l3.yml @@ -0,0 +1,225 @@ +input: +- topology/input/lag-l3.yml +- package:topology-defaults.yml +lag: + lacp: fast + lacp_mode: active + mode: 802.3ad +links: +- bridge: input_1 + interfaces: + - ifindex: 30000 + ifname: bond0 + ipv4: 10.1.0.1/30 + node: r1 + - ifindex: 30000 + ifname: bond0 + ipv4: 10.1.0.2/30 + node: r2 + lag: + ifindex: 0 + linkindex: 1 + mtu: 1600 + node_count: 2 + prefix: + ipv4: 10.1.0.0/30 + type: lag +- interfaces: + - ifindex: 1 + ifname: eth1 + node: r1 + - ifindex: 1 + ifname: eth1 + node: r2 + lag: + ifindex: 0 + linkindex: 2 + mtu: 1600 + node_count: 2 + parentindex: 1 + prefix: false + type: p2p +- interfaces: + - ifindex: 2 + ifname: eth2 + node: r1 + - ifindex: 2 + ifname: eth2 + node: r2 + lag: + ifindex: 0 + linkindex: 3 + mtu: 1600 + node_count: 2 + parentindex: 1 + prefix: false + type: p2p +module: +- lag +name: input +nodes: + r1: + af: + ipv4: true + box: quay.io/frrouting/frr:10.0.1 + clab: + binds: + - clab_files/r1/daemons:/etc/frr/daemons + - clab_files/r1/hosts:/etc/hosts + config_templates: + - daemons:/etc/frr/daemons + - hosts:/etc/hosts + kind: linux + device: frr + hostname: clab-input-r1 + id: 1 + interfaces: + - ifindex: 30000 + ifname: bond0 + ipv4: 10.1.0.1/30 + lag: + ifindex: 0 + lacp: fast + lacp_mode: active + mode: 802.3ad + linkindex: 1 + mtu: 1600 + name: r1 -> r2 + neighbors: + - ifname: bond0 + ipv4: 10.1.0.2/30 + node: r2 + type: lag + virtual_interface: true + - ifindex: 1 + ifname: eth1 + lag: + ifindex: 0 + lacp: fast + lacp_mode: active + mode: 802.3ad + linkindex: 2 + mtu: 1600 + name: r1 -> r2 + neighbors: + - ifname: eth1 + node: r2 + parentindex: 1 + type: p2p + - ifindex: 2 + ifname: eth2 + lag: + ifindex: 0 + lacp: fast + lacp_mode: active + mode: 802.3ad + linkindex: 3 + mtu: 1600 + name: r1 -> r2 + neighbors: + - ifname: eth2 + node: r2 + parentindex: 1 + type: p2p + lag: + lacp: fast + lacp_mode: active + mode: 802.3ad + loopback: + ifindex: 0 + ifname: lo + ipv4: 10.0.0.1/32 + neighbors: [] + type: loopback + virtual_interface: true + mgmt: + ifname: eth0 + ipv4: 192.168.121.101 + mac: 08:4f:a9:00:00:01 + module: + - lag + mtu: 1500 + name: r1 + r2: + af: + ipv4: true + box: quay.io/frrouting/frr:10.0.1 + clab: + binds: + - clab_files/r2/daemons:/etc/frr/daemons + - clab_files/r2/hosts:/etc/hosts + config_templates: + - daemons:/etc/frr/daemons + - hosts:/etc/hosts + kind: linux + device: frr + hostname: clab-input-r2 + id: 2 + interfaces: + - ifindex: 30000 + ifname: bond0 + ipv4: 10.1.0.2/30 + lag: + ifindex: 0 + lacp: fast + lacp_mode: active + mode: 802.3ad + linkindex: 1 + mtu: 1600 + name: r2 -> r1 + neighbors: + - ifname: bond0 + ipv4: 10.1.0.1/30 + node: r1 + type: lag + virtual_interface: true + - ifindex: 1 + ifname: eth1 + lag: + ifindex: 0 + lacp: fast + lacp_mode: active + mode: 802.3ad + linkindex: 2 + mtu: 1600 + name: r2 -> r1 + neighbors: + - ifname: eth1 + node: r1 + parentindex: 1 + type: p2p + - ifindex: 2 + ifname: eth2 + lag: + ifindex: 0 + lacp: fast + lacp_mode: active + mode: 802.3ad + linkindex: 3 + mtu: 1600 + name: r2 -> r1 + neighbors: + - ifname: eth2 + node: r1 + parentindex: 1 + type: p2p + lag: + lacp: fast + lacp_mode: active + mode: 802.3ad + loopback: + ifindex: 0 + ifname: lo + ipv4: 10.0.0.2/32 + neighbors: [] + type: loopback + virtual_interface: true + mgmt: + ifname: eth0 + ipv4: 192.168.121.102 + mac: 08:4f:a9:00:00:02 + module: + - lag + mtu: 1500 + name: r2 +provider: clab diff --git a/tests/topology/input/lag-l3-vlan-trunk.yml b/tests/topology/input/lag-l3-vlan-trunk.yml new file mode 100644 index 0000000000..99c2578b3b --- /dev/null +++ b/tests/topology/input/lag-l3-vlan-trunk.yml @@ -0,0 +1,18 @@ +# +# Basic L3 LAG with VLANs example - 3 member links +# + +defaults: + provider: clab + device: frr + +module: [ lag,vlan ] + +vlans: + v1: + v2: + +nodes: [ r1, r2 ] +links: +- vlan.trunk: [v1,v2] + lag.members: [ r1-r2, r1-r2, r1-r2 ] diff --git a/tests/topology/input/lag-l3.yml b/tests/topology/input/lag-l3.yml new file mode 100644 index 0000000000..1dffdb07c6 --- /dev/null +++ b/tests/topology/input/lag-l3.yml @@ -0,0 +1,13 @@ +# +# Basic L3 LAG example - single lag with 2 member links and custom MTU setting +# + +defaults: + provider: clab + device: frr + +module: [ lag ] +nodes: [ r1, r2 ] +links: +- mtu: 1600 # Test that MTU is copied to member links + lag.members: [ r1-r2, r1-r2 ]