2023-09-01 13:27:55 +02:00
|
|
|
import argparse
|
2023-09-04 00:06:17 +02:00
|
|
|
import logging
|
2023-09-01 13:27:55 +02:00
|
|
|
import os
|
|
|
|
import socket
|
|
|
|
import time
|
|
|
|
|
|
|
|
from dotenv import load_dotenv
|
|
|
|
from prometheus_client import Enum, Gauge, start_http_server
|
|
|
|
|
2023-09-04 00:06:17 +02:00
|
|
|
from opnsense_exporter.opnsense_api import OPNSenseAPI, OPNSenseHAState, OPNSenseRole
|
2023-09-01 13:27:55 +02:00
|
|
|
|
2023-09-04 00:06:17 +02:00
|
|
|
logger = logging.getLogger(__name__)
|
2023-09-01 13:27:55 +02:00
|
|
|
load_dotenv()
|
|
|
|
|
2023-09-04 00:06:17 +02:00
|
|
|
HA_STATES = [enum.value for enum in list(OPNSenseHAState)]
|
|
|
|
|
|
|
|
|
|
|
|
opnsense_server_ha_state = Enum(
|
|
|
|
"opnsense_server_ha_state",
|
|
|
|
"OPNSense server HA state",
|
|
|
|
[
|
|
|
|
"instance",
|
|
|
|
"host",
|
|
|
|
"role",
|
|
|
|
],
|
|
|
|
states=HA_STATES,
|
|
|
|
)
|
|
|
|
|
2023-09-03 23:38:34 +02:00
|
|
|
opnsense_active_server_traffic_rate = Gauge(
|
|
|
|
"opnsense_active_server_traffic_rate",
|
|
|
|
"Active OPNSense server bytes in/out per interface",
|
2023-09-01 16:23:20 +02:00
|
|
|
[
|
|
|
|
"instance",
|
|
|
|
"host",
|
2023-09-03 21:52:19 +02:00
|
|
|
"role",
|
2023-09-03 23:38:34 +02:00
|
|
|
"interface",
|
|
|
|
"metric",
|
2023-09-01 16:23:20 +02:00
|
|
|
],
|
2023-09-01 13:27:55 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
2023-09-03 22:08:24 +02:00
|
|
|
class OPNSensePrometheusExporter:
|
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
main: OPNSenseAPI,
|
|
|
|
backup: OPNSenseAPI,
|
2023-09-03 23:38:34 +02:00
|
|
|
interfaces,
|
2023-09-03 22:08:24 +02:00
|
|
|
exporter_instance: str = "",
|
|
|
|
check_frequency: int = 1,
|
|
|
|
):
|
|
|
|
self.main = main
|
|
|
|
self.backup = backup
|
2023-09-03 23:38:34 +02:00
|
|
|
self.interfaces = interfaces
|
2023-09-03 22:08:24 +02:00
|
|
|
self.exporter_instance = exporter_instance
|
|
|
|
self.check_frequency = check_frequency
|
2023-09-01 13:27:55 +02:00
|
|
|
|
2023-09-03 22:08:24 +02:00
|
|
|
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()
|
2023-09-04 00:21:56 +02:00
|
|
|
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)
|
2023-09-03 22:08:24 +02:00
|
|
|
active_opnsense = None
|
2023-09-04 00:06:17 +02:00
|
|
|
if main_state == OPNSenseHAState.ACTIVE:
|
2023-09-03 22:08:24 +02:00
|
|
|
active_opnsense = self.main
|
2023-09-04 00:06:17 +02:00
|
|
|
if backup_sate == OPNSenseHAState.ACTIVE:
|
2023-09-03 22:08:24 +02:00
|
|
|
active_opnsense = self.backup
|
|
|
|
if active_opnsense:
|
2023-09-03 23:38:34 +02:00
|
|
|
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)
|
2023-09-01 13:27:55 +02:00
|
|
|
|
2023-09-04 00:32:02 +02:00
|
|
|
def start_server(self, port=8000):
|
2023-09-03 22:08:24 +02:00
|
|
|
# Start up the server to expose the metrics.
|
2023-09-04 00:32:02 +02:00
|
|
|
start_http_server(port)
|
|
|
|
logger.info("listen port %s", port)
|
2023-09-03 22:08:24 +02:00
|
|
|
# Generate some requests.
|
|
|
|
while True:
|
|
|
|
self.process_requests()
|
|
|
|
time.sleep(self.check_frequency)
|
2023-09-01 13:27:55 +02:00
|
|
|
|
|
|
|
|
|
|
|
def run():
|
|
|
|
parser = argparse.ArgumentParser(
|
|
|
|
description="OPNSense prometheus exporter",
|
|
|
|
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
"--check-frequency-seconds",
|
|
|
|
"-c",
|
|
|
|
type=int,
|
|
|
|
dest="frequency",
|
|
|
|
default=int(os.environ.get("CHECK_FREQUENCY_SECONDS", 2)),
|
|
|
|
help="How often (in seconds) this server requests OPNSense servers",
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
"--main-host",
|
|
|
|
"-m",
|
|
|
|
type=str,
|
|
|
|
dest="main",
|
|
|
|
default=os.environ.get("OPNSENSE_MAIN_HOST", None),
|
|
|
|
help="MAIN OPNsense server that should be in `active` state in normal configuration.",
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
"--backup-host",
|
|
|
|
"-b",
|
|
|
|
type=str,
|
|
|
|
dest="backup",
|
|
|
|
default=os.environ.get("OPNSENSE_BACKUP_HOST", None),
|
|
|
|
help="BACKUP OPNsense server that should be `hot_standby` state in normal configuration.",
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
"--opnsense-user",
|
|
|
|
"-u",
|
|
|
|
type=str,
|
|
|
|
dest="user",
|
|
|
|
default=os.environ.get("OPNSENSE_USERNAME", None),
|
|
|
|
help="OPNsense user. Expect to be the same on MAIN and BACKUP servers",
|
|
|
|
)
|
2023-09-03 23:38:34 +02:00
|
|
|
parser.add_argument(
|
|
|
|
"--opnsense-interfaces",
|
|
|
|
"-i",
|
|
|
|
type=str,
|
|
|
|
dest="interfaces",
|
|
|
|
default=os.environ.get("OPNSENSE_INTERFACES", "wan,lan"),
|
2023-09-06 09:09:41 +02:00
|
|
|
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.",
|
2023-09-03 23:38:34 +02:00
|
|
|
)
|
2023-09-06 10:39:54 +02:00
|
|
|
parser.add_argument(
|
|
|
|
"--opnsense-timeout-sec-get-vip-status",
|
|
|
|
type=int,
|
|
|
|
dest="get_vip_status_timeout_sec",
|
|
|
|
default=int(os.environ.get("OPNSENSE_TIMEOUT_SEC_GET_VIP_STATUS", 5)),
|
|
|
|
help="Allow to configure timeout while requesting OPNSense REST API /api/diagnostics/interface/get_vip_status/",
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
"--opnsense-timeout-sec-get-traffic",
|
|
|
|
type=int,
|
|
|
|
dest="get_traffic_timeout_sec",
|
|
|
|
default=int(os.environ.get("OPNSENSE_TIMEOUT_SEC_GET_TRAFFIC", 15)),
|
|
|
|
help="Allow to configure timeout while requesting OPNSense REST API /api/diagnostics/traffic/top/[INTERFACES]",
|
|
|
|
)
|
2023-09-01 13:27:55 +02:00
|
|
|
parser.add_argument(
|
|
|
|
"--opnsense-password",
|
|
|
|
"-p",
|
|
|
|
type=str,
|
|
|
|
dest="password",
|
|
|
|
default=os.environ.get("OPNSENSE_PASSWORD", None),
|
|
|
|
help="OPNsense password. Expect to be the same on MAIN and BACKUP servers",
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
"--prometheus-instance",
|
|
|
|
dest="prom_instance",
|
|
|
|
type=str,
|
|
|
|
default=socket.gethostname(),
|
|
|
|
help=(
|
2023-09-01 16:23:20 +02:00
|
|
|
"Exporter Instance name, default value computed with hostname "
|
2023-09-01 13:27:55 +02:00
|
|
|
"where the server is running. Use to set the instance label."
|
|
|
|
),
|
|
|
|
)
|
|
|
|
|
|
|
|
arguments = parser.parse_args()
|
2023-09-03 22:08:24 +02:00
|
|
|
|
|
|
|
server = OPNSensePrometheusExporter(
|
2023-09-03 21:52:19 +02:00
|
|
|
OPNSenseAPI(
|
2023-09-06 10:39:54 +02:00
|
|
|
OPNSenseRole.MAIN,
|
|
|
|
arguments.main,
|
|
|
|
arguments.user,
|
|
|
|
arguments.password,
|
|
|
|
get_vip_status_timeout_sec=arguments.get_vip_status_timeout_sec,
|
|
|
|
get_traffic_timeout_sec=arguments.get_traffic_timeout_sec,
|
2023-09-03 21:52:19 +02:00
|
|
|
),
|
|
|
|
OPNSenseAPI(
|
2023-09-06 10:39:54 +02:00
|
|
|
OPNSenseRole.BACKUP,
|
|
|
|
arguments.backup,
|
|
|
|
arguments.user,
|
|
|
|
arguments.password,
|
|
|
|
get_vip_status_timeout_sec=arguments.get_vip_status_timeout_sec,
|
|
|
|
get_traffic_timeout_sec=arguments.get_traffic_timeout_sec,
|
2023-09-03 21:52:19 +02:00
|
|
|
),
|
2023-09-03 23:38:34 +02:00
|
|
|
arguments.interfaces,
|
2023-09-01 13:27:55 +02:00
|
|
|
check_frequency=arguments.frequency,
|
2023-09-01 16:23:20 +02:00
|
|
|
exporter_instance=arguments.prom_instance,
|
2023-09-01 13:27:55 +02:00
|
|
|
)
|
2023-09-03 22:08:24 +02:00
|
|
|
server.start_server()
|
|
|
|
|
|
|
|
# return the server instance mainly for test purpose
|
|
|
|
return server
|