summaryrefslogtreecommitdiff
path: root/redfish-core/lib
diff options
context:
space:
mode:
authorJason M. Bills <jason.m.bills@linux.intel.com>2018-07-18 22:12:00 +0300
committerEd Tanous <ed.tanous@intel.com>2018-10-19 23:22:52 +0300
commite1f26343d52c4ea8b4c491f4323744d72b34e6ba (patch)
tree2ec4068b827229429b264f41aedd31b426c8e0a0 /redfish-core/lib
parent0a63b1c7e5dafbfae592031c89fcda61c4504a29 (diff)
downloadbmcweb-e1f26343d52c4ea8b4c491f4323744d72b34e6ba.tar.xz
Add a BMC LogService
Adds a redfish LogService that provides the contents of the BMC journald instance available over readfish. This should be considered a prototype at this time, and suggestions on how this could be done better are welcome. At some point in the future, we could make this available over the redfish TaskService via SSE. Change-Id: Ibca0d473dcef545763256cc1df5574571a3af414 Signed-off-by: Ed Tanous <ed.tanous@intel.com> Signed-off-by: Jason M. Bills <jason.m.bills@linux.intel.com>
Diffstat (limited to 'redfish-core/lib')
-rw-r--r--redfish-core/lib/log_services.hpp730
1 files changed, 519 insertions, 211 deletions
diff --git a/redfish-core/lib/log_services.hpp b/redfish-core/lib/log_services.hpp
index 37a3a5454f..ee342cfbb9 100644
--- a/redfish-core/lib/log_services.hpp
+++ b/redfish-core/lib/log_services.hpp
@@ -17,7 +17,10 @@
#include "node.hpp"
+#include <systemd/sd-journal.h>
+
#include <boost/container/flat_map.hpp>
+#include <boost/utility/string_view.hpp>
#include <experimental/filesystem>
namespace redfish
@@ -29,7 +32,7 @@ constexpr char const *cpuLogImmediatePath = "/com/intel/CpuDebugLog/Immediate";
constexpr char const *cpuLogInterface = "com.intel.CpuDebugLog";
constexpr char const *cpuLogImmediateInterface =
"com.intel.CpuDebugLog.Immediate";
-constexpr char const *cpuLogRawPeciInterface =
+constexpr char const *cpuLogRawPECIInterface =
"com.intel.CpuDebugLog.SendRawPeci";
namespace fs = std::experimental::filesystem;
@@ -45,12 +48,12 @@ class LogServiceCollection : public Node
// load dynamic data so the duplicate static members don't get displayed
Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices";
entityPrivileges = {
- {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
- {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
- {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
- {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
- {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
- {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
+ {boost::beast::http::verb::get, {{"Login"}}},
+ {boost::beast::http::verb::head, {{"Login"}}},
+ {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
+ {boost::beast::http::verb::put, {{"ConfigureManager"}}},
+ {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
+ {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
}
private:
@@ -60,44 +63,346 @@ class LogServiceCollection : public Node
void doGet(crow::Response &res, const crow::Request &req,
const std::vector<std::string> &params) override
{
+ std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
// Collections don't include the static data added by SubRoute because
// it has a duplicate entry for members
- res.jsonValue["@odata.type"] =
+ asyncResp->res.jsonValue["@odata.type"] =
"#LogServiceCollection.LogServiceCollection";
- res.jsonValue["@odata.context"] =
+ asyncResp->res.jsonValue["@odata.context"] =
"/redfish/v1/"
"$metadata#LogServiceCollection.LogServiceCollection";
- res.jsonValue["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices";
- res.jsonValue["Name"] = "Open BMC Log Services Collection";
- res.jsonValue["Description"] =
+ asyncResp->res.jsonValue["@odata.id"] =
+ "/redfish/v1/Managers/bmc/LogServices";
+ asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
+ asyncResp->res.jsonValue["Description"] =
"Collection of LogServices for this Manager";
- nlohmann::json &logserviceArray = res.jsonValue["Members"];
+ nlohmann::json &logserviceArray = asyncResp->res.jsonValue["Members"];
logserviceArray = nlohmann::json::array();
+ logserviceArray.push_back(
+ {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/BmcLog"}});
#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
logserviceArray.push_back(
{{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/CpuLog"}});
#endif
- res.jsonValue["Members@odata.count"] = logserviceArray.size();
- res.end();
+ asyncResp->res.jsonValue["Members@odata.count"] =
+ logserviceArray.size();
}
};
-class CpuLogService : public Node
+class BMCLogService : public Node
{
public:
template <typename CrowApp>
- CpuLogService(CrowApp &app) :
- Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog")
+ BMCLogService(CrowApp &app) :
+ Node(app, "/redfish/v1/Managers/bmc/LogServices/BmcLog/")
+ {
+ // Set the id for SubRoute
+ Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices/BmcLog";
+ entityPrivileges = {
+ {boost::beast::http::verb::get, {{"Login"}}},
+ {boost::beast::http::verb::head, {{"Login"}}},
+ {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
+ {boost::beast::http::verb::put, {{"ConfigureManager"}}},
+ {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
+ {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
+ }
+
+ private:
+ void doGet(crow::Response &res, const crow::Request &req,
+ const std::vector<std::string> &params) override
+ {
+ std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
+ // Copy over the static data to include the entries added by SubRoute
+ asyncResp->res.jsonValue = Node::json;
+ asyncResp->res.jsonValue["@odata.type"] =
+ "#LogService.v1_1_0.LogService";
+ asyncResp->res.jsonValue["@odata.context"] =
+ "/redfish/v1/$metadata#LogService.LogService";
+ asyncResp->res.jsonValue["Name"] = "Open BMC Log Service";
+ asyncResp->res.jsonValue["Description"] = "BMC Log Service";
+ asyncResp->res.jsonValue["Id"] = "BMC Log";
+ asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
+ }
+};
+
+static int fillBMCLogEntryJson(const std::string &bmcLogEntryID,
+ sd_journal *journal,
+ nlohmann::json &bmcLogEntryJson)
+{
+ // Get the Log Entry contents
+ int ret = 0;
+ const char *data = nullptr;
+ size_t length = 0;
+
+ ret =
+ sd_journal_get_data(journal, "MESSAGE", (const void **)&data, &length);
+ if (ret < 0)
+ {
+ BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
+ return 1;
+ }
+ boost::string_view msg;
+ msg = boost::string_view(data, length);
+ // Only use the content after the "=" character.
+ msg.remove_prefix(std::min(msg.find("=") + 1, msg.size()));
+
+ // Get the severity from the PRIORITY field
+ boost::string_view priority;
+ int severity = 8; // Default to an invalid priority
+ ret =
+ sd_journal_get_data(journal, "PRIORITY", (const void **)&data, &length);
+ if (ret < 0)
+ {
+ BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
+ return 1;
+ }
+ priority = boost::string_view(data, length);
+ // Check length for sanity. Must be a single digit in the form
+ // "PRIORITY=[0-7]"
+ if (priority.size() > sizeof("PRIORITY=0"))
+ {
+ BMCWEB_LOG_ERROR << "Invalid PRIORITY field length";
+ return 1;
+ }
+ // Only use the content after the "=" character.
+ priority.remove_prefix(std::min(priority.find("=") + 1, priority.size()));
+ severity = strtol(priority.data(), nullptr, 10);
+
+ // Get the Created time from the timestamp
+ // Get the entry timestamp
+ uint64_t timestamp = 0;
+ ret = sd_journal_get_realtime_usec(journal, &timestamp);
+ if (ret < 0)
+ {
+ BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
+ << strerror(-ret);
+ }
+ time_t t =
+ static_cast<time_t>(timestamp / 1000 / 1000); // Convert from us to s
+ struct tm *loctime = localtime(&t);
+ char entryTime[64] = {};
+ if (NULL != loctime)
+ {
+ strftime(entryTime, sizeof(entryTime), "%FT%T%z", loctime);
+ }
+ // Insert the ':' into the timezone
+ boost::string_view t1(entryTime);
+ boost::string_view t2(entryTime);
+ if (t1.size() > 2 && t2.size() > 2)
+ {
+ t1.remove_suffix(2);
+ t2.remove_prefix(t2.size() - 2);
+ }
+ const std::string entryTimeStr(t1.to_string() + ":" + t2.to_string());
+
+ // Fill in the log entry with the gathered data
+ bmcLogEntryJson = {
+ {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
+ {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
+ {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries/" +
+ bmcLogEntryID},
+ {"Name", "BMC Journal Entry"},
+ {"Id", bmcLogEntryID},
+ {"Message", msg.to_string()},
+ {"EntryType", "Oem"},
+ {"Severity",
+ severity <= 2 ? "Critical"
+ : severity <= 4 ? "Warning" : severity <= 7 ? "OK" : ""},
+ {"OemRecordFormat", "Intel BMC Journal Entry"},
+ {"Created", std::move(entryTimeStr)}};
+ return 0;
+}
+
+class BMCLogEntryCollection : public Node
+{
+ public:
+ template <typename CrowApp>
+ BMCLogEntryCollection(CrowApp &app) :
+ Node(app, "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries/")
+ {
+ // Collections use static ID for SubRoute to add to its parent, but only
+ // load dynamic data so the duplicate static members don't get displayed
+ Node::json["@odata.id"] =
+ "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
+ entityPrivileges = {
+ {boost::beast::http::verb::get, {{"Login"}}},
+ {boost::beast::http::verb::head, {{"Login"}}},
+ {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
+ {boost::beast::http::verb::put, {{"ConfigureManager"}}},
+ {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
+ {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
+ }
+
+ private:
+ void doGet(crow::Response &res, const crow::Request &req,
+ const std::vector<std::string> &params) override
+ {
+ std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
+ // Collections don't include the static data added by SubRoute because
+ // it has a duplicate entry for members
+ asyncResp->res.jsonValue["@odata.type"] =
+ "#LogEntryCollection.LogEntryCollection";
+ asyncResp->res.jsonValue["@odata.context"] =
+ "/redfish/v1/"
+ "$metadata#LogEntryCollection.LogEntryCollection";
+ asyncResp->res.jsonValue["@odata.id"] =
+ "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
+ asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
+ asyncResp->res.jsonValue["Description"] =
+ "Collection of BMC Journal Entries";
+ nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
+ logEntryArray = nlohmann::json::array();
+
+ // Go through the journal and use the timestamp to create a unique ID
+ // for each entry
+ sd_journal *journalTmp = nullptr;
+ int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
+ if (ret < 0)
+ {
+ BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
+ asyncResp->res.result(
+ boost::beast::http::status::internal_server_error);
+ return;
+ }
+ std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
+ journalTmp, sd_journal_close);
+ journalTmp = nullptr;
+ uint64_t prevTs = 0;
+ int index = 0;
+ SD_JOURNAL_FOREACH(journal.get())
+ {
+ // Get the entry timestamp
+ uint64_t curTs = 0;
+ ret = sd_journal_get_realtime_usec(journal.get(), &curTs);
+ if (ret < 0)
+ {
+ BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
+ << strerror(-ret);
+ continue;
+ }
+ // If the timestamp isn't unique, increment the index
+ if (curTs == prevTs)
+ {
+ index++;
+ }
+ else
+ {
+ // Otherwise, reset it
+ index = 0;
+ }
+ // Save the timestamp
+ prevTs = curTs;
+
+ std::string idStr(std::to_string(curTs));
+ if (index > 0)
+ {
+ idStr += "_" + std::to_string(index);
+ }
+ logEntryArray.push_back({});
+ nlohmann::json &bmcLogEntry = logEntryArray.back();
+ if (fillBMCLogEntryJson(idStr, journal.get(), bmcLogEntry) != 0)
+ {
+ asyncResp->res.result(
+ boost::beast::http::status::internal_server_error);
+ return;
+ }
+ }
+ asyncResp->res.jsonValue["Members@odata.count"] = logEntryArray.size();
+ }
+};
+
+class BMCLogEntry : public Node
+{
+ public:
+ BMCLogEntry(CrowApp &app) :
+ Node(app, "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries/<str>/",
+ std::string())
+ {
+ entityPrivileges = {
+ {boost::beast::http::verb::get, {{"Login"}}},
+ {boost::beast::http::verb::head, {{"Login"}}},
+ {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
+ {boost::beast::http::verb::put, {{"ConfigureManager"}}},
+ {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
+ {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
+ }
+
+ private:
+ void doGet(crow::Response &res, const crow::Request &req,
+ const std::vector<std::string> &params) override
+ {
+ std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
+ if (params.size() != 1)
+ {
+ asyncResp->res.result(
+ boost::beast::http::status::internal_server_error);
+ return;
+ }
+ // Convert the unique ID back to a timestamp to find the entry
+ boost::string_view tsStr(params[0]);
+ boost::string_view indexStr(params[0]);
+ uint64_t ts = 0;
+ uint16_t index = 0;
+ auto underscorePos = tsStr.find("_");
+ if (underscorePos == tsStr.npos)
+ {
+ // Timestamp has no index
+ ts = strtoull(tsStr.data(), nullptr, 10);
+ }
+ else
+ {
+ // Timestamp has an index
+ tsStr.remove_suffix(tsStr.size() - underscorePos + 1);
+ ts = strtoull(tsStr.data(), nullptr, 10);
+ indexStr.remove_prefix(underscorePos + 1);
+ index =
+ static_cast<uint16_t>(strtoul(indexStr.data(), nullptr, 10));
+ }
+
+ sd_journal *journalTmp = nullptr;
+ int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
+ if (ret < 0)
+ {
+ BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
+ asyncResp->res.result(
+ boost::beast::http::status::internal_server_error);
+ return;
+ }
+ std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
+ journalTmp, sd_journal_close);
+ journalTmp = nullptr;
+ // Go to the timestamp in the log and move to the entry at the index
+ ret = sd_journal_seek_realtime_usec(journal.get(), ts);
+ for (int i = 0; i <= index; i++)
+ {
+ sd_journal_next(journal.get());
+ }
+ if (fillBMCLogEntryJson(params[0], journal.get(),
+ asyncResp->res.jsonValue) != 0)
+ {
+ asyncResp->res.result(
+ boost::beast::http::status::internal_server_error);
+ return;
+ }
+ }
+};
+
+class CPULogService : public Node
+{
+ public:
+ template <typename CrowApp>
+ CPULogService(CrowApp &app) :
+ Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/")
{
// Set the id for SubRoute
Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices/CpuLog";
entityPrivileges = {
- {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
- {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
- {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
- {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
- {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
- {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
+ {boost::beast::http::verb::get, {{"Login"}}},
+ {boost::beast::http::verb::head, {{"Login"}}},
+ {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
+ {boost::beast::http::verb::put, {{"ConfigureManager"}}},
+ {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
+ {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
}
private:
@@ -107,17 +412,20 @@ class CpuLogService : public Node
void doGet(crow::Response &res, const crow::Request &req,
const std::vector<std::string> &params) override
{
+ std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
// Copy over the static data to include the entries added by SubRoute
- res.jsonValue = Node::json;
- res.jsonValue["@odata.type"] = "#LogService.v1_1_0.LogService";
- res.jsonValue["@odata.context"] = "/redfish/v1/"
- "$metadata#LogService.LogService";
- res.jsonValue["Name"] = "Open BMC CPU Log Service";
- res.jsonValue["Description"] = "CPU Log Service";
- res.jsonValue["Id"] = "CPU Log";
- res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
- res.jsonValue["MaxNumberOfRecords"] = 3;
- res.jsonValue["Actions"] = {
+ asyncResp->res.jsonValue = Node::json;
+ asyncResp->res.jsonValue["@odata.type"] =
+ "#LogService.v1_1_0.LogService";
+ asyncResp->res.jsonValue["@odata.context"] =
+ "/redfish/v1/"
+ "$metadata#LogService.LogService";
+ asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Service";
+ asyncResp->res.jsonValue["Description"] = "CPU Log Service";
+ asyncResp->res.jsonValue["Id"] = "CPU Log";
+ asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
+ asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
+ asyncResp->res.jsonValue["Actions"] = {
{"Oem",
{{"#CpuLog.Immediate",
{{"target",
@@ -125,34 +433,33 @@ class CpuLogService : public Node
"CpuLog.Immediate"}}}}}};
#ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
- res.jsonValue["Actions"]["Oem"].push_back(
+ asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
{"#CpuLog.SendRawPeci",
{{"target",
"/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
"CpuLog.SendRawPeci"}}});
#endif
- res.end();
}
};
-class CpuLogEntryCollection : public Node
+class CPULogEntryCollection : public Node
{
public:
template <typename CrowApp>
- CpuLogEntryCollection(CrowApp &app) :
- Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries")
+ CPULogEntryCollection(CrowApp &app) :
+ Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/")
{
// Collections use static ID for SubRoute to add to its parent, but only
// load dynamic data so the duplicate static members don't get displayed
Node::json["@odata.id"] =
"/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries";
entityPrivileges = {
- {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
- {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
- {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
- {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
- {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
- {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
+ {boost::beast::http::verb::get, {{"Login"}}},
+ {boost::beast::http::verb::head, {{"Login"}}},
+ {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
+ {boost::beast::http::verb::put, {{"ConfigureManager"}}},
+ {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
+ {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
}
private:
@@ -162,52 +469,55 @@ class CpuLogEntryCollection : public Node
void doGet(crow::Response &res, const crow::Request &req,
const std::vector<std::string> &params) override
{
+ std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
// Collections don't include the static data added by SubRoute because
// it has a duplicate entry for members
- auto getLogEntriesCallback =
- [&res](const boost::system::error_code ec,
- const std::vector<std::string> &resp) {
- if (ec)
+ auto getLogEntriesCallback = [asyncResp](
+ const boost::system::error_code ec,
+ const std::vector<std::string> &resp) {
+ if (ec)
+ {
+ if (ec.value() !=
+ boost::system::errc::no_such_file_or_directory)
{
- if (ec.value() !=
- boost::system::errc::no_such_file_or_directory)
- {
- BMCWEB_LOG_DEBUG << "failed to get entries ec: "
- << ec.message();
- res.result(
- boost::beast::http::status::internal_server_error);
- res.end();
- return;
- }
+ BMCWEB_LOG_DEBUG << "failed to get entries ec: "
+ << ec.message();
+ asyncResp->res.result(
+ boost::beast::http::status::internal_server_error);
+ return;
}
- res.jsonValue["@odata.type"] =
- "#LogEntryCollection.LogEntryCollection";
- res.jsonValue["@odata.context"] =
- "/redfish/v1/"
- "$metadata#LogEntryCollection.LogEntryCollection";
- res.jsonValue["Name"] = "Open BMC CPU Log Entries";
- res.jsonValue["Description"] = "Collection of CPU Log Entries";
- nlohmann::json &logentryArray = res.jsonValue["Members"];
- logentryArray = nlohmann::json::array();
- for (const std::string &objpath : resp)
+ }
+ asyncResp->res.jsonValue["@odata.type"] =
+ "#LogEntryCollection.LogEntryCollection";
+ asyncResp->res.jsonValue["@odata.context"] =
+ "/redfish/v1/"
+ "$metadata#LogEntryCollection.LogEntryCollection";
+ asyncResp->res.jsonValue["@odata.id"] =
+ "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries";
+ asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Entries";
+ asyncResp->res.jsonValue["Description"] =
+ "Collection of CPU Log Entries";
+ nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
+ logEntryArray = nlohmann::json::array();
+ for (const std::string &objpath : resp)
+ {
+ // Don't list the immediate log
+ if (objpath.compare(cpuLogImmediatePath) == 0)
{
- // Don't list the immediate log
- if (objpath.compare(cpuLogImmediatePath) == 0)
- {
- continue;
- }
- std::size_t lastPos = objpath.rfind("/");
- if (lastPos != std::string::npos)
- {
- logentryArray.push_back(
- {{"@odata.id", "/redfish/v1/Managers/bmc/"
- "LogServices/CpuLog/Entries/" +
- objpath.substr(lastPos + 1)}});
- }
+ continue;
}
- res.jsonValue["Members@odata.count"] = logentryArray.size();
- res.end();
- };
+ std::size_t lastPos = objpath.rfind("/");
+ if (lastPos != std::string::npos)
+ {
+ logEntryArray.push_back(
+ {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/"
+ "CpuLog/Entries/" +
+ objpath.substr(lastPos + 1)}});
+ }
+ }
+ asyncResp->res.jsonValue["Members@odata.count"] =
+ logEntryArray.size();
+ };
crow::connections::systemBus->async_method_call(
std::move(getLogEntriesCallback),
"xyz.openbmc_project.ObjectMapper",
@@ -237,73 +547,76 @@ std::string getLogCreatedTime(const nlohmann::json &cpuLog)
return std::string();
}
-class CpuLogEntry : public Node
+class CPULogEntry : public Node
{
public:
- CpuLogEntry(CrowApp &app) :
+ CPULogEntry(CrowApp &app) :
Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/<str>/",
std::string())
{
entityPrivileges = {
- {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
- {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
- {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
- {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
- {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
- {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
+ {boost::beast::http::verb::get, {{"Login"}}},
+ {boost::beast::http::verb::head, {{"Login"}}},
+ {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
+ {boost::beast::http::verb::put, {{"ConfigureManager"}}},
+ {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
+ {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
}
private:
void doGet(crow::Response &res, const crow::Request &req,
const std::vector<std::string> &params) override
{
+ std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
if (params.size() != 1)
{
- res.result(boost::beast::http::status::internal_server_error);
- res.end();
+ asyncResp->res.result(
+ boost::beast::http::status::internal_server_error);
return;
}
const uint8_t logId = std::atoi(params[0].c_str());
- auto getStoredLogCallback = [&res,
- logId](const boost::system::error_code ec,
- const sdbusplus::message::variant<
- std::string> &resp) {
- if (ec)
- {
- BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
- res.result(boost::beast::http::status::internal_server_error);
- res.end();
- return;
- }
- const std::string *log = mapbox::getPtr<const std::string>(resp);
- if (log == nullptr)
- {
- res.result(boost::beast::http::status::internal_server_error);
- res.end();
- return;
- }
- nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
- if (j.is_discarded())
- {
- res.result(boost::beast::http::status::internal_server_error);
- res.end();
- return;
- }
- std::string t = getLogCreatedTime(j);
- res.jsonValue = {
- {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
- {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
- {"@odata.id",
- "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/" +
- std::to_string(logId)},
- {"Name", "CPU Debug Log"},
- {"Id", logId},
- {"EntryType", "Oem"},
- {"OemRecordFormat", "Intel CPU Log"},
- {"Oem", {{"Intel", std::move(j)}}},
- {"Created", std::move(t)}};
- res.end();
- };
+ auto getStoredLogCallback =
+ [asyncResp,
+ logId](const boost::system::error_code ec,
+ const sdbusplus::message::variant<std::string> &resp) {
+ if (ec)
+ {
+ BMCWEB_LOG_DEBUG << "failed to get log ec: "
+ << ec.message();
+ asyncResp->res.result(
+ boost::beast::http::status::internal_server_error);
+ return;
+ }
+ const std::string *log =
+ mapbox::getPtr<const std::string>(resp);
+ if (log == nullptr)
+ {
+ asyncResp->res.result(
+ boost::beast::http::status::internal_server_error);
+ return;
+ }
+ nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
+ if (j.is_discarded())
+ {
+ asyncResp->res.result(
+ boost::beast::http::status::internal_server_error);
+ return;
+ }
+ std::string t = getLogCreatedTime(j);
+ asyncResp->res.jsonValue = {
+ {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
+ {"@odata.context",
+ "/redfish/v1/$metadata#LogEntry.LogEntry"},
+ {"@odata.id",
+ "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/" +
+ std::to_string(logId)},
+ {"Name", "CPU Debug Log"},
+ {"Id", logId},
+ {"EntryType", "Oem"},
+ {"OemRecordFormat", "Intel CPU Log"},
+ {"Oem", {{"Intel", std::move(j)}}},
+ {"Created", std::move(t)}};
+ };
crow::connections::systemBus->async_method_call(
std::move(getStoredLogCallback), cpuLogObject,
cpuLogPath + std::string("/") + std::to_string(logId),
@@ -311,45 +624,47 @@ class CpuLogEntry : public Node
}
};
-class ImmediateCpuLog : public Node
+class ImmediateCPULog : public Node
{
public:
- ImmediateCpuLog(CrowApp &app) :
+ ImmediateCPULog(CrowApp &app) :
Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
- "CpuLog.Immediate")
+ "CpuLog.Immediate/")
{
entityPrivileges = {
- {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
- {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
- {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
- {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
- {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
- {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
+ {boost::beast::http::verb::get, {{"Login"}}},
+ {boost::beast::http::verb::head, {{"Login"}}},
+ {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
+ {boost::beast::http::verb::put, {{"ConfigureManager"}}},
+ {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
+ {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
}
private:
void doPost(crow::Response &res, const crow::Request &req,
const std::vector<std::string> &params) override
{
+ std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
static std::unique_ptr<sdbusplus::bus::match::match>
immediateLogMatcher;
// Only allow one Immediate Log request at a time
if (immediateLogMatcher != nullptr)
{
- res.addHeader("Retry-After", "30");
- res.result(boost::beast::http::status::service_unavailable);
+ asyncResp->res.addHeader("Retry-After", "30");
+ asyncResp->res.result(
+ boost::beast::http::status::service_unavailable);
messages::addMessageToJson(
- res.jsonValue, messages::serviceTemporarilyUnavailable("30"),
+ asyncResp->res.jsonValue,
+ messages::serviceTemporarilyUnavailable("30"),
"/CpuLog.Immediate");
- res.end();
return;
}
// Make this static so it survives outside this method
static boost::asio::deadline_timer timeout(*req.ioService);
timeout.expires_from_now(boost::posix_time::seconds(30));
- timeout.async_wait([&res](const boost::system::error_code &ec) {
+ timeout.async_wait([asyncResp](const boost::system::error_code &ec) {
immediateLogMatcher = nullptr;
if (ec)
{
@@ -363,11 +678,11 @@ class ImmediateCpuLog : public Node
}
BMCWEB_LOG_ERROR << "Timed out waiting for immediate log";
- res.result(boost::beast::http::status::internal_server_error);
- res.end();
+ asyncResp->res.result(
+ boost::beast::http::status::internal_server_error);
});
- auto immediateLogMatcherCallback = [&res](
+ auto immediateLogMatcherCallback = [asyncResp](
sdbusplus::message::message &m) {
BMCWEB_LOG_DEBUG << "Immediate log available match fired";
boost::system::error_code ec;
@@ -387,8 +702,8 @@ class ImmediateCpuLog : public Node
interfacesAdded[cpuLogInterface]["Log"]);
if (log == nullptr)
{
- res.result(boost::beast::http::status::internal_server_error);
- res.end();
+ asyncResp->res.result(
+ boost::beast::http::status::internal_server_error);
// Careful with immediateLogMatcher. It is a unique_ptr to the
// match object inside which this lambda is executing. Once it
// is set to nullptr, the match object will be destroyed and the
@@ -400,8 +715,8 @@ class ImmediateCpuLog : public Node
nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
if (j.is_discarded())
{
- res.result(boost::beast::http::status::internal_server_error);
- res.end();
+ asyncResp->res.result(
+ boost::beast::http::status::internal_server_error);
// Careful with immediateLogMatcher. It is a unique_ptr to the
// match object inside which this lambda is executing. Once it
// is set to nullptr, the match object will be destroyed and the
@@ -411,7 +726,7 @@ class ImmediateCpuLog : public Node
return;
}
std::string t = getLogCreatedTime(j);
- res.jsonValue = {
+ asyncResp->res.jsonValue = {
{"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
{"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
{"Name", "CPU Debug Log"},
@@ -419,7 +734,6 @@ class ImmediateCpuLog : public Node
{"OemRecordFormat", "Intel CPU Log"},
{"Oem", {{"Intel", std::move(j)}}},
{"Created", std::move(t)}};
- res.end();
// Careful with immediateLogMatcher. It is a unique_ptr to the
// match object inside which this lambda is executing. Once it is
// set to nullptr, the match object will be destroyed and the lambda
@@ -434,25 +748,24 @@ class ImmediateCpuLog : public Node
std::move(immediateLogMatcherCallback));
auto generateImmediateLogCallback =
- [&res](const boost::system::error_code ec,
- const std::string &resp) {
+ [asyncResp](const boost::system::error_code ec,
+ const std::string &resp) {
if (ec)
{
if (ec.value() ==
boost::system::errc::operation_not_supported)
{
messages::addMessageToJson(
- res.jsonValue, messages::resourceInStandby(),
- "/CpuLog.Immediate");
- res.result(
+ asyncResp->res.jsonValue,
+ messages::resourceInStandby(), "/CpuLog.Immediate");
+ asyncResp->res.result(
boost::beast::http::status::service_unavailable);
}
else
{
- res.result(
+ asyncResp->res.result(
boost::beast::http::status::internal_server_error);
}
- res.end();
boost::system::error_code timeoutec;
timeout.cancel(timeoutec);
if (timeoutec)
@@ -470,12 +783,12 @@ class ImmediateCpuLog : public Node
}
};
-class SendRawPeci : public Node
+class SendRawPECI : public Node
{
public:
- SendRawPeci(CrowApp &app) :
+ SendRawPECI(CrowApp &app) :
Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
- "CpuLog.SendRawPeci")
+ "CpuLog.SendRawPeci/")
{
entityPrivileges = {
{boost::beast::http::verb::get, {{"ConfigureComponents"}}},
@@ -490,67 +803,63 @@ class SendRawPeci : public Node
void doPost(crow::Response &res, const crow::Request &req,
const std::vector<std::string> &params) override
{
+ std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
// Get the Raw PECI command from the request
- nlohmann::json rawPeciCmd;
- if (!json_util::processJsonFromRequest(res, req, rawPeciCmd))
+ nlohmann::json rawPECICmd;
+ if (!json_util::processJsonFromRequest(res, req, rawPECICmd))
{
return;
}
// Get the Client Address from the request
- nlohmann::json::const_iterator caIt = rawPeciCmd.find("ClientAddress");
- if (caIt == rawPeciCmd.end())
+ nlohmann::json::const_iterator caIt = rawPECICmd.find("ClientAddress");
+ if (caIt == rawPECICmd.end())
{
messages::addMessageToJson(
- res.jsonValue, messages::propertyMissing("ClientAddress"),
- "/ClientAddress");
- res.result(boost::beast::http::status::bad_request);
- res.end();
+ asyncResp->res.jsonValue,
+ messages::propertyMissing("ClientAddress"), "/ClientAddress");
+ asyncResp->res.result(boost::beast::http::status::bad_request);
return;
}
const uint64_t *ca = caIt->get_ptr<const uint64_t *>();
if (ca == nullptr)
{
messages::addMessageToJson(
- res.jsonValue,
+ asyncResp->res.jsonValue,
messages::propertyValueTypeError(caIt->dump(), "ClientAddress"),
"/ClientAddress");
- res.result(boost::beast::http::status::bad_request);
- res.end();
+ asyncResp->res.result(boost::beast::http::status::bad_request);
return;
}
// Get the Read Length from the request
const uint8_t clientAddress = static_cast<uint8_t>(*ca);
- nlohmann::json::const_iterator rlIt = rawPeciCmd.find("ReadLength");
- if (rlIt == rawPeciCmd.end())
+ nlohmann::json::const_iterator rlIt = rawPECICmd.find("ReadLength");
+ if (rlIt == rawPECICmd.end())
{
- messages::addMessageToJson(res.jsonValue,
+ messages::addMessageToJson(asyncResp->res.jsonValue,
messages::propertyMissing("ReadLength"),
"/ReadLength");
- res.result(boost::beast::http::status::bad_request);
- res.end();
+ asyncResp->res.result(boost::beast::http::status::bad_request);
return;
}
const uint64_t *rl = rlIt->get_ptr<const uint64_t *>();
if (rl == nullptr)
{
messages::addMessageToJson(
- res.jsonValue,
+ asyncResp->res.jsonValue,
messages::propertyValueTypeError(rlIt->dump(), "ReadLength"),
"/ReadLength");
- res.result(boost::beast::http::status::bad_request);
- res.end();
+ asyncResp->res.result(boost::beast::http::status::bad_request);
return;
}
// Get the PECI Command from the request
const uint32_t readLength = static_cast<uint32_t>(*rl);
- nlohmann::json::const_iterator pcIt = rawPeciCmd.find("PECICommand");
- if (pcIt == rawPeciCmd.end())
+ nlohmann::json::const_iterator pcIt = rawPECICmd.find("PECICommand");
+ if (pcIt == rawPECICmd.end())
{
- messages::addMessageToJson(res.jsonValue,
+ messages::addMessageToJson(asyncResp->res.jsonValue,
messages::propertyMissing("PECICommand"),
"/PECICommand");
- res.result(boost::beast::http::status::bad_request);
- res.end();
+ asyncResp->res.result(boost::beast::http::status::bad_request);
return;
}
std::vector<uint8_t> peciCommand;
@@ -560,36 +869,35 @@ class SendRawPeci : public Node
if (val == nullptr)
{
messages::addMessageToJson(
- res.jsonValue,
+ asyncResp->res.jsonValue,
messages::propertyValueTypeError(
pc.dump(),
"PECICommand/" + std::to_string(peciCommand.size())),
"/PECICommand");
- res.result(boost::beast::http::status::bad_request);
- res.end();
+ asyncResp->res.result(boost::beast::http::status::bad_request);
return;
}
peciCommand.push_back(static_cast<uint8_t>(*val));
}
// Callback to return the Raw PECI response
- auto sendRawPeciCallback = [&res](const boost::system::error_code ec,
- const std::vector<uint8_t> &resp) {
- if (ec)
- {
- BMCWEB_LOG_DEBUG << "failed to send PECI command ec: "
- << ec.message();
- res.result(boost::beast::http::status::internal_server_error);
- res.end();
- return;
- }
- res.jsonValue = {{"Name", "PECI Command Response"},
- {"PECIResponse", resp}};
- res.end();
- };
+ auto sendRawPECICallback =
+ [asyncResp](const boost::system::error_code ec,
+ const std::vector<uint8_t> &resp) {
+ if (ec)
+ {
+ BMCWEB_LOG_DEBUG << "failed to send PECI command ec: "
+ << ec.message();
+ asyncResp->res.result(
+ boost::beast::http::status::internal_server_error);
+ return;
+ }
+ asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
+ {"PECIResponse", resp}};
+ };
// Call the SendRawPECI command with the provided data
crow::connections::systemBus->async_method_call(
- std::move(sendRawPeciCallback), cpuLogObject, cpuLogPath,
- cpuLogRawPeciInterface, "SendRawPeci", clientAddress, readLength,
+ std::move(sendRawPECICallback), cpuLogObject, cpuLogPath,
+ cpuLogRawPECIInterface, "SendRawPeci", clientAddress, readLength,
peciCommand);
}
};