diff --git a/netsim/augment/addressing.py b/netsim/augment/addressing.py index 85f25a262..54a5e2d3b 100644 --- a/netsim/augment/addressing.py +++ b/netsim/augment/addressing.py @@ -198,9 +198,9 @@ def validate_pools(addrs: Box, topology: Box) -> None: module='addressing') if 'ipv6' in pfx and 'ipv6_pfx' in pfx: - if pfx.ipv6_pfx.prefixlen > 56: + if pfx.ipv6_pfx.prefixlen > 56 and pool != 'mgmt': log.error( - "Error in '%s' addressing pool: IPv6 pool prefix cannot be longer than /56" % pool, + f"Error in '{pool}' addressing pool: IPv6 pool prefix cannot be longer than /56", category=log.IncorrectValue, module='addressing') diff --git a/netsim/providers/__init__.py b/netsim/providers/__init__.py index 62d64410f..bae5b6f39 100644 --- a/netsim/providers/__init__.py +++ b/netsim/providers/__init__.py @@ -10,6 +10,7 @@ import os import typing import pathlib +import ipaddress # Related modules from box import Box @@ -378,7 +379,6 @@ def get_forwarded_ports(node: Box, topology: Box) -> list: """ validate_images -- check the images used by individual nodes against provider image repo """ - def validate_images(topology: Box) -> None: p_cache: dict = {} @@ -386,3 +386,41 @@ def validate_images(topology: Box) -> None: execute_node('validate_node_image',n_data,topology) log.exit_on_error() + +""" +validate_mgmt_ip -- Validate management IP addresses +""" +def validate_mgmt_ip( + node: Box, + provider: str, + mgmt: Box, + required: bool = False, + v4only: bool = False) -> None: + + valid_af = ['ipv4'] if v4only else ['ipv4','ipv6'] + n_mgmt = node.mgmt + node_af = [ n_af for n_af in n_mgmt.keys() if n_af in valid_af ] + if not node_af and required: + log.error( + f'Node {node.name} must have {" or ".join(valid_af)} management address', + category=log.MissingValue, + module=provider) + + if not mgmt: + return + + for af in ['ipv4','ipv6']: + if af not in n_mgmt: + continue + m_addr = ipaddress.ip_interface(n_mgmt[af]) + pfx = mgmt.get(f'{af}_pfx',None) + if pfx is None: + log.error( + f'Node {node.name} has an {af} management address, but the mgmt pool does not have an {af} prefix', + category=log.IncorrectValue, + module=provider) + elif not m_addr.network.subnet_of(pfx): + log.error( + f'Management {af} address of node {node.name} ({n_mgmt[af]}) is not part of the management subnet', + category=log.IncorrectValue, + module=provider) diff --git a/netsim/providers/clab.py b/netsim/providers/clab.py index dec911d20..af4626cc5 100644 --- a/netsim/providers/clab.py +++ b/netsim/providers/clab.py @@ -7,7 +7,7 @@ import pathlib import argparse -from . import _Provider,get_forwarded_ports +from . import _Provider,get_forwarded_ports,validate_mgmt_ip from ..utils import log, strings, linuxbridge from ..data import filemaps, get_empty_box, append_to_list from ..data.types import must_be_dict @@ -183,6 +183,7 @@ def augment_node_data(self, node: Box, topology: Box) -> None: def node_post_transform(self, node: Box, topology: Box) -> None: add_daemon_filemaps(node,topology) normalize_clab_filemaps(node) + validate_mgmt_ip(node,required=True,provider='clab',mgmt=topology.addressing.mgmt) self.create_extra_files_mappings(node,topology) diff --git a/netsim/providers/libvirt.py b/netsim/providers/libvirt.py index 9f43bc833..16b033f4a 100644 --- a/netsim/providers/libvirt.py +++ b/netsim/providers/libvirt.py @@ -16,7 +16,7 @@ from ..data import types,get_empty_box,get_box from ..utils import log,strings,linuxbridge from ..utils import files as _files -from . import _Provider +from . import _Provider,validate_mgmt_ip from ..augment import devices from ..augment.links import get_link_by_index from ..cli import is_dry_run,external_commands @@ -263,7 +263,7 @@ def create_vagrant_batches(topology: Box) -> None: class Libvirt(_Provider): """ - post_transform hook: mark multi-provider links as LAN links + pre_transform hook: mark multi-provider links as LAN links """ def pre_transform(self, topology: Box) -> None: if not 'links' in topology: @@ -295,6 +295,7 @@ def pre_transform(self, topology: Box) -> None: def node_post_transform(self, node: Box, topology: Box) -> None: if node.get('_set_ifindex'): pad_node_interfaces(node,topology) + validate_mgmt_ip(node,required=True,v4only=True,provider='libvirt',mgmt=topology.addressing.mgmt) def transform_node_images(self, topology: Box) -> None: self.node_image_version(topology) diff --git a/tests/errors/mgmt-subnet.log b/tests/errors/mgmt-subnet.log new file mode 100644 index 000000000..4959740e0 --- /dev/null +++ b/tests/errors/mgmt-subnet.log @@ -0,0 +1,4 @@ +IncorrectValue in libvirt: Management ipv4 address of node a (192.168.44.1) is not part of the management subnet +MissingValue in libvirt: Node b must have ipv4 management address +IncorrectValue in clab: Management ipv6 address of node d (2001:db8:43::3) is not part of the management subnet +Fatal error in netlab: Cannot proceed beyond this point due to errors, exiting diff --git a/tests/errors/mgmt-subnet.yml b/tests/errors/mgmt-subnet.yml new file mode 100644 index 000000000..9f55198ff --- /dev/null +++ b/tests/errors/mgmt-subnet.yml @@ -0,0 +1,22 @@ +defaults.device: none +addressing.mgmt: + ipv4: 192.168.42.0/24 + ipv6: 2001:db8:42::/64 + +nodes: + a: + provider: libvirt + mgmt.ipv4: 192.168.44.1 + b: + provider: libvirt + mgmt.ipv6: 2001:db8:42::2 + c: + provider: libvirt + mgmt.ipv4: 192.168.42.3 + mgmt.ipv6: 2001:db8:42::3 + d: + provider: clab + mgmt.ipv6: 2001:db8:43::3 + e: + provider: clab + mgmt.ipv6: 2001:db8:42::5