Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Interface config #158

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 45 additions & 18 deletions plugins/module_utils/interfaces_assignments_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -68,13 +71,16 @@ def __init__(
identifier: str,
device: str,
descr: Optional[str] = None,
enable: Optional[bool] = False,
lock: Optional[bool] = False,
**kwargs,
):
self.identifier = identifier
self.device = device
if descr is not None:
self.descr = descr
self.extra_attrs = kwargs
self.enable = enable

@staticmethod
def from_xml(element: Element) -> "InterfaceAssignment":
Expand Down Expand Up @@ -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(
Expand All @@ -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 (
Expand Down Expand Up @@ -212,14 +222,22 @@ 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 = {
key: value
for key, value in interface_assignment_dict.items()
if value is not None
}

return cls(**interface_assignment_dict)


Expand Down Expand Up @@ -376,47 +394,56 @@ 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 (
interface_assignment.device in free_interfaces
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"
Expand Down
112 changes: 112 additions & 0 deletions plugins/modules/interfaces_assignments.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'''
Expand Down Expand Up @@ -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(
Expand Down
Loading