Skip to content
Open
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
129 changes: 129 additions & 0 deletions plugins/module_utils/lagg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# -*- coding: utf-8 -*-

# Copyright: (c) 2023, Example
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

from __future__ import absolute_import, division, print_function
__metaclass__ = type

from ansible_collections.pfsensible.core.plugins.module_utils.module_base import PFSenseModuleBase

LAGG_ARGUMENT_SPEC = dict(
state=dict(default='present', choices=['present', 'absent']),
laggif=dict(required=True, type='str'), # z.B. "lagg0", "lagg1" usw.
members=dict(required=True, type='list', elements='str'),
proto=dict(default='lacp', choices=['lacp', 'failover', 'loadbalance', 'roundrobin', 'none']),
lacptimeout=dict(default='fast', choices=['fast', 'slow']),
lagghash=dict(default='l2,l3,l4', type='str'),
descr=dict(default='', type='str'),
)


class PFSenseLaggModule(PFSenseModuleBase):
@staticmethod
def get_argument_spec():
return LAGG_ARGUMENT_SPEC

def __init__(self, module, pfsense=None):
super(PFSenseLaggModule, self).__init__(module, pfsense)
self.name = "pfsense_lagg"
self.root_elt = self.pfsense.get_element('laggs')
self.obj = dict()
if self.root_elt is None:
self.root_elt = self.pfsense.new_element('laggs')
self.pfsense.root.append(self.root_elt)

self.setup_lagg_cmds = ""

def _params_to_obj(self):
params = self.params
obj = dict()

obj['laggif'] = params['laggif']
obj['members'] = ",".join(params['members'])
obj['proto'] = params['proto']
obj['lacptimeout'] = params['lacptimeout']
obj['lagghash'] = params['lagghash']
obj['descr'] = params['descr']

return obj

def _validate_params(self):
if not self.params['members']:
self.module.fail_json(msg="members muss mindestens ein Interface enthalten.")

def _find_target(self):
requested = self.obj['laggif']
for lagg_node in self.root_elt.findall('lagg'):
node_laggif = lagg_node.findtext('laggif') or ''
if node_laggif.lower() == requested.lower():
self.obj['laggif'] = node_laggif
return lagg_node
return None

def _create_target(self):
return self.pfsense.new_element('lagg')

def _copy_and_add_target(self):
super(PFSenseLaggModule, self)._copy_and_add_target()
self.setup_lagg_cmds += self._cmd_create()

def _copy_and_update_target(self):
before_laggif = self.target_elt.find('laggif').text
(before, changed) = super(PFSenseLaggModule, self)._copy_and_update_target()
if changed:
self.setup_lagg_cmds += "pfSense_interface_destroy('{}');\n".format(before_laggif)
self.setup_lagg_cmds += self._cmd_create()
return (before, changed)

def _pre_remove_target_elt(self):
if self.pfsense.get_interface_by_port(self.obj['laggif']) is not None:
self.module.fail_json(
msg="LAGG {} is in use therefore you can't delete it.".format(self.obj['laggif'])
)
self.setup_lagg_cmds += "pfSense_interface_destroy('{}');\n".format(self.obj['laggif'])

def _cmd_create(self):
cmd = "$lagg = array();\n"
cmd += "$lagg['laggif'] = '{}';\n".format(self.obj['laggif'])
cmd += "$lagg['members'] = '{}';\n".format(self.obj['members'])
cmd += "$lagg['descr'] = '{}';\n".format(self.obj['descr'])
cmd += "$lagg['proto'] = '{}';\n".format(self.obj['proto'])
cmd += "$lagg['lacptimeout'] = '{}';\n".format(self.obj['lacptimeout'])
cmd += "$lagg['lagghash'] = '{}';\n".format(self.obj['lagghash'])
cmd += "$laggif = interface_lagg_configure($lagg);\n"
cmd += "if (($laggif == NULL) || ($laggif != $lagg['laggif'])) {\n"
cmd += " pfSense_interface_destroy('{}');\n".format(self.obj['laggif'])
cmd += "} else {\n"
interface = self.pfsense.get_interface_by_port(self.obj['laggif'])
if interface is not None:
cmd += " interface_configure('{}', true);\n".format(interface)
cmd += "}\n"
return cmd

def get_update_cmds(self):
cmd = 'require_once("filter.inc");\n'
if self.setup_lagg_cmds:
cmd += 'require_once("interfaces.inc");\n'
cmd += self.setup_lagg_cmds
cmd += "if (filter_configure() == 0) { clear_subsystem_dirty('filter'); }\n"
return cmd

def _update(self):
return self.pfsense.phpshell(self.get_update_cmds())

