Ensure that LCP names are globally unique (between interface/sub-interface/loopback/bridgedomain)

This commit is contained in:
Pim van Pelt
2022-03-13 19:53:13 +00:00
parent 0d8a28cadd
commit 763c1ca74c
7 changed files with 85 additions and 23 deletions

View File

@ -55,4 +55,4 @@ bridgedomains:
description: "Bridge Domain 12, LCP but no address" description: "Bridge Domain 12, LCP but no address"
mtu: 9000 mtu: 9000
interfaces: [ GigabitEthernet4/0/0, GigabitEthernet4/0/1 ] interfaces: [ GigabitEthernet4/0/0, GigabitEthernet4/0/1 ]
lcp: "bvi11" lcp: "bvi12"

View File

@ -0,0 +1,18 @@
test:
description: "Two interfaces or subinterfaces can't have the same LCP"
errors:
expected:
- "interface .* does not have a unique LCP name"
- "sub-interface .* does not have a unique LCP name"
count: 3
---
interfaces:
GigabitEthernet1/0/0:
lcp: "e1"
sub-interfaces:
100:
lcp: "e1"
GigabitEthernet1/0/1:
lcp: "e1"

View File

@ -0,0 +1,26 @@
test:
description: "Loopback and BridgeDomain and Interfaces can't have the same LCP"
errors:
expected:
- "interface .* does not have a unique LCP name"
- "loopback .* does not have a unique LCP name"
- "bridgedomain .* does not have a unique LCP name"
count: 3
---
interfaces:
GigabitEthernet1/0/0:
lcp: "e1"
GigabitEthernet2/0/0:
mtu: 1500
GigabitEthernet2/0/1:
mtu: 1500
loopbacks:
loop0:
lcp: "e1"
bridgedomains:
bd0:
lcp: "e1"
interfaces: [ GigabitEthernet2/0/0, GigabitEthernet2/0/1 ]

View File

@ -1,5 +1,6 @@
import logging import logging
import validator.interface as interface import validator.interface as interface
import validator.lcp as lcp
class NullHandler(logging.Handler): class NullHandler(logging.Handler):
def emit(self, record): def emit(self, record):
@ -35,6 +36,9 @@ def validate_bridgedomains(yaml):
if 'addresses' in iface and not 'lcp' in iface: if 'addresses' in iface and not 'lcp' in iface:
msgs.append("bridgedomain %s has an address but no LCP" % ifname) msgs.append("bridgedomain %s has an address but no LCP" % ifname)
result = False 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 'interfaces' in iface: if 'interfaces' in iface:
for member in iface['interfaces']: for member in iface['interfaces']:

View File

