diff --git a/README.md b/README.md index a63d5df..415b443 100644 --- a/README.md +++ b/README.md @@ -470,7 +470,6 @@ Set breakpoints in modules such as `hbd/server/udp.py`, `hbd/server/dns.py`, or - `hbd_host`: bind address for HTTP/WSS - `pickfile`: path for persisted state - `logfile`: path to log file -- `logfmt`: `text` or `msg` - `pushsrv`: push service (`pushover`|`mattermost`|`all`) - `interval` / `grace`: heartbeat timing configuration - `dyndomains`: list of dyndomains to update via `nsupdate` diff --git a/REFACTORING.md b/REFACTORING.md index 660e26b..afd0fa0 100644 --- a/REFACTORING.md +++ b/REFACTORING.md @@ -59,7 +59,7 @@ Server-specific defaults: - `hb_port`: Port to listen for heartbeats (default: 50003) - `hbd_port`: HTTP API port (default: 50004) - `ws_port`: WebSocket port (default: 50005) -- `logfile`, `logfmt`: Logging configuration +- `logfile`: Log file path - `pushsrv`, `pushover_token`, etc.: Notification settings - `watchhosts`, `dyndnshosts`: Host monitoring - `smtpserver`, etc.: Email settings diff --git a/docs/CONFIG_RELOAD.md b/docs/CONFIG_RELOAD.md index ded5709..ee73134 100644 --- a/docs/CONFIG_RELOAD.md +++ b/docs/CONFIG_RELOAD.md @@ -81,7 +81,6 @@ The following settings **cannot** be reloaded and require a service restart: - **Logging** - `logfile` - Log file path - - `logfmt` - Log format - **Journal Settings** - `journal_enabled` - Enable/disable journaling diff --git a/hbd/server/config.py b/hbd/server/config.py index 601b013..921907a 100644 --- a/hbd/server/config.py +++ b/hbd/server/config.py @@ -16,12 +16,10 @@ SERVER_DEFAULTS = { "hbd_host": "", # Bind address (empty = all interfaces) # Persistence - "pickfile": "/tmp/hb.pick", + "pickfile": os.path.join(os.path.expanduser("~"), ".hb.pick"), # File to store host state between restarts # Logging - "logfile": "/var/log/heartbeat.log", - "logfmt": "text", # text or msg or json - + "logfile": os.path.join(os.path.expanduser("~"), ".hb.log"), # Notification channels "notification_channels": {}, # Named channels with type and credentials "default_notification_channels": [], # Default channels if host doesn't specify diff --git a/hbd/server/main.py b/hbd/server/main.py index f819d82..6c4e125 100644 --- a/hbd/server/main.py +++ b/hbd/server/main.py @@ -27,6 +27,7 @@ def save_state(config, hbdclass): """Save current state to pickle file. Safe to call at any time.""" import pickle import os + from . import users as users_mod # Clear timer references before pickling (they can't be serialized) for hostname, host in list(hbdclass.Host.hosts.items()): @@ -48,6 +49,7 @@ def save_state(config, hbdclass): pick = pickle.Pickler(pickf) pick.dump(hbdclass.Host.hosts) pick.dump(data.msgs) + pick.dump(users_mod.save_sessions()) os.replace(tmpfile, pickfile) except Exception as e: logger.error("Failed to save state: %s", e) @@ -432,6 +434,7 @@ def load_pickled_hosts(config, hbdclass): import os import pickle from . import config as config_mod + from . import users as users_mod pickfile = config.get("pickfile", "hbd.pickle") dyndnshosts = config_mod.get_dyndnshosts(config) @@ -445,6 +448,10 @@ def load_pickled_hosts(config, hbdclass): try: hbdclass.Host.hosts = pick.load() data.msgs = pick.load() + try: + users_mod.load_sessions(pick.load()) + except Exception: + pass # older pickle without sessions — fine pickf.close() except Exception as e: logger.exception("load pickled failed: %s", e) diff --git a/hbd/server/settings.py b/hbd/server/settings.py index 754066a..ab192e6 100644 --- a/hbd/server/settings.py +++ b/hbd/server/settings.py @@ -252,8 +252,6 @@ def get_settings_sections(config: dict) -> list: "Path to the pickle file used to persist host state across restarts."), field("logfile", "Event log", "path", "Path to the event log file."), - field("logfmt", "Log format", "select", - "Format for event log entries: text, msg, or json."), ], }, { diff --git a/hbd/server/threshold.py b/hbd/server/threshold.py index 10969af..da19ee0 100644 --- a/hbd/server/threshold.py +++ b/hbd/server/threshold.py @@ -1096,9 +1096,9 @@ class ThresholdChecker: threshold: Threshold configuration plugin_data: Optional dictionary of all plugin data fields """ - if alert_state.level == AlertLevel.OK: + if alert_state.level != AlertLevel.CRITICAL: return - + # Skip reminders if alert has been acknowledged if alert_state.acknowledged: return diff --git a/hbd/server/users.py b/hbd/server/users.py index a568847..b515e57 100644 --- a/hbd/server/users.py +++ b/hbd/server/users.py @@ -226,3 +226,17 @@ def _purge_expired_sessions() -> None: expired = [t for t, s in list(_sessions.items()) if s["expires"] < now] for t in expired: del _sessions[t] + + +def save_sessions() -> dict: + """Return a snapshot of non-expired sessions suitable for pickling.""" + _purge_expired_sessions() + return dict(_sessions) + + +def load_sessions(snapshot: dict) -> None: + """Restore sessions from a pickled snapshot, dropping any that have expired.""" + global _sessions + now = time.time() + _sessions = {t: s for t, s in snapshot.items() if s.get("expires", 0) > now} + logger.debug("Restored %d session(s) from pickle", len(_sessions))