-
Notifications
You must be signed in to change notification settings - Fork 70
Open
Description
I like to have a LAAG configuration option therefore I start to create my own modul and i like to share with you guys.
maybe it is good enought for copy paste into your next Version or it need some adjustment.
modules/pfsense_lagg.py
#!/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()modules_utils/lagg.py
# -*- 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 valsplease let me know if there are some questions or unclear.
And if you can give me an feedback it will be great.
Thanks and Regards
Diogo Ferrario
Metadata
Metadata
Assignees
Labels
No labels