Add ip_interface() validator
This commit is contained in:
45
README.md
45
README.md
@ -1,17 +1,38 @@
|
|||||||
## Design
|
## Design
|
||||||
|
|
||||||
### Validators
|
### YAML Configuration
|
||||||
|
|
||||||
Validators are functions which are passed the fully formed YAML configuration and are meant to
|
The main file that is handled by this program is the **Configuration File**.
|
||||||
check it for syntax and semantic validity. A validator has a unique name and takes the (yaml)
|
|
||||||
configuration as the only argument. Validators are expected to return a tuple of (bool,[string])
|
|
||||||
where the boolean signals success (False meaning the validator rejected the configuration file,
|
|
||||||
True meaning it is known to be correct), and a list of zero or more strings which contain messages
|
|
||||||
meant for human consumption. They can contain INFO, WARN or ERROR messages, and are meant to help
|
|
||||||
the caller understand why the validator rejected the configuration.
|
|
||||||
|
|
||||||
Validators can be disabled with the --skip-validator <name> [<name>] flag. It is not
|
## Validation
|
||||||
advised to skip validators. The purpose of the validators is to ensure the configuration is sane
|
|
||||||
and semantically correct.
|
|
||||||
|
|
||||||
Validators can be registered as follows:
|
There are three types of validation: _schema_ which ensures that the input YAML has the correct
|
||||||
|
fields of well known types, _semantic_ which ensures that the configuration doesn't violate
|
||||||
|
semantic constraints and _runtime_ which ensures that the configuration can be applied to the
|
||||||
|
VPP daemon.
|
||||||
|
|
||||||
|
### Schema Validators
|
||||||
|
|
||||||
|
First the configuration file is held against a structural validator, provided by [Yamale](https://github.com/23andMe/Yamale/).
|
||||||
|
Based on a validation schema in `schema.yaml`, the input file is checked for syntax correctness.
|
||||||
|
For example, a `dot1q` field must be an integer between 1 and 4095, wile an `lcp` string must
|
||||||
|
match a certain regular expression. After this first pass of syntax validation, I'm certain that
|
||||||
|
_if_ a field is set, it is of the right type (ie. string, int, enum).
|
||||||
|
|
||||||
|
### Semantic Validators
|
||||||
|
|
||||||
|
A set of semantic validators, each with a unique name, ensure that the _semantics_ of the YAML
|
||||||
|
are correct. For example, a physical interface cannot have an LCP, addresses or sub-interfaces,
|
||||||
|
if it is to be a member of a BondEthernet.
|
||||||
|
|
||||||
|
Validators are expected to return a tuple of (bool,[string]) where the boolean signals success
|
||||||
|
(False meaning the validator rejected the configuration file, True meaning it is known to be
|
||||||
|
correct), and a list of zero or more strings which contain messages meant for human consumption.
|
||||||
|
|
||||||
|
### Runtime Validators
|
||||||
|
|
||||||
|
After the configuration file is considered syntax and semanticly valid, there is one more set of
|
||||||
|
checks to perform -- runtime validators ensure that the configuration elements such as physical
|
||||||
|
network devices (ie. `HundredGigabitEthernet12/0/0` or plugin `lcpng` are present on the system.
|
||||||
|
It does this by connecting to VPP and querying the runtime state to ensure that what is modeled
|
||||||
|
in the configuration file is able to be committed.
|
||||||
|
@ -9,17 +9,13 @@ interface:
|
|||||||
description: str(exclude='\'"',required=False)
|
description: str(exclude='\'"',required=False)
|
||||||
lcp: str(max=8,matches='[a-z]+[a-z0-9-]{,7}',required=False)
|
lcp: str(max=8,matches='[a-z]+[a-z0-9-]{,7}',required=False)
|
||||||
mac: mac(required=False)
|
mac: mac(required=False)
|
||||||
addresses: list(include('v4'),include('v6'),min=1,max=6,required=False)
|
addresses: list(ip_interface(),min=1,max=6,required=False)
|
||||||
sub-interfaces: map(include('sub-interface'),key=int(min=1,max=4294967295),required=False)
|
sub-interfaces: map(include('sub-interface'),key=int(min=1,max=4294967295),required=False)
|
||||||
---
|
---
|
||||||
v4: str(matches='[0-9\.]+/[0-9]+')
|
|
||||||
---
|
|
||||||
v6: str(matches='[0-9a-f:]+/[0-9]+',ignore_case=True)
|
|
||||||
---
|
|
||||||
sub-interface:
|
sub-interface:
|
||||||
description: str(exclude='\'"',required=False)
|
description: str(exclude='\'"',required=False)
|
||||||
lcp: str(max=8,matches='[a-z]+[a-z0-9-]{,7}',required=False)
|
lcp: str(max=8,matches='[a-z]+[a-z0-9-]{,7}',required=False)
|
||||||
addresses: list(ip(),required=False)
|
addresses: list(ip_interface(),required=False)
|
||||||
encapsulation: include('encapsulation',required=False)
|
encapsulation: include('encapsulation',required=False)
|
||||||
---
|
---
|
||||||
encapsulation:
|
encapsulation:
|
||||||
|
@ -17,6 +17,33 @@ from validator.bondethernet import bondethernet
|
|||||||
from validator.interface import interface
|
from validator.interface import interface
|
||||||
from validator.bridgedomain import bridgedomain
|
from validator.bridgedomain import bridgedomain
|
||||||
|
|
||||||
|
from yamale.validators import DefaultValidators, Validator
|
||||||
|
import ipaddress
|
||||||
|
|
||||||
|
class IPInterfaceWithPrefixLength(Validator):
|
||||||
|
""" Custom IPAddress validator - takes IP/prefixlen as input:
|
||||||
|
192.0.2.1/29 or 2001:db8::1/64 are correct. The PrefixLength
|
||||||
|
is required, and must be a number (0-32 for IPv4 and 0-128 for
|
||||||
|
IPv6).
|
||||||
|
"""
|
||||||
|
tag = 'ip_interface'
|
||||||
|
|
||||||
|
def _is_valid(self, value):
|
||||||
|
try:
|
||||||
|
network = ipaddress.ip_interface(value)
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
if not '/' in value:
|
||||||
|
return False
|
||||||
|
e = value.split('/')
|
||||||
|
if not len(e) == 2:
|
||||||
|
return False
|
||||||
|
if not e[1].isnumeric():
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class NullHandler(logging.Handler):
|
class NullHandler(logging.Handler):
|
||||||
def emit(self, record):
|
def emit(self, record):
|
||||||
pass
|
pass
|
||||||
@ -35,7 +62,9 @@ class Validator(object):
|
|||||||
if self.args.schema:
|
if self.args.schema:
|
||||||
try:
|
try:
|
||||||
self.logger.info("Validating against schema %s" % self.args.schema)
|
self.logger.info("Validating against schema %s" % self.args.schema)
|
||||||
schema = yamale.make_schema(self.args.schema)
|
validators = DefaultValidators.copy()
|
||||||
|
validators[IPInterfaceWithPrefixLength.tag] = IPInterfaceWithPrefixLength
|
||||||
|
schema = yamale.make_schema(self.args.schema, validators=validators)
|
||||||
data = yamale.make_data(content=str(yaml))
|
data = yamale.make_data(content=str(yaml))
|
||||||
yamale.validate(schema, data)
|
yamale.validate(schema, data)
|
||||||
self.logger.debug("Schema correctly validated by yamale")
|
self.logger.debug("Schema correctly validated by yamale")
|
||||||
|
Reference in New Issue
Block a user