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: add nginx upstream server module #65

Merged
merged 19 commits into from
Jun 20, 2024
Merged
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
118 changes: 118 additions & 0 deletions docs/source/modules/nginx.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
.. _modules_nginx:

.. include:: ../_include/head.rst

=====
Nginx
=====

**STATE**: unstable

**TESTS**: `nginx_upstream_server <https://github.com/ansibleguy/collection_opnsense/blob/latest/tests/nginx_upstream_server.yml>`_

**API Docs**: `Plugins - Nginx <https://docs.opnsense.org/development/api/plugins/nginx.html>`_

**Service Docs**: `Nginx <https://docs.opnsense.org/manual/how-tos/nginx.html>`_

Contribution
************

Thanks to `@atammy-narmi <https://github.com/atammy-narmi>`_ for developing these modules!

Prerequisites
*************

You need to install the following plugin:

.. code-block:: bash

os-nginx

You can also install it using the :ref:`ansibleguy.opnsense.package <modules_package>` module.


Definition
**********

.. include:: ../_include/param_basic.rst

ansibleguy.opnsense.nginx_general
=========================================

.. csv-table:: Definition
:header: "Parameter", "Type", "Required", "Default", "Aliases", "Comment"
:widths: 15 10 10 10 10 45

"enabled","boolean","false","true","\-","Enable configured services."
"ban_ttl","integer","false","0","\-","Set autoblock lifetime in minutes. Set to 0 for infinite."


ansibleguy.opnsense.nginx_upstream_server
=========================================

.. csv-table:: Definition
:header: "Parameter", "Type", "Required", "Default", "Aliases", "Comment"
:widths: 15 10 10 10 10 45

"description","string","true","\-","name","\-"
"server","string","true","\-","\-","\-"
"port","integer","true","\-","\-","\-"
"priority","integer","true","\-","\-","\-"
"max_conns","integer","false","\-","\-","\-"
"max_fails","integer","false","\-","\-","\-"
"fail_timeout","integer","false","\-","\-","\-"
"no_use","string","false","\-","\-","Choice of empty, 'down' or 'backup'."
"state","string","false","present","\-","Choice of 'present' or 'absent'."
"reload","boolean","false","true","\-", .. include:: ../_include/param_reload.rst


Usage
*****

Enabling the nginx configured services.

Examples
********

ansibleguy.opnsense.nginx_upstream_server
=========================================

.. code-block:: yaml

- hosts: localhost
gather_facts: false
module_defaults:
group/ansibleguy.opnsense.all:
firewall: 'opnsense.template.ansibleguy.net'
api_credential_file: '/home/guy/.secret/opn.key'

ansibleguy.opnsense.list:
target: 'nginx_upstream_server'

tasks:
- name: Add an upstream server
ansibleguy.opnsense.nginx_upstream_server:
name: 'upstream1'
server: '192.168.1.1'
port: 80
priority: 1
max_conns: 100
max_fails: 50
fail_timeout: 10
no_use: 'down'
# state: 'present'
# reload: true

- name: Changing the server
ansibleguy.opnsense.nginx_upstream_server:
name: 'upstream1'
server: '192.168.1.100'

- name: Listing upstream servers
ansibleguy.opnsense.list:
# target: 'nginx_upstream_server'
register: existing_servers

