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