diff options
-rw-r--r-- | redfish-core/include/redfish.hpp | 13 | ||||
-rw-r--r-- | redfish-core/lib/log_services.hpp | 730 |
2 files changed, 527 insertions, 216 deletions
diff --git a/redfish-core/include/redfish.hpp b/redfish-core/include/redfish.hpp index 0998634f02..3aef06a126 100644 --- a/redfish-core/include/redfish.hpp +++ b/redfish-core/include/redfish.hpp @@ -68,14 +68,17 @@ class RedfishService nodes.emplace_back( std::make_unique<VlanNetworkInterfaceCollection>(app)); nodes.emplace_back(std::make_unique<LogServiceCollection>(app)); + nodes.emplace_back(std::make_unique<BMCLogService>(app)); + nodes.emplace_back(std::make_unique<BMCLogEntryCollection>(app)); + nodes.emplace_back(std::make_unique<BMCLogEntry>(app)); #ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG - nodes.emplace_back(std::make_unique<CpuLogService>(app)); - nodes.emplace_back(std::make_unique<CpuLogEntryCollection>(app)); - nodes.emplace_back(std::make_unique<CpuLogEntry>(app)); - nodes.emplace_back(std::make_unique<ImmediateCpuLog>(app)); + nodes.emplace_back(std::make_unique<CPULogService>(app)); + nodes.emplace_back(std::make_unique<CPULogEntryCollection>(app)); + nodes.emplace_back(std::make_unique<CPULogEntry>(app)); + nodes.emplace_back(std::make_unique<ImmediateCPULog>(app)); #ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI - nodes.emplace_back(std::make_unique<SendRawPeci>(app)); + nodes.emplace_back(std::make_unique<SendRawPECI>(app)); #endif // BMCWEB_ENABLE_REDFISH_RAW_PECI #endif // BMCWEB_ENABLE_REDFISH_CPU_LOG 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> ¶ms) 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> ¶ms) 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, ×tamp); + 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> ¶ms) 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> ¶ms) 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> ¶ms) 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> ¶ms) 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> ¶ms) 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> ¶ms) 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> ¶ms) 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); } }; |