Compare commits

..

No commits in common. "b59a6368c9919fe9ff488c4bae20d530a769241b" and "a2a1384e9d9bdef5b86608bea6278953dabd5289" have entirely different histories.

11 changed files with 373 additions and 886 deletions

View file

@ -1,5 +1,5 @@
[bumpversion] [bumpversion]
current_version = 0.5.1 current_version = 0.4.0
commit = True commit = True
tag = True tag = True

View file

@ -1,6 +0,0 @@
CHECK_FREQUENCY_SECONDS: default value for --check-frequency-seconds param
OPNSENSE_MAIN_HOST: default value for --main-host param
OPNSENSE_BACKUP_HOST: default value for --backup-host param
OPNSENSE_USERNAME: default value for --opnsense-user param
OPNSENSE_PASSWORD: default value for --opnsense-password param
OPNSENSE_INTERFACES: default value for --opnsense-interfaces param

View file

@ -1,22 +0,0 @@
FROM python:latest
#WORKDIR /usr/app/src
# Install package
WORKDIR /code
COPY . .
#COPY prometheus-ssh-exporter.py ./
#COPY requirements.txt ./
RUN pip3 install -r requirements.txt
RUN python -u setup.py install
# Set this to the port you want to expose
EXPOSE 8000
# Set the -p option to the port you exposed above, defaults to 8000
#CMD ["python", "-u", "opnsense-exporter"]
#CMD ["sleep", "60"]
CMD ["opnsense-exporter"]

View file

