refactor: remove broken NUT/UPS plugin and managed-NUT startup

The UPS plugin's default driver was snmp-ups, so NUT was just translating
network SNMP back into its own protocol — no value over polling the UPS
directly through the existing snmp plugin. The managed-NUT entrypoint path
also broke container startup when config was incomplete.

Deletes nut_setup.py, the NUT block from entrypoint.sh, NUT packages from
the Dockerfile, the ups entry from the plugin catalog example, and the
ups wiring in alerts METRIC_CATALOG, widgets, settings routes, and the
rules form. The plugins/ups source tree (untracked) is also removed from
the working copy. Existing ups_* DB tables are orphaned but harmless in
dev; a drop migration isn't needed.

Follow-up: rebuild "on battery → Ansible shutdown" automation on top of
the alerts system as a new action type that runs a playbook.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-13 22:41:08 -04:00
parent 0596f1d9c1
commit d6e094bbb5
8 changed files with 6 additions and 263 deletions
-37
View File
@@ -404,11 +404,6 @@ def _build_plugin_cfg_from_form(plugin: dict, form) -> dict:
plugin_cfg[cfg_key] = default_val
else:
plugin_cfg[cfg_key] = raw_val
# Managed NUT: when nut_managed is on, the NUT daemon runs in this container
# — force nut_host/port to localhost so the plugin connects automatically.
if name == "ups" and plugin_cfg.get("nut_managed"):
plugin_cfg["nut_host"] = "localhost"
plugin_cfg["nut_port"] = 3493
return plugin_cfg
@@ -483,9 +478,6 @@ async def save_plugin_detail(name: str):
from roundtable.core.plugin_manager import hot_reload_plugin
hot_reload_plugin(current_app._get_current_object(), name)
if name == "ups":
_sync_nut_managed_config(form)
return redirect(url_for("settings.plugin_detail", name=name))
@@ -546,35 +538,6 @@ async def plugin_repos_save(idx: int):
return await _plugin_repos_partial({"plugins.repositories": repos})
def _sync_nut_managed_config(form) -> None:
"""Write or remove /data/nut_managed.json based on UPS plugin managed NUT settings."""
import json
nut_managed_path = Path("/data/nut_managed.json")
nut_managed = "plugin.ups.nut_managed" in form
ups_host = form.get("plugin.ups.nut_ups_host", "").strip()
# Only write the file when managed mode is on AND a host is configured.
# Without a host, NUT can't start and the container would loop on errors.
if nut_managed and ups_host:
cfg = {
"ups_host": ups_host,
"driver": form.get("plugin.ups.nut_driver", "snmp-ups").strip(),
"ups_name": form.get("plugin.ups.ups_name", "ups").strip(),
"snmp_community": form.get("plugin.ups.nut_snmp_community", "public").strip(),
"snmp_version": form.get("plugin.ups.nut_snmp_version", "v1").strip(),
"username": form.get("plugin.ups.nut_username", "").strip(),
"password": form.get("plugin.ups.nut_password", "").strip(),
}
try:
nut_managed_path.write_text(json.dumps(cfg, indent=2))
except OSError as exc:
logger.warning("Could not write %s: %s", nut_managed_path, exc)
else:
try:
nut_managed_path.unlink(missing_ok=True)
except OSError as exc:
logger.warning("Could not remove %s: %s", nut_managed_path, exc)
# ── Plugin catalog (HTMX partial) ─────────────────────────────────────────────
@settings_bp.get("/plugins/catalog/")