diff --git a/schema.yaml b/schema.yaml index 665c530..9c654bc 100644 --- a/schema.yaml +++ b/schema.yaml @@ -1,7 +1,14 @@ -interfaces: map(include('interface'),key=str(matches='.*GigabitEthernet[0-9]+/[0-9]+/[0-9]+|BondEthernet[0-9]+'),required=False) +interfaces: map(include('interface'),key=str(matches='.*GigabitEthernet[0-9]+/[0-9]+/[0-9]+|BondEthernet[0-9]+|vxlan_tunnel[0-9]+'),required=False) bondethernets: map(include('bondethernet'),key=str(matches='BondEthernet[0-9]+'),required=False) loopbacks: map(include('loopback'),key=str(matches='loop[0-9]+'),required=False) bridgedomains: map(include('bridgedomain'),key=str(matches='bd[0-9]+'),required=False) +vxlan_tunnels: map(include('vxlan'),key=str(matches='vxlan_tunnel[0-9]+'),required=False) +--- +vxlan: + description: str(exclude='\'"',required=False) + local: ip() + remote: ip() + vni: int(min=1,max=16777215) --- bridgedomain: description: str(exclude='\'"',required=False) diff --git a/unittest/correct-vxlan.yaml b/unittest/correct-vxlan.yaml new file mode 100644 index 0000000..01ab612 --- /dev/null +++ b/unittest/correct-vxlan.yaml @@ -0,0 +1,42 @@ +test: + description: "A few correct examples of well formed VXLANs" + errors: + count: 0 +--- +vxlan_tunnels: + vxlan_tunnel0: + local: 192.0.2.1 + remote: 192.0.2.2 + vni: 100 + + vxlan_tunnel1: + local: 2001:db8::1 + remote: 2001:db8::2 + vni: 101 + + vxlan_tunnel2: + local: 2001:db8::1 + remote: 2001:db8::2 + vni: 102 + +interfaces: + GigabitEthernet1/0/0: + sub-interfaces: + 100: + l2xc: vxlan_tunnel1 + + vxlan_tunnel0: + mtu: 9216 + description: "Bridgedomain member" + + vxlan_tunnel1: + l2xc: GigabitEthernet1/0/0.100 + + vxlan_tunnel2: + lcp: 'vxlan2' + addresses: [ 10.0.0.1/24, 2001:db8:1::1/64 ] + +bridgedomains: + bd10: + mtu: 9216 + interfaces: [ vxlan_tunnel0 ] diff --git a/validator/__init__.py b/validator/__init__.py index c554b6f..83edc39 100644 --- a/validator/__init__.py +++ b/validator/__init__.py @@ -29,6 +29,7 @@ from validator.loopback import validate_loopbacks from validator.bondethernet import validate_bondethernets from validator.interface import validate_interfaces from validator.bridgedomain import validate_bridgedomains +from validator.vxlan_tunnel import validate_vxlan_tunnels from yamale.validators import DefaultValidators, Validator import ipaddress @@ -121,6 +122,12 @@ class Validator(object): if not rv: ret_rv = False + rv, msgs = validate_vxlan_tunnels(yaml) + if msgs: + ret_msgs.extend(msgs) + if not rv: + ret_rv = False + if ret_rv: self.logger.debug("Semantics correctly validated") return ret_rv, ret_msgs diff --git a/validator/vxlan_tunnel.py b/validator/vxlan_tunnel.py new file mode 100644 index 0000000..dae55f5 --- /dev/null +++ b/validator/vxlan_tunnel.py @@ -0,0 +1,57 @@ +# +# 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. +# +import logging +import validator.interface as interface + +class NullHandler(logging.Handler): + def emit(self, record): + pass + + +def get_by_name(yaml, ifname): + """ Return the VXLAN by name, if it exists. Return None otherwise. """ + try: + if ifname in yaml['vxlan_tunnels']: + return yaml['vxlan_tunnels'][ifname] + except: + pass + return None + +def vni_unique(yaml, vni): + """ Return True if the VNI is unique amongst all VXLANs """ + if not 'vxlan_tunnels' in yaml: + return True + + ncount = 0 + for ifname, iface in yaml['vxlan_tunnels'].items(): + if iface['vni'] == vni: + ncount = ncount + 1 + + if ncount > 1: + return False + return True + + +def validate_vxlan_tunnels(yaml): + result = True + msgs = [] + logger = logging.getLogger('vppcfg.validator') + logger.addHandler(NullHandler()) + + if not 'vxlan_tunnels' in yaml: + return result, msgs + + for ifname, iface in yaml['vxlan_tunnels'].items(): + logger.debug("vxlan_tunnel %s: %s" % (ifname, iface)) + return result, msgs