- name: Printing
ansible.builtin.debug:
var: existing_servers.data
4 changes: 4 additions & 0 deletions meta/runtime.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ action_groups:
- ansibleguy.opnsense.webproxy_pac_proxy
- ansibleguy.opnsense.webproxy_pac_match
- ansibleguy.opnsense.webproxy_pac_rule
nginx:
- ansibleguy.opnsense.nginx_general
- ansibleguy.opnsense.nginx_upstream_server
route:
- ansibleguy.opnsense.route
nat:
Expand Down Expand Up @@ -128,6 +131,7 @@ action_groups:
- ansibleguy.opnsense.frr
- ansibleguy.opnsense.bind
- ansibleguy.opnsense.webproxy
- ansibleguy.opnsense.nginx
- ansibleguy.opnsense.route
- ansibleguy.opnsense.nat
- ansibleguy.opnsense.system
Expand Down
3 changes: 2 additions & 1 deletion plugins/module_utils/helper/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,8 @@ def validate_int_fields(

for field, valid in field_minmax.items():
try:
if int(data[field]) < valid['min'] or int(data[field]) > valid['max']:
if ('min' in valid and int(data[field]) < valid['min']) or \
('max' in valid and int(data[field]) > valid['max']):
error_func(
f"Value of field '{field}' is not valid - "
f"Must be between {valid['min']} and {valid['max']}!"
Expand Down
33 changes: 33 additions & 0 deletions plugins/module_utils/main/nginx_general.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
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):
FIELD_ID = 'name'
CMDS = {
'set': 'set',
'search': 'get',
}
API_KEY_PATH = 'nginx.general'
API_KEY_PATH_REQ = API_KEY_PATH
API_MOD = 'nginx'
API_CONT = 'settings'
API_CONT_REL = 'service'
API_CMD_REL = 'reconfigure'
FIELDS_CHANGE = [
'enabled', 'ban_ttl',
]
FIELDS_ALL = FIELDS_CHANGE
INT_VALIDATIONS = {
'ban_ttl': {'min': 0},
}
FIELDS_TYPING = {
'bool': ['enabled'],
'int': ['ban_ttl'],
}

def __init__(self, module: AnsibleModule, result: dict, session: Session = None):
GeneralModule.__init__(self=self, m=module, r=result, s=session)
52 changes: 52 additions & 0 deletions plugins/module_utils/main/nginx_upstream_server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
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 BaseModule
from ansible_collections.ansibleguy.opnsense.plugins.module_utils.helper.main import is_unset


class UpstreamServer(BaseModule):
FIELD_ID = 'description'
CMDS = {
'add': 'addupstreamserver',
'del': 'delupstreamserver',
'detail': 'getupstreamserver',
'search': 'searchupstreamserver',
'set': 'setupstreamserver',
}
API_KEY_PATH = 'upstream_server'
API_MOD = 'nginx'
API_CONT = 'settings'
API_CONT_REL = 'service'
API_CMD_REL = 'reconfigure'
FIELDS_CHANGE = [
'description', 'server', 'port', 'priority',
'max_conns', 'max_fails', 'fail_timeout', 'no_use'
]
FIELDS_ALL = FIELDS_CHANGE
FIELDS_TYPING = {
'select': ['no_use'],
}
INT_VALIDATIONS = {
'priority': {'min': 0, 'max': 1000000000},
'port': {'min': 1, 'max': 65535},
}
FIELDS_IGNORE = []
EXIST_ATTR = 'upstream_server'

def __init__(self, module: AnsibleModule, result: dict, session: Session = None):
BaseModule.__init__(self=self, m=module, r=result, s=session)
self.upstream_server = {}

def check(self) -> None:
if self.p['state'] == 'present':
if is_unset(self.p['port']) or is_unset(self.p['priority']) or is_unset(self.p['server']):
self.m.fail_json(
"You need to provide a 'port', 'server' and 'priority' to create a server!"
)

self._base_check()

def update(self) -> None:
self.b.update(enable_switch=False)
6 changes: 5 additions & 1 deletion plugins/modules/list.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
'webproxy_traffic', 'webproxy_remote_acl', 'webproxy_pac_proxy', 'webproxy_pac_match', 'webproxy_pac_rule',
'cron', 'unbound_dot', 'ipsec_cert', 'ipsec_psk', 'source_nat', 'frr_bgp_prefix_list', 'frr_bgp_community_list',
'frr_bgp_as_path', 'frr_bgp_route_map', 'frr_ospf_prefix_list', 'frr_ospf_route_map', 'webproxy_forward',
'webproxy_acl', 'webproxy_icap', 'webproxy_auth', 'ipsec_connection', 'ipsec_pool',
'webproxy_acl', 'webproxy_icap', 'webproxy_auth', 'nginx_upstream_server', 'ipsec_connection', 'ipsec_pool',
'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',
Expand Down Expand Up @@ -305,6 +305,10 @@ def run_module():
from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.webproxy_pac_rule import \
Rule as Target_Obj

elif target == 'nginx_upstream_server':
from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.nginx_upstream_server import \
UpstreamServer as Target_Obj

elif target == 'ipsec_connection':
from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.ipsec_connection import \
Connection as Target_Obj
Expand Down
60 changes: 60 additions & 0 deletions plugins/modules/nginx_general.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#!/usr/bin/env python3

# Copyright: (C) 2023, AnsibleGuy <guy@ansibleguy.net>
# 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.main import diff_remove_empty
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.nginx_general import General


except MODULE_EXCEPTIONS:
module_dependency_error()

# DOCUMENTATION = 'https://opnsense.ansibleguy.net/en/latest/modules/nginx.html'
# EXAMPLES = 'https://opnsense.ansibleguy.net/en/latest/modules/nginx.html'


def run_module():
module_args = dict(
ban_ttl=dict(type='int', required=False, default=0),
**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))
result['diff'] = diff_remove_empty(result['diff'])

