diff --git a/README.md b/README.md index 5ca98d4..ca892b0 100644 --- a/README.md +++ b/README.md @@ -190,6 +190,7 @@ not implemented => development => [testing](https://github.com/ansibleguy/collec | **DHCP Relay** | ansibleguy.opnsense.dhcrelay | [Docs](https://opnsense.ansibleguy.net/en/latest/modules/dhcrelay_relay.html) | unstable | | **DHCP Relay** | ansibleguy.opnsense.dhcrelay_destination | [Docs](https://opnsense.ansibleguy.net/en/latest/modules/dhcrelay_destination.html) | unstable | | **DHCP Reservation** | ansibleguy.opnsense.dhcp_reservation | [Docs](https://opnsense.ansibleguy.net/en/latest/modules/dhcp.html) | unstable | +| **DHCP Controlagent** | ansibleguy.opnsense.dhcp_controlagent | [Docs](https://opnsense.ansibleguy.net/en/latest/modules/dhcp.html) | unstable | ### Roadmap diff --git a/docs/source/modules/dhcp.rst b/docs/source/modules/dhcp.rst index 9b69760..166f60b 100644 --- a/docs/source/modules/dhcp.rst +++ b/docs/source/modules/dhcp.rst @@ -8,7 +8,8 @@ DHCP **STATE**: unstable -**TESTS**: `Playbook `_ +**TESTS**: `Reservation `_ | +`ControlAgent `_ **API Docs**: `Core - KEA `_ @@ -17,7 +18,7 @@ DHCP Contribution ************ -Thanks to `@KalleDK `_ for helping with the Reservation module! +Thanks to `@KalleDK `_ for developing these module! ---- @@ -40,6 +41,18 @@ ansibleguy.opnsense.dhcp_reservation "description","string","false","","\-","Optional description" "reload","boolean","false","true","\-", .. include:: ../_include/param_reload.rst +ansibleguy.opnsense.dhcp_controlagent +===================================== + +.. csv-table:: Definition + :header: "Parameter", "Type", "Required", "Default", "Aliases", "Comment" + :widths: 15 10 10 10 10 45 + + "enabled","boolean","false","true","\-","Enable or disable the control agent" + "http_host","string","false","127.0.0.1","","Address on which the RESTful interface should be available" + "http_port","int","false","8000","","MAC/Ether address of the client in question" + "reload","boolean","false","true","\-", .. include:: ../_include/param_reload.rst + ---- Examples @@ -91,3 +104,31 @@ ansibleguy.opnsense.dhcp_reservation - name: Show existing reservations ansible.builtin.debug: var: existing_entries.data + +---- + +ansibleguy.opnsense.dhcp_controlagent +===================================== + +.. code-block:: yaml + + - hosts: localhost + gather_facts: no + module_defaults: + group/ansibleguy.opnsense.all: + firewall: 'opnsense.template.ansibleguy.net' + api_credential_file: '/home/guy/.secret/opn.key' + + tasks: + - name: Example + ansibleguy.opnsense.dhcp_controlagent: + enabled: true + http_host: 127.0.0.1 + http_port: 8000 + # reload: true + # debug: false + + - name: Stopping + ansibleguy.opnsense.dhcp_controlagent: + enabled: false + reload: true diff --git a/meta/runtime.yml b/meta/runtime.yml index 1c2ea29..807d928 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -127,6 +127,7 @@ action_groups: - ansibleguy.opnsense.dhcrelay_relay dhcp: - ansibleguy.opnsense.dhcp_reservation + - ansibleguy.opnsense.dhcp_controlagent all: - metadata: extend_group: diff --git a/plugins/module_utils/main/dhcp_controlagent.py b/plugins/module_utils/main/dhcp_controlagent.py new file mode 100644 index 0000000..556d07e --- /dev/null +++ b/plugins/module_utils/main/dhcp_controlagent.py @@ -0,0 +1,44 @@ +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.helper.main import \ + is_ip, validate_port +from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.cls import GeneralModule + + +class ControlAgent(GeneralModule): + FIELD_ID = 'ip' + CMDS = { + 'set': 'set', + 'search': 'get', + } + API_KEY_PATH = 'ctrlagent.general' + API_KEY_PATH_REQ = API_KEY_PATH + API_MOD = 'kea' + API_CONT = 'ctrl_agent' + API_CONT_REL = 'service' + API_CMD_REL = 'reconfigure' + FIELDS_CHANGE = [ + 'enabled', 'http_host', 'http_port' + ] + FIELDS_ALL = [*FIELDS_CHANGE] + FIELDS_TYPING = { + 'bool': ['enabled'], + 'int': ['http_port'], + } + INT_VALIDATIONS = { + 'http_port': {'min': 1, 'max': 65535}, + } + + def __init__(self, module: AnsibleModule, result: dict, session: Session = None): + GeneralModule.__init__(self=self, m=module, r=result, s=session) + + def check(self) -> None: + if not validate_port(module=self.m, port=self.p['http_port']): + self.m.fail_json('The provided port is invalid!') + + if not is_ip(self.p['http_host']): + self.m.fail_json('The provided IP is invalid!') + + super().check() diff --git a/plugins/modules/dhcp_controlagent.py b/plugins/modules/dhcp_controlagent.py new file mode 100644 index 0000000..bdad487 --- /dev/null +++ b/plugins/modules/dhcp_controlagent.py @@ -0,0 +1,66 @@ +#!/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/wireguard.html + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.handler import \ + module_dependency_error, MODULE_EXCEPTIONS + + +try: + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.helper.wrapper import module_wrapper + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.defaults.main import \ + OPN_MOD_ARGS, EN_ONLY_MOD_ARG, RELOAD_MOD_ARG + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.dhcp_controlagent import ControlAgent + +except MODULE_EXCEPTIONS: + module_dependency_error() + + +# DOCUMENTATION = 'https://opnsense.ansibleguy.net/en/latest/modules/dhcp.html' +# EXAMPLES = 'https://opnsense.ansibleguy.net/en/latest/modules/dhcp.html' + + +def run_module(): + module_args = dict( + http_port=dict( + type='int', required=False, default=8000, + description='Portnumber to use for the RESTful interface' + ), + http_host=dict( + type='str', required=False, default='127.0.0.1', aliases=['host'], + description='Address on which the RESTful interface should be available' + ), + **EN_ONLY_MOD_ARG, + **OPN_MOD_ARGS, + **RELOAD_MOD_ARG, + ) + + result = dict( + changed=False, + diff={ + 'before': {}, + 'after': {}, + } + ) + + module = AnsibleModule( + argument_spec=module_args, + supports_check_mode=True, + ) + + module_wrapper(ControlAgent(module=module, result=result)) + module.exit_json(**result) + + +def main(): + run_module() + + +if __name__ == '__main__': + main() diff --git a/scripts/test.sh b/scripts/test.sh index 640c342..ca91ff1 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -152,6 +152,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_controlagent' 1 run_test 'dhcp_reservation' 1 run_test 'system' 1 run_test 'package' 1 diff --git a/tests/cleanup.yml b/tests/cleanup.yml index f11ccc2..b936ad9 100644 --- a/tests/cleanup.yml +++ b/tests/cleanup.yml @@ -669,3 +669,9 @@ loop: - '192.168.69.76' - '192.168.69.86' + + - name: Cleanup DHCP-Controlagent + ansibleguy.opnsense.dhcp_controlagent: + enabled: false + http_host: '127.0.0.1' + http_port: 8000 diff --git a/tests/dhcp_controlagent.yml b/tests/dhcp_controlagent.yml new file mode 100644 index 0000000..f6a77ef --- /dev/null +++ b/tests/dhcp_controlagent.yml @@ -0,0 +1,69 @@ +--- + +- name: Testing DHCP-Controlagent + 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 + + tasks: + - name: Configuring + ansibleguy.opnsense.dhcp_controlagent: + enabled: true + register: opn1 + failed_when: > + opn1.failed or + not opn1.changed + + - name: Changing + ansibleguy.opnsense.dhcp_controlagent: + enabled: true + http_port: 8082 + http_host: '192.168.0.55' + register: opn5 + failed_when: > + opn5.failed or + not opn5.changed + + - name: Disabling 1 + ansibleguy.opnsense.dhcp_controlagent: + enabled: false + http_port: 8082 + http_host: '192.168.0.55' + register: opn2 + failed_when: > + opn2.failed or + not opn2.changed + when: not ansible_check_mode + + - name: Disabling 1 - nothing changed + ansibleguy.opnsense.dhcp_controlagent: + enabled: false + http_port: 8082 + http_host: '192.168.0.55' + register: opn3 + failed_when: > + opn3.failed or + opn3.changed + when: not ansible_check_mode + + - name: Enabling 1 + ansibleguy.opnsense.dhcp_controlagent: + enabled: true + http_port: 8082 + http_host: '192.168.0.55' + register: opn4 + failed_when: > + opn4.failed or + not opn4.changed + when: not ansible_check_mode + + - name: Cleanup + ansibleguy.opnsense.dhcp_controlagent: + enabled: false + http_host: '127.0.0.1' + http_port: 8000 + when: not ansible_check_mode