From d04356379eb01c2acaaba136e5afc3bb6b9d5b7d Mon Sep 17 00:00:00 2001 From: Pierre Verkest Date: Wed, 6 Sep 2023 10:39:54 +0200 Subject: [PATCH] allow to configure OPNSense REST API calls timeout per REST API endpoint two new paramters available and parameters. --- README.md | 22 +++++++++++++++++++--- opnsense_exporter/opnsense_api.py | 31 +++++++++++++++++++++++++++---- opnsense_exporter/server.py | 28 ++++++++++++++++++++++++++-- tests/test_opnsense_api.py | 28 ++++++++++++++++++++++++++++ tests/test_server.py | 8 ++++++++ 5 files changed, 108 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index f0b1385..1b0624e 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,16 @@ optional arguments: means not calling the traffic diagnostic REST API so no `opnsense_active_server_traffic_rate` metric. (default: wan,lan) + --opnsense-timeout-sec-get-vip-status GET_VIP_STATUS_TIMEOUT_SEC + Allow to configure timeout while requesting + OPNSense REST API + /api/diagnostics/interface/get_vip_status/ + (default: 5) + --opnsense-timeout-sec-get-traffic GET_TRAFFIC_TIMEOUT_SEC + Allow to configure timeout while requesting + OPNSense REST API + /api/diagnostics/traffic/top/[INTERFACES] + (default: 15) --opnsense-password PASSWORD, -p PASSWORD OPNsense password. Expect to be the same on MAIN and BACKUP servers @@ -98,16 +108,23 @@ You can setup env through `.env` file or environment variables with defined as d - **OPNSENSE_USERNAME**: default value for `--opnsense-user` param - **OPNSENSE_PASSWORD**: default value for `--opnsense-password` param - **OPNSENSE_INTERFACES**: default value for `--opnsense-interfaces` param +- **OPNSENSE_TIMEOUT_SEC_GET_VIP_STATUS**: default value for `--opnsense-timeout-sec-get-vip-status` param +- **OPNSENSE_TIMEOUT_SEC_GET_TRAFFIC**: default value for `--opnsense-timeout-sec-get-traffic` param ## Roadmap - allow to change the listening port (today it force using `8000`) -- allow to configure timeouts using environment variables - improves logging to get a debug mode to understand errors based on unexpected payloads ## Changelog -### Version 1.0.0 (UNRELEASED) +### Version 1.1.0 (2023-09-06) + +- allow to configure OPNSense REST API calls timeout per REST API endpoint adding + `--opnsense-timeout-sec-get-vip-status` and + `--opnsense-timeout-sec-get-traffic` parameters. + +### Version 1.0.0 (2023-09-06) - remove `opnsense_main_ha_state` and `opnsense_backup_ha_state` metrics marked as deprecated on version 0.5.0 and replace @@ -115,7 +132,6 @@ You can setup env through `.env` file or environment variables with defined as d - allow empty string interfaces to **not** call diagnostic traffic REST API - ### Version 0.5.1 (2023-09-04) - FIX `opnsense_server_ha_state` calls were not diff --git a/opnsense_exporter/opnsense_api.py b/opnsense_exporter/opnsense_api.py index 5cdfc1a..75c0ab8 100644 --- a/opnsense_exporter/opnsense_api.py +++ b/opnsense_exporter/opnsense_api.py @@ -24,7 +24,12 @@ class OPNSenseTraffic: metric: OPNSenseTrafficMetric = None value: int = 0 - def __init__(self, interface: str, metric: OPNSenseTrafficMetric, value: int = 0): + def __init__( + self, + interface: str, + metric: OPNSenseTrafficMetric, + value: int = 0, + ): self.value = value self.interface = interface self.metric = metric @@ -55,12 +60,24 @@ class OPNSenseAPI: login: str = None password: str = None role: OPNSenseRole = None + get_vip_status_timeout_sec: int = 5 + get_traffic_timeout_sec: int = 15 - def __init__(self, role, host, login, password): + def __init__( + self, + role, + host, + login, + password, + get_vip_status_timeout_sec: int = 5, + get_traffic_timeout_sec: int = 5, + ): self.role = role self.host = host self.login = login self.password = password + self.get_vip_status_timeout_sec = get_vip_status_timeout_sec + self.get_traffic_timeout_sec = get_traffic_timeout_sec @property def labels(self): @@ -86,7 +103,10 @@ class OPNSenseAPI: def get_interface_vip_status(self) -> OPNSenseHAState: try: - data = self.get("/api/diagnostics/interface/get_vip_status/") + data = self.get( + "/api/diagnostics/interface/get_vip_status/", + timeout=self.get_vip_status_timeout_sec, + ) except RequestException as ex: logger.error( "Get VIP STATUS on %s failed with the following error %r", self.host, ex @@ -109,7 +129,10 @@ class OPNSenseAPI: if not interfaces: return [] try: - data = self.get(f"/api/diagnostics/traffic/top/{interfaces}", timeout=15) + data = self.get( + f"/api/diagnostics/traffic/top/{interfaces}", + timeout=self.get_traffic_timeout_sec, + ) except RequestException as ex: logger.error( "Get diagnostics traffic on %s interface(s) for %s host failed with the following error %r", diff --git a/opnsense_exporter/server.py b/opnsense_exporter/server.py index 14f6853..3578c69 100644 --- a/opnsense_exporter/server.py +++ b/opnsense_exporter/server.py @@ -135,6 +135,20 @@ def run(): "An empty string '' means not calling the traffic diagnostic REST API so no " "`opnsense_active_server_traffic_rate` metric.", ) + parser.add_argument( + "--opnsense-timeout-sec-get-vip-status", + type=int, + dest="get_vip_status_timeout_sec", + default=int(os.environ.get("OPNSENSE_TIMEOUT_SEC_GET_VIP_STATUS", 5)), + help="Allow to configure timeout while requesting OPNSense REST API /api/diagnostics/interface/get_vip_status/", + ) + parser.add_argument( + "--opnsense-timeout-sec-get-traffic", + type=int, + dest="get_traffic_timeout_sec", + default=int(os.environ.get("OPNSENSE_TIMEOUT_SEC_GET_TRAFFIC", 15)), + help="Allow to configure timeout while requesting OPNSense REST API /api/diagnostics/traffic/top/[INTERFACES]", + ) parser.add_argument( "--opnsense-password", "-p", @@ -158,10 +172,20 @@ def run(): server = OPNSensePrometheusExporter( OPNSenseAPI( - OPNSenseRole.MAIN, arguments.main, arguments.user, arguments.password + OPNSenseRole.MAIN, + arguments.main, + arguments.user, + arguments.password, + get_vip_status_timeout_sec=arguments.get_vip_status_timeout_sec, + get_traffic_timeout_sec=arguments.get_traffic_timeout_sec, ), OPNSenseAPI( - OPNSenseRole.BACKUP, arguments.backup, arguments.user, arguments.password + OPNSenseRole.BACKUP, + arguments.backup, + arguments.user, + arguments.password, + get_vip_status_timeout_sec=arguments.get_vip_status_timeout_sec, + get_traffic_timeout_sec=arguments.get_traffic_timeout_sec, ), arguments.interfaces, check_frequency=arguments.frequency, diff --git a/tests/test_opnsense_api.py b/tests/test_opnsense_api.py index 73fbc1a..fdf9d1a 100644 --- a/tests/test_opnsense_api.py +++ b/tests/test_opnsense_api.py @@ -1,3 +1,5 @@ +from unittest import mock + import responses from opnsense_exporter.opnsense_api import ( @@ -17,6 +19,32 @@ from .common import ( ) +def test_get_vip_status_timeout_sec(): + with mock.patch("opnsense_exporter.opnsense_api.OPNSenseAPI.get") as get_mock: + OPNSenseAPI( + OPNSenseRole.MAIN, + MAIN_HOST, + LOGIN, + PASSWORD, + get_vip_status_timeout_sec=11, + ).get_interface_vip_status() + get_mock.assert_called_once_with( + "/api/diagnostics/interface/get_vip_status/", timeout=11 + ) + + +def test_get_traffic_timeout_sec(): + with mock.patch("opnsense_exporter.opnsense_api.OPNSenseAPI.get") as get_mock: + OPNSenseAPI( + OPNSenseRole.MAIN, + MAIN_HOST, + LOGIN, + PASSWORD, + get_traffic_timeout_sec=11, + ).get_traffic("wan") + get_mock.assert_called_once_with("/api/diagnostics/traffic/top/wan", timeout=11) + + @responses.activate def test_get_interface_vip_status_active(): responses.add( diff --git a/tests/test_server.py b/tests/test_server.py index cadea0d..45b5c43 100644 --- a/tests/test_server.py +++ b/tests/test_server.py @@ -84,6 +84,10 @@ def test_parser(server_mock): "user-test", "-p", "pwd-test", + "--opnsense-timeout-sec-get-vip-status", + "7", + "--opnsense-timeout-sec-get-traffic", + "9", "-i", "efg,hij", "--prometheus-instance", @@ -97,10 +101,14 @@ def test_parser(server_mock): assert server.main.host == "main.host" assert server.main.login == "user-test" assert server.main.password == "pwd-test" + assert server.main.get_vip_status_timeout_sec == 7 + assert server.main.get_traffic_timeout_sec == 9 assert server.backup.role == OPNSenseRole.BACKUP assert server.backup.host == "backup.host" assert server.backup.login == "user-test" assert server.backup.password == "pwd-test" + assert server.backup.get_vip_status_timeout_sec == 7 + assert server.backup.get_traffic_timeout_sec == 9 assert server.check_frequency == 15 assert server.interfaces == "efg,hij"