diff --git a/ansible_collections/arista/avd/molecule/eos_designs_negative_unit_tests/inventory/host_vars/duplicate-vrfs-tenant-igmp-snooping-conflict.yml b/ansible_collections/arista/avd/molecule/eos_designs_negative_unit_tests/inventory/host_vars/duplicate-vrfs-tenant-igmp-snooping-conflict.yml index 472a6c97283..865d48fbeb0 100644 --- a/ansible_collections/arista/avd/molecule/eos_designs_negative_unit_tests/inventory/host_vars/duplicate-vrfs-tenant-igmp-snooping-conflict.yml +++ b/ansible_collections/arista/avd/molecule/eos_designs_negative_unit_tests/inventory/host_vars/duplicate-vrfs-tenant-igmp-snooping-conflict.yml @@ -48,6 +48,6 @@ tenants: ip_address_virtual: 10.0.100.1/24 expected_error_message: >- - Found duplicate objects with conflicting data while generating configuration for IGMP snooping for SVIs in VRF 'VRF1'. + Found duplicate objects with conflicting data while generating configuration for Vlans. {'id': 100, 'querier': {'enabled': True, 'address': '192.168.255.101'}} conflicts with {'id': 100, 'querier': {'enabled': False}}. diff --git a/python-avd/pyavd/_eos_designs/structured_config/network_services/ip_igmp_snooping.py b/python-avd/pyavd/_eos_designs/structured_config/network_services/ip_igmp_snooping.py index 01d84395fdb..fb3d56997d9 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/network_services/ip_igmp_snooping.py +++ b/python-avd/pyavd/_eos_designs/structured_config/network_services/ip_igmp_snooping.py @@ -3,10 +3,11 @@ # that can be found in the LICENSE file. from __future__ import annotations -from functools import cached_property from typing import TYPE_CHECKING, Protocol -from pyavd._utils import append_if_not_duplicate, default, strip_empties_from_dict +from pyavd._eos_cli_config_gen.schema import EosCliConfigGen +from pyavd._eos_designs.structured_config.structured_config_generator import structured_config_contributor +from pyavd._utils import default, strip_empties_from_dict if TYPE_CHECKING: from pyavd._eos_designs.schema import EosDesigns @@ -21,51 +22,32 @@ class IpIgmpSnoopingMixin(Protocol): Class should only be used as Mixin to a AvdStructuredConfig class. """ - @cached_property - def ip_igmp_snooping(self: AvdStructuredConfigNetworkServicesProtocol) -> dict | None: - """Return structured config for ip_igmp_snooping.""" + @structured_config_contributor + def ip_igmp_snooping(self: AvdStructuredConfigNetworkServicesProtocol) -> None: + """Set structured config for ip_igmp_snooping.""" if not self.shared_utils.network_services_l2: - return None + return igmp_snooping_enabled = self.shared_utils.igmp_snooping_enabled - ip_igmp_snooping = {"globally_enabled": igmp_snooping_enabled} if not igmp_snooping_enabled: - return ip_igmp_snooping + self.structured_config.ip_igmp_snooping.globally_enabled = igmp_snooping_enabled + return - vlans = [] for tenant in self.shared_utils.filtered_tenants: for vrf in tenant.vrfs: for svi in vrf.svis: - if vlan := self._ip_igmp_snooping_vlan(svi, tenant): - append_if_not_duplicate( - list_of_dicts=vlans, - primary_key="id", - new_dict=vlan, - context=f"IGMP snooping for SVIs in VRF '{vrf.name}'", - context_keys=["id"], - ) - + self._ip_igmp_snooping_vlan(svi, tenant) for l2vlan in tenant.l2vlans: - if vlan := self._ip_igmp_snooping_vlan(l2vlan, tenant): - append_if_not_duplicate( - list_of_dicts=vlans, - primary_key="id", - new_dict=vlan, - context="IGMP snooping for L2VLANs", - context_keys=["id"], - ) - - if vlans: - ip_igmp_snooping["vlans"] = vlans + self._ip_igmp_snooping_vlan(l2vlan, tenant) - return ip_igmp_snooping + self.structured_config.ip_igmp_snooping.globally_enabled = igmp_snooping_enabled def _ip_igmp_snooping_vlan( self: AvdStructuredConfigNetworkServicesProtocol, vlan: EosDesigns._DynamicKeys.DynamicNetworkServicesItem.NetworkServicesItem.VrfsItem.SvisItem | EosDesigns._DynamicKeys.DynamicNetworkServicesItem.NetworkServicesItem.L2vlansItem, tenant: EosDesigns._DynamicKeys.DynamicNetworkServicesItem.NetworkServicesItem, - ) -> dict: + ) -> None: """ ip_igmp_snooping logic for one vlan. @@ -87,24 +69,18 @@ def _ip_igmp_snooping_vlan( if self.shared_utils.network_services_l3 and self.shared_utils.uplink_type in ["p2p", "p2p-vrfs"]: igmp_snooping_querier_enabled = default(vlan.igmp_snooping_querier.enabled, tenant.igmp_snooping_querier.enabled) - ip_igmp_snooping_vlan = strip_empties_from_dict( - { - "enabled": igmp_snooping_enabled, - "querier": { - "enabled": igmp_snooping_querier_enabled, - "address": ( - default(vlan.igmp_snooping_querier.source_address, tenant.igmp_snooping_querier.source_address, self.shared_utils.router_id) - if igmp_snooping_querier_enabled - else None - ), - "version": default(vlan.igmp_snooping_querier.version, tenant.igmp_snooping_querier.version) if igmp_snooping_querier_enabled else None, - }, - # IGMP snooping fast-leave feature is enabled only when evpn_l2_multicast is enabled - "fast_leave": default(vlan.igmp_snooping_querier.fast_leave, tenant.evpn_l2_multicast.fast_leave) if evpn_l2_multicast_enabled else None, - } + vlan_item = EosCliConfigGen.IpIgmpSnooping.VlansItem( + enabled=igmp_snooping_enabled, + querier=EosCliConfigGen.IpIgmpSnooping.VlansItem.Querier( + enabled=igmp_snooping_querier_enabled, + address=default(vlan.igmp_snooping_querier.source_address, tenant.igmp_snooping_querier.source_address, self.shared_utils.router_id) + if igmp_snooping_querier_enabled + else None, + version=default(vlan.igmp_snooping_querier.version, tenant.igmp_snooping_querier.version) if igmp_snooping_querier_enabled else None, + ), + fast_leave=default(vlan.igmp_snooping_querier.fast_leave, tenant.evpn_l2_multicast.fast_leave) if evpn_l2_multicast_enabled else None, ) - if ip_igmp_snooping_vlan: - return {"id": vlan.id, **ip_igmp_snooping_vlan} - - return ip_igmp_snooping_vlan + if strip_empties_from_dict(vlan_item._as_dict()): + vlan_item._update(id=vlan.id) + self.structured_config.ip_igmp_snooping.vlans.append(vlan_item)