diff --git a/ansible_collections/arista/avd/plugins/filter/range_expand.py b/ansible_collections/arista/avd/plugins/filter/range_expand.py index 8feea48ce81..2d58bafea84 100644 --- a/ansible_collections/arista/avd/plugins/filter/range_expand.py +++ b/ansible_collections/arista/avd/plugins/filter/range_expand.py @@ -8,10 +8,23 @@ __metaclass__ = type -import re - from ansible.errors import AnsibleFilterError +from ansible_collections.arista.avd.plugins.plugin_utils.pyavd_wrappers import RaiseOnUse, wrap_filter + +PLUGIN_NAME = "arista.avd.range_expand" + +try: + from pyavd.j2filters.range_expand import range_expand +except ImportError as e: + range_expand = RaiseOnUse( + AnsibleFilterError( + f"The '{PLUGIN_NAME}' plugin requires the 'pyavd' Python library. Got import error", + orig_exc=e, + ) + ) + + DOCUMENTATION = r""" --- name: range_expand @@ -82,153 +95,8 @@ """ -def range_expand(range_to_expand): - if not (isinstance(range_to_expand, list) or isinstance(range_to_expand, str)): - raise AnsibleFilterError(f"value must be of type list or str, got {type(range_to_expand)}") - - result = [] - - # If we got a list, unpack it and run this function recursively - if isinstance(range_to_expand, list): - for r in range_to_expand: - result.extend(range_expand(r)) - - # Must be a str now - else: - prefix = "" - - # Unpack list in string - for one_range in range_to_expand.split(","): - if one_range is None: - continue - - # Find prefix (if any) - regex = r"^(.*?)(((\d+)-)?(\d+)\/)?(((\d+)-)?(\d+)\/)?(((\d+)-)?(\d+))(\.((\d+)-)?(\d+))?" - # Number of groups in this regex. - regex_groups = 17 - # Groups one-by-one: - # Group 1 (.*?) matches prefix ex. Ethernet, Eth, Po, Port-Channel - # Group 2 (((\d+)-)?(\d+)\/)? matches module(s) and slash ex. 12/, 1-3/ - # Group 3 ((\d+)-)? matches first module and dash ex. 1- - # Group 4 (\d+) matches first module ex. 1 - # Group 5 (\d+) matches last module ex. 12, 3 - # Group 6 (((\d+)-)?(\d+)\/)? matches parent interface(s) and slash ex. 47/, 1-48/ - # Group 7 ((\d+)-)? matches parent interface(s) and dash ex. 47- - # Group 8 (\d+) matches first parent interface ex. 1 - # Group 9 (\d+) matches last parent interface ex. 47, 48 - # Group 10 (((\d+)-)?(\d+)) matches (breakout) interface(s) ex. 1, 1-4, 1-48 - # Group 11 ((\d+)-)? matches first interfaces and dash ex. 1-, 1- - # Group 12 (\d+) matches first interface - # Group 13 (\d+) matches last interface ex. 1, 4, 48 - # Group 14 (\.((\d+)-)?(\d+))? matches dot and sub-interface(s) ex. .141, .12-15 - # Group 15 ((\d+)-)? matches first sub-interface and dash ex. 12- - # Group 16 (\d+) matches first sub-interface ex. 12 - # Group 17 (\d+) matches last sub-interface ex. 141, 15 - # Remember that the groups() object is 0-based and the group numbers above are 1-based - search_result = re.search(regex, one_range) - if search_result: - if len(search_result.groups()) == regex_groups: - groups = search_result.groups() - first_module = last_module = None - first_parent_interface = last_parent_interface = None - first_interface = last_interface = None - first_subinterface = last_subinterface = None - # Set prefix if found (otherwise use last set prefix) - if groups[0]: - prefix = groups[0] - if groups[4]: - last_module = int(groups[4]) - if groups[3]: - first_module = int(groups[3]) - else: - first_module = last_module - if groups[8]: - last_parent_interface = int(groups[8]) - if groups[7]: - first_parent_interface = int(groups[7]) - else: - first_parent_interface = last_parent_interface - if groups[12]: - last_interface = int(groups[12]) - if groups[11]: - first_interface = int(groups[11]) - else: - first_interface = last_interface - if groups[16]: - last_subinterface = int(groups[16]) - if groups[15]: - first_subinterface = int(groups[15]) - else: - first_subinterface = last_subinterface - - def expand_subinterfaces(interface_string): - result = [] - if last_subinterface is not None: - if first_subinterface > last_subinterface: - raise AnsibleFilterError( - f"Range {one_range} could not be expanded because the first subinterface {first_subinterface} is larger than last" - f" subinterface {last_subinterface} in the range." - ) - for subinterface in range(first_subinterface, last_subinterface + 1): - result.append(f"{interface_string}.{subinterface}") - else: - result.append(interface_string) - return result - - def expand_interfaces(interface_string): - result = [] - if first_interface > last_interface: - raise AnsibleFilterError( - f"Range {one_range} could not be expanded because the first interface {first_interface} is larger than last interface" - f" {last_interface} in the range." - ) - for interface in range(first_interface, last_interface + 1): - for res in expand_subinterfaces(f"{interface_string}{interface}"): - result.append(res) - return result - - def expand_parent_interfaces(interface_string): - result = [] - if last_parent_interface: - if first_parent_interface > last_parent_interface: - raise AnsibleFilterError( - f"Range {one_range} could not be expanded because the first interface {first_parent_interface} is larger than last" - f" interface {last_parent_interface} in the range." - ) - for parent_interface in range(first_parent_interface, last_parent_interface + 1): - for res in expand_interfaces(f"{interface_string}{parent_interface}/"): - result.append(res) - else: - for res in expand_interfaces(f"{interface_string}"): - result.append(res) - return result - - def expand_module(interface_string): - result = [] - if last_module: - if first_module > last_module: - raise AnsibleFilterError( - f"Range {one_range} could not be expanded because the first module {first_module} is larger than last module" - f" {last_module} in the range." - ) - for module in range(first_module, last_module + 1): - for res in expand_parent_interfaces(f"{interface_string}{module}/"): - result.append(res) - else: - for res in expand_parent_interfaces(f"{interface_string}"): - result.append(res) - return result - - result.extend(expand_module(prefix)) - - else: - raise AnsibleFilterError(f"Invalid range, got {one_range} and found {search_result.groups()}") - - return result - - class FilterModule(object): def filters(self): return { - "range_expand": range_expand, + "range_expand": wrap_filter(PLUGIN_NAME)(range_expand), } diff --git a/python-avd/Makefile b/python-avd/Makefile index ed4f8cab5b6..2a67d092107 100644 --- a/python-avd/Makefile +++ b/python-avd/Makefile @@ -95,6 +95,7 @@ fix-libs: ## Fix/remove various Ansible specifics things from python files find $(PACKAGE_DIR) -name '*.py' -exec sed -i -e 's/ansible_collections\.arista\.avd\.plugins\.filter.generate_route_target/$(PYAVD_FILTER_IMPORT)\.generate_route_target/g' {} + find $(PACKAGE_DIR) -name '*.py' -exec sed -i -e 's/ansible_collections\.arista\.avd\.plugins\.filter.hide_passwords/$(PYAVD_FILTER_IMPORT)\.hide_passwords/g' {} + find $(PACKAGE_DIR) -name '*.py' -exec sed -i -e 's/ansible_collections\.arista\.avd\.plugins\.filter.list_compress/$(PYAVD_FILTER_IMPORT)\.list_compress/g' {} + + find $(PACKAGE_DIR) -name '*.py' -exec sed -i -e 's/ansible_collections\.arista\.avd\.plugins\.filter.range_expand/$(PYAVD_FILTER_IMPORT)\.range_expand/g' {} + find $(PACKAGE_DIR) -name '*.py' -exec sed -i -e 's/ansible_collections\.arista\.avd\.plugins\.filter.status_render/$(PYAVD_FILTER_IMPORT)\.status_render/g' {} + find $(PACKAGE_DIR) -name '*.py' -exec sed -i -e 's/ansible_collections\.arista\.avd\.plugins\.filter/$(VENDOR_IMPORT)\.j2\.filter/g' {} + diff --git a/python-avd/pyavd/_eos_designs/eos_designs_facts/uplinks.py b/python-avd/pyavd/_eos_designs/eos_designs_facts/uplinks.py index 4b706265f4a..5501ba84953 100644 --- a/python-avd/pyavd/_eos_designs/eos_designs_facts/uplinks.py +++ b/python-avd/pyavd/_eos_designs/eos_designs_facts/uplinks.py @@ -8,8 +8,8 @@ from typing import TYPE_CHECKING from ...j2filters.list_compress import list_compress +from ...j2filters.range_expand import range_expand from ...vendor.errors import AristaAvdError -from ...vendor.j2.filter.range_expand import range_expand from ...vendor.utils import append_if_not_duplicate, get, unique if TYPE_CHECKING: diff --git a/python-avd/pyavd/_eos_designs/eos_designs_facts/vlans.py b/python-avd/pyavd/_eos_designs/eos_designs_facts/vlans.py index 664207cbde0..e51a5f7f08a 100644 --- a/python-avd/pyavd/_eos_designs/eos_designs_facts/vlans.py +++ b/python-avd/pyavd/_eos_designs/eos_designs_facts/vlans.py @@ -9,7 +9,7 @@ from ...j2filters.convert_dicts import convert_dicts from ...j2filters.list_compress import list_compress -from ...vendor.j2.filter.range_expand import range_expand +from ...j2filters.range_expand import range_expand from ...vendor.utils import get if TYPE_CHECKING: diff --git a/python-avd/pyavd/_eos_designs/ip_addressing/utils.py b/python-avd/pyavd/_eos_designs/ip_addressing/utils.py index ea2d0dd259b..79ad1d392ce 100644 --- a/python-avd/pyavd/_eos_designs/ip_addressing/utils.py +++ b/python-avd/pyavd/_eos_designs/ip_addressing/utils.py @@ -6,8 +6,8 @@ from functools import cached_property from typing import TYPE_CHECKING +from ...j2filters.range_expand import range_expand from ...vendor.errors import AristaAvdError, AristaAvdMissingVariableError -from ...vendor.j2.filter.range_expand import range_expand from ...vendor.utils import get if TYPE_CHECKING: diff --git a/python-avd/pyavd/_eos_designs/shared_utils/cv_topology.py b/python-avd/pyavd/_eos_designs/shared_utils/cv_topology.py index 22a7e847e48..c1f9a777d06 100644 --- a/python-avd/pyavd/_eos_designs/shared_utils/cv_topology.py +++ b/python-avd/pyavd/_eos_designs/shared_utils/cv_topology.py @@ -6,7 +6,7 @@ from functools import cached_property from typing import TYPE_CHECKING -from ...vendor.j2.filter.range_expand import range_expand +from ...j2filters.range_expand import range_expand from ...vendor.utils import get, get_item if TYPE_CHECKING: diff --git a/python-avd/pyavd/_eos_designs/shared_utils/filtered_tenants.py b/python-avd/pyavd/_eos_designs/shared_utils/filtered_tenants.py index 6f2a046a98a..f46af05fd5a 100644 --- a/python-avd/pyavd/_eos_designs/shared_utils/filtered_tenants.py +++ b/python-avd/pyavd/_eos_designs/shared_utils/filtered_tenants.py @@ -9,8 +9,8 @@ from ..._utils.merge import merge from ...j2filters.convert_dicts import convert_dicts from ...j2filters.natural_sort import natural_sort +from ...j2filters.range_expand import range_expand from ...vendor.errors import AristaAvdError, AristaAvdMissingVariableError -from ...vendor.j2.filter.range_expand import range_expand from ...vendor.utils import default, get, get_item, unique if TYPE_CHECKING: diff --git a/python-avd/pyavd/_eos_designs/shared_utils/misc.py b/python-avd/pyavd/_eos_designs/shared_utils/misc.py index a678b5a5dd4..ddfbea17e96 100644 --- a/python-avd/pyavd/_eos_designs/shared_utils/misc.py +++ b/python-avd/pyavd/_eos_designs/shared_utils/misc.py @@ -9,8 +9,8 @@ from ...j2filters.convert_dicts import convert_dicts from ...j2filters.natural_sort import natural_sort +from ...j2filters.range_expand import range_expand from ...vendor.errors.errors import AristaAvdError, AristaAvdMissingVariableError -from ...vendor.j2.filter.range_expand import range_expand from ...vendor.utils import default, get, get_item if TYPE_CHECKING: diff --git a/python-avd/pyavd/_eos_designs/shared_utils/mlag.py b/python-avd/pyavd/_eos_designs/shared_utils/mlag.py index f856785dddb..2faebe19e99 100644 --- a/python-avd/pyavd/_eos_designs/shared_utils/mlag.py +++ b/python-avd/pyavd/_eos_designs/shared_utils/mlag.py @@ -8,8 +8,8 @@ from re import findall from typing import TYPE_CHECKING +from ...j2filters.range_expand import range_expand from ...vendor.errors import AristaAvdError, AristaAvdMissingVariableError -from ...vendor.j2.filter.range_expand import range_expand from ...vendor.utils import default, get if TYPE_CHECKING: diff --git a/python-avd/pyavd/_eos_designs/shared_utils/routing.py b/python-avd/pyavd/_eos_designs/shared_utils/routing.py index 0da7c85e80b..6fdf6b5dabc 100644 --- a/python-avd/pyavd/_eos_designs/shared_utils/routing.py +++ b/python-avd/pyavd/_eos_designs/shared_utils/routing.py @@ -6,8 +6,8 @@ from functools import cached_property from typing import TYPE_CHECKING +from ...j2filters.range_expand import range_expand from ...vendor.errors.errors import AristaAvdError, AristaAvdMissingVariableError -from ...vendor.j2.filter.range_expand import range_expand from ...vendor.utils import get if TYPE_CHECKING: diff --git a/python-avd/pyavd/_eos_designs/structured_config/connected_endpoints/ethernet_interfaces.py b/python-avd/pyavd/_eos_designs/structured_config/connected_endpoints/ethernet_interfaces.py index 2bd93b8399e..5c6bc6e4c0c 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/connected_endpoints/ethernet_interfaces.py +++ b/python-avd/pyavd/_eos_designs/structured_config/connected_endpoints/ethernet_interfaces.py @@ -8,8 +8,8 @@ from functools import cached_property from typing import TYPE_CHECKING +from ....j2filters.range_expand import range_expand from ....vendor.errors import AristaAvdError, AristaAvdMissingVariableError -from ....vendor.j2.filter.range_expand import range_expand from ....vendor.strip_empties import strip_null_from_data from ....vendor.utils import append_if_not_duplicate, default, get, replace_or_append_item from ...interface_descriptions import InterfaceDescriptionData diff --git a/python-avd/pyavd/_eos_designs/structured_config/connected_endpoints/monitor_sessions.py b/python-avd/pyavd/_eos_designs/structured_config/connected_endpoints/monitor_sessions.py index 297fb480cf8..bea4b2d3adb 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/connected_endpoints/monitor_sessions.py +++ b/python-avd/pyavd/_eos_designs/structured_config/connected_endpoints/monitor_sessions.py @@ -8,7 +8,7 @@ from typing import TYPE_CHECKING from ...._utils.merge import merge -from ....vendor.j2.filter.range_expand import range_expand +from ....j2filters.range_expand import range_expand from ....vendor.strip_empties import strip_null_from_data from ....vendor.utils import append_if_not_duplicate, get, groupby from .utils import UtilsMixin diff --git a/python-avd/pyavd/_eos_designs/structured_config/connected_endpoints/port_channel_interfaces.py b/python-avd/pyavd/_eos_designs/structured_config/connected_endpoints/port_channel_interfaces.py index 74656d6bfbe..68ac5e349f2 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/connected_endpoints/port_channel_interfaces.py +++ b/python-avd/pyavd/_eos_designs/structured_config/connected_endpoints/port_channel_interfaces.py @@ -11,7 +11,7 @@ from ....j2filters.generate_esi import generate_esi from ....j2filters.generate_lacp_id import generate_lacp_id from ....j2filters.generate_route_target import generate_route_target -from ....vendor.j2.filter.range_expand import range_expand +from ....j2filters.range_expand import range_expand from ....vendor.strip_empties import strip_null_from_data from ....vendor.utils import append_if_not_duplicate, get from ...interface_descriptions import InterfaceDescriptionData diff --git a/python-avd/pyavd/_eos_designs/structured_config/network_services/utils.py b/python-avd/pyavd/_eos_designs/structured_config/network_services/utils.py index 34c8e7ca754..315b87ea068 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/network_services/utils.py +++ b/python-avd/pyavd/_eos_designs/structured_config/network_services/utils.py @@ -9,8 +9,8 @@ from ...._utils.password_utils.password import simple_7_encrypt from ....j2filters.natural_sort import natural_sort +from ....j2filters.range_expand import range_expand from ....vendor.errors import AristaAvdError, AristaAvdMissingVariableError -from ....vendor.j2.filter.range_expand import range_expand from ....vendor.utils import default, get, get_item from .utils_zscaler import UtilsZscalerMixin diff --git a/python-avd/pyavd/_eos_designs/structured_config/network_services/vxlan_interface.py b/python-avd/pyavd/_eos_designs/structured_config/network_services/vxlan_interface.py index a9eea70a2df..172925b4dde 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/network_services/vxlan_interface.py +++ b/python-avd/pyavd/_eos_designs/structured_config/network_services/vxlan_interface.py @@ -7,8 +7,8 @@ from typing import TYPE_CHECKING, NoReturn from ....j2filters.natural_sort import natural_sort +from ....j2filters.range_expand import range_expand from ....vendor.errors import AristaAvdError, AristaAvdMissingVariableError -from ....vendor.j2.filter.range_expand import range_expand from ....vendor.utils import append_if_not_duplicate, default, get, get_item, unique from .utils import UtilsMixin diff --git a/python-avd/pyavd/_eos_designs/structured_config/underlay/utils.py b/python-avd/pyavd/_eos_designs/structured_config/underlay/utils.py index 326c80348b2..ae41a0837c0 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/underlay/utils.py +++ b/python-avd/pyavd/_eos_designs/structured_config/underlay/utils.py @@ -7,8 +7,8 @@ from typing import TYPE_CHECKING from ....j2filters.natural_sort import natural_sort +from ....j2filters.range_expand import range_expand from ....vendor.errors import AristaAvdError -from ....vendor.j2.filter.range_expand import range_expand from ....vendor.strip_empties import strip_empties_from_dict from ....vendor.utils import default, get, get_item from ...interface_descriptions import InterfaceDescriptionData diff --git a/python-avd/pyavd/_eos_designs/structured_config/underlay/vlans.py b/python-avd/pyavd/_eos_designs/structured_config/underlay/vlans.py index d4634e9a8a5..a9164180ed1 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/underlay/vlans.py +++ b/python-avd/pyavd/_eos_designs/structured_config/underlay/vlans.py @@ -7,7 +7,7 @@ from typing import TYPE_CHECKING from ....j2filters.natural_sort import natural_sort -from ....vendor.j2.filter.range_expand import range_expand +from ....j2filters.range_expand import range_expand from ....vendor.utils import get, get_item from .utils import UtilsMixin diff --git a/python-avd/pyavd/j2filters/range_expand.py b/python-avd/pyavd/j2filters/range_expand.py new file mode 100644 index 00000000000..14784633939 --- /dev/null +++ b/python-avd/pyavd/j2filters/range_expand.py @@ -0,0 +1,152 @@ +# Copyright (c) 2024 Arista Networks, Inc. +# Use of this source code is governed by the Apache License 2.0 +# that can be found in the LICENSE file. +# pylint: disable=cell-var-from-loop +from __future__ import annotations + +import re + + +def range_expand(range_to_expand): + if not isinstance(range_to_expand, (list, str)): + raise TypeError(f"value must be of type list or str, got {type(range_to_expand)}") + + result = [] + + # If we got a list, unpack it and run this function recursively + if isinstance(range_to_expand, list): + for r in range_to_expand: + result.extend(range_expand(r)) + + # Must be a str now + else: + prefix = "" + + # Unpack list in string + for one_range in range_to_expand.split(","): + if one_range is None: + continue + + # Find prefix (if any) + regex = r"^(.*?)(((\d+)-)?(\d+)\/)?(((\d+)-)?(\d+)\/)?(((\d+)-)?(\d+))(\.((\d+)-)?(\d+))?" + # Number of groups in this regex. + regex_groups = 17 + # Groups one-by-one: + # Group 1 (.*?) matches prefix ex. Ethernet, Eth, Po, Port-Channel + # Group 2 (((\d+)-)?(\d+)\/)? matches module(s) and slash ex. 12/, 1-3/ + # Group 3 ((\d+)-)? matches first module and dash ex. 1- + # Group 4 (\d+) matches first module ex. 1 + # Group 5 (\d+) matches last module ex. 12, 3 + # Group 6 (((\d+)-)?(\d+)\/)? matches parent interface(s) and slash ex. 47/, 1-48/ + # Group 7 ((\d+)-)? matches parent interface(s) and dash ex. 47- + # Group 8 (\d+) matches first parent interface ex. 1 + # Group 9 (\d+) matches last parent interface ex. 47, 48 + # Group 10 (((\d+)-)?(\d+)) matches (breakout) interface(s) ex. 1, 1-4, 1-48 + # Group 11 ((\d+)-)? matches first interfaces and dash ex. 1-, 1- + # Group 12 (\d+) matches first interface + # Group 13 (\d+) matches last interface ex. 1, 4, 48 + # Group 14 (\.((\d+)-)?(\d+))? matches dot and sub-interface(s) ex. .141, .12-15 + # Group 15 ((\d+)-)? matches first sub-interface and dash ex. 12- + # Group 16 (\d+) matches first sub-interface ex. 12 + # Group 17 (\d+) matches last sub-interface ex. 141, 15 + # Remember that the groups() object is 0-based and the group numbers above are 1-based + search_result = re.search(regex, one_range) + if search_result: + if len(search_result.groups()) == regex_groups: + groups = search_result.groups() + first_module = last_module = None + first_parent_interface = last_parent_interface = None + first_interface = last_interface = None + first_subinterface = last_subinterface = None + # Set prefix if found (otherwise use last set prefix) + if groups[0]: + prefix = groups[0] + if groups[4]: + last_module = int(groups[4]) + if groups[3]: + first_module = int(groups[3]) + else: + first_module = last_module + if groups[8]: + last_parent_interface = int(groups[8]) + if groups[7]: + first_parent_interface = int(groups[7]) + else: + first_parent_interface = last_parent_interface + if groups[12]: + last_interface = int(groups[12]) + if groups[11]: + first_interface = int(groups[11]) + else: + first_interface = last_interface + if groups[16]: + last_subinterface = int(groups[16]) + if groups[15]: + first_subinterface = int(groups[15]) + else: + first_subinterface = last_subinterface + + def expand_subinterfaces(interface_string): + result = [] + if last_subinterface is not None: + if first_subinterface > last_subinterface: + raise ValueError( + f"Range {one_range} could not be expanded because the first subinterface {first_subinterface} is larger than last" + f" subinterface {last_subinterface} in the range." + ) + for subinterface in range(first_subinterface, last_subinterface + 1): + result.append(f"{interface_string}.{subinterface}") + else: + result.append(interface_string) + return result + + def expand_interfaces(interface_string): + result = [] + if first_interface > last_interface: + raise ValueError( + f"Range {one_range} could not be expanded because the first interface {first_interface} is larger than last interface" + f" {last_interface} in the range." + ) + for interface in range(first_interface, last_interface + 1): + for res in expand_subinterfaces(f"{interface_string}{interface}"): + result.append(res) + return result + + def expand_parent_interfaces(interface_string): + result = [] + if last_parent_interface: + if first_parent_interface > last_parent_interface: + raise ValueError( + f"Range {one_range} could not be expanded because the first interface {first_parent_interface} is larger than last" + f" interface {last_parent_interface} in the range." + ) + for parent_interface in range(first_parent_interface, last_parent_interface + 1): + for res in expand_interfaces(f"{interface_string}{parent_interface}/"): + result.append(res) + else: + for res in expand_interfaces(f"{interface_string}"): + result.append(res) + return result + + def expand_module(interface_string): + result = [] + if last_module: + if first_module > last_module: + raise ValueError( + f"Range {one_range} could not be expanded because the first module {first_module} is larger than last module" + f" {last_module} in the range." + ) + for module in range(first_module, last_module + 1): + for res in expand_parent_interfaces(f"{interface_string}{module}/"): + result.append(res) + else: + for res in expand_parent_interfaces(f"{interface_string}"): + result.append(res) + return result + + result.extend(expand_module(prefix)) + + else: + raise ValueError(f"Invalid range, got {one_range} and found {search_result.groups()}") + + return result diff --git a/python-avd/pyavd/templater.py b/python-avd/pyavd/templater.py index a41885f79ea..e393af55461 100644 --- a/python-avd/pyavd/templater.py +++ b/python-avd/pyavd/templater.py @@ -78,10 +78,10 @@ def import_filters_and_tests(self) -> None: from .j2filters.is_in_filter import is_in_filter from .j2filters.list_compress import list_compress from .j2filters.natural_sort import natural_sort + from .j2filters.range_expand import range_expand from .j2filters.snmp_hash import snmp_hash from .j2filters.status_render import status_render from .j2tests.contains import contains - from .vendor.j2.filter.range_expand import range_expand from .vendor.j2.test.defined import defined # pylint: enable=import-outside-toplevel diff --git a/ansible_collections/arista/avd/tests/unit/plugins/filter/test_range_expand.py b/python-avd/tests/pyavd/j2filters/test_range_expand.py similarity index 78% rename from ansible_collections/arista/avd/tests/unit/plugins/filter/test_range_expand.py rename to python-avd/tests/pyavd/j2filters/test_range_expand.py index cb86a14b134..2c2ebf41fe6 100644 --- a/ansible_collections/arista/avd/tests/unit/plugins/filter/test_range_expand.py +++ b/python-avd/tests/pyavd/j2filters/test_range_expand.py @@ -1,39 +1,37 @@ # Copyright (c) 2023-2024 Arista Networks, Inc. # Use of this source code is governed by the Apache License 2.0 # that can be found in the LICENSE file. -from __future__ import absolute_import, division, print_function -__metaclass__ = type +from __future__ import annotations import pytest - -from ansible_collections.arista.avd.plugins.filter.range_expand import AnsibleFilterError, FilterModule, range_expand +from pyavd.j2filters.range_expand import range_expand RANGE_TO_EXPAND_INVALID_VALUES = [ - pytest.param(True, AnsibleFilterError, "value must be of type list or str, got ", id="Wrong input type - bool"), - pytest.param({"key": "value"}, AnsibleFilterError, "value must be of type list or str, got ", id="Wrong input type - dict"), - pytest.param(33, AnsibleFilterError, "", id="Wrong input type - int"), + pytest.param(True, TypeError, "value must be of type list or str, got ", id="Wrong input type - bool"), + pytest.param({"key": "value"}, TypeError, "value must be of type list or str, got ", id="Wrong input type - dict"), + pytest.param(33, TypeError, "", id="Wrong input type - int"), pytest.param( "Ethernet4-2", - AnsibleFilterError, + ValueError, "Range Ethernet4-2 could not be expanded because the first interface 4 is larger than last interface 2 in the range.", id="Wrong interface range", ), pytest.param( "Ethernet1,51-3/2", - AnsibleFilterError, + ValueError, "Range 51-3/2 could not be expanded because the first module 51 is larger than last module 3 in the range.", id="Wrong module range", ), pytest.param( "Ethernet1.42-21", - AnsibleFilterError, + ValueError, "Range Ethernet1.42-21 could not be expanded because the first subinterface 42 is larger than last subinterface 21 in the range.", id="Wrong subinterface range", ), pytest.param( "Ethernet4/2-1/4", - AnsibleFilterError, + ValueError, "Range Ethernet4/2-1/4 could not be expanded because the first interface 2 is larger than last interface 1 in the range.", id="Wrong parent interface range", ), @@ -79,8 +77,6 @@ ["1.0", "1.1", "2.0", "2.1"], ] -f = FilterModule() - class TestRangeExpandFilter: @pytest.mark.parametrize("input_value, expected_raise, expected_raise_message", RANGE_TO_EXPAND_INVALID_VALUES) @@ -92,8 +88,3 @@ def test_range_expand_invalid(self, input_value, expected_raise, expected_raise_ def test_range_expand_valid(self, RANGE_TO_EXPAND_VALID): resp = range_expand(RANGE_TO_EXPAND_VALID) assert resp in EXPECTED_RESULT_VALID_VALUES - - def test_range_expand_filter(self): - resp = f.filters() - assert isinstance(resp, dict) - assert "range_expand" in resp.keys()