Skip to content

Commit 096bf76

Browse files
authored
Support for Nut #324 (#384)
* Add nut & nut_diagnostics modules
1 parent 87f864f commit 096bf76

File tree

14 files changed

+843
-2
lines changed

14 files changed

+843
-2
lines changed

docs/meta/sitemap.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,6 @@
5959
<url><loc>https://ansible-opnsense.oxl.app/modules/unbound_host_alias.html</loc></url>
6060
<url><loc>https://ansible-opnsense.oxl.app/modules/webproxy.html</loc></url>
6161
<url><loc>https://ansible-opnsense.oxl.app/modules/wireguard.html</loc></url>
62-
</urlset>
62+
<url><loc>https://ansible-opnsense.oxl.app/modules/nut.html</loc></url>
63+
<url><loc>https://ansible-opnsense.oxl.app/modules/nut_diagnostics.html</loc></url>
64+
</urlset>

docs/source/modules/nut.rst

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
.. _modules_nut:
2+
3+
.. include:: ../_include/head.rst
4+
5+
=======================
6+
NUT - Network UPS Tools
7+
=======================
8+
9+
**State:** Unstable
10+
11+
**Tests:** `nut.yml <https://github.com/O-X-L/ansible_opnsense/blob/latest/tests/nut.yml>`_
12+
13+
**API Docs**: `Plugins - NUT <https://docs.opnsense.org/development/api/plugins/nut.html>`_
14+
15+
**Service Docs**: `NUT - Network UPS Tools <https://docs.opnsense.org/manual/how-tos/nut.html>`_
16+
17+
Contribution
18+
************
19+
20+
Author: `@wmatusiak <https://github.com/wmatusiak>`_
21+
22+
If you encounter any issues or have suggestions for improvements, please feel free to contribute to the project.
23+
24+
----
25+
26+
Prerequisites
27+
*************
28+
29+
You need to install the NUT plugin:
30+
31+
```
32+
os-nut
33+
```
34+
You can also install it using the :ref:`oxlorg.opnsense.package <modules_package>` module.
35+
36+
----
37+
38+
Functions
39+
*********
40+
41+
This module allows you to configure the Network UPS Tools on your OPNsense firewall.
42+
43+
NUT is a service that monitors UPS device and shutdown your firewall if the battery level is low.
44+
45+
Parameters
46+
##########
47+
48+
.. csv-table:: Definition
49+
:header: "Parameter", "Type", "Required", "Default", "Aliases", "Comment"
50+
:widths: 15 10 10 10 10 45
51+
52+
"enabled","boolean","false","true","\-","Enable/disable NUT service"
53+
"mode","choice","false","standalone","\-","Service mode. One of: 'standalone','netclient'."
54+
"name","string","false","UPSName","\-","Name for your UPS."
55+
"listen","list","false","['127.0.0.1','::1']","\-","Addresses this service listen on."
56+
"admin_password","string","false","Password","\-","Password for admin user 'admin'."
57+
"monitor_password","string","false","Password","\-","Password for monitoring user 'monuser'."
58+
"usbhid_enable","boolean","false","false","\-","Enable the USBHID driver."
59+
"usbhid_args","list","false","['port=auto']","\-","Extra arguments for USBHID driver, e.g. 'port=auto'."
60+
"apcsmart_enable","boolean","false","false","\-","Enable the APCSMART driver."
61+
"apcsmart_args","list","false","['port=auto']","\-","Extra arguments for APCSMART driver, e.g. 'port=auto'."
62+
"apcupsd_enable","boolean","false","false","\-","Enable the APCUPSD controlled devices driver."
63+
"apcupsd_host","string","false","localhost","\-","Hostname or ip of the remote apcupsd server."
64+
"apcupsd_port","int","false","\-","\-","Port of the remote apcupsd server (optional)."
65+
"bcmxcpusb_enable","boolean","false","false","\-","Enable the PowerWare BCMXCPUSB driver."
66+
"bcmxcpusb_args","list","false","['port=auto']","\-","Extra arguments for WoerWare BCMXCPUSB driver, e.g. 'port=auto'."
67+
"blazerusb_enable","boolean","false","false","\-","Enable the BlazerUSB driver."
68+
"blazerusb_args","list","false","['port=auto']","\-","Extra arguments for BlazerUSB driver, e.g. 'port=auto'."
69+
"blazerser_enable","boolean","false","false","\-","Enable the BlazerSerial driver. Please be aware that this driver needs to run nut-tools as root."
70+
"blazerser_args","list","false","['port=auto']","\-","Extra arguments for BlazerSerial driver, e.g. 'port=auto'."
71+
"netclient_enable","boolean","false","false","\-","Enable the Netclient driver"
72+
"netclient_address","string","false","\-","\-","IP address of the remote NUT server."
73+
"netclient_port","int","false","3493","\-","TCP port of the remote NUT server."
74+
"netclient_user","string","false","\-","\-","Usernname of the remote NUT server."
75+
"netclient_password","string","false","\-","\-","Password of the remote NUT server."
76+
"qx_enable","boolean","false","false","\-","Enable the OX driver."
77+
"qx_args","list","false","['port=auto']","\-","Extra arguments for QX driver, e.g. 'port=auto'."
78+
"riello_enable","boolean","false","false","\-","Enable the Riello driver."
79+
"riello_args","list","false","['port=auto']","\-","Extra arguments for Riello driver, e.g. 'port=auto'."
80+
"snmp_enable","boolean","false","false","\-","Enable the SNMP driver."
81+
"snmp_args","list","false","['community=public']","\-","Extra arguments for SNMP driver, e.g. 'community=public'."
82+
83+
.. include:: ../_include/param_basic.rst
84+
85+
.. include:: ../_include/param_reload.rst
86+
87+
----
88+
89+
Usages
90+
******
91+
92+
This module configures all NUT service settings.
93+
94+
After configuration changes, the service will be reloaded automatically.
95+
96+
----
97+
98+
Examples
99+
********
100+
101+
.. code-block:: yaml
102+
103+
- hosts: firewalls
104+
connection: local
105+
gather_facts: false
106+
module_defaults:
107+
group/oxlorg.opnsense.all:
108+
firewall: 'opnsense.template.oxlorg.net'
109+
api_credential_file: '/home/guy/.secret/opn.key'
110+
oxlorg.opnsense.nut:
111+
# repalce defaults password 'Password' with something random
112+
admin_password: some random password here
113+
monitor_password: some random password here
114+
115+
tasks:
116+
- name: Configure NUT service with USBHID driver
117+
oxlorg.opnsense.nut:
118+
enabled: True
119+
mode: standalone
120+
usbhid_enable: True
121+
122+
- name: Configure NUT service with SNMP driver
123+
oxlorg.opnsense.nut:
124+
enabled: True
125+
mode: standalone
126+
snmp_enable: True
127+
snmp_args:
128+
- community=public
129+
- port=192.168.1.200
130+
- snmp_version=v2c
131+
132+
- name: Configure NUT service in Netclient mode
133+
oxlorg.opnsense.nut:
134+
enabled: Ture
135+
mode: netclient
136+
netclient_enable: True
137+
netclient_address: 192.168.1.100
138+
netclient_user: remotemon
139+
netclient_password: password for remotemon user on 192.168.1.100 NUT server
140+
141+
----
142+
143+
Troubleshooting
144+
***************
145+
146+
Check Service -> Nut -> Diagnostics view to check UPS status.
147+
148+
You can use :ref:`oxlorg.opnsense.nut_diagnostics <modules_nut>` to acces status from ansible.
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
.. _modules_nut_diagnostics:
2+
3+
.. include:: ../_include/head.rst
4+
5+
=====================================
6+
NUT - Network UPS Tools - Diagnostics
7+
=====================================
8+
9+
**State:** Unstable
10+
11+
**Tests:** `nut_diagnostics.yml <https://github.com/O-X-L/ansible_opnsense/blob/latest/tests/nut_diagnostics.yml>`_
12+
13+
**API Docs**: `Plugins - NUT <https://docs.opnsense.org/development/api/plugins/nut.html>`_
14+
15+
**Service Docs**: `NUT - Network UPS Tools <https://docs.opnsense.org/manual/how-tos/nut.html>`_
16+
17+
Contribution
18+
************
19+
20+
Author: `@wmatusiak <https://github.com/wmatusiak>`_
21+
22+
If you encounter any issues or have suggestions for improvements, please feel free to contribute to the project.
23+
24+
----
25+
26+
Prerequisites
27+
*************
28+
29+
You need to install the NUT plugin:
30+
31+
```
32+
os-nut
33+
```
34+
You can also install it using the :ref:`oxlorg.opnsense.package <modules_package>` module.
35+
36+
----
37+
38+
Functions
39+
*********
40+
41+
This module allows you to read UPS Status from Network UPS Tools on OPNsense firewall.
42+
43+
Parameters
44+
##########
45+
46+
.. include:: ../_include/param_basic.rst
47+
48+
.. include:: ../_include/param_reload.rst
49+
50+
----
51+
52+
Examples
53+
********
54+
55+
.. code-block:: yaml
56+
57+
- hosts: firewalls
58+
connection: local
59+
gather_facts: false
60+
module_defaults:
61+
group/oxlorg.opnsense.all:
62+
firewall: 'opnsense.template.oxlorg.net'
63+
api_credential_file: '/home/guy/.secret/opn.key'
64+
65+
tasks:
66+
- name: Read UPS status
67+
oxlorg.opnsense.nut_diagnostics:
68+
register: ups_status
69+
70+
- name: Display UPS status
71+
ansible.builtin.debug:
72+
msg: " {{ ups_status.data }}"
73+

