127 lines
3.8 KiB
Python
127 lines
3.8 KiB
Python
#
|
|
# Copyright (c) 2023 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 validates prefixlists """
|
|
import logging
|
|
import ipaddress
|
|
|
|
|
|
def get_prefixlists(yaml):
|
|
"""Return a list of all prefixlists."""
|
|
ret = []
|
|
if "prefixlists" in yaml:
|
|
for plname, _pl in yaml["prefixlists"].items():
|
|
ret.append(plname)
|
|
return ret
|
|
|
|
|
|
def get_by_name(yaml, plname):
|
|
"""Return the prefixlist by name, if it exists. Return None otherwise."""
|
|
try:
|
|
if plname in yaml["prefixlists"]:
|
|
return plname, yaml["prefixlists"][plname]
|
|
except KeyError:
|
|
pass
|
|
return None, None
|
|
|
|
|
|
def get_network_list(yaml, plname, want_ipv4=True, want_ipv6=True):
|
|
"""Returns a list of 0 or more ip_network elements, that represent the members
|
|
in a prefixlist of given name. Return the empty list if the prefixlist doesn't
|
|
exist. Optionally, want_ipv4 or want_ipv6 can be set to False to filter the list."""
|
|
ret = []
|
|
plname, plist = get_by_name(yaml, plname)
|
|
if not plist:
|
|
return ret
|
|
for member in plist["members"]:
|
|
ipn = ipaddress.ip_network(member, strict=False)
|
|
if ipn.version == 4 and want_ipv4:
|
|
ret.append(ipn)
|
|
if ipn.version == 6 and want_ipv6:
|
|
ret.append(ipn)
|
|
return ret
|
|
|
|
|
|
def count(yaml, plname):
|
|
"""Return the number of IPv4 and IPv6 entries in the prefixlist.
|
|
Returns 0, 0 if it doesn't exist"""
|
|
ipv4, ipv6 = 0, 0
|
|
|
|
plname, plist = get_by_name(yaml, plname)
|
|
if not plist:
|
|
return 0, 0
|
|
for member in plist["members"]:
|
|
ipn = ipaddress.ip_network(member, strict=False)
|
|
if ipn.version == 4:
|
|
ipv4 += 1
|
|
elif ipn.version == 6:
|
|
ipv6 += 1
|
|
|
|
return ipv4, ipv6
|
|
|
|
|
|
def count_ipv4(yaml, plname):
|
|
"""Return the number of IPv4 entries in the prefixlist."""
|
|
ipv4, _ = count(yaml, plname)
|
|
return ipv4
|
|
|
|
|
|
def count_ipv6(yaml, plname):
|
|
"""Return the number of IPv6 entries in the prefixlist."""
|
|
_, ipv6 = count(yaml, plname)
|
|
return ipv6
|
|
|
|
|
|
def has_ipv4(yaml, plname):
|
|
"""Return True if the prefixlist has at least one IPv4 entry."""
|
|
ipv4, _ = count(yaml, plname)
|
|
return ipv4 > 0
|
|
|
|
|
|
def has_ipv6(yaml, plname):
|
|
"""Return True if the prefixlist has at least one IPv6 entry."""
|
|
_, ipv6 = count(yaml, plname)
|
|
return ipv6 > 0
|
|
|
|
|
|
def is_empty(yaml, plname):
|
|
"""Return True if the prefixlist has no entries."""
|
|
ipv4, ipv6 = count(yaml, plname)
|
|
return ipv4 + ipv6 == 0
|
|
|
|
|
|
def validate_prefixlists(yaml):
|
|
"""Validate the semantics of all YAML 'prefixlists' entries"""
|
|
result = True
|
|
msgs = []
|
|
logger = logging.getLogger("vppcfg.config")
|
|
logger.addHandler(logging.NullHandler())
|
|
|
|
if not "prefixlists" in yaml:
|
|
return result, msgs
|
|
|
|
for plname, plist in yaml["prefixlists"].items():
|
|
logger.debug(f"prefixlist {plname}: {plist}")
|
|
if plname in ["any"]:
|
|
## Note: ACL 'source' and 'destination', when they are empty, will resolve
|
|
## to 'any', and can thus never refer to a prefixlist called 'any'.
|
|
msgs.append(f"prefixlist {plname} is a reserved name")
|
|
result = False
|
|
|
|
members = 0
|
|
for pl_member in plist["members"]:
|
|
members += 1
|
|
logger.debug(f"prefixlist {plname} member {members} is {pl_member}")
|
|
|
|
return result, msgs
|