def _get_obj_name(self):
return "'{}'".format(self.obj['laggif'])

def _log_fields(self, before=None):
vals = ''
if before is None:
# Neu
vals += self.format_cli_field(self.obj, 'proto')
vals += self.format_cli_field(self.obj, 'members')
vals += self.format_cli_field(self.obj, 'descr')
else:
vals += self.format_updated_cli_field(self.obj, before, 'proto', add_comma=(vals))
vals += self.format_updated_cli_field(self.obj, before, 'members', add_comma=(vals))
vals += self.format_updated_cli_field(self.obj, before, 'descr', add_comma=(vals))
return vals
133 changes: 133 additions & 0 deletions plugins/modules/pfsense_lagg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright: (c) 2023, Example
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

from __future__ import absolute_import, division, print_function
__metaclass__ = type

DOCUMENTATION = r"""
---
module: pfsense_lagg
version_added: "0.1.0"
author:
- "Your Name (@your_github_handle)"
short_description: Manage pfSense LAGG (Link Aggregations)
description:
- This module manages pfSense LAGG interfaces (Link Aggregation). It can create, update, or remove LAGGs.
options:
laggif:
description:
- The name of the LAGG interface (e.g. C(lagg0), C(lagg1), etc.).
- Make sure this matches what pfSense actually stores in its config (case sensitivity may matter unless you
implement case-insensitive matching in your code).
required: true
type: str
members:
description:
- A list of physical interfaces to be aggregated, e.g. C(['igb0','igb1']).
- They must exist on the pfSense device and should not be part of any other LAGG.
required: true
type: list
elements: str
proto:
description:
- The LAGG protocol to use. Available protocols:
- C(none): Disables any traffic on this LAGG without disabling the interface itself.
- C(lacp): Uses the IEEE 802.3ad Link Aggregation Control Protocol (LACP) and the Marker Protocol.
Negotiates aggregable links with the peer into one or more Link Aggregated Groups.
- C(failover): Sends and receives traffic through the master port only. If the master port
becomes unavailable, the next active port is used.
- C(loadbalance): Balances outgoing traffic across active ports based on hashed protocol header
information, and accepts incoming traffic from any active port. (Static setup, no dynamic negotiation.)
- C(roundrobin): Distributes outgoing traffic in a round-robin fashion through all active ports,
and accepts incoming traffic from any active port.
choices: ["none", "lacp", "failover", "loadbalance", "roundrobin"]
default: "lacp"
type: str
lacptimeout:
description:
- LACP timeout mode (only relevant if proto = lacp).
- C(fast) or C(slow). Typically defaults to fast on pfSense.
choices: ["fast", "slow"]
default: "fast"
type: str
lagghash:
description:
- Hash method for load distribution.
- Possible options are:
- (l2,l3,l4) layer 2/3/4 (default)
- (l2) layer 2 (MAC addresses)
- (l3) layer 3 (IP addresses)
- (l4) layer 4 (Port numbers)
- (l2,l3) layer 2/3 (MAC + IP)
- (l3,l4) layer 3/4 (IP + Port)
- (l2,l4) layer 2/4 (MAC + Port)
default: "l2,l3,l4"
type: str
descr:
description:
- Description for the LAGG interface, for reference only (not parsed except for display).
default: ""
type: str
state:
description:
- Whether the LAGG should be present (created/updated) or absent (removed).
choices: ["present", "absent"]
default: "present"
type: str
"""

EXAMPLES = r"""
- name: Create a LAGG (lacp) with igb0 and igb1
pfsense_lagg:
laggif: lagg1
members:
- igb0
- igb1
proto: lacp
lacptimeout: fast
lagghash: "l2,l3,l4"
descr: "WAN-LACP"
state: present

- name: Remove that LAGG
pfsense_lagg:
laggif: lagg1
members:
- igb0
- igb1
state: absent
"""

RETURN = r"""
commands:
description: A list of pseudo-CLI commands that the module generated (for debugging purposes).
returned: always
type: list
sample:
- "create lagg 'lagg1', proto='lacp', members='igb0,igb1'"
- "update lagg 'lagg1', set proto='failover'"
- "delete lagg 'lagg1'"
"""

from ansible.module_utils.basic import AnsibleModule
from ansible_collections.pfsensible.core.plugins.module_utils.lagg import (
PFSenseLaggModule,
LAGG_ARGUMENT_SPEC
)

def main():
module = AnsibleModule(
argument_spec=LAGG_ARGUMENT_SPEC,
supports_check_mode=True
)

pfmodule = PFSenseLaggModule(module)
pfmodule.run(module.params)
pfmodule.commit_changes()


if __name__ == '__main__':
main()
Loading