mirror of
https://github.com/hansjone/netx.git
synced 2026-10-09 08:10:47 +08:00
Add fabric link browsing, conservative job retention, hour-based intervals, multi-worker start locking, and trim canvas discover UI in favor of the LLDP page. Co-authored-by: Cursor <cursoragent@cursor.com>
96 lines
4.2 KiB
Python
96 lines
4.2 KiB
Python
"""Startup helpers for final topology schema (drop legacy map tables)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
|
|
from sqlalchemy import text
|
|
from sqlalchemy.engine import Connection
|
|
|
|
_log = logging.getLogger("netx.topology.migrate")
|
|
|
|
_LEGACY_TABLES = ("topology_edge", "topology_node", "topology_map")
|
|
|
|
|
|
def drop_legacy_topology_tables(conn: Connection) -> None:
|
|
"""Remove document-style topology_* tables after cutover to fabric/view."""
|
|
dialect = str(getattr(conn.dialect, "name", "") or "").lower()
|
|
for table in _LEGACY_TABLES:
|
|
try:
|
|
if dialect.startswith("postgres"):
|
|
conn.execute(text(f'DROP TABLE IF EXISTS "{table}" CASCADE'))
|
|
else:
|
|
conn.execute(text(f"DROP TABLE IF EXISTS {table}"))
|
|
except Exception:
|
|
_log.exception("drop legacy topology table failed: %s", table)
|
|
|
|
|
|
def ensure_topology_schema(conn: Connection) -> None:
|
|
drop_legacy_topology_tables(conn)
|
|
dialect = str(getattr(conn.dialect, "name", "") or "").lower()
|
|
# Best-effort column add for existing DBs (create_all won't alter).
|
|
alter_stmts: list[str] = []
|
|
if dialect.startswith("postgres"):
|
|
alter_stmts.extend(
|
|
[
|
|
"ALTER TABLE topo_discover_job ADD COLUMN IF NOT EXISTS trigger_mode VARCHAR(32) DEFAULT 'manual'",
|
|
"ALTER TABLE lldp_collect_policy ADD COLUMN IF NOT EXISTS history_keep INTEGER DEFAULT 30",
|
|
"ALTER TABLE lldp_collect_policy ADD COLUMN IF NOT EXISTS interval_hours INTEGER DEFAULT 24",
|
|
]
|
|
)
|
|
elif dialect.startswith("sqlite"):
|
|
# SQLite: ignore if column already exists.
|
|
alter_stmts.extend(
|
|
[
|
|
"ALTER TABLE topo_discover_job ADD COLUMN trigger_mode VARCHAR(32) DEFAULT 'manual'",
|
|
"ALTER TABLE lldp_collect_policy ADD COLUMN history_keep INTEGER DEFAULT 30",
|
|
"ALTER TABLE lldp_collect_policy ADD COLUMN interval_hours INTEGER DEFAULT 24",
|
|
]
|
|
)
|
|
for sql in alter_stmts:
|
|
try:
|
|
conn.execute(text(sql))
|
|
except Exception:
|
|
_log.debug("topology alter skipped/failed: %s", sql[:80], exc_info=True)
|
|
|
|
# Backfill hours from legacy days when hours unset/zero.
|
|
try:
|
|
conn.execute(
|
|
text(
|
|
"UPDATE lldp_collect_policy SET interval_hours = "
|
|
"CASE WHEN COALESCE(interval_hours, 0) <= 0 "
|
|
"THEN GREATEST(1, COALESCE(interval_days, 1)) * 24 "
|
|
"ELSE interval_hours END"
|
|
)
|
|
)
|
|
except Exception:
|
|
try:
|
|
# SQLite has no GREATEST in older builds — use MAX.
|
|
conn.execute(
|
|
text(
|
|
"UPDATE lldp_collect_policy SET interval_hours = "
|
|
"CASE WHEN COALESCE(interval_hours, 0) <= 0 "
|
|
"THEN MAX(1, COALESCE(interval_days, 1)) * 24 "
|
|
"ELSE interval_hours END"
|
|
)
|
|
)
|
|
except Exception:
|
|
_log.debug("interval_hours backfill skipped", exc_info=True)
|
|
|
|
if not dialect.startswith("postgres"):
|
|
return
|
|
stmts = [
|
|
"CREATE INDEX IF NOT EXISTS ix_topo_fabric_edge_layer_a ON topo_fabric_edge (layer, a_node_id)",
|
|
"CREATE INDEX IF NOT EXISTS ix_topo_fabric_edge_layer_b ON topo_fabric_edge (layer, b_node_id)",
|
|
"CREATE INDEX IF NOT EXISTS ix_topo_fabric_edge_layer_seen ON topo_fabric_edge (layer, last_seen_at)",
|
|
"CREATE INDEX IF NOT EXISTS ix_topo_fabric_edge_active ON topo_fabric_edge (layer) WHERE status = 'active'",
|
|
"CREATE INDEX IF NOT EXISTS ix_topo_view_node_view ON topo_view_node (view_id)",
|
|
"CREATE UNIQUE INDEX IF NOT EXISTS uq_topo_fabric_node_managed_nn ON topo_fabric_node (managed_ne_id) WHERE managed_ne_id IS NOT NULL",
|
|
"CREATE UNIQUE INDEX IF NOT EXISTS uq_topo_fabric_node_ume_nn ON topo_fabric_node (ume_ne_id) WHERE ume_ne_id IS NOT NULL",
|
|
"CREATE INDEX IF NOT EXISTS ix_topo_discover_job_trigger ON topo_discover_job (trigger_mode)",
|
|
]
|
|
for sql in stmts:
|
|
try:
|
|
conn.execute(text(sql))
|
|
except Exception:
|
|
_log.exception("ensure topology index failed: %s", sql[:80])
|