Files
OpenBMC/meta-luxshare/recipes-phosphor/interfaces/bmcweb/0001-Add-Managers-SEL-log-service.patch
T
2026-04-23 17:07:55 +08:00

576 lines
23 KiB
Diff
Executable File

From 76889ebb1cdc9d5cc2e29c065d1ab0c906b88f47 Mon Sep 17 00:00:00 2001
From: roly <Rolyli.Li@luxshare-ict.com>
Date: Mon, 9 Dec 2024 14:30:51 +0800
Subject: [PATCH] Add Managers SEL log service
---
include/luxshare/sel.hpp | 434 ++++++++++++++++++++++++++++++
include/luxshare/utils.hpp | 38 +++
meson.build | 1 +
meson_options.txt | 8 +
redfish-core/lib/log_services.hpp | 6 +
src/webserver_main.cpp | 6 +
6 files changed, 493 insertions(+)
create mode 100755 include/luxshare/sel.hpp
create mode 100755 include/luxshare/utils.hpp
diff --git a/include/luxshare/sel.hpp b/include/luxshare/sel.hpp
new file mode 100755
index 00000000..cbaa1cd5
--- /dev/null
+++ b/include/luxshare/sel.hpp
@@ -0,0 +1,434 @@
+#pragma once
+#include <app.hpp>
+#include <async_resp.hpp>
+#include <luxshare/utils.hpp>
+#include <error_messages.hpp>
+#include <registries/privilege_registry.hpp>
+
+namespace crow
+{
+namespace luxshare_sel_api
+{
+
+enum class SELStatus
+{
+ DEASSERTED,
+ ASSERTED,
+ UNKNOWN
+};
+
+
+
+inline std::string translateSeverityDbusToRedfish(const std::string& s)
+{
+ if ((s == "xyz.openbmc_project.Logging.Entry.Level.Critical") ||
+ (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency") ||
+ (s == "xyz.openbmc_project.Logging.Entry.Level.Error"))
+ {
+ return "Critical";
+ }
+ if ((s == "xyz.openbmc_project.Logging.Entry.Level.Debug") ||
+ (s == "xyz.openbmc_project.Logging.Entry.Level.Informational") ||
+ (s == "xyz.openbmc_project.Logging.Entry.Level.Notice"))
+ {
+ return "OK";
+ }
+ if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning")
+ {
+ return "Warning";
+ }
+ if (s == "xyz.openbmc_project.Logging.Entry.Level.Alert")
+ {
+ return "NonRecoverable";
+ }
+ return "";
+}
+
+inline void requestRoutesSelClear(App& app)
+{
+ BMCWEB_ROUTE(
+ app,
+ "/redfish/v1/Managers/bmc/LogServices/SEL/Actions/LogService.ClearLog/")
+ .privileges(redfish::privileges::privilegeSetConfigureComponents)
+ .methods(boost::beast::http::verb::post)(
+ [](const crow::Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
+ if (!redfish::luxshare::checkPostRequestBodyEmpty(req))
+ {
+ redfish::messages::actionParameterNotSupported(
+ asyncResp->res, req.body(), "ClearLog");
+ return;
+ }
+ using IpmiDbusRspType = std::tuple<uint8_t, uint8_t, uint8_t,
+ uint8_t, std::vector<uint8_t>>;
+ crow::connections::systemBus->async_method_call(
+ [asyncResp](const boost::system::error_code &ec, IpmiDbusRspType response1) {
+ // Handle the response of the first method call
+ if (ec) {
+ BMCWEB_LOG_ERROR("Error calling execute method: {}", ec.message());
+ redfish::messages::internalError(asyncResp->res);
+ return;
+ }
+
+ const uint8_t& cc1 = std::get<3>(response1);
+ if (cc1 != 0) {
+ BMCWEB_LOG_ERROR("Failed to reserve sel: cc: {}", cc1);
+ redfish::messages::internalError(asyncResp->res);
+ return;
+ }
+
+ // Extract reservation ID from the response
+ std::vector<uint8_t> reservationId = std::get<4>(response1);
+ std::vector<uint8_t> commandData = {0x43, 0x4C, 0x52, 0xAA};
+ commandData.insert(commandData.begin(), reservationId.begin(), reservationId.end());
+
+ // Execute "Clear SEL" IPMI command
+ crow::connections::systemBus->async_method_call(
+ [asyncResp](const boost::system::error_code &ec1,IpmiDbusRspType response2) {
+ // Handle the response of the clear SEL command
+ if (ec1) {
+ BMCWEB_LOG_ERROR("Error calling execute method: {}", ec1.message());
+ redfish::messages::internalError(asyncResp->res);
+ return;
+ }
+
+ const uint8_t& cc2 = std::get<3>(response2);
+ if (cc2 != 0) {
+ BMCWEB_LOG_ERROR("Failed to clear sel: cc: {}", cc2);
+ redfish::messages::internalError(asyncResp->res);
+ return;
+ }
+
+ // Success logic for clearing SEL can go here
+ BMCWEB_LOG_INFO("Successfully cleared SEL.");
+ },
+ "xyz.openbmc_project.Ipmi.Host",
+ "/xyz/openbmc_project/Ipmi",
+ "xyz.openbmc_project.Ipmi.Server", "execute",
+ uint8_t{0x0a}, uint8_t{0x00}, uint8_t{0x47}, // Command to clear SEL
+ commandData,
+ std::map<std::string, std::variant<uint8_t>>{}
+ );
+ },
+ "xyz.openbmc_project.Ipmi.Host",
+ "/xyz/openbmc_project/Ipmi",
+ "xyz.openbmc_project.Ipmi.Server", "execute",
+ uint8_t{0x0a}, uint8_t{0x00}, uint8_t{0x42}, // Command to reserve SEL
+ std::vector<uint8_t>{},
+ std::map<std::string, std::variant<uint8_t>>{});
+ });
+}
+
+inline void requestRoutesSelEntries(App& app)
+{
+ BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/SEL/Entries/")
+ .privileges(redfish::privileges::getLogService)
+ .methods(boost::beast::http::verb::get)(
+ [&app](const crow::Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
+ if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+ {
+ return;
+ }
+ // Get sensor info from xyz.openbmc_project.Ipmi.Host
+ auto respHandler =
+ [asyncResp](
+ const boost::system::error_code& ec,
+ const std::unordered_map<std::string, std::vector<uint8_t>>&
+ sensorInfo) {
+ if (ec)
+ {
+ BMCWEB_LOG_ERROR("Error getting SensorInfo property: {}",
+ ec.message());
+ redfish::messages::internalError(asyncResp->res);
+ return;
+ }
+ // Get all sel log from xyz.openbmc_project.Logging service.
+ sdbusplus::message::object_path path(
+ "/xyz/openbmc_project/logging");
+ dbus::utility::getManagedObjects(
+ "xyz.openbmc_project.Logging", path,
+ [asyncResp,
+ sensorInfo](const boost::system::error_code& errorCode,
+ const dbus::utility::ManagedObjectType& resp) {
+ if (errorCode)
+ {
+ BMCWEB_LOG_ERROR("Logging GetManagedObjects error: {}",
+ errorCode.message());
+ redfish::messages::internalError(asyncResp->res);
+ return;
+ }
+
+ nlohmann::json& entriesArray =
+ asyncResp->res.jsonValue["Members"];
+ entriesArray = nlohmann::json::array();
+ std::map<uint32_t, uint64_t> idToTimestamp;
+ for (const auto& objectPath : resp)
+ {
+ const uint32_t* id = nullptr;
+ const uint64_t* timestamp = nullptr;
+ const std::string* severity = nullptr;
+ const std::string* message = nullptr;
+ const std::vector<std::string>* additionalData = nullptr;
+ nlohmann::json sensorName;
+ nlohmann::json sensorValue;
+ nlohmann::json sensorNumber;
+ nlohmann::json sensorType;
+ nlohmann::json eventType;
+ std::string sensorPath;
+ SELStatus selStatus = SELStatus::UNKNOWN;
+
+ for (const auto& interfaceMap : objectPath.second)
+ {
+ if (interfaceMap.first ==
+ "xyz.openbmc_project.Logging.Entry")
+ {
+ for (const auto& propertyMap : interfaceMap.second)
+ {
+ if (propertyMap.first == "Id")
+ {
+ id = std::get_if<uint32_t>(
+ &propertyMap.second);
+ }
+ else if (propertyMap.first == "Timestamp")
+ {
+ timestamp = std::get_if<uint64_t>(
+ &propertyMap.second);
+ }
+ else if (propertyMap.first == "Severity")
+ {
+ severity = std::get_if<std::string>(
+ &propertyMap.second);
+ }
+ else if (propertyMap.first == "Message")
+ {
+ message = std::get_if<std::string>(
+ &propertyMap.second);
+ if (*message ==
+ "xyz.openbmc_project.Common.Error."
+ "InternalFailure")
+ {
+ // Skip this entry
+ break;
+ }
+ }
+ else if (propertyMap.first == "AdditionalData")
+ {
+ additionalData =
+ std::get_if<std::vector<std::string>>(
+ &propertyMap.second);
+ }
+ }
+ }
+ }
+
+ if (id == nullptr || message == nullptr ||
+ timestamp == nullptr || severity == nullptr ||
+ additionalData == nullptr)
+ {
+ continue;
+ }
+
+ idToTimestamp[*id] = *timestamp;
+
+ entriesArray.push_back({});
+ nlohmann::json& thisEntry = entriesArray.back();
+ thisEntry["@odata.id"] = boost::urls::format(
+ "/redfish/v1/Managers/bmc/LogServices/SEL/Entries/{}",
+ std::to_string(*id));
+ thisEntry["Id"] = *id;
+ thisEntry["Message"] = *message;
+
+ // Get the Created time from the timestamp.
+ thisEntry["Created"] =
+ redfish::time_utils::getDateTimeUintMs(*timestamp);
+ thisEntry["Severity"] =
+ translateSeverityDbusToRedfish(*severity);
+
+ for (const auto& data : *additionalData)
+ {
+ if (data.starts_with("SENSOR_PATH="))
+ {
+ sensorPath =
+ data.substr(data.find_first_of('=') + 1);
+ if (!sensorPath.empty())
+ {
+ // Get the sensor name from the last level of
+ // the path.
+ sensorName =
+ data.substr(data.find_last_of('/') + 1);
+ }
+ }
+ else if (data.starts_with("EVENT_DIR="))
+ {
+ selStatus = static_cast<SELStatus>(std::stoi(
+ data.substr(data.find_first_of('=') + 1)));
+ }
+ else if (data.starts_with("READING="))
+ {
+ sensorValue =
+ data.substr(data.find_first_of('=') + 1);
+ }
+ else if (data.starts_with("SENSOR_DATA="))
+ {
+ // The second character is the status of the
+ // discrete sensor. Convert to string format
+ // starting with 0x.
+ auto sensorValueStr =
+ data.substr(data.find_first_of('=') + 2, 1);
+
+ try
+ {
+ std::stringstream output;
+ output << "0x" << std::hex << std::setw(2)
+ << std::setfill('0')
+ << static_cast<uint16_t>(std::stoi(
+ sensorValueStr, nullptr, 16));
+
+ sensorValue = output.str();
+ }
+ catch (const std::exception&)
+ {
+ sensorValue = nlohmann::json::value_t::null;
+ }
+ }
+ }
+
+ if (selStatus == SELStatus::UNKNOWN)
+ {
+ // Match SEL status in message property.
+ std::string messageStr = *message;
+ if (messageStr.find("deassert") != std::string::npos)
+ {
+ selStatus = SELStatus::DEASSERTED;
+ }
+ else
+ {
+ selStatus = SELStatus::ASSERTED;
+ }
+ }
+
+ if (sensorValue == nlohmann::json::value_t::null)
+ {
+ // Get SEL Reading in message properties.
+ std::string messageErased(*message);
+ std::string::size_type posString = 0;
+ posString = messageErased.find("Reading ");
+ if (posString == std::string::npos)
+ {
+ posString = messageErased.find("Reading=");
+ }
+
+ if (posString != std::string::npos)
+ {
+ // Offset the length of 'Reading ' or 'Reading=' to
+ // obtain the substring before the space.
+ messageErased = messageErased.substr(posString + 8);
+ auto posSpace = messageErased.find_last_of(' ');
+ if (posSpace != std::string::npos)
+ {
+ sensorValue = messageErased.substr(0, posSpace);
+ }
+ }
+ }
+
+ auto sensorEntryInfo = sensorInfo.find(sensorPath);
+ if (sensorEntryInfo != sensorInfo.end())
+ {
+ std::stringstream output;
+ // Sensor number subscript in the vector is 6.
+ output << "0x" << std::hex << std::setw(2)
+ << std::setfill('0')
+ << static_cast<uint16_t>(
+ sensorEntryInfo->second[6]);
+ sensorNumber = output.str();
+ // Sensor type subscript in the vector is 0.
+ output.str("");
+ output << "0x" << std::hex << std::setw(2)
+ << std::setfill('0')
+ << static_cast<uint16_t>(
+ sensorEntryInfo->second[0]);
+ sensorType = output.str();
+ // Event type subscript in the vector is 1.
+ output.str("");
+ output << "0x" << std::hex << std::setw(2)
+ << std::setfill('0')
+ << static_cast<uint16_t>(
+ sensorEntryInfo->second[1]);
+ eventType = output.str();
+ }
+
+ thisEntry["SensorType"] = sensorType;
+ thisEntry["SensorName"] = sensorName;
+ thisEntry["SensorNumber"] = sensorNumber;
+ thisEntry["SensorValue"] = sensorValue;
+ thisEntry["EventType"] = eventType;
+ if (selStatus == SELStatus::DEASSERTED)
+ {
+ thisEntry["Status"] = "Deasserted";
+ }
+ else
+ {
+ thisEntry["Status"] = "Asserted";
+ }
+ }
+
+ std::sort(entriesArray.begin(), entriesArray.end(),
+ [&idToTimestamp](const nlohmann::json& left,
+ const nlohmann::json& right) {
+ return (idToTimestamp[left["Id"].get<uint32_t>()] >=
+ idToTimestamp[right["Id"].get<uint32_t>()]);
+ });
+ asyncResp->res.jsonValue["Members@odata.count"] =
+ entriesArray.size();
+ });
+ };
+
+ sdbusplus::asio::getProperty<
+ std::unordered_map<std::string, std::vector<uint8_t>>>(
+ *crow::connections::systemBus, "xyz.openbmc_project.Ipmi.Host",
+ "/xyz/openbmc_project/Ipmi/SensorInfo",
+ "xyz.openbmc_project.IPMI.SensorInfo", "SensorInfo",
+ std::move(respHandler));
+ });
+}
+
+inline void requestRoutesSel(App& app)
+{
+ BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/SEL/")
+ .privileges(redfish::privileges::getLogService)
+ .methods(boost::beast::http::verb::get)(
+ [&app](const crow::Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
+ if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+ {
+ return;
+ }
+
+ asyncResp->res.jsonValue["@odata.type"] =
+ "#LogService.v1_1_0.LogService";
+ asyncResp->res.jsonValue["@odata.id"] =
+ "/redfish/v1/Managers/bmc/LogServices/SEL";
+ asyncResp->res.jsonValue["Name"] = "Event Log Service";
+ asyncResp->res.jsonValue["Description"] = "System Event Log Service";
+ asyncResp->res.jsonValue["Id"] = "SEL";
+ std::pair<std::string, std::string> redfishDateTimeOffset =
+ redfish::time_utils::getDateTimeOffsetNow();
+ asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
+ asyncResp->res.jsonValue["DateTimeLocalOffset"] =
+ redfishDateTimeOffset.second;
+
+ asyncResp->res.jsonValue["Entries"]["@odata.id"] =
+ "/redfish/v1/Managers/bmc/LogServices/SEL/Entries";
+ });
+}
+
+inline void requestRoutes(App& app)
+{
+ requestRoutesSelClear(app);
+ requestRoutesSelEntries(app);
+ requestRoutesSel(app);
+}
+
+} // namespace luxshare_sel_api
+} // namespace crow
diff --git a/include/luxshare/utils.hpp b/include/luxshare/utils.hpp
new file mode 100755
index 00000000..d1d7b97a
--- /dev/null
+++ b/include/luxshare/utils.hpp
@@ -0,0 +1,38 @@
+#pragma once
+#include <utils/json_utils.hpp>
+#include <utils/sw_utils.hpp>
+
+
+namespace redfish
+{
+namespace luxshare
+{
+
+inline bool checkPostRequestBodyEmpty(const crow::Request& req)
+{
+ if (req.body().empty())
+ {
+ return true;
+ }
+
+ nlohmann::json jsonRequest = nlohmann::json::parse(req.body(), nullptr,
+ false);
+ if (jsonRequest.is_discarded())
+ {
+ BMCWEB_LOG_DEBUG("Failed to parse json in request");
+ return false;
+ }
+
+ nlohmann::json::object_t* object =
+ jsonRequest.get_ptr<nlohmann::json::object_t*>();
+ if (object == nullptr || (!object->empty()))
+ {
+ BMCWEB_LOG_DEBUG("Json value is not object or empty");
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace luxshare
+} // namespace redfish
diff --git a/meson.build b/meson.build
index fcc0d7c4..3afb44e1 100644
--- a/meson.build
+++ b/meson.build
@@ -94,6 +94,7 @@ feature_map = {
'experimental-redfish-multi-computer-system' : '-DBMCWEB_ENABLE_MULTI_COMPUTERSYSTEM',
'vm-websocket' : '-DBMCWEB_ENABLE_VM_WEBSOCKET',
'xtoken-auth' : '-DBMCWEB_ENABLE_XTOKEN_AUTHENTICATION',
+ 'luxshare-api' : '-DBMCWEB_ENABLE_LUXSHARE_API',
#'vm-nbdproxy' : '-DBMCWEB_ENABLE_VM_NBDPROXY',
}
diff --git a/meson_options.txt b/meson_options.txt
index 017c16bd..562ac55b 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -200,6 +200,14 @@ option(
/google/v1/'''
)
+# Luxshare redfish option
+option(
+ 'luxshare-api',
+ type: 'feature',
+ value : 'enabled',
+ description: 'Enable Luxshare readfish feature'
+)
+
option(
'http-body-limit',
type: 'integer',
diff --git a/redfish-core/lib/log_services.hpp b/redfish-core/lib/log_services.hpp
index 9cb1fe05..51fe2313 100644
--- a/redfish-core/lib/log_services.hpp
+++ b/redfish-core/lib/log_services.hpp
@@ -2365,6 +2365,12 @@ inline void handleBMCLogServicesCollectionGet(
nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
logServiceArray = nlohmann::json::array();
+#ifdef BMCWEB_ENABLE_LUXSHARE_API
+ nlohmann::json::object_t eventLog;
+ eventLog["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices/SEL";
+ logServiceArray.emplace_back(std::move(eventLog));
+#endif
+
#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
nlohmann::json::object_t journal;
journal["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices/Journal";
diff --git a/src/webserver_main.cpp b/src/webserver_main.cpp
index 67e2aaef..0008ad52 100644
--- a/src/webserver_main.cpp
+++ b/src/webserver_main.cpp
@@ -28,6 +28,8 @@
#include <sdbusplus/bus.hpp>
#include <sdbusplus/server.hpp>
+#include <luxshare/sel.hpp>
+
#include <exception>
#include <memory>
#include <string>
@@ -116,6 +118,10 @@ static int run()
crow::google_api::requestRoutes(app);
#endif
+#ifdef BMCWEB_ENABLE_LUXSHARE_API
+ crow::luxshare_sel_api::requestRoutes(app);
+#endif
+
if (bmcwebInsecureDisableXssPrevention != 0)
{
cors_preflight::requestRoutes(app);
--
2.25.1