diff --git a/docs/nixos-options.md b/docs/nixos-options.md index 596cc5e..296d9c6 100644 --- a/docs/nixos-options.md +++ b/docs/nixos-options.md @@ -33,7 +33,7 @@ List of enabled check sets\. Each check set is a module which checks something a *Type:* -list of (one of “start”, “stop”, “cpu”, “ram”, “temp”, “vulnix”) +list of (one of “start”, “stop”, “remind”, “cpu”, “ram”, “temp”, “net”, “vulnix”) @@ -52,7 +52,7 @@ CPU load percentage for a critical alert to be sent\. Null means never generate *Type:* -null or floating point number +null or (positive integer or floating point number, meaning >0) @@ -73,7 +73,7 @@ CPU load percentage for a warning alert to be sent\. Null means never generate a *Type:* -null or floating point number +null or (positive integer or floating point number, meaning >0) @@ -85,6 +85,166 @@ null or floating point number +## services\.lego-monitoring\.checks\.net\.interfaces + + + +Per-interface configuration of IO byte thresholds\. + + + +*Type:* +attribute set of (submodule) + + + +*Default:* +` { } ` + + + +*Example:* + +``` +{ + br0 = { + warningThresholdCombBytes = 700 * 1024 * 128; # 700 Megabits + criticalThresholdCombBytes = 1 * 1024 * 1024 * 128; # 1 Gigabit + }; +} +``` + +*Declared by:* + - [modules/options\.nix](../modules/options.nix) + + + +## services\.lego-monitoring\.checks\.net\.interfaces\.\\.criticalThresholdCombBytes + + + +Combined (sent + received) bytes per second threshold for a critical alert to be sent\. If null, this threshold is disabled and not checked\. + + + +*Type:* +null or (positive integer, meaning >0) + + + +*Default:* +` null ` + +*Declared by:* + - [modules/options\.nix](../modules/options.nix) + + + +## services\.lego-monitoring\.checks\.net\.interfaces\.\\.criticalThresholdRecvBytes + + + +Received bytes per second threshold for a critical alert to be sent\. If null, this threshold is disabled and not checked\. + + + +*Type:* +null or (positive integer, meaning >0) + + + +*Default:* +` null ` + +*Declared by:* + - [modules/options\.nix](../modules/options.nix) + + + +## services\.lego-monitoring\.checks\.net\.interfaces\.\\.criticalThresholdSentBytes + + + +Sent bytes per second threshold for a critical alert to be sent\. If null, this threshold is disabled and not checked\. + + + +*Type:* +null or (positive integer, meaning >0) + + + +*Default:* +` null ` + +*Declared by:* + - [modules/options\.nix](../modules/options.nix) + + + +## services\.lego-monitoring\.checks\.net\.interfaces\.\\.warningThresholdCombBytes + + + +Combined (sent + received) bytes per second threshold for a warning alert to be sent\. If null, this threshold is disabled and not checked\. + + + +*Type:* +null or (positive integer, meaning >0) + + + +*Default:* +` null ` + +*Declared by:* + - [modules/options\.nix](../modules/options.nix) + + + +## services\.lego-monitoring\.checks\.net\.interfaces\.\\.warningThresholdRecvBytes + + + +Received bytes per second threshold for a warning alert to be sent\. If null, this threshold is disabled and not checked\. + + + +*Type:* +null or (positive integer, meaning >0) + + + +*Default:* +` null ` + +*Declared by:* + - [modules/options\.nix](../modules/options.nix) + + + +## services\.lego-monitoring\.checks\.net\.interfaces\.\\.warningThresholdSentBytes + + + +Sent bytes per second threshold for a warning alert to be sent\. If null, this threshold is disabled and not checked\. + + + +*Type:* +null or (positive integer, meaning >0) + + + +*Default:* +` null ` + +*Declared by:* + - [modules/options\.nix](../modules/options.nix) + + + ## services\.lego-monitoring\.checks\.ram\.criticalPercentage @@ -94,7 +254,7 @@ RAM usage percentage for a critical alert to be sent\. Null means never generate *Type:* -null or floating point number +null or (positive integer or floating point number, meaning >0) @@ -115,7 +275,7 @@ RAM usage percentage for a warning alert to be sent\. Null means never generate *Type:* -null or floating point number +null or (positive integer or floating point number, meaning >0) @@ -265,7 +425,7 @@ Critical temperature threshold\. *Type:* -null or floating point number +null or (positive integer or floating point number, meaning >0) @@ -307,7 +467,7 @@ Warning temperature threshold\. *Type:* -null or floating point number +null or (positive integer or floating point number, meaning >0) diff --git a/flake.lock b/flake.lock index 3738870..1cc4fe8 100644 --- a/flake.lock +++ b/flake.lock @@ -16,6 +16,22 @@ "type": "github" } }, + "nixpkgs-29335f": { + "locked": { + "lastModified": 1745804731, + "narHash": "sha256-v/sK3AS0QKu/Tu5sHIfddiEHCvrbNYPv8X10Fpux68g=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "29335f23bea5e34228349ea739f31ee79e267b88", + "type": "github" + }, + "original": { + "owner": "nixos", + "repo": "nixpkgs", + "rev": "29335f23bea5e34228349ea739f31ee79e267b88", + "type": "github" + } + }, "pyproject-build-systems": { "inputs": { "nixpkgs": [ @@ -65,6 +81,7 @@ "root": { "inputs": { "nixpkgs": "nixpkgs", + "nixpkgs-29335f": "nixpkgs-29335f", "pyproject-build-systems": "pyproject-build-systems", "pyproject-nix": "pyproject-nix", "uv2nix": "uv2nix" @@ -80,11 +97,11 @@ ] }, "locked": { - "lastModified": 1745697651, - "narHash": "sha256-r4A/fkiCenEapHkjJWPiNUZEfviuXMCr6mRozJ5dC4o=", + "lastModified": 1749170547, + "narHash": "sha256-zOptuFhTr9P0A+unFaOBFx5E5T6yx0qE8VrUGVrM96U=", "owner": "pyproject-nix", "repo": "uv2nix", - "rev": "cb6508484d534dafd097713b575f2aebc3417de0", + "rev": "7ae60727d4fc2e41aefd30da665e4f92ba8298f1", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 7fb2bd2..28f27fa 100644 --- a/flake.nix +++ b/flake.nix @@ -1,6 +1,8 @@ { inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05"; + # this is for uv 0.6.17, 0.7.0 has a change uv2nix doesn't yet support: https://github.com/astral-sh/uv/pull/13176 + nixpkgs-29335f.url = "github:nixos/nixpkgs/29335f23bea5e34228349ea739f31ee79e267b88"; pyproject-nix = { url = "github:pyproject-nix/pyproject.nix"; @@ -25,6 +27,7 @@ { self, nixpkgs, + nixpkgs-29335f, uv2nix, pyproject-nix, pyproject-build-systems, @@ -78,6 +81,7 @@ # This example is only using x86_64-linux pkgs = nixpkgs.legacyPackages.x86_64-linux; + pkgs-29335f = nixpkgs-29335f.legacyPackages.x86_64-linux; # Use Python 3.12 from nixpkgs python = pkgs.python312; @@ -125,7 +129,7 @@ impure = pkgs.mkShell { packages = [ python - pkgs.uv + pkgs-29335f.uv ]; env = { @@ -205,7 +209,7 @@ pkgs.mkShell { packages = [ virtualenv - pkgs.uv + pkgs-29335f.uv ]; env = { diff --git a/modules/default.nix b/modules/default.nix index 1cff5ad..4dd838d 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -60,6 +60,15 @@ package: warning_percentage = warningPercentage; critical_percentage = criticalPercentage; }; + + net.interfaces = lib.mapAttrs (_: interfaceCfg: { + warning_threshold_sent_bytes = interfaceCfg.warningThresholdSentBytes; + critical_threshold_sent_bytes = interfaceCfg.criticalThresholdSentBytes; + warning_threshold_recv_bytes = interfaceCfg.warningThresholdRecvBytes; + critical_threshold_recv_bytes = interfaceCfg.warningThresholdRecvBytes; + warning_threshold_comb_bytes = interfaceCfg.warningThresholdCombBytes; + critical_threshold_comb_bytes = interfaceCfg.criticalThresholdCombBytes; + }) cfg.checks.net.interfaces; }; }; in lib.mkIf cfg.enable { diff --git a/modules/options.nix b/modules/options.nix index b4984d6..6cf9a24 100644 --- a/modules/options.nix +++ b/modules/options.nix @@ -6,6 +6,7 @@ let tempSensorOptions = (import ./suboptions/tempSensorOptions.nix) { inherit lib; }; vulnixWhitelistRule = (import ./suboptions/vulnixWhitelistRule.nix) { inherit lib; }; + netInterfaceOptions = (import ./suboptions/netInterfaceOptions.nix) { inherit lib; }; in { options.services.lego-monitoring = { @@ -32,6 +33,7 @@ in "cpu" "ram" "temp" + "net" "vulnix" ]); @@ -96,12 +98,12 @@ in cpu = { warningPercentage = lib.mkOption { - type = lib.types.nullOr lib.types.float; + type = lib.types.nullOr lib.types.numbers.positive; default = 80.0; description = "CPU load percentage for a warning alert to be sent. Null means never generate a CPU warning alert."; }; criticalPercentage = lib.mkOption { - type = lib.types.nullOr lib.types.float; + type = lib.types.nullOr lib.types.numbers.positive; default = 90.0; description = "CPU load percentage for a critical alert to be sent. Null means never generate a CPU critical alert."; }; @@ -109,16 +111,31 @@ in ram = { warningPercentage = lib.mkOption { - type = lib.types.nullOr lib.types.float; + type = lib.types.nullOr lib.types.numbers.positive; default = 80.0; description = "RAM usage percentage for a warning alert to be sent. Null means never generate a RAM warning alert."; }; criticalPercentage = lib.mkOption { - type = lib.types.nullOr lib.types.float; + type = lib.types.nullOr lib.types.numbers.positive; default = 90.0; description = "RAM usage percentage for a critical alert to be sent. Null means never generate a RAM critical alert."; }; }; + + net = { + interfaces = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule netInterfaceOptions); + default = { }; + description = "Per-interface configuration of IO byte thresholds."; + example = lib.literalExpression '' + { + br0 = { + warningThresholdCombBytes = 700 * 1024 * 128; # 700 Megabits + criticalThresholdCombBytes = 1 * 1024 * 1024 * 128; # 1 Gigabit + }; + }''; + }; + }; }; }; } diff --git a/modules/suboptions/netInterfaceOptions.nix b/modules/suboptions/netInterfaceOptions.nix new file mode 100644 index 0000000..7abd4cf --- /dev/null +++ b/modules/suboptions/netInterfaceOptions.nix @@ -0,0 +1,39 @@ +{ + lib, + ... +}: + +{ + options = { + warningThresholdSentBytes = lib.mkOption { + type = lib.types.nullOr lib.types.ints.positive; + default = null; + description = "Sent bytes per second threshold for a warning alert to be sent. If null, this threshold is disabled and not checked."; + }; + criticalThresholdSentBytes = lib.mkOption { + type = lib.types.nullOr lib.types.ints.positive; + default = null; + description = "Sent bytes per second threshold for a critical alert to be sent. If null, this threshold is disabled and not checked."; + }; + warningThresholdRecvBytes = lib.mkOption { + type = lib.types.nullOr lib.types.ints.positive; + default = null; + description = "Received bytes per second threshold for a warning alert to be sent. If null, this threshold is disabled and not checked."; + }; + criticalThresholdRecvBytes = lib.mkOption { + type = lib.types.nullOr lib.types.ints.positive; + default = null; + description = "Received bytes per second threshold for a critical alert to be sent. If null, this threshold is disabled and not checked."; + }; + warningThresholdCombBytes = lib.mkOption { + type = lib.types.nullOr lib.types.ints.positive; + default = null; + description = "Combined (sent + received) bytes per second threshold for a warning alert to be sent. If null, this threshold is disabled and not checked."; + }; + criticalThresholdCombBytes = lib.mkOption { + type = lib.types.nullOr lib.types.ints.positive; + default = null; + description = "Combined (sent + received) bytes per second threshold for a critical alert to be sent. If null, this threshold is disabled and not checked."; + }; + }; +} diff --git a/modules/suboptions/tempSensorOptions.nix b/modules/suboptions/tempSensorOptions.nix index 31d72fc..c986fe7 100644 --- a/modules/suboptions/tempSensorOptions.nix +++ b/modules/suboptions/tempSensorOptions.nix @@ -16,12 +16,12 @@ let description = "Whether this reading is enabled."; }; warningTemp = lib.mkOption { - type = lib.types.nullOr lib.types.float; + type = lib.types.nullOr lib.types.numbers.positive; default = null; description = "Warning temperature threshold."; }; criticalTemp = lib.mkOption { - type = lib.types.nullOr lib.types.float; + type = lib.types.nullOr lib.types.numbers.positive; default = null; description = "Critical temperature threshold."; }; diff --git a/pyproject.toml b/pyproject.toml index dcb602a..b28402e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,6 +6,7 @@ readme = "README.md" requires-python = ">=3.12" dependencies = [ "alt-utils>=0.0.8", + "humanize>=4.12.3", "psutil>=7.0.0", "telethon>=1.40.0", ] diff --git a/src/lego_monitoring/__init__.py b/src/lego_monitoring/__init__.py index aafc1ca..655e102 100644 --- a/src/lego_monitoring/__init__.py +++ b/src/lego_monitoring/__init__.py @@ -82,6 +82,9 @@ async def async_main(): is_reminder=True, ) ], + check_sets.NET: [ + IntervalChecker(checks.NetIOTracker().net_check, interval=datetime.timedelta(minutes=5), persistent=True) + ], } checkers = [] diff --git a/src/lego_monitoring/alerting/alert.py b/src/lego_monitoring/alerting/alert.py index a593d60..88d758b 100644 --- a/src/lego_monitoring/alerting/alert.py +++ b/src/lego_monitoring/alerting/alert.py @@ -1,4 +1,5 @@ -from dataclasses import dataclass +from dataclasses import dataclass, field +from datetime import datetime from .enum import AlertType, Severity @@ -8,3 +9,4 @@ class Alert: alert_type: AlertType message: str severity: Severity + created: datetime = field(default_factory=datetime.now) diff --git a/src/lego_monitoring/alerting/enum.py b/src/lego_monitoring/alerting/enum.py index 003398b..d014145 100644 --- a/src/lego_monitoring/alerting/enum.py +++ b/src/lego_monitoring/alerting/enum.py @@ -3,13 +3,16 @@ from enum import IntEnum, StrEnum class AlertType(StrEnum): BOOT = "BOOT" - CPU = "CPU" ERROR = "ERROR" + TEST = "TEST" + REMIND = "REMIND" + + CPU = "CPU" + NET = "NET" RAM = "RAM" TEMP = "TEMP" - TEST = "TEST" + VULN = "VULN" - REMIND = "REMIND" # LOGIN = "LOGIN" # SMART = "SMART" # TODO # RAID = "RAID" diff --git a/src/lego_monitoring/alerting/sender.py b/src/lego_monitoring/alerting/sender.py index b85dbff..53f09ed 100644 --- a/src/lego_monitoring/alerting/sender.py +++ b/src/lego_monitoring/alerting/sender.py @@ -18,8 +18,12 @@ async def get_client() -> TelegramClient: def format_message(alert: Alert, note: str) -> str: severity_emoji = SEVERITY_TO_EMOJI[alert.severity] - note_formatted = f" - {note}" if note else "" - message = f"{severity_emoji} {alert.alert_type} Alert{note_formatted}\n{alert.message}" + note_formatted = f"{note}, " if note else "" + if "ongoing" in note_formatted: + note_formatted += f"since {alert.created.isoformat()}" + else: + note_formatted += f"at {alert.created.isoformat()}" + message = f"{severity_emoji} {alert.alert_type} Alert - {note_formatted}\n{alert.message}" return message diff --git a/src/lego_monitoring/checks/__init__.py b/src/lego_monitoring/checks/__init__.py index f076917..62a11da 100644 --- a/src/lego_monitoring/checks/__init__.py +++ b/src/lego_monitoring/checks/__init__.py @@ -1,4 +1,5 @@ from .cpu import cpu_check +from .net import NetIOTracker from .ram import ram_check from .remind import remind_check from .temp import temp_check diff --git a/src/lego_monitoring/checks/cpu.py b/src/lego_monitoring/checks/cpu.py index ae335e9..dc19ecf 100644 --- a/src/lego_monitoring/checks/cpu.py +++ b/src/lego_monitoring/checks/cpu.py @@ -10,19 +10,19 @@ IS_TESTING = False def cpu_check() -> list[Alert]: percentage = cpu_percent() config = cvars.config.get().checks.cpu - if config.critical_percentage and (IS_TESTING or percentage > config.critical_percentage): + if config.critical_percentage and (IS_TESTING or percentage >= config.critical_percentage): return [ Alert( alert_type=AlertType.CPU, - message=f"CPU load: {percentage:.2f}% > {config.critical_percentage:.2f}%", + message=f"CPU load: {percentage:.2f}% >= {config.critical_percentage:.2f}%", severity=Severity.CRITICAL, ) ] - elif config.warning_percentage and (IS_TESTING or percentage > config.warning_percentage): + elif config.warning_percentage and (IS_TESTING or percentage >= config.warning_percentage): return [ Alert( alert_type=AlertType.CPU, - message=f"CPU load: {percentage:.2f}% > {config.warning_percentage:.2f}%", + message=f"CPU load: {percentage:.2f}% >= {config.warning_percentage:.2f}%", severity=Severity.WARNING, ) ] diff --git a/src/lego_monitoring/checks/net.py b/src/lego_monitoring/checks/net.py new file mode 100644 index 0000000..62c59c8 --- /dev/null +++ b/src/lego_monitoring/checks/net.py @@ -0,0 +1,87 @@ +from dataclasses import dataclass, field +from typing import Optional + +from humanize import naturalsize +from psutil import net_io_counters + +from lego_monitoring.alerting.alert import Alert +from lego_monitoring.alerting.enum import AlertType, Severity +from lego_monitoring.core import cvars + +IS_TESTING = False +SECONDS_BETWEEN_CHECKS = 5 * 60 + + +@dataclass +class NetIOTracker: + sent_per_interface: dict[str, int] = field(default_factory=dict, init=False) + recv_per_interface: dict[str, int] = field(default_factory=dict, init=False) + + @staticmethod + def check_threshold( + current_stat_bytes_per_sec: float, + critical_threshold: Optional[int], + warning_threshold: Optional[int], + stat_name: str, + interface: str, + ) -> Optional[Alert]: + if critical_threshold and (IS_TESTING or current_stat_bytes_per_sec >= critical_threshold): + current_stat_natural = naturalsize(current_stat_bytes_per_sec, binary=True) + critical_threshold_natural = naturalsize(critical_threshold, binary=True) + return Alert( + alert_type=AlertType.NET, + message=f"Interface {interface} {stat_name} {current_stat_natural}/s >= {critical_threshold_natural}/s", + severity=Severity.CRITICAL, + ) + elif warning_threshold and (IS_TESTING or current_stat_bytes_per_sec >= warning_threshold): + current_stat_natural = naturalsize(current_stat_bytes_per_sec, binary=True) + warning_threshold_natural = naturalsize(warning_threshold, binary=True) + return Alert( + alert_type=AlertType.NET, + message=f"Interface {interface} {stat_name} {current_stat_natural}/s >= {warning_threshold_natural}/s", + severity=Severity.WARNING, + ) + + def net_check(self) -> list[Alert]: + alerts = [] + current_stats = net_io_counters(pernic=True) + config = cvars.config.get().checks.net + for interface, thresholds in config.interfaces.items(): + if interface in self.sent_per_interface and interface in self.recv_per_interface: + sent_since_last_check_per_sec = ( + current_stats[interface].bytes_sent - self.sent_per_interface[interface] + ) / SECONDS_BETWEEN_CHECKS + recv_since_last_check_per_sec = ( + current_stats[interface].bytes_recv - self.recv_per_interface[interface] + ) / SECONDS_BETWEEN_CHECKS + comb_since_last_check_per_sec = sent_since_last_check_per_sec + recv_since_last_check_per_sec + + if alert := self.check_threshold( + sent_since_last_check_per_sec, + thresholds.critical_threshold_sent_bytes, + thresholds.warning_threshold_sent_bytes, + "sent", + interface, + ): + alerts.append(alert) + if alert := self.check_threshold( + recv_since_last_check_per_sec, + thresholds.critical_threshold_recv_bytes, + thresholds.warning_threshold_recv_bytes, + "recv", + interface, + ): + alerts.append(alert) + if alert := self.check_threshold( + comb_since_last_check_per_sec, + thresholds.critical_threshold_comb_bytes, + thresholds.warning_threshold_comb_bytes, + "comb", + interface, + ): + alerts.append(alert) + + self.sent_per_interface[interface] = current_stats[interface].bytes_sent + self.recv_per_interface[interface] = current_stats[interface].bytes_recv + + return alerts diff --git a/src/lego_monitoring/checks/ram.py b/src/lego_monitoring/checks/ram.py index eff87f7..f1eb40d 100644 --- a/src/lego_monitoring/checks/ram.py +++ b/src/lego_monitoring/checks/ram.py @@ -10,19 +10,19 @@ IS_TESTING = False def ram_check() -> list[Alert]: percentage = virtual_memory().percent config = cvars.config.get().checks.ram - if config.critical_percentage and (IS_TESTING or percentage > config.critical_percentage): + if config.critical_percentage and (IS_TESTING or percentage >= config.critical_percentage): return [ Alert( alert_type=AlertType.RAM, - message=f"RAM usage: {percentage:.2f}% > {config.critical_percentage:.2f}%", + message=f"RAM usage: {percentage:.2f}% >= {config.critical_percentage:.2f}%", severity=Severity.CRITICAL, ) ] - elif config.warning_percentage and (IS_TESTING or percentage > config.warning_percentage): + elif config.warning_percentage and (IS_TESTING or percentage >= config.warning_percentage): return [ Alert( alert_type=AlertType.RAM, - message=f"RAM usage: {percentage:.2f}% > {config.warning_percentage:.2f}%", + message=f"RAM usage: {percentage:.2f}% >= {config.warning_percentage:.2f}%", severity=Severity.WARNING, ) ] diff --git a/src/lego_monitoring/checks/temp/__init__.py b/src/lego_monitoring/checks/temp/__init__.py index 9c68a3b..6322a72 100644 --- a/src/lego_monitoring/checks/temp/__init__.py +++ b/src/lego_monitoring/checks/temp/__init__.py @@ -11,16 +11,16 @@ def temp_check() -> list[Alert]: temps = sensors.get_readings() for sensor, readings in temps.items(): for r in readings: - if r.critical_temp is not None and (IS_TESTING or r.current_temp > r.critical_temp): + if r.critical_temp is not None and (IS_TESTING or r.current_temp >= r.critical_temp): alert = Alert( alert_type=AlertType.TEMP, - message=f"{sensor} {r.label}: {r.current_temp}°C > {r.critical_temp}°C", + message=f"{sensor} {r.label}: {r.current_temp}°C >= {r.critical_temp}°C", severity=Severity.CRITICAL, ) - elif r.warning_temp is not None and (IS_TESTING or r.current_temp > r.warning_temp): + elif r.warning_temp is not None and (IS_TESTING or r.current_temp >= r.warning_temp): alert = Alert( alert_type=AlertType.TEMP, - message=f"{sensor} {r.label}: {r.current_temp}°C > {r.warning_temp}°C", + message=f"{sensor} {r.label}: {r.current_temp}°C >= {r.warning_temp}°C", severity=Severity.WARNING, ) else: diff --git a/src/lego_monitoring/config/__init__.py b/src/lego_monitoring/config/__init__.py index 3d8917e..4354df8 100644 --- a/src/lego_monitoring/config/__init__.py +++ b/src/lego_monitoring/config/__init__.py @@ -6,6 +6,7 @@ from alt_utils import NestedDeserializableDataclass from . import enums from .checks.cpu import CpuCheckConfig +from .checks.net import NetCheckConfig from .checks.ram import RamCheckConfig from .checks.temp import TempCheckConfig from .checks.vulnix import VulnixCheckConfig @@ -17,6 +18,7 @@ class ChecksConfig(NestedDeserializableDataclass): ram: RamCheckConfig = field(default_factory=RamCheckConfig) temp: TempCheckConfig = field(default_factory=TempCheckConfig) vulnix: Optional[VulnixCheckConfig] = None # vulnix check WILL raise if this config section is None + net: NetCheckConfig = field(default_factory=NetCheckConfig) @dataclass diff --git a/src/lego_monitoring/config/checks/net.py b/src/lego_monitoring/config/checks/net.py new file mode 100644 index 0000000..5da92c9 --- /dev/null +++ b/src/lego_monitoring/config/checks/net.py @@ -0,0 +1,19 @@ +from dataclasses import dataclass, field +from typing import Optional + +from alt_utils import NestedDeserializableDataclass + + +@dataclass +class NetInterfaceConfig: + warning_threshold_sent_bytes: Optional[int] = None + critical_threshold_sent_bytes: Optional[int] = None + warning_threshold_recv_bytes: Optional[int] = None + critical_threshold_recv_bytes: Optional[int] = None + warning_threshold_comb_bytes: Optional[int] = None + critical_threshold_comb_bytes: Optional[int] = None + + +@dataclass +class NetCheckConfig(NestedDeserializableDataclass): + interfaces: dict[str, NetInterfaceConfig] = field(default_factory=dict) diff --git a/src/lego_monitoring/config/enums.py b/src/lego_monitoring/config/enums.py index fc3e38e..a020a8b 100644 --- a/src/lego_monitoring/config/enums.py +++ b/src/lego_monitoring/config/enums.py @@ -9,6 +9,7 @@ class CheckSet(StrEnum): CPU = "cpu" RAM = "ram" TEMP = "temp" + NET = "net" VULNIX = "vulnix" diff --git a/uv.lock b/uv.lock index f5ec571..bc590c7 100644 --- a/uv.lock +++ b/uv.lock @@ -1,13 +1,23 @@ version = 1 +revision = 2 requires-python = ">=3.12" [[package]] name = "alt-utils" version = "0.0.8" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/31/15/67246107a8c808a9e99b34fd0024bebe954a67f3c315821eae985b87db7f/alt_utils-0.0.8.tar.gz", hash = "sha256:4b2901df0be4af736210277d58e231d4c4bce597a8fc665a8dd3e7b582705081", size = 6103 } +sdist = { url = "https://files.pythonhosted.org/packages/31/15/67246107a8c808a9e99b34fd0024bebe954a67f3c315821eae985b87db7f/alt_utils-0.0.8.tar.gz", hash = "sha256:4b2901df0be4af736210277d58e231d4c4bce597a8fc665a8dd3e7b582705081", size = 6103, upload_time = "2025-05-10T19:36:49.187Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/5a/7fe15b55fa0ff5528643750c409cd14da005406aef312b32512d8a8487ab/alt_utils-0.0.8-py3-none-any.whl", hash = "sha256:af5549c49543ff4a02b735308bc2a5bfb7f20755620652fd969a648bbaecbc47", size = 6378 }, + { url = "https://files.pythonhosted.org/packages/9a/5a/7fe15b55fa0ff5528643750c409cd14da005406aef312b32512d8a8487ab/alt_utils-0.0.8-py3-none-any.whl", hash = "sha256:af5549c49543ff4a02b735308bc2a5bfb7f20755620652fd969a648bbaecbc47", size = 6378, upload_time = "2025-05-10T19:36:47.954Z" }, +] + +[[package]] +name = "humanize" +version = "4.12.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/22/d1/bbc4d251187a43f69844f7fd8941426549bbe4723e8ff0a7441796b0789f/humanize-4.12.3.tar.gz", hash = "sha256:8430be3a615106fdfceb0b2c1b41c4c98c6b0fc5cc59663a5539b111dd325fb0", size = 80514, upload_time = "2025-04-30T11:51:07.98Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/1e/62a2ec3104394a2975a2629eec89276ede9dbe717092f6966fcf963e1bf0/humanize-4.12.3-py3-none-any.whl", hash = "sha256:2cbf6370af06568fa6d2da77c86edb7886f3160ecd19ee1ffef07979efc597f6", size = 128487, upload_time = "2025-04-30T11:51:06.468Z" }, ] [[package]] @@ -16,6 +26,7 @@ version = "0.1.0" source = { editable = "." } dependencies = [ { name = "alt-utils" }, + { name = "humanize" }, { name = "psutil" }, { name = "telethon" }, ] @@ -23,6 +34,7 @@ dependencies = [ [package.metadata] requires-dist = [ { name = "alt-utils", specifier = ">=0.0.8" }, + { name = "humanize", specifier = ">=4.12.3" }, { name = "psutil", specifier = ">=7.0.0" }, { name = "telethon", specifier = ">=1.40.0" }, ] @@ -31,30 +43,30 @@ requires-dist = [ name = "psutil" version = "7.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2a/80/336820c1ad9286a4ded7e845b2eccfcb27851ab8ac6abece774a6ff4d3de/psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456", size = 497003 } +sdist = { url = "https://files.pythonhosted.org/packages/2a/80/336820c1ad9286a4ded7e845b2eccfcb27851ab8ac6abece774a6ff4d3de/psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456", size = 497003, upload_time = "2025-02-13T21:54:07.946Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/e6/2d26234410f8b8abdbf891c9da62bee396583f713fb9f3325a4760875d22/psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25", size = 238051 }, - { url = "https://files.pythonhosted.org/packages/04/8b/30f930733afe425e3cbfc0e1468a30a18942350c1a8816acfade80c005c4/psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da", size = 239535 }, - { url = "https://files.pythonhosted.org/packages/2a/ed/d362e84620dd22876b55389248e522338ed1bf134a5edd3b8231d7207f6d/psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91", size = 275004 }, - { url = "https://files.pythonhosted.org/packages/bf/b9/b0eb3f3cbcb734d930fdf839431606844a825b23eaf9a6ab371edac8162c/psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34", size = 277986 }, - { url = "https://files.pythonhosted.org/packages/eb/a2/709e0fe2f093556c17fbafda93ac032257242cabcc7ff3369e2cb76a97aa/psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993", size = 279544 }, - { url = "https://files.pythonhosted.org/packages/50/e6/eecf58810b9d12e6427369784efe814a1eec0f492084ce8eb8f4d89d6d61/psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99", size = 241053 }, - { url = "https://files.pythonhosted.org/packages/50/1b/6921afe68c74868b4c9fa424dad3be35b095e16687989ebbb50ce4fceb7c/psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553", size = 244885 }, + { url = "https://files.pythonhosted.org/packages/ed/e6/2d26234410f8b8abdbf891c9da62bee396583f713fb9f3325a4760875d22/psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25", size = 238051, upload_time = "2025-02-13T21:54:12.36Z" }, + { url = "https://files.pythonhosted.org/packages/04/8b/30f930733afe425e3cbfc0e1468a30a18942350c1a8816acfade80c005c4/psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da", size = 239535, upload_time = "2025-02-13T21:54:16.07Z" }, + { url = "https://files.pythonhosted.org/packages/2a/ed/d362e84620dd22876b55389248e522338ed1bf134a5edd3b8231d7207f6d/psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91", size = 275004, upload_time = "2025-02-13T21:54:18.662Z" }, + { url = "https://files.pythonhosted.org/packages/bf/b9/b0eb3f3cbcb734d930fdf839431606844a825b23eaf9a6ab371edac8162c/psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34", size = 277986, upload_time = "2025-02-13T21:54:21.811Z" }, + { url = "https://files.pythonhosted.org/packages/eb/a2/709e0fe2f093556c17fbafda93ac032257242cabcc7ff3369e2cb76a97aa/psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993", size = 279544, upload_time = "2025-02-13T21:54:24.68Z" }, + { url = "https://files.pythonhosted.org/packages/50/e6/eecf58810b9d12e6427369784efe814a1eec0f492084ce8eb8f4d89d6d61/psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99", size = 241053, upload_time = "2025-02-13T21:54:34.31Z" }, + { url = "https://files.pythonhosted.org/packages/50/1b/6921afe68c74868b4c9fa424dad3be35b095e16687989ebbb50ce4fceb7c/psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553", size = 244885, upload_time = "2025-02-13T21:54:37.486Z" }, ] [[package]] name = "pyaes" version = "1.6.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/44/66/2c17bae31c906613795711fc78045c285048168919ace2220daa372c7d72/pyaes-1.6.1.tar.gz", hash = "sha256:02c1b1405c38d3c370b085fb952dd8bea3fadcee6411ad99f312cc129c536d8f", size = 28536 } +sdist = { url = "https://files.pythonhosted.org/packages/44/66/2c17bae31c906613795711fc78045c285048168919ace2220daa372c7d72/pyaes-1.6.1.tar.gz", hash = "sha256:02c1b1405c38d3c370b085fb952dd8bea3fadcee6411ad99f312cc129c536d8f", size = 28536, upload_time = "2017-09-20T21:17:54.23Z" } [[package]] name = "pyasn1" version = "0.6.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322 } +sdist = { url = "https://files.pythonhosted.org/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322, upload_time = "2024-09-10T22:41:42.55Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135 }, + { url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135, upload_time = "2024-09-11T16:00:36.122Z" }, ] [[package]] @@ -64,9 +76,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyasn1" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/da/8a/22b7beea3ee0d44b1916c0c1cb0ee3af23b700b6da9f04991899d0c555d4/rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75", size = 29034 } +sdist = { url = "https://files.pythonhosted.org/packages/da/8a/22b7beea3ee0d44b1916c0c1cb0ee3af23b700b6da9f04991899d0c555d4/rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75", size = 29034, upload_time = "2025-04-16T09:51:18.218Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696 }, + { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696, upload_time = "2025-04-16T09:51:17.142Z" }, ] [[package]] @@ -77,7 +89,7 @@ dependencies = [ { name = "pyaes" }, { name = "rsa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/58/af/9b7111e3f63fffe8e55b7ceb8bda023173e2052f420b6debcb25fd2fbc15/telethon-1.40.0.tar.gz", hash = "sha256:40e83326877a2e68b754d4b6d0d1ca5ac924110045b039e02660f2d67add97db", size = 646723 } +sdist = { url = "https://files.pythonhosted.org/packages/58/af/9b7111e3f63fffe8e55b7ceb8bda023173e2052f420b6debcb25fd2fbc15/telethon-1.40.0.tar.gz", hash = "sha256:40e83326877a2e68b754d4b6d0d1ca5ac924110045b039e02660f2d67add97db", size = 646723, upload_time = "2025-04-21T09:12:10.506Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/5a/c5370edb3215d19a6e858f4169b8eec725ba55f9d39df0f557508048c037/Telethon-1.40.0-py3-none-any.whl", hash = "sha256:146fd4cb2a7afa66bc67f9c2167756096a37b930f65711a3e7399ec9874dcfa7", size = 722013 }, + { url = "https://files.pythonhosted.org/packages/2c/5a/c5370edb3215d19a6e858f4169b8eec725ba55f9d39df0f557508048c037/Telethon-1.40.0-py3-none-any.whl", hash = "sha256:146fd4cb2a7afa66bc67f9c2167756096a37b930f65711a3e7399ec9874dcfa7", size = 722013, upload_time = "2025-04-21T09:12:08.399Z" }, ]