meta/runtime.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,9 @@ action_groups:
196196
- oxlorg.opnsense.user
197197
- oxlorg.opnsense.group
198198
- oxlorg.opnsense.privilege
199+
nut:
200+
- oxlorg.opnsense.nut
201+
- oxlorg.opnsense.nut_diagnostics
199202
all:
200203
- metadata:
201204
extend_group:
@@ -227,6 +230,8 @@ action_groups:
227230
- oxlorg.opnsense.access
228231
- oxlorg.opnsense.dnsmasq
229232
- oxlorg.opnsense.haproxy
233+
- oxlorg.opnsense.nut
234+
- oxlorg.opnsense.nut_diagnostics
230235

231236
plugin_routing:
232237
modules:

plugins/module_utils/main/nut.py

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
from ansible.module_utils.basic import AnsibleModule
2+
3+
from ansible_collections.oxlorg.opnsense.plugins.module_utils.base.api import Session
4+
from ansible_collections.oxlorg.opnsense.plugins.module_utils.base.cls import GeneralModule
5+
from ansible_collections.oxlorg.opnsense.plugins.module_utils.helper.main import is_unset
6+
from ansible_collections.oxlorg.opnsense.plugins.module_utils.helper.validate import is_ip, is_valid_domain
7+
8+
9+
class Nut(GeneralModule):
10+
CMDS = {
11+
'set': 'set',
12+
'search': 'get',
13+
}
14+
API_KEY_PATH = 'nut'
15+
API_KEY_PATH_REQ = API_KEY_PATH
16+
API_MOD = 'nut'
17+
API_CONT = 'settings'
18+
API_CONT_REL = 'service'
19+
API_CMD_REL = 'reconfigure'
20+
21+
FIELDS_TRANSLATE = {
22+
# General section
23+
'enabled': ('general', 'enable'),
24+
'mode': ('general', 'mode'),
25+
'name': ('general', 'name'),
26+
'listen': ('general', 'listen'),
27+
# Account section
28+
'admin_password': ('account', 'admin_password'),
29+
'monitor_password': ('account', 'mon_password'),
30+
# USBHID-Driver section
31+
'usbhid_enable': ('usbhid', 'enable'),
32+
'usbhid_args': ('usbhid', 'args'),
33+
# APCSMART-Driver section
34+
'apcsmart_enable': ('apcsmart', 'enable'),
35+
'apcsmart_args': ('apcsmart', 'args'),
36+
# APCUPSD-Driver section
37+
'apcupsd_enable': ('apcupsd', 'enable'),
38+
'apcupsd_host': ('apcupsd', 'hostname'),
39+
'apcupsd_port': ('apcupsd', 'port'),
40+
# BCMXCPUSB-Driver section
41+
'bcmxcpusb_enable': ('bcmxcpusb', 'enable'),
42+
'bcmxcpusb_args': ('bcmxcpusb', 'args'),
43+
# BlazerUSB-Driver section
44+
'blazerusb_enable': ('blazerusb', 'enable'),
45+
'blazerusb_args': ('blazerusb', 'args'),
46+
# BlazerSerial-Driver section
47+
'blazerser_enable': ('blazerser', 'enable'),
48+
'blazerser_args': ('blazerser', 'args'),
49+
# Netclient section
50+
'netclient_enable': ('netclient', 'enable'),
51+
'netclient_address': ('netclient', 'address'),
52+
'netclient_port': ('netclient', 'port'),
53+
'netclient_user': ('netclient', 'user'),
54+
'netclient_password': ('netclient', 'password'),
55+
# QX-Driver section
56+
'qx_enable': ('qx', 'enable'),
57+
'qx_args': ('qx', 'args'),
58+
# Riello-Driver section
59+
'riello_enable': ('riello', 'enable'),
60+
'riello_args': ('riello', 'args'),
61+
# SNMP-Driver section
62+
'snmp_enable': ('snmp', 'enable'),
63+
'snmp_args': ('snmp', 'args'),
64+
}
65+
66+
FIELDS_CHANGE = list(FIELDS_TRANSLATE.keys())
67+
FIELDS_ALL = FIELDS_CHANGE
68+
FIELDS_DIFF_NO_LOG = [
69+
'admin_password', 'monitor_password', 'netclient_password'
70+
]
71+
72+
FIELDS_TYPING = {
73+
'bool': [
74+
'enabled', 'usbhid_enable', 'apcsmart_enable', 'apcupsd_enable',
75+
'bcmxcpusb_enable', 'blazerusb_enable', 'blazerser_enable',
76+
'netclient_enable', 'qx_enable', 'riello_enable', 'snmp_enable'
77+
],
78+
'str': [
79+
'name', 'admin_password', 'monitor_password', 'apcupsd_host',
80+
'netclient_address', 'netclient_user', 'netclient_password'
81+
],
82+
'int': ['apcupsd_port', 'netclient_port'],
83+
'list': [
84+
'listen', 'usbhid_args', 'apcsmart_args', 'bcmxcpusb_args',
85+
'blazerusb_args', 'blazerser_args', 'qx_args', 'riello_args',
86+
'snmp_args'
87+
],
88+
'select': ['mode'],
89+
}
90+
91+
STR_VALIDATIONS = {
92+
'name': r'^([0-9a-zA-Z._\-]){1,128}$',
93+
}
94+
INT_VALIDATIONS = {
95+
'apcupsd_port': {'min': 1, 'max': 65535},
96+
'netclient_port': {'min': 1, 'max': 65535},
97+
}
98+
99+
TIMEOUT = 60.0
100+
101+
def __init__(self, module: AnsibleModule, result: dict, session: Session = None):
102+
GeneralModule.__init__(self=self, m=module, r=result, s=session)
103+
104+
@staticmethod
105+
def _is_ip_or_domain(value) -> bool:
106+
# Ignoring empty
107+
if is_unset(value):
108+
return True
109+
110+
return is_ip(value, ignore_empty=True) or is_valid_domain(value)
111+
112+
def check(self) -> None:
113+
self._base_check()
114+
if isinstance(self.p['listen'], list):
115+
for ip in self.p['listen']:
116+
if not is_ip(ip, ignore_empty=True):
117+
self.m.fail_json(
118+
f"It seems you provided a invalid IP address as 'listen': '{ip}'"
119+
)
120+
121+
else:
122+
ip = self.p['listen']
123+
if not is_ip(ip, ignore_empty=True):
124+
self.m.fail_json(
125+
f"It seems you provided a invalid IP address as 'listen': '{ip}'"
126+
)
127+
128+
for field in ['apcupsd_host', 'netclient_address']:
129+
ip = self.p[field]
130+
if field == 'apcupsd_host' and ip == 'localhost':
131+
# Default is localhost
132+
continue
133+
134+
if not self._is_ip_or_domain(ip):
135+
self.m.fail_json(
136+
f"It seems you provided a invalid IP or domain as '{field}': '{ip}'"
137+
)

0 commit comments

Comments
 (0)