diff --git a/plugins/module_utils/interfaces_assignments_utils.py b/plugins/module_utils/interfaces_assignments_utils.py index 56d818fd..4fbcf01d 100644 --- a/plugins/module_utils/interfaces_assignments_utils.py +++ b/plugins/module_utils/interfaces_assignments_utils.py @@ -7,6 +7,7 @@ from dataclasses import dataclass, asdict, field from typing import List, Optional, Dict, Any +from pprint import pprint from xml.etree.ElementTree import Element, ElementTree, SubElement @@ -59,6 +60,8 @@ class InterfaceAssignment: identifier: str device: str descr: Optional[str] = None + enable: Optional[bool] = False + lock: Optional[bool] = False # since only the above attributes are needed, the rest is handled here extra_attrs: Dict[str, Any] = field(default_factory=dict, repr=False) @@ -68,6 +71,8 @@ def __init__( identifier: str, device: str, descr: Optional[str] = None, + enable: Optional[bool] = False, + lock: Optional[bool] = False, **kwargs, ): self.identifier = identifier @@ -75,6 +80,7 @@ def __init__( if descr is not None: self.descr = descr self.extra_attrs = kwargs + self.enable = enable @staticmethod def from_xml(element: Element) -> "InterfaceAssignment": @@ -127,7 +133,12 @@ def to_etree(self) -> Element: # Special handling for 'device' and 'descr' SubElement(main_element, "if").text = interface_assignment_dict.get("device") SubElement(main_element, "descr").text = interface_assignment_dict.get("descr") - + if getattr(self, "enable", None): + SubElement(main_element, "enable").text = "1" + # Enumerate the basic attributes if the interface is enabled + + if getattr(self, "lock", None): + SubElement(main_element, "lock").text = "1" # handle special cases if getattr(self, "alias-subnet", None): interface_assignment_dict["extra_attrs"]["alias-subnet"] = getattr( @@ -152,7 +163,6 @@ def to_etree(self) -> Element: interface_assignment_dict["extra_attrs"]["track6-prefix-id"] = getattr( self, "track6-prefix-id", None ) - # Serialize extra attributes for key, value in interface_assignment_dict["extra_attrs"].items(): if ( @@ -212,6 +222,15 @@ def from_ansible_module_params(cls, params: dict) -> "InterfaceAssignment": "identifier": params.get("identifier"), "device": params.get("device"), "descr": params.get("description"), + "enable": params.get("enabled"), + "lock": params.get("locked"), + # "blockpriv": params.get("block_private"), + # "blockbogons": params.get("block_bogons"), + # "spoofmac": params.get("mac_address"), + # "promisc": params.get("promiscuous_mode"), + # "mtu": params.get("mtu"), + # "mss": params.get("mss"), + # "gateway_interface": params.get("dynamic_gateway"), } interface_assignment_dict = { @@ -219,7 +238,6 @@ def from_ansible_module_params(cls, params: dict) -> "InterfaceAssignment": for key, value in interface_assignment_dict.items() if value is not None } - return cls(**interface_assignment_dict) @@ -376,27 +394,26 @@ def update(self, interface_assignment: InterfaceAssignment) -> None: raise OPNSenseDeviceNotFoundError( "Device was not found on OPNsense Instance!" ) - - interface_to_update: Optional[InterfaceAssignment] = next( - ( - interface - for interface in self._interfaces_assignments - if interface.device == interface_assignment.device + for interface in self._interfaces_assignments: + if ( + interface.device == interface_assignment.device or interface.identifier == interface_assignment.identifier - ), - None, - ) - + ): + interface_to_update = interface + print(interface_to_update) + break + else: + interface_to_update = None if not interface_to_update: - interface_to_create: InterfaceAssignment = InterfaceAssignment( identifier=interface_assignment.identifier, device=interface_assignment.device, descr=interface_assignment.descr, + enable=interface_assignment.enable, ) self._interfaces_assignments.append(interface_to_create) - + pprint(interface_to_create) return if ( @@ -404,19 +421,29 @@ def update(self, interface_assignment: InterfaceAssignment) -> None: or interface_assignment.device == interface_to_update.device ): - if interface_assignment.identifier in identifier_list_set: + if interface_assignment.identifier in identifier_list_set or interface_assignment.device == interface_to_update.device: # Merge extra_attrs interface_assignment.extra_attrs.update(interface_to_update.extra_attrs) # Update the existing interface interface_to_update.__dict__.update(interface_assignment.__dict__) - + else: raise OPNSenseDeviceAlreadyAssignedError( "This device is already assigned, please unassign this device first" - ) + ) + elif interface_assignment.enable != interface_to_update.enable: + if interface_assignment.enable: + # Merge extra_attrs + interface_assignment.extra_attrs.update(interface_to_update.extra_attrs) + + # Update the existing interface + interface_to_update.__dict__.update(interface_assignment.__dict__) + else: + interface_assignment.enable = False + interface_to_update.__dict__.update(interface_assignment.__dict__) else: raise OPNSenseDeviceAlreadyAssignedError( "This device is already assigned, please unassign this device first" diff --git a/plugins/modules/interfaces_assignments.py b/plugins/modules/interfaces_assignments.py index 2ea53729..345a08b3 100644 --- a/plugins/modules/interfaces_assignments.py +++ b/plugins/modules/interfaces_assignments.py @@ -37,6 +37,101 @@ - Input will be trimmed, as no whitespaces are allowed. type: str required: false + enabled: + description: + - Enable or disable the interface + type: bool + required: false + locked: + description: + - Prevent interface removal + type: bool + required: false + block_private: + description: + - When set, this option blocks traffic from IP addresses that are reserved for private networks as per RFC 1918 (10/8, 172.16/12, 192.168/16) as well as loopback addresses (127/8) and Carrier-grade NAT addresses (100.64/10). This option should only be set for WAN interfaces that use the public IP address space. + type: bool + required: false + block_bogons: + description: + - When set, this option blocks traffic from IP addresses that are reserved for private networks as per RFC 1918 (10/8, 172.16/12, 192.168/16) as well as loopback addresses (127/8) and Carrier-grade NAT addresses (100.64/10). This option should only be set for WAN interfaces that use the public IP address space. + type: bool + required: false + ipv4_configuration_type: + description: + - + type: str + required: false + ipv6_configuration_type: + description: + - + type: str + required: false + ipv4_address: + description: + - + type: str + required: false + # ipv4_subnet: + # description: + # - + # type: int + # required: false + ipv4_gateway: + description: + - + type: str + required: false + ipv6_address: + description: + - + type: str + required: false + # ipv6_subnet: + # description: + # - + # type: int + # required: false + ipv6_gateway: + description: + - + type: str + required: false + track6_interface: + description: + - + type: str + required: false + track6_prefix_id: + description: + - + type: int + required: false + mac_address: + description: + - + type: str + required: false + promiscuous_mode: + description: + - + type: bool + required: false + mtu: + description: + - If you leave this field blank, the adapter's default MTU will be used. This is typically 1500 bytes but can vary in some circumstances. + type: int + required: false + mss: + description: + - If you enter a value in this field, then MSS clamping for TCP connections to the value entered above minus 40 (IPv4) or 60 (IPv6) will be in effect (TCP/IP header size). + type: int + required: false + dynamic_gateway: + description: + - If the destination is directly reachable via an interface requiring no intermediary system to act as a gateway, you can select this option which allows dynamic gateways to be created without direct target addresses. Some tunnel types support this. + type: bool + required: false ''' EXAMPLES = r''' @@ -98,6 +193,23 @@ def main(): "identifier": {"type": "str", "required": True}, "device": {"type": "str", "required": True}, "description": {"type": "str", "required": False}, + "enabled": {"type": "bool", "required": False, "default": False}, + "locked": {"type": "bool", "required": False, "default": False}, + "block_private": {"type": "bool", "required": False, "default": False}, + "block_bogons": {"type": "bool", "required": False, "default": False}, + "ipv4_address": {"type": "str", "required": False}, + "ipv4_subnet": {"type": "int", "required": False}, + "ipv4_gateway": {"type": "str", "required": False}, + "ipv6_address": {"type": "str", "required": False}, + "ipv6_subnet": {"type": "int", "required": False}, + "ipv6_gateway": {"type": "str", "required": False}, + "track6_interface": {"type": "str", "required": False}, + "track6_prefix_id": {"type": "int", "required": False}, + "mac_address": {"type": "str", "required": False}, + "promiscuous_mode": {"type": "bool", "required": False, "default": False}, + "mtu": {"type": "int", "required": False}, + "mss": {"type": "int", "required": False}, + "dynamic_gateway": {"type": "bool", "required": False, "default": False}, } module = AnsibleModule(