Add ip_interface() validator
This commit is contained in:
45
README.md
45
README.md
@ -1,17 +1,38 @@
|
||||
## Design
|
||||
|
||||
### Validators
|
||||
### YAML Configuration
|
||||
|
||||
Validators are functions which are passed the fully formed YAML configuration and are meant to
|
||||
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.
|
||||
The main file that is handled by this program is the **Configuration File**.
|
||||
|
||||
Validators can be disabled with the --skip-validator <name> [<name>] flag. It is not
|
||||
advised to skip validators. The purpose of the validators is to ensure the configuration is sane
|
||||
and semantically correct.
|
||||
## Validation
|
||||
|
||||
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)
|
||||
lcp: str(max=8,matches='[a-z]+[a-z0-9-]{,7}',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)
|
||||
---
|
||||
v4: str(matches='[0-9\.]+/[0-9]+')
|
||||
---
|
||||
v6: str(matches='[0-9a-f:]+/[0-9]+',ignore_case=True)
|
||||
---
|
||||
sub-interface:
|
||||
description: str(exclude='\'"',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:
|
||||
|
@ -17,6 +17,33 @@ from validator.bondethernet import bondethernet
|
||||
from validator.interface import interface
|
||||
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):
|
||||
def emit(self, record):
|
||||
pass
|
||||
@ -35,7 +62,9 @@ class Validator(object):
|
||||
if self.args.schema:
|
||||
try:
|
||||
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))
|
||||
yamale.validate(schema, data)
|
||||
self.logger.debug("Schema correctly validated by yamale")
|
||||
|
Reference in New Issue
Block a user