feat(host_agent): agent reports its primary IP; mirror into Host.address (task 274)
The agent now detects its own primary non-loopback IP (UDP-socket trick, no packets sent) and sends it in the metadata bag; bumps AGENT_VERSION 1.0.0->1.1.0 and re-detects on SIGHUP. The server persists it on HostAgentRegistration.host_ip and mirrors it into Host.address ONLY when that field is blank — an admin-typed address is never overwritten. The reported IP shows in the settings table so admins can see drift regardless. - agent.py: detect_primary_ip(); host_ip in collect_metadata(); refresh on reload - routes.py: pure pick_host_address() helper; ingest persists host_ip + mirrors - models.py + migration host_agent_002_host_ip: new String(45) column - settings_list.html: Reported IP column - plugin.yaml: 1.0.0 -> 1.1.0 - tests: pick_host_address (fill-when-blank / never-clobber / no-op), detect_primary_ip (never raises, non-loopback), metadata carries host_ip Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
"""Tests for agent-reported primary IP (task 274)."""
|
||||
from plugins.host_agent.routes import pick_host_address
|
||||
from plugins.host_agent import agent as a
|
||||
|
||||
|
||||
# ── pick_host_address: fill only when blank, never clobber ────────────────────
|
||||
|
||||
def test_pick_fills_when_current_blank():
|
||||
assert pick_host_address("", "10.0.0.5") == "10.0.0.5"
|
||||
assert pick_host_address(None, "10.0.0.5") == "10.0.0.5"
|
||||
assert pick_host_address(" ", "10.0.0.5") == "10.0.0.5"
|
||||
|
||||
|
||||
def test_pick_keeps_admin_value():
|
||||
# A non-blank current address is never overwritten.
|
||||
assert pick_host_address("monitor.example.com", "10.0.0.5") is None
|
||||
assert pick_host_address("192.168.1.9", "10.0.0.5") is None
|
||||
|
||||
|
||||
def test_pick_no_reported_ip_is_noop():
|
||||
assert pick_host_address("", None) is None
|
||||
assert pick_host_address("", "") is None
|
||||
assert pick_host_address(None, None) is None
|
||||
|
||||
|
||||
# ── detect_primary_ip: never raises; None or a non-loopback string ────────────
|
||||
|
||||
def test_detect_primary_ip_never_raises_and_is_sane():
|
||||
ip = a.detect_primary_ip()
|
||||
assert ip is None or isinstance(ip, str)
|
||||
if ip is not None:
|
||||
assert not ip.startswith("127.")
|
||||
assert ip # non-empty
|
||||
|
||||
|
||||
# ── host_ip rides in the metadata bag the agent sends ─────────────────────────
|
||||
|
||||
def test_collect_metadata_includes_host_ip_key():
|
||||
md = a.collect_metadata()
|
||||
assert "host_ip" in md # present even if None when offline
|
||||
assert set(md) >= {"kernel", "distro", "arch", "host_ip"}
|
||||
Reference in New Issue
Block a user