Add Redfish debug route and bulk clear action for sensor value overrides

- Exposes GET /redfish/tlbmc/Debug/SensorValueOverrides to view active sensor value overrides formatted and grouped by board config key.
- Exposes POST /redfish/tlbmc/Debug/SensorValueOverrides/Actions/SensorValueOverrides.ClearOverrides action to clear all active sensor value overrides across all sensors.
- Gates ClearOverrides action using IsSensorOverrideEnabled() to ensure safety on prod images.
- Registers authorization pattern in private_pattern_to_entity_array.h.

Google-Bug-Id:550410302
PiperOrigin-RevId: 979537721
Change-Id: I82583f58015a25d8bba224ec71ea3841d2d30e1b
diff --git a/tlbmc/redfish/routes/debug.cc b/tlbmc/redfish/routes/debug.cc
index 76cd5c6..6598cba 100644
--- a/tlbmc/redfish/routes/debug.cc
+++ b/tlbmc/redfish/routes/debug.cc
@@ -5,6 +5,7 @@
 #include <cstddef>
 #include <functional>
 #include <memory>
+#include <optional>
 #include <string>
 #include <string_view>
 #include <utility>
@@ -12,6 +13,7 @@
 
 #include "absl/container/flat_hash_map.h"
 #include "absl/container/flat_hash_set.h"
+#include "absl/flags/flag.h"
 #include "absl/functional/bind_front.h"
 #include "absl/log/log.h"
 #include "absl/strings/str_cat.h"
@@ -23,6 +25,7 @@
 #include "tlbmc/redfish/app.h"
 #include "tlbmc/redfish/request.h"
 #include "tlbmc/redfish/response.h"
+#include "tlbmc/redfish/routes/sensor.h"
 #include "tlbmc/sensors/sensor.h"
 #include "tlbmc/sensors/shared_mem_based_sensor.h"
 #include "tlbmc/store/store.h"
@@ -34,7 +37,8 @@
 namespace {
 
 constexpr auto kResources = std::to_array<std::string_view>(
-    {"App", "Config", "Scheduler", "Store", "HftService", "FruCollector"});
+    {"App", "Config", "Scheduler", "Store", "HftService", "FruCollector",
+     "SensorValueOverrides"});
 
 void HandleSharedMemorySensorsResource(const RedfishApp& app,
                                        const RedfishRequest& req,
@@ -225,6 +229,96 @@
   resp.SetKeyInJsonBody("/SmbiosFruTable", smbios_fru_table_json);
 }
 
+void HandleSensorValueOverridesResource(const RedfishApp& app,
+                                        const RedfishRequest& req,
+                                        RedfishResponse& resp) {
+  DLOG(INFO) << "HandleSensorValueOverridesResource";
+  resp.SetKeyInJsonBody("/@odata.id",
+                        "/redfish/tlbmc/Debug/SensorValueOverrides");
+  resp.SetKeyInJsonBody("/@odata.type",
+                        "#TlbmcDebugSensorValueOverrides.v1_0_0."
+                        "TlbmcDebugSensorValueOverrides");
+  resp.SetKeyInJsonBody("/Name", "SensorValueOverrides");
+  resp.SetKeyInJsonBody(
+      "/Description",
+      "Detailed view and control of active tlBMC sensor value overrides.");
+
+  nlohmann::json action_target;
+  action_target["target"] =
+      "/redfish/tlbmc/Debug/SensorValueOverrides/Actions/"
+      "SensorValueOverrides.ClearOverrides";
+  resp.SetKeyInJsonBody("/Actions/#SensorValueOverrides.ClearOverrides",
+                        std::move(action_target));
+
+  const Store* store = app.GetStore();
+  if (store == nullptr) {
+    resp.SetToInternalError("Store is null");
+    return;
+  }
+
+  std::vector<std::shared_ptr<const Sensor>> all_sensors =
+      store->GetAllSensors();
+
+  nlohmann::json::object_t overrides_by_board;
+  for (const auto& sensor : all_sensors) {
+    if (!sensor->IsOverridden()) {
+      continue;
+    }
+    std::string key = sensor->GetKey();
+    std::string board_key = sensor->GetBoardConfigKey();
+
+    std::optional<SensorValue> val_opt = sensor->GetOverrideValue();
+    if (!val_opt.has_value()) {
+      continue;
+    }
+
+    nlohmann::json info;
+    if (val_opt->has_reading()) {
+      info["Reading"] = val_opt->reading();
+    } else if (val_opt->has_count()) {
+      info["Reading"] = val_opt->count();
+    }
+    info["Status"] = "STATUS_READY";
+    overrides_by_board[board_key][key] = std::move(info);
+  }
+
+  resp.SetKeyInJsonBody("/SensorValueOverrides", overrides_by_board);
+}
+
+void HandleClearAllSensorOverridesAction(const RedfishApp& app,
+                                         const RedfishRequest& req,
+                                         RedfishResponse& resp) {
+  DLOG(INFO) << "HandleClearAllSensorOverridesAction";
+  if (!absl::GetFlag(FLAGS_enable_tlbmc_sensor_override)) {
+    resp.SetToForbidden(
+        "Sensor error injection and software overrides are disabled on "
+        "production images. Use a dev-signed image or enable "
+        "--enable_tlbmc_sensor_override.");
+    return;
+  }
+
+  Store* store = app.GetStore();
+  if (store == nullptr) {
+    resp.SetToInternalError("Store is null");
+    return;
+  }
+
+  absl::Status status = store->ClearAllSensorOverrides();
+  if (!status.ok()) {
+    resp.SetToAbslStatus(status);
+    return;
+  }
+
+  nlohmann::json success = {
+      {{"@odata.type", "#Message.v1_1_1.Message"},
+       {"Message", "All sensor overrides successfully cleared."},
+       {"MessageArgs", nlohmann::json::array_t()},
+       {"MessageId", "Base.1.13.0.Success"},
+       {"MessageSeverity", "OK"},
+       {"Resolution", "None"}}};
+  resp.SetKeyInJsonBody("/@Message.ExtendedInfo", success);
+}
+
 }  // namespace
 
 void RegisterRoutes(RedfishApp& app) {
@@ -259,6 +353,16 @@
   TLBMC_ROUTE(app, "/redfish/tlbmc/Debug/Store/SharedMemorySensors/")
       .methods(boost::beast::http::verb::get)(
           absl::bind_front(HandleSharedMemorySensorsResource, std::cref(app)));
+
+  TLBMC_ROUTE(app, "/redfish/tlbmc/Debug/SensorValueOverrides/")
+      .methods(boost::beast::http::verb::get)(
+          absl::bind_front(HandleSensorValueOverridesResource, std::cref(app)));
+
+  TLBMC_ROUTE(app,
+              "/redfish/tlbmc/Debug/SensorValueOverrides/Actions/"
+              "SensorValueOverrides.ClearOverrides/")
+      .methods(boost::beast::http::verb::post)(absl::bind_front(
+          HandleClearAllSensorOverridesAction, std::cref(app)));
 }
 
 }  // namespace milotic_tlbmc::debug