diff --git a/README.md b/README.md index 44f45db..c2413de 100644 --- a/README.md +++ b/README.md @@ -190,6 +190,7 @@ not implemented => development => [testing](https://github.com/ansibleguy/collec | **Nginx** | ansibleguy.opnsense.nginx_upstream_server | [Docs](https://opnsense.ansibleguy.net/modules/nginx.html#ansibleguy-opnsense-nginx-upstream-server) | unstable | | **DHCP Relay** | ansibleguy.opnsense.dhcrelay | [Docs](https://opnsense.ansibleguy.net/modules/dhcrelay_relay.html) | unstable | | **DHCP Relay** | ansibleguy.opnsense.dhcrelay_destination | [Docs](https://opnsense.ansibleguy.net/modules/dhcrelay_destination.html) | unstable | +| **DHCP** | ansibleguy.opnsense.dhcp_general | [Docs](https://opnsense.ansibleguy.net/modules/dhcp.html) | unstable | | **DHCP Reservation** | ansibleguy.opnsense.dhcp_reservation | [Docs](https://opnsense.ansibleguy.net/modules/dhcp.html) | unstable | | **DHCP Controlagent** | ansibleguy.opnsense.dhcp_controlagent | [Docs](https://opnsense.ansibleguy.net/modules/dhcp.html) | unstable | | **ACME (Certificates)** | ansibleguy.opnsense.acme_account | [Docs](https://opnsense.ansibleguy.net/modules/acmeclient.html) | unstable | diff --git a/docs/source/modules/dhcp.rst b/docs/source/modules/dhcp.rst index afec40a..47812dd 100644 --- a/docs/source/modules/dhcp.rst +++ b/docs/source/modules/dhcp.rst @@ -53,6 +53,18 @@ ansibleguy.opnsense.dhcp_controlagent "http_port","int","false","8000","","MAC/Ether address of the client in question" "reload","boolean","false","true","\-", .. include:: ../_include/param_reload.rst +ansibleguy.opnsense.dhcp_general +================================ + +.. csv-table:: Definition + :header: "Parameter", "Type", "Required", "Default", "Aliases", "Comment" + :widths: 15 10 10 10 10 45 + + "interfaces", "list", "false", "[]", "ints", "Comma separated list of network interfaces to listen on for DHCP requests" + "socket_type", "string", "false", "raw", "dhcp_socket_type", "Socket type used for DHCP communication. Allowed values: 'raw', 'udp'" + "fw_rules", "boolean", "false", "true", "fwrules, rules", "Automatically add a basic set of firewall rules to allow DHCP traffic" + "lifetime", "int", "false", "4000", "valid_lifetime", "Defines how long the addresses (leases) given out by the server are valid (in seconds)" + ---- Examples @@ -132,3 +144,23 @@ ansibleguy.opnsense.dhcp_controlagent ansibleguy.opnsense.dhcp_controlagent: enabled: false reload: true + +---- + +ansibleguy.opnsense.dhcp_general +================================ + +.. code-block:: yaml + + - hosts: localhost + gather_facts: no + module_defaults: + group/ansibleguy.opnsense.all: + firewall: 'opnsense.template.ansibleguy.net' + api_credentials_file: '/home/guy/.secret/opn.key' + + tasks: + - name: Listen to network interfaces + ansibleguy.opnsense.dhcp_general: + enabled: true + interfaces: 'lan,opt1,opt2,vlan0.10' diff --git a/meta/runtime.yml b/meta/runtime.yml index 42762b2..2263e60 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -127,6 +127,7 @@ action_groups: dhcp: - ansibleguy.opnsense.dhcp_reservation - ansibleguy.opnsense.dhcp_controlagent + - ansibleguy.opnsense.dhcp_general acme: - ansibleguy.opnsense.acme_general - ansibleguy.opnsense.acme_account diff --git a/plugins/module_utils/main/dhcp_general.py b/plugins/module_utils/main/dhcp_general.py new file mode 100644 index 0000000..666b289 --- /dev/null +++ b/plugins/module_utils/main/dhcp_general.py @@ -0,0 +1,38 @@ +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.api import \ + Session +from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.cls import GeneralModule + + +class General(GeneralModule): + CMDS = { + 'set': 'set', + 'search': 'get' + } + API_KEY_PATH = 'dhcpv4.general' + API_KEY_PATH_REQ = API_KEY_PATH + API_MOD = 'kea' + API_CONT = 'dhcpv4' + API_CONT_REL = 'service' + FIELDS_CHANGE = [ + 'enabled', 'interfaces', 'socket_type', 'fw_rules', 'lifetime' + ] + FIELDS_ALL = FIELDS_CHANGE + FIELDS_TRANSLATE = { + 'lifetime': 'valid_lifetime', + 'fw_rules': 'fwrules', + 'socket_type': 'dhcp_socket_type', + } + FIELDS_TYPING = { + 'bool': ['enabled', 'fw_rules'], + 'int': ['lifetime'], + 'list': ['interfaces'], + 'select': ['socket_type'], + } + INT_VALIDATIONS = { + 'lifetime': {'min': 0}, + } + + def __init__(self, module: AnsibleModule, result: dict, session: Session = None): + GeneralModule.__init__(self=self, m=module, r=result, s=session) diff --git a/plugins/modules/dhcp_general.py b/plugins/modules/dhcp_general.py new file mode 100644 index 0000000..0aa2276 --- /dev/null +++ b/plugins/modules/dhcp_general.py @@ -0,0 +1,74 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (C) 2024, AnsibleGuy +# GNU General Public License v3.0+ (see https://www.gnu.org/licenses/gpl-3.0.txt) + +# see: https://docs.opnsense.org/development/api/plugins/nginx.html + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.handler import \ + module_dependency_error, MODULE_EXCEPTIONS +from ansible_collections.ansibleguy.opnsense.plugins.module_utils.helper.wrapper import module_wrapper + +try: + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.defaults.main import \ + EN_ONLY_MOD_ARG, OPN_MOD_ARGS, RELOAD_MOD_ARG + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.dhcp_general import General + + +except MODULE_EXCEPTIONS: + module_dependency_error() + +# DOCUMENTATION = 'https://opnsense.ansibleguy.net/modules/dhcp.html' +# EXAMPLES = 'https://opnsense.ansibleguy.net/modules/dhcp.html' + + +def run_module(): + module_args = dict( + interfaces=dict( + type='list', elements='str', required=False, default=[], aliases=['ints'], + description='Comma separated list of network interfaces to listen on for DHCP requests' + ), + socket_type=dict( + type='str', required=False, default='raw', choices=['raw', 'udp'], aliases=['dhcp_socket_type'], + description='Socket type used for DHCP communication', + ), + fw_rules=dict( + type='bool', required=False, default=True, aliases=['fwrules', 'rules'], + description='Automatically add a basic set of firewall rules to allow dhcp traffic, ' + 'more fine grained controls can be offered manually when disabling this option', + ), + lifetime=dict( + type='int', required=False, default=4000, aliases=['valid_lifetime'], + description='Defines how long the addresses (leases) given out by the server are valid (in seconds)', + ), + **EN_ONLY_MOD_ARG, + **RELOAD_MOD_ARG, + **OPN_MOD_ARGS, + ) + + module = AnsibleModule( + argument_spec=module_args, + supports_check_mode=True, + ) + + result = dict( + changed=False, + diff={ + 'before': {}, + 'after': {}, + } + ) + + module_wrapper(General(module=module, result=result)) + module.exit_json(**result) + + +def main(): + run_module() + + +if __name__ == '__main__': + main() diff --git a/plugins/modules/list.py b/plugins/modules/list.py index 1aa02c5..5a2dc74 100644 --- a/plugins/modules/list.py +++ b/plugins/modules/list.py @@ -36,8 +36,8 @@ 'ipsec_child', 'ipsec_vti', 'ipsec_auth_local', 'ipsec_auth_remote', 'frr_general', 'unbound_general', 'unbound_acl', 'ids_general', 'ids_policy', 'ids_rule', 'ids_ruleset', 'ids_user_rule', 'ids_policy_rule', 'openvpn_instance', 'openvpn_static_key', 'openvpn_client_override', 'dhcrelay_destination', 'dhcrelay_relay', - 'interface_lagg', 'interface_loopback', 'unbound_dnsbl', 'dhcp_reservation', 'acme_general', 'acme_account', - 'acme_validation', 'acme_action', 'acme_certificate', + 'interface_lagg', 'interface_loopback', 'unbound_dnsbl', 'dhcp_reservation', 'dhcp_general', 'acme_general', + 'acme_account', 'acme_validation', 'acme_action', 'acme_certificate', ] @@ -419,6 +419,10 @@ def run_module(): from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.acme_certificate import \ Certificate as Target_Obj + elif target == 'dhcp_general': + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.dhcp_general import \ + General as Target_Obj + except AttributeError: module_dependency_error() diff --git a/scripts/test.sh b/scripts/test.sh index 64a98f9..1421f5b 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -153,6 +153,7 @@ run_test 'nginx_general' 1 run_test 'nginx_upstream_server' 1 run_test 'dhcrelay_destination' 1 run_test 'dhcrelay_relay' 1 +run_test 'dhcp_general' 1 run_test 'dhcp_controlagent' 1 run_test 'dhcp_reservation' 1 run_test 'system' 1 diff --git a/tests/1_cleanup.yml b/tests/1_cleanup.yml index b664c03..3fdc80f 100644 --- a/tests/1_cleanup.yml +++ b/tests/1_cleanup.yml @@ -717,3 +717,7 @@ - 'ANSIBLE_TEST_1_9' - 'ANSIBLE_TEST_1_10' - 'ANSIBLE_TEST_DUMMY_1_1' + + - name: Cleanup DHCP Settings + ansibleguy.opnsense.dhcp_general: + enabled: false diff --git a/tests/dhcp_general.yml b/tests/dhcp_general.yml new file mode 100644 index 0000000..2210c8f --- /dev/null +++ b/tests/dhcp_general.yml @@ -0,0 +1,64 @@ +--- + +- name: Testing DHCP Setting + hosts: localhost + gather_facts: no + module_defaults: + group/ansibleguy.opnsense.all: + firewall: "{{ lookup('ansible.builtin.env', 'TEST_FIREWALL') }}" + api_credential_file: "{{ lookup('ansible.builtin.env', 'TEST_API_KEY') }}" + ssl_verify: false + + ansibleguy.opnsense.list: + target: 'dhcp_general' + + tasks: + - name: Listing + ansibleguy.opnsense.list: + register: opn_pre1 + failed_when: > + opn_pre1.failed or + 'data' not in opn_pre1 + + - name: Configuring - failing because of invalid lifetime + ansibleguy.opnsense.dhcp_general: + lifetime: -1 + register: opn_fail1 + failed_when: not opn_fail1.failed + + - name: Configuring + ansibleguy.opnsense.dhcp_general: + enabled: true + interfaces: ['opt1'] + register: opn1 + failed_when: > + opn1.failed or + not opn1.changed + + - name: Changing + ansibleguy.opnsense.dhcp_general: + enabled: true + interfaces: ['opt1'] + fw_rules: false + lifetime: 5000 + register: opn2 + failed_when: > + opn2.failed or + not opn2.changed + when: not ansible_check_mode + + - name: Nothing changed + ansibleguy.opnsense.dhcp_general: + enabled: true + interfaces: ['opt1'] + fw_rules: false + lifetime: 5000 + register: opn3 + failed_when: > + opn3.failed or + opn3.changed + when: not ansible_check_mode + + - name: Cleanup + ansibleguy.opnsense.dhcp_general: + enabled: false