fix(reboot): add windows99 vmx source locator
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 2m11s
CD Pipeline / post-deploy-checks (push) Has been cancelled
CD Pipeline / build-and-deploy (push) Has been cancelled
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 2m11s
CD Pipeline / post-deploy-checks (push) Has been cancelled
CD Pipeline / build-and-deploy (push) Has been cancelled
This commit is contained in:
@@ -483,6 +483,9 @@ from src.services.telegram_alert_monitoring_live_receipt_apply import (
|
||||
apply_latest_telegram_alert_monitoring_live_receipts,
|
||||
load_latest_telegram_alert_monitoring_live_receipt_apply_readback,
|
||||
)
|
||||
from src.services.windows99_vmx_source_locator_readback import (
|
||||
load_latest_windows99_vmx_source_locator_readback,
|
||||
)
|
||||
from src.services.windows99_vmx_source_repair_package import (
|
||||
load_latest_windows99_vmx_source_repair_package,
|
||||
mark_windows99_vmx_source_repair_public_redaction_boundary,
|
||||
@@ -1573,6 +1576,36 @@ async def get_windows99_vmx_source_repair_package() -> dict[str, Any]:
|
||||
) from exc
|
||||
|
||||
|
||||
@router.get(
|
||||
"/windows99-vmx-source-locator-readback",
|
||||
response_model=dict[str, Any],
|
||||
summary="取得 Windows99 VMX source locator check-mode readback",
|
||||
description=(
|
||||
"讀取 P0-006 Windows99 VMX source locator 的 no-secret check-mode "
|
||||
"readback。此端點不 SSH、不讀 VMX 內容、不回傳原始 VMX 路徑、不啟停 VM、"
|
||||
"不寫 Windows、不改 registry/task、不重啟主機、不讀 secret。"
|
||||
),
|
||||
)
|
||||
async def get_windows99_vmx_source_locator_readback() -> dict[str, Any]:
|
||||
"""回傳 Windows99 VMX source locator check-mode readback。"""
|
||||
try:
|
||||
payload = await asyncio.to_thread(
|
||||
load_latest_windows99_vmx_source_locator_readback
|
||||
)
|
||||
return redact_public_lan_topology(payload)
|
||||
except FileNotFoundError as exc:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=str(exc),
|
||||
) from exc
|
||||
except (json.JSONDecodeError, ValueError) as exc:
|
||||
logger.error("windows99_vmx_source_locator_readback_invalid", error=str(exc))
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Windows99 VMX source locator readback 無效",
|
||||
) from exc
|
||||
|
||||
|
||||
@router.get(
|
||||
"/reboot-auto-recovery-drill-preflight",
|
||||
response_model=dict[str, Any],
|
||||
|
||||
@@ -2984,6 +2984,9 @@ def _enrich_from_current_readbacks(
|
||||
apply_gitea_repo_bundle_backup_readback,
|
||||
load_latest_reboot_auto_recovery_slo_scorecard,
|
||||
)
|
||||
from src.services.windows99_vmx_source_locator_readback import (
|
||||
build_windows99_vmx_source_locator_readback,
|
||||
)
|
||||
from src.services.windows99_vmx_source_repair_package import (
|
||||
build_windows99_vmx_source_repair_package,
|
||||
)
|
||||
@@ -3029,6 +3032,12 @@ def _enrich_from_current_readbacks(
|
||||
windows99_vmx_source_repair_package = (
|
||||
build_windows99_vmx_source_repair_package(reboot_slo)
|
||||
)
|
||||
windows99_vmx_source_locator_readback = (
|
||||
build_windows99_vmx_source_locator_readback(
|
||||
reboot_slo,
|
||||
repair_package=windows99_vmx_source_repair_package,
|
||||
)
|
||||
)
|
||||
windows99_vmx_source_repair_action_chain = _dict(
|
||||
windows99_vmx_source_repair_package.get("action_chain")
|
||||
)
|
||||
@@ -3417,6 +3426,51 @@ def _enrich_from_current_readbacks(
|
||||
)
|
||||
or ""
|
||||
)
|
||||
state["windows99_vmx_source_locator_status"] = str(
|
||||
windows99_vmx_source_locator_readback.get("status") or "unknown"
|
||||
)
|
||||
state["windows99_vmx_source_locator_required"] = bool(
|
||||
windows99_vmx_source_locator_readback.get("source_locator_required") is True
|
||||
)
|
||||
state["windows99_vmx_source_locator_check_mode_ready"] = bool(
|
||||
windows99_vmx_source_locator_readback.get("locator_check_mode_ready") is True
|
||||
)
|
||||
state["windows99_vmx_source_locator_target_aliases"] = _strings(
|
||||
windows99_vmx_source_locator_readback.get("target_vm_aliases")
|
||||
)
|
||||
state["windows99_vmx_source_locator_next_executable_step"] = str(
|
||||
windows99_vmx_source_locator_readback.get("next_executable_step") or ""
|
||||
)
|
||||
state["windows99_vmx_source_locator_expected_vmx_path_count"] = _int(
|
||||
windows99_vmx_source_locator_readback.get("expected_vmx_path_count")
|
||||
)
|
||||
state["windows99_vmx_source_locator_candidate_source_count_known"] = bool(
|
||||
windows99_vmx_source_locator_readback.get("candidate_source_count_known")
|
||||
is True
|
||||
)
|
||||
state["windows99_vmx_source_locator_collector_readback_present"] = bool(
|
||||
windows99_vmx_source_locator_readback.get("collector_readback_present")
|
||||
is True
|
||||
)
|
||||
state["windows99_vmx_source_locator_collector_collection_status"] = str(
|
||||
windows99_vmx_source_locator_readback.get("collector_collection_status")
|
||||
or "unknown"
|
||||
)
|
||||
state["windows99_vmx_source_locator_raw_path_output"] = bool(
|
||||
windows99_vmx_source_locator_readback.get("raw_path_output") is True
|
||||
)
|
||||
state["windows99_vmx_source_locator_path_fingerprint_only"] = bool(
|
||||
windows99_vmx_source_locator_readback.get("path_fingerprint_only") is True
|
||||
)
|
||||
state["windows99_vmx_source_locator_file_content_read"] = bool(
|
||||
windows99_vmx_source_locator_readback.get("file_content_read") is True
|
||||
)
|
||||
state["windows99_vmx_source_locator_remote_write_performed"] = bool(
|
||||
windows99_vmx_source_locator_readback.get("remote_write_performed") is True
|
||||
)
|
||||
state["windows99_vmx_source_locator_vm_power_change_performed"] = bool(
|
||||
windows99_vmx_source_locator_readback.get("vm_power_change_performed") is True
|
||||
)
|
||||
state["stale_snapshot_or_old_cd_runs_must_not_reopen_closed_work"] = True
|
||||
state["p0_004_template_copy_apply_gate_production_http_status"] = 200
|
||||
state["p0_004_template_copy_apply_gate_runtime_readback_state"] = (
|
||||
@@ -3759,6 +3813,48 @@ def _enrich_from_current_readbacks(
|
||||
evidence["windows99_vmx_source_repair_playbook_trust_writeback_ref"] = (
|
||||
state["windows99_vmx_source_repair_playbook_trust_writeback_ref"]
|
||||
)
|
||||
evidence["windows99_vmx_source_locator_status"] = state[
|
||||
"windows99_vmx_source_locator_status"
|
||||
]
|
||||
evidence["windows99_vmx_source_locator_required"] = state[
|
||||
"windows99_vmx_source_locator_required"
|
||||
]
|
||||
evidence["windows99_vmx_source_locator_check_mode_ready"] = state[
|
||||
"windows99_vmx_source_locator_check_mode_ready"
|
||||
]
|
||||
evidence["windows99_vmx_source_locator_target_aliases"] = state[
|
||||
"windows99_vmx_source_locator_target_aliases"
|
||||
]
|
||||
evidence["windows99_vmx_source_locator_next_executable_step"] = state[
|
||||
"windows99_vmx_source_locator_next_executable_step"
|
||||
]
|
||||
evidence["windows99_vmx_source_locator_expected_vmx_path_count"] = state[
|
||||
"windows99_vmx_source_locator_expected_vmx_path_count"
|
||||
]
|
||||
evidence["windows99_vmx_source_locator_candidate_source_count_known"] = state[
|
||||
"windows99_vmx_source_locator_candidate_source_count_known"
|
||||
]
|
||||
evidence["windows99_vmx_source_locator_collector_readback_present"] = state[
|
||||
"windows99_vmx_source_locator_collector_readback_present"
|
||||
]
|
||||
evidence["windows99_vmx_source_locator_collector_collection_status"] = state[
|
||||
"windows99_vmx_source_locator_collector_collection_status"
|
||||
]
|
||||
evidence["windows99_vmx_source_locator_raw_path_output"] = state[
|
||||
"windows99_vmx_source_locator_raw_path_output"
|
||||
]
|
||||
evidence["windows99_vmx_source_locator_path_fingerprint_only"] = state[
|
||||
"windows99_vmx_source_locator_path_fingerprint_only"
|
||||
]
|
||||
evidence["windows99_vmx_source_locator_file_content_read"] = state[
|
||||
"windows99_vmx_source_locator_file_content_read"
|
||||
]
|
||||
evidence["windows99_vmx_source_locator_remote_write_performed"] = state[
|
||||
"windows99_vmx_source_locator_remote_write_performed"
|
||||
]
|
||||
evidence["windows99_vmx_source_locator_vm_power_change_performed"] = state[
|
||||
"windows99_vmx_source_locator_vm_power_change_performed"
|
||||
]
|
||||
evidence["drill_preflight_status"] = str(reboot_preflight.get("status") or "")
|
||||
evidence["drill_preflight_ready"] = (
|
||||
reboot_preflight_rollups.get("preflight_ready") is True
|
||||
@@ -4479,6 +4575,52 @@ def _set_rollups_and_summary(
|
||||
state.get("windows99_vmx_source_repair_playbook_trust_writeback_ref")
|
||||
or ""
|
||||
),
|
||||
"windows99_vmx_source_locator_status": str(
|
||||
state.get("windows99_vmx_source_locator_status") or "unknown"
|
||||
),
|
||||
"windows99_vmx_source_locator_required": (
|
||||
state.get("windows99_vmx_source_locator_required") is True
|
||||
),
|
||||
"windows99_vmx_source_locator_check_mode_ready": (
|
||||
state.get("windows99_vmx_source_locator_check_mode_ready") is True
|
||||
),
|
||||
"windows99_vmx_source_locator_target_aliases": _strings(
|
||||
state.get("windows99_vmx_source_locator_target_aliases")
|
||||
),
|
||||
"windows99_vmx_source_locator_next_executable_step": str(
|
||||
state.get("windows99_vmx_source_locator_next_executable_step") or ""
|
||||
),
|
||||
"windows99_vmx_source_locator_expected_vmx_path_count": _int(
|
||||
state.get("windows99_vmx_source_locator_expected_vmx_path_count")
|
||||
),
|
||||
"windows99_vmx_source_locator_candidate_source_count_known": (
|
||||
state.get("windows99_vmx_source_locator_candidate_source_count_known")
|
||||
is True
|
||||
),
|
||||
"windows99_vmx_source_locator_collector_readback_present": (
|
||||
state.get("windows99_vmx_source_locator_collector_readback_present")
|
||||
is True
|
||||
),
|
||||
"windows99_vmx_source_locator_collector_collection_status": str(
|
||||
state.get("windows99_vmx_source_locator_collector_collection_status")
|
||||
or "unknown"
|
||||
),
|
||||
"windows99_vmx_source_locator_raw_path_output": (
|
||||
state.get("windows99_vmx_source_locator_raw_path_output") is True
|
||||
),
|
||||
"windows99_vmx_source_locator_path_fingerprint_only": (
|
||||
state.get("windows99_vmx_source_locator_path_fingerprint_only") is True
|
||||
),
|
||||
"windows99_vmx_source_locator_file_content_read": (
|
||||
state.get("windows99_vmx_source_locator_file_content_read") is True
|
||||
),
|
||||
"windows99_vmx_source_locator_remote_write_performed": (
|
||||
state.get("windows99_vmx_source_locator_remote_write_performed") is True
|
||||
),
|
||||
"windows99_vmx_source_locator_vm_power_change_performed": (
|
||||
state.get("windows99_vmx_source_locator_vm_power_change_performed")
|
||||
is True
|
||||
),
|
||||
"p0_004_runtime_readback_ready": p0_004_ready,
|
||||
"reboot_drill_preflight_runtime_readback_ready": (
|
||||
state.get("reboot_drill_preflight_runtime_readback_state") == "ready"
|
||||
@@ -4725,6 +4867,52 @@ def _set_rollups_and_summary(
|
||||
state.get("windows99_vmx_source_repair_playbook_trust_writeback_ref")
|
||||
or ""
|
||||
),
|
||||
"windows99_vmx_source_locator_status": str(
|
||||
state.get("windows99_vmx_source_locator_status") or "unknown"
|
||||
),
|
||||
"windows99_vmx_source_locator_required": (
|
||||
state.get("windows99_vmx_source_locator_required") is True
|
||||
),
|
||||
"windows99_vmx_source_locator_check_mode_ready": (
|
||||
state.get("windows99_vmx_source_locator_check_mode_ready") is True
|
||||
),
|
||||
"windows99_vmx_source_locator_target_aliases": _strings(
|
||||
state.get("windows99_vmx_source_locator_target_aliases")
|
||||
),
|
||||
"windows99_vmx_source_locator_next_executable_step": str(
|
||||
state.get("windows99_vmx_source_locator_next_executable_step") or ""
|
||||
),
|
||||
"windows99_vmx_source_locator_expected_vmx_path_count": _int(
|
||||
state.get("windows99_vmx_source_locator_expected_vmx_path_count")
|
||||
),
|
||||
"windows99_vmx_source_locator_candidate_source_count_known": (
|
||||
state.get("windows99_vmx_source_locator_candidate_source_count_known")
|
||||
is True
|
||||
),
|
||||
"windows99_vmx_source_locator_collector_readback_present": (
|
||||
state.get("windows99_vmx_source_locator_collector_readback_present")
|
||||
is True
|
||||
),
|
||||
"windows99_vmx_source_locator_collector_collection_status": str(
|
||||
state.get("windows99_vmx_source_locator_collector_collection_status")
|
||||
or "unknown"
|
||||
),
|
||||
"windows99_vmx_source_locator_raw_path_output": (
|
||||
state.get("windows99_vmx_source_locator_raw_path_output") is True
|
||||
),
|
||||
"windows99_vmx_source_locator_path_fingerprint_only": (
|
||||
state.get("windows99_vmx_source_locator_path_fingerprint_only") is True
|
||||
),
|
||||
"windows99_vmx_source_locator_file_content_read": (
|
||||
state.get("windows99_vmx_source_locator_file_content_read") is True
|
||||
),
|
||||
"windows99_vmx_source_locator_remote_write_performed": (
|
||||
state.get("windows99_vmx_source_locator_remote_write_performed") is True
|
||||
),
|
||||
"windows99_vmx_source_locator_vm_power_change_performed": (
|
||||
state.get("windows99_vmx_source_locator_vm_power_change_performed")
|
||||
is True
|
||||
),
|
||||
"windows99_winrm_http_open": (
|
||||
state.get("windows99_winrm_http_open") is True
|
||||
),
|
||||
|
||||
178
apps/api/src/services/windows99_vmx_source_locator_readback.py
Normal file
178
apps/api/src/services/windows99_vmx_source_locator_readback.py
Normal file
@@ -0,0 +1,178 @@
|
||||
"""Windows99 VMX source locator readback.
|
||||
|
||||
Builds the API-facing readback for the no-secret Windows99 VMX source locator.
|
||||
The service only projects the current repair package into an executable
|
||||
check-mode contract; it does not SSH, read VMX contents, write Windows state,
|
||||
start VMs, stop VMs, restart services, or reboot hosts.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from src.services.reboot_auto_recovery_slo_scorecard import (
|
||||
_taipei_now_iso,
|
||||
load_latest_reboot_auto_recovery_slo_scorecard,
|
||||
)
|
||||
from src.services.windows99_vmx_source_repair_package import (
|
||||
build_windows99_vmx_source_repair_package,
|
||||
)
|
||||
|
||||
_SCHEMA_VERSION = "windows99_vmx_source_locator_readback_v1"
|
||||
_COLLECTOR_SCHEMA_VERSION = "windows99_vmx_source_locator_collector_v1"
|
||||
_LOCATOR_SCHEMA_VERSION = "windows99_vmx_source_locator_v1"
|
||||
_COLLECTOR_SCRIPT_PATH = "scripts/reboot-recovery/locate-windows99-vmx-source.sh"
|
||||
_LOCATOR_SCRIPT_PATH = "scripts/reboot-recovery/windows99-vmx-source-locator.ps1"
|
||||
|
||||
|
||||
def _dict(value: Any) -> dict[str, Any]:
|
||||
return value if isinstance(value, dict) else {}
|
||||
|
||||
|
||||
def _strings(value: Any) -> list[str]:
|
||||
if isinstance(value, list):
|
||||
return [str(item).strip() for item in value if str(item).strip()]
|
||||
if isinstance(value, str):
|
||||
return [part.strip() for part in value.split(",") if part.strip()]
|
||||
return []
|
||||
|
||||
|
||||
def _bool(value: Any) -> bool:
|
||||
return value is True
|
||||
|
||||
|
||||
def _vm_rows_for_aliases(
|
||||
repair_package: dict[str, Any],
|
||||
target_aliases: list[str],
|
||||
) -> list[dict[str, Any]]:
|
||||
targets = set(target_aliases)
|
||||
rows: list[dict[str, Any]] = []
|
||||
for row in repair_package.get("vm_rows") or []:
|
||||
if not isinstance(row, dict):
|
||||
continue
|
||||
alias = str(row.get("alias") or "")
|
||||
if alias in targets:
|
||||
rows.append(row)
|
||||
return rows
|
||||
|
||||
|
||||
def build_windows99_vmx_source_locator_readback(
|
||||
scorecard: dict[str, Any],
|
||||
*,
|
||||
repair_package: dict[str, Any] | None = None,
|
||||
generated_at: str | None = None,
|
||||
) -> dict[str, Any]:
|
||||
package = repair_package or build_windows99_vmx_source_repair_package(scorecard)
|
||||
missing_aliases = _strings(package.get("missing_vmx_aliases"))
|
||||
target_aliases = missing_aliases or _strings(package.get("powered_off_aliases"))
|
||||
target_rows = _vm_rows_for_aliases(package, missing_aliases)
|
||||
expected_vmx_path_count = sum(
|
||||
len(row.get("expected_vmx_candidates") or []) for row in target_rows
|
||||
)
|
||||
source_locator_required = bool(missing_aliases)
|
||||
locator_check_mode_ready = bool(
|
||||
source_locator_required
|
||||
and package.get("package_ready") is True
|
||||
and package.get("check_mode_allowed_by_this_package") is True
|
||||
)
|
||||
if locator_check_mode_ready:
|
||||
status = "check_mode_locator_ready_windows99_vmx_source_required"
|
||||
next_step = "run_no_secret_windows99_vmx_source_locator_collect"
|
||||
safe_next_step = (
|
||||
"bash scripts/reboot-recovery/locate-windows99-vmx-source.sh "
|
||||
"--collect"
|
||||
)
|
||||
elif source_locator_required:
|
||||
status = "blocked_locator_package_not_ready"
|
||||
next_step = "repair_package_first"
|
||||
safe_next_step = "rebuild_windows99_vmx_source_repair_package"
|
||||
else:
|
||||
status = "ready_no_windows99_vmx_source_locator_required"
|
||||
next_step = "rerun_no_secret_vmware_verify_collector"
|
||||
safe_next_step = (
|
||||
"bash scripts/reboot-recovery/collect-windows99-vmware-verify.sh "
|
||||
"--collect"
|
||||
)
|
||||
|
||||
return {
|
||||
"schema_version": _SCHEMA_VERSION,
|
||||
"generated_at": generated_at
|
||||
or str(scorecard.get("generated_at") or _taipei_now_iso()),
|
||||
"status": status,
|
||||
"target_host_alias": "99",
|
||||
"target_vm_aliases": target_aliases,
|
||||
"missing_vmx_aliases": missing_aliases,
|
||||
"source_locator_required": source_locator_required,
|
||||
"locator_check_mode_ready": locator_check_mode_ready,
|
||||
"next_executable_step": next_step,
|
||||
"safe_next_step": safe_next_step,
|
||||
"collector_script_path": _COLLECTOR_SCRIPT_PATH,
|
||||
"locator_script_path": _LOCATOR_SCRIPT_PATH,
|
||||
"collector_schema_version": _COLLECTOR_SCHEMA_VERSION,
|
||||
"locator_schema_version": _LOCATOR_SCHEMA_VERSION,
|
||||
"collector_readback_present": False,
|
||||
"collector_collection_status": "not_collected_by_api_readback",
|
||||
"remote_locator_mode": "in_memory_stdin_scriptblock",
|
||||
"expected_vmx_path_count": expected_vmx_path_count,
|
||||
"expected_vmx_path_present_count": None,
|
||||
"candidate_source_count": None,
|
||||
"candidate_source_count_known": False,
|
||||
"candidate_source_fingerprint_count": None,
|
||||
"public_lan_topology_redacted": True,
|
||||
"redacted_public_display_only": True,
|
||||
"raw_path_output": False,
|
||||
"path_fingerprint_only": True,
|
||||
"file_content_read": False,
|
||||
"secret_value_read": False,
|
||||
"password_prompt_allowed": False,
|
||||
"remote_write_performed": False,
|
||||
"host_reboot_performed": False,
|
||||
"vm_power_change_performed": False,
|
||||
"windows_registry_apply_performed": False,
|
||||
"scheduled_task_write_performed": False,
|
||||
"apply_allowed_by_this_readback": False,
|
||||
"vm_power_change_allowed": False,
|
||||
"destructive_restore_allowed": False,
|
||||
"operation_boundaries": {
|
||||
"secret_value_read": False,
|
||||
"password_prompt_allowed": False,
|
||||
"file_content_read": False,
|
||||
"raw_path_output": False,
|
||||
"remote_write_performed": False,
|
||||
"host_reboot_performed": False,
|
||||
"vm_power_change_performed": False,
|
||||
"windows_registry_apply_performed": False,
|
||||
"scheduled_task_write_performed": False,
|
||||
},
|
||||
"check_mode": {
|
||||
"allowed": locator_check_mode_ready,
|
||||
"path_existence_only": True,
|
||||
"vmx_file_content_read": False,
|
||||
"raw_path_output": False,
|
||||
"path_fingerprint_only": True,
|
||||
"collector_command": safe_next_step if locator_check_mode_ready else "",
|
||||
},
|
||||
"target_selector": {
|
||||
"host_alias": "99",
|
||||
"vm_aliases": target_aliases,
|
||||
"missing_vmx_aliases": missing_aliases,
|
||||
"source_scope": "expected_vmx_paths_and_alias_named_vmx_candidates",
|
||||
"secret_collection_allowed": False,
|
||||
"vm_power_change_allowed": False,
|
||||
"destructive_restore_allowed": False,
|
||||
},
|
||||
"post_locator_verifier": [
|
||||
"bash scripts/reboot-recovery/collect-windows99-vmware-verify.sh --collect",
|
||||
"GET /api/v1/agents/reboot-auto-recovery-slo-scorecard",
|
||||
"GET /api/v1/agents/windows99-vmx-source-repair-package",
|
||||
"GET /api/v1/agents/windows99-vmx-source-locator-readback",
|
||||
],
|
||||
"source_repair_package_ready": _bool(package.get("package_ready")),
|
||||
"source_repair_work_order_id": str(package.get("work_order_id") or ""),
|
||||
"source_repair_action_stage": str(package.get("action_stage") or ""),
|
||||
}
|
||||
|
||||
|
||||
def load_latest_windows99_vmx_source_locator_readback() -> dict[str, Any]:
|
||||
scorecard = load_latest_reboot_auto_recovery_slo_scorecard()
|
||||
return build_windows99_vmx_source_locator_readback(scorecard)
|
||||
@@ -737,6 +737,21 @@ def test_awoooi_priority_work_order_readback_routes_closed_backup_to_host_boot_a
|
||||
assert state[
|
||||
"windows99_vmx_source_repair_playbook_trust_writeback_ref"
|
||||
].startswith("playbook://awoooi/windows99-vmx-source-repair/")
|
||||
assert state["windows99_vmx_source_locator_status"] == (
|
||||
"check_mode_locator_ready_windows99_vmx_source_required"
|
||||
)
|
||||
assert state["windows99_vmx_source_locator_required"] is True
|
||||
assert state["windows99_vmx_source_locator_check_mode_ready"] is True
|
||||
assert state["windows99_vmx_source_locator_target_aliases"] == ["111"]
|
||||
assert state["windows99_vmx_source_locator_next_executable_step"] == (
|
||||
"run_no_secret_windows99_vmx_source_locator_collect"
|
||||
)
|
||||
assert state["windows99_vmx_source_locator_expected_vmx_path_count"] == 1
|
||||
assert state["windows99_vmx_source_locator_candidate_source_count_known"] is False
|
||||
assert state["windows99_vmx_source_locator_collector_readback_present"] is False
|
||||
assert state["windows99_vmx_source_locator_file_content_read"] is False
|
||||
assert state["windows99_vmx_source_locator_remote_write_performed"] is False
|
||||
assert state["windows99_vmx_source_locator_vm_power_change_performed"] is False
|
||||
assert payload["summary"]["controlled_service_data_backup_blocker_count"] == 0
|
||||
assert payload["summary"]["active_p0_primary_blocker"] == (
|
||||
"reboot_event_required_host_unreachable"
|
||||
@@ -814,6 +829,21 @@ def test_awoooi_priority_work_order_readback_routes_closed_backup_to_host_boot_a
|
||||
assert payload["rollups"]["windows99_vmx_source_repair_target_vm_aliases"] == [
|
||||
"111"
|
||||
]
|
||||
assert payload["summary"]["windows99_vmx_source_locator_status"] == (
|
||||
"check_mode_locator_ready_windows99_vmx_source_required"
|
||||
)
|
||||
assert payload["summary"]["windows99_vmx_source_locator_check_mode_ready"] is True
|
||||
assert payload["summary"]["windows99_vmx_source_locator_target_aliases"] == [
|
||||
"111"
|
||||
]
|
||||
assert (
|
||||
payload["rollups"]["windows99_vmx_source_locator_remote_write_performed"]
|
||||
is False
|
||||
)
|
||||
assert (
|
||||
payload["rollups"]["windows99_vmx_source_locator_file_content_read"]
|
||||
is False
|
||||
)
|
||||
assert "service/data/backup readback is green" in payload[
|
||||
"next_execution_order"
|
||||
][0]
|
||||
@@ -863,6 +893,21 @@ def test_awoooi_priority_work_order_readback_routes_closed_backup_to_host_boot_a
|
||||
assert in_progress["evidence"][
|
||||
"windows99_vmx_source_repair_playbook_trust_writeback_ref"
|
||||
].startswith("playbook://awoooi/windows99-vmx-source-repair/")
|
||||
assert in_progress["evidence"]["windows99_vmx_source_locator_status"] == (
|
||||
"check_mode_locator_ready_windows99_vmx_source_required"
|
||||
)
|
||||
assert (
|
||||
in_progress["evidence"]["windows99_vmx_source_locator_check_mode_ready"]
|
||||
is True
|
||||
)
|
||||
assert (
|
||||
in_progress["evidence"]["windows99_vmx_source_locator_target_aliases"]
|
||||
== ["111"]
|
||||
)
|
||||
assert (
|
||||
in_progress["evidence"]["windows99_vmx_source_locator_remote_write_performed"]
|
||||
is False
|
||||
)
|
||||
assert "Windows99 verify collection" in in_progress["professional_fix"][
|
||||
"action"
|
||||
]
|
||||
|
||||
110
apps/api/tests/test_windows99_vmx_source_locator_readback_api.py
Normal file
110
apps/api/tests/test_windows99_vmx_source_locator_readback_api.py
Normal file
@@ -0,0 +1,110 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
from fastapi import FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from src.api.v1 import agents
|
||||
from src.api.v1.agents import router
|
||||
from src.services.windows99_vmx_source_locator_readback import (
|
||||
build_windows99_vmx_source_locator_readback,
|
||||
)
|
||||
from src.services.windows99_vmx_source_repair_package import (
|
||||
build_windows99_vmx_source_repair_package,
|
||||
)
|
||||
|
||||
|
||||
def _scorecard() -> dict:
|
||||
return {
|
||||
"status": "blocked_reboot_auto_recovery_slo_not_ready",
|
||||
"generated_at": "2026-07-03T11:20:00+08:00",
|
||||
"windows99_vmware_autostart": {
|
||||
"required_vm_aliases": ["111", "188", "120", "121", "112"],
|
||||
"missing_vmx_aliases": ["111"],
|
||||
"powered_off_aliases": ["111", "112", "120", "121", "188"],
|
||||
},
|
||||
"readback": {
|
||||
"windows99_no_secret_collector_status": (
|
||||
"collected_windows99_vmware_verify_stdout"
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def test_windows99_vmx_source_locator_builder_is_no_secret_check_mode() -> None:
|
||||
repair_package = build_windows99_vmx_source_repair_package(
|
||||
_scorecard(),
|
||||
generated_at="2026-07-03T11:21:00+08:00",
|
||||
)
|
||||
|
||||
payload = build_windows99_vmx_source_locator_readback(
|
||||
_scorecard(),
|
||||
repair_package=repair_package,
|
||||
generated_at="2026-07-03T11:22:00+08:00",
|
||||
)
|
||||
|
||||
assert payload["schema_version"] == "windows99_vmx_source_locator_readback_v1"
|
||||
assert payload["status"] == (
|
||||
"check_mode_locator_ready_windows99_vmx_source_required"
|
||||
)
|
||||
assert payload["target_host_alias"] == "99"
|
||||
assert payload["target_vm_aliases"] == ["111"]
|
||||
assert payload["source_locator_required"] is True
|
||||
assert payload["locator_check_mode_ready"] is True
|
||||
assert payload["next_executable_step"] == (
|
||||
"run_no_secret_windows99_vmx_source_locator_collect"
|
||||
)
|
||||
assert payload["expected_vmx_path_count"] == 1
|
||||
assert payload["candidate_source_count_known"] is False
|
||||
assert payload["collector_readback_present"] is False
|
||||
assert payload["collector_script_path"] == (
|
||||
"scripts/reboot-recovery/locate-windows99-vmx-source.sh"
|
||||
)
|
||||
assert payload["locator_script_path"] == (
|
||||
"scripts/reboot-recovery/windows99-vmx-source-locator.ps1"
|
||||
)
|
||||
assert payload["check_mode"]["allowed"] is True
|
||||
assert payload["check_mode"]["vmx_file_content_read"] is False
|
||||
assert payload["raw_path_output"] is False
|
||||
assert payload["path_fingerprint_only"] is True
|
||||
assert payload["file_content_read"] is False
|
||||
assert payload["secret_value_read"] is False
|
||||
assert payload["password_prompt_allowed"] is False
|
||||
assert payload["remote_write_performed"] is False
|
||||
assert payload["host_reboot_performed"] is False
|
||||
assert payload["vm_power_change_performed"] is False
|
||||
assert payload["apply_allowed_by_this_readback"] is False
|
||||
assert payload["destructive_restore_allowed"] is False
|
||||
|
||||
|
||||
def test_windows99_vmx_source_locator_endpoint_returns_public_safe_readback(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
monkeypatch.setattr(
|
||||
agents,
|
||||
"load_latest_windows99_vmx_source_locator_readback",
|
||||
lambda: build_windows99_vmx_source_locator_readback(
|
||||
_scorecard(),
|
||||
generated_at="2026-07-03T11:22:00+08:00",
|
||||
),
|
||||
)
|
||||
app = FastAPI()
|
||||
app.include_router(router, prefix="/api/v1")
|
||||
client = TestClient(app)
|
||||
|
||||
response = client.get("/api/v1/agents/windows99-vmx-source-locator-readback")
|
||||
|
||||
assert response.status_code == 200
|
||||
payload = response.json()
|
||||
assert payload["schema_version"] == "windows99_vmx_source_locator_readback_v1"
|
||||
assert payload["status"] == (
|
||||
"check_mode_locator_ready_windows99_vmx_source_required"
|
||||
)
|
||||
assert payload["target_vm_aliases"] == ["111"]
|
||||
assert payload["locator_check_mode_ready"] is True
|
||||
assert payload["public_lan_topology_redacted"] is True
|
||||
assert payload["raw_path_output"] is False
|
||||
assert payload["file_content_read"] is False
|
||||
assert payload["remote_write_performed"] is False
|
||||
assert payload["vm_power_change_performed"] is False
|
||||
assert "192.168.0." not in response.text
|
||||
Reference in New Issue
Block a user