254 lines
4.4 KiB
Python
254 lines
4.4 KiB
Python
#!/usr/bin/env python3
|
|
|
|
import sys
|
|
import ast
|
|
import time
|
|
|
|
|
|
IP_TO_VM = {
|
|
"172.22.156.3": "1",
|
|
"172.22.158.2": "2",
|
|
"172.22.94.2": "3",
|
|
"172.22.156.4": "4",
|
|
"172.22.158.3": "5",
|
|
"172.22.94.3": "6",
|
|
"172.22.156.5": "7",
|
|
"172.22.158.4": "8",
|
|
"172.22.94.4": "9",
|
|
"172.22.156.6": "10",
|
|
}
|
|
|
|
|
|
|
|
# COLORS
|
|
|
|
|
|
GREEN = "\033[92m"
|
|
YELLOW = "\033[93m"
|
|
RED = "\033[91m"
|
|
CYAN = "\033[96m"
|
|
WHITE = "\033[97m"
|
|
GRAY = "\033[90m"
|
|
|
|
BOLD = "\033[1m"
|
|
RESET = "\033[0m"
|
|
|
|
CLEAR_LINE = "\033[K"
|
|
|
|
|
|
STATE_STYLE = {
|
|
"ALIVE": (GREEN, "A"),
|
|
"SUSPECT": (YELLOW, "S"),
|
|
"FAILED": (RED, "F"),
|
|
}
|
|
|
|
|
|
def render(lines):
|
|
|
|
|
|
# STOPPED / SSH ERROR
|
|
|
|
|
|
for line in lines:
|
|
if line.startswith("**STOPPED**"):
|
|
|
|
reason = line.replace(
|
|
"**STOPPED**",
|
|
""
|
|
).strip()
|
|
|
|
return (
|
|
f"{CLEAR_LINE}"
|
|
f"{BOLD}{RED}>>> STOPPED <<<{RESET}"
|
|
f"{CLEAR_LINE}\n"
|
|
f"{GRAY}{reason}{RESET}"
|
|
f"{CLEAR_LINE}\n"
|
|
)
|
|
|
|
|
|
# PARSE
|
|
|
|
|
|
members = []
|
|
|
|
mode = "?"
|
|
drop_rate = "?"
|
|
|
|
now = time.time()
|
|
|
|
for raw_line in lines:
|
|
|
|
line = raw_line.strip()
|
|
|
|
if not line:
|
|
continue
|
|
|
|
# ----------------------------------------------------
|
|
# MODE / DROP RATE LINE
|
|
# ----------------------------------------------------
|
|
|
|
if line.startswith("Mode:"):
|
|
|
|
try:
|
|
pieces = line.split(",")
|
|
|
|
mode = pieces[0].split(":", 1)[1].strip()
|
|
drop_rate = pieces[1].split(":", 1)[1].strip()
|
|
|
|
except Exception:
|
|
pass
|
|
|
|
continue
|
|
|
|
# ----------------------------------------------------
|
|
# MEMBERSHIP ENTRY
|
|
# ----------------------------------------------------
|
|
|
|
try:
|
|
|
|
id_text, data_text = line.split(": ", 1)
|
|
|
|
member_id = ast.literal_eval(id_text)
|
|
member_data = ast.literal_eval(data_text)
|
|
|
|
ip, port, timestamp = member_id
|
|
|
|
local_last_seen = member_data.get(
|
|
"local_last_seen",
|
|
now
|
|
)
|
|
|
|
members.append({
|
|
"vm": IP_TO_VM.get(ip, "?"),
|
|
|
|
"hb": member_data.get(
|
|
"heart_beat",
|
|
0
|
|
),
|
|
|
|
"state": member_data.get(
|
|
"state",
|
|
"?"
|
|
).upper(),
|
|
|
|
"age": max(
|
|
0.0,
|
|
now - local_last_seen
|
|
),
|
|
|
|
"inc": member_data.get(
|
|
"incarnation_counter",
|
|
0
|
|
),
|
|
})
|
|
|
|
except Exception:
|
|
continue
|
|
|
|
|
|
# HEADER
|
|
|
|
|
|
out = []
|
|
|
|
mode_upper = str(mode).upper()
|
|
|
|
if mode_upper == "SUSPECT":
|
|
mode_color = YELLOW
|
|
elif mode_upper == "NOSUSPECT":
|
|
mode_color = GREEN
|
|
else:
|
|
mode_color = GRAY
|
|
|
|
out.append(
|
|
f"{BOLD}{CYAN}MODE:{RESET} "
|
|
f"{mode_color}{BOLD}{mode_upper}{RESET} "
|
|
f"{GRAY}|{RESET} "
|
|
f"{BOLD}{CYAN}DROP:{RESET} "
|
|
f"{WHITE}{drop_rate}%{RESET}"
|
|
f"{CLEAR_LINE}"
|
|
)
|
|
|
|
|
|
# NO DATA
|
|
|
|
|
|
if not members:
|
|
|
|
out.append(
|
|
f"{BOLD}{YELLOW}"
|
|
f"waiting for membership..."
|
|
f"{RESET}"
|
|
f"{CLEAR_LINE}"
|
|
)
|
|
|
|
return "\n".join(out) + "\n"
|
|
|
|
|
|
# SORT
|
|
|
|
|
|
def vm_number(member):
|
|
|
|
try:
|
|
return int(member["vm"])
|
|
|
|
except Exception:
|
|
return 999
|
|
|
|
members.sort(
|
|
key=vm_number
|
|
)
|
|
|
|
|
|
# TABLE HEADER
|
|
|
|
|
|
out.append(
|
|
f"{BOLD}{CYAN}"
|
|
f"{'VM':<3} "
|
|
f"{'S':<2} "
|
|
f"{'HB':<6} "
|
|
f"{'AGE':<5} "
|
|
f"{'INC'}"
|
|
f"{RESET}"
|
|
f"{CLEAR_LINE}"
|
|
)
|
|
|
|
|
|
# MEMBERS
|
|
|
|
|
|
for member in members:
|
|
|
|
color, code = STATE_STYLE.get(
|
|
member["state"],
|
|
(GRAY, "?")
|
|
)
|
|
|
|
out.append(
|
|
f"{WHITE}"
|
|
f"{member['vm']:<3}"
|
|
f"{RESET} "
|
|
|
|
f"{color}{BOLD}"
|
|
f"{code:<2}"
|
|
f"{RESET} "
|
|
|
|
f"{member['hb']:<6} "
|
|
|
|
f"{member['age']:>4.1f} "
|
|
|
|
f"{member['inc']}"
|
|
|
|
f"{CLEAR_LINE}"
|
|
)
|
|
|
|
return "\n".join(out) + "\n"
|
|
|
|
|
|
print(
|
|
render(list(sys.stdin)),
|
|
end="",
|
|
flush=True
|
|
) |