| #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/power_control_collector.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::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); |
| } |
| |
| 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))); |
| } |
| |
| } // namespace milotic_tlbmc::system |