@ -26,24 +26,16 @@ 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` to determine the OPNSense server role.
### Enums ### Enums
- `opnsense_main_ha_state`: (deprecated) OPNSense HA state of the MAIN server - `opnsense_main_ha_state`: OPNSense HA state of the MAIN server
- `opnsense_backup_ha_state`: (deprecated) OPNSense HA state of the BACKUP server - `opnsense_backup_ha_state`: OPNSense HA state of the BACKUP server
- `opnsense_server_ha_state`: OPNSense HA state, on of following value:
- **active**: that OPNSense server is receiving traffic
- **hot_standby**: the OPNSense server is ready to be promote as active server
- **maintenancemode**: the OPNSense server was turned into maintenance mode
- **unavailable**: the OPNSense server wasn't accessible or return unexpected value
### Gauges ### Gauges
- `opnsense_active_server_traffic_rate`: Active OPNSense server traffic rate per interfaces bits/s - `opnsense_active_server_bytes_received`: Active OPNSense server bytes received on WAN interface
add following labels: - `opnsense_active_server_bytes_transmitted`: Active OPNSense server bytes transmitted on WAN interface
- **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
@ -54,7 +46,6 @@ 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]
@ -69,24 +60,17 @@ 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 `hot_standby` BACKUP OPNsense server that should be
state in normal configuration. `hot_standby` 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). An empty string ''
means not calling the traffic diagnostic REST API
so no `opnsense_active_server_traffic_rate`
metric. (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 with Exporter Instance name, default value computed
hostname where the server is running. Use to set with hostname where the server is running. Use to
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
@ -97,52 +81,25 @@ You can setup env through `.env` file or environment variables with defined as d
- **OPNSENSE_BACKUP_HOST**: default value for `--backup-host` param - **OPNSENSE_BACKUP_HOST**: default value for `--backup-host` param
- **OPNSENSE_USERNAME**: default value for `--opnsense-user` param - **OPNSENSE_USERNAME**: default value for `--opnsense-user` param
- **OPNSENSE_PASSWORD**: default value for `--opnsense-password` param - **OPNSENSE_PASSWORD**: default value for `--opnsense-password` param
- **OPNSENSE_INTERFACES**: default value for `--opnsense-interfaces` param
## Roadmap ## Roadmap
- allow to change the listening port (today it force using `8000`) - 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 ## Changelog
### Version 1.0.0 (UNRELEASED) ### Version 0.4.0
- remove `opnsense_main_ha_state` and `opnsense_backup_ha_state` Higher timeout while getting WAN traffic info
metrics marked as deprecated on version 0.5.0 and replace
by `opnsense_server_ha_state` and `role` label
- allow empty string interfaces to **not** call diagnostic
traffic REST API
### Version 0.3.0
### Version 0.5.1 (2023-09-04) Use proper method to compute WAN traffic
- FIX `opnsense_server_ha_state` calls were not ### Version 0.2.0
implemented
### Version 0.5.0 (2023-09-04) Setup automatic release from gitlab while pushing new tag
- add role label in metrics ### Version 0.1.0
- all to configure supervised interfaces using `--opnsense-interfaces`
- replace `active_server_bytes_received` and
`active_server_bytes_transmitted` by
`opnsense_active_server_traffic_rate`
- add `opnsense_server_ha_state` and mark `opnsense_main_ha_state`
and `opnsense_backup_ha_state` as deprecated.
### Version 0.4.0 (2023-09-02) Initial version
- Higher timeout while getting WAN traffic info
### Version 0.3.0 (2023-09-02)
- Use proper method to compute WAN traffic
### Version 0.2.0 (2023-09-01)
- Setup automatic release from gitlab while pushing new tag
### Version 0.1.0 (2023-09-01)
- Initial version

View file

@ -1,20 +0,0 @@
version: '3.4'
services:
opnsense-exporter:
image: opnsense-exporter
build:
context: .
dockerfile: ./Dockerfile
restart: always
container_name: opnsense-exporter
#network_mode: "host"
ports:
- 8000:8000
env_file:
- .env
logging:
driver: "json-file"
options:
max-file: "3"
max-size: 10m

View file

@ -1,5 +1,4 @@
import logging import logging
from enum import Enum
import requests import requests
from requests import RequestException from requests import RequestException
@ -7,68 +6,16 @@ from requests import RequestException
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class OPNSenseHAState(Enum):
ACTIVE = "active"
HOT_STANDBY = "hot_standby"
UNAVAILABLE = "unavailable"
MAINTENANCE_MODE = "maintenancemode"
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"
class OPNSenseAPI: class OPNSenseAPI:
host: str = None host: str = None
login: str = None login: str = None
password: str = None password: str = None
role: OPNSenseRole = None
def __init__(self, role, host, login, password): def __init__(self, host, login, password):
self.role = role
self.host = host self.host = host
self.login = login self.login = login
self.password = password self.password = password
@property
def labels(self):
return {
"host": self.host,
"role": self.role.value,
}
def prepare_url(self, path): def prepare_url(self, path):
return f"https://{self.host}{path}" return f"https://{self.host}{path}"
@ -84,46 +31,41 @@ class OPNSenseAPI:
response.raise_for_status() response.raise_for_status()
return response.json() return response.json()
def get_interface_vip_status(self) -> OPNSenseHAState: def get_interface_vip_status(self):
try: try:
data = self.get("/api/diagnostics/interface/get_vip_status/") data = self.get("/api/diagnostics/interface/get_vip_status/")
except RequestException as ex: except RequestException as ex:
logger.error( logger.error(
"Get VIP STATUS on %s failed with the following error %r", self.host, ex "Get VIP STATUS on %s failed with the following error %r", self.host, ex
) )
return OPNSenseHAState.UNAVAILABLE return "unavailable"
if data["carp"]["maintenancemode"]: if data["carp"]["maintenancemode"]:
return OPNSenseHAState.MAINTENANCE_MODE return "maintenancemode"
is_active = all([row["status"] == "MASTER" for row in data["rows"]]) is_active = all([row["status"] == "MASTER" for row in data["rows"]])
if is_active: if is_active:
return OPNSenseHAState.ACTIVE return "active"
is_backup = all([row["status"] == "BACKUP" for row in data["rows"]]) is_backup = all([row["status"] == "BACKUP" for row in data["rows"]])
if is_backup: if is_backup:
return OPNSenseHAState.HOT_STANDBY return "hot_standby"
logger.warning( logger.warning(
"this host %s is no active nor backup received payload %s", self.host, data "this host %s is no active nor backup received payload %s", self.host, data
) )
return OPNSenseHAState.UNAVAILABLE return "unavailable"
def get_traffic(self, interfaces): def get_wan_trafic(self):
if not interfaces:
return []
try: try:
data = self.get(f"/api/diagnostics/traffic/top/{interfaces}", timeout=15) data = self.get("/api/diagnostics/traffic/top/wan", timeout=15)
except RequestException as ex: except RequestException as ex:
logger.error( logger.error(
"Get diagnostics traffic on %s interface(s) for %s host failed with the following error %r", "Get diagnostics traffic on WAN interface for %s host failed with the following error %r",
interfaces,
self.host, self.host,
ex, ex,
) )
return [] return None, None
traffics = []
for interface in interfaces.split(","): received = 0
traffic_in = OPNSenseTraffic(interface, OPNSenseTrafficMetric.IN) transmitted = 0
traffic_out = OPNSenseTraffic(interface, OPNSenseTrafficMetric.OUT) for record in data["wan"]["records"]:
for record in data.get(interface, {}).get("records", []): received += record["rate_bits_in"]
traffic_in.value += record.get(OPNSenseTrafficMetric.IN.value, 0) transmitted += record["rate_bits_out"]
traffic_out.value += record.get(OPNSenseTrafficMetric.OUT.value, 0) return received, transmitted
traffics.extend([traffic_in, traffic_out])
return traffics

View file

@ -1,5 +1,4 @@
import argparse import argparse
import logging
import os import os
import socket import socket
import time import time
@ -7,85 +6,84 @@ import time
from dotenv import load_dotenv from dotenv import load_dotenv
from prometheus_client import Enum, Gauge, start_http_server from prometheus_client import Enum, Gauge, start_http_server
from opnsense_exporter.opnsense_api import OPNSenseAPI, OPNSenseHAState, OPNSenseRole from opnsense_exporter.opnsense_api import OPNSenseAPI
logger = logging.getLogger(__name__)
load_dotenv() load_dotenv()
HA_STATES = [enum.value for enum in list(OPNSenseHAState)] HA_STATES = ["active", "hot_standby", "unavailable", "maintenancemode"]
main_ha_state = Enum(
"opnsense_main_ha_state",
opnsense_server_ha_state = Enum( "OPNSense HA state of the MAIN server",
"opnsense_server_ha_state",
"OPNSense server HA state",
[ [
"instance", "instance",
"host", "host",
"role",
], ],
states=HA_STATES, states=HA_STATES,
) )
backup_ha_state = Enum(
opnsense_active_server_traffic_rate = Gauge( "opnsense_backup_ha_state",
"opnsense_active_server_traffic_rate", "OPNSense HA state of the BACKUP server",
"Active OPNSense server bytes in/out per interface", [
"instance",
"host",
],
states=HA_STATES,
)
active_server_bytes_received = Gauge(
"opnsense_active_server_bytes_received",
"Active OPNSense server bytes received on WAN interface",
[
"instance",
"host",
],
)
active_server_bytes_transmitted = Gauge(
"opnsense_active_server_bytes_transmitted",
"Active OPNSense server bytes transmitted on WAN interface",
[ [
"instance", "instance",
"host", "host",
"role",
"interface",
"metric",
], ],
) )
class OPNSensePrometheusExporter: def process_requests(main, backup, exporter_instance: str = ""):
def __init__( """A dummy function that takes some time."""
self, main_state = main.get_interface_vip_status()
backup_sate = backup.get_interface_vip_status()
main_ha_state.labels(instance=exporter_instance, host=main.host).state(main_state)
backup_ha_state.labels(instance=exporter_instance, host=backup.host).state(
backup_sate
)
active_opnsense = None
if main_state == "active":
active_opnsense = main
if backup_sate == "active":
active_opnsense = 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=exporter_instance, host=active_opnsense.host
).set(bytes_received)
if bytes_transmitted or bytes_transmitted == 0:
active_server_bytes_transmitted.labels(
instance=exporter_instance, host=active_opnsense.host
).set(bytes_transmitted)
def start_server(
main: OPNSenseAPI, main: OPNSenseAPI,
backup: OPNSenseAPI, backup: OPNSenseAPI,
interfaces,
exporter_instance: str = "",
check_frequency: int = 1, check_frequency: int = 1,
): exporter_instance: str = "",
self.main = main ):
self.backup = backup
self.interfaces = interfaces
self.exporter_instance = exporter_instance
self.check_frequency = check_frequency
def process_requests(self):
"""A dummy function that takes some time."""
main_state = self.main.get_interface_vip_status()
backup_sate = self.backup.get_interface_vip_status()
opnsense_server_ha_state.labels(
instance=self.exporter_instance, **self.main.labels
).state(main_state.value)
opnsense_server_ha_state.labels(
instance=self.exporter_instance, **self.backup.labels
).state(backup_sate.value)
active_opnsense = None
if main_state == OPNSenseHAState.ACTIVE:
active_opnsense = self.main
if backup_sate == OPNSenseHAState.ACTIVE:
active_opnsense = self.backup
if active_opnsense:
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, port=8000):
# Start up the server to expose the metrics. # Start up the server to expose the metrics.
start_http_server(port) start_http_server(8000)
logger.info("listen port %s", port)
# Generate some requests. # Generate some requests.
while True: while True:
self.process_requests() process_requests(main, backup, exporter_instance=exporter_instance)
time.sleep(self.check_frequency) time.sleep(check_frequency)
def run(): def run():
@ -125,16 +123,6 @@ 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). "
"An empty string '' means not calling the traffic diagnostic REST API so no "
"`opnsense_active_server_traffic_rate` metric.",
)
parser.add_argument( parser.add_argument(
"--opnsense-password", "--opnsense-password",
"-p", "-p",
@ -155,19 +143,9 @@ def run():
) )
arguments = parser.parse_args() arguments = parser.parse_args()
start_server(
server = OPNSensePrometheusExporter( OPNSenseAPI(arguments.main, arguments.user, arguments.password),
OPNSenseAPI( OPNSenseAPI(arguments.backup, arguments.user, arguments.password),
OPNSenseRole.MAIN, arguments.main, arguments.user, arguments.password
),
OPNSenseAPI(
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,
) )
server.start_server()
# return the server instance mainly for test purpose
return server

View file

@ -3,7 +3,7 @@ from urllib.parse import urlparse
from setuptools import find_packages, setup from setuptools import find_packages, setup
version = "0.5.1" version = "0.4.0"
HERE = pathlib.Path(__file__).parent HERE = pathlib.Path(__file__).parent

View file

@ -47,158 +47,126 @@ 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": 62300, "rate_bits_in": 15300,
"rate_bits_out": 66100, "rate_bits_out": 1720,
"rate_bits": 128400, "rate_bits": 17020,
"cumulative_bytes_in": 15600, "cumulative_bytes_in": 3830,
"cumulative_bytes_out": 16500, "cumulative_bytes_out": 441,
"cumulative_bytes": 32100, "cumulative_bytes": 4271,
"tags": [], "tags": [],
"details": [ "details": [
{ {
"address": "0.1.2.3", "address": "0.1.2.3",
"rate": "62.3Kb", "rate": "15.3Kb",
"rate_bits": 62300, "rate_bits": 15300,
"cumulative": "15.6KB", "cumulative": "3.83KB",
"cumulative_bytes": 15600, "cumulative_bytes": 3830,
"tags": ["local"], "tags": ["local"],
} }
], ],
"rname": "fake rname value", "rname": "fake value",
"rate_in": "62.3 kb", "rate_in": "15.3 kb",
"rate_out": "66.1 kb", "rate_out": "1.72 kb",
"rate": "128.4 kb", "rate": "17.02 kb",
"cumulative_in": "15.6 kb", "cumulative_in": "3.83 kb",
"cumulative_out": "16.5 kb", "cumulative_out": "441.0 b",
"cumulative": "32.1 kb", "cumulative": "4.27 kb",
}, },
{ {
"address": "0.1.2.3", "address": "0.1.2.3",
"rate_bits_in": 36200, "rate_bits_in": 4470,
"rate_bits_out": 16100, "rate_bits_out": 7290,
"rate_bits": 52300, "rate_bits": 11760,
"cumulative_bytes_in": 9060, "cumulative_bytes_in": 1120,
"cumulative_bytes_out": 4020, "cumulative_bytes_out": 1820,
"cumulative_bytes": 13080, "cumulative_bytes": 2940,
"tags": [], "tags": [],
"details": [ "details": [
{ {
"address": "0.1.2.3", "address": "0.1.2.3",
"rate": "36.2Kb", "rate": "4.47Kb",
"rate_bits": 36200, "rate_bits": 4470,
"cumulative": "9.06KB", "cumulative": "1.12KB",
"cumulative_bytes": 9060, "cumulative_bytes": 1120,
"tags": ["local"], "tags": ["local"],
} }
], ],
"rname": "fake rname value", "rname": "fake value",
"rate_in": "36.2 kb", "rate_in": "4.47 kb",
"rate_out": "16.1 kb", "rate_out": "7.29 kb",
"rate": "52.3 kb", "rate": "11.76 kb",
"cumulative_in": "9.06 kb", "cumulative_in": "1.12 kb",
"cumulative_out": "4.02 kb", "cumulative_out": "1.82 kb",
"cumulative": "13.08 kb", "cumulative": "2.94 kb",
}, },
{ {
"address": "0.1.2.3", "address": "0.1.2.3",
"rate_bits_in": 1790, "rate_bits_in": 272,
"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": "1.79Kb",
"rate_bits": 1790,
"cumulative": "459B",
"cumulative_bytes": 459,
"tags": ["local"],
}
],
"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": 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": "512b",
"rate_bits": 512,
"cumulative": "128B",
"cumulative_bytes": 128,
"tags": ["local"],
}
],
"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": 448,
"rate_bits": 448,
"cumulative_bytes_in": 0,
"cumulative_bytes_out": 112,
"cumulative_bytes": 112,
"tags": [],
"details": [
{
"address": "0.1.2.3",
"rate": "0b",
"rate_bits": 0,
"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_out": 272,
"rate_bits": 272, "rate_bits": 544,
"cumulative_bytes_in": 0, "cumulative_bytes_in": 68,
"cumulative_bytes_out": 68, "cumulative_bytes_out": 68,
"cumulative_bytes": 136,
"tags": [],
"details": [
{
"address": "0.1.2.3",
"rate": "272b",
"rate_bits": 272,
"cumulative": "68B",
"cumulative_bytes": 68, "cumulative_bytes": 68,
"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",
},
{
"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,
"tags": [],
"details": [
{
"address": "0.1.2.3",
"rate": "272b",
"rate_bits": 272,
"cumulative": "68B",
"cumulative_bytes": 68,
"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",
},
{
"address": "0.1.2.3",
"rate_bits_in": 0,
"rate_bits_out": 480,
"rate_bits": 480,
"cumulative_bytes_in": 0,
"cumulative_bytes_out": 120,
"cumulative_bytes": 120,
"tags": [], "tags": [],
"details": [ "details": [
{ {
@ -208,15 +176,23 @@ def generate_diagnostics_traffic_interface_paylaod():
"cumulative": "0B", "cumulative": "0B",
"cumulative_bytes": 0, "cumulative_bytes": 0,
"tags": ["local"], "tags": ["local"],
} },
{
"address": "0.1.2.3",
"rate": "0b",
"rate_bits": 0,
"cumulative": "0B",
"cumulative_bytes": 0,
"tags": ["local"],
},
], ],
"rname": "fake rname value", "rname": "fake value",
"rate_in": "0.0 b", "rate_in": "0.0 b",
"rate_out": "272.0 b", "rate_out": "480.0 b",
"rate": "272.0 b", "rate": "480.0 b",
"cumulative_in": "0.0 b", "cumulative_in": "0.0 b",
"cumulative_out": "68.0 b", "cumulative_out": "120.0 b",
"cumulative": "68.0 b", "cumulative": "120.0 b",
}, },
{ {
"address": "0.1.2.3", "address": "0.1.2.3",
@ -237,7 +213,7 @@ def generate_diagnostics_traffic_interface_paylaod():
"tags": ["local"], "tags": ["local"],
} }
], ],
"rname": "fake rname value", "rname": "fake 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",
@ -247,224 +223,6 @@ 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,14 +1,8 @@
import responses import responses
from opnsense_exporter.opnsense_api import ( from opnsense_exporter.opnsense_api import OPNSenseAPI
OPNSenseAPI,
OPNSenseRole,
OPNSenseTraffic,
OPNSenseTrafficMetric,
)
from .common import ( from .common import (
BACKUP_HOST,
LOGIN, LOGIN,
MAIN_HOST, MAIN_HOST,
PASSWORD, PASSWORD,
@ -26,10 +20,7 @@ def test_get_interface_vip_status_active():
) )
assert ( assert (
OPNSenseAPI(OPNSenseRole.MAIN, MAIN_HOST, LOGIN, PASSWORD) OPNSenseAPI(MAIN_HOST, LOGIN, PASSWORD).get_interface_vip_status() == "active"
.get_interface_vip_status()
.value
== "active"
) )
@ -42,9 +33,7 @@ def test_get_interface_vip_status_backup():
) )
assert ( assert (
OPNSenseAPI(OPNSenseRole.MAIN, MAIN_HOST, LOGIN, PASSWORD) OPNSenseAPI(MAIN_HOST, LOGIN, PASSWORD).get_interface_vip_status()
.get_interface_vip_status()
.value
== "hot_standby" == "hot_standby"
) )
@ -58,9 +47,7 @@ def test_get_interface_vip_status_mainteance_mode():
) )
assert ( assert (
OPNSenseAPI(OPNSenseRole.MAIN, MAIN_HOST, LOGIN, PASSWORD) OPNSenseAPI(MAIN_HOST, LOGIN, PASSWORD).get_interface_vip_status()
.get_interface_vip_status()
.value
== "maintenancemode" == "maintenancemode"
) )
@ -73,9 +60,7 @@ def test_get_interface_vip_status_unavailable_weird_case():
body=generate_get_vip_status_paylaod("MASTER", "BACKUP", False), body=generate_get_vip_status_paylaod("MASTER", "BACKUP", False),
) )
assert ( assert (
OPNSenseAPI(OPNSenseRole.MAIN, MAIN_HOST, LOGIN, PASSWORD) OPNSenseAPI(MAIN_HOST, LOGIN, PASSWORD).get_interface_vip_status()
.get_interface_vip_status()
.value
== "unavailable" == "unavailable"
) )
@ -89,65 +74,33 @@ def test_get_interface_vip_status_unavailable_rest_api_error():
status=404, status=404,
) )
assert ( assert (
OPNSenseAPI(OPNSenseRole.MAIN, MAIN_HOST, LOGIN, PASSWORD) OPNSenseAPI(MAIN_HOST, LOGIN, PASSWORD).get_interface_vip_status()
.get_interface_vip_status()
.value
== "unavailable" == "unavailable"
) )
@responses.activate @responses.activate
def test_get_traffic(): def test_get_wan_traffic():
responses.add( responses.add(
responses.GET, responses.GET,
f"https://{MAIN_HOST}/api/diagnostics/traffic/top/wan,lan", f"https://{MAIN_HOST}/api/diagnostics/traffic/top/wan",
body=generate_diagnostics_traffic_interface_paylaod(), body=generate_diagnostics_traffic_interface_paylaod(),
) )
assert OPNSenseAPI(OPNSenseRole.MAIN, MAIN_HOST, LOGIN, PASSWORD).get_traffic( assert OPNSenseAPI(MAIN_HOST, LOGIN, PASSWORD).get_wan_trafic() == (
"wan,lan" 20538,
) == [ 10034,
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 @responses.activate
def test_get_traffic_none(): def test_get_wan_traffic_none():
responses.add( responses.add(
responses.GET, responses.GET,
f"https://{MAIN_HOST}/api/diagnostics/traffic/top/test-not-found", f"https://{MAIN_HOST}/api/diagnostics/traffic/top/wan",
json={"error": "not found"}, json={"error": "not found"},
status=404, status=404,
) )
assert ( assert OPNSenseAPI(MAIN_HOST, LOGIN, PASSWORD).get_wan_trafic() == (
OPNSenseAPI(OPNSenseRole.MAIN, MAIN_HOST, LOGIN, PASSWORD).get_traffic( None,
"test-not-found" None,
) )
== []
)
@responses.activate
def test_get_traffic_empty_string():
rsp = responses.add(
responses.GET,
f"https://{MAIN_HOST}/api/diagnostics/traffic/top/",
json={"not": "called"},
)
assert (
OPNSenseAPI(OPNSenseRole.MAIN, MAIN_HOST, LOGIN, PASSWORD).get_traffic("") == []
)
assert rsp.call_count == 0
def test_labels():
assert OPNSenseAPI(OPNSenseRole.MAIN, MAIN_HOST, LOGIN, PASSWORD).labels == {
"role": "main",
"host": MAIN_HOST,
}
assert OPNSenseAPI(OPNSenseRole.BACKUP, BACKUP_HOST, LOGIN, PASSWORD).labels == {
"role": "backup",
"host": BACKUP_HOST,
}

View file

@ -1,10 +1,9 @@
from typing import List
from unittest import mock from unittest import mock
import responses import responses
from opnsense_exporter.opnsense_api import OPNSenseAPI, OPNSenseRole from opnsense_exporter.opnsense_api import OPNSenseAPI
from opnsense_exporter.server import OPNSensePrometheusExporter, run from opnsense_exporter.server import process_requests, run
from .common import ( from .common import (
BACKUP_HOST, BACKUP_HOST,
@ -18,57 +17,31 @@ 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: str = None _state = None
_state_calls: List[str] = [] count_state_calls = 0
def __init__(self): def state(self, state):
super().__init__() self.count_state_calls += 1
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: int = None value = None
_set_calls: List[int] = [] count_set_calls = 0
def __init__(self): def set(self, value):
super().__init__() self.count_set_calls += 1
self._set_calls = [] self.value = value
@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.start_server")
def test_parser(server_mock): def test_parser(server_mock):
with mock.patch( with mock.patch(
"sys.argv", "sys.argv",
@ -84,25 +57,20 @@ 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",
], ],
): ):
server = run() run()
server_mock.assert_called_once() server_mock.assert_called_once()
main, bck = server_mock.call_args.args
assert server.main.role == OPNSenseRole.MAIN assert main.login == "user-test"
assert server.main.host == "main.host" assert bck.login == "user-test"
assert server.main.login == "user-test" assert main.password == "pwd-test"
assert server.main.password == "pwd-test" assert bck.password == "pwd-test"
assert server.backup.role == OPNSenseRole.BACKUP assert main.host == "main.host"
assert server.backup.host == "backup.host" assert bck.host == "backup.host"
assert server.backup.login == "user-test" assert server_mock.call_args.kwargs["check_frequency"] == 15
assert server.backup.password == "pwd-test"
assert server.check_frequency == 15
assert server.interfaces == "efg,hij"
@responses.activate @responses.activate
@ -123,56 +91,49 @@ def test_process_requests():
body=generate_diagnostics_traffic_interface_paylaod(), body=generate_diagnostics_traffic_interface_paylaod(),
) )
opnsense_server_ha_state_mock = FakePromEnum() main_ha_state_mock = FakePromEnum()
opnsense_active_server_traffic_rate_mock = FakePromGauge() backup_ha_state_mock = FakePromEnum()
active_server_bytes_received_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( with mock.patch(
"opnsense_exporter.server.opnsense_server_ha_state", "opnsense_exporter.server.backup_ha_state", new=backup_ha_state_mock
new=opnsense_server_ha_state_mock,
): ):
with mock.patch( with mock.patch(
"opnsense_exporter.server.opnsense_active_server_traffic_rate", "opnsense_exporter.server.active_server_bytes_received",
new=opnsense_active_server_traffic_rate_mock, new=active_server_bytes_received_mock,
): ):
OPNSensePrometheusExporter( with mock.patch(
OPNSenseAPI(OPNSenseRole.MAIN, MAIN_HOST, LOGIN, PASSWORD), "opnsense_exporter.server.active_server_bytes_transmitted",
OPNSenseAPI(OPNSenseRole.BACKUP, BACKUP_HOST, LOGIN, PASSWORD), new=active_server_bytes_transmitted_mock,
"wan", ):
).process_requests() process_requests(
OPNSenseAPI(MAIN_HOST, LOGIN, PASSWORD),
OPNSenseAPI(BACKUP_HOST, LOGIN, PASSWORD),
)
assert opnsense_server_ha_state_mock.count_state_calls == 2 assert main_ha_state_mock._state == "active"
assert opnsense_server_ha_state_mock._labels_calls == [ assert main_ha_state_mock.count_state_calls == 1
{ assert main_ha_state_mock._labels == {"instance": "", "host": MAIN_HOST}
"instance": "",
"host": MAIN_HOST,
"role": "main",
},
{
"instance": "",
"host": BACKUP_HOST,
"role": "backup",
},
]
assert opnsense_server_ha_state_mock._state_calls == ["active", "hot_standby"]
assert opnsense_active_server_traffic_rate_mock.count_set_calls == 2 assert backup_ha_state_mock._state == "hot_standby"
assert opnsense_active_server_traffic_rate_mock._labels_calls == [ assert backup_ha_state_mock.count_state_calls == 1
{ assert backup_ha_state_mock._labels == {"instance": "", "host": BACKUP_HOST}
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": "", "instance": "",
"host": MAIN_HOST, "host": MAIN_HOST,
"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", }
"interface": "wan",
"metric": "rate_bits_out",
},
]
assert opnsense_active_server_traffic_rate_mock._set_calls == [101026, 86020]
@responses.activate @responses.activate
@ -193,56 +154,48 @@ def test_process_requests_backup_active():
body=generate_diagnostics_traffic_interface_paylaod(), body=generate_diagnostics_traffic_interface_paylaod(),
) )
opnsense_server_ha_state_mock = FakePromEnum() main_ha_state_mock = FakePromEnum()
opnsense_active_server_traffic_rate_mock = FakePromGauge() backup_ha_state_mock = FakePromEnum()
active_server_bytes_received_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( with mock.patch(
"opnsense_exporter.server.opnsense_server_ha_state", "opnsense_exporter.server.backup_ha_state", new=backup_ha_state_mock
new=opnsense_server_ha_state_mock,
): ):
with mock.patch( with mock.patch(
"opnsense_exporter.server.opnsense_active_server_traffic_rate", "opnsense_exporter.server.active_server_bytes_received",
new=opnsense_active_server_traffic_rate_mock, new=active_server_bytes_received_mock,
): ):
OPNSensePrometheusExporter( with mock.patch(
OPNSenseAPI(OPNSenseRole.MAIN, MAIN_HOST, LOGIN, PASSWORD), "opnsense_exporter.server.active_server_bytes_transmitted",
OPNSenseAPI(OPNSenseRole.BACKUP, BACKUP_HOST, LOGIN, PASSWORD), new=active_server_bytes_transmitted_mock,
"wan", ):
).process_requests() process_requests(
OPNSenseAPI(MAIN_HOST, LOGIN, PASSWORD),
OPNSenseAPI(BACKUP_HOST, LOGIN, PASSWORD),
)
assert main_ha_state_mock._state == "maintenancemode"
assert main_ha_state_mock.count_state_calls == 1
assert main_ha_state_mock._labels == {"instance": "", "host": MAIN_HOST}
assert opnsense_server_ha_state_mock.count_state_calls == 2 assert backup_ha_state_mock._state == "active"
assert opnsense_server_ha_state_mock._labels_calls == [ assert backup_ha_state_mock.count_state_calls == 1
{ assert backup_ha_state_mock._labels == {"instance": "", "host": BACKUP_HOST}
"instance": "",
"host": MAIN_HOST,
"role": "main",
},
{
"instance": "",
"host": BACKUP_HOST,
"role": "backup",
},
]
assert opnsense_server_ha_state_mock._state_calls == ["maintenancemode", "active"]
assert opnsense_active_server_traffic_rate_mock.count_set_calls == 2 assert active_server_bytes_received_mock.value == 20538
opnsense_active_server_traffic_rate_mock._labels_calls == [ assert active_server_bytes_received_mock.count_set_calls == 1
{ assert active_server_bytes_received_mock._labels == {
"instance": "", "instance": "",
"host": BACKUP_HOST, "host": BACKUP_HOST,
"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", }
"interface": "wan",
"metric": "rate_bits_out",
},
]
assert opnsense_active_server_traffic_rate_mock._set_calls == [101026, 86020]
@responses.activate @responses.activate
@ -264,42 +217,38 @@ def test_process_no_active():
body=generate_diagnostics_traffic_interface_paylaod(), body=generate_diagnostics_traffic_interface_paylaod(),
) )
opnsense_server_ha_state_mock = FakePromEnum() main_ha_state_mock = FakePromEnum()
opnsense_active_server_traffic_rate_mock = FakePromGauge() backup_ha_state_mock = FakePromEnum()
active_server_bytes_received_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( with mock.patch(
"opnsense_exporter.server.opnsense_server_ha_state", "opnsense_exporter.server.backup_ha_state", new=backup_ha_state_mock
new=opnsense_server_ha_state_mock,
): ):
with mock.patch( with mock.patch(
"opnsense_exporter.server.opnsense_active_server_traffic_rate", "opnsense_exporter.server.active_server_bytes_received",
new=opnsense_active_server_traffic_rate_mock, new=active_server_bytes_received_mock,
): ):
OPNSensePrometheusExporter( with mock.patch(
OPNSenseAPI(OPNSenseRole.MAIN, MAIN_HOST, LOGIN, PASSWORD), "opnsense_exporter.server.active_server_bytes_transmitted",
OPNSenseAPI(OPNSenseRole.BACKUP, BACKUP_HOST, LOGIN, PASSWORD), new=active_server_bytes_transmitted_mock,
"wan", ):
).process_requests() process_requests(
OPNSenseAPI(MAIN_HOST, LOGIN, PASSWORD),
OPNSenseAPI(BACKUP_HOST, LOGIN, PASSWORD),
)
assert opnsense_server_ha_state_mock.count_state_calls == 2 assert main_ha_state_mock._state == "maintenancemode"
assert opnsense_server_ha_state_mock._labels_calls == [ assert main_ha_state_mock.count_state_calls == 1
{ assert main_ha_state_mock._labels == {"instance": "", "host": MAIN_HOST}
"instance": "",
"host": MAIN_HOST,
"role": "main",
},
{
"instance": "",
"host": BACKUP_HOST,
"role": "backup",
},
]
assert opnsense_server_ha_state_mock._state_calls == [
"maintenancemode",
"unavailable",
]
assert opnsense_active_server_traffic_rate_mock.count_set_calls == 0 assert backup_ha_state_mock._state == "unavailable"
assert backup_ha_state_mock.count_state_calls == 1
assert backup_ha_state_mock._labels == {"instance": "", "host": BACKUP_HOST}
assert active_server_bytes_received_mock.count_set_calls == 0
assert active_server_bytes_transmitted_mock.count_set_calls == 0
@responses.activate @responses.activate
@ -321,36 +270,34 @@ def test_process_with_falsy_value():
status=404, status=404,
) )
opnsense_server_ha_state_mock = FakePromEnum() main_ha_state_mock = FakePromEnum()
opnsense_active_server_traffic_rate_mock = FakePromGauge() backup_ha_state_mock = FakePromEnum()
active_server_bytes_received_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( with mock.patch(
"opnsense_exporter.server.opnsense_server_ha_state", "opnsense_exporter.server.backup_ha_state", new=backup_ha_state_mock
new=opnsense_server_ha_state_mock,
): ):
with mock.patch( with mock.patch(
"opnsense_exporter.server.opnsense_active_server_traffic_rate", "opnsense_exporter.server.active_server_bytes_received",
new=opnsense_active_server_traffic_rate_mock, new=active_server_bytes_received_mock,
): ):
OPNSensePrometheusExporter( with mock.patch(
OPNSenseAPI(OPNSenseRole.MAIN, MAIN_HOST, LOGIN, PASSWORD), "opnsense_exporter.server.active_server_bytes_transmitted",
OPNSenseAPI(OPNSenseRole.BACKUP, BACKUP_HOST, LOGIN, PASSWORD), new=active_server_bytes_transmitted_mock,
"wan", ):
).process_requests() process_requests(
OPNSenseAPI(MAIN_HOST, LOGIN, PASSWORD),
OPNSenseAPI(BACKUP_HOST, LOGIN, PASSWORD),
)
assert main_ha_state_mock._state == "active"
assert main_ha_state_mock.count_state_calls == 1
assert main_ha_state_mock._labels == {"instance": "", "host": MAIN_HOST}
assert opnsense_server_ha_state_mock.count_state_calls == 2 assert backup_ha_state_mock.count_state_calls == 1
assert opnsense_server_ha_state_mock._labels_calls == [ assert backup_ha_state_mock._state == "hot_standby"
{ assert backup_ha_state_mock._labels == {"instance": "", "host": BACKUP_HOST}
"instance": "",
"host": MAIN_HOST,
"role": "main",
},
{
"instance": "",
"host": BACKUP_HOST,
"role": "backup",
},
]
assert opnsense_server_ha_state_mock._state_calls == ["active", "hot_standby"]
assert opnsense_active_server_traffic_rate_mock.count_set_calls == 0 assert active_server_bytes_received_mock.count_set_calls == 0
assert active_server_bytes_transmitted_mock.count_set_calls == 0