module.exit_json(**result)


def main():
run_module()


if __name__ == '__main__':
main()
69 changes: 69 additions & 0 deletions plugins/modules/nginx_upstream_server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#!/usr/bin/env python3

# Copyright: (C) 2023, AnsibleGuy <guy@ansibleguy.net>
# GNU General Public License v3.0+ (see https://www.gnu.org/licenses/gpl-3.0.txt)

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.helper.main import \
diff_remove_empty
from ansible_collections.ansibleguy.opnsense.plugins.module_utils.defaults.main import \
OPN_MOD_ARGS, STATE_ONLY_MOD_ARG, RELOAD_MOD_ARG_DEF_FALSE
from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.nginx_upstream_server import UpstreamServer

except MODULE_EXCEPTIONS:
module_dependency_error()


# DOCUMENTATION = 'https://opnsense.ansibleguy.net/en/latest/modules/nginx.html'
# EXAMPLES = 'https://opnsense.ansibleguy.net/en/latest/modules/nginx.html'


def run_module():
module_args = dict(
description=dict(type='str', alias=['name'], required=True, aliases=['name']),
server=dict(type='str', required=False),
port=dict(type='int', required=False),
priority=dict(type='int', required=False),
max_conns=dict(type='int', required=False),
max_fails=dict(type='int', required=False),
fail_timeout=dict(type='int', required=False),
no_use=dict(
type='str', required=False, choices=['', 'down', 'backup'], default='',
),
**RELOAD_MOD_ARG_DEF_FALSE,
**STATE_ONLY_MOD_ARG,
**OPN_MOD_ARGS,
)

result = dict(
changed=False,
diff={
'before': {},
'after': {},
}
)

module = AnsibleModule(
argument_spec=module_args,
supports_check_mode=True,
)


module_wrapper(UpstreamServer(module=module, result=result))

result['diff'] = diff_remove_empty(result['diff'])
module.exit_json(**result)


def main():
run_module()


if __name__ == '__main__':
main()
2 changes: 2 additions & 0 deletions scripts/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ run_test 'openvpn_server' 1
run_test 'openvpn_client_override' 0 # check mode => dependency on server-entry
# run_test 'openvpn_client_template' 1
# run_test 'openvpn_client_export' 1
run_test 'nginx_general' 1
run_test 'nginx_upstream_server' 1
run_test 'system' 1
run_test 'package' 1

Expand Down
Loading
Loading