| #include "tlbmc/redfish/routes/system.h" |
| |
| #include <cstdint> |
| #include <functional> |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <utility> |
| |
| #include "absl/functional/bind_front.h" |
| #include "absl/log/log.h" |
| #include "absl/status/status.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/str_format.h" |
| #include "absl/strings/string_view.h" |
| #include "absl/time/time.h" |
| #include "boost/beast/http/verb.hpp" // NOLINT: boost is commonly used in BMC codebase |
| #include "http_request.hpp" |
| #include "async_resp.hpp" |
| #include <nlohmann/json.hpp> |
| #include "json_utils.h" |
| #include "tlbmc/collector/led_collector.h" |
| #include "tlbmc/collector/power_control_collector.h" |
| #include "led_config.pb.h" |
| #include "power_control.pb.h" |
| #include "tlbmc/host_state/power_control.h" |
| #include "tlbmc/redfish/app.h" |
| #include "tlbmc/redfish/request.h" |
| #include "tlbmc/redfish/response.h" |
| #include "tlbmc/resource/reset_type.h" |
| #include "resource.pb.h" |
| #include "software_metrics.pb.h" |
| #include "tlbmc/store/store.h" |
| #include "smart_router.h" |
| |
| namespace milotic_tlbmc::system { |
| |
| using ::milotic::authz::GetValueAsBool; |
| using ::milotic::authz::GetValueAsString; |
| using ::milotic::authz::GetValueAsUintFromStringOrInteger; |
| |
| void HandleSystemReset(const RedfishApp& app, const RedfishRequest& req, |
| RedfishResponse& resp, const std::string& system_id) { |
| LOG(INFO) << "HandleSystemReset: " << system_id; |
| absl::string_view req_body = req.Body(); |
| nlohmann::json req_json = nlohmann::json::parse(req_body, nullptr, false); |
| if (req_json.is_discarded()) { |
| resp.SetToInternalError("Failed to parse request body"); |
| return; |
| } |
| const std::string* reset_type_str = GetValueAsString(req_json, "ResetType"); |
| if (reset_type_str == nullptr) { |
| resp.SetToInternalError("Invalid ResetType in request body"); |
| return; |
| } |
| ResetType reset_type = GetResetType(*reset_type_str); |
| if (reset_type == RESET_TYPE_UNSPECIFIED) { |
| resp.SetToInternalError( |
| absl::StrCat("Unsupported ResetType: ", *reset_type_str)); |
| return; |
| } |
| |
| // TODO(b/456665397): Implement Delay. |
| std::optional<uint64_t> delay = |
| GetValueAsUintFromStringOrInteger(req_json, "Delay"); |
| if (!delay.has_value()) { |
| // Delay is optional. |
| delay = 0; |
| } |
| absl::Status status = app.GetStore()->SendSystemResetRequest( |
| system_id, reset_type, absl::Seconds(*delay)); |
| if (!status.ok()) { |
| resp.SetToAbslStatus(status); |
| return; |
| } |
| resp.SetToAccepted(absl::StrCat("/redfish/v1/Systems/", system_id, |
| "/Actions/ComputerSystem.Reset/")); |
| resp.SetKeyInJsonBody("/@Message.ExtendedInfo", |
| nlohmann::json::array_t({nlohmann::json::object_t{ |
| {"@odata.type", "#Message.v1_1_1.Message"}, |
| {"Message", "The request completed successfully."}, |
| {"MessageArgs", nlohmann::json::array_t()}, |
| {"MessageId", "Base.1.13.0.Success"}, |
| {"MessageSeverity", "OK"}, |
| {"Resolution", "None"}, |
| }})); |
| } |
| |
| void HandleSystemResetActionInfo(const RedfishApp& app, |
| const RedfishRequest& req, |
| RedfishResponse& resp, |
| const std::string& system_id) { |
| resp.SetKeyInJsonBody( |
| "/@odata.id", |
| absl::StrCat("/redfish/v1/Systems/", system_id, "/ResetActionInfo/")); |
| resp.SetKeyInJsonBody("/@odata.type", "#ActionInfo.v1_1_2.ActionInfo"); |
| resp.SetKeyInJsonBody("/Id", "ResetActionInfo"); |
| resp.SetKeyInJsonBody("/Name", "Reset Action Info"); |
| nlohmann::json::array_t allowable_values = { |
| "ForceOff", "ForceOn", "ForceRestart", "GracefulRestart", |
| "GracefulShutdown", "On", "PowerCycle"}; |
| nlohmann::json::array_t parameters = { |
| nlohmann::json::object_t{ |
| {"Name", "ResetType"}, |
| {"DataType", "String"}, |
| {"Required", true}, |
| {"AllowableValues", std::move(allowable_values)}}, |
| nlohmann::json::object_t{ |
| {"Name", "Delay"}, {"DataType", "Number"}, {"Required", false}}}; |
| resp.SetKeyInJsonBody("/Parameters", parameters); |
| } |
| |
| static void InjectBiosKeyMetrics(const Store& store, nlohmann::json& json) { |
| SoftwareMetricsValue metrics = store.GetMetricValues(); |
| if (!metrics.has_bios_key_status()) { |
| return; |
| } |
| const auto& key_status = metrics.bios_key_status(); |
| json["Oem"]["Google"]["Security"]["BiosKeyMetrics"] = { |
| {"ImageFamily", key_status.image_family()}, |
| {"ValidationMethod", key_status.validation_method()}, |
| {"Version", key_status.version()}, |
| {"ValidationKeyData", |
| absl::StrFormat("0x%016x", key_status.validation_key_data())}}; |
| } |
| |
| void HandleSystemGetAfterGbmcweb( |
| const RedfishApp& app, const ::crow::Request& request, |
| const std::shared_ptr<bmcweb::AsyncResp>& async_resp, |
| const std::string& system_id) { |
| nlohmann::json& json = async_resp->res.jsonValue; |
| const Store* store = app.GetStore(); |
| PowerControl* power_control = nullptr; |
| if (auto* collector = store->GetPowerControlCollector(); |
| collector != nullptr) { |
| power_control = collector->GetPowerControl(system_id); |
| } |
| if (power_control != nullptr) { |
| HostState host_state = power_control->GetHostState(); |
| |
| if (host_state.os_state() == OSState::OS_STATE_STANDBY) { |
| json["PowerState"] = "On"; |
| json["Status"]["State"] = "Enabled"; |
| } else if (host_state.os_state() == OSState::OS_STATE_INACTIVE) { |
| json["PowerState"] = "Off"; |
| json["Status"]["State"] = "Disabled"; |
| } |
| |
| json["LastResetTime"] = absl::FormatTime( |
| absl::RFC3339_sec, power_control->GetLastStateChangeTime(), |
| absl::UTCTimeZone()); |
| } else { |
| LOG(WARNING) << "PowerControl not found for system: " << system_id; |
| } |
| |
| InjectBiosKeyMetrics(*store, json); |
| } |
| |
| // Appends indicator LED properties (IndicatorLED, LocationIndicatorActive) |
| // to the GET response of a System resource if an indicator LED is configured. |
| void HandleSystemIndicatorGetAfterGbmcweb( |
| const RedfishApp& app, const ::crow::Request& /*request*/, |
| const std::shared_ptr<bmcweb::AsyncResp>& async_resp, |
| const std::string& system_id) { |
| const Store* store = app.GetStore(); |
| LedCollector* led_collector = store->GetLedCollector(); |
| if (led_collector == nullptr) { |
| return; |
| } |
| if (!led_collector->HasIndicatorConfig(system_id)) { |
| return; |
| } |
| if (absl::StatusOr<LedState> state = |
| led_collector->GetIndicatorState(system_id); |
| state.ok()) { |
| nlohmann::json& json = async_resp->res.jsonValue; |
| switch (*state) { |
| case LED_STATE_ON: |
| json["IndicatorLED"] = "Lit"; |
| json["LocationIndicatorActive"] = true; |
| break; |
| case LED_STATE_BLINK: |
| json["IndicatorLED"] = "Blinking"; |
| json["LocationIndicatorActive"] = true; |
| break; |
| case LED_STATE_OFF: |
| json["IndicatorLED"] = "Off"; |
| json["LocationIndicatorActive"] = false; |
| break; |
| case LED_STATE_UNSPECIFIED: |
| default: |
| json["IndicatorLED"] = "Unknown"; |
| break; |
| } |
| } |
| } |
| |
| // Handles append-mode PATCH requests to update the indicator LED state on a |
| // System resource. |
| void HandleSystemIndicatorPatchAfterGbmcweb( |
| const RedfishApp& app, const ::crow::Request& request, |
| const std::shared_ptr<bmcweb::AsyncResp>& async_resp, |
| const std::string& system_id) { |
| const Store* store = app.GetStore(); |
| LedCollector* led_collector = store->GetLedCollector(); |
| if (led_collector == nullptr) { |
| return; |
| } |
| if (!led_collector->HasIndicatorConfig(system_id)) { |
| return; |
| } |
| |
| nlohmann::json req_json = |
| nlohmann::json::parse(request.body(), nullptr, false); |
| if (req_json.is_discarded()) { |
| return; |
| } |
| |
| const std::string* indicator_led = GetValueAsString(req_json, "IndicatorLED"); |
| const bool* location_indicator_active = |
| GetValueAsBool(req_json, "LocationIndicatorActive"); |
| |
| if (indicator_led == nullptr && location_indicator_active == nullptr) { |
| return; |
| } |
| |
| absl::Status status = absl::OkStatus(); |
| if (indicator_led != nullptr) { |
| if (*indicator_led == "Lit") { |
| status = led_collector->SetIndicatorState(system_id, LED_STATE_ON); |
| } else if (*indicator_led == "Blinking") { |
| status = led_collector->SetIndicatorState(system_id, LED_STATE_BLINK); |
| } else if (*indicator_led == "Off") { |
| status = led_collector->SetIndicatorState(system_id, LED_STATE_OFF); |
| } else { |
| async_resp->res.result(boost::beast::http::status::bad_request); |
| return; |
| } |
| } else if (location_indicator_active != nullptr) { |
| if (*location_indicator_active) { |
| status = led_collector->SetIndicatorState(system_id, LED_STATE_BLINK); |
| } else { |
| status = led_collector->SetIndicatorState(system_id, LED_STATE_OFF); |
| } |
| } |
| |
| if (!status.ok()) { |
| LOG(ERROR) << "Failed to set indicator LED: " << status; |
| async_resp->res.result(boost::beast::http::status::internal_server_error); |
| return; |
| } |
| |
| if (async_resp->res.result() == boost::beast::http::status::ok) { |
| async_resp->res.result(boost::beast::http::status::no_content); |
| } |
| } |
| |
| void RegisterResetRoutes(RedfishApp& app) { |
| TLBMC_ROUTE(app, "/redfish/v1/Systems/<str>/Actions/ComputerSystem.Reset/") |
| .methods(boost::beast::http::verb::post)( |
| absl::bind_front(HandleSystemReset, std::cref(app))); |
| TLBMC_ROUTE(app, "/redfish/v1/Systems/<str>/ResetActionInfo/") |
| .methods(boost::beast::http::verb::get)( |
| absl::bind_front(HandleSystemResetActionInfo, std::cref(app))); |
| } |
| |
| void RegisterAppendModeRoutes(RedfishApp& app) { |
| // Deal with platforms that have no x86-power-control. |
| TLBMC_APPEND_GBMCWEB( |
| app.GetSmartRouter(), "/redfish/v1/Systems/<str>/", |
| boost::beast::http::verb::get, |
| absl::bind_front(HandleSystemGetAfterGbmcweb, std::cref(app))); |
| } |
| |
| void RegisterIndicatorRoutes(RedfishApp& app) { |
| TLBMC_APPEND_GBMCWEB( |
| app.GetSmartRouter(), "/redfish/v1/Systems/<str>/", |
| boost::beast::http::verb::get, |
| absl::bind_front(HandleSystemIndicatorGetAfterGbmcweb, std::cref(app))); |
| TLBMC_APPEND_GBMCWEB( |
| app.GetSmartRouter(), "/redfish/v1/Systems/<str>/", |
| boost::beast::http::verb::patch, |
| absl::bind_front(HandleSystemIndicatorPatchAfterGbmcweb, std::cref(app))); |
| } |
| |
| } // namespace milotic_tlbmc::system |