diff --git a/.gitignore b/.gitignore index b11e259fff..1e374d5c90 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ docs/_venv/ docs/dictionary/tmp **/__pycache__/ .vscode +.venv/* +.env diff --git a/Makefile b/Makefile index 4af9636280..fb2a493c0b 100644 --- a/Makefile +++ b/Makefile @@ -235,3 +235,14 @@ spelling: docs ## Run spell check as in CI fi pyspelling -c .spellcheck.yml -v -n documentation -S "docs/_build/html/**/*.html" + +.PHONY: plugin-development-enable +plugin-development-enable: # Replace all imports from ansible_collecton to the local git repo and override the PYTHONPATH environment var + grep -rl --include=\*.py --exclude-dir=.venv 'ansible_collections.cifmw.general' | xargs gsed -i 's/from ansible_collections\.cifmw\.general./from /g' + grep PYTHONPATH .env 2>/dev/null || echo "PYTHONPATH=${PWD}" >> .env + +.PHONY: plugin-development-disable +plugin-development-disable: # Revert all changes and delete .env if no longer needed + grep -lEr --include=\*.py --exclude-dir=.venv 'from (?:plugins|tests)' | xargs gsed -i -e 's/from plugins/from ansible_collections\.cifmw\.general\.plugins/g' -e 's/from tests/from ansible_collections\.cifmw\.general\.tests/g' + gsed -i '/PYTHONPATH=/d' .env + [ -s .env ] || rm .env diff --git a/plugins/module_utils/net_map/networking_definition.py b/plugins/module_utils/net_map/networking_definition.py index 03f1685822..067b6e6f79 100644 --- a/plugins/module_utils/net_map/networking_definition.py +++ b/plugins/module_utils/net_map/networking_definition.py @@ -471,6 +471,26 @@ def check_host_network_ranges_collisions( return None, None +def _get_field_ip_net_version(raw_field: str) -> typing.Union[int, None]: + if not raw_field: + return None + + try: + # Avoid that start/end integers are takes as IPv4 + # str allows passing IPs directly without converting to int + # and making it fail, so they will be parsed after the except + value = int(str(raw_field)) + if value is not None: + return None + except ValueError: + pass + + try: + return ipaddress.ip_network(raw_field).version + except ValueError: + return None + + class HostNetworkRange(ansible_encoding.RawConvertibleObject): """Parser and validator of network ranges @@ -494,7 +514,7 @@ def __init__( ] = None, length: typing.Union[int, str, None] = None, ): - """Creates a network range instance form it's net and limits. + """Creates a network range instance from it's net and limits. Creates a range instance for the provided network based on the given limits start and end (or length instead of end if provided). @@ -565,7 +585,7 @@ def get_version_from_raw( """Fetches the IP version of a range in dict format. Args: - raw_range: The range as a dictionary that for which the + raw_range: The range as a dictionary for which the version is requested. Returns: @@ -574,7 +594,7 @@ def get_version_from_raw( Raises: exceptions.NetworkMappingValidationError: If the range is - malformatted and contains IPs poiting to IPv4 and IPv6 + malformed and contains IPs pointing to IPv4 and IPv6 at the same time. """ if ( @@ -583,10 +603,10 @@ def get_version_from_raw( ): return None - start_version = cls.__get_field_ip_net_version( + start_version = _get_field_ip_net_version( raw_range.get(cls.__FIELD_RANGE_START, None) ) - end_version = cls.__get_field_ip_net_version( + end_version = _get_field_ip_net_version( raw_range.get(cls.__FIELD_RANGE_END, None) ) @@ -628,7 +648,7 @@ def from_raw( Args: network: The network to which the range belongs to. raw_range: The dictionary that contains the range data - as key-values of its start, end and/or lenth. + as key-values of its start, end and/or length. Returns: An instance of the given range. @@ -841,26 +861,6 @@ def __parse_validate_range_limit( field=field_name, ) from err - @staticmethod - def __get_field_ip_net_version(raw_field: str) -> typing.Union[int, None]: - if not raw_field: - return None - - try: - # Avoid that start/end integers are takes as IPv4 - # str allows passing IPs directly without converting to int - # and making it fail, so they will be parsed after the except - value = int(str(raw_field)) - if value is not None: - return None - except ValueError: - pass - - try: - return ipaddress.ip_network(raw_field).version - except ValueError: - return None - def __contains__(self, element): ip = element try: @@ -894,6 +894,134 @@ def __str__(self): return f"{self.__start_ip}-{self.__end_ip}" +class HostNetworkRoute: + """Parser and validator of network routes + + Handles the parsing and validation of a network route based on two + paramerters: destination and gateway. + Supports both IPv4 and IPv6 routes. + """ + + __FIELD_ROUTE_DESTINATION = "destination" + __FIELD_ROUTE_GATEWAY = "gateway" + + def __init__( + self, + destination: typing.Union[ + ipaddress.IPv4Address, ipaddress.IPv6Address, str, int, None + ] = None, + gateway: typing.Union[ + ipaddress.IPv4Address, ipaddress.IPv6Address, str, int, None + ] = None, + ): + """Creates a network route instance. + + Creates a route instance for the provided network based on the + given destination and gateway. + + Args: + destination: The destination network for the route. + gateway: The gateway address to use for packets + matching the destination. + Raises: + ValueError: If destination is not provided + exceptions.NetworkMappingValidationError: + If the end of the range if behind the start. + If start/end IP are not of the same family of the + given network. + If the start/end/length ternary is out of range + of the network. + If any format error exists of one of the inputs. + """ + if not destination: + raise ValueError("destination is a mandatory argument") + self.__destination = ipaddress.ip_network(destination) + self.__gateway = ( + ipaddress.ip_network(gateway).network_address if gateway else None + ) + + @property + def destination(self) -> typing.Union[ipaddress.IPv4Address, ipaddress.IPv6Address]: + """The route destination.""" + return self.__destination + + @property + def gateway(self) -> typing.Union[ipaddress.IPv4Address, ipaddress.IPv6Address]: + """The gateway to use for the destination""" + return self.__gateway + + @classmethod + def from_raw( + cls, + raw_route: typing.Union[typing.Dict[str, typing.Any], str], + ) -> "HostNetworkRoute": + """Parses and validated a route from its dict representation. + + The route raw definition must adhere to the following format: + destination: + gateway: + + Args: + raw_route: The dictionary that contains the range data + as key-values of its destination and gateway. + + Returns: + An instance of the given route. + + Raises: + exceptions.NetworkMappingValidationError: When the format + of the provided route fields is not correct. + """ + if raw_route and not isinstance(raw_route, (dict, str)): + raise exceptions.NetworkMappingValidationError( + "raw_route argument must be a dict or a string", + invalid_value=raw_route, + ) + + return HostNetworkRoute( + destination=raw_route.get(cls.__FIELD_ROUTE_DESTINATION, None), + gateway=raw_route.get(cls.__FIELD_ROUTE_GATEWAY, None), + ) + + @classmethod + def get_version_from_raw( + cls, + raw_route: typing.Union[typing.Dict[str, typing.Any], str], + ) -> int: + """Fetches the IP version of a route in dict format. + + Args: + raw_route: The route as a dictionary for which the + version is requested. + + Returns: + The version of the route. + + Raises: + exceptions.NetworkMappingValidationError: If the route is + malformed and destination and gateway are not the same + IP version + """ + destination_version = _get_field_ip_net_version( + raw_route.get(cls.__FIELD_ROUTE_DESTINATION, None) + ) + gateway_version = _get_field_ip_net_version( + raw_route.get(cls.__FIELD_ROUTE_GATEWAY, None) + ) + + if ( + destination_version + and gateway_version + and (destination_version != gateway_version) + ): + raise exceptions.NetworkMappingValidationError( + "destination and gateway are mixed IP versions", + invalid_value=raw_route, + ) + + return destination_version + + class SubnetBasedNetworkToolDefinition: """ Handles the parsing and validation of networking configuration for a tool @@ -902,11 +1030,17 @@ class SubnetBasedNetworkToolDefinition: This class is responsible for ensuring that the parsed ranges are part of the selected network and that their format contains a proper IPv4/6 network address. + + This class is also responsible for optional route configuration and making + sure the fields are valid """ __FIELD_RANGES = "ranges" __FIELD_RANGES_IPV4 = "ranges-v4" __FIELD_RANGES_IPV6 = "ranges-v6" + __FIELD_ROUTES = "routes" + __FIELD_ROUTES_IPV4 = "routes-v4" + __FIELD_ROUTES_IPV6 = "routes-v6" def __init__( self, @@ -917,8 +1051,8 @@ def __init__( """Initializes a SubnetBasedNetworkToolDefinition instance. Args: - network: The network to which the tool blongs to. - raw_config: The dictionary that contains the ranges + network: The network to which the tool belongs to. + raw_config: The dictionary that contains the ranges and routes of the tool. object_name: The name of the parsed tool. """ @@ -928,6 +1062,9 @@ def __init__( self.__object_name = object_name self.__ipv4_ranges: typing.List[HostNetworkRange] = [] self.__ipv6_ranges: typing.List[HostNetworkRange] = [] + self.__ipv4_routes: typing.List[HostNetworkRoute] = [] + self.__ipv6_routes: typing.List[HostNetworkRoute] = [] + self.__parse_raw(raw_config) def __parse_raw(self, raw_definition: typing.Dict[str, typing.Any]): @@ -941,6 +1078,16 @@ def __parse_raw(self, raw_definition: typing.Dict[str, typing.Any]): parent_name=self.__object_name, alone_field=self.__FIELD_RANGES, ) + _validate_fields_one_of( + [ + self.__FIELD_ROUTES, + self.__FIELD_ROUTES_IPV4, + self.__FIELD_ROUTES_IPV6, + ], + raw_definition, + parent_name=self.__object_name, + alone_field=self.__FIELD_ROUTES, + ) self.__parse_raw_range_field(raw_definition, self.__FIELD_RANGES) self.__parse_raw_range_field( @@ -950,6 +1097,14 @@ def __parse_raw(self, raw_definition: typing.Dict[str, typing.Any]): raw_definition, self.__FIELD_RANGES_IPV6, ip_version=6 ) + self.__parse_raw_route_field(raw_definition, self.__FIELD_ROUTES) + self.__parse_raw_route_field( + raw_definition, self.__FIELD_ROUTES_IPV4, ip_version=4 + ) + self.__parse_raw_route_field( + raw_definition, self.__FIELD_ROUTES_IPV6, ip_version=6 + ) + def __parse_raw_range_field( self, raw_definition: typing.Dict[str, typing.Any], @@ -983,6 +1138,16 @@ def ranges_ipv6(self) -> typing.List[HostNetworkRange]: """The parsed IPv6 ranges.""" return self.__ipv6_ranges + @property + def routes_ipv4(self) -> typing.List[HostNetworkRoute]: + """The parsed IPv4 routes.""" + return self.__ipv4_routes + + @property + def routes_ipv6(self) -> typing.List[HostNetworkRoute]: + """The parsed IPv6 routes.""" + return self.__ipv6_routes + def __hash__(self) -> int: return hash( ( @@ -1002,6 +1167,26 @@ def __eq__(self, other) -> bool: and self.__ipv6_ranges == other.__ipv6_ranges ) + def __parse_raw_route_field( + self, raw_definition, field_name: str, ip_version: int = None + ): + if field_name in raw_definition: + routes = _validate_parse_field_type( + field_name, + raw_definition, + list, + parent_name=self.__object_name, + ) + + for route in routes: + ipv4_route, ipv6_route = self.__network.parse_route_from_raw( + route, ip_version=ip_version + ) + if ipv4_route: + self.__ipv4_routes.append(ipv4_route) + if ipv6_route: + self.__ipv6_routes.append(ipv6_route) + class MultusNetworkDefinition(SubnetBasedNetworkToolDefinition): """Parses and holds Multus configuration for a given network.""" @@ -1321,29 +1506,34 @@ def __pick_nets_from_raw( typing.Union[ipaddress.IPv4Network, None], typing.Union[ipaddress.IPv6Network, None], ]: - """Gets the built-in networks (IPv4/6) for the given raw range + """Gets the built-in networks (IPv4/6) for the given raw range or route - Gets the networks that applies for a range based on its IP version. + Gets the networks that applies for a range or route based on its IP + version. Args: - raw_definition: The range definition as a dict. - ip_version: IP version of the requested range. If not given the - version will be inferred from the raw_definition. + raw_definition: The range or route definition as a dict. + ip_version: IP version of the requested range or route. If not + given the version will be inferred from the raw_definition. Returns: A tuple with the IPv4 and IPv6 networks. - If the range targets - only the IPv4 net only that one is returned. Same for the IPv6 one. - If the range targets both elements of the tuple are returned. + If the range or route only targets IPv4 or IPv6 than only that will + be returned. If the range or route targets both IPv4 and IPv6 + than both will be returned. Raises: exceptions.NetworkMappingValidationError: - If the content of the range is invalid. - If the range content doesn't match the requested IP version. + If the content of the range or route is invalid. + If the range or route content doesn't match the requested IP + version. If the network doesn't support the requested IP version. """ - # Fetch the IP version of the given range based on it's fields. - range_version = HostNetworkRange.get_version_from_raw(raw_definition) + # Fetch the IP version of the given range or route based on it's fields. + if "destination" in raw_definition: + range_version = HostNetworkRoute.get_version_from_raw(raw_definition) + else: + range_version = HostNetworkRange.get_version_from_raw(raw_definition) # If given (can be empty if it applies to IPv4 and 6) check if it's # the expected one @@ -1595,6 +1785,35 @@ def __eq__(self, other) -> bool: and self.__metallb_config == other.__metallb_config ) + def parse_route_from_raw( + self, raw_definition: typing.Dict[str, typing.Any], ip_version: int = None + ) -> typing.Tuple[ + typing.Union[HostNetworkRoute, None], + typing.Union[HostNetworkRoute, None], + ]: + """Creates HostNetworkRoute instance for the route based on their + definition + + Args: + raw_definition: The full network definition as a dict. + ip_version: IP version of the requested route. If not given the + version will be inferred from the raw_definition. + + Returns: Tuple of the IPv4 and IPv6 routes for the network. + If version is specified only one of the items of the tuple will be + filled. + """ + ipv4_route_net, ipv6_route_net = self.__pick_nets_from_raw( + raw_definition, ip_version=ip_version + ) + ipv4_route = ( + HostNetworkRoute.from_raw(raw_definition) if ipv4_route_net else None + ) + ipv6_route = ( + HostNetworkRoute.from_raw(raw_definition) if ipv6_route_net else None + ) + return ipv4_route, ipv6_route + @dataclasses.dataclass(frozen=True) class GroupTemplateNetworkDefinition: diff --git a/plugins/module_utils/net_map/networking_env_definitions.py b/plugins/module_utils/net_map/networking_env_definitions.py index d65acfe14b..b9943db84c 100644 --- a/plugins/module_utils/net_map/networking_env_definitions.py +++ b/plugins/module_utils/net_map/networking_env_definitions.py @@ -43,6 +43,34 @@ class MappedIpv6NetworkRange: length: int +@dataclasses.dataclass(frozen=True) +class MappedIpv4NetworkRoute: + """Represents an IPv4 network route + + Attributes: + destination: The destination network for the route. + gateway: The gateway address to use for packets + matching the destination. + """ + + destination: ipaddress.IPv4Address + gateway: ipaddress.IPv4Address + + +@dataclasses.dataclass(frozen=True) +class MappedIpv6NetworkRoute: + """Represents an IPv6 network route + + Attributes: + destination: The destination network for the route. + gateway: The gateway address to use for packets + matching the destination. + """ + + destination: ipaddress.IPv6Address + gateway: ipaddress.IPv6Address + + @dataclasses.dataclass(frozen=True) class MappedInstanceNetwork: """Defines a network attached to an instance @@ -106,11 +134,15 @@ class MappedMultusNetworkConfig: Attributes: ipv4_ranges: IPv4 ranges assigned to Multus. ipv6_ranges: IPv6 ranges assigned to Multus. + ipv4_routes: IPv4 routes assigned to Multus. + ipv6_routes: IPv6 routes assigned to Multus. """ ipv4_ranges: typing.List[MappedIpv4NetworkRange] ipv6_ranges: typing.List[MappedIpv6NetworkRange] + ipv4_routes: typing.List[MappedIpv4NetworkRoute] + ipv6_routes: typing.List[MappedIpv6NetworkRoute] @dataclasses.dataclass(frozen=True) diff --git a/plugins/module_utils/net_map/networking_mapper.py b/plugins/module_utils/net_map/networking_mapper.py index 95d79b9df7..f3d680a075 100644 --- a/plugins/module_utils/net_map/networking_mapper.py +++ b/plugins/module_utils/net_map/networking_mapper.py @@ -106,6 +106,23 @@ def _map_host_network_range_to_output( ) +def _map_host_network_route_to_output( + host_net_route: networking_definition.HostNetworkRoute, +) -> typing.Union[ + networking_env_definitions.MappedIpv4NetworkRoute, + networking_env_definitions.MappedIpv6NetworkRoute, +]: + args = [ + host_net_route.destination, + host_net_route.gateway, + ] + return ( + networking_env_definitions.MappedIpv4NetworkRoute(*args) + if host_net_route.destination.version == 4 + else networking_env_definitions.MappedIpv6NetworkRoute(*args) + ) + + class NetworkingInstanceMapper: """Converts the Networking Definition and facts into a MappedInstance @@ -631,7 +648,11 @@ def __build_network_tools( @staticmethod def __build_network_tool_common( tool_net_def: networking_definition.SubnetBasedNetworkToolDefinition, - tool_type: typing.Type, + tool_type: typing.Union[ + networking_env_definitions.MappedMetallbNetworkConfig, + networking_env_definitions.MappedMultusNetworkConfig, + networking_env_definitions.MappedNetconfigNetworkConfig, + ], ) -> typing.Union[ networking_env_definitions.MappedMetallbNetworkConfig, networking_env_definitions.MappedMultusNetworkConfig, @@ -647,6 +668,22 @@ def __build_network_tool_common( for ip_range in tool_net_def.ranges_ipv6 ], ] + route_args_list = [ + [ + _map_host_network_route_to_output(ip_route) + for ip_route in tool_net_def.routes_ipv4 + ], + [ + _map_host_network_route_to_output(ip_route) + for ip_route in tool_net_def.routes_ipv6 + ], + ] + + if any( + route_field in tool_type.__dataclass_fields__ + for route_field in ["ipv4_routes", "ipv6_routes"] + ): + args_list = args_list + route_args_list return tool_type(*args_list) diff --git a/tests/unit/module_utils/net_map/test_networking_definitions_network.py b/tests/unit/module_utils/net_map/test_networking_definitions_network.py index f4feb4c1ae..ab1d95f402 100644 --- a/tests/unit/module_utils/net_map/test_networking_definitions_network.py +++ b/tests/unit/module_utils/net_map/test_networking_definitions_network.py @@ -794,3 +794,41 @@ def test_network_definition_parse_valid_router_ok(): assert router_def.name == "testing-router" assert router_def.external_network == "provider-net" assert router_def.networks == ["test-net1", "test-net2"] + + +# This will test if passing a route that doesn't match the network version +# will cause a failure. +def test_network_definition_parse_tools_route_version_fail(): + net_name = "testing-net" + + for network_ip in ( + net_map_stub_data.NETWORK_1_IPV4_NET, + net_map_stub_data.NETWORK_1_IPV6_NET, + ): + net_config = { + "network": str(network_ip), + "tools": { + "multus": { + "ranges": [ + { + "start": str(network_ip[100]), + "length": 20, + }, + ], + "routes": [ + { + "destination": "192.168.121.0/24", + "gateway": "192.168.122.1", + }, + { + "destination": "fdc0:8b54:108a:c949::/64", + "gateway": "fdc0:8b54:108a:c948:0000:0000:0000:0001", + }, + ], + }, + }, + } + with pytest.raises(exceptions.NetworkMappingValidationError) as exc_info: + networking_definition.NetworkDefinition(net_name, net_config) + assert net_name in str(exc_info.value) + assert "are not supported in" in str(exc_info.value) diff --git a/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-dual-stack-full-map-out.json b/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-dual-stack-full-map-out.json index ba46da75d4..51b32dc95e 100644 --- a/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-dual-stack-full-map-out.json +++ b/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-dual-stack-full-map-out.json @@ -34,6 +34,22 @@ "end_host": 39, "length": 10 } + ], + "ipv4_routes": [ + { + "destination": "192.168.121.0/24", + "gateway": "192.168.122.1" + }, + { + "destination": "172.168.121.0/24", + "gateway": "172.168.122.1" + } + ], + "ipv6_routes": [ + { + "destination": "fdc0:8b54:108a:c949::/64", + "gateway": "fdc0:8b54:108a:c948:0000:0000:0000:0001" + } ] }, "netconfig": { @@ -104,7 +120,9 @@ "end_host": 39, "length": 10 } - ] + ], + "ipv4_routes": [], + "ipv6_routes": [] }, "netconfig": { "ipv4_ranges": [], @@ -224,7 +242,9 @@ "end_host": 39, "length": 10 } - ] + ], + "ipv4_routes": [], + "ipv6_routes": [] }, "netconfig": { "ipv4_ranges": [ diff --git a/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-dual-stack-networks-out.json b/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-dual-stack-networks-out.json index 12982cca36..e801ef66a1 100644 --- a/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-dual-stack-networks-out.json +++ b/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-dual-stack-networks-out.json @@ -33,6 +33,22 @@ "end_host": 39, "length": 10 } + ], + "ipv4_routes": [ + { + "destination": "192.168.121.0/24", + "gateway": "192.168.122.1" + }, + { + "destination": "172.168.121.0/24", + "gateway": "172.168.122.1" + } + ], + "ipv6_routes": [ + { + "destination": "fdc0:8b54:108a:c949::/64", + "gateway": "fdc0:8b54:108a:c948:0000:0000:0000:0001" + } ] }, "netconfig": { @@ -103,7 +119,9 @@ "end_host": 39, "length": 10 } - ] + ], + "ipv4_routes": [], + "ipv6_routes": [] }, "netconfig": { "ipv4_ranges": [], @@ -223,7 +241,9 @@ "end_host": 39, "length": 10 } - ] + ], + "ipv4_routes": [], + "ipv6_routes": [] }, "netconfig": { "ipv4_ranges": [ diff --git a/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-dual-stack-partial-map-out.json b/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-dual-stack-partial-map-out.json index e4a345fb10..1bfb8d6c32 100644 --- a/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-dual-stack-partial-map-out.json +++ b/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-dual-stack-partial-map-out.json @@ -34,6 +34,22 @@ "end_host": 39, "length": 10 } + ], + "ipv4_routes": [ + { + "destination": "192.168.121.0/24", + "gateway": "192.168.122.1" + }, + { + "destination": "172.168.121.0/24", + "gateway": "172.168.122.1" + } + ], + "ipv6_routes": [ + { + "destination": "fdc0:8b54:108a:c949::/64", + "gateway": "fdc0:8b54:108a:c948:0000:0000:0000:0001" + } ] }, "netconfig": { @@ -104,7 +120,9 @@ "end_host": 39, "length": 10 } - ] + ], + "ipv4_routes": [], + "ipv6_routes": [] }, "netconfig": { "ipv4_ranges": [], @@ -224,7 +242,9 @@ "end_host": 39, "length": 10 } - ] + ], + "ipv4_routes": [], + "ipv6_routes": [] }, "netconfig": { "ipv4_ranges": [ diff --git a/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-dual-stack-partial-reduced-map-out.json b/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-dual-stack-partial-reduced-map-out.json index 56197a0232..4f820a1155 100644 --- a/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-dual-stack-partial-reduced-map-out.json +++ b/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-dual-stack-partial-reduced-map-out.json @@ -34,6 +34,22 @@ "end_host": 39, "length": 10 } + ], + "ipv4_routes": [ + { + "destination": "192.168.121.0/24", + "gateway": "192.168.122.1" + }, + { + "destination": "172.168.121.0/24", + "gateway": "172.168.122.1" + } + ], + "ipv6_routes": [ + { + "destination": "fdc0:8b54:108a:c949::/64", + "gateway": "fdc0:8b54:108a:c948:0000:0000:0000:0001" + } ] }, "netconfig": { @@ -104,7 +120,9 @@ "end_host": 39, "length": 10 } - ] + ], + "ipv4_routes": [], + "ipv6_routes": [] }, "netconfig": { "ipv4_ranges": [], @@ -224,7 +242,9 @@ "end_host": 39, "length": 10 } - ] + ], + "ipv4_routes": [], + "ipv6_routes": [] }, "netconfig": { "ipv4_ranges": [ diff --git a/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-dual-stack.yml b/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-dual-stack.yml index 4f943976c6..20974e4fe3 100644 --- a/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-dual-stack.yml +++ b/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-dual-stack.yml @@ -17,6 +17,13 @@ networks: ranges: - start: 30 end: 39 + routes: + - destination: "192.168.121.0/24" + gateway: "192.168.122.1" + - destination: "172.168.121.0/24" + gateway: "172.168.122.1" + - destination: "fdc0:8b54:108a:c949::/64" + gateway: "fdc0:8b54:108a:c948:0000:0000:0000:0001" netconfig: ranges: - start: "192.168.122.40" @@ -97,9 +104,9 @@ group-templates: start: 10 length: 5 networks: - network-3: { } - network-4: { } - network-2: { } + network-3: {} + network-4: {} + network-2: {} group-3: networks: network-1: diff --git a/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-full-map-out.json b/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-full-map-out.json index a8c5bc24c5..b4b128c17a 100644 --- a/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-full-map-out.json +++ b/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-full-map-out.json @@ -26,7 +26,9 @@ "length": 10 } ], - "ipv6_ranges": [] + "ipv6_ranges": [], + "ipv4_routes": [], + "ipv6_routes": [] }, "netconfig": { "ipv4_ranges": [ @@ -79,7 +81,14 @@ "length": 10 } ], - "ipv6_ranges": [] + "ipv6_ranges": [], + "ipv4_routes": [ + { + "destination": "192.168.121.0/24", + "gateway": "192.168.122.1" + } + ], + "ipv6_routes": [] }, "netconfig": { "ipv4_ranges": [ @@ -133,7 +142,9 @@ "length": 10 } ], - "ipv6_ranges": [] + "ipv6_ranges": [], + "ipv4_routes": [], + "ipv6_routes": [] }, "netconfig": { "ipv4_ranges": [ @@ -187,7 +198,9 @@ "length": 10 } ], - "ipv6_ranges": [] + "ipv6_ranges": [], + "ipv4_routes": [], + "ipv6_routes": [] }, "netconfig": { "ipv4_ranges": [ diff --git a/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-ipv6-only-full-map-out.json b/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-ipv6-only-full-map-out.json index 3ff378518b..79e5bfa30f 100644 --- a/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-ipv6-only-full-map-out.json +++ b/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-ipv6-only-full-map-out.json @@ -26,6 +26,13 @@ "end_host": 39, "length": 10 } + ], + "ipv4_routes": [], + "ipv6_routes": [ + { + "destination": "fdc0:8b54:108a:c949::/64", + "gateway": "fdc0:8b54:108a:c948:0000:0000:0000:0001" + } ] }, "netconfig": { @@ -82,7 +89,9 @@ "end_host": 99, "length": 70 } - ] + ], + "ipv4_routes": [], + "ipv6_routes": [] }, "netconfig": { "ipv4_ranges": [], diff --git a/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-ipv6-only-networks-out.json b/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-ipv6-only-networks-out.json index ab6f7ac86c..2bdf947456 100644 --- a/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-ipv6-only-networks-out.json +++ b/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-ipv6-only-networks-out.json @@ -25,6 +25,13 @@ "end_host": 39, "length": 10 } + ], + "ipv4_routes": [], + "ipv6_routes": [ + { + "destination": "fdc0:8b54:108a:c949::/64", + "gateway": "fdc0:8b54:108a:c948:0000:0000:0000:0001" + } ] }, "netconfig": { @@ -81,7 +88,9 @@ "end_host": 99, "length": 70 } - ] + ], + "ipv4_routes": [], + "ipv6_routes": [] }, "netconfig": { "ipv4_ranges": [], diff --git a/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-ipv6-only-partial-map-out.json b/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-ipv6-only-partial-map-out.json index 82576f3540..ae25194120 100644 --- a/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-ipv6-only-partial-map-out.json +++ b/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-ipv6-only-partial-map-out.json @@ -26,6 +26,13 @@ "end_host": 39, "length": 10 } + ], + "ipv4_routes": [], + "ipv6_routes": [ + { + "destination": "fdc0:8b54:108a:c949::/64", + "gateway": "fdc0:8b54:108a:c948:0000:0000:0000:0001" + } ] }, "netconfig": { @@ -82,7 +89,9 @@ "end_host": 99, "length": 70 } - ] + ], + "ipv4_routes": [], + "ipv6_routes": [] }, "netconfig": { "ipv4_ranges": [], diff --git a/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-ipv6-only-partial-reduced-map-out.json b/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-ipv6-only-partial-reduced-map-out.json index ec630b6e8c..4b0022edfc 100644 --- a/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-ipv6-only-partial-reduced-map-out.json +++ b/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-ipv6-only-partial-reduced-map-out.json @@ -26,6 +26,13 @@ "end_host": 39, "length": 10 } + ], + "ipv4_routes": [], + "ipv6_routes": [ + { + "destination": "fdc0:8b54:108a:c949::/64", + "gateway": "fdc0:8b54:108a:c948:0000:0000:0000:0001" + } ] }, "netconfig": { @@ -82,7 +89,9 @@ "end_host": 99, "length": 70 } - ] + ], + "ipv4_routes": [], + "ipv6_routes": [] }, "netconfig": { "ipv4_ranges": [], diff --git a/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-ipv6-only.yml b/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-ipv6-only.yml index 2a42cf9082..123e45148b 100644 --- a/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-ipv6-only.yml +++ b/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-ipv6-only.yml @@ -11,6 +11,9 @@ networks: ranges: - start: 30 end: 39 + routes: + - destination: "fdc0:8b54:108a:c949::/64" + gateway: "fdc0:8b54:108a:c948:0000:0000:0000:0001" netconfig: ranges: - start: 40 @@ -64,7 +67,7 @@ group-templates: start: 300 length: 40 networks: - network-1: { } + network-1: {} group-2: network-template: range: diff --git a/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-networks-out.json b/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-networks-out.json index 999a420c78..8f7ad6f134 100644 --- a/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-networks-out.json +++ b/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-networks-out.json @@ -25,7 +25,9 @@ "length": 10 } ], - "ipv6_ranges": [] + "ipv6_ranges": [], + "ipv4_routes": [], + "ipv6_routes": [] }, "netconfig": { "ipv4_ranges": [ @@ -78,7 +80,14 @@ "length": 10 } ], - "ipv6_ranges": [] + "ipv6_ranges": [], + "ipv4_routes": [ + { + "destination": "192.168.121.0/24", + "gateway": "192.168.122.1" + } + ], + "ipv6_routes": [] }, "netconfig": { "ipv4_ranges": [ @@ -132,7 +141,9 @@ "length": 10 } ], - "ipv6_ranges": [] + "ipv6_ranges": [], + "ipv4_routes": [], + "ipv6_routes": [] }, "netconfig": { "ipv4_ranges": [ @@ -186,7 +197,9 @@ "length": 10 } ], - "ipv6_ranges": [] + "ipv6_ranges": [], + "ipv4_routes": [], + "ipv6_routes": [] }, "netconfig": { "ipv4_ranges": [ diff --git a/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-partial-map-out.json b/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-partial-map-out.json index 3011b33dac..c9c3ee3f3a 100644 --- a/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-partial-map-out.json +++ b/tests/unit/module_utils/test_files/networking-definition-valid-all-tools-partial-map-out.json @@ -26,7 +26,9 @@ "length": 10 } ], - "ipv6_ranges": [] + "ipv6_ranges": [], + "ipv4_routes": [], + "ipv6_routes": [] }, "netconfig": { "ipv4_ranges": [ @@ -79,7 +81,14 @@ "length": 10 } ], - "ipv6_ranges": [] + "ipv6_ranges": [], + "ipv4_routes": [ + { + "destination": "192.168.121.0/24", + "gateway": "192.168.122.1" + } + ], + "ipv6_routes": [] }, "netconfig": { "ipv4_ranges": [ @@ -133,7 +142,9 @@ "length": 10 } ], - "ipv6_ranges": [] + "ipv6_ranges": [], + "ipv4_routes": [], + "ipv6_routes": [] }, "netconfig": { "ipv4_ranges": [ @@ -187,7 +198,9 @@ "length": 10 } ], - "ipv6_ranges": [] + "ipv6_ranges": [], + "ipv4_routes": [], + "ipv6_routes": [] }, "netconfig": { "ipv4_ranges": [ diff --git a/tests/unit/module_utils/test_files/networking-definition-valid-all-tools.yml b/tests/unit/module_utils/test_files/networking-definition-valid-all-tools.yml index 0661be5cc0..e981c5fb38 100644 --- a/tests/unit/module_utils/test_files/networking-definition-valid-all-tools.yml +++ b/tests/unit/module_utils/test_files/networking-definition-valid-all-tools.yml @@ -26,6 +26,9 @@ networks: ranges: - start: 30 end: 39 + routes: + - destination: "192.168.121.0/24" + gateway: "192.168.122.1" netconfig: ranges: - start: "172.17.0.40"