Fix missing/wrong anchor fields crashing the weathermap panel

tamirsuliman-weathermap-panel (the plugin actually installed, not the
allamiro fork the schema research was done against) requires node.anchors
-- a numLinks/numFilledLinks tally per anchor position -- and expects
link.sides.*.anchor as the plugin's numeric TS enum value (Center=0,
Top=1, Bottom=2, Left=3, Right=4), not an anchor name string. Omitting
anchors entirely (per the "safe to omit, auto-defaulted" schema-research
note, which turns out to be fork-specific) crashed the panel on load with
"TypeError: undefined is not an object (evaluating 'i.anchors[0]')".

Enum values reverse-engineered from the installed plugin's module.js.

Refs #48
This commit is contained in:
2026-07-09 17:13:27 +00:00
parent 0aff25f509
commit 36c93d9ecd
2 changed files with 775 additions and 144 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -50,6 +50,12 @@ GRID_SPACING_X = 180
GRID_SPACING_Y = 150 GRID_SPACING_Y = 150
SITE_BAND_Y = {"dc": 0, "core": 450, "campus": 750} SITE_BAND_Y = {"dc": 0, "core": 450, "campus": 750}
# tamirsuliman-weathermap-panel's numeric anchor enum (Center=0, Top=1,
# Bottom=2, Left=3, Right=4) -- reverse-engineered from module.js, since
# link.sides.*.anchor and node.anchors{} both index by this numeric value,
# not by the anchor name string.
ANCHOR = {"Center": 0, "Top": 1, "Bottom": 2, "Left": 3, "Right": 4}
NODE_COLORS = {"font": "#ffffff", "background": "#22252b", "border": "#5794F2", "statusDown": "#F2495C"} NODE_COLORS = {"font": "#ffffff", "background": "#22252b", "border": "#5794F2", "statusDown": "#F2495C"}
STATUS_VALUE_MAPPINGS = [{"value": 0, "color": "#F2495C"}, {"value": 1, "color": "#73BF69"}] STATUS_VALUE_MAPPINGS = [{"value": 0, "color": "#F2495C"}, {"value": 1, "color": "#73BF69"}]
DEFAULT_LINK_BANDWIDTH_BPS = 10_000_000_000 # 10G fallback when IPFabric speed is missing DEFAULT_LINK_BANDWIDTH_BPS = 10_000_000_000 # 10G fallback when IPFabric speed is missing
@@ -238,6 +244,14 @@ def build_weathermap(devices, links, interface_speeds, positions, prometheus_url
return gnmic_int return gnmic_int
# -- nodes -- # -- nodes --
# anchors{} tallies how many link-sides attach to each anchor position on
# this node; every link below uses Right for its A side and Left for its
# Z side, so that's what gets counted per node here.
anchor_counts = {host: {a: 0 for a in ANCHOR.values()} for host in (d["hostname"] for d in devices)}
for link in links:
anchor_counts[link["a_host"]][ANCHOR["Right"]] += 1
anchor_counts[link["z_host"]][ANCHOR["Left"]] += 1
nodes = [] nodes = []
for dev in devices: for dev in devices:
host = dev["hostname"] host = dev["hostname"]
@@ -254,6 +268,7 @@ def build_weathermap(devices, links, interface_speeds, positions, prometheus_url
"statusQuery": f"BGP {host}", "statusQuery": f"BGP {host}",
"nodeStatusColorTarget": "border", "nodeStatusColorTarget": "border",
"statusValueMappings": [dict(m) for m in STATUS_VALUE_MAPPINGS], "statusValueMappings": [dict(m) for m in STATUS_VALUE_MAPPINGS],
"anchors": {a: {"numLinks": anchor_counts[host][a], "numFilledLinks": 0} for a in ANCHOR.values()},
} }
if host in vtep_hosts: if host in vtep_hosts:
node["tooltipMetrics"] = [ node["tooltipMetrics"] = [
@@ -285,12 +300,12 @@ def build_weathermap(devices, links, interface_speeds, positions, prometheus_url
"A": { "A": {
"bandwidth": a_bw, "bandwidth": a_bw,
"query": f"{link['a_host']} {a_gnmic_int} tx", "query": f"{link['a_host']} {a_gnmic_int} tx",
"labelOffset": 55, "anchor": "Right", "dashboardLink": "", "labelOffset": 55, "anchor": ANCHOR["Right"], "dashboardLink": "",
}, },
"Z": { "Z": {
"bandwidth": z_bw, "bandwidth": z_bw,
"query": f"{link['z_host']} {z_gnmic_int} rx", "query": f"{link['z_host']} {z_gnmic_int} rx",
"labelOffset": 55, "anchor": "Left", "dashboardLink": "", "labelOffset": 55, "anchor": ANCHOR["Left"], "dashboardLink": "",
}, },
}, },
"units": "bps", "units": "bps",