blob: 5cc12db044c16cf961907f763557b7a4ea2883a7 [file]
#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