Allow to monitor multiple interfaces

This commit is contained in:
Pierre Verkest 2023-09-03 23:38:34 +02:00
parent 334be5b4c2
commit 7501a0d9b8
6 changed files with 548 additions and 236 deletions

View file

@ -26,7 +26,7 @@ This exporter gives following metrics, all metrics received following labels:
- `instance`: by default this is set with the hostname where is running this exporter service
- `host`: the host of the OPNSense
- `role`: `main` or `backup`
- `role`: `main` or `backup` to determine the OPNSense server role.
### Enums
@ -35,8 +35,10 @@ This exporter gives following metrics, all metrics received following labels:
### Gauges
- `opnsense_active_server_bytes_received`: Active OPNSense server bytes received on WAN interface
- `opnsense_active_server_bytes_transmitted`: Active OPNSense server bytes transmitted on WAN interface
- `opnsense_active_server_traffic_rate`: Active OPNSense server traffic rate per interfaces bits/s
add following labels:
- **interface**: the interface to export (values given using `--opnsense-interfaces`)
- **metric**: the metric name (as today one of `rate_bits_in`, `rate_bits_in`)
## Usage
@ -47,6 +49,7 @@ opnsense-exporter --help
usage: opnsense-exporter [-h] [--check-frequency-seconds FREQUENCY]
[--main-host MAIN] [--backup-host BACKUP]
[--opnsense-user USER]
[--opnsense-interfaces INTERFACES]
[--opnsense-password PASSWORD]
[--prometheus-instance PROM_INSTANCE]
@ -61,17 +64,21 @@ optional arguments:
MAIN OPNsense server that should be in `active`
state in normal configuration.
--backup-host BACKUP, -b BACKUP
BACKUP OPNsense server that should be
`hot_standby` state in normal configuration.
BACKUP OPNsense server that should be `hot_standby`
state in normal configuration.
--opnsense-user USER, -u USER
OPNsense user. Expect to be the same on MAIN and
BACKUP servers
--opnsense-interfaces INTERFACES, -i INTERFACES
OPNsense interfaces (coma separated) list to export
trafic rates (bytes/s) (default: wan,lan)
--opnsense-password PASSWORD, -p PASSWORD
OPNsense password. Expect to be the same on MAIN
and BACKUP servers
--prometheus-instance PROM_INSTANCE
Exporter Instance name, default value computed
with hostname where the server is running. Use to
Exporter Instance name, default value computed with
hostname where the server is running. Use to set
the instance label. (default: my-opnsense-prom-exporter-server)
```
You can setup env through `.env` file or environment variables with defined as default values
@ -86,8 +93,6 @@ You can setup env through `.env` file or environment variables with defined as d
## Roadmap
- merge `opnsense_active_server_bytes_received` and `opnsense_active_server_bytes_transmitted`
metrics adding labels to distinguish rates transmitted and rate received
- allow to configure interfaces to get traffic rates for lan,wan and/or other names
- refactor server in a class to avoid transmitted params over methods
- allow to change the listening port (today it force using `8000`)
@ -96,22 +101,26 @@ You can setup env through `.env` file or environment variables with defined as d
## Changelog
### Version 0.0.5 (UNRELEASED)
### Version 0.5.0 (UNRELEASED)
* add role label in metrics
- add role label in metrics
- all to configure supervised interfaces using `--opnsense-interfaces`
- replace `active_server_bytes_received` and
`active_server_bytes_transmitted` by
`opnsense_active_server_traffic_rate`
### Version 0.4.0 (2023-09-02)
* Higher timeout while getting WAN traffic info
- Higher timeout while getting WAN traffic info
### Version 0.3.0 (2023-09-02)
* Use proper method to compute WAN traffic
- Use proper method to compute WAN traffic
### Version 0.2.0 (2023-09-01)
* Setup automatic release from gitlab while pushing new tag
- Setup automatic release from gitlab while pushing new tag
### Version 0.1.0 (2023-09-01)
* Initial version
- Initial version

View file

@ -7,6 +7,37 @@ from requests import RequestException
logger = logging.getLogger(__name__)
class OPNSenseTrafficMetric(Enum):
IN = "rate_bits_in"
OUT = "rate_bits_out"
class OPNSenseTraffic:
interface: str = None
metric: OPNSenseTrafficMetric = None
value: int = 0
def __init__(self, interface: str, metric: OPNSenseTrafficMetric, value: int = 0):
self.value = value
self.interface = interface
self.metric = metric
@property
def labels(self):
return {"metric": self.metric.value, "interface": self.interface}
def __eq__(self, opn_traffic):
"""Used by unittest to assert expected values"""
return (
self.interface == opn_traffic.interface
and self.metric == opn_traffic.metric
and self.value == opn_traffic.value
)
def __repr__(self):
return f"{self.interface} - {self.metric} = {self.value}"
class OPNSenseRole(Enum):
MAIN = "main"
BACKUP = "backup"
@ -67,20 +98,23 @@ class OPNSenseAPI:
)
return "unavailable"
def get_wan_trafic(self):
def get_traffic(self, interfaces):
try:
data = self.get("/api/diagnostics/traffic/top/wan", timeout=15)
data = self.get(f"/api/diagnostics/traffic/top/{interfaces}", timeout=15)
except RequestException as ex:
logger.error(
"Get diagnostics traffic on WAN interface for %s host failed with the following error %r",
"Get diagnostics traffic on %s interface(s) for %s host failed with the following error %r",
interfaces,
self.host,
ex,
)
return None, None
received = 0
transmitted = 0
for record in data.get("wan", []).get("records", []):
received += record.get("rate_bits_in", 0)
transmitted += record.get("rate_bits_out", 0)
return received, transmitted
return []
traffics = []
for interface in interfaces.split(","):
traffic_in = OPNSenseTraffic(interface, OPNSenseTrafficMetric.IN)
traffic_out = OPNSenseTraffic(interface, OPNSenseTrafficMetric.OUT)
for record in data.get(interface, []).get("records", []):
traffic_in.value += record.get(OPNSenseTrafficMetric.IN.value, 0)
traffic_out.value += record.get(OPNSenseTrafficMetric.OUT.value, 0)
traffics.extend([traffic_in, traffic_out])
return traffics

View file

@ -31,22 +31,15 @@ backup_ha_state = Enum(
],
states=HA_STATES,
)
active_server_bytes_received = Gauge(
"opnsense_active_server_bytes_received",
"Active OPNSense server bytes received on WAN interface",
[
"instance",
"host",
"role",
],
)
active_server_bytes_transmitted = Gauge(
"opnsense_active_server_bytes_transmitted",
"Active OPNSense server bytes transmitted on WAN interface",
opnsense_active_server_traffic_rate = Gauge(
"opnsense_active_server_traffic_rate",
"Active OPNSense server bytes in/out per interface",
[
"instance",
"host",
"role",
"interface",
"metric",
],
)
@ -56,11 +49,13 @@ class OPNSensePrometheusExporter:
self,
main: OPNSenseAPI,
backup: OPNSenseAPI,
interfaces,
exporter_instance: str = "",
check_frequency: int = 1,
):
self.main = main
self.backup = backup
self.interfaces = interfaces
self.exporter_instance = exporter_instance
self.check_frequency = check_frequency
@ -80,15 +75,13 @@ class OPNSensePrometheusExporter:
if backup_sate == "active":
active_opnsense = self.backup
if active_opnsense:
bytes_received, bytes_transmitted = active_opnsense.get_wan_trafic()
if bytes_received or bytes_received == 0:
active_server_bytes_received.labels(
instance=self.exporter_instance, **active_opnsense.labels
).set(bytes_received)
if bytes_transmitted or bytes_transmitted == 0:
active_server_bytes_transmitted.labels(
instance=self.exporter_instance, **active_opnsense.labels
).set(bytes_transmitted)
for traffic in active_opnsense.get_traffic(self.interfaces):
if traffic.value:
opnsense_active_server_traffic_rate.labels(
instance=self.exporter_instance,
**active_opnsense.labels,
**traffic.labels
).set(traffic.value)
def start_server(self):
# Start up the server to expose the metrics.
@ -136,6 +129,14 @@ def run():
default=os.environ.get("OPNSENSE_USERNAME", None),
help="OPNsense user. Expect to be the same on MAIN and BACKUP servers",
)
parser.add_argument(
"--opnsense-interfaces",
"-i",
type=str,
dest="interfaces",
default=os.environ.get("OPNSENSE_INTERFACES", "wan,lan"),
help="OPNsense interfaces (coma separated) list to export trafic rates (bytes/s)",
)
parser.add_argument(
"--opnsense-password",
"-p",
@ -164,6 +165,7 @@ def run():
OPNSenseAPI(
OPNSenseRole.BACKUP, arguments.backup, arguments.user, arguments.password
),
arguments.interfaces,
check_frequency=arguments.frequency,
exporter_instance=arguments.prom_instance,
)

View file

@ -47,126 +47,131 @@ def generate_get_vip_status_paylaod(state_wan, state_lan, maintenance_mode):
def generate_diagnostics_traffic_interface_paylaod():
# wan - rate_bits_in: 101026
# wan - rate_bits_out: 86020
# lan - rate_bits_in: 188490
# lan - rate_bits_out: 952
return json.dumps(
{
"wan": {
"records": [
{
"address": "0.1.2.3",
"rate_bits_in": 15300,
"rate_bits_out": 1720,
"rate_bits": 17020,
"cumulative_bytes_in": 3830,
"cumulative_bytes_out": 441,
"cumulative_bytes": 4271,
"rate_bits_in": 62300,
"rate_bits_out": 66100,
"rate_bits": 128400,
"cumulative_bytes_in": 15600,
"cumulative_bytes_out": 16500,
"cumulative_bytes": 32100,
"tags": [],
"details": [
{
"address": "0.1.2.3",
"rate": "15.3Kb",
"rate_bits": 15300,
"cumulative": "3.83KB",
"cumulative_bytes": 3830,
"rate": "62.3Kb",
"rate_bits": 62300,
"cumulative": "15.6KB",
"cumulative_bytes": 15600,
"tags": ["local"],
}
],
"rname": "fake value",
"rate_in": "15.3 kb",
"rate_out": "1.72 kb",
"rate": "17.02 kb",
"cumulative_in": "3.83 kb",
"cumulative_out": "441.0 b",
"cumulative": "4.27 kb",
"rname": "fake rname value",
"rate_in": "62.3 kb",
"rate_out": "66.1 kb",
"rate": "128.4 kb",
"cumulative_in": "15.6 kb",
"cumulative_out": "16.5 kb",
"cumulative": "32.1 kb",
},
{
"address": "0.1.2.3",
"rate_bits_in": 4470,
"rate_bits_out": 7290,
"rate_bits": 11760,
"cumulative_bytes_in": 1120,
"cumulative_bytes_out": 1820,
"cumulative_bytes": 2940,
"rate_bits_in": 36200,
"rate_bits_out": 16100,
"rate_bits": 52300,
"cumulative_bytes_in": 9060,
"cumulative_bytes_out": 4020,
"cumulative_bytes": 13080,
"tags": [],
"details": [
{
"address": "0.1.2.3",
"rate": "4.47Kb",
"rate_bits": 4470,
"cumulative": "1.12KB",
"cumulative_bytes": 1120,
"rate": "36.2Kb",
"rate_bits": 36200,
"cumulative": "9.06KB",
"cumulative_bytes": 9060,
"tags": ["local"],
}
],
"rname": "fake value",
"rate_in": "4.47 kb",
"rate_out": "7.29 kb",
"rate": "11.76 kb",
"cumulative_in": "1.12 kb",
"cumulative_out": "1.82 kb",
"cumulative": "2.94 kb",
"rname": "fake rname value",
"rate_in": "36.2 kb",
"rate_out": "16.1 kb",
"rate": "52.3 kb",
"cumulative_in": "9.06 kb",
"cumulative_out": "4.02 kb",
"cumulative": "13.08 kb",
},
{
"address": "0.1.2.3",
"rate_bits_in": 272,
"rate_bits_out": 272,
"rate_bits": 544,
"cumulative_bytes_in": 68,
"cumulative_bytes_out": 68,
"cumulative_bytes": 136,
"rate_bits_in": 1790,
"rate_bits_out": 1520,
"rate_bits": 3310,
"cumulative_bytes_in": 459,
"cumulative_bytes_out": 389,
"cumulative_bytes": 848,
"tags": [],
"details": [
{
"address": "0.1.2.3",
"rate": "272b",
"rate_bits": 272,
"cumulative": "68B",
"cumulative_bytes": 68,
"rate": "1.79Kb",
"rate_bits": 1790,
"cumulative": "459B",
"cumulative_bytes": 459,
"tags": ["local"],
}
],
"rname": "fake value",
"rate_in": "272.0 b",
"rate_out": "272.0 b",
"rate": "544.0 b",
"cumulative_in": "68.0 b",
"cumulative_out": "68.0 b",
"cumulative": "136.0 b",
"rname": "fake rname value",
"rate_in": "1.79 kb",
"rate_out": "1.52 kb",
"rate": "3.31 kb",
"cumulative_in": "459.0 b",
"cumulative_out": "389.0 b",
"cumulative": "848.0 b",
},
{
"address": "0.1.2.3",
"rate_bits_in": 272,
"rate_bits_out": 272,
"rate_bits": 544,
"cumulative_bytes_in": 68,
"cumulative_bytes_out": 68,
"cumulative_bytes": 136,
"rate_bits_in": 512,
"rate_bits_out": 1580,
"rate_bits": 2092,
"cumulative_bytes_in": 128,
"cumulative_bytes_out": 405,
"cumulative_bytes": 533,
"tags": [],
"details": [
{
"address": "0.1.2.3",
"rate": "272b",
"rate_bits": 272,
"cumulative": "68B",
"cumulative_bytes": 68,
"rate": "512b",
"rate_bits": 512,
"cumulative": "128B",
"cumulative_bytes": 128,
"tags": ["local"],
}
],
"rname": "fake value",
"rate_in": "272.0 b",
"rate_out": "272.0 b",
"rate": "544.0 b",
"cumulative_in": "68.0 b",
"cumulative_out": "68.0 b",
"cumulative": "136.0 b",
"rname": "fake rname value",
"rate_in": "512.0 b",
"rate_out": "1.58 kb",
"rate": "2.09 kb",
"cumulative_in": "128.0 b",
"cumulative_out": "405.0 b",
"cumulative": "533.0 b",
},
{
"address": "0.1.2.3",
"rate_bits_in": 0,
"rate_bits_out": 480,
"rate_bits": 480,
"rate_bits_out": 448,
"rate_bits": 448,
"cumulative_bytes_in": 0,
"cumulative_bytes_out": 120,
"cumulative_bytes": 120,
"cumulative_bytes_out": 112,
"cumulative_bytes": 112,
"tags": [],
"details": [
{
@ -176,7 +181,26 @@ def generate_diagnostics_traffic_interface_paylaod():
"cumulative": "0B",
"cumulative_bytes": 0,
"tags": ["local"],
},
}
],
"rname": "fake rname value",
"rate_in": "0.0 b",
"rate_out": "448.0 b",
"rate": "448.0 b",
"cumulative_in": "0.0 b",
"cumulative_out": "112.0 b",
"cumulative": "112.0 b",
},
{
"address": "0.1.2.3",
"rate_bits_in": 0,
"rate_bits_out": 272,
"rate_bits": 272,
"cumulative_bytes_in": 0,
"cumulative_bytes_out": 68,
"cumulative_bytes": 68,
"tags": [],
"details": [
{
"address": "0.1.2.3",
"rate": "0b",
@ -184,15 +208,15 @@ def generate_diagnostics_traffic_interface_paylaod():
"cumulative": "0B",
"cumulative_bytes": 0,
"tags": ["local"],
},
}
],
"rname": "fake value",
"rname": "fake rname value",
"rate_in": "0.0 b",
"rate_out": "480.0 b",
"rate": "480.0 b",
"rate_out": "272.0 b",
"rate": "272.0 b",
"cumulative_in": "0.0 b",
"cumulative_out": "120.0 b",
"cumulative": "120.0 b",
"cumulative_out": "68.0 b",
"cumulative": "68.0 b",
},
{
"address": "0.1.2.3",
@ -213,7 +237,7 @@ def generate_diagnostics_traffic_interface_paylaod():
"tags": ["local"],
}
],
"rname": "fake value",
"rname": "fake rname value",
"rate_in": "224.0 b",
"rate_out": "0.0 b",
"rate": "224.0 b",
@ -223,6 +247,224 @@ def generate_diagnostics_traffic_interface_paylaod():
},
],
"status": "ok",
}
},
"lan": {
"records": [
{
"address": "0.1.2.3",
"rate_bits_in": 65200,
"rate_bits_out": 0,
"rate_bits": 65200,
"cumulative_bytes_in": 16270,
"cumulative_bytes_out": 0,
"cumulative_bytes": 16270,
"tags": [],
"details": [
{
"address": "0.1.2.3",
"rate": "45.3Kb",
"rate_bits": 45300,
"cumulative": "11.3KB",
"cumulative_bytes": 11300,
"tags": ["private"],
},
{
"address": "0.1.2.3",
"rate": "19.9Kb",
"rate_bits": 19900,
"cumulative": "4.97KB",
"cumulative_bytes": 4970,
"tags": ["private"],
},
],
"rname": "fake rname value",
"rate_in": "65.2 kb",
"rate_out": "0.0 b",
"rate": "65.2 kb",
"cumulative_in": "16.27 kb",
"cumulative_out": "0.0 b",
"cumulative": "16.27 kb",
},
{
"address": "0.1.2.3",
"rate_bits_in": 47900,
"rate_bits_out": 0,
"rate_bits": 47900,
"cumulative_bytes_in": 12000,
"cumulative_bytes_out": 0,
"cumulative_bytes": 12000,
"tags": ["private"],
"details": [
{
"address": "0.1.2.3",
"rate": "47.9Kb",
"rate_bits": 47900,
"cumulative": "12.0KB",
"cumulative_bytes": 12000,
"tags": [],
}
],
"rname": "fake rname value",
"rate_in": "47.9 kb",
"rate_out": "0.0 b",
"rate": "47.9 kb",
"cumulative_in": "12.0 kb",
"cumulative_out": "0.0 b",
"cumulative": "12.0 kb",
},
{
"address": "0.1.2.3",
"rate_bits_in": 36200,
"rate_bits_out": 0,
"rate_bits": 36200,
"cumulative_bytes_in": 9060,
"cumulative_bytes_out": 0,
"cumulative_bytes": 9060,
"tags": [],
"details": [
{
"address": "0.1.2.3",
"rate": "36.2Kb",
"rate_bits": 36200,
"cumulative": "9.06KB",
"cumulative_bytes": 9060,
"tags": ["private"],
}
],
"rname": "fake rname value",
"rate_in": "36.2 kb",
"rate_out": "0.0 b",
"rate": "36.2 kb",
"cumulative_in": "9.06 kb",
"cumulative_out": "0.0 b",
"cumulative": "9.06 kb",
},
{
"address": "0.1.2.3",
"rate_bits_in": 19200,
"rate_bits_out": 0,
"rate_bits": 19200,
"cumulative_bytes_in": 4814,
"cumulative_bytes_out": 0,
"cumulative_bytes": 4814,
"tags": ["private"],
"details": [
{
"address": "0.1.2.3",
"rate": "16.1Kb",
"rate_bits": 16100,
"cumulative": "4.02KB",
"cumulative_bytes": 4020,
"tags": [],
},
{
"address": "0.1.2.3",
"rate": "1.58Kb",
"rate_bits": 1580,
"cumulative": "405B",
"cumulative_bytes": 405,
"tags": [],
},
{
"address": "0.1.2.3",
"rate": "1.52Kb",
"rate_bits": 1520,
"cumulative": "389B",
"cumulative_bytes": 389,
"tags": [],
},
],
"rname": "fake rname value",
"rate_in": "19.2 kb",
"rate_out": "0.0 b",
"rate": "19.2 kb",
"cumulative_in": "4.81 kb",
"cumulative_out": "0.0 b",
"cumulative": "4.81 kb",
},
{
"address": "0.1.2.3",
"rate_bits_in": 18200,
"rate_bits_out": 0,
"rate_bits": 18200,
"cumulative_bytes_in": 4550,
"cumulative_bytes_out": 0,
"cumulative_bytes": 4550,
"tags": ["private"],
"details": [
{
"address": "0.1.2.3",
"rate": "18.2Kb",
"rate_bits": 18200,
"cumulative": "4.55KB",
"cumulative_bytes": 4550,
"tags": [],
}
],
"rname": "fake rname value",
"rate_in": "18.2 kb",
"rate_out": "0.0 b",
"rate": "18.2 kb",
"cumulative_in": "4.55 kb",
"cumulative_out": "0.0 b",
"cumulative": "4.55 kb",
},
{
"address": "0.1.2.3",
"rate_bits_in": 1790,
"rate_bits_out": 0,
"rate_bits": 1790,
"cumulative_bytes_in": 459,
"cumulative_bytes_out": 0,
"cumulative_bytes": 459,
"tags": [],
"details": [
{
"address": "0.1.2.3",
"rate": "1.79Kb",
"rate_bits": 1790,
"cumulative": "459B",
"cumulative_bytes": 459,
"tags": ["private"],
}
],
"rname": "fake rname value",
"rate_in": "1.79 kb",
"rate_out": "0.0 b",
"rate": "1.79 kb",
"cumulative_in": "459.0 b",
"cumulative_out": "0.0 b",
"cumulative": "459.0 b",
},
{
"address": "0.1.2.3",
"rate_bits_in": 0,
"rate_bits_out": 952,
"rate_bits": 952,
"cumulative_bytes_in": 0,
"cumulative_bytes_out": 238,
"cumulative_bytes": 238,
"tags": ["private"],
"details": [
{
"address": "0.1.2.3",
"rate": "0b",
"rate_bits": 0,
"cumulative": "0B",
"cumulative_bytes": 0,
"tags": ["private"],
}
],
"rname": "fake rname value",
"rate_in": "0.0 b",
"rate_out": "952.0 b",
"rate": "952.0 b",
"cumulative_in": "0.0 b",
"cumulative_out": "238.0 b",
"cumulative": "238.0 b",
},
],
"status": "ok",
},
}
)

View file

@ -1,6 +1,11 @@
import responses
from opnsense_exporter.opnsense_api import OPNSenseAPI, OPNSenseRole
from opnsense_exporter.opnsense_api import (
OPNSenseAPI,
OPNSenseRole,
OPNSenseTraffic,
OPNSenseTrafficMetric,
)
from .common import (
BACKUP_HOST,
@ -92,33 +97,35 @@ def test_get_interface_vip_status_unavailable_rest_api_error():
@responses.activate
def test_get_wan_traffic():
def test_get_traffic():
responses.add(
responses.GET,
f"https://{MAIN_HOST}/api/diagnostics/traffic/top/wan",
f"https://{MAIN_HOST}/api/diagnostics/traffic/top/wan,lan",
body=generate_diagnostics_traffic_interface_paylaod(),
)
assert OPNSenseAPI(
OPNSenseRole.MAIN, MAIN_HOST, LOGIN, PASSWORD
).get_wan_trafic() == (
20538,
10034,
)
assert OPNSenseAPI(OPNSenseRole.MAIN, MAIN_HOST, LOGIN, PASSWORD).get_traffic(
"wan,lan"
) == [
OPNSenseTraffic("wan", OPNSenseTrafficMetric.IN, value=101026),
OPNSenseTraffic("wan", OPNSenseTrafficMetric.OUT, value=86020),
OPNSenseTraffic("lan", OPNSenseTrafficMetric.IN, value=188490),
OPNSenseTraffic("lan", OPNSenseTrafficMetric.OUT, value=952),
]
@responses.activate
def test_get_wan_traffic_none():
def test_get_traffic_none():
responses.add(
responses.GET,
f"https://{MAIN_HOST}/api/diagnostics/traffic/top/wan",
f"https://{MAIN_HOST}/api/diagnostics/traffic/top/test-not-found",
json={"error": "not found"},
status=404,
)
assert OPNSenseAPI(
OPNSenseRole.MAIN, MAIN_HOST, LOGIN, PASSWORD
).get_wan_trafic() == (
None,
None,
assert (
OPNSenseAPI(OPNSenseRole.MAIN, MAIN_HOST, LOGIN, PASSWORD).get_traffic(
"test-not-found"
)
== []
)

View file

@ -1,3 +1,4 @@
from typing import List
from unittest import mock
import responses
@ -17,28 +18,54 @@ from .common import (
class FakePromMetric:
_labels = {}
_labels_calls = None
def __init__(self):
self._labels = {}
self._labels_calls = []
@property
def count_labels_calls(self) -> int:
return len(self._labels_calls)
def labels(self, *args, **kwargs):
self._labels = kwargs
self._labels_calls.append(kwargs)
return self
class FakePromEnum(FakePromMetric):
_state = None
count_state_calls = 0
_state: str = None
_state_calls: List[str] = []
def state(self, state):
self.count_state_calls += 1
def __init__(self):
super().__init__()
self._state_calls = []
@property
def count_state_calls(self) -> int:
return len(self._state_calls)
def state(self, state: str):
self._state = state
self._state_calls.append(state)
class FakePromGauge(FakePromMetric):
value = None
count_set_calls = 0
_value: int = None
_set_calls: List[int] = []
def set(self, value):
self.count_set_calls += 1
self.value = value
def __init__(self):
super().__init__()
self._set_calls = []
@property
def count_set_calls(self) -> int:
return len(self._set_calls)
def set(self, value: int):
self._value = value
self._set_calls.append(value)
@mock.patch("opnsense_exporter.server.OPNSensePrometheusExporter.start_server")
@ -57,6 +84,8 @@ def test_parser(server_mock):
"user-test",
"-p",
"pwd-test",
"-i",
"efg,hij",
"--prometheus-instance",
"server-hostname-instance",
],
@ -73,6 +102,7 @@ def test_parser(server_mock):
assert server.backup.login == "user-test"
assert server.backup.password == "pwd-test"
assert server.check_frequency == 15
assert server.interfaces == "efg,hij"
@responses.activate
@ -95,28 +125,24 @@ def test_process_requests():
main_ha_state_mock = FakePromEnum()
backup_ha_state_mock = FakePromEnum()
active_server_bytes_received_mock = FakePromGauge()
active_server_bytes_transmitted_mock = FakePromGauge()
opnsense_active_server_traffic_rate_mock = FakePromGauge()
with mock.patch("opnsense_exporter.server.main_ha_state", new=main_ha_state_mock):
with mock.patch(
"opnsense_exporter.server.backup_ha_state", new=backup_ha_state_mock
):
with mock.patch(
"opnsense_exporter.server.active_server_bytes_received",
new=active_server_bytes_received_mock,
"opnsense_exporter.server.opnsense_active_server_traffic_rate",
new=opnsense_active_server_traffic_rate_mock,
):
with mock.patch(
"opnsense_exporter.server.active_server_bytes_transmitted",
new=active_server_bytes_transmitted_mock,
):
OPNSensePrometheusExporter(
OPNSenseAPI(OPNSenseRole.MAIN, MAIN_HOST, LOGIN, PASSWORD),
OPNSenseAPI(OPNSenseRole.BACKUP, BACKUP_HOST, LOGIN, PASSWORD),
).process_requests()
OPNSensePrometheusExporter(
OPNSenseAPI(OPNSenseRole.MAIN, MAIN_HOST, LOGIN, PASSWORD),
OPNSenseAPI(OPNSenseRole.BACKUP, BACKUP_HOST, LOGIN, PASSWORD),
"wan",
).process_requests()
assert main_ha_state_mock._state == "active"
assert main_ha_state_mock.count_state_calls == 1
assert main_ha_state_mock.count_state_calls == 1, main_ha_state_mock._state_calls
assert main_ha_state_mock._labels == {
"instance": "",
"host": MAIN_HOST,
@ -131,21 +157,24 @@ def test_process_requests():
"role": "backup",
}
assert active_server_bytes_received_mock.value == 20538
assert active_server_bytes_received_mock.count_set_calls == 1
assert active_server_bytes_received_mock._labels == {
"instance": "",
"host": MAIN_HOST,
"role": "main",
}
assert active_server_bytes_transmitted_mock.value == 10034
assert active_server_bytes_transmitted_mock.count_set_calls == 1
assert active_server_bytes_transmitted_mock._labels == {
"instance": "",
"host": MAIN_HOST,
"role": "main",
}
assert opnsense_active_server_traffic_rate_mock.count_set_calls == 2
assert opnsense_active_server_traffic_rate_mock._labels_calls == [
{
"instance": "",
"host": MAIN_HOST,
"role": "main",
"interface": "wan",
"metric": "rate_bits_in",
},
{
"instance": "",
"host": MAIN_HOST,
"role": "main",
"interface": "wan",
"metric": "rate_bits_out",
},
]
assert opnsense_active_server_traffic_rate_mock._set_calls == [101026, 86020]
@responses.activate
@ -168,25 +197,21 @@ def test_process_requests_backup_active():
main_ha_state_mock = FakePromEnum()
backup_ha_state_mock = FakePromEnum()
active_server_bytes_received_mock = FakePromGauge()
active_server_bytes_transmitted_mock = FakePromGauge()
opnsense_active_server_traffic_rate_mock = FakePromGauge()
with mock.patch("opnsense_exporter.server.main_ha_state", new=main_ha_state_mock):
with mock.patch(
"opnsense_exporter.server.backup_ha_state", new=backup_ha_state_mock
):
with mock.patch(
"opnsense_exporter.server.active_server_bytes_received",
new=active_server_bytes_received_mock,
"opnsense_exporter.server.opnsense_active_server_traffic_rate",
new=opnsense_active_server_traffic_rate_mock,
):
with mock.patch(
"opnsense_exporter.server.active_server_bytes_transmitted",
new=active_server_bytes_transmitted_mock,
):
OPNSensePrometheusExporter(
OPNSenseAPI(OPNSenseRole.MAIN, MAIN_HOST, LOGIN, PASSWORD),
OPNSenseAPI(OPNSenseRole.BACKUP, BACKUP_HOST, LOGIN, PASSWORD),
).process_requests()
OPNSensePrometheusExporter(
OPNSenseAPI(OPNSenseRole.MAIN, MAIN_HOST, LOGIN, PASSWORD),
OPNSenseAPI(OPNSenseRole.BACKUP, BACKUP_HOST, LOGIN, PASSWORD),
"wan",
).process_requests()
assert main_ha_state_mock._state == "maintenancemode"
assert main_ha_state_mock.count_state_calls == 1
assert main_ha_state_mock._labels == {
@ -203,21 +228,24 @@ def test_process_requests_backup_active():
"role": "backup",
}
assert active_server_bytes_received_mock.value == 20538
assert active_server_bytes_received_mock.count_set_calls == 1
assert active_server_bytes_received_mock._labels == {
"instance": "",
"host": BACKUP_HOST,
"role": "backup",
}
assert active_server_bytes_transmitted_mock.value == 10034
assert active_server_bytes_transmitted_mock.count_set_calls == 1
assert active_server_bytes_transmitted_mock._labels == {
"instance": "",
"host": BACKUP_HOST,
"role": "backup",
}
assert opnsense_active_server_traffic_rate_mock.count_set_calls == 2
opnsense_active_server_traffic_rate_mock._labels_calls == [
{
"instance": "",
"host": BACKUP_HOST,
"role": "backup",
"interface": "wan",
"metric": "rate_bits_in",
},
{
"instance": "",
"host": BACKUP_HOST,
"role": "backup",
"interface": "wan",
"metric": "rate_bits_out",
},
]
assert opnsense_active_server_traffic_rate_mock._set_calls == [101026, 86020]
@responses.activate
@ -241,25 +269,21 @@ def test_process_no_active():
main_ha_state_mock = FakePromEnum()
backup_ha_state_mock = FakePromEnum()
active_server_bytes_received_mock = FakePromGauge()
active_server_bytes_transmitted_mock = FakePromGauge()
opnsense_active_server_traffic_rate_mock = FakePromGauge()
with mock.patch("opnsense_exporter.server.main_ha_state", new=main_ha_state_mock):
with mock.patch(
"opnsense_exporter.server.backup_ha_state", new=backup_ha_state_mock
):
with mock.patch(
"opnsense_exporter.server.active_server_bytes_received",
new=active_server_bytes_received_mock,
"opnsense_exporter.server.opnsense_active_server_traffic_rate",
new=opnsense_active_server_traffic_rate_mock,
):
with mock.patch(
"opnsense_exporter.server.active_server_bytes_transmitted",
new=active_server_bytes_transmitted_mock,
):
OPNSensePrometheusExporter(
OPNSenseAPI(OPNSenseRole.MAIN, MAIN_HOST, LOGIN, PASSWORD),
OPNSenseAPI(OPNSenseRole.BACKUP, BACKUP_HOST, LOGIN, PASSWORD),
).process_requests()
OPNSensePrometheusExporter(
OPNSenseAPI(OPNSenseRole.MAIN, MAIN_HOST, LOGIN, PASSWORD),
OPNSenseAPI(OPNSenseRole.BACKUP, BACKUP_HOST, LOGIN, PASSWORD),
"wan",
).process_requests()
assert main_ha_state_mock._state == "maintenancemode"
assert main_ha_state_mock.count_state_calls == 1
@ -277,8 +301,7 @@ def test_process_no_active():
"role": "backup",
}
assert active_server_bytes_received_mock.count_set_calls == 0
assert active_server_bytes_transmitted_mock.count_set_calls == 0
assert opnsense_active_server_traffic_rate_mock.count_set_calls == 0
@responses.activate
@ -302,25 +325,21 @@ def test_process_with_falsy_value():
main_ha_state_mock = FakePromEnum()
backup_ha_state_mock = FakePromEnum()
active_server_bytes_received_mock = FakePromGauge()
active_server_bytes_transmitted_mock = FakePromGauge()
opnsense_active_server_traffic_rate_mock = FakePromGauge()
with mock.patch("opnsense_exporter.server.main_ha_state", new=main_ha_state_mock):
with mock.patch(
"opnsense_exporter.server.backup_ha_state", new=backup_ha_state_mock
):
with mock.patch(
"opnsense_exporter.server.active_server_bytes_received",
new=active_server_bytes_received_mock,
"opnsense_exporter.server.opnsense_active_server_traffic_rate",
new=opnsense_active_server_traffic_rate_mock,
):
with mock.patch(
"opnsense_exporter.server.active_server_bytes_transmitted",
new=active_server_bytes_transmitted_mock,
):
OPNSensePrometheusExporter(
OPNSenseAPI(OPNSenseRole.MAIN, MAIN_HOST, LOGIN, PASSWORD),
OPNSenseAPI(OPNSenseRole.BACKUP, BACKUP_HOST, LOGIN, PASSWORD),
).process_requests()
OPNSensePrometheusExporter(
OPNSenseAPI(OPNSenseRole.MAIN, MAIN_HOST, LOGIN, PASSWORD),
OPNSenseAPI(OPNSenseRole.BACKUP, BACKUP_HOST, LOGIN, PASSWORD),
"wan",
).process_requests()
assert main_ha_state_mock._state == "active"
assert main_ha_state_mock.count_state_calls == 1
assert main_ha_state_mock._labels == {
@ -337,5 +356,4 @@ def test_process_with_falsy_value():
"role": "backup",
}
assert active_server_bytes_received_mock.count_set_calls == 0
assert active_server_bytes_transmitted_mock.count_set_calls == 0
assert opnsense_active_server_traffic_rate_mock.count_set_calls == 0