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 - `instance`: by default this is set with the hostname where is running this exporter service
- `host`: the host of the OPNSense - `host`: the host of the OPNSense
- `role`: `main` or `backup` - `role`: `main` or `backup` to determine the OPNSense server role.
### Enums ### Enums
@ -35,8 +35,10 @@ This exporter gives following metrics, all metrics received following labels:
### Gauges ### Gauges
- `opnsense_active_server_bytes_received`: Active OPNSense server bytes received on WAN interface - `opnsense_active_server_traffic_rate`: Active OPNSense server traffic rate per interfaces bits/s
- `opnsense_active_server_bytes_transmitted`: Active OPNSense server bytes transmitted on WAN interface 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 ## Usage
@ -47,6 +49,7 @@ opnsense-exporter --help
usage: opnsense-exporter [-h] [--check-frequency-seconds FREQUENCY] usage: opnsense-exporter [-h] [--check-frequency-seconds FREQUENCY]
[--main-host MAIN] [--backup-host BACKUP] [--main-host MAIN] [--backup-host BACKUP]
[--opnsense-user USER] [--opnsense-user USER]
[--opnsense-interfaces INTERFACES]
[--opnsense-password PASSWORD] [--opnsense-password PASSWORD]
[--prometheus-instance PROM_INSTANCE] [--prometheus-instance PROM_INSTANCE]
@ -61,17 +64,21 @@ optional arguments:
MAIN OPNsense server that should be in `active` MAIN OPNsense server that should be in `active`
state in normal configuration. state in normal configuration.
--backup-host BACKUP, -b BACKUP --backup-host BACKUP, -b BACKUP
BACKUP OPNsense server that should be BACKUP OPNsense server that should be `hot_standby`
`hot_standby` state in normal configuration. state in normal configuration.
--opnsense-user USER, -u USER --opnsense-user USER, -u USER
OPNsense user. Expect to be the same on MAIN and OPNsense user. Expect to be the same on MAIN and
BACKUP servers 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 PASSWORD, -p PASSWORD
OPNsense password. Expect to be the same on MAIN OPNsense password. Expect to be the same on MAIN
and BACKUP servers and BACKUP servers
--prometheus-instance PROM_INSTANCE --prometheus-instance PROM_INSTANCE
Exporter Instance name, default value computed Exporter Instance name, default value computed with
with hostname where the server is running. Use to 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 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 ## 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 - 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 - refactor server in a class to avoid transmitted params over methods
- allow to change the listening port (today it force using `8000`) - 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 ## 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) ### 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) ### 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) ### 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) ### 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__) 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): class OPNSenseRole(Enum):
MAIN = "main" MAIN = "main"
BACKUP = "backup" BACKUP = "backup"
@ -67,20 +98,23 @@ class OPNSenseAPI:
) )
return "unavailable" return "unavailable"
def get_wan_trafic(self): def get_traffic(self, interfaces):
try: 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: except RequestException as ex:
logger.error( 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, self.host,
ex, ex,
) )
return None, None return []
traffics = []
received = 0 for interface in interfaces.split(","):
transmitted = 0 traffic_in = OPNSenseTraffic(interface, OPNSenseTrafficMetric.IN)
for record in data.get("wan", []).get("records", []): traffic_out = OPNSenseTraffic(interface, OPNSenseTrafficMetric.OUT)
received += record.get("rate_bits_in", 0) for record in data.get(interface, []).get("records", []):
transmitted += record.get("rate_bits_out", 0) traffic_in.value += record.get(OPNSenseTrafficMetric.IN.value, 0)
return received, transmitted 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, states=HA_STATES,
) )
active_server_bytes_received = Gauge( opnsense_active_server_traffic_rate = Gauge(
"opnsense_active_server_bytes_received", "opnsense_active_server_traffic_rate",
"Active OPNSense server bytes received on WAN interface", "Active OPNSense server bytes in/out per interface",
[
"instance",
"host",
"role",
],
)
active_server_bytes_transmitted = Gauge(
"opnsense_active_server_bytes_transmitted",
"Active OPNSense server bytes transmitted on WAN interface",
[ [
"instance", "instance",
"host", "host",
"role", "role",
"interface",
"metric",
], ],
) )
@ -56,11 +49,13 @@ class OPNSensePrometheusExporter:
self, self,
main: OPNSenseAPI, main: OPNSenseAPI,
backup: OPNSenseAPI, backup: OPNSenseAPI,
interfaces,
exporter_instance: str = "", exporter_instance: str = "",
check_frequency: int = 1, check_frequency: int = 1,
): ):
self.main = main self.main = main
self.backup = backup self.backup = backup
self.interfaces = interfaces
self.exporter_instance = exporter_instance self.exporter_instance = exporter_instance
self.check_frequency = check_frequency self.check_frequency = check_frequency
@ -80,15 +75,13 @@ class OPNSensePrometheusExporter:
if backup_sate == "active": if backup_sate == "active":
active_opnsense = self.backup active_opnsense = self.backup
if active_opnsense: if active_opnsense:
bytes_received, bytes_transmitted = active_opnsense.get_wan_trafic() for traffic in active_opnsense.get_traffic(self.interfaces):
if bytes_received or bytes_received == 0: if traffic.value:
active_server_bytes_received.labels( opnsense_active_server_traffic_rate.labels(
instance=self.exporter_instance, **active_opnsense.labels instance=self.exporter_instance,
).set(bytes_received) **active_opnsense.labels,
if bytes_transmitted or bytes_transmitted == 0: **traffic.labels
active_server_bytes_transmitted.labels( ).set(traffic.value)
instance=self.exporter_instance, **active_opnsense.labels
).set(bytes_transmitted)
def start_server(self): def start_server(self):
# Start up the server to expose the metrics. # Start up the server to expose the metrics.
@ -136,6 +129,14 @@ def run():
default=os.environ.get("OPNSENSE_USERNAME", None), default=os.environ.get("OPNSENSE_USERNAME", None),
help="OPNsense user. Expect to be the same on MAIN and BACKUP servers", 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( parser.add_argument(
"--opnsense-password", "--opnsense-password",
"-p", "-p",
@ -164,6 +165,7 @@ def run():
OPNSenseAPI( OPNSenseAPI(
OPNSenseRole.BACKUP, arguments.backup, arguments.user, arguments.password OPNSenseRole.BACKUP, arguments.backup, arguments.user, arguments.password
), ),
arguments.interfaces,
check_frequency=arguments.frequency, check_frequency=arguments.frequency,
exporter_instance=arguments.prom_instance, 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(): 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( return json.dumps(
{ {
"wan": { "wan": {
"records": [ "records": [
{ {
"address": "0.1.2.3", "address": "0.1.2.3",
"rate_bits_in": 15300, "rate_bits_in": 62300,
"rate_bits_out": 1720, "rate_bits_out": 66100,
"rate_bits": 17020, "rate_bits": 128400,
"cumulative_bytes_in": 3830, "cumulative_bytes_in": 15600,
"cumulative_bytes_out": 441, "cumulative_bytes_out": 16500,
"cumulative_bytes": 4271, "cumulative_bytes": 32100,
"tags": [], "tags": [],
"details": [ "details": [
{ {
"address": "0.1.2.3", "address": "0.1.2.3",
"rate": "15.3Kb", "rate": "62.3Kb",
"rate_bits": 15300, "rate_bits": 62300,
"cumulative": "3.83KB", "cumulative": "15.6KB",
"cumulative_bytes": 3830, "cumulative_bytes": 15600,
"tags": ["local"], "tags": ["local"],
} }
], ],
"rname": "fake value", "rname": "fake rname value",
"rate_in": "15.3 kb", "rate_in": "62.3 kb",
"rate_out": "1.72 kb", "rate_out": "66.1 kb",
"rate": "17.02 kb", "rate": "128.4 kb",
"cumulative_in": "3.83 kb", "cumulative_in": "15.6 kb",
"cumulative_out": "441.0 b", "cumulative_out": "16.5 kb",
"cumulative": "4.27 kb", "cumulative": "32.1 kb",
}, },
{ {
"address": "0.1.2.3", "address": "0.1.2.3",
"rate_bits_in": 4470, "rate_bits_in": 36200,
"rate_bits_out": 7290, "rate_bits_out": 16100,
"rate_bits": 11760, "rate_bits": 52300,
"cumulative_bytes_in": 1120, "cumulative_bytes_in": 9060,
"cumulative_bytes_out": 1820, "cumulative_bytes_out": 4020,
"cumulative_bytes": 2940, "cumulative_bytes": 13080,
"tags": [], "tags": [],
"details": [ "details": [
{ {
"address": "0.1.2.3", "address": "0.1.2.3",
"rate": "4.47Kb", "rate": "36.2Kb",
"rate_bits": 4470, "rate_bits": 36200,
"cumulative": "1.12KB", "cumulative": "9.06KB",
"cumulative_bytes": 1120, "cumulative_bytes": 9060,
"tags": ["local"], "tags": ["local"],
} }
], ],
"rname": "fake value", "rname": "fake rname value",
"rate_in": "4.47 kb", "rate_in": "36.2 kb",
"rate_out": "7.29 kb", "rate_out": "16.1 kb",
"rate": "11.76 kb", "rate": "52.3 kb",
"cumulative_in": "1.12 kb", "cumulative_in": "9.06 kb",
"cumulative_out": "1.82 kb", "cumulative_out": "4.02 kb",
"cumulative": "2.94 kb", "cumulative": "13.08 kb",
}, },
{ {
"address": "0.1.2.3", "address": "0.1.2.3",
"rate_bits_in": 272, "rate_bits_in": 1790,
"rate_bits_out": 272, "rate_bits_out": 1520,
"rate_bits": 544, "rate_bits": 3310,
"cumulative_bytes_in": 68, "cumulative_bytes_in": 459,
"cumulative_bytes_out": 68, "cumulative_bytes_out": 389,
"cumulative_bytes": 136, "cumulative_bytes": 848,
"tags": [], "tags": [],
"details": [ "details": [
{ {
"address": "0.1.2.3", "address": "0.1.2.3",
"rate": "272b", "rate": "1.79Kb",
"rate_bits": 272, "rate_bits": 1790,
"cumulative": "68B", "cumulative": "459B",
"cumulative_bytes": 68, "cumulative_bytes": 459,
"tags": ["local"], "tags": ["local"],
} }
], ],
"rname": "fake value", "rname": "fake rname value",
"rate_in": "272.0 b", "rate_in": "1.79 kb",
"rate_out": "272.0 b", "rate_out": "1.52 kb",
"rate": "544.0 b", "rate": "3.31 kb",
"cumulative_in": "68.0 b", "cumulative_in": "459.0 b",
"cumulative_out": "68.0 b", "cumulative_out": "389.0 b",
"cumulative": "136.0 b", "cumulative": "848.0 b",
}, },
{ {
"address": "0.1.2.3", "address": "0.1.2.3",
"rate_bits_in": 272, "rate_bits_in": 512,
"rate_bits_out": 272, "rate_bits_out": 1580,
"rate_bits": 544, "rate_bits": 2092,
"cumulative_bytes_in": 68, "cumulative_bytes_in": 128,
"cumulative_bytes_out": 68, "cumulative_bytes_out": 405,
"cumulative_bytes": 136, "cumulative_bytes": 533,
"tags": [], "tags": [],
"details": [ "details": [
{ {
"address": "0.1.2.3", "address": "0.1.2.3",
"rate": "272b", "rate": "512b",
"rate_bits": 272, "rate_bits": 512,
"cumulative": "68B", "cumulative": "128B",
"cumulative_bytes": 68, "cumulative_bytes": 128,
"tags": ["local"], "tags": ["local"],
} }
], ],
"rname": "fake value", "rname": "fake rname value",
"rate_in": "272.0 b", "rate_in": "512.0 b",
"rate_out": "272.0 b", "rate_out": "1.58 kb",
"rate": "544.0 b", "rate": "2.09 kb",
"cumulative_in": "68.0 b", "cumulative_in": "128.0 b",
"cumulative_out": "68.0 b", "cumulative_out": "405.0 b",
"cumulative": "136.0 b", "cumulative": "533.0 b",
}, },
{ {
"address": "0.1.2.3", "address": "0.1.2.3",
"rate_bits_in": 0, "rate_bits_in": 0,
"rate_bits_out": 480, "rate_bits_out": 448,
"rate_bits": 480, "rate_bits": 448,
"cumulative_bytes_in": 0, "cumulative_bytes_in": 0,
"cumulative_bytes_out": 120, "cumulative_bytes_out": 112,
"cumulative_bytes": 120, "cumulative_bytes": 112,
"tags": [], "tags": [],
"details": [ "details": [
{ {
@ -176,7 +181,26 @@ def generate_diagnostics_traffic_interface_paylaod():
"cumulative": "0B", "cumulative": "0B",
"cumulative_bytes": 0, "cumulative_bytes": 0,
"tags": ["local"], "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", "address": "0.1.2.3",
"rate": "0b", "rate": "0b",
@ -184,15 +208,15 @@ def generate_diagnostics_traffic_interface_paylaod():
"cumulative": "0B", "cumulative": "0B",
"cumulative_bytes": 0, "cumulative_bytes": 0,
"tags": ["local"], "tags": ["local"],
}, }
], ],
"rname": "fake value", "rname": "fake rname value",
"rate_in": "0.0 b", "rate_in": "0.0 b",
"rate_out": "480.0 b", "rate_out": "272.0 b",
"rate": "480.0 b", "rate": "272.0 b",
"cumulative_in": "0.0 b", "cumulative_in": "0.0 b",
"cumulative_out": "120.0 b", "cumulative_out": "68.0 b",
"cumulative": "120.0 b", "cumulative": "68.0 b",
}, },
{ {
"address": "0.1.2.3", "address": "0.1.2.3",
@ -213,7 +237,7 @@ def generate_diagnostics_traffic_interface_paylaod():
"tags": ["local"], "tags": ["local"],
} }
], ],
"rname": "fake value", "rname": "fake rname value",
"rate_in": "224.0 b", "rate_in": "224.0 b",
"rate_out": "0.0 b", "rate_out": "0.0 b",
"rate": "224.0 b", "rate": "224.0 b",
@ -223,6 +247,224 @@ def generate_diagnostics_traffic_interface_paylaod():
}, },
], ],
"status": "ok", "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 import responses
from opnsense_exporter.opnsense_api import OPNSenseAPI, OPNSenseRole from opnsense_exporter.opnsense_api import (
OPNSenseAPI,
OPNSenseRole,
OPNSenseTraffic,
OPNSenseTrafficMetric,
)
from .common import ( from .common import (
BACKUP_HOST, BACKUP_HOST,
@ -92,33 +97,35 @@ def test_get_interface_vip_status_unavailable_rest_api_error():
@responses.activate @responses.activate
def test_get_wan_traffic(): def test_get_traffic():
responses.add( responses.add(
responses.GET, 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(), body=generate_diagnostics_traffic_interface_paylaod(),
) )
assert OPNSenseAPI( assert OPNSenseAPI(OPNSenseRole.MAIN, MAIN_HOST, LOGIN, PASSWORD).get_traffic(
OPNSenseRole.MAIN, MAIN_HOST, LOGIN, PASSWORD "wan,lan"
).get_wan_trafic() == ( ) == [
20538, OPNSenseTraffic("wan", OPNSenseTrafficMetric.IN, value=101026),
10034, OPNSenseTraffic("wan", OPNSenseTrafficMetric.OUT, value=86020),
) OPNSenseTraffic("lan", OPNSenseTrafficMetric.IN, value=188490),
OPNSenseTraffic("lan", OPNSenseTrafficMetric.OUT, value=952),
]
@responses.activate @responses.activate
def test_get_wan_traffic_none(): def test_get_traffic_none():
responses.add( responses.add(
responses.GET, 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"}, json={"error": "not found"},
status=404, status=404,
) )
assert OPNSenseAPI( assert (
OPNSenseRole.MAIN, MAIN_HOST, LOGIN, PASSWORD OPNSenseAPI(OPNSenseRole.MAIN, MAIN_HOST, LOGIN, PASSWORD).get_traffic(
).get_wan_trafic() == ( "test-not-found"
None, )
None, == []
) )

View file

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