152 lines
5.2 KiB
Python
152 lines
5.2 KiB
Python
import sys
|
|
import requests
|
|
from pathlib import Path
|
|
from typing import Any, Dict, Sequence
|
|
|
|
# Add project root to sys.path
|
|
root = Path(__file__).resolve().parent.parent
|
|
if str(root) not in sys.path:
|
|
sys.path.insert(0, str(root))
|
|
|
|
from cmdlet._shared import Cmdlet
|
|
from SYS.config import load_config
|
|
from SYS.result_table import Table
|
|
from API import zerotier as zt
|
|
|
|
def exec_zerotier(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
|
|
# Use provided config or fall back to CWD load
|
|
cfg = config if config else load_config(Path.cwd())
|
|
|
|
table = Table("ZeroTier Status")
|
|
|
|
# 1. Local Hub Status
|
|
row = table.add_row()
|
|
row.add_column("TYPE", "HOST")
|
|
row.add_column("NAME", "localhost")
|
|
|
|
# Try to get node ID via CLI info
|
|
node_id = "???"
|
|
try:
|
|
if hasattr(zt, "_run_cli_json"):
|
|
info = zt._run_cli_json("info", "-j")
|
|
node_id = info.get("address", "???")
|
|
except:
|
|
pass
|
|
row.add_column("ID", node_id)
|
|
|
|
# Check if local server is responsive
|
|
try:
|
|
# endpoint is /health for remote_storage_server
|
|
# We try 127.0.0.1 first with a more generous timeout
|
|
# Using a list of potential local hits to be robust against Windows networking quirks
|
|
import socket
|
|
|
|
def get_local_ip():
|
|
try:
|
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
s.connect(("8.8.8.8", 80))
|
|
ip = s.getsockname()[0]
|
|
s.close()
|
|
return ip
|
|
except:
|
|
return None
|
|
|
|
hosts = ["127.0.0.1", "localhost"]
|
|
local_ip = get_local_ip()
|
|
if local_ip:
|
|
hosts.append(local_ip)
|
|
|
|
success = False
|
|
last_err = ""
|
|
|
|
# Try multiple times if server just started
|
|
import time
|
|
for attempt in range(3):
|
|
for host in hosts:
|
|
try:
|
|
resp = requests.get(f"http://{host}:999/health", timeout=3, proxies={"http": None, "https": None})
|
|
if resp.status_code == 200:
|
|
row.add_column("STATUS", "ONLINE")
|
|
row.add_column("ADDRESS", f"{host}:999")
|
|
row.add_column("DETAIL", f"Serving {cfg.get('active_store', 'default')}")
|
|
success = True
|
|
break
|
|
elif resp.status_code == 401:
|
|
row.add_column("STATUS", "Serving (Locked)")
|
|
row.add_column("ADDRESS", f"{host}:999")
|
|
row.add_column("DETAIL", "401 Unauthorized - API Key required")
|
|
success = True
|
|
break
|
|
except Exception as e:
|
|
last_err = str(e)
|
|
continue
|
|
if success:
|
|
break
|
|
time.sleep(1) # Wait between attempts
|
|
|
|
if not success:
|
|
row.add_column("STATUS", "OFFLINE")
|
|
row.add_column("ADDRESS", "127.0.0.1:999")
|
|
row.add_column("DETAIL", f"Server not responding on port 999. Last attempt ({hosts[-1]}): {last_err}")
|
|
|
|
except Exception as e:
|
|
row.add_column("STATUS", "OFFLINE")
|
|
row.add_column("ADDRESS", "127.0.0.1:999")
|
|
row.add_column("DETAIL", f"Status check failed: {e}")
|
|
|
|
# 2. Add Networks
|
|
if zt.is_available():
|
|
try:
|
|
networks = zt.list_networks()
|
|
for net in networks:
|
|
row = table.add_row()
|
|
row.add_column("TYPE", "NETWORK")
|
|
row.add_column("NAME", getattr(net, "name", "Unnamed"))
|
|
row.add_column("ID", getattr(net, "id", ""))
|
|
|
|
status = getattr(net, "status", "OK")
|
|
assigned = getattr(net, "assigned_addresses", [])
|
|
ip_str = assigned[0] if assigned else ""
|
|
|
|
row.add_column("STATUS", status)
|
|
row.add_column("ADDRESS", ip_str)
|
|
except Exception as e:
|
|
row = table.add_row()
|
|
row.add_column("TYPE", "ERROR")
|
|
row.add_column("DETAIL", f"Failed to list networks: {e}")
|
|
else:
|
|
row = table.add_row()
|
|
row.add_column("TYPE", "SYSTEM")
|
|
row.add_column("NAME", "ZeroTier")
|
|
row.add_column("STATUS", "NOT FOUND")
|
|
row.add_column("DETAIL", "zerotier-cli not in path")
|
|
|
|
# Output
|
|
try:
|
|
from cmdnat.out_table import TableOutput
|
|
TableOutput().render(table)
|
|
except Exception:
|
|
# Fallback for raw CLI
|
|
print(f"\n--- {table.title} ---")
|
|
for r in table.rows:
|
|
# Use the get_column method from ResultRow
|
|
t = r.get_column("TYPE") or ""
|
|
n = r.get_column("NAME") or ""
|
|
s = r.get_column("STATUS") or ""
|
|
a = r.get_column("ADDRESS") or ""
|
|
id = r.get_column("ID") or ""
|
|
d = r.get_column("DETAIL") or ""
|
|
print(f"[{t:7}] {n:15} | {s:15} | {a:20} | {id} | {d}")
|
|
print("-" * 100)
|
|
|
|
return 0
|
|
|
|
CMDLET = Cmdlet(
|
|
name=".zerotier",
|
|
summary="Check ZeroTier node and hosting status",
|
|
usage=".zerotier",
|
|
exec=exec_zerotier,
|
|
)
|
|
|
|
if __name__ == "__main__":
|
|
exec_zerotier(None, sys.argv[1:], {}) |