From 0aff25f50981a5c861c8c7b42f38246011f505d1 Mon Sep 17 00:00:00 2001 From: Damien Arnodo Date: Thu, 9 Jul 2026 17:08:51 +0000 Subject: [PATCH] Fix PromQL regex escaping for hyphenated hostnames re.escape() escapes '-' as '\-', which Python's regex engine accepts but PromQL's RE2 engine rejects outright (every hostname in this lab is hyphenated, e.g. dc-leaf1). All 4 panel targets were failing with "unknown escape sequence U+002D" until now. Also swaps the dashboard to the newly-created NetLab datasource, which actually has gnmic data (the previously-wired external Prometheus datasource had none). Refs #48 --- configs/grafana/weathermap-dashboard.json | 18 +++++++++--------- scripts/generate_weathermap.py | 17 +++++++++++++---- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/configs/grafana/weathermap-dashboard.json b/configs/grafana/weathermap-dashboard.json index 8d0019b..ff51c79 100644 --- a/configs/grafana/weathermap-dashboard.json +++ b/configs/grafana/weathermap-dashboard.json @@ -24,43 +24,43 @@ }, "datasource": { "type": "prometheus", - "uid": "aflu2t514uf40a" + "uid": "cfrlusac89se8a" }, "targets": [ { "refId": "A", - "expr": "min by (device) (network_instances_network_instance_protocols_protocol_bgp_neighbors_neighbor_state_session_state{network_instance_name=\"default\", device=~\"campus\\-spine1|dc\\-spine1|dc\\-spine2|campus\\-spine2|campus\\-border\\-leaf1|dc\\-leaf1|campus\\-border\\-leaf2|dc\\-leaf4|dc\\-leaf3|dc\\-leaf5|dc\\-border\\-leaf1|dc\\-leaf6|dc\\-access1|dc\\-access2|dc\\-leaf8|dc\\-leaf7|dc\\-access4|dc\\-access3|dc\\-border\\-leaf2|dc\\-leaf2|campus\\-leaf1|campus\\-access1|campus\\-leaf2|campus\\-leaf3|campus\\-access2|campus\\-leaf4|core1|core2\"})", + "expr": "min by (device) (network_instances_network_instance_protocols_protocol_bgp_neighbors_neighbor_state_session_state{network_instance_name=\"default\", device=~\"campus-spine1|dc-spine1|dc-spine2|campus-spine2|campus-border-leaf1|dc-leaf1|campus-border-leaf2|dc-leaf4|dc-leaf3|dc-leaf5|dc-border-leaf1|dc-leaf6|dc-access1|dc-access2|dc-leaf8|dc-leaf7|dc-access4|dc-access3|dc-border-leaf2|dc-leaf2|campus-leaf1|campus-access1|campus-leaf2|campus-leaf3|campus-access2|campus-leaf4|core1|core2\"})", "legendFormat": "BGP {{device}}", "datasource": { "type": "prometheus", - "uid": "aflu2t514uf40a" + "uid": "cfrlusac89se8a" } }, { "refId": "B", - "expr": "rate(interfaces_interface_state_counters_out_octets{device=~\"campus\\-spine1|dc\\-spine1|dc\\-spine2|campus\\-spine2|campus\\-border\\-leaf1|dc\\-leaf1|campus\\-border\\-leaf2|dc\\-leaf4|dc\\-leaf3|dc\\-leaf5|dc\\-border\\-leaf1|dc\\-leaf6|dc\\-access1|dc\\-access2|dc\\-leaf8|dc\\-leaf7|dc\\-access4|dc\\-access3|dc\\-border\\-leaf2|dc\\-leaf2|campus\\-leaf1|campus\\-access1|campus\\-leaf2|campus\\-leaf3|campus\\-access2|campus\\-leaf4|core1|core2\", interface=~\"Ethernet1|Ethernet10|Ethernet11|Ethernet12|Ethernet13|Ethernet14|Ethernet2|Ethernet3|Ethernet4|Ethernet5|Ethernet6|Ethernet7|Ethernet8|Ethernet9\"}[5m]) * 8", + "expr": "rate(interfaces_interface_state_counters_out_octets{device=~\"campus-spine1|dc-spine1|dc-spine2|campus-spine2|campus-border-leaf1|dc-leaf1|campus-border-leaf2|dc-leaf4|dc-leaf3|dc-leaf5|dc-border-leaf1|dc-leaf6|dc-access1|dc-access2|dc-leaf8|dc-leaf7|dc-access4|dc-access3|dc-border-leaf2|dc-leaf2|campus-leaf1|campus-access1|campus-leaf2|campus-leaf3|campus-access2|campus-leaf4|core1|core2\", interface=~\"Ethernet1|Ethernet10|Ethernet11|Ethernet12|Ethernet13|Ethernet14|Ethernet2|Ethernet3|Ethernet4|Ethernet5|Ethernet6|Ethernet7|Ethernet8|Ethernet9\"}[5m]) * 8", "legendFormat": "{{device}} {{interface}} tx", "datasource": { "type": "prometheus", - "uid": "aflu2t514uf40a" + "uid": "cfrlusac89se8a" } }, { "refId": "C", - "expr": "rate(interfaces_interface_state_counters_in_octets{device=~\"campus\\-spine1|dc\\-spine1|dc\\-spine2|campus\\-spine2|campus\\-border\\-leaf1|dc\\-leaf1|campus\\-border\\-leaf2|dc\\-leaf4|dc\\-leaf3|dc\\-leaf5|dc\\-border\\-leaf1|dc\\-leaf6|dc\\-access1|dc\\-access2|dc\\-leaf8|dc\\-leaf7|dc\\-access4|dc\\-access3|dc\\-border\\-leaf2|dc\\-leaf2|campus\\-leaf1|campus\\-access1|campus\\-leaf2|campus\\-leaf3|campus\\-access2|campus\\-leaf4|core1|core2\", interface=~\"Ethernet1|Ethernet10|Ethernet11|Ethernet12|Ethernet13|Ethernet14|Ethernet2|Ethernet3|Ethernet4|Ethernet5|Ethernet6|Ethernet7|Ethernet8|Ethernet9\"}[5m]) * 8", + "expr": "rate(interfaces_interface_state_counters_in_octets{device=~\"campus-spine1|dc-spine1|dc-spine2|campus-spine2|campus-border-leaf1|dc-leaf1|campus-border-leaf2|dc-leaf4|dc-leaf3|dc-leaf5|dc-border-leaf1|dc-leaf6|dc-access1|dc-access2|dc-leaf8|dc-leaf7|dc-access4|dc-access3|dc-border-leaf2|dc-leaf2|campus-leaf1|campus-access1|campus-leaf2|campus-leaf3|campus-access2|campus-leaf4|core1|core2\", interface=~\"Ethernet1|Ethernet10|Ethernet11|Ethernet12|Ethernet13|Ethernet14|Ethernet2|Ethernet3|Ethernet4|Ethernet5|Ethernet6|Ethernet7|Ethernet8|Ethernet9\"}[5m]) * 8", "legendFormat": "{{device}} {{interface}} rx", "datasource": { "type": "prometheus", - "uid": "aflu2t514uf40a" + "uid": "cfrlusac89se8a" } }, { "refId": "D", - "expr": "count by (device, vlan) (network_instances_network_instance_fdb_mac_table_entries_entry_vlan{device=~\"campus\\-leaf1|campus\\-leaf2|campus\\-leaf3|campus\\-leaf4|dc\\-leaf1|dc\\-leaf2|dc\\-leaf5|dc\\-leaf6\"})\n* on(device, vlan) group_left(vlan_to_vni_state_vni)\ninterfaces_interface_arista_vxlan_vlan_to_vnis_vlan_to_vni_state_vni{device=~\"campus\\-leaf1|campus\\-leaf2|campus\\-leaf3|campus\\-leaf4|dc\\-leaf1|dc\\-leaf2|dc\\-leaf5|dc\\-leaf6\"}", + "expr": "count by (device, vlan) (network_instances_network_instance_fdb_mac_table_entries_entry_vlan{device=~\"campus-leaf1|campus-leaf2|campus-leaf3|campus-leaf4|dc-leaf1|dc-leaf2|dc-leaf5|dc-leaf6\"})\n* on(device, vlan) group_left(vlan_to_vni_state_vni)\ninterfaces_interface_arista_vxlan_vlan_to_vnis_vlan_to_vni_state_vni{device=~\"campus-leaf1|campus-leaf2|campus-leaf3|campus-leaf4|dc-leaf1|dc-leaf2|dc-leaf5|dc-leaf6\"}", "legendFormat": "VNI {{device}} {{vlan}}", "datasource": { "type": "prometheus", - "uid": "aflu2t514uf40a" + "uid": "cfrlusac89se8a" } } ], diff --git a/scripts/generate_weathermap.py b/scripts/generate_weathermap.py index b208163..0172336 100644 --- a/scripts/generate_weathermap.py +++ b/scripts/generate_weathermap.py @@ -149,6 +149,15 @@ def alias_interface(ipf_name): return f"{full}{rest}" +def promql_escape(s): + """re.escape() escapes '-' as '\\-', which Python's own regex engine + accepts but PromQL's RE2-based engine rejects ("unknown escape sequence + U+002D '-'") -- and every hostname in this lab contains hyphens. '-' is + not a regex metacharacter outside a character class, so it's safe to + leave unescaped.""" + return re.escape(s).replace("\\-", "-") + + # -------------------------------------------------------------------------- # Topology processing # -------------------------------------------------------------------------- @@ -211,7 +220,7 @@ def dedupe_links(connectivity_matrix): def build_weathermap(devices, links, interface_speeds, positions, prometheus_url, mismatches): hostnames = [d["hostname"] for d in devices] - host_regex = "|".join(re.escape(h) for h in hostnames) + host_regex = "|".join(promql_escape(h) for h in hostnames) known_pairs = known_interface_pairs(prometheus_url) vtep_vlan_pairs = known_vtep_vlan_pairs(prometheus_url) @@ -259,8 +268,8 @@ def build_weathermap(devices, links, interface_speeds, positions, prometheus_url for link in links: a_gnmic_int = resolve_and_check(link["a_host"], link["a_int"], "side A") z_gnmic_int = resolve_and_check(link["z_host"], link["z_int"], "side Z") - interface_regex_parts.add(re.escape(a_gnmic_int)) - interface_regex_parts.add(re.escape(z_gnmic_int)) + interface_regex_parts.add(promql_escape(a_gnmic_int)) + interface_regex_parts.add(promql_escape(z_gnmic_int)) a_bw = interface_speeds.get((link["a_host"], link["a_int"]), DEFAULT_LINK_BANDWIDTH_BPS) z_bw = interface_speeds.get((link["z_host"], link["z_int"]), DEFAULT_LINK_BANDWIDTH_BPS) @@ -311,7 +320,7 @@ def build_weathermap(devices, links, interface_speeds, positions, prometheus_url }, ] if vtep_hosts: - vtep_regex = "|".join(re.escape(h) for h in vtep_hosts) + vtep_regex = "|".join(promql_escape(h) for h in vtep_hosts) # Verbatim join from #44 "VXLAN (MAC count per VNI)", scoped to VTEP nodes. targets.append({ "refId": "D",