First part of a BVI refactor
The handling of BVI is awkward, with the autoderived interface name "bviXX" based on the bridgedomain bd_id. Lots of special casing happens on account of this decision, and to make matters worse there is poor interaction (leading to VPP crashes) when BVIs and Loopbacks are used at the same time: https://lists.fd.io/g/vpp-dev/message/21116 This is step one of a refactor of the logic. In this commit, I'm removing all of the BVI logic from the codebase, rendering bridgedomains unable to have IP interfaces. In the next commit, I will introduce new behavior in the schema, allowing for 'bvi' to be a loopback interfacename which will be used as BVI for a bridgedomain, restoring the ability to use bridgedomains with IP interfaces (using a loop).
This commit is contained in:
@ -146,9 +146,6 @@ and finally all objects are synchronized with the configuration (IP addresses, M
|
|||||||
* Remove all member interfaces (including BVIs) that are not in the config, return them to
|
* Remove all member interfaces (including BVIs) that are not in the config, return them to
|
||||||
L3 mode
|
L3 mode
|
||||||
* Remove tag-rewrite options on removed member interfaces if they have encapsulation
|
* Remove tag-rewrite options on removed member interfaces if they have encapsulation
|
||||||
1. Retrieve all BVIs from VPP
|
|
||||||
* Remove all IP addresses that are not in the config
|
|
||||||
* Remove those that do not exist in the config
|
|
||||||
1. For L2 Cross Connects from VPP
|
1. For L2 Cross Connects from VPP
|
||||||
* For interfaces that do not exist in the config (either as source or target):
|
* For interfaces that do not exist in the config (either as source or target):
|
||||||
* Return the interface to L3 mode
|
* Return the interface to L3 mode
|
||||||
@ -172,7 +169,6 @@ and finally all objects are synchronized with the configuration (IP addresses, M
|
|||||||
### Creating
|
### Creating
|
||||||
|
|
||||||
1. Loopbacks
|
1. Loopbacks
|
||||||
1. BVIs
|
|
||||||
1. BondEthernets
|
1. BondEthernets
|
||||||
1. Tunnels
|
1. Tunnels
|
||||||
1. Sub Interfaces: First Dot1Q and Dot1AD (one tag), and then QinQ and QinAD (two tags)
|
1. Sub Interfaces: First Dot1Q and Dot1AD (one tag), and then QinQ and QinAD (two tags)
|
||||||
|
@ -16,16 +16,6 @@ import config.interface as interface
|
|||||||
import config.lcp as lcp
|
import config.lcp as lcp
|
||||||
import config.address as address
|
import config.address as address
|
||||||
|
|
||||||
def get_bvis(yaml):
|
|
||||||
""" Return a list of all bridgedomains which have an LCP (ie bvi*). """
|
|
||||||
ret = []
|
|
||||||
if not 'bridgedomains' in yaml:
|
|
||||||
return ret
|
|
||||||
for ifname, iface in yaml['bridgedomains'].items():
|
|
||||||
if 'lcp' in iface:
|
|
||||||
instance = int(ifname[2:])
|
|
||||||
ret.append("bvi%d" % instance)
|
|
||||||
return ret
|
|
||||||
|
|
||||||
def get_bridgedomains(yaml):
|
def get_bridgedomains(yaml):
|
||||||
""" Return a list of all bridgedomains. """
|
""" Return a list of all bridgedomains. """
|
||||||
@ -37,33 +27,6 @@ def get_bridgedomains(yaml):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def get_by_lcp_name(yaml, lcpname):
|
|
||||||
""" Returns the bridgedomain by a given lcp name, or None,None if it does not exist """
|
|
||||||
if not 'bridgedomains' in yaml:
|
|
||||||
return None,None
|
|
||||||
for ifname, iface in yaml['bridgedomains'].items():
|
|
||||||
if 'lcp' in iface and iface['lcp'] == lcpname:
|
|
||||||
return ifname, iface
|
|
||||||
return None,None
|
|
||||||
|
|
||||||
|
|
||||||
def get_by_bvi_name(yaml, ifname):
|
|
||||||
""" Return the BridgeDomain by BVI interface name (bvi*), if it exists. Return None,None otherwise. """
|
|
||||||
|
|
||||||
if not 'bridgedomains' in yaml:
|
|
||||||
return None,None
|
|
||||||
if not ifname.startswith("bvi"):
|
|
||||||
return None,None
|
|
||||||
idx = ifname[3:]
|
|
||||||
if not idx.isnumeric():
|
|
||||||
return None,None
|
|
||||||
|
|
||||||
bridgename = "bd%d" % int(idx)
|
|
||||||
if bridgename in yaml['bridgedomains'] and 'lcp' in yaml['bridgedomains'][bridgename]:
|
|
||||||
return bridgename, yaml['bridgedomains'][bridgename]
|
|
||||||
return None, None
|
|
||||||
|
|
||||||
|
|
||||||
def get_by_name(yaml, ifname):
|
def get_by_name(yaml, ifname):
|
||||||
""" Return the BridgeDomain by name (bd*), if it exists. Return None,None otherwise. """
|
""" Return the BridgeDomain by name (bd*), if it exists. Return None,None otherwise. """
|
||||||
try:
|
try:
|
||||||
@ -80,19 +43,6 @@ def is_bridgedomain(yaml, ifname):
|
|||||||
return not iface == None
|
return not iface == None
|
||||||
|
|
||||||
|
|
||||||
def is_bvi(yaml, ifname):
|
|
||||||
""" Returns True if the interface name (bvi*) is an existing BVI. """
|
|
||||||
|
|
||||||
bridgename, bridge = get_by_bvi_name(yaml, ifname)
|
|
||||||
if not bridge:
|
|
||||||
return False
|
|
||||||
|
|
||||||
## If the BridgeDomain has an 'lcp', then it has a Bridge Virtual Interface
|
|
||||||
if 'lcp' in bridge:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def get_bridge_interfaces(yaml):
|
def get_bridge_interfaces(yaml):
|
||||||
""" Returns a list of all interfaces that are bridgedomain members """
|
""" Returns a list of all interfaces that are bridgedomain members """
|
||||||
|
|
||||||
@ -138,18 +88,6 @@ def validate_bridgedomains(yaml):
|
|||||||
msgs.append("bridgedomain %s is reserved" % ifname)
|
msgs.append("bridgedomain %s is reserved" % ifname)
|
||||||
result = False
|
result = False
|
||||||
|
|
||||||
if 'addresses' in iface and not 'lcp' in iface:
|
|
||||||
msgs.append("bridgedomain %s has an address but no LCP" % ifname)
|
|
||||||
result = False
|
|
||||||
if 'lcp' in iface and not lcp.is_unique(yaml, iface['lcp']):
|
|
||||||
msgs.append("bridgedomain %s does not have a unique LCP name %s" % (ifname, iface['lcp']))
|
|
||||||
result = False
|
|
||||||
if 'addresses' in iface:
|
|
||||||
for a in iface['addresses']:
|
|
||||||
if not address.is_allowed(yaml, ifname, iface['addresses'], a):
|
|
||||||
msgs.append("bridgedomain %s IP address %s conflicts with another" % (ifname, a))
|
|
||||||
result = False
|
|
||||||
|
|
||||||
if 'interfaces' in iface:
|
if 'interfaces' in iface:
|
||||||
for member in iface['interfaces']:
|
for member in iface['interfaces']:
|
||||||
if (None, None) == interface.get_by_name(yaml, member):
|
if (None, None) == interface.get_by_name(yaml, member):
|
||||||
@ -160,7 +98,6 @@ def validate_bridgedomains(yaml):
|
|||||||
if not is_bridge_interface_unique(yaml, member):
|
if not is_bridge_interface_unique(yaml, member):
|
||||||
msgs.append("bridgedomain %s member %s is not unique" % (ifname, member))
|
msgs.append("bridgedomain %s member %s is not unique" % (ifname, member))
|
||||||
result = False
|
result = False
|
||||||
|
|
||||||
if interface.has_lcp(yaml, member):
|
if interface.has_lcp(yaml, member):
|
||||||
msgs.append("bridgedomain %s member %s has an LCP" % (ifname, member))
|
msgs.append("bridgedomain %s member %s has an LCP" % (ifname, member))
|
||||||
result = False
|
result = False
|
||||||
|
@ -256,7 +256,7 @@ def get_phys(yaml):
|
|||||||
""" Return a list of all toplevel (ie. non-sub) interfaces which are
|
""" Return a list of all toplevel (ie. non-sub) interfaces which are
|
||||||
assumed to be physical network cards, eg TenGigabitEthernet1/0/0. Note
|
assumed to be physical network cards, eg TenGigabitEthernet1/0/0. Note
|
||||||
that derived/created interfaces such as Tunnels, BondEthernets and
|
that derived/created interfaces such as Tunnels, BondEthernets and
|
||||||
Loopbacks/BVIs are not returned """
|
Loopbacks are not returned """
|
||||||
ret = []
|
ret = []
|
||||||
if not 'interfaces' in yaml:
|
if not 'interfaces' in yaml:
|
||||||
return ret
|
return ret
|
||||||
@ -279,8 +279,6 @@ def is_phy(yaml, ifname):
|
|||||||
return False
|
return False
|
||||||
if loopback.is_loopback(yaml, ifname):
|
if loopback.is_loopback(yaml, ifname):
|
||||||
return False
|
return False
|
||||||
if bridgedomain.is_bvi(yaml, ifname):
|
|
||||||
return False
|
|
||||||
if vxlan_tunnel.is_vxlan_tunnel(yaml, ifname):
|
if vxlan_tunnel.is_vxlan_tunnel(yaml, ifname):
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
@ -7,28 +7,6 @@ class TestBridgeDomainMethods(unittest.TestCase):
|
|||||||
with open("unittest/test_bridgedomain.yaml", "r") as f:
|
with open("unittest/test_bridgedomain.yaml", "r") as f:
|
||||||
self.cfg = yaml.load(f, Loader = yaml.FullLoader)
|
self.cfg = yaml.load(f, Loader = yaml.FullLoader)
|
||||||
|
|
||||||
def test_get_by_lcp_name(self):
|
|
||||||
ifname, iface = bridgedomain.get_by_lcp_name(self.cfg, "bvi12")
|
|
||||||
self.assertIsNotNone(iface)
|
|
||||||
self.assertEqual("bd12", ifname)
|
|
||||||
|
|
||||||
ifname, iface = bridgedomain.get_by_lcp_name(self.cfg, "bvi-noexist")
|
|
||||||
self.assertIsNone(iface)
|
|
||||||
self.assertIsNone(ifname)
|
|
||||||
|
|
||||||
def test_get_by_bvi_name(self):
|
|
||||||
ifname, iface = bridgedomain.get_by_bvi_name(self.cfg, "bvi11")
|
|
||||||
self.assertEqual("bd11", ifname)
|
|
||||||
self.assertIsNotNone(iface)
|
|
||||||
|
|
||||||
ifname, iface = bridgedomain.get_by_bvi_name(self.cfg, "bvi10")
|
|
||||||
self.assertIsNone(ifname)
|
|
||||||
self.assertIsNone(iface)
|
|
||||||
|
|
||||||
ifname, iface = bridgedomain.get_by_bvi_name(self.cfg, "bd11")
|
|
||||||
self.assertIsNone(ifname)
|
|
||||||
self.assertIsNone(iface)
|
|
||||||
|
|
||||||
def test_get_by_name(self):
|
def test_get_by_name(self):
|
||||||
ifname, iface = bridgedomain.get_by_name(self.cfg, "bd10")
|
ifname, iface = bridgedomain.get_by_name(self.cfg, "bd10")
|
||||||
self.assertIsNotNone(iface)
|
self.assertIsNotNone(iface)
|
||||||
@ -43,17 +21,9 @@ class TestBridgeDomainMethods(unittest.TestCase):
|
|||||||
def test_is_bridgedomain(self):
|
def test_is_bridgedomain(self):
|
||||||
self.assertTrue(bridgedomain.is_bridgedomain(self.cfg, "bd10"))
|
self.assertTrue(bridgedomain.is_bridgedomain(self.cfg, "bd10"))
|
||||||
self.assertTrue(bridgedomain.is_bridgedomain(self.cfg, "bd11"))
|
self.assertTrue(bridgedomain.is_bridgedomain(self.cfg, "bd11"))
|
||||||
self.assertTrue(bridgedomain.is_bridgedomain(self.cfg, "bd12"))
|
|
||||||
self.assertFalse(bridgedomain.is_bridgedomain(self.cfg, "bd-notexist"))
|
self.assertFalse(bridgedomain.is_bridgedomain(self.cfg, "bd-notexist"))
|
||||||
self.assertFalse(bridgedomain.is_bridgedomain(self.cfg, "GigabitEthernet1/0/0"))
|
self.assertFalse(bridgedomain.is_bridgedomain(self.cfg, "GigabitEthernet1/0/0"))
|
||||||
|
|
||||||
def test_is_bvi(self):
|
|
||||||
self.assertFalse(bridgedomain.is_bvi(self.cfg, "bvi10"))
|
|
||||||
self.assertTrue(bridgedomain.is_bvi(self.cfg, "bvi11"))
|
|
||||||
self.assertTrue(bridgedomain.is_bvi(self.cfg, "bvi12"))
|
|
||||||
self.assertFalse(bridgedomain.is_bvi(self.cfg, "bvi-notexist"))
|
|
||||||
self.assertFalse(bridgedomain.is_bvi(self.cfg, "GigabitEthernet1/0/0"))
|
|
||||||
|
|
||||||
def test_members(self):
|
def test_members(self):
|
||||||
self.assertTrue(bridgedomain.is_bridge_interface(self.cfg, "GigabitEthernet1/0/0"))
|
self.assertTrue(bridgedomain.is_bridge_interface(self.cfg, "GigabitEthernet1/0/0"))
|
||||||
self.assertTrue(bridgedomain.is_bridge_interface(self.cfg, "GigabitEthernet2/0/0.100"))
|
self.assertTrue(bridgedomain.is_bridge_interface(self.cfg, "GigabitEthernet2/0/0.100"))
|
||||||
@ -74,10 +44,3 @@ class TestBridgeDomainMethods(unittest.TestCase):
|
|||||||
def test_get_bridgedomains(self):
|
def test_get_bridgedomains(self):
|
||||||
ifs = bridgedomain.get_bridgedomains(self.cfg)
|
ifs = bridgedomain.get_bridgedomains(self.cfg)
|
||||||
self.assertEqual(len(ifs), 3)
|
self.assertEqual(len(ifs), 3)
|
||||||
|
|
||||||
def test_get_bvis(self):
|
|
||||||
ifs = bridgedomain.get_bvis(self.cfg)
|
|
||||||
self.assertEqual(len(ifs), 2)
|
|
||||||
self.assertNotIn("bvi10", ifs)
|
|
||||||
self.assertIn("bvi11", ifs)
|
|
||||||
self.assertIn("bvi12", ifs)
|
|
||||||
|
12
example.yaml
12
example.yaml
@ -28,6 +28,9 @@ interfaces:
|
|||||||
GigabitEthernet3/0/0:
|
GigabitEthernet3/0/0:
|
||||||
description: "Infra: Bridge Domain 10"
|
description: "Infra: Bridge Domain 10"
|
||||||
|
|
||||||
|
GigabitEthernet3/0/1:
|
||||||
|
l2xc: BondEthernet0.204
|
||||||
|
|
||||||
BondEthernet0:
|
BondEthernet0:
|
||||||
description: "Bond, James Bond!"
|
description: "Bond, James Bond!"
|
||||||
mac: 00:01:02:03:04:05
|
mac: 00:01:02:03:04:05
|
||||||
@ -52,6 +55,11 @@ interfaces:
|
|||||||
encapsulation:
|
encapsulation:
|
||||||
dot1ad: 1000
|
dot1ad: 1000
|
||||||
inner-dot1q: 1000
|
inner-dot1q: 1000
|
||||||
|
204:
|
||||||
|
encapsulation:
|
||||||
|
dot1ad: 1001
|
||||||
|
inner-dot1q: 1001
|
||||||
|
l2xc: GigabitEthernet3/0/1
|
||||||
|
|
||||||
loopbacks:
|
loopbacks:
|
||||||
loop0:
|
loop0:
|
||||||
@ -59,11 +67,11 @@ loopbacks:
|
|||||||
mtu: 9216
|
mtu: 9216
|
||||||
lcp: "loop0"
|
lcp: "loop0"
|
||||||
addresses: [ 192.0.2.1/32, 2001:db8:1::1/128 ]
|
addresses: [ 192.0.2.1/32, 2001:db8:1::1/128 ]
|
||||||
|
loop1:
|
||||||
|
description: "Another loop"
|
||||||
|
|
||||||
bridgedomains:
|
bridgedomains:
|
||||||
bd10:
|
bd10:
|
||||||
description: "Bridge Domain 10"
|
description: "Bridge Domain 10"
|
||||||
mtu: 1500
|
mtu: 1500
|
||||||
lcp: "bvi10"
|
|
||||||
addresses: [ 192.0.2.9/29, 2001:db8:2::1/64 ]
|
|
||||||
interfaces: [ BondEthernet0.203, GigabitEthernet3/0/0 ]
|
interfaces: [ BondEthernet0.203, GigabitEthernet3/0/0 ]
|
||||||
|
@ -13,8 +13,6 @@ vxlan:
|
|||||||
bridgedomain:
|
bridgedomain:
|
||||||
description: str(exclude='\'"',len=64,required=False)
|
description: str(exclude='\'"',len=64,required=False)
|
||||||
mtu: int(min=128,max=9216,required=False)
|
mtu: int(min=128,max=9216,required=False)
|
||||||
lcp: str(max=15,matches='[a-z]+[a-z0-9-]*',required=False)
|
|
||||||
addresses: list(ip_interface(),min=1,max=6,required=False)
|
|
||||||
interfaces: list(str(),required=False)
|
interfaces: list(str(),required=False)
|
||||||
---
|
---
|
||||||
loopback:
|
loopback:
|
||||||
|
@ -44,13 +44,10 @@ bridgedomains:
|
|||||||
mtu: 3000
|
mtu: 3000
|
||||||
interfaces: [ GigabitEthernet1/0/0, GigabitEthernet1/0/1, BondEthernet0 ]
|
interfaces: [ GigabitEthernet1/0/0, GigabitEthernet1/0/1, BondEthernet0 ]
|
||||||
bd11:
|
bd11:
|
||||||
description: "Bridge Domain 11, both LCP and address"
|
description: "Bridge Domain 11, with sub-interfaces"
|
||||||
mtu: 2000
|
mtu: 2000
|
||||||
interfaces: [ GigabitEthernet2/0/0.100, GigabitEthernet2/0/1.100, BondEthernet0.100 ]
|
interfaces: [ GigabitEthernet2/0/0.100, GigabitEthernet2/0/1.100, BondEthernet0.100 ]
|
||||||
lcp: "bvi123456789012"
|
|
||||||
addresses: [ 192.0.2.1/29, 2001:db8::1/64 ]
|
|
||||||
bd12:
|
bd12:
|
||||||
description: "Bridge Domain 12, invalid because it has Gi1/0/0 as well"
|
description: "Bridge Domain 12, invalid because it has Gi1/0/0 as well"
|
||||||
mtu: 9000
|
mtu: 9000
|
||||||
interfaces: [ GigabitEthernet4/0/0, GigabitEthernet1/0/0 ]
|
interfaces: [ GigabitEthernet4/0/0, GigabitEthernet1/0/0 ]
|
||||||
lcp: "bvi12"
|
|
||||||
|
@ -29,6 +29,4 @@ bridgedomains:
|
|||||||
bd10:
|
bd10:
|
||||||
description: "Bridge Domain 10"
|
description: "Bridge Domain 10"
|
||||||
mtu: 2000
|
mtu: 2000
|
||||||
lcp: "bvi10"
|
|
||||||
addresses: [ 10.0.0.2/32, 2001:db8::2/128 ]
|
|
||||||
interfaces: [ GigabitEthernet3/0/0 ]
|
interfaces: [ GigabitEthernet3/0/0 ]
|
||||||
|
@ -46,15 +46,8 @@ bridgedomains:
|
|||||||
mtu: 3000
|
mtu: 3000
|
||||||
interfaces: [ GigabitEthernet1/0/0, GigabitEthernet1/0/1, BondEthernet0 ]
|
interfaces: [ GigabitEthernet1/0/0, GigabitEthernet1/0/1, BondEthernet0 ]
|
||||||
bd11:
|
bd11:
|
||||||
description: "Bridge Domain 11, both LCP and address"
|
description: "Bridge Domain 11"
|
||||||
mtu: 2000
|
mtu: 2000
|
||||||
interfaces: [ GigabitEthernet2/0/0.100, GigabitEthernet2/0/1.100, BondEthernet0.100 ]
|
interfaces: [ GigabitEthernet2/0/0.100, GigabitEthernet2/0/1.100 ]
|
||||||
lcp: "bvi123456789012"
|
|
||||||
addresses: [ 192.0.2.1/29, 2001:db8::1/64 ]
|
|
||||||
bd12:
|
bd12:
|
||||||
description: "Bridge Domain 12, LCP but no address"
|
description: "Bridge Domain 12, it is OK to have no member interfaces"
|
||||||
mtu: 9000
|
|
||||||
interfaces: [ GigabitEthernet4/0/0, GigabitEthernet4/0/1 ]
|
|
||||||
lcp: "bvi12"
|
|
||||||
bd13:
|
|
||||||
description: "Bridge Domain 13, it is OK to have no member interfaces"
|
|
||||||
|
@ -71,6 +71,4 @@ bridgedomains:
|
|||||||
bd10:
|
bd10:
|
||||||
description: "Bridge Domain 10"
|
description: "Bridge Domain 10"
|
||||||
mtu: 1500
|
mtu: 1500
|
||||||
lcp: "bvi10"
|
|
||||||
addresses: [ 192.0.2.9/29, 2001:db8:2::1/64 ]
|
|
||||||
interfaces: [ BondEthernet0.203, GigabitEthernet3/0/0 ]
|
interfaces: [ BondEthernet0.203, GigabitEthernet3/0/0 ]
|
||||||
|
@ -5,8 +5,7 @@ test:
|
|||||||
- "interface .* IP address .* conflicts with another"
|
- "interface .* IP address .* conflicts with another"
|
||||||
- "sub-interface .* IP address .* conflicts with another"
|
- "sub-interface .* IP address .* conflicts with another"
|
||||||
- "loopback .* IP address .* conflicts with another"
|
- "loopback .* IP address .* conflicts with another"
|
||||||
- "bridgedomain .* IP address .* conflicts with another"
|
count: 14
|
||||||
count: 18
|
|
||||||
---
|
---
|
||||||
interfaces:
|
interfaces:
|
||||||
GigabitEthernet1/0/0:
|
GigabitEthernet1/0/0:
|
||||||
@ -27,8 +26,6 @@ interfaces:
|
|||||||
|
|
||||||
GigabitEthernet1/0/2:
|
GigabitEthernet1/0/2:
|
||||||
lcp: e0-2
|
lcp: e0-2
|
||||||
description: "These addresses overlap with bd1"
|
|
||||||
addresses: [ 192.0.2.18/29, 2001:db8:3::2/64 ]
|
|
||||||
|
|
||||||
GigabitEthernet1/0/3:
|
GigabitEthernet1/0/3:
|
||||||
lcp: e0-3
|
lcp: e0-3
|
||||||
@ -54,11 +51,3 @@ loopbacks:
|
|||||||
description: "These addresses overlap with Gi1/0/1.101"
|
description: "These addresses overlap with Gi1/0/1.101"
|
||||||
lcp: "loop0"
|
lcp: "loop0"
|
||||||
addresses: [ 192.0.2.9/29, 2001:db8:2::1/64 ]
|
addresses: [ 192.0.2.9/29, 2001:db8:2::1/64 ]
|
||||||
|
|
||||||
bridgedomains:
|
|
||||||
bd1:
|
|
||||||
description: "These addresses overlap with Gi1/0/2"
|
|
||||||
mtu: 2000
|
|
||||||
lcp: "bvi1"
|
|
||||||
addresses: [ 192.0.2.17/29, 2001:db8:3::1/64 ]
|
|
||||||
interfaces: [ GigabitEthernet3/0/0 ]
|
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
test:
|
|
||||||
description: "A bridgedomain with a BVI address must have an LCP"
|
|
||||||
errors:
|
|
||||||
expected:
|
|
||||||
- "bridgedomain .* has an address but no LCP"
|
|
||||||
count: 1
|
|
||||||
---
|
|
||||||
interfaces:
|
|
||||||
GigabitEthernet1/0/0:
|
|
||||||
mtu: 3000
|
|
||||||
GigabitEthernet1/0/1:
|
|
||||||
mtu: 3000
|
|
||||||
|
|
||||||
bridgedomains:
|
|
||||||
bd13:
|
|
||||||
description: "Bridge Domain 13, address but no LCP"
|
|
||||||
mtu: 3000
|
|
||||||
interfaces: [ GigabitEthernet1/0/0, GigabitEthernet1/0/1 ]
|
|
||||||
addresses: [ 192.0.2.9/29, 2001:db8:1::1/64 ]
|
|
@ -4,9 +4,8 @@ test:
|
|||||||
expected:
|
expected:
|
||||||
- "interface .* does not have a unique LCP name"
|
- "interface .* does not have a unique LCP name"
|
||||||
- "loopback .* does not have a unique LCP name"
|
- "loopback .* does not have a unique LCP name"
|
||||||
- "bridgedomain .* does not have a unique LCP name"
|
|
||||||
- "bridgedomain bd0 is reserved"
|
- "bridgedomain bd0 is reserved"
|
||||||
count: 4
|
count: 3
|
||||||
---
|
---
|
||||||
interfaces:
|
interfaces:
|
||||||
GigabitEthernet1/0/0:
|
GigabitEthernet1/0/0:
|
||||||
@ -24,5 +23,4 @@ loopbacks:
|
|||||||
bridgedomains:
|
bridgedomains:
|
||||||
bd0:
|
bd0:
|
||||||
description: "bd_id 0 in VPP is reserved"
|
description: "bd_id 0 in VPP is reserved"
|
||||||
lcp: "e1"
|
|
||||||
interfaces: [ GigabitEthernet2/0/0, GigabitEthernet2/0/1 ]
|
interfaces: [ GigabitEthernet2/0/0, GigabitEthernet2/0/1 ]
|
||||||
|
@ -75,9 +75,6 @@ class Reconciler():
|
|||||||
if not self.prune_bridgedomains():
|
if not self.prune_bridgedomains():
|
||||||
self.logger.warning("Could not prune BridgeDomains from VPP")
|
self.logger.warning("Could not prune BridgeDomains from VPP")
|
||||||
ret = False
|
ret = False
|
||||||
if not self.prune_bvis():
|
|
||||||
self.logger.warning("Could not prune BVIs from VPP")
|
|
||||||
ret = False
|
|
||||||
if not self.prune_l2xcs():
|
if not self.prune_l2xcs():
|
||||||
self.logger.warning("Could not prune L2 Cross Connects from VPP")
|
self.logger.warning("Could not prune L2 Cross Connects from VPP")
|
||||||
ret = False
|
ret = False
|
||||||
@ -158,36 +155,6 @@ class Reconciler():
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def prune_bvis(self):
|
|
||||||
""" Remove BVIs (bridge-domain virtual interfaces) from VPP, if they do not occur in the config. """
|
|
||||||
removed_interfaces=[]
|
|
||||||
for numtags in [ 2, 1, 0 ]:
|
|
||||||
for idx, vpp_iface in self.vpp.config['interfaces'].items():
|
|
||||||
if vpp_iface.interface_dev_type!='BVI':
|
|
||||||
continue
|
|
||||||
if vpp_iface.sub_number_of_tags != numtags:
|
|
||||||
continue
|
|
||||||
config_ifname, config_iface = bridgedomain.get_by_bvi_name(self.cfg, vpp_iface.interface_name)
|
|
||||||
if not config_iface:
|
|
||||||
self.prune_addresses(vpp_iface.interface_name, [])
|
|
||||||
if numtags == 0:
|
|
||||||
self.logger.info("1> bvi delete %s" % vpp_iface.interface_name)
|
|
||||||
removed_interfaces.append(vpp_iface.interface_name)
|
|
||||||
else:
|
|
||||||
self.logger.info("1> delete sub %s" % vpp_iface.interface_name)
|
|
||||||
removed_interfaces.append(vpp_iface.interface_name)
|
|
||||||
continue
|
|
||||||
self.logger.debug("BVI OK: %s" % (vpp_iface.interface_name))
|
|
||||||
addresses = []
|
|
||||||
if 'addresses' in config_iface:
|
|
||||||
addresses = config_iface['addresses']
|
|
||||||
self.prune_addresses(vpp_iface.interface_name, addresses)
|
|
||||||
|
|
||||||
for ifname in removed_interfaces:
|
|
||||||
self.vpp.remove_interface(ifname)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def prune_bridgedomains(self):
|
def prune_bridgedomains(self):
|
||||||
""" Remove bridge-domains from VPP, if they do not occur in the config. If any interfaces are
|
""" Remove bridge-domains from VPP, if they do not occur in the config. If any interfaces are
|
||||||
@ -418,7 +385,7 @@ class Reconciler():
|
|||||||
|
|
||||||
def prune_lcps(self):
|
def prune_lcps(self):
|
||||||
""" Remove LCPs which are not in the configuration, starting with QinQ/QinAD interfaces, then Dot1Q/Dot1AD,
|
""" Remove LCPs which are not in the configuration, starting with QinQ/QinAD interfaces, then Dot1Q/Dot1AD,
|
||||||
and finally PHYs/BondEthernets/Tunnels/BVIs/Loopbacks. For QinX, special care is taken to ensure that
|
and finally PHYs/BondEthernets/Tunnels/Loopbacks. For QinX, special care is taken to ensure that
|
||||||
their intermediary interface exists, and has the correct encalsulation. If the intermediary interface
|
their intermediary interface exists, and has the correct encalsulation. If the intermediary interface
|
||||||
changed, the QinX LCP is removed. The same is true for Dot1Q/Dot1AD interfaces: if their encapsulation
|
changed, the QinX LCP is removed. The same is true for Dot1Q/Dot1AD interfaces: if their encapsulation
|
||||||
has changed, we will have to re-create the underlying sub-interface, so the LCP has to be removed.
|
has changed, we will have to re-create the underlying sub-interface, so the LCP has to be removed.
|
||||||
@ -544,7 +511,7 @@ class Reconciler():
|
|||||||
|
|
||||||
self.logger.debug("Dot1Q/Dot1AD LCP OK: %s -> (vpp=%s, config=%s)" % (lcp.host_if_name, vpp_iface.interface_name, config_ifname))
|
self.logger.debug("Dot1Q/Dot1AD LCP OK: %s -> (vpp=%s, config=%s)" % (lcp.host_if_name, vpp_iface.interface_name, config_ifname))
|
||||||
|
|
||||||
## Remove LCPs for interfaces, bonds, tunnels, loops, bvis
|
## Remove LCPs for interfaces, bonds, tunnels, loops
|
||||||
for idx, lcp in lcps.items():
|
for idx, lcp in lcps.items():
|
||||||
vpp_iface = self.vpp.config['interfaces'][lcp.phy_sw_if_index]
|
vpp_iface = self.vpp.config['interfaces'][lcp.phy_sw_if_index]
|
||||||
if vpp_iface.sub_inner_vlan_id > 0 or vpp_iface.sub_outer_vlan_id > 0:
|
if vpp_iface.sub_inner_vlan_id > 0 or vpp_iface.sub_outer_vlan_id > 0:
|
||||||
@ -552,8 +519,6 @@ class Reconciler():
|
|||||||
|
|
||||||
if vpp_iface.interface_dev_type=='Loopback':
|
if vpp_iface.interface_dev_type=='Loopback':
|
||||||
config_ifname, config_iface = loopback.get_by_lcp_name(self.cfg, lcp.host_if_name)
|
config_ifname, config_iface = loopback.get_by_lcp_name(self.cfg, lcp.host_if_name)
|
||||||
elif vpp_iface.interface_dev_type=='BVI':
|
|
||||||
config_ifname, config_iface = bridgedomain.get_by_lcp_name(self.cfg, lcp.host_if_name)
|
|
||||||
else:
|
else:
|
||||||
config_ifname, config_iface = interface.get_by_lcp_name(self.cfg, lcp.host_if_name)
|
config_ifname, config_iface = interface.get_by_lcp_name(self.cfg, lcp.host_if_name)
|
||||||
|
|
||||||
@ -575,8 +540,8 @@ class Reconciler():
|
|||||||
|
|
||||||
def prune_admin_state(self):
|
def prune_admin_state(self):
|
||||||
""" Set admin-state down for all interfaces that are not in the config. """
|
""" Set admin-state down for all interfaces that are not in the config. """
|
||||||
for ifname in self.vpp.get_qinx_interfaces() + self.vpp.get_dot1x_interfaces() + self.vpp.get_bondethernets() + self.vpp.get_phys() + self.vpp.get_vxlan_tunnels() + self.vpp.get_bvis() + self.vpp.get_loopbacks():
|
for ifname in self.vpp.get_qinx_interfaces() + self.vpp.get_dot1x_interfaces() + self.vpp.get_bondethernets() + self.vpp.get_phys() + self.vpp.get_vxlan_tunnels() + self.vpp.get_loopbacks():
|
||||||
if not ifname in interface.get_interfaces(self.cfg) + loopback.get_loopbacks(self.cfg) + bridgedomain.get_bvis(self.cfg):
|
if not ifname in interface.get_interfaces(self.cfg) + loopback.get_loopbacks(self.cfg):
|
||||||
vpp_iface = self.vpp.config['interface_names'][ifname]
|
vpp_iface = self.vpp.config['interface_names'][ifname]
|
||||||
|
|
||||||
if self.__tap_is_lcp(vpp_iface.sw_if_index):
|
if self.__tap_is_lcp(vpp_iface.sw_if_index):
|
||||||
@ -595,9 +560,6 @@ class Reconciler():
|
|||||||
if not self.create_loopbacks():
|
if not self.create_loopbacks():
|
||||||
self.logger.warning("Could not create Loopbacks in VPP")
|
self.logger.warning("Could not create Loopbacks in VPP")
|
||||||
ret = False
|
ret = False
|
||||||
if not self.create_bvis():
|
|
||||||
self.logger.warning("Could not create BVIs in VPP")
|
|
||||||
ret = False
|
|
||||||
if not self.create_bondethernets():
|
if not self.create_bondethernets():
|
||||||
self.logger.warning("Could not create BondEthernets in VPP")
|
self.logger.warning("Could not create BondEthernets in VPP")
|
||||||
ret = False
|
ret = False
|
||||||
@ -623,17 +585,6 @@ class Reconciler():
|
|||||||
self.logger.info("1> create loopback interface instance %d" % (instance))
|
self.logger.info("1> create loopback interface instance %d" % (instance))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def create_bvis(self):
|
|
||||||
for ifname in bridgedomain.get_bridgedomains(self.cfg):
|
|
||||||
ifname, iface = bridgedomain.get_by_name(self.cfg, ifname)
|
|
||||||
instance = int(ifname[2:])
|
|
||||||
if not 'lcp' in iface:
|
|
||||||
continue
|
|
||||||
if 'bvi%d'%instance in self.vpp.config['interface_names']:
|
|
||||||
continue
|
|
||||||
self.logger.info("1> bvi create instance %d" % (instance))
|
|
||||||
return True
|
|
||||||
|
|
||||||
def create_bondethernets(self):
|
def create_bondethernets(self):
|
||||||
for ifname in bondethernet.get_bondethernets(self.cfg):
|
for ifname in bondethernet.get_bondethernets(self.cfg):
|
||||||
if ifname in self.vpp.config['interface_names']:
|
if ifname in self.vpp.config['interface_names']:
|
||||||
@ -783,10 +734,6 @@ class Reconciler():
|
|||||||
bridge_members = []
|
bridge_members = []
|
||||||
|
|
||||||
config_bridge_ifname, config_bridge_iface = bridgedomain.get_by_name(self.cfg, "bd%d"%instance)
|
config_bridge_ifname, config_bridge_iface = bridgedomain.get_by_name(self.cfg, "bd%d"%instance)
|
||||||
if 'lcp' in config_bridge_iface:
|
|
||||||
bviname = "bvi%d" % instance
|
|
||||||
if not bviname in bridge_members:
|
|
||||||
self.logger.info("1> set interface l2 bridge bvi%d %d bvi" % (instance, instance))
|
|
||||||
if not 'interfaces' in config_bridge_iface:
|
if not 'interfaces' in config_bridge_iface:
|
||||||
continue
|
continue
|
||||||
for member_ifname in config_bridge_iface['interfaces']:
|
for member_ifname in config_bridge_iface['interfaces']:
|
||||||
@ -836,7 +783,7 @@ class Reconciler():
|
|||||||
tag_list = [ 0, 1, 2 ]
|
tag_list = [ 0, 1, 2 ]
|
||||||
|
|
||||||
for numtags in tag_list:
|
for numtags in tag_list:
|
||||||
for ifname in interface.get_interfaces(self.cfg) + loopback.get_loopbacks(self.cfg) + bridgedomain.get_bridgedomains(self.cfg):
|
for ifname in interface.get_interfaces(self.cfg) + loopback.get_loopbacks(self.cfg):
|
||||||
if numtags == 0 and interface.is_sub(self.cfg, ifname):
|
if numtags == 0 and interface.is_sub(self.cfg, ifname):
|
||||||
continue
|
continue
|
||||||
if numtags == 1 and not interface.is_sub(self.cfg, ifname):
|
if numtags == 1 and not interface.is_sub(self.cfg, ifname):
|
||||||
@ -853,15 +800,6 @@ class Reconciler():
|
|||||||
vpp_ifname, config_iface = loopback.get_by_name(self.cfg, ifname)
|
vpp_ifname, config_iface = loopback.get_by_name(self.cfg, ifname)
|
||||||
if 'mtu' in config_iface:
|
if 'mtu' in config_iface:
|
||||||
config_mtu = config_iface['mtu']
|
config_mtu = config_iface['mtu']
|
||||||
elif ifname.startswith("bd"):
|
|
||||||
instance=int(ifname[2:])
|
|
||||||
bviname="bvi%d" % instance
|
|
||||||
if bviname in self.vpp.config['interface_names']:
|
|
||||||
vpp_mtu = self.vpp.config['interface_names'][bviname].mtu[0]
|
|
||||||
config_ifname, config_iface = bridgedomain.get_by_name(self.cfg, ifname)
|
|
||||||
if 'mtu' in config_iface:
|
|
||||||
config_mtu = config_iface['mtu']
|
|
||||||
vpp_ifname = bviname
|
|
||||||
else:
|
else:
|
||||||
if numtags > 0:
|
if numtags > 0:
|
||||||
vpp_mtu = 0
|
vpp_mtu = 0
|
||||||
@ -880,7 +818,7 @@ class Reconciler():
|
|||||||
for idx, vpp_iface in self.vpp.config['interfaces'].items():
|
for idx, vpp_iface in self.vpp.config['interfaces'].items():
|
||||||
if vpp_iface.sub_number_of_tags != 0:
|
if vpp_iface.sub_number_of_tags != 0:
|
||||||
continue
|
continue
|
||||||
if vpp_iface.interface_dev_type in ['local', 'Loopback', 'BVI', 'VXLAN', 'virtio']:
|
if vpp_iface.interface_dev_type in ['local', 'Loopback', 'VXLAN', 'virtio']:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
config_ifname, config_iface = interface.get_by_name(self.cfg, vpp_iface.interface_name)
|
config_ifname, config_iface = interface.get_by_name(self.cfg, vpp_iface.interface_name)
|
||||||
@ -933,19 +871,13 @@ class Reconciler():
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
def sync_addresses(self):
|
def sync_addresses(self):
|
||||||
for ifname in interface.get_interfaces(self.cfg) + loopback.get_loopbacks(self.cfg) + bridgedomain.get_bridgedomains(self.cfg):
|
for ifname in interface.get_interfaces(self.cfg) + loopback.get_loopbacks(self.cfg):
|
||||||
config_addresses=[]
|
config_addresses=[]
|
||||||
vpp_addresses=[]
|
vpp_addresses=[]
|
||||||
if ifname.startswith("loop"):
|
if ifname.startswith("loop"):
|
||||||
vpp_ifname, config_iface = loopback.get_by_name(self.cfg, ifname)
|
vpp_ifname, config_iface = loopback.get_by_name(self.cfg, ifname)
|
||||||
if 'addresses' in config_iface:
|
if 'addresses' in config_iface:
|
||||||
config_addresses = config_iface['addresses']
|
config_addresses = config_iface['addresses']
|
||||||
elif ifname.startswith("bd"):
|
|
||||||
config_ifname, config_iface = bridgedomain.get_by_name(self.cfg, ifname)
|
|
||||||
instance = int(ifname[2:])
|
|
||||||
vpp_ifname = "bvi%d" % instance
|
|
||||||
if 'addresses' in config_iface:
|
|
||||||
config_addresses = config_iface['addresses']
|
|
||||||
else:
|
else:
|
||||||
vpp_ifname, config_iface = interface.get_by_name(self.cfg, ifname)
|
vpp_ifname, config_iface = interface.get_by_name(self.cfg, ifname)
|
||||||
if 'addresses' in config_iface:
|
if 'addresses' in config_iface:
|
||||||
@ -961,15 +893,10 @@ class Reconciler():
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def sync_admin_state(self):
|
def sync_admin_state(self):
|
||||||
for ifname in interface.get_interfaces(self.cfg) + loopback.get_loopbacks(self.cfg) + bridgedomain.get_bridgedomains(self.cfg):
|
for ifname in interface.get_interfaces(self.cfg) + loopback.get_loopbacks(self.cfg):
|
||||||
if ifname.startswith("loop"):
|
if ifname.startswith("loop"):
|
||||||
vpp_ifname, config_iface = loopback.get_by_name(self.cfg, ifname)
|
vpp_ifname, config_iface = loopback.get_by_name(self.cfg, ifname)
|
||||||
config_admin_state = 1
|
config_admin_state = 1
|
||||||
elif ifname.startswith("bd"):
|
|
||||||
config_ifname, config_iface = bridgedomain.get_by_name(self.cfg, ifname)
|
|
||||||
instance = int(ifname[2:])
|
|
||||||
vpp_ifname = "bvi%d" % instance
|
|
||||||
config_admin_state = 1
|
|
||||||
else:
|
else:
|
||||||
vpp_ifname, config_iface = interface.get_by_name(self.cfg, ifname)
|
vpp_ifname, config_iface = interface.get_by_name(self.cfg, ifname)
|
||||||
config_admin_state = 1
|
config_admin_state = 1
|
||||||
|
@ -231,10 +231,6 @@ class VPPApi():
|
|||||||
loopbacks = [self.config['interfaces'][x].interface_name for x in self.config['interfaces'] if self.config['interfaces'][x].interface_dev_type=='Loopback']
|
loopbacks = [self.config['interfaces'][x].interface_name for x in self.config['interfaces'] if self.config['interfaces'][x].interface_dev_type=='Loopback']
|
||||||
return loopbacks
|
return loopbacks
|
||||||
|
|
||||||
def get_bvis(self):
|
|
||||||
bvis = [self.config['interfaces'][x].interface_name for x in self.config['interfaces'] if self.config['interfaces'][x].interface_dev_type=='BVI']
|
|
||||||
return bvis
|
|
||||||
|
|
||||||
def get_phys(self):
|
def get_phys(self):
|
||||||
phys = [self.config['interfaces'][x].interface_name for x in self.config['interfaces'] if self.config['interfaces'][x].sw_if_index == self.config['interfaces'][x].sup_sw_if_index and self.config['interfaces'][x].interface_dev_type not in ['virtio', 'BVI', 'Loopback', 'VXLAN', 'local', 'bond']]
|
phys = [self.config['interfaces'][x].interface_name for x in self.config['interfaces'] if self.config['interfaces'][x].sw_if_index == self.config['interfaces'][x].sup_sw_if_index and self.config['interfaces'][x].interface_dev_type not in ['virtio', 'BVI', 'Loopback', 'VXLAN', 'local', 'bond']]
|
||||||
return phys
|
return phys
|
||||||
|
Reference in New Issue
Block a user