4
Makefile
4
Makefile
@ -25,6 +25,10 @@ install:
|
||||
pkg-deb:
|
||||
dpkg-buildpackage -uc -us -b
|
||||
|
||||
.PHONY: check-style
|
||||
check-style:
|
||||
PYTHONPATH=./$(VPPCFG) pylint ./$(VPPCFG)
|
||||
|
||||
.PHONY: uninstall
|
||||
uninstall:
|
||||
sudo $(PIP) uninstall $(VPPCFG)
|
||||
|
1
debian/rules
vendored
1
debian/rules
vendored
@ -3,4 +3,5 @@
|
||||
%:
|
||||
dh $@ --with python3 --buildsystem=pybuild --with systemd
|
||||
|
||||
# TODO: fix test.py to that unit tests can be automagically called.
|
||||
override_dh_auto_test:
|
||||
|
@ -13,7 +13,7 @@ types of validation:
|
||||
to a running VPP. *Note*: Some semantic checks are stricter than VPP, because applying
|
||||
them may leave the dataplane in a non-recoverable state.
|
||||
|
||||
For the curious, the Yamale syntax validation lives in [this schema](../schema.yaml).
|
||||
For the curious, the Yamale syntax validation lives in [this schema](../vppcfg/schema.yaml).
|
||||
If you want to get started quickly and don't mind cargo-culting, take a look at [this example](../example.yaml).
|
||||
|
||||
### Basic structure
|
||||
|
5
setup.py
5
setup.py
@ -1,4 +1,5 @@
|
||||
from setuptools import setup, find_packages
|
||||
"""vppcfg setuptools setup.py for pip and deb pkg installations"""
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
name="vppcfg",
|
||||
@ -17,5 +18,5 @@ setup(
|
||||
"vppcfg = vppcfg.vppcfg:main",
|
||||
]
|
||||
},
|
||||
include_package_data=True,
|
||||
package_data={"vppcfg": ["*.yaml"]},
|
||||
)
|
||||
|
@ -1,9 +1,16 @@
|
||||
import os, sys
|
||||
|
||||
ROOT_DIR = os.path.dirname(__file__)
|
||||
|
||||
# fix the module load path
|
||||
sys.path.insert(0, ROOT_DIR)
|
||||
|
||||
# fix the yaml search path
|
||||
os.chdir(ROOT_DIR)
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright (c) 2022 Ray Kinsella
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
""" __init__.py added to suppress pylint error """
|
||||
|
@ -32,12 +32,12 @@ except ImportError:
|
||||
sys.exit(-2)
|
||||
from yamale.validators import DefaultValidators, Validator
|
||||
|
||||
from config.loopback import validate_loopbacks
|
||||
from config.bondethernet import validate_bondethernets
|
||||
from config.interface import validate_interfaces
|
||||
from config.bridgedomain import validate_bridgedomains
|
||||
from config.vxlan_tunnel import validate_vxlan_tunnels
|
||||
from config.tap import validate_taps
|
||||
from .loopback import validate_loopbacks
|
||||
from .bondethernet import validate_bondethernets
|
||||
from .interface import validate_interfaces
|
||||
from .bridgedomain import validate_bridgedomains
|
||||
from .vxlan_tunnel import validate_vxlan_tunnels
|
||||
from .tap import validate_taps
|
||||
|
||||
|
||||
class IPInterfaceWithPrefixLength(Validator):
|
||||
@ -104,13 +104,12 @@ class Validator:
|
||||
if self.schema:
|
||||
fname = self.schema
|
||||
self.logger.debug(f"Validating against --schema {fname}")
|
||||
elif hasattr(sys, "_MEIPASS"):
|
||||
## See vppcfg.spec data_files that includes schema.yaml into the bundle
|
||||
self.logger.debug("Validating against built-in schema")
|
||||
fname = os.path.join(sys._MEIPASS, "schema.yaml")
|
||||
else:
|
||||
fname = "./schema.yaml"
|
||||
self.logger.debug(f"Validating against fallthrough default schema {fname}")
|
||||
## See setup.py data files that includes schema.yaml into the bundle
|
||||
self.logger.debug("Validating against built-in schema")
|
||||
fname = os.path.abspath(
|
||||
os.path.join(os.path.dirname(__file__), "..", "schema.yaml")
|
||||
)
|
||||
|
||||
if not os.path.isfile(fname):
|
||||
self.logger.error(f"Cannot file schema file: {fname}")
|
||||
|
@ -13,8 +13,8 @@
|
||||
#
|
||||
""" A vppcfg configuration module that handles bondethernets """
|
||||
import logging
|
||||
from config import interface
|
||||
from config import mac
|
||||
from . import interface
|
||||
from . import mac
|
||||
|
||||
|
||||
def get_bondethernets(yaml):
|
||||
|
@ -13,8 +13,8 @@
|
||||
#
|
||||
""" A vppcfg configuration module that handles bridgedomains """
|
||||
import logging
|
||||
from config import interface
|
||||
from config import loopback
|
||||
from . import interface
|
||||
from . import loopback
|
||||
|
||||
|
||||
def get_bridgedomains(yaml):
|
||||
|
@ -13,14 +13,14 @@
|
||||
#
|
||||
""" A vppcfg configuration module that validates interfaces """
|
||||
import logging
|
||||
from config import bondethernet
|
||||
from config import bridgedomain
|
||||
from config import loopback
|
||||
from config import vxlan_tunnel
|
||||
from config import lcp
|
||||
from config import address
|
||||
from config import mac
|
||||
from config import tap
|
||||
from . import bondethernet
|
||||
from . import bridgedomain
|
||||
from . import loopback
|
||||
from . import vxlan_tunnel
|
||||
from . import lcp
|
||||
from . import address
|
||||
from . import mac
|
||||
from . import tap
|
||||
|
||||
|
||||
def get_qinx_parent_by_name(yaml, ifname):
|
||||
|
@ -13,9 +13,9 @@
|
||||
#
|
||||
""" A vppcfg configuration module that validates loopbacks """
|
||||
import logging
|
||||
from config import lcp
|
||||
from config import address
|
||||
from config import mac
|
||||
from . import lcp
|
||||
from . import address
|
||||
from . import mac
|
||||
|
||||
|
||||
def get_loopbacks(yaml):
|
||||
|
@ -13,7 +13,7 @@
|
||||
#
|
||||
""" A vppcfg configuration module that validates taps """
|
||||
import logging
|
||||
from config import mac
|
||||
from . import mac
|
||||
|
||||
|
||||
def get_taps(yaml):
|
||||
|
@ -15,12 +15,13 @@
|
||||
""" Unit tests for bondethernet """
|
||||
import unittest
|
||||
import yaml
|
||||
import config.bondethernet as bondethernet
|
||||
from . import bondethernet
|
||||
from .unittestyaml import UnitTestYaml
|
||||
|
||||
|
||||
class TestBondEthernetMethods(unittest.TestCase):
|
||||
def setUp(self):
|
||||
with open("unittest/test_bondethernet.yaml", "r") as f:
|
||||
with UnitTestYaml("test_bondethernet.yaml") as f:
|
||||
self.cfg = yaml.load(f, Loader=yaml.FullLoader)
|
||||
|
||||
def test_get_by_name(self):
|
||||
|
@ -15,12 +15,13 @@
|
||||
""" Unit tests for bridgedomains """
|
||||
import unittest
|
||||
import yaml
|
||||
import config.bridgedomain as bridgedomain
|
||||
from . import bridgedomain
|
||||
from .unittestyaml import UnitTestYaml
|
||||
|
||||
|
||||
class TestBridgeDomainMethods(unittest.TestCase):
|
||||
def setUp(self):
|
||||
with open("unittest/test_bridgedomain.yaml", "r") as f:
|
||||
with UnitTestYaml("test_bridgedomain.yaml") as f:
|
||||
self.cfg = yaml.load(f, Loader=yaml.FullLoader)
|
||||
|
||||
def test_get_by_name(self):
|
||||
|
@ -15,12 +15,13 @@
|
||||
""" Unit tests for interfaces """
|
||||
import unittest
|
||||
import yaml
|
||||
import config.interface as interface
|
||||
from . import interface
|
||||
from .unittestyaml import UnitTestYaml
|
||||
|
||||
|
||||
class TestInterfaceMethods(unittest.TestCase):
|
||||
def setUp(self):
|
||||
with open("unittest/test_interface.yaml", "r") as f:
|
||||
with UnitTestYaml("test_interface.yaml") as f:
|
||||
self.cfg = yaml.load(f, Loader=yaml.FullLoader)
|
||||
|
||||
def test_enumerators(self):
|
||||
|
@ -15,13 +15,14 @@
|
||||
""" Unit tests for LCPs """
|
||||
import unittest
|
||||
import yaml
|
||||
import config.lcp as lcp
|
||||
import config.interface as interface
|
||||
from . import lcp
|
||||
from . import interface
|
||||
from .unittestyaml import UnitTestYaml
|
||||
|
||||
|
||||
class TestLCPMethods(unittest.TestCase):
|
||||
def setUp(self):
|
||||
with open("unittest/test_lcp.yaml", "r") as f:
|
||||
with UnitTestYaml("test_lcp.yaml") as f:
|
||||
self.cfg = yaml.load(f, Loader=yaml.FullLoader)
|
||||
|
||||
def test_enumerators(self):
|
||||
|
@ -15,12 +15,13 @@
|
||||
""" Unit tests for loopbacks """
|
||||
import unittest
|
||||
import yaml
|
||||
import config.loopback as loopback
|
||||
from . import loopback
|
||||
from .unittestyaml import UnitTestYaml
|
||||
|
||||
|
||||
class TestLoopbackMethods(unittest.TestCase):
|
||||
def setUp(self):
|
||||
with open("unittest/test_loopback.yaml", "r") as f:
|
||||
with UnitTestYaml("test_loopback.yaml") as f:
|
||||
self.cfg = yaml.load(f, Loader=yaml.FullLoader)
|
||||
|
||||
def test_get_by_lcp_name(self):
|
||||
|
@ -14,7 +14,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
""" Unit tests for MAC addresses """
|
||||
import unittest
|
||||
import config.mac as mac
|
||||
from . import mac
|
||||
|
||||
|
||||
class TestMACMethods(unittest.TestCase):
|
||||
|
@ -15,12 +15,13 @@
|
||||
""" Unit tests for taps """
|
||||
import unittest
|
||||
import yaml
|
||||
import config.tap as tap
|
||||
from . import tap
|
||||
from .unittestyaml import UnitTestYaml
|
||||
|
||||
|
||||
class TestTAPMethods(unittest.TestCase):
|
||||
def setUp(self):
|
||||
with open("unittest/test_tap.yaml", "r") as f:
|
||||
with UnitTestYaml("test_tap.yaml") as f:
|
||||
self.cfg = yaml.load(f, Loader=yaml.FullLoader)
|
||||
|
||||
def test_get_by_name(self):
|
||||
|
@ -15,12 +15,13 @@
|
||||
""" Unit tests for vxlan_tunnels """
|
||||
import unittest
|
||||
import yaml
|
||||
import config.vxlan_tunnel as vxlan_tunnel
|
||||
from . import vxlan_tunnel
|
||||
from .unittestyaml import UnitTestYaml
|
||||
|
||||
|
||||
class TestVXLANMethods(unittest.TestCase):
|
||||
def setUp(self):
|
||||
with open("unittest/test_vxlan_tunnel.yaml", "r") as f:
|
||||
with UnitTestYaml("test_vxlan_tunnel.yaml") as f:
|
||||
self.cfg = yaml.load(f, Loader=yaml.FullLoader)
|
||||
|
||||
def test_get_by_name(self):
|
||||
|
41
vppcfg/config/unittestyaml.py
Normal file
41
vppcfg/config/unittestyaml.py
Normal file
@ -0,0 +1,41 @@
|
||||
""" module to help locate unittest resources """
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright (c) 2022 Ray Kinsella
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
|
||||
|
||||
class UnitTestYaml:
|
||||
"""Helper classes to find and load unit test yaml resources"""
|
||||
|
||||
def __init__(self, fpath):
|
||||
self.file = None
|
||||
self.filename = fpath
|
||||
self.resdir = os.path.abspath(
|
||||
os.path.join(os.path.dirname(__file__), "../unittest")
|
||||
)
|
||||
|
||||
def __enter__(self):
|
||||
self.file = open(
|
||||
os.path.join(self.resdir, self.filename), "r", encoding="utf-8"
|
||||
)
|
||||
return self.file
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self.file.close()
|
||||
|
||||
def __str__(self):
|
||||
return self.file
|
@ -14,13 +14,19 @@
|
||||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
""" This is a unit test suite for vppcfg """
|
||||
|
||||
# pylint: disable=duplicate-code
|
||||
import os
|
||||
import sys
|
||||
import glob
|
||||
import re
|
||||
import unittest
|
||||
import yaml
|
||||
from config import Validator
|
||||
|
||||
try:
|
||||
from vppcfg.config import Validator
|
||||
except ModuleNotFoundError:
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
||||
from vppcfg.config import Validator
|
||||
|
||||
try:
|
||||
import argparse
|
||||
|
@ -17,7 +17,7 @@ The functions in this file interact with the VPP API to modify certain
|
||||
interface metadata.
|
||||
"""
|
||||
|
||||
from vpp.vppapi import VPPApi
|
||||
from .vppapi import VPPApi
|
||||
|
||||
|
||||
class Applier(VPPApi):
|
||||
|
@ -19,8 +19,8 @@ interface metadata and write it to a YAML file.
|
||||
|
||||
import sys
|
||||
import yaml
|
||||
from vpp.vppapi import VPPApi
|
||||
from config import bondethernet
|
||||
from vppcfg.config import bondethernet
|
||||
from .vppapi import VPPApi
|
||||
|
||||
|
||||
class Dumper(VPPApi):
|
||||
|
@ -19,14 +19,14 @@ metadata, and plan configuration changes towards a given YAML target configurati
|
||||
"""
|
||||
import sys
|
||||
import logging
|
||||
from config import loopback
|
||||
from config import interface
|
||||
from config import bondethernet
|
||||
from config import bridgedomain
|
||||
from config import vxlan_tunnel
|
||||
from config import lcp
|
||||
from config import tap
|
||||
from vpp.vppapi import VPPApi
|
||||
from vppcfg.config import loopback
|
||||
from vppcfg.config import interface
|
||||
from vppcfg.config import bondethernet
|
||||
from vppcfg.config import bridgedomain
|
||||
from vppcfg.config import vxlan_tunnel
|
||||
from vppcfg.config import lcp
|
||||
from vppcfg.config import tap
|
||||
from .vppapi import VPPApi
|
||||
|
||||
|
||||
class Reconciler:
|
||||
|
@ -15,12 +15,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""vppcfg is a utility to configure a running VPP Dataplane using YAML
|
||||
config files. See http://github.com/pimvanpelt/vppcfg/README.md for details. """
|
||||
# pylint: disable=duplicate-code
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
import yaml
|
||||
from config import Validator
|
||||
from vpp.reconciler import Reconciler
|
||||
from vpp.dumper import Dumper
|
||||
|
||||
# Ensure the paths are correct when we execute from the source tree
|
||||
try:
|
||||
from vppcfg.config import Validator
|
||||
except ModuleNotFoundError:
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
||||
from vppcfg.config import Validator
|
||||
from vppcfg.vpp.reconciler import Reconciler
|
||||
from vppcfg.vpp.dumper import Dumper
|
||||
|
||||
try:
|
||||
import argparse
|
||||
|
Reference in New Issue
Block a user