From 2fd90ad98a5103afa6c47c1031019d6a8b096b99 Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 3 Jul 2026 10:16:52 +0800 Subject: [PATCH] fix(reboot): sync service backup readback overlay --- .../reboot_auto_recovery_slo_scorecard.py | 46 +++++++++++++++++++ ..._reboot_auto_recovery_slo_scorecard_api.py | 18 ++++++++ 2 files changed, 64 insertions(+) diff --git a/apps/api/src/services/reboot_auto_recovery_slo_scorecard.py b/apps/api/src/services/reboot_auto_recovery_slo_scorecard.py index 5a2b16e7e..280366719 100644 --- a/apps/api/src/services/reboot_auto_recovery_slo_scorecard.py +++ b/apps/api/src/services/reboot_auto_recovery_slo_scorecard.py @@ -991,6 +991,7 @@ def _apply_prometheus_service_backup_boolean_readback( rollups["controlled_service_data_backup_can_clear_blockers"] = not blocking_fields rollups["controlled_service_data_backup_readback_status"] = service_backup["status"] rollups["runtime_metric_service_backup_boolean_overlay_applied"] = True + _sync_controlled_service_data_backup_readback_fields(payload) def _apply_prometheus_metric_active_blockers( @@ -1306,6 +1307,51 @@ def apply_stockplatform_runtime_readback( rollups["controlled_service_data_backup_blocker_count"] = blocker_count rollups["controlled_service_data_backup_can_clear_blockers"] = can_clear rollups["controlled_service_data_backup_readback_status"] = service_backup["status"] + _sync_controlled_service_data_backup_readback_fields(payload) + + +def _sync_controlled_service_data_backup_readback_fields( + payload: dict[str, Any], +) -> None: + service_backup = _dict( + payload.setdefault("controlled_service_data_backup_readback", {}) + ) + blocking_fields = _strings(service_backup.get("blocking_fields")) + blocker_count = _int( + service_backup.get("controlled_service_data_backup_blocker_count") + ) + if blocker_count == 0 and blocking_fields: + blocker_count = len(blocking_fields) + readback_present = service_backup.get("readback_present") is True + can_clear = readback_present and blocker_count == 0 + status = ( + "ready_service_data_backup_green" + if can_clear + else "blocked_service_data_backup_readback_not_green" + ) + next_safe_action = ( + "keep_timer_live_wait_for_fresh_all_host_reboot_event_or_drill_preflight" + if can_clear + else "collect_public_service_data_backup_readback_and_refresh_reboot_slo_scorecard_no_reboot_no_db_write" + ) + + service_backup["status"] = status + service_backup["can_clear_service_data_backup_blockers"] = can_clear + service_backup["controlled_service_data_backup_blocker_count"] = blocker_count + service_backup["next_safe_action"] = next_safe_action + + readback = _dict(payload.setdefault("readback", {})) + readback["controlled_service_data_backup_readback_present"] = readback_present + readback["controlled_service_data_backup_readback_status"] = status + readback["controlled_service_data_backup_blocker_count"] = blocker_count + readback["controlled_service_data_backup_next_safe_action"] = next_safe_action + + rollups = _dict(payload.setdefault("rollups", {})) + rollups["controlled_service_data_backup_readback_present"] = readback_present + rollups["controlled_service_data_backup_readback_status"] = status + rollups["controlled_service_data_backup_blocker_count"] = blocker_count + rollups["controlled_service_data_backup_can_clear_blockers"] = can_clear + rollups["controlled_service_data_backup_next_safe_action"] = next_safe_action def _append_live_stockplatform_blockers( diff --git a/apps/api/tests/test_reboot_auto_recovery_slo_scorecard_api.py b/apps/api/tests/test_reboot_auto_recovery_slo_scorecard_api.py index 080b1f9b2..9142bde7c 100644 --- a/apps/api/tests/test_reboot_auto_recovery_slo_scorecard_api.py +++ b/apps/api/tests/test_reboot_auto_recovery_slo_scorecard_api.py @@ -493,6 +493,16 @@ def test_reboot_auto_recovery_slo_scorecard_overlays_prometheus_runtime_metrics( payload["rollups"]["controlled_service_data_backup_can_clear_blockers"] is True ) + assert payload["rollups"]["controlled_service_data_backup_readback_status"] == ( + "ready_service_data_backup_green" + ) + assert payload["readback"]["controlled_service_data_backup_blocker_count"] == 0 + assert payload["readback"]["controlled_service_data_backup_readback_status"] == ( + "ready_service_data_backup_green" + ) + assert payload["readback"]["controlled_service_data_backup_next_safe_action"] == ( + "keep_timer_live_wait_for_fresh_all_host_reboot_event_or_drill_preflight" + ) action_by_blocker = { item["blocker"]: item for item in payload["active_blocker_action_matrix"]["items"] @@ -626,6 +636,14 @@ def test_reboot_auto_recovery_slo_scorecard_keeps_service_backup_ready_after_liv payload["rollups"]["controlled_service_data_backup_readback_status"] == "ready_service_data_backup_green" ) + assert payload["readback"]["controlled_service_data_backup_blocker_count"] == 0 + assert ( + payload["readback"]["controlled_service_data_backup_readback_status"] + == "ready_service_data_backup_green" + ) + assert payload["readback"]["controlled_service_data_backup_next_safe_action"] == ( + "keep_timer_live_wait_for_fresh_all_host_reboot_event_or_drill_preflight" + ) def test_reboot_auto_recovery_slo_scorecard_keeps_prometheus_source_missing_when_source_control_missing(