log_services: Add download of post code log entries
- Add a GET method /redfish/v1/Systems/system/LogServices/PostCodes
/Entries/<str>/attachment/, Get the attribute value through the
getPostCodes method and encode it as base64, and send it off.
- This allows the use to offload error logs for analysis and further
parsing if needed. An http header of "Accept:
application/octet-stream" or the default "*/*" is expected.
Tested:
- Ran Redfish validator.
- pldmtool raw --data 0x80 0x3F 0xC 0x0A 0x00 0x00 0x00 0x00 0x00 0x07 0x00 0x00 0x00 0x48 0x00 0x00 0x00 0x02 0x00 0x00 0x01 0x00 0x00 0x00 0x48 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x53 0x54 0x41 0x4e 0x44 0x42 0x59 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20
$curl -k https://127.0.0.1:2443/redfish/v1/Systems/system/LogServices/PostCodes/Entries/B1-1/attachment/
output:
AgAAAQAAAEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFNUQU5EQlkgICAgICAgICAgICAgICAgICAgICAgICAg
Signed-off-by: George Liu <liuxiwei@inspur.com>
Change-Id: I74dd6c1dc2d3dfb7908f7741e0d9e7825c1df816
diff --git a/redfish-core/lib/log_services.hpp b/redfish-core/lib/log_services.hpp
index 8a68534..2b06414 100644
--- a/redfish-core/lib/log_services.hpp
+++ b/redfish-core/lib/log_services.hpp
@@ -15,6 +15,7 @@
*/
#pragma once
+#include "http_utility.hpp"
#include "registries.hpp"
#include "registries/base_message_registry.hpp"
#include "registries/openbmc_message_registry.hpp"
@@ -32,6 +33,7 @@
#include <boost/system/linux_error.hpp>
#include <error_messages.hpp>
+#include <charconv>
#include <filesystem>
#include <optional>
#include <string_view>
@@ -1691,24 +1693,7 @@
const std::string& param)
{
- std::string_view acceptHeader = req.getHeaderValue("Accept");
- // The iterators in boost/http/rfc7230.hpp end the string if '/'
- // is found, so replace it with arbitrary character '|' which is
- // not part of the Accept header syntax.
- std::string acceptStr = boost::replace_all_copy(
- std::string(acceptHeader), "/", "|");
- boost::beast::http::ext_list acceptTypes{acceptStr};
- bool supported = false;
- for (const auto& type : acceptTypes)
- {
- if ((type.first == "*|*") ||
- (type.first == "application|octet-stream"))
- {
- supported = true;
- break;
- }
- }
- if (!supported)
+ if (!http_helpers::isOctetAccepted(req))
{
asyncResp->res.result(
boost::beast::http::status::bad_request);
@@ -3032,6 +3017,117 @@
"xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount");
}
+inline static bool parsePostCode(std::string postCodeID, uint64_t& currentValue,
+ uint16_t& index)
+{
+ // postCodeID = B1-1
+ std::vector<std::string> split;
+ boost::algorithm::split(split, postCodeID, boost::is_any_of("-"));
+ if (split.size() != 2 || split[0].length() < 2)
+ {
+ return false;
+ }
+
+ const char* start = split[0].data() + 1;
+ const char* end = split[0].data() + split[0].size();
+ auto [ptrIndex, ecIndex] = std::from_chars(start, end, index);
+
+ if (ptrIndex != end || ecIndex != std::errc() || !index)
+ {
+ return false;
+ }
+
+ start = split[1].data();
+ end = split[1].data() + split[1].size();
+ auto [ptrValue, ecValue] = std::from_chars(start, end, currentValue);
+ if (ptrValue != end || ecValue != std::errc() || !currentValue)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+inline void requestRoutesPostCodesEntryAdditionalData(App& app)
+{
+ BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/PostCodes/"
+ "Entries/<str>/attachment/")
+ .privileges({"Login"})
+ .methods(boost::beast::http::verb::get)(
+ [](const crow::Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::string& postCodeID) {
+ if (!http_helpers::isOctetAccepted(req))
+ {
+ asyncResp->res.result(
+ boost::beast::http::status::bad_request);
+ return;
+ }
+
+ uint64_t currentValue = 0;
+ uint16_t index = 0;
+ if (!parsePostCode(postCodeID, currentValue, index))
+ {
+ messages::resourceNotFound(asyncResp->res, "LogEntry",
+ postCodeID);
+ return;
+ }
+
+ crow::connections::systemBus->async_method_call(
+ [asyncResp, postCodeID, currentValue](
+ const boost::system::error_code ec,
+ const std::vector<std::tuple<
+ uint64_t, std::vector<uint8_t>>>& postcodes) {
+ if (ec.value() == EBADR)
+ {
+ messages::resourceNotFound(asyncResp->res,
+ "LogEntry", postCodeID);
+ return;
+ }
+ if (ec)
+ {
+ BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
+ messages::internalError(asyncResp->res);
+ return;
+ }
+
+ if (currentValue < 1 ||
+ postcodes.size() <= currentValue)
+ {
+ BMCWEB_LOG_ERROR << "Wrong currentValue value";
+ messages::resourceNotFound(asyncResp->res,
+ "LogEntry", postCodeID);
+ return;
+ }
+
+ size_t value = static_cast<size_t>(currentValue) - 1;
+ auto& [tID, code] = postcodes[value];
+
+ if (code.size() == 0)
+ {
+ BMCWEB_LOG_INFO << "No found post code data";
+ messages::internalError(asyncResp->res);
+ return;
+ }
+
+ std::string_view strData(
+ reinterpret_cast<const char*>(code.data()),
+ code.size());
+
+ asyncResp->res.addHeader("Content-Type",
+ "application/octet-stream");
+ asyncResp->res.addHeader("Content-Transfer-Encoding",
+ "Base64");
+ asyncResp->res.body() =
+ std::move(crow::utility::base64encode(strData));
+ },
+ "xyz.openbmc_project.State.Boot.PostCode0",
+ "/xyz/openbmc_project/State/Boot/PostCode0",
+ "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodes",
+ index);
+ });
+}
+
inline void requestRoutesPostCodesEntryCollection(App& app)
{
BMCWEB_ROUTE(app,