78 lines
2.5 KiB
Python
78 lines
2.5 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
"""
|
||
|
|
netvm-registry.py — single source of truth for node -> CDP port mapping.
|
||
|
|
|
||
|
|
Parses NODES.md (the fleet registry). Every script that needs a node's
|
||
|
|
CDP port reads it from here instead of hardcoding — new nodes propagate
|
||
|
|
automatically.
|
||
|
|
|
||
|
|
Usage:
|
||
|
|
from netvm_registry import port_for, load
|
||
|
|
port_for("muse") # -> 9410 (int), None if unknown
|
||
|
|
|
||
|
|
CLI:
|
||
|
|
netvm-registry.py # prints "node:port" lines for active nodes
|
||
|
|
netvm-registry.py <node> # prints just the port (for bash)
|
||
|
|
"""
|
||
|
|
import os
|
||
|
|
import re
|
||
|
|
import sys
|
||
|
|
|
||
|
|
REGISTRY = os.path.join(os.path.dirname(os.path.dirname(
|
||
|
|
os.path.abspath(__file__))), "NODES.md")
|
||
|
|
|
||
|
|
|
||
|
|
def load(registry_path=None):
|
||
|
|
"""Return {node: {netns, egress_ip, cdp_port, status, agent}}."""
|
||
|
|
path = registry_path or REGISTRY
|
||
|
|
nodes = {}
|
||
|
|
try:
|
||
|
|
with open(path) as f:
|
||
|
|
for line in f:
|
||
|
|
line = line.rstrip("\n")
|
||
|
|
if not line.startswith("|"):
|
||
|
|
continue
|
||
|
|
cells = [c.strip() for c in line.strip("|").split("|")]
|
||
|
|
if len(cells) < 6:
|
||
|
|
continue
|
||
|
|
node, netns, egress, port, status, agent = cells[:6]
|
||
|
|
if node in ("node", "") or not re.fullmatch(r"[a-z0-9-]+",
|
||
|
|
node):
|
||
|
|
continue
|
||
|
|
if node.startswith("-"):
|
||
|
|
continue
|
||
|
|
try:
|
||
|
|
port_n = int(port)
|
||
|
|
except ValueError:
|
||
|
|
continue
|
||
|
|
nodes[node] = {"netns": netns, "egress_ip": egress,
|
||
|
|
"cdp_port": port_n, "status": status,
|
||
|
|
"agent": agent}
|
||
|
|
except FileNotFoundError:
|
||
|
|
pass
|
||
|
|
return nodes
|
||
|
|
|
||
|
|
|
||
|
|
def port_for(node, registry_path=None):
|
||
|
|
"""CDP port for a node, or None if the node isn't registered."""
|
||
|
|
rec = load(registry_path).get(node)
|
||
|
|
return rec["cdp_port"] if rec else None
|
||
|
|
|
||
|
|
|
||
|
|
def active_nodes(registry_path=None):
|
||
|
|
"""{node: port} for nodes with status == active."""
|
||
|
|
return {n: r["cdp_port"] for n, r in load(registry_path).items()
|
||
|
|
if r["status"] == "active"}
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
if len(sys.argv) == 2:
|
||
|
|
port = port_for(sys.argv[1])
|
||
|
|
if port is None:
|
||
|
|
print("unknown node: %s" % sys.argv[1], file=sys.stderr)
|
||
|
|
sys.exit(1)
|
||
|
|
print(port)
|
||
|
|
else:
|
||
|
|
for node, port in sorted(active_nodes().items()):
|
||
|
|
print("%s:%d" % (node, port))
|