@ -1,5 +1,6 @@
import logging import logging
import validator.bondethernet as bondethernet import validator.bondethernet as bondethernet
import validator.lcp as lcp
class NullHandler(logging.Handler): class NullHandler(logging.Handler):
def emit(self, record): def emit(self, record):
@ -18,6 +19,7 @@ def get_parent_by_name(yaml, ifname):
pass pass
return None return None
def get_by_name(yaml, ifname): def get_by_name(yaml, ifname):
""" Returns the interface or sub-interface by a given name, or None if it does not exist """ """ Returns the interface or sub-interface by a given name, or None if it does not exist """
if '.' in ifname: if '.' in ifname:
@ -84,25 +86,6 @@ def is_bond_member(yaml, ifname):
return True return True
return False return False
def unique_lcp(yaml, ifname):
""" Returns true if this interface has a unique LCP name """
if not 'interfaces' in yaml:
return True
iface = get_by_name(yaml, ifname)
lcp = get_lcp(yaml, ifname)
if not lcp:
return True
ncount = 0
for sibling_ifname, sibling_iface in yaml['interfaces'].items():
sibling_lcp = get_lcp(yaml, sibling_ifname)
if sibling_lcp == lcp and sibling_ifname != ifname:
## print("%s overlaps with %s: %s" % (ifname, sibling_ifname, lcp))
ncount = ncount + 1
if ncount == 0:
return True
return False
def has_lcp(yaml, ifname): def has_lcp(yaml, ifname):
""" Returns True if this interface or sub-interface has an LCP""" """ Returns True if this interface or sub-interface has an LCP"""
if not 'interfaces' in yaml: if not 'interfaces' in yaml:
@ -282,9 +265,9 @@ def validate_interfaces(yaml):
if iface_address and not iface_lcp: if iface_address and not iface_lcp:
msgs.append("interface %s has an address but no LCP" % ifname) msgs.append("interface %s has an address but no LCP" % ifname)
result = False result = False
if iface_lcp and not unique_lcp(yaml, ifname): iface_lcp = get_lcp(yaml, ifname)
lcp = get_lcp(yaml, ifname) if iface_lcp and not lcp.is_unique(yaml, iface_lcp):
msgs.append("interface %s does not have a unique LCP name %s" % (ifname, lcp)) msgs.append("interface %s does not have a unique LCP name %s" % (ifname, iface_lcp))
result = False result = False
if has_sub(yaml, ifname): if has_sub(yaml, ifname):
@ -300,6 +283,9 @@ def validate_interfaces(yaml):
if sub_lcp and len(sub_lcp)>15: if sub_lcp and len(sub_lcp)>15:
msgs.append("sub-interface %s has LCP with too long name '%s'" % (sub_ifname, sub_lcp)) msgs.append("sub-interface %s has LCP with too long name '%s'" % (sub_ifname, sub_lcp))
result = False result = False
if iface_lcp and not lcp.is_unique(yaml, iface_lcp):
msgs.append("sub-interface %s does not have a unique LCP name %s" % (sub_ifname, sub_lcp))
result = False
sub_mtu = get_mtu(yaml, sub_ifname) sub_mtu = get_mtu(yaml, sub_ifname)
if sub_mtu > iface_mtu: if sub_mtu > iface_mtu:
msgs.append("sub-interface %s has MTU %d higher than parent MTU %d" % (sub_ifname, sub_iface['mtu'], iface_mtu)) msgs.append("sub-interface %s has MTU %d higher than parent MTU %d" % (sub_ifname, sub_iface['mtu'], iface_mtu))

24
validator/lcp.py Normal file
View File

@ -0,0 +1,24 @@
import logging
def is_unique(yaml, lcpname):
""" Returns True if there is at most one occurence of the LCP name in the entire config."""
ncount=0
if 'interfaces' in yaml:
for ifname, iface in yaml['interfaces'].items():
if 'lcp' in iface and iface['lcp'] == lcpname:
ncount = ncount + 1
if 'sub-interfaces' in iface:
for sub_ifname, sub_iface in iface['sub-interfaces'].items():
if 'lcp' in sub_iface and sub_iface['lcp'] == lcpname:
ncount = ncount + 1
if 'loopbacks' in yaml:
for ifname, iface in yaml['loopbacks'].items():
if 'lcp' in iface and iface['lcp'] == lcpname:
ncount = ncount + 1
if 'bridgedomains' in yaml:
for ifname, iface in yaml['bridgedomains'].items():
if 'lcp' in iface and iface['lcp'] == lcpname:
ncount = ncount + 1
if ncount > 1:
return False
return True

View File

@ -1,4 +1,5 @@
import logging import logging
import validator.lcp as lcp
class NullHandler(logging.Handler): class NullHandler(logging.Handler):
def emit(self, record): def emit(self, record):
@ -29,5 +30,8 @@ def validate_loopbacks(yaml):
if 'addresses' in iface and not 'lcp' in iface: if 'addresses' in iface and not 'lcp' in iface:
msgs.append("loopback %s has an address but no LCP" % ifname) msgs.append("loopback %s has an address but no LCP" % ifname)
result = False result = False
if 'lcp' in iface and not lcp.is_unique(yaml, iface['lcp']):
msgs.append("loopback %s does not have a unique LCP name %s" % (ifname, iface['lcp']))
result = False
return result, msgs return result, msgs