Fixed python load paths so that vppcfg will work installed as python library and standalone from the source directory, fixing load pathes for resources such as yaml files along the way. Added a make target for pylint called 'make check-style', fixed a number of minor pylint issues along the way. Signed-off-by: Ray Kinsella <mdr@ashroe.eu>
188 lines
6.6 KiB
Python
188 lines
6.6 KiB
Python
#
|
|
# Copyright (c) 2022 Pim van Pelt
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at:
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
#
|
|
""" A vppcfg configuration module that handles bridgedomains """
|
|
import logging
|
|
from . import interface
|
|
from . import loopback
|
|
|
|
|
|
def get_bridgedomains(yaml):
|
|
"""Return a list of all bridgedomains."""
|
|
ret = []
|
|
if not "bridgedomains" in yaml:
|
|
return ret
|
|
for ifname, _iface in yaml["bridgedomains"].items():
|
|
ret.append(ifname)
|
|
return ret
|
|
|
|
|
|
def get_by_name(yaml, ifname):
|
|
"""Return the BridgeDomain by name (bd*), if it exists. Return None,None otherwise."""
|
|
try:
|
|
if ifname in yaml["bridgedomains"]:
|
|
return ifname, yaml["bridgedomains"][ifname]
|
|
except KeyError:
|
|
pass
|
|
return None, None
|
|
|
|
|
|
def is_bridgedomain(yaml, ifname):
|
|
"""Returns True if the name (bd*) is an existing bridgedomain."""
|
|
ifname, iface = get_by_name(yaml, ifname)
|
|
return iface is not None
|
|
|
|
|
|
def get_bridge_interfaces(yaml):
|
|
"""Returns a list of all interfaces that are bridgedomain members"""
|
|
|
|
ret = []
|
|
if not "bridgedomains" in yaml:
|
|
return ret
|
|
|
|
for _ifname, iface in yaml["bridgedomains"].items():
|
|
if "interfaces" in iface:
|
|
ret.extend(iface["interfaces"])
|
|
|
|
return ret
|
|
|
|
|
|
def is_bridge_interface_unique(yaml, ifname):
|
|
"""Returns True if this interface is referenced in bridgedomains zero or one times"""
|
|
|
|
ifs = get_bridge_interfaces(yaml)
|
|
return ifs.count(ifname) < 2
|
|
|
|
|
|
def is_bridge_interface(yaml, ifname):
|
|
"""Returns True if this interface is a member of a BridgeDomain"""
|
|
|
|
return ifname in get_bridge_interfaces(yaml)
|
|
|
|
|
|
def bvi_unique(yaml, bviname):
|
|
"""Returns True if the BVI identified by bviname is unique among all BridgeDomains."""
|
|
if not "bridgedomains" in yaml:
|
|
return True
|
|
ncount = 0
|
|
for _ifname, iface in yaml["bridgedomains"].items():
|
|
if "bvi" in iface and iface["bvi"] == bviname:
|
|
ncount += 1
|
|
return ncount < 2
|
|
|
|
|
|
def get_settings(yaml, ifname):
|
|
"""Return a dictionary of 'settings' including their VPP defaults, for the
|
|
bridgedomain identified by 'ifname' (bd10)"""
|
|
ifname, iface = get_by_name(yaml, ifname)
|
|
if not iface:
|
|
return None
|
|
|
|
settings = {
|
|
"learn": True,
|
|
"unicast-flood": True,
|
|
"unknown-unicast-flood": True,
|
|
"unicast-forward": True,
|
|
"arp-termination": False,
|
|
"arp-unicast-forward": False,
|
|
"mac-age-minutes": 0, ## 0 means disabled
|
|
}
|
|
if "settings" in iface:
|
|
if "learn" in iface["settings"]:
|
|
settings["learn"] = iface["settings"]["learn"]
|
|
if "unicast-flood" in iface["settings"]:
|
|
settings["unicast-flood"] = iface["settings"]["unicast-flood"]
|
|
if "unknown-unicast-flood" in iface["settings"]:
|
|
settings["unknown-unicast-flood"] = iface["settings"][
|
|
"unknown-unicast-flood"
|
|
]
|
|
if "unicast-forward" in iface["settings"]:
|
|
settings["unicast-forward"] = iface["settings"]["unicast-forward"]
|
|
if "arp-termination" in iface["settings"]:
|
|
settings["arp-termination"] = iface["settings"]["arp-termination"]
|
|
if "arp-unicast-forward" in iface["settings"]:
|
|
settings["arp-unicast-forward"] = iface["settings"]["arp-unicast-forward"]
|
|
if "mac-age-minutes" in iface["settings"]:
|
|
settings["mac-age-minutes"] = int(iface["settings"]["mac-age-minutes"])
|
|
return settings
|
|
|
|
|
|
def validate_bridgedomains(yaml):
|
|
"""Validate the semantics of all YAML 'bridgedomains' entries"""
|
|
result = True
|
|
msgs = []
|
|
logger = logging.getLogger("vppcfg.config")
|
|
logger.addHandler(logging.NullHandler())
|
|
|
|
if not "bridgedomains" in yaml:
|
|
return result, msgs
|
|
|
|
for ifname, iface in yaml["bridgedomains"].items():
|
|
logger.debug(f"bridgedomain {iface}")
|
|
bd_mtu = 1500
|
|
if "mtu" in iface:
|
|
bd_mtu = iface["mtu"]
|
|
instance = int(ifname[2:])
|
|
if instance == 0:
|
|
msgs.append(f"bridgedomain {ifname} is reserved")
|
|
result = False
|
|
elif instance > 16777215:
|
|
msgs.append(
|
|
f"bridgedomain {ifname} has instance {int(instance)} which is too large"
|
|
)
|
|
result = False
|
|
|
|
if "bvi" in iface:
|
|
bvi_ifname, bvi_iface = loopback.get_by_name(yaml, iface["bvi"])
|
|
if not bvi_unique(yaml, bvi_ifname):
|
|
msgs.append(f"bridgedomain {ifname} BVI {bvi_ifname} is not unique")
|
|
result = False
|
|
if not bvi_iface:
|
|
msgs.append(f"bridgedomain {ifname} BVI {bvi_ifname} does not exist")
|
|
result = False
|
|
continue
|
|
|
|
bvi_mtu = 1500
|
|
if "mtu" in bvi_iface:
|
|
bvi_mtu = bvi_iface["mtu"]
|
|
if bvi_mtu != bd_mtu:
|
|
msgs.append(
|
|
f"bridgedomain {ifname} BVI {bvi_ifname} has MTU {int(bvi_mtu)}, while bridge has {int(bd_mtu)}"
|
|
)
|
|
result = False
|
|
|
|
if "interfaces" in iface:
|
|
for member in iface["interfaces"]:
|
|
if (None, None) == interface.get_by_name(yaml, member):
|
|
msgs.append(f"bridgedomain {ifname} member {member} does not exist")
|
|
result = False
|
|
continue
|
|
|
|
if not is_bridge_interface_unique(yaml, member):
|
|
msgs.append(f"bridgedomain {ifname} member {member} is not unique")
|
|
result = False
|
|
if interface.has_lcp(yaml, member):
|
|
msgs.append(f"bridgedomain {ifname} member {member} has an LCP")
|
|
result = False
|
|
if interface.has_address(yaml, member):
|
|
msgs.append(f"bridgedomain {ifname} member {member} has an address")
|
|
result = False
|
|
member_mtu = interface.get_mtu(yaml, member)
|
|
if member_mtu != bd_mtu:
|
|
msgs.append(
|
|
f"bridgedomain {ifname} member {member} has MTU {int(member_mtu)}, while bridge has {int(bd_mtu)}"
|
|
)
|
|
result = False
|
|
|
|
return result, msgs
|