152 lines
5.6 KiB
Python
152 lines
5.6 KiB
Python
from __future__ import annotations
|
|
|
|
import os
|
|
from typing import Any, Dict, List, Optional, Sequence, Tuple
|
|
|
|
from cmdlet._shared import Cmdlet, CmdletArg
|
|
from SYS.result_table import ResultTable
|
|
from SYS.config import load_config
|
|
from API import zerotier as zt
|
|
|
|
def exec(pipe: Sequence[Any], args: Sequence[str], options: Dict[str, Any]) -> ResultTable:
|
|
table = ResultTable(title="ZeroTier Status", max_columns=10)
|
|
|
|
cfg = load_config()
|
|
|
|
# 1. Local Node Status
|
|
node_id = "unknown"
|
|
try:
|
|
# Best effort to get local node ID
|
|
st = zt._run_cli_json("status")
|
|
if isinstance(st, dict):
|
|
node_id = st.get("address") or node_id
|
|
except: pass
|
|
|
|
# 2. Hosting Status
|
|
zt_net = cfg.get("networking", {}).get("zerotier", {})
|
|
serve_target = zt_net.get("serve")
|
|
if serve_target:
|
|
port = zt_net.get("port") or 999
|
|
net_id = zt_net.get("network_id") or "all"
|
|
|
|
status = "OFFLINE"
|
|
detail = ""
|
|
|
|
# Try to find the local ZT address for this network
|
|
zt_addrs = []
|
|
if net_id and net_id != "all":
|
|
zt_addrs = zt.get_assigned_addresses(net_id)
|
|
|
|
# We probe localhost for hosting status, but show ZT IP in the table
|
|
display_addr = zt_addrs[0] if zt_addrs else "localhost"
|
|
|
|
# Try probes
|
|
# Using 127.0.0.1 is often more reliable than 'localhost' on Windows
|
|
probe_targets = [f"http://127.0.0.1:{port}/health"]
|
|
if zt_addrs:
|
|
probe_targets.insert(0, f"http://{zt_addrs[0]}:{port}/health")
|
|
|
|
from API.HTTP import HTTPClient
|
|
with HTTPClient(timeout=1.0, retries=0) as client:
|
|
for url in probe_targets:
|
|
try:
|
|
resp = client.get(url)
|
|
if resp.status_code == 200:
|
|
status = "ONLINE"
|
|
payload = resp.json()
|
|
detail = f"Serving {payload.get('name') or serve_target}"
|
|
break
|
|
else:
|
|
status = f"HTTP {resp.status_code}"
|
|
except Exception as exc:
|
|
if not detail: # Keep the first failure reason if all fail
|
|
detail = f"Probe failed: {exc}"
|
|
|
|
if status == "OFFLINE" and not zt_addrs:
|
|
detail = "No ZeroTier IP assigned yet. Check 'zerotier-cli listnetworks'."
|
|
|
|
row = table.add_row()
|
|
row.add_column("TYPE", "HOST")
|
|
row.add_column("NAME", serve_target)
|
|
row.add_column("ID", net_id)
|
|
row.add_column("ADDRESS", f"{display_addr}:{port}")
|
|
row.add_column("STATUS", status)
|
|
row.add_column("DETAIL", detail)
|
|
|
|
# 3. Connections (Remote Stores)
|
|
zt_stores = cfg.get("store", {}).get("zerotier", {})
|
|
if zt_stores:
|
|
for name, sconf in zt_stores.items():
|
|
net_id = sconf.get("NETWORK_ID") or sconf.get("network_id") or ""
|
|
host = sconf.get("HOST") or sconf.get("host") or ""
|
|
port = sconf.get("PORT") or sconf.get("port") or 999
|
|
svc = sconf.get("SERVICE") or sconf.get("service") or "remote"
|
|
|
|
status = "probing..."
|
|
detail = ""
|
|
|
|
if not host:
|
|
status = "MISCONFIGURED"
|
|
detail = "No host IP"
|
|
else:
|
|
try:
|
|
from API.HTTP import HTTPClient
|
|
with HTTPClient(timeout=2.0) as client:
|
|
# Paths depend on service type
|
|
path = "/api_version" if svc == "hydrus" else "/health"
|
|
resp = client.get(f"http://{host}:{port}{path}")
|
|
if resp.status_code == 200:
|
|
status = "ONLINE"
|
|
if svc == "remote":
|
|
p = resp.json()
|
|
detail = f"Remote store: {p.get('name', 'unknown')}"
|
|
else:
|
|
detail = "Hydrus API"
|
|
else:
|
|
status = f"HTTP {resp.status_code}"
|
|
except Exception as exc:
|
|
status = "OFFLINE"
|
|
detail = str(exc)
|
|
|
|
row = table.add_row()
|
|
row.add_column("TYPE", "REMOTE")
|
|
row.add_column("NAME", name)
|
|
row.add_column("ID", net_id)
|
|
row.add_column("ADDRESS", f"{host}:{port}")
|
|
row.add_column("STATUS", status)
|
|
row.add_column("DETAIL", detail)
|
|
|
|
# 4. Networking Networks (Raw ZT status)
|
|
try:
|
|
nets = zt.list_networks()
|
|
if not nets:
|
|
row = table.add_row()
|
|
row.add_column("TYPE", "INFO")
|
|
row.add_column("NAME", "ZeroTier CLI")
|
|
row.add_column("STATUS", "No networks found or CLI error")
|
|
row.add_column("DETAIL", f"CLI Path: {zt._get_cli_path() or 'Not found'}")
|
|
|
|
for n in nets:
|
|
row = table.add_row()
|
|
row.add_column("TYPE", "NETWORK")
|
|
row.add_column("NAME", n.name)
|
|
row.add_column("ID", n.id)
|
|
row.add_column("ADDRESS", ", ".join(n.assigned_addresses))
|
|
row.add_column("STATUS", n.status)
|
|
row.add_column("DETAIL", "")
|
|
except Exception as exc:
|
|
row = table.add_row()
|
|
row.add_column("TYPE", "ERROR")
|
|
row.add_column("NAME", "ZeroTier CLI")
|
|
row.add_column("STATUS", "EXCEPTION")
|
|
row.add_column("DETAIL", str(exc))
|
|
|
|
return table
|
|
|
|
CMDLET = Cmdlet(
|
|
name=".zerotier",
|
|
summary="Check ZeroTier hosting and connection status",
|
|
usage=".zerotier",
|
|
exec=exec
|
|
)
|