Files
getDomain/domain-api/deploy/multi-region/check_ops_center_stack.sh
2026-04-18 23:52:51 +08:00

1273 lines
57 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
set -euo pipefail
BASE_URL="${1:-http://127.0.0.1:8100}"
VIEW_MODE="${2:-full}"
CURL_CONNECT_TIMEOUT="${CURL_CONNECT_TIMEOUT:-3}"
CURL_MAX_TIME="${CURL_MAX_TIME:-8}"
FETCH_BODY=""
FETCH_STATUS="000"
extract_api_data_json() {
local raw_json="${1:-}"
python3 - "${raw_json}" <<'PY'
import json
import sys
raw_json = str(sys.argv[1] or "")
try:
payload = json.loads(raw_json)
except Exception:
raise SystemExit(1)
data = payload.get("data")
if not isinstance(data, dict):
raise SystemExit(1)
print(json.dumps(data, ensure_ascii=False, indent=2))
PY
}
fetch_json() {
local url="${1:-}"
local raw_text
raw_text="$(curl -sS \
--connect-timeout "${CURL_CONNECT_TIMEOUT}" \
--max-time "${CURL_MAX_TIME}" \
"${url}" \
-w $'\nHTTP_STATUS=%{http_code}' || true)"
FETCH_BODY="${raw_text%$'\n'HTTP_STATUS=*}"
FETCH_STATUS="${raw_text##*$'\n'HTTP_STATUS=}"
}
fetch_json "${BASE_URL}/api/v1/ops/stack-diagnosis?base_url=${BASE_URL}"
STACK_DIAGNOSIS_JSON="${FETCH_BODY}"
STACK_DIAGNOSIS_STATUS="${FETCH_STATUS}"
if [[ "${STACK_DIAGNOSIS_STATUS}" == 2* ]]; then
if STACK_DIAGNOSIS_DATA_JSON="$(extract_api_data_json "${STACK_DIAGNOSIS_JSON}" 2>/dev/null)"; then
if [[ "${VIEW_MODE}" != "summary" ]]; then
echo "[1/1] ops stack diagnosis (api contract)"
printf 'status=%s\n%s\n\n' "${STACK_DIAGNOSIS_STATUS}" "${STACK_DIAGNOSIS_JSON}"
fi
echo "[9/9] condensed stack summary"
STACK_BASE_URL="${BASE_URL}" python3 - <<'PY' "${STACK_DIAGNOSIS_DATA_JSON}"
import json
import os
import sys
def ensure_dict(value):
return value if isinstance(value, dict) else {}
def ensure_list(value):
return value if isinstance(value, list) else []
def ensure_str(value):
return value if isinstance(value, str) else ""
def command(*parts: str) -> str:
tokens = ["bash", "domain-api/deploy/multi-region/drive_ops_center.sh"]
tokens.extend([str(part).strip() for part in parts if str(part).strip()])
return " ".join(tokens)
def release_command(*parts: str) -> str:
tokens = ["bash", "domain-api/deploy/multi-region/drive_release_hub.sh"]
tokens.extend([str(part).strip() for part in parts if str(part).strip()])
return " ".join(tokens)
payload = json.loads(str(sys.argv[1] or "{}"))
base_url = ensure_str(os.environ.get("STACK_BASE_URL")) or "http://127.0.0.1:8100"
diagnosis = ensure_dict(payload.get("diagnosis"))
overview = ensure_dict(payload.get("overview"))
managed_nodes = ensure_dict(payload.get("managed_nodes"))
release_hub = ensure_dict(payload.get("release_hub"))
issues = ensure_list(diagnosis.get("issues"))
recommended_actions = ensure_list(diagnosis.get("recommended_actions"))
next_step = ensure_dict(diagnosis.get("next_step"))
backend_operator_decision = ensure_dict(diagnosis.get("operator_decision"))
backend_next_actions = ensure_dict(diagnosis.get("next_actions"))
backend_recommended_commands = ensure_dict(diagnosis.get("recommended_commands"))
log_sync = ensure_dict(overview.get("log_sync"))
problem_nodes = ensure_list(managed_nodes.get("problem_nodes"))
next_step_focus_ref = ensure_dict(next_step.get("focus_ref"))
launchpad_target_node_code = ensure_str(diagnosis.get("launchpad_recommended_target_node_code")) or ensure_str(next_step_focus_ref.get("node_code"))
launchpad_recovery_label = ensure_str(diagnosis.get("launchpad_recommended_recovery_label"))
launchpad_recovery_summary = ensure_str(diagnosis.get("launchpad_recommended_recovery_summary")) or ensure_str(next_step.get("reason"))
handover_next_action_code = ensure_str(next_step.get("action_code"))
scene_log_target_node_code = ""
missing_nodes = ensure_list(log_sync.get("missing_sample_node_codes"))
if missing_nodes:
scene_log_target_node_code = ensure_str(missing_nodes[0])
elif problem_nodes:
scene_log_target_node_code = ensure_str(ensure_dict(problem_nodes[0]).get("node_code"))
recommended_commands = {
"stack_summary": f"bash domain-api/deploy/multi-region/check_ops_center_stack.sh {base_url} summary",
"doctor": command("doctor", base_url),
"driver_feed": command("driver-feed", base_url),
"driver_focus_preview": command("driver-focus-preview", base_url),
"driver_focus_run": command("driver-focus-run", base_url),
"go_live_recover": command("go-live-recover", base_url),
"log_sync_recover": command("log-sync-recover", base_url, "key"),
"log_sync_recover_full": command("log-sync-recover", base_url, "full"),
"agent_gap_check": command("agent-gap-check", base_url),
"agent_gap_recover": command("agent-gap-recover", base_url),
"api_restart": command("runtime-refresh-recover", base_url),
"node_onboarding": (
command("node-onboarding", base_url, ensure_str(ensure_dict(problem_nodes[0]).get("node_code")))
if problem_nodes and ensure_str(ensure_dict(problem_nodes[0]).get("node_code"))
else ""
),
"node_recover": (
command("node-recover", base_url, ensure_str(ensure_dict(problem_nodes[0]).get("node_code")))
if problem_nodes and ensure_str(ensure_dict(problem_nodes[0]).get("node_code"))
else ""
),
"node_bootstrap_preview": (
command("node-bootstrap-preview", base_url, ensure_str(ensure_dict(problem_nodes[0]).get("node_code")))
if problem_nodes and ensure_str(ensure_dict(problem_nodes[0]).get("node_code"))
else ""
),
"node_bootstrap_run": (
command("node-bootstrap-run", base_url, launchpad_target_node_code, "cli")
if launchpad_target_node_code
else command("node-bootstrap-run", base_url, ensure_str(ensure_dict(problem_nodes[0]).get("node_code")), "cli")
if problem_nodes and ensure_str(ensure_dict(problem_nodes[0]).get("node_code"))
else ""
),
"node_acceptance_run": (
command("node-acceptance-run", base_url, launchpad_target_node_code, "cli")
if launchpad_target_node_code
else command("node-acceptance-run", base_url, ensure_str(ensure_dict(problem_nodes[0]).get("node_code")), "cli")
if launchpad_target_node_code or (problem_nodes and ensure_str(ensure_dict(problem_nodes[0]).get("node_code")))
else ""
),
"node_acceptance_plan": (
command("node-acceptance-plan", base_url, ensure_str(ensure_dict(problem_nodes[0]).get("node_code")))
if problem_nodes and ensure_str(ensure_dict(problem_nodes[0]).get("node_code"))
else ""
),
"scene_log": (
command("scene-node-log", base_url, scene_log_target_node_code, "120", "key")
if scene_log_target_node_code
else ""
),
"release_launchpad": release_command(base_url, "launchpad"),
"release_preview_latest": release_command(base_url, "preview-latest"),
"jobs": command("activity-stream", base_url, "kind=ops_job"),
"node_handover": (
command("node-handover", base_url, ensure_str(ensure_dict(problem_nodes[0]).get("node_code")))
if problem_nodes and ensure_str(ensure_dict(problem_nodes[0]).get("node_code"))
else command("stack-first-gap-handover", base_url)
),
}
operator_decision = {
"lane": "steady",
"priority": "low",
"reason_code": "steady_state",
"title": "当前进入稳定观察态",
"summary": "当前总检没有发现新的阻断问题,可继续按既有链路观察与推进。",
"next_focus": ensure_dict(next_step.get("focus_ref")),
"primary_command_key": "driver_feed",
"secondary_command_key": "jobs",
}
participating_total = int(log_sync.get("participating_nodes_total", 0) or 0)
remote_access_ready = int(managed_nodes.get("remote_access_ready", 0) or 0)
managed_enabled = int(managed_nodes.get("managed_enabled", 0) or 0)
default_rollout_gate_status = ensure_str(release_hub.get("default_rollout_gate_status"))
if not bool(log_sync.get("enabled")) and participating_total > 0:
operator_decision = {
"lane": "observability",
"priority": "high",
"reason_code": "scene_log_disabled",
"title": "先恢复远端现场日志可见性",
"summary": "当前已有参与检测节点,但远端日志回传仍关闭,海外控制面仍处于半盲态。",
"next_focus": {"kind": "node_scene_log", "node_code": scene_log_target_node_code, "mode": "key", "limit": 80}
if scene_log_target_node_code
else {},
"primary_command_key": "log_sync_recover",
"secondary_command_key": "scene_log",
}
elif managed_enabled > 0 and remote_access_ready == 0:
primary_command_key = "agent_gap_recover"
secondary_command_key = "node_handover"
title = "先补齐节点接管与 remote-agent 可达性"
summary = (
f"managed_enabled={managed_enabled}remote_access_ready={remote_access_ready}"
"当前还不能从海外控制面统一下发标准动作。"
)
if handover_next_action_code == "bootstrap_run" and launchpad_target_node_code:
primary_command_key = "node_bootstrap_run"
secondary_command_key = "node_handover"
title = "先跑首台节点接入收口"
summary = (
launchpad_recovery_summary
or join_text(
launchpad_recovery_label,
launchpad_target_node_code,
"当前真正阻断已经收敛到首台节点接入收口。"
)
)
elif handover_next_action_code == "run_acceptance" and launchpad_target_node_code:
primary_command_key = "node_acceptance_run"
secondary_command_key = "node_handover"
title = "先跑首台节点接管验收"
summary = (
launchpad_recovery_summary
or join_text(
launchpad_recovery_label,
launchpad_target_node_code,
"当前真正阻断已经收敛到首台节点接管验收。"
)
)
operator_decision = {
"lane": "node_handover",
"priority": "high",
"reason_code": "managed_nodes_agent_pending",
"title": title,
"summary": summary,
"next_focus": next_step_focus_ref,
"primary_command_key": primary_command_key,
"secondary_command_key": secondary_command_key,
}
elif default_rollout_gate_status == "blocked":
operator_decision = {
"lane": "release",
"priority": "medium",
"reason_code": "release_rollout_gate_blocked",
"title": "先检查发布门禁与 rollout 阻断",
"summary": ensure_str(release_hub.get("default_rollout_gate_summary")),
"next_focus": ensure_dict(release_hub.get("default_rollout_gate_focus_ref") or release_hub.get("focus_ref")),
"primary_command_key": "release_launchpad",
"secondary_command_key": "release_preview_latest",
}
elif any(ensure_str(ensure_dict(item).get("code")) == "runtime_build_schema_stale" for item in issues):
operator_decision = {
"lane": "runtime_recovery",
"priority": "high",
"reason_code": "runtime_build_schema_stale",
"title": "先执行运行时刷新与上线复核",
"summary": "当前已经识别到仓库代码与运行中 API schema 之间存在漂移,先走统一恢复链,再继续节点接管或发布判断。",
"next_focus": ensure_dict(next_step.get("focus_ref")),
"primary_command_key": "go_live_recover",
"secondary_command_key": "api_restart",
}
elif recommended_actions or issues:
operator_decision = {
"lane": "ops_jobs",
"priority": "medium",
"reason_code": "issues_need_attention",
"title": "先按总检建议逐项收口当前问题",
"summary": (
f"recommended_actions={len(recommended_actions)}issues={len(issues)}"
f"stack_status={ensure_str(diagnosis.get('stack_status')) or 'unknown'}。"
),
"next_focus": ensure_dict(next_step.get("focus_ref")),
"primary_command_key": "doctor",
"secondary_command_key": "jobs",
}
if backend_recommended_commands:
merged_recommended_commands = dict(recommended_commands)
merged_recommended_commands.update({
key: ensure_str(value)
for key, value in backend_recommended_commands.items()
if ensure_str(key) and ensure_str(value)
})
recommended_commands = merged_recommended_commands
if backend_operator_decision:
operator_decision = {
"lane": ensure_str(backend_operator_decision.get("lane")) or ensure_str(operator_decision.get("lane")),
"priority": ensure_str(backend_operator_decision.get("priority")) or ensure_str(operator_decision.get("priority")),
"reason_code": ensure_str(backend_operator_decision.get("reason_code")) or ensure_str(operator_decision.get("reason_code")),
"title": ensure_str(backend_operator_decision.get("title")) or ensure_str(operator_decision.get("title")),
"summary": ensure_str(backend_operator_decision.get("summary")) or ensure_str(operator_decision.get("summary")),
"next_focus": ensure_dict(backend_operator_decision.get("next_focus")) or ensure_dict(operator_decision.get("next_focus")),
"primary_command_key": ensure_str(backend_operator_decision.get("primary_command_key")) or ensure_str(operator_decision.get("primary_command_key")),
"secondary_command_key": ensure_str(backend_operator_decision.get("secondary_command_key")) or ensure_str(operator_decision.get("secondary_command_key")),
}
next_actions = {
"primary_command_key": ensure_str(operator_decision.get("primary_command_key")),
"primary_command": ensure_str(recommended_commands.get(ensure_str(operator_decision.get("primary_command_key")))),
"secondary_command_key": ensure_str(operator_decision.get("secondary_command_key")),
"secondary_command": ensure_str(recommended_commands.get(ensure_str(operator_decision.get("secondary_command_key")))),
}
if backend_next_actions:
next_actions["primary_command_key"] = ensure_str(backend_next_actions.get("primary_command_key")) or next_actions["primary_command_key"]
next_actions["secondary_command_key"] = ensure_str(backend_next_actions.get("secondary_command_key")) or next_actions["secondary_command_key"]
next_actions["primary_command"] = (
ensure_str(backend_next_actions.get("primary_command"))
or ensure_str(recommended_commands.get(next_actions["primary_command_key"]))
or next_actions["primary_command"]
)
next_actions["secondary_command"] = (
ensure_str(backend_next_actions.get("secondary_command"))
or ensure_str(recommended_commands.get(next_actions["secondary_command_key"]))
or next_actions["secondary_command"]
)
diagnosis["operator_decision"] = operator_decision
diagnosis["next_actions"] = next_actions
diagnosis["recommended_commands"] = recommended_commands
payload["diagnosis"] = diagnosis
print(json.dumps(payload, ensure_ascii=False, indent=2))
PY
exit 0
fi
fi
fetch_json "${BASE_URL}/health"
HEALTH_JSON="${FETCH_BODY}"
HEALTH_STATUS="${FETCH_STATUS}"
fetch_json "${BASE_URL}/api/v1/ops/contracts"
CONTRACTS_JSON="${FETCH_BODY}"
CONTRACTS_STATUS="${FETCH_STATUS}"
fetch_json "${BASE_URL}/api/v1/ops/link-snapshot"
LINK_SNAPSHOT_JSON="${FETCH_BODY}"
LINK_SNAPSHOT_STATUS="${FETCH_STATUS}"
fetch_json "${BASE_URL}/api/v1/ops/overview"
OVERVIEW_JSON="${FETCH_BODY}"
OVERVIEW_STATUS="${FETCH_STATUS}"
fetch_json "${BASE_URL}/api/v1/ops/nodes"
NODES_JSON="${FETCH_BODY}"
NODES_STATUS="${FETCH_STATUS}"
fetch_json "${BASE_URL}/api/v1/ops/releases/launchpad"
LAUNCHPAD_JSON="${FETCH_BODY}"
LAUNCHPAD_STATUS="${FETCH_STATUS}"
fetch_json "${BASE_URL}/api/v1/ops/playbook-runs?limit=6"
PLAYBOOK_RUNS_JSON="${FETCH_BODY}"
PLAYBOOK_RUNS_STATUS="${FETCH_STATUS}"
fetch_json "${BASE_URL}/api/v1/ops/activity-stream?limit=8"
ACTIVITY_JSON="${FETCH_BODY}"
ACTIVITY_STATUS="${FETCH_STATUS}"
if [[ "${VIEW_MODE}" != "summary" ]]; then
echo "[1/9] api health"
printf 'status=%s\n%s\n\n' "${HEALTH_STATUS}" "${HEALTH_JSON}"
echo "[2/9] ops contracts"
printf 'status=%s\n%s\n\n' "${CONTRACTS_STATUS}" "${CONTRACTS_JSON}"
echo "[3/9] ops link snapshot"
printf 'status=%s\n%s\n\n' "${LINK_SNAPSHOT_STATUS}" "${LINK_SNAPSHOT_JSON}"
echo "[4/9] ops overview"
printf 'status=%s\n%s\n\n' "${OVERVIEW_STATUS}" "${OVERVIEW_JSON}"
echo "[5/9] managed nodes"
printf 'status=%s\n%s\n\n' "${NODES_STATUS}" "${NODES_JSON}"
echo "[6/9] release launchpad"
printf 'status=%s\n%s\n\n' "${LAUNCHPAD_STATUS}" "${LAUNCHPAD_JSON}"
echo "[7/9] recent playbook runs"
printf 'status=%s\n%s\n\n' "${PLAYBOOK_RUNS_STATUS}" "${PLAYBOOK_RUNS_JSON}"
echo "[8/9] recent activity stream"
printf 'status=%s\n%s\n\n' "${ACTIVITY_STATUS}" "${ACTIVITY_JSON}"
fi
echo "[9/9] condensed stack summary"
STACK_BASE_URL="${BASE_URL}" python3 - <<'PY' \
"${STACK_DIAGNOSIS_JSON}" \
"${STACK_DIAGNOSIS_STATUS}" \
"${HEALTH_JSON}" \
"${HEALTH_STATUS}" \
"${CONTRACTS_JSON}" \
"${CONTRACTS_STATUS}" \
"${LINK_SNAPSHOT_JSON}" \
"${LINK_SNAPSHOT_STATUS}" \
"${OVERVIEW_JSON}" \
"${OVERVIEW_STATUS}" \
"${NODES_JSON}" \
"${NODES_STATUS}" \
"${LAUNCHPAD_JSON}" \
"${LAUNCHPAD_STATUS}" \
"${PLAYBOOK_RUNS_JSON}" \
"${PLAYBOOK_RUNS_STATUS}" \
"${ACTIVITY_JSON}" \
"${ACTIVITY_STATUS}"
import json
import os
import sys
def ensure_str(value):
return value if isinstance(value, str) else ""
def load_payload(raw_text: str) -> dict:
try:
return json.loads(raw_text)
except Exception:
return {}
def as_data(payload: dict) -> dict:
if isinstance(payload.get("data"), dict):
return dict(payload.get("data") or {})
return {}
def issue_rank(severity: str) -> int:
return {"blocked": 3, "warning": 2, "info": 1}.get(str(severity or "").strip(), 0)
def command(*parts: str) -> str:
tokens = ["bash", "domain-api/deploy/multi-region/drive_ops_center.sh"]
tokens.extend([str(part).strip() for part in parts if str(part).strip()])
return " ".join(tokens)
def release_command(*parts: str) -> str:
tokens = ["bash", "domain-api/deploy/multi-region/drive_release_hub.sh"]
tokens.extend([str(part).strip() for part in parts if str(part).strip()])
return " ".join(tokens)
stack_diagnosis_payload = load_payload(sys.argv[1])
STACK_DIAGNOSIS_STATUS = str(sys.argv[2] or "000")
health = load_payload(sys.argv[3])
health_status = str(sys.argv[4] or "000")
contracts_payload = load_payload(sys.argv[5])
contracts_status = str(sys.argv[6] or "000")
link_snapshot_payload = load_payload(sys.argv[7])
link_snapshot_status = str(sys.argv[8] or "000")
overview_payload = load_payload(sys.argv[9])
overview_status = str(sys.argv[10] or "000")
nodes_root_payload = load_payload(sys.argv[11])
nodes_status = str(sys.argv[12] or "000")
launchpad_payload = load_payload(sys.argv[13])
launchpad_status_code = str(sys.argv[14] or "000")
playbook_runs_root_payload = load_payload(sys.argv[15])
playbook_runs_status = str(sys.argv[16] or "000")
activity_root_payload = load_payload(sys.argv[17])
activity_status = str(sys.argv[18] or "000")
contracts = as_data(contracts_payload)
link_snapshot = as_data(link_snapshot_payload)
overview = as_data(overview_payload)
nodes_payload = as_data(nodes_root_payload)
launchpad = as_data(launchpad_payload)
playbook_runs_payload = as_data(playbook_runs_root_payload)
activity_payload = as_data(activity_root_payload)
stack_base_url = str(os.environ.get("STACK_BASE_URL") or "").strip()
health_build = dict(health.get("build") or {})
health_route_surface = dict(health_build.get("route_surface") or {})
stack_diagnosis = as_data(stack_diagnosis_payload)
diagnosis = dict(stack_diagnosis.get("diagnosis") or {})
contracts_rows = list(contracts.get("contracts") or [])
nodes = list(nodes_payload.get("nodes") or [])
nodes_summary = dict(nodes_payload.get("summary") or {})
playbook_runs = list(playbook_runs_payload.get("runs") or playbook_runs_payload.get("playbook_runs") or [])
activities = list(activity_payload.get("items") or activity_payload.get("activities") or [])
recommendation = dict(overview.get("recommendation") or {})
execution_scene = dict(overview.get("execution_scene") or {})
log_sync = dict(execution_scene.get("log_sync") or overview.get("log_sync") or {})
release_hub = dict(overview.get("release_hub") or {})
default_rollout_gate = dict(release_hub.get("default_rollout_gate") or {})
launchpad_status = dict(launchpad.get("launchpad_status") or {})
latest_release = dict(launchpad.get("latest_release") or release_hub.get("latest_release") or {})
problem_nodes = []
for node in nodes:
node_code = str(node.get("node_code") or "").strip()
if not node_code:
continue
queue_state = str(node.get("delivery_queue_state") or "").strip()
agent_state = str(node.get("agent_state") or "").strip()
remote_access_state = str(node.get("remote_access_state") or "").strip()
if queue_state == "dead_letter" or agent_state not in {"online", "online_busy"} or remote_access_state in {
"agent_pending",
"disabled",
"unmanaged",
}:
problem_nodes.append(
{
"node_code": node_code,
"agent_state": agent_state,
"remote_access_state": remote_access_state,
"delivery_queue_state": queue_state,
"participation_state": str(node.get("participation_state") or "").strip(),
}
)
problem_runs = [
{
"run_code": str(item.get("run_code") or "").strip(),
"status": str(item.get("status") or "").strip(),
"status_label": str(item.get("status_label") or "").strip(),
"focus_step_key": str(item.get("focus_step_key") or "").strip(),
"focus_summary": str(item.get("focus_summary") or item.get("summary_text") or "").strip(),
}
for item in playbook_runs
if str(item.get("status") or "").strip() not in {"success", "completed", "healthy"}
]
activity_counts = {}
for item in activities:
status_key = str(item.get("status") or item.get("job_status") or "unknown").strip() or "unknown"
activity_counts[status_key] = int(activity_counts.get(status_key, 0) or 0) + 1
issues = []
def add_issue(
*,
code: str,
severity: str,
layer: str,
summary: str,
detail: str = "",
action_code: str = "",
focus_ref=None,
commands=None,
) -> None:
issues.append(
{
"code": str(code or "").strip(),
"severity": str(severity or "").strip() or "warning",
"layer": str(layer or "").strip(),
"summary": str(summary or "").strip(),
"detail": str(detail or "").strip(),
"action_code": str(action_code or "").strip(),
"focus_ref": dict(focus_ref or {}),
"commands": [str(item).strip() for item in list(commands or []) if str(item).strip()],
}
)
surface_matrix = [
("stack_diagnosis", STACK_DIAGNOSIS_STATUS.startswith("2")),
("api", health_status.startswith("2")),
("contracts", contracts_status.startswith("2")),
("link_snapshot", link_snapshot_status.startswith("2")),
("overview", overview_status.startswith("2")),
("managed_nodes", nodes_status.startswith("2")),
("release_hub", launchpad_status_code.startswith("2")),
("playbook_runs", playbook_runs_status.startswith("2")),
("activity_stream", activity_status.startswith("2")),
]
surface_status_counts = {
"total": len(surface_matrix),
"available": sum(1 for _, available in surface_matrix if available),
"missing": sum(1 for _, available in surface_matrix if not available),
}
missing_surfaces = [name for name, available in surface_matrix if not available]
if not health_status.startswith("2"):
add_issue(
code="api_unreachable",
severity="blocked",
layer="api",
summary="控制面 API 当前不可用,总检无法进入正式判断链路。",
detail=f"/health 返回 HTTP {health_status}。",
commands=[
f"curl -s {stack_base_url}/health",
"systemctl status domaincheck-api --no-pager -l",
],
)
stack_diagnosis_hint = (
"当前 stack-diagnosis 没有成功返回,总检已退化为多 surface 拼装视图。"
)
if not STACK_DIAGNOSIS_STATUS.startswith("2"):
stack_diag_detail = (
f"/api/v1/ops/stack-diagnosis 返回 HTTP {STACK_DIAGNOSIS_STATUS}。"
if str(STACK_DIAGNOSIS_STATUS).isdigit() and str(STACK_DIAGNOSIS_STATUS) != "000"
else "/api/v1/ops/stack-diagnosis 当前不可达或请求超时。"
)
add_issue(
code="stack_diagnosis_unavailable",
severity="warning" if health_status.startswith("2") else "blocked",
layer="stack_diagnosis",
summary=stack_diagnosis_hint,
detail=(
stack_diag_detail
+ " 建议先单独复跑 stack-diagnosis / stack-next确认是不是 API 慢、路由未挂齐,或者服务未切到最新版本。"
),
commands=[
f"curl -s {stack_base_url}/api/v1/ops/stack-diagnosis?base_url={stack_base_url}",
f"bash domain-api/deploy/multi-region/drive_ops_center.sh stack-next {stack_base_url}",
"systemctl status domaincheck-api --no-pager -l",
],
)
if health_status.startswith("2") and not contracts_status.startswith("2"):
add_issue(
code="ops_contracts_endpoint_missing",
severity="warning",
layer="contracts",
summary="控制面 API 已在线,但 ops/contracts 未正常返回,存在运行实例与当前代码契约不一致的风险。",
detail=(
f"/api/v1/ops/contracts 返回 HTTP {contracts_status}"
"这通常意味着当前 systemd 进程没有跑到最新接口集,或部署包/服务重启未完全对齐。"
),
commands=[
f"curl -s {stack_base_url}/api/v1/ops/contracts",
"systemctl status domaincheck-api --no-pager -l",
f"bash domain-api/deploy/multi-region/drive_ops_center.sh runtime-refresh-recover {stack_base_url}",
],
)
if health_status.startswith("2") and not health_build:
add_issue(
code="api_build_identity_missing",
severity="warning",
layer="api",
summary="控制面 health 还没有暴露 build 指纹,当前不利于快速判断服务是否真的切到最新部署内容。",
detail="建议升级到包含 build + route_surface 指纹的新版本后再继续多机联调。",
commands=[
f"curl -s {stack_base_url}/health",
f"bash domain-api/deploy/multi-region/drive_ops_center.sh runtime-refresh-recover {stack_base_url}",
],
)
if health_status.startswith("2") and health_route_surface and not bool(health_route_surface.get("surface_complete", True)):
add_issue(
code="api_route_surface_incomplete",
severity="warning",
layer="api.routes",
summary="当前运行中的控制面实例没有挂齐关键协议路由,存在代码与服务实例不一致的风险。",
detail=(
"缺少路由: "
+ ", ".join([str(item).strip() for item in list(health_route_surface.get("missing_paths") or []) if str(item).strip()])
),
commands=[
f"curl -s {stack_base_url}/health",
f"bash domain-api/deploy/multi-region/drive_ops_center.sh runtime-refresh-recover {stack_base_url}",
],
)
if health_status.startswith("2") and missing_surfaces and any(
name in missing_surfaces for name in ("link_snapshot", "overview", "managed_nodes", "release_hub")
):
add_issue(
code="ops_surface_partial",
severity="warning",
layer="stack",
summary="海外单脑控制面关键观察层没有全部联通,总检已退化为部分收口。",
detail=f"当前缺失层: {', '.join(missing_surfaces)}",
commands=[
f"bash domain-api/deploy/multi-region/drive_ops_center.sh stack-diagnosis {stack_base_url} full",
f"bash domain-api/deploy/multi-region/drive_ops_center.sh doctor {stack_base_url}",
],
)
participating_nodes_total = int(log_sync.get("participating_nodes_total", log_sync.get("participating_node_count", 0)) or 0)
if bool(log_sync.get("enabled", False)) is False and participating_nodes_total > 0:
log_sync_action = ""
next_action_codes = list(link_snapshot.get("next_actions") or [])
for item in next_action_codes:
action_code = str(item.get("action_code") or "").strip()
if action_code.startswith("enable_log_sync"):
log_sync_action = action_code
break
if not log_sync_action:
log_sync_action = "enable_log_sync_key"
add_issue(
code="remote_log_sync_disabled",
severity="warning",
layer="overview.log_sync",
summary="现场已有参与检测节点,但远端日志回传仍关闭,海外控制面对执行细节仍处于半盲态。",
detail=(
f"参与检测节点 {participating_nodes_total} 台,"
f"已覆盖 {int(log_sync.get('covered_participating_nodes', log_sync.get('covered_participating_node_count', 0)) or 0)} 台。"
),
action_code=log_sync_action,
commands=[
f"bash domain-api/deploy/multi-region/drive_ops_center.sh driver-resolve {stack_base_url} {log_sync_action}",
],
)
managed_enabled = int(nodes_summary.get("managed_enabled", 0) or 0)
remote_access_ready = int(nodes_summary.get("remote_access_ready", 0) or 0)
if managed_enabled > 0 and remote_access_ready == 0:
add_issue(
code="managed_nodes_agent_pending",
severity="blocked",
layer="managed_nodes",
summary="托管节点虽然已经纳入 Ops Center但 0 台 remote-agent 就绪,海外主控还不能统一下发标准执行动作。",
detail=(
f"managed_enabled={managed_enabled}remote_access_ready={remote_access_ready}"
"当前节点多数仍处于 agent_pending / runtime_only / pending_bootstrap。"
),
action_code=str(launchpad_status.get("recommended_action_code") or "fix_managed_nodes"),
focus_ref=dict(launchpad_status.get("focus_ref") or {}),
commands=[
f"bash domain-api/deploy/multi-region/drive_ops_center.sh driver-resolve {stack_base_url} fix_managed_nodes",
f"bash domain-api/deploy/multi-region/drive_ops_center.sh doctor {stack_base_url}",
],
)
queue_dead_letter_nodes = int(nodes_summary.get("queue_dead_letter_nodes", 0) or 0)
queue_dead_letter_records = int(nodes_summary.get("queue_dead_letter_records", 0) or 0)
if queue_dead_letter_nodes > 0 or queue_dead_letter_records > 0:
add_issue(
code="delivery_queue_dead_letter",
severity="warning",
layer="managed_nodes.queue",
summary="存在 Node Agent 回执死信,后续自动化动作可能反复失败或无法收敛。",
detail=(
f"queue_dead_letter_nodes={queue_dead_letter_nodes}"
f"queue_dead_letter_records={queue_dead_letter_records}"
),
commands=[
f"bash domain-api/deploy/multi-region/drive_ops_center.sh doctor {stack_base_url}",
],
)
if str(default_rollout_gate.get("status") or "").strip() == "blocked":
add_issue(
code="release_rollout_gate_blocked",
severity="warning",
layer="release_hub",
summary="ReleaseHub 默认门禁当前阻断,说明虽然发布包已准备好,但正式 Rollout 还不具备条件。",
detail=str(default_rollout_gate.get("summary_text") or default_rollout_gate.get("summary") or "").strip(),
action_code=str(launchpad_status.get("recommended_action_code") or "").strip(),
focus_ref=dict(default_rollout_gate.get("focus_ref") or launchpad_status.get("focus_ref") or {}),
commands=[
f"bash domain-api/deploy/multi-region/drive_ops_center.sh release-launchpad {stack_base_url}",
f"bash domain-api/deploy/multi-region/drive_ops_center.sh driver-resolve {stack_base_url} {str(launchpad_status.get('recommended_action_code') or 'fix_managed_nodes').strip()}",
],
)
if problem_runs:
add_issue(
code="playbook_runs_need_attention",
severity="warning",
layer="playbook_runs",
summary="最近存在未完全收口的 playbook run建议先确认执行序列是否停在中间步骤。",
detail=f"problem_runs_total={len(problem_runs)}",
commands=[
f"bash domain-api/deploy/multi-region/drive_ops_center.sh doctor {stack_base_url}",
],
)
ordered_action_candidates = [
{
"source": "overview.primary",
"action_code": str(recommendation.get("primary_action_code") or "").strip(),
"title": str(recommendation.get("title") or "").strip(),
"reason": "来自 overview.recommendation.primary_action_code",
"focus_ref": dict(recommendation.get("focus_ref") or {}),
},
{
"source": "release_hub.launchpad",
"action_code": str(launchpad_status.get("recommended_action_code") or "").strip(),
"title": str(launchpad_status.get("status_label") or "").strip(),
"reason": "来自 release_launchpad.launchpad_status.recommended_action_code",
"focus_ref": dict(launchpad_status.get("focus_ref") or {}),
},
{
"source": "overview.secondary",
"action_code": str(recommendation.get("secondary_action_code") or "").strip(),
"title": str(recommendation.get("title") or "").strip(),
"reason": "来自 overview.recommendation.secondary_action_code",
"focus_ref": dict(recommendation.get("focus_ref") or {}),
},
]
for issue in issues:
issue_action_code = str(issue.get("action_code") or "").strip()
if issue_action_code:
ordered_action_candidates.append(
{
"source": f"issue:{issue.get('code')}",
"action_code": issue_action_code,
"title": str(issue.get("summary") or "").strip(),
"reason": str(issue.get("detail") or issue.get("summary") or "").strip(),
"focus_ref": dict(issue.get("focus_ref") or {}),
}
)
recommended_actions = []
seen_action_codes = set()
for item in ordered_action_candidates:
action_code = str(item.get("action_code") or "").strip()
if not action_code or action_code in seen_action_codes:
continue
seen_action_codes.add(action_code)
recommended_actions.append(
{
"action_code": action_code,
"source": str(item.get("source") or "").strip(),
"title": str(item.get("title") or "").strip(),
"reason": str(item.get("reason") or "").strip(),
"focus_ref": dict(item.get("focus_ref") or {}),
"command": f"bash domain-api/deploy/multi-region/drive_ops_center.sh driver-resolve {stack_base_url} {action_code}",
}
)
blocking_issue_total = sum(1 for item in issues if str(item.get("severity") or "").strip() == "blocked")
warning_issue_total = sum(1 for item in issues if str(item.get("severity") or "").strip() == "warning")
info_issue_total = sum(1 for item in issues if str(item.get("severity") or "").strip() == "info")
surface_status = "healthy"
if not health_status.startswith("2"):
surface_status = "broken"
elif missing_surfaces:
surface_status = "partial"
automation_status = "ready"
if blocking_issue_total > 0:
automation_status = "blocked"
elif warning_issue_total > 0:
automation_status = "attention"
stack_status = "ready"
if surface_status == "broken" or automation_status == "blocked":
stack_status = "blocked"
elif surface_status != "healthy" or automation_status != "ready":
stack_status = "attention"
top_issue = None
if issues:
top_issue = sorted(
issues,
key=lambda item: (
-issue_rank(str(item.get("severity") or "").strip()),
str(item.get("code") or "").strip(),
),
)[0]
next_step = {}
if recommended_actions:
first_action = dict(recommended_actions[0] or {})
next_step = {
"action_code": str(first_action.get("action_code") or "").strip(),
"source": str(first_action.get("source") or "").strip(),
"reason": str(first_action.get("reason") or "").strip(),
"command": str(first_action.get("command") or "").strip(),
"focus_ref": dict(first_action.get("focus_ref") or {}),
}
elif top_issue:
next_step = {
"action_code": "",
"source": f"issue:{str(top_issue.get('code') or '').strip()}",
"reason": str(top_issue.get("summary") or "").strip(),
"command": "",
"focus_ref": dict(top_issue.get("focus_ref") or {}),
}
next_step_focus_ref = dict(next_step.get("focus_ref") or {})
launchpad_target_node_code = str(
diagnosis.get("launchpad_recommended_target_node_code")
or next_step_focus_ref.get("node_code")
or ""
).strip()
launchpad_recovery_label = str(diagnosis.get("launchpad_recommended_recovery_label") or "").strip()
launchpad_recovery_summary = str(diagnosis.get("launchpad_recommended_recovery_summary") or next_step.get("reason") or "").strip()
handover_next_action_code = str(next_step.get("action_code") or "").strip()
scene_log_missing_nodes = list(
log_sync.get("missing_sample_node_codes")
or log_sync.get("missing_participating_nodes")
or []
)
scene_log_target_node_code = ""
if scene_log_missing_nodes:
scene_log_target_node_code = str(scene_log_missing_nodes[0] or "").strip()
elif problem_nodes:
scene_log_target_node_code = str(problem_nodes[0].get("node_code") or "").strip()
recommended_commands = {
"stack_summary": f"bash domain-api/deploy/multi-region/check_ops_center_stack.sh {stack_base_url} summary",
"doctor": command("doctor", stack_base_url),
"driver_feed": command("driver-feed", stack_base_url),
"driver_focus_preview": command("driver-focus-preview", stack_base_url),
"driver_focus_run": command("driver-focus-run", stack_base_url),
"go_live_recover": command("go-live-recover", stack_base_url),
"log_sync_recover": command("log-sync-recover", stack_base_url, "key"),
"log_sync_recover_full": command("log-sync-recover", stack_base_url, "full"),
"agent_gap_check": command("agent-gap-check", stack_base_url),
"agent_gap_recover": command("agent-gap-recover", stack_base_url),
"api_restart": command("runtime-refresh-recover", stack_base_url),
"node_onboarding": (
command("node-onboarding", stack_base_url, str(problem_nodes[0].get("node_code") or "").strip())
if problem_nodes and str(problem_nodes[0].get("node_code") or "").strip()
else ""
),
"node_recover": (
command("node-recover", stack_base_url, str(problem_nodes[0].get("node_code") or "").strip())
if problem_nodes and str(problem_nodes[0].get("node_code") or "").strip()
else ""
),
"node_bootstrap_preview": (
command("node-bootstrap-preview", stack_base_url, str(problem_nodes[0].get("node_code") or "").strip())
if problem_nodes and str(problem_nodes[0].get("node_code") or "").strip()
else ""
),
"node_bootstrap_run": (
command("node-bootstrap-run", stack_base_url, launchpad_target_node_code, "cli")
if launchpad_target_node_code
else command("node-bootstrap-run", stack_base_url, str(problem_nodes[0].get("node_code") or "").strip(), "cli")
if launchpad_target_node_code or (problem_nodes and str(problem_nodes[0].get("node_code") or "").strip())
else ""
),
"node_acceptance_run": (
command("node-acceptance-run", stack_base_url, launchpad_target_node_code, "cli")
if launchpad_target_node_code
else command("node-acceptance-run", stack_base_url, str(problem_nodes[0].get("node_code") or "").strip(), "cli")
if launchpad_target_node_code or (problem_nodes and str(problem_nodes[0].get("node_code") or "").strip())
else ""
),
"node_acceptance_plan": (
command("node-acceptance-plan", stack_base_url, str(problem_nodes[0].get("node_code") or "").strip())
if problem_nodes and str(problem_nodes[0].get("node_code") or "").strip()
else ""
),
"scene_log": (
command("scene-node-log", stack_base_url, scene_log_target_node_code, "120", "key")
if scene_log_target_node_code
else ""
),
"release_launchpad": release_command(stack_base_url, "launchpad"),
"release_preview_latest": release_command(stack_base_url, "preview-latest"),
"jobs": command("activity-stream", stack_base_url, "kind=ops_job"),
"node_handover": (
command("node-handover", stack_base_url, str(problem_nodes[0].get("node_code") or "").strip())
if problem_nodes and str(problem_nodes[0].get("node_code") or "").strip()
else command("stack-first-gap-handover", stack_base_url)
),
}
operator_decision = {
"lane": "steady",
"priority": "low",
"reason_code": "steady_state",
"title": "当前进入稳定观察态",
"summary": "当前总检没有发现新的阻断问题,可继续按既有链路观察与推进。",
"next_focus": next_step_focus_ref,
"primary_command_key": "driver_feed",
"secondary_command_key": "jobs",
}
if str(log_sync.get("enabled", False)).lower() in {"false", ""} and participating_nodes_total > 0:
operator_decision = {
"lane": "observability",
"priority": "high",
"reason_code": "scene_log_disabled",
"title": "先恢复远端现场日志可见性",
"summary": "当前已有参与检测节点,但远端日志回传仍关闭,海外控制面仍处于半盲态。",
"next_focus": {"kind": "node_scene_log", "node_code": scene_log_target_node_code, "mode": "key", "limit": 80}
if scene_log_target_node_code
else {},
"primary_command_key": "log_sync_recover",
"secondary_command_key": "scene_log",
}
elif managed_enabled > 0 and remote_access_ready == 0:
primary_command_key = "agent_gap_recover"
secondary_command_key = "node_handover"
title = "先补齐节点接管与 remote-agent 可达性"
summary = (
f"managed_enabled={managed_enabled}remote_access_ready={remote_access_ready}"
"当前还不能从海外控制面统一下发标准动作。"
)
if handover_next_action_code == "bootstrap_run" and launchpad_target_node_code:
primary_command_key = "node_bootstrap_run"
secondary_command_key = "node_handover"
title = "先跑首台节点接入收口"
summary = (
launchpad_recovery_summary
or join_text(
launchpad_recovery_label,
launchpad_target_node_code,
"当前真正阻断已经收敛到首台节点接入收口。"
)
)
elif handover_next_action_code == "run_acceptance" and launchpad_target_node_code:
primary_command_key = "node_acceptance_run"
secondary_command_key = "node_handover"
title = "先跑首台节点接管验收"
summary = (
launchpad_recovery_summary
or join_text(
launchpad_recovery_label,
launchpad_target_node_code,
"当前真正阻断已经收敛到首台节点接管验收。"
)
)
operator_decision = {
"lane": "node_handover",
"priority": "high",
"reason_code": "managed_nodes_agent_pending",
"title": title,
"summary": summary,
"next_focus": next_step_focus_ref,
"primary_command_key": primary_command_key,
"secondary_command_key": secondary_command_key,
}
elif str(default_rollout_gate.get("status") or "").strip() == "blocked":
operator_decision = {
"lane": "release",
"priority": "medium",
"reason_code": "release_rollout_gate_blocked",
"title": "先检查发布门禁与 rollout 阻断",
"summary": str(default_rollout_gate.get("summary_text") or default_rollout_gate.get("summary") or "").strip(),
"next_focus": dict(default_rollout_gate.get("focus_ref") or launchpad_status.get("focus_ref") or {}),
"primary_command_key": "release_launchpad",
"secondary_command_key": "release_preview_latest",
}
elif any(str(item.get("code") or "").strip() == "runtime_build_schema_stale" for item in issues):
operator_decision = {
"lane": "runtime_recovery",
"priority": "high",
"reason_code": "runtime_build_schema_stale",
"title": "先执行运行时刷新与上线复核",
"summary": "当前已经识别到仓库代码与运行中 API schema 之间存在漂移,先走统一恢复链,再继续节点接管或发布判断。",
"next_focus": dict(next_step.get("focus_ref") or {}),
"primary_command_key": "go_live_recover",
"secondary_command_key": "api_restart",
}
elif problem_runs:
operator_decision = {
"lane": "ops_jobs",
"priority": "medium",
"reason_code": "playbook_runs_need_attention",
"title": "先确认未收口的 playbook run",
"summary": f"最近存在 {len(problem_runs)} 个未完全收口的 playbook run。",
"next_focus": dict(next_step.get("focus_ref") or {}),
"primary_command_key": "doctor",
"secondary_command_key": "jobs",
}
next_actions = {
"primary_command": str(recommended_commands.get(str(operator_decision.get("primary_command_key") or "").strip()) or "").strip(),
"secondary_command": str(recommended_commands.get(str(operator_decision.get("secondary_command_key") or "").strip()) or "").strip(),
}
quick_commands = [
f"bash domain-api/deploy/multi-region/drive_ops_center.sh stack-diagnosis {stack_base_url} summary",
f"bash domain-api/deploy/multi-region/drive_ops_center.sh doctor {stack_base_url}",
]
for action in recommended_actions[:3]:
command = str(action.get("command") or "").strip()
if command and command not in quick_commands:
quick_commands.append(command)
if not contracts_status.startswith("2"):
contract_check_command = f"curl -s {stack_base_url}/api/v1/ops/contracts"
if contract_check_command not in quick_commands:
quick_commands.append(contract_check_command)
operator_hints = []
if stack_status == "blocked":
operator_hints.append("当前总检已经能看到现场,但海外单脑自动化执行链路仍未正式打通,先处理阻断问题再继续 Rollout 或批量运维。")
elif stack_status == "attention":
operator_hints.append("当前总检可用,但仍有缺口;建议先按 next_step 收口,再进入更细粒度脚本。")
else:
operator_hints.append("当前海外单脑控制面总检已基本就绪,可以把它作为后续所有联调和发布的固定起手式。")
if not contracts_status.startswith("2") and health_status.startswith("2"):
operator_hints.append("health 正常但 contracts 缺失,优先怀疑运行中的 API 实例不是这份最新代码,或服务重启后未真正切到最新部署内容。")
if bool(log_sync.get("enabled", False)) is False and participating_nodes_total > 0:
operator_hints.append("远端日志回传当前关闭,后续如果继续多机联调,海外控制面会继续处于半盲态。")
summary = {
"diagnosis": {
"base_url": stack_base_url,
"surface_status": surface_status,
"automation_status": automation_status,
"stack_status": stack_status,
"issue_total": len(issues),
"blocking_issue_total": blocking_issue_total,
"warning_issue_total": warning_issue_total,
"info_issue_total": info_issue_total,
"missing_surfaces": missing_surfaces,
"launchpad_recommended_target_node_code": ensure_str(diagnosis.get("launchpad_recommended_target_node_code")),
"launchpad_recommended_recovery_label": ensure_str(diagnosis.get("launchpad_recommended_recovery_label")),
"launchpad_recommended_recovery_summary": ensure_str(diagnosis.get("launchpad_recommended_recovery_summary")),
"launchpad_onboarding_bootstrap_pending_nodes": int(diagnosis.get("launchpad_onboarding_bootstrap_pending_nodes", 0) or 0),
"launchpad_onboarding_acceptance_ready_nodes": int(diagnosis.get("launchpad_onboarding_acceptance_ready_nodes", 0) or 0),
"next_step": next_step,
"recommended_actions": recommended_actions[:5],
"operator_decision": operator_decision,
"next_actions": next_actions,
"issues": issues[:8],
"operator_hints": operator_hints,
"quick_commands": quick_commands,
"recommended_commands": recommended_commands,
},
"api": {
"stack_diagnosis_http_status": STACK_DIAGNOSIS_STATUS,
"stack_diagnosis_available": STACK_DIAGNOSIS_STATUS.startswith("2"),
"http_status": health_status,
"available": health_status.startswith("2"),
"service": str(health.get("service") or ""),
"version": str(health.get("version") or ""),
"api_prefix": str(health.get("api_prefix") or ""),
"worker_mode": str(health.get("worker_mode") or ""),
"build": {
"source": str(health_build.get("source") or ""),
"package_name": str(health_build.get("package_name") or ""),
"generated_at": str(health_build.get("generated_at") or ""),
"commit_sha": str(health_build.get("commit_sha") or ""),
"commit_ref": str(health_build.get("commit_ref") or ""),
"manifest_path": str(health_build.get("manifest_path") or ""),
"route_surface_complete": bool(health_route_surface.get("surface_complete", False)) if health_route_surface else False,
"route_surface_missing": [
str(item).strip()
for item in list(health_route_surface.get("missing_paths") or [])
if str(item).strip()
],
},
},
"surface_matrix": {
"status_counts": surface_status_counts,
"items": [
{"name": name, "available": bool(available)}
for name, available in surface_matrix
],
},
"contracts": {
"http_status": contracts_status,
"available": contracts_status.startswith("2"),
"registry_version": str(contracts.get("registry_version") or ""),
"schema_version": str(contracts.get("schema_version") or ""),
"contracts_total": len(contracts_rows),
"contract_keys": [str(item.get("key") or "").strip() for item in contracts_rows if str(item.get("key") or "").strip()],
"error": str(contracts_payload.get("detail") or contracts_payload.get("message") or ""),
},
"link_snapshot": {
"http_status": link_snapshot_status,
"available": link_snapshot_status.startswith("2"),
"status": str(link_snapshot.get("status") or ""),
"headline": str(link_snapshot.get("headline") or ""),
"next_action_codes": [
str(item.get("action_code") or "").strip()
for item in list(link_snapshot.get("next_actions") or [])
if str(item.get("action_code") or "").strip()
],
"error": str(link_snapshot_payload.get("detail") or link_snapshot_payload.get("message") or ""),
},
"overview": {
"http_status": overview_status,
"available": overview_status.startswith("2"),
"recommendation_key": str(recommendation.get("key") or ""),
"recommendation_title": str(recommendation.get("title") or ""),
"primary_action_code": str(recommendation.get("primary_action_code") or ""),
"secondary_action_code": str(recommendation.get("secondary_action_code") or ""),
"focus_ref": dict(recommendation.get("focus_ref") or {}),
"execution_scene_summary": str(
execution_scene.get("summary")
or (execution_scene.get("participation_summary") or {}).get("summary")
or execution_scene.get("phase_detail")
or ""
),
"execution_scene_counts": dict(execution_scene.get("counts") or {}),
"log_sync": {
"status": str(log_sync.get("status") or log_sync.get("state") or ""),
"status_label": str(log_sync.get("status_label") or ""),
"mode": str(log_sync.get("mode") or ""),
"mode_label": str(log_sync.get("mode_label") or ""),
"enabled": bool(log_sync.get("enabled", False)),
"covered_participating_nodes": int(
log_sync.get("covered_participating_nodes", log_sync.get("covered_participating_node_count", 0)) or 0
),
"participating_nodes_total": int(
log_sync.get("participating_nodes_total", log_sync.get("participating_node_count", 0)) or 0
),
"missing_sample_node_codes": list(
log_sync.get("missing_sample_node_codes")
or log_sync.get("missing_participating_nodes")
or []
),
},
"error": str(overview_payload.get("detail") or overview_payload.get("message") or ""),
},
"managed_nodes": {
"http_status": nodes_status,
"available": nodes_status.startswith("2"),
"total": int(nodes_summary.get("total", len(nodes)) or 0),
"managed_total": int(nodes_summary.get("managed_total", 0) or 0),
"managed_enabled": int(nodes_summary.get("managed_enabled", 0) or 0),
"online": int(nodes_summary.get("online", 0) or 0),
"agent_ready": int(nodes_summary.get("agent_ready", 0) or 0),
"ssh_ready": int(nodes_summary.get("ssh_ready", 0) or 0),
"remote_access_ready": int(nodes_summary.get("remote_access_ready", 0) or 0),
"participating": int(nodes_summary.get("participating", 0) or 0),
"dispatch_active": int(nodes_summary.get("dispatch_active", 0) or 0),
"standby": int(nodes_summary.get("standby", 0) or 0),
"load_syncing": int(nodes_summary.get("load_syncing", 0) or 0),
"queue_retrying_nodes": int(nodes_summary.get("queue_retrying_nodes", 0) or 0),
"queue_dead_letter_nodes": int(nodes_summary.get("queue_dead_letter_nodes", 0) or 0),
"queue_pending_records": int(nodes_summary.get("queue_pending_records", 0) or 0),
"queue_dead_letter_records": int(nodes_summary.get("queue_dead_letter_records", 0) or 0),
"agent_state_counts": dict(nodes_summary.get("status_counts") or {}),
"remote_access_state_counts": dict(nodes_summary.get("remote_access_state_counts") or {}),
"delivery_queue_state_counts": dict(nodes_summary.get("delivery_queue_state_counts") or {}),
"problem_nodes": problem_nodes[:8],
"error": str(nodes_root_payload.get("detail") or nodes_root_payload.get("message") or ""),
},
"release_hub": {
"http_status": launchpad_status_code,
"available": launchpad_status_code.startswith("2"),
"latest_release_version": str(latest_release.get("release_version") or ""),
"latest_release_status": str(latest_release.get("status") or ""),
"default_rollout_gate_status": str(default_rollout_gate.get("status") or ""),
"default_rollout_gate_label": str(default_rollout_gate.get("status_label") or ""),
"default_rollout_gate_summary": str(default_rollout_gate.get("summary_text") or default_rollout_gate.get("summary") or ""),
"default_rollout_gate_focus_ref": dict(default_rollout_gate.get("focus_ref") or {}),
"launchpad_status": str(launchpad_status.get("status") or ""),
"launchpad_status_label": str(launchpad_status.get("status_label") or ""),
"launchpad_summary": str(launchpad_status.get("summary_text") or launchpad_status.get("summary") or ""),
"recommended_action_code": str(launchpad_status.get("recommended_action_code") or ""),
"recommended_target_node_code": str(launchpad_status.get("recommended_target_node_code") or ""),
"recommended_recovery_label": str(launchpad_status.get("recommended_recovery_label") or ""),
"recommended_recovery_summary": str(launchpad_status.get("recommended_recovery_summary") or ""),
"onboarding_bootstrap_pending_nodes": int(launchpad_status.get("onboarding_bootstrap_pending_nodes", 0) or 0),
"onboarding_acceptance_ready_nodes": int(launchpad_status.get("onboarding_acceptance_ready_nodes", 0) or 0),
"recommended_execution_mode": str(launchpad_status.get("recommended_execution_mode") or ""),
"focus_ref": dict(launchpad_status.get("focus_ref") or {}),
"error": str(launchpad_payload.get("detail") or launchpad_payload.get("message") or ""),
},
"playbook_runs": {
"http_status": playbook_runs_status,
"available": playbook_runs_status.startswith("2"),
"recent_total": len(playbook_runs),
"problem_runs_total": len(problem_runs),
"problem_runs": problem_runs[:5],
"error": str(playbook_runs_root_payload.get("detail") or playbook_runs_root_payload.get("message") or ""),
},
"activity_stream": {
"http_status": activity_status,
"available": activity_status.startswith("2"),
"recent_total": len(activities),
"status_counts": activity_counts,
"top_items": [
{
"kind": str(item.get("kind") or "").strip(),
"status": str(item.get("status") or item.get("job_status") or "").strip(),
"summary": str(item.get("summary_text") or item.get("summary") or "").strip(),
"focus_ref": dict(item.get("focus_ref") or {}),
}
for item in activities[:5]
],
"error": str(activity_root_payload.get("detail") or activity_root_payload.get("message") or ""),
},
}
print(json.dumps(summary, ensure_ascii=False, indent=2))
PY